Commit f1a406e4 authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[3232] Implemented support for Rebind.

parent fb010f7b
......@@ -147,6 +147,13 @@ information about existing subnet was removed. Note that in a sense this is wors
case of DHCP6_EXTEND_NA_UNKNOWN, as not only the lease is unknown, but also the subnet
is. Depending on the reasons of this condition, it may or may not correct on its own.
% DHCP6_EXTEND_PD_NO_BINDING client sent a %1 message to extend lifetimes of prefixes for an unknown binding: duid=%2, iaid=%3, subnet=%4
This warning message is logged when a client attempts to extend the lifetime of the
prefix it is using, but the server was unable to find the binding to which
this lease belongs. This error condition has different implications for the
Renew and Rebind messages. For the former, the server will respond with NoBinding
status code. For the latter, the server will discard the message.
% DHCP6_EXTEND_PD_UNKNOWN_SUBNET %1 message received from client on unknown subnet (duid=%2, iaid=%3)
A warning message indicating that a client is attempting to extend lease lifetime
for the prefix, but the server doesn't have any information about the subnet this
......@@ -546,18 +553,3 @@ This warning message is printed when client attempts to release an prefix
lease, but no such lease is known by the server. See the explanation
of the status code DHCP6_UNKNOWN_RENEW_PD for possible reasons for
such behavior.
% DHCP6_UNKNOWN_RENEW_PD received unknown IA_NA RENEW from client (duid=%1, iaid=%2) in subnet %3
This warning message is printed when client attempts to renew an address lease
(in IA_NA option), but no such lease is known by the server. It typically means
that client has attempted to use its lease past its lifetime: causes of this
include a adjustment of the clients date/time setting or poor support
for sleep/recovery. A properly implemented client will recover from such
a situation by restarting the lease allocation process after receiving
a negative reply from the server.
An alternative cause could be that the server has lost its database
recently and does not recognize its well-behaving clients. This is more
probable if you see many such messages. Clients will recover from this,
but they will most likely get a different IP addresses and experience
a brief service interruption.
......@@ -136,7 +136,7 @@ const bool FQDN_REPLACE_CLIENT_NAME = false;
static const char* SERVER_DUID_FILE = "b10-dhcp6-serverid";
Dhcpv6Srv::Dhcpv6Srv(uint16_t port)
:alloc_engine_(), serverid_(), shutdown_(true), port_(port)
:alloc_engine_(), serverid_(), port_(port), shutdown_(true)
{
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_OPEN_SOCKET).arg(port);
......@@ -211,30 +211,48 @@ void Dhcpv6Srv::sendPacket(const Pkt6Ptr& packet) {
}
bool
Dhcpv6Srv::testServerID(const Pkt6Ptr& pkt){
/// @todo Currently we always check server identifier regardless if
/// it is allowed in the received message or not (per RFC3315).
/// If the server identifier is not allowed in the message, the
/// sanityCheck function should deal with it. We may rethink this
/// design if we decide that it is appropriate to check at this stage
/// of message processing that the server identifier must or must not
/// be present. In such case however, the logic checking server id
/// will have to be removed from sanityCheck and placed here instead,
/// to avoid duplicate checks.
OptionPtr server_id = pkt->getOption(D6O_SERVERID);
if (server_id){
// Let us test received ServerID if it is same as ServerID
// which is beeing used by server
if (getServerID()->getData() != server_id->getData()){
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL_DATA, DHCP6_PACKET_MISMATCH_SERVERID_DROP)
.arg(pkt->getName())
.arg(pkt->getTransid())
.arg(pkt->getIface());
return (false);
}
}
// retun True if: no serverid received or ServerIDs matching
return (true);
Dhcpv6Srv::testServerID(const Pkt6Ptr& pkt) {
/// @todo Currently we always check server identifier regardless if
/// it is allowed in the received message or not (per RFC3315).
/// If the server identifier is not allowed in the message, the
/// sanityCheck function should deal with it. We may rethink this
/// design if we decide that it is appropriate to check at this stage
/// of message processing that the server identifier must or must not
/// be present. In such case however, the logic checking server id
/// will have to be removed from sanityCheck and placed here instead,
/// to avoid duplicate checks.
OptionPtr server_id = pkt->getOption(D6O_SERVERID);
if (server_id){
// Let us test received ServerID if it is same as ServerID
// which is beeing used by server
if (getServerID()->getData() != server_id->getData()){
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL_DATA, DHCP6_PACKET_MISMATCH_SERVERID_DROP)
.arg(pkt->getName())
.arg(pkt->getTransid())
.arg(pkt->getIface());
return (false);
}
}
// retun True if: no serverid received or ServerIDs matching
return (true);
}
bool
Dhcpv6Srv::testUnicast(const Pkt6Ptr& pkt) const {
switch (pkt->getType()) {
case DHCPV6_SOLICIT:
case DHCPV6_CONFIRM:
case DHCPV6_REBIND:
case DHCPV6_INFORMATION_REQUEST:
if (pkt->relay_info_.empty() && !pkt->getLocalAddr().isV6Multicast()) {
return (false);
}
break;
default:
// do nothing
;
}
return (true);
}
bool Dhcpv6Srv::run() {
......@@ -258,7 +276,8 @@ bool Dhcpv6Srv::run() {
LOG_ERROR(dhcp6_logger, DHCP6_PACKET_RECEIVE_FAIL).arg(e.what());
}
// Timeout may be reached or signal received, which breaks select() with no packet received
// Timeout may be reached or signal received, which breaks select()
// with no packet received
if (!query) {
continue;
}
......@@ -311,8 +330,15 @@ bool Dhcpv6Srv::run() {
}
// Check if received query carries server identifier matching
// server identifier being used by the server.
if (!testServerID(query)){
continue;
if (!testServerID(query)) {
continue;
}
// Check if the received query has been sent to unicast or multicast.
// The Solicit, Confirm, Rebind and Information Request will be
// discarded if sent to unicast address.
if (!testUnicast(query)) {
continue;
}
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_PACKET_RECEIVED)
......@@ -1657,66 +1683,133 @@ Dhcpv6Srv::extendIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
}
OptionPtr
Dhcpv6Srv::renewIA_PD(const Subnet6Ptr& subnet, const DuidPtr& duid,
const Pkt6Ptr& query, boost::shared_ptr<Option6IA> ia) {
Dhcpv6Srv::extendIA_PD(const Subnet6Ptr& subnet, const DuidPtr& duid,
const Pkt6Ptr& query, boost::shared_ptr<Option6IA> ia) {
// Let's create a IA_NA response and fill it in later
boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_PD, ia->getIAID()));
// Let's create a IA_PD response and fill it in later
Option6IAPtr ia_rsp(new Option6IA(D6O_IA_PD, ia->getIAID()));
// If there is no subnet for the particular client, we can't retrieve
// information about client's leases from lease database. We treat this
// as no binding for the client.
if (!subnet) {
// Insert status code NoBinding
ia_rsp->addOption(createStatusCode(STATUS_NoBinding,
"Sorry, no known leases for this duid/iaid."));
// Per RFC3633, section 12.2, if there is no binding and we are
// processing a Renew, the NoBinding status code should be returned.
if (query->getType() == DHCPV6_RENEW) {
// Insert status code NoBinding
ia_rsp->addOption(createStatusCode(STATUS_NoBinding,
"Sorry, no known PD leases"
" for this duid/iaid."));
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_EXTEND_PD_UNKNOWN_SUBNET)
.arg(duid->toText())
.arg(ia->getIAID());
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL,
DHCP6_EXTEND_PD_UNKNOWN_SUBNET)
.arg(duid->toText())
.arg(ia->getIAID());
return (ia_rsp);
return (ia_rsp);
// Per RFC3633, section 12.2, if there is no binding and we are
// processing Rebind, the message has to be discarded (assuming that
// the server doesn't know if the prefix in the IA_PD option is
// appropriate for the client's link). The exception being thrown
// here should propagate to the main loop and cause the message to
// be discarded.
} else {
isc_throw(DHCPv6DiscardMessageError, "no subnet found for the"
" client sending Rebind to extend lifetime of the"
" prefix (DUID=" << duid->toText() << ", IAID="
<< ia->getIAID());
}
}
Lease6Ptr lease = LeaseMgrFactory::instance().getLease6(Lease::TYPE_PD,
*duid, ia->getIAID(),
subnet->getID());
// There is a subnet selected. Let's pick the lease.
Lease6Ptr lease =
LeaseMgrFactory::instance().getLease6(Lease::TYPE_PD,
*duid, ia->getIAID(),
subnet->getID());
// There is no binding for the client.
if (!lease) {
// Client is renewing a lease that we don't know about.
// Insert status code NoBinding
ia_rsp->addOption(createStatusCode(STATUS_NoBinding,
"Sorry, no known leases for this duid/iaid."));
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_UNKNOWN_RENEW_PD)
.arg(duid->toText())
.arg(ia->getIAID())
.arg(subnet->toText());
// Per RFC3633, section 12.2, if there is no binding and we are
// processing a Renew, the NoBinding status code should be returned.
if (query->getType() == DHCPV6_RENEW) {
// Insert status code NoBinding.
ia_rsp->addOption(createStatusCode(STATUS_NoBinding,
"Sorry, no known PD"
" leases for this duid/iaid."));
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL,
DHCP6_EXTEND_PD_NO_BINDING)
.arg(query->getName())
.arg(duid->toText())
.arg(ia->getIAID())
.arg(subnet->toText());
// Per RFC3633, section 12.2, if there is no binding and we are
// processing Rebind, the message has to be discarded (assuming that
// the server doesn't know if the prefix in the IA_PD option is
// appropriate for the client's link). The exception being thrown
// here should propagate to the main loop and cause the message to
// be discarded.
} else {
isc_throw(DHCPv6DiscardMessageError, "no binding found for the"
" DUID=" << duid->toText() << ", IAID="
<< ia->getIAID() << ", subnet="
<< subnet->toText() << " when processing a Rebind"
" message with IA_PD option");
}
return (ia_rsp);
}
// Keep the old data in case the callout tells us to skip update.
Lease6 old_data = *lease;
// Do the actual lease update
lease->preferred_lft_ = subnet->getPreferred();
lease->valid_lft_ = subnet->getValid();
lease->t1_ = subnet->getT1();
lease->t2_ = subnet->getT2();
lease->cltt_ = time(NULL);
bool invalid_prefix = false;
// Check what prefix the client has sent. The prefix should match the
// one that we have associated with the IAID. If it doesn't match we
// have to return the prefix with the lifetimes set to 0 (see section
// 12.2. of RFC3633).
Option6IAPrefixPtr ia_prefix = boost::dynamic_pointer_cast<
Option6IAPrefix>(ia->getOption(D6O_IAPREFIX));
if (ia_prefix && ((ia_prefix->getAddress() != lease->addr_) ||
(ia_prefix->getLength() != lease->prefixlen_))) {
Option6IAPrefixPtr prefix(new Option6IAPrefix(D6O_IAPREFIX,
ia_prefix->getAddress(),
ia_prefix->getLength(),
0, 0));
ia_rsp->addOption(prefix);
invalid_prefix = true;
// Also update IA_PD container with proper T1, T2 values
ia_rsp->setT1(subnet->getT1());
ia_rsp->setT2(subnet->getT2());
} else {
// The prefix sent by a client is correct. Let's extend the lease
// for the client.
lease->preferred_lft_ = subnet->getPreferred();
lease->valid_lft_ = subnet->getValid();
// Do the actual lease update
lease->t1_ = subnet->getT1();
lease->t2_ = subnet->getT2();
lease->cltt_ = time(NULL);
// Also update IA_PD container with proper T1, T2 values
ia_rsp->setT1(subnet->getT1());
ia_rsp->setT2(subnet->getT2());
Option6IAPrefixPtr prefix(new Option6IAPrefix(D6O_IAPREFIX,
lease->addr_,
lease->prefixlen_,
lease->preferred_lft_,
lease->valid_lft_));
ia_rsp->addOption(prefix);
}
boost::shared_ptr<Option6IAPrefix>
prefix(new Option6IAPrefix(D6O_IAPREFIX, lease->addr_,
lease->prefixlen_, lease->preferred_lft_,
lease->valid_lft_));
ia_rsp->addOption(prefix);
bool skip = false;
// Execute all callouts registered for packet6_send
if (HooksManager::calloutsPresent(Hooks.hook_index_lease6_renew_)) {
// Get the callouts specific for the processed message and execute them.
int hook_point = query->getType() == DHCPV6_RENEW ?
Hooks.hook_index_lease6_renew_ : Hooks.hook_index_lease6_rebind_;
if (HooksManager::calloutsPresent(hook_point)) {
CalloutHandlePtr callout_handle = getCalloutHandle(query);
// Delete all previous arguments
......@@ -1732,7 +1825,7 @@ Dhcpv6Srv::renewIA_PD(const Subnet6Ptr& subnet, const DuidPtr& duid,
callout_handle->setArgument("ia_pd", ia_rsp);
// Call all installed callouts
HooksManager::callCallouts(Hooks.hook_index_lease6_renew_,
HooksManager::callCallouts(hook_point,
*callout_handle);
// Remember hook's instruction whether we want to skip update or not
......@@ -1740,12 +1833,17 @@ Dhcpv6Srv::renewIA_PD(const Subnet6Ptr& subnet, const DuidPtr& duid,
}
if (!skip) {
LeaseMgrFactory::instance().updateLease6(lease);
// If the prefix specified by the client is wrong, we don't want to
// update client's lease.
if (!invalid_prefix) {
LeaseMgrFactory::instance().updateLease6(lease);
}
} else {
// Callouts decided to skip the next processing step. The next
// processing step would to actually renew the lease, so skip at this
// stage means "keep the old lease as it is".
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_HOOKS, DHCP6_HOOK_LEASE6_EXTEND_SKIP);
// processing step would to actually renew/rebind the lease, so skip
// at this stage means "keep the old lease as it is".
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_HOOKS, DHCP6_HOOK_LEASE6_EXTEND_SKIP)
.arg(query->getName());
// Copy back the original date to the lease. For MySQL it doesn't make
// much sense, but for memfile, the Lease6Ptr points to the actual lease
......@@ -1816,9 +1914,9 @@ Dhcpv6Srv::extendLeases(const Pkt6Ptr& query, Pkt6Ptr& reply) {
}
case D6O_IA_PD: {
OptionPtr answer_opt = renewIA_PD(subnet, duid, query,
boost::dynamic_pointer_cast<
Option6IA>(opt->second));
OptionPtr answer_opt = extendIA_PD(subnet, duid, query,
boost::dynamic_pointer_cast<
Option6IA>(opt->second));
if (answer_opt) {
reply->addOption(answer_opt);
}
......@@ -2258,9 +2356,18 @@ Dhcpv6Srv::processRenew(const Pkt6Ptr& renew) {
Pkt6Ptr
Dhcpv6Srv::processRebind(const Pkt6Ptr& rebind) {
/// @todo: Implement this
Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, rebind->getTransid()));
return reply;
copyDefaultOptions(rebind, reply);
appendDefaultOptions(rebind, reply);
appendRequestedOptions(rebind, reply);
processClientFqdn(rebind, reply);
extendLeases(rebind, reply);
generateFqdn(reply);
createNameChangeRequests(rebind);
return (reply);
}
Pkt6Ptr
......
......@@ -35,6 +35,14 @@
namespace isc {
namespace dhcp {
/// @brief This exception is thrown when DHCP server hits the error which should
/// result in discarding the message being processed.
class DHCPv6DiscardMessageError : public Exception {
public:
DHCPv6DiscardMessageError(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what) { };
};
/// @brief DHCPv6 server service.
///
/// This class represents DHCPv6 server. It contains all
......@@ -129,6 +137,18 @@ protected:
/// used by the server; false otherwise.
bool testServerID(const Pkt6Ptr& pkt);
/// @brief Check if the message can be sent to unicast.
///
/// This function checks if the received message conforms to the section 15
/// of RFC3315 which says that: "A server MUST discard any Solicit, Confirm,
/// Rebind or Information-request messages it receives with a unicast
/// destination address.
///
/// @param pkt DHCPv6 message to be checked.
/// @return false if the message has been sent to unicast address but it is
/// not allowed according to RFC3315, section 15; true otherwise.
bool testUnicast(const Pkt6Ptr& pkt) const;
/// @brief verifies if specified packet meets RFC requirements
///
/// Checks if mandatory option is really there, that forbidden option
......@@ -271,21 +291,29 @@ protected:
/// @return IA_NA option (server's response)
OptionPtr extendIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
const Pkt6Ptr& query, const Pkt6Ptr& answer,
boost::shared_ptr<Option6IA> ia);
Option6IAPtr ia);
/// @brief Renews specific IA_PD option
/// @brief Extends lifetime of the prefix.
///
/// This function is called by the logic which processes Renew and Rebind
/// messages to extend the lifetime of the existing prefix.
///
/// Generates response to IA_PD in Renew. This typically includes finding a
/// lease that corresponds to the received prefix. If no such lease is
/// found, an IA_PD response is generated with an appropriate status code.
/// The behavior of this function is different in that when there is no
/// binding found in the lease database for the particular client the
/// NoBinding status code is returned when processing Renew, the exception
/// is thrown when there is no binding and the Rebind message is processed
/// (see RFC3633, section 12.2. for details).
///
/// @param subnet subnet the sender belongs to
/// @param duid client's duid
/// @param query client's message
/// @param ia IA_PD option that is being renewed
/// @return IA_PD option (server's response)
OptionPtr renewIA_PD(const Subnet6Ptr& subnet, const DuidPtr& duid,
const Pkt6Ptr& query, boost::shared_ptr<Option6IA> ia);
/// @throw DHCPv6DiscardMessageError when the message being processed should
/// be discarded by the server, i.e. there is no binding for the client doing
/// Rebind.
OptionPtr extendIA_PD(const Subnet6Ptr& subnet, const DuidPtr& duid,
const Pkt6Ptr& query, Option6IAPtr ia);
/// @brief Releases specific IA_NA option
///
......@@ -602,15 +630,15 @@ private:
/// Server DUID (to be sent in server-identifier option)
OptionPtr serverid_;
/// Indicates if shutdown is in progress. Setting it to true will
/// initiate server shutdown procedure.
volatile bool shutdown_;
/// UDP port number on which server listens.
uint16_t port_;
protected:
/// Indicates if shutdown is in progress. Setting it to true will
/// initiate server shutdown procedure.
volatile bool shutdown_;
/// Holds a list of @c isc::dhcp_ddns::NameChangeRequest objects, which
/// are waiting for sending to b10-dhcp-ddns module.
std::queue<isc::dhcp_ddns::NameChangeRequest> name_change_reqs_;
......
......@@ -81,6 +81,8 @@ dhcp6_unittests_SOURCES += ../dhcp6_log.h ../dhcp6_log.cc
dhcp6_unittests_SOURCES += ../ctrl_dhcp6_srv.cc
dhcp6_unittests_SOURCES += ../config_parser.cc ../config_parser.h
dhcp6_unittests_SOURCES += wireshark.cc
dhcp6_unittests_SOURCES += dhcp6_client.cc dhcp6_client.h
dhcp6_unittests_SOURCES += rebind_unittest.cc
nodist_dhcp6_unittests_SOURCES = ../dhcp6_messages.h ../dhcp6_messages.cc
nodist_dhcp6_unittests_SOURCES += marker_file.h test_libraries.h
......
// 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/dhcp6.h>
#include <dhcp/option_custom.h>
#include <dhcp/option6_ia.h>
#include <dhcp/option6_iaaddr.h>
#include <dhcp/pkt6.h>
#include <dhcpsrv/lease.h>
#include <dhcp6/tests/dhcp6_client.h>
#include <util/buffer.h>
#include <boost/pointer_cast.hpp>
#include <time.h>
using namespace isc::test;
namespace isc {
namespace dhcp {
namespace test {
Dhcp6Client::Dhcp6Client() :
relay_link_addr_("3000:1::1"),
curr_transid_(0),
dest_addr_(ALL_DHCP_RELAY_AGENTS_AND_SERVERS),
duid_(generateDUID(DUID::DUID_LLT)),
link_local_("fe80::3a60:77ff:fed5:cdef"),
srv_(boost::shared_ptr<NakedDhcpv6Srv>(new NakedDhcpv6Srv(0))),
use_na_(false),
use_pd_(false),
use_relay_(false) {
}
void
Dhcp6Client::applyConfiguration(const Pkt6Ptr& reply) {
typedef OptionCollection Opts;
// Get all options in the reply message and pick IA_NA and IA_PD.
Opts opts = reply->options_;
for (Opts::const_iterator opt = opts.begin(); opt != opts.end(); ++opt) {
Option6IAPtr ia = boost::dynamic_pointer_cast<Option6IA>(opt->second);
// If the current one is not IA option, get the next one.
if (!ia) {
continue;
}
// The default value of the prefix length is 128 (as for IPv6 address),
// as at this point we don't know if we are dealing with the address
// of prefix.
int prefix_len = 128;
// Check if this is the address.
Option6IAAddrPtr iaaddr = boost::dynamic_pointer_cast<
Option6IAAddr>(ia->getOption(D6O_IAADDR));
// If this is not the address it may be a prefix.
if (!iaaddr) {
iaaddr = boost::dynamic_pointer_cast<
Option6IAAddr>(ia->getOption(D6O_IAPREFIX));
// If this is a prefix, modify the prefix length accordingly.
if (iaaddr) {
prefix_len = boost::dynamic_pointer_cast<
Option6IAPrefix>(ia->getOption(D6O_IAPREFIX))->getLength();
}
}
/// Set the lease information if we have a prefix or address.
LeaseInfo lease_info;
if (iaaddr) {
Lease6 lease((prefix_len == 128 ? Lease::TYPE_NA : Lease::TYPE_PD),
iaaddr->getAddress(), duid_,
ia->getIAID(), iaaddr->getPreferred(),
iaaddr->getValid(), ia->getT1(), ia->getT2(), 0,
prefix_len);
lease.cltt_ = time(NULL);
lease_info.lease_ = lease;
} else {
// There is no prefix and no address. This IA option may simply
// contain a status code, so let's just reset the lease and keep
// IAID around.
lease_info.lease_ = Lease6();
lease_info.lease_.iaid_ = ia->getIAID();
}
// Check if the server has sent status code. If no status code, assume
// the status code to be 0.
OptionCustomPtr status_code = boost::dynamic_pointer_cast<
OptionCustom>(ia->getOption(D6O_STATUS_CODE));
if (status_code) {
lease_info.status_code_ = status_code->readInteger<uint16_t>(0);
} else {
lease_info.status_code_ = 0;
}
applyLease(lease_info);
}
}
void
Dhcp6Client::applyLease(const LeaseInfo& lease_info) {
// Go over existing leases and try to match the one that we have.
for (int i = 0; i < config_.leases_.size(); ++i) {
Lease6 existing_lease = config_.leases_[i].lease_;
// If IAID is matching and there is an actual address assigned
// replace the current lease. The default address is :: if the
// server hasn't sent the IA option. In this case, there is no
// lease assignment so we keep what we have.
if ((existing_lease.iaid_ == lease_info.lease_.iaid_)
&& (lease_info.lease_.addr_ != asiolink::IOAddress("::"))) {
config_.leases_[i] = lease_info;
return;
} else if (lease_info.lease_.addr_ == asiolink::IOAddress("::")) {
config_.leases_[i].status_code_ = lease_info.status_code_;
return;
}
}
// It is a new lease. Add it.
config_.leases_.push_back(lease_info);
}
void
Dhcp6Client::copyIAs(const Pkt6Ptr& source, const Pkt6Ptr& dest) {
typedef OptionCollection Opts;
// Copy IA_NAs.
Opts opts = source->getOptions(D6O_IA_NA);
for (Opts::const_iterator opt = opts.begin(); opt != opts.end(); ++opt) {
dest->addOption(opt->second);