Browse Source

[2326] RELEASE support implemented.

Tomek Mrugalski 12 years ago
parent
commit
318530edbf
4 changed files with 246 additions and 5 deletions
  1. 6 1
      ChangeLog
  2. 33 0
      src/bin/dhcp6/dhcp6_messages.mes
  3. 171 1
      src/bin/dhcp6/dhcp6_srv.cc
  4. 36 3
      src/bin/dhcp6/dhcp6_srv.h

+ 6 - 1
ChangeLog

@@ -1,4 +1,9 @@
-526.	[bug]		syephen
+5XX.	[func]		tomek
+	b10-dhcp6: Support for RELEASE message has been added. Clients
+	are now able to release their non-temporary IPv6 addresses.
+	(Trac #2326, git TBD)
+
+526.	[bug]		stephen
 	Miscellaneous fixes to DHCP code including rationalisation of
 	Miscellaneous fixes to DHCP code including rationalisation of
 	some methods in LeaseMgr and resolving some Doxygen/cppcheck
 	some methods in LeaseMgr and resolving some Doxygen/cppcheck
 	issues.
 	issues.

+ 33 - 0
src/bin/dhcp6/dhcp6_messages.mes

@@ -65,6 +65,13 @@ This informational message is printed every time DHCPv6 is started.
 It indicates what database backend type is being to store lease and
 It indicates what database backend type is being to store lease and
 other information.
 other information.
 
 
+% DHCP6_DB_ERROR_LEASE6_WITHOUT_DUID database error: DB returns a lease for address %1 without any DUID
+This error message indicates database consistency failure. Database has an entry
+for a given address and believes it is currently used, but it does not
+contain client-id information. This is most likely a software error.
+Please contact ISC. As a temporary workaround, please manually remove
+lease entry from the database.
+
 % DHCP6_LEASE_ADVERT lease %1 advertised (client duid=%2, iaid=%3)
 % DHCP6_LEASE_ADVERT lease %1 advertised (client duid=%2, iaid=%3)
 This debug message indicates that the server successfully advertised
 This debug message indicates that the server successfully advertised
 a lease. It is up to the client to choose one server out of othe advertised
 a lease. It is up to the client to choose one server out of othe advertised
@@ -86,6 +93,27 @@ This message indicates that the server failed to grant (in response to
 received REQUEST) a lease for a given client. There may be many reasons for
 received REQUEST) a lease for a given client. There may be many reasons for
 such failure. Each specific failure is logged in a separate log entry.
 such failure. Each specific failure is logged in a separate log entry.
 
 
+% DHCP6_RELEASE Address %1 belonging to client duid=%2, iaid=%3 was released properly.
+This debug message indicates that an address was released properly. This
+is a normal operation during client shutdown.
+
+% DHCP6_RELEASE_FAIL Database failed to remove lease for address %1 for duid=%2, iaid=%3
+This error message incidates that database failed to release a lease.
+This indicates a database operation error and likely requires administrator
+intervention (e.g. check if DHCP process has sufficient priviledges to update
+database).
+
+% DHCP6_RELEASE_FAIL_WRONG_DUID Client (duid=%2) tried to release address %2, but it belongs to client (duid=%3)
+This warning message indicates that client tried to release an address that
+does belong to a different client. This should not happen in normal circumstances,
+but since the client is releasing it will stop using released addresses anyway,
+so there is a good chance that the situation will correct itself.
+
+% DHCP6_RELEASE_FAIL_WRONG_IAID Client (duid=%2) tried to release address %2, but it used wrong IAID (expected %4, but got %3)
+This warning message indicates that client tried to release an address that
+does belong to it, but was expected in a different IA container. This likely
+means that the client's support for multiple address is buggy.
+
 % DHCP6_REQUIRED_OPTIONS_CHECK_FAIL %1 message received from %2 failed the following check: %3
 % 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
 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,
 to a number of reasons, e.g. the mandatory client-id option is missing,
@@ -216,3 +244,8 @@ recently and does not recognize its well-behaving clients. This is more
 probable if you see many such messages. Clients will recover from this,
 probable if you see many such messages. Clients will recover from this,
 but they will most likely get a different IP addresses and experience
 but they will most likely get a different IP addresses and experience
 a brief service interruption.
 a brief service interruption.
+
+% DHCP6_UNKNOWN_RELEASE received RELEASE from unknown client (duid=%1, iaid=%2)
+This warning message is printed when client attempts to release a lease,
+but no such lease is known by the server. See DHCP6_UNKNOWN_RENEW for
+possible reasons for such behavior.

+ 171 - 1
src/bin/dhcp6/dhcp6_srv.cc

@@ -436,6 +436,8 @@ void Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer) {
 
 
     // We need to allocate addresses for all IA_NA options in the client's
     // We need to allocate addresses for all IA_NA options in the client's
     // question (i.e. SOLICIT or REQUEST) message.
     // question (i.e. SOLICIT or REQUEST) message.
+    // @todo add support for IA_TA
+    // @todo add support for IA_PD
 
 
     // We need to select a subnet the client is connected in.
     // We need to select a subnet the client is connected in.
     Subnet6Ptr subnet = selectSubnet(question);
     Subnet6Ptr subnet = selectSubnet(question);
@@ -640,6 +642,8 @@ void Dhcpv6Srv::renewLeases(const Pkt6Ptr& renew, Pkt6Ptr& reply) {
 
 
     // We need to renew addresses for all IA_NA options in the client's
     // We need to renew addresses for all IA_NA options in the client's
     // RENEW message.
     // RENEW message.
+    // @todo add support for IA_TA
+    // @todo add support for IA_PD
 
 
     // We need to select a subnet the client is connected in.
     // We need to select a subnet the client is connected in.
     Subnet6Ptr subnet = selectSubnet(renew);
     Subnet6Ptr subnet = selectSubnet(renew);
@@ -688,11 +692,169 @@ void Dhcpv6Srv::renewLeases(const Pkt6Ptr& renew, Pkt6Ptr& reply) {
             break;
             break;
         }
         }
     }
     }
+}
+
+void Dhcpv6Srv::releaseLeases(const Pkt6Ptr& release, Pkt6Ptr& reply) {
 
 
+    // We need to release addresses for all IA_NA options in the client's
+    // RELEASE message.
+    // @todo Add support for IA_TA
+    // @todo Add support for IA_PD
+    // @todo Consider supporting more than one address in a single IA_NA.
+    // That was envisaged by RFC3315, but it never happened. The only
+    // software that supports that is Dibbler, but its author seriously doubts
+    // if anyone is really using it. Clients that want more than one address
+    // just include more instances of IA_NA options.
 
 
+    // 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 = release->getOption(D6O_CLIENTID);
+    if (!opt_duid) {
+        // This should not happen. We have checked this before.
+        reply->addOption(createStatusCode(STATUS_UnspecFail,
+                         "You did not include mandatory client-id"));
+        return;
+    }
+    DuidPtr duid(new DUID(opt_duid->getData()));
+
+    int general_status = STATUS_Success;
+    for (Option::OptionCollection::iterator opt = release->options_.begin();
+         opt != release->options_.end(); ++opt) {
+        switch (opt->second->getType()) {
+        case D6O_IA_NA: {
+            OptionPtr answer_opt = releaseIA_NA(duid, release, general_status,
+                                   boost::dynamic_pointer_cast<Option6IA>(opt->second));
+            if (answer_opt) {
+                reply->addOption(answer_opt);
+            }
+            break;
+        }
+        default:
+            break;
+        }
+    }
 
 
+    // To be pedantic, we should also include status code in the top-level
+    // scope, not just in each IA_NA. See RFC3315, section 18.2.6.
+    // This behavior will likely go away in RFC3315bis.
+    reply->addOption(createStatusCode(general_status,
+                     "Summary status for all processed IA_NAs"));
 }
 }
 
 
+OptionPtr Dhcpv6Srv::releaseIA_NA(const DuidPtr& duid, Pkt6Ptr question,
+                                  int& general_status,
+                                  boost::shared_ptr<Option6IA> ia) {
+    // Release can be done in one of two ways:
+    // Approach 1: extract address from client's IA_NA and see if it belongs
+    // to this particular client.
+    // Approach 2: find a subnet for this client, get a lease for
+    // this subnet/duid/iaid and check if its content matches to what the
+    // client is asking us to release.
+    //
+    // This method implements approach 1.
+
+    // That's our response
+    boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
+
+    boost::shared_ptr<Option6IAAddr> release_addr = boost::dynamic_pointer_cast<Option6IAAddr>
+        (ia->getOption(D6O_IAADDR));
+    if (!release_addr) {
+        ia_rsp->addOption(createStatusCode(STATUS_NoBinding,
+                                           "You did not include address in your RELEASE"));
+        general_status = STATUS_NoBinding;
+        return (ia_rsp);
+    }
+
+    Lease6Ptr lease = LeaseMgrFactory::instance().getLease6(release_addr->getAddress());
+
+    if (!lease) {
+        // client releasing a lease that we don't know about.
+
+        // Insert status code NoAddrsAvail.
+        ia_rsp->addOption(createStatusCode(STATUS_NoBinding,
+                          "Sorry, no known leases for this duid/iaid, can't release."));
+        general_status = STATUS_NoBinding;
+
+        LOG_WARN(dhcp6_logger, DHCP6_UNKNOWN_RELEASE)
+            .arg(duid->toText())
+            .arg(ia->getIAID());
+
+        return (ia_rsp);
+    }
+
+    if (!lease->duid_) {
+        // Something is gravely wrong here. We do have a lease, but it does not
+        // have mandatory DUID information attached. Someone was messing with our
+        // database.
+
+        LOG_ERROR(dhcp6_logger, DHCP6_DB_ERROR_LEASE6_WITHOUT_DUID)
+            .arg(release_addr->getAddress().toText());
+
+        general_status = STATUS_UnspecFail;
+        ia_rsp->addOption(createStatusCode(STATUS_UnspecFail,
+                          "Database consistency check failed when trying to RELEASE"));
+        return (ia_rsp);
+    }
+
+    if (*duid != *(lease->duid_)) {
+        // Sorry, it's not your address. You can't release it.
+
+        LOG_WARN(dhcp6_logger, DHCP6_RELEASE_FAIL_WRONG_DUID)
+            .arg(release_addr->getAddress().toText())
+            .arg(duid->toText())
+            .arg(lease->duid_->toText());
+
+        general_status = STATUS_NoBinding;
+        ia_rsp->addOption(createStatusCode(STATUS_NoBinding,
+                          "This address does not belong to you, you can't release it"));
+        return (ia_rsp);
+    }
+
+    if (ia->getIAID() != lease->iaid_) {
+        // This address belongs to this client, but to a different IA
+        LOG_WARN(dhcp6_logger, DHCP6_RELEASE_FAIL_WRONG_IAID)
+            .arg(release_addr->getAddress().toText())
+            .arg(duid->toText())
+            .arg(ia->getIAID())
+            .arg(lease->iaid_);
+        ia_rsp->addOption(createStatusCode(STATUS_NoBinding,
+                          "This is your address, but you used wrong IAID"));
+        general_status = STATUS_NoBinding;
+        return (ia_rsp);
+    }
+
+    // It is not necessary if the address matches as we used getLease6(addr)
+    // method that is supposed to return a proper lease.
+
+    // Ok, we've passed all checks. Let's release this address.
+
+    if (!LeaseMgrFactory::instance().deleteLease(lease->addr_)) {
+        ia_rsp->addOption(createStatusCode(STATUS_UnspecFail,
+                          "Server failed to release a lease"));
+
+        LOG_ERROR(dhcp6_logger, DHCP6_RELEASE_FAIL)
+            .arg(lease->addr_.toText())
+            .arg(duid->toText())
+            .arg(lease->iaid_);
+        general_status = STATUS_UnspecFail;
+
+        return (ia_rsp);
+    } else {
+        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_RELEASE)
+            .arg(lease->addr_.toText())
+            .arg(duid->toText())
+            .arg(lease->iaid_);
+
+        ia_rsp->addOption(createStatusCode(STATUS_Success,
+                          "Lease released. Thank you, please come again."));
+
+        return (ia_rsp);
+    }
+}
+
+
 Pkt6Ptr Dhcpv6Srv::processSolicit(const Pkt6Ptr& solicit) {
 Pkt6Ptr Dhcpv6Srv::processSolicit(const Pkt6Ptr& solicit) {
 
 
     sanityCheck(solicit, MANDATORY, FORBIDDEN);
     sanityCheck(solicit, MANDATORY, FORBIDDEN);
@@ -751,8 +913,16 @@ Pkt6Ptr Dhcpv6Srv::processConfirm(const Pkt6Ptr& confirm) {
 }
 }
 
 
 Pkt6Ptr Dhcpv6Srv::processRelease(const Pkt6Ptr& release) {
 Pkt6Ptr Dhcpv6Srv::processRelease(const Pkt6Ptr& release) {
-    /// @todo: Implement this
+
+    sanityCheck(release, MANDATORY, MANDATORY);
+
     Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, release->getTransid()));
     Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, release->getTransid()));
+
+    copyDefaultOptions(release, reply);
+    appendDefaultOptions(release, reply);
+
+    releaseLeases(release, reply);
+
     return reply;
     return reply;
 }
 }
 
 

+ 36 - 3
src/bin/dhcp6/dhcp6_srv.h

@@ -212,17 +212,39 @@ protected:
 
 
     /// @brief Renews specific IA_NA option
     /// @brief Renews specific IA_NA option
     ///
     ///
-    /// Generates response to IA_NA. 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. 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 subnet subnet the sender belongs to
     /// @param duid client's duid
     /// @param duid client's duid
     /// @param question client's message
     /// @param question client's message
     /// @param ia IA_NA option that is being renewed
     /// @param ia IA_NA option that is being renewed
+    /// @return IA_NA option (server's response)
     OptionPtr renewIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
     OptionPtr renewIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
                          Pkt6Ptr question, boost::shared_ptr<Option6IA> ia);
                          Pkt6Ptr question, boost::shared_ptr<Option6IA> ia);
 
 
+    /// @brief Releases specific IA_NA option
+    ///
+    /// Generates response to IA_NA in Release message. This covers finding and
+    /// removal of 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.
+    ///
+    /// As RFC3315 requires to also send global (one for the whole message),
+    /// this method may update passed general_status. It is set to SUCCESS
+    /// when message processing begins, but may be update to some error code
+    /// if release process fails.
+    ///
+    /// @param duid client's duid
+    /// @param question client's message
+    /// @param general_status a global status (it may be updated in case of errors)
+    /// @param ia IA_NA option that is being renewed
+    /// @return IA_NA option (server's response)
+    OptionPtr releaseIA_NA(const DuidPtr& duid, Pkt6Ptr question,
+                           int& general_status,
+                           boost::shared_ptr<Option6IA> ia);
+
     /// @brief Copies required options from client message to server answer.
     /// @brief Copies required options from client message to server answer.
     ///
     ///
     /// Copies options that must appear in any server response (ADVERTISE, REPLY)
     /// Copies options that must appear in any server response (ADVERTISE, REPLY)
@@ -271,6 +293,17 @@ protected:
     /// @param reply server's response
     /// @param reply server's response
     void renewLeases(const Pkt6Ptr& renew, Pkt6Ptr& reply);
     void renewLeases(const Pkt6Ptr& renew, Pkt6Ptr& reply);
 
 
+    /// @brief Attempts to release received addresses
+    ///
+    /// It iterates through received IA_NA options and attempts to release
+    /// received addresses. If no such leases are found, or the lease fails
+    /// proper checks (e.g. belongs to someone else), a proper status
+    /// code is added to reply message. Released addresses are not added
+    /// to REPLY packet, just its IA_NA containers.
+    /// @param release client's message asking to release
+    /// @param reply server's response
+    void releaseLeases(const Pkt6Ptr& release, Pkt6Ptr& reply);
+
     /// @brief Sets server-identifier.
     /// @brief Sets server-identifier.
     ///
     ///
     /// This method attempts to set server-identifier DUID. It loads it
     /// This method attempts to set server-identifier DUID. It loads it