2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-22 09:57:41 +00:00

[#3861] allow containers in vendor options

This commit is contained in:
Razvan Becheriu 2025-08-04 21:06:21 +03:00
parent 6a10ce215b
commit a12da4f9fd
2 changed files with 144 additions and 0 deletions

View File

@ -1606,6 +1606,141 @@ TEST_F(VendorOptsTest, vivsoInResponseOnly) {
EXPECT_FALSE(cdesc.option_->getOption(2));
}
// Checks that it's possible to have a vivso (125) option in the response
// only. Once specific client (Genexis) sends only vendor-class info and
// expects the server to include vivso in the response. This test adds
// an empty container option (code 193) with 2 suboptions
// (code 16 - ipv4-address and code 20 - uint16).
TEST_F(VendorOptsTest, vivsoInResponseOnlyComplex) {
Dhcp4Client client(srv_);
// The config defines custom vendor 125 suboption 193 that conveys 2 suboptions:
// suboption 16 (ipv4-address) and suboption 20 (uint16).
// The client doesn't send vendor 125 option, so normal vendor option
// processing is impossible. However, since there's a class defined that
// matches client's packets and that class inserts vivso in the response,
// Kea should be able to figure out the vendor-id and then also insert
// suboption 193 with 2 suboptions: 16 and 20.
string config =
"{"
" \"interfaces-config\": {"
" \"interfaces\": [ \"*\" ]"
" },"
" \"option-def\": ["
" {"
" \"name\": \"pma-addr\","
" \"code\": 16,"
" \"type\": \"ipv4-address\","
" \"array\": false,"
" \"space\": \"vendor-25167-193\""
" },"
" {"
" \"name\": \"pma-port\","
" \"code\": 20,"
" \"type\": \"uint16\","
" \"array\": false,"
" \"space\": \"vendor-25167-193\""
" },"
" {"
" \"name\": \"pma\","
" \"code\": 193,"
" \"space\": \"vendor-25167\","
" \"type\": \"empty\","
" \"array\": false,"
" \"encapsulate\": \"vendor-25167-193\","
" \"record-types\": \"\""
" }"
" ],"
" \"client-classes\": ["
" {"
" \"name\": \"cpe_genexis\","
" \"test\": \"substring(option[60].hex,0,7) == 'HMC1000'\","
" \"option-data\": ["
" {"
" \"name\": \"vivso-suboptions\","
" \"data\": \"25167\""
" },"
" {"
" \"name\": \"pma-addr\","
" \"space\": \"vendor-25167-193\","
" \"code\": 16,"
" \"data\": \"192.0.3.1\","
" \"always-send\": true"
" },"
" {"
" \"name\": \"pma-port\","
" \"space\": \"vendor-25167-193\","
" \"code\": 20,"
" \"data\": \"300\","
" \"always-send\": true"
" },"
" {"
" \"name\": \"pma\","
" \"space\": \"vendor-25167\","
" \"code\": 193,"
" \"always-send\": true"
" } ]"
" } ],"
"\"subnet4\": [ { "
" \"id\": 10,"
" \"pools\": [ { \"pool\": \"192.0.2.0/25\" } ],"
" \"subnet\": \"192.0.2.0/24\", "
" \"interface\": \"eth0\" "
" } ]"
"}";
EXPECT_NO_THROW(configure(config, *client.getServer()));
// Add a vendor-class identifier (this matches what Genexis hardware sends)
OptionPtr vopt(new OptionString(Option::V4, DHO_VENDOR_CLASS_IDENTIFIER,
"HMC1000.v1.3.0-R,Element-P1090,genexis.eu"));
client.addExtraOption(vopt);
client.requestOptions(DHO_VIVSO_SUBOPTIONS);
// Let's check whether the server is not able to process this packet
// and include vivso with appropriate sub-options
EXPECT_NO_THROW(client.doDiscover());
ASSERT_TRUE(client.getContext().response_);
// Check there's a response.
OptionPtr rsp = client.getContext().response_->getOption(DHO_VIVSO_SUBOPTIONS);
ASSERT_TRUE(rsp);
// Check that it includes vivso with vendor-id = 25167
OptionVendorPtr rsp_vivso = boost::dynamic_pointer_cast<OptionVendor>(rsp);
ASSERT_TRUE(rsp_vivso);
EXPECT_EQ(rsp_vivso->getVendorId(), 25167);
// Now check that it contains suboption 193 with appropriate content.
OptionPtr subopt193 = rsp_vivso->getOption(193);
ASSERT_TRUE(subopt193);
OptionPtr expected(new Option(Option::V4, 193));
Option4AddrLstPtr opt16(new Option4AddrLst(16, IOAddress("192.0.3.1")));
boost::shared_ptr<OptionIntArray<uint16_t>> opt20(new OptionIntArray<uint16_t>(Option::V4, 20));
opt20->addValue(300);
expected->addOption(opt16);
expected->addOption(opt20);
vector<uint8_t> subopt193bin = subopt193->toBinary(false);
vector<uint8_t> expectedbin = expected->toBinary(false);
ASSERT_EQ(expectedbin, subopt193bin);
// Check the config was not altered by unwanted side effect
// on the vendor option.
// Get class config:
ClientClassDefPtr cdef = CfgMgr::instance().getCurrentCfg()->
getClientClassDictionary()->findClass("cpe_genexis");
ASSERT_TRUE(cdef);
OptionDescriptor cdesc = cdef->getCfgOption()->
get(DHCP4_OPTION_SPACE, DHO_VIVSO_SUBOPTIONS);
ASSERT_TRUE(cdesc.option_);
// If the config was altered these two EXPECT will fail.
EXPECT_TRUE(cdesc.option_->getOptions().empty());
EXPECT_FALSE(cdesc.option_->getOption(2));
}
// Verifies last resort option 43 is backward compatible
TEST_F(VendorOptsTest, option43LastResort) {
// If there is no definition for option 43 a last resort

View File

@ -302,6 +302,15 @@ CfgOption::encapsulateInternal(const std::string& option_space) {
for (auto const& opt : *options) {
encapsulateInternal(opt.option_);
}
for (auto const& vendor : getVendorIds()) {
OptionContainerPtr vendor_options = getAll(vendor);
// For each option in the option space we will append sub-options
// from the option spaces they encapsulate.
for (auto const& opt : *vendor_options) {
encapsulateInternal(opt.option_);
}
}
}
void