ISC DHCP  4.4.3
A reference DHCPv4 and DHCPv6 implementation
confparse.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2017-2022 Internet Systems Consortium, Inc. ("ISC")
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
14  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  *
16  * Internet Systems Consortium, Inc.
17  * PO Box 360
18  * Newmarket, NH 03857 USA
19  * <info@isc.org>
20  * https://www.isc.org/
21  *
22  */
23 
24 /* From server/confpars.c */
25 
26 #include "keama.h"
27 
28 #include <sys/errno.h>
29 #include <arpa/inet.h>
30 #include <assert.h>
31 #include <ctype.h>
32 #include <fcntl.h>
33 #include <time.h>
34 #include <stdlib.h>
35 #include <string.h>
36 
37 /* Print failover stuff once */
39 
40 /* To manage host-reservation-identifiers */
44 
45 /* option and relays used for flexible host identifier */
46 const struct option *host_id_option = NULL;
48 
49 /* Simple or complex config */
50 unsigned subnet_counter = 0;
51 
52 /* For subclass name generation */
53 unsigned subclass_counter = 0;
54 
55 /* To map reservations to declared subnets */
56 struct subnet {
57  struct element *subnet;
58  struct element *share;
59  struct string *addr;
60  struct string *mask;
61  TAILQ_ENTRY(subnet) next;
62 };
63 
64 TAILQ_HEAD(subnets, subnet) known_subnets;
65 
66 /* To map pools to subnets inside a shared-network */
67 struct range {
68  struct element *pool;
69  struct element *share;
70  struct string *low;
71  TAILQ_ENTRY(range) next;
72 };
73 
74 TAILQ_HEAD(ranges, range) known_ranges;
75 
76 static void post_process_lifetimes(struct parse *);
77 static size_t post_process_reservations(struct parse *);
78 static void post_process_classes(struct parse *);
79 static void post_process_generated_classes(struct parse *);
80 static void check_depend(struct element *, struct element *);
81 static void post_process_option_definitions(struct parse *);
82 static void add_host_reservation_identifiers(struct parse *, const char *);
83 static void add_host_id_option(struct parse *, const struct option *, int);
84 static void subclass_inherit(struct parse *, struct element *,
85  struct element *);
86 static void add_match_class(struct parse *, struct element *,
87  struct element *);
88 static void option_data_derive(struct parse *, struct handle *,
89  struct handle *);
90 static void derive_classes(struct parse *, struct handle *, struct handle *);
91 static isc_boolean_t is_hexa_only(const char *, unsigned len);
92 static void new_network_interface(struct parse *, struct element *);
93 static struct string *addrmask(const struct string *, const struct string *);
94 static struct element *find_match(struct parse *, struct element *,
95  isc_boolean_t *);
96 static struct element *find_location(struct element *, struct range *);
97 static int get_prefix_length(const char *, const char *);
98 static struct element *get_class(struct parse *, struct element *);
99 static void concat_classes(struct parse *, struct element *, struct element *);
100 static void generate_class(struct parse *, struct element *, struct element *,
101  struct element *);
102 
103 static struct string *CLASS_ALL;
104 static struct string *CLASS_KNOWN;
105 
106 /* Add head config file comments to the DHCP server map */
107 
108 size_t
109 conf_file_parse(struct parse *cfile)
110 {
111  struct element *top;
112  struct element *dhcp;
113  size_t issues;
114 
115  TAILQ_INIT(&known_subnets);
116  TAILQ_INIT(&known_ranges);
117  CLASS_ALL = makeString(-1, "ALL");
118  CLASS_KNOWN = makeString(-1, "KNOWN");
119 
120  top = createMap();
121  top->kind = TOPLEVEL;
122  TAILQ_CONCAT(&top->comments, &cfile->comments);
123 
124  dhcp = createMap();
125  dhcp->kind = ROOT_GROUP;
126  (void) peek_token(NULL, NULL, cfile);
127  TAILQ_CONCAT(&dhcp->comments, &cfile->comments);
128  stackPush(cfile, dhcp);
129  assert(cfile->stack_top == 1);
130  cfile->stack[0] = top;
131 
132  if (local_family == AF_INET)
133  mapSet(top, dhcp, "Dhcp4");
134  else if (local_family == AF_INET6)
135  mapSet(top, dhcp, "Dhcp6");
136  else
137  parse_error(cfile, "address family is not set");
138 
139  issues = conf_file_subparse(cfile, ROOT_GROUP);
140 
141  /* Add a warning when interfaces-config is not present */
142  if (subnet_counter > 0) {
143  struct element *ifconf;
144 
145  ifconf = mapGet(cfile->stack[1], "interfaces-config");
146  if (ifconf == NULL) {
147  struct comment *comment;
148 
149  comment = createComment("/// This configuration "
150  "declares some subnets but "
151  "has no interfaces-config");
152  TAILQ_INSERT_TAIL(&cfile->stack[1]->comments, comment);
153  comment = createComment("/// Reference Kea #245");
154  TAILQ_INSERT_TAIL(&cfile->stack[1]->comments, comment);
155  }
156  }
157 
158  post_process_lifetimes(cfile);
159  if (!global_hr)
160  issues += post_process_reservations(cfile);
161  post_process_classes(cfile);
162  post_process_generated_classes(cfile);
163  post_process_option_definitions(cfile);
164 
165  return issues;
166 }
167 
168 /* Lifetime post-processing */
169 static void
170 post_process_lifetimes(struct parse *cfile)
171 {
172  struct element *entry;
173 
174  entry = mapGet(cfile->stack[1], "valid-lifetime");
175  if ((entry == NULL) && use_isc_lifetimes) {
176  struct comment *comment;
177 
178  /* DEFAULT_DEFAULT_LEASE_TIME is 43200 */
179  entry = createInt(43200);
180  comment = createComment("/// Use ISC DHCP default lifetime");
182  mapSet(cfile->stack[1], entry, "valid-lifetime");
183  }
184 
185  entry = mapGet(cfile->stack[1], "min-valid-lifetime");
186  if ((entry == NULL) && use_isc_lifetimes) {
187  struct comment *comment;
188 
189  /* DEFAULT_MIN_LEASE_TIME is 300 */
190  entry = createInt(300);
191  comment = createComment("/// Use ISC DHCP min lifetime");
193  mapSet(cfile->stack[1], entry, "min-valid-lifetime");
194  }
195 
196  entry = mapGet(cfile->stack[1], "max-valid-lifetime");
197  if ((entry == NULL) && use_isc_lifetimes) {
198  struct comment *comment;
199 
200  /* DEFAULT_MAX_LEASE_TIME is 86400 */
201  entry = createInt(86400);
202  comment = createComment("/// Use ISC DHCP max lifetime");
204  mapSet(cfile->stack[1], entry, "max-valid-lifetime");
205  }
206 
207  /* Done for DHCPv4 */
208  if (local_family == AF_INET)
209  return;
210 
211  /* There is no builtin default for preferred-lifetime,
212  nor min/max values in ISC DHCP. */
213 }
214 
215 /* Reservation post-processing */
216 
217 static size_t
218 post_process_reservations(struct parse *cfile)
219 {
220  struct element *hosts;
221  struct element *orphans;
222  struct element *host;
223  struct element *where;
224  struct element *dest;
225  isc_boolean_t used_heuristic;
226  size_t issues;
227 
228  issues = 0;
229  hosts = mapGet(cfile->stack[1], "reservations");
230  if ((hosts == NULL) || global_hr)
231  return issues;
232  mapRemove(cfile->stack[1], "reservations");
233  orphans = createList();
234  orphans->kind = HOST_DECL;
235  while (listSize(hosts) > 0) {
236  host = listGet(hosts, 0);
237  listRemove(hosts, 0);
238  used_heuristic = ISC_FALSE;
239  where = find_match(cfile, host, &used_heuristic);
240  if (where == cfile->stack[1])
241  dest = orphans;
242  else
243  dest = mapGet(where, "reservations");
244  if (dest == NULL) {
245  dest = createList();
246  dest->kind = HOST_DECL;
247  mapSet(where, dest, "reservations");
248  }
249  listPush(dest, host);
250  }
251  if (listSize(orphans) > 0) {
252  struct comment *comment;
253 
254  comment = createComment("/// Orphan reservations");
255  TAILQ_INSERT_TAIL(&orphans->comments, comment);
256  comment = createComment("/// Kea reservations are per subnet");
257  TAILQ_INSERT_TAIL(&orphans->comments, comment);
258  comment = createComment("/// Reference Kea #231");
259  TAILQ_INSERT_TAIL(&orphans->comments, comment);
260  orphans->skip = ISC_TRUE;
261  issues++;
262  mapSet(cfile->stack[1], orphans, "reservations");
263  }
264  return issues;
265 }
266 
267 /* Cleanup classes */
268 
269 static void
270 post_process_classes(struct parse *cfile)
271 {
272  struct element *classes;
273  struct element *class;
274  struct element *name;
275  struct element *entry;
276  struct element *reduced;
277  struct string *msg;
278  struct comment *comment;
279  isc_boolean_t lose;
280  size_t i;
281 
282  classes = mapGet(cfile->stack[1], "client-classes");
283  if ((classes == NULL) || (listSize(classes) == 0))
284  return;
285  for (i = 0; i < listSize(classes); i++) {
286  class = listGet(classes, i);
287  if ((class == NULL) || (class->type != ELEMENT_MAP))
288  parse_error(cfile, "null global class at %i",
289  (unsigned)i);
290  name = mapGet(class, "name");
291  if ((name == NULL) || (name->type != ELEMENT_STRING))
292  parse_error(cfile, "global class at %u "
293  "without a name", (unsigned)i);
294  if (!mapContains(class, "super"))
295  goto cleanup_superclass;
296 
297  /* cleanup subclass */
298  mapRemove(class,"super");
299  entry = mapGet(class, "string");
300  if (entry != NULL) {
301  if (entry->type != ELEMENT_STRING)
302  parse_error(cfile, "subclass %s has "
303  "a bad string selector",
304  stringValue(name)->content);
305  msg = makeString(-1, "/// subclass selector ");
306  appendString(msg, "'");
307  concatString(msg, stringValue(entry));
308  appendString(msg, "'");
309  comment = createComment(msg->content);
310  TAILQ_INSERT_TAIL(&class->comments, comment);
311  mapRemove(class, "string");
312  continue;
313  }
314  entry = mapGet(class, "binary");
315  if (entry == NULL)
316  parse_error(cfile, "subclass %s has no selector",
317  stringValue(name)->content);
318  msg = makeString(-1, "/// subclass selector 0x");
319  concatString(msg, stringValue(entry));
320  comment = createComment(msg->content);
321  TAILQ_INSERT_TAIL(&class->comments, comment);
322  mapRemove(class, "binary");
323 
324  cleanup_superclass:
325  /* cleanup superclass */
326  entry = mapGet(class, "spawning");
327  if (entry == NULL)
328  goto cleanup_class;
329  if (entry->type != ELEMENT_BOOLEAN)
330  parse_error(cfile, "superclass %s has bad "
331  "spawning flag",
332  stringValue(name)->content);
333  if (boolValue(entry)) {
334  msg = makeString(-1, "/// Spawning classes "
335  "are not supported by Kea");
336  comment = createComment(msg->content);
337  TAILQ_INSERT_TAIL(&class->comments, comment);
338  msg = makeString(-1, "/// Reference Kea #248");
339  comment = createComment(msg->content);
340  TAILQ_INSERT_TAIL(&class->comments, comment);
341  msg = makeString(-1, "/// spawn with: ");
342  } else
343  msg = makeString(-1, "/// match: ");
344  entry = mapGet(class, "submatch");
345 
346  if (entry == NULL)
347  parse_error(cfile, "superclass %s has no submatch",
348  stringValue(name)->content);
349  lose = ISC_FALSE;
350  appendString(msg, print_data_expression(entry, &lose));
351  if (!lose) {
352  comment = createComment(msg->content);
353  TAILQ_INSERT_TAIL(&class->comments, comment);
354  mapRemove(class, "spawning");
355  mapRemove(class, "submatch");
356  }
357 
358  cleanup_class:
359  /* cleanup class */
360  entry = mapGet(class, "match-if");
361  if (entry == NULL)
362  continue;
363  reduced = mapGet(class, "test");
364  lose = ISC_FALSE;
365  if (reduced != NULL)
366  msg = makeString(-1, "/// from: match if ");
367  else
368  msg = makeString(-1, "/// match if ");
369  appendString(msg, print_boolean_expression(entry, &lose));
370  if (!lose) {
371  comment = createComment(msg->content);
372  if (reduced != NULL) {
373  TAILQ_INSERT_TAIL(&reduced->comments, comment);
374  mapRemove(class, "match-if");
375  continue;
376  }
378  }
379  }
380 }
381 
382 /* Move generated client classes to the end of client class list */
383 
384 static void
385 post_process_generated_classes(struct parse *cfile)
386 {
387  struct element *generated;
388  struct element *classes;
389  struct element *class;
390 
391  generated = mapGet(cfile->stack[1], "generated-classes");
392  if (generated == NULL)
393  return;
394  mapRemove(cfile->stack[1], "generated-classes");
395  if (listSize(generated) == 0)
396  return;
397  classes = mapGet(cfile->stack[1], "client-classes");
398  if (classes == NULL) {
399  classes = createList();
400  mapSet(cfile->stack[1], classes, "client-classes");
401  }
402 
403  while (listSize(generated) > 0) {
404  class = listGet(generated, 0);
405  listRemove(generated, 0);
406  check_depend(class, classes);
407  listPush(classes, class);
408  }
409 }
410 
411 static void
412 check_depend(struct element *class, struct element *classes)
413 {
414  struct element *list;
415 
416  if (!mapContains(class, "depend"))
417  return;
418  list = mapGet(class, "depend");
419  mapRemove(class, "depend");
420  while (listSize(list) > 0) {
421  struct element *depend;
422  struct string *dname;
423  struct string *msg;
424  struct comment *comment;
425  isc_boolean_t found;
426  size_t i;
427 
428  depend = listGet(list, 0);
429  listRemove(list, 0);
430  assert(depend != NULL);
431  assert(depend->type == ELEMENT_STRING);
432  dname = stringValue(depend);
433  if (eqString(dname, CLASS_ALL) ||
434  eqString(dname, CLASS_KNOWN))
435  continue;
436  found = ISC_FALSE;
437  for (i = 0; i < listSize(classes); i++) {
438  struct element *item;
439  struct element *name;
440 
441  item = listGet(classes, i);
442  assert(item != NULL);
443  assert(item->type == ELEMENT_MAP);
444  name = mapGet(item, "name");
445  if (name == NULL)
446  continue;
447  assert(name->type == ELEMENT_STRING);
448  if (eqString(stringValue(name), dname)) {
449  found = ISC_TRUE;
450  break;
451  }
452  }
453  if (found)
454  continue;
455  msg = makeString(-1, "/// Depend on missing '");
456  concatString(msg, dname);
457  appendString(msg, "' class");
458  comment = createComment(msg->content);
459  TAILQ_INSERT_TAIL(&class->comments, comment);
460  class->skip = ISC_TRUE;
461  }
462 }
463 
464 static void
465 post_process_option_definitions(struct parse *cfile)
466 {
467  struct element *opt_def;
468  struct element *def, *ndef;
469 
470  opt_def = mapGet(cfile->stack[1], "option-def");
471  if (opt_def == NULL)
472  return;
473  TAILQ_FOREACH_SAFE(def, &opt_def->value.list_value, ndef) {
474  if (mapContains(def, "no-export"))
475  TAILQ_REMOVE(&opt_def->value.list_value, def);
476  }
477 }
478 
479 void
480 read_conf_file(struct parse *parent, const char *filename, int group_type)
481 {
482  int file;
483  struct parse *cfile;
484  struct string *msg;
485  struct comment *comment;
486  size_t amount = parent->stack_size * sizeof(struct element *);
487 
488  if ((file = open (filename, O_RDONLY)) < 0)
489 
490  parse_error(parent, "Can't open %s: %s",
491  filename, strerror(errno));
492 
493  cfile = new_parse(file, NULL, 0, filename, 0);
494  if (cfile == NULL)
495  parse_error(parent, "Can't create new parse structure");
496 
497  cfile->stack = (struct element **)malloc(amount);
498  if (cfile->stack == NULL)
499  parse_error(parent, "Can't create new element stack");
500  memcpy(cfile->stack, parent->stack, amount);
501  cfile->stack_size = parent->stack_size;
502  cfile->stack_top = parent->stack_top;
503  cfile->issue_counter = parent->issue_counter;
504 
505  msg = makeString(-1, "/// Begin file ");
506  concatString(msg, makeString(-1, filename));
507  comment = createComment(msg->content);
509 
510  conf_file_subparse(cfile, group_type);
511 
512  amount = cfile->stack_size * sizeof(struct element *);
513  if (cfile->stack_size > parent->stack_size) {
514  parent->stack =
515  (struct element **)realloc(parent->stack, amount);
516  if (parent->stack == NULL)
517  parse_error(cfile, "can't resize element stack");
518  }
519  memcpy(parent->stack, cfile->stack, amount);
520  parent->stack_size = cfile->stack_size;
521  parent->stack_top = cfile->stack_top;
522  parent->issue_counter = cfile->issue_counter;
523  msg = makeString(-1, "/// End file ");
524  concatString(msg, makeString(-1, filename));
527  end_parse(cfile);
528 }
529 
530 /* conf-file :== parameters declarations END_OF_FILE
531  parameters :== <nil> | parameter | parameters parameter
532  declarations :== <nil> | declaration | declarations declaration */
533 
534 size_t
535 conf_file_subparse(struct parse *cfile, int type)
536 {
537  const char *val;
538  enum dhcp_token token;
539  isc_boolean_t declaration = ISC_FALSE;
540 
541  for (;;) {
542  token = peek_token(&val, NULL, cfile);
543  if (token == END_OF_FILE)
544  break;
545  declaration = parse_statement(cfile, type, declaration);
546  }
547  skip_token(&val, NULL, cfile);
548 
549  return cfile->issue_counter;
550 }
551 
552 /* statement :== parameter | declaration | PERCENT directive
553 
554  parameter :== DEFAULT_LEASE_TIME lease_time
555  | MAX_LEASE_TIME lease_time
556  | DYNAMIC_BOOTP_LEASE_CUTOFF date
557  | DYNAMIC_BOOTP_LEASE_LENGTH lease_time
558  | BOOT_UNKNOWN_CLIENTS boolean
559  | ONE_LEASE_PER_CLIENT boolean
560  | GET_LEASE_HOSTNAMES boolean
561  | USE_HOST_DECL_NAME boolean
562  | NEXT_SERVER ip-addr-or-hostname SEMI
563  | option_parameter
564  | SERVER-IDENTIFIER ip-addr-or-hostname SEMI
565  | FILENAME string-parameter
566  | SERVER_NAME string-parameter
567  | hardware-parameter
568  | fixed-address-parameter
569  | ALLOW allow-deny-keyword
570  | DENY allow-deny-keyword
571  | USE_LEASE_ADDR_FOR_DEFAULT_ROUTE boolean
572  | AUTHORITATIVE
573  | NOT AUTHORITATIVE
574 
575  declaration :== host-declaration
576  | group-declaration
577  | shared-network-declaration
578  | subnet-declaration
579  | VENDOR_CLASS class-declaration
580  | USER_CLASS class-declaration
581  | RANGE address-range-declaration */
582 
584 parse_statement(struct parse *cfile, int type, isc_boolean_t declaration)
585 {
586  enum dhcp_token token;
587  const char *val;
588  struct element *hardware;
589  struct element *cache;
590  struct element *et;
591  isc_boolean_t lose;
593  isc_boolean_t authoritative;
594  struct option *option;
595  size_t host_decl = 0;
596  size_t subnet = 0;
597  size_t i;
598 
599  token = peek_token(&val, NULL, cfile);
600 
601  switch (token) {
602  case INCLUDE:
603  skip_token(&val, NULL, cfile);
604  token = next_token(&val, NULL, cfile);
605  if (token != STRING)
606  parse_error(cfile, "filename string expected.");
607  read_conf_file(cfile, val, type);
608  parse_semi(cfile);
609  return 1;
610 
611  case HOST:
612  skip_token(&val, NULL, cfile);
613  if (type != HOST_DECL && type != CLASS_DECL)
614  parse_host_declaration(cfile);
615  else
616  parse_error(cfile,
617  "host declarations not allowed here.");
618  return 1;
619 
620  case GROUP:
621  skip_token(&val, NULL, cfile);
622  if (type != HOST_DECL && type != CLASS_DECL)
624  else
625  parse_error(cfile,
626  "group declarations not allowed here.");
627  return 1;
628 
629  case SHARED_NETWORK:
630  skip_token(&val, NULL, cfile);
631  if (type == SHARED_NET_DECL ||
632  type == HOST_DECL ||
633  type == SUBNET_DECL ||
634  type == CLASS_DECL)
635  parse_error(cfile, "shared-network parameters not %s.",
636  "allowed here");
638  return 1;
639 
640  case SUBNET:
641  case SUBNET6:
642  skip_token(&val, NULL, cfile);
643  if (type == HOST_DECL || type == SUBNET_DECL ||
644  type == CLASS_DECL)
645  parse_error(cfile,
646  "subnet declarations not allowed here.");
647 
648  if (token == SUBNET)
650  else
652  return 1;
653 
654  case VENDOR_CLASS:
655  case USER_CLASS:
656  case CLASS:
657  case SUBCLASS:
658  skip_token(&val, NULL, cfile);
659  if (token == VENDOR_CLASS)
660  parse_error(cfile, "obsolete 'vendor-class' "
661  "declaration");
662  if (token == USER_CLASS)
663  parse_error(cfile, "obsolete 'user-class' "
664  "declaration");
665  if (type == CLASS_DECL)
666  parse_error(cfile,
667  "class declarations not allowed here.");
668  parse_class_declaration(cfile, token == CLASS
671  return 1;
672 
673  case HARDWARE:
674  if (!use_hw_address) {
675  add_host_reservation_identifiers(cfile,
676  "hw-address");
678  }
679 
680  skip_token(&val, NULL, cfile);
681  if (!host_decl) {
682  for (i = cfile->stack_top; i > 0; --i) {
683  if (cfile->stack[i]->kind == HOST_DECL) {
684  host_decl = i;
685  break;
686  }
687  }
688  }
689  if (!host_decl)
690  parse_error(cfile, "hardware address parameter %s",
691  "not allowed here.");
692  if (mapContains(cfile->stack[host_decl], "hw-address"))
693  parse_error(cfile, "Host hardware address already "
694  "configured.");
696  mapSet(cfile->stack[host_decl], hardware, "hw-address");
697  if (hardware->skip)
698  cfile->stack[host_decl]->skip = ISC_TRUE;
699  break;
700 
701  case FIXED_ADDR:
702  case FIXED_ADDR6:
703  skip_token(&val, NULL, cfile);
704  if (!host_decl) {
705  for (i = cfile->stack_top; i > 0; --i) {
706  if (cfile->stack[i]->kind == HOST_DECL) {
707  host_decl = i;
708  break;
709  }
710  }
711  }
712  if (!host_decl)
713  parse_error(cfile,
714  "fixed-address parameter not "
715  "allowed here.");
716  cache = parse_fixed_addr_param(cfile, token);
717  if (token == FIXED_ADDR) {
718  struct element *addr;
719 
720  if (mapContains(cfile->stack[host_decl], "ip-address"))
721  parse_error(cfile, "Only one fixed address "
722  "declaration per host.");
723  addr = listGet(cache, 0);
724  listRemove(cache, 0);
725  mapSet(cfile->stack[host_decl], addr, "ip-address");
726  if (listSize(cache) > 0) {
727  cache->skip = ISC_TRUE;
728  cfile->issue_counter++;
729  mapSet(cfile->stack[host_decl],
730  cache, "extra-ip-addresses");
731  }
732  } else {
733  if (mapContains(cfile->stack[host_decl],
734  "ip-addresses"))
735  parse_error(cfile, "Only one fixed address "
736  "declaration per host.");
737  mapSet(cfile->stack[host_decl], cache, "ip-addresses");
738  }
739  break;
740 
741  case POOL:
742  skip_token(&val, NULL, cfile);
743  if (type == POOL_DECL)
744  parse_error(cfile, "pool declared within pool.");
745  if (type != SUBNET_DECL && type != SHARED_NET_DECL)
746  parse_error(cfile, "pool declared outside of network");
747  parse_pool_statement(cfile, type);
748 
749  return declaration;
750 
751  case RANGE:
752  skip_token(&val, NULL, cfile);
753  if (!subnet) {
754  for (i = cfile->stack_top; i > 0; --i) {
755  if (cfile->stack[i]->kind == SUBNET_DECL) {
756  subnet = i;
757  break;
758  }
759  }
760  }
761  if (type != SUBNET_DECL || !subnet)
762  parse_error(cfile,
763  "range declaration not allowed here.");
765  return declaration;
766 
767  case RANGE6:
768  if (local_family != AF_INET6)
769  goto unknown;
770  skip_token(NULL, NULL, cfile);
771  if (!subnet) {
772  for (i = cfile->stack_top; i > 0; --i) {
773  if (cfile->stack[i]->kind == SUBNET_DECL) {
774  subnet = i;
775  break;
776  }
777  }
778  }
779  if ((type != SUBNET_DECL) || !subnet)
780  parse_error(cfile,
781  "range6 declaration not allowed here.");
783  return declaration;
784 
785  case PREFIX6:
786  if (local_family != AF_INET6)
787  goto unknown;
788  skip_token(NULL, NULL, cfile);
789  if (!subnet) {
790  for (i = cfile->stack_top; i > 0; --i) {
791  if (cfile->stack[i]->kind == SUBNET_DECL) {
792  subnet = i;
793  break;
794  }
795  }
796  }
797  if ((type != SUBNET_DECL) || !subnet)
798  parse_error(cfile,
799  "prefix6 declaration not allowed here.");
800  parse_prefix6(cfile, type, subnet);
801  return declaration;
802 
803  case FIXED_PREFIX6:
804  if (local_family != AF_INET6)
805  goto unknown;
806  skip_token(&val, NULL, cfile);
807  if (!host_decl) {
808  for (i = cfile->stack_top; i > 0; --i) {
809  if (cfile->stack[i]->kind == HOST_DECL) {
810  host_decl = i;
811  break;
812  }
813  }
814  }
815  if (!host_decl)
816  parse_error(cfile,
817  "fixed-prefix6 declaration not "
818  "allowed here.");
820  break;
821 
822  case POOL6:
823  if (local_family != AF_INET6)
824  goto unknown;
825  skip_token(&val, NULL, cfile);
826  if (type == POOL_DECL)
827  parse_error(cfile, "pool6 declared within pool.");
828  if (type != SUBNET_DECL)
829  parse_error(cfile,
830  "pool6 declared outside of network");
831  parse_pool6_statement(cfile, type);
832 
833  return declaration;
834 
835  case TOKEN_NOT:
836  skip_token(&val, NULL, cfile);
837  token = next_token(&val, NULL, cfile);
838  switch (token) {
839  case AUTHORITATIVE:
840  authoritative = ISC_FALSE;
841  goto authoritative;
842  default:
843  parse_error(cfile, "expecting assertion");
844  }
845  break;
846 
847  case AUTHORITATIVE:
848  skip_token(&val, NULL, cfile);
849  authoritative = ISC_TRUE;
850  authoritative:
851  if (type == HOST_DECL)
852  parse_error(cfile, "authority makes no sense here.");
853  if (!subnet) {
854  for (i = cfile->stack_top; i > 0; --i) {
855  int kind;
856 
857  kind = cfile->stack[i]->kind;
858  if ((kind == SUBNET_DECL) ||
859  (kind == SHARED_NET_DECL) ||
860  (kind == ROOT_GROUP)) {
861  subnet = i;
862  break;
863  }
864  }
865  }
866  if (!subnet)
867  parse_error(cfile, "can't find root group");
868  if (local_family == AF_INET) {
869  cache = createBool(authoritative);
870  TAILQ_CONCAT(&cache->comments, &cfile->comments);
871  mapSet(cfile->stack[subnet], cache, "authoritative");
872  }
873  parse_semi(cfile);
874  break;
875 
876  /* "server-identifier" is a special hack, equivalent to
877  "option dhcp-server-identifier". */
878  case SERVER_IDENTIFIER:
879  option = option_lookup_code("dhcp",
881  assert(option);
882  skip_token(&val, NULL, cfile);
883  goto finish_option;
884 
885  case OPTION:
886  skip_token(&val, NULL, cfile);
887  token = peek_token(&val, NULL, cfile);
888  if (token == SPACE) {
889  if (type != ROOT_GROUP)
890  parse_error(cfile,
891  "option space definitions %s",
892  "may not be scoped.");
894  return declaration;
895  }
896 
897  known = ISC_FALSE;
899  token = peek_token(&val, NULL, cfile);
900  if (token == CODE) {
901  if (type != ROOT_GROUP)
902  parse_error(cfile,
903  "option definitions%s",
904  " may not be scoped.");
905  skip_token(&val, NULL, cfile);
906 
907  /* next function must deal with redefinitions */
909  return declaration;
910  }
911  /* If this wasn't an option code definition, don't
912  allow an unknown option. */
913  if (!known)
914  parse_error(cfile, "unknown option %s.%s",
915  option->space->old, option->old);
916  finish_option:
917  parse_option_statement(NULL, cfile, option,
919  return declaration;
920 
921  case FAILOVER:
922  if (failover_once)
923  fprintf(stderr, "ignoring failover\n");
925  skip_to_semi(cfile);
926  break;
927 
928  case SERVER_DUID:
929  if (local_family != AF_INET6)
930  goto unknown;
931  parse_server_duid_conf(cfile);
932  break;
933 
934  case LEASE_ID_FORMAT:
935  token = next_token(&val, NULL, cfile);
936  /* ignore: ISC DHCP specific */
937  break;
938 
939  case PERCENT:
940  skip_token(&val, NULL, cfile);
941  if (type != ROOT_GROUP)
942  parse_error(cfile, "directives are only supported "
943  "at toplevel");
944  parse_directive(cfile);
945  return declaration;
946 
947  unknown:
948  skip_token(&val, NULL, cfile);
949 
950  default:
951  et = createMap();
952  TAILQ_CONCAT(&et->comments, &cfile->comments);
953  lose = ISC_FALSE;
954  if (!parse_executable_statement(et, cfile, &lose,
955  context_any, ISC_TRUE)) {
956  if (!lose) {
957  if (declaration)
958  parse_error(cfile,
959  "expecting a declaration");
960  else
961  parse_error(cfile,
962  "expecting a parameter %s",
963  "or declaration");
964  }
965  return declaration;
966  }
967  if (mapSize(et) == 0)
968  return declaration;
969 
970  et->skip = ISC_TRUE;
971  cfile->issue_counter++;
972  mapSet(cfile->stack[cfile->stack_top], et, "statement");
973  }
974 
975  return 0;
976 }
977 
1003 void
1004 get_permit(struct parse *cfile, struct element *permit_head)
1005 {
1006  enum dhcp_token token;
1007  const char *val;
1008  struct string *permit;
1009  struct string *alias = NULL;
1010  struct comment *comment = NULL;
1011  struct element *member;
1012  isc_boolean_t need_clients = ISC_TRUE;
1013  isc_boolean_t negative = ISC_FALSE;
1014 
1015  token = next_token(&val, NULL, cfile);
1016  switch (token) {
1017  case UNKNOWN:
1018  permit = CLASS_KNOWN;
1019  negative = ISC_TRUE;
1020  alias = makeString(-1, "unknown clients");
1021  break;
1022 
1023  case KNOWN_CLIENTS:
1024  need_clients = ISC_FALSE;
1025  permit = CLASS_KNOWN;
1026  alias = makeString(-1, "known-clients");
1027  break;
1028 
1029  case UNKNOWN_CLIENTS:
1030  need_clients = ISC_FALSE;
1031  permit = CLASS_KNOWN;
1032  negative = ISC_TRUE;
1033  alias = makeString(-1, "unknown-clients");
1034  break;
1035 
1036  case KNOWN:
1037  permit = CLASS_KNOWN;
1038  alias = makeString(-1, "known clients");
1039  break;
1040 
1041  case AUTHENTICATED:
1042  permit = CLASS_ALL;
1043  alias = makeString(-1, "authenticated clients");
1044  negative = ISC_TRUE;
1045  authenticated_clients:
1046  comment = createComment("/// [un]authenticated-clients is "
1047  "not supported by ISC DHCP and Kea");
1048  break;
1049 
1050  case UNAUTHENTICATED:
1051  permit = CLASS_ALL;
1052  alias = makeString(-1, "unauthenticated clients");
1053  goto authenticated_clients;
1054  break;
1055 
1056  case ALL:
1057  permit = CLASS_ALL;
1058  alias = makeString(-1, "all clients");
1059  break;
1060 
1061  case DYNAMIC:
1062  /* bootp is not supported by Kea so the dynamic bootp
1063  * client set is the empty set. */
1064  if (next_token(&val, NULL, cfile) != TOKEN_BOOTP)
1065  parse_error(cfile, "expecting \"bootp\"");
1066  permit = CLASS_ALL;
1067  negative = ISC_TRUE;
1068  alias = makeString(-1, "dynamic bootp clients");
1069  cfile->issue_counter++;
1070  comment = createComment("/// dynamic-bootp-client is not "
1071  "supported by Kea");
1072  break;
1073 
1074  case MEMBERS:
1075  /* we don't check the class... */
1076  need_clients = ISC_FALSE;
1077  if (next_token(&val, NULL, cfile) != OF)
1078  parse_error(cfile, "expecting \"of\"");
1079  if (next_token(&val, NULL, cfile) != STRING)
1080  parse_error(cfile, "expecting class name.");
1081  permit = makeString(-1, val);
1082  break;
1083 
1084  case AFTER:
1085  /* don't use parse_date_code() */
1086  need_clients = ISC_FALSE;
1087  permit = makeString(-1, "AFTER_");
1088  alias = makeString(-1, "after ");
1089  while (peek_raw_token(NULL, NULL, cfile) != SEMI) {
1090  next_raw_token(&val, NULL, cfile);
1091  appendString(permit, val);
1092  appendString(alias, val);
1093  }
1094  permit_head->skip = ISC_TRUE;
1095  cfile->issue_counter++;
1096  comment = createComment("/// after <date> is not yet "
1097  "supported by Kea");
1098  break;
1099 
1100  default:
1101  parse_error(cfile, "expecting permit type.");
1102  }
1103 
1104  /*
1105  * The need_clients flag is set if we are expecting the
1106  * CLIENTS token
1107  */
1108  if (need_clients && (next_token(&val, NULL, cfile) != CLIENTS))
1109  parse_error(cfile, "expecting \"clients\"");
1110  member = createMap();
1111  mapSet(member, createString(permit), "class");
1112  mapSet(member, createBool(!negative), "way");
1113  if (alias != NULL)
1114  mapSet(member, createString(alias), "real");
1115  if (comment != NULL)
1116  TAILQ_INSERT_TAIL(&permit_head->comments, comment);
1117  listPush(permit_head, member);
1118  parse_semi(cfile);
1119 
1120  return;
1121 }
1122 
1141 void
1142 parse_pool_statement(struct parse *cfile, int type)
1143 {
1144  enum dhcp_token token;
1145  const char *val;
1146  isc_boolean_t done = ISC_FALSE;
1147  struct element *pool;
1148  struct element *pools;
1149  struct element *permit;
1150  struct element *prohibit;
1151  int declaration = 0;
1152  unsigned range_counter = 0;
1153 
1154  pool = createMap();
1155  pool->kind = POOL_DECL;
1156  TAILQ_CONCAT(&pool->comments, &cfile->comments);
1157 
1158  if (type != SUBNET_DECL && type != SHARED_NET_DECL)
1159  parse_error(cfile, "Dynamic pools are only valid inside "
1160  "subnet or shared-network statements.");
1161  parse_lbrace(cfile);
1162 
1163  stackPush(cfile, pool);
1164  type = POOL_DECL;
1165 
1166  permit = createList();
1167  prohibit = createList();
1168 
1169  do {
1170  token = peek_token(&val, NULL, cfile);
1171  switch (token) {
1172  case TOKEN_NO:
1173  case FAILOVER:
1174  if (failover_once)
1175  fprintf(stderr, "ignoring failover\n");
1177  skip_to_semi(cfile);
1178  break;
1179 
1180  case RANGE:
1181  skip_token(&val, NULL, cfile);
1182  parse_address_range(cfile, type, cfile->stack_top);
1183  range_counter++;
1184  break;
1185 
1186  case ALLOW:
1187  skip_token(&val, NULL, cfile);
1188  get_permit(cfile, permit);
1189  break;
1190 
1191  case DENY:
1192  skip_token(&val, NULL, cfile);
1193  get_permit(cfile, prohibit);
1194  break;
1195 
1196  case RBRACE:
1197  skip_token(&val, NULL, cfile);
1198  done = ISC_TRUE;
1199  break;
1200 
1201  case END_OF_FILE:
1202  /*
1203  * We can get to END_OF_FILE if, for instance,
1204  * the parse_statement() reads all available tokens
1205  * and leaves us at the end.
1206  */
1207  parse_error(cfile, "unexpected end of file");
1208 
1209  default:
1210  declaration = parse_statement(cfile, type,
1211  declaration);
1212  break;
1213  }
1214  } while (!done);
1215 
1216  cfile->stack_top--;
1217 
1218  generate_class(cfile, pool, permit, prohibit);
1219 
1220  pools = mapGet(cfile->stack[cfile->stack_top], "pools");
1221  if (pools == NULL) {
1222  pools = createList();
1223  pools->kind = POOL_DECL;
1224  mapSet(cfile->stack[cfile->stack_top], pools, "pools");
1225  }
1226  if (range_counter == 0) {
1227  struct comment *comment;
1228 
1229  /* no range */
1230  comment = createComment("empty pool");
1231  TAILQ_INSERT_TAIL(&pool->comments, comment);
1232  pool->skip = ISC_TRUE;
1233  cfile->issue_counter++;
1234  listPush(pools, pool);
1235  return;
1236  }
1237  /* spread extra ranges into pool copies */
1238  while (--range_counter != 0) {
1239  struct handle *handle;
1240  struct element *first;
1241  struct element *saved;
1242  isc_boolean_t seen = ISC_FALSE;
1243 
1244  first = createMap();
1245  saved = copy(pool);
1246  TAILQ_CONCAT(&first->comments, &pool->comments);
1247  while (mapSize(pool) > 0) {
1248  handle = mapPop(pool);
1249  if ((handle == NULL) || (handle->key == NULL) ||
1250  (handle->value == NULL))
1251  parse_error(cfile, "bad pool entry");
1252  if (strcmp(handle->key, "pool") != 0)
1253  mapSet(first, handle->value, handle->key);
1254  else if (!seen) {
1255  mapSet(first, handle->value, handle->key);
1256  mapRemove(saved, "pool");
1257  seen = ISC_TRUE;
1258  }
1259  }
1260  listPush(pools, first);
1261  pool = saved;
1262  }
1263  listPush(pools, pool);
1264 }
1265 
1266 /* Expect a left brace */
1267 
1268 void
1269 parse_lbrace(struct parse *cfile)
1270 {
1271  enum dhcp_token token;
1272  const char *val;
1273 
1274  token = next_token(&val, NULL, cfile);
1275  if (token != LBRACE)
1276  parse_error(cfile, "expecting left brace.");
1277 }
1278 
1279 /* host-declaration :== hostname RBRACE parameters declarations LBRACE */
1280 
1281 void
1283 {
1284  const char *val;
1285  enum dhcp_token token;
1286  struct element *host;
1287  struct string *name;
1288  struct element *where;
1289  struct element *hosts = NULL;
1290  int declaration = 0;
1291  isc_boolean_t used_heuristic = ISC_FALSE;
1292 
1293  host = createMap();
1294  host->kind = HOST_DECL;
1295  TAILQ_CONCAT(&host->comments, &cfile->comments);
1296 
1297  name = parse_host_name(cfile);
1298  if (!name)
1299  parse_error(cfile, "expecting a name for host declaration.");
1300 
1301  mapSet(host, createString(name), "hostname");
1302 
1303  parse_lbrace(cfile);
1304 
1305  stackPush(cfile, host);
1306 
1307  for (;;) {
1308  token = peek_token(&val, NULL, cfile);
1309  if (token == RBRACE) {
1310  skip_token(&val, NULL, cfile);
1311  break;
1312  }
1313  if (token == END_OF_FILE)
1314  parse_error(cfile, "unexpected end of file");
1315  /* If the host declaration was created by the server,
1316  remember to save it. */
1317  if (token == DYNAMIC) {
1318  skip_token(&val, NULL, cfile);
1319  parse_error(cfile, "dynamic hosts don't exist "
1320  "in the config file");
1321  }
1322  /* If the host declaration was created by the server,
1323  remember to save it. */
1324  if (token == TOKEN_DELETED) {
1325  skip_token(&val, NULL, cfile);
1326  parse_error(cfile, "deleted hosts don't exist "
1327  "in the config file");
1328  }
1329 
1330  if (token == GROUP) {
1331  struct element *group;
1332  struct comment *comment;
1333 
1334  skip_token(&val, NULL, cfile);
1335  token = next_token(&val, NULL, cfile);
1336  if (token != STRING && !is_identifier(token))
1337  parse_error(cfile,
1338  "expecting string or identifier.");
1339  group = createString(makeString(-1, val));
1340  group->skip = ISC_TRUE;
1341  cfile->issue_counter++;
1342  comment = createComment("/// Unsupported group in "
1343  "host reservations");
1344  TAILQ_INSERT_TAIL(&group->comments, comment);
1345  comment = createComment("/// Reference Kea #233");
1346  TAILQ_INSERT_TAIL(&group->comments, comment);
1347  mapSet(host, group, "group");
1348  parse_semi(cfile);
1349  continue;
1350  }
1351 
1352  if (token == UID) {
1353  struct string *client_id;
1354 
1355  if (!use_client_id) {
1356  add_host_reservation_identifiers(cfile,
1357  "client-id");
1359  }
1360 
1361  skip_token(&val, NULL, cfile);
1362 
1363  if (mapContains(host, "client-id"))
1364  parse_error(cfile, "Host %s already has a "
1365  "client identifier.",
1366  name->content);
1367 
1368  /* See if it's a string or a cshl. */
1369  token = peek_token(&val, NULL, cfile);
1370  if (token == STRING) {
1371  skip_token(&val, NULL, cfile);
1372  client_id = makeString(-1, val);
1373  } else {
1374  struct string *bin;
1375  unsigned len = 0;
1376 
1378  (cfile, NULL, &len, ':', 16, 8);
1379  if (!bin)
1380  parse_error(cfile,
1381  "expecting hex list.");
1382  client_id = makeStringExt(bin->length,
1383  bin->content, 'H');
1384  }
1385  mapSet(host, createString(client_id), "client-id");
1386 
1387  parse_semi(cfile);
1388  continue;
1389  }
1390 
1391  if (token == HOST_IDENTIFIER) {
1392  struct string *host_id;
1394  struct option *option;
1395  struct element *expr;
1396  struct string *data;
1397  int relays = 0;
1398 
1399  if (!use_flex_id) {
1400  add_host_reservation_identifiers(cfile,
1401  "flex-id");
1403  }
1404 
1405  if (mapContains(host, "host-identifier") ||
1406  mapContains(host, "flex-id"))
1407  parse_error(cfile,
1408  "only one host-identifier allowed "
1409  "per host");
1410  skip_token(&val, NULL, cfile);
1411  token = next_token(&val, NULL, cfile);
1412  host_id = makeString(-1, val);
1413  appendString(host_id, " ");
1414  if (token == V6RELOPT) {
1415  token = next_token(&val, NULL, cfile);
1416 
1417  if (token != NUMBER)
1418  parse_error(cfile,
1419  "host-identifier v6relopt "
1420  "must have a number");
1421  appendString(host_id, val);
1422  appendString(host_id, " ");
1423  relays = atoi(val);
1424  if (relays < 0)
1425  parse_error(cfile,
1426  "host-identifier v6relopt "
1427  "must have a number >= 0");
1428  if (relays > MAX_V6RELAY_HOPS)
1429  relays = MAX_V6RELAY_HOPS + 1;
1430  } else if (token != OPTION)
1431  parse_error(cfile,
1432  "host-identifier must be an option"
1433  " or v6relopt");
1434  known = ISC_FALSE;
1435  option = parse_option_name(cfile, ISC_TRUE, &known);
1436  if (!known)
1437  parse_error(cfile, "unknown option %s.%s",
1438  option->space->old, option->old);
1439  appendString(host_id, option->space->name);
1440  appendString(host_id, ".");
1441  appendString(host_id, option->name);
1442  appendString(host_id, " ");
1443 
1444  data = parse_option_textbin(cfile, option);
1445  parse_semi(cfile);
1446 
1447  if (data == NULL)
1448  parse_error(cfile, "can't get option data");
1449  concatString(host_id, data);
1450  expr = createString(host_id);
1451  expr->skip = ISC_TRUE;
1452  cfile->issue_counter++;
1453  mapSet(host, expr, "host-identifier");
1454 
1455  if (host_id_option == NULL)
1456  add_host_id_option(cfile, option, relays);
1457  else if ((host_id_option != option) ||
1458  (host_id_relays != relays)) {
1459  struct string *msg;
1460  struct comment *comment;
1461 
1462  msg = allocString();
1463  appendString(msg, "/// Another option (");
1465  appendString(msg, ") is already used as ");
1466  appendString(msg, "host-identifier");
1467  comment = createComment(msg->content);
1469  continue;
1470  }
1471 
1472  /*
1473  * Everything good: set a flex-id and remove
1474  * the host-identifier entry.
1475  */
1476  mapSet(host, createString(data), "flex-id");
1477  mapRemove(host, "host-identifier");
1478  continue;
1479  }
1480 
1481  declaration = parse_statement(cfile, HOST_DECL, declaration);
1482  }
1483 
1484  cfile->stack_top--;
1485 
1486  where = find_match(cfile, host, &used_heuristic);
1487  hosts = mapGet(where, "reservations");
1488  if (hosts == NULL) {
1489  hosts = createList();
1490  hosts->kind = HOST_DECL;
1491  mapSet(where, hosts, "reservations");
1492  if (used_heuristic) {
1493  struct comment *comment;
1494 
1495  comment = createComment("/// Host reservations "
1496  "without fixed addresses "
1497  "were put in the last "
1498  "declared subnet");
1500  comment = createComment("/// Reference Kea #231");
1502  }
1503  }
1504  listPush(hosts, host);
1505 }
1506 
1507 /* Simple tool to declare used (and only used) reservation identifiers */
1508 static void
1509 add_host_reservation_identifiers(struct parse *cfile, const char *id)
1510 {
1511  struct element *ids;
1512 
1513  ids = mapGet(cfile->stack[1], "host-reservation-identifiers");
1514  if (ids == NULL) {
1515  ids = createList();
1516  mapSet(cfile->stack[1], ids, "host-reservation-identifiers");
1517  }
1518  listPush(ids, createString(makeString(-1, id)));
1519 }
1520 
1521 /* Add the flexible host identifier glue */
1522 static void
1523 add_host_id_option(struct parse *cfile,
1524  const struct option *option, int relays)
1525 {
1526  struct string *path;
1527  struct string *expr;
1528  struct element *params;
1529  struct element *entry;
1530  struct element *hooks;
1531  struct comment *comment;
1532  char buf[40];
1533 
1535  host_id_relays = relays;
1536 
1537  /*
1538  * Using the example from the Kea Administrator Reference Manual
1539  * as recommended by Tomek
1540  */
1541  hooks = createList();
1542  mapSet(cfile->stack[1], hooks, "hooks-libraries");
1543  comment = createComment("/// The flexible host identifier "
1544  "is a premium feature");
1546  entry = createMap();
1547  listPush(hooks, entry);
1548  if (hook_library_path != NULL)
1549  path = makeString(-1, hook_library_path);
1550  else
1551  path = makeString(-1, "/path/");
1552  appendString(path, "libdhcp_flex_id.so");
1553  params = createString(path);
1554  if (hook_library_path == NULL) {
1555  comment = createComment("/// Please update the path here");
1556  TAILQ_INSERT_TAIL(&params->comments, comment);
1557  }
1558  mapSet(entry, params, "library");
1559  params = createMap();
1560  mapSet(entry, params, "parameters");
1561 
1562  snprintf(buf, sizeof(buf), "%soption[%u].hex",
1563  relays > 0 ? "relay[0]." : "", option->code);
1564  expr = makeString(-1, buf);
1565  mapSet(params, createString(expr), "identifier-expression");
1566 }
1567 
1568 static void add_host_reservation_identifiers(struct parse *, const char *);
1569 /* class-declaration :== STRING LBRACE parameters declarations RBRACE
1570  *
1571  * in fact:
1572  * (CLASS) NAME(STRING) LBRACE ... RBRACE
1573  * (SUBCLASS) SUPER(STRING) DATA/HASH(STRING | <hexa>) [BRACE ... RBRACE]
1574  *
1575  * class "name" { MATCH IF <boolean-expr> }: direct: belong when true
1576  * class "name" { MATCH <data-expr> }: indirect: use subclasses
1577  * class "name" { MATCH <data-expr> SPAWN WITH <data-expr> }: indirect:
1578  * create dynamically a subclass
1579  * subclass "super" <data-expr = string or binary aka hash>: belongs when
1580  * super <data-expr> == <hash>
1581  */
1582 
1583 void
1584 parse_class_declaration(struct parse *cfile, int type)
1585 {
1586  const char *val = NULL;
1587  enum dhcp_token token;
1588  size_t group = 0;
1589  size_t i = 0;
1590  struct element *group_classes = NULL;
1591  struct element *classes = NULL;
1592  struct element *class = NULL;
1593  struct element *pc = NULL; /* p(arent)c(lass) */
1594  struct element *tmp = NULL;
1595  struct element *expr = NULL;
1596  struct element *data = NULL;
1597  isc_boolean_t binary = ISC_FALSE;
1598  int declaration = 0;
1599  struct string *name = NULL;
1600  isc_boolean_t lose = ISC_FALSE;
1601  isc_boolean_t matchedonce = ISC_FALSE;
1602  isc_boolean_t submatchedonce = ISC_FALSE;
1603 
1604  token = next_token(&val, NULL, cfile);
1605  if (token != STRING)
1606  parse_error(cfile, "Expecting class name");
1607 
1608  /* Find group and root classes */
1609  classes = mapGet(cfile->stack[1], "client-classes");
1610  if (classes == NULL) {
1611  classes = createList();
1612  classes->kind = CLASS_DECL;
1613  mapSet(cfile->stack[1], classes, "client-classes");
1614  }
1615  for (group = cfile->stack_top; group > 0; --group) {
1616  int kind;
1617 
1618  kind = cfile->stack[group]->kind;
1619  if (kind == CLASS_DECL)
1620  parse_error(cfile, "class in class");
1621  if ((kind == GROUP_DECL) || (kind == ROOT_GROUP))
1622  break;
1623  }
1624  if (!group)
1625  parse_error(cfile, "can't find root group");
1626  if (cfile->stack[group]->kind == GROUP_DECL) {
1627  group_classes = mapGet(cfile->stack[group], "client-classes");
1628  if (group_classes == NULL) {
1629  group_classes = createList();
1630  group_classes->kind = CLASS_DECL;
1631  mapSet(cfile->stack[group], group_classes,
1632  "client-classes");
1633  }
1634  } else
1635  group_classes = classes;
1636 
1637  /* See if there's already a class with the specified name. */
1638  for (i = 0; i < listSize(classes); i++) {
1639  struct element *name;
1640 
1641  tmp = listGet(classes, i);
1642  name = mapGet(tmp, "name");
1643  if (name == NULL)
1644  continue;
1645  if (strcmp(stringValue(name)->content, val) == 0) {
1646  pc = tmp;
1647  break;
1648  }
1649  }
1650 
1651  /* If it is a class, we're updating it. If it's any of the other
1652  * types (subclass, vendor or user class), the named class is a
1653  * reference to the parent class so its mandatory.
1654  */
1655  if ((pc != NULL) && (type == CLASS_TYPE_CLASS)) {
1656  class = pc;
1657  pc = NULL;
1658  } else if (type != CLASS_TYPE_CLASS) {
1659  if (pc == NULL)
1660  parse_error(cfile, "no class named %s", val);
1661  if (!mapContains(pc, "spawning") ||
1662  !mapContains(pc, "submatch"))
1663  parse_error(cfile, "found class name %s but it is "
1664  "not a suitable superclass", val);
1665  }
1666 
1667  name = makeString(-1, val);
1668  /* If this is a straight subclass, parse the hash string. */
1669  if (type == CLASS_TYPE_SUBCLASS) {
1670  token = peek_token(&val, NULL, cfile);
1671  if (token == STRING) {
1672  unsigned len;
1673 
1674  skip_token(&val, &len, cfile);
1675  data = createString(makeString(len, val));
1676  } else if (token == NUMBER_OR_NAME || token == NUMBER) {
1677  data = createHexa(parse_hexa(cfile));
1678  binary = ISC_TRUE;
1679  } else {
1680  skip_token(&val, NULL, cfile);
1681  parse_error(cfile, "Expecting string or hex list.");
1682  }
1683  }
1684 
1685  /* See if there's already a class in the hash table matching the
1686  hash data. */
1687  if (type != CLASS_TYPE_CLASS) {
1688  for (i = 0; i < listSize(classes); i++) {
1689  struct element *super;
1690  struct element *selector;
1691 
1692  tmp = listGet(classes, i);
1693  super = mapGet(tmp, "super");
1694  if (super == NULL)
1695  continue;
1696  if (!eqString(stringValue(super), name))
1697  continue;
1698  if (binary)
1699  selector = mapGet(tmp, "binary");
1700  else
1701  selector = mapGet(tmp, "string");
1702  if (selector == NULL)
1703  continue;
1704  if (eqString(stringValue(selector),
1705  stringValue(data))) {
1706  class = tmp;
1707  break;
1708  }
1709  }
1710  }
1711 
1712  /* Note the class declaration in the enclosing group */
1713  if (group_classes != classes) {
1714  struct element *gc;
1715 
1716  gc = createMap();
1717  gc->kind = CLASS_DECL;
1718  tmp = createString(name);
1719  if (type == CLASS_TYPE_CLASS)
1720  mapSet(gc, tmp, "name");
1721  else {
1722  tmp->skip = ISC_TRUE;
1723  mapSet(gc, tmp, "super");
1724  data->skip = ISC_TRUE;
1725  if (binary)
1726  mapSet(gc, data, "binary");
1727  else
1728  mapSet(gc, data, "string");
1729  }
1730  listPush(group_classes, gc);
1731  }
1732 
1733  /* If we didn't find an existing class, allocate a new one. */
1734  if (!class) {
1735  /* Allocate the class structure... */
1736  class = createMap();
1737  class->kind = CLASS_DECL;
1738  TAILQ_CONCAT(&class->comments, &cfile->comments);
1739  if (type == CLASS_TYPE_SUBCLASS) {
1740  struct string *subname;
1741  char buf[40];
1742 
1743  cfile->issue_counter++;
1744  tmp = createString(name);
1745  tmp->skip = ISC_TRUE;
1746  mapSet(class, tmp, "super");
1747  data->skip = ISC_TRUE;
1748  if (binary)
1749  mapSet(class, data, "binary");
1750  else
1751  mapSet(class, data, "string");
1752  subname = makeString(-1, "sub#");
1753  concatString(subname, name);
1754  snprintf(buf, sizeof(buf),
1755  "#%u", subclass_counter++);
1756  appendString(subname, buf);
1757  mapSet(class, createString(subname), "name");
1758  } else
1759  /* Save the name, if there is one. */
1760  mapSet(class, createString(name), "name");
1761  listPush(classes, class);
1762  }
1763 
1764  /* Spawned classes don't have to have their own settings. */
1765  if (type == CLASS_TYPE_SUBCLASS) {
1766  token = peek_token(&val, NULL, cfile);
1767  if (token == SEMI) {
1768  skip_token(&val, NULL, cfile);
1769  subclass_inherit(cfile, class, copy(pc));
1770  return;
1771  }
1772  }
1773 
1774  parse_lbrace(cfile);
1775 
1776  stackPush(cfile, class);
1777 
1778  for (;;) {
1779  token = peek_token(&val, NULL, cfile);
1780  if (token == RBRACE) {
1781  skip_token(&val, NULL, cfile);
1782  break;
1783  } else if (token == END_OF_FILE) {
1784  skip_token(&val, NULL, cfile);
1785  parse_error(cfile, "unexpected end of file");
1786  } else if (token == DYNAMIC) {
1787  skip_token(&val, NULL, cfile);
1788  parse_error(cfile, "dynamic classes don't exist "
1789  "in the config file");
1790  } else if (token == TOKEN_DELETED) {
1791  skip_token(&val, NULL, cfile);
1792  parse_error(cfile, "deleted hosts don't exist "
1793  "in the config file");
1794  } else if (token == MATCH) {
1795  skip_token(&val, NULL, cfile);
1796  if (pc)
1797  parse_error(cfile,
1798  "invalid match in subclass.");
1799  token = peek_token(&val, NULL, cfile);
1800  if (token != IF) {
1801  expr = createBool(ISC_FALSE);
1802  expr->skip = 1;
1803  mapSet(class, expr, "spawning");
1804  goto submatch;
1805  }
1806 
1807  skip_token(&val, NULL, cfile);
1808  if (matchedonce)
1809  parse_error(cfile,
1810  "A class may only have "
1811  "one 'match if' clause.");
1812  matchedonce = ISC_TRUE;
1813  expr = createMap();
1814  if (!parse_boolean_expression(expr, cfile, &lose)) {
1815  if (!lose)
1816  parse_error(cfile,
1817  "expecting boolean expr.");
1818  } else {
1819  expr->skip = ISC_TRUE;
1820  mapSet(class, expr, "match-if");
1821  add_match_class(cfile, class, copy(expr));
1822  parse_semi(cfile);
1823  }
1824  } else if (token == SPAWN) {
1825  skip_token(&val, NULL, cfile);
1826  if (pc)
1827  parse_error(cfile,
1828  "invalid spawn in subclass.");
1829  expr = createBool(ISC_TRUE);
1830  expr->skip = ISC_TRUE;
1831  cfile->issue_counter++;
1832  mapSet(class, expr, "spawning");
1833  token = next_token(&val, NULL, cfile);
1834  if (token != WITH)
1835  parse_error(cfile,
1836  "expecting with after spawn");
1837  submatch:
1838  if (submatchedonce)
1839  parse_error(cfile,
1840  "can't override existing "
1841  "submatch/spawn");
1842  submatchedonce = ISC_TRUE;
1843  expr = createMap();
1844  if (!parse_data_expression(expr, cfile, &lose)) {
1845  if (!lose)
1846  parse_error(cfile,
1847  "expecting data expr.");
1848  } else {
1849  expr->skip = ISC_TRUE;
1850  cfile->issue_counter++;
1851  mapSet(class, expr, "submatch");
1852  parse_semi(cfile);
1853  }
1854  } else if (token == LEASE) {
1855  struct comment *comment;
1856 
1857  skip_token(&val, NULL, cfile);
1858  token = next_token(&val, NULL, cfile);
1859  if (token != LIMIT)
1860  parse_error(cfile, "expecting \"limit\"");
1861  token = next_token(&val, NULL, cfile);
1862  if (token != NUMBER)
1863  parse_error(cfile, "expecting a number");
1864  tmp = createInt(atoll(val));
1865  tmp->skip = ISC_TRUE;
1866  cfile->issue_counter++;
1867  comment = createComment("/// Per-class limit is not "
1868  "supported by Kea");
1870  comment = createComment("/// Reference Kea #237");
1872  mapSet(class, tmp, "lease-limit");
1873  parse_semi(cfile);
1874  } else
1875  declaration = parse_statement(cfile, CLASS_DECL,
1876  declaration);
1877  }
1878 
1879  cfile->stack_top--;
1880 
1881  if (type == CLASS_TYPE_SUBCLASS)
1882  subclass_inherit(cfile, class, copy(pc));
1883 }
1884 
1885 /*
1886  * Inherit entries:
1887  * - first copy entries from the current superclass to the subclass
1888  * - second try to reduce the subclass matching condition
1889  */
1890 
1891 static void
1892 subclass_inherit(struct parse *cfile,
1893  struct element *class,
1894  struct element *superclass)
1895 {
1896  struct string *name;
1897  struct element *guard;
1898  struct element *submatch;
1899  struct handle *handle;
1900  struct string *gmsg;
1901  struct string *mmsg;
1902  struct string *dmsg;
1903  struct element *expr;
1904  struct element *data;
1905  struct element *match;
1906  struct element *reduced;
1907  unsigned order = 0;
1908  struct comment *comment;
1909  isc_boolean_t marked = ISC_FALSE;
1910  isc_boolean_t lose = ISC_FALSE;
1911  isc_boolean_t modified = ISC_FALSE;
1912 
1913  expr = mapGet(superclass, "name");
1914  if (expr == NULL)
1915  parse_error(cfile, "can't get superclass name");
1916  name = stringValue(expr);
1917  guard = mapGet(superclass, "match-if");
1918  submatch = mapGet(superclass, "submatch");
1919  if (submatch == NULL)
1920  parse_error(cfile, "can't get superclass submatch");
1921 
1922  /* Iterates on (copy of) superclass entries */
1923  while (mapSize(superclass) > 0) {
1924  handle = mapPop(superclass);
1925  if ((handle == NULL) || (handle->key == NULL) ||
1926  (handle->value == NULL))
1927  parse_error(cfile, "can't get superclass %s item at "
1928  "%u", name->content, order);
1929  handle->order = order++;
1930  /* Superclass specific entries */
1931  if ((strcmp(handle->key, "name") == 0) ||
1932  (strcmp(handle->key, "spawning") == 0) ||
1933  (strcmp(handle->key, "match-if") == 0) ||
1934  (strcmp(handle->key, "test") == 0) ||
1935  (strcmp(handle->key, "submatch") == 0))
1936  continue;
1937  /* Subclass specific so impossible entries */
1938  if ((strcmp(handle->key, "super") == 0) ||
1939  (strcmp(handle->key, "binary") == 0) ||
1940  (strcmp(handle->key, "string") == 0))
1941  parse_error(cfile, "superclass %s has unexpected %s "
1942  "at %u",
1943  name->content, handle->key, order);
1944  /* Special entries */
1945  if (strcmp(handle->key, "option-data") == 0) {
1946  struct element *opt_list;
1947 
1948  opt_list = mapGet(class, handle->key);
1949  if (opt_list != NULL)
1950  merge_option_data(handle->value, opt_list);
1951  else
1952  mapSet(class, handle->value, handle->key);
1953  continue;
1954  }
1955  /* Just copy */
1956  if ((strcmp(handle->key, "lease-limit") == 0) ||
1957  (strcmp(handle->key, "boot-file-name") == 0) ||
1958  (strcmp(handle->key, "serverhostname") == 0) ||
1959  (strcmp(handle->key, "next-server") == 0)) {
1960  mapSet(class, handle->value, handle->key);
1961  continue;
1962  }
1963  /* Unknown */
1964  if (!marked) {
1965  marked = ISC_TRUE;
1966  comment = createComment("/// copied from superclass");
1968  }
1969  comment = createComment("/// unhandled entry");
1971  if (!handle->value->skip) {
1972  handle->value->skip = ISC_TRUE;
1973  cfile->issue_counter++;
1974  }
1975  mapSet(class, handle->value, handle->key);
1976  }
1977 
1978  /* build [guard and] submatch = data */
1979  expr = mapGet(class, "binary");
1980  if (expr != NULL) {
1981  data = createMap();
1982  mapSet(data, copy(expr), "const-data");
1983  } else
1984  data = mapGet(class, "string");
1985  if (data == NULL)
1986  parse_error(cfile, "can't get subclass %s data",
1987  name->content);
1988  match = createMap();
1989  mapSet(match, copy(submatch), "left");
1990  mapSet(match, copy(data), "right");
1991  expr = createMap();
1992  mapSet(expr, match, "equal");
1993 
1994  if (guard != NULL) {
1995  match = createMap();
1996  mapSet(match, copy(guard), "left");
1997  mapSet(match, expr, "right");
1998  expr = createMap();
1999  mapSet(expr, match, "and");
2000 
2001  gmsg = makeString(-1, "/// from: match-if ");
2002  appendString(gmsg, print_boolean_expression(guard, &lose));
2003  mmsg = makeString(-1, "/// match: ");
2004  } else {
2005  gmsg = NULL;
2006  mmsg = makeString(-1, "/// from: match ");
2007  }
2008 
2009  appendString(mmsg, print_data_expression(submatch, &lose));
2010  dmsg = makeString(-1, "/// data: ");
2011  appendString(dmsg, print_data_expression(data, &lose));
2012 
2013  /* evaluate the expression and try to reduce it */
2014  reduced = eval_boolean_expression(expr, &modified);
2015  reduced = reduce_boolean_expression(reduced);
2016  if ((reduced != NULL) && (reduced->type == ELEMENT_BOOLEAN))
2017  parse_error(cfile, "class matching rule evaluated to a "
2018  "constant boolean expression: %s = %s",
2019  print_data_expression(submatch, &lose),
2020  print_data_expression(data, &lose));
2021  if ((reduced == NULL) || (reduced->type != ELEMENT_STRING))
2022  return;
2023  if (!lose) {
2024  if (gmsg != NULL) {
2025  comment = createComment(gmsg->content);
2026  TAILQ_INSERT_TAIL(&reduced->comments, comment);
2027  }
2028  comment = createComment(mmsg->content);
2029  TAILQ_INSERT_TAIL(&reduced->comments, comment);
2030  comment = createComment(dmsg->content);
2031  TAILQ_INSERT_TAIL(&reduced->comments, comment);
2032  }
2033  mapSet(class, reduced, "test");
2034 }
2035 
2036 /*
2037  * Try to reduce a match-if condition into a Kea evaluate bool "test"
2038  */
2039 
2040 static void
2041 add_match_class(struct parse *cfile,
2042  struct element *class,
2043  struct element *expr)
2044 {
2045  struct element *reduced;
2046  isc_boolean_t modified = ISC_FALSE;
2047  isc_boolean_t lose = ISC_FALSE;
2048 
2049  /* evaluate the expression and try to reduce it */
2050  reduced = eval_boolean_expression(expr, &modified);
2051  reduced = reduce_boolean_expression(reduced);
2052  if ((reduced != NULL) && (reduced->type == ELEMENT_BOOLEAN))
2053  parse_error(cfile, "'match if' with a constant boolean "
2054  "expression %s",
2055  print_boolean_expression(expr, &lose));
2056  if ((reduced != NULL) && (reduced->type == ELEMENT_STRING))
2057  mapSet(class, reduced, "test");
2058  else
2059  cfile->issue_counter++;
2060 }
2061 
2062 /* Move pools to subnets */
2063 
2064 static void
2065 relocate_pools(struct element *share)
2066 {
2067  struct element *srcs;
2068  struct element *dsts;
2069  struct element *subnet;
2070  struct range *range;
2071  size_t i;
2072 
2073  srcs = mapGet(share, "pools");
2074  if (srcs == NULL)
2075  return;
2076  if (listSize(srcs) == 0)
2077  return;
2078  TAILQ_FOREACH(range, &known_ranges) {
2079  if (range->share != share)
2080  continue;
2081  subnet = find_location(share, range);
2082  if (subnet == NULL)
2083  continue;
2084  for (i = 0; i < listSize(srcs); i++) {
2085  struct element *pool;
2086 
2087  pool = listGet(srcs, i);
2088  if (range->pool != pool)
2089  continue;
2090  listRemove(srcs, i);
2091  dsts = mapGet(subnet, "pools");
2092  if (dsts == NULL) {
2093  dsts = createList();
2094  mapSet(subnet, dsts, "pools");
2095  }
2096  listPush(dsts, pool);
2097  }
2098  }
2099 }
2100 
2101 /* shared-network-declaration :==
2102  hostname LBRACE declarations parameters RBRACE */
2103 
2104 void
2106 {
2107  const char *val;
2108  enum dhcp_token token;
2109  struct element *share;
2110  struct element *subnets;
2111  struct element *interface;
2112  struct element *subnet;
2113  struct string *name;
2114  int declaration = 0;
2115 
2116  share = createMap();
2117  share->kind = SHARED_NET_DECL;
2118  TAILQ_CONCAT(&share->comments, &cfile->comments);
2119 
2120  /* Get the name of the shared network... */
2121  token = peek_token(&val, NULL, cfile);
2122  if (token == STRING) {
2123  skip_token(&val, NULL, cfile);
2124 
2125  if (val[0] == 0)
2126  parse_error(cfile, "zero-length shared network name");
2127  name = makeString(-1, val);
2128  } else {
2129  name = parse_host_name(cfile);
2130  if (!name)
2131  parse_error(cfile,
2132  "expecting a name for shared-network");
2133  }
2134  mapSet(share, createString(name), "name");
2135 
2136  subnets = createList();
2137  mapSet(share, subnets,
2138  local_family == AF_INET ? "subnet4" : "subnet6");
2139 
2140  parse_lbrace(cfile);
2141 
2142  stackPush(cfile, share);
2143 
2144  for (;;) {
2145  token = peek_token(&val, NULL, cfile);
2146  if (token == RBRACE) {
2147  skip_token(&val, NULL, cfile);
2148  break;
2149  } else if (token == END_OF_FILE) {
2150  skip_token(&val, NULL, cfile);
2151  parse_error(cfile, "unexpected end of file");
2152  } else if (token == INTERFACE) {
2153  skip_token(&val, NULL, cfile);
2154  token = next_token(&val, NULL, cfile);
2155  if (mapContains(share, "interface"))
2156  parse_error(cfile,
2157  "A shared network can't be "
2158  "connected to two interfaces.");
2159  interface = createString(makeString(-1, val));
2160  mapSet(share, interface, "interface");
2161  new_network_interface(cfile, interface);
2162  parse_semi(cfile);
2163  continue;
2164  }
2165 
2166  declaration = parse_statement(cfile, SHARED_NET_DECL,
2167  declaration);
2168  }
2169 
2170  cfile->stack_top--;
2171 
2172  if (listSize(subnets) == 0)
2173  parse_error(cfile, "empty shared-network decl");
2174  if (listSize(subnets) > 1) {
2175  struct element *shares;
2176  struct element *pools;
2177 
2178  shares = mapGet(cfile->stack[cfile->stack_top],
2179  "shared-networks");
2180  if (shares == NULL) {
2181  struct comment *comment;
2182 
2183  shares = createList();
2184  shares->kind = SHARED_NET_DECL;
2185  mapSet(cfile->stack[cfile->stack_top],
2186  shares, "shared-networks");
2187  comment = createComment("/// Kea shared-networks "
2188  "are different, cf Kea #236");
2189  TAILQ_INSERT_TAIL(&shares->comments, comment);
2190  }
2191  listPush(shares, share);
2192 
2193  /* Pools are forbidden at shared-network level in Kea */
2194  relocate_pools(share);
2195  pools = mapGet(share, "pools");
2196  if ((pools != NULL) && (listSize(pools) == 0)) {
2197  mapRemove(share, "pools");
2198  pools = NULL;
2199  }
2200  if (pools != NULL) {
2201  struct comment *comment;
2202 
2203  pools->skip = ISC_TRUE;
2204  cfile->issue_counter++;
2205  comment = createComment("/// Kea pools must be "
2206  "in a subnet");
2207  TAILQ_INSERT_TAIL(&pools->comments, comment);
2208  comment = createComment("/// Reference Kea #249");
2209  TAILQ_INSERT_TAIL(&pools->comments, comment);
2210  }
2211  pools = mapGet(share, "pd-pools");
2212  if ((pools != NULL) && (listSize(pools) == 0)) {
2213  mapRemove(share, "pd-pools");
2214  pools = NULL;
2215  }
2216  if (pools != NULL) {
2217  struct comment *comment;
2218 
2219  pools->skip = ISC_TRUE;
2220  cfile->issue_counter++;
2221  comment = createComment("/// Kea pools must be "
2222  "in a subnet");
2223  TAILQ_INSERT_TAIL(&pools->comments, comment);
2224  comment = createComment("/// Reference Kea #249");
2225  TAILQ_INSERT_TAIL(&pools->comments, comment);
2226  }
2227  return;
2228  }
2229 
2230  /* There is one subnet so the shared network is useless */
2231  subnet = listGet(subnets, 0);
2232  listRemove(subnets, 0);
2233  mapRemove(share, "name");
2234  mapRemove(share, local_family == AF_INET ? "subnet4" : "subnet6");
2235  /* specific case before calling generic merge */
2236  if (mapContains(share, "pools") &&
2237  mapContains(subnet, "pools")) {
2238  struct element *pools;
2239  struct element *sub;
2240 
2241  pools = mapGet(share, "pools");
2242  mapRemove(share, "pools");
2243  sub = mapGet(subnet, "pools");
2244  concat(sub, pools);
2245  }
2246  if (mapContains(share, "pd-pools") &&
2247  mapContains(subnet, "pd-pools")) {
2248  struct element *pools;
2249  struct element *sub;
2250 
2251  pools = mapGet(share, "pd-pools");
2252  mapRemove(share, "pd-pools");
2253  sub = mapGet(subnet, "pd-pools");
2254  concat(sub, pools);
2255  }
2256  if (mapContains(share, "option-data") &&
2257  mapContains(subnet, "option-data")) {
2258  struct element *opt_list;
2259  struct element *sub;
2260 
2261  opt_list = mapGet(share, "option-data");
2262  mapRemove(share, "option-data");
2263  sub = mapGet(subnet, "option-data");
2264  merge_option_data(opt_list, sub);
2265  }
2266  merge(subnet, share);
2267 
2268  if (local_family == AF_INET) {
2269  subnets = mapGet(cfile->stack[1], "subnet4");
2270  if (subnets == NULL) {
2271  subnets = createList();
2272  subnets->kind = SUBNET_DECL;
2273  mapSet(cfile->stack[1], subnets, "subnet4");
2274  }
2275  } else {
2276  subnets = mapGet(cfile->stack[1], "subnet6");
2277  if (subnets == NULL) {
2278  subnets = createList();
2279  subnets->kind = SUBNET_DECL;
2280  mapSet(cfile->stack[1], subnets, "subnet6");
2281  }
2282  }
2284 }
2285 
2286 static void
2287 common_subnet_parsing(struct parse *cfile,
2288  struct element *subnets,
2289  struct element *subnet)
2290 {
2291  enum dhcp_token token;
2292  const char *val;
2293  struct element *interface;
2294  int declaration = 0;
2295 
2296  parse_lbrace(cfile);
2297 
2298  stackPush(cfile, subnet);
2299 
2300  for (;;) {
2301  token = peek_token(&val, NULL, cfile);
2302  if (token == RBRACE) {
2303  skip_token(&val, NULL, cfile);
2304  break;
2305  } else if (token == END_OF_FILE) {
2306  skip_token(&val, NULL, cfile);
2307  parse_error(cfile, "unexpected end of file");
2308  break;
2309  } else if (token == INTERFACE) {
2310  skip_token(&val, NULL, cfile);
2311  token = next_token(&val, NULL, cfile);
2312  if (mapContains(subnet, "interface"))
2313  parse_error(cfile,
2314  "A subnet can't be connected "
2315  "to two interfaces.");
2316  interface = createString(makeString(-1, val));
2317  mapSet(subnet, interface, "interface");
2318  new_network_interface(cfile, interface);
2319  parse_semi(cfile);
2320  continue;
2321  }
2322  declaration = parse_statement(cfile, SUBNET_DECL, declaration);
2323  }
2324 
2325  cfile->stack_top--;
2326 
2327  /* Add the subnet to the list of subnets in this shared net. */
2329 
2330  return;
2331 }
2332 
2333 /* subnet-declaration :==
2334  net NETMASK netmask RBRACE parameters declarations LBRACE */
2335 
2336 void
2338 {
2339  const char *val;
2340  enum dhcp_token token;
2341  struct element *subnet;
2342  struct subnet *chain;
2343  struct element *subnets;
2344  struct string *address;
2345  struct string *netmask;
2346  struct string *prefix;
2347  unsigned char addr[4];
2348  unsigned len = sizeof(addr);
2349  size_t parent = 0;
2350  size_t i;
2351  int kind = 0;
2352 
2353  subnet = createMap();
2354  subnet->kind = SUBNET_DECL;
2355  TAILQ_CONCAT(&subnet->comments, &cfile->comments);
2356 
2357  subnet_counter++;
2359 
2360  chain = (struct subnet *)malloc(sizeof(*chain));
2361  if (chain == NULL)
2362  parse_error(cfile, "can't allocate subnet");
2363  memset(chain, 0, sizeof(*chain));
2364  chain->subnet = subnet;
2365  TAILQ_INSERT_TAIL(&known_subnets, chain);
2366 
2367  /* Find parent */
2368  for (i = cfile->stack_top; i > 0; --i) {
2369  kind = cfile->stack[i]->kind;
2370  if ((kind == SHARED_NET_DECL) ||
2371  (kind == GROUP_DECL) ||
2372  (kind == ROOT_GROUP)) {
2373  parent = i;
2374  break;
2375  }
2376  }
2377  if (kind == 0)
2378  parse_error(cfile, "can't find a place to put subnet");
2379  if (kind == SHARED_NET_DECL)
2380  chain->share = cfile->stack[parent];
2381  subnets = mapGet(cfile->stack[parent], "subnet4");
2382  if (subnets == NULL) {
2383  if (kind == SHARED_NET_DECL)
2384  parse_error(cfile, "shared network without subnets");
2385  subnets = createList();
2386  subnets->kind = SUBNET_DECL;
2387  mapSet(cfile->stack[parent], subnets, "subnet4");
2388  }
2389 
2390  /* Get the network number... */
2391  address = parse_numeric_aggregate(cfile, addr, &len, DOT, 10, 8);
2392  if (address == NULL)
2393  parse_error(cfile, "can't decode network number");
2394  if (address->length != 4)
2395  parse_error(cfile, "bad IPv4 address length");
2396  chain->addr = address;
2397 
2398  token = next_token(&val, NULL, cfile);
2399  if (token != NETMASK)
2400  parse_error(cfile, "Expecting netmask");
2401 
2402  /* Get the netmask... */
2403  netmask = parse_numeric_aggregate(cfile, addr, &len, DOT, 10, 8);
2404  if (netmask == NULL)
2405  parse_error(cfile, "can't decode network mask");
2406  if (netmask->length != address->length)
2407  parse_error(cfile, "bad IPv4 mask length");
2408  chain->mask = netmask;
2409 
2410  prefix = addrmask(address, netmask);
2411  if (prefix == NULL) {
2412  char bufa[INET_ADDRSTRLEN];
2413  char bufm[INET_ADDRSTRLEN];
2414 
2415  inet_ntop(AF_INET, address->content, bufa, INET_ADDRSTRLEN);
2416  inet_ntop(AF_INET, netmask->content, bufm, INET_ADDRSTRLEN);
2417  parse_error(cfile, "can't get a prefix from %s mask %s",
2418  bufa, bufm);
2419  }
2420  mapSet(subnet, createString(prefix), "subnet");
2421 
2422  common_subnet_parsing(cfile, subnets, subnet);
2423 }
2424 
2425 /* subnet6-declaration :==
2426  net / bits RBRACE parameters declarations LBRACE */
2427 
2428 void
2430 {
2431  enum dhcp_token token;
2432  const char *val;
2433  struct element *subnet;
2434  struct subnet *chain;
2435  struct element *subnets;
2436  struct string *address;
2437  struct string *prefix;
2438  struct string *netmask;
2439  size_t parent = 0;
2440  size_t i;
2441  int kind = 0;
2442  char *p;
2443 
2444  if (local_family != AF_INET6)
2445  parse_error(cfile, "subnet6 statement is only supported "
2446  "in DHCPv6 mode.");
2447 
2448  subnet = createMap();
2449  subnet->kind = SUBNET_DECL;
2450  TAILQ_CONCAT(&subnet->comments, &cfile->comments);
2451 
2452  subnet_counter++;
2454 
2455  chain = (struct subnet *)malloc(sizeof(*chain));
2456  if (chain == NULL)
2457  parse_error(cfile, "can't allocate subnet");
2458  memset(chain, 0, sizeof(*chain));
2459  chain->subnet = subnet;
2460  TAILQ_INSERT_TAIL(&known_subnets, chain);
2461 
2462  /* Find parent */
2463  for (i = cfile->stack_top; i > 0; --i) {
2464  kind = cfile->stack[i]->kind;
2465  if ((kind == SHARED_NET_DECL) ||
2466  (kind == GROUP_DECL) ||
2467  (kind == ROOT_GROUP)) {
2468  parent = i;
2469  break;
2470  }
2471  }
2472  if (kind == 0)
2473  parse_error(cfile, "can't find a place to put subnet");
2474  if (kind == SHARED_NET_DECL)
2475  chain->share = cfile->stack[parent];
2476  subnets = mapGet(cfile->stack[parent], "subnet6");
2477  if (subnets == NULL) {
2478  if (kind == SHARED_NET_DECL)
2479  parse_error(cfile, "shared network without subnets");
2480  subnets = createList();
2481  subnets->kind = SUBNET_DECL;
2482  mapSet(cfile->stack[parent], subnets, "subnet6");
2483  }
2484 
2485  address = parse_ip6_addr(cfile);
2486  if (address == NULL)
2487  parse_error(cfile, "can't decode network number");
2488  if (address->length != 16)
2489  parse_error(cfile, "bad IPv6 address length");
2490  chain->addr = address;
2491 
2492  prefix = makeStringExt(address->length, address->content, '6');
2493 
2494  token = next_token(&val, NULL, cfile);
2495  if (token != SLASH)
2496  parse_error(cfile, "Expecting a '/'.");
2497  appendString(prefix, val);
2498 
2499  token = next_token(&val, NULL, cfile);
2500  if (token != NUMBER)
2501  parse_error(cfile, "Expecting a number.");
2502  appendString(prefix, val);
2503 
2504  netmask = makeString(16, "0123456789abcdef");
2505  memset(netmask->content, 0, 16);
2506  p = netmask->content;
2507  for (i = atoi(val); i >= 8; i -= 8)
2508  *p++ = 0xff;
2509  *p = 0xff << (8 - i);
2510  chain->mask = netmask;
2511 
2512  mapSet(subnet, createString(prefix), "subnet");
2513 
2514  common_subnet_parsing(cfile, subnets, subnet);
2515 }
2516 
2517 /* group-declaration :== RBRACE parameters declarations LBRACE */
2518 
2519 void
2521 {
2522  const char *val;
2523  enum dhcp_token token;
2524  struct element *group;
2525  int declaration = 0;
2526  struct string *name = NULL;
2527 
2528  if (mapContains(cfile->stack[cfile->stack_top], "group"))
2529  parse_error(cfile, "another group is already open");
2530  group = createMap();
2531  group->skip = ISC_TRUE;
2532  group->kind = GROUP_DECL;
2533  TAILQ_CONCAT(&group->comments, &cfile->comments);
2534  mapSet(cfile->stack[cfile->stack_top], group, "group");
2535 
2536  token = peek_token(&val, NULL, cfile);
2537  if (is_identifier(token) || token == STRING) {
2538  skip_token(&val, NULL, cfile);
2539 
2540  name = makeString(-1, val);
2541  if (!name)
2542  parse_error(cfile, "no memory for group decl name %s",
2543  val);
2544  }
2545 
2546  parse_lbrace(cfile);
2547 
2548  stackPush(cfile, group);
2549 
2550  for (;;) {
2551  token = peek_token(&val, NULL, cfile);
2552  if (token == RBRACE) {
2553  skip_token(&val, NULL, cfile);
2554  break;
2555  } else if (token == END_OF_FILE) {
2556  skip_token(&val, NULL, cfile);
2557  parse_error(cfile, "unexpected end of file");
2558  break;
2559  } else if (token == TOKEN_DELETED) {
2560  skip_token(&val, NULL, cfile);
2561  parse_error(cfile, "deleted groups don't exist "
2562  "in the config file");
2563  } else if (token == DYNAMIC) {
2564  skip_token(&val, NULL, cfile);
2565  parse_error(cfile, "dynamic groups don't exist "
2566  "in the config file");
2567  } else if (token == STATIC) {
2568  skip_token(&val, NULL, cfile);
2569  parse_error(cfile, "static groups don't exist "
2570  "in the config file");
2571  }
2572  declaration = parse_statement(cfile, GROUP_DECL, declaration);
2573  }
2574 
2575  cfile->stack_top--;
2576 
2577  if (name != NULL)
2578  mapSet(group, createString(name), "name");
2579  close_group(cfile, group);
2580 }
2581 
2582 /*
2583  * Close a group. Called when a group is closed.
2584  * - spread parameters to children
2585  * - attach declarations at an upper level
2586  */
2587 
2588 void
2589 close_group(struct parse *cfile, struct element *group)
2590 {
2591  struct handle *handle;
2592  struct handle *nh;
2593  struct element *parent;
2594  struct element *item;
2595  struct element *param;
2596  struct handle *hosts = NULL;
2597  struct handle *shares = NULL;
2598  struct handle *subnets = NULL;
2599  struct handle *classes = NULL;
2600  struct handle *pdpools = NULL;
2601  struct handle *pools = NULL;
2602  struct handles downs;
2603  struct comment *comment;
2604  const char *key = NULL;
2605  const char *name = NULL;
2606  unsigned order = 0;
2607  isc_boolean_t marked = ISC_FALSE;
2608 
2609  TAILQ_INIT(&downs);
2610 
2611  /* check that group is in its parent */
2612  parent = cfile->stack[cfile->stack_top];
2613  if (parent->kind == PARAMETER)
2614  parse_error(cfile, "unexpected kind for group parent %d",
2615  parent->kind);
2616  item = mapGet(parent, "group");
2617  if (item == NULL)
2618  parse_error(cfile, "no group in parent");
2619  if (item != group)
2620  parse_error(cfile, "got a different group from parent");
2621  mapRemove(parent, "group");
2622 
2623  /* classify content */
2624  while (mapSize(group) > 0) {
2625  handle = mapPop(group);
2626  if ((handle == NULL) || (handle->key == NULL) ||
2627  (handle->value == NULL))
2628  parse_error(cfile, "can't get group item at %u",
2629  order);
2630  handle->order = order++;
2631  switch (handle->value->kind) {
2632  case TOPLEVEL:
2633  case ROOT_GROUP:
2634  case GROUP_DECL:
2635  badkind:
2636  parse_error(cfile, "impossible group item (kind %d) "
2637  "for %s at order %u",
2638  handle->value->kind, handle->key, order);
2639 
2640  case HOST_DECL:
2641  if (strcmp(handle->key, "reservations") != 0)
2642  parse_error(cfile, "expected reservations "
2643  "got %s at %u",
2644  handle->key, order);
2645  if (hosts != NULL)
2646  parse_error(cfile, "got reservations twice "
2647  "at %u and %u",
2648  hosts->order, order);
2649  if ((parent->kind == HOST_DECL) ||
2650  (parent->kind == CLASS_DECL))
2651  parse_error(cfile, "host declarations not "
2652  "allowed here.");
2653  hosts = handle;
2654  handle = NULL;
2655  break;
2656 
2657  case SHARED_NET_DECL:
2658  if (strcmp(handle->key, "shared-networks") != 0)
2659  parse_error(cfile, "expected shared-networks "
2660  "got %s at %u",
2661  handle->key, order);
2662  if ((parent->kind == SHARED_NET_DECL) ||
2663  (parent->kind == HOST_DECL) ||
2664  (parent->kind == SUBNET_DECL) ||
2665  (parent->kind == CLASS_DECL))
2666  parse_error(cfile, "shared-network parameters "
2667  "not allowed here.");
2668  shares = handle;
2669  handle = NULL;
2670  break;
2671 
2672  case SUBNET_DECL:
2673  key = local_family == AF_INET ? "subnet4" : "subnet6";
2674  if (strcmp(handle->key, key) != 0)
2675  parse_error(cfile, "expected %s got %s at %u",
2676  key, handle->key, order);
2677  if (subnets != NULL)
2678  parse_error(cfile, "got %s twice at %u and %u",
2679  key, subnets->order, order);
2680  if ((parent->kind == HOST_DECL) ||
2681  (parent->kind == SUBNET_DECL) ||
2682  (parent->kind == CLASS_DECL))
2683  parse_error(cfile, "subnet declarations not "
2684  "allowed here.");
2685  subnets = handle;
2686  handle = NULL;
2687  break;
2688 
2689  case CLASS_DECL:
2690  if (strcmp(handle->key, "client-classes") != 0)
2691  parse_error(cfile, "expected client-classes "
2692  "got %s at %u",
2693  handle->key, order);
2694  if (classes != NULL)
2695  parse_error(cfile, "got %s twice at %u and %u",
2696  key, classes->order, order);
2697  if (parent->kind == CLASS_DECL)
2698  parse_error(cfile, "class declarations not "
2699  "allowed here.");
2700  classes = handle;
2701  handle = NULL;
2702  break;
2703 
2704  case POOL_DECL:
2705  if (strcmp(handle->key, "pd-pools") == 0) {
2706  if (pdpools != NULL)
2707  parse_error(cfile, "got pd-pools "
2708  "twice at %u and %u",
2709  pdpools->order, order);
2710  pdpools = handle;
2711  } else if (strcmp(handle->key, "pools") == 0) {
2712  if (pools != NULL)
2713  parse_error(cfile, "got pools twice "
2714  "at %u and %u",
2715  pools->order, order);
2716  pools = handle;
2717  } else
2718  parse_error(cfile, "expecyed [pd-]pools got "
2719  "%s at %u",
2720  handle->key, order);
2721  if (parent->kind == POOL_DECL)
2722  parse_error(cfile, "pool declared within "
2723  "pool.");
2724  if ((parent->kind == HOST_DECL) ||
2725  (parent->kind == CLASS_DECL))
2726  parse_error(cfile, "pool declared outside "
2727  "of network");
2728  handle = NULL;
2729  break;
2730  default:
2731  if (handle->value->kind != PARAMETER)
2732  goto badkind;
2733  }
2734  if (handle == NULL)
2735  continue;
2736 
2737  /* we have a parameter */
2738  param = handle->value;
2739  /* group name */
2740  if (strcmp(handle->key, "name") == 0) {
2741  name = stringValue(param)->content;
2742  continue;
2743  }
2744  /* unexpected values */
2745  if ((strcmp(handle->key, "reservations") == 0) ||
2746  (strcmp(handle->key, "group") == 0) ||
2747  (strcmp(handle->key, "shared-networks") == 0) ||
2748  (strcmp(handle->key, "subnet4") == 0) ||
2749  (strcmp(handle->key, "subnet6") == 0) ||
2750  (strcmp(handle->key, "subnet") == 0) ||
2751  (strcmp(handle->key, "client-classes") == 0) ||
2752  (strcmp(handle->key, "hw-address") == 0) ||
2753  (strcmp(handle->key, "ip-address") == 0) ||
2754  (strcmp(handle->key, "extra-ip-addresses") == 0) ||
2755  (strcmp(handle->key, "ip-addresses") == 0) ||
2756  (strcmp(handle->key, "prefixes") == 0) ||
2757  (strcmp(handle->key, "pool") == 0) ||
2758  (strcmp(handle->key, "prefix") == 0) ||
2759  (strcmp(handle->key, "delegated-len") == 0) ||
2760  (strcmp(handle->key, "prefix-len") == 0) ||
2761  (strcmp(handle->key, "prefix-highest") == 0) ||
2762  (strcmp(handle->key, "option-def") == 0) ||
2763  (strcmp(handle->key, "hostname") == 0) ||
2764  (strcmp(handle->key, "client-id") == 0) ||
2765  (strcmp(handle->key, "host-identifier") == 0) ||
2766  (strcmp(handle->key, "flex-id") == 0) ||
2767  (strcmp(handle->key, "test") == 0) ||
2768  (strcmp(handle->key, "authoritative") == 0) ||
2769  (strcmp(handle->key, "dhcp-ddns") == 0) ||
2770  (strcmp(handle->key, "host-reservation-identifiers") == 0))
2771  parse_error(cfile, "unexpected parameter %s "
2772  "in group at %u",
2773  handle->key, order);
2774 
2775  /* to parent at group position */
2776  if ((strcmp(handle->key, "option-space") == 0) ||
2777  (strcmp(handle->key, "server-duid") == 0) ||
2778  (strcmp(handle->key, "statement") == 0) ||
2779  (strcmp(handle->key, "config") == 0) ||
2780  (strcmp(handle->key, "ddns-update-style") == 0) ||
2781  (strcmp(handle->key, "echo-client-id") == 0)) {
2782  if (!marked) {
2783  struct string *msg;
2784 
2785  marked = ISC_TRUE;
2786  msg = makeString(-1, "/// moved from group");
2787  if (name != NULL)
2788  appendString(msg, " ");
2789  appendString(msg, name);
2790  comment = createComment(msg->content);
2792  }
2793  mapSet(parent, param, handle->key);
2794  free(handle);
2795  continue;
2796  }
2797  /* To reconsider: qualifying-suffix, enable-updates */
2798  if ((strcmp(handle->key, "option-data") == 0) ||
2799  (strcmp(handle->key, "allow") == 0) ||
2800  (strcmp(handle->key, "deny") == 0) ||
2801  (strcmp(handle->key, "interface") == 0) ||
2802  (strcmp(handle->key, "valid-lifetime") == 0) ||
2803  (strcmp(handle->key, "preferred-lifetime") == 0) ||
2804  (strcmp(handle->key, "renew-timer") == 0) ||
2805  (strcmp(handle->key, "rebind-timer") == 0) ||
2806  (strcmp(handle->key, "boot-file-name") == 0) ||
2807  (strcmp(handle->key, "server-hostname") == 0) ||
2808  (strcmp(handle->key, "next-server") == 0) ||
2809  (strcmp(handle->key, "match-client-id") == 0)) {
2810  TAILQ_INSERT_TAIL(&downs, handle);
2811  continue;
2812  }
2813  /* unknown */
2814  if (!marked) {
2815  struct string *msg;
2816 
2817  marked = ISC_TRUE;
2818  msg = makeString(-1, "/// moved from group");
2819  if (name != NULL)
2820  appendString(msg, " ");
2821  appendString(msg, name);
2822  comment = createComment(msg->content);
2824  }
2825  comment = createComment("/// unhandled parameter");
2827  param->skip = ISC_TRUE;
2828  cfile->issue_counter++;
2829  mapSet(parent, param, handle->key);
2830  free(handle);
2831  }
2832  TAILQ_FOREACH_SAFE(handle, &downs, nh) {
2833  if (strcmp(handle->key, "option-data") == 0) {
2834  option_data_derive(cfile, handle, hosts);
2835  option_data_derive(cfile, handle, shares);
2836  option_data_derive(cfile, handle, subnets);
2837  derive_classes(cfile, handle, classes);
2838  option_data_derive(cfile, handle, pdpools);
2839  option_data_derive(cfile, handle, pools);
2840  } else if ((strcmp(handle->key, "allow") == 0) ||
2841  (strcmp(handle->key, "deny") == 0)) {
2842  derive(handle, pdpools);
2843  derive(handle, pools);
2844  } else if ((strcmp(handle->key, "interface") == 0) ||
2845  (strcmp(handle->key, "valid-lifetime") == 0) ||
2846  (strcmp(handle->key, "preferred-lifetime") == 0) ||
2847  (strcmp(handle->key, "renew-timer") == 0) ||
2848  (strcmp(handle->key, "rebind-timer") == 0) ||
2849  (strcmp(handle->key, "match-client-id") == 0)) {
2850  derive(handle, shares);
2851  derive(handle, subnets);
2852  } else if ((strcmp(handle->key, "boot-file-name") == 0) ||
2853  (strcmp(handle->key, "server-hostname") == 0)) {
2854  derive(handle, hosts);
2855  derive_classes(cfile, handle, classes);
2856  } else if (strcmp(handle->key, "next-server") == 0) {
2857  derive(handle, hosts);
2858  derive(handle, subnets);
2859  derive_classes(cfile, handle, classes);
2860  } else
2861  parse_error(cfile, "unexpected parameter %s to derive",
2862  handle->key);
2863  }
2864  if (hosts != NULL) {
2865  struct element *root;
2866 
2867  root = mapGet(cfile->stack[1], "reservations");
2868  if (root == NULL)
2869  mapSet(cfile->stack[1], hosts->value, "reservations");
2870  else
2871  concat(root, hosts->value);
2872  }
2873  if (shares != NULL) {
2874  struct element *upper;
2875 
2876  upper = mapGet(parent, "shared-networks");
2877  if (upper == NULL)
2878  mapSet(parent, shares->value, "shared-networks");
2879  else
2880  concat(upper, shares->value);
2881  }
2882  key = local_family == AF_INET ? "subnet4" : "subnet6";
2883  if (subnets != NULL) {
2884  struct element *upper;
2885 
2886  upper = mapGet(parent, key);
2887  if (upper == NULL)
2888  mapSet(parent, subnets->value, key);
2889  else
2890  concat(upper, subnets->value);
2891  }
2892  if (classes != NULL) {
2893  struct element *upper;
2894  size_t where;
2895  int kind = 0;
2896 
2897  for (where = cfile->stack_top; where > 0; --where) {
2898  kind = cfile->stack[where]->kind;
2899  if ((kind == GROUP_DECL) || (kind == ROOT_GROUP))
2900  break;
2901  }
2902  if (kind == GROUP_DECL) {
2903  upper = mapGet(cfile->stack[where], "client-classes");
2904  if (upper == NULL)
2905  mapSet(cfile->stack[where],
2906  classes->value,
2907  "client-classes");
2908  else
2909  concat_classes(cfile, upper, classes->value);
2910  }
2911  }
2912  if (pdpools != NULL) {
2913  struct element *upper;
2914 
2915  upper = mapGet(parent, "pd-pools");
2916  if (upper == NULL)
2917  mapSet(parent, pdpools->value, "pools");
2918  else
2919  concat(upper, pdpools->value);
2920  }
2921  if (pools != NULL) {
2922  struct element *upper;
2923 
2924  upper = mapGet(parent, "pools");
2925  if (upper == NULL)
2926  mapSet(parent, pools->value, "pools");
2927  else
2928  concat(upper, pools->value);
2929  }
2930 }
2931 
2932 /*
2933  * Specialized derivation routine for option-data
2934  * (options are identified by space + name and/or code
2935  */
2936 
2937 static void
2938 option_data_derive(struct parse *cfile, struct handle *src, struct handle *dst)
2939 {
2940  struct element *list;
2941  struct element *item;
2942  struct element *opt_list;
2943  size_t i;
2944 
2945  if (dst == NULL)
2946  return;
2947  list = dst->value;
2948  assert(list != NULL);
2949  assert(list->type == ELEMENT_LIST);
2950  for (i = 0; i < listSize(list); i++) {
2951  item = listGet(list, i);
2952  assert(item != NULL);
2953  assert(item->type == ELEMENT_MAP);
2954  opt_list = mapGet(item, src->key);
2955  if (opt_list != NULL) {
2956  merge_option_data(src->value, opt_list);
2957  continue;
2958  }
2959  opt_list = copy(src->value);
2960  mapSet(item, opt_list, src->key);
2961  }
2962 }
2963 
2964 /*
2965  * Specialized derivation routine for classes
2966  * (which are by reference so a resolution step is needed)
2967  */
2968 static void
2969 derive_classes(struct parse *cfile, struct handle *src, struct handle *dst)
2970 {
2971  struct element *list;
2972  struct element *item;
2973  size_t i;
2974 
2975  if (dst == NULL)
2976  return;
2977  list = dst->value;
2978  assert(list != NULL);
2979  assert(list->type == ELEMENT_LIST);
2980  for (i = 0; i < listSize(list); i++) {
2981  item = listGet(list, i);
2982  assert(item != NULL);
2983  assert(item->type == ELEMENT_MAP);
2984  item = get_class(cfile, item);
2985  if (item == NULL)
2986  parse_error(cfile, "dangling class reference");
2987  if (strcmp(src->key, "option-data") == 0) {
2988  struct element *opt_list;
2989 
2990  opt_list = mapGet(item, "option-data");
2991  if (opt_list != NULL)
2992  merge_option_data(src->value, opt_list);
2993  else
2994  mapSet(item, copy(src->value), "option-data");
2995  continue;
2996  }
2997  if (mapContains(item, src->key))
2998  continue;
2999  mapSet(item, copy(src->value), src->key);
3000  }
3001 }
3002 
3003 /* fixed-addr-parameter :== ip-addrs-or-hostnames SEMI
3004  ip-addrs-or-hostnames :== ip-addr-or-hostname
3005  | ip-addrs-or-hostnames ip-addr-or-hostname */
3006 
3007 struct element *
3009  const char *val;
3010  enum dhcp_token token;
3011  struct element *addr;
3012  struct element *addresses;
3013  struct string *address;
3014 
3015  addresses = createList();
3016  TAILQ_CONCAT(&addresses->comments, &cfile->comments);
3017 
3018  do {
3019  address = NULL;
3020  if (type == FIXED_ADDR)
3021  address = parse_ip_addr_or_hostname(cfile, ISC_TRUE);
3022  else if (type == FIXED_ADDR6)
3023  address = parse_ip6_addr_txt(cfile);
3024  else
3025  parse_error(cfile, "requires FIXED_ADDR[6]");
3026  if (address == NULL)
3027  parse_error(cfile, "can't parse fixed address");
3028  addr = createString(address);
3029  /* Take the comment for resolution into multiple addresses */
3030  TAILQ_CONCAT(&addr->comments, &cfile->comments);
3031  listPush(addresses, addr);
3032  token = peek_token(&val, NULL, cfile);
3033  if (token == COMMA)
3034  token = next_token(&val, NULL, cfile);
3035  } while (token == COMMA);
3036 
3037  parse_semi(cfile);
3038 
3039  /* Sanity */
3040  if (listSize(addresses) == 0)
3041  parse_error(cfile, "can't get fixed address");
3042 
3043  return addresses;
3044 
3045 }
3046 
3047 #ifdef notyet
3048 /* Parse the right side of a 'binding value'.
3049  *
3050  * set foo = "bar"; is a string
3051  * set foo = false; is a boolean
3052  * set foo = %31; is a numeric value.
3053  */
3054 static struct element *
3055 parse_binding_value(struct parse *cfile)
3056 {
3057  struct element *value = NULL;
3058  struct string *data;
3059  const char *val;
3060  unsigned buflen;
3061  int token;
3062 
3063  token = peek_token(&val, NULL, cfile);
3064  if (token == STRING) {
3065  skip_token(&val, &buflen, cfile);
3066  data = makeString(buflen, val);
3067  value = createString(data);
3068  } else if (token == NUMBER_OR_NAME) {
3069  value = createMap();
3070  data = parse_hexa(cfile);
3071  mapSet(value, createHexa(data), "const-data");
3072  } else if (token == PERCENT) {
3073  skip_token(&val, NULL, cfile);
3074  token = next_token(&val, NULL, cfile);
3075  if (token != NUMBER)
3076  parse_error(cfile, "expecting decimal number.");
3077  value = createInt(atol(val));
3078  } else if (token == NAME) {
3079  token = next_token(&val, NULL, cfile);
3080  if (!strcasecmp(val, "true"))
3082  else if (!strcasecmp(val, "false"))
3084  else
3085  parse_error(cfile, "expecting true or false");
3086  } else
3087  parse_error(cfile, "expecting a constant value.");
3088 
3089  return value;
3090 }
3091 #endif
3092 
3093 /* address-range-declaration :== ip-address ip-address SEMI
3094  | DYNAMIC_BOOTP ip-address ip-address SEMI */
3095 
3096 void
3097 parse_address_range(struct parse *cfile, int type, size_t where)
3098 {
3099  struct string *low, *high, *range;
3100  unsigned char addr[4];
3101  unsigned len = sizeof(addr);
3102  enum dhcp_token token;
3103  const char *val;
3104  struct element *pool;
3105  struct element *r;
3106  struct range *chain;
3107  size_t i;
3108  int kind;
3109 
3110  if ((token = peek_token(&val, NULL, cfile)) == DYNAMIC_BOOTP) {
3111  skip_token(&val, NULL, cfile);
3112  }
3113 
3114  /* Get the bottom address in the range... */
3115  low = parse_numeric_aggregate(cfile, addr, &len, DOT, 10, 8);
3116  if (low == NULL)
3117  parse_error(cfile, "can't parse range (low)");
3118 
3119  /* Only one address? */
3120  token = peek_token(&val, NULL, cfile);
3121  if (token == SEMI)
3122  high = low;
3123  else {
3124  /* Get the top address in the range... */
3125  high = parse_numeric_aggregate(cfile, addr, &len, DOT, 10, 8);
3126  if (high == NULL)
3127  parse_error(cfile, "can't parse range (high)");
3128  }
3129 
3130  token = next_token(&val, NULL, cfile);
3131  if (token != SEMI)
3132  parse_error(cfile, "semicolon expected.");
3133 
3134  if (type != POOL_DECL) {
3135  struct element *group;
3136  struct element *pools;
3137 #ifdef want_bootp
3138  struct element *permit;
3139 #endif
3140 
3141  group = cfile->stack[where];
3142  pool = createMap();
3143 #ifdef want_bootp
3144  permit = createList();
3145  permit->skip = ISC_TRUE;
3146 
3147  /* Dynamic pools permit all clients. Otherwise
3148  we prohibit BOOTP clients. */
3149  if (dynamic) {
3150  struct string *all;
3151 
3152  all = makeString(-1, "all clients");
3153  listPush(permit, createString(all));
3154  mapSet(pool, permit, "allow");
3155  } else {
3156  struct string *dyn_bootp;
3157 
3158  dyn_bootp = makeString(-1, "dynamic bootp clients");
3159  listPush(permit, createString(dyn_bootp));
3160  mapSet(pool, permit, "deny");
3161  }
3162 #endif
3163 
3164  pools = mapGet(group, "pools");
3165  if (pools == NULL) {
3166  pools = createList();
3167  pools->kind = POOL_DECL;
3168  mapSet(group, pools, "pools");
3169  }
3170  listPush(pools, pool);
3171  } else
3172  pool = cfile->stack[where];
3173 
3174  /* Create the new address range... */
3175  if (memcmp(high->content, low->content, high->length) < 0) {
3176  struct string *swap;
3177 
3178  swap = low;
3179  low = high;
3180  high = swap;
3181  }
3182  range = makeStringExt(low->length, low->content, 'I');
3183  appendString(range, " - ");
3184  concatString(range, makeStringExt(high->length, high->content, 'I'));
3185 
3186  r = createString(range);
3187  TAILQ_CONCAT(&r->comments, &cfile->comments);
3188 
3189  mapSet(pool, r, "pool");
3190 
3191  chain = (struct range *)malloc(sizeof(*chain));
3192  if (chain == NULL)
3193  parse_error(cfile, "can't allocate range");
3194  memset(chain, 0, sizeof(*chain));
3195  chain->pool = pool;
3196  for (i = where; i > 0; --i) {
3197  kind = cfile->stack[i]->kind;
3198  if (kind == SHARED_NET_DECL) {
3199  chain->share = cfile->stack[i];
3200  break;
3201  }
3202  }
3203  chain->low = low;
3204  TAILQ_INSERT_TAIL(&known_ranges, chain);
3205 }
3206 
3207 /* address-range6-declaration :== ip-address6 ip-address6 SEMI
3208  | ip-address6 SLASH number SEMI
3209  | ip-address6 [SLASH number] TEMPORARY SEMI */
3210 
3211 void
3212 parse_address_range6(struct parse *cfile, int type, size_t where)
3213 {
3214  struct string *low, *high, *range;
3215  enum dhcp_token token;
3216  const char *val;
3217  isc_boolean_t is_temporary = ISC_FALSE;
3218  struct element *pool;
3219  struct element *r;
3220  struct range *chain;
3221  size_t i;
3222  int kind;
3223 
3224  if (local_family != AF_INET6)
3225  parse_error(cfile, "range6 statement is only supported "
3226  "in DHCPv6 mode.");
3227 
3228  /*
3229  * Read starting address as text.
3230  */
3231  low = parse_ip6_addr_txt(cfile);
3232  if (low == NULL)
3233  parse_error(cfile, "can't parse range6 address (low)");
3234  range = allocString();
3235  concatString(range, low);
3236 
3237  /*
3238  * See if we we're using range or CIDR notation or TEMPORARY
3239  */
3240  token = peek_token(&val, NULL, cfile);
3241  if (token == SLASH) {
3242  appendString(range, val);
3243  /*
3244  * '/' means CIDR notation, so read the bits we want.
3245  */
3246  skip_token(NULL, NULL, cfile);
3247  token = next_token(&val, NULL, cfile);
3248  if (token != NUMBER)
3249  parse_error(cfile, "expecting number");
3250  /*
3251  * no sanity checks
3252  */
3253  appendString(range, val);
3254  /*
3255  * can be temporary (RFC 4941 like)
3256  */
3257  token = peek_token(&val, NULL, cfile);
3258  if (token == TEMPORARY) {
3259  is_temporary = ISC_TRUE;
3260  appendString(range, " ");
3261  appendString(range, val);
3262  skip_token(NULL, NULL, cfile);
3263  }
3264  } else if (token == TEMPORARY) {
3265  /*
3266  * temporary (RFC 4941)
3267  */
3268  is_temporary = ISC_TRUE;
3269  appendString(range, "/64 ");
3270  appendString(range, val);
3271  skip_token(NULL, NULL, cfile);
3272  } else {
3273  /*
3274  * No '/', so we are looking for the end address of
3275  * the IPv6 pool.
3276  */
3277  high = parse_ip6_addr_txt(cfile);
3278  if (high == NULL)
3279  parse_error(cfile,
3280  "can't parse range6 address (high)");
3281  /* No sanity checks */
3282  appendString(range, " - ");
3283  appendString(range, high->content);
3284  }
3285 
3286  token = next_token(NULL, NULL, cfile);
3287  if (token != SEMI)
3288  parse_error(cfile, "semicolon expected.");
3289 
3290  if (type != POOL_DECL) {
3291  struct element *group;
3292  struct element *pools;
3293 
3294  group = cfile->stack[where];
3295  pool = createMap();
3296  pools = mapGet(group, "pools");
3297  if (pools == NULL) {
3298  pools = createList();
3299  pools->kind = POOL_DECL;
3300  mapSet(group, pools, "pools");
3301  }
3302  listPush(pools, pool);
3303  } else
3304  pool = cfile->stack[where];
3305 
3306  r = createString(range);
3307  TAILQ_CONCAT(&r->comments, &cfile->comments);
3308  if (is_temporary) {
3309  pool->skip = ISC_TRUE;
3310  cfile->issue_counter++;
3311  }
3312  mapSet(pool, r, "pool");
3313 
3314  chain = (struct range *)malloc(sizeof(*chain));
3315  if (chain == NULL)
3316  parse_error(cfile, "can't allocate range");
3317  memset(chain, 0, sizeof(*chain));
3318  chain->pool = pool;
3319  for (i = where; i > 0; --i) {
3320  kind = cfile->stack[i]->kind;
3321  if (kind == SHARED_NET_DECL) {
3322  chain->share = cfile->stack[i];
3323  break;
3324  }
3325  }
3326  chain->low = low;
3327  TAILQ_INSERT_TAIL(&known_ranges, chain);
3328 }
3329 
3330 /* prefix6-declaration :== ip-address6 ip-address6 SLASH number SEMI */
3331 
3332 void
3333 parse_prefix6(struct parse *cfile, int type, size_t where)
3334 {
3335  struct string *lo, *hi;
3336  int plen;
3337  int bits;
3338  enum dhcp_token token;
3339  const char *val;
3340  struct element *pool;
3341  struct element *prefix;
3342 
3343  if (local_family != AF_INET6)
3344  parse_error(cfile, "prefix6 statement is only supported "
3345  "in DHCPv6 mode.");
3346 
3347  /*
3348  * Read starting and ending address as text.
3349  */
3350  lo = parse_ip6_addr_txt(cfile);
3351  if (lo == NULL)
3352  parse_error(cfile, "can't parse prefix6 address (low)");
3353 
3354  hi = parse_ip6_addr_txt(cfile);
3355  if (hi == NULL)
3356  parse_error(cfile, "can't parse prefix6 address (high)");
3357 
3358  /*
3359  * Next is '/' number ';'.
3360  */
3361  token = next_token(NULL, NULL, cfile);
3362  if (token != SLASH)
3363  parse_error(cfile, "expecting '/'");
3364  token = next_token(&val, NULL, cfile);
3365  if (token != NUMBER)
3366  parse_error(cfile, "expecting number");
3367  bits = atoi(val);
3368  if ((bits <= 0) || (bits >= 128))
3369  parse_error(cfile, "networks have 0 to 128 bits (exclusive)");
3370 
3371  token = next_token(NULL, NULL, cfile);
3372  if (token != SEMI)
3373  parse_error(cfile, "semicolon expected.");
3374 
3375  if (type != POOL_DECL) {
3376  struct element *group;
3377  struct element *pools;
3378 
3379  group = cfile->stack[where];
3380  pool = createMap();
3381  pools = mapGet(group, "pd-pools");
3382  if (pools == NULL) {
3383  pools = createList();
3384  pools->kind = POOL_DECL;
3385  mapSet(group, pools, "pd-pools");
3386  }
3387  listPush(pools, pool);
3388  } else
3389  pool = cfile->stack[where];
3390 
3391  prefix = createString(lo);
3392  TAILQ_CONCAT(&prefix->comments, &cfile->comments);
3393  mapSet(pool, prefix, "prefix");
3394  mapSet(pool, createInt(bits), "delegated-len");
3395  plen = get_prefix_length(lo->content, hi->content);
3396  if (plen >= 0)
3397  mapSet(pool, createInt(plen), "prefix-len");
3398  else {
3399  if (!pool->skip)
3400  cfile->issue_counter++;
3401  pool->skip = ISC_TRUE;
3402  mapSet(pool, createString(hi), "prefix-highest");
3403  }
3404 }
3405 
3406 /* fixed-prefix6 :== ip6-address SLASH number SEMI */
3407 
3408 void
3409 parse_fixed_prefix6(struct parse *cfile, size_t host_decl)
3410 {
3411  struct string *ia;
3412  enum dhcp_token token;
3413  const char *val;
3414  struct element *host;
3415  struct element *prefixes;
3416  struct element *prefix;
3417 
3418  if (local_family != AF_INET6)
3419  parse_error(cfile, "fixed-prefix6 statement is only "
3420  "supported in DHCPv6 mode.");
3421 
3422  /*
3423  * Get the fixed-prefix list.
3424  */
3425  host = cfile->stack[host_decl];
3426  prefixes = mapGet(host, "prefixes");
3427  if (prefixes == NULL) {
3428  prefixes = createList();
3429  mapSet(host, prefixes, "prefixes");
3430  }
3431 
3432  ia = parse_ip6_addr_txt(cfile);
3433  if (ia == NULL)
3434  parse_error(cfile, "can't parse fixed-prefix6 address");
3435  token = next_token(&val, NULL, cfile);
3436  if (token != SLASH)
3437  parse_error(cfile, "expecting '/'");
3438  appendString(ia, val);
3439  token = next_token(&val, NULL, cfile);
3440  if (token != NUMBER)
3441  parse_error(cfile, "expecting number");
3442  appendString(ia, val);
3443  token = next_token(NULL, NULL, cfile);
3444  if (token != SEMI)
3445  parse_error(cfile, "semicolon expected.");
3446 
3447  prefix = createString(ia);
3448  TAILQ_CONCAT(&prefix->comments, &cfile->comments);
3449  listPush(prefixes, prefix);
3450 }
3451 
3471 void
3472 parse_pool6_statement(struct parse *cfile, int type)
3473 {
3474  enum dhcp_token token;
3475  const char *val;
3476  isc_boolean_t done = ISC_FALSE;
3477  struct element *pool;
3478  struct element *pools;
3479  struct element *pdpool;
3480  struct element *pdpools;
3481  struct element *permit;
3482  struct element *prohibit;
3483  int declaration = 0;
3484  unsigned range_counter = 0;
3485  unsigned prefix_counter = 0;
3486 
3487  if (local_family != AF_INET6)
3488  parse_error(cfile, "pool6 statement is only supported "
3489  "in DHCPv6 mode.");
3490 
3491  pool = createMap();
3492  pool->kind = POOL_DECL;
3493  TAILQ_CONCAT(&pool->comments, &cfile->comments);
3494 
3495  if (type != SUBNET_DECL)
3496  parse_error(cfile, "pool6s are only valid inside "
3497  "subnet statements.");
3498  parse_lbrace(cfile);
3499 
3500  stackPush(cfile, pool);
3501  type = POOL_DECL;
3502 
3503  permit = createList();
3504  prohibit = createList();
3505 
3506  do {
3507  token = peek_token(&val, NULL, cfile);
3508  switch (token) {
3509  case RANGE6:
3510  skip_token(NULL, NULL, cfile);
3511  parse_address_range6(cfile, type, cfile->stack_top);
3512  range_counter++;
3513  break;
3514 
3515  case PREFIX6:
3516  skip_token(NULL, NULL, cfile);
3517  parse_prefix6(cfile, type, cfile->stack_top);
3518  mapSet(pool, createNull(), "***mark***");
3519  prefix_counter++;
3520  break;
3521 
3522  case ALLOW:
3523  skip_token(NULL, NULL, cfile);
3524  get_permit(cfile, permit);
3525  break;
3526 
3527  case DENY:
3528  skip_token(NULL, NULL, cfile);
3529  get_permit(cfile, prohibit);
3530  break;
3531 
3532  case RBRACE:
3533  skip_token(&val, NULL, cfile);
3534  done = ISC_TRUE;
3535  break;
3536 
3537  case END_OF_FILE:
3538  /*
3539  * We can get to END_OF_FILE if, for instance,
3540  * the parse_statement() reads all available tokens
3541  * and leaves us at the end.
3542  */
3543  parse_error(cfile, "unexpected end of file");
3544 
3545  default:
3546  declaration = parse_statement(cfile, POOL_DECL,
3547  declaration);
3548  break;
3549  }
3550  } while (!done);
3551 
3552  cfile->stack_top--;
3553 
3554  generate_class(cfile, pool, permit, prohibit);
3555 
3556  /*
3557  * Spread and eventually split between pools and pd-pools
3558  */
3559  if (prefix_counter == 0) {
3560  /* we need pools list */
3561  pools = mapGet(cfile->stack[cfile->stack_top], "pools");
3562  if (pools == NULL) {
3563  pools = createList();
3564  pools->kind = POOL_DECL;
3565  mapSet(cfile->stack[cfile->stack_top], pools, "pools");
3566  }
3567 
3568  /* no address or prefix range */
3569  if (range_counter == 0) {
3570  struct comment *comment;
3571 
3572  comment = createComment("empty pool6");
3573  TAILQ_INSERT_TAIL(&pool->comments, comment);
3574  pool->skip = ISC_TRUE;
3575  cfile->issue_counter++;
3576  listPush(pools, pool);
3577  return;
3578  }
3579  } else {
3580  /* we need pd-pools list */
3581  pdpools = mapGet(cfile->stack[cfile->stack_top], "pd-pools");
3582  if (pdpools == NULL) {
3583  pdpools = createList();
3584  pdpools->kind = POOL_DECL;
3585  mapSet(cfile->stack[cfile->stack_top],
3586  pdpools, "pd-pools");
3587  }
3588 
3589  /* split and purge copies */
3590  pdpool = copy(pool);
3591  while (mapContains(pdpool, "pool"))
3592  mapRemove(pdpool, "pool");
3593  while (mapContains(pool, "prefix"))
3594  mapRemove(pool, "prefix");
3595  while (mapContains(pool, "prefix-len"))
3596  mapRemove(pool, "prefix-len");
3597  while (mapContains(pool, "delegated-len"))
3598  mapRemove(pool, "delegated-len");
3599  while (mapContains(pool, "excluded-prefix"))
3600  mapRemove(pool, "excluded-prefix");
3601  while (mapContains(pool, "excluded-prefix-len"))
3602  mapRemove(pool, "excluded-prefix-len");
3603  while (mapContains(pool, "***mark***"))
3604  mapRemove(pool, "***mark***");
3605 
3606  /* spread extra prefixes into pdpool copies */
3607  while (--prefix_counter != 0) {
3608  struct handle *handle;
3609  struct element *first;
3610  struct element *saved;
3611  isc_boolean_t seen = ISC_FALSE;
3612 
3613  first = createMap();
3614  saved = copy(pdpool);
3615  while (mapSize(pdpool) > 0) {
3616  handle = mapPop(pdpool);
3617  if ((handle == NULL) ||
3618  (handle->key == NULL) ||
3619  (handle->value == NULL))
3620  parse_error(cfile, "bad pdpool entry");
3621  if (strcmp(handle->key, "***mark***") == 0) {
3622  if (!seen) {
3623  mapRemove(saved, handle->key);
3624  seen = ISC_TRUE;
3625  }
3626  continue;
3627  }
3628  if ((strcmp(handle->key, "prefix") != 0) &&
3629  (strcmp(handle->key, "prefix-len") != 0) &&
3630  (strcmp(handle->key,
3631  "delegated-len") != 0) &&
3632  (strcmp(handle->key,
3633  "excluded-prefix") != 0) &&
3634  (strcmp(handle->key,
3635  "excluded-prefix-len") != 0))
3636  mapSet(first, handle->value,
3637  handle->key);
3638  else if (!seen) {
3639  mapSet(first, handle->value,
3640  handle->key);
3641  mapRemove(saved, handle->key);
3642  }
3643  }
3644  listPush(pdpools, first);
3645  pdpool = saved;
3646  }
3647  if (!mapContains(pdpool, "***mark***"))
3648  parse_error(cfile, "can't find prefix marker");
3649  mapRemove(pdpool, "***mark***");
3650  if (mapContains(pdpool, "***mark***"))
3651  parse_error(cfile, "unexpected prefix marker");
3652  listPush(pdpools, pdpool);
3653  }
3654 
3655  /* Do pools now */
3656  if (range_counter != 0) {
3657  /* we need pools list */
3658  pools = mapGet(cfile->stack[cfile->stack_top], "pools");
3659  if (pools == NULL) {
3660  pools = createList();
3661  pools->kind = POOL_DECL;
3662  mapSet(cfile->stack[cfile->stack_top], pools, "pools");
3663  }
3664 
3665  /* spread extra prefixes into pool copies */
3666  while (--range_counter != 0) {
3667  struct handle *handle;
3668  struct element *first;
3669  struct element *saved;
3670  isc_boolean_t seen = ISC_FALSE;
3671 
3672  first = createMap();
3673  saved = copy(pool);
3674  while (mapSize(pool) > 0) {
3675  handle = mapPop(pool);
3676  if ((handle == NULL) ||
3677  (handle->key == NULL) ||
3678  (handle->value == NULL))
3679  parse_error(cfile, "bad pool entry");
3680  if (strcmp(handle->key, "pool") != 0)
3681  mapSet(first, handle->value,
3682  handle->key);
3683  else if (!seen) {
3684  mapSet(first, handle->value,
3685  handle->key);
3686  mapRemove(saved, "pool");
3687  seen = ISC_TRUE;
3688  }
3689  }
3690  listPush(pools, first);
3691  pool = saved;
3692  }
3693  listPush(pools, pool);
3694  }
3695 }
3696 
3697 /* allow-deny-keyword :== BOOTP
3698  | BOOTING
3699  | DYNAMIC_BOOTP
3700  | UNKNOWN_CLIENTS */
3701 
3702 struct element *
3703 parse_allow_deny(struct parse *cfile, int flag)
3704 {
3705  enum dhcp_token token;
3706  const char *val;
3707  const char *value;
3708  const char *name;
3709  struct element *config;
3710  struct option *option;
3711 
3712  switch (flag) {
3713  case 0:
3714  value = "deny";
3715  break;
3716  case 1:
3717  value = "allow";
3718  break;
3719  case 2:
3720  value = "ignore";
3721  break;
3722  default:
3723  value = "unknown?";
3724  break;
3725  }
3726 
3727  token = next_token(&val, NULL, cfile);
3728  switch (token) {
3729  case TOKEN_BOOTP:
3730  name = "allow-bootp";
3731  break;
3732 
3733  case BOOTING:
3734  name = "allow-booting";
3735  break;
3736 
3737  case DYNAMIC_BOOTP:
3738  name = "dynamic-bootp";
3739  break;
3740 
3741  case UNKNOWN_CLIENTS:
3742  name = "boot-unknown-clients";
3743  break;
3744 
3745  case DUPLICATES:
3746  name = "duplicates";
3747  break;
3748 
3749  case DECLINES:
3750  name = "declines";
3751  break;
3752 
3753  case CLIENT_UPDATES:
3754  name = "client-updates";
3755  break;
3756 
3757  case LEASEQUERY:
3758  name = "leasequery";
3759  break;
3760 
3761  default:
3762  parse_error(cfile, "expecting allow/deny key");
3763  }
3764  parse_semi(cfile);
3765 
3766  config = createMap();
3767  mapSet(config, createString(makeString(-1, value)), "value");
3768  mapSet(config, createString(makeString(-1, name)), "name");
3769  option = option_lookup_name("server", name);
3770  if (option == NULL)
3771  parse_error(cfile, "unknown allow/deny keyword (%s)", name);
3772  mapSet(config, createInt(option->code), "code");
3773  config->skip = ISC_TRUE;
3774  cfile->issue_counter++;
3775  return config;
3776 }
3777 
3778 /*
3779  * When we parse a server-duid statement in a config file, we will
3780  * have the type of the server DUID to generate, and possibly the
3781  * actual value defined.
3782  *
3783  * server-duid llt;
3784  * server-duid llt ethernet|ieee802|fddi 213982198 00:16:6F:49:7D:9B;
3785  * server-duid ll;
3786  * server-duid ll ethernet|ieee802|fddi 00:16:6F:49:7D:9B;
3787  * server-duid en 2495 "enterprise-specific-identifier-1234";
3788  */
3789 void
3791  enum dhcp_token token;
3792  const char *val;
3793  unsigned int len;
3794  struct string *ll_addr;
3795  struct element *duid;
3796  struct element *item;
3797  int ll_type;
3798 
3799  duid = createMap();
3800  TAILQ_CONCAT(&duid->comments, &cfile->comments);
3801 
3802  /*
3803  * Consume the SERVER_DUID token.
3804  */
3805  next_token(&val, NULL, cfile);
3806 
3807  /*
3808  * Obtain the DUID type.
3809  */
3810  token = next_token(&val, NULL, cfile);
3811 
3812  /*
3813  * Enterprise is the easiest - enterprise number and raw data
3814  * are required.
3815  */
3816  if (token == EN) {
3817  item = createString(makeString(-1, "EN"));
3818  mapSet(duid, item, "type");
3819 
3820  /*
3821  * Get enterprise number and identifier.
3822  */
3823  token = next_token(&val, NULL, cfile);
3824  if (token != NUMBER)
3825  parse_error(cfile, "enterprise number expected");
3826  item = createInt(atoi(val));
3827  mapSet(duid, item, "enterprise-id");
3828 
3829  token = next_token(&val, &len, cfile);
3830  if (token != STRING)
3831  parse_error(cfile, "identifier expected");
3832  /* Kea requires a hexadecimal identifier */
3833  if (is_hexa_only(val, len))
3834  item = createString(makeString(len, val));
3835  else
3836  item = createString(makeStringExt(len, val, 'X'));
3837  mapSet(duid, item, "identifier");
3838  }
3839 
3840  /*
3841  * Next easiest is the link-layer DUID. It consists only of
3842  * the LL directive, or optionally the specific value to use.
3843  *
3844  * If we have LL only, then we set the type. If we have the
3845  * value, then we set the actual DUID.
3846  */
3847  else if (token == LL) {
3848  item = createString(makeString(-1, "LL"));
3849  mapSet(duid, item, "type");
3850 
3851  if (peek_token(NULL, NULL, cfile) != SEMI) {
3852  /*
3853  * Get our hardware type and address.
3854  */
3855  token = next_token(NULL, NULL, cfile);
3856  switch (token) {
3857  case ETHERNET:
3858  ll_type = HTYPE_ETHER;
3859  break;
3860  case TOKEN_RING:
3861  ll_type = HTYPE_IEEE802;
3862  break;
3863  case TOKEN_FDDI:
3864  ll_type = HTYPE_FDDI;
3865  break;
3866  default:
3867  parse_error(cfile, "hardware type expected");
3868  }
3869  item = createInt(ll_type);
3870  mapSet(duid, item, "htype");
3871 
3872  ll_addr = parse_hexa(cfile);
3873  if (ll_addr == NULL)
3874  parse_error(cfile,
3875  "can't get hardware address");
3876  item = createString(ll_addr);
3877  mapSet(duid, item, "identifier");
3878  }
3879  }
3880 
3881  /*
3882  * Finally the link-layer DUID plus time. It consists only of
3883  * the LLT directive, or optionally the specific value to use.
3884  *
3885  * If we have LLT only, then we set the type. If we have the
3886  * value, then we set the actual DUID.
3887  */
3888  else if (token == LLT) {
3889  item = createString(makeString(-1, "LLT"));
3890  mapSet(duid, item, "type");
3891 
3892  if (peek_token(NULL, NULL, cfile) != SEMI) {
3893  /*
3894  * Get our hardware type, timestamp, and address.
3895  */
3896  token = next_token(NULL, NULL, cfile);
3897  switch (token) {
3898  case ETHERNET:
3899  ll_type = HTYPE_ETHER;
3900  break;
3901  case TOKEN_RING:
3902  ll_type = HTYPE_IEEE802;
3903  break;
3904  case TOKEN_FDDI:
3905  ll_type = HTYPE_FDDI;
3906  break;
3907  default:
3908  parse_error(cfile, "hardware type expected");
3909  }
3910  item = createInt(ll_type);
3911  mapSet(duid, item, "htype");
3912 
3913  token = next_token(&val, NULL, cfile);
3914  if (token != NUMBER)
3915  parse_error(cfile, "timestamp expected");
3916  item = createInt(atoi(val));
3917  mapSet(duid, item, "time");
3918 
3919  ll_addr = parse_hexa(cfile);
3920  if (ll_addr == NULL)
3921  parse_error(cfile,
3922  "can't get hardware address");
3923  item = createString(ll_addr);
3924  mapSet(duid, item, "identifier");
3925  }
3926  }
3927 
3928  /*
3929  * If users want they can use a number for DUID types.
3930  * This is useful for supporting future, not-yet-defined
3931  * DUID types.
3932  *
3933  * In this case, they have to put in the complete value.
3934  *
3935  * This also works for existing DUID types of course.
3936  */
3937  else if (token == NUMBER) {
3938  item = createString(makeString(-1, val));
3939  item->skip = ISC_TRUE;
3940  /* Kea wants EN, LL or LLT so skip the whole thing */
3941  duid->skip = ISC_TRUE;
3942  cfile->issue_counter++;
3943  mapSet(duid, item, "type");
3944 
3945  token = next_token(&val, &len, cfile);
3946  if (token != STRING)
3947  parse_error(cfile, "identifier expected");
3948  item = createString(makeString(len, val));
3949  mapSet(duid, item, "identifier");
3950  }
3951 
3952  /*
3953  * Anything else is an error.
3954  */
3955  else
3956  parse_error(cfile, "DUID type of LLT, EN, or LL expected");
3957 
3958  /*
3959  * Finally consume our trailing semicolon.
3960  */
3961  token = next_token(NULL, NULL, cfile);
3962  if (token != SEMI)
3963  parse_error(cfile, "semicolon expected");
3964 
3965  /* server-id is a global parameter */
3966  if (mapContains(cfile->stack[1], "server-id"))
3967  parse_error(cfile, "there is already a server-id");
3968  /* DHCPv6 only but not fatal */
3969  if ((local_family != AF_INET6) && !duid->skip) {
3970  duid->skip = ISC_TRUE;
3971  cfile->issue_counter++;
3972  }
3973  mapSet(cfile->stack[1], duid, "server-id");
3974 }
3975 
3976 /* Check whether the argument is encoded in hexadecimal or not */
3977 static isc_boolean_t
3978 is_hexa_only(const char *s, unsigned l)
3979 {
3980  unsigned i;
3981 
3982  for (i = 0; i < l; i++)
3983  if (!isxdigit((int)s[i]))
3984  return ISC_FALSE;
3985  return ISC_TRUE;
3986 }
3987 
4000 void
4001 parse_directive(struct parse *cfile)
4002 {
4003  enum dhcp_token token;
4004  const char *val;
4006  struct option *option;
4007 
4008  token = peek_token(&val, NULL, cfile);
4009 
4010  switch (token) {
4011  case OPTION:
4012  skip_token(&val, NULL, cfile);
4013  token = peek_token(&val, NULL, cfile);
4014  if (token == SPACE) {
4015  parse_option_space_dir(cfile);
4016  return;
4017  }
4018 
4019  known = ISC_FALSE;
4020  option = parse_option_name(cfile, ISC_TRUE, &known);
4021  token = next_token(&val, NULL, cfile);
4022  if (token == CHECK) {
4023  struct string *datatype;
4024  isc_boolean_t is_array = ISC_FALSE;
4025  isc_boolean_t encapsulate = ISC_FALSE;
4026 
4027  datatype = convert_format(option->format,
4028  &is_array,
4029  &encapsulate);
4030  printf("option ISC DHCP (Kea)\n"
4031  " %s.%s (%s.%s)\n"
4032  " format \"%s\" (type \"%s\" "
4033  "array %s encap %s)\n"
4034  " status %s\n",
4035  option->space->old, option->old,
4036  option->space->name, option->name,
4037  option->format, datatype->content,
4038  is_array ? "true" : "false",
4039  encapsulate ? "true" : "false",
4041  parse_semi(cfile);
4042  return;
4043  }
4044  if (option->space->status == special)
4045  parse_error(cfile, "attempt to modify config %s.%s",
4046  option->space->old, option->name);
4047  if (token == ALIAS) {
4048  token = next_token(&val, NULL, cfile);
4049  if (!is_identifier(token))
4050  parse_error(cfile,
4051  "expecting identifier after "
4052  "alias keyword.");
4053  if (option->status != dynamic)
4054  parse_error(cfile,
4055  "attempt to rename %s.%s to %s",
4056  option->space->name,
4057  option->name, val);
4058  option->name = strdup(val);
4059  parse_semi(cfile);
4060  return;
4061  }
4062  if (token == CODE) {
4063  parse_option_code_dir(cfile, option);
4064  return;
4065  }
4066  if ((token == KNOWN) || (token == UNKNOWN) ||
4067  (token == DYNAMIC)) {
4068  parse_option_status_dir(cfile, option, token);
4069  return;
4070  }
4071  if (token == LOCAL) {
4073  parse_semi(cfile);
4074  return;
4075  }
4076  if (token == DEFINE) {
4078  parse_semi(cfile);
4079  return;
4080  }
4081  parse_error(cfile, "unknown option directive %s", val);
4082 
4083  default:
4084  parse_error(cfile, "unknown directive %s", val);
4085  }
4086 }
4087 
4088 /* Set alias and status for option spaces */
4089 
4090 void
4092 {
4093  enum dhcp_token token;
4094  const char *val;
4095  struct space *space;
4096 
4097  skip_token(NULL, NULL, cfile); /* Discard SPACE */
4098  token = next_token(&val, NULL, cfile);
4099  if (!is_identifier(token))
4100  parse_error(cfile, "expecting identifier.");
4101  space = space_lookup(val);
4102  if (space == NULL)
4103  parse_error(cfile, "can't find space '%s", val);
4104 
4105  token = next_token(&val, NULL, cfile);
4106  if (token == CHECK) {
4107  printf("space ISC DHCP (kea)\n"
4108  " %s (%s)\n status %s\n%s",
4109  space->old, space->name,
4111  space->vendor != NULL ? " vendor\n" : "");
4112  parse_semi(cfile);
4113  return;
4114  }
4115  if (token == ALIAS) {
4116  token = next_token(&val, NULL, cfile);
4117  if (!is_identifier(token))
4118  parse_error(cfile,
4119  "expecting identifier after "
4120  "alias keyword.");
4121  if (space->status != dynamic)
4122  parse_error(cfile,
4123  "attempt to rename %s to %s",
4124  space->name, val);
4125  space->name = strdup(val);
4126  parse_semi(cfile);
4127  return;
4128  }
4129  if (token == DYNAMIC)
4130  space->status = dynamic;
4131  else if (token == UNKNOWN) {
4132  token = next_token(NULL, NULL, cfile);
4133  if (token == KNOWN)
4134  space->status = known;
4135  else if (token == UNKNOWN)
4137  else
4138  parse_error(cfile, "expected KNOW or UNKNOWN");
4139  } else if (token != UNKNOWN)
4140  parse_error(cfile, "expected KNOW or UNKNOWN or DYNAMIC");
4141  else {
4142  if (token == KNOWN)
4144  else if (token == UNKNOWN)
4145  parse_error(cfile, "illicit combination: space "
4146  "%s is known by nobody", space->name);
4147  else
4148  parse_error(cfile, "expected KNOW or UNKNOWN");
4149  }
4150  parse_semi(cfile);
4151 }
4152 
4153 /* Alternative to parse_option_code_decl using the raw ISC DHCP format */
4154 
4155 void
4156 parse_option_code_dir(struct parse *cfile, struct option *option)
4157 {
4158  const char *val;
4159  enum dhcp_token token;
4160  unsigned code;
4161  struct element *def;
4162  struct element *optdef;
4163  struct string *datatype;
4164  isc_boolean_t is_array = ISC_FALSE;
4165  isc_boolean_t encapsulate = ISC_FALSE;
4166 
4167  def = createMap();
4168  mapSet(def,
4170  "space");
4171  mapSet(def, createString(makeString(-1, option->name)), "name");
4172 
4173  /* Parse the option code. */
4174  token = next_token(&val, NULL, cfile);
4175  if (token != NUMBER)
4176  parse_error(cfile, "expecting option code number.");
4177  code = atoi(val);
4178  mapSet(def, createInt(code), "code");
4179 
4180  /* We have the code so we can get the real option now */
4181  if (option->code == 0) {
4182  struct option *from_code;
4183 
4184  option->code = code;
4185  from_code = option_lookup_code(option->space->old, code);
4186  if (from_code != NULL)
4187  option = from_code;
4188  }
4189 
4190  /* Redefinitions are not allowed */
4191  if ((option->status != dynamic) ||
4192  (strcmp(option->format, "u") != 0))
4193  parse_error(cfile, "attempt to redefine %s.%s code %u",
4194  option->space->name, option->name, code);
4195 
4196  token = next_token(&val, NULL, cfile);
4197  if (token != EQUAL)
4198  parse_error(cfile, "expecting \"=\"");
4199  token = next_token(&val, NULL, cfile);
4200  if (token != STRING)
4201  parse_error(cfile, "expecting format string");
4202  option->format = strdup(val);
4203  parse_semi(cfile);
4204 
4205  datatype = convert_format(val, &is_array, &encapsulate);
4206 
4207  if ((datatype == NULL) && (strchr(datatype->content, '?') != NULL))
4208  parse_error(cfile, "failed to convert format \"%s\" for "
4209  "option %s.%s code %u",
4210  val, option->space->name, option->name, code);
4211  /* todo */
4212  if (encapsulate)
4213  parse_error(cfile, "option %s.%s code %u encapsulate?",
4214  option->space->name, option->name, code);
4215 
4216  if (strchr(datatype->content, ',') == NULL)
4217  mapSet(def, createString(datatype), "type");
4218  else {
4219  mapSet(def, createString(datatype), "record-types");
4220  mapSet(def, createString(makeString(-1, "record")), "type");
4221  }
4222  if (is_array)
4223  mapSet(def, createBool(ISC_TRUE), "array");
4224 
4225  optdef = mapGet(cfile->stack[1], "option-def");
4226  if (optdef == NULL) {
4227  optdef = createList();
4228  mapSet(cfile->stack[1], optdef, "option-def");
4229  }
4230  listPush(optdef, def);
4231 }
4232 
4233 /* Update the option status for instance to add standard options */
4234 
4235 void
4237  enum dhcp_token token)
4238 {
4239  if (token == DYNAMIC)
4240  option->status = dynamic;
4241  else if (token == KNOWN) {
4242  token = next_token(NULL, NULL, cfile);
4243  if (token == KNOWN)
4244  option->status = known;
4245  else if (token == UNKNOWN)
4247  else
4248  parse_error(cfile, "expected KNOW or UNKNOWN");
4249  } else if (token != UNKNOWN)
4250  parse_error(cfile, "expected KNOW or UNKNOWN or DYNAMIC");
4251  else {
4252  if (token == KNOWN)
4254  else if (token == UNKNOWN)
4255  parse_error(cfile, "illicit combination: option "
4256  "%s.%s code %u is known by nobody",
4257  option->space->name, option->name,
4258  option->code);
4259  else
4260  parse_error(cfile, "expected KNOW or UNKNOWN");
4261  }
4262  parse_semi(cfile);
4263 }
4264 
4265 /* Make the option definition not exported to Kea */
4266 
4267 void
4269 {
4270  struct element *optdef;
4271  struct element *def;
4272  struct element *elem;
4273  size_t i;
4274 
4275  def = NULL;
4276  if (option->code == 0)
4277  parse_error(cfile, "unknown code for option %s.%s",
4278  option->space->name, option->name);
4279 
4280  optdef = mapGet(cfile->stack[1], "option-def");
4281  if (optdef == NULL) {
4282  optdef = createList();
4283  mapSet(cfile->stack[1], optdef, "option-def");
4284  goto not_found;
4285  }
4286  for (i = 0; i < listSize(optdef); i++) {
4287  def = listGet(optdef, i);
4288  elem = mapGet(def, "space");
4289  if ((elem == NULL) || (elem->type != ELEMENT_STRING))
4290  parse_error(cfile, "got an option definition "
4291  "without space at %u", (unsigned)i);
4292  if (strcmp(option->space->name,
4293  stringValue(elem)->content) != 0)
4294  continue;
4295  elem = mapGet(def, "code");
4296  if ((elem == NULL) || (elem->type != ELEMENT_INTEGER))
4297  parse_error(cfile, "got an option definition "
4298  "without code at %u", (unsigned)i);
4299  if (intValue(elem) == option->code)
4300  break;
4301  }
4302  if (def == NULL)
4303  goto not_found;
4304  def->skip = ISC_TRUE;
4305  mapSet(def, createNull(), "no-export");
4306  return;
4307 
4308 not_found:
4309  parse_error(cfile, "can't find option %s.%s code %u in definitions",
4311 }
4312 
4313 /* Make the opposite: force the definition */
4314 
4315 void
4317 {
4318  struct element *optdef;
4319  struct element *def;
4320  struct element *elem;
4321  struct string *datatype;
4322  isc_boolean_t is_array = ISC_FALSE;
4323  isc_boolean_t encapsulate = ISC_FALSE;
4324  size_t i;
4325 
4326  def = NULL;
4327  if (option->code == 0)
4328  parse_error(cfile, "unknown code for option %s.%s",
4329  option->space->name, option->name);
4330 
4331  optdef = mapGet(cfile->stack[1], "option-def");
4332  if (optdef == NULL) {
4333  optdef = createList();
4334  mapSet(cfile->stack[1], optdef, "option-def");
4335  goto no_search;
4336  }
4337  for (i = 0; i < listSize(optdef); i++) {
4338  def = listGet(optdef, i);
4339  elem = mapGet(def, "space");
4340  if ((elem == NULL) || (elem->type != ELEMENT_STRING))
4341  parse_error(cfile, "got an option definition "
4342  "without space at %u", (unsigned)i);
4343  if (strcmp(option->space->name,
4344  stringValue(elem)->content) != 0)
4345  continue;
4346  elem = mapGet(def, "code");
4347  if ((elem == NULL) || (elem->type != ELEMENT_INTEGER))
4348  parse_error(cfile, "got an option definition "
4349  "without code at %u", (unsigned)i);
4350  if (intValue(elem) == option->code)
4351  parse_error(cfile, "unexpected definition for "
4352  "option %s.%s code %u",
4353  option->space->name, option->name,
4354  option->code);
4355  }
4356 no_search:
4357  def = createMap();
4358  mapSet(def,
4360  "space");
4361  mapSet(def, createString(makeString(-1, option->name)), "name");
4362  mapSet(def, createInt(option->code), "code");
4363 
4364  datatype = convert_format(option->format, &is_array, &encapsulate);
4365 
4366  if ((datatype == NULL) && (strchr(datatype->content, '?') != NULL))
4367  parse_error(cfile, "failed to convert format \"%s\" for "
4368  "option %s.%s code %u",
4370  option->name, option->code);
4371  /* todo */
4372  if (encapsulate)
4373  parse_error(cfile, "option %s.%s code %u encapsulate?",
4375 
4376  if (strchr(datatype->content, ',') == NULL)
4377  mapSet(def, createString(datatype), "type");
4378  else {
4379  mapSet(def, createString(datatype), "record-types");
4380  mapSet(def, createString(makeString(-1, "record")), "type");
4381  }
4382  if (is_array)
4383  mapSet(def, createBool(ISC_TRUE), "array");
4384 
4385  listPush(optdef, def);
4386 
4387  return;
4388 }
4389 
4390 /*
4391  * Push new interface on the interface list when it is not already.
4392  */
4393 
4394 static void
4395 new_network_interface(struct parse *cfile, struct element *iface)
4396 {
4397  struct element *ifconf;
4398  struct element *iflist;
4399  struct string *name = stringValue(iface);
4400  int i;
4401 
4402  ifconf = mapGet(cfile->stack[1], "interfaces-config");
4403  if (ifconf == NULL) {
4404  ifconf = createMap();
4405  mapSet(cfile->stack[1], ifconf, "interfaces-config");
4406  }
4407 
4408  iflist = mapGet(ifconf, "interfaces");
4409  if (iflist == NULL) {
4410  iflist = createList();
4411  mapSet(ifconf, iflist, "interfaces");
4412  }
4413 
4414  for (i = 0; i < listSize(iflist); i++) {
4415  struct element *item;
4416 
4417  item = listGet(iflist, i);
4418  if ((item != NULL) &&
4419  (item->type == ELEMENT_STRING) &&
4420  eqString(stringValue(item), name))
4421  return;
4422  }
4423 
4424  listPush(iflist, createString(name));
4425 }
4426 
4427 /* Convert address and mask in binary into address/len text */
4428 
4429 static const uint32_t bitmasks[32 + 1] = {
4430  0xffffffff, 0x7fffffff, 0x3fffffff, 0x1fffffff,
4431  0x0fffffff, 0x07ffffff, 0x03ffffff, 0x01ffffff,
4432  0x00ffffff, 0x007fffff, 0x003fffff, 0x001fffff,
4433  0x000fffff, 0x0007ffff, 0x0003ffff, 0x0001ffff,
4434  0x0000ffff, 0x00007fff, 0x00003fff, 0x00001fff,
4435  0x00000fff, 0x000007ff, 0x000003ff, 0x000001ff,
4436  0x000000ff, 0x0000007f, 0x0000003f, 0x0000001f,
4437  0x0000000f, 0x00000007, 0x00000003, 0x00000001,
4438  0x00000000 };
4439 
4440 static struct string *
4441 addrmask(const struct string *address, const struct string *netmask)
4442 {
4443  struct string *result;
4444  uint8_t plen;
4445  uint32_t mask;
4446 
4447  result = makeStringExt(address->length, address->content, 'I');
4448 
4449  memcpy(&mask, netmask->content, 4);
4450  mask = ntohl(mask);
4451  for (plen = 0; plen <= 32; ++plen)
4452  if (~mask == bitmasks[plen])
4453  break;
4454  if (plen > 32)
4455  return NULL;
4456 
4457  appendString(result, "/");
4458  concatString(result, makeStringExt(1, (char *)&plen, 'B'));
4459  return result;
4460 }
4461 
4462 /*
4463  * find a place where to put a reservation
4464  * (reservations aka hosts must be in a subnet in Kea < 1.5)
4465  * (defaulting to the last defined subnet (e.g. for reservations
4466  * without any address).
4467  * (first step is to find an enclosing group).
4468  */
4469 
4470 static struct element *
4471 find_match(struct parse *cfile, struct element *host,
4472  isc_boolean_t *used_heuristicp)
4473 {
4474  struct element *address;
4475  struct subnet *subnet;
4476  char addr[16];
4477  size_t group;
4478  size_t i, len;
4479  int kind;
4480 
4481  if (global_hr) {
4482  struct element *hosts;
4483 
4484  hosts = mapGet(cfile->stack[1], "reservations");
4485  if (!hosts) {
4486  mapSet(cfile->stack[1],
4487  createString(makeString(-1, "global")),
4488  "reservation-mode");
4489  hosts = createList();
4490  mapSet(cfile->stack[1], hosts, "reservations");
4491  }
4492  *used_heuristicp = ISC_FALSE;
4493  return cfile->stack[1];
4494  }
4495 
4496  for (group = cfile->stack_top; group > 0; --group) {
4497  kind = cfile->stack[group]->kind;
4498  if ((kind == GROUP_DECL) || (kind == ROOT_GROUP))
4499  break;
4500  }
4501  if (!group)
4502  parse_error(cfile, "can't find root group");
4503  if (kind == GROUP_DECL)
4504  return cfile->stack[group];
4505 
4506  if (local_family == AF_INET) {
4507  address = mapGet(host, "ip-address");
4508  if (address == NULL) {
4509  if (TAILQ_EMPTY(&known_subnets))
4510  return cfile->stack[1];
4511  if (used_heuristicp)
4512  *used_heuristicp = ISC_TRUE;
4513  return TAILQ_LAST(&known_subnets, subnets)->subnet;
4514  }
4515  len = 4;
4516  } else {
4517  address = mapGet(host, "ip-addresses");
4518  if (address == NULL) {
4519  if (TAILQ_EMPTY(&known_subnets))
4520  return cfile->stack[1];
4521  if (used_heuristicp)
4522  *used_heuristicp = ISC_TRUE;
4523  return TAILQ_LAST(&known_subnets, subnets)->subnet;
4524  }
4525  address = listGet(address, 0);
4526  if (address == NULL)
4527  return TAILQ_LAST(&known_subnets, subnets)->subnet;
4528  len = 16;
4529  }
4530 
4531  if (inet_pton(local_family, stringValue(address)->content, addr) != 1)
4532  parse_error(cfile, "bad address %s",
4533  stringValue(address)->content);
4534  TAILQ_FOREACH(subnet, &known_subnets) {
4535  isc_boolean_t matching = ISC_TRUE;
4536 
4537  if (subnet->mask->length != len)
4538  continue;
4539  for (i = 0; i < len; i++)
4540  if ((addr[i] & subnet->mask->content[i]) !=
4541  subnet->addr->content[i]) {
4542  matching = ISC_FALSE;
4543  break;
4544  }
4545  if (matching)
4546  return subnet->subnet;
4547  }
4548  return cfile->stack[1];
4549 }
4550 
4551 /*
4552  * find a subnet where to put a pool
4553  * (pools are not allowed at shared-network level in Kea)
4554  */
4555 
4556 static struct element *
4557 find_location(struct element *share, struct range *range)
4558 {
4559  struct subnet *subnet;
4560  size_t i;
4561 
4562  TAILQ_FOREACH(subnet, &known_subnets) {
4563  isc_boolean_t matching = ISC_TRUE;
4564 
4565  if (subnet->share != share)
4566  continue;
4567  if (subnet->mask->length != range->low->length)
4568  continue;
4569  for (i = 0; i < range->low->length; i++)
4570  if ((range->low->content[i] &
4571  subnet->mask->content[i]) !=
4572  subnet->addr->content[i]) {
4573  matching = ISC_FALSE;
4574  break;
4575  }
4576  if (matching)
4577  return subnet->subnet;
4578  }
4579  return NULL;
4580 }
4581 
4582 /*
4583  * Compute a prefix length from lower - higher IPv6 addresses.
4584  */
4585 
4586 static const uint8_t bytemasks[8] = {
4587  0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff
4588 };
4589 
4590 static int
4591 get_prefix_length(const char *low, const char *high)
4592 {
4593  uint8_t lo[16];
4594  uint8_t hi[16];
4595  uint8_t xor[16];
4596  int i, plen;
4597 
4598  if ((inet_pton(AF_INET6, low, lo) != 1) ||
4599  (inet_pton(AF_INET6, high, hi) != 1))
4600  return -100;
4601 
4602  for (i = 0; i < 16; i++)
4603  xor[i] = lo[i] ^ hi[i];
4604  for (plen = 0; plen < 128; plen += 8)
4605  if (xor[plen / 8] != 0)
4606  break;
4607  if (plen == 128)
4608  return plen;
4609  for (i = (plen / 8) + 1; i < 16; i++)
4610  if (xor[i] != 0)
4611  return -2;
4612  for (i = 0; i < 8; i++) {
4613  uint8_t msk = ~xor[plen / 8];
4614 
4615  if (msk == bytemasks[i])
4616  return plen + i + 1;
4617  }
4618  return -1;
4619 }
4620 
4621 /*
4622  * Get a (global) class from its reference, i.e.:
4623  * - name for a (super)class
4624  * - super, and binary or string for a subclass
4625  */
4626 static struct element *
4627 get_class(struct parse *cfile, struct element *ref)
4628 {
4629  struct element *classes;
4630  struct element *class;
4631  struct element *name;
4632  struct element *selector;
4633  struct element *param;
4634  size_t i;
4635 
4636  classes = mapGet(cfile->stack[1], "client-classes");
4637  if ((classes == NULL) || (listSize(classes) == 0))
4638  return NULL;
4639 
4640  name = mapGet(ref, "super");
4641  if (name == NULL) {
4642  name = mapGet(ref, "name");
4643  if (name == NULL)
4644  return NULL;
4645  for (i = 0; i < listSize(classes); i++) {
4646  class = listGet(classes, i);
4647  if (mapContains(ref, "super"))
4648  continue;
4649  param = mapGet(class, "name");
4650  if (param == NULL)
4651  continue;
4652  if (eqString(stringValue(name), stringValue(param)))
4653  return class;
4654  }
4655  return NULL;
4656  }
4657  selector = mapGet(ref, "string");
4658  if (selector == NULL) {
4659  selector = mapGet(ref, "binary");
4660  if (selector == NULL)
4661  return NULL;
4662  for (i = 0; i <listSize(classes); i++) {
4663  class = listGet(classes, i);
4664  param = mapGet(class, "super");
4665  if (param == NULL)
4666  continue;
4667  if (!eqString(stringValue(name), stringValue(param)))
4668  continue;
4669  param = mapGet(class, "binary");
4670  if (param == NULL)
4671  continue;
4672  if (eqString(stringValue(selector),
4673  stringValue(param)))
4674  return class;
4675  }
4676  return NULL;
4677  }
4678  for (i = 0; i <listSize(classes); i++) {
4679  class = listGet(classes, i);
4680  param = mapGet(class, "super");
4681  if (param == NULL)
4682  continue;
4683  if (!eqString(stringValue(name), stringValue(param)))
4684  continue;
4685  param = mapGet(class, "string");
4686  if (param == NULL)
4687  continue;
4688  if (eqString(stringValue(selector), stringValue(param)))
4689  return class;
4690  }
4691  return NULL;
4692 }
4693 
4694 /*
4695  * Concatenate two class reference lists eliminating duplicates
4696  * (complexity is bad: if this becomes a performance pig, use a hash table)
4697  */
4698 
4699 static void
4700 concat_classes(struct parse *cfile, struct element *dst, struct element *src)
4701 {
4702  struct element *class;
4703  struct element *sitem;
4704  struct element *ditem;
4705  size_t i;
4706  isc_boolean_t dup;
4707 
4708  while (listSize(src) > 0) {
4709  sitem = listGet(src, 0);
4710  listRemove(src, 0);
4711  class = get_class(cfile, sitem);
4712  if (class == NULL)
4713  /* just ignore */
4714  continue;
4715  dup = ISC_FALSE;
4716  for (i = 0; i < listSize(dst); i++) {
4717  ditem = listGet(dst, i);
4718  if (class == get_class(cfile, ditem)) {
4719  dup = ISC_TRUE;
4720  break;
4721  }
4722  }
4723  if (dup)
4724  continue;
4725  listPush(dst, sitem);
4726  }
4727 }
4728 
4729 /* Generate a class from allow/deny member lists */
4730 
4731 static void
4732 generate_class(struct parse *cfile, struct element *pool,
4733  struct element *allow, struct element *deny)
4734 {
4735  struct element *classes;
4736  struct element *class;
4737  struct element *elem;
4738  struct element *prop;
4739  struct element *depend;
4740  struct element *result = NULL;
4741  struct string *name;
4742  struct string *expr;
4743  struct string *msg;
4744  struct comments comments;
4745  struct comment *comment;
4746  isc_boolean_t rescan;
4747  size_t i;
4748 
4749  if ((listSize(allow) == 0) && (listSize(deny) == 0))
4750  return;
4751 
4752  classes = mapGet(cfile->stack[1], "generated-classes");
4753  if (classes == NULL) {
4754  classes = createList();
4755  mapSet(cfile->stack[1], classes, "generated-classes");
4756  }
4757 
4758  /* Create comments */
4759  TAILQ_INIT(&comments);
4760  comment = createComment("/// From:");
4761  TAILQ_INSERT_TAIL(&comments, comment);
4762  for (i = 0; i < listSize(allow); i++) {
4763  struct element *alias;
4764 
4765  elem = listGet(allow, i);
4766  assert(elem != NULL);
4767  prop = mapGet(elem, "class");
4768  assert(prop != NULL);
4769  assert(prop->type == ELEMENT_STRING);
4770  alias = mapGet(elem, "real");
4771  msg = makeString(-1, "/// allow ");
4772  concatString(msg, stringValue(alias ? alias : prop));
4773  comment = createComment(msg->content);
4775  }
4776  for (i = 0; i < listSize(deny); i++) {
4777  struct element *alias;
4778 
4779  elem = listGet(deny, i);
4780  assert(elem != NULL);
4781  prop = mapGet(elem, "class");
4782  assert(prop != NULL);
4783  assert(prop->type == ELEMENT_STRING);
4784  alias = mapGet(elem, "real");
4785  msg = makeString(-1, "/// deny ");
4786  concatString(msg, stringValue(alias ? alias : prop));
4787  comment = createComment(msg->content);
4789  }
4790  TAILQ_CONCAT(&comments, &allow->comments);
4791  TAILQ_CONCAT(&comments, &deny->comments);
4792 
4793  /* Deal with special cases */
4794  for (;;) {
4795  rescan = ISC_FALSE;
4796  for (i = 0; i < listSize(allow); i++) {
4797  elem = listGet(allow, i);
4798  assert(elem != NULL);
4799  prop = mapGet(elem, "way");
4800  assert(prop != NULL);
4801  assert(prop->type == ELEMENT_BOOLEAN);
4802  class = mapGet(elem, "class");
4803  assert(class != NULL);
4804  assert(class->type == ELEMENT_STRING);
4805  /* allow !ALL and other */
4806  if (eqString(stringValue(class), CLASS_ALL) &&
4807  !boolValue(prop) && (listSize(allow) > 1)) {
4808  listRemove(allow, i);
4809  rescan = ISC_TRUE;
4810  break;
4811  }
4812  /* allow ALL alone */
4813  if (eqString(stringValue(class), CLASS_ALL) &&
4814  boolValue(prop) && (listSize(allow) == 1)) {
4815  resetList(allow);
4816  rescan = ISC_TRUE;
4817  break;
4818  }
4819  }
4820  if (!rescan)
4821  break;
4822  }
4823 
4824  for (;;) {
4825  rescan = ISC_FALSE;
4826  for (i = 0; i < listSize(deny); i++) {
4827  elem = listGet(deny, i);
4828  assert(elem != NULL);
4829  prop = mapGet(elem, "way");
4830  assert(prop != NULL);
4831  assert(prop->type == ELEMENT_BOOLEAN);
4832  class = mapGet(elem, "class");
4833  assert(class != NULL);
4834  assert(class->type == ELEMENT_STRING);
4835  /* DENY !ALL */
4836  if (eqString(stringValue(class), CLASS_ALL) &&
4837  !boolValue(prop)) {
4838  listRemove(deny, i);
4839  rescan = ISC_TRUE;
4840  break;
4841  }
4842  /* DENY ALL */
4843  if (eqString(stringValue(class), CLASS_ALL) &&
4844  boolValue(prop)) {
4845  resetList(allow);
4846  if (listSize(deny) > 1) {
4847  listRemove(deny, i);
4848  resetList(deny);
4849  listPush(deny, elem);
4850  }
4851  break;
4852  }
4853  }
4854  if (!rescan)
4855  break;
4856  }
4857 
4858  /* Fully cleaned? */
4859  if ((listSize(allow) == 0) && (listSize(deny) == 0)) {
4860  if (result != NULL)
4861  TAILQ_CONCAT(&result->comments, &comments);
4862  else
4863  TAILQ_CONCAT(&pool->comments, &comments);
4864  return;
4865  }
4866 
4867  /* Unique allow member short cut */
4868  if ((listSize(allow) == 1) && (listSize(deny) == 0) &&
4869  !allow->skip && !deny->skip) {
4870  elem = listGet(allow, 0);
4871  assert(elem != NULL);
4872  prop = mapGet(elem, "way");
4873  assert(prop != NULL);
4874  assert(prop->type == ELEMENT_BOOLEAN);
4875  class = mapGet(elem, "class");
4876  assert(class != NULL);
4877  assert(class->type == ELEMENT_STRING);
4878  if (boolValue(prop)) {
4879  result = createString(stringValue(class));
4880  TAILQ_CONCAT(&result->comments, &comments);
4881  mapSet(pool, result, "client-class");
4882  return;
4883  }
4884  }
4885 
4886  /* Build name */
4887  name = makeString(-1, "gen#");
4888  for (i = 0; i < listSize(allow); i++) {
4889  elem = listGet(allow, i);
4890  assert(elem != NULL);
4891  prop = mapGet(elem, "way");
4892  assert(prop != NULL);
4893  assert(prop->type == ELEMENT_BOOLEAN);
4894  if (!boolValue(prop))
4895  appendString(name, "!");
4896  prop = mapGet(elem, "class");
4897  assert(prop != NULL);
4898  assert(prop->type == ELEMENT_STRING);
4899  concatString(name, stringValue(prop));
4900  appendString(name, "#");
4901  }
4902  if (listSize(deny) > 0) {
4903  appendString(name, "_AND_#");
4904  for (i = 0; i < listSize(deny); i++) {
4905  elem = listGet(deny, i);
4906  assert(elem != NULL);
4907  prop = mapGet(elem, "way");
4908  assert(prop != NULL);
4909  assert(prop->type == ELEMENT_BOOLEAN);
4910  if (boolValue(prop))
4911  appendString(name, "!");
4912  prop = mapGet(elem, "class");
4913  assert(prop != NULL);
4914  assert(prop->type == ELEMENT_STRING);
4915  concatString(name, stringValue(prop));
4916  appendString(name, "#");
4917  }
4918  }
4919 
4920  /* Check if it already exists */
4921  for (i = 0; i < listSize(classes); i++) {
4922  struct element *descr;
4923 
4924  class = listGet(classes, i);
4925  assert(class != NULL);
4926  descr = mapGet(class, "name");
4927  assert(descr != NULL);
4928  assert(descr->type == ELEMENT_STRING);
4929  if (!eqString(name, stringValue(descr)))
4930  continue;
4931  result = createString(name);
4932  TAILQ_CONCAT(&result->comments, &comments);
4933  mapSet(pool, result, "client-class");
4934  return;
4935  }
4936 
4937  /* Create expression */
4938  class = createMap();
4939  depend = createList();
4940  expr = allocString();
4941 
4942  if ((listSize(allow) > 0) && (listSize(deny) > 0))
4943  appendString(expr, "(");
4944 
4945  for (i = 0; i < listSize(allow); i++) {
4946  isc_boolean_t negative;
4947 
4948  if (i > 0)
4949  appendString(expr, " or ");
4950  elem = listGet(allow, i);
4951  prop = mapGet(elem, "way");
4952  negative = !boolValue(prop);
4953  prop = mapGet(elem, "class");
4954  if (negative)
4955  appendString(expr, "not ");
4956  appendString(expr, "member('");
4957  concatString(expr, stringValue(prop));
4958  appendString(expr, "')");
4959  listPush(depend, createString(stringValue(prop)));
4960  }
4961 
4962  if ((listSize(allow) > 0) && (listSize(deny) > 0))
4963  appendString(expr, ") and ");
4964 
4965  for (i = 0; i < listSize(deny); i++) {
4966  isc_boolean_t negative;
4967 
4968  if (i > 0)
4969  appendString(expr, " and ");
4970  elem = listGet(deny, i);
4971  prop = mapGet(elem, "way");
4972  negative = boolValue(prop);
4973  prop = mapGet(elem, "class");
4974  if (negative)
4975  appendString(expr, "not ");
4976  appendString(expr, "member('");
4977  concatString(expr, stringValue(prop));
4978  appendString(expr, "')");
4979  listPush(depend, createString(stringValue(prop)));
4980  }
4981 
4982  mapSet(class, createString(name), "name");
4983  mapSet(class, createString(expr), "test");
4984  mapSet(class, depend, "depend");
4985  /* inherit untranslatable cases */
4986  class->skip |= allow->skip || deny->skip;
4987  listPush(classes, class);
4988 
4989  result = createString(name);
4990  TAILQ_CONCAT(&result->comments, &comments);
4991  mapSet(pool, result, "client-class");
4992 }
enum dhcp_token next_raw_token(const char **rval, unsigned *rlen, struct parse *cfile)
Definition: conflex.c:380
enum dhcp_token peek_raw_token(const char **rval, unsigned *rlen, struct parse *cfile)
Definition: conflex.c:454
enum dhcp_token peek_token(const char **rval, unsigned *rlen, struct parse *cfile)
Definition: conflex.c:443
enum dhcp_token next_token(const char **rval, unsigned *rlen, struct parse *cfile)
Definition: conflex.c:369
isc_result_t end_parse(struct parse **cfile)
Definition: conflex.c:103
isc_result_t new_parse(struct parse **cfile, int file, char *inbuf, unsigned buflen, const char *name, int eolp)
Definition: conflex.c:41
char * parse_host_name(struct parse *cfile)
Definition: parse.c:196
int parse_boolean_expression(struct expression **expr, struct parse *cfile, int *lose)
Definition: parse.c:3475
void skip_to_semi(struct parse *cfile)
Definition: parse.c:81
int parse_option_statement(struct executable_statement **result, struct parse *cfile, int lookups, struct option *option, enum statement_op op)
Definition: parse.c:4919
int parse_semi(struct parse *cfile)
Definition: parse.c:139
int parse_option_code_definition(struct parse *cfile, struct option *option)
Definition: parse.c:1572
int parse_ip6_addr(struct parse *cfile, struct iaddr *addr)
Definition: parse.c:405
int parse_data_expression(struct expression **expr, struct parse *cfile, int *lose)
Definition: parse.c:3542
unsigned char * parse_numeric_aggregate(struct parse *cfile, unsigned char *buf, unsigned *max, int separator, int base, unsigned size)
Definition: parse.c:734
int parse_ip_addr_or_hostname(struct expression **expr, struct parse *cfile, int uniform)
Definition: parse.c:268
void parse_hardware_param(struct parse *cfile, struct hardware *hardware)
Definition: parse.c:615
void parse_option_space_decl(struct parse *cfile)
Definition: parse.c:1349
int parse_executable_statement(struct executable_statement **result, struct parse *cfile, int *lose, enum expression_context case_context)
Definition: parse.c:2133
isc_result_t parse_option_name(struct parse *cfile, int allocate, int *known, struct option **opt)
Definition: parse.c:1208
void parse_option_status_dir(struct parse *cfile, struct option *option, enum dhcp_token token)
Definition: confparse.c:4236
void parse_address_range(struct parse *cfile, int type, size_t where)
Definition: confparse.c:3097
void parse_pool6_statement(struct parse *cfile, int type)
Parse a pool6 statement.
Definition: confparse.c:3472
void parse_server_duid_conf(struct parse *cfile)
Definition: confparse.c:3790
void parse_prefix6(struct parse *cfile, int type, size_t where)
Definition: confparse.c:3333
void get_permit(struct parse *cfile, struct element *permit_head)
Parse allow and deny statements.
Definition: confparse.c:1004
void parse_class_declaration(struct parse *cfile, int type)
Definition: confparse.c:1584
void parse_address_range6(struct parse *cfile, int type, size_t where)
Definition: confparse.c:3212
void parse_subnet_declaration(struct parse *cfile)
Definition: confparse.c:2337
isc_boolean_t failover_once
Definition: confparse.c:38
struct element * parse_allow_deny(struct parse *cfile, int flag)
Definition: confparse.c:3703
void close_group(struct parse *cfile, struct element *group)
Definition: confparse.c:2589
void parse_option_code_dir(struct parse *cfile, struct option *option)
Definition: confparse.c:4156
isc_boolean_t use_flex_id
Definition: confparse.c:42
struct element * parse_fixed_addr_param(struct parse *cfile, enum dhcp_token type)
Definition: confparse.c:3008
void read_conf_file(struct parse *parent, const char *filename, int group_type)
Definition: confparse.c:480
void parse_option_space_dir(struct parse *cfile)
Definition: confparse.c:4091
isc_boolean_t parse_statement(struct parse *cfile, int type, isc_boolean_t declaration)
Definition: confparse.c:584
void parse_option_local_dir(struct parse *cfile, struct option *option)
Definition: confparse.c:4268
void parse_subnet6_declaration(struct parse *cfile)
Definition: confparse.c:2429
TAILQ_HEAD(subnets, subnet)
Definition: confparse.c:64
void parse_host_declaration(struct parse *cfile)
Definition: confparse.c:1282
void parse_lbrace(struct parse *cfile)
Definition: confparse.c:1269
void parse_pool_statement(struct parse *cfile, int type)
Parse a pool statement.
Definition: confparse.c:1142
unsigned subclass_counter
Definition: confparse.c:53
unsigned subnet_counter
Definition: confparse.c:50
void parse_option_define_dir(struct parse *cfile, struct option *option)
Definition: confparse.c:4316
const struct option * host_id_option
Definition: confparse.c:46
isc_boolean_t use_hw_address
Definition: confparse.c:43
isc_boolean_t use_client_id
Definition: confparse.c:41
void parse_fixed_prefix6(struct parse *cfile, size_t host_decl)
Definition: confparse.c:3409
void parse_shared_net_declaration(struct parse *cfile)
Definition: confparse.c:2105
void parse_directive(struct parse *cfile)
Parse (and execute) a directive (extension)
Definition: confparse.c:4001
int host_id_relays
Definition: confparse.c:47
void parse_group_declaration(struct parse *cfile)
Definition: confparse.c:2520
size_t conf_file_subparse(struct parse *cfile, int type)
Definition: confparse.c:535
void concatString(struct string *s, const struct string *a)
Definition: data.c:330
void listPush(struct element *l, struct element *e)
Definition: data.c:697
struct element * createHexa(struct string *h)
Definition: data.c:1249
isc_boolean_t eqString(const struct string *s, const struct string *o)
Definition: data.c:343
struct comment * createComment(const char *line)
Definition: data.c:367
struct string * makeString(int l, const char *s)
Definition: data.c:44
isc_boolean_t boolValue(const struct element *e)
Definition: data.c:399
void listRemove(struct element *l, int i)
Definition: data.c:707
struct element * createBool(isc_boolean_t b)
Definition: data.c:469
struct element * mapGet(struct element *m, const char *k)
Definition: data.c:759
size_t mapSize(const struct element *m)
Definition: data.c:829
struct element * createList(void)
Definition: data.c:504
struct string * stringValue(struct element *e)
Definition: data.c:408
void appendString(struct string *s, const char *a)
Definition: data.c:311
struct element * createInt(int64_t i)
Definition: data.c:445
void resetList(struct element *e)
Definition: data.c:585
void concat(struct element *l, struct element *o)
Definition: data.c:748
struct element * createNull(void)
Definition: data.c:481
isc_boolean_t mapContains(const struct element *m, const char *k)
Definition: data.c:811
struct handle * mapPop(struct element *m)
Definition: data.c:1186
struct string * allocString(void)
Definition: data.c:32
struct element * createString(const struct string *s)
Definition: data.c:492
struct element * copy(struct element *e)
Definition: data.c:1115
void derive(struct handle *src, struct handle *dst)
Definition: data.c:1212
struct element * listGet(struct element *l, int i)
Definition: data.c:646
struct string * makeStringExt(int l, const char *s, char fmt)
Definition: data.c:64
void merge(struct element *m, struct element *o)
Definition: data.c:847
void mapSet(struct element *m, struct element *e, const char *k)
Definition: data.c:777
size_t listSize(const struct element *l)
Definition: data.c:730
struct element * createMap(void)
Definition: data.c:516
void mapRemove(struct element *m, const char *k)
Definition: data.c:792
int64_t intValue(const struct element *e)
Definition: data.c:383
#define ELEMENT_INTEGER
Definition: data.h:162
#define TAILQ_FOREACH_SAFE(var, head, tvar)
Definition: data.h:67
#define ELEMENT_LIST
Definition: data.h:167
#define TAILQ_INSERT_TAIL(head, elm)
Definition: data.h:105
#define TAILQ_FOREACH(var, head)
Definition: data.h:62
#define TAILQ_INIT(head)
Definition: data.h:72
#define TAILQ_CONCAT(head1, head2)
Definition: data.h:49
isc_boolean_t
Definition: data.h:150
#define ELEMENT_STRING
Definition: data.h:166
#define TAILQ_REMOVE(head, elm)
Definition: data.h:120
#define ELEMENT_BOOLEAN
Definition: data.h:164
#define ISC_TRUE
Definition: data.h:153
#define TAILQ_EMPTY(head)
Definition: data.h:58
#define TAILQ_LAST(head, headname)
Definition: data.h:112
#define ISC_FALSE
Definition: data.h:152
#define ELEMENT_MAP
Definition: data.h:168
#define TAILQ_ENTRY(type)
Definition: data.h:40
void dhcp(struct packet *packet)
Definition: dhclient.c:2289
#define MAX_V6RELAY_HOPS
Definition: dhcp6.h:246
#define HTYPE_IEEE802
Definition: dhcp.h:76
#define HTYPE_FDDI
Definition: dhcp.h:77
#define HTYPE_ETHER
Definition: dhcp.h:75
#define DHO_DHCP_SERVER_IDENTIFIER
Definition: dhcp.h:143
#define skip_token(a, b, c)
Definition: dhcpd.h:2192
#define POOL_DECL
Definition: dhcpd.h:692
#define CLASS_DECL
Definition: dhcpd.h:690
struct subnet * subnets
Definition: mdb.c:32
struct ipv6_pool ** pools
#define CLASS_TYPE_SUBCLASS
Definition: dhcpd.h:1099
#define CLASS_TYPE_CLASS
Definition: dhcpd.h:1098
#define GROUP_DECL
Definition: dhcpd.h:691
#define SUBNET_DECL
Definition: dhcpd.h:689
#define SHARED_NET_DECL
Definition: dhcpd.h:688
const char * file
Definition: dhcpd.h:3802
#define ROOT_GROUP
Definition: dhcpd.h:686
#define HOST_DECL
Definition: dhcpd.h:687
int local_family
Definition: discover.c:59
struct element * eval_boolean_expression(struct element *expr, isc_boolean_t *modifiedp)
Definition: eval.c:72
dhcp_token
Definition: dhctoken.h:34
@ ALLOW
Definition: dhctoken.h:114
@ SUBNET
Definition: dhctoken.h:82
@ FAILOVER
Definition: dhctoken.h:167
@ LBRACE
Definition: dhctoken.h:40
@ EN
Definition: dhctoken.h:349
@ TOKEN_NOT
Definition: dhctoken.h:183
@ LEASEQUERY
Definition: dhctoken.h:331
@ MATCH
Definition: dhctoken.h:149
@ LL
Definition: dhctoken.h:350
@ NETMASK
Definition: dhctoken.h:83
@ NUMBER
Definition: dhctoken.h:67
@ FIXED_ADDR
Definition: dhctoken.h:63
@ ALIAS
Definition: dhctoken.h:120
@ HOST
Definition: dhctoken.h:59
@ KNOWN_CLIENTS
Definition: dhctoken.h:315
@ TOKEN_RING
Definition: dhctoken.h:96
@ IF
Definition: dhctoken.h:143
@ LEASE
Definition: dhctoken.h:75
@ FIXED_PREFIX6
Definition: dhctoken.h:358
@ DECLINES
Definition: dhctoken.h:220
@ HOST_IDENTIFIER
Definition: dhctoken.h:337
@ INCLUDE
Definition: dhctoken.h:268
@ PERCENT
Definition: dhctoken.h:49
@ RANGE
Definition: dhctoken.h:76
@ LLT
Definition: dhctoken.h:348
@ UID
Definition: dhctoken.h:73
@ CLIENT_UPDATES
Definition: dhctoken.h:300
@ TEMPORARY
Definition: dhctoken.h:356
@ INTERFACE
Definition: dhctoken.h:109
@ SERVER_DUID
Definition: dhctoken.h:347
@ USER_CLASS
Definition: dhctoken.h:87
@ EQUAL
Definition: dhctoken.h:46
@ RANGE6
Definition: dhctoken.h:351
@ SUBNET6
Definition: dhctoken.h:336
@ CODE
Definition: dhctoken.h:189
@ NUMBER_OR_NAME
Definition: dhctoken.h:68
@ SERVER_IDENTIFIER
Definition: dhctoken.h:91
@ NAME
Definition: dhctoken.h:69
@ SEMI
Definition: dhctoken.h:35
@ DENY
Definition: dhctoken.h:115
@ UNKNOWN
Definition: dhctoken.h:154
@ OF
Definition: dhctoken.h:162
@ VENDOR_CLASS
Definition: dhctoken.h:86
@ TOKEN_NO
Definition: dhctoken.h:230
@ TOKEN_DELETED
Definition: dhctoken.h:216
@ AFTER
Definition: dhctoken.h:354
@ END_OF_FILE
Definition: dhctoken.h:307
@ CLIENTS
Definition: dhctoken.h:155
@ DOT
Definition: dhctoken.h:36
@ SPAWN
Definition: dhctoken.h:150
@ POOL6
Definition: dhctoken.h:369
@ CLASS
Definition: dhctoken.h:74
@ V6RELOPT
Definition: dhctoken.h:371
@ ETHERNET
Definition: dhctoken.h:65
@ FIXED_ADDR6
Definition: dhctoken.h:334
@ LEASE_ID_FORMAT
Definition: dhctoken.h:376
@ SUBCLASS
Definition: dhctoken.h:148
@ UNKNOWN_CLIENTS
Definition: dhctoken.h:113
@ RBRACE
Definition: dhctoken.h:41
@ GROUP
Definition: dhctoken.h:97
@ OPTION
Definition: dhctoken.h:64
@ SLASH
Definition: dhctoken.h:39
@ DYNAMIC
Definition: dhctoken.h:160
@ UNAUTHENTICATED
Definition: dhctoken.h:158
@ ALL
Definition: dhctoken.h:159
@ KNOWN
Definition: dhctoken.h:156
@ LIMIT
Definition: dhctoken.h:164
@ PREFIX6
Definition: dhctoken.h:357
@ SPACE
Definition: dhctoken.h:198
@ BOOTING
Definition: dhctoken.h:116
@ TOKEN_FDDI
Definition: dhctoken.h:181
@ TOKEN_BOOTP
Definition: dhctoken.h:277
@ MEMBERS
Definition: dhctoken.h:161
@ LOCAL
Definition: dhctoken.h:325
@ AUTHENTICATED
Definition: dhctoken.h:157
@ SHARED_NETWORK
Definition: dhctoken.h:88
@ AUTHORITATIVE
Definition: dhctoken.h:182
@ STATIC
Definition: dhctoken.h:213
@ DYNAMIC_BOOTP
Definition: dhctoken.h:90
@ CHECK
Definition: dhctoken.h:141
@ STRING
Definition: dhctoken.h:66
@ COMMA
Definition: dhctoken.h:38
@ HARDWARE
Definition: dhctoken.h:61
@ DUPLICATES
Definition: dhctoken.h:219
@ WITH
Definition: dhctoken.h:151
@ POOL
Definition: dhctoken.h:153
@ DEFINE
Definition: dhctoken.h:254
#define is_identifier(x)
Definition: dhctoken.h:385
char * hook_library_path
Definition: keama.c:54
isc_boolean_t use_isc_lifetimes
Definition: keama.c:59
void parse_error(struct parse *cfile, const char *fmt,...)
Definition: keama.c:197
void stackPush(struct parse *pc, struct element *elem)
Definition: keama.c:181
isc_boolean_t global_hr
Definition: keama.c:60
#define TOPLEVEL
Definition: keama.h:126
struct option * option_lookup_code(const char *, unsigned)
Definition: options.c:624
@ kea_unknown
Definition: keama.h:262
@ dynamic
Definition: keama.h:266
@ isc_dhcp_unknown
Definition: keama.h:263
@ special
Definition: keama.h:265
@ known
Definition: keama.h:264
struct option * option_lookup_name(const char *, const char *)
Definition: options.c:579
const char * print_data_expression(struct element *, isc_boolean_t *)
Definition: print.c:496
#define PARAMETER
Definition: keama.h:125
struct element * reduce_boolean_expression(struct element *)
Definition: reduce.c:52
size_t conf_file_parse(struct parse *)
struct space * space_lookup(const char *)
Definition: options.c:565
struct string * parse_hexa(struct parse *)
Definition: parse.c:1524
struct string * parse_option_textbin(struct parse *, struct option *)
Definition: parse.c:4169
const char * print_boolean_expression(struct element *, isc_boolean_t *)
Definition: print.c:59
@ supersede_option_statement
Definition: keama.h:170
struct string * convert_format(const char *, isc_boolean_t *, isc_boolean_t *)
Definition: parse.c:1338
const char * display_status(enum option_status)
Definition: options.c:1140
struct string * parse_ip6_addr_txt(struct parse *)
Definition: parse.c:379
void merge_option_data(struct element *, struct element *)
Definition: options.c:708
Definition: dhcpd.h:1102
Definition: data.h:190
Definition: data.h:216
isc_boolean_t skip
Definition: data.h:219
union value value
Definition: data.h:221
int kind
Definition: data.h:218
int type
Definition: data.h:217
char * key
Definition: data.h:220
struct comments comments
Definition: data.h:222
Definition: dhcpd.h:962
Definition: data.h:289
struct element * value
Definition: data.h:292
char * key
Definition: data.h:291
unsigned order
Definition: data.h:290
Definition: tree.h:345
const char * format
Definition: tree.h:347
unsigned code
Definition: tree.h:349
enum option_status status
Definition: keama.h:297
const char * old
Definition: keama.h:292
const struct space * space
Definition: keama.h:295
const char * name
Definition: tree.h:346
Definition: dhcpd.h:288
size_t issue_counter
Definition: keama.h:113
struct comments comments
Definition: keama.h:116
size_t stack_size
Definition: keama.h:111
size_t stack_top
Definition: keama.h:112
struct element ** stack
Definition: keama.h:110
Definition: dhcpd.h:1001
Definition: dhcpd.h:1029
Definition: keama.h:283
struct element * vendor
Definition: keama.h:287
const char * old
Definition: keama.h:284
enum option_status status
Definition: keama.h:286
const char * name
Definition: keama.h:285
Definition: data.h:171
char * content
Definition: data.h:173
size_t length
Definition: data.h:172
Definition: dhcpd.h:1075
struct string * mask
Definition: confparse.c:60
struct group * group
Definition: dhcpd.h:1085
struct iaddr netmask
Definition: dhcpd.h:1083
struct element * share
Definition: confparse.c:58
struct string * addr
Definition: confparse.c:59
struct element * subnet
Definition: confparse.c:57
@ context_any
Definition: tree.h:84
Definition: data.h:205
struct list list_value
Definition: data.h:211