Browse Source

[3232] Refactored the srv code to have common bits for IA_NA Renew/Rebind

Marcin Siodelski 11 years ago
parent
commit
fb010f7bc9
3 changed files with 131 additions and 92 deletions
  1. 48 31
      src/bin/dhcp6/dhcp6_messages.mes
  2. 63 45
      src/bin/dhcp6/dhcp6_srv.cc
  3. 20 16
      src/bin/dhcp6/dhcp6_srv.h

+ 48 - 31
src/bin/dhcp6/dhcp6_messages.mes

@@ -110,6 +110,48 @@ that the DNS Update has been performed for it, but the FQDN held in the lease
 database has invalid format and can't be transformed to the canonical on-wire
 format.
 
+% DHCP6_EXTEND_LEASE_SUBNET_SELECTED the %1 subnet was selected for client extending its lease
+This is a debug message informing that a given subnet was selected. It will
+be used for extending lifetime of the lease. This is one of the early steps
+in the processing of incoming client message.
+
+% DHCP6_EXTEND_LEASE_SUBNET_SELECT_FAILED failed to select a subnet for received %1: src=%2 type=%3
+This warning message is output when a Renew or Rebind was received from a
+subnet for which the DHCPv6 server has not been configured. The cause is
+most likely due to a misconfiguration of the server. The packet processing
+will continue, but the response will only contain generic configuration
+parameters and no addresses or prefixes.
+
+% DHCP6_EXTEND_NA_UNKNOWN received unknown IA_NA from client (duid=%1, iaid=%2) in subnet %3
+This warning message is printed when client attempts to extend the lease
+for the address (in the 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 client's date/time
+setting or poor support on the client 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.
+
+% DHCP6_EXTEND_NA_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 address, but the server does not have any information about the subnet this
+client belongs to. This may mean that faulty the mobile client changed its location
+and is trying to renew its old address (client is supposed to send confirm, not rewew
+in such cases, according to RFC3315) or the server configuration has changed and
+information about existing subnet was removed. Note that in a sense this is worse
+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_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
+client belongs to.
+
 % DHCP6_HOOK_BUFFER_RCVD_SKIP received DHCPv6 buffer was dropped because a callout set the skip flag
 This debug message is printed when a callout installed on buffer6_receive
 hook point set the skip flag. For this particular hook point, the
@@ -122,13 +164,13 @@ setting of the flag by a callout instructs the server to drop the packet.
 Server completed all the processing (e.g. may have assigned, updated
 or released leases), but the response will not be send to the client.
 
-% DHCP6_HOOK_LEASE6_RENEW_SKIP DHCPv6 lease was not renewed because a callout set the skip flag
+% DHCP6_HOOK_LEASE6_EXTEND_SKIP DHCPv6 lease lifetime was not extended because a callout set the skip flag for message %1
 This debug message is printed when a callout installed on lease6_renew
-hook point set the skip flag. For this particular hook point, the setting
-of the flag by a callout instructs the server to not renew a lease. If
-client requested renewal of multiples leases (by sending multiple IA
-options), the server will skip the renewal of the one in question and
-will proceed with other renewals as usual.
+or lease6_rebind hook point set the skip flag. For this particular hook
+point, the setting of the flag by a callout instructs the server to not
+extend the lifetime for a lease. If client requested renewal of multiples
+leases (by sending multiple IA options), the server will skip the renewal
+of the one in question and will proceed with other renewals as usual.
 
 % DHCP6_HOOK_LEASE6_RELEASE_NA_SKIP DHCPv6 address lease was not released because a callout set the skip flag
 This debug message is printed when a callout installed on the
@@ -396,16 +438,6 @@ mandatory client-id option. This is most likely caused by a buggy client
 (or a relay that malformed forwarded message). This request will not be
 processed and a response with error status code will be sent back.
 
-% DHCP6_RENEW_UNKNOWN_SUBNET RENEW message received from client on unknown subnet (duid=%1, iaid=%2)
-A warning message indicating that a client is attempting to renew his lease,
-but the server does not have any information about the subnet this client belongs
-to. This may mean that faulty the mobile client changed its location and is trying to
-renew its old address (client is supposed to send confirm, not rewew in such cases,
-according to RFC3315) or the server configuration has changed and information about
-existing subnet was removed. Note that in a sense this is worse case of DHCP6_UNKNOWN_RENEW,
-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_REQUIRED_OPTIONS_CHECK_FAIL %1 message received from %2 failed the following check: %3
 This message indicates that received DHCPv6 packet is invalid.  This may be due
 to a number of reasons, e.g. the mandatory client-id option is missing,
@@ -515,21 +547,6 @@ 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_NA 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 the 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 client's date/time
-setting or poor support on the client 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.
-
 % 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

+ 63 - 45
src/bin/dhcp6/dhcp6_srv.cc

@@ -71,6 +71,7 @@ struct Dhcp6Hooks {
     int hook_index_pkt6_receive_;   ///< index for "pkt6_receive" hook point
     int hook_index_subnet6_select_; ///< index for "subnet6_select" hook point
     int hook_index_lease6_renew_;   ///< index for "lease6_renew" hook point
+    int hook_index_lease6_rebind_;  ///< index for "lease6_rebind" hook point
     int hook_index_lease6_release_; ///< index for "lease6_release" hook point
     int hook_index_pkt6_send_;      ///< index for "pkt6_send" hook point
     int hook_index_buffer6_send_;   ///< index for "buffer6_send" hook point
@@ -81,6 +82,7 @@ struct Dhcp6Hooks {
         hook_index_pkt6_receive_   = HooksManager::registerHook("pkt6_receive");
         hook_index_subnet6_select_ = HooksManager::registerHook("subnet6_select");
         hook_index_lease6_renew_   = HooksManager::registerHook("lease6_renew");
+        hook_index_lease6_rebind_   = HooksManager::registerHook("lease6_rebind");
         hook_index_lease6_release_ = HooksManager::registerHook("lease6_release");
         hook_index_pkt6_send_      = HooksManager::registerHook("pkt6_send");
         hook_index_buffer6_send_   = HooksManager::registerHook("buffer6_send");
@@ -1499,18 +1501,26 @@ Dhcpv6Srv::assignIA_PD(const Subnet6Ptr& subnet, const DuidPtr& duid,
 }
 
 OptionPtr
-Dhcpv6Srv::renewIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
-                      const Pkt6Ptr& query, const Pkt6Ptr& answer,
-                      boost::shared_ptr<Option6IA> ia) {
+Dhcpv6Srv::extendIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
+                       const Pkt6Ptr& query, const Pkt6Ptr& answer,
+                       boost::shared_ptr<Option6IA> ia) {
     if (!subnet) {
-        // There's no subnet select for this client. There's nothing to renew.
-        boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
-
-        // Insert status code NoAddrsAvail.
+        /// @todo For simpliclty and due to limitations of LeaseMgr we don't
+        /// get the binding for the client for which we don't get subnet id.
+        /// Subnet id is a required value when searching for the bindings.
+        /// The fact that we can't identify the subnet for the returning client
+        /// doesn't really mean that the client has no binding. It is possible
+        /// that due to server's reconfiguration the subnet has been removed
+        /// or modified since the client has got his lease. However, we think
+        /// it is fine to send NoBinding status code because this code is
+        /// supposed to be sent when server doesn't find the binding for
+        /// the client (which is the case here).
+        Option6IAPtr ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
+        // 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_RENEW_UNKNOWN_SUBNET)
+        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_EXTEND_NA_UNKNOWN_SUBNET)
+            .arg(query->getName())
             .arg(duid->toText())
             .arg(ia->getIAID());
 
@@ -1521,17 +1531,17 @@ Dhcpv6Srv::renewIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
                                                             *duid, ia->getIAID(),
                                                             subnet->getID());
 
+    // Client extending a lease that we don't know about.
     if (!lease) {
-        // client renewing a lease that we don't know about.
-
         // Create empty IA_NA option with IAID matching the request.
         boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
 
-        // Insert status code NoAddrsAvail.
+        // Insert status code NoBinding to indicate that the lease does not
+        // exist for this client.
         ia_rsp->addOption(createStatusCode(STATUS_NoBinding,
-                          "Sorry, no known leases for this duid/iaid."));
+                          "Sorry, no known leases for this duid/iaid/subnet."));
 
-        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_UNKNOWN_RENEW_NA)
+        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_EXTEND_NA_UNKNOWN)
             .arg(duid->toText())
             .arg(ia->getIAID())
             .arg(subnet->toText());
@@ -1539,7 +1549,7 @@ Dhcpv6Srv::renewIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
         return (ia_rsp);
     }
 
-    // Keep the old data in case the callout tells us to skip update
+    // Keep the old data in case the callout tells us to skip update.
     Lease6 old_data = *lease;
 
     // At this point, we have to make make some decisions with respect to the
@@ -1601,8 +1611,10 @@ Dhcpv6Srv::renewIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
     ia_rsp->addOption(addr);
 
     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
@@ -1618,14 +1630,16 @@ Dhcpv6Srv::renewIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
         callout_handle->setArgument("ia_na", ia_rsp);
 
         // Call all installed callouts
-        HooksManager::callCallouts(Hooks.hook_index_lease6_renew_, *callout_handle);
+        HooksManager::callCallouts(hook_point, *callout_handle);
 
         // 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".
         if (callout_handle->getSkip()) {
             skip = true;
-            LOG_DEBUG(dhcp6_logger, DBG_DHCP6_HOOKS, DHCP6_HOOK_LEASE6_RENEW_SKIP);
+            LOG_DEBUG(dhcp6_logger, DBG_DHCP6_HOOKS,
+                      DHCP6_HOOK_LEASE6_EXTEND_SKIP)
+                .arg(query->getName());
         }
     }
 
@@ -1654,7 +1668,7 @@ Dhcpv6Srv::renewIA_PD(const Subnet6Ptr& subnet, const DuidPtr& duid,
         ia_rsp->addOption(createStatusCode(STATUS_NoBinding,
                           "Sorry, no known leases for this duid/iaid."));
 
-        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_RENEW_UNKNOWN_SUBNET)
+        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_EXTEND_PD_UNKNOWN_SUBNET)
             .arg(duid->toText())
             .arg(ia->getIAID());
 
@@ -1731,7 +1745,7 @@ Dhcpv6Srv::renewIA_PD(const Subnet6Ptr& subnet, const DuidPtr& duid,
         // 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_RENEW_SKIP);
+        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_HOOKS, DHCP6_HOOK_LEASE6_EXTEND_SKIP);
 
         // 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
@@ -1744,15 +1758,16 @@ Dhcpv6Srv::renewIA_PD(const Subnet6Ptr& subnet, const DuidPtr& duid,
 }
 
 void
-Dhcpv6Srv::renewLeases(const Pkt6Ptr& renew, Pkt6Ptr& reply) {
+Dhcpv6Srv::extendLeases(const Pkt6Ptr& query, Pkt6Ptr& reply) {
 
-    // We need to renew addresses for all IA_NA options in the client's
-    // RENEW message.
-    // @todo add support for IA_TA
-    // @todo add support for IA_PD
+    // We will try to extend lease lifetime for all IA options in the client's
+    // Renew or Rebind message.
+    /// @todo add support for IA_TA
 
-    // We need to select a subnet the client is connected in.
-    Subnet6Ptr subnet = selectSubnet(renew);
+    // We need to select a subnet the client is connected in. This is needed
+    // to get the client's bindings from the lease database. The subnet id
+    // is one of the lease search parameters.
+    Subnet6Ptr subnet = selectSubnet(query);
     if (!subnet) {
         // This particular client is out of luck today. We do not have
         // information about the subnet he is connected to. This likely means
@@ -1761,20 +1776,24 @@ Dhcpv6Srv::renewLeases(const Pkt6Ptr& renew, Pkt6Ptr& reply) {
         // addresses or prefixes, no subnet specific configuration etc. The only
         // thing this client can get is some global information (like DNS
         // servers).
-
-        LOG_WARN(dhcp6_logger, DHCP6_SUBNET_SELECTION_FAILED)
-            .arg(renew->getRemoteAddr().toText())
-            .arg(renew->getName());
+        LOG_WARN(dhcp6_logger, DHCP6_EXTEND_LEASE_SUBNET_SELECT_FAILED)
+            .arg(query->getName())
+            .arg(query->getRemoteAddr().toText())
+            .arg(query->getName());
     } else {
-        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL_DATA, DHCP6_SUBNET_SELECTED)
+        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL_DATA,
+                  DHCP6_EXTEND_LEASE_SUBNET_SELECTED)
             .arg(subnet->toText());
     }
 
     // Let's find client's DUID. Client is supposed to include its client-id
-    // option almost all the time (the only exception is an anonymous inf-request,
-    // but that is mostly a theoretical case). Our allocation engine needs DUID
-    // and will refuse to allocate anything to anonymous clients.
-    OptionPtr opt_duid = renew->getOption(D6O_CLIENTID);
+    // option almost all the time (the only exception is an anonymous
+    // inf-request, but that is mostly a theoretical case). Our allocation
+    // engine needs DUID and will refuse to allocate anything to anonymous
+    // clients.
+    /// @todo Consider removing this check from here and rely on what we have
+    /// checked on the earlier processing stage.
+    OptionPtr opt_duid = query->getOption(D6O_CLIENTID);
     if (!opt_duid) {
         // This should not happen. We have checked this before.
         reply->addOption(createStatusCode(STATUS_UnspecFail,
@@ -1783,14 +1802,13 @@ Dhcpv6Srv::renewLeases(const Pkt6Ptr& renew, Pkt6Ptr& reply) {
     }
     DuidPtr duid(new DUID(opt_duid->getData()));
 
-    for (OptionCollection::iterator opt = renew->options_.begin();
-         opt != renew->options_.end(); ++opt) {
+    for (OptionCollection::iterator opt = query->options_.begin();
+         opt != query->options_.end(); ++opt) {
         switch (opt->second->getType()) {
-
         case D6O_IA_NA: {
-            OptionPtr answer_opt = renewIA_NA(subnet, duid, renew, reply,
-                                              boost::dynamic_pointer_cast<
-                                              Option6IA>(opt->second));
+            OptionPtr answer_opt = extendIA_NA(subnet, duid, query, reply,
+                                               boost::dynamic_pointer_cast<
+                                                   Option6IA>(opt->second));
             if (answer_opt) {
                 reply->addOption(answer_opt);
             }
@@ -1798,7 +1816,7 @@ Dhcpv6Srv::renewLeases(const Pkt6Ptr& renew, Pkt6Ptr& reply) {
         }
 
         case D6O_IA_PD: {
-            OptionPtr answer_opt = renewIA_PD(subnet, duid, renew,
+            OptionPtr answer_opt = renewIA_PD(subnet, duid, query,
                                               boost::dynamic_pointer_cast<
                                                   Option6IA>(opt->second));
             if (answer_opt) {
@@ -2230,7 +2248,7 @@ Dhcpv6Srv::processRenew(const Pkt6Ptr& renew) {
     appendRequestedOptions(renew, reply);
 
     processClientFqdn(renew, reply);
-    renewLeases(renew, reply);
+    extendLeases(renew, reply);
     generateFqdn(reply);
     createNameChangeRequests(reply);
 

+ 20 - 16
src/bin/dhcp6/dhcp6_srv.h

@@ -253,23 +253,25 @@ protected:
                           const Pkt6Ptr& query,
                           boost::shared_ptr<Option6IA> ia);
 
-    /// @brief Renews specific IA_NA option
+    /// @brief Extends lifetime of the specific IA_NA option.
     ///
-    /// Generates response to IA_NA in Renew. This typically includes finding a
-    /// lease that corresponds to the received address. If no such lease is
-    /// found, an IA_NA response is generated with an appropriate status code.
+    /// Generates response to IA_NA in Renew or Rebind. This typically includes
+    /// finding a lease that corresponds to the received address. If no such
+    /// lease is found, an IA_NA response is generated with an appropriate
+    /// status code.
     ///
     /// @param subnet subnet the sender belongs to
     /// @param duid client's duid
-    /// @param query client's message
+    /// @param query client's message (Renew or Rebind)
     /// @param answer server's response to the client's message. This
     /// message should contain Client FQDN option being sent by the server
     /// to the client (if the client sent this option to the server).
-    /// @param ia IA_NA option that is being renewed
+    /// @param ia IA_NA option which carries adress for which lease lifetime
+    /// will be extended.
     /// @return IA_NA option (server's response)
-    OptionPtr renewIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
-                         const Pkt6Ptr& query, const Pkt6Ptr& answer,
-                         boost::shared_ptr<Option6IA> ia);
+    OptionPtr extendIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
+                          const Pkt6Ptr& query, const Pkt6Ptr& answer,
+                          boost::shared_ptr<Option6IA> ia);
 
     /// @brief Renews specific IA_PD option
     ///
@@ -438,15 +440,17 @@ protected:
     /// NameChangeSender will be used to deliver requests to the other module.
     void sendNameChangeRequests();
 
-    /// @brief Attempts to renew received addresses
+    /// @brief Attempts to extend the lifetime of IAs.
+    ///
+    /// This function is called when a client sends Renew or Rebind message.
+    /// It iterates through received IA options and attempts to extend
+    /// corresponding lease lifetimes. Internally, it calls
+    /// @c Dhcpv6Srv::extendIA_NA and @c Dhcpv6Srv::extendIA_PD to extend
+    /// the lifetime of IA_NA and IA_PD leases accordingly.
     ///
-    /// It iterates through received IA_NA options and attempts to renew
-    /// received addresses. If no such leases are found, proper status
-    /// code is added to reply message. Renewed addresses are added
-    /// as IA_NA/IAADDR to reply packet.
-    /// @param renew client's message asking for renew
+    /// @param query client's Renew or Rebind message
     /// @param reply server's response
-    void renewLeases(const Pkt6Ptr& renew, Pkt6Ptr& reply);
+    void extendLeases(const Pkt6Ptr& query, Pkt6Ptr& reply);
 
     /// @brief Attempts to release received addresses
     ///