1module Test_dhcpd =
2
3let lns = Dhcpd.lns
4
5let conf = "#
6# Sample configuration file for ISC dhcpd for Debian
7#
8# Attention: If /etc/ltsp/dhcpd.conf exists, that will be used as
9# configuration file instead of this file.
10#
11# $Id: dhcpd.conf,v 1.1.1.1 2002/05/21 00:07:44 peloy Exp $
12#
13
14# The ddns-updates-style parameter controls whether or not the server will
15# attempt to do a DNS update when a lease is confirmed. We default to the
16# behavior of the version 2 packages ('none', since DHCP v2 didn't
17# have support for DDNS.)
18ddns-update-style none;
19
20# option definitions common to all supported networks...
21option domain-name \"example.org\";
22option domain-name-servers ns1.example.org, ns2.example.org;
23
24default-lease-time 600;
25max-lease-time 7200;
26
27# If this DHCP server is the official DHCP server for the local
28# network, the authoritative directive should be uncommented.
29authoritative;
30
31allow booting;
32allow bootp;
33
34# Use this to send dhcp log messages to a different log file (you also
35# have to hack syslog.conf to complete the redirection).
36log-facility local7;
37
38# No service will be given on this subnet, but declaring it helps the
39# DHCP server to understand the network topology.
40
41subnet 10.152.187.0 netmask 255.255.255.0 {
42}
43
44# This is a very basic subnet declaration.
45
46subnet 10.254.239.0 netmask 255.255.255.224 {
47  range 10.254.239.10 10.254.239.20;
48  option routers rtr-239-0-1.example.org, rtr-239-0-2.example.org;
49}
50
51# This declaration allows BOOTP clients to get dynamic addresses,
52# which we don't really recommend.
53
54subnet 10.254.239.32 netmask 255.255.255.224 {
55  range dynamic-bootp 10.254.239.40 10.254.239.60;
56  option broadcast-address 10.254.239.31;
57  option routers rtr-239-32-1.example.org;
58}
59
60# A slightly different configuration for an internal subnet.
61subnet 10.5.5.0 netmask 255.255.255.224 {
62  range 10.5.5.26 10.5.5.30;
63  option domain-name-servers ns1.internal.example.org;
64  option domain-name \"internal.example.org\";
65  option routers 10.5.5.1;
66  option broadcast-address 10.5.5.31;
67  default-lease-time 600;
68  max-lease-time 7200;
69}
70
71# Hosts which require special configuration options can be listed in
72# host statements.   If no address is specified, the address will be
73# allocated dynamically (if possible), but the host-specific information
74# will still come from the host declaration.
75
76host passacaglia {
77  hardware ethernet 0:0:c0:5d:bd:95;
78  filename \"vmunix.passacaglia\";
79  server-name \"toccata.fugue.com\";
80}
81
82# Fixed IP addresses can also be specified for hosts.   These addresses
83# should not also be listed as being available for dynamic assignment.
84# Hosts for which fixed IP addresses have been specified can boot using
85# BOOTP or DHCP.   Hosts for which no fixed address is specified can only
86# be booted with DHCP, unless there is an address range on the subnet
87# to which a BOOTP client is connected which has the dynamic-bootp flag
88# set.
89host fantasia {
90  hardware ethernet 08:00:07:26:c0:a5;
91  fixed-address fantasia.fugue.com;
92}
93
94# You can declare a class of clients and then do address allocation
95# based on that.   The example below shows a case where all clients
96# in a certain class get addresses on the 10.17.224/24 subnet, and all
97# other clients get addresses on the 10.0.29/24 subnet.
98
99#class \"foo\" {
100#  match if substring (option vendor-class-identifier, 0, 4) = \"SUNW\";
101#}
102
103shared-network 224-29 {
104  subnet 10.17.224.0 netmask 255.255.255.0 {
105    option routers rtr-224.example.org;
106  }
107  subnet 10.0.29.0 netmask 255.255.255.0 {
108    option routers rtr-29.example.org;
109  }
110  pool {
111    allow members of \"foo\";
112    range 10.17.224.10 10.17.224.250;
113  }
114  pool {
115    deny members of \"foo\";
116    range 10.0.29.10 10.0.29.230;
117  }
118}
119"
120
121test lns get "authoritative;" = { "authoritative" }
122test lns get "ddns-update-style none;" = { "ddns-update-style" = "none" }
123test lns get "option domain-name \"example.org\";" =
124  { "option"
125    { "domain-name"
126      { "arg" = "example.org" }
127    }
128  }
129
130test lns get "option domain-name-servers ns1.example.org, ns2.example.org;" =
131  { "option"
132    { "domain-name-servers"
133      { "arg" = "ns1.example.org" }
134      { "arg" = "ns2.example.org" }
135    }
136  }
137
138test lns get "default-lease-time 600;" = { "default-lease-time" = "600" }
139test lns get "range 10.254.239.60;" =
140{ "range"
141    { "to" = "10.254.239.60" }
142  }
143
144test lns get "range dynamic-bootp 10.254.239.60;" =
145  { "range"
146    { "flag" = "dynamic-bootp" }
147    { "to" = "10.254.239.60" }
148  }
149
150test lns get "range dynamic-bootp 10.254.239.40 10.254.239.60;" =
151  { "range"
152    { "flag" = "dynamic-bootp" }
153    { "from" = "10.254.239.40" }
154    { "to" = "10.254.239.60" }
155  }
156
157test lns get "subnet 10.152.187.0 netmask 255.255.255.0 {}\n" =
158  { "subnet"
159    { "network" = "10.152.187.0" }
160    { "netmask" = "255.255.255.0" }
161  }
162
163test lns get " pool {
164    pool {
165
166    }
167}
168" =
169  { "pool"
170    { "pool" }
171  }
172
173test lns get "group { host some-host {hardware ethernet 00:00:aa:bb:cc:dd;
174fixed-address 10.1.1.1;}}" =
175  { "group"
176    { "host" = "some-host"
177      { "hardware"
178        { "type" = "ethernet" }
179        { "address" = "00:00:aa:bb:cc:dd" }
180      }
181      { "fixed-address" = "10.1.1.1" }
182    }
183  }
184
185test lns get "group fan-tas_tic { }" =
186  { "group" = "fan-tas_tic" }
187
188test Dhcpd.stmt_secu get "allow members of \"foo\";" =  { "allow-members-of" = "foo" }
189test Dhcpd.stmt_secu get "allow booting;" =  { "allow" = "booting" }
190test Dhcpd.stmt_secu get "allow bootp;" =  { "allow" = "bootp" }
191test Dhcpd.stmt_option get "option voip-boot-server code 66 = string;" =
192  { "rfc-code"
193    { "label" = "voip-boot-server" }
194    { "code" = "66" }
195    { "type" = "string" }
196  }
197
198test Dhcpd.stmt_option get "option special-option code 25 = array of string;" =
199  { "rfc-code"
200    { "label" = "special-option" }
201    { "code" = "25" }
202    { "type" = "array of string" }
203  }
204
205test Dhcpd.stmt_option get "option special-option code 25 = integer 32;" =
206  { "rfc-code"
207    { "label" = "special-option" }
208    { "code" = "25" }
209    { "type" = "integer 32" }
210  }
211
212
213test Dhcpd.stmt_option get "option special-option code 25 = array of integer 32;" =
214  { "rfc-code"
215    { "label" = "special-option" }
216    { "code" = "25" }
217    { "type" = "array of integer 32" }
218  }
219
220
221
222test Dhcpd.lns get "authoritative;
223log-facility local7;
224ddns-update-style none;
225default-lease-time 21600;
226max-lease-time 43200;
227
228# Additional options for VOIP
229option voip-boot-server code 66 = string;
230option voip-vlan-id code 128 = string;
231" =
232  { "authoritative" }
233  { "log-facility" = "local7" }
234  { "ddns-update-style" = "none" }
235  { "default-lease-time" = "21600" }
236  { "max-lease-time" = "43200"
237    { "#comment" = "Additional options for VOIP" }
238  }
239  { "rfc-code"
240    { "label" = "voip-boot-server" }
241    { "code" = "66" }
242    { "type" = "string" }
243  }
244  { "rfc-code"
245    { "label" = "voip-vlan-id" }
246    { "code" = "128" }
247    { "type" = "string" }
248  }
249
250
251test Dhcpd.lns get "
252option domain-name-servers 10.1.1.1, 10.11.2.1, 10.1.3.1;
253next-server 10.1.1.1;
254
255failover peer \"redondance01\" {
256         primary;
257         address 10.1.1.1;
258         port 647;
259         peer address 10.1.1.1;
260         peer port 647;
261         max-response-delay 20;
262         max-unacked-updates 10;
263         mclt 3600;         #comment.
264         split 128;         #comment.
265         load balance max seconds 3;
266       }
267" =
268  {  }
269  { "option"
270    { "domain-name-servers"
271      { "arg" = "10.1.1.1" }
272      { "arg" = "10.11.2.1" }
273      { "arg" = "10.1.3.1" }
274    }
275  }
276  { "next-server" = "10.1.1.1" }
277  { "failover peer" = "redondance01"
278    { "primary" }
279    { "address" = "10.1.1.1" }
280    { "port" = "647" }
281    { "peer address" = "10.1.1.1" }
282    { "peer port" = "647" }
283    { "max-response-delay" = "20" }
284    { "max-unacked-updates" = "10" }
285    { "mclt" = "3600"
286      { "#comment" = "comment." }
287    }
288    { "split" = "128"
289      { "#comment" = "comment." }
290    }
291    { "load balance max seconds" = "3" }
292  }
293
294
295(* test get and put for record types *)
296let record_test = "option test_records code 123 = { string, ip-address, integer 32, ip6-address, domain-list };"
297
298test Dhcpd.lns get record_test =
299 { "rfc-code"
300   { "label" = "test_records" }
301   { "code" = "123" }
302     { "record"
303        { "1" = "string" }
304        { "2" = "ip-address" }
305        { "3" = "integer 32" }
306        { "4" = "ip6-address" }
307        { "5" = "domain-list" }
308     }
309 }
310
311test Dhcpd.lns put record_test after set "/rfc-code[1]/code" "124" =
312  "option test_records code 124 = { string, ip-address, integer 32, ip6-address, domain-list };"
313
314test Dhcpd.lns get "
315option CallManager code 150 = ip-address;
316option slp-directory-agent true 10.1.1.1, 10.2.2.2;
317option slp-service-scope true \"SLP-GLOBAL\";
318option nds-context \"EXAMPLE\";
319option nds-tree-name \"EXAMPLE\";
320" =
321  {  }
322  { "rfc-code"
323    { "label" = "CallManager" }
324    { "code" = "150" }
325    { "type" = "ip-address" }
326  }
327  { "option"
328    { "slp-directory-agent" = "true"
329      { "arg" = "10.1.1.1" }
330      { "arg" = "10.2.2.2" }
331    }
332  }
333  { "option"
334    { "slp-service-scope" = "true"
335      { "arg" = "SLP-GLOBAL" }
336    }
337  }
338  { "option"
339    { "nds-context"
340      { "arg" = "EXAMPLE" }
341    }
342  }
343  { "option"
344    { "nds-tree-name"
345      { "arg" = "EXAMPLE" }
346    }
347  }
348
349
350test Dhcpd.lns get "option voip-vlan-id \"VLAN=1234;\";" =
351  { "option"
352    { "voip-vlan-id"
353      { "arg" = "VLAN=1234;" }
354    }
355  }
356
357test Dhcpd.lns get "option domain-name \"x.example.com y.example.com z.example.com\";" =
358  { "option"
359    { "domain-name"
360      { "arg" = "x.example.com y.example.com z.example.com" }
361    }
362  }
363
364test Dhcpd.lns get "include \"/etc/dhcpd.master\";" =
365  { "include" = "/etc/dhcpd.master" }
366
367test Dhcpd.lns put "\n" after set "/include" "/etc/dhcpd.master" =
368  "\ninclude \"/etc/dhcpd.master\";\n"
369
370test Dhcpd.fct_args get "(option dhcp-client-identifier, 1, 3)" =
371  { "args"
372    { "arg" = "option dhcp-client-identifier" }
373    { "arg" = "1" }
374    { "arg" = "3" }
375  }
376
377test Dhcpd.stmt_match get "match if substring (option dhcp-client-identifier, 1, 3) = \"RAS\";" =
378  { "match"
379    { "function" = "substring"
380      { "args"
381        { "arg" = "option dhcp-client-identifier" }
382        { "arg" = "1" }
383        { "arg" = "3" }
384      }
385    }
386    { "value" = "RAS" }
387  }
388
389test Dhcpd.stmt_match get "match if suffix (option dhcp-client-identifier, 4) = \"RAS\";" =
390  { "match"
391    { "function" = "suffix"
392      { "args"
393        { "arg" = "option dhcp-client-identifier" }
394        { "arg" = "4" }
395      }
396    }
397    { "value" = "RAS" }
398  }
399
400test Dhcpd.stmt_match get "match if option vendor-class-identifier=\"RAS\";" =
401  { "match"
402    { "option" = "vendor-class-identifier"
403      { "value" = "RAS" }
404    }
405  }
406
407
408test Dhcpd.lns get "match pick-first-value (option dhcp-client-identifier, hardware);" =
409  { "match"
410    { "function" = "pick-first-value"
411      { "args"
412        { "arg" = "option dhcp-client-identifier" }
413        { "arg" = "hardware"  }
414      }
415    }
416  }
417
418test Dhcpd.fct_args get "(16, 32, \"\", substring(hardware, 0, 4))" =
419  { "args"
420    { "arg" = "16" }
421    { "arg" = "32" }
422    { "arg" = "\"\"" }
423    { "arg" = "substring(hardware, 0, 4)" }
424  }
425
426test Dhcpd.stmt_match get "match if binary-to-ascii(16, 32, \"\", substring(hardware, 0, 4)) = \"1525400\";" =
427  { "match"
428    { "function" = "binary-to-ascii"
429      { "args"
430        { "arg" = "16" }
431        { "arg" = "32" }
432        { "arg" = "\"\"" }
433        { "arg" = "substring(hardware, 0, 4)" }
434      }
435    }
436    { "value" = "1525400" }
437  }
438
439test Dhcpd.lns get "subclass allocation-class-1 1:8:0:2b:4c:39:ad;" =
440  { "subclass"
441    { "name" = "allocation-class-1" }
442    { "value" = "1:8:0:2b:4c:39:ad" }
443  }
444
445
446test Dhcpd.lns get "subclass \"allocation-class-1\" 1:8:0:2b:4c:39:ad;" =
447  { "subclass"
448    { "name" = "allocation-class-1" }
449    { "value" = "1:8:0:2b:4c:39:ad" }
450  }
451
452test Dhcpd.lns get "subclass \"quoted class\" \"quoted value\";" =
453  { "subclass"
454    { "name" = "quoted class" }
455    { "value" = "quoted value" }
456  }
457
458
459(* overall test *)
460test Dhcpd.lns put conf after rm "/x" = conf
461
462(* bug #293: primary should support argument *)
463let input293 = "zone EXAMPLE.ORG. {
464  primary 127.0.0.1;
465}"
466
467test Dhcpd.lns get input293 =
468  { "zone" = "EXAMPLE.ORG."
469    { "primary" = "127.0.0.1" }
470  }
471
472(* bug #311: filename should be quoted *)
473let input311 = "subnet 172.16.0.0 netmask 255.255.255.0 {
474filename \"pxelinux.0\";
475}"
476
477test Dhcpd.lns put "subnet 172.16.0.0 netmask 255.255.255.0 {
478}" after
479  set "subnet/filename" "pxelinux.0" = input311
480
481(* GH issue #34: support conditional structures *)
482let gh34_empty = "if exists dhcp-parameter-request-list {
483}\n"
484
485test Dhcpd.lns get gh34_empty =
486  { "@if" = "exists dhcp-parameter-request-list" }
487
488let gh34_empty_multi = "subnet 192.168.100.0 netmask 255.255.255.0 {
489 if true {
490 } elsif false {
491 } else {
492 }
493}\n"
494
495test Dhcpd.lns get gh34_empty_multi =
496  { "subnet"
497    { "network" = "192.168.100.0" }
498    { "netmask" = "255.255.255.0" }
499    { "@if" = "true"
500      { "@elsif" = "false" }
501      { "@else" } }
502  }
503
504let gh34_simple = "if exists dhcp-parameter-request-list {
505  default-lease-time 600;
506  } else {
507default-lease-time 200;
508}\n"
509
510test Dhcpd.lns get gh34_simple =
511  { "@if" = "exists dhcp-parameter-request-list"
512    { "default-lease-time" = "600" }
513    { "@else"
514      { "default-lease-time" = "200" } } }
515
516test Dhcpd.lns get "omapi-key fookey;" =
517  { "omapi-key" = "fookey" }
518
519(* almost all DHCP groups should support braces starting on the next line *)
520test Dhcpd.lns get "class introduction
521{
522}" =
523  { "class" = "introduction" }
524
525(* equals should work the same *)
526test Dhcpd.lns get "option test_records code 123 =
527                             string;" =
528 { "rfc-code"
529   { "label" = "test_records" }
530   { "code" = "123" }
531   { "type" = "string" }
532 }
533
534test Dhcpd.lns get "deny members of \"Are things like () allowed?\";" =
535  { "deny-members-of" = "Are things like () allowed?" }
536
537test Dhcpd.lns get "deny unknown clients;" =
538  { "deny" = "unknown clients" }
539test Dhcpd.lns get "deny known-clients;" =
540  { "deny" = "known-clients" }
541
542test Dhcpd.lns get "set ClientMac = binary-to-ascii(16, 8, \":\" , substring(hardware, 1, 6));" =
543  { "set" = "ClientMac"
544    { "value" = "binary-to-ascii(16, 8, \":\" , substring(hardware, 1, 6))" }
545  }
546
547test Dhcpd.lns get "set myvariable = foo;" =
548  { "set" = "myvariable"
549    { "value" = "foo" }
550  }
551
552test Dhcpd.stmt_hardware get "hardware fddi 00:01:02:03:04:05;" =
553  { "hardware"
554    { "type" = "fddi" }
555    { "address" = "00:01:02:03:04:05" }
556  }
557
558test Dhcpd.lns get "on commit
559{
560  set test = thing;
561}" =
562  { "on" = "commit"
563    { "set" = "test"
564      { "value" = "thing" }
565    }
566  }
567
568(* key block get/put/set test *)
569let key_tests = "key sample {
570    algorithm hmac-md5;
571    secret \"secret==\";
572}
573
574key \"interesting\" { };
575
576key \"third key\" {
577    secret \"two==\";
578}"
579
580test Dhcpd.lns get key_tests =
581  { "key_block" = "sample"
582    { "algorithm"  = "hmac-md5" }
583    { "secret" = "secret==" }
584  }
585  { "key_block" = "interesting" }
586  { "key_block" = "third key"
587    { "secret" = "two==" }
588  }
589
590test Dhcpd.lns put key_tests after set "/key_block[1]" "sample2" =
591  "key sample2 {
592    algorithm hmac-md5;
593    secret \"secret==\";
594}
595
596key \"interesting\" { };
597
598key \"third key\" {
599    secret \"two==\";
600}"
601
602test Dhcpd.lns get "group \"hello\" { }" =
603  { "group" = "hello" }
604
605test Dhcpd.lns get "class \"testing class with spaces and quotes and ()\" {}" =
606  { "class" = "testing class with spaces and quotes and ()" }
607