Skip to content

Commit a12da4f

Browse files
author
Razvan Becheriu
committed
[#3861] allow containers in vendor options
1 parent 6a10ce2 commit a12da4f

File tree

2 files changed

+144
-0
lines changed

2 files changed

+144
-0
lines changed

src/bin/dhcp4/tests/vendor_opts_unittest.cc

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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
16101745
TEST_F(VendorOptsTest, option43LastResort) {
16111746
// If there is no definition for option 43 a last resort

src/lib/dhcpsrv/cfg_option.cc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,15 @@ CfgOption::encapsulateInternal(const std::string& option_space) {
302302
for (auto const& opt : *options) {
303303
encapsulateInternal(opt.option_);
304304
}
305+
306+
for (auto const& vendor : getVendorIds()) {
307+
OptionContainerPtr vendor_options = getAll(vendor);
308+
// For each option in the option space we will append sub-options
309+
// from the option spaces they encapsulate.
310+
for (auto const& opt : *vendor_options) {
311+
encapsulateInternal(opt.option_);
312+
}
313+
}
305314
}
306315

307316
void

0 commit comments

Comments
 (0)