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) {
// 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;
}
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) {
// 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
// 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
/// 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);
const bool Dhcp6SrvD2Test::SHOULD_FAIL;
Dhcp6SrvD2Test::Dhcp6SrvD2Test() : rcode_(-1) {
+ reset();
}
Dhcp6SrvD2Test::~Dhcp6SrvD2Test() {
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) :
use_relay_(false),
use_oro_(false),
use_client_id_(true),
- prefix_hint_() {
+ use_rapid_commit_(true),
+ prefix_hint_(),
+ fqdn_() {
}
void
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;
}
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
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();
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();
Dhcp6Client::doRebind() {
Pkt6Ptr query = createMsg(DHCPV6_REBIND);
copyIAsFromLeases(query);
+
+ // Add Client FQDN if configured.
+ appendFQDN();
+
context_.query_ = query;
sendMsg(context_.query_);
context_.response_ = receiveOneMsg();
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
#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>
/// @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_;
/// @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.
bool use_oro_; ///< Conth
bool use_client_id_;
+ bool use_rapid_commit_;
/// @brief Pointer to the option holding a prefix hint.
Option6IAPrefixPtr prefix_hint_;
/// 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
#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;
/// - 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\": {"
" \"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.
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_;
};
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