mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-30 21:45:37 +00:00
[master] Merged trac4057 (DHCPv4 Relay Agent Link Selection Option)
This commit is contained in:
@@ -292,6 +292,29 @@ Dhcpv4Srv::selectSubnet(const Pkt4Ptr& query) const {
|
|||||||
selector.client_classes_ = query->classes_;
|
selector.client_classes_ = query->classes_;
|
||||||
selector.iface_name_ = query->getIface();
|
selector.iface_name_ = query->getIface();
|
||||||
|
|
||||||
|
// If the link-selection sub-option is present, extract its value.
|
||||||
|
// "The link-selection sub-option is used by any DHCP relay agent
|
||||||
|
// that desires to specify a subnet/link for a DHCP client request
|
||||||
|
// that it is relaying but needs the subnet/link specification to
|
||||||
|
// be different from the IP address the DHCP server should use
|
||||||
|
// when communicating with the relay agent." (RFC 3257)
|
||||||
|
OptionPtr rai = query->getOption(DHO_DHCP_AGENT_OPTIONS);
|
||||||
|
if (rai) {
|
||||||
|
OptionCustomPtr rai_custom =
|
||||||
|
boost::dynamic_pointer_cast<OptionCustom>(rai);
|
||||||
|
if (rai_custom) {
|
||||||
|
OptionPtr link_select =
|
||||||
|
rai_custom->getOption(RAI_OPTION_LINK_SELECTION);
|
||||||
|
if (link_select) {
|
||||||
|
OptionBuffer link_select_buf = link_select->getData();
|
||||||
|
if (link_select_buf.size() == sizeof(uint32_t)) {
|
||||||
|
selector.option_select_ =
|
||||||
|
IOAddress::fromBytes(AF_INET, &link_select_buf[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
CfgMgr& cfgmgr = CfgMgr::instance();
|
CfgMgr& cfgmgr = CfgMgr::instance();
|
||||||
subnet = cfgmgr.getCurrentCfg()->getCfgSubnets4()->selectSubnet(selector);
|
subnet = cfgmgr.getCurrentCfg()->getCfgSubnets4()->selectSubnet(selector);
|
||||||
|
|
||||||
|
@@ -3236,6 +3236,106 @@ TEST_F(Dhcpv4SrvTest, relayOverrideAndClientClass) {
|
|||||||
EXPECT_TRUE(subnet1 == srv_.selectSubnet(dis));
|
EXPECT_TRUE(subnet1 == srv_.selectSubnet(dis));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Checks if a RAI link selection sub-option works as expected
|
||||||
|
TEST_F(Dhcpv4SrvTest, relayLinkSelect) {
|
||||||
|
|
||||||
|
// We have 3 subnets defined.
|
||||||
|
string config = "{ \"interfaces-config\": {"
|
||||||
|
" \"interfaces\": [ \"*\" ]"
|
||||||
|
"},"
|
||||||
|
"\"rebind-timer\": 2000, "
|
||||||
|
"\"renew-timer\": 1000, "
|
||||||
|
"\"subnet4\": [ "
|
||||||
|
"{ \"pools\": [ { \"pool\": \"192.0.2.2 - 192.0.2.100\" } ],"
|
||||||
|
" \"relay\": { "
|
||||||
|
" \"ip-address\": \"192.0.5.1\""
|
||||||
|
" },"
|
||||||
|
" \"subnet\": \"192.0.2.0/24\" }, "
|
||||||
|
"{ \"pools\": [ { \"pool\": \"192.0.3.1 - 192.0.3.100\" } ],"
|
||||||
|
" \"subnet\": \"192.0.3.0/24\" }, "
|
||||||
|
"{ \"pools\": [ { \"pool\": \"192.0.4.1 - 192.0.4.100\" } ],"
|
||||||
|
" \"client-class\": \"foo\", "
|
||||||
|
" \"subnet\": \"192.0.4.0/24\" } "
|
||||||
|
"],"
|
||||||
|
"\"valid-lifetime\": 4000 }";
|
||||||
|
|
||||||
|
// Use this config to set up the server
|
||||||
|
ASSERT_NO_THROW(configure(config));
|
||||||
|
|
||||||
|
// Let's get the subnet configuration objects
|
||||||
|
const Subnet4Collection* subnets =
|
||||||
|
CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getAll();
|
||||||
|
ASSERT_EQ(3, subnets->size());
|
||||||
|
|
||||||
|
// Let's get them for easy reference
|
||||||
|
Subnet4Ptr subnet1 = (*subnets)[0];
|
||||||
|
Subnet4Ptr subnet2 = (*subnets)[1];
|
||||||
|
Subnet4Ptr subnet3 = (*subnets)[2];
|
||||||
|
ASSERT_TRUE(subnet1);
|
||||||
|
ASSERT_TRUE(subnet2);
|
||||||
|
ASSERT_TRUE(subnet3);
|
||||||
|
|
||||||
|
// Let's create a packet.
|
||||||
|
Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
|
||||||
|
dis->setRemoteAddr(IOAddress("192.0.2.1"));
|
||||||
|
dis->setIface("eth0");
|
||||||
|
dis->setHops(1);
|
||||||
|
OptionPtr clientid = generateClientId();
|
||||||
|
dis->addOption(clientid);
|
||||||
|
|
||||||
|
// Let's create a Relay Agent Information option
|
||||||
|
OptionDefinitionPtr rai_def = LibDHCP::getOptionDef(Option::V4,
|
||||||
|
DHO_DHCP_AGENT_OPTIONS);
|
||||||
|
ASSERT_TRUE(rai_def);
|
||||||
|
OptionCustomPtr rai(new OptionCustom(*rai_def, Option::V4));
|
||||||
|
ASSERT_TRUE(rai);
|
||||||
|
IOAddress addr("192.0.3.2");
|
||||||
|
OptionPtr ols(new Option(Option::V4,
|
||||||
|
RAI_OPTION_LINK_SELECTION,
|
||||||
|
addr.toBytes()));
|
||||||
|
ASSERT_TRUE(ols);
|
||||||
|
rai->addOption(ols);
|
||||||
|
|
||||||
|
// This is just a sanity check, we're using regular method: ciaddr 192.0.3.1
|
||||||
|
// belongs to the second subnet, so it is selected
|
||||||
|
dis->setGiaddr(IOAddress("192.0.3.1"));
|
||||||
|
EXPECT_TRUE(subnet2 == srv_.selectSubnet(dis));
|
||||||
|
|
||||||
|
// Setup a relay override for the first subnet as it has a high precedence
|
||||||
|
dis->setGiaddr(IOAddress("192.0.5.1"));
|
||||||
|
EXPECT_TRUE(subnet1 == srv_.selectSubnet(dis));
|
||||||
|
|
||||||
|
// Put a RAI to select back the second subnet as it has
|
||||||
|
// the highest precedence
|
||||||
|
dis->addOption(rai);
|
||||||
|
EXPECT_TRUE(subnet2 == srv_.selectSubnet(dis));
|
||||||
|
|
||||||
|
// Check client-classification still applies
|
||||||
|
IOAddress addr_foo("192.0.4.2");
|
||||||
|
ols.reset(new Option(Option::V4, RAI_OPTION_LINK_SELECTION,
|
||||||
|
addr_foo.toBytes()));
|
||||||
|
rai->delOption(RAI_OPTION_LINK_SELECTION);
|
||||||
|
dis->delOption(DHO_DHCP_AGENT_OPTIONS);
|
||||||
|
rai->addOption(ols);
|
||||||
|
dis->addOption(rai);
|
||||||
|
// Note it shall fail (vs. try the next criterion).
|
||||||
|
EXPECT_FALSE(srv_.selectSubnet(dis));
|
||||||
|
// Add the packet to the class and check again: now it shall succeed
|
||||||
|
dis->addClass("foo");
|
||||||
|
EXPECT_TRUE(subnet3 == srv_.selectSubnet(dis));
|
||||||
|
|
||||||
|
// Check it fails with a bad address in the sub-option
|
||||||
|
IOAddress addr_bad("10.0.0.1");
|
||||||
|
ols.reset(new Option(Option::V4, RAI_OPTION_LINK_SELECTION,
|
||||||
|
addr_bad.toBytes()));
|
||||||
|
rai->delOption(RAI_OPTION_LINK_SELECTION);
|
||||||
|
dis->delOption(DHO_DHCP_AGENT_OPTIONS);
|
||||||
|
rai->addOption(ols);
|
||||||
|
dis->addOption(rai);
|
||||||
|
EXPECT_FALSE(srv_.selectSubnet(dis));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// This test verifies that the direct message is dropped when it has been
|
// This test verifies that the direct message is dropped when it has been
|
||||||
// received by the server via an interface for which there is no subnet
|
// received by the server via an interface for which there is no subnet
|
||||||
// configured. It also checks that the message is not dropped (is processed)
|
// configured. It also checks that the message is not dropped (is processed)
|
||||||
|
@@ -39,6 +39,12 @@ CfgSubnets4::add(const Subnet4Ptr& subnet) {
|
|||||||
|
|
||||||
Subnet4Ptr
|
Subnet4Ptr
|
||||||
CfgSubnets4::selectSubnet(const SubnetSelector& selector) const {
|
CfgSubnets4::selectSubnet(const SubnetSelector& selector) const {
|
||||||
|
// First use RAI link select sub-option or subnet select option
|
||||||
|
if (!selector.option_select_.isV4Zero()) {
|
||||||
|
return (selectSubnet(selector.option_select_,
|
||||||
|
selector.client_classes_));
|
||||||
|
}
|
||||||
|
|
||||||
// If relayed message has been received, try to match the giaddr with the
|
// If relayed message has been received, try to match the giaddr with the
|
||||||
// relay address specified for a subnet. It is also possible that the relay
|
// relay address specified for a subnet. It is also possible that the relay
|
||||||
// address will not match with any of the relay addresses accross all
|
// address will not match with any of the relay addresses accross all
|
||||||
|
@@ -61,6 +61,9 @@ public:
|
|||||||
/// parameters extracted from the client's message using the following
|
/// parameters extracted from the client's message using the following
|
||||||
/// logic.
|
/// logic.
|
||||||
///
|
///
|
||||||
|
/// First when link select suboption of relay agent information option
|
||||||
|
/// or subnet select option in this order exists the address is used
|
||||||
|
///
|
||||||
/// If the giaddr value is set in the selector it means that the client's
|
/// If the giaddr value is set in the selector it means that the client's
|
||||||
/// message was relayed. The subnet configuration allows for setting the
|
/// message was relayed. The subnet configuration allows for setting the
|
||||||
/// relay address for each subnet to indicate that the subnet must be
|
/// relay address for each subnet to indicate that the subnet must be
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
|
// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
|
||||||
//
|
//
|
||||||
// Permission to use, copy, modify, and/or distribute this software for any
|
// Permission to use, copy, modify, and/or distribute this software for any
|
||||||
// purpose with or without fee is hereby granted, provided that the above
|
// purpose with or without fee is hereby granted, provided that the above
|
||||||
@@ -35,6 +35,8 @@ struct SubnetSelector {
|
|||||||
asiolink::IOAddress ciaddr_;
|
asiolink::IOAddress ciaddr_;
|
||||||
/// @brief giaddr from the client's message.
|
/// @brief giaddr from the client's message.
|
||||||
asiolink::IOAddress giaddr_;
|
asiolink::IOAddress giaddr_;
|
||||||
|
/// @brief RAI link select or subnet select option
|
||||||
|
asiolink::IOAddress option_select_;
|
||||||
//@}
|
//@}
|
||||||
|
|
||||||
/// @name DHCPv6 specific parameters.
|
/// @name DHCPv6 specific parameters.
|
||||||
@@ -60,6 +62,7 @@ struct SubnetSelector {
|
|||||||
SubnetSelector()
|
SubnetSelector()
|
||||||
: ciaddr_(asiolink::IOAddress("0.0.0.0")),
|
: ciaddr_(asiolink::IOAddress("0.0.0.0")),
|
||||||
giaddr_(asiolink::IOAddress("0.0.0.0")),
|
giaddr_(asiolink::IOAddress("0.0.0.0")),
|
||||||
|
option_select_(asiolink::IOAddress("0.0.0.0")),
|
||||||
interface_id_(),
|
interface_id_(),
|
||||||
first_relay_linkaddr_(asiolink::IOAddress("::")),
|
first_relay_linkaddr_(asiolink::IOAddress("::")),
|
||||||
local_address_(asiolink::IOAddress("0.0.0.0")),
|
local_address_(asiolink::IOAddress("0.0.0.0")),
|
||||||
|
@@ -132,7 +132,7 @@ TEST(CfgOptionTest, add) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This test verifies that two option configurations can be merged.
|
// This test verifies that two option configurations can be merged.
|
||||||
TEST(CfgOption, merge) {
|
TEST(CfgOptionTest, merge) {
|
||||||
CfgOption cfg_src;
|
CfgOption cfg_src;
|
||||||
CfgOption cfg_dst;
|
CfgOption cfg_dst;
|
||||||
|
|
||||||
@@ -308,7 +308,7 @@ TEST(CfgOptionTest, encapsulate) {
|
|||||||
|
|
||||||
// This test verifies that single option can be retrieved from the configuration
|
// This test verifies that single option can be retrieved from the configuration
|
||||||
// using option code and option space.
|
// using option code and option space.
|
||||||
TEST(CfgOption, get) {
|
TEST(CfgOptionTest, get) {
|
||||||
CfgOption cfg;
|
CfgOption cfg;
|
||||||
|
|
||||||
// Add 10 options to a "dhcp6" option space in the subnet.
|
// Add 10 options to a "dhcp6" option space in the subnet.
|
||||||
|
@@ -143,9 +143,46 @@ TEST(CfgSubnets4Test, selectSubnetByClasses) {
|
|||||||
EXPECT_FALSE(cfg.selectSubnet(selector));
|
EXPECT_FALSE(cfg.selectSubnet(selector));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This test verifies the option selection can be used and is only
|
||||||
|
// used when present.
|
||||||
|
TEST(CfgSubnets4Test, selectSubnetByOptionSelect) {
|
||||||
|
CfgSubnets4 cfg;
|
||||||
|
|
||||||
|
// Create 3 subnets.
|
||||||
|
Subnet4Ptr subnet1(new Subnet4(IOAddress("192.0.2.0"), 26, 1, 2, 3));
|
||||||
|
Subnet4Ptr subnet2(new Subnet4(IOAddress("192.0.2.64"), 26, 1, 2, 3));
|
||||||
|
Subnet4Ptr subnet3(new Subnet4(IOAddress("192.0.2.128"), 26, 1, 2, 3));
|
||||||
|
|
||||||
|
// Add them to the configuration.
|
||||||
|
cfg.add(subnet1);
|
||||||
|
cfg.add(subnet2);
|
||||||
|
cfg.add(subnet3);
|
||||||
|
|
||||||
|
SubnetSelector selector;
|
||||||
|
|
||||||
|
// Check that without option selection something else is used
|
||||||
|
selector.ciaddr_ = IOAddress("192.0.2.5");
|
||||||
|
EXPECT_EQ(subnet1, cfg.selectSubnet(selector));
|
||||||
|
|
||||||
|
// The option selection has precedence
|
||||||
|
selector.option_select_ = IOAddress("192.0.2.130");
|
||||||
|
EXPECT_EQ(subnet3, cfg.selectSubnet(selector));
|
||||||
|
|
||||||
|
// Over relay-info too
|
||||||
|
selector.giaddr_ = IOAddress("10.0.0.1");
|
||||||
|
subnet2->setRelayInfo(IOAddress("10.0.0.1"));
|
||||||
|
EXPECT_EQ(subnet3, cfg.selectSubnet(selector));
|
||||||
|
selector.option_select_ = IOAddress("0.0.0.0");
|
||||||
|
EXPECT_EQ(subnet2, cfg.selectSubnet(selector));
|
||||||
|
|
||||||
|
// Check that a not matching option selection it shall fail
|
||||||
|
selector.option_select_ = IOAddress("10.0.0.1");
|
||||||
|
EXPECT_FALSE(cfg.selectSubnet(selector));
|
||||||
|
}
|
||||||
|
|
||||||
// This test verifies that the relay information can be used to retrieve the
|
// This test verifies that the relay information can be used to retrieve the
|
||||||
// subnet.
|
// subnet.
|
||||||
TEST(CfgSubnetsTest, selectSubnetByRelayAddress) {
|
TEST(CfgSubnets4Test, selectSubnetByRelayAddress) {
|
||||||
CfgSubnets4 cfg;
|
CfgSubnets4 cfg;
|
||||||
|
|
||||||
// Create 3 subnets.
|
// Create 3 subnets.
|
||||||
@@ -184,7 +221,7 @@ TEST(CfgSubnetsTest, selectSubnetByRelayAddress) {
|
|||||||
|
|
||||||
// This test verifies that the subnet can be selected for the client
|
// This test verifies that the subnet can be selected for the client
|
||||||
// using a source address if the client hasn't set the ciaddr.
|
// using a source address if the client hasn't set the ciaddr.
|
||||||
TEST(CfgSubnetsTest, selectSubnetNoCiaddr) {
|
TEST(CfgSubnets4Test, selectSubnetNoCiaddr) {
|
||||||
CfgSubnets4 cfg;
|
CfgSubnets4 cfg;
|
||||||
|
|
||||||
// Create 3 subnets.
|
// Create 3 subnets.
|
||||||
@@ -224,7 +261,7 @@ TEST(CfgSubnetsTest, selectSubnetNoCiaddr) {
|
|||||||
|
|
||||||
// This test verifies that the subnet can be selected using an address
|
// This test verifies that the subnet can be selected using an address
|
||||||
// set on the local interface.
|
// set on the local interface.
|
||||||
TEST(CfgSubnetsTest, selectSubnetInterface) {
|
TEST(CfgSubnets4Test, selectSubnetInterface) {
|
||||||
// The IfaceMgrTestConfig object initializes fake interfaces:
|
// The IfaceMgrTestConfig object initializes fake interfaces:
|
||||||
// eth0, eth1 and lo on the configuration manager. The CfgSubnets4
|
// eth0, eth1 and lo on the configuration manager. The CfgSubnets4
|
||||||
// object uses addresses assigned to these fake interfaces to
|
// object uses addresses assigned to these fake interfaces to
|
||||||
@@ -276,7 +313,7 @@ TEST(CfgSubnetsTest, selectSubnetInterface) {
|
|||||||
|
|
||||||
// Checks that detection of duplicated subnet IDs works as expected. It should
|
// Checks that detection of duplicated subnet IDs works as expected. It should
|
||||||
// not be possible to add two IPv4 subnets holding the same ID.
|
// not be possible to add two IPv4 subnets holding the same ID.
|
||||||
TEST(CfgSubnets4, duplication) {
|
TEST(CfgSubnets4Test, duplication) {
|
||||||
CfgSubnets4 cfg;
|
CfgSubnets4 cfg;
|
||||||
|
|
||||||
Subnet4Ptr subnet1(new Subnet4(IOAddress("192.0.2.0"), 26, 1, 2, 3, 123));
|
Subnet4Ptr subnet1(new Subnet4(IOAddress("192.0.2.0"), 26, 1, 2, 3, 123));
|
||||||
|
@@ -337,7 +337,7 @@ TEST(CfgSubnets6Test, selectSubnetByInterfaceIdAndClassify) {
|
|||||||
|
|
||||||
// Checks that detection of duplicated subnet IDs works as expected. It should
|
// Checks that detection of duplicated subnet IDs works as expected. It should
|
||||||
// not be possible to add two IPv6 subnets holding the same ID.
|
// not be possible to add two IPv6 subnets holding the same ID.
|
||||||
TEST(CfgSubnets6, duplication) {
|
TEST(CfgSubnets6Test, duplication) {
|
||||||
CfgSubnets6 cfg;
|
CfgSubnets6 cfg;
|
||||||
|
|
||||||
Subnet6Ptr subnet1(new Subnet6(IOAddress("2000::"), 48, 1, 2, 3, 4, 123));
|
Subnet6Ptr subnet1(new Subnet6(IOAddress("2000::"), 48, 1, 2, 3, 4, 123));
|
||||||
|
@@ -119,7 +119,7 @@ TEST(Pool4Test, unique_id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Simple check if toText returns reasonable values
|
// Simple check if toText returns reasonable values
|
||||||
TEST(Poo4Test,toText) {
|
TEST(Pool4Test,toText) {
|
||||||
Pool4 pool1(IOAddress("192.0.2.7"), IOAddress("192.0.2.17"));
|
Pool4 pool1(IOAddress("192.0.2.7"), IOAddress("192.0.2.17"));
|
||||||
EXPECT_EQ("type=V4, 192.0.2.7-192.0.2.17", pool1.toText());
|
EXPECT_EQ("type=V4, 192.0.2.7-192.0.2.17", pool1.toText());
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user