- name: @b response4, type: isc::dhcp::Pkt4Ptr, direction: <b>in</b>
- name: @b subnet4, type: isc::dhcp::Subnet4Ptr, direction: <b>in</b>
- name: @b hostname, type: std::string, direction: <b>in/out</b>
- - name: @b fwd_update, type: bool, direction: <b>in/out</b>
- - name: @b rev_update, type: bool, direction: <b>in/out</b>
- - name: @b ddns_params, type: isc::dhcp::DdnsParamsPtr, direction: <b>in</b>
+ - name: @b fwd-update, type: bool, direction: <b>in/out</b>
+ - name: @b rev-update, type: bool, direction: <b>in/out</b>
+ - name: @b ddns-params, type: isc::dhcp::DdnsParamsPtr, direction: <b>in</b>
- @b Description: this callout is executed after the server has selected
a lease and has formed a host name to associate with the lease and/or use
host name as well as whether or not forward and/or reverse updates are
enabled.
- Upon entry into the callout, the arguments <b>hostname</b>,<b>fwd_update</b>,
- and <b>rev_update</b> have been set by the server based on the client packet,
+ Upon entry into the callout, the arguments <b>hostname</b>,<b>fwd-update</b>,
+ and <b>rev-update</b> have been set by the server based on the client packet,
and various configuration values (e.g host reservations, DDNS behavioral
parameters, etc). Upon return from the callout, any changes to these
values will be applied as follows:
- If <b>hostname</b> has changed it will be used to update the outbound
host name (option 12) if it exists, the output FQDN option (option 81)
if it exists, and used as the FQDN sent in DNS updates
- - Forward DNS update(s) will be done if <b>fwd_update</b> is true (and
+ - Forward DNS update(s) will be done if <b>fwd-update</b> is true (and
<b>kea-dhcp-ddns</b> connectivity is enabled)
- - Reverse DNS update(s) will be done if <b>rev_update</b> is true (and
+ - Reverse DNS update(s) will be done if <b>rev-update</b> is true (and
<b>kea-dhcp-ddns</b> connectivity is enabled)
- <b>Next step status</b>: Not applicable, its value will be ignored.
int hook_index_host4_identifier = -1;
int hook_index_lease4_offer = -1;
int hook_index_lease4_server_decline = -1;
+ int hook_index_ddns4_update = -1;
// check if appropriate indexes are set
EXPECT_NO_THROW(hook_index_dhcp4_srv_configured = ServerHooks::getServerHooks()
.getIndex("lease4_offer"));
EXPECT_NO_THROW(hook_index_lease4_server_decline = ServerHooks::getServerHooks()
.getIndex("lease4_server_decline"));
+ EXPECT_NO_THROW(hook_index_ddns4_update = ServerHooks::getServerHooks()
+ .getIndex("ddns4_update"));
EXPECT_TRUE(hook_index_dhcp4_srv_configured > 0);
EXPECT_TRUE(hook_index_buffer4_receive > 0);
EXPECT_TRUE(hook_index_host4_identifier > 0);
EXPECT_TRUE(hook_index_lease4_offer > 0);
EXPECT_TRUE(hook_index_lease4_server_decline > 0);
+ EXPECT_TRUE(hook_index_ddns4_update > 0);
}
// A dummy MAC address, padded with 0s
HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("host4_identifier");
HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("lease4_offer");
HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("lease4_server_decline");
+ HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("ddns4_update");
HooksManager::setTestMode(false);
bool status = HooksManager::unloadLibraries();
return (0);
}
+ /// @brief Test callback that stores callout name and passed parameters.
+ ///
+ /// @param callout_handle handle passed by the hooks framework
+ /// @return always 0
+ static int
+ ddns4_update_callout(CalloutHandle& callout_handle) {
+ callback_name_ = string("ddns4_update");
+
+ callout_handle.getArgument("query4", callback_qry_pkt4_);
+ callout_handle.getArgument("response4", callback_resp_pkt4_);
+ callout_handle.getArgument("subnet4", callback_subnet4_);
+ callout_handle.getArgument("hostname", callback_hostname_);
+ callout_handle.getArgument("fwd-update", callback_fwd_update_);
+ callout_handle.getArgument("rev-update", callback_rev_update_);
+ callout_handle.getArgument("ddns-params", callback_ddns_params_);
+
+ callback_argument_names_ = callout_handle.getArgumentNames();
+ sort(callback_argument_names_.begin(), callback_argument_names_.end());
+
+ return (0);
+ }
+
+
/// Resets buffers used to store data received by callouts
void resetCalloutBuffers() {
callback_name_ = string("");
/// Old lease returned in the lease4_offer callout
static Lease4Ptr callback_old_lease_;
+
+ /// Hostname argument returned in ddns4_update callout.
+ static std::string callback_hostname_;
+
+ /// Forward update flag returned in ddns4_update callout.
+ static bool callback_fwd_update_;
+
+ /// Reverse update flag returned in ddns4_update callout.
+ static bool callback_rev_update_;
+
+ /// DDNS behavioral parameters returned in ddns4_update callout.
+ static DdnsParamsPtr callback_ddns_params_;
};
// The following fields are used in testing pkt4_receive_callout.
bool HooksDhcpv4SrvTest::callback_resp_options_copy_;
uint32_t HooksDhcpv4SrvTest::callback_offer_lft_;
Lease4Ptr HooksDhcpv4SrvTest::callback_old_lease_;
+std::string HooksDhcpv4SrvTest::callback_hostname_;
+bool HooksDhcpv4SrvTest::callback_fwd_update_;
+bool HooksDhcpv4SrvTest::callback_rev_update_;
+DdnsParamsPtr HooksDhcpv4SrvTest::callback_ddns_params_;
/// @brief Fixture class used to do basic library load/unload tests
class LoadUnloadDhcpv4SrvTest : public ::testing::Test {
EXPECT_TRUE(checkMarkerFile(UNLOAD_MARKER_FILE, "4"));
}
+// This test verifies that the callout installed on the ddns4_update hook
+// point is executed as a result of DHCPREQUEST message sent to allocate
+// a lease.
+TEST_F(HooksDhcpv4SrvTest, ddns4Update) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
+ string config = "{ \"interfaces-config\": {"
+ " \"interfaces\": [ \"*\" ]"
+ "},"
+ "\"rebind-timer\": 2000, "
+ "\"renew-timer\": 1000, "
+ "\"parked-packet-limit\": 1,"
+ "\"subnet4\": [ { "
+ " \"pools\": [ { \"pool\": \"192.0.2.0/24\" } ],"
+ " \"subnet\": \"192.0.2.0/24\", "
+ " \"id\": 1, "
+ " \"interface\": \"eth1\" "
+ " } ],"
+ " \"dhcp-ddns\" : {"
+ " \"enable-updates\": true"
+ "},"
+ " \"ddns-send-updates\": true,"
+ " \"ddns-qualifying-suffix\": \"example.com\","
+ "\"valid-lifetime\": 4000"
+ "}";
+
+ ConstElementPtr json;
+ EXPECT_NO_THROW(json = parseDHCP4(config));
+ ConstElementPtr status;
+
+ // Configure the server and make sure the config is accepted
+ EXPECT_NO_THROW(status = Dhcpv4SrvTest::configure(*srv_, json));
+ ASSERT_TRUE(status);
+ comment_ = parseAnswer(rcode_, status);
+ ASSERT_EQ(0, rcode_);
+
+ // Commit the config
+ CfgMgr::instance().commit();
+ IfaceMgr::instance().openSockets4();
+
+ // Start D2 client so NCR send will succeed.
+ srv_->startD2();
+
+ // Register ddns4_update callout.
+ ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+ "ddns4_update", ddns4_update_callout));
+
+ // Carry out a DORA.
+ Dhcp4Client client(Dhcp4Client::SELECTING);
+ client.setIfaceName("eth1");
+ client.setIfaceIndex(ETH1_INDEX);
+ client.includeFQDN(Option4ClientFqdn::FLAG_S | Option4ClientFqdn::FLAG_E,
+ "client-name", Option4ClientFqdn::PARTIAL);
+ ASSERT_NO_THROW(client.doDORA(boost::shared_ptr<IOAddress>(new IOAddress("192.0.2.100"))));
+
+ // Make sure that we received a response
+ ASSERT_TRUE(client.getContext().response_);
+
+ // Check that the callback called is indeed the one we installed
+ EXPECT_EQ("ddns4_update", callback_name_);
+
+ // Check if all expected parameters were really received
+ vector<string> expected_argument_names;
+ expected_argument_names.push_back("query4");
+ expected_argument_names.push_back("response4");
+ expected_argument_names.push_back("subnet4");
+ expected_argument_names.push_back("hostname");
+ expected_argument_names.push_back("fwd-update");
+ expected_argument_names.push_back("rev-update");
+ expected_argument_names.push_back("ddns-params");
+
+ sort(expected_argument_names.begin(), expected_argument_names.end());
+ EXPECT_TRUE(callback_argument_names_ == expected_argument_names);
+
+ // Verify query in the callout is as expected.
+ ASSERT_TRUE(callback_qry_pkt4_);
+ ASSERT_TRUE(client.getContext().query_);
+ EXPECT_EQ(client.getContext().query_->getType(), callback_qry_pkt4_->getType());
+ EXPECT_EQ(client.getContext().query_->getLabel(), callback_qry_pkt4_->getLabel());
+
+ // Verify response in the callout is as expected.
+ ASSERT_TRUE(callback_resp_pkt4_);
+ ASSERT_TRUE(client.getContext().response_);
+ EXPECT_EQ(client.getContext().response_->getType(), callback_resp_pkt4_->getType());
+ EXPECT_EQ(client.getContext().response_->getLabel(), callback_resp_pkt4_->getLabel());
+
+ // Verify the subnet.
+ ASSERT_TRUE(callback_subnet4_);
+ EXPECT_EQ(1, callback_subnet4_->getID());
+
+ // Verify the hostname.
+ EXPECT_EQ("client-name.example.com.", callback_hostname_);
+
+ // Verify the update direction flags.
+ EXPECT_TRUE(callback_fwd_update_);
+ EXPECT_TRUE(callback_rev_update_);
+
+ // Verify behavioral parameter set.
+ ASSERT_TRUE(callback_ddns_params_);
+ EXPECT_EQ("example.com", callback_ddns_params_->getQualifyingSuffix());
+
+ // Check if the callout handle state was reset after the callout.
+ checkCalloutHandleReset(client.getContext().query_);
+
+ resetCalloutBuffers();
+}
+
} // namespace
- name: @b response6, type: isc::dhcp::Pkt6Ptr, direction: <b>in</b>
- name: @b subnet6, type: isc::dhcp::Subnet6Ptr, direction: <b>in</b>
- name: @b hostname, type: std::string, direction: <b>in/out</b>
- - name: @b fwd_update, type: bool, direction: <b>in/out</b>
- - name: @b rev_update, type: bool, direction: <b>in/out</b>
- - name: @b ddns_params, type: isc::dhcp::DdnsParamsPtr, direction: <b>in</b>
+ - name: @b fwd-update, type: bool, direction: <b>in/out</b>
+ - name: @b rev-update, type: bool, direction: <b>in/out</b>
+ - name: @b ddns-params, type: isc::dhcp::DdnsParamsPtr, direction: <b>in</b>
- @b Description: this callout is executed after the server has selected
a lease and has formed a host name to associate with the lease and/or use
host name as well as whether or not forward and/or reverse updates are
enabled.
- Upon entry into the callout, the arguments <b>hostname</b>,<b>fwd_update</b>,
- and <b>rev_update</b> have been set by the server based on the client packet,
+ Upon entry into the callout, the arguments <b>hostname</b>,<b>fwd-update</b>,
+ and <b>rev-update</b> have been set by the server based on the client packet,
and various configuration values (e.g host reservations, DDNS behavioral
parameters, etc). Upon return from the callout, any changes to these
values will be applied as follows:
- If <b>hostname</b> has changed it will be used to update the outbound
FQDN option (option 39) if it exists, and used as the FQDN sent in DNS
updates
- - Forward DNS update(s) will be done if <b>fwd_update</b> is true (and
+ - Forward DNS update(s) will be done if <b>fwd-update</b> is true (and
<b>kea-dhcp-ddns</b> connectivity is enabled)
- - Reverse DNS update(s) will be done if <b>rev_update</b> is true (and
+ - Reverse DNS update(s) will be done if <b>rev-update</b> is true (and
<b>kea-dhcp-ddns</b> connectivity is enabled)
- <b>Next step status</b>: Not applicable, its value will be ignored.
#include <util/buffer.h>
#include <util/range_utilities.h>
#include <util/multi_threading_mgr.h>
+#include <testutils/gtest_utils.h>
#include <boost/scoped_ptr.hpp>
#include <gtest/gtest.h>
#include <iostream>
#include <sstream>
-
using namespace isc::asiolink;
using namespace isc::config;
using namespace isc::data;
int hook_index_select_subnet = -1;
int hook_index_leases6_committed = -1;
int hook_index_host6_identifier = -1;
+ int hook_index_ddns6_update = -1;
// check if appropriate indexes are set
EXPECT_NO_THROW(hook_index_dhcp6_srv_configured = ServerHooks::getServerHooks()
.getIndex("leases6_committed"));
EXPECT_NO_THROW(hook_index_host6_identifier = ServerHooks::getServerHooks()
.getIndex("host6_identifier"));
+ EXPECT_NO_THROW(hook_index_ddns6_update = ServerHooks::getServerHooks()
+ .getIndex("ddns6_update"));
EXPECT_TRUE(hook_index_dhcp6_srv_configured > 0);
EXPECT_TRUE(hook_index_buffer6_receive > 0);
EXPECT_TRUE(hook_index_select_subnet > 0);
EXPECT_TRUE(hook_index_leases6_committed > 0);
EXPECT_TRUE(hook_index_host6_identifier > 0);
+ EXPECT_TRUE(hook_index_ddns6_update > 0);
}
/// @brief a class dedicated to Hooks testing in DHCPv6 server
HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("lease6_rebind");
HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("lease6_decline");
HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("host6_identifier");
+ HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("ddns6_update");
HooksManager::setTestMode(false);
bool status = HooksManager::unloadLibraries();
return (0);
}
+ /// @brief Test callback that stores callout name and passed parameters.
+ ///
+ /// @param callout_handle handle passed by the hooks framework
+ /// @return always 0
+ static int
+ ddns6_update_callout(CalloutHandle& callout_handle) {
+ callback_name_ = string("ddns6_update");
+
+ callout_handle.getArgument("query6", callback_qry_pkt6_);
+ callout_handle.getArgument("response6", callback_resp_pkt6_);
+ callout_handle.getArgument("subnet6", callback_subnet6_);
+ callout_handle.getArgument("hostname", callback_hostname_);
+ callout_handle.getArgument("fwd-update", callback_fwd_update_);
+ callout_handle.getArgument("rev-update", callback_rev_update_);
+ callout_handle.getArgument("ddns-params", callback_ddns_params_);
+
+ callback_argument_names_ = callout_handle.getArgumentNames();
+ sort(callback_argument_names_.begin(), callback_argument_names_.end());
+
+ return (0);
+ }
+
/// Resets buffers used to store data received by callouts
void resetCalloutBuffers() {
callback_name_ = string("");
/// Flag indicating if copying retrieved options was enabled for
/// a response during callout execution.
static bool callback_resp_options_copy_;
+
+ /// Hostname argument returned in ddns6_update callout.
+ static std::string callback_hostname_;
+
+ /// Forward update flag returned in ddns6_update callout.
+ static bool callback_fwd_update_;
+
+ /// Reverse update flag returned in ddns6_update callout.
+ static bool callback_rev_update_;
+
+ /// DDNS behavioral parameters returned in ddns6_update callout.
+ static DdnsParamsPtr callback_ddns_params_;
};
// The following parameters are used by callouts to override
vector<string> HooksDhcpv6SrvTest::callback_argument_names_;
bool HooksDhcpv6SrvTest::callback_qry_options_copy_;
bool HooksDhcpv6SrvTest::callback_resp_options_copy_;
+std::string HooksDhcpv6SrvTest::callback_hostname_;
+bool HooksDhcpv6SrvTest::callback_fwd_update_;
+bool HooksDhcpv6SrvTest::callback_rev_update_;
+DdnsParamsPtr HooksDhcpv6SrvTest::callback_ddns_params_;
/// @brief Fixture class used to do basic library load/unload tests
class LoadUnloadDhcpv6SrvTest : public Dhcpv6SrvTest {
EXPECT_TRUE(checkMarkerFile(UNLOAD_MARKER_FILE, "4"));
}
+// This test verifies that the callout installed on the ddns6_update hook
+// point is executed as a result of DHCPREQUEST message sent to allocate
+// a lease.
+TEST_F(HooksDhcpv6SrvTest, ddns6Update) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets6();
+
+ string config =
+ "{ \"interfaces-config\": {"
+ " \"interfaces\": [ \"*\" ]"
+ "},"
+ "\"preferred-lifetime\": 3000,"
+ "\"rebind-timer\": 2000, "
+ "\"renew-timer\": 1000, "
+ "\"subnet6\": [ { "
+ " \"id\": 1, "
+ " \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
+ " \"subnet\": \"2001:db8:1::/48\", "
+ " \"interface\": \"eth1\" "
+ " } ],"
+ "\"valid-lifetime\": 4000,"
+ "\"dhcp-ddns\" : {"
+ " \"enable-updates\": true"
+ "},"
+ " \"ddns-send-updates\": true,"
+ " \"ddns-qualifying-suffix\": \"example.com\" "
+ "}";
+
+ ConstElementPtr json;
+ EXPECT_NO_THROW(json = parseDHCP6(config));
+ ConstElementPtr status;
+
+ // Configure the server and make sure the config is accepted
+ ASSERT_NO_THROW_LOG(status = Dhcpv6SrvTest::configure(*srv_, json));
+ ASSERT_TRUE(status);
+ comment_ = parseAnswer(rcode_, status);
+ ASSERT_EQ(0, rcode_);
+
+ // Commit the config
+ CfgMgr::instance().commit();
+ IfaceMgr::instance().openSockets6();
+
+ // Start D2 client so NCR send will succeed.
+ srv_->startD2();
+
+ // Register ddns6_update callout.
+ ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+ "ddns6_update", ddns6_update_callout));
+
+ // Carry out a SARR.
+ Dhcp6Client client;
+ client.setInterface("eth1");
+ client.requestAddress(0xabca, IOAddress("2001:db8:1::28"));
+ client.useFQDN(Option6ClientFqdn::FLAG_S, "client-name", Option6ClientFqdn::PARTIAL);
+ ASSERT_NO_THROW(client.doSARR());
+
+ // Check that the callback called is indeed the one we installed
+ EXPECT_EQ("ddns6_update", callback_name_);
+
+ // Check if all expected parameters were really received
+ vector<string> expected_argument_names;
+ expected_argument_names.push_back("query6");
+ expected_argument_names.push_back("response6");
+ expected_argument_names.push_back("subnet6");
+ expected_argument_names.push_back("hostname");
+ expected_argument_names.push_back("fwd-update");
+ expected_argument_names.push_back("rev-update");
+ expected_argument_names.push_back("ddns-params");
+
+ sort(expected_argument_names.begin(), expected_argument_names.end());
+ EXPECT_TRUE(callback_argument_names_ == expected_argument_names);
+
+ // Verify query in the callout is as expected.
+ ASSERT_TRUE(callback_qry_pkt6_);
+ ASSERT_TRUE(client.getContext().query_);
+ EXPECT_EQ(client.getContext().query_->getType(), callback_qry_pkt6_->getType());
+ EXPECT_EQ(client.getContext().query_->getLabel(), callback_qry_pkt6_->getLabel());
+
+ // Verify response in the callout is as expected.
+ ASSERT_TRUE(callback_resp_pkt6_);
+ ASSERT_TRUE(client.getContext().response_);
+ EXPECT_EQ(client.getContext().response_->getType(), callback_resp_pkt6_->getType());
+ EXPECT_EQ(client.getContext().response_->getLabel(), callback_resp_pkt6_->getLabel());
+
+ // Verify the subnet.
+ ASSERT_TRUE(callback_subnet6_);
+ EXPECT_EQ(1, callback_subnet6_->getID());
+
+ // Verify the hostname.
+ EXPECT_EQ("client-name.example.com.", callback_hostname_);
+
+ // Verify the update direction flags.
+ EXPECT_TRUE(callback_fwd_update_);
+ EXPECT_TRUE(callback_rev_update_);
+
+ // Verify behavioral parameter set.
+ ASSERT_TRUE(callback_ddns_params_);
+ EXPECT_EQ("example.com", callback_ddns_params_->getQualifyingSuffix());
+
+ // Check if the callout handle state was reset after the callout.
+ checkCalloutHandleReset(client.getContext().query_);
+
+ resetCalloutBuffers();
+}
+
} // namespace
work_.reset();
}
- /// @brief Return the native @ref io_service object used in this wrapper.
+ /// @brief Return the native @c io_service object used in this wrapper.
///
/// This is a short term work around to support other Kea modules
- /// that share the same @ref io_service with the authoritative server.
+ /// that share the same @c io_service with the authoritative server.
/// It will eventually be removed once the wrapper interface is
/// generalized.
boost::asio::io_service& getInternalIOService() {
/// @brief Defines a smart pointer to an IOService instance.
typedef boost::shared_ptr<IOService> IOServicePtr;
-/// @brief The @ref IOService class is a wrapper for the ASIO @ref io_service
+/// @brief The @ref IOService class is a wrapper for the ASIO @c io_service
/// class.
class IOService {
/// @brief Constructors and Destructor.
/// when all handlers have been invoked.
void stopWork();
- /// @brief Return the native @ref io_service object used in this wrapper.
+ /// @brief Return the native @c io_service object used in this wrapper.
///
/// This is a short term work around to support other Kea modules
- /// that share the same @ref io_service with the authoritative server.
+ /// that share the same @c io_service with the authoritative server.
/// It will eventually be removed once the wrapper interface is
/// generalized.
///
/// In the TSIG protocol, hash algorithms are represented in the form of
/// domain name.
/// Our interfaces provide direct translation of this concept; for example,
-/// the constructor from parameters take a @class Name object to specify the
+/// the constructor from parameters take a @ref Name object to specify the
/// algorithm.
/// On one hand, this may be counter intuitive.
/// An API user would rather specify "hmac-md5" instead of