Kea  2.3.7
adaptor_config.cc
Go to the documentation of this file.
1 // Copyright (C) 2018-2022 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 
12 using namespace std;
13 using namespace isc::data;
14 using namespace isc::dhcp;
15 
16 namespace {
17 const string DHCP4_SPACE = "dhcp4";
18 const string DHCP6_SPACE = "dhcp6";
19 }
20 
21 namespace isc {
22 namespace yang {
23 
24 bool
25 AdaptorConfig::subnetsCollectID(ConstElementPtr subnets, SubnetIDSet& set) {
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 
46 bool
47 AdaptorConfig::sharedNetworksCollectID(ConstElementPtr networks,
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 
79 void
80 AdaptorConfig::subnetsAssignID(ConstElementPtr subnets, SubnetIDSet& set,
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 
93 void
94 AdaptorConfig::sharedNetworksAssignID(ConstElementPtr networks,
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 
115 void
116 AdaptorConfig::sanitizePools(ConstElementPtr pools) {
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);
126  AdaptorPool::canonizePool(pool);
127  }
128 }
129 
130 void
131 AdaptorConfig::sanitizePoolsInSubnets(ConstElementPtr subnets) {
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 
142 void
143 AdaptorConfig::sanitizePoolsInSharedNetworks(ConstElementPtr networks,
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 
155 void
156 AdaptorConfig::sanitizeOptionDefList(ConstElementPtr defs,
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 
175 void
176 AdaptorConfig::sanitizeOptionDataList(ConstElementPtr options,
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  }
191 }
192 
193 void
194 AdaptorConfig::sanitizeOptionClasses(ConstElementPtr classes,
195  const string& space,
196  OptionCodes& codes) {
197  if (!classes || classes->empty()) {
198  // nothing to do here.
199  return;
200  }
201 
202  // For every client class defined...
203  for (size_t i = 0; i < classes->size(); ++i) {
204  ElementPtr cclass = classes->getNonConst(i);
205 
206  if (space == DHCP4_SPACE) {
207  ConstElementPtr options = cclass->get("option-def");
208  if (options) {
209  if (!options->empty()) {
210  // If present, sanitize it.
211  sanitizeOptionDefList(options, space, codes);
212  } else {
213  // If empty, remove it.
214  cclass->remove("option-def");
215  }
216  }
217  }
218 
219  // also sanitize option data.
220  ConstElementPtr options = cclass->get("option-data");
221  if (options) {
222  if (!options->empty()) {
223  // If present, sanitize it.
224  sanitizeOptionDataList(options, space, codes);
225  } else {
226  // If empty, remove it.
227  cclass->remove("option-data");
228  }
229  }
230  }
231 }
232 
233 void
234 AdaptorConfig::sanitizeOptionPools(ConstElementPtr pools, const string& space,
235  const OptionCodes& codes) {
236  if (!pools || pools->empty()) {
237  // nothing to do here.
238  return;
239  }
240 
241  for (size_t i = 0; i < pools->size(); ++i) {
242  ElementPtr pool = pools->getNonConst(i);
243  ConstElementPtr options = pool->get("option-data");
244  if (options) {
245  if (!options->empty()) {
246  sanitizeOptionDataList(options, space, codes);
247  } else {
248  pool->remove("option-data");
249  }
250  }
251  }
252 }
253 
254 void
255 AdaptorConfig::sanitizeOptionHosts(ConstElementPtr hosts, const string& space,
256  const OptionCodes& codes) {
257  if (!hosts || hosts->empty()) {
258  // nothing to do here.
259  return;
260  }
261 
262  for (size_t i = 0; i < hosts->size(); ++i) {
263  ElementPtr host = hosts->getNonConst(i);
264  ConstElementPtr options = host->get("option-data");
265  if (options) {
266  if (!options->empty()) {
267  sanitizeOptionDataList(options, space, codes);
268  } else {
269  host->remove("option-data");
270  }
271  }
272  }
273 }
274 
275 void
276 AdaptorConfig::sanitizeOptionSubnets(ConstElementPtr subnets,
277  const string& space,
278  const OptionCodes& codes) {
279  if (!subnets || subnets->empty()) {
280  // nothing to do here.
281  return;
282  }
283 
284  for (size_t i = 0; i < subnets->size(); ++i) {
285  ElementPtr subnet = subnets->getNonConst(i);
286 
287  // Let's try to sanitize option-data first.
288  ConstElementPtr options = subnet->get("option-data");
289  if (options) {
290  if (!options->empty()) {
291  sanitizeOptionDataList(options, space, codes);
292  } else {
293  subnet->remove("option-data");
294  }
295  }
296 
297  // Then try to sanitize pools.
298  ConstElementPtr pools = subnet->get("pools");
299  if (pools) {
300  if (!pools->empty()) {
301  sanitizeOptionPools(pools, space, codes);
302  } else {
303  subnet->remove("pools");
304  }
305  }
306 
307  // If this is v6, also sanitize pd-pools.
308  if (space == DHCP6_SPACE) {
309  ConstElementPtr pd_pools = subnet->get("pd-pools");
310  if (pd_pools) {
311  if (!pd_pools->empty()) {
312  sanitizeOptionPools(pd_pools, space, codes);
313  } else {
314  subnet->remove("pd-pools");
315  }
316  }
317  }
318 
319  // Finally, sanitize host reservations.
320  ConstElementPtr hosts = subnet->get("reservations");
321  if (hosts) {
322  if (!hosts->empty()) {
323  sanitizeOptionHosts(hosts, space, codes);
324  } else {
325  subnet->remove("reservations");
326  }
327  }
328  }
329 }
330 
331 void
332 AdaptorConfig::sanitizeOptionSharedNetworks(ConstElementPtr networks,
333  const string& space,
334  const OptionCodes& codes) {
335  if (!networks || networks->empty()) {
336  // nothing to do here.
337  return;
338  }
339 
340  // For every shared network...
341  for (size_t i = 0; i < networks->size(); ++i) {
342  ElementPtr network = networks->getNonConst(i);
343 
344  // try to sanitize shared network options first.
345  ConstElementPtr options = network->get("option-data");
346  if (options) {
347  if (!options->empty()) {
348  sanitizeOptionDataList(options, space, codes);
349  } else {
350  network->remove("option-data");
351  }
352  }
353  string subnet = "subnet";
354  if (space == DHCP4_SPACE) {
355  subnet += "4";
356  } else {
357  subnet += "6";
358  }
359 
360  // Now try to sanitize subnets.
361  ConstElementPtr subnets = network->get(subnet);
362  if (subnets) {
363  if (!subnets->empty()) {
364  sanitizeOptionSubnets(subnets, space, codes);
365  } else {
366  network->remove(subnet);
367  }
368  }
369  }
370 }
371 
372 void
373 AdaptorConfig::sanitizeRequireClassesPools(ConstElementPtr pools) {
374  if (!pools || pools->empty()) {
375  // nothing to do here.
376  return;
377  }
378 
379  for (size_t i = 0; i < pools->size(); ++i) {
380  ElementPtr pool = pools->getNonConst(i);
381  ConstElementPtr require = pool->get("require-client-classes");
382  if (require && require->empty()) {
383  pool->remove("require-client-classes");
384  }
385  }
386 }
387 
388 void
389 AdaptorConfig::sanitizeRequireClassesSubnets(ConstElementPtr subnets) {
390  if (!subnets || subnets->empty()) {
391  // nothing to do here.
392  return;
393  }
394 
395  for (size_t i = 0; i < subnets->size(); ++i) {
396  ElementPtr subnet = subnets->getNonConst(i);
397  sanitizeRequireClassesPools(subnet->get("pools"));
398  sanitizeRequireClassesPools(subnet->get("pd-pools"));
399  ConstElementPtr require = subnet->get("require-client-classes");
400  if (require && require->empty()) {
401  subnet->remove("require-client-classes");
402  }
403  }
404 }
405 
406 void
407 AdaptorConfig::requireClassesSharedNetworks(ConstElementPtr networks,
408  const string& subsel) {
409  if (!networks || networks->empty()) {
410  // nothing to do here.
411  return;
412  }
413 
414  for (size_t i = 0; i < networks->size(); ++i) {
415  ElementPtr network = networks->getNonConst(i);
416  sanitizeRequireClassesSubnets(network->get(subsel));
417  ConstElementPtr require = network->get("require-client-classes");
418  if (require && require->empty()) {
419  network->remove("require-client-classes");
420  }
421  }
422 }
423 
424 void
425 AdaptorConfig::sanitizeHostList(ConstElementPtr hosts) {
426 
427  if (!hosts || hosts->empty()) {
428  // nothing to do here.
429  return;
430  }
431 
432  for (size_t i = 0; i < hosts->size(); ++i) {
433  ElementPtr host = hosts->getNonConst(i);
434  quoteIdentifier(host);
435  }
436 }
437 
438 void
439 AdaptorConfig::sanitizeHostSubnets(ConstElementPtr subnets) {
440 
441  if (!subnets || subnets->empty()) {
442  // nothing to do here.
443  return;
444  }
445 
446  for (ElementPtr const& subnet : subnets->listValue()) {
447  sanitizeHostList(subnet->get("reservations"));
448  }
449 }
450 
451 void
452 AdaptorConfig::SanitizeHostsInSharedNetworks(ConstElementPtr networks,
453  const string& space) {
454  if (!networks || networks->empty()) {
455  // nothing to do here.
456  return;
457  }
458 
459  for (ElementPtr const& network : networks->listValue()) {
460  if (space == DHCP4_SPACE) {
461  sanitizeHostSubnets(network->get("subnet4"));
462  } else {
463  sanitizeHostSubnets(network->get("subnet6"));
464  }
465  }
466 }
467 
468 void
469 AdaptorConfig::sanitizeRelaySubnets(ConstElementPtr subnets) {
470  if (!subnets || subnets->empty()) {
471  // nothing to do here.
472  return;
473  }
474 
475  for (size_t i = 0; i < subnets->size(); ++i) {
476  ElementPtr subnet = subnets->getNonConst(i);
477  updateRelay(subnet);
478  }
479 }
480 
481 void
482 AdaptorConfig::sanitizeRelayInSharedNetworks(ConstElementPtr networks,
483  const string& subsel) {
484  if (!networks || networks->empty()) {
485  // nothing to do here.
486  return;
487  }
488 
489  for (size_t i = 0; i < networks->size(); ++i) {
490  ElementPtr network = networks->getNonConst(i);
491  updateRelay(network);
492  sanitizeRelaySubnets(network->get(subsel));
493  }
494 }
495 
496 void
497 AdaptorConfig::sanitizeDatabase(ElementPtr dhcp) {
498  ConstElementPtr database = dhcp->get("hosts-database");
499  if (!database) {
500  // nothing to do here.
501  return;
502  }
503 
504  dhcp->remove("hosts-database");
505  ElementPtr list = Element::createList();
506  list->add(copy(database, 0));
507  dhcp->set("hosts-databases", list);
508 }
509 
510 void
511 AdaptorConfig::sanitizeRelaySuppliedOptions(ElementPtr dhcp) {
512  ConstElementPtr options = dhcp->get("relay-supplied-options");
513  if (!options || !options->empty()) {
514  // nothing to do here.
515  return;
516  }
517  dhcp->remove("relay-supplied-options");
518 }
519 
520 void
521 AdaptorConfig::preProcess(ElementPtr dhcp, const string& subsel,
522  const string& space) {
523  if (!dhcp) {
524  isc_throw(BadValue, "preProcess: null DHCP config");
525  }
526  bool have_ids = true;
527  SubnetIDSet set;
528  ConstElementPtr subnets = dhcp->get(subsel);
529  if (subnets) {
530  if (!subnets->empty()) {
531  if (!subnetsCollectID(subnets, set)) {
532  have_ids = false;
533  }
534  } else {
535  dhcp->remove(subsel);
536  }
537  }
538  ConstElementPtr networks = dhcp->get("shared-networks");
539  if (networks) {
540  if (!networks->empty()) {
541  if (!sharedNetworksCollectID(networks, set, subsel)) {
542  have_ids = false;
543  }
544  } else {
545  dhcp->remove("shared-networks");
546  }
547  }
548 
549  if (!have_ids) {
550  SubnetID next(1);
551  subnetsAssignID(subnets, set, next);
552  sharedNetworksAssignID(networks, set, next, subsel);
553  }
554 
555  OptionCodes codes;
556  initCodes(codes, space);
557  ConstElementPtr defs = dhcp->get("option-def");
558  if (defs) {
559  if (!defs->empty()) {
560  sanitizeOptionDefList(defs, space, codes);
561  } else {
562  dhcp->remove("option-def");
563  }
564  }
565  ConstElementPtr options = dhcp->get("option-data");
566  if (options) {
567  if (!options->empty()) {
568  sanitizeOptionDataList(options, space, codes);
569  } else {
570  dhcp->remove("option-data");
571  }
572  }
573  ConstElementPtr classes = dhcp->get("client-classes");
574  if (classes) {
575  if (!classes->empty()) {
576  sanitizeOptionClasses(classes, space, codes);
577  } else {
578  dhcp->remove("client-classes");
579  }
580  }
581  ConstElementPtr hosts = dhcp->get("reservations");
582  if (hosts) {
583  if (!hosts->empty()) {
584  sanitizeHostList(hosts);
585  sanitizeOptionHosts(hosts, space, codes);
586  } else {
587  dhcp->remove("reservations");
588  }
589  }
590  sanitizeOptionSubnets(subnets, space, codes);
591  sanitizeOptionSharedNetworks(networks, space, codes);
592 
593  sanitizePoolsInSubnets(subnets);
594  sanitizePoolsInSharedNetworks(networks, subsel);
595 
596  sanitizeHostSubnets(subnets);
597  SanitizeHostsInSharedNetworks(networks, space);
598 
599  sanitizeRelaySubnets(subnets);
600  sanitizeRelayInSharedNetworks(networks, subsel);
601 
602  sanitizeRequireClassesSubnets(subnets);
603  requireClassesSharedNetworks(networks, subsel);
604 
605  sanitizeDatabase(dhcp);
606 
607  if (space == DHCP6_SPACE) {
608  sanitizeRelaySuppliedOptions(dhcp);
609  }
610 }
611 
612 void
613 AdaptorConfig::preProcess4(ElementPtr config) {
614  if (!config) {
615  isc_throw(BadValue, "preProcess4: null config");
616  }
617  if (config->getType() != Element::map) {
618  isc_throw(BadValue, "preProcess4: not map: " << config->str());
619  }
620  if (config->contains("Logging")) {
621  isc_throw(BadValue, "preProcess4: got Logging object");
622  }
623  ConstElementPtr dhcp = config->get("Dhcp4");
624  if (!dhcp) {
625  return;
626  }
627  ElementPtr mutable_dhcp(copy(dhcp, 0));
628  preProcess(mutable_dhcp, "subnet4", DHCP4_SPACE);
629  config->set("Dhcp4", mutable_dhcp);
630 }
631 
632 void
633 AdaptorConfig::preProcess6(ElementPtr config) {
634  if (!config) {
635  isc_throw(BadValue, "preProcess6: null config");
636  }
637  if (config->getType() != Element::map) {
638  isc_throw(BadValue, "preProcess6: not map: " << config->str());
639  }
640  if (config->contains("Logging")) {
641  isc_throw(BadValue, "preProcess6: got Logging object");
642  }
643  ConstElementPtr dhcp = config->get("Dhcp6");
644  if (!dhcp) {
645  return;
646  }
647  ElementPtr mutable_dhcp(copy(dhcp, 0));
648  preProcess(mutable_dhcp, "subnet6", DHCP6_SPACE);
649  config->set("Dhcp6", mutable_dhcp);
650 }
651 
652 } // namespace yang
653 } // namespace isc
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
#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:1360
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:27
boost::shared_ptr< Element > ElementPtr
Definition: data.h:24
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.