]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#464,!238] v6 vendor options moved to separate file
authorTomek Mrugalski <tomasz@isc.org>
Mon, 18 Feb 2019 14:49:22 +0000 (15:49 +0100)
committerTomek Mrugalski <tomasz@isc.org>
Tue, 19 Feb 2019 11:15:37 +0000 (12:15 +0100)
src/bin/dhcp6/tests/Makefile.am
src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
src/bin/dhcp6/tests/vendor_opts_unittest.cc [new file with mode: 0644]

index f92c8d77e59b39dd306c5350fac2d7bfe5e9e5ee..81e5078edd14f162feead4aa7edfd64ceed19a7e 100644 (file)
@@ -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
 
index 99d4f5d3c2a8e368499d97df41a93d79b2baddad..b8fe023e2e0875031e679bb746a09f74b11a471c 100644 (file)
@@ -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 <dhcp/option_int.h>
 #include <dhcp/option_int_array.h>
 #include <dhcp/option_string.h>
-#include <dhcp/option_vendor.h>
-#include <dhcp/option_vendor_class.h>
 #include <dhcp/iface_mgr.h>
 #include <dhcp6/json_config_parser.h>
 #include <dhcp/dhcp6.h>
@@ -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<OptionVendor> vendor = boost::dynamic_pointer_cast<OptionVendor>(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<OptionVendor> vendor = boost::dynamic_pointer_cast<OptionVendor>(opt);
-    ASSERT_TRUE(vendor);
-
-    opt = vendor->getOption(DOCSIS3_V6_ORO);
-    ASSERT_TRUE(opt);
-
-    OptionUint16ArrayPtr oro = boost::dynamic_pointer_cast<OptionUint16Array>(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<OptionUint16Array> 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<OptionVendor> vendor_resp =
-        boost::dynamic_pointer_cast<OptionVendor>(tmp);
-    ASSERT_TRUE(vendor_resp);
-
-    OptionPtr docsis33 = vendor_resp->getOption(33);
-    ASSERT_TRUE(docsis33);
-
-    OptionStringPtr config_file = boost::dynamic_pointer_cast<OptionString>(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<OptionVendor> vendor_resp =
-        boost::dynamic_pointer_cast<OptionVendor>(tmp);
-    ASSERT_TRUE(vendor_resp);
-
-    OptionPtr docsis33 = vendor_resp->getOption(33);
-    ASSERT_TRUE(docsis33);
-
-    OptionStringPtr config_file = boost::dynamic_pointer_cast<OptionString>(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 (file)
index 0000000..e076dd6
--- /dev/null
@@ -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 <config.h>
+
+#include <asiolink/io_address.h>
+#include <dhcp/dhcp6.h>
+#include <dhcp/option_vendor.h>
+#include <dhcp/option_vendor_class.h>
+#include <dhcp6/tests/dhcp6_test_utils.h>
+#include <dhcp6/tests/dhcp6_client.h>
+#include <dhcp6/json_config_parser.h>
+#include <dhcp/tests/pkt_captures.h>
+#include <dhcp/docsis3_option_defs.h>
+#include <dhcp/tests/iface_mgr_test_config.h>
+#include <dhcp/option_string.h>
+#include <cc/command_interpreter.h>
+#include <gtest/gtest.h>
+
+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<OptionVendor> vendor = boost::dynamic_pointer_cast<OptionVendor>(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<OptionVendor> vendor = boost::dynamic_pointer_cast<OptionVendor>(opt);
+    ASSERT_TRUE(vendor);
+
+    opt = vendor->getOption(DOCSIS3_V6_ORO);
+    ASSERT_TRUE(opt);
+
+    OptionUint16ArrayPtr oro = boost::dynamic_pointer_cast<OptionUint16Array>(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<OptionUint16Array> 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<OptionVendor> vendor_resp =
+        boost::dynamic_pointer_cast<OptionVendor>(tmp);
+    ASSERT_TRUE(vendor_resp);
+
+    OptionPtr docsis33 = vendor_resp->getOption(33);
+    ASSERT_TRUE(docsis33);
+
+    OptionStringPtr config_file = boost::dynamic_pointer_cast<OptionString>(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<OptionVendor> vendor_resp =
+        boost::dynamic_pointer_cast<OptionVendor>(tmp);
+    ASSERT_TRUE(vendor_resp);
+
+    OptionPtr docsis33 = vendor_resp->getOption(33);
+    ASSERT_TRUE(docsis33);
+
+    OptionStringPtr config_file = boost::dynamic_pointer_cast<OptionString>(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());
+}
+