Commit 9c956ea5 authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[4765] Moved classification specific unit tests to a separate file.

parent b0daac3d
......@@ -92,6 +92,7 @@ dhcp6_unittests_SOURCES += decline_unittest.cc
dhcp6_unittests_SOURCES += dhcp6_message_test.cc dhcp6_message_test.h
dhcp6_unittests_SOURCES += kea_controller_unittest.cc
dhcp6_unittests_SOURCES += dhcp6to4_ipc_unittest.cc
dhcp6_unittests_SOURCES += classify_unittests.cc
nodist_dhcp6_unittests_SOURCES = marker_file.h test_libraries.h
......
This diff is collapsed.
......@@ -1721,432 +1721,6 @@ TEST_F(Dhcpv6SrvTest, vendorOptionsDocsisDefinitions) {
ASSERT_EQ(0, rcode_);
}
// Checks if DOCSIS client packets are classified properly
TEST_F(Dhcpv6SrvTest, docsisClientClassification) {
NakedDhcpv6Srv srv(0);
// Let's create a relayed SOLICIT. This particular relayed SOLICIT has
// vendor-class set to docsis3.0
Pkt6Ptr sol1;
ASSERT_NO_THROW(sol1 = PktCaptures::captureDocsisRelayedSolicit());
ASSERT_NO_THROW(sol1->unpack());
srv.classifyPacket(sol1);
// It should belong to docsis3.0 class. It should not belong to eRouter1.0
EXPECT_TRUE(sol1->inClass("VENDOR_CLASS_docsis3.0"));
EXPECT_FALSE(sol1->inClass("eRouter1.0"));
// Let's get a relayed SOLICIT. This particular relayed SOLICIT has
// vendor-class set to eRouter1.0
Pkt6Ptr sol2;
ASSERT_NO_THROW(sol2 = PktCaptures::captureeRouterRelayedSolicit());
ASSERT_NO_THROW(sol2->unpack());
srv.classifyPacket(sol2);
EXPECT_TRUE(sol2->inClass(srv.VENDOR_CLASS_PREFIX + "eRouter1.0"));
EXPECT_FALSE(sol2->inClass(srv.VENDOR_CLASS_PREFIX + "docsis3.0"));
}
// Checks if client packets are classified properly using match expressions.
// Note option names and definitions are used.
TEST_F(Dhcpv6SrvTest, matchClassification) {
IfaceMgrTestConfig test_config(true);
NakedDhcpv6Srv srv(0);
// The router class matches incoming packets with foo in a host-name
// option (code 1234) and sets an ipv6-forwarding option in the response.
string config = "{ \"interfaces-config\": {"
" \"interfaces\": [ \"*\" ] }, "
"\"preferred-lifetime\": 3000,"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"valid-lifetime\": 4000, "
"\"option-def\": [ "
"{ \"name\": \"host-name\","
" \"code\": 1234,"
" \"type\": \"string\" },"
"{ \"name\": \"ipv6-forwarding\","
" \"code\": 2345,"
" \"type\": \"boolean\" }],"
"\"subnet6\": [ "
"{ \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ], "
" \"subnet\": \"2001:db8:1::/48\", "
" \"interface\": \"eth1\" } ],"
"\"client-classes\": [ "
"{ \"name\": \"router\", "
" \"option-data\": ["
" { \"name\": \"ipv6-forwarding\", "
" \"data\": \"true\" } ], "
" \"test\": \"option[host-name].text == 'foo'\" } ] }";
ASSERT_NO_THROW(configure(config));
// Create packets with enough to select the subnet
OptionPtr clientid = generateClientId();
Pkt6Ptr query1(new Pkt6(DHCPV6_SOLICIT, 1234));
query1->setRemoteAddr(IOAddress("fe80::abcd"));
query1->addOption(clientid);
query1->setIface("eth1");
query1->addOption(generateIA(D6O_IA_NA, 123, 1500, 3000));
Pkt6Ptr query2(new Pkt6(DHCPV6_SOLICIT, 1234));
query2->setRemoteAddr(IOAddress("fe80::abcd"));
query2->addOption(clientid);
query2->setIface("eth1");
query2->addOption(generateIA(D6O_IA_NA, 234, 1500, 3000));
Pkt6Ptr query3(new Pkt6(DHCPV6_SOLICIT, 1234));
query3->setRemoteAddr(IOAddress("fe80::abcd"));
query3->addOption(clientid);
query3->setIface("eth1");
query3->addOption(generateIA(D6O_IA_NA, 345, 1500, 3000));
// Create and add an ORO option to the first 2 queries
OptionUint16ArrayPtr oro(new OptionUint16Array(Option::V6, D6O_ORO));
ASSERT_TRUE(oro);
oro->addValue(2345);
query1->addOption(oro);
query2->addOption(oro);
// Create and add a host-name option to the first and last queries
OptionStringPtr hostname(new OptionString(Option::V6, 1234, "foo"));
ASSERT_TRUE(hostname);
query1->addOption(hostname);
query3->addOption(hostname);
// Classify packets
srv.classifyPacket(query1);
srv.classifyPacket(query2);
srv.classifyPacket(query3);
// Packets with the exception of the second should be in the router class
EXPECT_TRUE(query1->inClass("router"));
EXPECT_FALSE(query2->inClass("router"));
EXPECT_TRUE(query3->inClass("router"));
// Process queries
Pkt6Ptr response1 = srv.processSolicit(query1);
Pkt6Ptr response2 = srv.processSolicit(query2);
Pkt6Ptr response3 = srv.processSolicit(query3);
// Classification processing should add an ip-forwarding option
OptionPtr opt1 = response1->getOption(2345);
EXPECT_TRUE(opt1);
// But only for the first query: second was not classified
OptionPtr opt2 = response2->getOption(2345);
EXPECT_FALSE(opt2);
// But only for the first query: third has no ORO
OptionPtr opt3 = response3->getOption(2345);
EXPECT_FALSE(opt3);
}
// Checks subnet options have the priority over class options
TEST_F(Dhcpv6SrvTest, subnetClassPriority) {
IfaceMgrTestConfig test_config(true);
NakedDhcpv6Srv srv(0);
// Subnet sets an ipv6-forwarding option in the response.
// The router class matches incoming packets with foo in a host-name
// option (code 1234) and sets an ipv6-forwarding option in the response.
string config = "{ \"interfaces-config\": {"
" \"interfaces\": [ \"*\" ] }, "
"\"preferred-lifetime\": 3000,"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"valid-lifetime\": 4000, "
"\"option-def\": [ "
"{ \"name\": \"host-name\","
" \"code\": 1234,"
" \"type\": \"string\" },"
"{ \"name\": \"ipv6-forwarding\","
" \"code\": 2345,"
" \"type\": \"boolean\" }],"
"\"subnet6\": [ "
"{ \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ], "
" \"subnet\": \"2001:db8:1::/48\", "
" \"interface\": \"eth1\", "
" \"option-data\": ["
" { \"name\": \"ipv6-forwarding\", "
" \"data\": \"false\" } ] } ], "
"\"client-classes\": [ "
"{ \"name\": \"router\","
" \"option-data\": ["
" { \"name\": \"ipv6-forwarding\", "
" \"data\": \"true\" } ], "
" \"test\": \"option[1234].text == 'foo'\" } ] }";
ASSERT_NO_THROW(configure(config));
// Create a packet with enough to select the subnet and go through
// the SOLICIT processing
Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 1234));
query->setRemoteAddr(IOAddress("fe80::abcd"));
OptionPtr clientid = generateClientId();
query->addOption(clientid);
query->setIface("eth1");
query->addOption(generateIA(D6O_IA_NA, 123, 1500, 3000));
// Create and add an ORO option to the query
OptionUint16ArrayPtr oro(new OptionUint16Array(Option::V6, D6O_ORO));
ASSERT_TRUE(oro);
oro->addValue(2345);
query->addOption(oro);
// Create and add a host-name option to the query
OptionStringPtr hostname(new OptionString(Option::V6, 1234, "foo"));
ASSERT_TRUE(hostname);
query->addOption(hostname);
// Classify the packet
srv.classifyPacket(query);
// The packet should be in the router class
EXPECT_TRUE(query->inClass("router"));
// Process the query
Pkt6Ptr response = srv.processSolicit(query);
// Processing should add an ip-forwarding option
OptionPtr opt = response->getOption(2345);
ASSERT_TRUE(opt);
ASSERT_GT(opt->len(), opt->getHeaderLen());
// Classification sets the value to true/1, subnet to false/0
// Here subnet has the priority
EXPECT_EQ(0, opt->getUint8());
}
// Checks subnet options have the priority over global options
TEST_F(Dhcpv6SrvTest, subnetGlobalPriority) {
IfaceMgrTestConfig test_config(true);
NakedDhcpv6Srv srv(0);
// Subnet sets an ipv6-forwarding option in the response.
// The router class matches incoming packets with foo in a host-name
// option (code 1234) and sets an ipv6-forwarding option in the response.
string config = "{ \"interfaces-config\": {"
" \"interfaces\": [ \"*\" ] }, "
"\"preferred-lifetime\": 3000,"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"valid-lifetime\": 4000, "
"\"option-def\": [ "
"{ \"name\": \"host-name\","
" \"code\": 1234,"
" \"type\": \"string\" },"
"{ \"name\": \"ipv6-forwarding\","
" \"code\": 2345,"
" \"type\": \"boolean\" }],"
"\"option-data\": ["
" { \"name\": \"ipv6-forwarding\", "
" \"data\": \"false\" } ], "
"\"subnet6\": [ "
"{ \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ], "
" \"subnet\": \"2001:db8:1::/48\", "
" \"interface\": \"eth1\", "
" \"option-data\": ["
" { \"name\": \"ipv6-forwarding\", "
" \"data\": \"false\" } ] } ] }";
ASSERT_NO_THROW(configure(config));
// Create a packet with enough to select the subnet and go through
// the SOLICIT processing
Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 1234));
query->setRemoteAddr(IOAddress("fe80::abcd"));
OptionPtr clientid = generateClientId();
query->addOption(clientid);
query->setIface("eth1");
query->addOption(generateIA(D6O_IA_NA, 123, 1500, 3000));
// Create and add an ORO option to the query
OptionUint16ArrayPtr oro(new OptionUint16Array(Option::V6, D6O_ORO));
ASSERT_TRUE(oro);
oro->addValue(2345);
query->addOption(oro);
// Create and add a host-name option to the query
OptionStringPtr hostname(new OptionString(Option::V6, 1234, "foo"));
ASSERT_TRUE(hostname);
query->addOption(hostname);
// Process the query
Pkt6Ptr response = srv.processSolicit(query);
// Processing should add an ip-forwarding option
OptionPtr opt = response->getOption(2345);
ASSERT_TRUE(opt);
ASSERT_GT(opt->len(), opt->getHeaderLen());
// Global sets the value to true/1, subnet to false/0
// Here subnet has the priority
EXPECT_EQ(0, opt->getUint8());
}
// Checks class options have the priority over global options
TEST_F(Dhcpv6SrvTest, classGlobalPriority) {
IfaceMgrTestConfig test_config(true);
NakedDhcpv6Srv srv(0);
// A global ipv6-forwarding option is set in the response.
// The router class matches incoming packets with foo in a host-name
// option (code 1234) and sets an ipv6-forwarding option in the response.
string config = "{ \"interfaces-config\": {"
" \"interfaces\": [ \"*\" ] }, "
"\"preferred-lifetime\": 3000,"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"valid-lifetime\": 4000, "
"\"option-def\": [ "
"{ \"name\": \"host-name\","
" \"code\": 1234,"
" \"type\": \"string\" },"
"{ \"name\": \"ipv6-forwarding\","
" \"code\": 2345,"
" \"type\": \"boolean\" }],"
"\"subnet6\": [ "
"{ \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ], "
" \"subnet\": \"2001:db8:1::/48\", "
" \"interface\": \"eth1\" } ],"
"\"option-data\": ["
" { \"name\": \"ipv6-forwarding\", "
" \"data\": \"false\" } ], "
"\"client-classes\": [ "
"{ \"name\": \"router\","
" \"option-data\": ["
" { \"name\": \"ipv6-forwarding\", "
" \"data\": \"true\" } ], "
" \"test\": \"option[1234].text == 'foo'\" } ] }";
ASSERT_NO_THROW(configure(config));
// Create a packet with enough to select the subnet and go through
// the SOLICIT processing
Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 1234));
query->setRemoteAddr(IOAddress("fe80::abcd"));
OptionPtr clientid = generateClientId();
query->addOption(clientid);
query->setIface("eth1");
query->addOption(generateIA(D6O_IA_NA, 123, 1500, 3000));
// Create and add an ORO option to the query
OptionUint16ArrayPtr oro(new OptionUint16Array(Option::V6, D6O_ORO));
ASSERT_TRUE(oro);
oro->addValue(2345);
query->addOption(oro);
// Create and add a host-name option to the query
OptionStringPtr hostname(new OptionString(Option::V6, 1234, "foo"));
ASSERT_TRUE(hostname);
query->addOption(hostname);
// Classify the packet
srv.classifyPacket(query);
// The packet should be in the router class
EXPECT_TRUE(query->inClass("router"));
// Process the query
Pkt6Ptr response = srv.processSolicit(query);
// Processing should add an ip-forwarding option
OptionPtr opt = response->getOption(2345);
ASSERT_TRUE(opt);
ASSERT_GT(opt->len(), opt->getHeaderLen());
// Classification sets the value to true/1, global to false/0
// Here class has the priority
EXPECT_NE(0, opt->getUint8());
}
// Checks if the client-class field is indeed used for subnet selection.
// Note that packet classification is already checked in Dhcpv6SrvTest
// .*Classification above.
TEST_F(Dhcpv6SrvTest, clientClassifySubnet) {
// This test configures 2 subnets. We actually only need the
// first one, but since there's still this ugly hack that picks
// the pool if there is only one, we must use more than one
// subnet. That ugly hack will be removed in #3242, currently
// under review.
// The second subnet does not play any role here. The client's
// IP address belongs to the first subnet, so only that first
// subnet is being tested.
string config = "{ \"interfaces-config\": {"
" \"interfaces\": [ \"*\" ]"
"},"
"\"preferred-lifetime\": 3000,"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"subnet6\": [ "
" { \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
" \"subnet\": \"2001:db8:1::/48\", "
" \"client-class\": \"foo\" "
" }, "
" { \"pools\": [ { \"pool\": \"2001:db8:2::/64\" } ],"
" \"subnet\": \"2001:db8:2::/48\", "
" \"client-class\": \"xyzzy\" "
" } "
"],"
"\"valid-lifetime\": 4000 }";
ASSERT_NO_THROW(configure(config));
Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
sol->setRemoteAddr(IOAddress("2001:db8:1::3"));
sol->addOption(generateIA(D6O_IA_NA, 234, 1500, 3000));
OptionPtr clientid = generateClientId();
sol->addOption(clientid);
// This discover does not belong to foo class, so it will not
// be serviced
EXPECT_FALSE(srv_.selectSubnet(sol));
// Let's add the packet to bar class and try again.
sol->addClass("bar");
// Still not supported, because it belongs to wrong class.
EXPECT_FALSE(srv_.selectSubnet(sol));
// Let's add it to matching class.
sol->addClass("foo");
// This time it should work
EXPECT_TRUE(srv_.selectSubnet(sol));
}
// Tests whether a packet with custom vendor-class (not erouter or docsis)
// is classified properly.
TEST_F(Dhcpv6SrvTest, vendorClientClassification2) {
NakedDhcpv6Srv srv(0);
// Let's create a SOLICIT.
Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
sol->setRemoteAddr(IOAddress("2001:db8:1::3"));
sol->addOption(generateIA(D6O_IA_NA, 234, 1500, 3000));
OptionPtr clientid = generateClientId();
sol->addOption(clientid);
// Now let's add a vendor-class with id=1234 and content "foo"
OptionVendorClassPtr vendor_class(new OptionVendorClass(Option::V6, 1234));
OpaqueDataTuple tuple(OpaqueDataTuple::LENGTH_2_BYTES);
tuple = "foo";
vendor_class->addTuple(tuple);
sol->addOption(vendor_class);
// Now the server classifies the packet.
srv.classifyPacket(sol);
// The packet should now belong to VENDOR_CLASS_foo.
EXPECT_TRUE(sol->inClass(srv.VENDOR_CLASS_PREFIX + "foo"));
// It should not belong to "foo"
EXPECT_FALSE(sol->inClass("foo"));
}
// This test checks that the server will handle a Solicit with the Vendor Class
// having a length of 4 (enterprise-id only).
TEST_F(Dhcpv6SrvTest, cableLabsShortVendorClass) {
......@@ -2251,75 +1825,6 @@ TEST_F(Dhcpv6SrvTest, relayOverride) {
EXPECT_FALSE(srv_.selectSubnet(sol));
}
// Checks if relay IP address specified in the relay-info structure can be
// used together with client-classification.
TEST_F(Dhcpv6SrvTest, relayOverrideAndClientClass) {
// This test configures 2 subnets. They both are on the same link, so they
// have the same relay-ip address. Furthermore, the first subnet is
// reserved for clients that belong to class "foo".
string config = "{ \"interfaces-config\": {"
" \"interfaces\": [ \"*\" ]"
"},"
"\"preferred-lifetime\": 3000,"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"subnet6\": [ "
" { \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
" \"subnet\": \"2001:db8:1::/48\", "
" \"client-class\": \"foo\", "
" \"relay\": { "
" \"ip-address\": \"2001:db8:3::1\""
" }"
" }, "
" { \"pools\": [ { \"pool\": \"2001:db8:2::/64\" } ],"
" \"subnet\": \"2001:db8:2::/48\", "
" \"relay\": { "
" \"ip-address\": \"2001:db8:3::1\""
" }"
" } "
"],"
"\"valid-lifetime\": 4000 }";
// Use this config to set up the server
ASSERT_NO_THROW(configure(config));
// Let's get the subnet configuration objects
const Subnet6Collection* subnets =
CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getAll();
ASSERT_EQ(2, subnets->size());
// Let's get them for easy reference
Subnet6Ptr subnet1 = (*subnets)[0];
Subnet6Ptr subnet2 = (*subnets)[1];
ASSERT_TRUE(subnet1);
ASSERT_TRUE(subnet2);
Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
sol->setRemoteAddr(IOAddress("2001:db8:1::3"));
sol->addOption(generateIA(D6O_IA_NA, 234, 1500, 3000));
OptionPtr clientid = generateClientId();
sol->addOption(clientid);
// Now pretend the packet came via one relay.
Pkt6::RelayInfo relay;
relay.linkaddr_ = IOAddress("2001:db8:3::1");
relay.peeraddr_ = IOAddress("fe80::1");
sol->relay_info_.push_back(relay);
// This packet does not belong to class foo, so it should be rejected in
// subnet[0], even though the relay-ip matches. It should be accepted in
// subnet[1], because the subnet matches and there are no class
// requirements.
EXPECT_TRUE(subnet2 == srv_.selectSubnet(sol));
// Now let's add this packet to class foo and recheck. This time it should
// be accepted in the first subnet, because both class and relay-ip match.
sol->addClass("foo");
EXPECT_TRUE(subnet1 == srv_.selectSubnet(sol));
}
/// @brief Creates RSOO option with suboptions
///
/// Creates Relay-Supplied Options option that includes nested options. The
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment