Browse Source

[2995] 4 hook points implemented:

 - pkt6_receive, subnet6_select, lease6_select, pkt6_send
 - framework updated
 - some unittests implemented
Tomek Mrugalski 12 years ago
parent
commit
eff9543714

+ 1 - 0
src/bin/dhcp4/Makefile.am

@@ -63,6 +63,7 @@ b10_dhcp4_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
 b10_dhcp4_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
 b10_dhcp4_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
 b10_dhcp4_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
 b10_dhcp4_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
 b10_dhcp4_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
 b10_dhcp4_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
+b10_dhcp4_LDADD += $(top_builddir)/src/lib/hooks/libb10-hooks.la
 
 
 b10_dhcp4dir = $(pkgdatadir)
 b10_dhcp4dir = $(pkgdatadir)
 b10_dhcp4_DATA = dhcp4.spec
 b10_dhcp4_DATA = dhcp4.spec

+ 1 - 0
src/bin/dhcp4/tests/Makefile.am

@@ -71,6 +71,7 @@ dhcp4_unittests_LDADD += $(top_builddir)/src/lib/dhcpsrv/libb10-dhcpsrv.la
 dhcp4_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
 dhcp4_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
 dhcp4_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
 dhcp4_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
 dhcp4_unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
 dhcp4_unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
+dhcp4_unittests_LDADD += $(top_builddir)/src/lib/hooks/libb10-hooks.la
 endif
 endif
 
 
 noinst_PROGRAMS = $(TESTS)
 noinst_PROGRAMS = $(TESTS)

+ 1 - 0
src/bin/dhcp6/Makefile.am

@@ -65,6 +65,7 @@ b10_dhcp6_LDADD += $(top_builddir)/src/lib/dhcpsrv/libb10-dhcpsrv.la
 b10_dhcp6_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
 b10_dhcp6_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
 b10_dhcp6_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
 b10_dhcp6_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
 b10_dhcp6_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
 b10_dhcp6_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
+b10_dhcp6_LDADD += $(top_builddir)/src/lib/hooks/libb10-hooks.la
 
 
 b10_dhcp6dir = $(pkgdatadir)
 b10_dhcp6dir = $(pkgdatadir)
 b10_dhcp6_DATA = dhcp6.spec
 b10_dhcp6_DATA = dhcp6.spec

+ 3 - 0
src/bin/dhcp6/dhcp6_log.h

@@ -38,6 +38,9 @@ const int DBG_DHCP6_COMMAND = DBGLVL_COMMAND;
 // Trace basic operations within the code.
 // Trace basic operations within the code.
 const int DBG_DHCP6_BASIC = DBGLVL_TRACE_BASIC;
 const int DBG_DHCP6_BASIC = DBGLVL_TRACE_BASIC;
 
 
+// Trace hook related operations
+const int DBG_DHCP6_HOOKS = DBGLVL_TRACE_BASIC;
+
 // Trace detailed operations, including errors raised when processing invalid
 // Trace detailed operations, including errors raised when processing invalid
 // packets.  (These are not logged at severities of WARN or higher for fear
 // packets.  (These are not logged at severities of WARN or higher for fear
 // that a set of deliberately invalid packets set to the server could overwhelm
 // that a set of deliberately invalid packets set to the server could overwhelm

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

@@ -65,6 +65,24 @@ This informational message is printed every time the IPv6 DHCP server
 is started.  It indicates what database backend type is being to store
 is started.  It indicates what database backend type is being to store
 lease and other information.
 lease and other information.
 
 
+% DHCP6_HOOK_PACKET_RCVD_SKIP received DHCPv6 packet was dropped, because a callout set skip flag.
+This debug message is printed when a callout installed on pkt6_received
+hook point sets skip flag. This flag instructs the server to skip the next processing
+stage, which would be to handle the packet. This effectively means drop the packet.
+
+% DHCP6_HOOK_PACKET_SEND_SKIP Prepared DHCPv6 response was not sent, because a callout set skip flag.
+This debug message is printed when a callout installed on pkt6_send
+hook point sets skip flag. This flag instructs the server to skip the next processing
+stage, which would be to send a response. This effectively means that the client will
+not get any response, even though the server processed client's request and acted on
+it (e.g. could possible allocate a lease).
+
+% DHCP6_HOOK_SUBNET6_SELECT_SKIP No subnet was selected, because a callout set skip flag.
+This debug message is printed when a callout installed on subnet6_select
+hook point sets a skip flag. It means that the server was told that no subnet
+should be selected. This severely limits further processing - server will be only
+able to offer global options. No addresses or prefixes could be assigned.
+
 % 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 the
 a lease. It is up to the client to choose one server out of the

+ 118 - 8
src/bin/dhcp6/dhcp6_srv.cc

@@ -37,6 +37,9 @@
 #include <util/io_utilities.h>
 #include <util/io_utilities.h>
 #include <util/range_utilities.h>
 #include <util/range_utilities.h>
 #include <util/encode/hex.h>
 #include <util/encode/hex.h>
+#include <hooks/server_hooks.h>
+#include <hooks/hooks_manager.h>
+#include <hooks/callout_handle.h>
 
 
 #include <boost/foreach.hpp>
 #include <boost/foreach.hpp>
 #include <boost/tokenizer.hpp>
 #include <boost/tokenizer.hpp>
@@ -50,6 +53,7 @@
 using namespace isc;
 using namespace isc;
 using namespace isc::asiolink;
 using namespace isc::asiolink;
 using namespace isc::dhcp;
 using namespace isc::dhcp;
+using namespace isc::hooks;
 using namespace isc::util;
 using namespace isc::util;
 using namespace std;
 using namespace std;
 
 
@@ -67,7 +71,9 @@ namespace dhcp {
 static const char* SERVER_DUID_FILE = "b10-dhcp6-serverid";
 static const char* SERVER_DUID_FILE = "b10-dhcp6-serverid";
 
 
 Dhcpv6Srv::Dhcpv6Srv(uint16_t port)
 Dhcpv6Srv::Dhcpv6Srv(uint16_t port)
-    : alloc_engine_(), serverid_(), shutdown_(true) {
+:alloc_engine_(), serverid_(), shutdown_(true), hook_index_pkt6_receive_(100),
+    hook_index_subnet6_select_(101), hook_index_pkt6_send_(102)
+{
 
 
     LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_OPEN_SOCKET).arg(port);
     LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_OPEN_SOCKET).arg(port);
 
 
@@ -106,6 +112,15 @@ Dhcpv6Srv::Dhcpv6Srv(uint16_t port)
         // Instantiate allocation engine
         // Instantiate allocation engine
         alloc_engine_.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100));
         alloc_engine_.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100));
 
 
+        // Register hook points
+        hook_index_pkt6_receive_  = ServerHooks::getServerHooks().registerHook("pkt6_receive");
+        hook_index_subnet6_select_ = ServerHooks::getServerHooks().registerHook("subnet6_select");
+        hook_index_pkt6_send_      = ServerHooks::getServerHooks().registerHook("pkt6_send");
+
+        /// @todo call loadLibraries() when handling configuration changes
+        vector<string> libraries; // no libraries at this time
+        HooksManager::getHooksManager().loadLibraries(libraries);
+
     } catch (const std::exception &e) {
     } catch (const std::exception &e) {
         LOG_ERROR(dhcp6_logger, DHCP6_SRV_CONSTRUCT_ERROR).arg(e.what());
         LOG_ERROR(dhcp6_logger, DHCP6_SRV_CONSTRUCT_ERROR).arg(e.what());
         return;
         return;
@@ -126,6 +141,10 @@ void Dhcpv6Srv::shutdown() {
     shutdown_ = true;
     shutdown_ = true;
 }
 }
 
 
+Pkt6Ptr Dhcpv6Srv::receivePacket(int timeout) {
+    return (IfaceMgr::instance().receive6(timeout));
+}
+
 bool Dhcpv6Srv::run() {
 bool Dhcpv6Srv::run() {
     while (!shutdown_) {
     while (!shutdown_) {
         /// @todo: calculate actual timeout to the next event (e.g. lease
         /// @todo: calculate actual timeout to the next event (e.g. lease
@@ -134,14 +153,15 @@ bool Dhcpv6Srv::run() {
         /// For now, we are just calling select for 1000 seconds. There
         /// For now, we are just calling select for 1000 seconds. There
         /// were some issues reported on some systems when calling select()
         /// were some issues reported on some systems when calling select()
         /// with too large values. Unfortunately, I don't recall the details.
         /// with too large values. Unfortunately, I don't recall the details.
-        int timeout = 1000;
+        //cppcheck-suppress variableScope This is temporary anyway
+        const int timeout = 1000;
 
 
         // client's message and server's response
         // client's message and server's response
         Pkt6Ptr query;
         Pkt6Ptr query;
         Pkt6Ptr rsp;
         Pkt6Ptr rsp;
 
 
         try {
         try {
-            query = IfaceMgr::instance().receive6(timeout);
+            query = receivePacket(timeout);
         } catch (const std::exception& e) {
         } catch (const std::exception& e) {
             LOG_ERROR(dhcp6_logger, DHCP6_PACKET_RECEIVE_FAIL).arg(e.what());
             LOG_ERROR(dhcp6_logger, DHCP6_PACKET_RECEIVE_FAIL).arg(e.what());
         }
         }
@@ -159,6 +179,24 @@ bool Dhcpv6Srv::run() {
                       .arg(query->getBuffer().getLength())
                       .arg(query->getBuffer().getLength())
                       .arg(query->toText());
                       .arg(query->toText());
 
 
+            // Let's execute all callouts registered for packet_received
+            if (HooksManager::getHooksManager().calloutsPresent(hook_index_pkt6_receive_)) {
+                CalloutHandlePtr callout_handle = getCalloutHandle(query);
+
+                // This is the first callout, so no need to clear any arguments
+                callout_handle->setArgument("pkt6", query);
+                HooksManager::getHooksManager().callCallouts(hook_index_pkt6_receive_,
+                                                *callout_handle);
+
+                // Callouts decided to skip the next processing step. The next
+                // processing step would to process the packet, so skip at this
+                // stage means drop.
+                if (callout_handle->getSkip()) {
+                    LOG_DEBUG(dhcp6_logger, DBG_DHCP6_HOOKS, DHCP6_HOOK_PACKET_RCVD_SKIP);
+                    continue;
+                }
+            }
+
             try {
             try {
                 switch (query->getType()) {
                 switch (query->getType()) {
                 case DHCPV6_SOLICIT:
                 case DHCPV6_SOLICIT:
@@ -203,7 +241,7 @@ bool Dhcpv6Srv::run() {
             } catch (const RFCViolation& e) {
             } catch (const RFCViolation& e) {
                 LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_REQUIRED_OPTIONS_CHECK_FAIL)
                 LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_REQUIRED_OPTIONS_CHECK_FAIL)
                     .arg(query->getName())
                     .arg(query->getName())
-                    .arg(query->getRemoteAddr())
+                    .arg(query->getRemoteAddr().toText())
                     .arg(e.what());
                     .arg(e.what());
 
 
             } catch (const isc::Exception& e) {
             } catch (const isc::Exception& e) {
@@ -217,7 +255,7 @@ bool Dhcpv6Srv::run() {
                 // packets.)
                 // packets.)
                 LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_PACKET_PROCESS_FAIL)
                 LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_PACKET_PROCESS_FAIL)
                     .arg(query->getName())
                     .arg(query->getName())
-                    .arg(query->getRemoteAddr())
+                    .arg(query->getRemoteAddr().toText())
                     .arg(e.what());
                     .arg(e.what());
             }
             }
 
 
@@ -229,6 +267,32 @@ bool Dhcpv6Srv::run() {
                 rsp->setIndex(query->getIndex());
                 rsp->setIndex(query->getIndex());
                 rsp->setIface(query->getIface());
                 rsp->setIface(query->getIface());
 
 
+                // Execute all callouts registered for packet6_send
+                if (HooksManager::getHooksManager().calloutsPresent(hook_index_pkt6_send_)) {
+                    boost::shared_ptr<CalloutHandle> callout_handle = getCalloutHandle(query);
+
+                    // Delete all previous arguments
+                    callout_handle->deleteAllArguments();
+
+                    // Clear skip flag if it was set in previous callouts
+                    callout_handle->setSkip(false);
+
+                    // Set our response
+                    callout_handle->setArgument("pkt6", rsp);
+
+                    // Call all installed callouts
+                    HooksManager::getHooksManager().callCallouts(hook_index_pkt6_send_,
+                                                    *callout_handle);
+
+                    // Callouts decided to skip the next processing step. The next
+                    // processing step would to send the packet, so skip at this
+                    // stage means "drop response".
+                    if (callout_handle->getSkip()) {
+                        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_HOOKS, DHCP6_HOOK_PACKET_SEND_SKIP);
+                        continue;
+                    }
+                }
+
                 LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL_DATA,
                 LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL_DATA,
                           DHCP6_RESPONSE_DATA)
                           DHCP6_RESPONSE_DATA)
                     .arg(static_cast<int>(rsp->getType())).arg(rsp->toText());
                     .arg(static_cast<int>(rsp->getType())).arg(rsp->toText());
@@ -559,6 +623,29 @@ Dhcpv6Srv::selectSubnet(const Pkt6Ptr& question) {
         }
         }
     }
     }
 
 
+    // Let's execute all callouts registered for packet_received
+    if (HooksManager::getHooksManager().calloutsPresent(hook_index_subnet6_select_)) {
+        boost::shared_ptr<CalloutHandle> callout_handle = getCalloutHandle(question);
+
+        // This is the first callout, so no need to clear any arguments
+        callout_handle->setArgument("pkt6", question);
+        callout_handle->setArgument("subnet6", subnet);
+        callout_handle->setArgument("subnet6collection", CfgMgr::instance().getSubnets6());
+        HooksManager::getHooksManager().callCallouts(hook_index_subnet6_select_,
+                                        *callout_handle);
+
+        // Callouts decided to skip this step. This means that no subnet will be
+        // selected. Packet processing will continue, but it will be severly limited
+        // (i.e. only global options will be assigned)
+        if (callout_handle->getSkip()) {
+            LOG_DEBUG(dhcp6_logger, DBG_DHCP6_HOOKS, DHCP6_HOOK_SUBNET6_SELECT_SKIP);
+            return (Subnet6Ptr());
+        }
+
+        // Use whatever subnet was specified by the callout
+        callout_handle->getArgument("subnet6", subnet);
+    }
+
     return (subnet);
     return (subnet);
 }
 }
 
 
@@ -618,7 +705,8 @@ Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer) {
         switch (opt->second->getType()) {
         switch (opt->second->getType()) {
         case D6O_IA_NA: {
         case D6O_IA_NA: {
             OptionPtr answer_opt = assignIA_NA(subnet, duid, question,
             OptionPtr answer_opt = assignIA_NA(subnet, duid, question,
-                                   boost::dynamic_pointer_cast<Option6IA>(opt->second));
+                                               boost::dynamic_pointer_cast<Option6IA>(opt->second),
+                                               question);
             if (answer_opt) {
             if (answer_opt) {
                 answer->addOption(answer_opt);
                 answer->addOption(answer_opt);
             }
             }
@@ -632,7 +720,7 @@ Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer) {
 
 
 OptionPtr
 OptionPtr
 Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
 Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
-                       Pkt6Ptr question, boost::shared_ptr<Option6IA> ia) {
+                       Pkt6Ptr question, boost::shared_ptr<Option6IA> ia, const Pkt6Ptr& query) {
     // If there is no subnet selected for handling this IA_NA, the only thing to do left is
     // If there is no subnet selected for handling this IA_NA, the only thing to do left is
     // to say that we are sorry, but the user won't get an address. As a convenience, we
     // to say that we are sorry, but the user won't get an address. As a convenience, we
     // use a different status text to indicate that (compare to the same status code,
     // use a different status text to indicate that (compare to the same status code,
@@ -676,12 +764,15 @@ Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
         fake_allocation = true;
         fake_allocation = true;
     }
     }
 
 
+    CalloutHandlePtr callout_handle = getCalloutHandle(query);
+
     // Use allocation engine to pick a lease for this client. Allocation engine
     // 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
     // will try to honour the hint, but it is just a hint - some other address
     // may be used instead. If fake_allocation is set to false, the lease will
     // may be used instead. If fake_allocation is set to false, the lease will
     // be inserted into the LeaseMgr as well.
     // be inserted into the LeaseMgr as well.
     Lease6Ptr lease = alloc_engine_->allocateAddress6(subnet, duid, ia->getIAID(),
     Lease6Ptr lease = alloc_engine_->allocateAddress6(subnet, duid, ia->getIAID(),
-                                                      hint, fake_allocation);
+                                                      hint, fake_allocation,
+                                                      callout_handle);
 
 
     // Create IA_NA that we will put in the response.
     // Create IA_NA that we will put in the response.
     // Do not use OptionDefinition to create option's instance so
     // Do not use OptionDefinition to create option's instance so
@@ -1102,5 +1193,24 @@ Dhcpv6Srv::processInfRequest(const Pkt6Ptr& infRequest) {
     return reply;
     return reply;
 }
 }
 
 
+isc::hooks::CalloutHandlePtr Dhcpv6Srv::getCalloutHandle(const Pkt6Ptr& pkt) {
+    CalloutHandlePtr callout_handle;
+
+    static Pkt6Ptr old_pointer;
+
+    if (!callout_handle ||
+        old_pointer != pkt) {
+        // This is the first packet or a different packet than previously
+        // passed to getCalloutHandle()
+
+        // Remember the pointer to this packet
+        old_pointer = pkt;
+
+        callout_handle = HooksManager::getHooksManager().createCalloutHandle();
+    }
+
+    return (callout_handle);
+}
+
 };
 };
 };
 };

+ 35 - 1
src/bin/dhcp6/dhcp6_srv.h

@@ -23,6 +23,7 @@
 #include <dhcp/pkt6.h>
 #include <dhcp/pkt6.h>
 #include <dhcpsrv/alloc_engine.h>
 #include <dhcpsrv/alloc_engine.h>
 #include <dhcpsrv/subnet.h>
 #include <dhcpsrv/subnet.h>
+#include <hooks/hooks_manager.h>
 
 
 #include <boost/noncopyable.hpp>
 #include <boost/noncopyable.hpp>
 
 
@@ -87,6 +88,20 @@ public:
     /// @brief Instructs the server to shut down.
     /// @brief Instructs the server to shut down.
     void shutdown();
     void shutdown();
 
 
+    /// @brief returns ServerHooks object
+    /// @todo: remove this as soon as ServerHooks object is converted
+    /// to a signleton.
+    //static boost::shared_ptr<isc::util::ServerHooks> getServerHooks();
+
+    /// @brief returns Callout Manager object
+    ///
+    /// This manager is used to manage callouts registered on various hook
+    /// points. @todo exact access method for HooksManager manager will change
+    /// when it will be converted to a singleton.
+    ///
+    /// @return CalloutManager instance
+    //static boost::shared_ptr<isc::util::HooksManager> getHooksManager();
+
 protected:
 protected:
 
 
     /// @brief verifies if specified packet meets RFC requirements
     /// @brief verifies if specified packet meets RFC requirements
@@ -189,7 +204,8 @@ protected:
     OptionPtr assignIA_NA(const isc::dhcp::Subnet6Ptr& subnet,
     OptionPtr assignIA_NA(const isc::dhcp::Subnet6Ptr& subnet,
                           const isc::dhcp::DuidPtr& duid,
                           const isc::dhcp::DuidPtr& duid,
                           isc::dhcp::Pkt6Ptr question,
                           isc::dhcp::Pkt6Ptr question,
-                          boost::shared_ptr<Option6IA> ia);
+                          boost::shared_ptr<Option6IA> ia,
+                          const Pkt6Ptr& query);
 
 
     /// @brief Renews specific IA_NA option
     /// @brief Renews specific IA_NA option
     ///
     ///
@@ -321,6 +337,13 @@ protected:
     /// @return string representation
     /// @return string representation
     static std::string duidToString(const OptionPtr& opt);
     static std::string duidToString(const OptionPtr& opt);
 
 
+
+    /// @brief dummy wrapper around IfaceMgr::receive6
+    ///
+    /// This method is useful for testing purposes, where its replacement
+    /// simulates reception of a packet. For that purpose it is protected.
+    virtual Pkt6Ptr receivePacket(int timeout);
+
 private:
 private:
     /// @brief Allocation Engine.
     /// @brief Allocation Engine.
     /// Pointer to the allocation engine that we are currently using
     /// Pointer to the allocation engine that we are currently using
@@ -334,6 +357,17 @@ private:
     /// 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_;
+
+    isc::hooks::CalloutHandlePtr getCalloutHandle(const Pkt6Ptr& pkt);
+
+    void packetProcessStart(const Pkt6Ptr& pkt);
+
+    void packetProcessEnd(const Pkt6Ptr& pkt);
+
+    /// Indexes for registered hook points
+    int hook_index_pkt6_receive_;
+    int hook_index_subnet6_select_;
+    int hook_index_pkt6_send_;
 };
 };
 
 
 }; // namespace isc::dhcp
 }; // namespace isc::dhcp

+ 1 - 0
src/bin/dhcp6/tests/Makefile.am

@@ -68,6 +68,7 @@ dhcp6_unittests_LDADD += $(top_builddir)/src/lib/dhcpsrv/libb10-dhcpsrv.la
 dhcp6_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
 dhcp6_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
 dhcp6_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
 dhcp6_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
 dhcp6_unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
 dhcp6_unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
+dhcp6_unittests_LDADD += $(top_builddir)/src/lib/hooks/libb10-hooks.la
 endif
 endif
 
 
 noinst_PROGRAMS = $(TESTS)
 noinst_PROGRAMS = $(TESTS)

+ 109 - 0
src/bin/dhcp6/tests/dhcp6_srv_unittest.cc

@@ -33,12 +33,16 @@
 #include <util/buffer.h>
 #include <util/buffer.h>
 #include <util/range_utilities.h>
 #include <util/range_utilities.h>
 
 
+#include <hooks/server_hooks.h>
+#include <hooks/callout_manager.h>
+
 #include <boost/scoped_ptr.hpp>
 #include <boost/scoped_ptr.hpp>
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
 #include <unistd.h>
 #include <unistd.h>
 #include <fstream>
 #include <fstream>
 #include <iostream>
 #include <iostream>
 #include <sstream>
 #include <sstream>
+#include <list>
 
 
 using namespace isc;
 using namespace isc;
 using namespace isc::asiolink;
 using namespace isc::asiolink;
@@ -46,6 +50,7 @@ using namespace isc::config;
 using namespace isc::data;
 using namespace isc::data;
 using namespace isc::dhcp;
 using namespace isc::dhcp;
 using namespace isc::util;
 using namespace isc::util;
+using namespace isc::hooks;
 using namespace std;
 using namespace std;
 
 
 // namespace has to be named, because friends are defined in Dhcpv6Srv class
 // namespace has to be named, because friends are defined in Dhcpv6Srv class
@@ -61,6 +66,26 @@ public:
         LeaseMgrFactory::create(memfile);
         LeaseMgrFactory::create(memfile);
     }
     }
 
 
+    virtual Pkt6Ptr receivePacket(int /*timeout*/) {
+
+        // If there is anything prepared as fake incoming
+        // traffic, use it
+        if (!to_be_received_.empty()) {
+            Pkt6Ptr pkt = to_be_received_.front();
+            to_be_received_.pop_front();
+            return (pkt);
+        }
+
+        // If not, just trigger shutdown and
+        // return immediately
+        shutdown();
+        return (Pkt6Ptr());
+    }
+
+    void fakeReceive(const Pkt6Ptr& pkt) {
+        to_be_received_.push_back(pkt);
+    }
+
     virtual ~NakedDhcpv6Srv() {
     virtual ~NakedDhcpv6Srv() {
         // Close the lease database
         // Close the lease database
         LeaseMgrFactory::destroy();
         LeaseMgrFactory::destroy();
@@ -75,6 +100,8 @@ public:
     using Dhcpv6Srv::sanityCheck;
     using Dhcpv6Srv::sanityCheck;
     using Dhcpv6Srv::loadServerID;
     using Dhcpv6Srv::loadServerID;
     using Dhcpv6Srv::writeServerID;
     using Dhcpv6Srv::writeServerID;
+
+    list<Pkt6Ptr> to_be_received_;
 };
 };
 
 
 static const char* DUID_FILE = "server-id-test.txt";
 static const char* DUID_FILE = "server-id-test.txt";
@@ -226,6 +253,9 @@ public:
     }
     }
 
 
     virtual ~NakedDhcpv6SrvTest() {
     virtual ~NakedDhcpv6SrvTest() {
+        // Remove all registered hook points
+        ServerHooks::getServerHooks().reset();
+
         // Let's clean up if there is such a file.
         // Let's clean up if there is such a file.
         unlink(DUID_FILE);
         unlink(DUID_FILE);
     };
     };
@@ -1756,6 +1786,85 @@ TEST_F(Dhcpv6SrvTest, ServerID) {
     EXPECT_EQ(duid1_text, text);
     EXPECT_EQ(duid1_text, text);
 }
 }
 
 
+// Checks if hooks are implemented properly.
+TEST_F(Dhcpv6SrvTest, Hooks) {
+    NakedDhcpv6Srv srv(0);
+
+    // check if appropriate hooks are registered
+
+    // check if appropriate indexes are set
+    int hook_index_pkt6_received = ServerHooks::getServerHooks().getIndex("pkt6_receive");
+    int hook_index_select_subnet = ServerHooks::getServerHooks().getIndex("subnet6_select");
+    int hook_index_pkt6_send     = ServerHooks::getServerHooks().getIndex("pkt6_send");
+
+    EXPECT_TRUE(hook_index_pkt6_received > 0);
+    EXPECT_TRUE(hook_index_select_subnet > 0);
+    EXPECT_TRUE(hook_index_pkt6_send > 0);
+}
+
+// This function returns buffer for empty packet (just DHCPv6 header)
+Pkt6* captureEmpty() {
+    Pkt6* pkt;
+    uint8_t data[4];
+    data[0] = 1; // type 1 = SOLICIT
+    data[1] = 0xca; // trans-id = 0xcafe01
+    data[2] = 0xfe;
+    data[3] = 0x01;
+
+    pkt = new Pkt6(data, sizeof(data));
+    pkt->setRemotePort(546);
+    pkt->setRemoteAddr(IOAddress("fe80::1"));
+    pkt->setLocalPort(0);
+    pkt->setLocalAddr(IOAddress("ff02::1:2"));
+    pkt->setIndex(2);
+    pkt->setIface("eth0");
+
+    return (pkt);
+}
+
+string callback_name("");
+
+Pkt6Ptr callback_packet;
+
+int
+pkt6_receive_callout(CalloutHandle& callout_handle) {
+    printf("pkt6_receive_callout called!");
+    callback_name = string("pkt6_receive");
+
+    callout_handle.getArgument("pkt6", callback_packet);
+    return (0);
+}
+
+// Checks if callouts installed on pkt6_received are indeed called
+// Note that the test name does not follow test naming convention,
+// but the proper hook name is "pkt6_receive".
+TEST_F(Dhcpv6SrvTest, Hook_pkt6_receive) {
+
+    // This calls Dhcpv6Srv::ctor, which registers hook names
+    NakedDhcpv6Srv srv(0);
+
+    // Let's pretend there will be 3 libraries
+    CalloutManager callout_mgr(3);
+
+    // Let's pretent we're the library 0
+    EXPECT_NO_THROW(callout_mgr.setLibraryIndex(0));
+
+    EXPECT_NO_THROW( callout_mgr.registerCallout("pkt6_receive", pkt6_receive_callout) );
+
+    // Let's create a REQUEST
+    Pkt6Ptr req = Pkt6Ptr(captureEmpty());
+
+    // Simulate that we have received that traffic
+    srv.fakeReceive(req);
+
+    // Server will now process to run its normal loop, but instead of calling
+    // IfaceMgr::receive6(), it will read all packets from the list set by
+    // fakeReceive()
+    // In particular, it should call registered pkt6_receive callback.
+    srv.run();
+}
+
+
 /// @todo: Add more negative tests for processX(), e.g. extend sanityCheck() test
 /// @todo: Add more negative tests for processX(), e.g. extend sanityCheck() test
 /// to call processX() methods.
 /// to call processX() methods.
 
 

+ 88 - 5
src/lib/dhcpsrv/alloc_engine.cc

@@ -16,11 +16,15 @@
 #include <dhcpsrv/dhcpsrv_log.h>
 #include <dhcpsrv/dhcpsrv_log.h>
 #include <dhcpsrv/lease_mgr_factory.h>
 #include <dhcpsrv/lease_mgr_factory.h>
 
 
+#include <hooks/server_hooks.h>
+#include <hooks/hooks_manager.h>
+
 #include <cstring>
 #include <cstring>
 #include <vector>
 #include <vector>
 #include <string.h>
 #include <string.h>
 
 
 using namespace isc::asiolink;
 using namespace isc::asiolink;
+using namespace isc::hooks;
 
 
 namespace isc {
 namespace isc {
 namespace dhcp {
 namespace dhcp {
@@ -161,6 +165,9 @@ AllocEngine::AllocEngine(AllocType engine_type, unsigned int attempts)
     default:
     default:
         isc_throw(BadValue, "Invalid/unsupported allocation algorithm");
         isc_throw(BadValue, "Invalid/unsupported allocation algorithm");
     }
     }
+
+    // Register hook points
+    hook_index_lease6_select_ = ServerHooks::getServerHooks().registerHook("lease6_select");
 }
 }
 
 
 Lease6Ptr
 Lease6Ptr
@@ -168,7 +175,8 @@ AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
                               const DuidPtr& duid,
                               const DuidPtr& duid,
                               uint32_t iaid,
                               uint32_t iaid,
                               const IOAddress& hint,
                               const IOAddress& hint,
-                              bool fake_allocation /* = false */ ) {
+                              bool fake_allocation,
+                              const isc::hooks::CalloutHandlePtr& callout_handle) {
 
 
     try {
     try {
         // That check is not necessary. We create allocator in AllocEngine
         // That check is not necessary. We create allocator in AllocEngine
@@ -201,7 +209,8 @@ AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
                 /// implemented
                 /// implemented
 
 
                 // the hint is valid and not currently used, let's create a lease for it
                 // the hint is valid and not currently used, let's create a lease for it
-                Lease6Ptr lease = createLease6(subnet, duid, iaid, hint, fake_allocation);
+                Lease6Ptr lease = createLease6(subnet, duid, iaid, hint, callout_handle,
+                                               fake_allocation);
 
 
                 // It can happen that the lease allocation failed (we could have lost
                 // It can happen that the lease allocation failed (we could have lost
                 // the race condition. That means that the hint is lo longer usable and
                 // the race condition. That means that the hint is lo longer usable and
@@ -212,7 +221,7 @@ AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
             } else {
             } else {
                 if (existing->expired()) {
                 if (existing->expired()) {
                     return (reuseExpiredLease(existing, subnet, duid, iaid,
                     return (reuseExpiredLease(existing, subnet, duid, iaid,
-                                              fake_allocation));
+                                              callout_handle, fake_allocation));
                 }
                 }
 
 
             }
             }
@@ -246,7 +255,7 @@ AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
                 // there's no existing lease for selected candidate, so it is
                 // there's no existing lease for selected candidate, so it is
                 // free. Let's allocate it.
                 // free. Let's allocate it.
                 Lease6Ptr lease = createLease6(subnet, duid, iaid, candidate,
                 Lease6Ptr lease = createLease6(subnet, duid, iaid, candidate,
-                                              fake_allocation);
+                                               callout_handle, fake_allocation);
                 if (lease) {
                 if (lease) {
                     return (lease);
                     return (lease);
                 }
                 }
@@ -257,7 +266,7 @@ AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
             } else {
             } else {
                 if (existing->expired()) {
                 if (existing->expired()) {
                     return (reuseExpiredLease(existing, subnet, duid, iaid,
                     return (reuseExpiredLease(existing, subnet, duid, iaid,
-                                              fake_allocation));
+                                              callout_handle, fake_allocation));
                 }
                 }
             }
             }
 
 
@@ -438,6 +447,7 @@ Lease6Ptr AllocEngine::reuseExpiredLease(Lease6Ptr& expired,
                                          const Subnet6Ptr& subnet,
                                          const Subnet6Ptr& subnet,
                                          const DuidPtr& duid,
                                          const DuidPtr& duid,
                                          uint32_t iaid,
                                          uint32_t iaid,
+                                         const isc::hooks::CalloutHandlePtr& callout_handle,
                                          bool fake_allocation /*= false */ ) {
                                          bool fake_allocation /*= false */ ) {
 
 
     if (!expired->expired()) {
     if (!expired->expired()) {
@@ -461,6 +471,42 @@ Lease6Ptr AllocEngine::reuseExpiredLease(Lease6Ptr& expired,
     /// @todo: log here that the lease was reused (there's ticket #2524 for
     /// @todo: log here that the lease was reused (there's ticket #2524 for
     /// logging in libdhcpsrv)
     /// logging in libdhcpsrv)
 
 
+    // Let's execute all callouts registered for lease6_ia_added
+    if (callout_handle &&
+        HooksManager::getHooksManager().calloutsPresent(hook_index_lease6_select_)) {
+
+        // Delete all previous arguments
+        callout_handle->deleteAllArguments();
+
+        // Clear skip flag if it was set in previous callouts
+        callout_handle->setSkip(false);
+
+        // Pass necessary arguments
+
+        // Subnet from which we do the allocation
+        callout_handle->setArgument("subnet6", subnet);
+
+        // Is this solicit (fake = true) or request (fake = false)
+        callout_handle->setArgument("fake_allocation", fake_allocation);
+        callout_handle->setArgument("lease6", expired);
+
+        // This is the first callout, so no need to clear any arguments
+        HooksManager::getHooksManager().callCallouts(hook_index_lease6_select_,
+                                                     *callout_handle);
+
+        // Callouts decided to skip the action. This means that the lease is not
+        // assigned, so the client will get NoAddrAvail as a result. The lease
+        // won't be inserted into the
+        if (callout_handle->getSkip()) {
+            LOG_DEBUG(dhcpsrv_logger, DHCPSRV_HOOKS, DHCPSRV_HOOK_LEASE6_IA_ADD_SKIP);
+            return (Lease6Ptr());
+        }
+
+        // Let's use whatever callout returned. Hopefully it is the same lease
+        // we handled to it.
+        callout_handle->getArgument("lease6", expired);
+    }
+
     if (!fake_allocation) {
     if (!fake_allocation) {
         // for REQUEST we do update the lease
         // for REQUEST we do update the lease
         LeaseMgrFactory::instance().updateLease6(expired);
         LeaseMgrFactory::instance().updateLease6(expired);
@@ -517,12 +563,49 @@ Lease6Ptr AllocEngine::createLease6(const Subnet6Ptr& subnet,
                                     const DuidPtr& duid,
                                     const DuidPtr& duid,
                                     uint32_t iaid,
                                     uint32_t iaid,
                                     const IOAddress& addr,
                                     const IOAddress& addr,
+                                    const isc::hooks::CalloutHandlePtr& callout_handle,
                                     bool fake_allocation /*= false */ ) {
                                     bool fake_allocation /*= false */ ) {
 
 
     Lease6Ptr lease(new Lease6(Lease6::LEASE_IA_NA, addr, duid, iaid,
     Lease6Ptr lease(new Lease6(Lease6::LEASE_IA_NA, addr, duid, iaid,
                                subnet->getPreferred(), subnet->getValid(),
                                subnet->getPreferred(), subnet->getValid(),
                                subnet->getT1(), subnet->getT2(), subnet->getID()));
                                subnet->getT1(), subnet->getT2(), subnet->getID()));
 
 
+    // Let's execute all callouts registered for lease6_ia_added
+    if (callout_handle &&
+        HooksManager::getHooksManager().calloutsPresent(hook_index_lease6_select_)) {
+
+        // Delete all previous arguments
+        callout_handle->deleteAllArguments();
+
+        // Clear skip flag if it was set in previous callouts
+        callout_handle->setSkip(false);
+
+        // Pass necessary arguments
+
+        // Subnet from which we do the allocation
+        callout_handle->setArgument("subnet6", subnet);
+
+        // Is this solicit (fake = true) or request (fake = false)
+        callout_handle->setArgument("fake_allocation", fake_allocation);
+        callout_handle->setArgument("lease6", lease);
+
+        // This is the first callout, so no need to clear any arguments
+        HooksManager::getHooksManager().callCallouts(hook_index_lease6_select_,
+                                                     *callout_handle);
+
+        // Callouts decided to skip the action. This means that the lease is not
+        // assigned, so the client will get NoAddrAvail as a result. The lease
+        // won't be inserted into the
+        if (callout_handle->getSkip()) {
+            LOG_DEBUG(dhcpsrv_logger, DHCPSRV_HOOKS, DHCPSRV_HOOK_LEASE6_IA_ADD_SKIP);
+            return (Lease6Ptr());
+        }
+
+        // Let's use whatever callout returned. Hopefully it is the same lease
+        // we handled to it.
+        callout_handle->getArgument("lease6", lease);
+    }
+
     if (!fake_allocation) {
     if (!fake_allocation) {
         // That is a real (REQUEST) allocation
         // That is a real (REQUEST) allocation
         bool status = LeaseMgrFactory::instance().addLease(lease);
         bool status = LeaseMgrFactory::instance().addLease(lease);

+ 15 - 1
src/lib/dhcpsrv/alloc_engine.h

@@ -20,6 +20,7 @@
 #include <dhcp/hwaddr.h>
 #include <dhcp/hwaddr.h>
 #include <dhcpsrv/subnet.h>
 #include <dhcpsrv/subnet.h>
 #include <dhcpsrv/lease_mgr.h>
 #include <dhcpsrv/lease_mgr.h>
+#include <hooks/callout_handle.h>
 
 
 #include <boost/shared_ptr.hpp>
 #include <boost/shared_ptr.hpp>
 #include <boost/noncopyable.hpp>
 #include <boost/noncopyable.hpp>
@@ -235,13 +236,17 @@ protected:
     /// @param hint a hint that the client provided
     /// @param hint a hint that the client provided
     /// @param fake_allocation is this real i.e. REQUEST (false) or just picking
     /// @param fake_allocation is this real i.e. REQUEST (false) or just picking
     ///        an address for SOLICIT that is not really allocated (true)
     ///        an address for SOLICIT that is not really allocated (true)
+    /// @param callout_handle a callout handle (used in hooks). A lease callouts
+    ///        will be executed if this parameter is passed.
+    ///
     /// @return Allocated IPv6 lease (or NULL if allocation failed)
     /// @return Allocated IPv6 lease (or NULL if allocation failed)
     Lease6Ptr
     Lease6Ptr
     allocateAddress6(const Subnet6Ptr& subnet,
     allocateAddress6(const Subnet6Ptr& subnet,
                      const DuidPtr& duid,
                      const DuidPtr& duid,
                      uint32_t iaid,
                      uint32_t iaid,
                      const isc::asiolink::IOAddress& hint,
                      const isc::asiolink::IOAddress& hint,
-                     bool fake_allocation);
+                     bool fake_allocation,
+                     const isc::hooks::CalloutHandlePtr& callout_handle);
 
 
     /// @brief Destructor. Used during DHCPv6 service shutdown.
     /// @brief Destructor. Used during DHCPv6 service shutdown.
     virtual ~AllocEngine();
     virtual ~AllocEngine();
@@ -276,12 +281,15 @@ private:
     /// @param duid client's DUID
     /// @param duid client's DUID
     /// @param iaid IAID from the IA_NA container the client sent to us
     /// @param iaid IAID from the IA_NA container the client sent to us
     /// @param addr an address that was selected and is confirmed to be available
     /// @param addr an address that was selected and is confirmed to be available
+    /// @param callout_handle a callout handle (used in hooks). A lease callouts
+    ///        will be executed if this parameter is passed.
     /// @param fake_allocation is this real i.e. REQUEST (false) or just picking
     /// @param fake_allocation is this real i.e. REQUEST (false) or just picking
     ///        an address for SOLICIT that is not really allocated (true)
     ///        an address for SOLICIT that is not really allocated (true)
     /// @return allocated lease (or NULL in the unlikely case of the lease just
     /// @return allocated lease (or NULL in the unlikely case of the lease just
     ///        becomed unavailable)
     ///        becomed unavailable)
     Lease6Ptr createLease6(const Subnet6Ptr& subnet, const DuidPtr& duid,
     Lease6Ptr createLease6(const Subnet6Ptr& subnet, const DuidPtr& duid,
                            uint32_t iaid, const isc::asiolink::IOAddress& addr,
                            uint32_t iaid, const isc::asiolink::IOAddress& addr,
+                           const isc::hooks::CalloutHandlePtr& callout_handle,
                            bool fake_allocation = false);
                            bool fake_allocation = false);
 
 
     /// @brief Reuses expired IPv4 lease
     /// @brief Reuses expired IPv4 lease
@@ -313,12 +321,15 @@ private:
     /// @param subnet subnet the lease is allocated from
     /// @param subnet subnet the lease is allocated from
     /// @param duid client's DUID
     /// @param duid client's DUID
     /// @param iaid IAID from the IA_NA container the client sent to us
     /// @param iaid IAID from the IA_NA container the client sent to us
+    /// @param callout_handle a callout handle (used in hooks). A lease callouts
+    ///        will be executed if this parameter is passed.
     /// @param fake_allocation is this real i.e. REQUEST (false) or just picking
     /// @param fake_allocation is this real i.e. REQUEST (false) or just picking
     ///        an address for SOLICIT that is not really allocated (true)
     ///        an address for SOLICIT that is not really allocated (true)
     /// @return refreshed lease
     /// @return refreshed lease
     /// @throw BadValue if trying to recycle lease that is still valid
     /// @throw BadValue if trying to recycle lease that is still valid
     Lease6Ptr reuseExpiredLease(Lease6Ptr& expired, const Subnet6Ptr& subnet,
     Lease6Ptr reuseExpiredLease(Lease6Ptr& expired, const Subnet6Ptr& subnet,
                                 const DuidPtr& duid, uint32_t iaid,
                                 const DuidPtr& duid, uint32_t iaid,
+                                const isc::hooks::CalloutHandlePtr& callout_handle,
                                 bool fake_allocation = false);
                                 bool fake_allocation = false);
 
 
     /// @brief a pointer to currently used allocator
     /// @brief a pointer to currently used allocator
@@ -326,6 +337,9 @@ private:
 
 
     /// @brief number of attempts before we give up lease allocation (0=unlimited)
     /// @brief number of attempts before we give up lease allocation (0=unlimited)
     unsigned int attempts_;
     unsigned int attempts_;
+
+    /// @brief hook name index (used in hooks callouts)
+    int hook_index_lease6_select_;
 };
 };
 
 
 }; // namespace isc::dhcp
 }; // namespace isc::dhcp

+ 5 - 0
src/lib/dhcpsrv/cfgmgr.cc

@@ -262,6 +262,11 @@ void CfgMgr::deleteSubnets6() {
     subnets6_.clear();
     subnets6_.clear();
 }
 }
 
 
+const Subnet6Collection& CfgMgr::getSubnets6() {
+    return (subnets6_);
+}
+
+
 std::string CfgMgr::getDataDir() {
 std::string CfgMgr::getDataDir() {
     return (datadir_);
     return (datadir_);
 }
 }

+ 9 - 0
src/lib/dhcpsrv/cfgmgr.h

@@ -201,6 +201,15 @@ public:
     /// completely new?
     /// completely new?
     void deleteSubnets6();
     void deleteSubnets6();
 
 
+
+    /// @brief returns const reference to all subnets6
+    ///
+    /// This is used in a hook (subnet6_select), where the hook is able
+    /// to choose a different subnet. Server code has to offer a list
+    /// of possible choices (i.e. all subnets).
+    /// @return const reference to Subnet6 collection
+    const Subnet6Collection& getSubnets6();
+
     /// @brief get IPv4 subnet by address
     /// @brief get IPv4 subnet by address
     ///
     ///
     /// Finds a matching subnet, based on an address. This can be used
     /// Finds a matching subnet, based on an address. This can be used

+ 3 - 0
src/lib/dhcpsrv/dhcpsrv_log.h

@@ -50,6 +50,9 @@ const int DHCPSRV_DBG_TRACE_DETAIL = DBGLVL_TRACE_DETAIL;
 /// Record detailed (and verbose) data on the server.
 /// Record detailed (and verbose) data on the server.
 const int DHCPSRV_DBG_TRACE_DETAIL_DATA = DBGLVL_TRACE_DETAIL_DATA;
 const int DHCPSRV_DBG_TRACE_DETAIL_DATA = DBGLVL_TRACE_DETAIL_DATA;
 
 
+// Trace hook related operations
+const int DHCPSRV_HOOKS = DBGLVL_TRACE_BASIC;
+
 ///@}
 ///@}
 
 
 
 

+ 6 - 0
src/lib/dhcpsrv/dhcpsrv_messages.mes

@@ -121,6 +121,12 @@ the database access parameters are changed: in the latter case, the
 server closes the currently open database, and opens a database using
 server closes the currently open database, and opens a database using
 the new parameters.
 the new parameters.
 
 
+% DHCPSRV_HOOK_LEASE6_IA_ADD_SKIP Lease6 (non-temporary) creation was skipped, because of callout skip flag.
+This debug message is printed when a callout installed on lease6_ia_assign
+hook point sets a skip flag. It means that the server was told that no lease6
+should be assigned. Server will not put that lease in its database and the client
+will get a NoAddrsAvail for that IA_NA option.
+
 % DHCPSRV_INVALID_ACCESS invalid database access string: %1
 % DHCPSRV_INVALID_ACCESS invalid database access string: %1
 This is logged when an attempt has been made to parse a database access string
 This is logged when an attempt has been made to parse a database access string
 and the attempt ended in error.  The access string in question - which
 and the attempt ended in error.  The access string in question - which

+ 1 - 0
src/lib/dhcpsrv/tests/Makefile.am

@@ -69,6 +69,7 @@ libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
 libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
 libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
 libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
 libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
 libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
 libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
+libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/hooks/libb10-hooks.la
 libdhcpsrv_unittests_LDADD += $(GTEST_LDADD)
 libdhcpsrv_unittests_LDADD += $(GTEST_LDADD)
 endif
 endif
 
 

+ 16 - 12
src/lib/dhcpsrv/tests/alloc_engine_unittest.cc

@@ -25,6 +25,8 @@
 
 
 #include <dhcpsrv/tests/test_utils.h>
 #include <dhcpsrv/tests/test_utils.h>
 
 
+#include <hooks/callout_handle.h>
+
 #include <boost/shared_ptr.hpp>
 #include <boost/shared_ptr.hpp>
 #include <boost/scoped_ptr.hpp>
 #include <boost/scoped_ptr.hpp>
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
@@ -37,6 +39,7 @@
 using namespace std;
 using namespace std;
 using namespace isc;
 using namespace isc;
 using namespace isc::asiolink;
 using namespace isc::asiolink;
+using namespace isc::hooks;
 using namespace isc::dhcp;
 using namespace isc::dhcp;
 using namespace isc::dhcp::test;
 using namespace isc::dhcp::test;
 
 
@@ -203,7 +206,7 @@ TEST_F(AllocEngine6Test, simpleAlloc6) {
     ASSERT_TRUE(engine);
     ASSERT_TRUE(engine);
 
 
     Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_, IOAddress("::"),
     Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_, IOAddress("::"),
-                                               false);
+                                               false, CalloutHandlePtr());
 
 
     // Check that we got a lease
     // Check that we got a lease
     ASSERT_TRUE(lease);
     ASSERT_TRUE(lease);
@@ -226,7 +229,7 @@ TEST_F(AllocEngine6Test, fakeAlloc6) {
     ASSERT_TRUE(engine);
     ASSERT_TRUE(engine);
 
 
     Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_, IOAddress("::"),
     Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_, IOAddress("::"),
-                                               true);
+                                               true, CalloutHandlePtr());
 
 
     // Check that we got a lease
     // Check that we got a lease
     ASSERT_TRUE(lease);
     ASSERT_TRUE(lease);
@@ -248,7 +251,7 @@ TEST_F(AllocEngine6Test, allocWithValidHint6) {
 
 
     Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_,
     Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_,
                                                IOAddress("2001:db8:1::15"),
                                                IOAddress("2001:db8:1::15"),
-                                               false);
+                                               false, CalloutHandlePtr());
 
 
     // Check that we got a lease
     // Check that we got a lease
     ASSERT_TRUE(lease);
     ASSERT_TRUE(lease);
@@ -286,7 +289,7 @@ TEST_F(AllocEngine6Test, allocWithUsedHint6) {
     // twice.
     // twice.
     Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_,
     Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_,
                                                IOAddress("2001:db8:1::1f"),
                                                IOAddress("2001:db8:1::1f"),
-                                               false);
+                                               false, CalloutHandlePtr());
     // Check that we got a lease
     // Check that we got a lease
     ASSERT_TRUE(lease);
     ASSERT_TRUE(lease);
 
 
@@ -319,7 +322,7 @@ TEST_F(AllocEngine6Test, allocBogusHint6) {
     // with the normal allocation
     // with the normal allocation
     Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_,
     Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_,
                                                IOAddress("3000::abc"),
                                                IOAddress("3000::abc"),
-                                               false);
+                                               false, CalloutHandlePtr());
     // Check that we got a lease
     // Check that we got a lease
     ASSERT_TRUE(lease);
     ASSERT_TRUE(lease);
 
 
@@ -345,12 +348,12 @@ TEST_F(AllocEngine6Test, allocateAddress6Nulls) {
 
 
     // Allocations without subnet are not allowed
     // Allocations without subnet are not allowed
     Lease6Ptr lease = engine->allocateAddress6(Subnet6Ptr(), duid_, iaid_,
     Lease6Ptr lease = engine->allocateAddress6(Subnet6Ptr(), duid_, iaid_,
-                                               IOAddress("::"), false);
+                                               IOAddress("::"), false, CalloutHandlePtr());
     ASSERT_FALSE(lease);
     ASSERT_FALSE(lease);
 
 
     // Allocations without DUID are not allowed either
     // Allocations without DUID are not allowed either
     lease = engine->allocateAddress6(subnet_, DuidPtr(), iaid_,
     lease = engine->allocateAddress6(subnet_, DuidPtr(), iaid_,
-                                     IOAddress("::"), false);
+                                     IOAddress("::"), false, CalloutHandlePtr());
     ASSERT_FALSE(lease);
     ASSERT_FALSE(lease);
 }
 }
 
 
@@ -439,7 +442,7 @@ TEST_F(AllocEngine6Test, smallPool6) {
     cfg_mgr.addSubnet6(subnet_);
     cfg_mgr.addSubnet6(subnet_);
 
 
     Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_, IOAddress("::"),
     Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_, IOAddress("::"),
-                                               false);
+                                               false, CalloutHandlePtr());
 
 
     // Check that we got that single lease
     // Check that we got that single lease
     ASSERT_TRUE(lease);
     ASSERT_TRUE(lease);
@@ -485,7 +488,7 @@ TEST_F(AllocEngine6Test, outOfAddresses6) {
     // There is just a single address in the pool and allocated it to someone
     // There is just a single address in the pool and allocated it to someone
     // else, so the allocation should fail
     // else, so the allocation should fail
     Lease6Ptr lease2 = engine->allocateAddress6(subnet_, duid_, iaid_,
     Lease6Ptr lease2 = engine->allocateAddress6(subnet_, duid_, iaid_,
-                                                IOAddress("::"), false);
+                                                IOAddress("::"), false, CalloutHandlePtr());
     EXPECT_FALSE(lease2);
     EXPECT_FALSE(lease2);
 }
 }
 
 
@@ -519,7 +522,7 @@ TEST_F(AllocEngine6Test, solicitReuseExpiredLease6) {
 
 
     // CASE 1: Asking for any address
     // CASE 1: Asking for any address
     lease = engine->allocateAddress6(subnet_, duid_, iaid_, IOAddress("::"),
     lease = engine->allocateAddress6(subnet_, duid_, iaid_, IOAddress("::"),
-                                     true);
+                                     true, CalloutHandlePtr());
     // Check that we got that single lease
     // Check that we got that single lease
     ASSERT_TRUE(lease);
     ASSERT_TRUE(lease);
     EXPECT_EQ(addr.toText(), lease->addr_.toText());
     EXPECT_EQ(addr.toText(), lease->addr_.toText());
@@ -529,7 +532,7 @@ TEST_F(AllocEngine6Test, solicitReuseExpiredLease6) {
 
 
     // CASE 2: Asking specifically for this address
     // CASE 2: Asking specifically for this address
     lease = engine->allocateAddress6(subnet_, duid_, iaid_, IOAddress(addr.toText()),
     lease = engine->allocateAddress6(subnet_, duid_, iaid_, IOAddress(addr.toText()),
-                                     true);
+                                     true, CalloutHandlePtr());
     // Check that we got that single lease
     // Check that we got that single lease
     ASSERT_TRUE(lease);
     ASSERT_TRUE(lease);
     EXPECT_EQ(addr.toText(), lease->addr_.toText());
     EXPECT_EQ(addr.toText(), lease->addr_.toText());
@@ -563,7 +566,8 @@ TEST_F(AllocEngine6Test, requestReuseExpiredLease6) {
 
 
     // A client comes along, asking specifically for this address
     // A client comes along, asking specifically for this address
     lease = engine->allocateAddress6(subnet_, duid_, iaid_,
     lease = engine->allocateAddress6(subnet_, duid_, iaid_,
-                                     IOAddress(addr.toText()), false);
+                                     IOAddress(addr.toText()), false,
+                                     CalloutHandlePtr());
 
 
     // Check that he got that single lease
     // Check that he got that single lease
     ASSERT_TRUE(lease);
     ASSERT_TRUE(lease);

+ 1 - 0
src/lib/hooks/Makefile.am

@@ -34,6 +34,7 @@ libb10_hooks_la_SOURCES += hooks_log.cc hooks_log.h
 libb10_hooks_la_SOURCES += library_handle.cc library_handle.h
 libb10_hooks_la_SOURCES += library_handle.cc library_handle.h
 libb10_hooks_la_SOURCES += library_manager.cc library_manager.h
 libb10_hooks_la_SOURCES += library_manager.cc library_manager.h
 libb10_hooks_la_SOURCES += server_hooks.cc server_hooks.h
 libb10_hooks_la_SOURCES += server_hooks.cc server_hooks.h
+libb10_hooks_la_SOURCES += hooks_manager.cc hooks_manager.h
 
 
 nodist_libb10_hooks_la_SOURCES = hooks_messages.cc hooks_messages.h
 nodist_libb10_hooks_la_SOURCES = hooks_messages.cc hooks_messages.h
 
 

+ 3 - 0
src/lib/hooks/callout_handle.h

@@ -353,6 +353,9 @@ private:
     bool skip_;
     bool skip_;
 };
 };
 
 
+/// a shared pointer to CalloutHandle object
+typedef boost::shared_ptr<CalloutHandle> CalloutHandlePtr;
+
 } // namespace util
 } // namespace util
 } // namespace isc
 } // namespace isc