From: Tomek Mrugalski Date: Mon, 18 Feb 2019 14:49:22 +0000 (+0100) Subject: [#464,!238] v6 vendor options moved to separate file X-Git-Tag: 397-cb-implement-mysqlconfigbackenddhcpv6_base~50 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=950d0601290aa21eae487d4174a354df4ecb1c5d;p=thirdparty%2Fkea.git [#464,!238] v6 vendor options moved to separate file --- diff --git a/src/bin/dhcp6/tests/Makefile.am b/src/bin/dhcp6/tests/Makefile.am index f92c8d77e5..81e5078edd 100644 --- a/src/bin/dhcp6/tests/Makefile.am +++ b/src/bin/dhcp6/tests/Makefile.am @@ -78,31 +78,35 @@ libco3_la_CPPFLAGS = $(AM_CPPFLAGS) libco3_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere TESTS += dhcp6_unittests -dhcp6_unittests_SOURCES = dhcp6_unittests.cc + +# This list is ordered alphabetically. When adding new files, please maintain +# this order. +dhcp6_unittests_SOURCES = classify_unittests.cc +dhcp6_unittests_SOURCES += config_parser_unittest.cc +dhcp6_unittests_SOURCES += confirm_unittest.cc +dhcp6_unittests_SOURCES += ctrl_dhcp6_srv_unittest.cc +dhcp6_unittests_SOURCES += d2_unittest.cc d2_unittest.h +dhcp6_unittests_SOURCES += decline_unittest.cc +dhcp6_unittests_SOURCES += dhcp6_client.cc dhcp6_client.h +dhcp6_unittests_SOURCES += dhcp6_message_test.cc dhcp6_message_test.h dhcp6_unittests_SOURCES += dhcp6_srv_unittest.cc +dhcp6_unittests_SOURCES += dhcp6_test_utils.cc dhcp6_test_utils.h +dhcp6_unittests_SOURCES += dhcp6_unittests.cc +dhcp6_unittests_SOURCES += dhcp6to4_ipc_unittest.cc dhcp6_unittests_SOURCES += fqdn_unittest.cc +dhcp6_unittests_SOURCES += get_config_unittest.cc get_config_unittest.h dhcp6_unittests_SOURCES += hooks_unittest.cc dhcp6_unittests_SOURCES += host_unittest.cc -dhcp6_unittests_SOURCES += dhcp6_test_utils.cc dhcp6_test_utils.h -dhcp6_unittests_SOURCES += d2_unittest.cc d2_unittest.h +dhcp6_unittests_SOURCES += infrequest_unittest.cc +dhcp6_unittests_SOURCES += kea_controller_unittest.cc dhcp6_unittests_SOURCES += marker_file.cc -dhcp6_unittests_SOURCES += ctrl_dhcp6_srv_unittest.cc -dhcp6_unittests_SOURCES += dhcp6_client.cc dhcp6_client.h +dhcp6_unittests_SOURCES += parser_unittest.cc dhcp6_unittests_SOURCES += rebind_unittest.cc dhcp6_unittests_SOURCES += renew_unittest.cc dhcp6_unittests_SOURCES += sarr_unittest.cc -dhcp6_unittests_SOURCES += config_parser_unittest.cc -dhcp6_unittests_SOURCES += confirm_unittest.cc -dhcp6_unittests_SOURCES += infrequest_unittest.cc -dhcp6_unittests_SOURCES += decline_unittest.cc -dhcp6_unittests_SOURCES += dhcp6_message_test.cc dhcp6_message_test.h -dhcp6_unittests_SOURCES += kea_controller_unittest.cc -dhcp6_unittests_SOURCES += dhcp6to4_ipc_unittest.cc -dhcp6_unittests_SOURCES += classify_unittests.cc -dhcp6_unittests_SOURCES += parser_unittest.cc dhcp6_unittests_SOURCES += simple_parser6_unittest.cc -dhcp6_unittests_SOURCES += get_config_unittest.cc get_config_unittest.h dhcp6_unittests_SOURCES += shared_network_unittest.cc +dhcp6_unittests_SOURCES += vendor_opts_unittest.cc nodist_dhcp6_unittests_SOURCES = marker_file.h test_libraries.h diff --git a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc index 99d4f5d3c2..b8fe023e2e 100644 --- a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc +++ b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2011-2018 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2011-2019 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -16,8 +16,6 @@ #include #include #include -#include -#include #include #include #include @@ -1858,311 +1856,6 @@ TEST_F(Dhcpv6SrvTest, docsisTraffic) { ASSERT_TRUE(adv); } -// Checks if server is able to handle a relayed traffic from DOCSIS3.0 modems -TEST_F(Dhcpv6SrvTest, docsisVendorOptionsParse) { - - // Let's get a traffic capture from DOCSIS3.0 modem - Pkt6Ptr sol = PktCaptures::captureDocsisRelayedSolicit(); - EXPECT_NO_THROW(sol->unpack()); - - // Check if the packet contain - OptionPtr opt = sol->getOption(D6O_VENDOR_OPTS); - ASSERT_TRUE(opt); - - boost::shared_ptr vendor = boost::dynamic_pointer_cast(opt); - ASSERT_TRUE(vendor); - - EXPECT_TRUE(vendor->getOption(DOCSIS3_V6_ORO)); - EXPECT_TRUE(vendor->getOption(36)); - EXPECT_TRUE(vendor->getOption(35)); - EXPECT_TRUE(vendor->getOption(DOCSIS3_V6_DEVICE_TYPE)); - EXPECT_TRUE(vendor->getOption(3)); - EXPECT_TRUE(vendor->getOption(4)); - EXPECT_TRUE(vendor->getOption(5)); - EXPECT_TRUE(vendor->getOption(6)); - EXPECT_TRUE(vendor->getOption(7)); - EXPECT_TRUE(vendor->getOption(8)); - EXPECT_TRUE(vendor->getOption(9)); - EXPECT_TRUE(vendor->getOption(DOCSIS3_V6_VENDOR_NAME)); - EXPECT_TRUE(vendor->getOption(15)); - - EXPECT_FALSE(vendor->getOption(20)); - EXPECT_FALSE(vendor->getOption(11)); - EXPECT_FALSE(vendor->getOption(17)); -} - -// Checks if server is able to parse incoming docsis option and extract suboption 1 (docsis ORO) -TEST_F(Dhcpv6SrvTest, docsisVendorORO) { - - NakedDhcpv6Srv srv(0); - - // Let's get a traffic capture from DOCSIS3.0 modem - Pkt6Ptr sol = PktCaptures::captureDocsisRelayedSolicit(); - ASSERT_NO_THROW(sol->unpack()); - - // Check if the packet contains vendor options option - OptionPtr opt = sol->getOption(D6O_VENDOR_OPTS); - ASSERT_TRUE(opt); - - boost::shared_ptr vendor = boost::dynamic_pointer_cast(opt); - ASSERT_TRUE(vendor); - - opt = vendor->getOption(DOCSIS3_V6_ORO); - ASSERT_TRUE(opt); - - OptionUint16ArrayPtr oro = boost::dynamic_pointer_cast(opt); - EXPECT_TRUE(oro); -} - -// This test checks if Option Request Option (ORO) in docsis (vendor-id=4491) -// vendor options is parsed correctly and the requested options are actually assigned. -TEST_F(Dhcpv6SrvTest, vendorOptionsORO) { - - IfaceMgrTestConfig test_config(true); - - string config = "{ \"interfaces-config\": {" - " \"interfaces\": [ \"*\" ]" - "}," - "\"preferred-lifetime\": 3000," - "\"rebind-timer\": 2000, " - "\"renew-timer\": 1000, " - " \"option-def\": [ {" - " \"name\": \"config-file\"," - " \"code\": 33," - " \"type\": \"string\"," - " \"space\": \"vendor-4491\"" - " } ]," - " \"option-data\": [ {" - " \"name\": \"config-file\"," - " \"space\": \"vendor-4491\"," - " \"data\": \"normal_erouter_v6.cm\"" - " }]," - "\"subnet6\": [ { " - " \"pools\": [ { \"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\": \"eth0\"" - " } ]," - "\"valid-lifetime\": 4000 }"; - - ASSERT_NO_THROW(configure(config)); - - Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234)); - sol->setRemoteAddr(IOAddress("fe80::abcd")); - sol->setIface("eth0"); - sol->addOption(generateIA(D6O_IA_NA, 234, 1500, 3000)); - OptionPtr clientid = generateClientId(); - sol->addOption(clientid); - - // Pass it to the server and get an advertise - AllocEngine::ClientContext6 ctx; - bool drop = false; - srv_.initContext(sol, ctx, drop); - ASSERT_FALSE(drop); - Pkt6Ptr adv = srv_.processSolicit(ctx); - - // check if we get response at all - ASSERT_TRUE(adv); - - // We did not include any vendor opts in SOLICIT, so there should be none - // in ADVERTISE. - ASSERT_FALSE(adv->getOption(D6O_VENDOR_OPTS)); - - // Let's add a vendor-option (vendor-id=4491) with a single sub-option. - // That suboption has code 1 and is a docsis ORO option. - boost::shared_ptr vendor_oro(new OptionUint16Array(Option::V6, - DOCSIS3_V6_ORO)); - vendor_oro->addValue(DOCSIS3_V6_CONFIG_FILE); // Request option 33 - OptionPtr vendor(new OptionVendor(Option::V6, 4491)); - vendor->addOption(vendor_oro); - sol->addOption(vendor); - - // Need to process SOLICIT again after requesting new option. - AllocEngine::ClientContext6 ctx2; - srv_.initContext(sol, ctx2, drop); - ASSERT_FALSE(drop); - adv = srv_.processSolicit(ctx2); - ASSERT_TRUE(adv); - - // Check if there is vendor option response - OptionPtr tmp = adv->getOption(D6O_VENDOR_OPTS); - ASSERT_TRUE(tmp); - - // The response should be OptionVendor object - boost::shared_ptr vendor_resp = - boost::dynamic_pointer_cast(tmp); - ASSERT_TRUE(vendor_resp); - - OptionPtr docsis33 = vendor_resp->getOption(33); - ASSERT_TRUE(docsis33); - - OptionStringPtr config_file = boost::dynamic_pointer_cast(docsis33); - ASSERT_TRUE(config_file); - EXPECT_EQ("normal_erouter_v6.cm", config_file->getValue()); -} - -// This test checks if Option Request Option (ORO) in docsis (vendor-id=4491) -// vendor options is parsed correctly and the persistent options are actually assigned. -TEST_F(Dhcpv6SrvTest, vendorPersistentOptions) { - - IfaceMgrTestConfig test_config(true); - - string config = "{ \"interfaces-config\": {" - " \"interfaces\": [ \"*\" ]" - "}," - "\"preferred-lifetime\": 3000," - "\"rebind-timer\": 2000, " - "\"renew-timer\": 1000, " - " \"option-def\": [ {" - " \"name\": \"config-file\"," - " \"code\": 33," - " \"type\": \"string\"," - " \"space\": \"vendor-4491\"" - " } ]," - " \"option-data\": [ {" - " \"name\": \"config-file\"," - " \"space\": \"vendor-4491\"," - " \"data\": \"normal_erouter_v6.cm\"," - " \"always-send\": true" - " }]," - "\"subnet6\": [ { " - " \"pools\": [ { \"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\": \"eth0\"" - " } ]," - "\"valid-lifetime\": 4000 }"; - - ASSERT_NO_THROW(configure(config)); - - Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234)); - sol->setRemoteAddr(IOAddress("fe80::abcd")); - sol->setIface("eth0"); - sol->addOption(generateIA(D6O_IA_NA, 234, 1500, 3000)); - OptionPtr clientid = generateClientId(); - sol->addOption(clientid); - - // Let's add a vendor-option (vendor-id=4491). - OptionPtr vendor(new OptionVendor(Option::V6, 4491)); - sol->addOption(vendor); - - // Pass it to the server and get an advertise - AllocEngine::ClientContext6 ctx; - bool drop = false; - srv_.initContext(sol, ctx, drop); - ASSERT_FALSE(drop); - Pkt6Ptr adv = srv_.processSolicit(ctx); - - // check if we get response at all - ASSERT_TRUE(adv); - - // Check if there is vendor option response - OptionPtr tmp = adv->getOption(D6O_VENDOR_OPTS); - ASSERT_TRUE(tmp); - - // The response should be OptionVendor object - boost::shared_ptr vendor_resp = - boost::dynamic_pointer_cast(tmp); - ASSERT_TRUE(vendor_resp); - - OptionPtr docsis33 = vendor_resp->getOption(33); - ASSERT_TRUE(docsis33); - - OptionStringPtr config_file = boost::dynamic_pointer_cast(docsis33); - ASSERT_TRUE(config_file); - 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-config\": {" - " \"interfaces\": [ ]" - "}," - "\"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\": [ { " - " \"pools\": [ { \"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; - - ConstElementPtr json_bogus; - ASSERT_NO_THROW(json_bogus = parseDHCP6(config_bogus)); - ConstElementPtr json_valid; - ASSERT_NO_THROW(json_valid = parseDHCP6(config_valid)); - - NakedDhcpv6Srv srv(0); - - // This should fail (missing option definition) - EXPECT_NO_THROW(x = configureDhcp6Server(srv, json_bogus)); - ASSERT_TRUE(x); - comment_ = isc::config::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_ = isc::config::parseAnswer(rcode_, x); - ASSERT_EQ(0, rcode_); -} - -// This test checks that the server will handle a Solicit with the Vendor Class -// having a length of 4 (enterprise-id only). -TEST_F(Dhcpv6SrvTest, cableLabsShortVendorClass) { - NakedDhcpv6Srv srv(0); - - // Create a simple Solicit with the 4-byte long vendor class option. - Pkt6Ptr sol = PktCaptures::captureCableLabsShortVendorClass(); - - // Simulate that we have received that traffic - srv.fakeReceive(sol); - - // Server will now process to run its normal loop, but instead of calling - // IfaceMgr::receive6(), it will read all packets from the list set by - // fakeReceive() - srv.run(); - - // Get Advertise... - ASSERT_FALSE(srv.fake_sent_.empty()); - Pkt6Ptr adv = srv.fake_sent_.front(); - ASSERT_TRUE(adv); - - // This is sent back to client, so port is 546 - EXPECT_EQ(DHCP6_CLIENT_PORT, adv->getRemotePort()); -} // Checks if relay IP address specified in the relay-info structure in // subnet6 is being used properly. diff --git a/src/bin/dhcp6/tests/vendor_opts_unittest.cc b/src/bin/dhcp6/tests/vendor_opts_unittest.cc new file mode 100644 index 0000000000..e076dd6b36 --- /dev/null +++ b/src/bin/dhcp6/tests/vendor_opts_unittest.cc @@ -0,0 +1,353 @@ +// Copyright (C) 2019 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +// This file is dedicated to testing vendor options in DHCPv6. There +// are several related options: +// +// client-class (15) - this specifies (as a plain string) what kind of +// device this is +// vendor-class (16) - contains an enterprise-id followed by zero or +// more of vendor-class data. +// vendor-option (17) - contains an enterprise-id followed by zero or +// more vendor suboptions + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace isc; +using namespace isc::config; +using namespace isc::dhcp; +using namespace isc::dhcp::test; +using namespace isc::asiolink; + +/// @brief Class dedicated to testing vendor options in DHCPv6 +/// +/// For the time being it does not provide any additional functionality, but it +/// groups all vendor related tests under a single name. There were too many +/// tests in Dhcpv4SrvTest class anyway. +class VendorOptsTest : public Dhcpv6SrvTest { + +}; + +// Checks if server is able to handle a relayed traffic from DOCSIS3.0 modems +TEST_F(VendorOptsTest, docsisVendorOptionsParse) { + + // Let's get a traffic capture from DOCSIS3.0 modem + Pkt6Ptr sol = PktCaptures::captureDocsisRelayedSolicit(); + EXPECT_NO_THROW(sol->unpack()); + + // Check if the packet contain + OptionPtr opt = sol->getOption(D6O_VENDOR_OPTS); + ASSERT_TRUE(opt); + + boost::shared_ptr vendor = boost::dynamic_pointer_cast(opt); + ASSERT_TRUE(vendor); + + EXPECT_TRUE(vendor->getOption(DOCSIS3_V6_ORO)); + EXPECT_TRUE(vendor->getOption(36)); + EXPECT_TRUE(vendor->getOption(35)); + EXPECT_TRUE(vendor->getOption(DOCSIS3_V6_DEVICE_TYPE)); + EXPECT_TRUE(vendor->getOption(3)); + EXPECT_TRUE(vendor->getOption(4)); + EXPECT_TRUE(vendor->getOption(5)); + EXPECT_TRUE(vendor->getOption(6)); + EXPECT_TRUE(vendor->getOption(7)); + EXPECT_TRUE(vendor->getOption(8)); + EXPECT_TRUE(vendor->getOption(9)); + EXPECT_TRUE(vendor->getOption(DOCSIS3_V6_VENDOR_NAME)); + EXPECT_TRUE(vendor->getOption(15)); + + EXPECT_FALSE(vendor->getOption(20)); + EXPECT_FALSE(vendor->getOption(11)); + EXPECT_FALSE(vendor->getOption(17)); +} + +// Checks if server is able to parse incoming docsis option and extract suboption 1 (docsis ORO) +TEST_F(VendorOptsTest, docsisVendorORO) { + + NakedDhcpv6Srv srv(0); + + // Let's get a traffic capture from DOCSIS3.0 modem + Pkt6Ptr sol = PktCaptures::captureDocsisRelayedSolicit(); + ASSERT_NO_THROW(sol->unpack()); + + // Check if the packet contains vendor options option + OptionPtr opt = sol->getOption(D6O_VENDOR_OPTS); + ASSERT_TRUE(opt); + + boost::shared_ptr vendor = boost::dynamic_pointer_cast(opt); + ASSERT_TRUE(vendor); + + opt = vendor->getOption(DOCSIS3_V6_ORO); + ASSERT_TRUE(opt); + + OptionUint16ArrayPtr oro = boost::dynamic_pointer_cast(opt); + EXPECT_TRUE(oro); +} + +// This test checks if Option Request Option (ORO) in docsis (vendor-id=4491) +// vendor options is parsed correctly and the requested options are actually assigned. +TEST_F(VendorOptsTest, vendorOptionsORO) { + + IfaceMgrTestConfig test_config(true); + + string config = "{ \"interfaces-config\": {" + " \"interfaces\": [ \"*\" ]" + "}," + "\"preferred-lifetime\": 3000," + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + " \"option-def\": [ {" + " \"name\": \"config-file\"," + " \"code\": 33," + " \"type\": \"string\"," + " \"space\": \"vendor-4491\"" + " } ]," + " \"option-data\": [ {" + " \"name\": \"config-file\"," + " \"space\": \"vendor-4491\"," + " \"data\": \"normal_erouter_v6.cm\"" + " }]," + "\"subnet6\": [ { " + " \"pools\": [ { \"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\": \"eth0\"" + " } ]," + "\"valid-lifetime\": 4000 }"; + + ASSERT_NO_THROW(configure(config)); + + Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234)); + sol->setRemoteAddr(IOAddress("fe80::abcd")); + sol->setIface("eth0"); + sol->addOption(generateIA(D6O_IA_NA, 234, 1500, 3000)); + OptionPtr clientid = generateClientId(); + sol->addOption(clientid); + + // Pass it to the server and get an advertise + AllocEngine::ClientContext6 ctx; + bool drop = false; + srv_.initContext(sol, ctx, drop); + ASSERT_FALSE(drop); + Pkt6Ptr adv = srv_.processSolicit(ctx); + + // check if we get response at all + ASSERT_TRUE(adv); + + // We did not include any vendor opts in SOLICIT, so there should be none + // in ADVERTISE. + ASSERT_FALSE(adv->getOption(D6O_VENDOR_OPTS)); + + // Let's add a vendor-option (vendor-id=4491) with a single sub-option. + // That suboption has code 1 and is a docsis ORO option. + boost::shared_ptr vendor_oro(new OptionUint16Array(Option::V6, + DOCSIS3_V6_ORO)); + vendor_oro->addValue(DOCSIS3_V6_CONFIG_FILE); // Request option 33 + OptionPtr vendor(new OptionVendor(Option::V6, 4491)); + vendor->addOption(vendor_oro); + sol->addOption(vendor); + + // Need to process SOLICIT again after requesting new option. + AllocEngine::ClientContext6 ctx2; + srv_.initContext(sol, ctx2, drop); + ASSERT_FALSE(drop); + adv = srv_.processSolicit(ctx2); + ASSERT_TRUE(adv); + + // Check if there is vendor option response + OptionPtr tmp = adv->getOption(D6O_VENDOR_OPTS); + ASSERT_TRUE(tmp); + + // The response should be OptionVendor object + boost::shared_ptr vendor_resp = + boost::dynamic_pointer_cast(tmp); + ASSERT_TRUE(vendor_resp); + + OptionPtr docsis33 = vendor_resp->getOption(33); + ASSERT_TRUE(docsis33); + + OptionStringPtr config_file = boost::dynamic_pointer_cast(docsis33); + ASSERT_TRUE(config_file); + EXPECT_EQ("normal_erouter_v6.cm", config_file->getValue()); +} + +// This test checks if Option Request Option (ORO) in docsis (vendor-id=4491) +// vendor options is parsed correctly and the persistent options are actually assigned. +TEST_F(VendorOptsTest, vendorPersistentOptions) { + + IfaceMgrTestConfig test_config(true); + + string config = "{ \"interfaces-config\": {" + " \"interfaces\": [ \"*\" ]" + "}," + "\"preferred-lifetime\": 3000," + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + " \"option-def\": [ {" + " \"name\": \"config-file\"," + " \"code\": 33," + " \"type\": \"string\"," + " \"space\": \"vendor-4491\"" + " } ]," + " \"option-data\": [ {" + " \"name\": \"config-file\"," + " \"space\": \"vendor-4491\"," + " \"data\": \"normal_erouter_v6.cm\"," + " \"always-send\": true" + " }]," + "\"subnet6\": [ { " + " \"pools\": [ { \"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\": \"eth0\"" + " } ]," + "\"valid-lifetime\": 4000 }"; + + ASSERT_NO_THROW(configure(config)); + + Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234)); + sol->setRemoteAddr(IOAddress("fe80::abcd")); + sol->setIface("eth0"); + sol->addOption(generateIA(D6O_IA_NA, 234, 1500, 3000)); + OptionPtr clientid = generateClientId(); + sol->addOption(clientid); + + // Let's add a vendor-option (vendor-id=4491). + OptionPtr vendor(new OptionVendor(Option::V6, 4491)); + sol->addOption(vendor); + + // Pass it to the server and get an advertise + AllocEngine::ClientContext6 ctx; + bool drop = false; + srv_.initContext(sol, ctx, drop); + ASSERT_FALSE(drop); + Pkt6Ptr adv = srv_.processSolicit(ctx); + + // check if we get response at all + ASSERT_TRUE(adv); + + // Check if there is vendor option response + OptionPtr tmp = adv->getOption(D6O_VENDOR_OPTS); + ASSERT_TRUE(tmp); + + // The response should be OptionVendor object + boost::shared_ptr vendor_resp = + boost::dynamic_pointer_cast(tmp); + ASSERT_TRUE(vendor_resp); + + OptionPtr docsis33 = vendor_resp->getOption(33); + ASSERT_TRUE(docsis33); + + OptionStringPtr config_file = boost::dynamic_pointer_cast(docsis33); + ASSERT_TRUE(config_file); + 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(VendorOptsTest, vendorOptionsDocsisDefinitions) { + ConstElementPtr x; + string config_prefix = "{ \"interfaces-config\": {" + " \"interfaces\": [ ]" + "}," + "\"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\": [ { " + " \"pools\": [ { \"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; + + ConstElementPtr json_bogus; + ASSERT_NO_THROW(json_bogus = parseDHCP6(config_bogus)); + ConstElementPtr json_valid; + ASSERT_NO_THROW(json_valid = parseDHCP6(config_valid)); + + NakedDhcpv6Srv srv(0); + + // This should fail (missing option definition) + EXPECT_NO_THROW(x = configureDhcp6Server(srv, json_bogus)); + ASSERT_TRUE(x); + comment_ = isc::config::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_ = isc::config::parseAnswer(rcode_, x); + ASSERT_EQ(0, rcode_); +} + +// This test checks that the server will handle a Solicit with the Vendor Class +// having a length of 4 (enterprise-id only). +TEST_F(VendorOptsTest, cableLabsShortVendorClass) { + NakedDhcpv6Srv srv(0); + + // Create a simple Solicit with the 4-byte long vendor class option. + Pkt6Ptr sol = PktCaptures::captureCableLabsShortVendorClass(); + + // Simulate that we have received that traffic + srv.fakeReceive(sol); + + // Server will now process to run its normal loop, but instead of calling + // IfaceMgr::receive6(), it will read all packets from the list set by + // fakeReceive() + srv.run(); + + // Get Advertise... + ASSERT_FALSE(srv.fake_sent_.empty()); + Pkt6Ptr adv = srv.fake_sent_.front(); + ASSERT_TRUE(adv); + + // This is sent back to client, so port is 546 + EXPECT_EQ(DHCP6_CLIENT_PORT, adv->getRemotePort()); +} +