Parcourir la source

[3070] Implemented Rapid Commit support for DHCPv6 server.

Marcin Siodelski il y a 10 ans
Parent
commit
e98d7592b2

+ 28 - 20
src/bin/dhcp6/dhcp6_srv.cc

@@ -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

+ 2 - 0
src/bin/dhcp6/dhcp6_srv.h

@@ -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);
 

+ 1 - 0
src/bin/dhcp6/tests/d2_unittest.cc

@@ -45,6 +45,7 @@ const bool Dhcp6SrvD2Test::SHOULD_PASS;
 const bool Dhcp6SrvD2Test::SHOULD_FAIL;
 
 Dhcp6SrvD2Test::Dhcp6SrvD2Test() : rcode_(-1) {
+    reset();
 }
 
 Dhcp6SrvD2Test::~Dhcp6SrvD2Test() {

+ 45 - 3
src/bin/dhcp6/tests/dhcp6_client.cc

@@ -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
@@ -196,6 +200,13 @@ Dhcp6Client::applyLease(const LeaseInfo& lease_info) {
 }
 
 void
+Dhcp6Client::appendFQDN() {
+    if (fqdn_) {
+        context_.query_->addOption(fqdn_);
+    }
+}
+
+void
 Dhcp6Client::copyIAs(const Pkt6Ptr& source, const Pkt6Ptr& dest) {
     typedef OptionCollection Opts;
     // Copy IA_NAs.
@@ -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

+ 30 - 1
src/bin/dhcp6/tests/dhcp6_client.h

@@ -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

+ 138 - 2
src/bin/dhcp6/tests/sarr_unittest.cc

@@ -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