@@ -1606,6 +1606,141 @@ TEST_F(VendorOptsTest, vivsoInResponseOnly) {
16061606 EXPECT_FALSE (cdesc.option_ ->getOption (2 ));
16071607}
16081608
1609+ // Checks that it's possible to have a vivso (125) option in the response
1610+ // only. Once specific client (Genexis) sends only vendor-class info and
1611+ // expects the server to include vivso in the response. This test adds
1612+ // an empty container option (code 193) with 2 suboptions
1613+ // (code 16 - ipv4-address and code 20 - uint16).
1614+ TEST_F (VendorOptsTest, vivsoInResponseOnlyComplex) {
1615+ Dhcp4Client client (srv_);
1616+
1617+ // The config defines custom vendor 125 suboption 193 that conveys 2 suboptions:
1618+ // suboption 16 (ipv4-address) and suboption 20 (uint16).
1619+ // The client doesn't send vendor 125 option, so normal vendor option
1620+ // processing is impossible. However, since there's a class defined that
1621+ // matches client's packets and that class inserts vivso in the response,
1622+ // Kea should be able to figure out the vendor-id and then also insert
1623+ // suboption 193 with 2 suboptions: 16 and 20.
1624+ string config =
1625+ " {"
1626+ " \" interfaces-config\" : {"
1627+ " \" interfaces\" : [ \" *\" ]"
1628+ " },"
1629+ " \" option-def\" : ["
1630+ " {"
1631+ " \" name\" : \" pma-addr\" ,"
1632+ " \" code\" : 16,"
1633+ " \" type\" : \" ipv4-address\" ,"
1634+ " \" array\" : false,"
1635+ " \" space\" : \" vendor-25167-193\" "
1636+ " },"
1637+ " {"
1638+ " \" name\" : \" pma-port\" ,"
1639+ " \" code\" : 20,"
1640+ " \" type\" : \" uint16\" ,"
1641+ " \" array\" : false,"
1642+ " \" space\" : \" vendor-25167-193\" "
1643+ " },"
1644+ " {"
1645+ " \" name\" : \" pma\" ,"
1646+ " \" code\" : 193,"
1647+ " \" space\" : \" vendor-25167\" ,"
1648+ " \" type\" : \" empty\" ,"
1649+ " \" array\" : false,"
1650+ " \" encapsulate\" : \" vendor-25167-193\" ,"
1651+ " \" record-types\" : \"\" "
1652+ " }"
1653+ " ],"
1654+ " \" client-classes\" : ["
1655+ " {"
1656+ " \" name\" : \" cpe_genexis\" ,"
1657+ " \" test\" : \" substring(option[60].hex,0,7) == 'HMC1000'\" ,"
1658+ " \" option-data\" : ["
1659+ " {"
1660+ " \" name\" : \" vivso-suboptions\" ,"
1661+ " \" data\" : \" 25167\" "
1662+ " },"
1663+ " {"
1664+ " \" name\" : \" pma-addr\" ,"
1665+ " \" space\" : \" vendor-25167-193\" ,"
1666+ " \" code\" : 16,"
1667+ " \" data\" : \" 192.0.3.1\" ,"
1668+ " \" always-send\" : true"
1669+ " },"
1670+ " {"
1671+ " \" name\" : \" pma-port\" ,"
1672+ " \" space\" : \" vendor-25167-193\" ,"
1673+ " \" code\" : 20,"
1674+ " \" data\" : \" 300\" ,"
1675+ " \" always-send\" : true"
1676+ " },"
1677+ " {"
1678+ " \" name\" : \" pma\" ,"
1679+ " \" space\" : \" vendor-25167\" ,"
1680+ " \" code\" : 193,"
1681+ " \" always-send\" : true"
1682+ " } ]"
1683+ " } ],"
1684+ " \" subnet4\" : [ { "
1685+ " \" id\" : 10,"
1686+ " \" pools\" : [ { \" pool\" : \" 192.0.2.0/25\" } ],"
1687+ " \" subnet\" : \" 192.0.2.0/24\" , "
1688+ " \" interface\" : \" eth0\" "
1689+ " } ]"
1690+ " }" ;
1691+
1692+ EXPECT_NO_THROW (configure (config, *client.getServer ()));
1693+
1694+ // Add a vendor-class identifier (this matches what Genexis hardware sends)
1695+ OptionPtr vopt (new OptionString (Option::V4, DHO_VENDOR_CLASS_IDENTIFIER,
1696+ " HMC1000.v1.3.0-R,Element-P1090,genexis.eu" ));
1697+ client.addExtraOption (vopt);
1698+ client.requestOptions (DHO_VIVSO_SUBOPTIONS);
1699+
1700+ // Let's check whether the server is not able to process this packet
1701+ // and include vivso with appropriate sub-options
1702+ EXPECT_NO_THROW (client.doDiscover ());
1703+ ASSERT_TRUE (client.getContext ().response_ );
1704+
1705+ // Check there's a response.
1706+ OptionPtr rsp = client.getContext ().response_ ->getOption (DHO_VIVSO_SUBOPTIONS);
1707+ ASSERT_TRUE (rsp);
1708+
1709+ // Check that it includes vivso with vendor-id = 25167
1710+ OptionVendorPtr rsp_vivso = boost::dynamic_pointer_cast<OptionVendor>(rsp);
1711+ ASSERT_TRUE (rsp_vivso);
1712+ EXPECT_EQ (rsp_vivso->getVendorId (), 25167 );
1713+
1714+ // Now check that it contains suboption 193 with appropriate content.
1715+ OptionPtr subopt193 = rsp_vivso->getOption (193 );
1716+ ASSERT_TRUE (subopt193);
1717+
1718+ OptionPtr expected (new Option (Option::V4, 193 ));
1719+ Option4AddrLstPtr opt16 (new Option4AddrLst (16 , IOAddress (" 192.0.3.1" )));
1720+ boost::shared_ptr<OptionIntArray<uint16_t >> opt20 (new OptionIntArray<uint16_t >(Option::V4, 20 ));
1721+ opt20->addValue (300 );
1722+ expected->addOption (opt16);
1723+ expected->addOption (opt20);
1724+
1725+ vector<uint8_t > subopt193bin = subopt193->toBinary (false );
1726+ vector<uint8_t > expectedbin = expected->toBinary (false );
1727+ ASSERT_EQ (expectedbin, subopt193bin);
1728+
1729+ // Check the config was not altered by unwanted side effect
1730+ // on the vendor option.
1731+
1732+ // Get class config:
1733+ ClientClassDefPtr cdef = CfgMgr::instance ().getCurrentCfg ()->
1734+ getClientClassDictionary ()->findClass (" cpe_genexis" );
1735+ ASSERT_TRUE (cdef);
1736+ OptionDescriptor cdesc = cdef->getCfgOption ()->
1737+ get (DHCP4_OPTION_SPACE, DHO_VIVSO_SUBOPTIONS);
1738+ ASSERT_TRUE (cdesc.option_ );
1739+ // If the config was altered these two EXPECT will fail.
1740+ EXPECT_TRUE (cdesc.option_ ->getOptions ().empty ());
1741+ EXPECT_FALSE (cdesc.option_ ->getOption (2 ));
1742+ }
1743+
16091744// Verifies last resort option 43 is backward compatible
16101745TEST_F (VendorOptsTest, option43LastResort) {
16111746 // If there is no definition for option 43 a last resort
0 commit comments