Kea 3.1.1
adaptor_config.cc
Go to the documentation of this file.
1// Copyright (C) 2018-2025 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7#include <config.h>
8
9#include <dhcpsrv/subnet_id.h>
10#include <yang/adaptor_config.h>
11
12using namespace std;
13using namespace isc::data;
14using namespace isc::dhcp;
15
16namespace {
17const string DHCP4_SPACE = "dhcp4";
18const string DHCP6_SPACE = "dhcp6";
19}
20
21namespace isc {
22namespace yang {
23
24bool
26 bool have_ids = true;
27
28 if (!subnets || subnets->empty()) {
29 // There are no subnets defined, so technically there are no ids.
30 // However, the flag is used to determine whether the code later
31 // needs to call assignIDs. Since there is no need to assign
32 // anything, the code returns true here.
33 return (true);
34 }
35
36 // If there are subnets defined, let's go over them one by one and
37 // collect subnet-ids used in them.
38 for (ElementPtr const& subnet : subnets->listValue()) {
39 if (!collectID(subnet, set)) {
40 have_ids = false;
41 }
42 }
43 return (have_ids);
44}
45
46bool
48 SubnetIDSet& set,
49 const string& subsel) {
50 if (!networks || networks->empty()) {
51 // There are no shared networks defined, so technically there are no
52 // ids. However, the flag is used to determine whether the code later
53 // needs to call assignIDs. Since there is no need to assign anything,
54 // the code returns true here.
55 return (true);
56 }
57
58 // This determines if EVERY subnet has subnet-id defined.
59 bool have_ids = true;
60 for (size_t i = 0; i < networks->size(); ++i) {
61 ElementPtr network = networks->getNonConst(i);
62 ConstElementPtr subnets = network->get(subsel);
63 if (subnets) {
64 if (!subnets->empty()) {
65 // If there are subnets, collect their subnet-ids. If any
66 // of them doesn't have a subnet-id, return false.
67 if (!subnetsCollectID(subnets, set)) {
68 have_ids = false;
69 }
70 } else {
71 // There's empty subnets list, so just remove it.
72 network->remove(subsel);
73 }
74 }
75 }
76 return (have_ids);
77}
78
79void
81 SubnetID& next) {
82 if (!subnets || subnets->empty()) {
83 // nothing to do here.
84 return;
85 }
86
87 for (size_t i = 0; i < subnets->size(); ++i) {
88 ElementPtr subnet = subnets->getNonConst(i);
89 assignID(subnet, set, next);
90 }
91}
92
93void
95 SubnetIDSet& set, SubnetID& next,
96 const string& subsel) {
97 if (!networks || networks->empty()) {
98 // nothing to do here.
99 return;
100 }
101
102 for (ElementPtr const& network : networks->listValue()) {
103 ConstElementPtr subnets = network->get(subsel);
104 if (!subnets || subnets->empty()) {
105 continue;
106 }
107
108 for (size_t i = 0; i < subnets->size(); ++i) {
109 ElementPtr subnet = subnets->getNonConst(i);
110 assignID(subnet, set, next);
111 }
112 }
113}
114
115void
117 if (!pools || pools->empty()) {
118 // nothing to do here.
119 return;
120 }
121
122 // Canonize (clean up name, remove extra spaces, add one space where
123 // needed) every pool on the list.
124 for (size_t i = 0; i < pools->size(); ++i) {
125 ElementPtr pool = pools->getNonConst(i);
127 }
128}
129
130void
132 if (!subnets || subnets->empty()) {
133 // nothing to do here.
134 return;
135 }
136
137 for (ElementPtr const& subnet : subnets->listValue()) {
138 sanitizePools(subnet->get("pools"));
139 }
140}
141
142void
144 const string& subsel) {
145 if (!networks || networks->empty()) {
146 // nothing to do here.
147 return;
148 }
149
150 for (ElementPtr const& network : networks->listValue()) {
151 sanitizePoolsInSubnets(network->get(subsel));
152 }
153}
154
155void
157 const string& space,
158 OptionCodes& codes) {
159 if (!defs || defs->empty()) {
160 // nothing to do here.
161 return;
162 }
163
164 // Do sanity checks on every option definition and fill any missing
165 // fields with default values.
166 for (size_t i = 0; i < defs->size(); ++i) {
167 ElementPtr def = defs->getNonConst(i);
168 checkCode(def);
169 checkType(def);
170 setSpace(def, space);
171 collect(def, codes);
172 }
173}
174
175void
177 const string& space,
178 const OptionCodes& codes) {
179 if (!options || options->empty()) {
180 // nothing to do here.
181 return;
182 }
183
184 // Sanitize option-data. The only missing elements we may possibly
185 // need to fill are option space and option code.
186 for (size_t i = 0; i < options->size(); ++i) {
187 ElementPtr option = options->getNonConst(i);
188 setSpace(option, space);
189 setCode(option, codes);
190 ConstElementPtr classes = option->get("client-classes");
191 if (classes && classes->empty()) {
192 option->remove("client-classes");
193 }
194 }
195}
196
197void
199 const string& space,
200 OptionCodes& codes) {
201 if (!classes || classes->empty()) {
202 // nothing to do here.
203 return;
204 }
205
206 // For every client class defined...
207 for (size_t i = 0; i < classes->size(); ++i) {
208 ElementPtr cclass = classes->getNonConst(i);
209
210 if (space == DHCP4_SPACE) {
211 ConstElementPtr options = cclass->get("option-def");
212 if (options) {
213 if (!options->empty()) {
214 // If present, sanitize it.
215 sanitizeOptionDefList(options, space, codes);
216 } else {
217 // If empty, remove it.
218 cclass->remove("option-def");
219 }
220 }
221 }
222
223 // also sanitize option data.
224 ConstElementPtr options = cclass->get("option-data");
225 if (options) {
226 if (!options->empty()) {
227 // If present, sanitize it.
228 sanitizeOptionDataList(options, space, codes);
229 } else {
230 // If empty, remove it.
231 cclass->remove("option-data");
232 }
233 }
234 }
235}
236
237void
239 const OptionCodes& codes) {
240 if (!pools || pools->empty()) {
241 // nothing to do here.
242 return;
243 }
244
245 for (size_t i = 0; i < pools->size(); ++i) {
246 ElementPtr pool = pools->getNonConst(i);
247 ConstElementPtr options = pool->get("option-data");
248 if (options) {
249 if (!options->empty()) {
250 sanitizeOptionDataList(options, space, codes);
251 } else {
252 pool->remove("option-data");
253 }
254 }
255 }
256}
257
258void
260 const OptionCodes& codes) {
261 if (!hosts || hosts->empty()) {
262 // nothing to do here.
263 return;
264 }
265
266 for (size_t i = 0; i < hosts->size(); ++i) {
267 ElementPtr host = hosts->getNonConst(i);
268 ConstElementPtr options = host->get("option-data");
269 if (options) {
270 if (!options->empty()) {
271 sanitizeOptionDataList(options, space, codes);
272 } else {
273 host->remove("option-data");
274 }
275 }
276 }
277}
278
279void
281 const string& space,
282 const OptionCodes& codes) {
283 if (!subnets || subnets->empty()) {
284 // nothing to do here.
285 return;
286 }
287
288 for (size_t i = 0; i < subnets->size(); ++i) {
289 ElementPtr subnet = subnets->getNonConst(i);
290
291 // Let's try to sanitize option-data first.
292 ConstElementPtr options = subnet->get("option-data");
293 if (options) {
294 if (!options->empty()) {
295 sanitizeOptionDataList(options, space, codes);
296 } else {
297 subnet->remove("option-data");
298 }
299 }
300
301 // Then try to sanitize pools.
302 ConstElementPtr pools = subnet->get("pools");
303 if (pools) {
304 if (!pools->empty()) {
305 sanitizeOptionPools(pools, space, codes);
306 } else {
307 subnet->remove("pools");
308 }
309 }
310
311 // If this is v6, also sanitize pd-pools.
312 if (space == DHCP6_SPACE) {
313 ConstElementPtr pd_pools = subnet->get("pd-pools");
314 if (pd_pools) {
315 if (!pd_pools->empty()) {
316 sanitizeOptionPools(pd_pools, space, codes);
317 } else {
318 subnet->remove("pd-pools");
319 }
320 }
321 }
322
323 // Finally, sanitize host reservations.
324 ConstElementPtr hosts = subnet->get("reservations");
325 if (hosts) {
326 if (!hosts->empty()) {
327 sanitizeOptionHosts(hosts, space, codes);
328 } else {
329 subnet->remove("reservations");
330 }
331 }
332 }
333}
334
335void
337 const string& space,
338 const OptionCodes& codes) {
339 if (!networks || networks->empty()) {
340 // nothing to do here.
341 return;
342 }
343
344 // For every shared network...
345 for (size_t i = 0; i < networks->size(); ++i) {
346 ElementPtr network = networks->getNonConst(i);
347
348 // try to sanitize shared network options first.
349 ConstElementPtr options = network->get("option-data");
350 if (options) {
351 if (!options->empty()) {
352 sanitizeOptionDataList(options, space, codes);
353 } else {
354 network->remove("option-data");
355 }
356 }
357 string subnet = "subnet";
358 if (space == DHCP4_SPACE) {
359 subnet += "4";
360 } else {
361 subnet += "6";
362 }
363
364 // Now try to sanitize subnets.
365 ConstElementPtr subnets = network->get(subnet);
366 if (subnets) {
367 if (!subnets->empty()) {
368 sanitizeOptionSubnets(subnets, space, codes);
369 } else {
370 network->remove(subnet);
371 }
372 }
373 }
374}
375
376void
378 if (!pools || pools->empty()) {
379 // nothing to do here.
380 return;
381 }
382
383 for (size_t i = 0; i < pools->size(); ++i) {
384 ElementPtr pool = pools->getNonConst(i);
385 ConstElementPtr require = pool->get("require-client-classes");
386 if (require && require->empty()) {
387 pool->remove("require-client-classes");
388 }
389 require = pool->get("evaluate-additional-classes");
390 if (require && require->empty()) {
391 pool->remove("evaluate-additional-classes");
392 }
393 ConstElementPtr classes = pool->get("client-class");
394 if (classes && classes->empty()) {
395 pool->remove("client-class");
396 }
397 classes = pool->get("client-classes");
398 if (classes && classes->empty()) {
399 pool->remove("client-classes");
400 }
401 }
402}
403
404void
406 if (!subnets || subnets->empty()) {
407 // nothing to do here.
408 return;
409 }
410
411 for (size_t i = 0; i < subnets->size(); ++i) {
412 ElementPtr subnet = subnets->getNonConst(i);
413 sanitizeEmptyListPools(subnet->get("pools"));
414 sanitizeEmptyListPools(subnet->get("pd-pools"));
415 ConstElementPtr require = subnet->get("require-client-classes");
416 if (require && require->empty()) {
417 subnet->remove("require-client-classes");
418 }
419 require = subnet->get("evaluate-additional-classes");
420 if (require && require->empty()) {
421 subnet->remove("evaluate-additional-classes");
422 }
423 ConstElementPtr classes = subnet->get("client-class");
424 if (classes && classes->empty()) {
425 subnet->remove("client-class");
426 }
427 classes = subnet->get("client-classes");
428 if (classes && classes->empty()) {
429 subnet->remove("client-classes");
430 }
431 }
432}
433
434void
436 const string& subsel) {
437 if (!networks || networks->empty()) {
438 // nothing to do here.
439 return;
440 }
441
442 for (size_t i = 0; i < networks->size(); ++i) {
443 ElementPtr network = networks->getNonConst(i);
444 sanitizeEmptyListSubnets(network->get(subsel));
445 ConstElementPtr require = network->get("require-client-classes");
446 if (require && require->empty()) {
447 network->remove("require-client-classes");
448 }
449 require = network->get("evaluate-additional-classes");
450 if (require && require->empty()) {
451 network->remove("evaluate-additional-classes");
452 }
453 ConstElementPtr classes = network->get("client-class");
454 if (classes && classes->empty()) {
455 network->remove("client-class");
456 }
457 classes = network->get("client-classes");
458 if (classes && classes->empty()) {
459 network->remove("client-classes");
460 }
461 }
462}
463
464void
466
467 if (!hosts || hosts->empty()) {
468 // nothing to do here.
469 return;
470 }
471
472 for (size_t i = 0; i < hosts->size(); ++i) {
473 ElementPtr host = hosts->getNonConst(i);
474 quoteIdentifier(host);
475 }
476}
477
478void
480
481 if (!subnets || subnets->empty()) {
482 // nothing to do here.
483 return;
484 }
485
486 for (ElementPtr const& subnet : subnets->listValue()) {
487 sanitizeHostList(subnet->get("reservations"));
488 }
489}
490
491void
493 const string& space) {
494 if (!networks || networks->empty()) {
495 // nothing to do here.
496 return;
497 }
498
499 for (ElementPtr const& network : networks->listValue()) {
500 if (space == DHCP4_SPACE) {
501 sanitizeHostSubnets(network->get("subnet4"));
502 } else {
503 sanitizeHostSubnets(network->get("subnet6"));
504 }
505 }
506}
507
508void
510 if (!subnets || subnets->empty()) {
511 // nothing to do here.
512 return;
513 }
514
515 for (size_t i = 0; i < subnets->size(); ++i) {
516 ElementPtr subnet = subnets->getNonConst(i);
517 updateRelay(subnet);
518 }
519}
520
521void
523 const string& subsel) {
524 if (!networks || networks->empty()) {
525 // nothing to do here.
526 return;
527 }
528
529 for (size_t i = 0; i < networks->size(); ++i) {
530 ElementPtr network = networks->getNonConst(i);
531 updateRelay(network);
532 sanitizeRelaySubnets(network->get(subsel));
533 }
534}
535
536void
538 ConstElementPtr database = dhcp->get("hosts-database");
539 if (!database) {
540 // nothing to do here.
541 return;
542 }
543
544 dhcp->remove("hosts-database");
546 list->add(copy(database, 0));
547 dhcp->set("hosts-databases", list);
548}
549
550void
552 ConstElementPtr options = dhcp->get("relay-supplied-options");
553 if (!options || !options->empty()) {
554 // nothing to do here.
555 return;
556 }
557 dhcp->remove("relay-supplied-options");
558}
559
560void
562 const string& space) {
563 if (!dhcp) {
564 isc_throw(BadValue, "preProcess: null DHCP config");
565 }
566 bool have_ids = true;
567 SubnetIDSet set;
568 ConstElementPtr subnets = dhcp->get(subsel);
569 if (subnets) {
570 if (!subnets->empty()) {
571 if (!subnetsCollectID(subnets, set)) {
572 have_ids = false;
573 }
574 } else {
575 dhcp->remove(subsel);
576 }
577 }
578 ConstElementPtr networks = dhcp->get("shared-networks");
579 if (networks) {
580 if (!networks->empty()) {
581 if (!sharedNetworksCollectID(networks, set, subsel)) {
582 have_ids = false;
583 }
584 } else {
585 dhcp->remove("shared-networks");
586 }
587 }
588
589 if (!have_ids) {
590 SubnetID next(1);
591 subnetsAssignID(subnets, set, next);
592 sharedNetworksAssignID(networks, set, next, subsel);
593 }
594
595 OptionCodes codes;
596 initCodes(codes, space);
597 ConstElementPtr defs = dhcp->get("option-def");
598 if (defs) {
599 if (!defs->empty()) {
600 sanitizeOptionDefList(defs, space, codes);
601 } else {
602 dhcp->remove("option-def");
603 }
604 }
605 ConstElementPtr options = dhcp->get("option-data");
606 if (options) {
607 if (!options->empty()) {
608 sanitizeOptionDataList(options, space, codes);
609 } else {
610 dhcp->remove("option-data");
611 }
612 }
613 ConstElementPtr classes = dhcp->get("client-classes");
614 if (classes) {
615 if (!classes->empty()) {
616 sanitizeOptionClasses(classes, space, codes);
617 } else {
618 dhcp->remove("client-classes");
619 }
620 }
621 ConstElementPtr hosts = dhcp->get("reservations");
622 if (hosts) {
623 if (!hosts->empty()) {
624 sanitizeHostList(hosts);
625 sanitizeOptionHosts(hosts, space, codes);
626 } else {
627 dhcp->remove("reservations");
628 }
629 }
630 sanitizeOptionSubnets(subnets, space, codes);
631 sanitizeOptionSharedNetworks(networks, space, codes);
632
633 sanitizePoolsInSubnets(subnets);
634 sanitizePoolsInSharedNetworks(networks, subsel);
635
636 sanitizeHostSubnets(subnets);
637 SanitizeHostsInSharedNetworks(networks, space);
638
639 sanitizeRelaySubnets(subnets);
640 sanitizeRelayInSharedNetworks(networks, subsel);
641
643 sanitizeEmptyListSharedNetworks(networks, subsel);
644
646
647 if (space == DHCP6_SPACE) {
649 }
650}
651
652void
654 if (!config) {
655 isc_throw(BadValue, "preProcess4: null config");
656 }
657 if (config->getType() != Element::map) {
658 isc_throw(BadValue, "preProcess4: not map: " << config->str());
659 }
660 if (config->contains("Logging")) {
661 isc_throw(BadValue, "preProcess4: got Logging object");
662 }
663 ConstElementPtr dhcp = config->get("Dhcp4");
664 if (!dhcp) {
665 return;
666 }
667 ElementPtr mutable_dhcp(copy(dhcp, 0));
668 preProcess(mutable_dhcp, "subnet4", DHCP4_SPACE);
669 config->set("Dhcp4", mutable_dhcp);
670}
671
672void
674 if (!config) {
675 isc_throw(BadValue, "preProcess6: null config");
676 }
677 if (config->getType() != Element::map) {
678 isc_throw(BadValue, "preProcess6: not map: " << config->str());
679 }
680 if (config->contains("Logging")) {
681 isc_throw(BadValue, "preProcess6: got Logging object");
682 }
683 ConstElementPtr dhcp = config->get("Dhcp6");
684 if (!dhcp) {
685 return;
686 }
687 ElementPtr mutable_dhcp(copy(dhcp, 0));
688 preProcess(mutable_dhcp, "subnet6", DHCP6_SPACE);
689 config->set("Dhcp6", mutable_dhcp);
690}
691
692} // namespace yang
693} // namespace isc
@ map
Definition data.h:147
static ElementPtr createList(const Position &pos=ZERO_POSITION())
Creates an empty ListElement type ElementPtr.
Definition data.cc:299
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
static void subnetsAssignID(isc::data::ConstElementPtr subnets, isc::dhcp::SubnetIDSet &set, isc::dhcp::SubnetID &next)
Assigns subnet-id to every subnet in a subnet list.
static void sanitizeOptionSharedNetworks(isc::data::ConstElementPtr networks, const std::string &space, const OptionCodes &codes)
Set missing option codes to a shared network list.
static void sharedNetworksAssignID(isc::data::ConstElementPtr networks, isc::dhcp::SubnetIDSet &set, isc::dhcp::SubnetID &next, const std::string &subsel)
Assigns subnet-id to every subnet in a shared network list.
static void sanitizeEmptyListPools(isc::data::ConstElementPtr pools)
Process empty lists in a pool list.
static void sanitizeEmptyListSharedNetworks(isc::data::ConstElementPtr networks, const std::string &subsel)
Process empty lists in a shared-network list.
static void sanitizeRelaySubnets(isc::data::ConstElementPtr subnets)
Sanitizes relay information in subnets in a subnet list.
static void sanitizeHostSubnets(isc::data::ConstElementPtr subnets)
Process host reservations in a subnet list.
static bool subnetsCollectID(isc::data::ConstElementPtr subnets, isc::dhcp::SubnetIDSet &set)
Collects subnet-ids on all subnets.
static void preProcess4(isc::data::ElementPtr config)
Pre process a DHCPv4 configuration.
static void sanitizeOptionPools(isc::data::ConstElementPtr pools, const std::string &space, const OptionCodes &codes)
Set missing option codes to a pool list.
static void preProcess(isc::data::ElementPtr dhcp, const std::string &subsel, const std::string &space)
Pre process a configuration.
static void sanitizePoolsInSubnets(isc::data::ConstElementPtr subnets)
Sanitizes all pools in a subnets list.
static void sanitizeDatabase(isc::data::ElementPtr dhcp)
Update (hosts) database.
static void sanitizeOptionHosts(isc::data::ConstElementPtr hosts, const std::string &space, const OptionCodes &codes)
Set missing option codes to a host reservation list.
static void sanitizeHostList(isc::data::ConstElementPtr hosts)
Process host reservation list.
static void SanitizeHostsInSharedNetworks(isc::data::ConstElementPtr networks, const std::string &space)
Process host reservations in a shared network list.
static void sanitizeOptionDefList(isc::data::ConstElementPtr defs, const std::string &space, OptionCodes &codes)
Collect option definitions from an option definition list.
static void sanitizeEmptyListSubnets(isc::data::ConstElementPtr subnets)
Process empty lists in a subnet list.
static void sanitizeOptionSubnets(isc::data::ConstElementPtr subnets, const std::string &space, const OptionCodes &codes)
Set missing option codes to a subnet list.
static void sanitizePools(isc::data::ConstElementPtr pools)
Sanitizes all pools in a pools list.
static void sanitizePoolsInSharedNetworks(isc::data::ConstElementPtr networks, const std::string &subsel)
Sanitizes all pools in all subnets in a shared network list.
static void sanitizeOptionClasses(isc::data::ConstElementPtr classes, const std::string &space, OptionCodes &codes)
Collect option definitions from a client class list and set missing option codes.
static void preProcess6(isc::data::ElementPtr config)
Pre process a DHCPv6 configuration.
static void sanitizeRelaySuppliedOptions(isc::data::ElementPtr dhcp)
Update relay supplied options.
static void sanitizeOptionDataList(isc::data::ConstElementPtr options, const std::string &space, const OptionCodes &codes)
Set missing option codes to an option data list.
static void sanitizeRelayInSharedNetworks(isc::data::ConstElementPtr networks, const std::string &subsel)
Sanitizes relay information in a shared network list.
static bool sharedNetworksCollectID(isc::data::ConstElementPtr networks, isc::dhcp::SubnetIDSet &set, const std::string &subsel)
Collects subnet-ids in all subnets in all shared network list.
static void quoteIdentifier(isc::data::ElementPtr host)
Quote when needed a host identifier.
static void setCode(isc::data::ElementPtr option, const OptionCodes &codes)
Set code from name and definitions.
static void initCodes(OptionCodes &codes, const std::string &space)
Initialize code map.
static void setSpace(isc::data::ElementPtr option, const std::string &space)
Set space.
static void checkType(isc::data::ConstElementPtr option)
Checks if type is specified in option definition.
static void collect(isc::data::ConstElementPtr option, OptionCodes &codes)
Collect definition.
static void checkCode(isc::data::ConstElementPtr option)
Check if code is specified in option defintion.
static void canonizePool(isc::data::ElementPtr pool)
Canonize pool.
static void assignID(isc::data::ElementPtr subnet, isc::dhcp::SubnetIDSet &set, isc::dhcp::SubnetID &next)
Assign subnet ID.
static bool collectID(isc::data::ConstElementPtr subnet, isc::dhcp::SubnetIDSet &set)
Collect a subnet ID.
static void updateRelay(isc::data::ElementPtr subnet)
Update relay.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
ElementPtr copy(ConstElementPtr from, int level)
Copy the data up to a nesting level.
Definition data.cc:1420
boost::shared_ptr< const Element > ConstElementPtr
Definition data.h:29
boost::shared_ptr< Element > ElementPtr
Definition data.h:28
std::set< dhcp::SubnetID > SubnetIDSet
Ordered list aka set of subnetIDs.
Definition subnet_id.h:43
uint32_t SubnetID
Defines unique IPv4 or IPv6 subnet identifier.
Definition subnet_id.h:25
std::unordered_map< std::string, uint16_t > OptionCodes
Map for DHCP option definitions handling code and an index built from space and name.
Defines the logger used by the top-level component of kea-lfc.