BIND 10 dhcp-vendoropts, updated. a7cbda0d6f5c2395c35ac9e7330b6fe158295262 [dhcp-vendoropts] appendRequestedVendorOptions for DHCPv4 implemented.
BIND 10 source code commits
bind10-changes at lists.isc.org
Mon Oct 14 16:17:15 UTC 2013
The branch, dhcp-vendoropts has been updated
via a7cbda0d6f5c2395c35ac9e7330b6fe158295262 (commit)
via d843534d3a9b561b1c471e89c12bdf9f41a33e1c (commit)
via 6adc7f8cb6048483fe367a8c37f977804815f14b (commit)
via 46e2bb18c4e734957469a044ab44cb30f4e2abe0 (commit)
via 4a0cded796c63d06b237f42acf30c7d18899224f (commit)
via cfeb9750b04c63bb4387088c8db7ef815a806856 (commit)
from 638c6a3abbc9e00f8609d7ea894219facc5dec71 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit a7cbda0d6f5c2395c35ac9e7330b6fe158295262
Author: Tomek Mrugalski <tomasz at isc.org>
Date: Mon Oct 14 18:16:51 2013 +0200
[dhcp-vendoropts] appendRequestedVendorOptions for DHCPv4 implemented.
commit d843534d3a9b561b1c471e89c12bdf9f41a33e1c
Author: Tomek Mrugalski <tomasz at isc.org>
Date: Mon Oct 14 18:13:44 2013 +0200
[dhcp-vendoropts] Config Parser now handles vendor options correctly
commit 6adc7f8cb6048483fe367a8c37f977804815f14b
Author: Tomek Mrugalski <tomasz at isc.org>
Date: Mon Oct 14 18:13:08 2013 +0200
[dhcp-vendoropts] LibDHCP::unpackVendorOptions4 rewritten, vendor_opt4 updated
commit 46e2bb18c4e734957469a044ab44cb30f4e2abe0
Author: Tomek Mrugalski <tomasz at isc.org>
Date: Mon Oct 14 18:12:13 2013 +0200
[dhcp-vendoropts] Fix in OptionVendor
commit 4a0cded796c63d06b237f42acf30c7d18899224f
Author: Tomek Mrugalski <tomasz at isc.org>
Date: Mon Oct 14 15:43:29 2013 +0200
[dhcp-vendoropts] Vendor opts unittests now pass even when run separately
commit cfeb9750b04c63bb4387088c8db7ef815a806856
Author: Tomek Mrugalski <tomasz at isc.org>
Date: Mon Oct 14 12:42:33 2013 +0200
[dhcp-vendoropts] unit-test for docsis definitions implemented.
-----------------------------------------------------------------------
Summary of changes:
src/bin/dhcp4/config_parser.cc | 7 +
src/bin/dhcp4/dhcp4_srv.cc | 80 +++++++++++
src/bin/dhcp4/dhcp4_srv.h | 12 ++
src/bin/dhcp4/tests/dhcp4_srv_unittest.cc | 89 ++++++++++++-
src/bin/dhcp4/tests/wireshark.cc | 2 +
src/bin/dhcp6/config_parser.cc | 7 +
src/bin/dhcp6/dhcp6_srv.cc | 1 -
src/bin/dhcp6/tests/config_parser_unittest.cc | 20 +--
src/bin/dhcp6/tests/dhcp6_srv_unittest.cc | 54 ++++++++
src/lib/dhcp/docsis3_option_defs.h | 5 +-
src/lib/dhcp/libdhcp++.cc | 178 ++++++++++++++++---------
src/lib/dhcp/libdhcp++.h | 7 +
src/lib/dhcp/option_vendor.cc | 2 +-
src/lib/dhcpsrv/dhcp_parsers.h | 16 +--
14 files changed, 393 insertions(+), 87 deletions(-)
-----------------------------------------------------------------------
diff --git a/src/bin/dhcp4/config_parser.cc b/src/bin/dhcp4/config_parser.cc
index 98393c1..ddf5ed4 100644
--- a/src/bin/dhcp4/config_parser.cc
+++ b/src/bin/dhcp4/config_parser.cc
@@ -96,6 +96,13 @@ protected:
<< " for DHCPv6 server");
}
+ // Check if this is a vendor-option. If it is, get vendor-specific
+ // definition.
+ uint32_t vendor_id = SubnetConfigParser::optionSpaceToVendorId(option_space);
+ if (vendor_id) {
+ def = LibDHCP::getVendorOptionDef(Option::V4, vendor_id, option_code);
+ }
+
return (def);
}
};
diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc
index f2903ba..aeb4729 100644
--- a/src/bin/dhcp4/dhcp4_srv.cc
+++ b/src/bin/dhcp4/dhcp4_srv.cc
@@ -20,7 +20,9 @@
#include <dhcp/option4_addrlst.h>
#include <dhcp/option_int.h>
#include <dhcp/option_int_array.h>
+#include <dhcp/option_vendor.h>
#include <dhcp/pkt4.h>
+#include <dhcp/docsis3_option_defs.h>
#include <dhcp4/dhcp4_log.h>
#include <dhcp4/dhcp4_srv.h>
#include <dhcpsrv/addr_utilities.h>
@@ -36,6 +38,7 @@
#include <boost/algorithm/string/erase.hpp>
#include <boost/bind.hpp>
+#include <boost/foreach.hpp>
#include <iomanip>
#include <fstream>
@@ -644,6 +647,62 @@ Dhcpv4Srv::appendRequestedOptions(const Pkt4Ptr& question, Pkt4Ptr& msg) {
}
void
+Dhcpv4Srv::appendRequestedVendorOptions(const Pkt4Ptr& question, Pkt4Ptr& answer) {
+ // Get the configured subnet suitable for the incoming packet.
+ Subnet4Ptr subnet = selectSubnet(question);
+ // Leave if there is no subnet matching the incoming packet.
+ // There is no need to log the error message here because
+ // it will be logged in the assignLease() when it fails to
+ // pick the suitable subnet. We don't want to duplicate
+ // error messages in such case.
+ if (!subnet) {
+ return;
+ }
+
+ // Try to get the vendor option
+ boost::shared_ptr<OptionVendor> vendor_req =
+ boost::dynamic_pointer_cast<OptionVendor>(question->getOption(DHO_VIVSO_SUBOPTIONS));
+ if (!vendor_req) {
+ return;
+ }
+
+ uint32_t vendor_id = vendor_req->getVendorId();
+
+ // Let's try to get ORO within that vendor-option
+ /// @todo This is very specific to vendor-id=4491 (Cable Labs). Other vendors
+ /// may have different policies.
+ OptionPtr oro = vendor_req->getOption(DOCSIS3_V4_ORO);
+
+ /// @todo: see OPT_UINT8_TYPE definition in OptionDefinition::optionFactory().
+ /// I think it should be OptionUint8Array, not OptionGeneric
+
+ // Option ORO not found. Don't do anything then.
+ if (!oro) {
+ return;
+ }
+
+ boost::shared_ptr<OptionVendor> vendor_rsp(new OptionVendor(Option::V4, vendor_id));
+
+ // Get the list of options that client requested.
+ bool added = false;
+ const OptionBuffer& requested_opts = oro->getData();
+
+ for (OptionBuffer::const_iterator code = requested_opts.begin();
+ code != requested_opts.end(); ++code) {
+ Subnet::OptionDescriptor desc = subnet->getVendorOptionDescriptor(vendor_id, *code);
+ if (desc.option) {
+ vendor_rsp->addOption(desc.option);
+ added = true;
+ }
+ }
+
+ if (added) {
+ answer->addOption(vendor_rsp);
+ }
+}
+
+
+void
Dhcpv4Srv::appendBasicOptions(const Pkt4Ptr& question, Pkt4Ptr& msg) {
// Identify options that we always want to send to the
// client (if they are configured).
@@ -858,6 +917,7 @@ Dhcpv4Srv::processDiscover(Pkt4Ptr& discover) {
copyDefaultFields(discover, offer);
appendDefaultOptions(offer, DHCPOFFER);
appendRequestedOptions(discover, offer);
+ appendRequestedVendorOptions(discover, offer);
assignLease(discover, offer);
@@ -881,6 +941,7 @@ Dhcpv4Srv::processRequest(Pkt4Ptr& request) {
copyDefaultFields(request, ack);
appendDefaultOptions(ack, DHCPACK);
appendRequestedOptions(request, ack);
+ appendRequestedVendorOptions(request, ack);
// Note that we treat REQUEST message uniformly, regardless if this is a
// first request (requesting for new address), renewing existing address
@@ -1226,6 +1287,25 @@ Dhcpv4Srv::unpackOptions(const OptionBuffer& buf,
<< "-byte long buffer.");
}
+ /// @todo: Not sure if this is needed. Perhaps it would be better to extend
+ /// DHO_VIVSO_SUBOPTIONS definitions in std_option_defs.h to cover
+ /// OptionVendor class?
+ if (opt_type == DHO_VIVSO_SUBOPTIONS) {
+ if (offset + 4 > buf.size()) {
+ // Truncated vendor-option. There is expected at least 4 bytes
+ // long enterprise-id field
+ return (offset);
+ }
+
+ // Parse this as vendor option
+ OptionPtr vendor_opt(new OptionVendor(Option::V4, buf.begin() + offset,
+ buf.begin() + offset + opt_len));
+ options.insert(std::make_pair(opt_type, vendor_opt));
+
+ offset += opt_len;
+ continue;
+ }
+
// Get all definitions with the particular option code. Note that option code
// is non-unique within this container however at this point we expect
// to get one option definition with the particular code. If more are
diff --git a/src/bin/dhcp4/dhcp4_srv.h b/src/bin/dhcp4/dhcp4_srv.h
index 5c89c85..66fe3a6 100644
--- a/src/bin/dhcp4/dhcp4_srv.h
+++ b/src/bin/dhcp4/dhcp4_srv.h
@@ -227,6 +227,18 @@ protected:
/// @param msg outgoing message (options will be added here)
void appendRequestedOptions(const Pkt4Ptr& question, Pkt4Ptr& msg);
+ /// @brief Appends requested vendor options as requested by client.
+ ///
+ /// This method is similar to \ref appendRequestedOptions(), but uses
+ /// vendor options. The major difference is that vendor-options use
+ /// its own option spaces (there may be more than one distinct set of vendor
+ /// options, each with unique vendor-id). Vendor options are requested
+ /// using separate options within their respective vendor-option spaces.
+ ///
+ /// @param question DISCOVER or REQUEST message from a client.
+ /// @param msg outgoing message (options will be added here)
+ void appendRequestedVendorOptions(const Pkt4Ptr& question, Pkt4Ptr& answer);
+
/// @brief Assigns a lease and appends corresponding options
///
/// This method chooses the most appropriate lease for reqesting
diff --git a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
index 3f2da84..4d2ba56 100644
--- a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
+++ b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
@@ -25,8 +25,10 @@
#include <dhcp/option4_addrlst.h>
#include <dhcp/option_custom.h>
#include <dhcp/option_int_array.h>
+#include <dhcp/option_vendor.h>
#include <dhcp/pkt_filter.h>
#include <dhcp/pkt_filter_inet.h>
+#include <dhcp/docsis3_option_defs.h>
#include <dhcp4/dhcp4_srv.h>
#include <dhcp4/dhcp4_log.h>
#include <dhcp4/config_parser.h>
@@ -1138,11 +1140,7 @@ TEST_F(Dhcpv4SrvTest, ServerID) {
EXPECT_EQ(srvid_text, text);
}
-// Checks if callouts installed on pkt4_receive are indeed called and the
-// all necessary parameters are passed.
-//
-// Note that the test name does not follow test naming convention,
-// but the proper hook name is "buffer4_receive".
+// Checks if received relay agent info option is echoed back to the client
TEST_F(Dhcpv4SrvTest, relayAgentInfoEcho) {
NakedDhcpv4Srv srv(0);
@@ -1180,6 +1178,87 @@ TEST_F(Dhcpv4SrvTest, relayAgentInfoEcho) {
EXPECT_TRUE(rai_response->equal(rai_query));
}
+// Checks if vendor options are parsed correctly and requested vendor options
+// are echoed back.
+TEST_F(Dhcpv4SrvTest, vendorOptionsDocsis) {
+
+ NakedDhcpv4Srv srv(0);
+
+ string config = "{ \"interfaces\": [ \"*\" ],"
+ "\"rebind-timer\": 2000, "
+ "\"renew-timer\": 1000, "
+ " \"option-data\": [ {"
+ " \"name\": \"tftp-servers\","
+ " \"space\": \"vendor-4491\","
+ " \"code\": 2,"
+ " \"data\": \"10.253.175.16\","
+ " \"csv-format\": True"
+ " }],"
+ "\"subnet4\": [ { "
+ " \"pool\": [ \"10.254.226.0/25\" ],"
+ " \"subnet\": \"10.254.226.0/24\", "
+ " \"interface\": \"" + valid_iface_ + "\" "
+ " }, {"
+ " \"pool\": [ \"192.0.3.0/25\" ],"
+ " \"subnet\": \"192.0.3.0/24\" "
+ " } ],"
+ "\"valid-lifetime\": 4000 }";
+
+ ElementPtr json = Element::fromJSON(config);
+ ConstElementPtr status;
+
+ // Configure the server and make sure the config is accepted
+ EXPECT_NO_THROW(status = configureDhcp4Server(srv, json));
+ ASSERT_TRUE(status);
+ comment_ = config::parseAnswer(rcode_, status);
+ ASSERT_EQ(0, rcode_);
+
+ // Let's create a relayed DISCOVER. This particular relayed DISCOVER has
+ // added option 82 (relay agent info) with 3 suboptions. The server
+ // is supposed to echo it back in its response.
+ Pkt4Ptr dis;
+ ASSERT_NO_THROW(dis = captureRelayedDiscover());
+
+ // Simulate that we have received that traffic
+ srv.fakeReceive(dis);
+
+ // Server will now process to run its normal loop, but instead of calling
+ // IfaceMgr::receive4(), it will read all packets from the list set by
+ // fakeReceive()
+ // In particular, it should call registered buffer4_receive callback.
+ srv.run();
+
+ // Check that the server did send a reposonse
+ ASSERT_EQ(1, srv.fake_sent_.size());
+
+ // Make sure that we received a response
+ Pkt4Ptr offer = srv.fake_sent_.front();
+ ASSERT_TRUE(offer);
+
+ // Get Relay Agent Info from query...
+ OptionPtr vendor_opt_response = offer->getOption(DHO_VIVSO_SUBOPTIONS);
+ ASSERT_TRUE(vendor_opt_response);
+
+ // Check if it's of a correct type
+ boost::shared_ptr<OptionVendor> vendor_opt =
+ boost::dynamic_pointer_cast<OptionVendor>(vendor_opt_response);
+ ASSERT_TRUE(vendor_opt);
+
+ // Get Relay Agent Info from response...
+ OptionPtr tftp_servers_generic = vendor_opt->getOption(DOCSIS3_V4_TFTP_SERVERS);
+ ASSERT_TRUE(tftp_servers_generic);
+
+ Option4AddrLstPtr tftp_servers =
+ boost::dynamic_pointer_cast<Option4AddrLst>(tftp_servers_generic);
+
+ ASSERT_TRUE(tftp_servers);
+
+ Option4AddrLst::AddressContainer addrs = tftp_servers->getAddresses();
+ ASSERT_EQ(1, addrs.size());
+ EXPECT_EQ("10.253.175.16", addrs[0].toText());
+}
+
+
/// @todo Implement tests for subnetSelect See tests in dhcp6_srv_unittest.cc:
/// selectSubnetAddr, selectSubnetIface, selectSubnetRelayLinkaddr,
/// selectSubnetRelayInterfaceId. Note that the concept of interface-id is not
diff --git a/src/bin/dhcp4/tests/wireshark.cc b/src/bin/dhcp4/tests/wireshark.cc
index b563354..80b4737 100644
--- a/src/bin/dhcp4/tests/wireshark.cc
+++ b/src/bin/dhcp4/tests/wireshark.cc
@@ -100,6 +100,8 @@ Bootstrap Protocol
Option: (55) Parameter Request List
Option: (60) Vendor class identifier
Option: (125) V-I Vendor-specific Information
+ - suboption 1 (Option Request): requesting option 2
+ - suboption 5 (Modem Caps): 117 bytes
Option: (43) Vendor-Specific Information (CableLabs)
Option: (61) Client identifier
Option: (57) Maximum DHCP Message Size
diff --git a/src/bin/dhcp6/config_parser.cc b/src/bin/dhcp6/config_parser.cc
index fdf0bae..0656297 100644
--- a/src/bin/dhcp6/config_parser.cc
+++ b/src/bin/dhcp6/config_parser.cc
@@ -111,6 +111,13 @@ protected:
<< " for DHCPv4 server");
}
+ // Check if this is a vendor-option. If it is, get vendor-specific
+ // definition.
+ uint32_t vendor_id = SubnetConfigParser::optionSpaceToVendorId(option_space);
+ if (vendor_id) {
+ def = LibDHCP::getVendorOptionDef(Option::V6, vendor_id, option_code);
+ }
+
return def;
}
};
diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc
index a43c821..b362850 100644
--- a/src/bin/dhcp6/dhcp6_srv.cc
+++ b/src/bin/dhcp6/dhcp6_srv.cc
@@ -736,7 +736,6 @@ Dhcpv6Srv::appendRequestedVendorOptions(const Pkt6Ptr& question, Pkt6Ptr& answer
}
}
-
OptionPtr
Dhcpv6Srv::createStatusCode(uint16_t code, const std::string& text) {
// @todo This function uses OptionCustom class to manage contents
diff --git a/src/bin/dhcp6/tests/config_parser_unittest.cc b/src/bin/dhcp6/tests/config_parser_unittest.cc
index 1773c23..c4b96ca 100644
--- a/src/bin/dhcp6/tests/config_parser_unittest.cc
+++ b/src/bin/dhcp6/tests/config_parser_unittest.cc
@@ -2062,14 +2062,14 @@ TEST_F(Dhcp6ParserTest, vendorOptionsHex) {
"\"option-data\": [ {"
" \"name\": \"option-one\","
" \"space\": \"vendor-4491\","
- " \"code\": 1,"
+ " \"code\": 100,"
" \"data\": \"AB CDEF0105\","
" \"csv-format\": False"
" },"
" {"
" \"name\": \"option-two\","
" \"space\": \"vendor-1234\","
- " \"code\": 1,"
+ " \"code\": 100,"
" \"data\": \"1234\","
" \"csv-format\": False"
" } ],"
@@ -2092,13 +2092,13 @@ TEST_F(Dhcp6ParserTest, vendorOptionsHex) {
ASSERT_TRUE(subnet);
// Try to get the option from the vendor space 4491
- Subnet::OptionDescriptor desc1 = subnet->getVendorOptionDescriptor(4491, 1);
+ Subnet::OptionDescriptor desc1 = subnet->getVendorOptionDescriptor(4491, 100);
ASSERT_TRUE(desc1.option);
- EXPECT_EQ(1, desc1.option->getType());
+ EXPECT_EQ(100, desc1.option->getType());
// Try to get the option from the vendor space 1234
- Subnet::OptionDescriptor desc2 = subnet->getVendorOptionDescriptor(1234, 1);
+ Subnet::OptionDescriptor desc2 = subnet->getVendorOptionDescriptor(1234, 100);
ASSERT_TRUE(desc2.option);
- EXPECT_EQ(1, desc1.option->getType());
+ EXPECT_EQ(100, desc1.option->getType());
// Try to get the non-existing option from the non-existing
// option space and expect that option is not returned.
@@ -2119,13 +2119,13 @@ TEST_F(Dhcp6ParserTest, vendorOptionsCsv) {
"\"option-data\": [ {"
" \"name\": \"foo\","
" \"space\": \"vendor-4491\","
- " \"code\": 1,"
+ " \"code\": 100,"
" \"data\": \"this is a string vendor-opt\","
" \"csv-format\": True"
" } ],"
"\"option-def\": [ {"
" \"name\": \"foo\","
- " \"code\": 1,"
+ " \"code\": 100,"
" \"type\": \"string\","
" \"array\": False,"
" \"record-types\": \"\","
@@ -2151,9 +2151,9 @@ TEST_F(Dhcp6ParserTest, vendorOptionsCsv) {
ASSERT_TRUE(subnet);
// Try to get the option from the vendor space 4491
- Subnet::OptionDescriptor desc1 = subnet->getVendorOptionDescriptor(4491, 1);
+ Subnet::OptionDescriptor desc1 = subnet->getVendorOptionDescriptor(4491, 100);
ASSERT_TRUE(desc1.option);
- EXPECT_EQ(1, desc1.option->getType());
+ EXPECT_EQ(100, desc1.option->getType());
// Try to get the non-existing option from the non-existing
// option space and expect that option is not returned.
diff --git a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
index 8da2df3..26a66c5 100644
--- a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
+++ b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
@@ -2169,6 +2169,60 @@ TEST_F(Dhcpv6SrvTest, vendorOptionsORO) {
EXPECT_EQ("normal_erouter_v6.cm", config_file->getValue());
}
+// Test checks whether it is possible to use option definitions defined in
+// src/lib/dhcp/docsis3_option_defs.h.
+TEST_F(Dhcpv6SrvTest, vendorOptionsDocsisDefinitions) {
+ ConstElementPtr x;
+ string config_prefix = "{ \"interfaces\": [ \"all\" ],"
+ "\"preferred-lifetime\": 3000,"
+ "\"rebind-timer\": 2000, "
+ "\"renew-timer\": 1000, "
+ " \"option-data\": [ {"
+ " \"name\": \"config-file\","
+ " \"space\": \"vendor-4491\","
+ " \"code\": ";
+ string config_postfix = ","
+ " \"data\": \"normal_erouter_v6.cm\","
+ " \"csv-format\": True"
+ " }],"
+ "\"subnet6\": [ { "
+ " \"pool\": [ \"2001:db8:1::/64\" ],"
+ " \"subnet\": \"2001:db8:1::/48\", "
+ " \"renew-timer\": 1000, "
+ " \"rebind-timer\": 1000, "
+ " \"preferred-lifetime\": 3000,"
+ " \"valid-lifetime\": 4000,"
+ " \"interface-id\": \"\","
+ " \"interface\": \"\""
+ " } ],"
+ "\"valid-lifetime\": 4000 }";
+
+ // There is docsis3 (vendor-id=4491) vendor option 33, which is a
+ // config-file. Its format is a single string.
+ string config_valid = config_prefix + "33" + config_postfix;
+
+ // There is no option 99 defined in vendor-id=4491. As there is no
+ // definition, the config should fail.
+ string config_bogus = config_prefix + "99" + config_postfix;
+
+ ElementPtr json_bogus = Element::fromJSON(config_bogus);
+ ElementPtr json_valid = Element::fromJSON(config_valid);
+
+ NakedDhcpv6Srv srv(0);
+
+ // This should fail (missing option definition)
+ EXPECT_NO_THROW(x = configureDhcp6Server(srv, json_bogus));
+ ASSERT_TRUE(x);
+ comment_ = parseAnswer(rcode_, x);
+ ASSERT_EQ(1, rcode_);
+
+ // This should work (option definition present)
+ EXPECT_NO_THROW(x = configureDhcp6Server(srv, json_valid));
+ ASSERT_TRUE(x);
+ comment_ = parseAnswer(rcode_, x);
+ ASSERT_EQ(0, rcode_);
+}
+
// This test verifies that the following option structure can be parsed:
// - option (option space 'foobar')
// - sub option (option space 'foo')
diff --git a/src/lib/dhcp/docsis3_option_defs.h b/src/lib/dhcp/docsis3_option_defs.h
index 14068a8..c8cb606 100644
--- a/src/lib/dhcp/docsis3_option_defs.h
+++ b/src/lib/dhcp/docsis3_option_defs.h
@@ -18,14 +18,17 @@
#include <dhcp/std_option_defs.h>
#include <dhcp/option_data_types.h>
+
namespace {
#define VENDOR_ID_CABLE_LABS 4491
-#define DOCSIS3_V4_TFTP_SERVERS 125
+#define DOCSIS3_V4_ORO 1
+#define DOCSIS3_V4_TFTP_SERVERS 2
/// @brief Definitions of standard DHCPv4 options.
const OptionDefParams DOCSIS3_V4_DEFS[] = {
+ { "oro", DOCSIS3_V4_ORO, OPT_UINT8_TYPE, true, NO_RECORD_DEF, "" },
{ "tftp-servers", DOCSIS3_V4_TFTP_SERVERS, OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF, "" }
};
diff --git a/src/lib/dhcp/libdhcp++.cc b/src/lib/dhcp/libdhcp++.cc
index a3f33f7..524d56a 100644
--- a/src/lib/dhcp/libdhcp++.cc
+++ b/src/lib/dhcp/libdhcp++.cc
@@ -54,9 +54,9 @@ VendorOptionDefContainers LibDHCP::vendor6_defs_;
// Let's keep it in .cc file. Moving it to .h would require including optionDefParams
// definitions there
-void initOptionSpace6(OptionDefContainer& defs,
- const OptionDefParams* params,
- size_t params_size);
+void initOptionSpace(OptionDefContainer& defs,
+ const OptionDefParams* params,
+ size_t params_size);
const OptionDefContainer&
LibDHCP::getOptionDefs(const Option::Universe u) {
@@ -64,11 +64,13 @@ LibDHCP::getOptionDefs(const Option::Universe u) {
case Option::V4:
if (v4option_defs_.empty()) {
initStdOptionDefs4();
+ initVendorOptsDocsis4();
}
return (v4option_defs_);
case Option::V6:
if (v6option_defs_.empty()) {
initStdOptionDefs6();
+ initVendorOptsDocsis6();
}
return (v6option_defs_);
default:
@@ -78,6 +80,12 @@ LibDHCP::getOptionDefs(const Option::Universe u) {
const OptionDefContainer*
LibDHCP::getVendorOption4Defs(uint32_t vendor_id) {
+
+ if (vendor_id == VENDOR_ID_CABLE_LABS &&
+ vendor4_defs_.find(VENDOR_ID_CABLE_LABS) == vendor4_defs_.end()) {
+ initVendorOptsDocsis4();
+ }
+
VendorOptionDefContainers::const_iterator def = vendor4_defs_.find(vendor_id);
if (def == vendor4_defs_.end()) {
// No such vendor-id space
@@ -113,6 +121,31 @@ LibDHCP::getOptionDef(const Option::Universe u, const uint16_t code) {
return (OptionDefinitionPtr());
}
+OptionDefinitionPtr
+LibDHCP::getVendorOptionDef(const Option::Universe u, const uint32_t vendor_id,
+ const uint16_t code) {
+ const OptionDefContainer* defs = NULL;
+ if (u == Option::V4) {
+ defs = getVendorOption4Defs(vendor_id);
+ } else if (u == Option::V6) {
+ defs = getVendorOption6Defs(vendor_id);
+ }
+
+ if (!defs) {
+ // Weird universe or unknown vendor_id. We don't care. No definitions
+ // one way or another
+ // What is it anyway?
+ return (OptionDefinitionPtr());
+ }
+
+ const OptionDefContainerTypeIndex& idx = defs->get<1>();
+ const OptionDefContainerTypeRange& range = idx.equal_range(code);
+ if (range.first != range.second) {
+ return (*range.first);
+ }
+ return (OptionDefinitionPtr());
+}
+
bool
LibDHCP::isStandardOption(const Option::Universe u, const uint16_t code) {
if (u == Option::V6) {
@@ -433,70 +466,88 @@ size_t LibDHCP::unpackVendorOptions4(uint32_t vendor_id, const OptionBuffer& buf
// The buffer being read comprises a set of options, each starting with
// a one-byte type code and a one-byte length field.
while (offset + 1 <= buf.size()) {
- uint8_t opt_type = buf[offset++];
- // DHO_END is a special, one octet long option
- if (opt_type == DHO_END)
- return (offset); // just return. Don't need to add DHO_END option
-
- // DHO_PAD is just a padding after DHO_END. Let's continue parsing
- // in case we receive a message without DHO_END.
- if (opt_type == DHO_PAD)
- continue;
+ // Note that Vendor-Specific info option (RFC3925) has a different option
+ // format than Vendor-Spec info for DHCPv6. (there's additional layer of
+ // data-length
+ uint8_t data_len = buf[offset++];
- if (offset + 1 >= buf.size()) {
- // opt_type must be cast to integer so as it is not treated as
- // unsigned char value (a number is presented in error message).
- isc_throw(OutOfRange, "Attempt to parse truncated option "
- << static_cast<int>(opt_type));
+ if (offset + data_len > buf.size()) {
+ // Truncated data-option
+ return (offset);
}
- uint8_t opt_len = buf[offset++];
- if (offset + opt_len > buf.size()) {
- isc_throw(OutOfRange, "Option parse failed. Tried to parse "
- << offset + opt_len << " bytes from " << buf.size()
- << "-byte long buffer.");
- }
+ uint8_t offset_end = offset + data_len;
- OptionPtr opt;
- opt.reset();
+ // beginning of data-chunk parser
+ while (offset + 1 <= offset_end) {
+ uint8_t opt_type = buf[offset++];
- if (idx) {
- // Get all definitions with the particular option code. Note that option code
- // is non-unique within this container however at this point we expect
- // to get one option definition with the particular code. If more are
- // returned we report an error.
- const OptionDefContainerTypeRange& range = idx->equal_range(opt_type);
+ // DHO_END is a special, one octet long option
+ if (opt_type == DHO_END)
+ return (offset); // just return. Don't need to add DHO_END option
+
+ // DHO_PAD is just a padding after DHO_END. Let's continue parsing
+ // in case we receive a message without DHO_END.
+ if (opt_type == DHO_PAD)
+ continue;
+
+ if (offset + 1 >= buf.size()) {
+ // opt_type must be cast to integer so as it is not treated as
+ // unsigned char value (a number is presented in error message).
+ isc_throw(OutOfRange, "Attempt to parse truncated option "
+ << static_cast<int>(opt_type));
+ }
+
+ uint8_t opt_len = buf[offset++];
+ if (offset + opt_len > buf.size()) {
+ isc_throw(OutOfRange, "Option parse failed. Tried to parse "
+ << offset + opt_len << " bytes from " << buf.size()
+ << "-byte long buffer.");
+ }
+
+ OptionPtr opt;
+ opt.reset();
+
+ if (idx) {
+ // Get all definitions with the particular option code. Note that option code
+ // is non-unique within this container however at this point we expect
+ // to get one option definition with the particular code. If more are
+ // returned we report an error.
+ const OptionDefContainerTypeRange& range = idx->equal_range(opt_type);
// Get the number of returned option definitions for the option code.
- size_t num_defs = distance(range.first, range.second);
+ size_t num_defs = distance(range.first, range.second);
+
+ if (num_defs > 1) {
+ // Multiple options of the same code are not supported right now!
+ isc_throw(isc::Unexpected, "Internal error: multiple option definitions"
+ " for option type " << static_cast<int>(opt_type)
+ << " returned. Currently it is not supported to initialize"
+ << " multiple option definitions for the same option code."
+ << " This will be supported once support for option spaces"
+ << " is implemented");
+ } else if (num_defs == 1) {
+ // The option definition has been found. Use it to create
+ // the option instance from the provided buffer chunk.
+ const OptionDefinitionPtr& def = *(range.first);
+ assert(def);
+ opt = def->optionFactory(Option::V4, opt_type,
+ buf.begin() + offset,
+ buf.begin() + offset + opt_len);
+ }
+ }
- if (num_defs > 1) {
- // Multiple options of the same code are not supported right now!
- isc_throw(isc::Unexpected, "Internal error: multiple option definitions"
- " for option type " << static_cast<int>(opt_type)
- << " returned. Currently it is not supported to initialize"
- << " multiple option definitions for the same option code."
- << " This will be supported once support for option spaces"
- << " is implemented");
- } else if (num_defs == 1) {
- // The option definition has been found. Use it to create
- // the option instance from the provided buffer chunk.
- const OptionDefinitionPtr& def = *(range.first);
- assert(def);
- opt = def->optionFactory(Option::V4, opt_type,
- buf.begin() + offset,
- buf.begin() + offset + opt_len);
+ if (!opt) {
+ opt = OptionPtr(new Option(Option::V4, opt_type,
+ buf.begin() + offset,
+ buf.begin() + offset + opt_len));
}
- }
- if (!opt) {
- opt = OptionPtr(new Option(Option::V4, opt_type,
- buf.begin() + offset,
- buf.begin() + offset + opt_len));
- }
+ options.insert(std::make_pair(opt_type, opt));
+ offset += opt_len;
+
+ } // end of data-chunk
- options.insert(std::make_pair(opt_type, opt));
- offset += opt_len;
}
return (offset);
}
@@ -608,18 +659,23 @@ LibDHCP::initStdOptionDefs4() {
void
LibDHCP::initStdOptionDefs6() {
- initOptionSpace6(v6option_defs_, OPTION_DEF_PARAMS6, OPTION_DEF_PARAMS_SIZE6);
+ initOptionSpace(v6option_defs_, OPTION_DEF_PARAMS6, OPTION_DEF_PARAMS_SIZE6);
+}
+
+void
+LibDHCP::initVendorOptsDocsis4() {
+ initOptionSpace(vendor4_defs_[VENDOR_ID_CABLE_LABS], DOCSIS3_V4_DEFS, DOCSIS3_V4_DEFS_SIZE);
}
void
LibDHCP::initVendorOptsDocsis6() {
vendor6_defs_[VENDOR_ID_CABLE_LABS] = OptionDefContainer();
- initOptionSpace6(vendor6_defs_[VENDOR_ID_CABLE_LABS], DOCSIS3_V6_DEFS, DOCSIS3_V6_DEFS_SIZE);
+ initOptionSpace(vendor6_defs_[VENDOR_ID_CABLE_LABS], DOCSIS3_V6_DEFS, DOCSIS3_V6_DEFS_SIZE);
}
-void initOptionSpace6(OptionDefContainer& defs,
- const OptionDefParams* params,
- size_t params_size) {
+void initOptionSpace(OptionDefContainer& defs,
+ const OptionDefParams* params,
+ size_t params_size) {
defs.clear();
for (int i = 0; i < params_size; ++i) {
diff --git a/src/lib/dhcp/libdhcp++.h b/src/lib/dhcp/libdhcp++.h
index a05d927..4931746 100644
--- a/src/lib/dhcp/libdhcp++.h
+++ b/src/lib/dhcp/libdhcp++.h
@@ -55,6 +55,11 @@ public:
static OptionDefinitionPtr getOptionDef(const Option::Universe u,
const uint16_t code);
+
+ static OptionDefinitionPtr getVendorOptionDef(const Option::Universe u,
+ const uint32_t vendor_id,
+ const uint16_t code);
+
/// @brief Check if the specified option is a standard option.
///
/// @param u universe (V4 or V6)
@@ -183,6 +188,8 @@ private:
/// is incorrect. This is a programming error.
static void initStdOptionDefs6();
+ static void initVendorOptsDocsis4();
+
static void initVendorOptsDocsis6();
/// pointers to factories that produce DHCPv6 options
diff --git a/src/lib/dhcp/option_vendor.cc b/src/lib/dhcp/option_vendor.cc
index ec7ebd5..22db1af 100644
--- a/src/lib/dhcp/option_vendor.cc
+++ b/src/lib/dhcp/option_vendor.cc
@@ -42,7 +42,7 @@ void OptionVendor::unpack(OptionBufferConstIter begin, OptionBufferConstIter end
isc_throw(OutOfRange, "Truncated vendor-specific information option");
}
- uint32_t vendor_id_ = isc::util::readUint32(&(*begin));
+ vendor_id_ = isc::util::readUint32(&(*begin));
OptionBuffer vendor_buffer(begin +4, end);
diff --git a/src/lib/dhcpsrv/dhcp_parsers.h b/src/lib/dhcpsrv/dhcp_parsers.h
index ff1fe3d..738c556 100644
--- a/src/lib/dhcpsrv/dhcp_parsers.h
+++ b/src/lib/dhcpsrv/dhcp_parsers.h
@@ -773,6 +773,14 @@ public:
/// @brief Adds the created subnet to a server's configuration.
virtual void commit() = 0;
+ /// @brief tries to convert option_space string to numeric vendor_id
+ ///
+ /// This will work if the option_space has format "vendor-1234".
+ /// This is used to detect whether a given option-space is a vendor
+ /// space or not. Returns 0 if the format is different.
+ /// @return numeric vendor-id (or 0 if the format does not match)
+ static uint32_t optionSpaceToVendorId(const std::string& option_space);
+
protected:
/// @brief creates parsers for entries in subnet definition
///
@@ -828,14 +836,6 @@ protected:
/// @throw DhcpConfigError when requested parameter is not present
isc::dhcp::Triplet<uint32_t> getParam(const std::string& name);
- /// @brief tries to convert option_space string to numeric vendor_id
- ///
- /// This will work if the option_space has format "vendor-1234".
- /// This is used to detect whether a given option-space is a vendor
- /// space or not. Returns 0 if the format is different.
- /// @return numeric vendor-id (or 0 if the format does not match)
- uint32_t optionSpaceToVendorId(const std::string& option_space);
-
private:
/// @brief Append sub-options to an option.
More information about the bind10-changes
mailing list