From: Francis Dupont Date: Sun, 31 Jul 2022 18:05:41 +0000 (+0200) Subject: [#2517] Revamped option sending X-Git-Tag: Kea-2.3.0~62 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=db67edd009992905d46d544decb065e00509a55e;p=thirdparty%2Fkea.git [#2517] Revamped option sending --- diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc index 1ad99099ea..118ba2128e 100644 --- a/src/bin/dhcp6/dhcp6_srv.cc +++ b/src/bin/dhcp6/dhcp6_srv.cc @@ -76,6 +76,7 @@ #include #include #include +#include #include using namespace isc; @@ -1469,7 +1470,7 @@ Dhcpv6Srv::appendRequestedOptions(const Pkt6Ptr& question, Pkt6Ptr& answer, return; } - std::vector requested_opts; + set requested_opts; // Client requests some options using ORO option. Try to // get this option from client's message. @@ -1479,8 +1480,13 @@ Dhcpv6Srv::appendRequestedOptions(const Pkt6Ptr& question, Pkt6Ptr& answer, // Get the list of options that client requested. if (option_oro) { - requested_opts = option_oro->getValues(); + set oro_req_opts; + for (uint16_t code : option_oro->getValues()) { + static_cast(oro_req_opts.insert(code)); + } + requested_opts = oro_req_opts; } + // Iterate on the configured option list to add persistent options for (CfgOptionList::const_iterator copts = co_list.begin(); copts != co_list.end(); ++copts) { @@ -1495,7 +1501,8 @@ Dhcpv6Srv::appendRequestedOptions(const Pkt6Ptr& question, Pkt6Ptr& answer, desc != range.second; ++desc) { // Add the persistent option code to requested options if (desc->option_) { - requested_opts.push_back(desc->option_->getType()); + uint16_t code = desc->option_->getType(); + static_cast(requested_opts.insert(code)); } } } @@ -1515,6 +1522,43 @@ Dhcpv6Srv::appendRequestedOptions(const Pkt6Ptr& question, Pkt6Ptr& answer, } } } + + // Special cases for vendor class. Vendor options are done later. + if (requested_opts.count(D6O_VENDOR_CLASS) > 0) { + set vendor_ids; + for (auto opt : answer->getOptions(D6O_VENDOR_CLASS)) { + if (opt.first != D6O_VENDOR_CLASS) { + continue; + } + OptionVendorClassPtr vendor_class; + vendor_class = boost::dynamic_pointer_cast(opt.second); + if (vendor_class) { + int32_t vendor_id = vendor_class->getVendorId(); + static_cast(vendor_ids.insert(vendor_id)); + } + } + // Iterate on the configured option list + for (CfgOptionList::const_iterator copts = co_list.begin(); + copts != co_list.end(); ++copts) { + for (OptionDescriptor desc : (*copts)->getList(DHCP6_OPTION_SPACE, + D6O_VENDOR_CLASS)) { + if (!desc.option_) { + continue; + } + OptionVendorClassPtr vendor_class = + boost::dynamic_pointer_cast(desc.option_); + if (!vendor_class) { + continue; + } + // Is the vendor id already in the response? + if (vendor_ids.count(vendor_class->getVendorId()) > 0) { + continue; + } + // Got it: add it. + answer->addOption(desc.option_); + } + } + } } void @@ -1534,51 +1578,72 @@ Dhcpv6Srv::appendRequestedVendorOptions(const Pkt6Ptr& question, return; } - uint32_t vendor_id = 0; + set vendor_ids; // The server could have provided the option using client classification or - // hooks. If there's a vendor info option in the response already, use that. - OptionVendorPtr vendor_rsp(boost::dynamic_pointer_cast( - answer->getOption(D6O_VENDOR_OPTS))); - if (vendor_rsp) { - vendor_id = vendor_rsp->getVendorId(); + // hooks. If there're vendor info options in the response already, use them. + map vendor_rsps; + for (auto opt : answer->getOptions(D6O_VENDOR_OPTS)) { + if (opt.first != D6O_VENDOR_OPTS) { + continue; + } + OptionVendorPtr vendor_rsp; + vendor_rsp = boost::dynamic_pointer_cast(opt.second); + if (vendor_rsp) { + uint32_t vendor_id = vendor_rsp->getVendorId(); + vendor_rsps[vendor_id] = vendor_rsp; + static_cast(vendor_ids.insert(vendor_id)); + } } // Otherwise, try to get the vendor-id from the client packet's // vendor-specific information option (17). - OptionVendorPtr vendor_req; - if (vendor_id == 0) { - vendor_req = boost::dynamic_pointer_cast( - question->getOption(D6O_VENDOR_OPTS)); - if (vendor_req) { - vendor_id = vendor_req->getVendorId(); + map vendor_reqs; + if (vendor_ids.empty()) { + for (auto opt : question->getOptions(D6O_VENDOR_OPTS)) { + if (opt.first != D6O_VENDOR_OPTS) { + continue; + } + OptionVendorPtr vendor_req; + vendor_req = boost::dynamic_pointer_cast(opt.second); + if (vendor_req) { + uint32_t vendor_id = vendor_req->getVendorId(); + vendor_reqs[vendor_id] = vendor_req; + static_cast(vendor_ids.insert(vendor_id)); + } } } // Finally, try to get the vendor-id from the client packet's vendor-class // option (16). - if (vendor_id == 0) { - OptionVendorClassPtr vendor_class( - boost::dynamic_pointer_cast( - question->getOption(D6O_VENDOR_CLASS))); - if (vendor_class) { - vendor_id = vendor_class->getVendorId(); + if (vendor_ids.empty()) { + for (auto opt : question->getOptions(D6O_VENDOR_CLASS)) { + if (opt.first != D6O_VENDOR_CLASS) { + continue; + } + OptionVendorClassPtr vendor_class; + vendor_class = boost::dynamic_pointer_cast(opt.second); + if (vendor_class) { + uint32_t vendor_id = vendor_class->getVendorId(); + static_cast(vendor_ids.insert(vendor_id)); + } } } // If there's no vendor option in either request or response, then there's no way - // to figure out what the vendor-id value is and we give up. - if (vendor_id == 0) { + // to figure out what the vendor-id values are and we give up. + if (vendor_ids.empty()) { return; } - std::vector requested_opts; + map > requested_opts; // Let's try to get ORO within that vendor-option. // This is specific to vendor-id=4491 (Cable Labs). Other vendors may have // different policies. OptionUint16ArrayPtr oro; - if (vendor_id == VENDOR_ID_CABLE_LABS && vendor_req) { + if (vendor_reqs.count(VENDOR_ID_CABLE_LABS) > 0) { + OptionVendorPtr vendor_req = vendor_reqs[VENDOR_ID_CABLE_LABS]; OptionPtr oro_generic = vendor_req->getOption(DOCSIS3_V6_ORO); if (oro_generic) { // Vendor ID 4491 makes Kea look at DOCSIS3_V6_OPTION_DEFINITIONS @@ -1586,63 +1651,74 @@ Dhcpv6Srv::appendRequestedVendorOptions(const Pkt6Ptr& question, // created as an OptionUint16Array, but might not be for other // vendor IDs. oro = boost::dynamic_pointer_cast(oro_generic); - if (oro) { - requested_opts = oro->getValues(); + } + if (oro) { + set oro_req_opts; + for (uint16_t code : oro->getValues()) { + static_cast(oro_req_opts.insert(code)); } + requested_opts[VENDOR_ID_CABLE_LABS] = oro_req_opts; } } // Iterate on the configured option list to add persistent options - for (CfgOptionList::const_iterator copts = co_list.begin(); - copts != co_list.end(); ++copts) { - const OptionContainerPtr& opts = (*copts)->getAll(vendor_id); - if (!opts) { - continue; - } - // Get persistent options - const OptionContainerPersistIndex& idx = opts->get<2>(); - const OptionContainerPersistRange& range = idx.equal_range(true); - for (OptionContainerPersistIndex::const_iterator desc = range.first; - desc != range.second; ++desc) { - // Add the persistent option code to requested options - if (desc->option_) { - requested_opts.push_back(desc->option_->getType()); + for (uint32_t vendor_id : vendor_ids) { + for (CfgOptionList::const_iterator copts = co_list.begin(); + copts != co_list.end(); ++copts) { + const OptionContainerPtr& opts = (*copts)->getAll(vendor_id); + if (!opts) { + continue; + } + // Get persistent options + const OptionContainerPersistIndex& idx = opts->get<2>(); + const OptionContainerPersistRange& range = idx.equal_range(true); + for (OptionContainerPersistIndex::const_iterator desc = range.first; + desc != range.second; ++desc) { + if (!desc->option_) { + continue; + } + // Add the persistent option code to requested options + uint16_t code = desc->option_->getType(); + static_cast(requested_opts[vendor_id].insert(code)); } } - } - // If there is nothing to add don't do anything then. - if (requested_opts.empty()) { - return; - } + // If there is nothing to add don't do anything then with this vendor. + if (requested_opts[vendor_id].empty()) { + continue; + } - if (!vendor_rsp) { // It's possible that the vendor opts option was inserted already // by client class or a hook. If that is so, let's use it. - vendor_rsp.reset(new OptionVendor(Option::V6, vendor_id)); - } - - // Get the list of options that client requested. - bool added = false; + OptionVendorPtr vendor_rsp; + if (vendor_rsps.count(vendor_id) > 0) { + vendor_rsp = vendor_rsps[vendor_id]; + } else { + vendor_rsp.reset(new OptionVendor(Option::V6, vendor_id)); + } - for (uint16_t opt : requested_opts) { - if (!vendor_rsp->getOption(opt)) { - for (CfgOptionList::const_iterator copts = co_list.begin(); - copts != co_list.end(); ++copts) { - OptionDescriptor desc = (*copts)->get(vendor_id, opt); - if (desc.option_) { - vendor_rsp->addOption(desc.option_); - added = true; - break; + // Get the list of options that client requested. + bool added = false; + + for (uint16_t opt : requested_opts[vendor_id]) { + if (!vendor_rsp->getOption(opt)) { + for (CfgOptionList::const_iterator copts = co_list.begin(); + copts != co_list.end(); ++copts) { + OptionDescriptor desc = (*copts)->get(vendor_id, opt); + if (desc.option_) { + vendor_rsp->addOption(desc.option_); + added = true; + break; + } } } } - } - // If we added some sub-options and the vendor opts option is not in - // the response already, then add it. - if (added && !answer->getOption(D6O_VENDOR_OPTS)) { - answer->addOption(vendor_rsp); + // If we added some sub-options and the vendor opts option is not in + // the response already, then add it. + if (added && (vendor_rsps.count(vendor_id) == 0)) { + answer->addOption(vendor_rsp); + } } } @@ -3871,24 +3947,28 @@ Dhcpv6Srv::processDhcp4Query(const Pkt6Ptr& dhcp4_query) { } void Dhcpv6Srv::classifyByVendor(const Pkt6Ptr& pkt, std::string& classes) { - OptionVendorClassPtr vclass = boost::dynamic_pointer_cast< - OptionVendorClass>(pkt->getOption(D6O_VENDOR_CLASS)); - - if (!vclass || vclass->getTuplesNum() == 0) { - return; - } + OptionVendorClassPtr vclass; + for (auto opt : pkt->getOptions(D6O_VENDOR_CLASS)) { + if (opt.first != D6O_VENDOR_CLASS) { + continue; + } + vclass = boost::dynamic_pointer_cast(opt.second); + if (!vclass || vclass->getTuplesNum() == 0) { + continue; + } - if (vclass->hasTuple(DOCSIS3_CLASS_MODEM)) { - pkt->addClass(VENDOR_CLASS_PREFIX + DOCSIS3_CLASS_MODEM); - classes += VENDOR_CLASS_PREFIX + DOCSIS3_CLASS_MODEM + " "; + if (vclass->hasTuple(DOCSIS3_CLASS_MODEM)) { + pkt->addClass(VENDOR_CLASS_PREFIX + DOCSIS3_CLASS_MODEM); + classes += VENDOR_CLASS_PREFIX + DOCSIS3_CLASS_MODEM + " "; - } else if (vclass->hasTuple(DOCSIS3_CLASS_EROUTER)) { - pkt->addClass(VENDOR_CLASS_PREFIX + DOCSIS3_CLASS_EROUTER); - classes += VENDOR_CLASS_PREFIX + DOCSIS3_CLASS_EROUTER + " "; + } else if (vclass->hasTuple(DOCSIS3_CLASS_EROUTER)) { + pkt->addClass(VENDOR_CLASS_PREFIX + DOCSIS3_CLASS_EROUTER); + classes += VENDOR_CLASS_PREFIX + DOCSIS3_CLASS_EROUTER + " "; - } else { - pkt->addClass(VENDOR_CLASS_PREFIX + vclass->getTuple(0).getText()); - classes + VENDOR_CLASS_PREFIX + vclass->getTuple(0).getText() + " "; + } else { + pkt->addClass(VENDOR_CLASS_PREFIX + vclass->getTuple(0).getText()); + classes + VENDOR_CLASS_PREFIX + vclass->getTuple(0).getText() + " "; + } } } diff --git a/src/bin/dhcp6/dhcp6_srv.h b/src/bin/dhcp6/dhcp6_srv.h index 2f095158a6..e6a0744d55 100644 --- a/src/bin/dhcp6/dhcp6_srv.h +++ b/src/bin/dhcp6/dhcp6_srv.h @@ -821,7 +821,7 @@ protected: /// @brief Assigns incoming packet to zero or more classes. /// /// @note This is done in two phases: first the content of the - /// vendor-class-identifier option is used as a class, by + /// vendor-class-identifier options are used as classes, by /// calling @ref classifyByVendor(). Second, the classification match /// expressions are evaluated. The resulting classes will be stored /// in the packet (see @ref isc::dhcp::Pkt6::classes_ and @@ -1042,7 +1042,7 @@ public: private: /// @public - /// @brief Assign class using vendor-class-identifier option + /// @brief Assign class using vendor-class-identifier options /// /// @note This is the first part of @ref classifyPacket /// diff --git a/src/lib/dhcp/pkt6.cc b/src/lib/dhcp/pkt6.cc index 55d3b5f6bd..701ea3916b 100644 --- a/src/lib/dhcp/pkt6.cc +++ b/src/lib/dhcp/pkt6.cc @@ -168,6 +168,71 @@ Pkt6::getAnyRelayOption(const uint16_t option_code, return (OptionPtr()); } +OptionCollection +Pkt6::getNonCopiedAnyRelayOptions(const uint16_t option_code, + const RelaySearchOrder& order) const { + if (relay_info_.empty()) { + // There's no relay info, this is a direct message + return (OptionCollection()); + } + + int start = 0; // First relay to check + int end = 0; // Last relay to check + int direction = 0; // How we going to iterate: forward or backward? + + prepareGetAnyRelayOption(order, start, end, direction); + + // This is a tricky loop. It must go from start to end, but it must work in + // both directions (start > end; or start < end). We can't use regular + // exit condition, because we don't know whether to use i <= end or i >= end. + // That's why we check if in the next iteration we would go past the + // list (end + direction). It is similar to STL concept of end pointing + // to a place after the last element + for (int i = start; i != end + direction; i += direction) { + OptionCollection opts = getNonCopiedRelayOptions(option_code, i); + if (!opts.empty()) { + return (opts); + } + } + + // We iterated over specified relays and haven't found what we were + // looking for + return (OptionCollection()); +} + +OptionCollection +Pkt6::getAnyRelayOptions(const uint16_t option_code, + const RelaySearchOrder& order) { + + if (relay_info_.empty()) { + // There's no relay info, this is a direct message + return (OptionCollection()); + } + + int start = 0; // First relay to check + int end = 0; // Last relay to check + int direction = 0; // How we going to iterate: forward or backward? + + prepareGetAnyRelayOption(order, start, end, direction); + + // This is a tricky loop. It must go from start to end, but it must work in + // both directions (start > end; or start < end). We can't use regular + // exit condition, because we don't know whether to use i <= end or i >= end. + // That's why we check if in the next iteration we would go past the + // list (end + direction). It is similar to STL concept of end pointing + // to a place after the last element + for (int i = start; i != end + direction; i += direction) { + OptionCollection opts = getRelayOptions(option_code, i); + if (!opts.empty()) { + return (opts); + } + } + + // We iterated over specified relays and haven't found what we were + // looking for + return (OptionCollection()); +} + OptionPtr Pkt6::getNonCopiedRelayOption(const uint16_t opt_type, const uint8_t relay_level) const { @@ -207,6 +272,51 @@ Pkt6::getRelayOption(const uint16_t opt_type, const uint8_t relay_level) { return (OptionPtr()); } +OptionCollection +Pkt6::getNonCopiedRelayOptions(const uint16_t opt_type, + const uint8_t relay_level) const { + if (relay_level >= relay_info_.size()) { + isc_throw(OutOfRange, "This message was relayed " + << relay_info_.size() << " time(s)." + << " There is no info about " + << relay_level + 1 << " relay."); + } + + std::pair range = + relay_info_[relay_level].options_.equal_range(opt_type); + return (OptionCollection(range.first, range.second)); +} + +OptionCollection +Pkt6::getRelayOptions(const uint16_t opt_type, + const uint8_t relay_level) { + if (relay_level >= relay_info_.size()) { + isc_throw(OutOfRange, "This message was relayed " + << relay_info_.size() << " time(s)." + << " There is no info about " + << relay_level + 1 << " relay."); + } + + OptionCollection options_copy; + + std::pair range = + relay_info_[relay_level].options_.equal_range(opt_type); + // If options should be copied on retrieval, we should now iterate over + // matching options, copy them and replace the original ones with new + // instances. + if (copy_retrieved_options_) { + for (OptionCollection::iterator opt_it = range.first; + opt_it != range.second; ++opt_it) { + OptionPtr option_copy = opt_it->second->clone(); + opt_it->second = option_copy; + } + } + // Finally, return updated options. This can also be empty in some cases. + return (OptionCollection(range.first, range.second)); +} + const isc::asiolink::IOAddress& Pkt6::getRelay6LinkAddress(uint8_t relay_level) const { if (relay_level >= relay_info_.size()) { @@ -846,11 +956,16 @@ Pkt6::getMACFromIPv6RelayOpt() { HWAddrPtr Pkt6::getMACFromDocsisModem() { HWAddrPtr mac; - OptionVendorPtr vendor = boost::dynamic_pointer_cast< - OptionVendor>(getNonCopiedOption(D6O_VENDOR_OPTS)); - - // Check if this is indeed DOCSIS3 environment - if (vendor && vendor->getVendorId() == VENDOR_ID_CABLE_LABS) { + OptionVendorPtr vendor; + for (auto opt : getNonCopiedOptions(D6O_VENDOR_OPTS)) { + if (opt.first != D6O_VENDOR_OPTS) { + continue; + } + vendor = boost::dynamic_pointer_cast< OptionVendor>(opt.second); + // Check if this is indeed DOCSIS3 environment + if (!vendor || vendor->getVendorId() != VENDOR_ID_CABLE_LABS) { + continue; + } // If it is, try to get device-id option OptionPtr device_id = vendor->getOption(DOCSIS3_V6_DEVICE_ID); if (device_id) { @@ -858,6 +973,7 @@ Pkt6::getMACFromDocsisModem() { if (!device_id->getData().empty()) { mac.reset(new HWAddr(device_id->getData(), HTYPE_DOCSIS)); mac->source_ = HWAddr::HWADDR_SOURCE_DOCSIS_MODEM; + break; } } } @@ -867,25 +983,32 @@ Pkt6::getMACFromDocsisModem() { HWAddrPtr Pkt6::getMACFromDocsisCMTS() { - HWAddrPtr mac; + if (relay_info_.empty()) { + return (HWAddrPtr()); + } // If the message passed through a CMTS, there'll // CMTS-specific options in it. - if (!relay_info_.empty()) { - OptionVendorPtr vendor = boost::dynamic_pointer_cast< - OptionVendor>(getAnyRelayOption(D6O_VENDOR_OPTS, - RELAY_SEARCH_FROM_CLIENT)); - + HWAddrPtr mac; + OptionVendorPtr vendor; + for (auto opt : getAnyRelayOptions(D6O_VENDOR_OPTS, + RELAY_SEARCH_FROM_CLIENT)) { + if (opt.first != D6O_VENDOR_OPTS) { + continue; + } + vendor = boost::dynamic_pointer_cast< OptionVendor>(opt.second); // Check if this is indeed DOCSIS3 environment - if (vendor && vendor->getVendorId() == VENDOR_ID_CABLE_LABS) { - // Try to get cable modem mac - OptionPtr cm_mac = vendor->getOption(DOCSIS3_V6_CMTS_CM_MAC); - - // If the option contains any data, use it as MAC address - if (cm_mac && !cm_mac->getData().empty()) { - mac.reset(new HWAddr(cm_mac->getData(), HTYPE_DOCSIS)); - mac->source_ = HWAddr::HWADDR_SOURCE_DOCSIS_CMTS; - } + if (!vendor || vendor->getVendorId() != VENDOR_ID_CABLE_LABS) { + continue; + } + // Try to get cable modem mac + OptionPtr cm_mac = vendor->getOption(DOCSIS3_V6_CMTS_CM_MAC); + + // If the option contains any data, use it as MAC address + if (cm_mac && !cm_mac->getData().empty()) { + mac.reset(new HWAddr(cm_mac->getData(), HTYPE_DOCSIS)); + mac->source_ = HWAddr::HWADDR_SOURCE_DOCSIS_CMTS; + break; } } diff --git a/src/lib/dhcp/pkt6.h b/src/lib/dhcp/pkt6.h index d7448b8b39..1aa02a870b 100644 --- a/src/lib/dhcp/pkt6.h +++ b/src/lib/dhcp/pkt6.h @@ -246,10 +246,26 @@ protected: /// @param relay_level Nesting level as described for /// @ref Pkt6::getRelayOption. /// - /// @return Pointer to the option or NULL if such option doesn't exist. + /// @return Pointer to the option or null if such option doesn't exist. OptionPtr getNonCopiedRelayOption(const uint16_t opt_type, const uint8_t relay_level) const; + /// @brief Returns all option instances inserted by relay agent. + /// + /// This is a variant of the @ref Pkt6::getRelayOptions function which + /// never copies an option returned. This method should be only used by + /// the @ref Pkt6 class and derived classes. Any external callers should + /// use @ref getRelayOption which copies the option before returning it + /// when the @ref Pkt::copy_retrieved_options_ flag is set to true. + /// + /// @param opt_type Code of the requested option. + /// @param relay_level Nesting level as described for + /// @ref Pkt6::getRelayOption. + /// + /// @return Collection of options found. + OptionCollection getNonCopiedRelayOptions(const uint16_t opt_type, + const uint8_t relay_level) const; + public: /// @brief Returns option inserted by relay @@ -266,9 +282,27 @@ public: /// @param option_code code of the requested option /// @param nesting_level see description above /// - /// @return pointer to the option (or NULL if there is no such option) + /// @return pointer to the option (or null if there is no such option) OptionPtr getRelayOption(uint16_t option_code, uint8_t nesting_level); + /// @brief Returns options inserted by relay + /// + /// Returns options from specified relay scope (inserted by a given relay + /// if this is received packet or to be decapsulated by a given relay if + /// this is a transmitted packet). nesting_level specifies which relay + /// scope is to be used. 0 is the outermost encapsulation (relay closest to + /// the server). pkt->relay_info_.size() - 1 is the innermost encapsulation + /// (relay closest to the client). + /// + /// @throw isc::OutOfRange if nesting level has invalid value. + /// + /// @param option_code code of the requested option + /// @param nesting_level see description above + /// + /// @return Collection of options found. + OptionCollection getRelayOptions(uint16_t option_code, + uint8_t nesting_level); + private: /// @brief Prepares parameters for loop used in @ref getAnyRelayOption @@ -303,10 +337,25 @@ protected: /// @param option_code Searched option. /// @param order Option search order (see @ref RelaySearchOrder). /// - /// @return Option pointer or NULL, if no option matches specified criteria. + /// @return Option pointer or null, if no option matches specified criteria. OptionPtr getNonCopiedAnyRelayOption(const uint16_t option_code, const RelaySearchOrder& order) const; + /// @brief Returns pointers to instances of specified option. + /// + /// This is a variant of @ref getAnyRelayOptions but it never copies + /// an option returned. This method should be only used by + /// the @ref Pkt6 class and derived classes. Any external callers should + /// use @ref getAnyRelayOption which copies the option before returning it + /// when the @ref Pkt::copy_retrieved_options_ flag is set to true. + /// + /// @param option_code Searched option. + /// @param order Option search order (see @ref RelaySearchOrder). + /// + /// @return Collection of options found. + OptionCollection getNonCopiedAnyRelayOptions(const uint16_t option_code, + const RelaySearchOrder& order) const; + public: /// @brief Return first instance of a specified option @@ -318,10 +367,23 @@ public: /// /// @param option_code searched option /// @param order option search order (see @ref RelaySearchOrder) - /// @return option pointer (or NULL if no option matches specified criteria) + /// @return option pointer (or null if no option matches specified criteria) OptionPtr getAnyRelayOption(const uint16_t option_code, const RelaySearchOrder& order); + /// @brief Return first instances of a specified option + /// + /// When a client's packet traverses multiple relays, each passing relay may + /// insert extra options. This method allows the specific instances of a given + /// option to be obtained (e.g. closest to the client, closest to the server, + /// etc.) See @ref RelaySearchOrder for a detailed description. + /// + /// @param option_code searched option + /// @param order option search order (see @ref RelaySearchOrder) + /// @return Collection of options found. + OptionCollection getAnyRelayOptions(const uint16_t option_code, + const RelaySearchOrder& order); + /// @brief return the link address field from a relay option /// /// As with @c Pkt6::getRelayOption this returns information from the diff --git a/src/lib/dhcpsrv/cfg_option.cc b/src/lib/dhcpsrv/cfg_option.cc index 00a087e579..de1378586e 100644 --- a/src/lib/dhcpsrv/cfg_option.cc +++ b/src/lib/dhcpsrv/cfg_option.cc @@ -96,7 +96,7 @@ void CfgOption::replace(const OptionDescriptor& desc, const std::string& option_space) { if (!desc.option_) { isc_throw(isc::BadValue, "option being replaced must not be NULL"); - } + } // Check for presence of options. OptionContainerPtr options = getAll(option_space); @@ -109,10 +109,10 @@ CfgOption::replace(const OptionDescriptor& desc, const std::string& option_space OptionContainerTypeIndex& idx = options->get<1>(); auto const& od_itr = idx.find(desc.option_->getType()); if (od_itr == idx.end()) { - isc_throw(isc::BadValue, "cannot replace option: " + isc_throw(isc::BadValue, "cannot replace option: " << option_space << ":" << desc.option_->getType() << ", it does not exist"); - } + } idx.replace(od_itr, desc); } @@ -154,7 +154,7 @@ CfgOption::createOptions(CfgOptionDefPtr cfg_def) { for (auto space : getOptionSpaceNames()) { for (auto opt_desc : *(getAll(space))) { if (createDescriptorOption(cfg_def, space, opt_desc)) { - // Option was recreated, let's replace the descriptor. + // Option was recreated, let's replace the descriptor. replace(opt_desc,space); } }