From: Tomek Mrugalski Date: Fri, 14 Dec 2012 16:00:24 +0000 (+0100) Subject: [2326] RELEASE support implemented. X-Git-Tag: bind10-1.0.0-rc-release~95^2~22^2~12^2~8 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=318530edbf905ff288f527ef73bb88da4c57b548;p=thirdparty%2Fkea.git [2326] RELEASE support implemented. --- diff --git a/ChangeLog b/ChangeLog index eb15baf422..30cf7460f4 100644 --- a/ChangeLog +++ b/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 some methods in LeaseMgr and resolving some Doxygen/cppcheck issues. diff --git a/src/bin/dhcp6/dhcp6_messages.mes b/src/bin/dhcp6/dhcp6_messages.mes index 6ab42b3c96..d8a1b0a722 100644 --- a/src/bin/dhcp6/dhcp6_messages.mes +++ b/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 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) 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 @@ -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 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 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, @@ -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, but they will most likely get a different IP addresses and experience 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. diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc index 2482833cc3..2e1ab9c565 100644 --- a/src/bin/dhcp6/dhcp6_srv.cc +++ b/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 // 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. 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 // 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. Subnet6Ptr subnet = selectSubnet(renew); @@ -688,11 +692,169 @@ void Dhcpv6Srv::renewLeases(const Pkt6Ptr& renew, Pkt6Ptr& reply) { 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(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 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 ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID())); + + boost::shared_ptr release_addr = boost::dynamic_pointer_cast + (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) { sanityCheck(solicit, MANDATORY, FORBIDDEN); @@ -751,8 +913,16 @@ Pkt6Ptr Dhcpv6Srv::processConfirm(const Pkt6Ptr& confirm) { } Pkt6Ptr Dhcpv6Srv::processRelease(const Pkt6Ptr& release) { - /// @todo: Implement this + + sanityCheck(release, MANDATORY, MANDATORY); + Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, release->getTransid())); + + copyDefaultOptions(release, reply); + appendDefaultOptions(release, reply); + + releaseLeases(release, reply); + return reply; } diff --git a/src/bin/dhcp6/dhcp6_srv.h b/src/bin/dhcp6/dhcp6_srv.h index b6b0306e0e..b3f8c4c501 100644 --- a/src/bin/dhcp6/dhcp6_srv.h +++ b/src/bin/dhcp6/dhcp6_srv.h @@ -212,17 +212,39 @@ protected: /// @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 duid client's duid /// @param question client's message /// @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, Pkt6Ptr question, boost::shared_ptr 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 ia); + /// @brief Copies required options from client message to server answer. /// /// Copies options that must appear in any server response (ADVERTISE, REPLY) @@ -271,6 +293,17 @@ protected: /// @param reply server's response 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. /// /// This method attempts to set server-identifier DUID. It loads it