mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-29 13:07:50 +00:00
[5425a] Applied changes - need regen
This commit is contained in:
parent
0b6ecc7e4c
commit
300cb63670
@ -97,6 +97,26 @@
|
|||||||
"client-classes": [ "VoIP" ]
|
"client-classes": [ "VoIP" ]
|
||||||
} ],
|
} ],
|
||||||
"interface": "ethX"
|
"interface": "ethX"
|
||||||
|
},
|
||||||
|
|
||||||
|
// The following list defines a subnet with pools. For some pools
|
||||||
|
// we defined a class that is allowed in that pool. If not specified
|
||||||
|
// everyone is allowed. When a class is specified, only packets belonging
|
||||||
|
// to that class are allowed for that pool.
|
||||||
|
{
|
||||||
|
"pools": [
|
||||||
|
{
|
||||||
|
// This one is for VoIP devices only.
|
||||||
|
"pool": "192.0.4.1 - 192.0.4.200",
|
||||||
|
"client-class": "VoIP"
|
||||||
|
},
|
||||||
|
// This one doesn't have any client-class specified, so everyone
|
||||||
|
// is allowed in.
|
||||||
|
{
|
||||||
|
"pool": "192.0.5.1 - 192.0.5.200"
|
||||||
|
} ],
|
||||||
|
"subnet": "192.0.4.0/23",
|
||||||
|
"interface": "ethY"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -73,7 +73,19 @@
|
|||||||
"client-classes": [ "cable-modems" ]
|
"client-classes": [ "cable-modems" ]
|
||||||
} ],
|
} ],
|
||||||
"interface": "ethX"
|
"interface": "ethX"
|
||||||
|
},
|
||||||
|
// The following subnet contains a pool with a class constraint: only
|
||||||
|
// clients which belong to the class are allowed to use this pool.
|
||||||
|
{
|
||||||
|
"pools": [
|
||||||
|
{
|
||||||
|
"pool": "2001:db8:3::/80",
|
||||||
|
"client-class": "cable-modems"
|
||||||
|
} ],
|
||||||
|
"subnet": "2001:db8:4::/64",
|
||||||
|
"interface": "ethY"
|
||||||
}
|
}
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -801,6 +801,56 @@ concatenation of the strings</entry></row>
|
|||||||
</para>
|
</para>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section id="classification-pools">
|
||||||
|
<title>Configuring Pools With Class Information</title>
|
||||||
|
<para>
|
||||||
|
Similar to subnets in certain cases access to certain address or
|
||||||
|
prefix pools must be restricted to only clients that belong to a
|
||||||
|
given class, using the "client-class" when defining the pool.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Let's assume that the server is connected to a network segment that uses
|
||||||
|
the 192.0.2.0/24 prefix. The Administrator of that network has decided
|
||||||
|
that addresses from range 192.0.2.10 to 192.0.2.20 are going to be
|
||||||
|
managed by the DHCP4 server. Only clients belonging to client class
|
||||||
|
Client_foo are allowed to use this pool. Such a
|
||||||
|
configuration can be achieved in the following way:
|
||||||
|
<screen>
|
||||||
|
"Dhcp4": {
|
||||||
|
"client-classes": [
|
||||||
|
{
|
||||||
|
"name": "Client_foo",
|
||||||
|
"test": "substring(option[61].hex,0,3) == 'foo'",
|
||||||
|
"option-data": [
|
||||||
|
{
|
||||||
|
"name": "domain-name-servers",
|
||||||
|
"code": 6,
|
||||||
|
"space": "dhcp4",
|
||||||
|
"csv-format": true,
|
||||||
|
"data": "192.0.2.1, 192.0.2.2"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
...
|
||||||
|
],<userinput>
|
||||||
|
"subnet4": [
|
||||||
|
{
|
||||||
|
"subnet": "192.0.2.0/24",
|
||||||
|
"pools": [
|
||||||
|
{
|
||||||
|
"pool": "192.0.2.10 - 192.0.2.20",
|
||||||
|
"client-class": "Client_foo"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
...
|
||||||
|
],</userinput>,
|
||||||
|
...
|
||||||
|
}</screen>
|
||||||
|
</para>
|
||||||
|
</section>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<title>Using Classes</title>
|
<title>Using Classes</title>
|
||||||
<para>
|
<para>
|
||||||
|
@ -2092,6 +2092,15 @@ It is merely echoed by the server
|
|||||||
class restrictions on subnets, see <xref linkend="classification-subnets"/>.
|
class restrictions on subnets, see <xref linkend="classification-subnets"/>.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
When subnets belong to a shared network the classification applies
|
||||||
|
to subnet selection but not to pools, e.g., a pool in a subnet
|
||||||
|
limited to a particular class can still be used by clients which do not
|
||||||
|
belong to the class if the pool they are expected to use is exhausted.
|
||||||
|
So the limit access based on class information is also available
|
||||||
|
at the pool level, see <xref linkend="classification-pools"/>.
|
||||||
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
The process of doing classification is conducted in three steps. The first step
|
The process of doing classification is conducted in three steps. The first step
|
||||||
is to assess an incoming packet and assign it to zero or more classes. The
|
is to assess an incoming packet and assign it to zero or more classes. The
|
||||||
|
@ -1950,6 +1950,16 @@ should include options from the isc option space:
|
|||||||
class restrictions on subnets, see <xref linkend="classification-subnets"/>.
|
class restrictions on subnets, see <xref linkend="classification-subnets"/>.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
When subnets belong to a shared network the classification applies
|
||||||
|
to subnet selection but not to pools, e.g., a pool in a subnet
|
||||||
|
limited to a particular class can still be used by clients which do not
|
||||||
|
belong to the class if the pool they are expected to use is exhausted.
|
||||||
|
So the limit access based on class information is also available
|
||||||
|
at the address/prefix pool level, see <xref
|
||||||
|
linkend="classification-pools"/>.
|
||||||
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
The process of doing classification is conducted in three steps. The first step
|
The process of doing classification is conducted in three steps. The first step
|
||||||
is to assess an incoming packet and assign it to zero or more classes. The
|
is to assess an incoming packet and assign it to zero or more classes. The
|
||||||
|
@ -823,6 +823,7 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence}
|
|||||||
\"client-class\" {
|
\"client-class\" {
|
||||||
switch(driver.ctx_) {
|
switch(driver.ctx_) {
|
||||||
case isc::dhcp::Parser4Context::SUBNET4:
|
case isc::dhcp::Parser4Context::SUBNET4:
|
||||||
|
case isc::dhcp::Parser4Context::POOLS:
|
||||||
case isc::dhcp::Parser4Context::SHARED_NETWORK:
|
case isc::dhcp::Parser4Context::SHARED_NETWORK:
|
||||||
case isc::dhcp::Parser4Context::CLIENT_CLASSES:
|
case isc::dhcp::Parser4Context::CLIENT_CLASSES:
|
||||||
return isc::dhcp::Dhcp4Parser::make_CLIENT_CLASS(driver.loc_);
|
return isc::dhcp::Dhcp4Parser::make_CLIENT_CLASS(driver.loc_);
|
||||||
|
@ -1344,6 +1344,7 @@ pool_params: pool_param
|
|||||||
|
|
||||||
pool_param: pool_entry
|
pool_param: pool_entry
|
||||||
| option_data_list
|
| option_data_list
|
||||||
|
| client_class
|
||||||
| user_context
|
| user_context
|
||||||
| comment
|
| comment
|
||||||
| unknown_map_entry
|
| unknown_map_entry
|
||||||
@ -1598,11 +1599,11 @@ client_classes: CLIENT_CLASSES {
|
|||||||
ctx.leave();
|
ctx.leave();
|
||||||
};
|
};
|
||||||
|
|
||||||
client_classes_list: client_class
|
client_classes_list: client_class_entry
|
||||||
| client_classes_list COMMA client_class
|
| client_classes_list COMMA client_class_entry
|
||||||
;
|
;
|
||||||
|
|
||||||
client_class: LCURLY_BRACKET {
|
client_class_entry: LCURLY_BRACKET {
|
||||||
ElementPtr m(new MapElement(ctx.loc2pos(@1)));
|
ElementPtr m(new MapElement(ctx.loc2pos(@1)));
|
||||||
ctx.stack_.back()->add(m);
|
ctx.stack_.back()->add(m);
|
||||||
ctx.stack_.push_back(m);
|
ctx.stack_.push_back(m);
|
||||||
|
@ -4100,6 +4100,7 @@ TEST_F(Dhcp4ParserTest, classifySubnets) {
|
|||||||
|
|
||||||
ConstElementPtr json;
|
ConstElementPtr json;
|
||||||
ASSERT_NO_THROW(json = parseDHCP4(config));
|
ASSERT_NO_THROW(json = parseDHCP4(config));
|
||||||
|
extractConfig(config);
|
||||||
|
|
||||||
EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
|
EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
|
||||||
checkResult(x, 0);
|
checkResult(x, 0);
|
||||||
@ -4157,6 +4158,95 @@ TEST_F(Dhcp4ParserTest, classifySubnets) {
|
|||||||
EXPECT_TRUE (subnets->at(3)->clientSupported(classes));
|
EXPECT_TRUE (subnets->at(3)->clientSupported(classes));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Goal of this test is to verify that multiple pools can be configured
|
||||||
|
// with defined client classes.
|
||||||
|
TEST_F(Dhcp4ParserTest, classifyPools) {
|
||||||
|
ConstElementPtr x;
|
||||||
|
string config = "{ " + genIfaceConfig() + "," +
|
||||||
|
"\"rebind-timer\": 2000, "
|
||||||
|
"\"renew-timer\": 1000, "
|
||||||
|
"\"subnet4\": [ { "
|
||||||
|
" \"pools\": [ { "
|
||||||
|
" \"pool\": \"192.0.2.1 - 192.0.2.100\", "
|
||||||
|
" \"client-class\": \"alpha\" "
|
||||||
|
" },"
|
||||||
|
" {"
|
||||||
|
" \"pool\": \"192.0.3.101 - 192.0.3.150\", "
|
||||||
|
" \"client-class\": \"beta\" "
|
||||||
|
" },"
|
||||||
|
" {"
|
||||||
|
" \"pool\": \"192.0.4.101 - 192.0.4.150\", "
|
||||||
|
" \"client-class\": \"gamma\" "
|
||||||
|
" },"
|
||||||
|
" {"
|
||||||
|
" \"pool\": \"192.0.5.101 - 192.0.5.150\" "
|
||||||
|
" } ],"
|
||||||
|
" \"subnet\": \"192.0.0.0/16\" "
|
||||||
|
" } ],"
|
||||||
|
"\"valid-lifetime\": 4000 }";
|
||||||
|
|
||||||
|
ConstElementPtr json;
|
||||||
|
ASSERT_NO_THROW(json = parseDHCP4(config, true));
|
||||||
|
extractConfig(config);
|
||||||
|
|
||||||
|
EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
|
||||||
|
checkResult(x, 0);
|
||||||
|
|
||||||
|
const Subnet4Collection* subnets =
|
||||||
|
CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->getAll();
|
||||||
|
ASSERT_TRUE(subnets);
|
||||||
|
ASSERT_EQ(1, subnets->size());
|
||||||
|
const PoolCollection& pools = subnets->at(0)->getPools(Lease::TYPE_V4);
|
||||||
|
ASSERT_EQ(4, pools.size()); // We expect 4 pools
|
||||||
|
|
||||||
|
// Let's check if client belonging to alpha class is supported in pool[0]
|
||||||
|
// and not supported in any other pool (except pool[3], which allows
|
||||||
|
// everyone).
|
||||||
|
ClientClasses classes;
|
||||||
|
classes.insert("alpha");
|
||||||
|
EXPECT_TRUE(pools.at(0)->clientSupported(classes));
|
||||||
|
EXPECT_FALSE(pools.at(1)->clientSupported(classes));
|
||||||
|
EXPECT_FALSE(pools.at(2)->clientSupported(classes));
|
||||||
|
EXPECT_TRUE(pools.at(3)->clientSupported(classes));
|
||||||
|
|
||||||
|
// Let's check if client belonging to beta class is supported in pool[1]
|
||||||
|
// and not supported in any other pool (except pools[3], which allows
|
||||||
|
// everyone).
|
||||||
|
classes.clear();
|
||||||
|
classes.insert("beta");
|
||||||
|
EXPECT_FALSE(pools.at(0)->clientSupported(classes));
|
||||||
|
EXPECT_TRUE(pools.at(1)->clientSupported(classes));
|
||||||
|
EXPECT_FALSE(pools.at(2)->clientSupported(classes));
|
||||||
|
EXPECT_TRUE(pools.at(3)->clientSupported(classes));
|
||||||
|
|
||||||
|
// Let's check if client belonging to gamma class is supported in pool[2]
|
||||||
|
// and not supported in any other pool (except pool[3], which allows
|
||||||
|
// everyone).
|
||||||
|
classes.clear();
|
||||||
|
classes.insert("gamma");
|
||||||
|
EXPECT_FALSE(pools.at(0)->clientSupported(classes));
|
||||||
|
EXPECT_FALSE(pools.at(1)->clientSupported(classes));
|
||||||
|
EXPECT_TRUE(pools.at(2)->clientSupported(classes));
|
||||||
|
EXPECT_TRUE(pools.at(3)->clientSupported(classes));
|
||||||
|
|
||||||
|
// Let's check if client belonging to some other class (not mentioned in
|
||||||
|
// the config) is supported only in pool[3], which allows everyone.
|
||||||
|
classes.clear();
|
||||||
|
classes.insert("delta");
|
||||||
|
EXPECT_FALSE(pools.at(0)->clientSupported(classes));
|
||||||
|
EXPECT_FALSE(pools.at(1)->clientSupported(classes));
|
||||||
|
EXPECT_FALSE(pools.at(2)->clientSupported(classes));
|
||||||
|
EXPECT_TRUE(pools.at(3)->clientSupported(classes));
|
||||||
|
|
||||||
|
// Finally, let's check class-less client. He should be allowed only in
|
||||||
|
// the last pool, which does not have any class restrictions.
|
||||||
|
classes.clear();
|
||||||
|
EXPECT_FALSE(pools.at(0)->clientSupported(classes));
|
||||||
|
EXPECT_FALSE(pools.at(1)->clientSupported(classes));
|
||||||
|
EXPECT_FALSE(pools.at(2)->clientSupported(classes));
|
||||||
|
EXPECT_TRUE(pools.at(3)->clientSupported(classes));
|
||||||
|
}
|
||||||
|
|
||||||
// This test verifies that the host reservations can be specified for
|
// This test verifies that the host reservations can be specified for
|
||||||
// respective IPv4 subnets.
|
// respective IPv4 subnets.
|
||||||
TEST_F(Dhcp4ParserTest, reservations) {
|
TEST_F(Dhcp4ParserTest, reservations) {
|
||||||
|
@ -2318,6 +2318,75 @@ TEST_F(Dhcpv4SrvTest, clientClassify) {
|
|||||||
EXPECT_TRUE(srv_.selectSubnet(dis));
|
EXPECT_TRUE(srv_.selectSubnet(dis));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Checks if the client-class field is indeed used for pool selection.
|
||||||
|
TEST_F(Dhcpv4SrvTest, clientPoolClassify) {
|
||||||
|
IfaceMgrTestConfig test_config(true);
|
||||||
|
IfaceMgr::instance().openSockets4();
|
||||||
|
|
||||||
|
NakedDhcpv4Srv srv(0);
|
||||||
|
|
||||||
|
// This test configures 2 pools.
|
||||||
|
// The second pool does not play any role here. The client's
|
||||||
|
// IP address belongs to the first pool, so only that first
|
||||||
|
// pool is being tested.
|
||||||
|
string config = "{ \"interfaces-config\": {"
|
||||||
|
" \"interfaces\": [ \"*\" ]"
|
||||||
|
"},"
|
||||||
|
"\"rebind-timer\": 2000, "
|
||||||
|
"\"renew-timer\": 1000, "
|
||||||
|
"\"subnet4\": [ "
|
||||||
|
"{ \"pools\": [ { "
|
||||||
|
" \"pool\": \"192.0.2.1 - 192.0.2.100\", "
|
||||||
|
" \"client-class\": \"foo\" }, "
|
||||||
|
" { \"pool\": \"192.0.3.1 - 192.0.3.100\", "
|
||||||
|
" \"client-class\": \"xyzzy\" } ], "
|
||||||
|
" \"subnet\": \"192.0.0.0/16\" } "
|
||||||
|
"],"
|
||||||
|
"\"valid-lifetime\": 4000 }";
|
||||||
|
|
||||||
|
ConstElementPtr json;
|
||||||
|
ASSERT_NO_THROW(json = parseDHCP4(config, true));
|
||||||
|
|
||||||
|
ConstElementPtr status;
|
||||||
|
EXPECT_NO_THROW(status = configureDhcp4Server(srv, json));
|
||||||
|
|
||||||
|
CfgMgr::instance().commit();
|
||||||
|
|
||||||
|
// check if returned status is OK
|
||||||
|
ASSERT_TRUE(status);
|
||||||
|
comment_ = config::parseAnswer(rcode_, status);
|
||||||
|
ASSERT_EQ(0, rcode_);
|
||||||
|
|
||||||
|
// Create a simple packet that we'll use for classification
|
||||||
|
Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
|
||||||
|
dis->setRemoteAddr(IOAddress("192.0.2.1"));
|
||||||
|
dis->setCiaddr(IOAddress("192.0.2.1"));
|
||||||
|
dis->setIface("eth0");
|
||||||
|
OptionPtr clientid = generateClientId();
|
||||||
|
dis->addOption(clientid);
|
||||||
|
|
||||||
|
// This discover does not belong to foo class, so it will not
|
||||||
|
// be serviced
|
||||||
|
Pkt4Ptr offer = srv.processDiscover(dis);
|
||||||
|
EXPECT_FALSE(offer);
|
||||||
|
|
||||||
|
// Let's add the packet to bar class and try again.
|
||||||
|
dis->addClass("bar");
|
||||||
|
|
||||||
|
// Still not supported, because it belongs to wrong class.
|
||||||
|
offer = srv.processDiscover(dis);
|
||||||
|
EXPECT_FALSE(offer);
|
||||||
|
|
||||||
|
// Let's add it to matching class.
|
||||||
|
dis->addClass("foo");
|
||||||
|
|
||||||
|
// This time it should work
|
||||||
|
offer = srv.processDiscover(dis);
|
||||||
|
ASSERT_TRUE(offer);
|
||||||
|
EXPECT_EQ(DHCPOFFER, offer->getType());
|
||||||
|
EXPECT_FALSE(offer->getYiaddr().isV4Zero());
|
||||||
|
}
|
||||||
|
|
||||||
// Verifies last resort option 43 is backward compatible
|
// Verifies last resort option 43 is backward compatible
|
||||||
TEST_F(Dhcpv4SrvTest, option43LastResort) {
|
TEST_F(Dhcpv4SrvTest, option43LastResort) {
|
||||||
IfaceMgrTestConfig test_config(true);
|
IfaceMgrTestConfig test_config(true);
|
||||||
|
@ -700,7 +700,7 @@ const char* NETWORKS_CONFIG[] = {
|
|||||||
|
|
||||||
// Configuration #13.
|
// Configuration #13.
|
||||||
// - 2 classes
|
// - 2 classes
|
||||||
// - 2 shared networks, each with 1 subnet and client class restricton
|
// - 2 shared networks, each with 1 subnet and client class restriction
|
||||||
"{"
|
"{"
|
||||||
" \"interfaces-config\": {"
|
" \"interfaces-config\": {"
|
||||||
" \"interfaces\": [ \"*\" ]"
|
" \"interfaces\": [ \"*\" ]"
|
||||||
@ -861,6 +861,158 @@ const char* NETWORKS_CONFIG[] = {
|
|||||||
" ]"
|
" ]"
|
||||||
" }"
|
" }"
|
||||||
" ]"
|
" ]"
|
||||||
|
"}",
|
||||||
|
|
||||||
|
// Configuration #16
|
||||||
|
// - 1 shared network with 1 subnet and 2 pools (first pool has class restriction)
|
||||||
|
"{"
|
||||||
|
" \"interfaces-config\": {"
|
||||||
|
" \"interfaces\": [ \"*\" ]"
|
||||||
|
" },"
|
||||||
|
" \"client-classes\": ["
|
||||||
|
" {"
|
||||||
|
" \"name\": \"a-devices\","
|
||||||
|
" \"test\": \"option[93].hex == 0x0001\""
|
||||||
|
" },"
|
||||||
|
" {"
|
||||||
|
" \"name\": \"b-devices\","
|
||||||
|
" \"test\": \"option[93].hex == 0x0002\""
|
||||||
|
" }"
|
||||||
|
" ],"
|
||||||
|
" \"valid-lifetime\": 600,"
|
||||||
|
" \"shared-networks\": ["
|
||||||
|
" {"
|
||||||
|
" \"name\": \"frog\","
|
||||||
|
" \"interface\": \"eth1\","
|
||||||
|
" \"subnet4\": ["
|
||||||
|
" {"
|
||||||
|
" \"subnet\": \"192.0.2.0/24\","
|
||||||
|
" \"id\": 10,"
|
||||||
|
" \"pools\": ["
|
||||||
|
" {"
|
||||||
|
" \"pool\": \"192.0.2.1 - 192.0.2.63\","
|
||||||
|
" \"client-class\": \"a-devices\""
|
||||||
|
" },"
|
||||||
|
" {"
|
||||||
|
" \"pool\": \"192.0.2.100 - 192.0.2.100\""
|
||||||
|
" }"
|
||||||
|
" ]"
|
||||||
|
" }"
|
||||||
|
" ]"
|
||||||
|
" }"
|
||||||
|
" ]"
|
||||||
|
"}",
|
||||||
|
|
||||||
|
// Configuration #17
|
||||||
|
// - 1 shared network with 1 subnet and 2 pools (each with class restriction)
|
||||||
|
"{"
|
||||||
|
" \"interfaces-config\": {"
|
||||||
|
" \"interfaces\": [ \"*\" ]"
|
||||||
|
" },"
|
||||||
|
" \"client-classes\": ["
|
||||||
|
" {"
|
||||||
|
" \"name\": \"a-devices\","
|
||||||
|
" \"test\": \"option[93].hex == 0x0001\""
|
||||||
|
" },"
|
||||||
|
" {"
|
||||||
|
" \"name\": \"b-devices\","
|
||||||
|
" \"test\": \"option[93].hex == 0x0002\""
|
||||||
|
" }"
|
||||||
|
" ],"
|
||||||
|
" \"valid-lifetime\": 600,"
|
||||||
|
" \"shared-networks\": ["
|
||||||
|
" {"
|
||||||
|
" \"name\": \"frog\","
|
||||||
|
" \"interface\": \"eth1\","
|
||||||
|
" \"subnet4\": ["
|
||||||
|
" {"
|
||||||
|
" \"subnet\": \"192.0.2.0/24\","
|
||||||
|
" \"id\": 10,"
|
||||||
|
" \"pools\": ["
|
||||||
|
" {"
|
||||||
|
" \"pool\": \"192.0.2.1 - 192.0.2.63\","
|
||||||
|
" \"client-class\": \"a-devices\""
|
||||||
|
" },"
|
||||||
|
" {"
|
||||||
|
" \"pool\": \"192.0.2.100 - 192.0.2.100\","
|
||||||
|
" \"client-class\": \"b-devices\""
|
||||||
|
" }"
|
||||||
|
" ]"
|
||||||
|
" }"
|
||||||
|
" ]"
|
||||||
|
" }"
|
||||||
|
" ]"
|
||||||
|
"}",
|
||||||
|
|
||||||
|
// Configuration #18
|
||||||
|
// - plain subnet and 2 pools (first pool has class restriction)
|
||||||
|
"{"
|
||||||
|
" \"interfaces-config\": {"
|
||||||
|
" \"interfaces\": [ \"*\" ]"
|
||||||
|
" },"
|
||||||
|
" \"client-classes\": ["
|
||||||
|
" {"
|
||||||
|
" \"name\": \"a-devices\","
|
||||||
|
" \"test\": \"option[93].hex == 0x0001\""
|
||||||
|
" },"
|
||||||
|
" {"
|
||||||
|
" \"name\": \"b-devices\","
|
||||||
|
" \"test\": \"option[93].hex == 0x0002\""
|
||||||
|
" }"
|
||||||
|
" ],"
|
||||||
|
" \"valid-lifetime\": 600,"
|
||||||
|
" \"subnet4\": ["
|
||||||
|
" {"
|
||||||
|
" \"subnet\": \"192.0.2.0/24\","
|
||||||
|
" \"id\": 10,"
|
||||||
|
" \"interface\": \"eth1\","
|
||||||
|
" \"pools\": ["
|
||||||
|
" {"
|
||||||
|
" \"pool\": \"192.0.2.1 - 192.0.2.63\","
|
||||||
|
" \"client-class\": \"a-devices\""
|
||||||
|
" },"
|
||||||
|
" {"
|
||||||
|
" \"pool\": \"192.0.2.100 - 192.0.2.100\""
|
||||||
|
" }"
|
||||||
|
" ]"
|
||||||
|
" }"
|
||||||
|
" ]"
|
||||||
|
"}",
|
||||||
|
|
||||||
|
// Configuration #19
|
||||||
|
// - plain subnet and 2 pools (each with class restriction)
|
||||||
|
"{"
|
||||||
|
" \"interfaces-config\": {"
|
||||||
|
" \"interfaces\": [ \"*\" ]"
|
||||||
|
" },"
|
||||||
|
" \"client-classes\": ["
|
||||||
|
" {"
|
||||||
|
" \"name\": \"a-devices\","
|
||||||
|
" \"test\": \"option[93].hex == 0x0001\""
|
||||||
|
" },"
|
||||||
|
" {"
|
||||||
|
" \"name\": \"b-devices\","
|
||||||
|
" \"test\": \"option[93].hex == 0x0002\""
|
||||||
|
" }"
|
||||||
|
" ],"
|
||||||
|
" \"valid-lifetime\": 600,"
|
||||||
|
" \"subnet4\": ["
|
||||||
|
" {"
|
||||||
|
" \"subnet\": \"192.0.2.0/24\","
|
||||||
|
" \"id\": 10,"
|
||||||
|
" \"interface\": \"eth1\","
|
||||||
|
" \"pools\": ["
|
||||||
|
" {"
|
||||||
|
" \"pool\": \"192.0.2.1 - 192.0.2.63\","
|
||||||
|
" \"client-class\": \"a-devices\""
|
||||||
|
" },"
|
||||||
|
" {"
|
||||||
|
" \"pool\": \"192.0.2.100 - 192.0.2.100\","
|
||||||
|
" \"client-class\": \"b-devices\""
|
||||||
|
" }"
|
||||||
|
" ]"
|
||||||
|
" }"
|
||||||
|
" ]"
|
||||||
"}"
|
"}"
|
||||||
|
|
||||||
};
|
};
|
||||||
@ -1813,4 +1965,123 @@ TEST_F(Dhcpv4SharedNetworkTest, customServerIdentifier) {
|
|||||||
EXPECT_EQ("2.3.4.5", client2.config_.serverid_.toText());
|
EXPECT_EQ("2.3.4.5", client2.config_.serverid_.toText());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Access to a pool within shared network is restricted by client
|
||||||
|
// classification.
|
||||||
|
TEST_F(Dhcpv4SharedNetworkTest, poolInSharedNetworkSelectedByClass) {
|
||||||
|
// Create client #1
|
||||||
|
Dhcp4Client client1(Dhcp4Client::SELECTING);
|
||||||
|
client1.setIfaceName("eth1");
|
||||||
|
|
||||||
|
// Configure the server with one shared network including one subnet and
|
||||||
|
// in 2 pools in it. The access to one of the pools is restricted
|
||||||
|
// by client classification.
|
||||||
|
configure(NETWORKS_CONFIG[16], *client1.getServer());
|
||||||
|
|
||||||
|
// Client #1 requests an address in the restricted pool but can't be assigned
|
||||||
|
// this address because the client doesn't belong to a certain class.
|
||||||
|
testAssigned([this, &client1] {
|
||||||
|
doDORA(client1, "192.0.2.100", "192.0.2.63");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Release the lease that the client has got, because we'll need this address
|
||||||
|
// further in the test.
|
||||||
|
testAssigned([this, &client1] {
|
||||||
|
ASSERT_NO_THROW(client1.doRelease());
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add option93 which would cause the client to be classified as "a-devices".
|
||||||
|
OptionPtr option93(new OptionUint16(Option::V4, 93, 0x0001));
|
||||||
|
client1.addExtraOption(option93);
|
||||||
|
|
||||||
|
// This time, the allocation of the address provided as hint should be successful.
|
||||||
|
testAssigned([this, &client1] {
|
||||||
|
doDORA(client1, "192.0.2.63", "192.0.2.63");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Client 2 should be assigned an address from the unrestricted pool.
|
||||||
|
Dhcp4Client client2(client1.getServer(), Dhcp4Client::SELECTING);
|
||||||
|
client2.setIfaceName("eth1");
|
||||||
|
testAssigned([this, &client2] {
|
||||||
|
doDORA(client2, "192.0.2.100");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Now, let's reconfigure the server to also apply restrictions on the
|
||||||
|
// pool to which client2 now belongs.
|
||||||
|
configure(NETWORKS_CONFIG[17], *client1.getServer());
|
||||||
|
|
||||||
|
// The client should be refused to renew the lease because it doesn't belong
|
||||||
|
// to "b-devices" class.
|
||||||
|
client2.setState(Dhcp4Client::RENEWING);
|
||||||
|
testAssigned([this, &client2] {
|
||||||
|
doRequest(client2, "");
|
||||||
|
});
|
||||||
|
|
||||||
|
// If we add option93 with a value matching this class, the lease should
|
||||||
|
// get renewed.
|
||||||
|
OptionPtr option93_bis(new OptionUint16(Option::V4, 93, 0x0002));
|
||||||
|
client2.addExtraOption(option93_bis);
|
||||||
|
|
||||||
|
testAssigned([this, &client2] {
|
||||||
|
doRequest(client2, "192.0.2.100");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Access to a pool within plain subnet is restricted by client classification.
|
||||||
|
TEST_F(Dhcpv4SharedNetworkTest, poolInSubnetSelectedByClass) {
|
||||||
|
// Create client #1
|
||||||
|
Dhcp4Client client1(Dhcp4Client::SELECTING);
|
||||||
|
client1.setIfaceName("eth1");
|
||||||
|
|
||||||
|
// Configure the server with one plain subnet including two pools.
|
||||||
|
// The access to one of the pools is restricted by client classification.
|
||||||
|
configure(NETWORKS_CONFIG[18], *client1.getServer());
|
||||||
|
|
||||||
|
// Client #1 requests an address in the restricted pool but can't be assigned
|
||||||
|
// this address because the client doesn't belong to a certain class.
|
||||||
|
testAssigned([this, &client1] {
|
||||||
|
doDORA(client1, "192.0.2.100", "192.0.2.63");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Release the lease that the client has got, because we'll need this address
|
||||||
|
// further in the test.
|
||||||
|
testAssigned([this, &client1] {
|
||||||
|
ASSERT_NO_THROW(client1.doRelease());
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add option93 which would cause the client to be classified as "a-devices".
|
||||||
|
OptionPtr option93(new OptionUint16(Option::V4, 93, 0x0001));
|
||||||
|
client1.addExtraOption(option93);
|
||||||
|
|
||||||
|
// This time, the allocation of the address provided as hint should be successful.
|
||||||
|
testAssigned([this, &client1] {
|
||||||
|
doDORA(client1, "192.0.2.63", "192.0.2.63");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Client 2 should be assigned an address from the unrestricted pool.
|
||||||
|
Dhcp4Client client2(client1.getServer(), Dhcp4Client::SELECTING);
|
||||||
|
client2.setIfaceName("eth1");
|
||||||
|
testAssigned([this, &client2] {
|
||||||
|
doDORA(client2, "192.0.2.100");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Now, let's reconfigure the server to also apply restrictions on the
|
||||||
|
// pool to which client2 now belongs.
|
||||||
|
configure(NETWORKS_CONFIG[19], *client1.getServer());
|
||||||
|
|
||||||
|
// The client should be refused to renew the lease because it doesn't belong
|
||||||
|
// to "b-devices" class.
|
||||||
|
client2.setState(Dhcp4Client::RENEWING);
|
||||||
|
testAssigned([this, &client2] {
|
||||||
|
doRequest(client2, "");
|
||||||
|
});
|
||||||
|
|
||||||
|
// If we add option93 with a value matching this class, the lease should
|
||||||
|
// get renewed.
|
||||||
|
OptionPtr option93_bis(new OptionUint16(Option::V4, 93, 0x0002));
|
||||||
|
client2.addExtraOption(option93_bis);
|
||||||
|
|
||||||
|
testAssigned([this, &client2] {
|
||||||
|
doRequest(client2, "192.0.2.100");
|
||||||
|
});
|
||||||
|
}
|
||||||
} // end of anonymous namespace
|
} // end of anonymous namespace
|
||||||
|
@ -609,7 +609,7 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence}
|
|||||||
switch(driver.ctx_) {
|
switch(driver.ctx_) {
|
||||||
case isc::dhcp::Parser6Context::DHCP6:
|
case isc::dhcp::Parser6Context::DHCP6:
|
||||||
case isc::dhcp::Parser6Context::SUBNET6:
|
case isc::dhcp::Parser6Context::SUBNET6:
|
||||||
case Parser6Context::SHARED_NETWORK:
|
case isc::dhcp::Parser6Context::SHARED_NETWORK:
|
||||||
return isc::dhcp::Dhcp6Parser::make_PREFERRED_LIFETIME(driver.loc_);
|
return isc::dhcp::Dhcp6Parser::make_PREFERRED_LIFETIME(driver.loc_);
|
||||||
default:
|
default:
|
||||||
return isc::dhcp::Dhcp6Parser::make_STRING("preferred-lifetime", driver.loc_);
|
return isc::dhcp::Dhcp6Parser::make_STRING("preferred-lifetime", driver.loc_);
|
||||||
@ -620,7 +620,7 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence}
|
|||||||
switch(driver.ctx_) {
|
switch(driver.ctx_) {
|
||||||
case isc::dhcp::Parser6Context::DHCP6:
|
case isc::dhcp::Parser6Context::DHCP6:
|
||||||
case isc::dhcp::Parser6Context::SUBNET6:
|
case isc::dhcp::Parser6Context::SUBNET6:
|
||||||
case Parser6Context::SHARED_NETWORK:
|
case isc::dhcp::Parser6Context::SHARED_NETWORK:
|
||||||
return isc::dhcp::Dhcp6Parser::make_VALID_LIFETIME(driver.loc_);
|
return isc::dhcp::Dhcp6Parser::make_VALID_LIFETIME(driver.loc_);
|
||||||
default:
|
default:
|
||||||
return isc::dhcp::Dhcp6Parser::make_STRING("valid-lifetime", driver.loc_);
|
return isc::dhcp::Dhcp6Parser::make_STRING("valid-lifetime", driver.loc_);
|
||||||
@ -631,7 +631,7 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence}
|
|||||||
switch(driver.ctx_) {
|
switch(driver.ctx_) {
|
||||||
case isc::dhcp::Parser6Context::DHCP6:
|
case isc::dhcp::Parser6Context::DHCP6:
|
||||||
case isc::dhcp::Parser6Context::SUBNET6:
|
case isc::dhcp::Parser6Context::SUBNET6:
|
||||||
case Parser6Context::SHARED_NETWORK:
|
case isc::dhcp::Parser6Context::SHARED_NETWORK:
|
||||||
return isc::dhcp::Dhcp6Parser::make_RENEW_TIMER(driver.loc_);
|
return isc::dhcp::Dhcp6Parser::make_RENEW_TIMER(driver.loc_);
|
||||||
default:
|
default:
|
||||||
return isc::dhcp::Dhcp6Parser::make_STRING("renew-timer", driver.loc_);
|
return isc::dhcp::Dhcp6Parser::make_STRING("renew-timer", driver.loc_);
|
||||||
@ -642,7 +642,7 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence}
|
|||||||
switch(driver.ctx_) {
|
switch(driver.ctx_) {
|
||||||
case isc::dhcp::Parser6Context::DHCP6:
|
case isc::dhcp::Parser6Context::DHCP6:
|
||||||
case isc::dhcp::Parser6Context::SUBNET6:
|
case isc::dhcp::Parser6Context::SUBNET6:
|
||||||
case Parser6Context::SHARED_NETWORK:
|
case isc::dhcp::Parser6Context::SHARED_NETWORK:
|
||||||
return isc::dhcp::Dhcp6Parser::make_REBIND_TIMER(driver.loc_);
|
return isc::dhcp::Dhcp6Parser::make_REBIND_TIMER(driver.loc_);
|
||||||
default:
|
default:
|
||||||
return isc::dhcp::Dhcp6Parser::make_STRING("rebind-timer", driver.loc_);
|
return isc::dhcp::Dhcp6Parser::make_STRING("rebind-timer", driver.loc_);
|
||||||
@ -661,7 +661,7 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence}
|
|||||||
\"subnet6\" {
|
\"subnet6\" {
|
||||||
switch(driver.ctx_) {
|
switch(driver.ctx_) {
|
||||||
case isc::dhcp::Parser6Context::DHCP6:
|
case isc::dhcp::Parser6Context::DHCP6:
|
||||||
case Parser6Context::SHARED_NETWORK:
|
case isc::dhcp::Parser6Context::SHARED_NETWORK:
|
||||||
return isc::dhcp::Dhcp6Parser::make_SUBNET6(driver.loc_);
|
return isc::dhcp::Dhcp6Parser::make_SUBNET6(driver.loc_);
|
||||||
default:
|
default:
|
||||||
return isc::dhcp::Dhcp6Parser::make_STRING("subnet6", driver.loc_);
|
return isc::dhcp::Dhcp6Parser::make_STRING("subnet6", driver.loc_);
|
||||||
@ -670,7 +670,7 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence}
|
|||||||
|
|
||||||
\"shared-networks\" {
|
\"shared-networks\" {
|
||||||
switch (driver.ctx_) {
|
switch (driver.ctx_) {
|
||||||
case Parser6Context::DHCP6:
|
case isc::dhcp::Parser6Context::DHCP6:
|
||||||
return Dhcp6Parser::make_SHARED_NETWORKS(driver.loc_);
|
return Dhcp6Parser::make_SHARED_NETWORKS(driver.loc_);
|
||||||
default:
|
default:
|
||||||
return Dhcp6Parser::make_STRING("shared-networks", driver.loc_);
|
return Dhcp6Parser::make_STRING("shared-networks", driver.loc_);
|
||||||
@ -695,7 +695,7 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence}
|
|||||||
case isc::dhcp::Parser6Context::RESERVATIONS:
|
case isc::dhcp::Parser6Context::RESERVATIONS:
|
||||||
case isc::dhcp::Parser6Context::CLIENT_CLASSES:
|
case isc::dhcp::Parser6Context::CLIENT_CLASSES:
|
||||||
case isc::dhcp::Parser6Context::CLIENT_CLASS:
|
case isc::dhcp::Parser6Context::CLIENT_CLASS:
|
||||||
case Parser6Context::SHARED_NETWORK:
|
case isc::dhcp::Parser6Context::SHARED_NETWORK:
|
||||||
return isc::dhcp::Dhcp6Parser::make_OPTION_DATA(driver.loc_);
|
return isc::dhcp::Dhcp6Parser::make_OPTION_DATA(driver.loc_);
|
||||||
default:
|
default:
|
||||||
return isc::dhcp::Dhcp6Parser::make_STRING("option-data", driver.loc_);
|
return isc::dhcp::Dhcp6Parser::make_STRING("option-data", driver.loc_);
|
||||||
@ -711,7 +711,7 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence}
|
|||||||
case isc::dhcp::Parser6Context::CLIENT_CLASSES:
|
case isc::dhcp::Parser6Context::CLIENT_CLASSES:
|
||||||
case isc::dhcp::Parser6Context::CLIENT_CLASS:
|
case isc::dhcp::Parser6Context::CLIENT_CLASS:
|
||||||
case isc::dhcp::Parser6Context::LOGGERS:
|
case isc::dhcp::Parser6Context::LOGGERS:
|
||||||
case Parser6Context::SHARED_NETWORK:
|
case isc::dhcp::Parser6Context::SHARED_NETWORK:
|
||||||
return isc::dhcp::Dhcp6Parser::make_NAME(driver.loc_);
|
return isc::dhcp::Dhcp6Parser::make_NAME(driver.loc_);
|
||||||
default:
|
default:
|
||||||
return isc::dhcp::Dhcp6Parser::make_STRING("name", driver.loc_);
|
return isc::dhcp::Dhcp6Parser::make_STRING("name", driver.loc_);
|
||||||
@ -864,7 +864,7 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence}
|
|||||||
\"interface\" {
|
\"interface\" {
|
||||||
switch(driver.ctx_) {
|
switch(driver.ctx_) {
|
||||||
case isc::dhcp::Parser6Context::SUBNET6:
|
case isc::dhcp::Parser6Context::SUBNET6:
|
||||||
case Parser6Context::SHARED_NETWORK:
|
case isc::dhcp::Parser6Context::SHARED_NETWORK:
|
||||||
return isc::dhcp::Dhcp6Parser::make_INTERFACE(driver.loc_);
|
return isc::dhcp::Dhcp6Parser::make_INTERFACE(driver.loc_);
|
||||||
default:
|
default:
|
||||||
return isc::dhcp::Dhcp6Parser::make_STRING("interface", driver.loc_);
|
return isc::dhcp::Dhcp6Parser::make_STRING("interface", driver.loc_);
|
||||||
@ -874,7 +874,7 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence}
|
|||||||
\"interface-id\" {
|
\"interface-id\" {
|
||||||
switch(driver.ctx_) {
|
switch(driver.ctx_) {
|
||||||
case isc::dhcp::Parser6Context::SUBNET6:
|
case isc::dhcp::Parser6Context::SUBNET6:
|
||||||
case Parser6Context::SHARED_NETWORK:
|
case isc::dhcp::Parser6Context::SHARED_NETWORK:
|
||||||
return isc::dhcp::Dhcp6Parser::make_INTERFACE_ID(driver.loc_);
|
return isc::dhcp::Dhcp6Parser::make_INTERFACE_ID(driver.loc_);
|
||||||
default:
|
default:
|
||||||
return isc::dhcp::Dhcp6Parser::make_STRING("interface-id", driver.loc_);
|
return isc::dhcp::Dhcp6Parser::make_STRING("interface-id", driver.loc_);
|
||||||
@ -893,7 +893,7 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence}
|
|||||||
\"rapid-commit\" {
|
\"rapid-commit\" {
|
||||||
switch(driver.ctx_) {
|
switch(driver.ctx_) {
|
||||||
case isc::dhcp::Parser6Context::SUBNET6:
|
case isc::dhcp::Parser6Context::SUBNET6:
|
||||||
case Parser6Context::SHARED_NETWORK:
|
case isc::dhcp::Parser6Context::SHARED_NETWORK:
|
||||||
return isc::dhcp::Dhcp6Parser::make_RAPID_COMMIT(driver.loc_);
|
return isc::dhcp::Dhcp6Parser::make_RAPID_COMMIT(driver.loc_);
|
||||||
default:
|
default:
|
||||||
return isc::dhcp::Dhcp6Parser::make_STRING("rapid-commit", driver.loc_);
|
return isc::dhcp::Dhcp6Parser::make_STRING("rapid-commit", driver.loc_);
|
||||||
@ -903,7 +903,7 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence}
|
|||||||
\"reservation-mode\" {
|
\"reservation-mode\" {
|
||||||
switch(driver.ctx_) {
|
switch(driver.ctx_) {
|
||||||
case isc::dhcp::Parser6Context::SUBNET6:
|
case isc::dhcp::Parser6Context::SUBNET6:
|
||||||
case Parser6Context::SHARED_NETWORK:
|
case isc::dhcp::Parser6Context::SHARED_NETWORK:
|
||||||
return isc::dhcp::Dhcp6Parser::make_RESERVATION_MODE(driver.loc_);
|
return isc::dhcp::Dhcp6Parser::make_RESERVATION_MODE(driver.loc_);
|
||||||
default:
|
default:
|
||||||
return isc::dhcp::Dhcp6Parser::make_STRING("reservation-mode", driver.loc_);
|
return isc::dhcp::Dhcp6Parser::make_STRING("reservation-mode", driver.loc_);
|
||||||
@ -1078,8 +1078,10 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence}
|
|||||||
\"client-class\" {
|
\"client-class\" {
|
||||||
switch(driver.ctx_) {
|
switch(driver.ctx_) {
|
||||||
case isc::dhcp::Parser6Context::SUBNET6:
|
case isc::dhcp::Parser6Context::SUBNET6:
|
||||||
|
case isc::dhcp::Parser6Context::POOLS:
|
||||||
|
case isc::dhcp::Parser6Context::PD_POOLS:
|
||||||
case isc::dhcp::Parser6Context::CLIENT_CLASSES:
|
case isc::dhcp::Parser6Context::CLIENT_CLASSES:
|
||||||
case Parser6Context::SHARED_NETWORK:
|
case isc::dhcp::Parser6Context::SHARED_NETWORK:
|
||||||
return isc::dhcp::Dhcp6Parser::make_CLIENT_CLASS(driver.loc_);
|
return isc::dhcp::Dhcp6Parser::make_CLIENT_CLASS(driver.loc_);
|
||||||
default:
|
default:
|
||||||
return isc::dhcp::Dhcp6Parser::make_STRING("client-class", driver.loc_);
|
return isc::dhcp::Dhcp6Parser::make_STRING("client-class", driver.loc_);
|
||||||
@ -1212,7 +1214,7 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence}
|
|||||||
\"relay\" {
|
\"relay\" {
|
||||||
switch(driver.ctx_) {
|
switch(driver.ctx_) {
|
||||||
case isc::dhcp::Parser6Context::SUBNET6:
|
case isc::dhcp::Parser6Context::SUBNET6:
|
||||||
case Parser6Context::SHARED_NETWORK:
|
case isc::dhcp::Parser6Context::SHARED_NETWORK:
|
||||||
return isc::dhcp::Dhcp6Parser::make_RELAY(driver.loc_);
|
return isc::dhcp::Dhcp6Parser::make_RELAY(driver.loc_);
|
||||||
default:
|
default:
|
||||||
return isc::dhcp::Dhcp6Parser::make_STRING("relay", driver.loc_);
|
return isc::dhcp::Dhcp6Parser::make_STRING("relay", driver.loc_);
|
||||||
|
@ -60,7 +60,7 @@ The first argument specifies the client and transaction identification
|
|||||||
information. The second argument includes all classes to which the
|
information. The second argument includes all classes to which the
|
||||||
packet has been assigned.
|
packet has been assigned.
|
||||||
|
|
||||||
% DHCP6_CLASS_UNCONFIGURED %1: client packet belongs to an unconfigured class: %1
|
% DHCP6_CLASS_UNCONFIGURED %1: client packet belongs to an unconfigured class: %2
|
||||||
This debug message informs that incoming packet belongs to a class
|
This debug message informs that incoming packet belongs to a class
|
||||||
which cannot be found in the configuration. Either a hook written
|
which cannot be found in the configuration. Either a hook written
|
||||||
before the classification was added to Kea is used, or class naming is
|
before the classification was added to Kea is used, or class naming is
|
||||||
|
@ -1307,6 +1307,7 @@ pool_params: pool_param
|
|||||||
|
|
||||||
pool_param: pool_entry
|
pool_param: pool_entry
|
||||||
| option_data_list
|
| option_data_list
|
||||||
|
| client_class
|
||||||
| user_context
|
| user_context
|
||||||
| comment
|
| comment
|
||||||
| unknown_map_entry
|
| unknown_map_entry
|
||||||
@ -1427,6 +1428,7 @@ pd_pool_param: pd_prefix
|
|||||||
| pd_prefix_len
|
| pd_prefix_len
|
||||||
| pd_delegated_len
|
| pd_delegated_len
|
||||||
| option_data_list
|
| option_data_list
|
||||||
|
| client_class
|
||||||
| excluded_prefix
|
| excluded_prefix
|
||||||
| excluded_prefix_len
|
| excluded_prefix_len
|
||||||
| user_context
|
| user_context
|
||||||
@ -1622,11 +1624,11 @@ client_classes: CLIENT_CLASSES {
|
|||||||
ctx.leave();
|
ctx.leave();
|
||||||
};
|
};
|
||||||
|
|
||||||
client_classes_list: client_class
|
client_classes_list: client_class_entry
|
||||||
| client_classes_list COMMA client_class
|
| client_classes_list COMMA client_class_entry
|
||||||
;
|
;
|
||||||
|
|
||||||
client_class: LCURLY_BRACKET {
|
client_class_entry: LCURLY_BRACKET {
|
||||||
ElementPtr m(new MapElement(ctx.loc2pos(@1)));
|
ElementPtr m(new MapElement(ctx.loc2pos(@1)));
|
||||||
ctx.stack_.back()->add(m);
|
ctx.stack_.back()->add(m);
|
||||||
ctx.stack_.push_back(m);
|
ctx.stack_.push_back(m);
|
||||||
|
@ -643,6 +643,98 @@ TEST_F(ClassifyTest, clientClassifySubnet) {
|
|||||||
EXPECT_TRUE(srv_.selectSubnet(sol));
|
EXPECT_TRUE(srv_.selectSubnet(sol));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Checks if the client-class field is indeed used for pool selection.
|
||||||
|
TEST_F(ClassifyTest, clientClassifyPool) {
|
||||||
|
IfaceMgrTestConfig test_config(true);
|
||||||
|
|
||||||
|
NakedDhcpv6Srv srv(0);
|
||||||
|
|
||||||
|
// This test configures 2 pools.
|
||||||
|
// The second pool does not play any role here. The client's
|
||||||
|
// IP address belongs to the first pool, so only that first
|
||||||
|
// pool is being tested.
|
||||||
|
std::string config = "{ \"interfaces-config\": {"
|
||||||
|
" \"interfaces\": [ \"*\" ]"
|
||||||
|
"},"
|
||||||
|
"\"preferred-lifetime\": 3000,"
|
||||||
|
"\"rebind-timer\": 2000, "
|
||||||
|
"\"renew-timer\": 1000, "
|
||||||
|
"\"client-classes\": [ "
|
||||||
|
" { "
|
||||||
|
" \"name\": \"foo\" "
|
||||||
|
" }, "
|
||||||
|
" { "
|
||||||
|
" \"name\": \"bar\" "
|
||||||
|
" } "
|
||||||
|
"], "
|
||||||
|
"\"subnet6\": [ "
|
||||||
|
" { \"pools\": [ "
|
||||||
|
" { "
|
||||||
|
" \"pool\": \"2001:db8:1::/64\", "
|
||||||
|
" \"client-class\": \"foo\" "
|
||||||
|
" }, "
|
||||||
|
" { "
|
||||||
|
" \"pool\": \"2001:db8:2::/64\", "
|
||||||
|
" \"client-class\": \"xyzzy\" "
|
||||||
|
" } "
|
||||||
|
" ], "
|
||||||
|
" \"subnet\": \"2001:db8:2::/40\" "
|
||||||
|
" } "
|
||||||
|
"], "
|
||||||
|
"\"valid-lifetime\": 4000 }";
|
||||||
|
|
||||||
|
ASSERT_NO_THROW(configure(config));
|
||||||
|
|
||||||
|
OptionPtr clientid = generateClientId();
|
||||||
|
Pkt6Ptr query1 = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
|
||||||
|
query1->setRemoteAddr(IOAddress("2001:db8:1::3"));
|
||||||
|
query1->addOption(generateIA(D6O_IA_NA, 234, 1500, 3000));
|
||||||
|
query1->addOption(clientid);
|
||||||
|
query1->setIface("eth1");
|
||||||
|
Pkt6Ptr query2 = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
|
||||||
|
query2->setRemoteAddr(IOAddress("2001:db8:1::3"));
|
||||||
|
query2->addOption(generateIA(D6O_IA_NA, 234, 1500, 3000));
|
||||||
|
query2->addOption(clientid);
|
||||||
|
query2->setIface("eth1");
|
||||||
|
Pkt6Ptr query3 = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
|
||||||
|
query3->setRemoteAddr(IOAddress("2001:db8:1::3"));
|
||||||
|
query3->addOption(generateIA(D6O_IA_NA, 234, 1500, 3000));
|
||||||
|
query3->addOption(clientid);
|
||||||
|
query3->setIface("eth1");
|
||||||
|
|
||||||
|
// This discover does not belong to foo class, so it will not
|
||||||
|
// be serviced
|
||||||
|
srv.classifyPacket(query1);
|
||||||
|
Pkt6Ptr response1 = srv.processSolicit(query1);
|
||||||
|
ASSERT_TRUE(response1);
|
||||||
|
OptionPtr ia_na1 = response1->getOption(D6O_IA_NA);
|
||||||
|
ASSERT_TRUE(ia_na1);
|
||||||
|
EXPECT_TRUE(ia_na1->getOption(D6O_STATUS_CODE));
|
||||||
|
EXPECT_FALSE(ia_na1->getOption(D6O_IAADDR));
|
||||||
|
|
||||||
|
// Let's add the packet to bar class and try again.
|
||||||
|
query2->addClass("bar");
|
||||||
|
// Still not supported, because it belongs to wrong class.
|
||||||
|
srv.classifyPacket(query2);
|
||||||
|
Pkt6Ptr response2 = srv.processSolicit(query2);
|
||||||
|
ASSERT_TRUE(response2);
|
||||||
|
OptionPtr ia_na2 = response2->getOption(D6O_IA_NA);
|
||||||
|
ASSERT_TRUE(ia_na2);
|
||||||
|
EXPECT_TRUE(ia_na2->getOption(D6O_STATUS_CODE));
|
||||||
|
EXPECT_FALSE(ia_na2->getOption(D6O_IAADDR));
|
||||||
|
|
||||||
|
// Let's add it to matching class.
|
||||||
|
query3->addClass("foo");
|
||||||
|
// This time it should work
|
||||||
|
srv.classifyPacket(query3);
|
||||||
|
Pkt6Ptr response3 = srv.processSolicit(query3);
|
||||||
|
ASSERT_TRUE(response3);
|
||||||
|
OptionPtr ia_na3 = response3->getOption(D6O_IA_NA);
|
||||||
|
ASSERT_TRUE(ia_na3);
|
||||||
|
EXPECT_FALSE(ia_na3->getOption(D6O_STATUS_CODE));
|
||||||
|
EXPECT_TRUE(ia_na3->getOption(D6O_IAADDR));
|
||||||
|
}
|
||||||
|
|
||||||
// Tests whether a packet with custom vendor-class (not erouter or docsis)
|
// Tests whether a packet with custom vendor-class (not erouter or docsis)
|
||||||
// is classified properly.
|
// is classified properly.
|
||||||
TEST_F(ClassifyTest, vendorClientClassification2) {
|
TEST_F(ClassifyTest, vendorClientClassification2) {
|
||||||
|
@ -4211,6 +4211,194 @@ TEST_F(Dhcp6ParserTest, classifySubnets) {
|
|||||||
EXPECT_TRUE (subnets->at(3)->clientSupported(classes));
|
EXPECT_TRUE (subnets->at(3)->clientSupported(classes));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Goal of this test is to verify that multiple pools can be configured
|
||||||
|
// with defined client classes.
|
||||||
|
TEST_F(Dhcp6ParserTest, classifyPools) {
|
||||||
|
ConstElementPtr x;
|
||||||
|
string config = "{ " + genIfaceConfig() + ","
|
||||||
|
"\"preferred-lifetime\": 3000,"
|
||||||
|
"\"rebind-timer\": 2000, "
|
||||||
|
"\"renew-timer\": 1000, "
|
||||||
|
"\"subnet6\": [ { "
|
||||||
|
" \"pools\": [ { "
|
||||||
|
" \"pool\": \"2001:db8:1::/80\", "
|
||||||
|
" \"client-class\": \"alpha\" "
|
||||||
|
" },"
|
||||||
|
" {"
|
||||||
|
" \"pool\": \"2001:db8:2::/80\", "
|
||||||
|
" \"client-class\": \"beta\" "
|
||||||
|
" },"
|
||||||
|
" {"
|
||||||
|
" \"pool\": \"2001:db8:3::/80\", "
|
||||||
|
" \"client-class\": \"gamma\" "
|
||||||
|
" },"
|
||||||
|
" {"
|
||||||
|
" \"pool\": \"2001:db8:4::/80\" "
|
||||||
|
" } ],"
|
||||||
|
" \"subnet\": \"2001:db8:0::/40\" "
|
||||||
|
" } ],"
|
||||||
|
"\"valid-lifetime\": 4000 }";
|
||||||
|
|
||||||
|
ConstElementPtr json;
|
||||||
|
ASSERT_NO_THROW(json = parseDHCP6(config, true));
|
||||||
|
extractConfig(config);
|
||||||
|
|
||||||
|
EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
|
||||||
|
checkResult(x, 0);
|
||||||
|
|
||||||
|
const Subnet6Collection* subnets =
|
||||||
|
CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->getAll();
|
||||||
|
ASSERT_TRUE(subnets);
|
||||||
|
ASSERT_EQ(1, subnets->size());
|
||||||
|
const PoolCollection& pools = subnets->at(0)->getPools(Lease::TYPE_NA);
|
||||||
|
ASSERT_EQ(4, pools.size()); // We expect 4 pools
|
||||||
|
|
||||||
|
// Let's check if client belonging to alpha class is supported in pool[0]
|
||||||
|
// and not supported in any other pool (except pool[3], which allows
|
||||||
|
// everyone).
|
||||||
|
ClientClasses classes;
|
||||||
|
classes.insert("alpha");
|
||||||
|
EXPECT_TRUE (pools.at(0)->clientSupported(classes));
|
||||||
|
EXPECT_FALSE(pools.at(1)->clientSupported(classes));
|
||||||
|
EXPECT_FALSE(pools.at(2)->clientSupported(classes));
|
||||||
|
EXPECT_TRUE (pools.at(3)->clientSupported(classes));
|
||||||
|
|
||||||
|
// Let's check if client belonging to beta class is supported in pool[1]
|
||||||
|
// and not supported in any other pool (except pool[3], which allows
|
||||||
|
// everyone).
|
||||||
|
classes.clear();
|
||||||
|
classes.insert("beta");
|
||||||
|
EXPECT_FALSE(pools.at(0)->clientSupported(classes));
|
||||||
|
EXPECT_TRUE (pools.at(1)->clientSupported(classes));
|
||||||
|
EXPECT_FALSE(pools.at(2)->clientSupported(classes));
|
||||||
|
EXPECT_TRUE (pools.at(3)->clientSupported(classes));
|
||||||
|
|
||||||
|
// Let's check if client belonging to gamma class is supported in pool[2]
|
||||||
|
// and not supported in any other pool (except pool[3], which allows
|
||||||
|
// everyone).
|
||||||
|
classes.clear();
|
||||||
|
classes.insert("gamma");
|
||||||
|
EXPECT_FALSE(pools.at(0)->clientSupported(classes));
|
||||||
|
EXPECT_FALSE(pools.at(1)->clientSupported(classes));
|
||||||
|
EXPECT_TRUE (pools.at(2)->clientSupported(classes));
|
||||||
|
EXPECT_TRUE (pools.at(3)->clientSupported(classes));
|
||||||
|
|
||||||
|
// Let's check if client belonging to some other class (not mentioned in
|
||||||
|
// the config) is supported only in pool[3], which allows everyone.
|
||||||
|
classes.clear();
|
||||||
|
classes.insert("delta");
|
||||||
|
EXPECT_FALSE(pools.at(0)->clientSupported(classes));
|
||||||
|
EXPECT_FALSE(pools.at(1)->clientSupported(classes));
|
||||||
|
EXPECT_FALSE(pools.at(2)->clientSupported(classes));
|
||||||
|
EXPECT_TRUE (pools.at(3)->clientSupported(classes));
|
||||||
|
|
||||||
|
// Finally, let's check class-less client. He should be allowed only in
|
||||||
|
// the last pool, which does not have any class restrictions.
|
||||||
|
classes.clear();
|
||||||
|
EXPECT_FALSE(pools.at(0)->clientSupported(classes));
|
||||||
|
EXPECT_FALSE(pools.at(1)->clientSupported(classes));
|
||||||
|
EXPECT_FALSE(pools.at(2)->clientSupported(classes));
|
||||||
|
EXPECT_TRUE (pools.at(3)->clientSupported(classes));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Goal of this test is to verify that multiple pdpools can be configured
|
||||||
|
// with defined client classes.
|
||||||
|
TEST_F(Dhcp6ParserTest, classifyPdPools) {
|
||||||
|
ConstElementPtr x;
|
||||||
|
string config = "{ " + genIfaceConfig() + ","
|
||||||
|
"\"preferred-lifetime\": 3000,"
|
||||||
|
"\"rebind-timer\": 2000, "
|
||||||
|
"\"renew-timer\": 1000, "
|
||||||
|
"\"subnet6\": [ { "
|
||||||
|
" \"pd-pools\": [ { "
|
||||||
|
" \"prefix-len\": 48, "
|
||||||
|
" \"delegated-len\": 64, "
|
||||||
|
" \"prefix\": \"2001:db8:1::\", "
|
||||||
|
" \"client-class\": \"alpha\" "
|
||||||
|
" },"
|
||||||
|
" {"
|
||||||
|
" \"prefix-len\": 48, "
|
||||||
|
" \"delegated-len\": 64, "
|
||||||
|
" \"prefix\": \"2001:db8:2::\", "
|
||||||
|
" \"client-class\": \"beta\" "
|
||||||
|
" },"
|
||||||
|
" {"
|
||||||
|
" \"prefix-len\": 48, "
|
||||||
|
" \"delegated-len\": 64, "
|
||||||
|
" \"prefix\": \"2001:db8:3::\", "
|
||||||
|
" \"client-class\": \"gamma\" "
|
||||||
|
" },"
|
||||||
|
" {"
|
||||||
|
" \"prefix-len\": 48, "
|
||||||
|
" \"delegated-len\": 64, "
|
||||||
|
" \"prefix\": \"2001:db8:4::\" "
|
||||||
|
" } ],"
|
||||||
|
" \"subnet\": \"2001:db8::/64\" "
|
||||||
|
" } ],"
|
||||||
|
"\"valid-lifetime\": 4000 }";
|
||||||
|
|
||||||
|
ConstElementPtr json;
|
||||||
|
ASSERT_NO_THROW(json = parseDHCP6(config, true));
|
||||||
|
extractConfig(config);
|
||||||
|
|
||||||
|
EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
|
||||||
|
checkResult(x, 0);
|
||||||
|
|
||||||
|
const Subnet6Collection* subnets =
|
||||||
|
CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->getAll();
|
||||||
|
ASSERT_TRUE(subnets);
|
||||||
|
ASSERT_EQ(1, subnets->size());
|
||||||
|
const PoolCollection& pools = subnets->at(0)->getPools(Lease::TYPE_PD);
|
||||||
|
ASSERT_EQ(4, pools.size()); // We expect 4 pools
|
||||||
|
|
||||||
|
// Let's check if client belonging to alpha class is supported in pool[0]
|
||||||
|
// and not supported in any other pool (except pool[3], which allows
|
||||||
|
// everyone).
|
||||||
|
ClientClasses classes;
|
||||||
|
classes.insert("alpha");
|
||||||
|
EXPECT_TRUE (pools.at(0)->clientSupported(classes));
|
||||||
|
EXPECT_FALSE(pools.at(1)->clientSupported(classes));
|
||||||
|
EXPECT_FALSE(pools.at(2)->clientSupported(classes));
|
||||||
|
EXPECT_TRUE (pools.at(3)->clientSupported(classes));
|
||||||
|
|
||||||
|
// Let's check if client belonging to beta class is supported in pool[1]
|
||||||
|
// and not supported in any other pool (except pool[3], which allows
|
||||||
|
// everyone).
|
||||||
|
classes.clear();
|
||||||
|
classes.insert("beta");
|
||||||
|
EXPECT_FALSE(pools.at(0)->clientSupported(classes));
|
||||||
|
EXPECT_TRUE (pools.at(1)->clientSupported(classes));
|
||||||
|
EXPECT_FALSE(pools.at(2)->clientSupported(classes));
|
||||||
|
EXPECT_TRUE (pools.at(3)->clientSupported(classes));
|
||||||
|
|
||||||
|
// Let's check if client belonging to gamma class is supported in pool[2]
|
||||||
|
// and not supported in any other pool (except pool[3], which allows
|
||||||
|
// everyone).
|
||||||
|
classes.clear();
|
||||||
|
classes.insert("gamma");
|
||||||
|
EXPECT_FALSE(pools.at(0)->clientSupported(classes));
|
||||||
|
EXPECT_FALSE(pools.at(1)->clientSupported(classes));
|
||||||
|
EXPECT_TRUE (pools.at(2)->clientSupported(classes));
|
||||||
|
EXPECT_TRUE (pools.at(3)->clientSupported(classes));
|
||||||
|
|
||||||
|
// Let's check if client belonging to some other class (not mentioned in
|
||||||
|
// the config) is supported only in pool[3], which allows everyone.
|
||||||
|
classes.clear();
|
||||||
|
classes.insert("delta");
|
||||||
|
EXPECT_FALSE(pools.at(0)->clientSupported(classes));
|
||||||
|
EXPECT_FALSE(pools.at(1)->clientSupported(classes));
|
||||||
|
EXPECT_FALSE(pools.at(2)->clientSupported(classes));
|
||||||
|
EXPECT_TRUE (pools.at(3)->clientSupported(classes));
|
||||||
|
|
||||||
|
// Finally, let's check class-less client. He should be allowed only in
|
||||||
|
// the last pool, which does not have any class restrictions.
|
||||||
|
classes.clear();
|
||||||
|
EXPECT_FALSE(pools.at(0)->clientSupported(classes));
|
||||||
|
EXPECT_FALSE(pools.at(1)->clientSupported(classes));
|
||||||
|
EXPECT_FALSE(pools.at(2)->clientSupported(classes));
|
||||||
|
EXPECT_TRUE (pools.at(3)->clientSupported(classes));
|
||||||
|
}
|
||||||
|
|
||||||
// This test checks the ability of the server to parse a configuration
|
// This test checks the ability of the server to parse a configuration
|
||||||
// containing a full, valid dhcp-ddns (D2ClientConfig) entry.
|
// containing a full, valid dhcp-ddns (D2ClientConfig) entry.
|
||||||
TEST_F(Dhcp6ParserTest, d2ClientConfig) {
|
TEST_F(Dhcp6ParserTest, d2ClientConfig) {
|
||||||
|
@ -957,7 +957,151 @@ const char* NETWORKS_CONFIG[] = {
|
|||||||
" ]"
|
" ]"
|
||||||
" }"
|
" }"
|
||||||
" ]"
|
" ]"
|
||||||
|
"}",
|
||||||
|
|
||||||
|
// Configuration #19.
|
||||||
|
// - one shared network with one subnet and two pools (the first has
|
||||||
|
// class restrictions)
|
||||||
|
"{"
|
||||||
|
" \"client-classes\": ["
|
||||||
|
" {"
|
||||||
|
" \"name\": \"a-devices\","
|
||||||
|
" \"test\": \"option[1234].hex == 0x0001\""
|
||||||
|
" },"
|
||||||
|
" {"
|
||||||
|
" \"name\": \"b-devices\","
|
||||||
|
" \"test\": \"option[1234].hex == 0x0002\""
|
||||||
|
" }"
|
||||||
|
" ],"
|
||||||
|
" \"shared-networks\": ["
|
||||||
|
" {"
|
||||||
|
" \"name\": \"frog\","
|
||||||
|
" \"interface\": \"eth1\","
|
||||||
|
" \"subnet6\": ["
|
||||||
|
" {"
|
||||||
|
" \"subnet\": \"2001:db8:1::/64\","
|
||||||
|
" \"id\": 10,"
|
||||||
|
" \"pools\": ["
|
||||||
|
" {"
|
||||||
|
" \"pool\": \"2001:db8:1::20 - 2001:db8:1::20\","
|
||||||
|
" \"client-class\": \"a-devices\""
|
||||||
|
" },"
|
||||||
|
" {"
|
||||||
|
" \"pool\": \"2001:db8:1::50 - 2001:db8:1::50\""
|
||||||
|
" }"
|
||||||
|
" ]"
|
||||||
|
" }"
|
||||||
|
" ]"
|
||||||
|
" }"
|
||||||
|
" ]"
|
||||||
|
"}",
|
||||||
|
|
||||||
|
// Configuration #20.
|
||||||
|
// - one shared network with one subnet and two pools (each with class
|
||||||
|
// restriction)
|
||||||
|
"{"
|
||||||
|
" \"client-classes\": ["
|
||||||
|
" {"
|
||||||
|
" \"name\": \"a-devices\","
|
||||||
|
" \"test\": \"option[1234].hex == 0x0001\""
|
||||||
|
" },"
|
||||||
|
" {"
|
||||||
|
" \"name\": \"b-devices\","
|
||||||
|
" \"test\": \"option[1234].hex == 0x0002\""
|
||||||
|
" }"
|
||||||
|
" ],"
|
||||||
|
" \"shared-networks\": ["
|
||||||
|
" {"
|
||||||
|
" \"name\": \"frog\","
|
||||||
|
" \"interface\": \"eth1\","
|
||||||
|
" \"subnet6\": ["
|
||||||
|
" {"
|
||||||
|
" \"subnet\": \"2001:db8:1::/64\","
|
||||||
|
" \"id\": 10,"
|
||||||
|
" \"pools\": ["
|
||||||
|
" {"
|
||||||
|
" \"pool\": \"2001:db8:1::20 - 2001:db8:1::20\","
|
||||||
|
" \"client-class\": \"a-devices\""
|
||||||
|
" },"
|
||||||
|
" {"
|
||||||
|
" \"pool\": \"2001:db8:1::50 - 2001:db8:1::50\","
|
||||||
|
" \"client-class\": \"b-devices\""
|
||||||
|
" }"
|
||||||
|
" ]"
|
||||||
|
" }"
|
||||||
|
" ]"
|
||||||
|
" }"
|
||||||
|
" ]"
|
||||||
|
"}",
|
||||||
|
|
||||||
|
// Configuration #21.
|
||||||
|
// - one plain subnet with two pools (the first has class restrictions)
|
||||||
|
"{"
|
||||||
|
" \"client-classes\": ["
|
||||||
|
" {"
|
||||||
|
" \"name\": \"a-devices\","
|
||||||
|
" \"test\": \"option[1234].hex == 0x0001\""
|
||||||
|
" },"
|
||||||
|
" {"
|
||||||
|
" \"name\": \"b-devices\","
|
||||||
|
" \"test\": \"option[1234].hex == 0x0002\""
|
||||||
|
" }"
|
||||||
|
" ],"
|
||||||
|
" \"subnet6\": ["
|
||||||
|
" {"
|
||||||
|
" \"subnet\": \"2001:db8:1::/64\","
|
||||||
|
" \"id\": 10,"
|
||||||
|
" \"interface\": \"eth1\","
|
||||||
|
" \"pools\": ["
|
||||||
|
" {"
|
||||||
|
" \"pool\": \"2001:db8:1::20 - 2001:db8:1::20\","
|
||||||
|
" \"client-class\": \"a-devices\""
|
||||||
|
" },"
|
||||||
|
" {"
|
||||||
|
" \"pool\": \"2001:db8:1::50 - 2001:db8:1::50\""
|
||||||
|
" }"
|
||||||
|
" ]"
|
||||||
|
" }"
|
||||||
|
" ]"
|
||||||
|
"}",
|
||||||
|
|
||||||
|
// Configuration #22.
|
||||||
|
// - one plain subnet with two pools (each with class restriction)
|
||||||
|
"{"
|
||||||
|
" \"client-classes\": ["
|
||||||
|
" {"
|
||||||
|
" \"name\": \"a-devices\","
|
||||||
|
" \"test\": \"option[1234].hex == 0x0001\""
|
||||||
|
" },"
|
||||||
|
" {"
|
||||||
|
" \"name\": \"b-devices\","
|
||||||
|
" \"test\": \"option[1234].hex == 0x0002\""
|
||||||
|
" }"
|
||||||
|
" ],"
|
||||||
|
" \"shared-networks\": ["
|
||||||
|
" {"
|
||||||
|
" \"name\": \"frog\","
|
||||||
|
" \"interface\": \"eth1\","
|
||||||
|
" \"subnet6\": ["
|
||||||
|
" {"
|
||||||
|
" \"subnet\": \"2001:db8:1::/64\","
|
||||||
|
" \"id\": 10,"
|
||||||
|
" \"pools\": ["
|
||||||
|
" {"
|
||||||
|
" \"pool\": \"2001:db8:1::20 - 2001:db8:1::20\","
|
||||||
|
" \"client-class\": \"a-devices\""
|
||||||
|
" },"
|
||||||
|
" {"
|
||||||
|
" \"pool\": \"2001:db8:1::50 - 2001:db8:1::50\","
|
||||||
|
" \"client-class\": \"b-devices\""
|
||||||
|
" }"
|
||||||
|
" ]"
|
||||||
|
" }"
|
||||||
|
" ]"
|
||||||
|
" }"
|
||||||
|
" ]"
|
||||||
"}"
|
"}"
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// @Brief Test fixture class for DHCPv6 server using shared networks.
|
/// @Brief Test fixture class for DHCPv6 server using shared networks.
|
||||||
@ -2191,4 +2335,131 @@ TEST_F(Dhcpv6SharedNetworkTest, sharedNetworkRapidCommit3) {
|
|||||||
testRapidCommit(NETWORKS_CONFIG[1], false, "", "");
|
testRapidCommit(NETWORKS_CONFIG[1], false, "", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pool is selected based on the client class specified.
|
||||||
|
TEST_F(Dhcpv6SharedNetworkTest, poolInSharedNetworkSelectedByClass) {
|
||||||
|
// Create client #1.
|
||||||
|
Dhcp6Client client1;
|
||||||
|
client1.setInterface("eth1");
|
||||||
|
|
||||||
|
// Configure the server with one shared network including one subnet and
|
||||||
|
// two pools. The access to one of the pools is restricted by
|
||||||
|
// by client classification.
|
||||||
|
ASSERT_NO_FATAL_FAILURE(configure(NETWORKS_CONFIG[19], *client1.getServer()));
|
||||||
|
|
||||||
|
// Client #1 requests an address in the restricted pool but can't be assigned
|
||||||
|
// this address because the client doesn't belong to a certain class.
|
||||||
|
ASSERT_NO_THROW(client1.requestAddress(0xabca, IOAddress("2001:db8:1::20")));
|
||||||
|
testAssigned([this, &client1] {
|
||||||
|
ASSERT_NO_THROW(client1.doSARR());
|
||||||
|
});
|
||||||
|
ASSERT_TRUE(hasLeaseForAddress(client1, IOAddress("2001:db8:1::50")));
|
||||||
|
|
||||||
|
// Release the lease that the client has got, because we'll need this address
|
||||||
|
// further in the test.
|
||||||
|
testAssigned([this, &client1] {
|
||||||
|
ASSERT_NO_THROW(client1.doRelease());
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add option 1234 which would cause the client to be classified as "a-devices".
|
||||||
|
OptionPtr option1234(new OptionUint16(Option::V6, 1234, 0x0001));
|
||||||
|
client1.addExtraOption(option1234);
|
||||||
|
|
||||||
|
// This time, the allocation of the address provided as hint should be successful.
|
||||||
|
testAssigned([this, &client1] {
|
||||||
|
ASSERT_NO_THROW(client1.doSARR());
|
||||||
|
});
|
||||||
|
ASSERT_TRUE(hasLeaseForAddress(client1, IOAddress("2001:db8:1::20")));
|
||||||
|
|
||||||
|
// Client 2 should be assigned an address from the unrestricted pool.
|
||||||
|
Dhcp6Client client2(client1.getServer());
|
||||||
|
client2.setInterface("eth1");
|
||||||
|
ASSERT_NO_THROW(client2.requestAddress(0xabca0));
|
||||||
|
testAssigned([this, &client2] {
|
||||||
|
ASSERT_NO_THROW(client2.doSARR());
|
||||||
|
});
|
||||||
|
ASSERT_TRUE(hasLeaseForAddress(client2, IOAddress("2001:db8:1::50")));
|
||||||
|
|
||||||
|
// Now, let's reconfigure the server to also apply restrictions on the
|
||||||
|
// pool to which client2 now belongs.
|
||||||
|
ASSERT_NO_FATAL_FAILURE(configure(NETWORKS_CONFIG[20], *client1.getServer()));
|
||||||
|
|
||||||
|
testAssigned([this, &client2] {
|
||||||
|
ASSERT_NO_THROW(client2.doRenew());
|
||||||
|
});
|
||||||
|
EXPECT_EQ(0, client2.getLeasesWithNonZeroLifetime().size());
|
||||||
|
|
||||||
|
// If we add option 1234 with a value matching this class, the lease should
|
||||||
|
// get renewed.
|
||||||
|
OptionPtr option1234_bis(new OptionUint16(Option::V6, 1234, 0x0002));
|
||||||
|
client2.addExtraOption(option1234_bis);
|
||||||
|
testAssigned([this, &client2] {
|
||||||
|
ASSERT_NO_THROW(client2.doRenew());
|
||||||
|
});
|
||||||
|
EXPECT_EQ(1, client2.getLeaseNum());
|
||||||
|
EXPECT_EQ(1, client2.getLeasesWithNonZeroLifetime().size());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pool is selected based on the client class specified using a plain subnet.
|
||||||
|
TEST_F(Dhcpv6SharedNetworkTest, poolInSubnetSelectedByClass) {
|
||||||
|
// Create client #1.
|
||||||
|
Dhcp6Client client1;
|
||||||
|
client1.setInterface("eth1");
|
||||||
|
|
||||||
|
// Configure the server with one plain subnet including two pools.
|
||||||
|
// The access to one of the pools is restricted by client classification.
|
||||||
|
ASSERT_NO_FATAL_FAILURE(configure(NETWORKS_CONFIG[21], *client1.getServer()));
|
||||||
|
|
||||||
|
// Client #1 requests an address in the restricted pool but can't be assigned
|
||||||
|
// this address because the client doesn't belong to a certain class.
|
||||||
|
ASSERT_NO_THROW(client1.requestAddress(0xabca, IOAddress("2001:db8:1::20")));
|
||||||
|
testAssigned([this, &client1] {
|
||||||
|
ASSERT_NO_THROW(client1.doSARR());
|
||||||
|
});
|
||||||
|
ASSERT_TRUE(hasLeaseForAddress(client1, IOAddress("2001:db8:1::50")));
|
||||||
|
|
||||||
|
// Release the lease that the client has got, because we'll need this address
|
||||||
|
// further in the test.
|
||||||
|
testAssigned([this, &client1] {
|
||||||
|
ASSERT_NO_THROW(client1.doRelease());
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add option 1234 which would cause the client to be classified as "a-devices".
|
||||||
|
OptionPtr option1234(new OptionUint16(Option::V6, 1234, 0x0001));
|
||||||
|
client1.addExtraOption(option1234);
|
||||||
|
|
||||||
|
// This time, the allocation of the address provided as hint should be successful.
|
||||||
|
testAssigned([this, &client1] {
|
||||||
|
ASSERT_NO_THROW(client1.doSARR());
|
||||||
|
});
|
||||||
|
ASSERT_TRUE(hasLeaseForAddress(client1, IOAddress("2001:db8:1::20")));
|
||||||
|
|
||||||
|
// Client 2 should be assigned an address from the unrestricted pool.
|
||||||
|
Dhcp6Client client2(client1.getServer());
|
||||||
|
client2.setInterface("eth1");
|
||||||
|
ASSERT_NO_THROW(client2.requestAddress(0xabca0));
|
||||||
|
testAssigned([this, &client2] {
|
||||||
|
ASSERT_NO_THROW(client2.doSARR());
|
||||||
|
});
|
||||||
|
ASSERT_TRUE(hasLeaseForAddress(client2, IOAddress("2001:db8:1::50")));
|
||||||
|
|
||||||
|
// Now, let's reconfigure the server to also apply restrictions on the
|
||||||
|
// pool to which client2 now belongs.
|
||||||
|
ASSERT_NO_FATAL_FAILURE(configure(NETWORKS_CONFIG[22], *client1.getServer()));
|
||||||
|
|
||||||
|
testAssigned([this, &client2] {
|
||||||
|
ASSERT_NO_THROW(client2.doRenew());
|
||||||
|
});
|
||||||
|
EXPECT_EQ(0, client2.getLeasesWithNonZeroLifetime().size());
|
||||||
|
|
||||||
|
// If we add option 1234 with a value matching this class, the lease should
|
||||||
|
// get renewed.
|
||||||
|
OptionPtr option1234_bis(new OptionUint16(Option::V6, 1234, 0x0002));
|
||||||
|
client2.addExtraOption(option1234_bis);
|
||||||
|
testAssigned([this, &client2] {
|
||||||
|
ASSERT_NO_THROW(client2.doRenew());
|
||||||
|
});
|
||||||
|
EXPECT_EQ(1, client2.getLeaseNum());
|
||||||
|
EXPECT_EQ(1, client2.getLeasesWithNonZeroLifetime().size());
|
||||||
|
}
|
||||||
|
|
||||||
} // end of anonymous namespace
|
} // end of anonymous namespace
|
||||||
|
Loading…
x
Reference in New Issue
Block a user