.arg(subnet->getID());
}
}
+
+ // Before we can check for static reservations, we need to prepare a set
+ // of identifiers to be used for this.
+
+ // HW address.
+ if (context_->hwaddr_ && !context_->hwaddr_->hwaddr_.empty()) {
+ context_->host_identifiers_[Host::IDENT_HWADDR] = context_->hwaddr_->hwaddr_;
+ }
+
+ // Client identifier
+ if (context_->clientid_) {
+ const std::vector<uint8_t>& vec = context_->clientid_->getDuid();
+ if (!vec.empty()) {
+ // Client identifier type = DUID? Client identifier holding a DUID
+ // comprises Type (1 byte), IAID (4 bytes), followed by the actual
+ // DUID. Thus, the minimal length is 6.
+ if ((vec[0] == CLIENT_ID_OPTION_TYPE_DUID) && (vec.size() > 5)) {
+ // Extract DUID, skip IAID.
+ context_->host_identifiers_.insert(
+ AllocEngine::IdentifierPair(Host::IDENT_DUID,
+ std::vector<uint8_t>(vec.begin() + 5,
+ vec.end())));
+ }
+ /// @todo Add support for other client identifiers (see #4317).
+ }
+ }
+ // Circuit id
+ OptionPtr rai = query_->getOption(DHO_DHCP_AGENT_OPTIONS);
+ if (rai) {
+ OptionPtr circuit_id_opt = rai->getOption(RAI_OPTION_AGENT_CIRCUIT_ID);
+ if (circuit_id_opt) {
+ const OptionBuffer& circuit_id_vec = circuit_id_opt->getData();
+ if (!circuit_id_vec.empty()) {
+ context_->host_identifiers_[Host::IDENT_CIRCUIT_ID] = circuit_id_vec;
+ }
+ }
+ }
+
// Check for static reservations.
alloc_engine->findReservation(*context_);
};
-// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
server_facing_relay_addr_("10.0.0.2"),
srv_(boost::shared_ptr<NakedDhcpv4Srv>(new NakedDhcpv4Srv(0))),
state_(state),
- use_relay_(false) {
+ use_relay_(false),
+ circuit_id_() {
}
Dhcp4Client::Dhcp4Client(boost::shared_ptr<NakedDhcpv4Srv> srv,
server_facing_relay_addr_("10.0.0.2"),
srv_(srv),
state_(state),
- use_relay_(false) {
+ use_relay_(false),
+ circuit_id_() {
}
void
msg->setHops(1);
msg->setGiaddr(relay_addr_);
msg->setLocalAddr(server_facing_relay_addr_);
+ // Insert RAI
+ OptionPtr rai(new Option(Option::V4, DHO_DHCP_AGENT_OPTIONS));
+ // Insert circuit id, if specified.
+ if (!circuit_id_.empty()) {
+ rai->addOption(OptionPtr(new Option(Option::V4, RAI_OPTION_AGENT_CIRCUIT_ID,
+ OptionBuffer(circuit_id_.begin(),
+ circuit_id_.end()))));
+ }
+ msg->addOption(rai);
}
// Repack the message to simulate wire-data parsing.
msg->pack();
-// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
const uint8_t option2 = 0,
const uint8_t option3 = 0);
+ /// @brief Sets circuit-id value to be included in the circuit-id
+ /// sub option of the RAI option.
+ ///
+ /// @param circuit_id New circuit-id value.
+ void setCircuitId(const std::string& circuit_id) {
+ circuit_id_ = circuit_id;
+ }
+
/// @brief Sets destination address for the messages being sent by the
/// client.
///
/// @brief Enable relaying messages to the server.
bool use_relay_;
+ /// @brief Specifies value to be inserted into circuit-id sub option
+ /// of the RAI option.
+ std::string circuit_id_;
+
/// @brief Extra options the client will send.
OptionCollection extra_options_;
};
-// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
" {"
" \"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\""
" }"
" ]"
"} ]"
ASSERT_TRUE(subnet->inPool(Lease::TYPE_V4, clientB.config_.lease_.addr_));
}
+// This test checks that it is possible to make a reservation by
+// 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"));
+ // Specify circuit-id.
+ client.setCircuitId("charter950");
+
+ // Configure DHCP server.
+ configure(DORA_CONFIGS[2], *client.getServer());
+ // Client A performs 4-way exchange and should obtain a reserved
+ // 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.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.
Dhcpv6Srv::createContext(const Pkt6Ptr& pkt) {
AllocEngine::ClientContext6 ctx;
ctx.subnet_ = selectSubnet(pkt);
+ ctx.query_ = pkt;
+
+ // Collect host identifiers.
+ // DUID
ctx.duid_ = pkt->getClientId();
+ if (ctx.duid_) {
+ ctx.host_identifiers_[Host::IDENT_DUID] = ctx.duid_->getDuid();
+ }
+ // HW Address.
ctx.hwaddr_ = getMAC(pkt);
- ctx.query_ = pkt;
+ if (ctx.hwaddr_) {
+ ctx.host_identifiers_[Host::IDENT_HWADDR] = ctx.hwaddr_->hwaddr_;
+ }
+ // And find a host reservation using those identifiers.
alloc_engine_->findReservation(ctx);
return (ctx);
ctx.hwaddr_ = orig_ctx.hwaddr_;
ctx.host_ = orig_ctx.host_;
ctx.query_ = orig_ctx.query_;
+ ctx.host_identifiers_ = orig_ctx.host_identifiers_;
Lease6Collection leases = alloc_engine_->allocateLeases6(ctx);
ctx.hwaddr_ = orig_ctx.hwaddr_;
ctx.host_ = orig_ctx.host_;
ctx.query_ = orig_ctx.query_;
+ ctx.host_identifiers_ = orig_ctx.host_identifiers_;
Lease6Collection leases = alloc_engine_->allocateLeases6(ctx);
-// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
iface_name_ = iface_name;
}
+ /// @brief Sets link local address used by the client.
+ ///
+ /// @param link_local New link local address.
+ void setLinkLocal(const asiolink::IOAddress& link_local) {
+ link_local_ = link_local;
+ }
+
/// @brief Set an address hint to be sent to a server.
///
/// @param pref_lft Preferred lifetime.
-// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <config.h>
+#include <asiolink/io_address.h>
#include <dhcp/tests/iface_mgr_test_config.h>
#include <dhcp6/tests/dhcp6_test_utils.h>
#include <dhcp6/tests/dhcp6_client.h>
using namespace isc;
+using namespace isc::asiolink;
using namespace isc::dhcp;
using namespace isc::dhcp::test;
///
/// - Configuration 0:
/// Single subnet with two reservations, one with a hostname, one without
+/// - Configuration 1:
+/// Multiple reservations using different host identifiers.
const char* CONFIGS[] = {
// Configuration 0:
"{ "
" \"ip-addresses\": [ \"2001:db8:1:1::babf\" ]"
" } ]"
" } ]"
+ "}",
+
+ // Configuration 1:
+ "{ "
+ "\"interfaces-config\": {"
+ " \"interfaces\": [ \"*\" ]"
+ "},"
+ "\"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
iface_mgr_test_config_(true) {
}
+ /// @brief Verifies that the reservation is retrieved by the server
+ /// using one of the host identifiers.
+ ///
+ /// @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 exp_ip_address Expected IPv6 address in the returned
+ /// reservation.
+ void testReservationByIdentifier(Dhcp6Client& client,
+ const std::string exp_ip_address) {
+ configure(CONFIGS[1], *client.getServer());
+
+ const Subnet6Collection* subnets = CfgMgr::instance().getCurrentCfg()->
+ getCfgSubnets6()->getAll();
+ ASSERT_EQ(1, subnets->size());
+
+ // Configure client to request IA_NA and append IA_NA option
+ // to the client's message.
+ client.useNA();
+ ASSERT_NO_THROW(client.useHint(100, 200, 64, "2001:db8:1:1::dead:beef"));
+
+ // Perform 4-way exchange.
+ ASSERT_NO_THROW(client.doSARR());
+
+ // Verify that the client we got the reserved address
+ ASSERT_EQ(1, client.getLeaseNum());
+ Lease6 lease_client = client.getLease(0);
+ EXPECT_EQ(exp_ip_address, lease_client.addr_.toText());
+ }
+
/// @brief Interface Manager's fake configuration control.
IfaceMgrTestConfig iface_mgr_test_config_;
};
EXPECT_EQ("alice", lease_server2->hostname_);
}
+// This test verfies that the host reservation by DUID is found by the
+// server.
+TEST_F(HostTest, reservationByDUID) {
+ Dhcp6Client client;
+ // 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");
+}
+
+// This test verfies that the host reservation by HW address is found
+// by the server.
+TEST_F(HostTest, reservationByHWAddress) {
+ Dhcp6Client client;
+ // 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"));
+ // Run the actual test.
+ testReservationByIdentifier(client, "2001:db8:1::1");
+}
+
+
+
} // end of anonymous namespace
#endif
+/* Client identifier types */
+static const uint8_t CLIENT_ID_OPTION_TYPE_DUID = 255;
+
} // end of isc::dhcp namespace
} // end of isc namespace
: subnet_(), duid_(), iaid_(0), type_(Lease::TYPE_NA), hwaddr_(),
hints_(), fwd_dns_update_(false), rev_dns_update_(false), hostname_(""),
callout_handle_(), fake_allocation_(false), old_leases_(), host_(),
- query_(), ia_rsp_() {
+ query_(), ia_rsp_(), host_identifiers_() {
}
AllocEngine::ClientContext6::ClientContext6(const Subnet6Ptr& subnet, const DuidPtr& duid,
subnet_(subnet), duid_(duid), iaid_(iaid), type_(type), hwaddr_(),
hints_(), fwd_dns_update_(fwd_dns), rev_dns_update_(rev_dns),
hostname_(hostname), fake_allocation_(fake_allocation),
- old_leases_(), host_(), query_(), ia_rsp_() {
+ old_leases_(), host_(), query_(), ia_rsp_(), host_identifiers_() {
static asiolink::IOAddress any("::");
}
// callout_handle, host pointers initiated to NULL by their
// respective constructors.
+
+ // Initialize host identifiers.
+ if (duid) {
+ host_identifiers_[Host::IDENT_DUID] = duid->getDuid();
+ }
}
return;
}
+ ctx.host_.reset();
+
// Check which host reservation mode is supported in this subnet.
Subnet::HRMode hr_mode = ctx.subnet_->getHostReservationMode();
// host info only if reservations are not disabled.
if (hr_mode != Subnet::HR_DISABLED) {
- ctx.host_ = HostMgr::instance().get6(ctx.subnet_->getID(), ctx.duid_,
- ctx.hwaddr_);
- } else {
- // Let's explicitly set it to NULL if reservations are disabled.
- ctx.host_.reset();
+ 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;
+ }
+ }
}
}
requested_address_(IOAddress::IPV4_ZERO_ADDRESS()),
fwd_dns_update_(false), rev_dns_update_(false),
hostname_(""), callout_handle_(), fake_allocation_(false),
- old_lease_(), host_(), conflicting_lease_(), query_() {
+ old_lease_(), host_(), conflicting_lease_(), query_(),
+ host_identifiers_() {
}
AllocEngine::ClientContext4::ClientContext4(const Subnet4Ptr& subnet,
requested_address_(requested_addr),
fwd_dns_update_(fwd_dns_update), rev_dns_update_(rev_dns_update),
hostname_(hostname), callout_handle_(),
- fake_allocation_(fake_allocation), old_lease_(), host_() {
+ fake_allocation_(fake_allocation), old_lease_(), host_(),
+ host_identifiers_() {
+
+ // Initialize host identifiers.
+ if (hwaddr) {
+ host_identifiers_[Host::IDENT_HWADDR] = hwaddr->hwaddr_;
+ }
}
Lease4Ptr
// Check if there is a host reseravtion for this client. Attempt to
// get host information
if (hr_mode != Subnet::HR_DISABLED) {
- // This method should handle the case when there is neither hwaddr
- // nor clientid_ available and simply return NULL.
- ctx.host_ = HostMgr::instance().get4(ctx.subnet_->getID(), ctx.hwaddr_,
- ctx.clientid_);
+ 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;
+ }
+ }
}
}
}
-// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2016 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
#include <boost/noncopyable.hpp>
#include <map>
+#include <utility>
namespace isc {
namespace dhcp {
/// @brief Container for client's hints.
typedef std::vector<HintType> HintContainer;
+ /// @brief A tuple holding host identifier type and value.
+ typedef std::pair<Host::IdentifierType, std::vector<uint8_t> > IdentifierPair;
+
+ /// @brief Map holding values to be used as host identifiers.
+ typedef std::map<Host::IdentifierType, std::vector<uint8_t> > IdentifierMap;
+
/// @brief Context information for the DHCPv6 leases allocation.
///
/// This structure holds a set of information provided by the DHCPv6
/// @brief A pointer to the IA_NA/IA_PD option to be sent in response
Option6IAPtr ia_rsp_;
+ /// @brief A map holding host identifiers extracted from a message
+ /// received by the server.
+ IdentifierMap host_identifiers_;
+
/// @brief Default constructor.
ClientContext6();
/// transaction identification information.
Pkt4Ptr query_;
+ /// @brief A map holding host identifiers extracted from a message
+ /// received by the server.
+ IdentifierMap host_identifiers_;
+
/// @brief Default constructor.
ClientContext4();
AllocEngine::ClientContext4 ctx(subnet_, clientid_, hwaddr_,
IOAddress("0.0.0.0"), false, false,
"", false);
+ ctx.host_identifiers_[Host::IDENT_HWADDR] = hwaddr_->hwaddr_;
+ ctx.host_identifiers_[Host::IDENT_DUID] = clientid_->getDuid();
// There is no reservation in the database so no host should be
// retruned.
-// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2016 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
AllocEngine::ClientContext6 ctx(subnet_, duid_, iaid_, hint, type,
false, false, "", fake);
ctx.hwaddr_ = hwaddr_;
+ ctx.host_identifiers_[Host::IDENT_HWADDR] = hwaddr_->hwaddr_;
ctx.query_.reset(new Pkt6(fake ? DHCPV6_SOLICIT : DHCPV6_REQUEST, 1234));
findReservation(*engine, ctx);