From: Tomek Mrugalski Date: Fri, 8 Aug 2014 17:13:38 +0000 (+0200) Subject: [master] Merge branch 'trac3464' (pool definitions are now structures) X-Git-Tag: trac3482_base~53 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=f73452e8ea71af4564e63182a3dcf6b2bda66355;p=thirdparty%2Fkea.git [master] Merge branch 'trac3464' (pool definitions are now structures) Conflicts: ChangeLog doc/examples/kea4/several-subnets.json doc/examples/kea4/single-subnet.json doc/examples/kea6/several-subnets.json doc/guide/dhcp4-srv.xml --- f73452e8ea71af4564e63182a3dcf6b2bda66355 diff --cc ChangeLog index 11b02f05d8,149e028a22..178d9b295c --- a/ChangeLog +++ b/ChangeLog @@@ -1,53 -1,8 +1,59 @@@ -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. diff --cc doc/examples/kea4/several-subnets.json index ce8cb984eb,62dc42ea65..aff824b640 --- a/doc/examples/kea4/several-subnets.json +++ b/doc/examples/kea4/several-subnets.json @@@ -26,30 -26,13 +26,30 @@@ # 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" + } + ] } } diff --cc doc/examples/kea4/single-subnet.json index 6bb92ba27a,a7b7024c95..0c2e10ca07 --- a/doc/examples/kea4/single-subnet.json +++ b/doc/examples/kea4/single-subnet.json @@@ -30,27 -30,13 +30,30 @@@ # "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" + } + ] } } diff --cc doc/examples/kea6/several-subnets.json index 420596da9f,2cf012feba..f138c059ea --- a/doc/examples/kea6/several-subnets.json +++ b/doc/examples/kea6/several-subnets.json @@@ -28,31 -28,16 +28,31 @@@ # 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" + } + ] } +} diff --cc doc/guide/dhcp6-srv.xml index 630afdaddf,7f3acd3d97..bd4443c7f7 --- a/doc/guide/dhcp6-srv.xml +++ b/doc/guide/dhcp6-srv.xml @@@ -456,6 -351,6 +456,13 @@@ temporarily override a list of interfac 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. ++ ++ Each pool is a structure that contains the ++ parameters th describe a single pool. Currently there is only one ++ parameter, pool, which gives the range of addresses ++ in the pool. Additional parameters will be added in future releases of ++ Kea. ++ 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 diff --cc src/bin/dhcp4/tests/dhcp4_process_tests.sh.in index 81704554cb,afbd8b2ff8..40967bf7ff --- a/src/bin/dhcp4/tests/dhcp4_process_tests.sh.in +++ b/src/bin/dhcp4/tests/dhcp4_process_tests.sh.in @@@ -32,23 -32,8 +32,23 @@@ CONFIG=" \"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 @@@ -68,23 -53,8 +68,23 @@@ CONFIG_INVALID=" \"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\" + } + ] } }" diff --cc src/bin/dhcp4/tests/dora_unittest.cc index 07e785545a,0000000000..8b44619ee4 mode 100644,000000..100644 --- a/src/bin/dhcp4/tests/dora_unittest.cc +++ b/src/bin/dhcp4/tests/dora_unittest.cc @@@ -1,352 -1,0 +1,352 @@@ +// 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 +#include +#include +#include +#include +#include +#include +#include + +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\", " - " \"pool\": [ \"10.0.0.10-10.0.0.100\" ]," ++ " \"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(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(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(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(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(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(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(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(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(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(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(resp->getType())); +} + +} // end of anonymous namespace diff --cc src/bin/dhcp6/tests/confirm_unittest.cc index b436144bff,0000000000..26bfe1e927 mode 100644,000000..100644 --- a/src/bin/dhcp6/tests/confirm_unittest.cc +++ b/src/bin/dhcp6/tests/confirm_unittest.cc @@@ -1,329 -1,0 +1,329 @@@ +// 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 +#include +#include +#include +#include +#include + +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:1::/64\" ]," ++ " \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ]," + " \"subnet\": \"2001:db8:1::/48\", " + " \"interface-id\": \"\"," + " \"interface\": \"eth0\"" + " }," + " {" - " \"pool\": [ \"2001:db8:2::/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:1::/64\" ]," ++ " \"pools\": [ { \"pool\": \"3000:1::/64\" } ]," + " \"subnet\": \"3000:1::/48\", " + " \"interface-id\": \"\"," + " \"interface\": \"eth0\"" + " }," + " {" - " \"pool\": [ \"3000:2::/64\" ]," ++ " \"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 diff --cc src/bin/dhcp6/tests/dhcp6_process_tests.sh.in index 75e9d0a62a,6f185a9630..54ee7e709a --- a/src/bin/dhcp6/tests/dhcp6_process_tests.sh.in +++ b/src/bin/dhcp6/tests/dhcp6_process_tests.sh.in @@@ -33,23 -33,8 +33,23 @@@ CONFIG=" \"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 @@@ -70,23 -55,8 +70,23 @@@ CONFIG_INVALID=" \"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\" + } + ] } }"