2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-31 05:55:28 +00:00

[#1894] negative test for cable labs vendor ID

This commit is contained in:
Andrei Pavel
2021-05-18 17:24:39 +03:00
parent 272b4f57d3
commit 2a92725a1a
2 changed files with 228 additions and 177 deletions

View File

@@ -48,7 +48,110 @@ using namespace isc::dhcp::test;
/// groups all vendor related tests under a single name. There were too many
/// tests in Dhcpv4SrvTest class anyway.
class VendorOptsTest : public Dhcpv4SrvTest {
public:
void testVendorOptionsORO(int vendor_id) {
IfaceMgrTestConfig test_config(true);
IfaceMgr::instance().openSockets4();
NakedDhcpv4Srv srv(0);
string config = R"(
{
"interfaces-config": {
"interfaces": [ "*" ]
},
"option-data": [
{
"code": 2,
"csv-format": true,
"data": "192.0.2.1, 192.0.2.2",
"name": "tftp-servers",
"space": "vendor-4491"
}
],
"subnet4": [
{
"interface": "eth0",
"pools": [
{
"pool": "192.0.2.0/25"
}
],
"subnet": "192.0.2.0/24"
}
]
}
)";
ConstElementPtr json;
ASSERT_NO_THROW(json = parseDHCP4(config));
ConstElementPtr x;
EXPECT_NO_THROW(x = configureDhcp4Server(srv, json));
ASSERT_TRUE(x);
comment_ = parseAnswer(rcode_, x);
ASSERT_EQ(0, rcode_);
CfgMgr::instance().commit();
boost::shared_ptr<Pkt4> dis(new Pkt4(DHCPDISCOVER, 1234));
// Set the giaddr and hops to non-zero address as if it was relayed.
dis->setGiaddr(IOAddress("192.0.2.1"));
dis->setHops(1);
OptionPtr clientid = generateClientId();
dis->addOption(clientid);
// Set interface. It is required by the server to generate server id.
dis->setIface("eth0");
dis->setIndex(ETH0_INDEX);
// Pass it to the server and get an advertise
Pkt4Ptr offer = srv.processDiscover(dis);
// check if we get response at all
ASSERT_TRUE(offer);
// We did not include any vendor opts in DISCOVER, so there should be none
// in OFFER.
ASSERT_FALSE(offer->getOption(DHO_VIVSO_SUBOPTIONS));
// 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<OptionUint8Array> vendor_oro(new OptionUint8Array(Option::V4,
DOCSIS3_V4_ORO));
vendor_oro->addValue(DOCSIS3_V4_TFTP_SERVERS); // Request option 33
OptionPtr vendor(new OptionVendor(Option::V4, vendor_id));
vendor->addOption(vendor_oro);
dis->addOption(vendor);
// Need to process SOLICIT again after requesting new option.
offer = srv.processDiscover(dis);
ASSERT_TRUE(offer);
// Check if there is (or not) a vendor option in the response.
OptionPtr tmp = offer->getOption(DHO_VIVSO_SUBOPTIONS);
if (vendor_id != VENDOR_ID_CABLE_LABS) {
EXPECT_FALSE(tmp);
return;
}
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 docsis2 = vendor_resp->getOption(DOCSIS3_V4_TFTP_SERVERS);
ASSERT_TRUE(docsis2);
Option4AddrLstPtr tftp_srvs = boost::dynamic_pointer_cast<Option4AddrLst>(docsis2);
ASSERT_TRUE(tftp_srvs);
Option4AddrLst::AddressContainer addrs = tftp_srvs->getAddresses();
ASSERT_EQ(2, addrs.size());
EXPECT_EQ("192.0.2.1", addrs[0].toText());
EXPECT_EQ("192.0.2.2", addrs[1].toText());
}
};
/// @todo Add more extensive vendor options tests, including multiple
@@ -184,92 +287,13 @@ TEST_F(VendorOptsTest, docsisVendorORO) {
// 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);
IfaceMgr::instance().openSockets4();
testVendorOptionsORO(VENDOR_ID_CABLE_LABS);
}
NakedDhcpv4Srv srv(0);
ConstElementPtr x;
string config = "{ \"interfaces-config\": {"
" \"interfaces\": [ \"*\" ]"
"},"
" \"option-data\": [ {"
" \"name\": \"tftp-servers\","
" \"space\": \"vendor-4491\","
" \"code\": 2,"
" \"data\": \"192.0.2.1, 192.0.2.2\","
" \"csv-format\": true"
" }],"
"\"subnet4\": [ { "
" \"pools\": [ { \"pool\": \"192.0.2.0/25\" } ],"
" \"subnet\": \"192.0.2.0/24\", "
" \"interface\": \"eth0\" "
" } ]"
"}";
ConstElementPtr json;
ASSERT_NO_THROW(json = parseDHCP4(config));
EXPECT_NO_THROW(x = configureDhcp4Server(srv, json));
ASSERT_TRUE(x);
comment_ = parseAnswer(rcode_, x);
ASSERT_EQ(0, rcode_);
CfgMgr::instance().commit();
boost::shared_ptr<Pkt4> dis(new Pkt4(DHCPDISCOVER, 1234));
// Set the giaddr and hops to non-zero address as if it was relayed.
dis->setGiaddr(IOAddress("192.0.2.1"));
dis->setHops(1);
OptionPtr clientid = generateClientId();
dis->addOption(clientid);
// Set interface. It is required by the server to generate server id.
dis->setIface("eth0");
dis->setIndex(ETH0_INDEX);
// Pass it to the server and get an advertise
Pkt4Ptr offer = srv.processDiscover(dis);
// check if we get response at all
ASSERT_TRUE(offer);
// We did not include any vendor opts in DISCOVER, so there should be none
// in OFFER.
ASSERT_FALSE(offer->getOption(DHO_VIVSO_SUBOPTIONS));
// 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<OptionUint8Array> vendor_oro(new OptionUint8Array(Option::V4,
DOCSIS3_V4_ORO));
vendor_oro->addValue(DOCSIS3_V4_TFTP_SERVERS); // Request option 33
OptionPtr vendor(new OptionVendor(Option::V4, 4491));
vendor->addOption(vendor_oro);
dis->addOption(vendor);
// Need to process SOLICIT again after requesting new option.
offer = srv.processDiscover(dis);
ASSERT_TRUE(offer);
// Check if there is a vendor option response
OptionPtr tmp = offer->getOption(DHO_VIVSO_SUBOPTIONS);
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 docsis2 = vendor_resp->getOption(DOCSIS3_V4_TFTP_SERVERS);
ASSERT_TRUE(docsis2);
Option4AddrLstPtr tftp_srvs = boost::dynamic_pointer_cast<Option4AddrLst>(docsis2);
ASSERT_TRUE(tftp_srvs);
Option4AddrLst::AddressContainer addrs = tftp_srvs->getAddresses();
ASSERT_EQ(2, addrs.size());
EXPECT_EQ("192.0.2.1", addrs[0].toText());
EXPECT_EQ("192.0.2.2", addrs[1].toText());
// Same as vendorOptionsORO except a different vendor ID than Cable Labs is
// provided and vendor options are expected to not be present in the response.
TEST_F(VendorOptsTest, vendorOptionsORODifferentVendorID) {
testVendorOptionsORO(32768);
}
// This test checks if Option Request Option (ORO) in docsis (vendor-id=4491)

View File

@@ -43,6 +43,113 @@ using namespace isc::asiolink;
/// @brief Class dedicated to testing vendor options in DHCPv6
class VendorOptsTest : public Dhcpv6SrvTest {
public:
void testVendorOptionsORO(int vendor_id) {
IfaceMgrTestConfig test_config(true);
string config = R"(
{
"interfaces-config": {
"interfaces": [ "*" ]
},
"option-data": [
{
"data": "normal_erouter_v6.cm",
"name": "config-file",
"space": "vendor-4491"
}
],
"option-def": [
{
"code": 33,
"name": "config-file",
"space": "vendor-4491",
"type": "string"
}
],
"preferred-lifetime": 3000,
"rebind-timer": 2000,
"renew-timer": 1000,
"subnet6": [
{
"interface": "eth0",
"interface-id": "",
"pools": [
{
"pool": "2001:db8:1::/64"
}
],
"preferred-lifetime": 3000,
"rebind-timer": 1000,
"renew-timer": 1000,
"subnet": "2001:db8:1::/48",
"valid-lifetime": 4000
}
],
"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->setIndex(ETH0_INDEX);
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, vendor_id));
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 (or not) a vendor option in the response.
OptionPtr tmp = adv->getOption(D6O_VENDOR_OPTS);
if (vendor_id != VENDOR_ID_CABLE_LABS) {
EXPECT_FALSE(tmp);
return;
}
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());
}
/// @brief Test what options a client can use to request vendor options.
void testRequestingOfVendorOptions(vector<int8_t> const& client_options) {
IfaceMgrTestConfig test_config(true);
@@ -76,12 +183,12 @@ public:
client.addExtraOption(vendor_option);
}
// Let's check whether the server is not able to process this packet
// and include vivso with appropriate sub-options
// Let's check whether the server is able to process this packet
// and include the appropriate options.
EXPECT_NO_THROW(client.doSolicit());
ASSERT_TRUE(client.getContext().response_);
// Check there's a response if an option was properly requested.
// Check that there is a response if an option was properly requested.
// Otherwise check that a response has not been provided and stop here.
OptionPtr response(
client.getContext().response_->getOption(D6O_VENDOR_OPTS));
@@ -98,9 +205,9 @@ public:
ASSERT_TRUE(response_vendor_options);
EXPECT_EQ(vendor_id_, response_vendor_options->getVendorId());
// Now check that it contains requested option with the appropriate
// content.
OptionPtr suboption(response_vendor_options->getOption(option_));
// Check that it contains requested option with the appropriate content.
OptionPtr suboption(
response_vendor_options->getOption(option_));
ASSERT_TRUE(suboption);
vector<uint8_t> binary_suboption = suboption->toBinary(false);
string text(binary_suboption.begin(), binary_suboption.end());
@@ -235,93 +342,13 @@ TEST_F(VendorOptsTest, docsisVendorORO) {
// 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) {
testVendorOptionsORO(VENDOR_ID_CABLE_LABS);
}
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->setIndex(ETH0_INDEX);
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());
// Same as vendorOptionsORO except a different vendor ID than Cable Labs is
// provided and vendor options are expected to not be present in the response.
TEST_F(VendorOptsTest, vendorOptionsORODifferentVendorID) {
testVendorOptionsORO(32768);
}
// This test checks if Option Request Option (ORO) in docsis (vendor-id=4491)