Commit 440fd061 authored by Marcin Siodelski's avatar Marcin Siodelski

[4303] Values in 'host-reservation-identifiers' used by the servers.

Both DHCPv4 and DHCPv6.
parent 6e228073
......@@ -448,6 +448,8 @@ DhcpConfigParser* createGlobalDhcp4ConfigParser(const std::string& config_id,
parser = new ExpirationConfigParser();
} else if (config_id.compare("client-classes") == 0) {
parser = new ClientClassDefListParser(config_id, globalContext());
} else if (config_id.compare("host-reservation-identifiers") == 0) {
parser = new HostReservationIdsParser4();
} else {
isc_throw(DhcpConfigError,
"unsupported global configuration parameter: "
......
......@@ -57,6 +57,11 @@ namespace {
/// - 1 pool: 10.0.0.10-10.0.0.100
/// - match-client-id flag is set to false, thus the server
/// uses HW address for lease lookup, rather than client id
///
/// - Configuration 4:
/// - The same as configuration 2, but using different values in
/// 'host-reservation-identifiers'
///
const char* DORA_CONFIGS[] = {
// Configuration 0
"{ \"interfaces-config\": {"
......@@ -154,6 +159,32 @@ const char* DORA_CONFIGS[] = {
" } ]"
" } ]"
"}",
// Configuration 4
"{ \"interfaces-config\": {"
" \"interfaces\": [ \"*\" ]"
"},"
"\"host-reservation-identifiers\": [ \"circuit-id\", \"hw-address\" ],"
"\"valid-lifetime\": 600,"
"\"subnet4\": [ { "
" \"subnet\": \"10.0.0.0/24\", "
" \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ],"
" \"reservations\": [ "
" {"
" \"hw-address\": \"aa:bb:cc:dd:ee:ff\","
" \"ip-address\": \"10.0.0.7\""
" },"
" {"
" \"duid\": \"01:02:03:04:05\","
" \"ip-address\": \"10.0.0.8\""
" },"
" {"
" \"circuit-id\": \"'charter950'\","
" \"ip-address\": \"10.0.0.9\""
" }"
" ]"
"} ]"
"}"
};
/// @brief Test fixture class for testing 4-way (DORA) exchanges.
......@@ -697,9 +728,8 @@ TEST_F(DORATest, reservation) {
}
// This test checks that it is possible to make a reservation by
// circuit-id inserted by the relay agent..
// circuit-id inserted by the relay agent.
TEST_F(DORATest, reservationByCircuitId) {
// Client A is a one which will have a reservation.
Dhcp4Client client(Dhcp4Client::SELECTING);
// Use relay agent so as the circuit-id can be inserted.
client.useRelay(true, IOAddress("10.0.0.1"), IOAddress("10.0.0.2"));
......@@ -721,6 +751,47 @@ TEST_F(DORATest, reservationByCircuitId) {
ASSERT_EQ("10.0.0.9", client.config_.lease_.addr_.toText());
}
// This test verifies that order in which host identifiers are used to
// retrieve host reservations can be controlled.
TEST_F(DORATest, hostIdentifiersOrder) {
Dhcp4Client client(Dhcp4Client::SELECTING);
client.setHWAddress("aa:bb:cc:dd:ee:ff");
// Use relay agent so as the circuit-id can be inserted.
client.useRelay(true, IOAddress("10.0.0.1"), IOAddress("10.0.0.2"));
// Specify circuit-id.
client.setCircuitId("charter950");
// Configure DHCP server.
configure(DORA_CONFIGS[2], *client.getServer());
// Perform 4-way exchange to obtain reserved address.
// The client has in fact two reserved addresses, but the one assigned
// should be by hw-address.
ASSERT_NO_THROW(client.doDORA(boost::shared_ptr<
IOAddress>(new IOAddress("0.0.0.0"))));
// 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()));
// Make sure that the client has got the lease for the reserved address.
ASSERT_EQ("10.0.0.7", client.config_.lease_.addr_.toText());
// Reconfigure the server to change the preference order of the
// host identifiers. The 'circuit-id' should now take precedence over
// the hw-address.
configure(DORA_CONFIGS[4], *client.getServer());
ASSERT_NO_THROW(client.doDORA(boost::shared_ptr<
IOAddress>(new IOAddress("0.0.0.0"))));
// 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()));
// Make sure that the client has got the lease for the reserved address.
ASSERT_EQ("10.0.0.9", client.config_.lease_.addr_.toText());
}
// This test checks that setting the match-client-id value to false causes
// the server to ignore changing client identifier when the client is
// using consistent HW address.
......
......@@ -702,6 +702,8 @@ DhcpConfigParser* createGlobal6DhcpConfigParser(const std::string& config_id,
parser = new ClientClassDefListParser(config_id, globalContext());
} else if (config_id.compare("server-id") == 0) {
parser = new DUIDConfigParser();
} else if (config_id.compare("host-reservation-identifiers") == 0) {
parser = new HostReservationIdsParser6();
} else {
isc_throw(DhcpConfigError,
"unsupported global configuration parameter: "
......
......@@ -23,6 +23,9 @@ namespace {
/// Single subnet with two reservations, one with a hostname, one without
/// - Configuration 1:
/// Multiple reservations using different host identifiers.
/// - Configuration 2:
/// Same as configuration 1 but 'host-reservation-identifiers' specified
/// in non-default order.
const char* CONFIGS[] = {
// Configuration 0:
"{ "
......@@ -76,8 +79,35 @@ const char* CONFIGS[] = {
" \"ip-addresses\": [ \"2001:db8:1::2\" ]"
" } ]"
" } ]"
"}"
"}",
// Configuration 2:
"{ "
"\"interfaces-config\": {"
" \"interfaces\": [ \"*\" ]"
"},"
"\"host-reservation-identifiers\": [ \"duid\", \"hw-address\" ],"
"\"valid-lifetime\": 4000, "
"\"preferred-lifetime\": 3000,"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"mac-sources\": [ \"ipv6-link-local\" ], "
"\"subnet6\": [ "
" { "
" \"subnet\": \"2001:db8:1::/48\", "
" \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
" \"interface\" : \"eth0\" , "
" \"reservations\": ["
" {"
" \"hw-address\": \"38:60:77:d5:ff:ee\","
" \"ip-addresses\": [ \"2001:db8:1::1\" ]"
" },"
" {"
" \"duid\": \"01:02:03:05\","
" \"ip-addresses\": [ \"2001:db8:1::2\" ]"
" } ]"
" } ]"
"}"
};
/// @brief Test fixture class for testing host reservations
......@@ -97,11 +127,14 @@ public:
/// @param client Reference to a client to be used in the test.
/// The client should be preconfigured to insert a specific identifier
/// into the message, e.g. DUID, HW address etc.
/// @param config_index Index of the configuration to use in the CONFIGS
/// table.
/// @param exp_ip_address Expected IPv6 address in the returned
/// reservation.
void testReservationByIdentifier(Dhcp6Client& client,
const unsigned int config_index,
const std::string exp_ip_address) {
configure(CONFIGS[1], *client.getServer());
configure(CONFIGS[config_index], *client.getServer());
const Subnet6Collection* subnets = CfgMgr::instance().getCurrentCfg()->
getCfgSubnets6()->getAll();
......@@ -315,7 +348,7 @@ TEST_F(HostTest, reservationByDUID) {
// Set DUID matching the one used to create host reservations.
client.setDUID("01:02:03:05");
// Run the actual test.
testReservationByIdentifier(client, "2001:db8:1::2");
testReservationByIdentifier(client, 1, "2001:db8:1::2");
}
// This test verfies that the host reservation by HW address is found
......@@ -327,9 +360,20 @@ TEST_F(HostTest, reservationByHWAddress) {
// decoded address will be used to search for host reservations.
client.setLinkLocal(IOAddress("fe80::3a60:77ff:fed5:ffee"));
// Run the actual test.
testReservationByIdentifier(client, "2001:db8:1::1");
testReservationByIdentifier(client, 1, "2001:db8:1::1");
}
// This test verifies that order in which host identifiers are used to
// retrieve host reservations can be controlled.
TEST_F(HostTest, hostIdentifiersOrder) {
Dhcp6Client client;
// Set DUID matching the one used to create host reservations.
client.setDUID("01:02:03:05");
// Set link local address for the client which the server will
// use to decode the HW address as 38:60:77:d5:ff:ee. This
// decoded address will be used to search for host reservations.
client.setLinkLocal(IOAddress("fe80::3a60:77ff:fed5:ffee"));
testReservationByIdentifier(client, 2, "2001:db8:1::2");
}
} // end of anonymous namespace
......@@ -12,6 +12,7 @@
#include <dhcp_ddns/ncr_msg.h>
#include <dhcpsrv/alloc_engine.h>
#include <dhcpsrv/alloc_engine_log.h>
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/dhcpsrv_log.h>
#include <dhcpsrv/host_mgr.h>
#include <dhcpsrv/host.h>
......@@ -27,6 +28,7 @@
#include <boost/foreach.hpp>
#include <algorithm>
#include <cstring>
#include <sstream>
#include <limits>
......@@ -301,6 +303,40 @@ AllocEngine::AllocatorPtr AllocEngine::getAllocator(Lease::Type type) {
return (alloc->second);
}
template<typename ContextType>
void
AllocEngine::findReservationInternal(ContextType& ctx,
const ConstCfgHostReservationsPtr& cfg,
const AllocEngine::HostGetFunc& host_get) {
ctx.host_.reset();
// We can only search for the reservation if a subnet has been selected.
if (ctx.subnet_) {
// Check which host reservation mode is supported in this subnet.
Subnet::HRMode hr_mode = ctx.subnet_->getHostReservationMode();
// Check if there is a host reseravtion for this client. Attempt to
// get host information
if (hr_mode != Subnet::HR_DISABLED) {
// Iterate over configured identifiers in the order of preference
// and try to use each of them to search for the reservations.
BOOST_FOREACH(const Host::IdentifierType& id, cfg->getIdentifierTypes()) {
IdentifierMap::const_iterator id_ctx = ctx.host_identifiers_.find(id);
if (id_ctx != ctx.host_identifiers_.end()) {
// Attempt to find a host using a specified identifier.
ctx.host_ = host_get(ctx.subnet_->getID(), id,
&id_ctx->second[0], id_ctx->second.size());
// If we found matching host, return.
if (ctx.host_) {
return;
}
}
}
}
}
}
// ##########################################################################
// # DHCPv6 lease allocation code starts here.
// ##########################################################################
......@@ -339,30 +375,12 @@ AllocEngine::ClientContext6::ClientContext6(const Subnet6Ptr& subnet, const Duid
}
void AllocEngine::findReservation(ClientContext6& ctx) const {
if (!ctx.subnet_ || !ctx.duid_) {
return;
}
ctx.host_.reset();
// Check which host reservation mode is supported in this subnet.
Subnet::HRMode hr_mode = ctx.subnet_->getHostReservationMode();
// Check if there's a host reservation for this client. Attempt to get
// host info only if reservations are not disabled.
if (hr_mode != Subnet::HR_DISABLED) {
BOOST_FOREACH(const IdentifierPair& id, ctx.host_identifiers_) {
ctx.host_ = HostMgr::instance().get6(ctx.subnet_->getID(),
id.first, &id.second[0],
id.second.size());
// If we found matching host, return.
if (ctx.host_) {
return;
}
}
}
void AllocEngine::findReservation(ClientContext6& ctx) {
ConstCfgHostReservationsPtr cfg =
CfgMgr::instance().getCurrentCfg()->getCfgHostReservations6();
findReservationInternal(ctx, cfg, boost::bind(&HostMgr::get6,
&HostMgr::instance(),
_1, _2, _3, _4));
}
Lease6Collection
......@@ -2117,27 +2135,11 @@ AllocEngine::allocateLease4(ClientContext4& ctx) {
void
AllocEngine::findReservation(ClientContext4& ctx) {
ctx.host_.reset();
// We can only search for the reservation if a subnet has been selected.
if (ctx.subnet_) {
// Check which host reservation mode is supported in this subnet.
Subnet::HRMode hr_mode = ctx.subnet_->getHostReservationMode();
// Check if there is a host reseravtion for this client. Attempt to
// get host information
if (hr_mode != Subnet::HR_DISABLED) {
BOOST_FOREACH(const IdentifierPair& id, ctx.host_identifiers_) {
ctx.host_ = HostMgr::instance().get4(ctx.subnet_->getID(),
id.first, &id.second[0],
id.second.size());
// If we found matching host, return.
if (ctx.host_) {
return;
}
}
}
}
ConstCfgHostReservationsPtr cfg =
CfgMgr::instance().getCurrentCfg()->getCfgHostReservations4();
findReservationInternal(ctx, cfg, boost::bind(&HostMgr::get4,
&HostMgr::instance(),
_1, _2, _3, _4));
}
Lease4Ptr
......
......@@ -13,6 +13,7 @@
#include <dhcp/pkt4.h>
#include <dhcp/pkt6.h>
#include <dhcp/option6_ia.h>
#include <dhcpsrv/cfg_host_reservations.h>
#include <dhcpsrv/host.h>
#include <dhcpsrv/subnet.h>
#include <dhcpsrv/lease_mgr.h>
......@@ -617,10 +618,33 @@ public:
/// Attempts to find appropriate host reservation in HostMgr. If found, it
/// will be set in ctx.host_.
/// @param ctx Client context that contains all necessary information.
void findReservation(ClientContext6& ctx) const;
static void findReservation(ClientContext6& ctx);
private:
/// @brief Type of the function used by @ref findReservationInternal to
/// retrieve reservations by subnet identifier and host identifier.
typedef boost::function<ConstHostPtr(const SubnetID&,
const Host::IdentifierType&,
const uint8_t*, const size_t)> HostGetFunc;
/// @brief Common function for searching host reservations.
///
/// This is a common function called by variants of @ref findReservation
/// functions.
///
/// @param ctx Reference to a @ref ClientContext6 or @ref ClientContext4.
/// @param cfg Pointer to object holding general configuration for host
/// reservations. It uses this object to read the desired order of
/// host identifiers to be used to search for reservations.
/// @param host_get Pointer to the @ref HostMgr functions to be used
/// to retrieve reservation by subnet identifier and host identifier.
/// @tparam ContextType Either @ref ClientContext6 or @ref ClientContext4.
template<typename ContextType>
static void findReservationInternal(ContextType& ctx,
const ConstCfgHostReservationsPtr& cfg,
const HostGetFunc& host_get);
/// @brief creates a lease and inserts it in LeaseMgr if necessary
///
/// Creates a lease based on specified parameters and tries to insert it
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment