-8xx. [func] tomek
++815. [func] tomek
+ Pool definitions in DHCPv4 and DHCPv6 are now lists of
+ structures. This makes adding new per-pool parameters easier in
+ the future.
- (Trac #3464, git tbd)
++ (Trac #3464, git 4bd0c0eda9d86608f8802d28bd360239fe88e905)
++
+814. [func,doc] tomek
+ It is now possible to specify logging parameters in a
+ configuration file for DHCPv4, DHCPv6 and DHCP-DDNS components.
+ (Trac #3427, git 23285903645c36fc35c6866a74c50c74089cd255)
+
+813. [func] tomek
+ Functions, methods and variables referring to BIND10 were renamed
+ to Kea. In particular, system variables (B10_LOGGER_ROOT,
+ B10_LOCKFILE_DIR_FROM_BUILD etc.) were renamed. B10_ prefix was
+ replaced with KEA_.
+ (Trac #3417, git 1db8988de6af435fa388dc9c7f909c4a004a01d0)
+
+812. [doc] tomek
+ DHCPv6 and DDNS sections in Kea Administrator Reference Manual
+ has been updated. Usage of keactl has been documented.
+ (Trac #3468, git 3945fc6211bcadb9bece7147039a6b50ebcf936b)
+ (Trac #3466, git fa9570d19c73cbe7effc75589b7eb855c411f6a3)
+
+811. [doc] tmark
+ Added documentation of message protocol between DHCP servers and the
+ DHCP-DDNS process.
+ (Trac #3505, git 6d9aed2f8fe181714e8260493c6cc06e13d0edd0)
+
+810. [func] stephen
+ perfdhcp is now installed in sbin as it requires root privilege to run.
+ The perfdhcp source has been moved to the directory src/bin/perfdhcp.
+ (Trac #3481, git d101aed6156a993476fa1164f0b0ec8395f5886c)
+
+809. [func] stephen
+ sockcreator is no longer built or installed. The code is being retained
+ in the repository for the moment, but may be deleted at some point in the
+ future.
+ (Trac #3480, git 2a55a469dde8fcc053b49e287c30d0906baa91b4)
+
+808. [func] stephen
+ Reduced number of startup and shutdown messages in the
+ DHCP-DDNS process by making some of them debug messages.
+ (Trac #3479, git bca0bae285de9ce904c0afd21af777dac2edb4e6)
+
+807. [func] marcin
+ DHCPv6 server responds to Confirm messages from clients.
+ (Trac #3269, git 4f43c309a994e30c07f5aa27057552fb195ec284)
+
+806. [func] marcin
+ DHCPv4 server processes Requested IP Address option (50).
+ (Trac #3320, git ad411a177a32bbe6a93f4baf813d985558c99e2f)
+
+805. [func] stephen
+ Changed all occurrences of "BIND 10" in message files to "Kea".
+ (Trac #3416, git e88090b57a75424920d9b96efbf50e3554048828)
804. [func] marcin
DHCPv4 server supports DHCPINFORM messages from the clients.
# The following list defines subnets. Each subnet consists of at
# least subnet and pool entries.
- "subnet4": [
- { "pool": [ "192.0.2.1 - 192.0.2.200" ],
+ "subnet4": [
+ { "pools": [ { "pool": "192.0.2.1 - 192.0.2.200" } ],
"subnet": "192.0.2.0/24" },
- { "pool": [ "192.0.3.100 - 192.0.3.200" ],
+ { "pools": [ { "pool": "192.0.3.100 - 192.0.3.200" } ],
"subnet": "192.0.3.0/24" },
- { "pool": [ "192.0.4.1 - 192.0.4.254" ],
+ { "pools": [ { "pool": "192.0.4.1 - 192.0.4.254" } ],
"subnet": "192.0.4.0/24" } ]
+},
+
+# The following configures logging. Kea will log all debug messages
+# to /var/log/kea-debug.log file.
+"Logging": {
+ "loggers": [
+ {
+ "name": "kea-dhcp4",
+ "output_options": [
+ {
+ "output": "/var/log/kea-debug.log"
+ }
+ ],
+ "debuglevel": 99,
+ "severity": "DEBUG"
+ }
+ ]
}
}
# "rebind-timer": 2000,
# The following list defines subnets. We have only one subnet
-# here.
+# here. We tell Kea that it is directly available over local interface.
"subnet4": [
- { "pool": [ "192.0.2.1 - 192.0.2.200" ],
+ {
- "pools": [ { "pool": "192.0.2.1 - 192.0.2.200" } ],
- "subnet": "192.0.2.0/24"
- }
++ "pools": [ { "pool": "192.0.2.1 - 192.0.2.200" } ],
+ "subnet": "192.0.2.0/24",
- "interface": "eth0" } ]
++ "interface": "eth0"
++ }
+ ]
+},
+
+# The following configures logging. It assumes that messages with at least
+# informational level (info, warn, error) will will be logged to stdout.
+"Logging": {
+ "loggers": [
+ {
+ "name": "kea-dhcp4",
+ "output_options": [
+ {
+ "output": "stdout"
+ }
+ ],
+ "severity": "INFO"
+ }
+ ]
}
}
# The following list defines subnets. Each subnet consists of at
# least subnet and pool entries.
- "subnet6": [
- { "pool": [ "2001:db8:1::/80" ],
+ "subnet6": [
+ { "pools": [ { "pool": "2001:db8:1::/80" } ],
"subnet": "2001:db8:1::/64" },
- { "pool": [ "2001:db8:2::/80" ],
- "subnet": "2001:db8:2::/64" },
- { "pool": [ "2001:db8:3::/80" ],
+ { "pools": [ { "pool": "2001:db8:2::/80" } ],
+ "subnet": "2001:db8:2::/64" },
+ { "pools": [ { "pool": "2001:db8:3::/80" } ],
"subnet": "2001:db8:3::/64" },
- { "pool": [ "2001:db8:4::/80" ],
+ { "pools": [ { "pool": "2001:db8:4::/80" } ],
"subnet": "2001:db8:4::/64" } ]
-}
+},
+# The following configures logging. It assumes that warning messages
+# will be logged to stdout.
+"Logging": {
+ "loggers": [
+ {
+ "name": "kea-dhcp6",
+ "output_options": [
+ {
+ "output": "stdout"
+ }
+ ],
+ "severity": "WARN"
+ }
+ ]
}
+}
is actually a list of pools: for this reason, the pool definition is
enclosed in square brackets, even though only one range of addresses
is specified.</para>
++
++ <para>Each <command>pool</command> is a structure that contains the
++ parameters th describe a single pool. Currently there is only one
++ parameter, <command>pool</command>, which gives the range of addresses
++ in the pool. Additional parameters will be added in future releases of
++ Kea.</para>
++
<para>It is possible to define more than one pool in a
subnet: continuing the previous example, further assume that
2001:db8:1:0:5::/80 should be also be managed by the server. It could be written as
\"subnet4\": [
{
\"subnet\": \"10.0.0.0/8\",
- \"pool\": [ \"10.0.0.10-10.0.0.100\" ]
+ \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ]
} ]
+ },
+
+ \"Logging\":
+ {
+ \"loggers\": [
+ {
+ \"name\": \"kea-dhcp4\",
+ \"output_options\": [
+ {
+ \"output\": \"$LOG_FILE\"
+ }
+ ],
+ \"severity\": \"INFO\"
+ }
+ ]
}
}"
# Invalid configuration (negative valid-lifetime) to check that Kea
\"subnet4\": [
{
\"subnet\": \"10.0.0.0/8\",
- \"pool\": [ \"10.0.0.10-10.0.0.100\" ]
+ \"pool\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ]
} ]
+ },
+
+ \"Logging\":
+ {
+ \"loggers\": [
+ {
+ \"name\": \"kea-dhcp4\",
+ \"output_options\": [
+ {
+ \"output\": \"$LOG_FILE\"
+ }
+ ],
+ \"severity\": \"INFO\"
+ }
+ ]
}
}"
--- /dev/null
- " \"pool\": [ \"10.0.0.10-10.0.0.100\" ],"
+// 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 <config.h>
+#include <asiolink/io_address.h>
+#include <cc/data.h>
+#include <dhcp/dhcp4.h>
+#include <dhcp/tests/iface_mgr_test_config.h>
+#include <dhcp4/tests/dhcp4_test_utils.h>
+#include <dhcp4/tests/dhcp4_client.h>
+#include <boost/shared_ptr.hpp>
+
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::data;
+using namespace isc::dhcp;
+using namespace isc::dhcp::test;
+
+namespace {
+
+/// @brief Set of JSON configurations used throughout the DORA tests.
+///
+/// - Configuration 0:
+/// - Used for testing direct traffic
+/// - 1 subnet: 10.0.0.0/24
+/// - 1 pool: 10.0.0.10-10.0.0.100
+/// - Router option present: 10.0.0.200 and 10.0.0.201
+/// - Domain Name Server option present: 10.0.0.202, 10.0.0.203.
+/// - Log Servers option present: 192.0.2.200 and 192.0.2.201
+/// - Quotes Servers option present: 192.0.2.202, 192.0.2.203.
+///
+/// - Configuration 1:
+/// - Use for testing relayed messages
+/// - 1 subnet: 192.0.2.0/24
+/// - Router option present: 192.0.2.200 and 192.0.2.201
+/// - Domain Name Server option present: 192.0.2.202, 192.0.2.203.
+/// - Log Servers option present: 192.0.2.200 and 192.0.2.201
+/// - Quotes Servers option present: 192.0.2.202, 192.0.2.203.
+const char* DORA_CONFIGS[] = {
+// Configuration 0
+ "{ \"interfaces\": [ \"all\" ],"
+ "\"valid-lifetime\": 600,"
+ "\"subnet4\": [ { "
+ " \"subnet\": \"10.0.0.0/24\", "
++ " \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ],"
+ " \"option-data\": [ {"
+ " \"name\": \"routers\","
+ " \"code\": 3,"
+ " \"data\": \"10.0.0.200,10.0.0.201\","
+ " \"csv-format\": true,"
+ " \"space\": \"dhcp4\""
+ " },"
+ " {"
+ " \"name\": \"domain-name-servers\","
+ " \"code\": 6,"
+ " \"data\": \"10.0.0.202,10.0.0.203\","
+ " \"csv-format\": true,"
+ " \"space\": \"dhcp4\""
+ " },"
+ " {"
+ " \"name\": \"log-servers\","
+ " \"code\": 7,"
+ " \"data\": \"10.0.0.200,10.0.0.201\","
+ " \"csv-format\": true,"
+ " \"space\": \"dhcp4\""
+ " },"
+ " {"
+ " \"name\": \"cookie-servers\","
+ " \"code\": 8,"
+ " \"data\": \"10.0.0.202,10.0.0.203\","
+ " \"csv-format\": true,"
+ " \"space\": \"dhcp4\""
+ " } ]"
+ " } ]"
+ "}",
+
+// Configuration 1
+ "{ \"interfaces\": [ \"all\" ],"
+ "\"valid-lifetime\": 600,"
+ "\"subnet4\": [ { "
+ " \"subnet\": \"192.0.2.0/24\", "
+ " \"option-data\": [ {"
+ " \"name\": \"routers\","
+ " \"code\": 3,"
+ " \"data\": \"192.0.2.200,192.0.2.201\","
+ " \"csv-format\": true,"
+ " \"space\": \"dhcp4\""
+ " },"
+ " {"
+ " \"name\": \"domain-name-servers\","
+ " \"code\": 6,"
+ " \"data\": \"192.0.2.202,192.0.2.203\","
+ " \"csv-format\": true,"
+ " \"space\": \"dhcp4\""
+ " },"
+ " {"
+ " \"name\": \"log-servers\","
+ " \"code\": 7,"
+ " \"data\": \"10.0.0.200,10.0.0.201\","
+ " \"csv-format\": true,"
+ " \"space\": \"dhcp4\""
+ " },"
+ " {"
+ " \"name\": \"cookie-servers\","
+ " \"code\": 8,"
+ " \"data\": \"10.0.0.202,10.0.0.203\","
+ " \"csv-format\": true,"
+ " \"space\": \"dhcp4\""
+ " } ]"
+ " } ]"
+ "}"
+};
+
+/// @brief Test fixture class for testing 4-way (DORA) exchanges.
+///
+/// @todo Currently there is a limit number of test cases covered here.
+/// In the future it is planned that the tests from the
+/// dhcp4_srv_unittest.cc will be migrated here and will use the
+/// @c Dhcp4Client class.
+class DORATest : public Dhcpv4SrvTest {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// Sets up fake interfaces.
+ DORATest()
+ : Dhcpv4SrvTest(),
+ iface_mgr_test_config_(true) {
+ IfaceMgr::instance().openSockets4();
+ }
+
+ /// @brief Interface Manager's fake configuration control.
+ IfaceMgrTestConfig iface_mgr_test_config_;
+
+};
+
+/// This test verifies that the client in the SELECTING state can get
+/// an address when it doesn't request any specific address in the
+/// DHCPDISCOVER message.
+TEST_F(DORATest, selectingDoNotRequestAddress) {
+ Dhcp4Client client(Dhcp4Client::SELECTING);
+ // Configure DHCP server.
+ configure(DORA_CONFIGS[0], *client.getServer());
+
+ // Perform 4-way exchange with the server but to not request any
+ // specific address in the DHCPDISCOVER message.
+ ASSERT_NO_THROW(client.doDORA());
+
+ // Make sure that the server responded.
+ ASSERT_TRUE(client.getContext().response_);
+ Pkt4Ptr resp = client.getContext().response_;
+ // Make sure that the server has responded with DHCPACK.
+ ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
+ // Response must not be relayed.
+ EXPECT_FALSE(resp->isRelayed());
+ // Make sure that the server id is present.
+ EXPECT_EQ("10.0.0.1", client.config_.serverid_.toText());
+ // Make sure that the client has got the lease with the requested address.
+ ASSERT_NE(client.config_.lease_.addr_.toText(), "0.0.0.0");
+}
+
+/// This test verifies that multiple clients may use the DHCPv4 server
+/// and obtain unique leases.
+TEST_F(DORATest, selectingMultipleClients) {
+ Dhcp4Client client(Dhcp4Client::SELECTING);
+ // Configure DHCP server.
+ configure(DORA_CONFIGS[0], *client.getServer());
+
+ // Get the first lease.
+ ASSERT_NO_THROW(client.doDORA());
+
+ // Make sure that the server responded.
+ Pkt4Ptr resp = client.getContext().response_;
+ ASSERT_TRUE(resp);
+ ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
+ // Store the lease.
+ Lease4 lease1 = client.config_.lease_;
+
+ // Get the lease for a different client.
+ client.modifyHWAddr();
+ ASSERT_NO_THROW(client.doDORA());
+ // Make sure that the server responded.
+ resp = client.getContext().response_;
+ ASSERT_TRUE(resp);
+ ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
+ // Store the lease.
+ Lease4 lease2 = client.config_.lease_;
+
+ // Get the lease for a different client.
+ client.modifyHWAddr();
+ ASSERT_NO_THROW(client.doDORA());
+ // Make sure that the server responded.
+ resp = client.getContext().response_;
+ ASSERT_TRUE(resp);
+ ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
+ // Store the lease.
+ Lease4 lease3 = client.config_.lease_;
+
+ // Make sure that unique addresses have been assigned.
+ EXPECT_NE(lease1.addr_, lease2.addr_);
+ EXPECT_NE(lease2.addr_, lease3.addr_);
+ EXPECT_NE(lease1.addr_, lease3.addr_);
+}
+
+// This test verifies that the client in a SELECTING state can request
+// a specific address and that this address will be assigned when
+// available. It also tests that if the client requests an address which
+// is in use the client will get a different address.
+TEST_F(DORATest, selectingRequestAddress) {
+ Dhcp4Client client(Dhcp4Client::SELECTING);
+ // Configure DHCP server.
+ configure(DORA_CONFIGS[0], *client.getServer());
+
+ // Perform 4-way exchange with the server.
+ ASSERT_NO_THROW(client.doDORA(boost::shared_ptr<
+ IOAddress>(new IOAddress("10.0.0.50"))));
+
+ // Make sure that the server responded.
+ ASSERT_TRUE(client.getContext().response_);
+ Pkt4Ptr resp = client.getContext().response_;
+ // Make sure that the server has responded with DHCPACK.
+ ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
+ // Response must not be relayed.
+ EXPECT_FALSE(resp->isRelayed());
+ // Make sure that the server id is present.
+ EXPECT_EQ("10.0.0.1", client.config_.serverid_.toText());
+ // Make sure that the client has got the lease with the requested address.
+ ASSERT_EQ("10.0.0.50", client.config_.lease_.addr_.toText());
+
+ // Simulate different client requesting the same address.
+ client.modifyHWAddr();
+ ASSERT_NO_THROW(client.doDORA(boost::shared_ptr<
+ IOAddress>(new IOAddress("10.0.0.50"))));
+ resp = client.getContext().response_;
+ // Make sure that the server responded.
+ ASSERT_TRUE(resp);
+ // Make sure that the server has responded with DHCPACK.
+ ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
+ // Response must not be relayed.
+ EXPECT_FALSE(resp->isRelayed());
+ // Make sure that the server id is present.
+ EXPECT_EQ("10.0.0.1", client.config_.serverid_.toText());
+ // Make sure that the client has got some address.
+ EXPECT_NE(client.config_.lease_.addr_.toText(), "0.0.0.0");
+ // Make sure that the client has got a different address than requested
+ // as the requested one is already in use.
+ EXPECT_NE(client.config_.lease_.addr_.toText(), "10.0.0.50");
+}
+
+// This test verifies that the client will get the address that it has
+// been allocated when the client requests a different address.
+TEST_F(DORATest, selectingRequestNonMatchingAddress) {
+ Dhcp4Client client(Dhcp4Client::SELECTING);
+ // Configure DHCP server.
+ configure(DORA_CONFIGS[0], *client.getServer());
+
+ // Perform 4-way exchange with the server.
+ ASSERT_NO_THROW(client.doDORA(boost::shared_ptr<
+ IOAddress>(new IOAddress("10.0.0.50"))));
+
+ // Make sure that the server responded.
+ ASSERT_TRUE(client.getContext().response_);
+ Pkt4Ptr resp = client.getContext().response_;
+ // Make sure that the server has responded with DHCPACK.
+ ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
+ // Response must not be relayed.
+ EXPECT_FALSE(resp->isRelayed());
+ // Make sure that the server id is present.
+ EXPECT_EQ("10.0.0.1", client.config_.serverid_.toText());
+ // Make sure that the client has got the lease with the requested address.
+ ASSERT_EQ("10.0.0.50", client.config_.lease_.addr_.toText());
+
+ // Let's request a different address. The server should respond with
+ // the one that the client already has allocated.
+ ASSERT_NO_THROW(client.doDORA(boost::shared_ptr<
+ IOAddress>(new IOAddress("10.0.0.80"))));
+ // Make sure that the server responded.
+ ASSERT_TRUE(client.getContext().response_);
+ resp = client.getContext().response_;
+ // Make sure that the server has responded with DHCPACK.
+ ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
+ // Response must not be relayed.
+ EXPECT_FALSE(resp->isRelayed());
+ // Make sure that the server id is present.
+ EXPECT_EQ("10.0.0.1", client.config_.serverid_.toText());
+ // Make sure that the client has got the lease with the address that
+ // the client has recorded in the lease database.
+ EXPECT_EQ("10.0.0.50", client.config_.lease_.addr_.toText());
+}
+
+// Test that the client in the INIT-REBOOT state can request the IP
+// address it has and the address is returned. Also, check that if
+// if the client requests in valid address the server sends a DHCPNAK.
+TEST_F(DORATest, InitRebootRequest) {
+ Dhcp4Client client(Dhcp4Client::SELECTING);
+ // Configure DHCP server.
+ configure(DORA_CONFIGS[0], *client.getServer());
+ // Obtain a lease from the server using the 4-way exchange.
+ ASSERT_NO_THROW(client.doDORA(boost::shared_ptr<
+ IOAddress>(new IOAddress("10.0.0.50"))));
+ // Make sure that the server responded.
+ ASSERT_TRUE(client.getContext().response_);
+ Pkt4Ptr resp = client.getContext().response_;
+ // Make sure that the server has responded with DHCPACK.
+ ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
+ // Response must not be relayed.
+ EXPECT_FALSE(resp->isRelayed());
+ // Make sure that the server id is present.
+ EXPECT_EQ("10.0.0.1", client.config_.serverid_.toText());
+ // Make sure that the client has got the lease with the requested address.
+ ASSERT_EQ("10.0.0.50", client.config_.lease_.addr_.toText());
+
+ // Client has a lease in the database. Let's transition the client
+ // to the INIT_REBOOT state so as the client can request the cached
+ // lease using the DHCPREQUEST message.
+ client.setState(Dhcp4Client::INIT_REBOOT);
+ ASSERT_NO_THROW(client.doRequest());
+
+ // Make sure that the server responded.
+ ASSERT_TRUE(client.getContext().response_);
+ resp = client.getContext().response_;
+ // Make sure that the server has responded with DHCPACK.
+ ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
+ // Response must not be relayed.
+ EXPECT_FALSE(resp->isRelayed());
+ // Make sure that the server id is present.
+ EXPECT_EQ("10.0.0.1", client.config_.serverid_.toText());
+ // Make sure that the client has got the lease with the requested address.
+ ASSERT_EQ("10.0.0.50", client.config_.lease_.addr_.toText());
+
+ // Try to request a different address than the client has. The server
+ // should respond with DHCPNAK.
+ client.config_.lease_.addr_ = IOAddress("10.0.0.30");
+ ASSERT_NO_THROW(client.doRequest());
+ // Make sure that the server responded.
+ ASSERT_TRUE(client.getContext().response_);
+ resp = client.getContext().response_;
+ EXPECT_EQ(DHCPNAK, static_cast<int>(resp->getType()));
+}
+
+} // end of anonymous namespace
--- /dev/null
- " \"pool\": [ \"2001:db8:1::/64\" ],"
+// 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 <config.h>
+#include <asiolink/io_address.h>
+#include <cc/data.h>
+#include <dhcp/tests/iface_mgr_test_config.h>
+#include <dhcp6/json_config_parser.h>
+#include <dhcp6/tests/dhcp6_message_test.h>
+
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::data;
+using namespace isc::dhcp;
+using namespace isc::dhcp::test;
+using namespace isc::test;
+
+namespace {
+
+/// @brief Set of JSON configurations used throughout the Confirm tests.
+///
+/// - Configuration 0:
+/// - only addresses (no prefixes)
+/// - 2 subnets with 2001:db8:1::/64 and 2001:db8:2::/64
+/// - 1 subnet for eth0 and 1 subnet for eth1
+///
+/// - Configuration 1:
+/// - similar to Configuration 0
+/// - pools configured: 3000:1::/64 and 3000:2::/64
+/// - this specific configuration is used by tests using relays
+///
+const char* CONFIRM_CONFIGS[] = {
+// Configuration 0
+ "{ \"interfaces\": [ \"all\" ],"
+ "\"preferred-lifetime\": 3000,"
+ "\"rebind-timer\": 2000, "
+ "\"renew-timer\": 1000, "
+ "\"subnet6\": [ { "
- " \"pool\": [ \"2001:db8:2::/64\" ],"
++ " \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
+ " \"subnet\": \"2001:db8:1::/48\", "
+ " \"interface-id\": \"\","
+ " \"interface\": \"eth0\""
+ " },"
+ " {"
- " \"pool\": [ \"3000:1::/64\" ],"
++ " \"pools\": [ { \"pool\": \"2001:db8:2::/64\" } ],"
+ " \"subnet\": \"2001:db8:2::/48\", "
+ " \"interface-id\": \"\","
+ " \"interface\": \"eth1\""
+ " } ],"
+ "\"valid-lifetime\": 4000 }",
+
+// Configuration 1
+ "{ \"interfaces\": [ \"all\" ],"
+ "\"preferred-lifetime\": 3000,"
+ "\"rebind-timer\": 2000, "
+ "\"renew-timer\": 1000, "
+ "\"subnet6\": [ { "
- " \"pool\": [ \"3000:2::/64\" ],"
++ " \"pools\": [ { \"pool\": \"3000:1::/64\" } ],"
+ " \"subnet\": \"3000:1::/48\", "
+ " \"interface-id\": \"\","
+ " \"interface\": \"eth0\""
+ " },"
+ " {"
++ " \"pools\": [ { \"pool\": \"3000:2::/64\" } ],"
+ " \"subnet\": \"3000:2::/48\", "
+ " \"interface-id\": \"\","
+ " \"interface\": \"eth1\""
+ " } ],"
+ "\"valid-lifetime\": 4000 }"
+};
+
+/// @brief Test fixture class for testing Confirm..
+class ConfirmTest : public isc::dhcp::test::Dhcpv6MessageTest {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// Sets up fake interfaces.
+ ConfirmTest()
+ : Dhcpv6MessageTest() {
+ }
+
+};
+
+
+// Test that directly connected client's Confirm message is processed and Reply
+// message is sent back. In this test case, the client sends Confirm for two
+// addresses that belong to the same IAID and are sent within the same IA_NA
+// option (RFC3315, section 18.2.2).
+TEST_F(ConfirmTest, directClientSameIAID) {
+ Dhcp6Client client;
+ // Configure client to request IA_NA.
+ client.useNA();
+ // Make 4-way exchange to get the lease.
+ ASSERT_NO_FATAL_FAILURE(requestLease(CONFIRM_CONFIGS[0], 2, client));
+ // Keep the client's lease for future reference.
+ Lease6 lease_client1 = client.getLease(0);
+ // Clone the lease and modify its address so as it is still in the range
+ // of the subnet to which the first lease belongs. When the client sends
+ // the Confirm it should include both addresses and the server should
+ // send Success because both of these addresses are on-link, regardless
+ // what the server has in the lease database.
+ Lease6 lease_client2 = lease_client1;
+ lease_client2.addr_ = bumpAddress(lease_client2.addr_);
+ client.createLease(lease_client2);
+ ASSERT_EQ(2, client.getLeaseNum());
+ // Send Confirm message to the server.
+ ASSERT_NO_THROW(client.doConfirm());
+ // Client should have received a status code option and this option should
+ // indicate the success.
+ ASSERT_TRUE(client.receivedStatusCode());
+ ASSERT_EQ(STATUS_Success, client.getStatusCode());
+
+ ASSERT_EQ(2, client.getLeaseNum());
+ lease_client2 = client.getLease(1);
+ lease_client2.addr_ = bumpSubnet(lease_client2.addr_);
+ client.createLease(lease_client2);
+ // Send confirm to the server. This time, one of the leases contains the
+ // address which doesn't belong to the configured subnet and the server
+ // should respond with STATUS_NotOnLink.
+ ASSERT_NO_THROW(client.doConfirm());
+ ASSERT_TRUE(client.receivedStatusCode());
+ ASSERT_EQ(STATUS_NotOnLink, client.getStatusCode());
+ // Make sure that the server id has been included.
+ EXPECT_TRUE(client.getContext().response_->getOption(D6O_SERVERID));
+}
+
+// Test that directly connected client's Confirm message is processed and Reply
+// message is sent back. In this test case, the client sends Confirm for two
+// addresses that belong to different IAIDs and are sent within the different
+// IA_NA options (RFC3315, section 18.2.2).
+TEST_F(ConfirmTest, directClientDifferentIAID) {
+ Dhcp6Client client;
+ // Configure client to request IA_NA.
+ client.useNA();
+ // Make 4-way exchange to get the lease.
+ ASSERT_NO_FATAL_FAILURE(requestLease(CONFIRM_CONFIGS[0], 2, client));
+ // Keep the client's lease for future reference.
+ Lease6 lease_client1 = client.getLease(0);
+ // Clone the lease and modify its address so as it is still in the range
+ // of the subnet to which the first lease belongs. When the client sends
+ // the Confirm it should include both addresses and the server should
+ // send Success because both of these addresses are on-link, regardless
+ // what the server has in the lease database.
+ Lease6 lease_client2 = lease_client1;
+ ++lease_client2.iaid_;
+ lease_client2.addr_ = bumpAddress(lease_client2.addr_);
+ client.createLease(lease_client2);
+ ASSERT_EQ(2, client.getLeaseNum());
+ // Send Confirm message to the server.
+ ASSERT_NO_THROW(client.doConfirm());
+ // Client should have received a status code option and this option should
+ // indicate the success.
+ ASSERT_TRUE(client.receivedStatusCode());
+ ASSERT_EQ(STATUS_Success, client.getStatusCode());
+ // Make sure that the server id and client id have been included.
+ EXPECT_TRUE(client.getContext().response_->getOption(D6O_SERVERID));
+ EXPECT_TRUE(client.getContext().response_->getOption(D6O_CLIENTID));
+
+ ASSERT_EQ(2, client.getLeaseNum());
+ lease_client2 = client.getLease(1);
+ lease_client2.addr_ = bumpSubnet(lease_client2.addr_);
+ client.createLease(lease_client2);
+ // Send confirm to the server. This time, one of the leases contains the
+ // address which doesn't belong to the configured subnet and the server
+ // should respond with STATUS_NotOnLink.
+ ASSERT_NO_THROW(client.doConfirm());
+ ASSERT_TRUE(client.receivedStatusCode());
+ ASSERT_EQ(STATUS_NotOnLink, client.getStatusCode());
+ // Make sure that the server id have been included.
+ EXPECT_TRUE(client.getContext().response_->getOption(D6O_SERVERID));
+ EXPECT_TRUE(client.getContext().response_->getOption(D6O_CLIENTID));
+}
+
+
+// Test that relayed client's Confirm message is processed and Reply message
+// is sent back (RFC3315, section 18.2.2).
+TEST_F(ConfirmTest, relayedClient) {
+ Dhcp6Client client;
+ // Client to send relayed message.
+ client.useRelay();
+ // Configure client to request IA_NA.
+ client.useNA();
+ // Make 4-way exchange to get the lease.
+ ASSERT_NO_FATAL_FAILURE(requestLease(CONFIRM_CONFIGS[1], 2, client));
+ // Keep the client's lease for future reference.
+ Lease6 lease_client1 = client.getLease(0);
+ // Clone the lease and modify its address so as it is still in the range
+ // of the subnet to which the first lease belongs. When the client sends
+ // the Confirm it should include both addresses and the server should
+ // send Success because both of these addresses are on-link, regardless
+ // what the server has in the lease database.
+ Lease6 lease_client2 = lease_client1;
+ lease_client2.addr_ = bumpAddress(lease_client2.addr_);
+ ++lease_client2.iaid_;
+ client.createLease(lease_client2);
+ // Send Confirm message to the server.
+ ASSERT_NO_THROW(client.doConfirm());
+ // Client should have received a status code option and this option should
+ // indicate the success.
+ ASSERT_TRUE(client.receivedStatusCode());
+ ASSERT_EQ(STATUS_Success, client.getStatusCode());
+
+ lease_client2 = client.getLease(1);
+ lease_client2.addr_ = bumpSubnet(lease_client2.addr_);
+ client.createLease(lease_client2);
+ // Send confirm to the server. This time, one of the leases contains the
+ // address which doesn't belong to the configured subnet and the server
+ // should respond with STATUS_NotOnLink.
+ ASSERT_NO_THROW(client.doConfirm());
+ ASSERT_TRUE(client.receivedStatusCode());
+ ASSERT_EQ(STATUS_NotOnLink, client.getStatusCode());
+ // Make sure that the server id and client id have been included.
+ EXPECT_TRUE(client.getContext().response_->getOption(D6O_SERVERID));
+ EXPECT_TRUE(client.getContext().response_->getOption(D6O_CLIENTID));
+}
+
+// Test that the Confirm message without any addresses is discarded
+// (RFC3315, section 18.2.2).
+TEST_F(ConfirmTest, relayedClientNoAddress) {
+ Dhcp6Client client;
+ // Configure the server.
+ configure(CONFIRM_CONFIGS[1], *client.getServer());
+ // Make sure we ended-up having expected number of subnets configured.
+ const Subnet6Collection* subnets = CfgMgr::instance().getSubnets6();
+ ASSERT_EQ(2, subnets->size());
+ // Client to send relayed message.
+ client.useRelay();
+ // Send Confirm message to the server. This message will contain no
+ // addresses because client has no leases.
+ ASSERT_NO_THROW(client.doConfirm());
+ EXPECT_FALSE(client.getContext().response_);
+}
+
+// This test checks that the server processes Confirm message correctly if
+// the subnet can't be selected for the client (RFC3315, section 18.2.2).
+TEST_F(ConfirmTest, relayedClientNoSubnet) {
+ Dhcp6Client client;
+ // Client to send relayed message.
+ client.useRelay();
+ // Configure client to request IA_NA.
+ client.useNA();
+ // Make 4-way exchange to get the lease.
+ ASSERT_NO_FATAL_FAILURE(requestLease(CONFIRM_CONFIGS[1], 2, client));
+ // Now that the client has a lease, let's remove any subnets to check
+ // how the server would respond to the Confirm.
+ ASSERT_NO_THROW(CfgMgr::instance().deleteSubnets6());
+ // Send Confirm message to the server.
+ ASSERT_NO_THROW(client.doConfirm());
+ // Client should have received a status code option and this option should
+ // indicate that the client is NotOnLink becuase subnet could not be
+ // selected.
+ ASSERT_TRUE(client.receivedStatusCode());
+ ASSERT_EQ(STATUS_NotOnLink, client.getStatusCode());
+
+ // Let's test another case that the client sends no addresses in the Confirm
+ // message. The subnet can't be selected for that client as in the previous
+ // case but this time the server must discard the client's message because
+ // it contains no addresses (is invalid).
+
+ // Set lifetimes to 0 so as the Confirm will ignore the specific address
+ // and send an empty IA_NA.
+ client.config_.leases_[0].lease_.preferred_lft_ = 0;
+ client.config_.leases_[0].lease_.valid_lft_ = 0;
+ ASSERT_NO_THROW(client.doConfirm());
+ EXPECT_FALSE(client.getContext().response_);
+
+ // Do similar test but this time remove the lease so as no IA_NA option
+ // is sent.
+ client.config_.clear();
+ ASSERT_NO_THROW(client.doConfirm());
+ EXPECT_FALSE(client.getContext().response_);
+}
+
+// This test checks that the relayed Confirm messsage is processed by the server
+// when sent to unicast address RFC3315, section 18.2.8).
+TEST_F(ConfirmTest, relayedUnicast) {
+ Dhcp6Client client;
+ // Client to send relayed message.
+ client.useRelay();
+ // Configure client to request IA_NA.
+ client.useNA();
+ // Make 4-way exchange to get the lease.
+ ASSERT_NO_FATAL_FAILURE(requestLease(CONFIRM_CONFIGS[1], 2, client));
+ // Make sure we have got the lease.
+ ASSERT_GT(client.getLeaseNum(), 0);
+ client.setDestAddress(IOAddress("2001:db8:1::1"));
+ // Send Confirm message to the server.
+ ASSERT_NO_THROW(client.doConfirm());
+ // Client should have received a response.
+ ASSERT_TRUE(client.getContext().response_);
+ // Client should have received a status code option and this option should
+ // indicate the success.
+ ASSERT_TRUE(client.receivedStatusCode());
+ ASSERT_EQ(STATUS_Success, client.getStatusCode());
+ // Make sure that the server id and client id have been included.
+ EXPECT_TRUE(client.getContext().response_->getOption(D6O_SERVERID));
+ EXPECT_TRUE(client.getContext().response_->getOption(D6O_CLIENTID));
+}
+
+// This test checks that the Confirm message is discarded by the server if it
+// has been sent to unicast address (RFC3315, section 15).
+TEST_F(ConfirmTest, unicast) {
+ Dhcp6Client client;
+ // Configure client to request IA_NA.
+ client.useNA();
+ // Make 4-way exchange to get the lease.
+ ASSERT_NO_FATAL_FAILURE(requestLease(CONFIRM_CONFIGS[0], 2, client));
+ // Make sure the client has got the lease.
+ ASSERT_GT(client.getLeaseNum(), 0);
+ // Send Confirm message to the server to the unicast address.
+ client.setDestAddress(IOAddress("2001:db8:1::1"));
+ ASSERT_NO_THROW(client.doConfirm());
+ // Mak sure that the server discarded client's Confirm message.
+ EXPECT_FALSE(client.getContext().response_);
+}
+
+} // end of anonymous namespace
\"subnet6\": [
{
\"subnet\": \"2001:db8:1::/64\",
- \"pool\": [ \"2001:db8:1::10-2001:db8:1::100\" ]
+ \"pools\": [ { \"pool\": \"2001:db8:1::10-2001:db8:1::100\" } ]
} ]
+ },
+
+ \"Logging\":
+ {
+ \"loggers\": [
+ {
+ \"name\": \"kea-dhcp6\",
+ \"output_options\": [
+ {
+ \"output\": \"$LOG_FILE\"
+ }
+ ],
+ \"severity\": \"INFO\"
+ }
+ ]
}
}"
# Invalid configuration (negative preferred-lifetime) to check that Kea
\"subnet6\": [
{
\"subnet\": \"2001:db8:1::/64\",
- \"pool\": [ \"2001:db8:1::10-2001:db8:1::100\" ]
+ \"pool\": [ { \"pool\": \"2001:db8:1::10-2001:db8:1::100\" } ]
} ]
+ },
+
+ \"Logging\":
+ {
+ \"loggers\": [
+ {
+ \"name\": \"kea-dhcp6\",
+ \"output_options\": [
+ {
+ \"output\": \"$LOG_FILE\"
+ }
+ ],
+ \"severity\": \"INFO\"
+ }
+ ]
}
}"