"Fossies" - the Fresh Open Source Software Archive

Member "kea-1.6.2/src/bin/dhcp4/tests/vendor_opts_unittest.cc" (21 Feb 2020, 60480 Bytes) of package /linux/misc/kea-1.6.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "vendor_opts_unittest.cc": 1.6.1_vs_1.6.2.

    1 // Copyright (C) 2019-2020 Internet Systems Consortium, Inc. ("ISC")
    2 //
    3 // This Source Code Form is subject to the terms of the Mozilla Public
    4 // License, v. 2.0. If a copy of the MPL was not distributed with this
    5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
    6 
    7 // This file is dedicated to testing vendor options. There are several
    8 // vendor options in DHCPv4:
    9 //
   10 // vivso (125) - vendor independent vendor specific option. This is by far the
   11 //               most popular
   12 // vendor specific (43) - this is probably the second most popular.
   13 //               Unfortunately, its definition is blurry, so there are many
   14 //               similar, but not exact implementations that do things in
   15 //               different ways.
   16 // vivco (124) - vendor indepdent vendor class option.
   17 // class identifier (60) - not exactly vendor specific. It's a string, but the
   18 //               content of that string identifies what kind of vendor device
   19 //               this is.
   20 
   21 #include <config.h>
   22 #include <asiolink/io_address.h>
   23 #include <cc/command_interpreter.h>
   24 #include <dhcp4/tests/dhcp4_test_utils.h>
   25 #include <dhcp4/dhcp4_srv.h>
   26 #include <dhcp4/json_config_parser.h>
   27 #include <dhcp4/tests/dhcp4_client.h>
   28 #include <dhcp/tests/iface_mgr_test_config.h>
   29 #include <dhcp/option_int_array.h>
   30 #include <dhcp/option_int.h>
   31 #include <dhcp/option_string.h>
   32 #include <dhcp/option_vendor.h>
   33 #include <dhcp/tests/pkt_captures.h>
   34 #include <dhcp/docsis3_option_defs.h>
   35 #include <dhcp/dhcp4.h>
   36 #include <dhcpsrv/cfgmgr.h>
   37 
   38 #include <gtest/gtest.h>
   39 
   40 using namespace isc::asiolink;
   41 using namespace isc::dhcp;
   42 using namespace isc::config;
   43 using namespace isc::dhcp::test;
   44 
   45 /// @brief Class dedicated to testing vendor options in DHCPv4
   46 ///
   47 /// For the time being it does not provide any additional functionality, but it
   48 /// groups all vendor related tests under a single name. There were too many
   49 /// tests in Dhcpv4SrvTest class anyway.
   50 class VendorOptsTest : public Dhcpv4SrvTest {
   51 
   52 };
   53 
   54 /// @todo Add more extensive vendor options tests, including multiple
   55 ///       vendor options
   56 
   57 // Checks if vendor options are parsed correctly and requested vendor options
   58 // are echoed back.
   59 TEST_F(VendorOptsTest, vendorOptionsDocsis) {
   60     IfaceMgrTestConfig test_config(true);
   61     IfaceMgr::instance().openSockets4();
   62 
   63     NakedDhcpv4Srv srv(0);
   64 
   65     string config = "{ \"interfaces-config\": {"
   66         "    \"interfaces\": [ \"*\" ]"
   67         "},"
   68         "    \"option-data\": [ {"
   69         "          \"name\": \"tftp-servers\","
   70         "          \"space\": \"vendor-4491\","
   71         "          \"code\": 2,"
   72         "          \"data\": \"10.253.175.16\","
   73         "          \"csv-format\": true"
   74         "        }],"
   75         "\"subnet4\": [ { "
   76         "    \"pools\": [ { \"pool\": \"10.254.226.0/25\" } ],"
   77         "    \"subnet\": \"10.254.226.0/24\", "
   78         "    \"interface\": \"eth0\" "
   79         " } ],"
   80         "\"valid-lifetime\": 4000 }";
   81 
   82     ConstElementPtr json;
   83     ASSERT_NO_THROW(json = parseDHCP4(config));
   84     ConstElementPtr status;
   85 
   86     // Configure the server and make sure the config is accepted
   87     EXPECT_NO_THROW(status = configureDhcp4Server(srv, json));
   88     ASSERT_TRUE(status);
   89     comment_ = parseAnswer(rcode_, status);
   90     ASSERT_EQ(0, rcode_);
   91 
   92     CfgMgr::instance().commit();
   93 
   94     // Let's create a relayed DISCOVER. This particular relayed DISCOVER has
   95     // added option 82 (relay agent info) with 3 suboptions. The server
   96     // is supposed to echo it back in its response.
   97     Pkt4Ptr dis;
   98     ASSERT_NO_THROW(dis = PktCaptures::captureRelayedDiscover());
   99 
  100     // Simulate that we have received that traffic
  101     srv.fakeReceive(dis);
  102 
  103     // Server will now process to run its normal loop, but instead of calling
  104     // IfaceMgr::receive4(), it will read all packets from the list set by
  105     // fakeReceive()
  106     // In particular, it should call registered buffer4_receive callback.
  107     srv.run();
  108 
  109     // Check that the server did send a response
  110     ASSERT_EQ(1, srv.fake_sent_.size());
  111 
  112     // Make sure that we received a response
  113     Pkt4Ptr offer = srv.fake_sent_.front();
  114     ASSERT_TRUE(offer);
  115 
  116     // Get Relay Agent Info from query...
  117     OptionPtr vendor_opt_response = offer->getOption(DHO_VIVSO_SUBOPTIONS);
  118     ASSERT_TRUE(vendor_opt_response);
  119 
  120     // Check if it's of a correct type
  121     boost::shared_ptr<OptionVendor> vendor_opt =
  122         boost::dynamic_pointer_cast<OptionVendor>(vendor_opt_response);
  123     ASSERT_TRUE(vendor_opt);
  124 
  125     // Get Relay Agent Info from response...
  126     OptionPtr tftp_servers_generic = vendor_opt->getOption(DOCSIS3_V4_TFTP_SERVERS);
  127     ASSERT_TRUE(tftp_servers_generic);
  128 
  129     Option4AddrLstPtr tftp_servers =
  130         boost::dynamic_pointer_cast<Option4AddrLst>(tftp_servers_generic);
  131 
  132     ASSERT_TRUE(tftp_servers);
  133 
  134     Option4AddrLst::AddressContainer addrs = tftp_servers->getAddresses();
  135     ASSERT_EQ(1, addrs.size());
  136     EXPECT_EQ("10.253.175.16", addrs[0].toText());
  137 }
  138 
  139 // Checks if server is able to handle a relayed traffic from DOCSIS3.0 modems
  140 TEST_F(VendorOptsTest, docsisVendorOptionsParse) {
  141 
  142     // Let's get a traffic capture from DOCSIS3.0 modem
  143     Pkt4Ptr dis = PktCaptures::captureRelayedDiscover();
  144     ASSERT_NO_THROW(dis->unpack());
  145 
  146     // Check if the packet contain
  147     OptionPtr opt = dis->getOption(DHO_VIVSO_SUBOPTIONS);
  148     ASSERT_TRUE(opt);
  149 
  150     boost::shared_ptr<OptionVendor> vendor = boost::dynamic_pointer_cast<OptionVendor>(opt);
  151     ASSERT_TRUE(vendor);
  152 
  153     // This particular capture that we have included options 1 and 5
  154     EXPECT_TRUE(vendor->getOption(1));
  155     EXPECT_TRUE(vendor->getOption(5));
  156 
  157     // It did not include options any other options
  158     EXPECT_FALSE(vendor->getOption(2));
  159     EXPECT_FALSE(vendor->getOption(3));
  160     EXPECT_FALSE(vendor->getOption(17));
  161 }
  162 
  163 // Checks if server is able to parse incoming docsis option and extract suboption 1 (docsis ORO)
  164 TEST_F(VendorOptsTest, docsisVendorORO) {
  165 
  166     // Let's get a traffic capture from DOCSIS3.0 modem
  167     Pkt4Ptr dis = PktCaptures::captureRelayedDiscover();
  168     EXPECT_NO_THROW(dis->unpack());
  169 
  170     // Check if the packet contains vendor specific information option
  171     OptionPtr opt = dis->getOption(DHO_VIVSO_SUBOPTIONS);
  172     ASSERT_TRUE(opt);
  173 
  174     boost::shared_ptr<OptionVendor> vendor = boost::dynamic_pointer_cast<OptionVendor>(opt);
  175     ASSERT_TRUE(vendor);
  176 
  177     opt = vendor->getOption(DOCSIS3_V4_ORO);
  178     ASSERT_TRUE(opt);
  179 
  180     OptionUint8ArrayPtr oro = boost::dynamic_pointer_cast<OptionUint8Array>(opt);
  181     EXPECT_TRUE(oro);
  182 }
  183 
  184 // This test checks if Option Request Option (ORO) in docsis (vendor-id=4491)
  185 // vendor options is parsed correctly and the requested options are actually assigned.
  186 TEST_F(VendorOptsTest, vendorOptionsORO) {
  187     IfaceMgrTestConfig test_config(true);
  188     IfaceMgr::instance().openSockets4();
  189 
  190     NakedDhcpv4Srv srv(0);
  191 
  192     ConstElementPtr x;
  193     string config = "{ \"interfaces-config\": {"
  194         "    \"interfaces\": [ \"*\" ]"
  195         "},"
  196         "    \"option-data\": [ {"
  197         "          \"name\": \"tftp-servers\","
  198         "          \"space\": \"vendor-4491\","
  199         "          \"code\": 2,"
  200         "          \"data\": \"192.0.2.1, 192.0.2.2\","
  201         "          \"csv-format\": true"
  202         "        }],"
  203         "\"subnet4\": [ { "
  204         "    \"pools\": [ { \"pool\": \"192.0.2.0/25\" } ],"
  205         "    \"subnet\": \"192.0.2.0/24\", "
  206         "    \"interface\": \"eth0\" "
  207         " } ]"
  208         "}";
  209 
  210     ConstElementPtr json;
  211     ASSERT_NO_THROW(json = parseDHCP4(config));
  212 
  213     EXPECT_NO_THROW(x = configureDhcp4Server(srv, json));
  214     ASSERT_TRUE(x);
  215     comment_ = parseAnswer(rcode_, x);
  216     ASSERT_EQ(0, rcode_);
  217 
  218     CfgMgr::instance().commit();
  219 
  220     boost::shared_ptr<Pkt4> dis(new Pkt4(DHCPDISCOVER, 1234));
  221     // Set the giaddr and hops to non-zero address as if it was relayed.
  222     dis->setGiaddr(IOAddress("192.0.2.1"));
  223     dis->setHops(1);
  224 
  225     OptionPtr clientid = generateClientId();
  226     dis->addOption(clientid);
  227     // Set interface. It is required by the server to generate server id.
  228     dis->setIface("eth0");
  229 
  230     // Pass it to the server and get an advertise
  231     Pkt4Ptr offer = srv.processDiscover(dis);
  232 
  233     // check if we get response at all
  234     ASSERT_TRUE(offer);
  235 
  236     // We did not include any vendor opts in DISCOVER, so there should be none
  237     // in OFFER.
  238     ASSERT_FALSE(offer->getOption(DHO_VIVSO_SUBOPTIONS));
  239 
  240     // Let's add a vendor-option (vendor-id=4491) with a single sub-option.
  241     // That suboption has code 1 and is a docsis ORO option.
  242     boost::shared_ptr<OptionUint8Array> vendor_oro(new OptionUint8Array(Option::V4,
  243                                                                         DOCSIS3_V4_ORO));
  244     vendor_oro->addValue(DOCSIS3_V4_TFTP_SERVERS); // Request option 33
  245     OptionPtr vendor(new OptionVendor(Option::V4, 4491));
  246     vendor->addOption(vendor_oro);
  247     dis->addOption(vendor);
  248 
  249     // Need to process SOLICIT again after requesting new option.
  250     offer = srv.processDiscover(dis);
  251     ASSERT_TRUE(offer);
  252 
  253     // Check if there is a vendor option response
  254     OptionPtr tmp = offer->getOption(DHO_VIVSO_SUBOPTIONS);
  255     ASSERT_TRUE(tmp);
  256 
  257     // The response should be OptionVendor object
  258     boost::shared_ptr<OptionVendor> vendor_resp =
  259         boost::dynamic_pointer_cast<OptionVendor>(tmp);
  260     ASSERT_TRUE(vendor_resp);
  261 
  262     OptionPtr docsis2 = vendor_resp->getOption(DOCSIS3_V4_TFTP_SERVERS);
  263     ASSERT_TRUE(docsis2);
  264 
  265     Option4AddrLstPtr tftp_srvs = boost::dynamic_pointer_cast<Option4AddrLst>(docsis2);
  266     ASSERT_TRUE(tftp_srvs);
  267 
  268     Option4AddrLst::AddressContainer addrs = tftp_srvs->getAddresses();
  269     ASSERT_EQ(2, addrs.size());
  270     EXPECT_EQ("192.0.2.1", addrs[0].toText());
  271     EXPECT_EQ("192.0.2.2", addrs[1].toText());
  272 }
  273 
  274 // This test checks if Option Request Option (ORO) in docsis (vendor-id=4491)
  275 // vendor options is parsed correctly and persistent options are actually assigned.
  276 TEST_F(VendorOptsTest, vendorPersistentOptions) {
  277     IfaceMgrTestConfig test_config(true);
  278     IfaceMgr::instance().openSockets4();
  279 
  280     NakedDhcpv4Srv srv(0);
  281 
  282     ConstElementPtr x;
  283     string config = "{ \"interfaces-config\": {"
  284         "    \"interfaces\": [ \"*\" ]"
  285         "},"
  286         "    \"option-data\": [ {"
  287         "          \"name\": \"tftp-servers\","
  288         "          \"space\": \"vendor-4491\","
  289         "          \"code\": 2,"
  290         "          \"data\": \"192.0.2.1, 192.0.2.2\","
  291         "          \"csv-format\": true,"
  292         "          \"always-send\": true"
  293         "        }],"
  294         "\"subnet4\": [ { "
  295         "    \"pools\": [ { \"pool\": \"192.0.2.0/25\" } ],"
  296         "    \"subnet\": \"192.0.2.0/24\", "
  297         "    \"interface\": \"eth0\" "
  298         " } ]"
  299         "}";
  300 
  301     ConstElementPtr json;
  302     ASSERT_NO_THROW(json = parseDHCP4(config));
  303 
  304     EXPECT_NO_THROW(x = configureDhcp4Server(srv, json));
  305     ASSERT_TRUE(x);
  306     comment_ = parseAnswer(rcode_, x);
  307     ASSERT_EQ(0, rcode_);
  308 
  309     CfgMgr::instance().commit();
  310 
  311     boost::shared_ptr<Pkt4> dis(new Pkt4(DHCPDISCOVER, 1234));
  312     // Set the giaddr and hops to non-zero address as if it was relayed.
  313     dis->setGiaddr(IOAddress("192.0.2.1"));
  314     dis->setHops(1);
  315 
  316     OptionPtr clientid = generateClientId();
  317     dis->addOption(clientid);
  318     // Set interface. It is required by the server to generate server id.
  319     dis->setIface("eth0");
  320 
  321     // Let's add a vendor-option (vendor-id=4491).
  322     OptionPtr vendor(new OptionVendor(Option::V4, 4491));
  323     dis->addOption(vendor);
  324 
  325     // Pass it to the server and get an advertise
  326     Pkt4Ptr offer = srv.processDiscover(dis);
  327 
  328     // check if we get response at all
  329     ASSERT_TRUE(offer);
  330 
  331     // Check if there is a vendor option response
  332     OptionPtr tmp = offer->getOption(DHO_VIVSO_SUBOPTIONS);
  333     ASSERT_TRUE(tmp);
  334 
  335     // The response should be OptionVendor object
  336     boost::shared_ptr<OptionVendor> vendor_resp =
  337         boost::dynamic_pointer_cast<OptionVendor>(tmp);
  338     ASSERT_TRUE(vendor_resp);
  339 
  340     OptionPtr docsis2 = vendor_resp->getOption(DOCSIS3_V4_TFTP_SERVERS);
  341     ASSERT_TRUE(docsis2);
  342 
  343     Option4AddrLstPtr tftp_srvs = boost::dynamic_pointer_cast<Option4AddrLst>(docsis2);
  344     ASSERT_TRUE(tftp_srvs);
  345 
  346     Option4AddrLst::AddressContainer addrs = tftp_srvs->getAddresses();
  347     ASSERT_EQ(2, addrs.size());
  348     EXPECT_EQ("192.0.2.1", addrs[0].toText());
  349     EXPECT_EQ("192.0.2.2", addrs[1].toText());
  350 }
  351 
  352 // Test checks whether it is possible to use option definitions defined in
  353 // src/lib/dhcp/docsis3_option_defs.h.
  354 TEST_F(VendorOptsTest, vendorOptionsDocsisDefinitions) {
  355     ConstElementPtr x;
  356     string config_prefix = "{ \"interfaces-config\": {"
  357         "    \"interfaces\": [ ]"
  358         "},"
  359         "    \"option-data\": [ {"
  360         "          \"name\": \"tftp-servers\","
  361         "          \"space\": \"vendor-4491\","
  362         "          \"code\": ";
  363     string config_postfix = ","
  364         "          \"data\": \"192.0.2.1\","
  365         "          \"csv-format\": true"
  366         "        }],"
  367         "\"subnet4\": [ { "
  368         "    \"pools\": [ { \"pool\":  \"192.0.2.1 - 192.0.2.50\" } ],"
  369         "    \"subnet\": \"192.0.2.0/24\", "
  370         "    \"interface\": \"\""
  371         " } ]"
  372         "}";
  373 
  374     // There is docsis3 (vendor-id=4491) vendor option 2, which is a
  375     // tftp-server. Its format is list of IPv4 addresses.
  376     string config_valid = config_prefix + "2" + config_postfix;
  377 
  378     // There is no option 99 defined in vendor-id=4491. As there is no
  379     // definition, the config should fail.
  380     string config_bogus = config_prefix + "99" + config_postfix;
  381 
  382     ConstElementPtr json_bogus;
  383     ASSERT_NO_THROW(json_bogus = parseDHCP4(config_bogus));
  384     ConstElementPtr json_valid;
  385     ASSERT_NO_THROW(json_valid = parseDHCP4(config_valid));
  386 
  387     NakedDhcpv4Srv srv(0);
  388 
  389     // This should fail (missing option definition)
  390     EXPECT_NO_THROW(x = configureDhcp4Server(srv, json_bogus));
  391     ASSERT_TRUE(x);
  392     comment_ = parseAnswer(rcode_, x);
  393     ASSERT_EQ(1, rcode_);
  394 
  395     // This should work (option definition present)
  396     EXPECT_NO_THROW(x = configureDhcp4Server(srv, json_valid));
  397     ASSERT_TRUE(x);
  398     comment_ = parseAnswer(rcode_, x);
  399     ASSERT_EQ(0, rcode_);
  400 }
  401 
  402 /// Checks if DOCSIS client packets are classified properly
  403 ///
  404 /// The test has been updated to work with the updated generic
  405 /// vendor options handling code.
  406 TEST_F(VendorOptsTest, docsisClientClassification) {
  407 
  408     NakedDhcpv4Srv srv(0);
  409 
  410     // Let's create a relayed DISCOVER. This particular relayed DISCOVER has
  411     // vendor-class set to docsis3.0
  412     Pkt4Ptr dis1;
  413     ASSERT_NO_THROW(dis1 = PktCaptures::captureRelayedDiscover());
  414     ASSERT_NO_THROW(dis1->unpack());
  415 
  416     srv.classifyPacket(dis1);
  417 
  418     EXPECT_TRUE(dis1->inClass(srv.VENDOR_CLASS_PREFIX + "docsis3.0:"));
  419     EXPECT_FALSE(dis1->inClass(srv.VENDOR_CLASS_PREFIX + "eRouter1.0"));
  420 
  421     // Let's create a relayed DISCOVER. This particular relayed DISCOVER has
  422     // vendor-class set to eRouter1.0
  423     Pkt4Ptr dis2;
  424     ASSERT_NO_THROW(dis2 = PktCaptures::captureRelayedDiscover2());
  425     ASSERT_NO_THROW(dis2->unpack());
  426 
  427     srv.classifyPacket(dis2);
  428 
  429     EXPECT_TRUE(dis2->inClass(srv.VENDOR_CLASS_PREFIX + "eRouter1.0"));
  430     EXPECT_FALSE(dis2->inClass(srv.VENDOR_CLASS_PREFIX + "docsis3.0:"));
  431 }
  432 
  433 // Checks that it's possible to have a vivso (125) option in the response
  434 // only. Once specific client (Genexis) sends only vendor-class info and
  435 // expects the server to include vivso in the response.
  436 TEST_F(VendorOptsTest, vivsoInResponseOnly) {
  437     IfaceMgrTestConfig test_config(true);
  438     IfaceMgr::instance().openSockets4();
  439     Dhcp4Client client;
  440 
  441     // The config defines custom vendor 125 suboption 2 that conveys a TFTP URL.
  442     // The client doesn't send vendor 125 option, so normal vendor option
  443     // processing is impossible. However, since there's a class defined that
  444     // matches client's packets and that class inserts vivso in the response,
  445     // Kea should be able to figure out the vendor-id and then also insert
  446     // suboption 2 with the TFTP URL.
  447     string config =
  448         "{"
  449         "    \"interfaces-config\": {"
  450         "        \"interfaces\": [ \"*\" ]"
  451         "    },"
  452         "    \"option-def\": ["
  453         "        {"
  454         "            \"name\": \"tftp\","
  455         "            \"code\": 2,"
  456         "            \"space\": \"vendor-25167\","
  457         "            \"type\": \"string\""
  458         "        }"
  459         "    ],"
  460         "    \"client-classes\": ["
  461         "    {"
  462         "        \"name\": \"cpe_genexis\","
  463         "        \"test\": \"substring(option[60].hex,0,7) == 'HMC1000'\","
  464         "        \"option-data\": ["
  465         "        {"
  466         "            \"name\": \"vivso-suboptions\","
  467         "            \"data\": \"25167\""
  468         "        },"
  469         "        {"
  470         "            \"name\": \"tftp\","
  471         "            \"space\": \"vendor-25167\","
  472         "            \"data\": \"tftp://192.0.2.1/genexis/HMC1000.v1.3.0-R.img\","
  473         "            \"always-send\": true"
  474         "        } ]"
  475         "    } ],"
  476         "\"subnet4\": [ { "
  477         "    \"pools\": [ { \"pool\": \"192.0.2.0/25\" } ],"
  478         "    \"subnet\": \"192.0.2.0/24\", "
  479         "    \"interface\": \"eth0\" "
  480         " } ]"
  481         "}";
  482 
  483     EXPECT_NO_THROW(configure(config, *client.getServer()));
  484 
  485     // Add a vendor-class identifier (this matches what Genexis hardware sends)
  486     OptionPtr vopt(new OptionString(Option::V4, DHO_VENDOR_CLASS_IDENTIFIER,
  487                                     "HMC1000.v1.3.0-R,Element-P1090,genexis.eu"));
  488     client.addExtraOption(vopt);
  489     client.requestOptions(DHO_VIVSO_SUBOPTIONS);
  490 
  491     // Let's check whether the server is not able to process this packet
  492     // and include vivso with appropriate sub-options
  493     EXPECT_NO_THROW(client.doDiscover());
  494     ASSERT_TRUE(client.getContext().response_);
  495 
  496     // Check there's a response.
  497     OptionPtr rsp = client.getContext().response_->getOption(DHO_VIVSO_SUBOPTIONS);
  498     ASSERT_TRUE(rsp);
  499 
  500     // Check that it includes vivso with vendor-id = 25167
  501     OptionVendorPtr rsp_vivso = boost::dynamic_pointer_cast<OptionVendor>(rsp);
  502     ASSERT_TRUE(rsp_vivso);
  503     EXPECT_EQ(25167, rsp_vivso->getVendorId());
  504 
  505     // Now check that it contains suboption 2 with appropriate content.
  506     OptionPtr subopt2 = rsp_vivso->getOption(2);
  507     ASSERT_TRUE(subopt2);
  508     vector<uint8_t> subopt2bin = subopt2->toBinary(false);
  509     string txt(subopt2bin.begin(), subopt2bin.end());
  510     EXPECT_EQ("tftp://192.0.2.1/genexis/HMC1000.v1.3.0-R.img", txt);
  511 }
  512 
  513 // Verifies last resort option 43 is backward compatible
  514 TEST_F(VendorOptsTest, option43LastResort) {
  515     IfaceMgrTestConfig test_config(true);
  516     IfaceMgr::instance().openSockets4();
  517 
  518     NakedDhcpv4Srv srv(0);
  519 
  520     // If there is no definition for option 43 a last resort
  521     // one is applied. This definition was used by Kea <= 1.2
  522     // so should be backward compatible.
  523     string config = "{ \"interfaces-config\": {"
  524         "    \"interfaces\": [ \"*\" ] }, "
  525         "\"subnet4\": [ "
  526         "{   \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ], "
  527         "    \"subnet\": \"192.0.2.0/24\" } ],"
  528         "\"option-def\": [ "
  529         "{   \"code\": 1, "
  530         "    \"name\": \"foo\", "
  531         "    \"space\":  \"vendor-encapsulated-options-space\", "
  532         "    \"type\": \"uint32\" } ],"
  533         "\"option-data\": [ "
  534         "{   \"name\": \"foo\", "
  535         "    \"space\": \"vendor-encapsulated-options-space\", "
  536         "    \"data\": \"12345678\" }, "
  537         "{   \"name\": \"vendor-class-identifier\", "
  538         "    \"data\": \"bar\" }, "
  539         "{   \"name\": \"vendor-encapsulated-options\" } ] }";
  540 
  541     ConstElementPtr json;
  542     ASSERT_NO_THROW(json = parseDHCP4(config));
  543     ConstElementPtr status;
  544 
  545     // Configure the server and make sure the config is accepted
  546     EXPECT_NO_THROW(status = configureDhcp4Server(srv, json));
  547     ASSERT_TRUE(status);
  548     comment_ = parseAnswer(rcode_, status);
  549     ASSERT_EQ(0, rcode_);
  550 
  551     CfgMgr::instance().commit();
  552 
  553     // Create a packet with enough to select the subnet and go through
  554     // the DISCOVER processing
  555     Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 1234));
  556     query->setRemoteAddr(IOAddress("192.0.2.1"));
  557     OptionPtr clientid = generateClientId();
  558     query->addOption(clientid);
  559     query->setIface("eth1");
  560 
  561     // Create and add a PRL option to the query
  562     OptionUint8ArrayPtr prl(new OptionUint8Array(Option::V4,
  563                                                  DHO_DHCP_PARAMETER_REQUEST_LIST));
  564     ASSERT_TRUE(prl);
  565     prl->addValue(DHO_VENDOR_ENCAPSULATED_OPTIONS);
  566     prl->addValue(DHO_VENDOR_CLASS_IDENTIFIER);
  567     query->addOption(prl);
  568 
  569     srv.classifyPacket(query);
  570     ASSERT_NO_THROW(srv.deferredUnpack(query));
  571 
  572     // Pass it to the server and get an offer
  573     Pkt4Ptr offer = srv.processDiscover(query);
  574 
  575     // Check if we get response at all
  576     checkResponse(offer, DHCPOFFER, 1234);
  577 
  578     // Processing should add a vendor-class-identifier (code 60)
  579     OptionPtr opt = offer->getOption(DHO_VENDOR_CLASS_IDENTIFIER);
  580     EXPECT_TRUE(opt);
  581 
  582     // And a vendor-encapsulated-options (code 43)
  583     opt = offer->getOption(DHO_VENDOR_ENCAPSULATED_OPTIONS);
  584     ASSERT_TRUE(opt);
  585     const OptionCollection& opts = opt->getOptions();
  586     ASSERT_EQ(1, opts.size());
  587     OptionPtr sopt = opts.begin()->second;
  588     ASSERT_TRUE(sopt);
  589     EXPECT_EQ(1, sopt->getType());
  590 }
  591 
  592 // Checks effect of raw not compatible option 43 (no failure)
  593 TEST_F(VendorOptsTest, option43BadRaw) {
  594     IfaceMgrTestConfig test_config(true);
  595     IfaceMgr::instance().openSockets4();
  596 
  597     NakedDhcpv4Srv srv(0);
  598 
  599     // The vendor-encapsulated-options has an incompatible data
  600     // so won't have the expected content but processing of truncated
  601     // (suboption length > available length) suboptions does not raise
  602     // an exception.
  603     string config = "{ \"interfaces-config\": {"
  604         "    \"interfaces\": [ \"*\" ] }, "
  605         "\"subnet4\": [ "
  606         "{   \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ], "
  607         "    \"subnet\": \"192.0.2.0/24\" } ],"
  608         "\"option-data\": [ "
  609         "{   \"name\": \"vendor-class-identifier\", "
  610         "    \"data\": \"bar\" }, "
  611         "{   \"name\": \"vendor-encapsulated-options\", "
  612         "    \"csv-format\": false, "
  613         "    \"data\": \"0102\" } ] }";
  614 
  615     ConstElementPtr json;
  616     ASSERT_NO_THROW(json = parseDHCP4(config));
  617     ConstElementPtr status;
  618 
  619     // Configure the server and make sure the config is accepted
  620     EXPECT_NO_THROW(status = configureDhcp4Server(srv, json));
  621     ASSERT_TRUE(status);
  622     comment_ = parseAnswer(rcode_, status);
  623     ASSERT_EQ(0, rcode_);
  624 
  625     CfgMgr::instance().commit();
  626 
  627     // Create a packet with enough to select the subnet and go through
  628     // the DISCOVER processing
  629     Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 1234));
  630     query->setRemoteAddr(IOAddress("192.0.2.1"));
  631     OptionPtr clientid = generateClientId();
  632     query->addOption(clientid);
  633     query->setIface("eth1");
  634 
  635     // Create and add a vendor-encapsulated-options (code 43)
  636     // with not compatible (not parsable as suboptions) content
  637     OptionBuffer buf;
  638     buf.push_back(0x01);
  639     buf.push_back(0x02);
  640     OptionPtr vopt(new Option(Option::V4, DHO_VENDOR_ENCAPSULATED_OPTIONS, buf));
  641     query->addOption(vopt);
  642     query->getDeferredOptions().push_back(DHO_VENDOR_ENCAPSULATED_OPTIONS);
  643 
  644     // Create and add a PRL option to the query
  645     OptionUint8ArrayPtr prl(new OptionUint8Array(Option::V4,
  646                                                  DHO_DHCP_PARAMETER_REQUEST_LIST));
  647     ASSERT_TRUE(prl);
  648     prl->addValue(DHO_VENDOR_ENCAPSULATED_OPTIONS);
  649     prl->addValue(DHO_VENDOR_CLASS_IDENTIFIER);
  650     query->addOption(prl);
  651 
  652     srv.classifyPacket(query);
  653     srv.deferredUnpack(query);
  654 
  655     // Check if the option was (uncorrectly) re-unpacked
  656     vopt = query->getOption(DHO_VENDOR_ENCAPSULATED_OPTIONS);
  657     OptionCustomPtr custom = boost::dynamic_pointer_cast<OptionCustom>(vopt);
  658     EXPECT_TRUE(custom);
  659 
  660     // Pass it to the server and get an offer
  661     Pkt4Ptr offer = srv.processDiscover(query);
  662 
  663     // Check if we get response at all
  664     checkResponse(offer, DHCPOFFER, 1234);
  665 
  666     // Processing should add a vendor-class-identifier (code 60)
  667     OptionPtr opt = offer->getOption(DHO_VENDOR_CLASS_IDENTIFIER);
  668     EXPECT_TRUE(opt);
  669 
  670     // And a vendor-encapsulated-options (code 43)
  671     opt = offer->getOption(DHO_VENDOR_ENCAPSULATED_OPTIONS);
  672     ASSERT_TRUE(opt);
  673     // But truncated.
  674     EXPECT_EQ(0, opt->len() - opt->getHeaderLen());
  675 }
  676 
  677 // Checks effect of raw not compatible option 43 (failure)
  678 TEST_F(VendorOptsTest, option43FailRaw) {
  679     IfaceMgrTestConfig test_config(true);
  680     IfaceMgr::instance().openSockets4();
  681 
  682     NakedDhcpv4Srv srv(0);
  683 
  684     // The vendor-encapsulated-options has an incompatible data
  685     // so won't have the expected content. Here the processing
  686     // of suboptions tries to unpack the uitn32 foo suboption and
  687     // raises an exception which is caught so the option stays unpacked.
  688     string config = "{ \"interfaces-config\": {"
  689         "    \"interfaces\": [ \"*\" ] }, "
  690         "\"subnet4\": [ "
  691         "{   \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ], "
  692         "    \"subnet\": \"192.0.2.0/24\" } ],"
  693         "\"option-def\": [ "
  694         "{   \"code\": 1, "
  695         "    \"name\": \"foo\", "
  696         "    \"space\":  \"vendor-encapsulated-options-space\", "
  697         "    \"type\": \"uint32\" } ],"
  698         "\"option-data\": [ "
  699         "{   \"name\": \"vendor-class-identifier\", "
  700         "    \"data\": \"bar\" }, "
  701         "{   \"name\": \"vendor-encapsulated-options\", "
  702         "    \"csv-format\": false, "
  703         "    \"data\": \"0102\" } ] }";
  704 
  705     ConstElementPtr json;
  706     ASSERT_NO_THROW(json = parseDHCP4(config));
  707     ConstElementPtr status;
  708 
  709     // Configure the server and make sure the config is accepted
  710     EXPECT_NO_THROW(status = configureDhcp4Server(srv, json));
  711     ASSERT_TRUE(status);
  712     comment_ = parseAnswer(rcode_, status);
  713     ASSERT_EQ(0, rcode_);
  714 
  715     CfgMgr::instance().commit();
  716 
  717     // Create a packet with enough to select the subnet and go through
  718     // the DISCOVER processing
  719     Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 1234));
  720     query->setRemoteAddr(IOAddress("192.0.2.1"));
  721     OptionPtr clientid = generateClientId();
  722     query->addOption(clientid);
  723     query->setIface("eth1");
  724 
  725     // Create and add a vendor-encapsulated-options (code 43)
  726     // with not compatible (not parsable as suboptions) content
  727     // which will raise an exception
  728     OptionBuffer buf;
  729     buf.push_back(0x01);
  730     buf.push_back(0x01);
  731     buf.push_back(0x01);
  732     OptionPtr vopt(new Option(Option::V4, DHO_VENDOR_ENCAPSULATED_OPTIONS, buf));
  733     query->addOption(vopt);
  734     query->getDeferredOptions().push_back(DHO_VENDOR_ENCAPSULATED_OPTIONS);
  735 
  736     // Create and add a PRL option to the query
  737     OptionUint8ArrayPtr prl(new OptionUint8Array(Option::V4,
  738                                                  DHO_DHCP_PARAMETER_REQUEST_LIST));
  739     ASSERT_TRUE(prl);
  740     prl->addValue(DHO_VENDOR_ENCAPSULATED_OPTIONS);
  741     prl->addValue(DHO_VENDOR_CLASS_IDENTIFIER);
  742     query->addOption(prl);
  743 
  744     srv.classifyPacket(query);
  745     EXPECT_NO_THROW(srv.deferredUnpack(query));
  746     ASSERT_TRUE(query->getOption(vopt->getType()));
  747     EXPECT_EQ(vopt, query->getOption(vopt->getType()));
  748 }
  749 
  750 // Verifies raw option 43 can be handled (global)
  751 TEST_F(VendorOptsTest, option43RawGlobal) {
  752     IfaceMgrTestConfig test_config(true);
  753     IfaceMgr::instance().openSockets4();
  754 
  755     NakedDhcpv4Srv srv(0);
  756 
  757     // The vendor-encapsulated-options is redefined as raw binary
  758     // in a global definition.
  759     string config = "{ \"interfaces-config\": {"
  760         "    \"interfaces\": [ \"*\" ] }, "
  761         "\"subnet4\": [ "
  762         "{   \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ], "
  763         "    \"subnet\": \"192.0.2.0/24\" } ],"
  764         "\"option-def\": [ "
  765         "{   \"code\": 43, "
  766         "    \"name\": \"vendor-encapsulated-options\", "
  767         "    \"type\": \"binary\" } ],"
  768         "\"option-data\": [ "
  769         "{   \"name\": \"vendor-class-identifier\", "
  770         "    \"data\": \"bar\" }, "
  771         "{   \"name\": \"vendor-encapsulated-options\", "
  772         "    \"csv-format\": false, "
  773         "    \"data\": \"0102\" } ] }";
  774 
  775     ConstElementPtr json;
  776     ASSERT_NO_THROW(json = parseDHCP4(config));
  777     ConstElementPtr status;
  778 
  779     // Configure the server and make sure the config is accepted
  780     EXPECT_NO_THROW(status = configureDhcp4Server(srv, json));
  781     ASSERT_TRUE(status);
  782     comment_ = parseAnswer(rcode_, status);
  783     ASSERT_EQ(0, rcode_);
  784 
  785     CfgMgr::instance().commit();
  786 
  787     // Create a packet with enough to select the subnet and go through
  788     // the DISCOVER processing
  789     Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 1234));
  790     query->setRemoteAddr(IOAddress("192.0.2.1"));
  791     OptionPtr clientid = generateClientId();
  792     query->addOption(clientid);
  793     query->setIface("eth1");
  794 
  795     // Create and add a vendor-encapsulated-options (code 43)
  796     // with not compatible (not parsable as suboptions) content
  797     OptionBuffer buf;
  798     buf.push_back(0x02);
  799     buf.push_back(0x03);
  800     OptionPtr vopt(new Option(Option::V4, DHO_VENDOR_ENCAPSULATED_OPTIONS, buf));
  801     query->addOption(vopt);
  802     query->getDeferredOptions().push_back(DHO_VENDOR_ENCAPSULATED_OPTIONS);
  803 
  804     // Create and add a PRL option to the query
  805     OptionUint8ArrayPtr prl(new OptionUint8Array(Option::V4,
  806                                                  DHO_DHCP_PARAMETER_REQUEST_LIST));
  807     ASSERT_TRUE(prl);
  808     prl->addValue(DHO_VENDOR_ENCAPSULATED_OPTIONS);
  809     prl->addValue(DHO_VENDOR_CLASS_IDENTIFIER);
  810     query->addOption(prl);
  811 
  812     srv.classifyPacket(query);
  813     ASSERT_NO_THROW(srv.deferredUnpack(query));
  814 
  815     // Check if the option was (correctly) re-unpacked
  816     vopt = query->getOption(DHO_VENDOR_ENCAPSULATED_OPTIONS);
  817     OptionCustomPtr custom = boost::dynamic_pointer_cast<OptionCustom>(vopt);
  818     EXPECT_FALSE(custom);
  819 
  820     // Pass it to the server and get an offer
  821     Pkt4Ptr offer = srv.processDiscover(query);
  822 
  823     // Check if we get response at all
  824     checkResponse(offer, DHCPOFFER, 1234);
  825 
  826     // Processing should add a vendor-class-identifier (code 60)
  827     OptionPtr opt = offer->getOption(DHO_VENDOR_CLASS_IDENTIFIER);
  828     EXPECT_TRUE(opt);
  829 
  830     // And a vendor-encapsulated-options (code 43)
  831     opt = offer->getOption(DHO_VENDOR_ENCAPSULATED_OPTIONS);
  832     ASSERT_TRUE(opt);
  833     // Verifies the content
  834     ASSERT_EQ(2, opt->len() - opt->getHeaderLen());
  835     EXPECT_EQ(0x01, opt->getData()[0]);
  836     EXPECT_EQ(0x02, opt->getData()[1]);
  837 }
  838 
  839 // Verifies raw option 43 can be handled (catch-all class)
  840 TEST_F(VendorOptsTest, option43RawClass) {
  841     IfaceMgrTestConfig test_config(true);
  842     IfaceMgr::instance().openSockets4();
  843 
  844     NakedDhcpv4Srv srv(0);
  845 
  846     // The vendor-encapsulated-options is redefined as raw binary
  847     // in a class definition.
  848     string config = "{ \"interfaces-config\": {"
  849         "    \"interfaces\": [ \"*\" ] }, "
  850         "\"subnet4\": [ "
  851         "{   \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ], "
  852         "    \"subnet\": \"192.0.2.0/24\" } ],"
  853         "\"client-classes\": [ "
  854         "{   \"name\": \"vendor\", "
  855         "    \"test\": \"option[vendor-encapsulated-options].exists\", "
  856         "    \"option-def\": [ "
  857         "    {   \"code\": 43, "
  858         "        \"name\": \"vendor-encapsulated-options\", "
  859         "        \"type\": \"binary\" } ],"
  860         "    \"option-data\": [ "
  861         "    {   \"name\": \"vendor-class-identifier\", "
  862         "        \"data\": \"bar\" }, "
  863         "    {   \"name\": \"vendor-encapsulated-options\", "
  864         "        \"csv-format\": false, "
  865         "        \"data\": \"0102\" } ] } ] }";
  866 
  867     ConstElementPtr json;
  868     ASSERT_NO_THROW(json = parseDHCP4(config));
  869     ConstElementPtr status;
  870 
  871     // Configure the server and make sure the config is accepted
  872     EXPECT_NO_THROW(status = configureDhcp4Server(srv, json));
  873     ASSERT_TRUE(status);
  874     comment_ = parseAnswer(rcode_, status);
  875     ASSERT_EQ(0, rcode_);
  876 
  877     CfgMgr::instance().commit();
  878 
  879     // Create a packet with enough to select the subnet and go through
  880     // the DISCOVER processing
  881     Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 1234));
  882     query->setRemoteAddr(IOAddress("192.0.2.1"));
  883     OptionPtr clientid = generateClientId();
  884     query->addOption(clientid);
  885     query->setIface("eth1");
  886 
  887     // Create and add a vendor-encapsulated-options (code 43)
  888     // with not compatible (not parsable as suboptions) content
  889     OptionBuffer buf;
  890     buf.push_back(0x02);
  891     buf.push_back(0x03);
  892     OptionPtr vopt(new Option(Option::V4, DHO_VENDOR_ENCAPSULATED_OPTIONS, buf));
  893     query->addOption(vopt);
  894     query->getDeferredOptions().push_back(DHO_VENDOR_ENCAPSULATED_OPTIONS);
  895 
  896     // Create and add a PRL option to the query
  897     OptionUint8ArrayPtr prl(new OptionUint8Array(Option::V4,
  898                                                  DHO_DHCP_PARAMETER_REQUEST_LIST));
  899     ASSERT_TRUE(prl);
  900     prl->addValue(DHO_VENDOR_ENCAPSULATED_OPTIONS);
  901     prl->addValue(DHO_VENDOR_CLASS_IDENTIFIER);
  902     query->addOption(prl);
  903 
  904     srv.classifyPacket(query);
  905     ASSERT_NO_THROW(srv.deferredUnpack(query));
  906 
  907     // Check if the option was (correctly) re-unpacked
  908     vopt = query->getOption(DHO_VENDOR_ENCAPSULATED_OPTIONS);
  909     OptionCustomPtr custom = boost::dynamic_pointer_cast<OptionCustom>(vopt);
  910     EXPECT_FALSE(custom);
  911 
  912     // Pass it to the server and get an offer
  913     Pkt4Ptr offer = srv.processDiscover(query);
  914 
  915     // Check if we get response at all
  916     checkResponse(offer, DHCPOFFER, 1234);
  917 
  918     // Processing should add a vendor-class-identifier (code 60)
  919     OptionPtr opt = offer->getOption(DHO_VENDOR_CLASS_IDENTIFIER);
  920     EXPECT_TRUE(opt);
  921 
  922     // And a vendor-encapsulated-options (code 43)
  923     opt = offer->getOption(DHO_VENDOR_ENCAPSULATED_OPTIONS);
  924     ASSERT_TRUE(opt);
  925     // Verifies the content
  926     ASSERT_EQ(2, opt->len() - opt->getHeaderLen());
  927     EXPECT_EQ(0x01, opt->getData()[0]);
  928     EXPECT_EQ(0x02, opt->getData()[1]);
  929 }
  930 
  931 // Verifies option 43 deferred processing (one class)
  932 TEST_F(VendorOptsTest, option43Class) {
  933     IfaceMgrTestConfig test_config(true);
  934     IfaceMgr::instance().openSockets4();
  935 
  936     NakedDhcpv4Srv srv(0);
  937 
  938     // A client class defines vendor-encapsulated-options (code 43)
  939     // and data for it and its sub-option.
  940     string config = "{ \"interfaces-config\": {"
  941         "    \"interfaces\": [ \"*\" ] }, "
  942         "\"option-def\": [ "
  943         "{   \"code\": 1, "
  944         "    \"name\": \"foo\", "
  945         "    \"space\":  \"alpha\", "
  946         "    \"type\": \"uint32\" } ],"
  947         "\"subnet4\": [ "
  948         "{   \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ], "
  949         "    \"subnet\": \"192.0.2.0/24\" } ],"
  950         "\"client-classes\": [ "
  951         "{   \"name\": \"alpha\", "
  952         "    \"test\": \"option[vendor-class-identifier].text == 'alpha'\", "
  953         "    \"option-def\": [ "
  954         "    {   \"code\": 43, "
  955         "        \"name\": \"vendor-encapsulated-options\", "
  956         "        \"type\": \"empty\", "
  957         "        \"encapsulate\": \"alpha\" } ],"
  958         "    \"option-data\": [ "
  959         "    {   \"name\": \"vendor-class-identifier\", "
  960         "        \"data\": \"alpha\" }, "
  961         "    {   \"name\": \"vendor-encapsulated-options\" }, "
  962         "    {   \"name\": \"foo\", "
  963         "        \"space\": \"alpha\", "
  964         "        \"data\": \"12345678\" } ] } ] }";
  965 
  966     ConstElementPtr json;
  967     ASSERT_NO_THROW(json = parseDHCP4(config));
  968     ConstElementPtr status;
  969 
  970     // Configure the server and make sure the config is accepted
  971     EXPECT_NO_THROW(status = configureDhcp4Server(srv, json));
  972     ASSERT_TRUE(status);
  973     comment_ = parseAnswer(rcode_, status);
  974     ASSERT_EQ(0, rcode_);
  975 
  976     CfgMgr::instance().commit();
  977 
  978     // Create a packet with enough to select the subnet and go through
  979     // the DISCOVER processing
  980     Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 1234));
  981     query->setRemoteAddr(IOAddress("192.0.2.1"));
  982     OptionPtr clientid = generateClientId();
  983     query->addOption(clientid);
  984     query->setIface("eth1");
  985 
  986     // Create and add a vendor-encapsulated-options (code 43)
  987     OptionBuffer buf;
  988     buf.push_back(0x01);
  989     buf.push_back(0x04);
  990     buf.push_back(0x87);
  991     buf.push_back(0x65);
  992     buf.push_back(0x43);
  993     buf.push_back(0x21);
  994     OptionPtr vopt(new Option(Option::V4, DHO_VENDOR_ENCAPSULATED_OPTIONS, buf));
  995     query->addOption(vopt);
  996     query->getDeferredOptions().push_back(DHO_VENDOR_ENCAPSULATED_OPTIONS);
  997 
  998     // Create and add a vendor-class-identifier (code 60)
  999     OptionStringPtr iopt(new OptionString(Option::V4,
 1000                                           DHO_VENDOR_CLASS_IDENTIFIER,
 1001                                           "alpha"));
 1002     query->addOption(iopt);
 1003 
 1004     // Create and add a PRL option to the query
 1005     OptionUint8ArrayPtr prl(new OptionUint8Array(Option::V4,
 1006                                                  DHO_DHCP_PARAMETER_REQUEST_LIST));
 1007     ASSERT_TRUE(prl);
 1008     prl->addValue(DHO_VENDOR_ENCAPSULATED_OPTIONS);
 1009     prl->addValue(DHO_VENDOR_CLASS_IDENTIFIER);
 1010     query->addOption(prl);
 1011 
 1012     srv.classifyPacket(query);
 1013     ASSERT_NO_THROW(srv.deferredUnpack(query));
 1014 
 1015     // Check if the option was (correctly) re-unpacked
 1016     vopt = query->getOption(DHO_VENDOR_ENCAPSULATED_OPTIONS);
 1017     OptionCustomPtr custom = boost::dynamic_pointer_cast<OptionCustom>(vopt);
 1018     EXPECT_TRUE(custom);
 1019     EXPECT_EQ(1, vopt->getOptions().size());
 1020 
 1021     // Pass it to the server and get an offer
 1022     Pkt4Ptr offer = srv.processDiscover(query);
 1023 
 1024     // Check if we get response at all
 1025     checkResponse(offer, DHCPOFFER, 1234);
 1026 
 1027     // Processing should add a vendor-class-identifier (code 60)
 1028     OptionPtr opt = offer->getOption(DHO_VENDOR_CLASS_IDENTIFIER);
 1029     EXPECT_TRUE(opt);
 1030 
 1031     // And a vendor-encapsulated-options (code 43)
 1032     opt = offer->getOption(DHO_VENDOR_ENCAPSULATED_OPTIONS);
 1033     ASSERT_TRUE(opt);
 1034     // Verifies the content
 1035     const OptionCollection& opts = opt->getOptions();
 1036     ASSERT_EQ(1, opts.size());
 1037     OptionPtr sopt = opts.begin()->second;
 1038     ASSERT_TRUE(sopt);
 1039     EXPECT_EQ(1, sopt->getType());
 1040     OptionUint32Ptr sopt32 = boost::dynamic_pointer_cast<OptionUint32>(sopt);
 1041     ASSERT_TRUE(sopt32);
 1042     EXPECT_EQ(12345678, sopt32->getValue());
 1043 }
 1044 
 1045 // Verifies option 43 priority
 1046 TEST_F(VendorOptsTest, option43ClassPriority) {
 1047     IfaceMgrTestConfig test_config(true);
 1048     IfaceMgr::instance().openSockets4();
 1049 
 1050     NakedDhcpv4Srv srv(0);
 1051 
 1052     // Both global and client-class scopes get vendor-encapsulated-options
 1053     // (code 43) definition and data. The client-class has precedence.
 1054     // Note it does not work without the vendor-encapsulated-options
 1055     // option-data in the client-class.
 1056     string config = "{ \"interfaces-config\": {"
 1057         "    \"interfaces\": [ \"*\" ] }, "
 1058         "\"option-def\": [ "
 1059         "{   \"code\": 1, "
 1060         "    \"name\": \"foo\", "
 1061         "    \"space\":  \"alpha\", "
 1062         "    \"type\": \"uint32\" },"
 1063         "{   \"code\": 1, "
 1064         "    \"name\": \"bar\", "
 1065         "    \"space\":  \"beta\", "
 1066         "    \"type\": \"uint8\" }, "
 1067         "{   \"code\": 43, "
 1068         "    \"name\": \"vendor-encapsulated-options\", "
 1069         "    \"type\": \"empty\", "
 1070         "    \"encapsulate\": \"beta\" } ],"
 1071         "\"option-data\": [ "
 1072         "{   \"name\": \"vendor-encapsulated-options\" }, "
 1073         "{   \"name\": \"vendor-class-identifier\", "
 1074         "    \"data\": \"beta\" }, "
 1075         "{   \"name\": \"bar\", "
 1076         "    \"space\": \"beta\", "
 1077         "    \"data\": \"33\" } ],"
 1078         "\"subnet4\": [ "
 1079         "{   \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ], "
 1080         "    \"subnet\": \"192.0.2.0/24\" } ],"
 1081         "\"client-classes\": [ "
 1082         "{   \"name\": \"alpha\", "
 1083         "    \"test\": \"option[vendor-class-identifier].text == 'alpha'\", "
 1084         "    \"option-def\": [ "
 1085         "    {   \"code\": 43, "
 1086         "        \"name\": \"vendor-encapsulated-options\", "
 1087         "        \"type\": \"empty\", "
 1088         "        \"encapsulate\": \"alpha\" } ],"
 1089         "    \"option-data\": [ "
 1090         "{   \"name\": \"vendor-encapsulated-options\" }, "
 1091         "    {   \"name\": \"vendor-class-identifier\", "
 1092         "        \"data\": \"alpha\" }, "
 1093         "    {   \"name\": \"foo\", "
 1094         "        \"space\": \"alpha\", "
 1095         "        \"data\": \"12345678\" } ] } ] }";
 1096 
 1097     ConstElementPtr json;
 1098     ASSERT_NO_THROW(json = parseDHCP4(config));
 1099     ConstElementPtr status;
 1100 
 1101     // Configure the server and make sure the config is accepted
 1102     EXPECT_NO_THROW(status = configureDhcp4Server(srv, json));
 1103     ASSERT_TRUE(status);
 1104     comment_ = parseAnswer(rcode_, status);
 1105     ASSERT_EQ(0, rcode_);
 1106 
 1107     CfgMgr::instance().commit();
 1108 
 1109     // Create a packet with enough to select the subnet and go through
 1110     // the DISCOVER processing
 1111     Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 1234));
 1112     query->setRemoteAddr(IOAddress("192.0.2.1"));
 1113     OptionPtr clientid = generateClientId();
 1114     query->addOption(clientid);
 1115     query->setIface("eth1");
 1116 
 1117     // Create and add a vendor-encapsulated-options (code 43)
 1118     OptionBuffer buf;
 1119     buf.push_back(0x01);
 1120     buf.push_back(0x04);
 1121     buf.push_back(0x87);
 1122     buf.push_back(0x65);
 1123     buf.push_back(0x43);
 1124     buf.push_back(0x21);
 1125     OptionPtr vopt(new Option(Option::V4, DHO_VENDOR_ENCAPSULATED_OPTIONS, buf));
 1126     query->addOption(vopt);
 1127     query->getDeferredOptions().push_back(DHO_VENDOR_ENCAPSULATED_OPTIONS);
 1128 
 1129     // Create and add a vendor-class-identifier (code 60)
 1130     OptionStringPtr iopt(new OptionString(Option::V4,
 1131                                           DHO_VENDOR_CLASS_IDENTIFIER,
 1132                                           "alpha"));
 1133     query->addOption(iopt);
 1134 
 1135     // Create and add a PRL option to the query
 1136     OptionUint8ArrayPtr prl(new OptionUint8Array(Option::V4,
 1137                                                  DHO_DHCP_PARAMETER_REQUEST_LIST));
 1138     ASSERT_TRUE(prl);
 1139     prl->addValue(DHO_VENDOR_ENCAPSULATED_OPTIONS);
 1140     prl->addValue(DHO_VENDOR_CLASS_IDENTIFIER);
 1141     query->addOption(prl);
 1142 
 1143     srv.classifyPacket(query);
 1144     ASSERT_NO_THROW(srv.deferredUnpack(query));
 1145 
 1146     // Check if the option was (correctly) re-unpacked
 1147     vopt = query->getOption(DHO_VENDOR_ENCAPSULATED_OPTIONS);
 1148     OptionCustomPtr custom = boost::dynamic_pointer_cast<OptionCustom>(vopt);
 1149     EXPECT_TRUE(custom);
 1150     EXPECT_EQ(1, vopt->getOptions().size());
 1151 
 1152     // Pass it to the server and get an offer
 1153     Pkt4Ptr offer = srv.processDiscover(query);
 1154 
 1155     // Check if we get response at all
 1156     checkResponse(offer, DHCPOFFER, 1234);
 1157 
 1158     // Processing should add a vendor-class-identifier (code 60)
 1159     OptionPtr opt = offer->getOption(DHO_VENDOR_CLASS_IDENTIFIER);
 1160     EXPECT_TRUE(opt);
 1161     OptionStringPtr id = boost::dynamic_pointer_cast<OptionString>(opt);
 1162     ASSERT_TRUE(id);
 1163     EXPECT_EQ("alpha", id->getValue());
 1164 
 1165     // And a vendor-encapsulated-options (code 43)
 1166     opt = offer->getOption(DHO_VENDOR_ENCAPSULATED_OPTIONS);
 1167     ASSERT_TRUE(opt);
 1168     // Verifies the content
 1169     const OptionCollection& opts = opt->getOptions();
 1170     ASSERT_EQ(1, opts.size());
 1171     OptionPtr sopt = opts.begin()->second;
 1172     ASSERT_TRUE(sopt);
 1173     EXPECT_EQ(1, sopt->getType());
 1174     EXPECT_EQ(2 + 4, sopt->len());
 1175     OptionUint32Ptr sopt32 = boost::dynamic_pointer_cast<OptionUint32>(sopt);
 1176     ASSERT_TRUE(sopt32);
 1177     EXPECT_EQ(12345678, sopt32->getValue());
 1178 }
 1179 
 1180 // Verifies option 43 deferred processing (two classes)
 1181 TEST_F(VendorOptsTest, option43Classes) {
 1182     IfaceMgrTestConfig test_config(true);
 1183     IfaceMgr::instance().openSockets4();
 1184 
 1185     NakedDhcpv4Srv srv(0);
 1186 
 1187     // Two client-class scopes get vendor-encapsulated-options
 1188     // (code 43) definition and data. The first matching client-class
 1189     // (from a set?) applies.
 1190     string config = "{ \"interfaces-config\": {"
 1191         "    \"interfaces\": [ \"*\" ] }, "
 1192         "\"option-def\": [ "
 1193         "{   \"code\": 1, "
 1194         "    \"name\": \"foo\", "
 1195         "    \"space\":  \"alpha\", "
 1196         "    \"type\": \"uint32\" },"
 1197         "{   \"code\": 1, "
 1198         "    \"name\": \"bar\", "
 1199         "    \"space\":  \"beta\", "
 1200         "    \"type\": \"uint8\" } ],"
 1201         "\"subnet4\": [ "
 1202         "{   \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ], "
 1203         "    \"subnet\": \"192.0.2.0/24\" } ],"
 1204         "\"client-classes\": [ "
 1205         "{   \"name\": \"alpha\", "
 1206         "    \"test\": \"option[vendor-class-identifier].text == 'alpha'\", "
 1207         "    \"option-def\": [ "
 1208         "    {   \"code\": 43, "
 1209         "        \"name\": \"vendor-encapsulated-options\", "
 1210         "        \"type\": \"empty\", "
 1211         "        \"encapsulate\": \"alpha\" } ],"
 1212         "    \"option-data\": [ "
 1213         "{   \"name\": \"vendor-encapsulated-options\" }, "
 1214         "    {   \"name\": \"vendor-class-identifier\", "
 1215         "        \"data\": \"alpha\" }, "
 1216         "    {   \"name\": \"foo\", "
 1217         "        \"space\": \"alpha\", "
 1218         "        \"data\": \"12345678\" } ] },"
 1219         "{   \"name\": \"beta\", "
 1220         "    \"test\": \"option[vendor-class-identifier].text == 'beta'\", "
 1221         "    \"option-def\": [ "
 1222         "    {   \"code\": 43, "
 1223         "        \"name\": \"vendor-encapsulated-options\", "
 1224         "        \"type\": \"empty\", "
 1225         "        \"encapsulate\": \"beta\" } ],"
 1226         "    \"option-data\": [ "
 1227         "{   \"name\": \"vendor-encapsulated-options\" }, "
 1228         "    {   \"name\": \"vendor-class-identifier\", "
 1229         "        \"data\": \"beta\" }, "
 1230         "    {   \"name\": \"bar\", "
 1231         "        \"space\": \"beta\", "
 1232         "        \"data\": \"33\" } ] } ] }";
 1233 
 1234     ConstElementPtr json;
 1235     ASSERT_NO_THROW(json = parseDHCP4(config));
 1236     ConstElementPtr status;
 1237 
 1238     // Configure the server and make sure the config is accepted
 1239     EXPECT_NO_THROW(status = configureDhcp4Server(srv, json));
 1240     ASSERT_TRUE(status);
 1241     comment_ = parseAnswer(rcode_, status);
 1242     ASSERT_EQ(0, rcode_);
 1243 
 1244     CfgMgr::instance().commit();
 1245 
 1246     // Create a packet with enough to select the subnet and go through
 1247     // the DISCOVER processing
 1248     Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 1234));
 1249     query->setRemoteAddr(IOAddress("192.0.2.1"));
 1250     OptionPtr clientid = generateClientId();
 1251     query->addOption(clientid);
 1252     query->setIface("eth1");
 1253 
 1254     // Create and add a vendor-encapsulated-options (code 43)
 1255     OptionBuffer buf;
 1256     buf.push_back(0x01);
 1257     buf.push_back(0x04);
 1258     buf.push_back(0x87);
 1259     buf.push_back(0x65);
 1260     buf.push_back(0x43);
 1261     buf.push_back(0x21);
 1262     OptionPtr vopt(new Option(Option::V4, DHO_VENDOR_ENCAPSULATED_OPTIONS, buf));
 1263     query->addOption(vopt);
 1264     query->getDeferredOptions().push_back(DHO_VENDOR_ENCAPSULATED_OPTIONS);
 1265 
 1266     // Create and add a vendor-class-identifier (code 60)
 1267     OptionStringPtr iopt(new OptionString(Option::V4,
 1268                                           DHO_VENDOR_CLASS_IDENTIFIER,
 1269                                           "alpha"));
 1270     query->addOption(iopt);
 1271 
 1272     // Create and add a PRL option to the query
 1273     OptionUint8ArrayPtr prl(new OptionUint8Array(Option::V4,
 1274                                                  DHO_DHCP_PARAMETER_REQUEST_LIST));
 1275     ASSERT_TRUE(prl);
 1276     prl->addValue(DHO_VENDOR_ENCAPSULATED_OPTIONS);
 1277     prl->addValue(DHO_VENDOR_CLASS_IDENTIFIER);
 1278     query->addOption(prl);
 1279 
 1280     srv.classifyPacket(query);
 1281     ASSERT_NO_THROW(srv.deferredUnpack(query));
 1282 
 1283     // Check if the option was (correctly) re-unpacked
 1284     vopt = query->getOption(DHO_VENDOR_ENCAPSULATED_OPTIONS);
 1285     OptionCustomPtr custom = boost::dynamic_pointer_cast<OptionCustom>(vopt);
 1286     EXPECT_TRUE(custom);
 1287     EXPECT_EQ(1, vopt->getOptions().size());
 1288 
 1289     // Pass it to the server and get an offer
 1290     Pkt4Ptr offer = srv.processDiscover(query);
 1291 
 1292     // Check if we get response at all
 1293     checkResponse(offer, DHCPOFFER, 1234);
 1294 
 1295     // Processing should add a vendor-class-identifier (code 60)
 1296     OptionPtr opt = offer->getOption(DHO_VENDOR_CLASS_IDENTIFIER);
 1297     EXPECT_TRUE(opt);
 1298     OptionStringPtr id = boost::dynamic_pointer_cast<OptionString>(opt);
 1299     ASSERT_TRUE(id);
 1300     EXPECT_EQ("alpha", id->getValue());
 1301 
 1302     // And a vendor-encapsulated-options (code 43)
 1303     opt = offer->getOption(DHO_VENDOR_ENCAPSULATED_OPTIONS);
 1304     ASSERT_TRUE(opt);
 1305     // Verifies the content
 1306     const OptionCollection& opts = opt->getOptions();
 1307     ASSERT_EQ(1, opts.size());
 1308     OptionPtr sopt = opts.begin()->second;
 1309     ASSERT_TRUE(sopt);
 1310     EXPECT_EQ(1, sopt->getType());
 1311     EXPECT_EQ(2 + 4, sopt->len());
 1312     OptionUint32Ptr sopt32 = boost::dynamic_pointer_cast<OptionUint32>(sopt);
 1313     ASSERT_TRUE(sopt32);
 1314     EXPECT_EQ(12345678, sopt32->getValue());
 1315 }
 1316 
 1317 // Checks effect of raw not compatible option 43 sent by a client (failure)
 1318 TEST_F(VendorOptsTest, clientOption43FailRaw) {
 1319     IfaceMgrTestConfig test_config(true);
 1320     IfaceMgr::instance().openSockets4();
 1321     Dhcp4Client client;
 1322 
 1323     // The vendor-encapsulated-options has an incompatible data
 1324     // so won't have the expected content. Here the processing
 1325     // of suboptions tries to unpack the uint32 foo suboption and
 1326     // raises an exception which is caught.
 1327     string config = "{ \"interfaces-config\": {"
 1328         "    \"interfaces\": [ \"*\" ] }, "
 1329         "\"subnet4\": [ "
 1330         "{   \"pools\": [ { \"pool\": \"10.0.0.10 - 10.0.0.100\" } ], "
 1331         "    \"subnet\": \"10.0.0.0/24\" } ],"
 1332         "\"option-def\": [ "
 1333         "{   \"code\": 1, "
 1334         "    \"name\": \"foo\", "
 1335         "    \"space\":  \"vendor-encapsulated-options-space\", "
 1336         "    \"type\": \"uint32\" } ] }";
 1337 
 1338     EXPECT_NO_THROW(configure(config, *client.getServer()));
 1339 
 1340     // Create and add a vendor-encapsulated-options (code 43)
 1341     // with not compatible (not parsable as suboptions) content
 1342     // which will raise an exception
 1343     OptionBuffer buf;
 1344     buf.push_back(0x01);
 1345     buf.push_back(0x01);
 1346     buf.push_back(0x01);
 1347     OptionPtr vopt(new Option(Option::V4, DHO_VENDOR_ENCAPSULATED_OPTIONS, buf));
 1348     client.addExtraOption(vopt);
 1349 
 1350     // Let's check whether the server is not able to process this packet
 1351     // and raises an exception which is caught so the response is not empty.
 1352     EXPECT_NO_THROW(client.doDiscover());
 1353     EXPECT_TRUE(client.getContext().response_);
 1354 }
 1355 
 1356 // Verifies raw option 43 sent by a client can be handled (global)
 1357 TEST_F(VendorOptsTest, clientOption43RawGlobal) {
 1358     IfaceMgrTestConfig test_config(true);
 1359     IfaceMgr::instance().openSockets4();
 1360     Dhcp4Client client;
 1361 
 1362     // The vendor-encapsulated-options is redefined as raw binary
 1363     // in a global definition.
 1364     string config = "{ \"interfaces-config\": {"
 1365         "    \"interfaces\": [ \"*\" ] }, "
 1366         "\"subnet4\": [ "
 1367         "{   \"pools\": [ { \"pool\": \"10.0.0.10 - 10.0.0.100\" } ], "
 1368         "    \"subnet\": \"10.0.0.0/24\" } ],"
 1369         "\"option-def\": [ "
 1370         "{   \"code\": 1, "
 1371         "    \"name\": \"foo\", "
 1372         "    \"space\":  \"vendor-encapsulated-options-space\", "
 1373         "    \"type\": \"uint32\" },"
 1374         "{   \"code\": 43, "
 1375         "    \"name\": \"vendor-encapsulated-options\", "
 1376         "    \"type\": \"binary\" } ],"
 1377         "\"option-data\": [ "
 1378         "{   \"name\": \"vendor-class-identifier\", "
 1379         "    \"data\": \"bar\" }, "
 1380         "{   \"name\": \"vendor-encapsulated-options\", "
 1381         "    \"csv-format\": false, "
 1382         "    \"data\": \"0102\" } ] }";
 1383 
 1384     EXPECT_NO_THROW(configure(config, *client.getServer()));
 1385 
 1386     // Create and add a vendor-encapsulated-options (code 43)
 1387     // with not compatible (not parsable as suboptions) content
 1388     OptionBuffer buf;
 1389     buf.push_back(0x01);
 1390     buf.push_back(0x01);
 1391     buf.push_back(0x01);
 1392     OptionPtr vopt(new Option(Option::V4, DHO_VENDOR_ENCAPSULATED_OPTIONS, buf));
 1393     client.addExtraOption(vopt);
 1394 
 1395     // Let's check whether the server is able to process this packet without
 1396     // throwing any exceptions so the response is not empty.
 1397     EXPECT_NO_THROW(client.doDiscover());
 1398     EXPECT_TRUE(client.getContext().response_);
 1399 }
 1400 
 1401 // Verifies raw option 43 sent by a client can be handled (catch-all class)
 1402 TEST_F(VendorOptsTest, clientOption43RawClass) {
 1403     IfaceMgrTestConfig test_config(true);
 1404     IfaceMgr::instance().openSockets4();
 1405     Dhcp4Client client;
 1406 
 1407     // The vendor-encapsulated-options is redefined as raw binary
 1408     // in a class definition.
 1409     string config = "{ \"interfaces-config\": {"
 1410         "    \"interfaces\": [ \"*\" ] }, "
 1411         "\"subnet4\": [ "
 1412         "{   \"pools\": [ { \"pool\": \"10.0.0.10 - 10.0.0.100\" } ], "
 1413         "    \"subnet\": \"10.0.0.0/24\" } ],"
 1414         "\"option-def\": [ "
 1415         "{   \"code\": 1, "
 1416         "    \"name\": \"foo\", "
 1417         "    \"space\":  \"vendor-encapsulated-options-space\", "
 1418         "    \"type\": \"uint32\" } ],"
 1419         "\"client-classes\": [ "
 1420         "{   \"name\": \"vendor\", "
 1421         "    \"test\": \"option[vendor-encapsulated-options].exists\", "
 1422         "    \"option-def\": [ "
 1423         "    {   \"code\": 43, "
 1424         "        \"name\": \"vendor-encapsulated-options\", "
 1425         "        \"type\": \"binary\" } ],"
 1426         "    \"option-data\": [ "
 1427         "    {   \"name\": \"vendor-class-identifier\", "
 1428         "        \"data\": \"bar\" }, "
 1429         "    {   \"name\": \"vendor-encapsulated-options\", "
 1430         "        \"csv-format\": false, "
 1431         "        \"data\": \"0102\" } ] } ] }";
 1432 
 1433     EXPECT_NO_THROW(configure(config, *client.getServer()));
 1434 
 1435     // Create and add a vendor-encapsulated-options (code 43)
 1436     // with not compatible (not parsable as suboptions) content
 1437     OptionBuffer buf;
 1438     buf.push_back(0x01);
 1439     buf.push_back(0x01);
 1440     buf.push_back(0x01);
 1441     OptionPtr vopt(new Option(Option::V4, DHO_VENDOR_ENCAPSULATED_OPTIONS, buf));
 1442     client.addExtraOption(vopt);
 1443 
 1444     // Let's check whether the server is able to process this packet without
 1445     // throwing any exceptions so the response is not empty.
 1446     EXPECT_NO_THROW(client.doDiscover());
 1447     EXPECT_TRUE(client.getContext().response_);
 1448 }
 1449 
 1450 // Verifies that an a client query with a truncated length in
 1451 // vendor option (125) will still be processed by the server.
 1452 TEST_F(Dhcpv4SrvTest, truncatedVIVSOOption) {
 1453     IfaceMgrTestConfig test_config(true);
 1454     IfaceMgr::instance().openSockets4();
 1455 
 1456     NakedDhcpv4Srv srv(0);
 1457 
 1458     string config = "{ \"interfaces-config\": {"
 1459         "    \"interfaces\": [ \"*\" ]"
 1460         "},"
 1461         "\"subnet4\": [ { "
 1462         "    \"pools\": [ { \"pool\": \"10.206.80.0/25\" } ],"
 1463         "    \"subnet\": \"10.206.80.0/24\", "
 1464         "    \"rebind-timer\": 2000, "
 1465         "    \"renew-timer\": 1000, "
 1466         "    \"valid-lifetime\": 4000,"
 1467         "    \"interface\": \"eth0\" "
 1468         " } ]"
 1469         "}";
 1470 
 1471     ConstElementPtr json;
 1472     ASSERT_NO_THROW(json = parseDHCP4(config));
 1473     ConstElementPtr status;
 1474 
 1475     // Configure the server and make sure the config is accepted
 1476     EXPECT_NO_THROW(status = configureDhcp4Server(srv, json));
 1477     ASSERT_TRUE(status);
 1478     comment_ = parseAnswer(rcode_, status);
 1479     ASSERT_EQ(0, rcode_);
 1480 
 1481     CfgMgr::instance().commit();
 1482 
 1483     // Create a DISCOVER with a VIVSO option whose length is
 1484     // too short.
 1485     Pkt4Ptr dis;
 1486     ASSERT_NO_THROW(dis = PktCaptures::discoverWithTruncatedVIVSO());
 1487 
 1488     // Simulate that we have received that traffic
 1489     srv.fakeReceive(dis);
 1490 
 1491     // Server will now process to run its normal loop, but instead of calling
 1492     // IfaceMgr::receive4(), it will read all packets from the list set by
 1493     // fakeReceive()
 1494     // In particular, it should call registered buffer4_receive callback.
 1495     srv.run();
 1496 
 1497     // Check that the server did send a response
 1498     ASSERT_EQ(1, srv.fake_sent_.size());
 1499 
 1500     // Make sure that we received an response and it was an offer
 1501     Pkt4Ptr offer = srv.fake_sent_.front();
 1502     ASSERT_TRUE(offer);
 1503 }
 1504 
 1505 /// Checks that it's possible to define and use a suboption 0.
 1506 TEST_F(VendorOptsTest, vendorOpsSubOption0) {
 1507     IfaceMgrTestConfig test_config(true);
 1508     IfaceMgr::instance().openSockets4();
 1509 
 1510     NakedDhcpv4Srv srv(0);
 1511 
 1512     // Zero Touch provisioning
 1513     string config =
 1514         "{"
 1515         "    \"interfaces-config\": {"
 1516         "        \"interfaces\": [ \"*\" ]"
 1517         "    },"
 1518         "    \"option-def\": ["
 1519         "        {"
 1520         "            \"name\": \"vendor-encapsulated-options\","
 1521         "            \"code\": 43,"
 1522         "            \"type\": \"empty\","
 1523         "            \"encapsulate\": \"ZTP\""
 1524         "        },"
 1525         "        {"
 1526         "            \"name\": \"config-file-name\","
 1527         "            \"code\": 1,"
 1528         "            \"space\": \"ZTP\","
 1529         "            \"type\": \"string\""
 1530         "        },"
 1531         "        {"
 1532         "            \"name\": \"image-file-name\","
 1533         "            \"code\": 0,"
 1534         "            \"space\": \"ZTP\","
 1535         "            \"type\": \"string\""
 1536         "        },"
 1537         "        {"
 1538         "            \"name\": \"image-file-type\","
 1539         "            \"code\": 2,"
 1540         "            \"space\": \"ZTP\","
 1541         "            \"type\": \"string\""
 1542         "        },"
 1543         "        {"
 1544         "            \"name\": \"transfer-mode\","
 1545         "            \"code\": 3,"
 1546         "            \"space\": \"ZTP\","
 1547         "            \"type\": \"string\""
 1548         "        },"
 1549         "        {"
 1550         "            \"name\": \"all-image-file-name\","
 1551         "            \"code\": 4,"
 1552         "            \"space\": \"ZTP\","
 1553         "            \"type\": \"string\""
 1554         "        },"
 1555         "        {"
 1556         "            \"name\": \"http-port\","
 1557         "            \"code\": 5,"
 1558         "            \"space\": \"ZTP\","
 1559         "            \"type\": \"string\""
 1560         "        }"
 1561         "    ],"
 1562         "    \"option-data\": ["
 1563         "        {"
 1564         "            \"name\": \"vendor-encapsulated-options\""
 1565         "        },"
 1566         "        {"
 1567         "            \"name\": \"image-file-name\","
 1568         "            \"data\": \"/dist/images/jinstall-ex.tgz\","
 1569         "            \"space\": \"ZTP\""
 1570         "        }"
 1571         "    ],"
 1572         "\"subnet4\": [ { "
 1573         "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
 1574         "    \"subnet\": \"192.0.2.0/24\""
 1575         " } ]"
 1576         "}";
 1577 
 1578     ConstElementPtr json;
 1579     ASSERT_NO_THROW(json = parseDHCP4(config, true));
 1580     ConstElementPtr status;
 1581 
 1582     // Configure the server and make sure the config is accepted
 1583     EXPECT_NO_THROW(status = configureDhcp4Server(srv, json));
 1584     ASSERT_TRUE(status);
 1585     comment_ = parseAnswer(rcode_, status);
 1586     ASSERT_EQ(0, rcode_);
 1587 
 1588     CfgMgr::instance().commit();
 1589 
 1590         // Create a packet with enough to select the subnet and go through
 1591     // the DISCOVER processing
 1592     Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 1234));
 1593     query->setRemoteAddr(IOAddress("192.0.2.1"));
 1594     OptionPtr clientid = generateClientId();
 1595     query->addOption(clientid);
 1596     query->setIface("eth1");
 1597 
 1598     // Create and add a PRL option to the query
 1599     OptionUint8ArrayPtr prl(new OptionUint8Array(Option::V4,
 1600                                                  DHO_DHCP_PARAMETER_REQUEST_LIST));
 1601     ASSERT_TRUE(prl);
 1602     prl->addValue(DHO_VENDOR_ENCAPSULATED_OPTIONS);
 1603     prl->addValue(DHO_VENDOR_CLASS_IDENTIFIER);
 1604     query->addOption(prl);
 1605 
 1606     srv.classifyPacket(query);
 1607     ASSERT_NO_THROW(srv.deferredUnpack(query));
 1608 
 1609     // Pass it to the server and get an offer
 1610     Pkt4Ptr offer = srv.processDiscover(query);
 1611 
 1612     // Check if we get response at all
 1613     checkResponse(offer, DHCPOFFER, 1234);
 1614 
 1615     // Processing should add a vendor-encapsulated-options (code 43)
 1616     OptionPtr opt = offer->getOption(DHO_VENDOR_ENCAPSULATED_OPTIONS);
 1617     ASSERT_TRUE(opt);
 1618     const OptionCollection& opts = opt->getOptions();
 1619     ASSERT_EQ(1, opts.size());
 1620     OptionPtr sopt = opts.begin()->second;
 1621     ASSERT_TRUE(sopt);
 1622     EXPECT_EQ(0, sopt->getType());
 1623 
 1624     // Check suboption 0 content.
 1625     OptionStringPtr sopt0 =
 1626         boost::dynamic_pointer_cast<OptionString>(sopt);
 1627     ASSERT_TRUE(sopt0);
 1628     EXPECT_EQ("/dist/images/jinstall-ex.tgz", sopt0->getValue());
 1629 }