Kea 1.9.11
cfg_option.cc
Go to the documentation of this file.
1// Copyright (C) 2014-2019 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 <dhcp/libdhcp++.h>
10#include <dhcpsrv/cfg_option.h>
11#include <dhcp/dhcp6.h>
12#include <dhcp/option_space.h>
13#include <util/encode/hex.h>
14#include <boost/algorithm/string/split.hpp>
15#include <boost/algorithm/string/classification.hpp>
16#include <boost/make_shared.hpp>
17#include <string>
18#include <sstream>
19#include <vector>
20
21using namespace isc::data;
22
23namespace isc {
24namespace dhcp {
25
27OptionDescriptor::create(const OptionPtr& opt, bool persist,
28 const std::string& formatted_value,
29 ConstElementPtr user_context) {
30 return (boost::make_shared<OptionDescriptor>(opt, persist, formatted_value,
31 user_context));
32}
33
36 return (boost::make_shared<OptionDescriptor>(persist));
37}
38
41 return (boost::make_shared<OptionDescriptor>(desc));
42}
43
44bool
46 return ((persistent_ == other.persistent_) &&
48 (space_name_ == other.space_name_) &&
49 option_->equals(other.option_));
50}
51
53}
54
55bool
57 return (options_.empty() && vendor_options_.empty());
58}
59
60bool
61CfgOption::equals(const CfgOption& other) const {
62 return (options_.equals(other.options_) &&
63 vendor_options_.equals(other.vendor_options_));
64}
65
66void
67CfgOption::add(const OptionPtr& option, const bool persistent,
68 const std::string& option_space,
69 const uint64_t id) {
70 OptionDescriptor desc(option, persistent);
71 if (id > 0) {
72 desc.setId(id);
73 }
74 add(desc, option_space);
75}
76
77void
78CfgOption::add(const OptionDescriptor& desc, const std::string& option_space) {
79 if (!desc.option_) {
80 isc_throw(isc::BadValue, "option being configured must not be NULL");
81
82 } else if (!OptionSpace::validateName(option_space)) {
83 isc_throw(isc::BadValue, "invalid option space name: '"
84 << option_space << "'");
85 }
86
87 const uint32_t vendor_id = LibDHCP::optionSpaceToVendorId(option_space);
88 if (vendor_id) {
89 vendor_options_.addItem(desc, vendor_id);
90 } else {
91 options_.addItem(desc, option_space);
92 }
93}
94
95void
96CfgOption::replace(const OptionDescriptor& desc, const std::string& option_space) {
97 if (!desc.option_) {
98 isc_throw(isc::BadValue, "option being replaced must not be NULL");
99 }
100
101 // Check for presence of options.
102 OptionContainerPtr options = getAll(option_space);
103 if (!options) {
104 isc_throw(isc::BadValue, "option space " << option_space
105 << " does not exist");
106 }
107
108 // Find the option we want to replace.
109 OptionContainerTypeIndex& idx = options->get<1>();
110 OptionContainerTypeIndex::const_iterator od_itr = idx.find(desc.option_->getType());
111 if (od_itr == idx.end()) {
112 isc_throw(isc::BadValue, "cannot replace option: "
113 << option_space << ":" << desc.option_->getType()
114 << ", it does not exist");
115 }
116
117 idx.replace(od_itr, desc);
118}
119
120
121std::list<std::string>
123 std::list<uint32_t> ids = getVendorIds();
124 std::list<std::string> names;
125 for (std::list<uint32_t>::const_iterator id = ids.begin();
126 id != ids.end(); ++id) {
127 std::ostringstream s;
128 // Vendor space name is constructed as "vendor-XYZ" where XYZ is an
129 // uint32_t value, without leading zeros.
130 s << "vendor-" << *id;
131 names.push_back(s.str());
132 }
133 return (names);
134}
135
136void
138 // First we merge our options into other.
139 // This adds my options that are not
140 // in other, to other (i.e we skip over
141 // duplicates).
142 mergeTo(other);
143
144 // Create option instances based on the given definitions.
145 other.createOptions(cfg_def);
146
147 // Next we copy "other" on top of ourself.
148 other.copyTo(*this);
149}
150
151void
153 // Iterate over all the option descriptors in
154 // all the spaces and instantiate the options
155 // based on the given definitions.
156 for (auto space : getOptionSpaceNames()) {
157 for (auto opt_desc : *(getAll(space))) {
158 if (createDescriptorOption(cfg_def, space, opt_desc)) {
159 // Option was recreated, let's replace the descriptor.
160 replace(opt_desc,space);
161 }
162 }
163 }
164}
165
166bool
167CfgOption::createDescriptorOption(CfgOptionDefPtr cfg_def, const std::string& space,
168 OptionDescriptor& opt_desc) {
169 if (!opt_desc.option_) {
171 "validateCreateOption: descriptor has no option instance");
172 }
173
174 Option::Universe universe = opt_desc.option_->getUniverse();
175 uint16_t code = opt_desc.option_->getType();
176
177 // Find the option's defintion, if it has one.
178 // First, check for a standard definition.
180
181 // If there is no standard definition but the option is vendor specific,
182 // we should search the definition within the vendor option space.
183 if (!def && (space != DHCP4_OPTION_SPACE) && (space != DHCP6_OPTION_SPACE)) {
184 uint32_t vendor_id = LibDHCP::optionSpaceToVendorId(space);
185 if (vendor_id > 0) {
186 def = LibDHCP::getVendorOptionDef(universe, vendor_id, code);
187 }
188 }
189
190 // Still haven't found the definition, so look for custom
191 // definition in the given set of configured definitions
192 if (!def) {
193 def = cfg_def->get(space, code);
194 }
195
196 std::string& formatted_value = opt_desc.formatted_value_;
197 if (!def) {
198 if (!formatted_value.empty()) {
199 isc_throw(InvalidOperation, "option: " << space << "." << code
200 << " has a formatted value: '" << formatted_value
201 << "' but no option definition");
202 }
203
204 // If there's no definition and no formatted string, we'll
205 // settle for the generic option already in the descriptor.
206 // Indicate no-change by returning false.
207 return (false);
208 }
209
210 try {
211 // Definition found. Let's replace the generic option in
212 // the descriptor with one created based on definition's factory.
213 if (formatted_value.empty()) {
214 // No formatted value, use data stored in the generic option.
215 opt_desc.option_ = def->optionFactory(universe, code, opt_desc.option_->getData());
216 } else {
217 // Spit the value specified in comma separated values format.
218 std::vector<std::string> split_vec;
219 boost::split(split_vec, formatted_value, boost::is_any_of(","));
220 opt_desc.option_ = def->optionFactory(universe, code, split_vec);
221 }
222 } catch (const std::exception& ex) {
223 isc_throw(InvalidOperation, "could not create option: " << space << "." << code
224 << " from data specified, reason: " << ex.what());
225 }
226
227 // Indicate we replaced the definition.
228 return(true);
229}
230
231void
233 // Merge non-vendor options.
234 mergeInternal(options_, other.options_);
235 // Merge vendor options.
236 mergeInternal(vendor_options_, other.vendor_options_);
237}
238
239void
241 // Remove any existing data in the destination.
242 other.options_.clearItems();
243 other.vendor_options_.clearItems();
244 mergeTo(other);
245}
246
247void
249 // Append sub-options to the top level "dhcp4" option space.
250 encapsulateInternal(DHCP4_OPTION_SPACE);
251 // Append sub-options to the top level "dhcp6" option space.
252 encapsulateInternal(DHCP6_OPTION_SPACE);
253}
254
255void
256CfgOption::encapsulateInternal(const std::string& option_space) {
257 // Get all options for the particular option space.
258 OptionContainerPtr options = getAll(option_space);
259 // For each option in the option space we will append sub-options
260 // from the option spaces they encapsulate.
261 for (OptionContainer::const_iterator opt = options->begin();
262 opt != options->end(); ++opt) {
263 encapsulateInternal(opt->option_);
264 }
265}
266
267void
268CfgOption::encapsulateInternal(const OptionPtr& option) {
269 // Get encapsulated option space for the option.
270 const std::string& encap_space = option->getEncapsulatedSpace();
271 // Empty value means that no option space is encapsulated.
272 if (!encap_space.empty()) {
273 // Retrieve all options from the encapsulated option space.
274 OptionContainerPtr encap_options = getAll(encap_space);
275 for (OptionContainer::const_iterator encap_opt =
276 encap_options->begin(); encap_opt != encap_options->end();
277 ++encap_opt) {
278 // Add sub-option if there isn't one added already.
279 if (!option->getOption(encap_opt->option_->getType())) {
280 option->addOption(encap_opt->option_);
281 }
282 // This is a workaround for preventing infinite recursion when
283 // trying to encapsulate options created with default global option
284 // spaces.
285 if (encap_space != DHCP4_OPTION_SPACE &&
286 encap_space != DHCP6_OPTION_SPACE) {
287 encapsulateInternal(encap_opt->option_);
288 }
289 }
290 }
291}
292
293template <typename Selector>
294void
295CfgOption::mergeInternal(const OptionSpaceContainer<OptionContainer,
296 OptionDescriptor, Selector>& src_container,
297 OptionSpaceContainer<OptionContainer,
298 OptionDescriptor, Selector>& dest_container) const {
299 // Get all option spaces used in source container.
300 std::list<Selector> selectors = src_container.getOptionSpaceNames();
301
302 // For each space in the source container retrieve the actual options and
303 // match them with the options held in the destination container under
304 // the same space.
305 for (typename std::list<Selector>::const_iterator it = selectors.begin();
306 it != selectors.end(); ++it) {
307 // Get all options in the destination container for the particular
308 // option space.
309 OptionContainerPtr dest_all = dest_container.getItems(*it);
310 OptionContainerPtr src_all = src_container.getItems(*it);
311 // For each option under this option space check if there is a
312 // corresponding option in the destination container. If not,
313 // add one.
314 for (OptionContainer::const_iterator src_opt = src_all->begin();
315 src_opt != src_all->end(); ++src_opt) {
316 const OptionContainerTypeIndex& idx = dest_all->get<1>();
317 const OptionContainerTypeRange& range =
318 idx.equal_range(src_opt->option_->getType());
319 // If there is no such option in the destination container,
320 // add one.
321 if (std::distance(range.first, range.second) == 0) {
322 dest_container.addItem(OptionDescriptor(*src_opt), *it);
323 }
324 }
325 }
326}
327
328
330CfgOption::getAll(const std::string& option_space) const {
331 return (options_.getItems(option_space));
332}
333
335CfgOption::getAll(const uint32_t vendor_id) const {
336 return (vendor_options_.getItems(vendor_id));
337}
338
339size_t
340CfgOption::del(const std::string& option_space, const uint16_t option_code) {
341 // Check for presence of options.
342 OptionContainerPtr options = getAll(option_space);
343 if (!options || options->empty()) {
344 // There are no options, so there is nothing to do.
345 return (0);
346 }
347
348 // If this is not top level option we may also need to delete the
349 // option instance from options encapsulating the particular option
350 // space.
351 if ((option_space != DHCP4_OPTION_SPACE) &&
352 (option_space != DHCP6_OPTION_SPACE)) {
353 // For each option space name iterate over the existing options.
354 auto option_space_names = getOptionSpaceNames();
355 for (auto option_space_from_list : option_space_names) {
356 // Get all options within the particular option space.
357 auto options_in_space = getAll(option_space_from_list);
358 for (auto option_it = options_in_space->begin();
359 option_it != options_in_space->end();
360 ++option_it) {
361
362 // Check if the option encapsulates our option space and
363 // it does, try to delete our option.
364 if (option_it->option_ &&
365 (option_it->option_->getEncapsulatedSpace() == option_space)) {
366 option_it->option_->delOption(option_code);
367 }
368 }
369 }
370 }
371
372 auto& idx = options->get<1>();
373 return (idx.erase(option_code));
374}
375
376size_t
377CfgOption::del(const uint32_t vendor_id, const uint16_t option_code) {
378 // Check for presence of options.
379 OptionContainerPtr vendor_options = getAll(vendor_id);
380 if (!vendor_options || vendor_options->empty()) {
381 // There are no options, so there is nothing to do.
382 return (0);
383 }
384
385 auto& idx = vendor_options->get<1>();
386 return (idx.erase(option_code));
387}
388
389size_t
390CfgOption::del(const uint64_t id) {
391 // Hierarchical nature of the options configuration requires that
392 // we go over all options and decapsulate them before removing
393 // any of them. Let's walk over the existing option spaces.
394 for (auto space_name : getOptionSpaceNames()) {
395 // Get all options for the option space.
396 auto options = getAll(space_name);
397 for (auto option_it = options->begin(); option_it != options->end();
398 ++option_it) {
399 if (!option_it->option_) {
400 continue;
401 }
402
403 // For each option within the option space we need to dereference
404 // any existing sub options.
405 auto sub_options = option_it->option_->getOptions();
406 for (auto sub = sub_options.begin(); sub != sub_options.end();
407 ++sub) {
408 // Dereference sub option.
409 option_it->option_->delOption(sub->second->getType());
410 }
411 }
412 }
413
414 // Now that we got rid of dependencies between the instances of the options
415 // we can delete all options having a specified id.
416 size_t num_deleted = options_.deleteItems(id) + vendor_options_.deleteItems(id);
417
418 // Let's encapsulate those options that remain in the configuration.
419 encapsulate();
420
421 // Return the number of deleted options.
422 return (num_deleted);
423}
424
427 return (toElementWithMetadata(false));
428}
429
431CfgOption::toElementWithMetadata(const bool include_metadata) const {
432 // option-data value is a list of maps
433 ElementPtr result = Element::createList();
434 // Iterate first on options using space names
435 const std::list<std::string>& names = options_.getOptionSpaceNames();
436 for (std::list<std::string>::const_iterator name = names.begin();
437 name != names.end(); ++name) {
438 OptionContainerPtr opts = getAll(*name);
439 for (OptionContainer::const_iterator opt = opts->begin();
440 opt != opts->end(); ++opt) {
441 // Get and fill the map for this option
442 ElementPtr map = Element::createMap();
443 // Set user context
444 opt->contextToElement(map);
445 // Set space from parent iterator
446 map->set("space", Element::create(*name));
447 // Set the code
448 uint16_t code = opt->option_->getType();
449 map->set("code", Element::create(code));
450 // Set the name (always for standard options else when asked for)
452 if (!def) {
453 def = LibDHCP::getRuntimeOptionDef(*name, code);
454 }
455 if (!def) {
456 def = LibDHCP::getLastResortOptionDef(*name, code);
457 }
458 if (def) {
459 map->set("name", Element::create(def->getName()));
460 }
461 // Set the data item
462 if (!opt->formatted_value_.empty()) {
463 map->set("csv-format", Element::create(true));
464 map->set("data", Element::create(opt->formatted_value_));
465 } else {
466 map->set("csv-format", Element::create(false));
467 std::vector<uint8_t> bin = opt->option_->toBinary();
468 std::string repr = util::encode::encodeHex(bin);
469 map->set("data", Element::create(repr));
470 }
471 // Set the persistency flag
472 map->set("always-send", Element::create(opt->persistent_));
473
474 // Include metadata if requested.
475 if (include_metadata) {
476 map->set("metadata", opt->getMetadata());
477 }
478
479 // Push on the list
480 result->add(map);
481 }
482 }
483 // Iterate first on vendor_options using vendor ids
484 const std::list<uint32_t>& ids = vendor_options_.getOptionSpaceNames();
485 for (std::list<uint32_t>::const_iterator id = ids.begin();
486 id != ids.end(); ++id) {
487 OptionContainerPtr opts = getAll(*id);
488 for (OptionContainer::const_iterator opt = opts->begin();
489 opt != opts->end(); ++opt) {
490 // Get and fill the map for this option
491 ElementPtr map = Element::createMap();
492 // Set user context
493 opt->contextToElement(map);
494 // Set space from parent iterator
495 std::ostringstream oss;
496 oss << "vendor-" << *id;
497 map->set("space", Element::create(oss.str()));
498 // Set the code
499 uint16_t code = opt->option_->getType();
500 map->set("code", Element::create(code));
501 // Set the name
502 Option::Universe universe = opt->option_->getUniverse();
504 LibDHCP::getVendorOptionDef(universe, *id, code);
505 if (!def) {
506 // vendor-XXX space is in oss
507 def = LibDHCP::getRuntimeOptionDef(oss.str(), code);
508 }
509 if (def) {
510 map->set("name", Element::create(def->getName()));
511 }
512 // Set the data item
513 if (!opt->formatted_value_.empty()) {
514 map->set("csv-format", Element::create(true));
515 map->set("data", Element::create(opt->formatted_value_));
516 } else {
517 map->set("csv-format", Element::create(false));
518 std::vector<uint8_t> bin = opt->option_->toBinary();
519 std::string repr = util::encode::encodeHex(bin);
520 map->set("data", Element::create(repr));
521 }
522 // Set the persistency flag
523 map->set("always-send", Element::create(opt->persistent_));
524 // Push on the list
525 result->add(map);
526 }
527 }
528 return (result);
529}
530
531
532} // namespace dhcp
533} // namespace isc
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
A generic exception that is thrown if a function is called in a prohibited way.
void setId(const uint64_t id)
Sets element's database identifier.
Represents option data configuration for the DHCP server.
Definition: cfg_option.h:314
void encapsulate()
Appends encapsulated options to top-level options.
Definition: cfg_option.cc:248
void replace(const OptionDescriptor &desc, const std::string &option_space)
Replaces the instance of an option within this collection.
Definition: cfg_option.cc:96
static bool createDescriptorOption(CfgOptionDefPtr cfg_def, const std::string &space, OptionDescriptor &opt_desc)
Creates an option descriptor's option based on a set of option defs.
Definition: cfg_option.cc:167
virtual isc::data::ElementPtr toElement() const
Unparse a configuration object.
Definition: cfg_option.cc:426
void createOptions(CfgOptionDefPtr cfg_def)
Re-create the option in each descriptor based on given definitions.
Definition: cfg_option.cc:152
isc::data::ElementPtr toElementWithMetadata(const bool include_metadata) const
Unparse a configuration object with optionally including the metadata.
Definition: cfg_option.cc:431
size_t del(const std::string &option_space, const uint16_t option_code)
Deletes option for the specified option space and option code.
Definition: cfg_option.cc:340
std::list< std::string > getOptionSpaceNames() const
Returns a list of configured option space names.
Definition: cfg_option.h:613
bool empty() const
Indicates the object is empty.
Definition: cfg_option.cc:56
void mergeTo(CfgOption &other) const
Merges this configuration to another configuration.
Definition: cfg_option.cc:232
void copyTo(CfgOption &other) const
Copies this configuration to another configuration.
Definition: cfg_option.cc:240
CfgOption()
default constructor
Definition: cfg_option.cc:52
std::list< std::string > getVendorIdsSpaceNames() const
Returns a list of option space names for configured vendor ids.
Definition: cfg_option.cc:122
OptionContainerPtr getAll(const std::string &option_space) const
Returns all options for the specified option space.
Definition: cfg_option.cc:330
void merge(CfgOptionDefPtr cfg_def, CfgOption &other)
Merges another option configuration into this one.
Definition: cfg_option.cc:137
void add(const OptionPtr &option, const bool persistent, const std::string &option_space, const uint64_t id=0)
Adds instance of the option to the configuration.
Definition: cfg_option.cc:67
bool equals(const CfgOption &other) const
Check if configuration is equal to other configuration.
Definition: cfg_option.cc:61
std::list< uint32_t > getVendorIds() const
Returns a list of all configured vendor identifiers.
Definition: cfg_option.h:618
static OptionDefinitionPtr getOptionDef(const std::string &space, const uint16_t code)
Return the first option definition matching a particular option code.
Definition: libdhcp++.cc:122
static OptionDefinitionPtr getVendorOptionDef(const Option::Universe u, const uint32_t vendor_id, const uint16_t code)
Returns vendor option definition for a given vendor-id and code.
Definition: libdhcp++.cc:164
static uint32_t optionSpaceToVendorId(const std::string &option_space)
Converts option space name to vendor id.
Definition: libdhcp++.cc:925
static OptionDefinitionPtr getRuntimeOptionDef(const std::string &space, const uint16_t code)
Returns runtime (non-standard) option definition by space and option code.
Definition: libdhcp++.cc:185
static OptionDefinitionPtr getLastResortOptionDef(const std::string &space, const uint16_t code)
Returns last resort option definition by space and option code.
Definition: libdhcp++.cc:245
Option descriptor.
Definition: cfg_option.h:42
OptionPtr option_
Option instance.
Definition: cfg_option.h:45
std::string space_name_
Option space name.
Definition: cfg_option.h:77
bool equals(const OptionDescriptor &other) const
Checks if the one descriptor is equal to another.
Definition: cfg_option.cc:45
std::string formatted_value_
Option value in textual (CSV) format.
Definition: cfg_option.h:66
static OptionDescriptorPtr create(const OptionPtr &opt, bool persist, const std::string &formatted_value="", data::ConstElementPtr user_context=data::ConstElementPtr())
Factory function creating an instance of the OptionDescriptor.
Definition: cfg_option.cc:27
bool persistent_
Persistence flag.
Definition: cfg_option.h:51
uint64_t deleteItems(const uint64_t id)
Remove all options or option definitions with a given database identifier.
void addItem(const ItemType &item, const Selector &option_space)
Adds a new item to the option_space.
bool empty() const
Indicates the container is empty.
void clearItems()
Remove all items from the container.
std::list< Selector > getOptionSpaceNames() const
Get a list of existing option spaces.
ItemsContainerPtr getItems(const Selector &option_space) const
Get all items for the particular option space.
bool equals(const OptionSpaceContainer &other) const
Check if two containers are equal.
static bool validateName(const std::string &name)
Checks that the provided option space name is valid.
Definition: option_space.cc:26
Universe
defines option universe DHCPv4 or DHCPv6
Definition: option.h:82
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:27
boost::shared_ptr< Element > ElementPtr
Definition: data.h:24
std::pair< OptionContainerTypeIndex::const_iterator, OptionContainerTypeIndex::const_iterator > OptionContainerTypeRange
Pair of iterators to represent the range of options having the same option type value.
Definition: cfg_option.h:279
OptionContainer::nth_index< 1 >::type OptionContainerTypeIndex
Type of the index #1 - option type.
Definition: cfg_option.h:274
boost::shared_ptr< CfgOptionDef > CfgOptionDefPtr
Non-const pointer.
boost::shared_ptr< OptionDefinition > OptionDefinitionPtr
Pointer to option definition object.
boost::multi_index_container< OptionDescriptor, boost::multi_index::indexed_by< boost::multi_index::sequenced<>, boost::multi_index::hashed_non_unique< KeyFromKeyExtractor< boost::multi_index::const_mem_fun< Option, uint16_t, &Option::getType >, boost::multi_index::member< OptionDescriptor, OptionPtr, &OptionDescriptor::option_ > > >, boost::multi_index::hashed_non_unique< boost::multi_index::member< OptionDescriptor, bool, &OptionDescriptor::persistent_ > >, boost::multi_index::ordered_non_unique< boost::multi_index::const_mem_fun< data::BaseStampedElement, boost::posix_time::ptime, &data::BaseStampedElement::getModificationTime > >, boost::multi_index::hashed_non_unique< boost::multi_index::tag< OptionIdIndexTag >, boost::multi_index::const_mem_fun< data::BaseStampedElement, uint64_t, &data::BaseStampedElement::getId > > > > OptionContainer
Multi index container for DHCP option descriptors.
Definition: cfg_option.h:269
boost::shared_ptr< OptionDescriptor > OptionDescriptorPtr
A pointer to option descriptor.
Definition: cfg_option.h:31
boost::shared_ptr< OptionContainer > OptionContainerPtr
Pointer to the OptionContainer object.
Definition: cfg_option.h:272
boost::shared_ptr< Option > OptionPtr
Definition: option.h:36
string encodeHex(const vector< uint8_t > &binary)
Encode binary data in the base16 ('hex') format.
Definition: base_n.cc:469
Defines the logger used by the top-level component of kea-lfc.
#define DHCP4_OPTION_SPACE
global std option spaces
#define DHCP6_OPTION_SPACE