BIND 10 master, updated. dbedf1862bc30ffd3e21140f2a6e72302d769b7a [master] Merge branch 'trac3322' (relay override in client classification)
BIND 10 source code commits
bind10-changes at lists.isc.org
Mon Feb 24 18:20:30 UTC 2014
The branch, master has been updated
via dbedf1862bc30ffd3e21140f2a6e72302d769b7a (commit)
via 5de565baea42c9096dff78ed5fbd05982a174469 (commit)
via 59ff338f6154978a12f20055efface3484b446f1 (commit)
via 97f4d442ff4afde00db47f4108212d17c89752a5 (commit)
via 1280b91ed7ff83f99d39b1c090044986198cc145 (commit)
via c4c70d6d679becb3cff8bca225f17ac3ac7f0afd (commit)
via 71c8ce2938e6e27dd8c8ee2c0bfd2f87028d2c2d (commit)
via 4a4c099967fa21b7d2f9816ed3ab3923348a7ac1 (commit)
via 8174421f666416dd3cb6a2053b3b2dd6db4d32b1 (commit)
via a3db6f08b0321dcfbd7539e07764f0c124ac19af (commit)
via 1158dbada8422f122b7a08b67bdb844a91be26ac (commit)
via 6975ad04e89daa261d03d2d8383a8b960fed3bae (commit)
via 6a6d055a324242f2b6986848474c1dfc27bf5a0f (commit)
via 349644e019d4d89c03aee21bfda5dc159d1fe065 (commit)
from 431746f950a40196267838a1a75e8af9fe9a66fc (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit dbedf1862bc30ffd3e21140f2a6e72302d769b7a
Merge: 431746f 5de565b
Author: Tomek Mrugalski <tomasz at isc.org>
Date: Mon Feb 24 19:20:15 2014 +0100
[master] Merge branch 'trac3322' (relay override in client classification)
Conflicts:
ChangeLog
src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
-----------------------------------------------------------------------
Summary of changes:
ChangeLog | 7 +
doc/guide/bind10-guide.xml | 198 ++++++++++++++++-
src/bin/dhcp4/dhcp4_srv.cc | 3 +-
src/bin/dhcp4/tests/dhcp4_srv_unittest.cc | 154 +++++++++++--
src/bin/dhcp6/dhcp6_srv.cc | 2 +-
src/bin/dhcp6/tests/Makefile.am | 1 +
src/bin/dhcp6/tests/dhcp6_srv_unittest.cc | 234 ++++++++++++++++----
src/bin/dhcp6/tests/dhcp6_test_utils.cc | 40 ++++
src/bin/dhcp6/tests/dhcp6_test_utils.h | 30 +--
src/bin/dhcp6/tests/fqdn_unittest.cc | 2 +
src/bin/dhcp6/tests/hooks_unittest.cc | 3 +
src/lib/dhcp/tests/Makefile.am | 2 +
src/lib/dhcp/tests/iface_mgr_test_config.cc | 5 +
src/lib/dhcp/tests/iface_mgr_test_config.h | 2 +
...ilter_test_stub.cc => pkt_filter6_test_stub.cc} | 28 +--
..._filter_test_stub.h => pkt_filter6_test_stub.h} | 68 +++---
src/lib/dhcpsrv/cfgmgr.cc | 42 ++--
src/lib/dhcpsrv/cfgmgr.h | 22 +-
src/lib/dhcpsrv/dhcpsrv_messages.mes | 10 +
src/lib/dhcpsrv/tests/cfgmgr_unittest.cc | 89 +++++++-
20 files changed, 767 insertions(+), 175 deletions(-)
copy src/lib/dhcp/tests/{pkt_filter_test_stub.cc => pkt_filter6_test_stub.cc} (67%)
copy src/lib/dhcp/tests/{pkt_filter_test_stub.h => pkt_filter6_test_stub.h} (57%)
-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index f4e58ab..c763736 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+759. [func] tomek
+ b10-dhcp4, b10-dhcp6: IP address of the relay agent can now be specified
+ for both IPv4 and IPv6 subnets. That information allows the server to
+ properly handle a case where relay agent address does not match subnet.
+ This is mostly useful in shared subnets and cable networks.
+ (Trac #3322, git 5de565baea42c9096dff78ed5fbd05982a174469)
+
758. [bug] tmark
b10-dhcp4 now correctly handles DHO_HOST_OPTION. This corrects a bug
where the server would fail to recognize the option in the DHCP request
diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml
index 7873e60..f666411 100644
--- a/doc/guide/bind10-guide.xml
+++ b/doc/guide/bind10-guide.xml
@@ -4593,6 +4593,11 @@ Dhcp4/subnet4 [] list (default)
If the message is relayed it is accepted through any interface. The giaddr
set by the relay agent is used to select the subnet for the client.
</para>
+ <para>
+ It is also possible to specify a relay IPv4 address for a given subnet. It
+ can be used to match incoming packets into a subnet in uncommon configurations,
+ e.g. shared subnets. See <xref linkend="dhcp4-relay-override"/> for details.
+ </para>
<note>
<para>The subnet selection mechanism described in this section is based
on the assumption that client classification is not used. The classification
@@ -4601,6 +4606,75 @@ Dhcp4/subnet4 [] list (default)
</note>
</section>
+ <section id="dhcp4-relay-override">
+ <title>Using specific relay agent for a subnet</title>
+ <para>
+ The relay has to have an interface connected to the link on which
+ the clients are being configured. Typically the relay has an IPv4
+ address configured on that interface that belongs to the subnet that
+ the server will assign addresses from. In such typical case, the
+ server is able to use IPv4 address inserted by the relay (in GIADDR
+ field of the DHCPv4 packet) to select appropriate subnet.
+ </para>
+ <para>
+ However, that is not always the case. In certain uncommon, but
+ valid deployments, the relay address may not match the subnet. This
+ usually means that there is more than one subnet allocated for a given
+ link. Two most common examples where this is the case are long lasting
+ network renumbering (where both old and new address space is still being
+ used) and a cable network. In a cable network both cable modems and the
+ devices behind them are physically connected to the same link, yet
+ they use distinct addressing. In such case, the DHCPv4 server needs
+ additional information (IPv4 address of the relay) to properly select
+ an appropriate subnet.
+ </para>
+ <para>
+ The following example assumes that there is a subnet 192.0.2.0/24
+ that is accessible via relay that uses 10.0.0.1 as its IPv4 address.
+ The server will be able to select this subnet for any incoming packets
+ that came from a relay that has an address in 192.0.2.0/24 subnet.
+ It will also select that subnet for a relay with address 10.0.0.1.
+ <screen>
+> <userinput>config add Dhcp4/subnet4</userinput>
+> <userinput>config set Dhcp4/subnet4[0]/subnet "192.0.2.0/24"</userinput>
+> <userinput>config set Dhcp4/subnet4[0]/pool [ "192.0.2.10 - 192.0.2.20" ]</userinput>
+> <userinput>config set Dhcp4/subnet4[0]/relay/ip-address "10.0.0.1"</userinput>
+> <userinput>config commit</userinput></screen>
+ </para>
+
+ </section>
+
+ <section id="dhcp4-srv-example-client-class-relay">
+ <title>Segregating IPv4 clients in a cable network</title>
+ <para>
+ In certain cases, it is useful to mix relay address information,
+ introduced in <xref linkend="dhcp4-relay-override"/> with client
+ classification, explained in <xref linkend="dhcp4-subnet-class"/>.
+ One specific example is cable network, where typically modems
+ get addresses from a different subnet than all devices connected
+ behind them.
+ </para>
+ <para>
+ Let's assume that there is one CMTS (Cable Modem Termination System)
+ with one CM MAC (a physical link that modems are connected to).
+ We want the modems to get addresses from the 10.1.1.0/24 subnet, while
+ everything connected behind modems should get addresses from another
+ subnet (192.0.2.0/24). The CMTS that acts as a relay an uses address
+ 10.1.1.1. The following configuration can serve that configuration:
+ <screen>
+> <userinput>config add Dhcp4/subnet4</userinput>
+> <userinput>config set Dhcp4/subnet4[0]/subnet "10.1.1.0/24"</userinput>
+> <userinput>config set Dhcp4/subnet4[0]/pool [ "10.1.1.2 - 10.1.1.20" ]</userinput>
+> <userinput>config set Dhcp4/subnet4[0]/client-class "docsis3.0"</userinput>
+> <userinput>config set Dhcp4/subnet4[0]/relay/ip-address "10.1.1.1"</userinput>
+> <userinput>config add Dhcp4/subnet4</userinput>
+> <userinput>config set Dhcp4/subnet4[1]/subnet "192.0.2.0/24"</userinput>
+> <userinput>config set Dhcp4/subnet4[1]/pool [ "192.0.2.10 - 192.0.2.20" ]</userinput>
+> <userinput>config set Dhcp4/subnet4[1]/relay/ip-address "10.1.1.1"</userinput>
+> <userinput>config commit</userinput></screen>
+ </para>
+ </section>
+
<section id="dhcp4-std">
<title>Supported Standards</title>
<para>The following standards and draft standards are currently
@@ -4692,6 +4766,23 @@ Dhcp4/renew-timer 1000 integer (default)
</itemizedlist>
</section>
+ <!--
+ <section id="dhcp4-srv-examples">
+ <title>Kea DHCPv4 server examples</title>
+
+ <para>
+ This section provides easy to use example. Each example can be read
+ separately. It is not intended to be read sequentially as there will
+ be many repetitions between examples. They are expected to serve as
+ easy to use copy-paste solutions to many common deployments.
+ </para>
+
+ @todo: add simple configuration for direct clients
+ @todo: add configuration for relayed clients
+ @todo: add client classification example
+
+ </section> -->
+
</chapter>
<chapter id="dhcp6">
@@ -5460,17 +5551,17 @@ should include options from the isc option space:
The DHCPv6 server may receive requests from local (connected to the
same subnet as the server) and remote (connecting via relays) clients.
As server may have many subnet configurations defined, it must select
- appropriate subnet for a given request. To do this, the server first
- checks if there is only one subnet defined and source of the packet is
- link-local. If this is the case, the server assumes that the only
- subnet defined is local and client is indeed connected to it. This
- check simplifies small deployments.
+ appropriate subnet for a given request.
</para>
<para>
- If there are two or more subnets defined, the server can not assume
- which of those (if any) subnets are local. Therefore an optional
- "interface" parameter is available within a subnet definition to
- designate that a given subnet is local, i.e. reachable directly over
+ The server can not assume which of configured subnets are local. It is
+ possible in IPv4, where there is reasonable expectation that the
+ server will have a (global) IPv4 address configured on the interface,
+ and can use that information to detect whether a subnet is local or
+ not. That assumption is not true in IPv6, as the DHCPv6 must be able
+ to operate with having link-local addresses only. Therefore an optional
+ "interface" parameter is available within a subnet definition
+ to designate that a given subnet is local, i.e. reachable directly over
specified interface. For example the server that is intended to serve
a local subnet over eth0 may be configured as follows:
<screen>
@@ -5643,6 +5734,78 @@ should include options from the isc option space:
</section>
+ <section id="dhcp6-relay-override">
+ <title>Using specific relay agent for a subnet</title>
+ <para>
+ The relay has to have an interface connected to the link on which
+ the clients are being configured. Typically the relay has a global IPv6
+ address configured on that interface that belongs to the subnet that
+ the server will assign addresses from. In such typical case, the
+ server is able to use IPv6 address inserted by the relay (in link-addr
+ field in RELAY-FORW message) to select appropriate subnet.
+ </para>
+ <para>
+ However, that is not always the case. The relay
+ address may not match the subnet in certain deployments. This
+ usually means that there is more than one subnet allocated for a given
+ link. Two most common examples where this is the case are long lasting
+ network renumbering (where both old and new address space is still being
+ used) and a cable network. In a cable network both cable modems and the
+ devices behind them are physically connected to the same link, yet
+ they use distinct addressing. In such case, the DHCPv6 server needs
+ additional information (like the value of interface-id option or IPv6
+ address inserted in the link-addr field in RELAY-FORW message) to
+ properly select an appropriate subnet.
+ </para>
+ <para>
+ The following example assumes that there is a subnet 2001:db8:1::/64
+ that is accessible via relay that uses 3000::1 as its IPv6 address.
+ The server will be able to select this subnet for any incoming packets
+ that came from a relay that has an address in 2001:db8:1::/64 subnet.
+ It will also select that subnet for a relay with address 3000::1.
+ <screen>
+> <userinput>config add Dhcp6/subnet6</userinput>
+> <userinput>config set Dhcp6/subnet6[0]/subnet "2001:db8:1::/64"</userinput>
+> <userinput>config set Dhcp6/subnet6[0]/pool [ "2001:db8:1::2 - 2001:db8:1::ffff" ]</userinput>
+> <userinput>config set Dhcp6/subnet6[0]/relay/ip-address "3000::1"</userinput>
+> <userinput>config commit</userinput></screen>
+ </para>
+
+ </section>
+
+ <section id="dhcp6-client-class-relay">
+ <title>Segregating IPv6 clients in a cable network</title>
+ <para>
+ In certain cases, it is useful to mix relay address information,
+ introduced in <xref linkend="dhcp6-relay-override"/> with client
+ classification, explained in <xref linkend="dhcp6-subnet-class"/>.
+ One specific example is cable network, where typically modems
+ get addresses from a different subnet than all devices connected
+ behind them.
+ </para>
+ <para>
+ Let's assume that there is one CMTS (Cable Modem Termination System)
+ with one CM MAC (a physical link that modems are connected to).
+ We want the modems to get addresses from the 3000::/64 subnet,
+ while everything connected behind modems should get addresses from
+ another subnet (2001:db8:1::/64). The CMTS that acts as a relay
+ an uses address 3000::1. The following configuration can serve
+ that configuration:
+ <screen>
+> <userinput>config add Dhcp6/subnet6</userinput>
+> <userinput>config set Dhcp6/subnet6[0]/subnet "3000::/64"</userinput>
+> <userinput>config set Dhcp6/subnet6[0]/pool [ "3000::2 - 3000::ffff" ]</userinput>
+> <userinput>config set Dhcp6/subnet6[0]/client-class "docsis3.0"</userinput>
+> <userinput>config set Dhcp6/subnet6[0]/relay/ip-address "3000::1"</userinput>
+> <userinput>config add Dhcp6/subnet6</userinput>
+> <userinput>config set Dhcp6/subnet6[1]/subnet "2001:db8:1::/64"</userinput>
+> <userinput>config set Dhcp6/subnet6[1]/pool [ "2001:db8:1::1 - 2001:db8:1::ffff" ]</userinput>
+> <userinput>config set Dhcp6/subnet6[1]/relay/ip-address "3000::1"</userinput>
+> <userinput>config commit</userinput></screen>
+ </para>
+ </section>
+
+
<section id="dhcp6-std">
<title>Supported Standards</title>
<para>The following standards and draft standards are currently
@@ -5714,6 +5877,23 @@ Dhcp6/renew-timer 1000 integer (default)
</itemizedlist>
</section>
+ <!--
+ <section id="dhcp6-srv-examples">
+ <title>Kea DHCPv6 server examples</title>
+
+ <para>
+ This section provides easy to use example. Each example can be read
+ separately. It is not intended to be read sequentially as there will
+ be many repetitions between examples. They are expected to serve as
+ easy to use copy-paste solutions to many common deployments.
+ </para>
+
+ @todo: add simple configuration for direct clients
+ @todo: add configuration for relayed clients
+ @todo: add client classification example
+
+ </section> -->
+
</chapter>
<chapter id="libdhcp">
diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc
index cb121e2..fcee56c 100644
--- a/src/bin/dhcp4/dhcp4_srv.cc
+++ b/src/bin/dhcp4/dhcp4_srv.cc
@@ -1419,7 +1419,8 @@ Dhcpv4Srv::selectSubnet(const Pkt4Ptr& question) const {
// level functions.
if (question->isRelayed()) {
subnet = CfgMgr::instance().getSubnet4(question->getGiaddr(),
- question->classes_);
+ question->classes_,
+ true);
// The message is not relayed so it is sent directly by a client. But
// the client may be renewing its lease and in such case it unicasts
diff --git a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
index af2d0ad..0f73c86 100644
--- a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
+++ b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
@@ -3314,10 +3314,6 @@ TEST_F(Dhcpv4SrvTest, clientClassification) {
// .clientClassification above.
TEST_F(Dhcpv4SrvTest, clientClassify2) {
- NakedDhcpv4Srv srv(0);
-
- ConstElementPtr status;
-
// 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
@@ -3340,15 +3336,9 @@ TEST_F(Dhcpv4SrvTest, clientClassify2) {
"],"
"\"valid-lifetime\": 4000 }";
- ElementPtr json = Element::fromJSON(config);
-
- EXPECT_NO_THROW(status = configureDhcp4Server(srv, json));
-
- // check if returned status is OK
- ASSERT_TRUE(status);
- comment_ = config::parseAnswer(rcode_, status);
- ASSERT_EQ(0, rcode_);
+ ASSERT_NO_THROW(configure(config));
+ // 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"));
@@ -3358,19 +3348,153 @@ TEST_F(Dhcpv4SrvTest, clientClassify2) {
// This discover does not belong to foo class, so it will not
// be serviced
- EXPECT_FALSE(srv.selectSubnet(dis));
+ EXPECT_FALSE(srv_.selectSubnet(dis));
// Let's add the packet to bar class and try again.
dis->addClass("bar");
// Still not supported, because it belongs to wrong class.
- EXPECT_FALSE(srv.selectSubnet(dis));
+ EXPECT_FALSE(srv_.selectSubnet(dis));
// Let's add it to maching class.
dis->addClass("foo");
// This time it should work
- EXPECT_TRUE(srv.selectSubnet(dis));
+ EXPECT_TRUE(srv_.selectSubnet(dis));
+}
+
+// Checks if relay IP address specified in the relay-info structure in
+// subnet4 is being used properly.
+TEST_F(Dhcpv4SrvTest, relayOverride) {
+
+ // We have 2 subnets defined. Note that both have a relay address
+ // defined. Both are not belonging to the subnets. That is
+ // important, because if the relay belongs to the subnet, there's
+ // no need to specify relay override.
+ string config = "{ \"interfaces\": [ \"*\" ],"
+ "\"rebind-timer\": 2000, "
+ "\"renew-timer\": 1000, "
+ "\"subnet4\": [ "
+ "{ \"pool\": [ \"192.0.2.2 - 192.0.2.100\" ],"
+ " \"relay\": { "
+ " \"ip-address\": \"192.0.5.1\""
+ " },"
+ " \"subnet\": \"192.0.2.0/24\" }, "
+ "{ \"pool\": [ \"192.0.3.1 - 192.0.3.100\" ],"
+ " \"relay\": { "
+ " \"ip-address\": \"192.0.5.2\""
+ " },"
+ " \"subnet\": \"192.0.3.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().getSubnets4();
+ ASSERT_EQ(2, subnets->size());
+
+ // Let's get them for easy reference
+ Subnet4Ptr subnet1 = (*subnets)[0];
+ Subnet4Ptr subnet2 = (*subnets)[1];
+ ASSERT_TRUE(subnet1);
+ ASSERT_TRUE(subnet2);
+
+ // 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);
+
+ // This is just a sanity check, we're using regular method: ciaddr 192.0.2.1
+ // belongs to the first subnet, so it is selected
+ dis->setGiaddr(IOAddress("192.0.2.1"));
+ EXPECT_TRUE(subnet1 == srv_.selectSubnet(dis));
+
+ // Relay belongs to the second subnet, so it should be selected.
+ dis->setGiaddr(IOAddress("192.0.3.1"));
+ EXPECT_TRUE(subnet2 == srv_.selectSubnet(dis));
+
+ // Now let's check if the relay override for the first subnets works
+ dis->setGiaddr(IOAddress("192.0.5.1"));
+ EXPECT_TRUE(subnet1 == srv_.selectSubnet(dis));
+
+ // The same check for the second subnet...
+ dis->setGiaddr(IOAddress("192.0.5.2"));
+ EXPECT_TRUE(subnet2 == srv_.selectSubnet(dis));
+
+ // And finally, let's check if mis-matched relay address will end up
+ // in not selecting a subnet at all
+ dis->setGiaddr(IOAddress("192.0.5.3"));
+ EXPECT_FALSE(srv_.selectSubnet(dis));
+
+ // Finally, check that the relay override works only with relay address
+ // (GIADDR) and does not affect client address (CIADDR)
+ dis->setGiaddr(IOAddress("0.0.0.0"));
+ dis->setHops(0);
+ dis->setCiaddr(IOAddress("192.0.5.1"));
+ EXPECT_FALSE(srv_.selectSubnet(dis));
+}
+
+// Checks if relay IP address specified in the relay-info structure can be
+// used together with client-classification.
+TEST_F(Dhcpv4SrvTest, 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\": [ \"*\" ],"
+ "\"rebind-timer\": 2000, "
+ "\"renew-timer\": 1000, "
+ "\"subnet4\": [ "
+ "{ \"pool\": [ \"192.0.2.2 - 192.0.2.100\" ],"
+ " \"client-class\": \"foo\", "
+ " \"relay\": { "
+ " \"ip-address\": \"192.0.5.1\""
+ " },"
+ " \"subnet\": \"192.0.2.0/24\" }, "
+ "{ \"pool\": [ \"192.0.3.1 - 192.0.3.100\" ],"
+ " \"relay\": { "
+ " \"ip-address\": \"192.0.5.1\""
+ " },"
+ " \"subnet\": \"192.0.3.0/24\" } "
+ "],"
+ "\"valid-lifetime\": 4000 }";
+
+ // Use this config to set up the server
+ ASSERT_NO_THROW(configure(config));
+
+ const Subnet4Collection* subnets = CfgMgr::instance().getSubnets4();
+ ASSERT_EQ(2, subnets->size());
+
+ // Let's get them for easy reference
+ Subnet4Ptr subnet1 = (*subnets)[0];
+ Subnet4Ptr subnet2 = (*subnets)[1];
+ ASSERT_TRUE(subnet1);
+ ASSERT_TRUE(subnet2);
+
+ // 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);
+ dis->setGiaddr(IOAddress("192.0.5.1"));
+ OptionPtr clientid = generateClientId();
+ dis->addOption(clientid);
+
+ // 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(dis));
+
+ // 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.
+ dis->addClass("foo");
+ EXPECT_TRUE(subnet1 == srv_.selectSubnet(dis));
}
// This test verifies that the direct message is dropped when it has been
diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc
index aed4db4..82073ea 100644
--- a/src/bin/dhcp6/dhcp6_srv.cc
+++ b/src/bin/dhcp6/dhcp6_srv.cc
@@ -864,7 +864,7 @@ Dhcpv6Srv::selectSubnet(const Pkt6Ptr& question) {
// if relay filled in link_addr field, then let's use it
if (link_addr != IOAddress("::")) {
subnet = CfgMgr::instance().getSubnet6(link_addr,
- question->classes_);
+ question->classes_, true);
}
}
}
diff --git a/src/bin/dhcp6/tests/Makefile.am b/src/bin/dhcp6/tests/Makefile.am
index 92a18cf..c0d6b14 100644
--- a/src/bin/dhcp6/tests/Makefile.am
+++ b/src/bin/dhcp6/tests/Makefile.am
@@ -92,6 +92,7 @@ dhcp6_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
dhcp6_unittests_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
dhcp6_unittests_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
dhcp6_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
+dhcp6_unittests_LDADD += $(top_builddir)/src/lib/dhcp/tests/libdhcptest.la
dhcp6_unittests_LDADD += $(top_builddir)/src/lib/dhcp_ddns/libb10-dhcp_ddns.la
dhcp6_unittests_LDADD += $(top_builddir)/src/lib/dhcpsrv/libb10-dhcpsrv.la
dhcp6_unittests_LDADD += $(top_builddir)/src/lib/hooks/libb10-hooks.la
diff --git a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
index 2900118..753bafc 100644
--- a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
+++ b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
@@ -29,6 +29,7 @@
#include <dhcp6/config_parser.h>
#include <dhcp/dhcp6.h>
#include <dhcp/docsis3_option_defs.h>
+#include <dhcp/tests/iface_mgr_test_config.h>
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/lease_mgr.h>
#include <dhcpsrv/lease_mgr_factory.h>
@@ -52,6 +53,7 @@ using namespace isc::config;
using namespace isc::test;
using namespace isc::asiolink;
using namespace isc::dhcp;
+using namespace isc::dhcp::test;
using namespace isc::util;
using namespace isc::hooks;
using namespace std;
@@ -283,6 +285,9 @@ TEST_F(Dhcpv6SrvTest, DUID) {
// This test checks if Option Request Option (ORO) is parsed correctly
// and the requested options are actually assigned.
TEST_F(Dhcpv6SrvTest, advertiseOptions) {
+
+ IfaceMgrTestConfig test_config(true);
+
ConstElementPtr x;
string config = "{ \"interfaces\": [ \"all\" ],"
"\"preferred-lifetime\": 3000,"
@@ -291,6 +296,7 @@ TEST_F(Dhcpv6SrvTest, advertiseOptions) {
"\"subnet6\": [ { "
" \"pool\": [ \"2001:db8:1::/64\" ],"
" \"subnet\": \"2001:db8:1::/48\", "
+ " \"interface\": \"eth0\", "
" \"option-data\": [ {"
" \"name\": \"dns-servers\","
" \"space\": \"dhcp6\","
@@ -307,25 +313,17 @@ TEST_F(Dhcpv6SrvTest, advertiseOptions) {
" } ]"
" } ],"
"\"valid-lifetime\": 4000 }";
-
- ElementPtr json = Element::fromJSON(config);
-
- NakedDhcpv6Srv srv(0);
-
- EXPECT_NO_THROW(x = configureDhcp6Server(srv, json));
- ASSERT_TRUE(x);
- comment_ = parseAnswer(rcode_, x);
-
- ASSERT_EQ(0, rcode_);
+ ASSERT_NO_THROW(configure(config));
Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
sol->setRemoteAddr(IOAddress("fe80::abcd"));
+ sol->setIface("eth0");
sol->addOption(generateIA(D6O_IA_NA, 234, 1500, 3000));
OptionPtr clientid = generateClientId();
sol->addOption(clientid);
// Pass it to the server and get an advertise
- Pkt6Ptr adv = srv.processSolicit(sol);
+ Pkt6Ptr adv = srv_.processSolicit(sol);
// check if we get response at all
ASSERT_TRUE(adv);
@@ -349,7 +347,7 @@ TEST_F(Dhcpv6SrvTest, advertiseOptions) {
sol->addOption(option_oro);
// Need to process SOLICIT again after requesting new option.
- adv = srv.processSolicit(sol);
+ adv = srv_.processSolicit(sol);
ASSERT_TRUE(adv);
OptionPtr tmp = adv->getOption(D6O_NAME_SERVERS);
@@ -404,6 +402,7 @@ TEST_F(Dhcpv6SrvTest, SolicitBasic) {
Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
sol->setRemoteAddr(IOAddress("fe80::abcd"));
+ sol->setIface("eth0");
sol->addOption(generateIA(D6O_IA_NA, 234, 1500, 3000));
OptionPtr clientid = generateClientId();
sol->addOption(clientid);
@@ -447,6 +446,7 @@ TEST_F(Dhcpv6SrvTest, pdSolicitBasic) {
Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
sol->setRemoteAddr(IOAddress("fe80::abcd"));
+ sol->setIface("eth0");
sol->addOption(generateIA(D6O_IA_PD, 234, 1500, 3000));
OptionPtr clientid = generateClientId();
sol->addOption(clientid);
@@ -492,6 +492,7 @@ TEST_F(Dhcpv6SrvTest, SolicitHint) {
// Let's create a SOLICIT
Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
sol->setRemoteAddr(IOAddress("fe80::abcd"));
+ sol->setIface("eth0");
boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_NA, 234, 1500, 3000);
// with a valid hint
@@ -546,6 +547,7 @@ TEST_F(Dhcpv6SrvTest, SolicitInvalidHint) {
// Let's create a SOLICIT
Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
sol->setRemoteAddr(IOAddress("fe80::abcd"));
+ sol->setIface("eth0");
boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_NA, 234, 1500, 3000);
IOAddress hint("2001:db8:1::cafe:babe");
ASSERT_FALSE(subnet_->inPool(Lease::TYPE_NA, hint));
@@ -596,6 +598,10 @@ TEST_F(Dhcpv6SrvTest, ManySolicits) {
sol2->setRemoteAddr(IOAddress("fe80::1223"));
sol3->setRemoteAddr(IOAddress("fe80::3467"));
+ sol1->setIface("eth0");
+ sol2->setIface("eth0");
+ sol3->setIface("eth0");
+
sol1->addOption(generateIA(D6O_IA_NA, 1, 1500, 3000));
sol2->addOption(generateIA(D6O_IA_NA, 2, 1500, 3000));
sol3->addOption(generateIA(D6O_IA_NA, 3, 1500, 3000));
@@ -673,6 +679,7 @@ TEST_F(Dhcpv6SrvTest, RequestBasic) {
// Let's create a REQUEST
Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234));
req->setRemoteAddr(IOAddress("fe80::abcd"));
+ req->setIface("eth0");
boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_NA, 234, 1500, 3000);
// with a valid hint
@@ -737,6 +744,7 @@ TEST_F(Dhcpv6SrvTest, pdRequestBasic) {
// Let's create a REQUEST
Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234));
req->setRemoteAddr(IOAddress("fe80::abcd"));
+ req->setIface("eth0");
boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_PD, 234, 1500, 3000);
// with a valid hint
@@ -800,6 +808,10 @@ TEST_F(Dhcpv6SrvTest, ManyRequests) {
req2->setRemoteAddr(IOAddress("fe80::1223"));
req3->setRemoteAddr(IOAddress("fe80::3467"));
+ req1->setIface("eth0");
+ req2->setIface("eth0");
+ req3->setIface("eth0");
+
req1->addOption(generateIA(D6O_IA_NA, 1, 1500, 3000));
req2->addOption(generateIA(D6O_IA_NA, 2, 1500, 3000));
req3->addOption(generateIA(D6O_IA_NA, 3, 1500, 3000));
@@ -1078,9 +1090,9 @@ TEST_F(Dhcpv6SrvTest, sanityCheck) {
// Check that the server is testing if server identifier received in the
// query, matches server identifier used by the server.
TEST_F(Dhcpv6SrvTest, testServerID) {
- NakedDhcpv6Srv srv(0);
+ NakedDhcpv6Srv srv(0);
- Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234));
+ Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234));
std::vector<uint8_t> bin;
// diud_llt constructed with: time = 0, macaddress = 00:00:00:00:00:00
@@ -1122,15 +1134,16 @@ TEST_F(Dhcpv6SrvTest, selectSubnetAddr) {
Subnet6Ptr subnet3(new Subnet6(IOAddress("2001:db8:3::"), 48, 1, 2, 3, 4));
// CASE 1: We have only one subnet defined and we received local traffic.
- // The only available subnet should be selected
+ // The only available subnet used to be picked, but not anymore
CfgMgr::instance().deleteSubnets6();
CfgMgr::instance().addSubnet6(subnet1); // just a single subnet
Pkt6Ptr pkt = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
pkt->setRemoteAddr(IOAddress("fe80::abcd"));
- Subnet6Ptr selected = srv.selectSubnet(pkt);
- EXPECT_EQ(selected, subnet1);
+ // The clause for assuming local subnet if there is only one subnet is was
+ // removed.
+ EXPECT_FALSE(srv.selectSubnet(pkt));
// CASE 2: We have only one subnet defined and we received relayed traffic.
// We should NOT select it.
@@ -1139,7 +1152,7 @@ TEST_F(Dhcpv6SrvTest, selectSubnetAddr) {
CfgMgr::instance().deleteSubnets6();
CfgMgr::instance().addSubnet6(subnet1); // just a single subnet
pkt->setRemoteAddr(IOAddress("2001:db8:abcd::2345"));
- selected = srv.selectSubnet(pkt);
+ Subnet6Ptr selected = srv.selectSubnet(pkt);
EXPECT_FALSE(selected);
// CASE 3: We have three subnets defined and we received local traffic.
@@ -1169,9 +1182,7 @@ TEST_F(Dhcpv6SrvTest, selectSubnetAddr) {
CfgMgr::instance().addSubnet6(subnet2);
CfgMgr::instance().addSubnet6(subnet3);
pkt->setRemoteAddr(IOAddress("2001:db8:4::baca"));
- selected = srv.selectSubnet(pkt);
- EXPECT_FALSE(selected);
-
+ EXPECT_FALSE(srv.selectSubnet(pkt));
}
// This test verifies if selectSubnet() selects proper subnet for a given
@@ -1497,7 +1508,9 @@ TEST_F(Dhcpv6SrvTest, docsisVendorORO) {
// This test checks if Option Request Option (ORO) in docsis (vendor-id=4491)
// vendor options is parsed correctly and the requested options are actually assigned.
TEST_F(Dhcpv6SrvTest, vendorOptionsORO) {
- ConstElementPtr x;
+
+ IfaceMgrTestConfig test_config(true);
+
string config = "{ \"interfaces\": [ \"all\" ],"
"\"preferred-lifetime\": 3000,"
"\"rebind-timer\": 2000, "
@@ -1526,28 +1539,21 @@ TEST_F(Dhcpv6SrvTest, vendorOptionsORO) {
" \"preferred-lifetime\": 3000,"
" \"valid-lifetime\": 4000,"
" \"interface-id\": \"\","
- " \"interface\": \"\""
+ " \"interface\": \"eth0\""
" } ],"
"\"valid-lifetime\": 4000 }";
- ElementPtr json = Element::fromJSON(config);
-
- NakedDhcpv6Srv srv(0);
-
- EXPECT_NO_THROW(x = configureDhcp6Server(srv, json));
- ASSERT_TRUE(x);
- comment_ = parseAnswer(rcode_, x);
-
- ASSERT_EQ(0, rcode_);
+ ASSERT_NO_THROW(configure(config));
Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
sol->setRemoteAddr(IOAddress("fe80::abcd"));
+ sol->setIface("eth0");
sol->addOption(generateIA(D6O_IA_NA, 234, 1500, 3000));
OptionPtr clientid = generateClientId();
sol->addOption(clientid);
// Pass it to the server and get an advertise
- Pkt6Ptr adv = srv.processSolicit(sol);
+ Pkt6Ptr adv = srv_.processSolicit(sol);
// check if we get response at all
ASSERT_TRUE(adv);
@@ -1566,7 +1572,7 @@ TEST_F(Dhcpv6SrvTest, vendorOptionsORO) {
sol->addOption(vendor);
// Need to process SOLICIT again after requesting new option.
- adv = srv.processSolicit(sol);
+ adv = srv_.processSolicit(sol);
ASSERT_TRUE(adv);
// Check if thre is vendor option response
@@ -1745,10 +1751,6 @@ TEST_F(Dhcpv6SrvTest, clientClassification) {
// .clientClassification above.
TEST_F(Dhcpv6SrvTest, clientClassify2) {
- NakedDhcpv6Srv srv(0);
-
- ConstElementPtr status;
-
// 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
@@ -1774,14 +1776,7 @@ TEST_F(Dhcpv6SrvTest, clientClassify2) {
"],"
"\"valid-lifetime\": 4000 }";
- ElementPtr json = Element::fromJSON(config);
-
- EXPECT_NO_THROW(status = configureDhcp6Server(srv, json));
-
- // check if returned status is OK
- ASSERT_TRUE(status);
- comment_ = config::parseAnswer(rcode_, status);
- ASSERT_EQ(0, rcode_);
+ ASSERT_NO_THROW(configure(config));
Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
sol->setRemoteAddr(IOAddress("2001:db8:1::3"));
@@ -1791,19 +1786,19 @@ TEST_F(Dhcpv6SrvTest, clientClassify2) {
// This discover does not belong to foo class, so it will not
// be serviced
- EXPECT_FALSE(srv.selectSubnet(sol));
+ 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));
+ EXPECT_FALSE(srv_.selectSubnet(sol));
// Let's add it to maching class.
sol->addClass("foo");
// This time it should work
- EXPECT_TRUE(srv.selectSubnet(sol));
+ EXPECT_TRUE(srv_.selectSubnet(sol));
}
// This test checks that the server will handle a Solicit with the Vendor Class
@@ -1829,7 +1824,148 @@ TEST_F(Dhcpv6SrvTest, cableLabsShortVendorClass) {
// This is sent back to client, so port is 546
EXPECT_EQ(DHCP6_CLIENT_PORT, adv->getRemotePort());
+}
+
+// Checks if relay IP address specified in the relay-info structure in
+// subnet6 is being used properly.
+TEST_F(Dhcpv6SrvTest, relayOverride) {
+ // We have 2 subnets defined. Note that both have a relay address
+ // defined. Both are not belonging to the subnets. That is
+ // important, because if the relay belongs to the subnet, there's
+ // no need to specify relay override.
+ string config = "{ \"interfaces\": [ \"*\" ],"
+ "\"preferred-lifetime\": 3000,"
+ "\"rebind-timer\": 2000, "
+ "\"renew-timer\": 1000, "
+ "\"subnet6\": [ "
+ " { \"pool\": [ \"2001:db8:1::/64\" ],"
+ " \"subnet\": \"2001:db8:1::/48\", "
+ " \"relay\": { "
+ " \"ip-address\": \"2001:db8:3::1\""
+ " }"
+ " }, "
+ " { \"pool\": [ \"2001:db8:2::/64\" ],"
+ " \"subnet\": \"2001:db8:2::/48\", "
+ " \"relay\": { "
+ " \"ip-address\": \"2001:db8:3::2\""
+ " }"
+ " } "
+ "],"
+ "\"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().getSubnets6();
+ 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:1::1");
+ relay.peeraddr_ = IOAddress("fe80::1");
+
+ sol->relay_info_.push_back(relay);
+
+ // This is just a sanity check, we're using regular method: the relay
+ // belongs to the first (2001:db8:1::/64) subnet, so it's an easy decision.
+ EXPECT_TRUE(subnet1 == srv_.selectSubnet(sol));
+
+ // Relay belongs to the second subnet, so it should be selected.
+ sol->relay_info_.back().linkaddr_ = IOAddress("2001:db8:2::1");
+ EXPECT_TRUE(subnet2 == srv_.selectSubnet(sol));
+
+ // Now let's check if the relay override for the first subnets works
+ sol->relay_info_.back().linkaddr_ = IOAddress("2001:db8:3::1");
+ EXPECT_TRUE(subnet1 == srv_.selectSubnet(sol));
+
+ // Now repeat that for relay matching the second subnet.
+ sol->relay_info_.back().linkaddr_ = IOAddress("2001:db8:3::2");
+ EXPECT_TRUE(subnet2 == srv_.selectSubnet(sol));
+
+ // Finally, let's check that completely mismatched relay will not get us
+ // anything
+ sol->relay_info_.back().linkaddr_ = IOAddress("2001:db8:1234::1");
+ 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\": [ \"*\" ],"
+ "\"preferred-lifetime\": 3000,"
+ "\"rebind-timer\": 2000, "
+ "\"renew-timer\": 1000, "
+ "\"subnet6\": [ "
+ " { \"pool\": [ \"2001:db8:1::/64\" ],"
+ " \"subnet\": \"2001:db8:1::/48\", "
+ " \"client-class\": \"foo\", "
+ " \"relay\": { "
+ " \"ip-address\": \"2001:db8:3::1\""
+ " }"
+ " }, "
+ " { \"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().getSubnets6();
+ 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));
}
/// @todo: Add more negative tests for processX(), e.g. extend sanityCheck() test
diff --git a/src/bin/dhcp6/tests/dhcp6_test_utils.cc b/src/bin/dhcp6/tests/dhcp6_test_utils.cc
index 75a2ced..fb05067 100644
--- a/src/bin/dhcp6/tests/dhcp6_test_utils.cc
+++ b/src/bin/dhcp6/tests/dhcp6_test_utils.cc
@@ -14,13 +14,39 @@
#include <gtest/gtest.h>
#include <dhcp6/tests/dhcp6_test_utils.h>
+#include <dhcp6/config_parser.h>
+using namespace isc::data;
using namespace isc::dhcp;
using namespace isc::asiolink;
namespace isc {
namespace test {
+Dhcpv6SrvTest::Dhcpv6SrvTest()
+:srv_(0) {
+ subnet_ = isc::dhcp::Subnet6Ptr
+ (new isc::dhcp::Subnet6(isc::asiolink::IOAddress("2001:db8:1::"),
+ 48, 1000, 2000, 3000, 4000));
+ subnet_->setIface("eth0");
+
+ pool_ = isc::dhcp::Pool6Ptr
+ (new isc::dhcp::Pool6(isc::dhcp::Lease::TYPE_NA,
+ isc::asiolink::IOAddress("2001:db8:1:1::"),
+ 64));
+ subnet_->addPool(pool_);
+
+ isc::dhcp::CfgMgr::instance().deleteSubnets6();
+ isc::dhcp::CfgMgr::instance().addSubnet6(subnet_);
+
+ // configure PD pool
+ pd_pool_ = isc::dhcp::Pool6Ptr
+ (new isc::dhcp::Pool6(isc::dhcp::Lease::TYPE_PD,
+ isc::asiolink::IOAddress("2001:db8:1:2::"),
+ 64, 80));
+ subnet_->addPool(pd_pool_);
+}
+
// Checks that server response (ADVERTISE or REPLY) contains proper IA_NA option
// It returns IAADDR option for each chaining with checkIAAddr method.
boost::shared_ptr<Option6IAAddr>
@@ -125,6 +151,7 @@ Dhcpv6SrvTest::createMessage(uint8_t message_type, Lease::Type lease_type,
// Let's create a RENEW
Pkt6Ptr msg = Pkt6Ptr(new Pkt6(message_type, 1234));
msg->setRemoteAddr(IOAddress("fe80::abcd"));
+ msg->setIface("eth0");
uint16_t code;
OptionPtr subopt;
@@ -544,6 +571,19 @@ Dhcpv6SrvTest::testReleaseReject(Lease::Type type, const IOAddress& addr) {
EXPECT_TRUE(LeaseMgrFactory::instance().deleteLease(addr));
}
+void
+Dhcpv6SrvTest::configure(const std::string& config) {
+ ElementPtr json = data::Element::fromJSON(config);
+ ConstElementPtr status;
+
+ // Configure the server and make sure the config is accepted
+ EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
+ ASSERT_TRUE(status);
+ int rcode;
+ ConstElementPtr comment = config::parseAnswer(rcode, status);
+ ASSERT_EQ(0, rcode);
+}
+
// Generate IA_NA option with specified parameters
boost::shared_ptr<Option6IA>
diff --git a/src/bin/dhcp6/tests/dhcp6_test_utils.h b/src/bin/dhcp6/tests/dhcp6_test_utils.h
index f680e33..b68f7b5 100644
--- a/src/bin/dhcp6/tests/dhcp6_test_utils.h
+++ b/src/bin/dhcp6/tests/dhcp6_test_utils.h
@@ -340,26 +340,7 @@ public:
///
/// Sets up a single subnet6 with one pool for addresses and second
/// pool for prefixes.
- Dhcpv6SrvTest() {
- subnet_ = isc::dhcp::Subnet6Ptr
- (new isc::dhcp::Subnet6(isc::asiolink::IOAddress("2001:db8:1::"),
- 48, 1000, 2000, 3000, 4000));
- pool_ = isc::dhcp::Pool6Ptr
- (new isc::dhcp::Pool6(isc::dhcp::Lease::TYPE_NA,
- isc::asiolink::IOAddress("2001:db8:1:1::"),
- 64));
- subnet_->addPool(pool_);
-
- isc::dhcp::CfgMgr::instance().deleteSubnets6();
- isc::dhcp::CfgMgr::instance().addSubnet6(subnet_);
-
- // configure PD pool
- pd_pool_ = isc::dhcp::Pool6Ptr
- (new isc::dhcp::Pool6(isc::dhcp::Lease::TYPE_PD,
- isc::asiolink::IOAddress("2001:db8:1:2::"),
- 64, 80));
- subnet_->addPool(pd_pool_);
- }
+ Dhcpv6SrvTest();
/// @brief destructor
///
@@ -368,6 +349,12 @@ public:
isc::dhcp::CfgMgr::instance().deleteSubnets6();
};
+ /// @brief Runs DHCPv6 configuration from the JSON string.
+ ///
+ /// @param config String holding server configuration in JSON format.
+ void
+ configure(const std::string& config);
+
/// @brief Checks that server response (ADVERTISE or REPLY) contains proper
/// IA_NA option
///
@@ -526,6 +513,9 @@ public:
/// A prefix pool used in most tests
isc::dhcp::Pool6Ptr pd_pool_;
+
+ /// @brief Server object under test.
+ NakedDhcpv6Srv srv_;
};
}; // end of isc::test namespace
diff --git a/src/bin/dhcp6/tests/fqdn_unittest.cc b/src/bin/dhcp6/tests/fqdn_unittest.cc
index 7347958..904e4b1 100644
--- a/src/bin/dhcp6/tests/fqdn_unittest.cc
+++ b/src/bin/dhcp6/tests/fqdn_unittest.cc
@@ -141,6 +141,7 @@ public:
OptionPtr srvid = OptionPtr()) {
Pkt6Ptr pkt = Pkt6Ptr(new Pkt6(msg_type, 1234));
pkt->setRemoteAddr(IOAddress("fe80::abcd"));
+ pkt->setIface("eth0");
Option6IAPtr ia = generateIA(D6O_IA_NA, 234, 1500, 3000);
if (msg_type != DHCPV6_REPLY) {
@@ -944,6 +945,7 @@ TEST_F(FqdnDhcpv6SrvTest, processRequestReuseExpiredLease) {
CfgMgr::instance().deleteSubnets6();
subnet_ = Subnet6Ptr(new Subnet6(IOAddress("2001:db8:1:1::"), 56, 1, 2,
3, 4));
+ subnet_->setIface("eth0");
pool_ = Pool6Ptr(new Pool6(Lease::TYPE_NA, addr, addr));
subnet_->addPool(pool_);
CfgMgr::instance().addSubnet6(subnet_);
diff --git a/src/bin/dhcp6/tests/hooks_unittest.cc b/src/bin/dhcp6/tests/hooks_unittest.cc
index d13ebb4..3b3a66a 100644
--- a/src/bin/dhcp6/tests/hooks_unittest.cc
+++ b/src/bin/dhcp6/tests/hooks_unittest.cc
@@ -1066,6 +1066,7 @@ TEST_F(HooksDhcpv6SrvTest, basic_lease6_renew) {
// Let's create a RENEW
Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RENEW, 1234));
req->setRemoteAddr(IOAddress("fe80::abcd"));
+ req->setIface("eth0");
boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_NA, iaid, 1500, 3000);
OptionPtr renewed_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
@@ -1163,6 +1164,7 @@ TEST_F(HooksDhcpv6SrvTest, leaseUpdate_lease6_renew) {
// Let's create a RENEW
Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RENEW, 1234));
req->setRemoteAddr(IOAddress("fe80::abcd"));
+ req->setIface("eth0");
boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_NA, iaid, 1500, 3000);
OptionPtr renewed_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
@@ -1254,6 +1256,7 @@ TEST_F(HooksDhcpv6SrvTest, skip_lease6_renew) {
// Let's create a RENEW
Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RENEW, 1234));
req->setRemoteAddr(IOAddress("fe80::abcd"));
+ req->setIface("eth0");
boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_NA, iaid, 1500, 3000);
OptionPtr renewed_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
diff --git a/src/lib/dhcp/tests/Makefile.am b/src/lib/dhcp/tests/Makefile.am
index c04bd00..9237e07 100644
--- a/src/lib/dhcp/tests/Makefile.am
+++ b/src/lib/dhcp/tests/Makefile.am
@@ -36,6 +36,7 @@ noinst_LTLIBRARIES = libdhcptest.la
libdhcptest_la_SOURCES = iface_mgr_test_config.cc iface_mgr_test_config.h
libdhcptest_la_SOURCES += pkt_filter_test_stub.cc pkt_filter_test_stub.h
+libdhcptest_la_SOURCES += pkt_filter6_test_stub.cc pkt_filter6_test_stub.h
libdhcptest_la_CXXFLAGS = $(AM_CXXFLAGS)
libdhcptest_la_CPPFLAGS = $(AM_CPPFLAGS)
libdhcptest_la_LDFLAGS = $(AM_LDFLAGS)
@@ -73,6 +74,7 @@ libdhcp___unittests_SOURCES += pkt_filter_unittest.cc
libdhcp___unittests_SOURCES += pkt_filter_inet_unittest.cc
libdhcp___unittests_SOURCES += pkt_filter_inet6_unittest.cc
libdhcp___unittests_SOURCES += pkt_filter_test_stub.cc pkt_filter_test_stub.h
+libdhcp___unittests_SOURCES += pkt_filter6_test_stub.cc pkt_filter_test_stub.h
libdhcp___unittests_SOURCES += pkt_filter_test_utils.h pkt_filter_test_utils.cc
libdhcp___unittests_SOURCES += pkt_filter6_test_utils.h pkt_filter6_test_utils.cc
diff --git a/src/lib/dhcp/tests/iface_mgr_test_config.cc b/src/lib/dhcp/tests/iface_mgr_test_config.cc
index 8da7e6e..14beee8 100644
--- a/src/lib/dhcp/tests/iface_mgr_test_config.cc
+++ b/src/lib/dhcp/tests/iface_mgr_test_config.cc
@@ -14,8 +14,10 @@
#include <dhcp/pkt_filter.h>
#include <dhcp/pkt_filter_inet.h>
+#include <dhcp/pkt_filter_inet6.h>
#include <dhcp/tests/iface_mgr_test_config.h>
#include <dhcp/tests/pkt_filter_test_stub.h>
+#include <dhcp/tests/pkt_filter6_test_stub.h>
using namespace isc::asiolink;
@@ -27,7 +29,9 @@ IfaceMgrTestConfig::IfaceMgrTestConfig(const bool default_config) {
IfaceMgr::instance().closeSockets();
IfaceMgr::instance().clearIfaces();
packet_filter4_ = PktFilterPtr(new PktFilterTestStub());
+ packet_filter6_ = PktFilter6Ptr(new PktFilter6TestStub());
IfaceMgr::instance().setPacketFilter(packet_filter4_);
+ IfaceMgr::instance().setPacketFilter(packet_filter6_);
// Create default set of fake interfaces: lo, eth0 and eth1.
if (default_config) {
@@ -39,6 +43,7 @@ IfaceMgrTestConfig::~IfaceMgrTestConfig() {
IfaceMgr::instance().closeSockets();
IfaceMgr::instance().clearIfaces();
IfaceMgr::instance().setPacketFilter(PktFilterPtr(new PktFilterInet()));
+ IfaceMgr::instance().setPacketFilter(PktFilter6Ptr(new PktFilterInet6()));
IfaceMgr::instance().detectIfaces();
}
diff --git a/src/lib/dhcp/tests/iface_mgr_test_config.h b/src/lib/dhcp/tests/iface_mgr_test_config.h
index a2ceba5..d7d08df 100644
--- a/src/lib/dhcp/tests/iface_mgr_test_config.h
+++ b/src/lib/dhcp/tests/iface_mgr_test_config.h
@@ -232,6 +232,8 @@ private:
/// @brief Currently used packet filter for DHCPv4.
PktFilterPtr packet_filter4_;
+ /// @brief Currently used packet filter for DHCPv6.
+ PktFilter6Ptr packet_filter6_;
};
};
diff --git a/src/lib/dhcp/tests/pkt_filter6_test_stub.cc b/src/lib/dhcp/tests/pkt_filter6_test_stub.cc
new file mode 100644
index 0000000..b752eaa
--- /dev/null
+++ b/src/lib/dhcp/tests/pkt_filter6_test_stub.cc
@@ -0,0 +1,49 @@
+// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <dhcp/tests/pkt_filter6_test_stub.h>
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+PktFilter6TestStub::PktFilter6TestStub() {
+}
+
+SocketInfo
+PktFilter6TestStub::openSocket(const Iface&,
+ const isc::asiolink::IOAddress& addr,
+ const uint16_t port, const bool) {
+ return (SocketInfo(addr, port, 0));
+}
+
+Pkt6Ptr
+PktFilter6TestStub::receive(const SocketInfo&) {
+ return Pkt6Ptr();
+}
+
+bool
+PktFilter6TestStub::joinMulticast(int, const std::string&,
+ const std::string &) {
+ return (true);
+}
+
+int
+PktFilter6TestStub::send(const Iface&, uint16_t, const Pkt6Ptr&) {
+ return (0);
+}
+
+}
+}
+}
diff --git a/src/lib/dhcp/tests/pkt_filter6_test_stub.h b/src/lib/dhcp/tests/pkt_filter6_test_stub.h
new file mode 100644
index 0000000..8a0c567
--- /dev/null
+++ b/src/lib/dhcp/tests/pkt_filter6_test_stub.h
@@ -0,0 +1,99 @@
+// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef PKT_FILTER6_TEST_STUB_H
+#define PKT_FILTER6_TEST_STUB_H
+
+#include <asiolink/io_address.h>
+#include <dhcp/iface_mgr.h>
+#include <dhcp/pkt_filter6.h>
+#include <dhcp/pkt6.h>
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+/// @brief A stub implementation of the PktFilter6 class.
+///
+/// This class implements abstract methods of the @c isc::dhcp::PktFilter6
+/// class. It is used by unit tests, which test protected methods of the
+/// @c isc::dhcp::test::PktFilter6 class. The implemented abstract methods are
+/// no-op.
+class PktFilter6TestStub : public PktFilter6 {
+public:
+
+ /// @brief Constructor.
+ PktFilter6TestStub();
+
+ /// @brief Simulate opening of the socket.
+ ///
+ /// This function simulates opening a primary socket. In reality, it doesn't
+ /// open a socket but the socket descriptor returned in the SocketInfo
+ /// structure is always set to 0.
+ ///
+ /// @param iface An interface descriptor.
+ /// @param addr Address on the interface to be used to send packets.
+ /// @param port Port number to bind socket to.
+ /// @param join_multicast A flag which indicates if the socket should be
+ /// configured to join multicast (if true).
+ ///
+ /// @note All parameters are ignored.
+ ///
+ /// @return A SocketInfo structure with the socket descriptor set to 0. The
+ /// fallback socket descriptor is set to a negative value.
+ virtual SocketInfo openSocket(const Iface& iface,
+ const isc::asiolink::IOAddress& addr,
+ const uint16_t port,
+ const bool join_multicast);
+
+ /// @brief Simulate reception of the DHCPv6 message.
+ ///
+ /// @param socket_info A structure holding socket information.
+ ///
+ /// @note All parameters are ignored.
+ ///
+ /// @return always a NULL object.
+ virtual Pkt6Ptr receive(const SocketInfo& socket_info);
+
+ /// @brief Simulates sending a DHCPv6 message.
+ ///
+ /// This function does nothing.
+ ///
+ /// @param iface An interface to be used to send DHCPv6 message.
+ /// @param port A port used to send a message.
+ /// @param pkt A DHCPv6 to be sent.
+ ///
+ /// @note All parameters are ignored.
+ ///
+ /// @return 0.
+ virtual int send(const Iface& iface, uint16_t port, const Pkt6Ptr& pkt);
+
+ /// @brief Simulate joining IPv6 multicast group on a socket.
+ ///
+ /// @note All parameters are ignored.
+ ///
+ /// @param sock A socket descriptor (socket must be bound).
+ /// @param ifname An interface name (for link-scoped multicast groups).
+ /// @param mcast A multicast address to join (e.g. "ff02::1:2").
+ ///
+ /// @return true if multicast join was successful
+ static bool joinMulticast(int sock, const std::string& ifname,
+ const std::string & mcast);
+};
+
+} // namespace isc::dhcp::test
+} // namespace isc::dhcp
+} // namespace isc
+
+#endif // PKT_FILTER6_TEST_STUB_H
diff --git a/src/lib/dhcpsrv/cfgmgr.cc b/src/lib/dhcpsrv/cfgmgr.cc
index 960cfd2..65b9dd7 100644
--- a/src/lib/dhcpsrv/cfgmgr.cc
+++ b/src/lib/dhcpsrv/cfgmgr.cc
@@ -154,22 +154,8 @@ CfgMgr::getSubnet6(const std::string& iface,
Subnet6Ptr
CfgMgr::getSubnet6(const isc::asiolink::IOAddress& hint,
- const isc::dhcp::ClientClasses& classes) {
-
- // If there's only one subnet configured, let's just use it
- // The idea is to keep small deployments easy. In a small network - one
- // router that also runs DHCPv6 server. User specifies a single pool and
- // expects it to just work. Without this, the server would complain that it
- // doesn't have IP address on its interfaces that matches that
- // configuration. Such requirement makes sense in IPv4, but not in IPv6.
- // The server does not need to have a global address (using just link-local
- // is ok for DHCPv6 server) from the pool it serves.
- if ((subnets6_.size() == 1) && hint.isV6LinkLocal()) {
- LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
- DHCPSRV_CFGMGR_ONLY_SUBNET6)
- .arg(subnets6_[0]->toText()).arg(hint.toText());
- return (subnets6_[0]);
- }
+ const isc::dhcp::ClientClasses& classes,
+ const bool relay) {
// If there is more than one, we need to choose the proper one
for (Subnet6Collection::iterator subnet = subnets6_.begin();
@@ -180,9 +166,17 @@ CfgMgr::getSubnet6(const isc::asiolink::IOAddress& hint,
continue;
}
- if ((*subnet)->inRange(hint)) {
+ // If the hint is a relay address, and there is relay info specified
+ // for this subnet and those two match, then use this subnet.
+ if (relay && ((*subnet)->getRelayInfo().addr_ == hint) ) {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
- DHCPSRV_CFGMGR_SUBNET6)
+ DHCPSRV_CFGMGR_SUBNET6_RELAY)
+ .arg((*subnet)->toText()).arg(hint.toText());
+ return (*subnet);
+ }
+
+ if ((*subnet)->inRange(hint)) {
+ LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_SUBNET6)
.arg((*subnet)->toText()).arg(hint.toText());
return (*subnet);
}
@@ -232,7 +226,8 @@ void CfgMgr::addSubnet6(const Subnet6Ptr& subnet) {
Subnet4Ptr
CfgMgr::getSubnet4(const isc::asiolink::IOAddress& hint,
- const isc::dhcp::ClientClasses& classes) const {
+ const isc::dhcp::ClientClasses& classes,
+ bool relay) const {
// Iterate over existing subnets to find a suitable one for the
// given address.
for (Subnet4Collection::const_iterator subnet = subnets4_.begin();
@@ -243,6 +238,15 @@ CfgMgr::getSubnet4(const isc::asiolink::IOAddress& hint,
continue;
}
+ // If the hint is a relay address, and there is relay info specified
+ // for this subnet and those two match, then use this subnet.
+ if (relay && ((*subnet)->getRelayInfo().addr_ == hint) ) {
+ LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
+ DHCPSRV_CFGMGR_SUBNET4_RELAY)
+ .arg((*subnet)->toText()).arg(hint.toText());
+ return (*subnet);
+ }
+
// Let's check if the client belongs to the given subnet
if ((*subnet)->inRange(hint)) {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
diff --git a/src/lib/dhcpsrv/cfgmgr.h b/src/lib/dhcpsrv/cfgmgr.h
index ca854d6..31ef534 100644
--- a/src/lib/dhcpsrv/cfgmgr.h
+++ b/src/lib/dhcpsrv/cfgmgr.h
@@ -169,12 +169,23 @@ public:
/// If there are any classes specified in a subnet, that subnet
/// will be selected only if the client belongs to appropriate class.
///
+ /// @note The client classification is checked before any relay
+ /// information checks are conducted.
+ ///
+ /// If relay is true then relay info overrides (i.e. value the sysadmin
+ /// can configure in Dhcp6/subnet6[X]/relay/ip-address) can be used.
+ /// That is applicable only for relays. Those overrides must not be used
+ /// for client address or for client hints. They are for link-addr field
+ /// in the RELAY_FORW message only.
+ ///
/// @param hint an address that belongs to a searched subnet
/// @param classes classes the client belongs to
+ /// @param relay true if address specified in hint is a relay
///
/// @return a subnet object (or NULL if no suitable match was fount)
Subnet6Ptr getSubnet6(const isc::asiolink::IOAddress& hint,
- const isc::dhcp::ClientClasses& classes);
+ const isc::dhcp::ClientClasses& classes,
+ const bool relay = false);
/// @brief get IPv6 subnet by interface name
///
@@ -262,12 +273,19 @@ public:
/// If there are any classes specified in a subnet, that subnet
/// will be selected only if the client belongs to appropriate class.
///
+ /// If relay is true then relay info overrides (i.e. value the sysadmin
+ /// can configure in Dhcp4/subnet4[X]/relay/ip-address) can be used.
+ /// That is true only for relays. Those overrides must not be used
+ /// for client address or for client hints. They are for giaddr only.
+ ///
/// @param hint an address that belongs to a searched subnet
/// @param classes classes the client belongs to
+ /// @param relay true if address specified in hint is a relay
///
/// @return a subnet object
Subnet4Ptr getSubnet4(const isc::asiolink::IOAddress& hint,
- const isc::dhcp::ClientClasses& classes) const;
+ const isc::dhcp::ClientClasses& classes,
+ bool relay = false) const;
/// @brief Returns a subnet for the specified local interface.
///
diff --git a/src/lib/dhcpsrv/dhcpsrv_messages.mes b/src/lib/dhcpsrv/dhcpsrv_messages.mes
index 5151851..2cf2ba7 100644
--- a/src/lib/dhcpsrv/dhcpsrv_messages.mes
+++ b/src/lib/dhcpsrv/dhcpsrv_messages.mes
@@ -112,11 +112,21 @@ This is a debug message reporting that the DHCP configuration manager has
returned the specified IPv4 subnet when given the address hint specified
as the address is within the subnet.
+% DHCPSRV_CFGMGR_SUBNET4_RELAY selected subnet %1, because of matching relay addr %2
+This is a debug message reporting that the DHCP configuration manager has
+returned the specified IPv4 subnet, because detected relay agent address
+matches value specified for this subnet.
+
% DHCPSRV_CFGMGR_SUBNET6 retrieved subnet %1 for address hint %2
This is a debug message reporting that the DHCP configuration manager has
returned the specified IPv6 subnet when given the address hint specified
as the address is within the subnet.
+% DHCPSRV_CFGMGR_SUBNET6_RELAY selected subnet %1, because of matching relay addr %2
+This is a debug message reporting that the DHCP configuration manager has
+returned the specified IPv6 subnet, because detected relay agent address
+matches value specified for this subnet.
+
% DHCPSRV_CFGMGR_SUBNET6_IFACE selected subnet %1 for packet received over interface %2
This is a debug message reporting that the DHCP configuration manager
has returned the specified IPv6 subnet for a packet received over
diff --git a/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc b/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc
index ee54c3d..6cffed5 100644
--- a/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc
+++ b/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc
@@ -472,6 +472,79 @@ TEST_F(CfgMgrTest, classifySubnet4) {
EXPECT_FALSE(cfg_mgr.getSubnet4(IOAddress("192.0.2.130"), classify_));
}
+// This test verifies if the configuration manager is able to hold v4 subnets
+// with their relay address information and return proper subnets, based on
+// those addresses.
+TEST_F(CfgMgrTest, subnet4RelayOverride) {
+ CfgMgr& cfg_mgr = CfgMgr::instance();
+
+ // Let's configure 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));
+
+ cfg_mgr.addSubnet4(subnet1);
+ cfg_mgr.addSubnet4(subnet2);
+ cfg_mgr.addSubnet4(subnet3);
+
+ // Check that without relay-info specified, subnets are not selected
+ EXPECT_FALSE(cfg_mgr.getSubnet4(IOAddress("10.0.0.1"), classify_, true));
+ EXPECT_FALSE(cfg_mgr.getSubnet4(IOAddress("10.0.0.2"), classify_, true));
+ EXPECT_FALSE(cfg_mgr.getSubnet4(IOAddress("10.0.0.3"), classify_, true));
+
+ // Now specify relay info
+ subnet1->setRelayInfo(IOAddress("10.0.0.1"));
+ subnet2->setRelayInfo(IOAddress("10.0.0.2"));
+ subnet3->setRelayInfo(IOAddress("10.0.0.3"));
+
+ // And try again. This time relay-info is there and should match.
+ EXPECT_EQ(subnet1, cfg_mgr.getSubnet4(IOAddress("10.0.0.1"), classify_, true));
+ EXPECT_EQ(subnet2, cfg_mgr.getSubnet4(IOAddress("10.0.0.2"), classify_, true));
+ EXPECT_EQ(subnet3, cfg_mgr.getSubnet4(IOAddress("10.0.0.3"), classify_, true));
+
+ // Finally, check that the relay works only if hint provided is relay address
+ EXPECT_FALSE(cfg_mgr.getSubnet4(IOAddress("10.0.0.1"), classify_, false));
+ EXPECT_FALSE(cfg_mgr.getSubnet4(IOAddress("10.0.0.2"), classify_, false));
+ EXPECT_FALSE(cfg_mgr.getSubnet4(IOAddress("10.0.0.3"), classify_, false));
+}
+
+// This test verifies if the configuration manager is able to hold v6 subnets
+// with their relay address information and return proper subnets, based on
+// those addresses.
+TEST_F(CfgMgrTest, subnet6RelayOverride) {
+ CfgMgr& cfg_mgr = CfgMgr::instance();
+
+ // Let's configure 3 subnets
+ Subnet6Ptr subnet1(new Subnet6(IOAddress("2001:db8:1::"), 48, 1, 2, 3, 4));
+ Subnet6Ptr subnet2(new Subnet6(IOAddress("2001:db8:2::"), 48, 1, 2, 3, 4));
+ Subnet6Ptr subnet3(new Subnet6(IOAddress("2001:db8:3::"), 48, 1, 2, 3, 4));
+
+ cfg_mgr.addSubnet6(subnet1);
+ cfg_mgr.addSubnet6(subnet2);
+ cfg_mgr.addSubnet6(subnet3);
+
+ // Check that without relay-info specified, subnets are not selected
+ EXPECT_FALSE(cfg_mgr.getSubnet6(IOAddress("2001:db8:ff::1"), classify_, true));
+ EXPECT_FALSE(cfg_mgr.getSubnet6(IOAddress("2001:db8:ff::2"), classify_, true));
+ EXPECT_FALSE(cfg_mgr.getSubnet6(IOAddress("2001:db8:ff::3"), classify_, true));
+
+ // Now specify relay info
+ subnet1->setRelayInfo(IOAddress("2001:db8:ff::1"));
+ subnet2->setRelayInfo(IOAddress("2001:db8:ff::2"));
+ subnet3->setRelayInfo(IOAddress("2001:db8:ff::3"));
+
+ // And try again. This time relay-info is there and should match.
+ EXPECT_EQ(subnet1, cfg_mgr.getSubnet6(IOAddress("2001:db8:ff::1"), classify_, true));
+ EXPECT_EQ(subnet2, cfg_mgr.getSubnet6(IOAddress("2001:db8:ff::2"), classify_, true));
+ EXPECT_EQ(subnet3, cfg_mgr.getSubnet6(IOAddress("2001:db8:ff::3"), classify_, true));
+
+ // Finally, check that the relay works only if hint provided is relay address
+ EXPECT_FALSE(cfg_mgr.getSubnet6(IOAddress("2001:db8:ff::1"), classify_, false));
+ EXPECT_FALSE(cfg_mgr.getSubnet6(IOAddress("2001:db8:ff::2"), classify_, false));
+ EXPECT_FALSE(cfg_mgr.getSubnet6(IOAddress("2001:db8:ff::3"), classify_, false));
+}
+
+
// This test verifies if the configuration manager is able to hold and return
// valid leases
TEST_F(CfgMgrTest, classifySubnet6) {
@@ -626,10 +699,10 @@ TEST_F(CfgMgrTest, subnet6) {
// Now we have only one subnet, any request will be served from it
EXPECT_EQ(subnet1, cfg_mgr.getSubnet6(IOAddress("2000::1"), classify_));
- // If we have only a single subnet and the request came from a local
- // address, let's use that subnet
- EXPECT_EQ(subnet1, cfg_mgr.getSubnet6(IOAddress("fe80::dead:beef"),
- classify_));
+ // We used to allow getting a sole subnet if there was only one subnet
+ // configured. That is no longer true. The code should not return
+ // a subnet.
+ EXPECT_FALSE(cfg_mgr.getSubnet6(IOAddress("fe80::dead:beef"), classify_));
cfg_mgr.addSubnet6(subnet2);
cfg_mgr.addSubnet6(subnet3);
@@ -670,10 +743,10 @@ TEST_F(CfgMgrTest, subnet6Interface) {
// only one subnet defined.
EXPECT_FALSE(cfg_mgr.getSubnet6("bar", classify_));
- // If we have only a single subnet and the request came from a local
- // address, let's use that subnet
- EXPECT_EQ(subnet1, cfg_mgr.getSubnet6(IOAddress("fe80::dead:beef"),
- classify_));
+ // We used to allow getting a sole subnet if there was only one subnet
+ // configured. That is no longer true. The code should not return
+ // a subnet.
+ EXPECT_FALSE(cfg_mgr.getSubnet6(IOAddress("fe80::dead:beef"), classify_));
cfg_mgr.addSubnet6(subnet2);
cfg_mgr.addSubnet6(subnet3);
More information about the bind10-changes
mailing list