ISC DHCP 4.4.3-P1
A reference DHCPv4 and DHCPv6 implementation
 
Loading...
Searching...
No Matches
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 */
46const struct option *host_id_option = NULL;
48
49/* Simple or complex config */
50unsigned subnet_counter = 0;
51
52/* For subclass name generation */
53unsigned subclass_counter = 0;
54
55/* To map reservations to declared subnets */
56struct subnet {
57 struct element *subnet;
58 struct element *share;
59 struct string *addr;
60 struct string *mask;
61 TAILQ_ENTRY(subnet) next;
62};
63
64TAILQ_HEAD(subnets, subnet) known_subnets;
65
66/* To map pools to subnets inside a shared-network */
67struct range {
68 struct element *pool;
69 struct element *share;
70 struct string *low;
71 TAILQ_ENTRY(range) next;
72};
73
74TAILQ_HEAD(ranges, range) known_ranges;
75
76static void post_process_lifetimes(struct parse *);
77static size_t post_process_reservations(struct parse *);
78static void post_process_classes(struct parse *);
79static void post_process_generated_classes(struct parse *);
80static void check_depend(struct element *, struct element *);
81static void post_process_option_definitions(struct parse *);
82static void add_host_reservation_identifiers(struct parse *, const char *);
83static void add_host_id_option(struct parse *, const struct option *, int);
84static void subclass_inherit(struct parse *, struct element *,
85 struct element *);
86static void add_match_class(struct parse *, struct element *,
87 struct element *);
88static void option_data_derive(struct parse *, struct handle *,
89 struct handle *);
90static void derive_classes(struct parse *, struct handle *, struct handle *);
91static isc_boolean_t is_hexa_only(const char *, unsigned len);
92static void new_network_interface(struct parse *, struct element *);
93static struct string *addrmask(const struct string *, const struct string *);
94static struct element *find_match(struct parse *, struct element *,
96static struct element *find_location(struct element *, struct range *);
97static int get_prefix_length(const char *, const char *);
98static struct element *get_class(struct parse *, struct element *);
99static void concat_classes(struct parse *, struct element *, struct element *);
100static void generate_class(struct parse *, struct element *, struct element *,
101 struct element *);
102
103static struct string *CLASS_ALL;
104static struct string *CLASS_KNOWN;
105
106/* Add head config file comments to the DHCP server map */
107
108size_t
109conf_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");
153 comment = createComment("/// Reference Kea #245");
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 */
169static void
170post_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
217static size_t
218post_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");
256 comment = createComment("/// Kea reservations are per subnet");
258 comment = createComment("/// Reference Kea #231");
260 orphans->skip = ISC_TRUE;
261 issues++;
262 mapSet(cfile->stack[1], orphans, "reservations");
263 }
264 return issues;
265}
266
267/* Cleanup classes */
268
269static void
270post_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, "'");
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));
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");
337 TAILQ_INSERT_TAIL(&class->comments, comment);
338 msg = makeString(-1, "/// Reference Kea #248");
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) {
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) {
372 if (reduced != NULL) {
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
384static void
385post_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
411static void
412check_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");
459 TAILQ_INSERT_TAIL(&class->comments, comment);
460 class->skip = ISC_TRUE;
461 }
462}
463
464static void
465post_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
479void
480read_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));
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
534size_t
535conf_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
584parse_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)
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.");
746 parse_error(cfile, "pool declared outside of network");
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");
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". */
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
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",
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;
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,
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
1002
1003void
1004get_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
1141void
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++;
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 }
1264}
1265
1266/* Expect a left brace */
1267
1268void
1269parse_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
1281void
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;
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 (");
1464 appendString(msg, host_id_option->name);
1465 appendString(msg, ") is already used as ");
1466 appendString(msg, "host-identifier");
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 */
1508static void
1509add_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 */
1522static void
1523add_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");
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
1568static 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
1583void
1584parse_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
1891static void
1892subclass_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) {
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);
2027 }
2028 comment = createComment(mmsg->content);
2030 comment = createComment(dmsg->content);
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
2040static void
2041add_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
2064static void
2065relocate_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
2104void
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");
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
2286static void
2287common_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
2336void
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
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
2428void
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
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
2519void
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
2588void
2589close_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);
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);
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);
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);
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);
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
2937static void
2938option_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 */
2968static void
2969derive_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
3007struct 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 */
3054static struct element *
3055parse_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
3096void
3097parse_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");
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 }
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
3211void
3212parse_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 }
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
3332void
3333parse_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 }
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
3408void
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
3470
3471void
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++;
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 }
3694 }
3695}
3696
3697/* allow-deny-keyword :== BOOTP
3698 | BOOTING
3699 | DYNAMIC_BOOTP
3700 | UNKNOWN_CLIENTS */
3701
3702struct element *
3703parse_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 */
3789void
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 */
3977static isc_boolean_t
3978is_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
3999
4000void
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) {
4016 return;
4017 }
4018
4019 known = ISC_FALSE;
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",
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) {
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
4090void
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
4155void
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",
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?",
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
4235void
4237 enum dhcp_token token)
4238{
4239 if (token == 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",
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
4267void
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",
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
4308not_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
4315void
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",
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",
4354 option->code);
4355 }
4356no_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
4394static void
4395new_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
4429static 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
4440static struct string *
4441addrmask(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
4470static struct element *
4471find_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
4556static struct element *
4557find_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
4586static const uint8_t bytemasks[8] = {
4587 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff
4588};
4589
4590static int
4591get_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 */
4626static struct element *
4627get_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
4699static void
4700concat_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
4731static void
4732generate_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));
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));
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
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_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
char * parse_host_name(struct parse *cfile)
Definition parse.c:196
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
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
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
void parse_host_declaration(struct parse *cfile)
Definition confparse.c:1282
void parse_lbrace(struct parse *cfile)
Definition confparse.c:1269
struct element * parse_fixed_addr_param(struct parse *cfile, enum dhcp_token type)
Definition confparse.c:3008
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
struct element * parse_allow_deny(struct parse *cfile, int flag)
Definition confparse.c:3703
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 comment * createComment(const char *line)
Definition data.c:367
isc_boolean_t eqString(const struct string *s, const struct string *o)
Definition data.c:343
struct string * makeString(int l, const char *s)
Definition data.c:44
isc_boolean_t boolValue(const struct element *e)
Definition data.c:399
struct element * copy(struct element *e)
Definition data.c:1115
struct element * createHexa(struct string *h)
Definition data.c:1249
void listRemove(struct element *l, int i)
Definition data.c:707
struct element * createList(void)
Definition data.c:504
struct element * createBool(isc_boolean_t b)
Definition data.c:469
struct string * allocString(void)
Definition data.c:32
struct handle * mapPop(struct element *m)
Definition data.c:1186
size_t mapSize(const struct element *m)
Definition data.c:829
void appendString(struct string *s, const char *a)
Definition data.c:311
struct element * listGet(struct element *l, int i)
Definition data.c:646
struct string * stringValue(struct element *e)
Definition data.c:408
struct element * createNull(void)
Definition data.c:481
void resetList(struct element *e)
Definition data.c:585
void concat(struct element *l, struct element *o)
Definition data.c:748
isc_boolean_t mapContains(const struct element *m, const char *k)
Definition data.c:811
void derive(struct handle *src, struct handle *dst)
Definition data.c:1212
struct element * createInt(int64_t i)
Definition data.c:445
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
struct element * createString(const struct string *s)
Definition data.c:492
void mapRemove(struct element *m, const char *k)
Definition data.c:792
struct element * mapGet(struct element *m, const char *k)
Definition data.c:759
int64_t intValue(const struct element *e)
Definition data.c:383
struct string * makeStringExt(int l, const char *s, char fmt)
Definition data.c:64
#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_HEAD(name, type)
Definition data.h:34
#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 element * reduce_boolean_expression(struct element *)
Definition reduce.c:52
struct option * option_lookup_name(const char *, const char *)
Definition options.c:579
struct string * parse_option_textbin(struct parse *, struct option *)
Definition parse.c:4169
#define PARAMETER
Definition keama.h:125
size_t conf_file_parse(struct parse *)
const char * print_data_expression(struct element *, isc_boolean_t *)
Definition print.c:496
const char * print_boolean_expression(struct element *, isc_boolean_t *)
Definition print.c:59
struct string * parse_ip6_addr_txt(struct parse *)
Definition parse.c:379
@ supersede_option_statement
Definition keama.h:170
struct string * parse_hexa(struct parse *)
Definition parse.c:1524
struct string * convert_format(const char *, isc_boolean_t *, isc_boolean_t *)
Definition parse.c:1338
struct space * space_lookup(const char *)
Definition options.c:565
const char * display_status(enum option_status)
Definition options.c:1140
void merge_option_data(struct element *, struct element *)
Definition options.c:708
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: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
struct string * mask
Definition confparse.c:60
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