#include <iomanip>
#include <fstream>
#include <sstream>
+#include <map>
#include <set>
using namespace isc;
return;
}
- std::vector<uint16_t> requested_opts;
+ set<uint16_t> requested_opts;
// Client requests some options using ORO option. Try to
// get this option from client's message.
// Get the list of options that client requested.
if (option_oro) {
- requested_opts = option_oro->getValues();
+ set<uint16_t> oro_req_opts;
+ for (uint16_t code : option_oro->getValues()) {
+ static_cast<void>(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) {
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<void>(requested_opts.insert(code));
}
}
}
}
}
}
+
+ // Special cases for vendor class. Vendor options are done later.
+ if (requested_opts.count(D6O_VENDOR_CLASS) > 0) {
+ set<uint32_t> 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<OptionVendorClass>(opt.second);
+ if (vendor_class) {
+ int32_t vendor_id = vendor_class->getVendorId();
+ static_cast<void>(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<OptionVendorClass>(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
return;
}
- uint32_t vendor_id = 0;
+ set<uint32_t> 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<OptionVendor>(
- 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<uint32_t, OptionVendorPtr> 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<OptionVendor>(opt.second);
+ if (vendor_rsp) {
+ uint32_t vendor_id = vendor_rsp->getVendorId();
+ vendor_rsps[vendor_id] = vendor_rsp;
+ static_cast<void>(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<OptionVendor>(
- question->getOption(D6O_VENDOR_OPTS));
- if (vendor_req) {
- vendor_id = vendor_req->getVendorId();
+ map<uint32_t, OptionVendorPtr> 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<OptionVendor>(opt.second);
+ if (vendor_req) {
+ uint32_t vendor_id = vendor_req->getVendorId();
+ vendor_reqs[vendor_id] = vendor_req;
+ static_cast<void>(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<OptionVendorClass>(
- 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<OptionVendorClass>(opt.second);
+ if (vendor_class) {
+ uint32_t vendor_id = vendor_class->getVendorId();
+ static_cast<void>(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<uint16_t> requested_opts;
+ map<uint32_t, set<uint16_t> > 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
// created as an OptionUint16Array, but might not be for other
// vendor IDs.
oro = boost::dynamic_pointer_cast<OptionUint16Array>(oro_generic);
- if (oro) {
- requested_opts = oro->getValues();
+ }
+ if (oro) {
+ set<uint16_t> oro_req_opts;
+ for (uint16_t code : oro->getValues()) {
+ static_cast<void>(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<void>(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);
+ }
}
}
}
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<OptionVendorClass>(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() + " ";
+ }
}
}
/// @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
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
///
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 {
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<OptionCollection::const_iterator,
+ OptionCollection::const_iterator> 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<OptionCollection::iterator,
+ OptionCollection::iterator> 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()) {
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) {
if (!device_id->getData().empty()) {
mac.reset(new HWAddr(device_id->getData(), HTYPE_DOCSIS));
mac->source_ = HWAddr::HWADDR_SOURCE_DOCSIS_MODEM;
+ break;
}
}
}
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;
}
}
/// @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
/// @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
/// @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
///
/// @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
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);
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);
}
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);
}
}