|
@@ -0,0 +1,381 @@
|
|
|
+// 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/iface_mgr.h>
|
|
|
+#include <dhcp6/config_parser.h>
|
|
|
+#include <dhcp6/tests/d2_unittest.h>
|
|
|
+#include <dhcpsrv/cfgmgr.h>
|
|
|
+
|
|
|
+#include <gtest/gtest.h>
|
|
|
+
|
|
|
+#include <string>
|
|
|
+
|
|
|
+using namespace isc;
|
|
|
+using namespace isc::asiolink;
|
|
|
+using namespace isc::data;
|
|
|
+
|
|
|
+namespace isc {
|
|
|
+namespace dhcp {
|
|
|
+namespace test {
|
|
|
+
|
|
|
+/// @todo
|
|
|
+void
|
|
|
+D2Dhcpv6Srv::d2ClientErrorHandler(const
|
|
|
+ dhcp_ddns::NameChangeSender::Result result,
|
|
|
+ dhcp_ddns::NameChangeRequestPtr& ncr) {
|
|
|
+ ++error_count_;
|
|
|
+ // call base class error handler
|
|
|
+ Dhcpv6Srv::d2ClientErrorHandler(result, ncr);
|
|
|
+}
|
|
|
+
|
|
|
+const bool Dhcp6SrvD2Test::SHOULD_PASS;
|
|
|
+const bool Dhcp6SrvD2Test::SHOULD_FAIL;
|
|
|
+
|
|
|
+Dhcp6SrvD2Test::Dhcp6SrvD2Test() : rcode_(-1) {
|
|
|
+}
|
|
|
+
|
|
|
+Dhcp6SrvD2Test::~Dhcp6SrvD2Test() {
|
|
|
+ reset();
|
|
|
+}
|
|
|
+
|
|
|
+dhcp_ddns::NameChangeRequestPtr
|
|
|
+Dhcp6SrvD2Test::buildTestNcr(uint32_t dhcid_id_num) {
|
|
|
+ // Build an NCR from json string.
|
|
|
+ std::ostringstream stream;
|
|
|
+
|
|
|
+ stream <<
|
|
|
+ "{"
|
|
|
+ " \"change_type\" : 0 , "
|
|
|
+ " \"forward_change\" : true , "
|
|
|
+ " \"reverse_change\" : false , "
|
|
|
+ " \"fqdn\" : \"myhost.example.com.\" , "
|
|
|
+ " \"ip_address\" : \"192.168.2.1\" , "
|
|
|
+ " \"dhcid\" : \""
|
|
|
+
|
|
|
+ << std::hex << std::setfill('0') << std::setw(16)
|
|
|
+ << dhcid_id_num << "\" , "
|
|
|
+
|
|
|
+ " \"lease_expires_on\" : \"20140121132405\" , "
|
|
|
+ " \"lease_length\" : 1300 "
|
|
|
+ "}";
|
|
|
+
|
|
|
+ return (dhcp_ddns::NameChangeRequest::fromJSON(stream.str()));
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+Dhcp6SrvD2Test::reset() {
|
|
|
+ std::string config = "{ \"interfaces\": [ \"all\" ],"
|
|
|
+ "\"hooks-libraries\": [ ],"
|
|
|
+ "\"preferred-lifetime\": 3000,"
|
|
|
+ "\"rebind-timer\": 2000, "
|
|
|
+ "\"renew-timer\": 1000, "
|
|
|
+ "\"valid-lifetime\": 4000, "
|
|
|
+ "\"subnet6\": [ ], "
|
|
|
+ "\"dhcp-ddns\": { \"enable-updates\" : false }, "
|
|
|
+ "\"option-def\": [ ], "
|
|
|
+ "\"option-data\": [ ] }";
|
|
|
+ configure(config, SHOULD_PASS);
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+Dhcp6SrvD2Test::configureD2(bool enable_d2, const bool exp_result,
|
|
|
+ const std::string& ip_address,
|
|
|
+ const uint32_t port) {
|
|
|
+ std::ostringstream config;
|
|
|
+ config <<
|
|
|
+ "{ \"interfaces\": [ \"*\" ],"
|
|
|
+ "\"rebind-timer\": 2000, "
|
|
|
+ "\"renew-timer\": 1000, "
|
|
|
+ "\"subnet6\": [ { "
|
|
|
+ " \"pool\": [ \"2001:db8:1::1 - 2001:db8:1::ffff\" ],"
|
|
|
+ " \"subnet\": \"2001:db8:1::/64\" } ],"
|
|
|
+ " \"dhcp-ddns\" : {"
|
|
|
+ " \"enable-updates\" : " << (enable_d2 ? "true" : "false") << ", "
|
|
|
+ " \"server-ip\" : \"" << ip_address << "\", "
|
|
|
+ " \"server-port\" : " << port << ", "
|
|
|
+ " \"ncr-protocol\" : \"UDP\", "
|
|
|
+ " \"ncr-format\" : \"JSON\", "
|
|
|
+ " \"always-include-fqdn\" : true, "
|
|
|
+ " \"allow-client-update\" : true, "
|
|
|
+ " \"override-no-update\" : true, "
|
|
|
+ " \"override-client-update\" : true, "
|
|
|
+ " \"replace-client-name\" : true, "
|
|
|
+ " \"generated-prefix\" : \"test.prefix\", "
|
|
|
+ " \"qualifying-suffix\" : \"test.suffix.\" },"
|
|
|
+ "\"valid-lifetime\": 4000 }";
|
|
|
+
|
|
|
+ configure(config.str(), exp_result);
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+Dhcp6SrvD2Test::configure(const std::string& config, bool exp_result) {
|
|
|
+ ElementPtr json = 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);
|
|
|
+ if (exp_result == SHOULD_PASS) {
|
|
|
+ ASSERT_EQ(0, rcode);
|
|
|
+ } else {
|
|
|
+ ASSERT_EQ(1, rcode);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// Tests ability to turn on and off ddns updates by submitting
|
|
|
+// by submitting the appropriate configuration to Dhcp6 server
|
|
|
+// and then invoking its startD2() method.
|
|
|
+TEST_F(Dhcp6SrvD2Test, enableDisable) {
|
|
|
+ // Grab the manager and verify that be default ddns is off
|
|
|
+ // and a sender was not started.
|
|
|
+ dhcp::D2ClientMgr& mgr = CfgMgr::instance().getD2ClientMgr();
|
|
|
+ ASSERT_FALSE(mgr.ddnsEnabled());
|
|
|
+ ASSERT_FALSE(mgr.amSending());
|
|
|
+
|
|
|
+ // Verify a valid config with ddns enabled configures ddns properly,
|
|
|
+ // but does not start the sender.
|
|
|
+ ASSERT_NO_FATAL_FAILURE(configureD2(true));
|
|
|
+ ASSERT_TRUE(mgr.ddnsEnabled());
|
|
|
+ ASSERT_FALSE(mgr.amSending());
|
|
|
+
|
|
|
+ // Verify that calling start does not throw and starts the sender.
|
|
|
+ ASSERT_NO_THROW(srv_.startD2());
|
|
|
+ ASSERT_TRUE(mgr.amSending());
|
|
|
+
|
|
|
+ // Verify a valid config with ddns disabled configures ddns properly.
|
|
|
+ // Sender should not have been started.
|
|
|
+ ASSERT_NO_FATAL_FAILURE(configureD2(false));
|
|
|
+ ASSERT_FALSE(mgr.ddnsEnabled());
|
|
|
+ ASSERT_FALSE(mgr.amSending());
|
|
|
+
|
|
|
+ // Verify that the sender does NOT get started when ddns is disabled.
|
|
|
+ srv_.startD2();
|
|
|
+ ASSERT_FALSE(mgr.amSending());
|
|
|
+}
|
|
|
+
|
|
|
+// Tests Dhcp6 server's ability to correctly handle a flawed dhcp-ddns configuration.
|
|
|
+// It does so by first enabling updates by submitting a valid configuration and then
|
|
|
+// ensuring they remain on after submitting a flawed configuration.
|
|
|
+// and then invoking its startD2() method.
|
|
|
+TEST_F(Dhcp6SrvD2Test, badConfig) {
|
|
|
+ // Grab the manager and verify that be default ddns is off
|
|
|
+ // and a sender was not started.
|
|
|
+ dhcp::D2ClientMgr& mgr = CfgMgr::instance().getD2ClientMgr();
|
|
|
+ ASSERT_FALSE(mgr.ddnsEnabled());
|
|
|
+
|
|
|
+ // Configure it enabled and start it.
|
|
|
+ ASSERT_NO_FATAL_FAILURE(configureD2(true));
|
|
|
+ ASSERT_TRUE(mgr.ddnsEnabled());
|
|
|
+ ASSERT_NO_THROW(srv_.startD2());
|
|
|
+ ASSERT_TRUE(mgr.amSending());
|
|
|
+
|
|
|
+ // Now attempt to give it an invalid configuration.
|
|
|
+ // Result should indicate failure.
|
|
|
+ ASSERT_NO_FATAL_FAILURE(configureD2(false, SHOULD_FAIL, "bogus_ip"));
|
|
|
+
|
|
|
+ // Configure was not altered, so ddns should be enabled and still sending.
|
|
|
+ ASSERT_TRUE(mgr.ddnsEnabled());
|
|
|
+ ASSERT_TRUE(mgr.amSending());
|
|
|
+
|
|
|
+ // Verify that calling start does not throw or stop the sender.
|
|
|
+ ASSERT_NO_THROW(srv_.startD2());
|
|
|
+ ASSERT_TRUE(mgr.amSending());
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+// Checks that submitting an identical dhcp-ddns configuration
|
|
|
+// is handled properly. Not effect should be no change in
|
|
|
+// status for ddns updating. Updates should still enabled and
|
|
|
+// in send mode. This indicates that the sender was not stopped.
|
|
|
+TEST_F(Dhcp6SrvD2Test, sameConfig) {
|
|
|
+ // Grab the manager and verify that be default ddns is off
|
|
|
+ // and a sender was not started.
|
|
|
+ dhcp::D2ClientMgr& mgr = CfgMgr::instance().getD2ClientMgr();
|
|
|
+ ASSERT_FALSE(mgr.ddnsEnabled());
|
|
|
+
|
|
|
+ // Configure it enabled and start it.
|
|
|
+ ASSERT_NO_FATAL_FAILURE(configureD2(true));
|
|
|
+ ASSERT_TRUE(mgr.ddnsEnabled());
|
|
|
+ ASSERT_NO_THROW(srv_.startD2());
|
|
|
+ ASSERT_TRUE(mgr.amSending());
|
|
|
+
|
|
|
+ // Now submit an identical configuration.
|
|
|
+ ASSERT_NO_FATAL_FAILURE(configureD2(true));
|
|
|
+
|
|
|
+ // Configuration was not altered, so ddns should still enabled and sending.
|
|
|
+ ASSERT_TRUE(mgr.ddnsEnabled());
|
|
|
+ ASSERT_TRUE(mgr.amSending());
|
|
|
+
|
|
|
+ // Verify that calling start does not throw or stop the sender.
|
|
|
+ ASSERT_NO_THROW(srv_.startD2());
|
|
|
+ ASSERT_TRUE(mgr.amSending());
|
|
|
+}
|
|
|
+
|
|
|
+// Checks that submitting an different, but valid dhcp-ddns configuration
|
|
|
+// is handled properly. Updates should be enabled, however they should
|
|
|
+// not yet be running. This indicates that the sender was stopped and
|
|
|
+// replaced, but not yet started.
|
|
|
+TEST_F(Dhcp6SrvD2Test, differentConfig) {
|
|
|
+ // Grab the manager and verify that be default ddns is off
|
|
|
+ // and a sender was not started.
|
|
|
+ dhcp::D2ClientMgr& mgr = CfgMgr::instance().getD2ClientMgr();
|
|
|
+ ASSERT_FALSE(mgr.ddnsEnabled());
|
|
|
+
|
|
|
+ // Configure it enabled and start it.
|
|
|
+ ASSERT_NO_FATAL_FAILURE(configureD2(true));
|
|
|
+ ASSERT_TRUE(mgr.ddnsEnabled());
|
|
|
+ ASSERT_NO_THROW(srv_.startD2());
|
|
|
+ ASSERT_TRUE(mgr.amSending());
|
|
|
+
|
|
|
+ // Now enable it on a different port.
|
|
|
+ ASSERT_NO_FATAL_FAILURE(configureD2(true, SHOULD_PASS, "127.0.0.1", 54001));
|
|
|
+
|
|
|
+ // Configuration was altered, so ddns should still enabled but not sending.
|
|
|
+ ASSERT_TRUE(mgr.ddnsEnabled());
|
|
|
+ ASSERT_FALSE(mgr.amSending());
|
|
|
+
|
|
|
+ // Verify that calling start starts the sender.
|
|
|
+ ASSERT_NO_THROW(srv_.startD2());
|
|
|
+ ASSERT_TRUE(mgr.amSending());
|
|
|
+}
|
|
|
+
|
|
|
+// Checks that given a valid, enabled configuration and placing
|
|
|
+// sender in send mode, permits NCR requests to be sent via UPD
|
|
|
+// socket. Note this test does not employ any sort of receiving
|
|
|
+// client to verify actual transmission. These types of tests
|
|
|
+// are including under dhcp_ddns and d2 unit testing.
|
|
|
+TEST_F(Dhcp6SrvD2Test, simpleUDPSend) {
|
|
|
+ // Grab the manager and verify that be default ddns is off
|
|
|
+ // and a sender was not started.
|
|
|
+ dhcp::D2ClientMgr& mgr = CfgMgr::instance().getD2ClientMgr();
|
|
|
+ ASSERT_FALSE(mgr.ddnsEnabled());
|
|
|
+
|
|
|
+ // Configure it enabled and start it.
|
|
|
+ ASSERT_NO_FATAL_FAILURE(configureD2(true));
|
|
|
+ ASSERT_TRUE(mgr.ddnsEnabled());
|
|
|
+ ASSERT_NO_THROW(srv_.startD2());
|
|
|
+ ASSERT_TRUE(mgr.amSending());
|
|
|
+
|
|
|
+ // Verify that we can queue up a message.
|
|
|
+ dhcp_ddns::NameChangeRequestPtr ncr = buildTestNcr();
|
|
|
+ ASSERT_NO_THROW(mgr.sendRequest(ncr));
|
|
|
+ EXPECT_EQ(1, mgr.getQueueSize());
|
|
|
+
|
|
|
+ // Calling receive should detect the ready IO on the sender's select-fd,
|
|
|
+ // and invoke callback, which should complete the send.
|
|
|
+ ASSERT_NO_THROW(IfaceMgr::instance().receive4(0,0));
|
|
|
+
|
|
|
+ // Verify the queue is now empty.
|
|
|
+ EXPECT_EQ(0, mgr.getQueueSize());
|
|
|
+}
|
|
|
+
|
|
|
+// Checks that an IO error in sending a request to D2, results in ddns updates
|
|
|
+// being suspended. This indicates that Dhcp6Srv's error handler has been
|
|
|
+// invoked as expected. Note that this unit test relies on an attempt to send
|
|
|
+// to a server address of 0.0.0.0 port 0 fails under all OSs.
|
|
|
+TEST_F(Dhcp6SrvD2Test, forceUDPSendFailure) {
|
|
|
+ // Grab the manager and verify that be default ddns is off
|
|
|
+ // and a sender was not started.
|
|
|
+ dhcp::D2ClientMgr& mgr = CfgMgr::instance().getD2ClientMgr();
|
|
|
+ ASSERT_FALSE(mgr.ddnsEnabled());
|
|
|
+
|
|
|
+ // Configure it enabled and start it.
|
|
|
+ // Using server address of 0.0.0.0/0 should induce failure on send.
|
|
|
+ ASSERT_NO_FATAL_FAILURE(configureD2(true, SHOULD_PASS, "0.0.0.0", 0));
|
|
|
+ ASSERT_TRUE(mgr.ddnsEnabled());
|
|
|
+ ASSERT_NO_THROW(srv_.startD2());
|
|
|
+ ASSERT_TRUE(mgr.amSending());
|
|
|
+
|
|
|
+ // Queue up 3 messages.
|
|
|
+ for (int i = 0; i < 3; i++) {
|
|
|
+ dhcp_ddns::NameChangeRequestPtr ncr = buildTestNcr(i + 1);
|
|
|
+ ASSERT_NO_THROW(mgr.sendRequest(ncr));
|
|
|
+ }
|
|
|
+ EXPECT_EQ(3, mgr.getQueueSize());
|
|
|
+
|
|
|
+ // Calling receive should detect the ready IO on the sender's select-fd,
|
|
|
+ // and invoke callback, which should complete the send, which should
|
|
|
+ // fail.
|
|
|
+ ASSERT_NO_THROW(IfaceMgr::instance().receive4(0,0));
|
|
|
+
|
|
|
+ // Verify the error handler was invoked.
|
|
|
+ EXPECT_EQ(1, srv_.error_count_);
|
|
|
+
|
|
|
+ // Verify that updates are disabled and we are no longer sending.
|
|
|
+ ASSERT_FALSE(mgr.ddnsEnabled());
|
|
|
+ ASSERT_FALSE(mgr.amSending());
|
|
|
+
|
|
|
+ // Verify message is still in the queue.
|
|
|
+ EXPECT_EQ(3, mgr.getQueueSize());
|
|
|
+
|
|
|
+ // Verify that we can't just restart it.
|
|
|
+ /// @todo This may change if we add ability to resume.
|
|
|
+ ASSERT_NO_THROW(srv_.startD2());
|
|
|
+ ASSERT_FALSE(mgr.amSending());
|
|
|
+
|
|
|
+ // Configure it enabled and start it.
|
|
|
+ ASSERT_NO_FATAL_FAILURE(configureD2(true));
|
|
|
+ ASSERT_TRUE(mgr.ddnsEnabled());
|
|
|
+ ASSERT_NO_THROW(srv_.startD2());
|
|
|
+ ASSERT_TRUE(mgr.amSending());
|
|
|
+
|
|
|
+ // Verify message is still in the queue.
|
|
|
+ EXPECT_EQ(3, mgr.getQueueSize());
|
|
|
+
|
|
|
+ // This will finish sending the 1st message in queue
|
|
|
+ // and initiate send of 2nd message.
|
|
|
+ ASSERT_NO_THROW(IfaceMgr::instance().receive4(0,0));
|
|
|
+ EXPECT_EQ(1, srv_.error_count_);
|
|
|
+
|
|
|
+ // First message is off the queue.
|
|
|
+ EXPECT_EQ(2, mgr.getQueueSize());
|
|
|
+}
|
|
|
+
|
|
|
+// Tests error handling of D2ClientMgr::sendRequest() failure
|
|
|
+// by attempting to queue maximum number of messages.
|
|
|
+TEST_F(Dhcp6SrvD2Test, queueMaxError) {
|
|
|
+ // Configure it enabled and start it.
|
|
|
+ dhcp::D2ClientMgr& mgr = CfgMgr::instance().getD2ClientMgr();
|
|
|
+ ASSERT_NO_FATAL_FAILURE(configureD2(true));
|
|
|
+ ASSERT_TRUE(mgr.ddnsEnabled());
|
|
|
+ ASSERT_NO_THROW(srv_.startD2());
|
|
|
+ ASSERT_TRUE(mgr.amSending());
|
|
|
+
|
|
|
+ // Attempt to queue more then the maximum allowed.
|
|
|
+ int max_msgs = mgr.getQueueMaxSize();
|
|
|
+ for (int i = 0; i < max_msgs + 1; i++) {
|
|
|
+ dhcp_ddns::NameChangeRequestPtr ncr = buildTestNcr(i + 1);
|
|
|
+ ASSERT_NO_THROW(mgr.sendRequest(ncr));
|
|
|
+ }
|
|
|
+
|
|
|
+ // Stopping sender will complete the first message so there
|
|
|
+ // should be max less one.
|
|
|
+ EXPECT_EQ(max_msgs - 1, mgr.getQueueSize());
|
|
|
+
|
|
|
+ // Verify the error handler was invoked.
|
|
|
+ EXPECT_EQ(1, srv_.error_count_);
|
|
|
+
|
|
|
+ // Verify that updates are disabled and we are no longer sending.
|
|
|
+ ASSERT_FALSE(mgr.ddnsEnabled());
|
|
|
+ ASSERT_FALSE(mgr.amSending());
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+} // namespace test
|
|
|
+} // namespace dhcp
|
|
|
+} // namespace isc
|
|
|
+
|