Browse Source

[2414] Allocation Engine is now used in dhcp6_srv.

Tomek Mrugalski 12 years ago
parent
commit
f63a6cabd8
4 changed files with 175 additions and 44 deletions
  1. 3 3
      src/bin/dhcp6/dhcp6_messages.mes
  2. 128 41
      src/bin/dhcp6/dhcp6_srv.cc
  3. 40 0
      src/bin/dhcp6/dhcp6_srv.h
  4. 4 0
      src/lib/dhcp/Makefile.am

+ 3 - 3
src/bin/dhcp6/dhcp6_messages.mes

@@ -50,7 +50,7 @@ The IPv6 DHCP server tried to receive a packet but an error
 occured during this attempt. The reason for the error is included in
 occured during this attempt. The reason for the error is included in
 the message.
 the message.
 
 
-% DHCP6_PACKET_RECEIVED %1 (type %2) packet received
+% DHCP6_PACKET_RECEIVED %1 packet received
 A debug message noting that the server has received the specified type
 A debug message noting that the server has received the specified type
 of packet.  Note that a packet marked as UNKNOWN may well be a valid
 of packet.  Note that a packet marked as UNKNOWN may well be a valid
 DHCP packet, just a type not expected by the server (e.g. it will report
 DHCP packet, just a type not expected by the server (e.g. it will report
@@ -66,10 +66,10 @@ This error is output if the server failed to assemble the data to be
 returned to the client into a valid packet.  The reason is most likely
 returned to the client into a valid packet.  The reason is most likely
 to be to a programming error: please raise a bug report.
 to be to a programming error: please raise a bug report.
 
 
-% DHCP6_QUERY_DATA received packet length %1, data length %2, data is <%3>
+% DHCP6_QUERY_DATA received packet length %1, data length %2, data is %3
 A debug message listing the data received from the client or relay.
 A debug message listing the data received from the client or relay.
 
 
-% DHCP6_RESPONSE_DATA responding with packet type %1 data is <%2>
+% DHCP6_RESPONSE_DATA responding with packet type %1 data is %2
 A debug message listing the data returned to the client.
 A debug message listing the data returned to the client.
 
 
 % DHCP6_SERVER_FAILED server failed: %1
 % DHCP6_SERVER_FAILED server failed: %1

+ 128 - 41
src/bin/dhcp6/dhcp6_srv.cc

@@ -28,6 +28,12 @@
 #include <util/io_utilities.h>
 #include <util/io_utilities.h>
 #include <util/range_utilities.h>
 #include <util/range_utilities.h>
 #include <dhcp/duid.h>
 #include <dhcp/duid.h>
+#include <dhcp/lease_mgr.h>
+#include <dhcp/cfgmgr.h>
+#include <dhcp/option6_iaaddr.h>
+
+// @todo: Replace this with MySQL_LeaseMgr once it is merged
+#include <dhcp/tests/memfile_lease_mgr.h>
 
 
 using namespace isc;
 using namespace isc;
 using namespace isc::asiolink;
 using namespace isc::asiolink;
@@ -35,20 +41,9 @@ using namespace isc::dhcp;
 using namespace isc::util;
 using namespace isc::util;
 using namespace std;
 using namespace std;
 
 
-const std::string HARDCODED_LEASE = "2001:db8:1::1234:abcd";
-const uint32_t HARDCODED_T1 = 1500; // in seconds
-const uint32_t HARDCODED_T2 = 2600; // in seconds
-const uint32_t HARDCODED_PREFERRED_LIFETIME = 3600; // in seconds
-const uint32_t HARDCODED_VALID_LIFETIME = 7200; // in seconds
 const std::string HARDCODED_DNS_SERVER = "2001:db8:1::1";
 const std::string HARDCODED_DNS_SERVER = "2001:db8:1::1";
 
 
 Dhcpv6Srv::Dhcpv6Srv(uint16_t port) {
 Dhcpv6Srv::Dhcpv6Srv(uint16_t port) {
-    if (port == 0) {
-        // used for testing purposes. Some tests, e.g. configuration parser,
-        // require Dhcpv6Srv object, but they don't really need it to do
-        // anything. This speed up and simplifies the tests.
-        return;
-    }
 
 
     LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_OPEN_SOCKET).arg(port);
     LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_OPEN_SOCKET).arg(port);
 
 
@@ -56,13 +51,18 @@ Dhcpv6Srv::Dhcpv6Srv(uint16_t port) {
     // it may throw something if things go wrong
     // it may throw something if things go wrong
     try {
     try {
 
 
-        if (IfaceMgr::instance().countIfaces() == 0) {
+        // used for testing purposes. Some tests, e.g. configuration parser,
-            LOG_ERROR(dhcp6_logger, DHCP6_NO_INTERFACES);
+        // require Dhcpv6Srv object, but they don't really need it to do
-            shutdown_ = true;
+        // anything. This speed up and simplifies the tests.
-            return;
+        if (port) {
-        }
+            if (IfaceMgr::instance().countIfaces() == 0) {
+                LOG_ERROR(dhcp6_logger, DHCP6_NO_INTERFACES);
+                shutdown_ = true;
+                return;
+            }
 
 
-        IfaceMgr::instance().openSockets6(port);
+            IfaceMgr::instance().openSockets6(port);
+        }
 
 
         setServerID();
         setServerID();
 
 
@@ -74,11 +74,20 @@ Dhcpv6Srv::Dhcpv6Srv(uint16_t port) {
         return;
         return;
     }
     }
 
 
+    // Instantiate LeaseMgr
+    // @todo: Replace this with MySQL_LeaseMgr once it is merged
+    new isc::dhcp::test::Memfile_LeaseMgr("");
+
+    // Instantiate allocation engine
+    alloc_engine_ = new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100);
+
     shutdown_ = false;
     shutdown_ = false;
 }
 }
 
 
 Dhcpv6Srv::~Dhcpv6Srv() {
 Dhcpv6Srv::~Dhcpv6Srv() {
     IfaceMgr::instance().closeSockets();
     IfaceMgr::instance().closeSockets();
+
+    LeaseMgr::destroy_instance();
 }
 }
 
 
 void Dhcpv6Srv::shutdown() {
 void Dhcpv6Srv::shutdown() {
@@ -108,10 +117,9 @@ bool Dhcpv6Srv::run() {
                 continue;
                 continue;
             }
             }
             LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_PACKET_RECEIVED)
             LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_PACKET_RECEIVED)
-                      .arg(serverReceivedPacketName(query->getType()))
+                      .arg(serverReceivedPacketName(query->getType()));
-                      .arg(query->getType());
             LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL_DATA, DHCP6_QUERY_DATA)
             LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL_DATA, DHCP6_QUERY_DATA)
-                      .arg(query->getType())
+                      .arg(static_cast<int>(query->getType()))
                       .arg(query->getBuffer().getLength())
                       .arg(query->getBuffer().getLength())
                       .arg(query->toText());
                       .arg(query->toText());
 
 
@@ -301,33 +309,112 @@ void Dhcpv6Srv::appendRequestedOptions(const Pkt6Ptr& /*question*/, Pkt6Ptr& ans
     answer->addOption(dnsservers);
     answer->addOption(dnsservers);
 }
 }
 
 
+OptionPtr Dhcpv6Srv::createStatusCode(uint16_t code, const std::string& text) {
+
+    // @todo: Implement Option6_StatusCode and rewrite this code here
+    vector<uint8_t> data(text.c_str(), text.c_str() + text.length());
+    data.insert(data.begin(), static_cast<uint8_t>(code % 256));
+    data.insert(data.begin(), static_cast<uint8_t>(code >> 8));
+    OptionPtr status(new Option(Option::V6, D6O_STATUS_CODE, data));
+    return (status);
+}
+
+Subnet6Ptr Dhcpv6Srv::getSubnet(const Pkt6Ptr& question) {
+    Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(question->getRemoteAddr());
+
+    return (subnet);
+}
+
 void Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer) {
 void Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer) {
-    /// TODO Rewrite this once LeaseManager is implemented.
+
-
+    Subnet6Ptr subnet = getSubnet(question);
-    // answer client's IA (this is mostly a dummy,
+    if (subnet) {
-    // so let's answer only first IA and hope there is only one)
+        cout << "#### Selected subnet " << subnet->toText() << endl;
-    boost::shared_ptr<Option> ia_opt = question->getOption(D6O_IA_NA);
+    } else {
-    if (ia_opt) {
+        cout << "#### Failed to select a subnet" << endl;
-        // found IA
+    }
-        Option* tmp = ia_opt.get();
+
-        Option6IA* ia_req = dynamic_cast<Option6IA*>(tmp);
+    // @todo: We should implement Option6Duid some day, but we can do without it
-        if (ia_req) {
+    // just fine for now
-            boost::shared_ptr<Option6IA>
+    DuidPtr duid;
-                ia_rsp(new Option6IA(D6O_IA_NA, ia_req->getIAID()));
+    OptionPtr opt_duid = question->getOption(D6O_CLIENTID);
-            ia_rsp->setT1(HARDCODED_T1);
+    if (opt_duid) {
-            ia_rsp->setT2(HARDCODED_T2);
+        duid = DuidPtr(new DUID(opt_duid->getData()));
-            boost::shared_ptr<Option6IAAddr>
+    }
-                addr(new Option6IAAddr(D6O_IAADDR,
+    if (duid) {
-                                       IOAddress(HARDCODED_LEASE),
+        cout << "#### Processing request from client with duid=" << duid->toText() << endl;
-                                       HARDCODED_PREFERRED_LIFETIME,
+    } else {
-                                       HARDCODED_VALID_LIFETIME));
+        cout << "#### Failed to find client-id :(" << endl;
-            ia_rsp->addOption(addr);
+    }
-            answer->addOption(ia_rsp);
+
+    for (Option::OptionCollection::iterator opt = question->options_.begin(); opt != question->options_.end(); ++opt) {
+        switch (opt->second->getType()) {
+        case D6O_IA_NA: {
+            OptionPtr answer_opt = handleIA_NA(subnet, duid, question, boost::dynamic_pointer_cast<Option6IA>(opt->second));
+            if (answer_opt) {
+                answer->addOption(answer_opt);
+            }
+            break;
+        }
+        default:
+            break;
         }
         }
     }
     }
 }
 }
 
 
+OptionPtr Dhcpv6Srv::handleIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid, Pkt6Ptr question,
+                                 boost::shared_ptr<Option6IA> ia) {
+    if (!subnet) {
+        boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
+
+        ia_rsp->addOption(createStatusCode(STATUS_NoAddrsAvail, "Sorry, no subnet available."));
+        return (ia_rsp);
+    }
+
+    boost::shared_ptr<Option6IAAddr> hintOpt = boost::dynamic_pointer_cast<Option6IAAddr>(ia->getOption(D6O_IAADDR));
+
+    IOAddress hint("::");
+    cout << "#### Processing request IA_NA: iaid=" << ia->getIAID();
+    if (hintOpt) {
+        hint = hintOpt->getAddress();
+        cout << ", hint=" << hint.toText() << endl;
+    } else {
+        cout << ", no hint provided" << endl;
+    }
+
+    bool fake_allocation = false;
+    if (question->getType() == DHCPV6_SOLICIT) {
+        /// @todo: Check if we support rapid commit
+        fake_allocation = true;
+    }
+
+    Lease6Ptr lease = alloc_engine_->allocateAddress6(subnet, duid, ia->getIAID(),
+                                                      hint, fake_allocation);
+
+    boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
+
+    if (lease) {
+        cout << "#### Allocated lease:" << lease->addr_.toText() << endl;
+
+        ia_rsp->setT1(subnet->getT1());
+        ia_rsp->setT2(subnet->getT2());
+
+        boost::shared_ptr<Option6IAAddr>
+            addr(new Option6IAAddr(D6O_IAADDR,
+                                   lease->addr_,
+                                   lease->preferred_lft_,
+                                   lease->valid_lft_));
+        ia_rsp->addOption(addr);
+    } else {
+        cout << "#### Failed to allocate a lease";
+
+        ia_rsp->addOption(createStatusCode(STATUS_NoAddrsAvail, "Sorry, no address could be allocated."));
+    }
+    return (ia_rsp);
+}
+
 Pkt6Ptr Dhcpv6Srv::processSolicit(const Pkt6Ptr& solicit) {
 Pkt6Ptr Dhcpv6Srv::processSolicit(const Pkt6Ptr& solicit) {
+
     Pkt6Ptr advertise(new Pkt6(DHCPV6_ADVERTISE, solicit->getTransid()));
     Pkt6Ptr advertise(new Pkt6(DHCPV6_ADVERTISE, solicit->getTransid()));
 
 
     copyDefaultOptions(solicit, advertise);
     copyDefaultOptions(solicit, advertise);

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

@@ -19,6 +19,10 @@
 #include <dhcp/dhcp6.h>
 #include <dhcp/dhcp6.h>
 #include <dhcp/pkt6.h>
 #include <dhcp/pkt6.h>
 #include <dhcp/option.h>
 #include <dhcp/option.h>
+#include <dhcp/subnet.h>
+#include <dhcp/duid.h>
+#include <dhcp/option6_ia.h>
+#include <dhcp/alloc_engine.h>
 #include <iostream>
 #include <iostream>
 
 
 namespace isc {
 namespace isc {
@@ -147,6 +151,36 @@ protected:
     /// @param infRequest message received from client
     /// @param infRequest message received from client
     Pkt6Ptr processInfRequest(const Pkt6Ptr& infRequest);
     Pkt6Ptr processInfRequest(const Pkt6Ptr& infRequest);
 
 
+    /// @brief creates status-code option
+    ///
+    /// @param code status code value (see RFC3315)
+    /// @param text textual explanation (will be sent in status code option)
+    /// @return status-code option
+    OptionPtr createStatusCode(uint16_t code, const std::string& text);
+
+    /// @brief selects a subnet for a given client's packet
+    ///
+    /// @return selected subnet (or NULL if no suitable subnet was found)
+    isc::dhcp::Subnet6Ptr getSubnet(const Pkt6Ptr& question);
+
+    /// @brief processes IA_NA option (and assigns addresses if necessary)
+    ///
+    /// Generates response to IA_NA. This typically includes selecting (and
+    /// allocating a lease in case of REQUEST) a lease and creating
+    /// IAADDR option. In case of allocation failure, it may contain
+    /// status code option with non-zero status, denoting cause of the
+    /// allocation failure.
+    ///
+    /// @param subnet subnet the client is connected to
+    /// @param duid client's duid
+    /// @param question client's message (typically SOLICIT or REQUEST)
+    /// @param ia pointer to client's IA_NA option (client's request)
+    /// @return IA_NA option (server's response)
+    OptionPtr handleIA_NA(const isc::dhcp::Subnet6Ptr& subnet,
+                          const isc::dhcp::DuidPtr& duid,
+                          isc::dhcp::Pkt6Ptr question,
+                          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)
@@ -202,6 +236,12 @@ protected:
     /// server DUID (to be sent in server-identifier option)
     /// server DUID (to be sent in server-identifier option)
     boost::shared_ptr<isc::dhcp::Option> serverid_;
     boost::shared_ptr<isc::dhcp::Option> serverid_;
 
 
+    /// @brief Allocation Engine
+    /// Pointer to the allocation engine that we are currently using
+    /// It must be a pointer, because we will support changing engines
+    /// during normal operation (e.g. to use different allocators)
+    AllocEngine* alloc_engine_;
+
     /// indicates if shutdown is in progress. Setting it to true will
     /// indicates if shutdown is in progress. Setting it to true will
     /// initiate server shutdown procedure.
     /// initiate server shutdown procedure.
     volatile bool shutdown_;
     volatile bool shutdown_;

+ 4 - 0
src/lib/dhcp/Makefile.am

@@ -39,6 +39,10 @@ libb10_dhcpsrv_la_SOURCES += pool.cc pool.h
 libb10_dhcpsrv_la_SOURCES += subnet.cc subnet.h
 libb10_dhcpsrv_la_SOURCES += subnet.cc subnet.h
 libb10_dhcpsrv_la_SOURCES += triplet.h
 libb10_dhcpsrv_la_SOURCES += triplet.h
 libb10_dhcpsrv_la_SOURCES += lease_mgr.cc lease_mgr.h
 libb10_dhcpsrv_la_SOURCES += lease_mgr.cc lease_mgr.h
+
+# @todo: Remove this once MySQL LeaseMgr is merged
+libb10_dhcpsrv_la_SOURCES += tests/memfile_lease_mgr.cc tests/memfile_lease_mgr.h
+
 libb10_dhcpsrv_la_SOURCES += addr_utilities.cc addr_utilities.h
 libb10_dhcpsrv_la_SOURCES += addr_utilities.cc addr_utilities.h
 libb10_dhcpsrv_la_SOURCES += alloc_engine.cc alloc_engine.h
 libb10_dhcpsrv_la_SOURCES += alloc_engine.cc alloc_engine.h
 libb10_dhcpsrv_la_CXXFLAGS = $(AM_CXXFLAGS)
 libb10_dhcpsrv_la_CXXFLAGS = $(AM_CXXFLAGS)