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

[3070] Implemented Rapid Commit support for DHCPv6 server.

parent 87d22f85
......@@ -1037,7 +1037,7 @@ Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer,
break;
}
case D6O_IA_PD: {
OptionPtr answer_opt = assignIA_PD(question, ctx,
OptionPtr answer_opt = assignIA_PD(question, answer, ctx,
boost::dynamic_pointer_cast<
Option6IA>(opt->second));
if (answer_opt) {
......@@ -1318,11 +1318,7 @@ Dhcpv6Srv::assignIA_NA(const Pkt6Ptr& query, const Pkt6Ptr& answer,
// the user selects this server to do actual allocation (i.e. sends REQUEST)
// it should include this hint. That will help us during the actual lease
// allocation.
bool fake_allocation = false;
if (query->getType() == DHCPV6_SOLICIT) {
/// @todo: Check if we support rapid commit
fake_allocation = true;
}
bool fake_allocation = (answer->getType() != DHCPV6_REPLY);
// Get DDNS update direction flags
bool do_fwd = false;
......@@ -1433,7 +1429,7 @@ Dhcpv6Srv::conditionalNCRRemoval(Lease6Ptr& old_lease, Lease6Ptr& new_lease,
}
OptionPtr
Dhcpv6Srv::assignIA_PD(const Pkt6Ptr& query,
Dhcpv6Srv::assignIA_PD(const Pkt6Ptr& query, const Pkt6Ptr& answer,
AllocEngine::ClientContext6& orig_ctx,
boost::shared_ptr<Option6IA> ia) {
......@@ -1477,7 +1473,7 @@ Dhcpv6Srv::assignIA_PD(const Pkt6Ptr& query,
// the user selects this server to do actual allocation (i.e. sends REQUEST)
// it should include this hint. That will help us during the actual lease
// allocation.
bool fake_allocation = (query->getType() == DHCPV6_SOLICIT);
bool fake_allocation = (answer->getType() != DHCPV6_REPLY);
// Use allocation engine to pick a lease for this client. Allocation engine
// will try to honour the hint, but it is just a hint - some other address
......@@ -2319,22 +2315,34 @@ Dhcpv6Srv::processSolicit(const Pkt6Ptr& solicit) {
// Let's create a simplified client context here.
AllocEngine::ClientContext6 ctx = createContext(solicit);
Pkt6Ptr advertise(new Pkt6(DHCPV6_ADVERTISE, solicit->getTransid()));
Pkt6Ptr response(new Pkt6(DHCPV6_ADVERTISE, solicit->getTransid()));
copyClientOptions(solicit, advertise);
appendDefaultOptions(solicit, advertise);
appendRequestedOptions(solicit, advertise, ctx);
appendRequestedVendorOptions(solicit, advertise, ctx);
// Handle Rapid Commit option, if prsent.
if (ctx.subnet_ && ctx.subnet_->getRapidCommit()) {
OptionPtr opt_rapid_commit = solicit->getOption(D6O_RAPID_COMMIT);
if (opt_rapid_commit) {
// If Rapid Commit has been sent by the client, change the
// response type to Reply and include Rapid Commit option.
response->setType(DHCPV6_REPLY);
response->addOption(opt_rapid_commit);
}
}
processClientFqdn(solicit, advertise, ctx);
assignLeases(solicit, advertise, ctx);
// Note, that we don't create NameChangeRequests here because we don't
// perform DNS Updates for Solicit. Client must send Request to update
// DNS.
copyClientOptions(solicit, response);
appendDefaultOptions(solicit, response);
appendRequestedOptions(solicit, response, ctx);
appendRequestedVendorOptions(solicit, response, ctx);
generateFqdn(advertise);
processClientFqdn(solicit, response, ctx);
assignLeases(solicit, response, ctx);
// Only generate name change requests if sending a Reply as a result
// of receiving Rapid Commit option.
if (response->getType() == DHCPV6_REPLY) {
createNameChangeRequests(response);
}
return (advertise);
return (response);
}
Pkt6Ptr
......
......@@ -298,10 +298,12 @@ protected:
/// allocation failure.
///
/// @param query client's message (typically SOLICIT or REQUEST)
/// @param answer server's response to the client's message.
/// @param orig_ctx client context (contains subnet, duid and other parameters)
/// @param ia pointer to client's IA_PD option (client's request)
/// @return IA_PD option (server's response)
OptionPtr assignIA_PD(const Pkt6Ptr& query,
const isc::dhcp::Pkt6Ptr& answer,
AllocEngine::ClientContext6& orig_ctx,
boost::shared_ptr<Option6IA> ia);
......
......@@ -45,6 +45,7 @@ const bool Dhcp6SrvD2Test::SHOULD_PASS;
const bool Dhcp6SrvD2Test::SHOULD_FAIL;
Dhcp6SrvD2Test::Dhcp6SrvD2Test() : rcode_(-1) {
reset();
}
Dhcp6SrvD2Test::~Dhcp6SrvD2Test() {
......
......@@ -44,7 +44,9 @@ Dhcp6Client::Dhcp6Client() :
use_relay_(false),
use_oro_(false),
use_client_id_(true),
prefix_hint_() {
use_rapid_commit_(true),
prefix_hint_(),
fqdn_() {
}
Dhcp6Client::Dhcp6Client(boost::shared_ptr<NakedDhcpv6Srv>& srv) :
......@@ -59,7 +61,9 @@ Dhcp6Client::Dhcp6Client(boost::shared_ptr<NakedDhcpv6Srv>& srv) :
use_relay_(false),
use_oro_(false),
use_client_id_(true),
prefix_hint_() {
use_rapid_commit_(true),
prefix_hint_(),
fqdn_() {
}
void
......@@ -195,6 +199,13 @@ Dhcp6Client::applyLease(const LeaseInfo& lease_info) {
config_.leases_.push_back(lease_info);
}
void
Dhcp6Client::appendFQDN() {
if (fqdn_) {
context_.query_->addOption(fqdn_);
}
}
void
Dhcp6Client::copyIAs(const Pkt6Ptr& source, const Pkt6Ptr& dest) {
typedef OptionCollection Opts;
......@@ -292,10 +303,22 @@ Dhcp6Client::doSolicit() {
}
context_.query_->addOption(ia);
}
if (use_rapid_commit_) {
context_.query_->addOption(OptionPtr(new Option(Option::V6,
D6O_RAPID_COMMIT)));
}
// Add Client FQDN if configured.
appendFQDN();
sendMsg(context_.query_);
context_.response_ = receiveOneMsg();
/// @todo Sanity check here
// If using Rapid Commit and the server has responded with Reply,
// let's apply received configuration.
if (use_rapid_commit_ && context_.response_ &&
context_.response_->getType() == DHCPV6_REPLY) {
applyRcvdConfiguration(context_.response_);
}
}
void
......@@ -303,6 +326,10 @@ Dhcp6Client::doRequest() {
Pkt6Ptr query = createMsg(DHCPV6_REQUEST);
query->addOption(context_.response_->getOption(D6O_SERVERID));
copyIAs(context_.response_, query);
// Add Client FQDN if configured.
appendFQDN();
context_.query_ = query;
sendMsg(context_.query_);
context_.response_ = receiveOneMsg();
......@@ -346,6 +373,10 @@ Dhcp6Client::doRenew() {
Pkt6Ptr query = createMsg(DHCPV6_RENEW);
query->addOption(context_.response_->getOption(D6O_SERVERID));
copyIAsFromLeases(query);
// Add Client FQDN if configured.
appendFQDN();
context_.query_ = query;
sendMsg(context_.query_);
context_.response_ = receiveOneMsg();
......@@ -359,6 +390,10 @@ void
Dhcp6Client::doRebind() {
Pkt6Ptr query = createMsg(DHCPV6_REBIND);
copyIAsFromLeases(query);
// Add Client FQDN if configured.
appendFQDN();
context_.query_ = query;
sendMsg(context_.query_);
context_.response_ = receiveOneMsg();
......@@ -510,6 +545,13 @@ Dhcp6Client::useHint(const uint32_t pref_lft, const uint32_t valid_lft,
len, pref_lft, valid_lft));
}
void
Dhcp6Client::useFQDN(const uint8_t flags, const std::string& fqdn_name,
Option6ClientFqdn::DomainNameType fqdn_type) {
fqdn_.reset(new Option6ClientFqdn(flags, fqdn_name, fqdn_type));
}
} // end of namespace isc::dhcp::test
} // end of namespace isc::dhcp
......
......@@ -18,6 +18,7 @@
#include <asiolink/io_address.h>
#include <dhcp/duid.h>
#include <dhcp/option.h>
#include <dhcp/option6_client_fqdn.h>
#include <dhcp6/tests/dhcp6_test_utils.h>
#include <boost/noncopyable.hpp>
#include <boost/shared_ptr.hpp>
......@@ -427,10 +428,28 @@ public:
/// @brief Controls whether the client should send a client-id or not
/// @param send should the client-id be sent?
void useClientId(bool send) {
void useClientId(const bool send) {
use_client_id_ = send;
}
/// @brief Specifies if the Rapid Commit option should be included in
/// the Solicit message.
///
/// @param rapid_commit Boolean parameter controlling if the Rapid Commit
/// option must be included in the Solicit (if true), or not (if false).
void useRapidCommit(const bool rapid_commit) {
use_rapid_commit_ = rapid_commit;
}
/// @brief Creates an instance of the Client FQDN option to be included
/// in the client's message.
///
/// @param flags Flags.
/// @param fqdn_name Name in the textual format.
/// @param fqdn_type Type of the name (fully qualified or partial).
void useFQDN(const uint8_t flags, const std::string& fqdn_name,
Option6ClientFqdn::DomainNameType fqdn_type);
/// @brief Lease configuration obtained by the client.
Configuration config_;
......@@ -492,6 +511,12 @@ private:
/// @param lease_info Structure holding new lease information.
void applyLease(const LeaseInfo& lease_info);
/// @brief Includes CLient FQDN in the client's message.
///
/// This method checks if @c fqdn_ is specified and includes it in
/// the client's message.
void appendFQDN();
/// @brief Copy IA options from one message to another.
///
/// This method copies IA_NA and IA_PD options from one message to another.
......@@ -568,6 +593,7 @@ private:
bool use_oro_; ///< Conth
bool use_client_id_;
bool use_rapid_commit_;
/// @brief Pointer to the option holding a prefix hint.
Option6IAPrefixPtr prefix_hint_;
......@@ -577,6 +603,9 @@ private:
/// Content of this vector will be sent as ORO if use_oro_ is set
/// to true. See @ref sendORO for details.
std::vector<uint16_t> oro_;
/// @brief FQDN requested by the client.
Option6ClientFqdnPtr fqdn_;
};
} // end of namespace isc::dhcp::test
......
......@@ -14,8 +14,11 @@
#include <config.h>
#include <dhcp/tests/iface_mgr_test_config.h>
#include <dhcp/option6_client_fqdn.h>
#include <dhcp6/tests/dhcp6_test_utils.h>
#include <dhcp6/tests/dhcp6_client.h>
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/d2_client_mgr.h>
using namespace isc;
using namespace isc::dhcp;
......@@ -32,6 +35,21 @@ namespace {
/// - the delegated prefix was intentionally selected to not match the
/// subnet prefix, to test that the delegated prefix doesn't need to
/// match the subnet prefix
///
/// - Configuration 1:
/// - one subnet 2001:db8:1::/48 used on eth0 interface
/// - one pool in a range of 2001:db8:1::1 - 2001:db8:1::10
/// - enables Rapid Commit for the subnet and can be used for testing
/// Rapid Commit option support
/// - DNS updates enabled
///
/// - Configuration 2:
/// - one subnet 2001:db8:1::/48 used on eth0 interface
/// - one pool in a range of 2001:db8:1::1 - 2001:db8:1::10
/// - disables Rapid Commit for the subnet and can be used for testing
/// that server ignores Rapid Commit option from the client.
/// - DNS updates enabled
///
const char* CONFIGS[] = {
// Configuration 0
"{ \"interfaces-config\": {"
......@@ -50,11 +68,49 @@ const char* CONFIGS[] = {
" \"interface-id\": \"\","
" \"interface\": \"eth0\""
" } ],"
"\"valid-lifetime\": 4000 }"
"\"valid-lifetime\": 4000 }",
// Configuration 1
"{ \"interfaces-config\": {"
" \"interfaces\": [ \"*\" ]"
"},"
"\"preferred-lifetime\": 3000,"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"subnet6\": [ { "
" \"pools\": [ { \"pool\": \"2001:db8:1::1 - 2001:db8:1::10\" } ],"
" \"subnet\": \"2001:db8:1::/48\", "
" \"interface\": \"eth0\","
" \"rapid-commit\": True"
" } ],"
"\"valid-lifetime\": 4000,"
" \"dhcp-ddns\" : {"
" \"enable-updates\" : True, "
" \"qualifying-suffix\" : \"example.com\" }"
"}",
// Configuration 2
"{ \"interfaces-config\": {"
" \"interfaces\": [ \"*\" ]"
"},"
"\"preferred-lifetime\": 3000,"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"subnet6\": [ { "
" \"pools\": [ { \"pool\": \"2001:db8:1::1 - 2001:db8:1::10\" } ],"
" \"subnet\": \"2001:db8:1::/48\", "
" \"interface\": \"eth0\","
" \"rapid-commit\": False"
" } ],"
"\"valid-lifetime\": 4000,"
" \"dhcp-ddns\" : {"
" \"enable-updates\" : True, "
" \"qualifying-suffix\" : \"example.com\" }"
"}"
};
/// @brief Test fixture class for testing 4-way exchange: Solicit-Advertise,
/// Request-Reply.
/// Request-Reply and 2-way exchange: Solicit-Reply.
class SARRTest : public Dhcpv6SrvTest {
public:
/// @brief Constructor.
......@@ -65,6 +121,14 @@ public:
iface_mgr_test_config_(true) {
}
/// @brief Destructor.
///
/// Clear the DHCP-DDNS configuration.
virtual ~SARRTest() {
D2ClientConfigPtr cfg(new D2ClientConfig());
CfgMgr::instance().setD2ClientConfig(cfg);
}
/// @brief Interface Manager's fake configuration control.
IfaceMgrTestConfig iface_mgr_test_config_;
};
......@@ -127,4 +191,76 @@ TEST_F(SARRTest, directClientPrefixHint) {
ASSERT_TRUE(lease_server);
}
// Check that when the client includes the Rapid Commit option in its
// Solicit, the server responds with Reply and commits the lease.
TEST_F(SARRTest, rapidCommitEnable) {
Dhcp6Client client;
// Configure client to request IA_NA
client.useNA();
configure(CONFIGS[1], *client.getServer());
ASSERT_NO_THROW(client.getServer()->startD2());
// Make sure we ended-up having expected number of subnets configured.
const Subnet6Collection* subnets = CfgMgr::instance().getCurrentCfg()->
getCfgSubnets6()->getAll();
ASSERT_EQ(1, subnets->size());
// Perform 2-way exchange.
client.useRapidCommit(true);
// Include FQDN to trigger generation of name change requests.
ASSERT_NO_THROW(client.useFQDN(Option6ClientFqdn::FLAG_S,
"client-name.example.org",
Option6ClientFqdn::FULL));
ASSERT_NO_THROW(client.doSolicit());
// Server should have committed a lease.
ASSERT_EQ(1, client.getLeaseNum());
Lease6 lease_client = client.getLease(0);
// Make sure that the address belongs to the subnet configured.
ASSERT_TRUE(CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->
selectSubnet(lease_client.addr_, ClientClasses()));
// Make sure that the server responded with Reply.
ASSERT_TRUE(client.getContext().response_);
EXPECT_EQ(DHCPV6_REPLY, client.getContext().response_->getType());
// Rapid Commit option should be included.
EXPECT_TRUE(client.getContext().response_->getOption(D6O_RAPID_COMMIT));
// Check that the lease has been committed.
Lease6Ptr lease_server = checkLease(lease_client);
EXPECT_TRUE(lease_server);
// There should be one name change request generated.
EXPECT_EQ(1, CfgMgr::instance().getD2ClientMgr().getQueueSize());
}
// Check that when the Rapid Commit support is disabled for the subnet
// the server replies with an Advertise and ignores the Rapid Commit
// option sent by the client.
TEST_F(SARRTest, rapidCommitDisable) {
Dhcp6Client client;
// Configure client to request IA_NA
client.useNA();
configure(CONFIGS[2], *client.getServer());
ASSERT_NO_THROW(client.getServer()->startD2());
// Make sure we ended-up having expected number of subnets configured.
const Subnet6Collection* subnets = CfgMgr::instance().getCurrentCfg()->
getCfgSubnets6()->getAll();
ASSERT_EQ(1, subnets->size());
// Send Rapid Commit option to the server.
client.useRapidCommit(true);
// Include FQDN to test that the server will not create name change
// requests when it sends Advertise (Rapid Commit disabled).
ASSERT_NO_THROW(client.useFQDN(Option6ClientFqdn::FLAG_S,
"client-name.example.org",
Option6ClientFqdn::FULL));
ASSERT_NO_THROW(client.doSolicit());
// There should be no lease because the server should have responded
// with Advertise.
ASSERT_EQ(0, client.getLeaseNum());
// Make sure that the server responded.
ASSERT_TRUE(client.getContext().response_);
EXPECT_EQ(DHCPV6_ADVERTISE, client.getContext().response_->getType());
// Make sure that the Rapid Commit option is not included.
EXPECT_FALSE(client.getContext().response_->getOption(D6O_RAPID_COMMIT));
// There should be no name change request generated.
EXPECT_EQ(0, CfgMgr::instance().getD2ClientMgr().getQueueSize());
}
} // end of anonymous namespace
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