Browse Source

[4110a] Ported previous code + a lot of improvements (but still things to do)

Francis Dupont 9 years ago
parent
commit
cbdcd2a80e

+ 35 - 0
src/bin/dhcp4/dhcp4_messages.mes

@@ -198,6 +198,41 @@ detected as a duplicate (i.e. another device in the network is using this addres
 However, the server does not have a record for this address. This may indicate
 However, the server does not have a record for this address. This may indicate
 a client's error or a server's purged database.
 a client's error or a server's purged database.
 
 
+% DHCP4_DHCP4O6_BAD_PACKET received malformed DHCPv4o6 packet: %1
+A malformed DHCPv4o6 packet was received.
+
+% DHCP6_DHCP4O6_PACKET_RECEIVED received DHCPv4o6 packet from DHCPv6 server (type %1) for %2 on interface %3
+This debug message is printed when the server is receiving a DHCPv4o6
+from the DHCPv6 server over inter-process communication.
+
+% DHCP4_DHCP4O6_PACKET_SEND %1: trying to send packet %2 (type %3) to %4 on interface %5 encapsulating %6 %7 (type %8)
+The arguments specify the client identification information (HW address
+and client identifier), DHCP message name and type, source IPv4
+address and port, destination IPv4 address and port and the
+interface name.
+
+% DHCP4_DHCP4O6_PACKET_SEND_FAIL %1: failed to send DHCPv4o6 packet: %2
+This error is output if the IPv4 DHCP server fails to send an
+DHCPv4o6 message to the IPv6 DHCP server. The reason for the
+error is included in the message.
+
+% DHCP4_DHCP4O6_RECEIVE_FAIL failed to receive DHCPv4o6: %1
+This debug message indicates the inter-process communication with the
+DHCPv6 server failed. The reason for the error is included in
+the message.
+
+% DHCP4_DHCP4O6_RECEIVING receiving DHCPv4o6 packet from DHCPv6 server
+This debug message is printed when the server is receiving a DHCPv4o6
+from the DHCPv6 server over inter-process communication socket.
+
+% DHCP4_DHCP4O6_RESPONSE_DATA %1: responding with packet %2 (type %3), packet details: %4
+A debug message including the detailed data about the packet being
+sent to the DHCPv6 server to be forwarded to the client. The first
+argument contains the client and the transaction identification
+information. The second and third argument contains the packet name
+and type respectively. The fourth argument contains detailed packet
+information.
+
 % DHCP4_DISCOVER_CLASS_PROCESSING_FAILED %1: client class specific processing failed for DHCPDISCOVER
 % DHCP4_DISCOVER_CLASS_PROCESSING_FAILED %1: client class specific processing failed for DHCPDISCOVER
 This debug message means that the server processing that is unique for each
 This debug message means that the server processing that is unique for each
 client class has reported a failure. The response packet will not be sent.
 client class has reported a failure. The response packet will not be sent.

+ 30 - 46
src/bin/dhcp4/dhcp4_srv.cc

@@ -70,34 +70,6 @@ using namespace isc::log;
 using namespace isc::stats;
 using namespace isc::stats;
 using namespace std;
 using namespace std;
 
 
-/// Structure that holds registered hook indexes
-struct Dhcp4Hooks {
-    int hook_index_buffer4_receive_;///< index for "buffer4_receive" hook point
-    int hook_index_pkt4_receive_;   ///< index for "pkt4_receive" hook point
-    int hook_index_subnet4_select_; ///< index for "subnet4_select" hook point
-    int hook_index_lease4_release_; ///< index for "lease4_release" hook point
-    int hook_index_pkt4_send_;      ///< index for "pkt4_send" hook point
-    int hook_index_buffer4_send_;   ///< index for "buffer4_send" hook point
-    int hook_index_lease4_decline_; ///< index for "lease4_decline" hook point
-
-    /// Constructor that registers hook points for DHCPv4 engine
-    Dhcp4Hooks() {
-        hook_index_buffer4_receive_= HooksManager::registerHook("buffer4_receive");
-        hook_index_pkt4_receive_   = HooksManager::registerHook("pkt4_receive");
-        hook_index_subnet4_select_ = HooksManager::registerHook("subnet4_select");
-        hook_index_pkt4_send_      = HooksManager::registerHook("pkt4_send");
-        hook_index_lease4_release_ = HooksManager::registerHook("lease4_release");
-        hook_index_buffer4_send_   = HooksManager::registerHook("buffer4_send");
-        hook_index_lease4_decline_ = HooksManager::registerHook("lease4_decline");
-    }
-};
-
-// Declare a Hooks object. As this is outside any function or method, it
-// will be instantiated (and the constructor run) when the module is loaded.
-// As a result, the hook indexes will be defined before any method in this
-// module is called.
-Dhcp4Hooks Hooks;
-
 namespace isc {
 namespace isc {
 namespace dhcp {
 namespace dhcp {
 
 
@@ -330,13 +302,30 @@ Dhcpv4Exchange::setHostIdentifiers() {
     }
     }
 }
 }
 
 
+// Static values so instantiated when the module is loaded.
+// As a result, the hook indexes will be defined before any method in this
+// module is called.
+
+int Dhcpv4Srv::hook_index_buffer4_receive_=
+    HooksManager::registerHook("buffer4_receive");
+int Dhcpv4Srv::hook_index_pkt4_receive_ =
+    HooksManager::registerHook("pkt4_receive");
+int Dhcpv4Srv::hook_index_subnet4_select_ =
+    HooksManager::registerHook("subnet4_select");
+int Dhcpv4Srv::hook_index_pkt4_send_ =
+    HooksManager::registerHook("pkt4_send");
+int Dhcpv4Srv::hook_index_lease4_release_ =
+    HooksManager::registerHook("lease4_release");
+int Dhcpv4Srv::hook_index_buffer4_send_ =
+    HooksManager::registerHook("buffer4_send");
+int Dhcpv4Srv::hook_index_lease4_decline_ =
+    HooksManager::registerHook("lease4_decline");
+
 const std::string Dhcpv4Srv::VENDOR_CLASS_PREFIX("VENDOR_CLASS_");
 const std::string Dhcpv4Srv::VENDOR_CLASS_PREFIX("VENDOR_CLASS_");
 
 
 Dhcpv4Srv::Dhcpv4Srv(uint16_t port, const bool use_bcast,
 Dhcpv4Srv::Dhcpv4Srv(uint16_t port, const bool use_bcast,
                      const bool direct_response_desired)
                      const bool direct_response_desired)
-    : shutdown_(true), alloc_engine_(), port_(port),
-      use_bcast_(use_bcast), hook_index_pkt4_receive_(-1),
-      hook_index_subnet4_select_(-1), hook_index_pkt4_send_(-1) {
+    : shutdown_(true), alloc_engine_(), port_(port), use_bcast_(use_bcast) {
 
 
     LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_OPEN_SOCKET).arg(port);
     LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_OPEN_SOCKET).arg(port);
     try {
     try {
@@ -360,11 +349,6 @@ Dhcpv4Srv::Dhcpv4Srv(uint16_t port, const bool use_bcast,
         alloc_engine_.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 0,
         alloc_engine_.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 0,
                                             false /* false = IPv4 */));
                                             false /* false = IPv4 */));
 
 
-        // Register hook points
-        hook_index_pkt4_receive_   = Hooks.hook_index_pkt4_receive_;
-        hook_index_subnet4_select_ = Hooks.hook_index_subnet4_select_;
-        hook_index_pkt4_send_      = Hooks.hook_index_pkt4_send_;
-
         /// @todo call loadLibraries() when handling configuration changes
         /// @todo call loadLibraries() when handling configuration changes
 
 
     } catch (const std::exception &e) {
     } catch (const std::exception &e) {
@@ -576,7 +560,7 @@ Dhcpv4Srv::selectSubnet4o6(const Pkt4Ptr& query) const {
     // Handle a DHCPv6 relayed query
     // Handle a DHCPv6 relayed query
     Pkt4o6Ptr query4o6 = boost::dynamic_pointer_cast<Pkt4o6>(query);
     Pkt4o6Ptr query4o6 = boost::dynamic_pointer_cast<Pkt4o6>(query);
     if (!query4o6) {
     if (!query4o6) {
-	isc_throw(Unexpected, "Can't get DHCP4o6 message");
+        isc_throw(Unexpected, "Can't get DHCP4o6 message");
     }
     }
     const Pkt6Ptr& query6 = query4o6->getPkt6();
     const Pkt6Ptr& query6 = query4o6->getPkt6();
 
 
@@ -591,7 +575,7 @@ Dhcpv4Srv::selectSubnet4o6(const Pkt4Ptr& query) const {
         }
         }
         selector.interface_id_ =
         selector.interface_id_ =
             query6->getAnyRelayOption(D6O_INTERFACE_ID,
             query6->getAnyRelayOption(D6O_INTERFACE_ID,
-				      Pkt6::RELAY_GET_FIRST);
+                                      Pkt6::RELAY_GET_FIRST);
     }
     }
 
 
     CfgMgr& cfgmgr = CfgMgr::instance();
     CfgMgr& cfgmgr = CfgMgr::instance();
@@ -762,7 +746,7 @@ Dhcpv4Srv::run_one() {
         // Option objects modification does not make sense anymore. Hooks
         // Option objects modification does not make sense anymore. Hooks
         // can only manipulate wire buffer at this stage.
         // can only manipulate wire buffer at this stage.
         // Let's execute all callouts registered for buffer4_send
         // Let's execute all callouts registered for buffer4_send
-        if (HooksManager::calloutsPresent(Hooks.hook_index_buffer4_send_)) {
+        if (HooksManager::calloutsPresent(hook_index_buffer4_send_)) {
             CalloutHandlePtr callout_handle = getCalloutHandle(query);
             CalloutHandlePtr callout_handle = getCalloutHandle(query);
 
 
             // Delete previously set arguments
             // Delete previously set arguments
@@ -772,7 +756,7 @@ Dhcpv4Srv::run_one() {
             callout_handle->setArgument("response4", rsp);
             callout_handle->setArgument("response4", rsp);
 
 
             // Call callouts
             // Call callouts
-            HooksManager::callCallouts(Hooks.hook_index_buffer4_send_,
+            HooksManager::callCallouts(hook_index_buffer4_send_,
                                        *callout_handle);
                                        *callout_handle);
 
 
             // Callouts decided to skip the next processing step. The next
             // Callouts decided to skip the next processing step. The next
@@ -831,7 +815,7 @@ Dhcpv4Srv::processPacket(Pkt4Ptr& query, Pkt4Ptr& rsp) {
 
 
     // The packet has just been received so contains the uninterpreted wire
     // The packet has just been received so contains the uninterpreted wire
     // data; execute callouts registered for buffer4_receive.
     // data; execute callouts registered for buffer4_receive.
-    if (HooksManager::calloutsPresent(Hooks.hook_index_buffer4_receive_)) {
+    if (HooksManager::calloutsPresent(hook_index_buffer4_receive_)) {
         CalloutHandlePtr callout_handle = getCalloutHandle(query);
         CalloutHandlePtr callout_handle = getCalloutHandle(query);
 
 
         // Delete previously set arguments
         // Delete previously set arguments
@@ -841,7 +825,7 @@ Dhcpv4Srv::processPacket(Pkt4Ptr& query, Pkt4Ptr& rsp) {
         callout_handle->setArgument("query4", query);
         callout_handle->setArgument("query4", query);
 
 
         // Call callouts
         // Call callouts
-        HooksManager::callCallouts(Hooks.hook_index_buffer4_receive_,
+        HooksManager::callCallouts(hook_index_buffer4_receive_,
                                    *callout_handle);
                                    *callout_handle);
 
 
         // Callouts decided to skip the next processing step. The next
         // Callouts decided to skip the next processing step. The next
@@ -2091,7 +2075,7 @@ Dhcpv4Srv::processRelease(Pkt4Ptr& release) {
         bool skip = false;
         bool skip = false;
 
 
         // Execute all callouts registered for lease4_release
         // Execute all callouts registered for lease4_release
-        if (HooksManager::calloutsPresent(Hooks.hook_index_lease4_release_)) {
+        if (HooksManager::calloutsPresent(hook_index_lease4_release_)) {
             CalloutHandlePtr callout_handle = getCalloutHandle(release);
             CalloutHandlePtr callout_handle = getCalloutHandle(release);
 
 
             // Delete all previous arguments
             // Delete all previous arguments
@@ -2104,7 +2088,7 @@ Dhcpv4Srv::processRelease(Pkt4Ptr& release) {
             callout_handle->setArgument("lease4", lease);
             callout_handle->setArgument("lease4", lease);
 
 
             // Call all installed callouts
             // Call all installed callouts
-            HooksManager::callCallouts(Hooks.hook_index_lease4_release_,
+            HooksManager::callCallouts(hook_index_lease4_release_,
                                        *callout_handle);
                                        *callout_handle);
 
 
             // Callouts decided to skip the next processing step. The next
             // Callouts decided to skip the next processing step. The next
@@ -2234,7 +2218,7 @@ Dhcpv4Srv::declineLease(const Lease4Ptr& lease, const Pkt4Ptr& decline) {
     // Let's check if there are hooks installed for decline4 hook point.
     // Let's check if there are hooks installed for decline4 hook point.
     // If they are, let's pass the lease and client's packet. If the hook
     // If they are, let's pass the lease and client's packet. If the hook
     // sets status to drop, we reject this Decline.
     // sets status to drop, we reject this Decline.
-    if (HooksManager::calloutsPresent(Hooks.hook_index_lease4_decline_)) {
+    if (HooksManager::calloutsPresent(hook_index_lease4_decline_)) {
         CalloutHandlePtr callout_handle = getCalloutHandle(decline);
         CalloutHandlePtr callout_handle = getCalloutHandle(decline);
 
 
         // Delete previously set arguments
         // Delete previously set arguments
@@ -2245,7 +2229,7 @@ Dhcpv4Srv::declineLease(const Lease4Ptr& lease, const Pkt4Ptr& decline) {
         callout_handle->setArgument("query4", decline);
         callout_handle->setArgument("query4", decline);
 
 
         // Call callouts
         // Call callouts
-        HooksManager::callCallouts(Hooks.hook_index_lease4_decline_,
+        HooksManager::callCallouts(hook_index_lease4_decline_,
                                    *callout_handle);
                                    *callout_handle);
 
 
         // Check if callouts decided to drop the packet. If any of them did,
         // Check if callouts decided to drop the packet. If any of them did,

+ 27 - 6
src/bin/dhcp4/dhcp4_srv.h

@@ -790,6 +790,12 @@ private:
     /// @return Option that contains netmask information
     /// @return Option that contains netmask information
     static OptionPtr getNetmaskOption(const Subnet4Ptr& subnet);
     static OptionPtr getNetmaskOption(const Subnet4Ptr& subnet);
 
 
+    uint16_t port_;  ///< UDP port number on which server listens.
+    bool use_bcast_; ///< Should broadcast be enabled on sockets (if true).
+
+public:
+    /// Class methods and variables for DHCPv4-over-DHCPv6 handler
+
     /// @brief Updates statistics for received packets
     /// @brief Updates statistics for received packets
     /// @param query packet received
     /// @param query packet received
     static void processStatsReceived(const Pkt4Ptr& query);
     static void processStatsReceived(const Pkt4Ptr& query);
@@ -798,13 +804,28 @@ private:
     /// @param query packet transmitted
     /// @param query packet transmitted
     static void processStatsSent(const Pkt4Ptr& response);
     static void processStatsSent(const Pkt4Ptr& response);
 
 
-    uint16_t port_;  ///< UDP port number on which server listens.
-    bool use_bcast_; ///< Should broadcast be enabled on sockets (if true).
-
     /// Indexes for registered hook points
     /// Indexes for registered hook points
-    int hook_index_pkt4_receive_;
-    int hook_index_subnet4_select_;
-    int hook_index_pkt4_send_;
+
+    /// @brief index for "buffer4_receive" hook point
+    static int hook_index_buffer4_receive_;
+
+    /// @brief index for "pkt4_receive" hook point
+    static int hook_index_pkt4_receive_;
+
+    /// @brief index for "subnet4_select" hook point
+    static int hook_index_subnet4_select_;
+
+    /// @brief index for "lease4_release" hook point
+    static int hook_index_lease4_release_;
+
+    /// @brief index for "pkt4_send" hook point
+    static int hook_index_pkt4_send_;
+
+    /// @brief index for "buffer4_send" hook point
+    static int hook_index_buffer4_send_;
+
+    /// @brief index for "lease4_decline" hook point
+    static int hook_index_lease4_decline_;
 };
 };
 
 
 }; // namespace isc::dhcp
 }; // namespace isc::dhcp

+ 116 - 17
src/bin/dhcp4/dhcp4to6_ipc.cc

@@ -8,10 +8,21 @@
 
 
 #include <util/buffer.h>
 #include <util/buffer.h>
 #include <dhcp/iface_mgr.h>
 #include <dhcp/iface_mgr.h>
+#include <dhcp/pkt4.h>
+#include <dhcp/pkt4o6.h>
+#include <dhcp/pkt6.h>
+#include <dhcpsrv/callout_handle_store.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/cfgmgr.h>
+#include <dhcp4/ctrl_dhcp4_srv.h>
 #include <dhcp4/dhcp4to6_ipc.h>
 #include <dhcp4/dhcp4to6_ipc.h>
+#include <dhcp4/dhcp4_log.h>
+#include <hooks/callout_handle.h>
+#include <hooks/hooks_log.h>
+#include <hooks/hooks_manager.h>
 
 
 using namespace std;
 using namespace std;
+using namespace isc::dhcp;
+using namespace isc::hooks;
 
 
 namespace isc {
 namespace isc {
 namespace dhcp {
 namespace dhcp {
@@ -44,13 +55,25 @@ void Dhcp4to6Ipc::open() {
 
 
 void Dhcp4to6Ipc::handler() {
 void Dhcp4to6Ipc::handler() {
     Dhcp4to6Ipc& ipc = Dhcp4to6Ipc::instance();
     Dhcp4to6Ipc& ipc = Dhcp4to6Ipc::instance();
+    Pkt6Ptr pkt;
+
+    try {
+        LOG_DEBUG(packet4_logger, DBG_DHCP4_DETAIL, DHCP4_DHCP4O6_RECEIVING);
+        // Receive message from the IPC socket.
+        pkt = ipc.receive();
+
+        // from Dhcpv4Srv::run_one() after receivePacket()
+        if (pkt) {
+            LOG_DEBUG(packet4_logger, DBG_DHCP4_BASIC, DHCP6_DHCP4O6_PACKET_RECEIVED)
+                .arg(static_cast<int>(pkt->getType()))
+                .arg(pkt->getRemoteAddr().toText())
+                .arg(pkt->getIface());
+        }
+    } catch (const std::exception& e) {
+        LOG_DEBUG(packet4_logger, DBG_DHCP4_DETAIL, DHCP4_DHCP4O6_RECEIVE_FAIL)
+            .arg(e.what());
+    }
 
 
-    // Reset received message in case we return from this method before the
-    // received message pointer is updated.
-    ipc.received_.reset();
-
-    // Receive message from the IPC socket.
-    Pkt6Ptr pkt = ipc.receive();
     if (!pkt) {
     if (!pkt) {
         return;
         return;
     }
     }
@@ -58,25 +81,101 @@ void Dhcp4to6Ipc::handler() {
     // Each message must contain option holding DHCPv4 message.
     // Each message must contain option holding DHCPv4 message.
     OptionCollection msgs = pkt->getOptions(D6O_DHCPV4_MSG);
     OptionCollection msgs = pkt->getOptions(D6O_DHCPV4_MSG);
     if (msgs.empty()) {
     if (msgs.empty()) {
-        isc_throw(Dhcp4o6IpcError, "DHCPv4 message option not present in the"
-                  " DHCPv4o6 message received by the DHCPv4 server");
+        LOG_DEBUG(packet4_logger, DBG_DHCP4_DETAIL, DHCP4_DHCP4O6_BAD_PACKET)
+            .arg("DHCPv4 message option not present");
+        return;
     } else if (msgs.size() > 1) {
     } else if (msgs.size() > 1) {
-        isc_throw(Dhcp4o6IpcError, "expected exactly one DHCPv4 message within"
-                  " DHCPv4 message option received by the DHCPv4 server");
+        LOG_DEBUG(packet4_logger, DBG_DHCP4_DETAIL, DHCP4_DHCP4O6_BAD_PACKET)
+            .arg("more than one DHCPv4 message option");
+        return;
     }
     }
 
 
+    // Get the DHCPv4 message 
     OptionPtr msg = msgs.begin()->second;
     OptionPtr msg = msgs.begin()->second;
     if (!msg) {
     if (!msg) {
-        isc_throw(Dhcp4o6IpcError, "null DHCPv4 message option in the"
-                  " DHCPv4o6 message received by the DHCPv4 server");
+        LOG_DEBUG(packet4_logger, DBG_DHCP4_DETAIL, DHCP4_DHCP4O6_BAD_PACKET)
+            .arg("null DHCPv4 message option");
+        return;
     }
     }
 
 
-    // Record this message.
-    ipc.received_.reset(new Pkt4o6(msg->getData(), pkt));
-}
+    // Extract the DHCPv4 packet with DHCPv6 packet attached
+    Pkt4Ptr query(new Pkt4o6(msg->getData(), pkt));
 
 
-Pkt4o6Ptr& Dhcp4to6Ipc::getReceived() {
-    return (received_);
+    // From Dhcpv4Srv::run_one() processing and after
+    Pkt4Ptr rsp;
+
+    ControlledDhcpv4Srv::getInstance()->processPacket(query, rsp);
+
+    if (!rsp) {
+        return;
+    }
+
+    try {
+        // Now all fields and options are constructed into output wire buffer.
+        // Option objects modification does not make sense anymore. Hooks
+        // can only manipulate wire buffer at this stage.
+        // Let's execute all callouts registered for buffer4_send
+        if (HooksManager::calloutsPresent(Dhcpv4Srv::hook_index_buffer4_send_)) {
+            CalloutHandlePtr callout_handle = getCalloutHandle(query);
+
+            // Delete previously set arguments
+            callout_handle->deleteAllArguments();
+
+            // Pass incoming packet as argument
+            callout_handle->setArgument("response4", rsp);
+
+            // Call callouts
+            HooksManager::callCallouts(Dhcpv4Srv::hook_index_buffer4_send_,
+                                       *callout_handle);
+
+            // Callouts decided to skip the next processing step. The next
+            // processing step would to parse the packet, so skip at this
+            // stage means drop.
+            if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
+                LOG_DEBUG(hooks_logger, DBG_DHCP4_HOOKS,
+                          DHCP4_HOOK_BUFFER_SEND_SKIP)
+                    .arg(rsp->getLabel());
+                return;
+            }
+
+            /// @todo: Add support for DROP status.
+
+            callout_handle->getArgument("response4", rsp);
+        }
+
+        Pkt4o6Ptr rsp6 = boost::dynamic_pointer_cast<Pkt4o6>(rsp);
+        // Should not happen
+        if (!rsp6) {
+            isc_throw(Unexpected, "Dhcp4o6 packet cast fail");
+        }
+
+        LOG_DEBUG(packet4_logger, DBG_DHCP4_BASIC, DHCP4_DHCP4O6_PACKET_SEND)
+            .arg(rsp6->getLabel())
+            .arg(rsp6->getName())
+            .arg(static_cast<int>(rsp6->getType()))
+            .arg(rsp6->getRemoteAddr())
+            .arg(rsp6->getIface())
+            .arg(rsp->getLabel())
+            .arg(rsp->getName())
+            .arg(static_cast<int>(rsp->getType()));
+
+        LOG_DEBUG(packet4_logger, DBG_DHCP4_DETAIL_DATA,
+                  DHCP4_DHCP4O6_RESPONSE_DATA)
+            .arg(rsp6->getLabel())
+            .arg(rsp6->getName())
+            .arg(static_cast<int>(rsp6->getType()))
+            .arg(rsp6->toText());
+
+        ipc.send(rsp6->getPkt6());
+
+        // Update statistics accordingly for sent packet.
+        Dhcpv4Srv::processStatsSent(rsp);
+
+    } catch (const std::exception& e) {
+        LOG_ERROR(packet4_logger, DHCP4_DHCP4O6_PACKET_SEND_FAIL)
+            .arg(rsp->getLabel())
+            .arg(e.what());
+    }
 }
 }
 
 
 };  // namespace dhcp
 };  // namespace dhcp

+ 0 - 10
src/bin/dhcp4/dhcp4to6_ipc.h

@@ -48,16 +48,6 @@ public:
     /// The handler processes the DHCPv4-query DHCPv6 packet and
     /// The handler processes the DHCPv4-query DHCPv6 packet and
     /// sends the DHCPv4-response DHCPv6 packet back to the DHCPv6 server
     /// sends the DHCPv4-response DHCPv6 packet back to the DHCPv6 server
     static void handler();
     static void handler();
-
-    /// @brief Returns last received packet
-    ///
-    /// @return a reference to a shared pointer to the last received packet
-    /// @note This reference should be cleared after use
-    Pkt4o6Ptr& getReceived();
-
-private:
-    /// @brief last received packet
-    Pkt4o6Ptr received_;
 };
 };
 
 
 } // namespace isc
 } // namespace isc

+ 158 - 9
src/bin/dhcp4/tests/dhcp4to6_ipc_unittest.cc

@@ -5,13 +5,19 @@
 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 
 #include <config.h>
 #include <config.h>
+
 #include <asiolink/io_address.h>
 #include <asiolink/io_address.h>
 #include <dhcp/pkt4o6.h>
 #include <dhcp/pkt4o6.h>
 #include <dhcp/pkt6.h>
 #include <dhcp/pkt6.h>
 #include <dhcp/tests/iface_mgr_test_config.h>
 #include <dhcp/tests/iface_mgr_test_config.h>
+#include <dhcp4/ctrl_dhcp4_srv.h>
 #include <dhcp4/dhcp4to6_ipc.h>
 #include <dhcp4/dhcp4to6_ipc.h>
+#include <dhcp4/tests/dhcp4_test_utils.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/testutils/dhcp4o6_test_ipc.h>
 #include <dhcpsrv/testutils/dhcp4o6_test_ipc.h>
+#include <hooks/callout_handle.h>
+#include <hooks/hooks_manager.h>
+
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
 #include <stdint.h>
 #include <stdint.h>
 
 
@@ -19,6 +25,7 @@ using namespace isc;
 using namespace isc::asiolink;
 using namespace isc::asiolink;
 using namespace isc::dhcp;
 using namespace isc::dhcp;
 using namespace isc::dhcp::test;
 using namespace isc::dhcp::test;
+using namespace isc::hooks;
 using namespace isc::util;
 using namespace isc::util;
 
 
 namespace {
 namespace {
@@ -30,7 +37,7 @@ const uint16_t TEST_PORT = 32000;
 typedef Dhcp4o6TestIpc TestIpc;
 typedef Dhcp4o6TestIpc TestIpc;
 
 
 /// @brief Test fixture class for DHCPv4 endpoint of DHCPv4o6 IPC.
 /// @brief Test fixture class for DHCPv4 endpoint of DHCPv4o6 IPC.
-class Dhcp4to6IpcTest : public ::testing::Test {
+class Dhcp4to6IpcTest : public Dhcpv4SrvTest {
 public:
 public:
 
 
     /// @brief Constructor
     /// @brief Constructor
@@ -38,8 +45,16 @@ public:
     /// Configures IPC to use a test port. It also provides a fake
     /// Configures IPC to use a test port. It also provides a fake
     /// configuration of interfaces.
     /// configuration of interfaces.
     Dhcp4to6IpcTest()
     Dhcp4to6IpcTest()
-        : iface_mgr_test_config_(true) {
+	: Dhcpv4SrvTest(),
+	iface_mgr_test_config_(true) {
         configurePort(TEST_PORT);
         configurePort(TEST_PORT);
+	// Install buffer4_receive_callout
+	EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().
+			registerCallout("buffer4_receive",
+					buffer4_receive_callout));
+        // Install buffer4_send_callout
+        EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().
+                        registerCallout("buffer4_send", buffer4_send_callout));
     }
     }
 
 
     /// @brief Configure DHCP4o6 port.
     /// @brief Configure DHCP4o6 port.
@@ -55,6 +70,42 @@ public:
     /// @return Pointer to the instance of the DHCPv4-query Message option.
     /// @return Pointer to the instance of the DHCPv4-query Message option.
     OptionPtr createDHCPv4MsgOption() const;
     OptionPtr createDHCPv4MsgOption() const;
 
 
+    /// @brief Handler for the buffer4_receive hook
+    ///
+    /// This hook is at the beginning of processPacket
+    ///
+    /// @param callout_handle handle passed by the hooks framework
+    /// @return always 0
+    static int
+        buffer4_receive_callout(CalloutHandle& callout_handle) {
+        callout_handle.getArgument("query4", callback_recv_pkt_);
+        return (0);
+    }
+
+    /// @brief Handler for the buffer4_send hook
+    ///
+    /// This hook is at the end of the DHCPv4o6 packet handler
+    ///
+    /// @param callout_handle handle passed by the hooks framework
+    /// @return always 0
+    static int
+        buffer4_send_callout(CalloutHandle& callout_handle) {
+        callout_handle.getArgument("response4", callback_sent_pkt_);
+        return (0);
+    }
+
+    /// @brief Response Pkt4 shared pointer returned in the receive callout
+    static Pkt4Ptr callback_recv_pkt_;
+
+    /// @brief Response Pkt4 shared pointer returned in the send callout
+    static Pkt4Ptr callback_sent_pkt_;
+
+    /// @brief reference to a controlled server
+    ///
+    /// Dhcp4to6Ipc::handler() uses the instance of the controlled server
+    /// so it has to be build. This reference does this.
+    ControlledDhcpv4Srv srv;
+
 private:
 private:
 
 
     /// @brief Provides fake configuration of interfaces.
     /// @brief Provides fake configuration of interfaces.
@@ -62,6 +113,9 @@ private:
 
 
 };
 };
 
 
+Pkt4Ptr Dhcp4to6IpcTest::callback_recv_pkt_;
+Pkt4Ptr Dhcp4to6IpcTest::callback_sent_pkt_;
+
 void
 void
 Dhcp4to6IpcTest::configurePort(uint16_t port) {
 Dhcp4to6IpcTest::configurePort(uint16_t port) {
     CfgMgr::instance().getStagingCfg()->setDhcp4o6Port(port);
     CfgMgr::instance().getStagingCfg()->setDhcp4o6Port(port);
@@ -94,6 +148,11 @@ TEST_F(Dhcp4to6IpcTest, invalidPortError) {
 // This test verifies that the DHCPv4 endpoint of the DHCPv4o6 IPC can
 // This test verifies that the DHCPv4 endpoint of the DHCPv4o6 IPC can
 // receive messages.
 // receive messages.
 TEST_F(Dhcp4to6IpcTest, receive) {
 TEST_F(Dhcp4to6IpcTest, receive) {
+    // Verify we have a controlled server
+    ControlledDhcpv4Srv* srv = NULL;
+    ASSERT_NO_THROW(srv = ControlledDhcpv4Srv::getInstance());
+    ASSERT_TRUE(srv);
+
     // Create instance of the IPC endpoint under test.
     // Create instance of the IPC endpoint under test.
     Dhcp4to6Ipc& ipc = Dhcp4to6Ipc::instance();
     Dhcp4to6Ipc& ipc = Dhcp4to6Ipc::instance();
     // Create instance of the IPC endpoint being used as a source of messages.
     // Create instance of the IPC endpoint being used as a source of messages.
@@ -110,12 +169,20 @@ TEST_F(Dhcp4to6IpcTest, receive) {
     pkt->setRemoteAddr(IOAddress("2001:db8:1::123"));
     pkt->setRemoteAddr(IOAddress("2001:db8:1::123"));
     ASSERT_NO_THROW(pkt->pack());
     ASSERT_NO_THROW(pkt->pack());
 
 
+    // Reset the received packet
+    Dhcp4to6IpcTest::callback_recv_pkt_.reset();
+
     // Send and wait up to 1 second to receive it.
     // Send and wait up to 1 second to receive it.
     ASSERT_NO_THROW(src_ipc.send(pkt));
     ASSERT_NO_THROW(src_ipc.send(pkt));
     ASSERT_NO_THROW(IfaceMgr::instance().receive6(1, 0));
     ASSERT_NO_THROW(IfaceMgr::instance().receive6(1, 0));
 
 
     // Make sure that the message has been received.
     // Make sure that the message has been received.
-    Pkt4o6Ptr pkt_received = ipc.getReceived();
+    // The buffer4_receive hook is at the beginning of processPacket
+    // so this proves it was passed to it.
+    Pkt4Ptr pkt4_received = Dhcp4to6IpcTest::callback_recv_pkt_;
+    ASSERT_TRUE(pkt4_received);
+    Pkt4o6Ptr pkt_received =
+        boost::dynamic_pointer_cast<Pkt4o6>(pkt4_received);
     ASSERT_TRUE(pkt_received);
     ASSERT_TRUE(pkt_received);
     Pkt6Ptr pkt6_received = pkt_received->getPkt6();
     Pkt6Ptr pkt6_received = pkt_received->getPkt6();
     ASSERT_TRUE(pkt6_received);
     ASSERT_TRUE(pkt6_received);
@@ -126,6 +193,11 @@ TEST_F(Dhcp4to6IpcTest, receive) {
 // This test verifies that message with multiple DHCPv4 query options
 // This test verifies that message with multiple DHCPv4 query options
 // is rejected.
 // is rejected.
 TEST_F(Dhcp4to6IpcTest, receiveMultipleQueries) {
 TEST_F(Dhcp4to6IpcTest, receiveMultipleQueries) {
+    // Verify we have a controlled server
+    ControlledDhcpv4Srv* srv = NULL;
+    ASSERT_NO_THROW(srv = ControlledDhcpv4Srv::getInstance());
+    ASSERT_TRUE(srv);
+
     // Create instance of the IPC endpoint under test.
     // Create instance of the IPC endpoint under test.
     Dhcp4to6Ipc& ipc = Dhcp4to6Ipc::instance();
     Dhcp4to6Ipc& ipc = Dhcp4to6Ipc::instance();
     // Create instance of the IPC endpoint being used as a source of messages.
     // Create instance of the IPC endpoint being used as a source of messages.
@@ -144,14 +216,25 @@ TEST_F(Dhcp4to6IpcTest, receiveMultipleQueries) {
     pkt->setRemoteAddr(IOAddress("2001:db8:1::123"));
     pkt->setRemoteAddr(IOAddress("2001:db8:1::123"));
     ASSERT_NO_THROW(pkt->pack());
     ASSERT_NO_THROW(pkt->pack());
 
 
-    // Send message.
+    // Reset the received packet
+    Dhcp4to6IpcTest::callback_recv_pkt_.reset();
+
+    // Send and wait up to 1 second to receive it.
     ASSERT_NO_THROW(src_ipc.send(pkt));
     ASSERT_NO_THROW(src_ipc.send(pkt));
-    // Reception handler should throw exception.
-    EXPECT_THROW(IfaceMgr::instance().receive6(1, 0), Dhcp4o6IpcError);
+    EXPECT_NO_THROW(IfaceMgr::instance().receive6(1, 0));
+
+    // No message should has been sent.
+    Pkt4Ptr pkt4_received = Dhcp4to6IpcTest::callback_recv_pkt_;
+    EXPECT_FALSE(pkt4_received);
 }
 }
 
 
 // This test verifies that message with no DHCPv4 query options is rejected.
 // This test verifies that message with no DHCPv4 query options is rejected.
 TEST_F(Dhcp4to6IpcTest, receiveNoQueries) {
 TEST_F(Dhcp4to6IpcTest, receiveNoQueries) {
+    // Verify we have a controlled server
+    ControlledDhcpv4Srv* srv = NULL;
+    ASSERT_NO_THROW(srv = ControlledDhcpv4Srv::getInstance());
+    ASSERT_TRUE(srv);
+
     // Create instance of the IPC endpoint under test.
     // Create instance of the IPC endpoint under test.
     Dhcp4to6Ipc& ipc = Dhcp4to6Ipc::instance();
     Dhcp4to6Ipc& ipc = Dhcp4to6Ipc::instance();
     // Create instance of the IPC endpoint being used as a source of messages.
     // Create instance of the IPC endpoint being used as a source of messages.
@@ -167,10 +250,76 @@ TEST_F(Dhcp4to6IpcTest, receiveNoQueries) {
     pkt->setRemoteAddr(IOAddress("2001:db8:1::123"));
     pkt->setRemoteAddr(IOAddress("2001:db8:1::123"));
     ASSERT_NO_THROW(pkt->pack());
     ASSERT_NO_THROW(pkt->pack());
 
 
-    // Send message.
+    // Reset the received packet
+    Dhcp4to6IpcTest::callback_recv_pkt_.reset();
+
+    // Send and wait up to 1 second to receive it.
+    ASSERT_NO_THROW(src_ipc.send(pkt));
+    EXPECT_NO_THROW(IfaceMgr::instance().receive6(1, 0));
+
+    // No message should has been sent.
+    Pkt4Ptr pkt4_received = Dhcp4to6IpcTest::callback_recv_pkt_;
+    EXPECT_FALSE(pkt4_received);
+}
+
+// This test verifies that the DHCPv4 endpoint of the DHCPv4o6 IPC can
+// process messages.
+TEST_F(Dhcp4to6IpcTest, process) {
+    // Verify we have a controlled server
+    ControlledDhcpv4Srv* srv = NULL;
+    ASSERT_NO_THROW(srv = ControlledDhcpv4Srv::getInstance());
+    ASSERT_TRUE(srv);
+
+    // Create instance of the IPC endpoint under test.
+    Dhcp4to6Ipc& ipc = Dhcp4to6Ipc::instance();
+    // Create instance of the IPC endpoint being used as a source of messages.
+    TestIpc src_ipc(TEST_PORT, TestIpc::ENDPOINT_TYPE_V6);
+
+    // Open both endpoints.
+    ASSERT_NO_THROW(ipc.open());
+    ASSERT_NO_THROW(src_ipc.open());
+
+    // Create message to be sent over IPC.
+    Pkt6Ptr pkt(new Pkt6(DHCPV6_DHCPV4_QUERY, 1234));
+    pkt->addOption(createDHCPv4MsgOption());
+    pkt->setIface("eth0");
+    pkt->setRemoteAddr(IOAddress("2001:db8:1::123"));
+    ASSERT_NO_THROW(pkt->pack());
+
+    // TODO: put enough in the packet and server config to make it pass
+    // through processPacket, in particular provide a subnet to select
+
+    // Reset the received packet
+    Dhcp4to6IpcTest::callback_recv_pkt_.reset();
+
+    // Send and wait up to 1 second to receive it.
     ASSERT_NO_THROW(src_ipc.send(pkt));
     ASSERT_NO_THROW(src_ipc.send(pkt));
-    // Reception handler should throw exception.
-    EXPECT_THROW(IfaceMgr::instance().receive6(1, 0), Dhcp4o6IpcError);
+    ASSERT_NO_THROW(IfaceMgr::instance().receive6(1, 0));
+
+    // Make sure that the message has been received.
+    Pkt4Ptr pkt4_received = Dhcp4to6IpcTest::callback_recv_pkt_;
+    ASSERT_TRUE(pkt4_received);
+    Pkt4o6Ptr pkt_received =
+        boost::dynamic_pointer_cast<Pkt4o6>(pkt4_received);
+    ASSERT_TRUE(pkt_received);
+    Pkt6Ptr pkt6_received = pkt_received->getPkt6();
+    ASSERT_TRUE(pkt6_received);
+    EXPECT_EQ("eth0", pkt6_received->getIface());
+    EXPECT_EQ("2001:db8:1::123", pkt6_received->getRemoteAddr().toText());
+
+    // Make sure that the message has been processed.
+    // Using the buffer4_send hook
+    Pkt4Ptr pkt4_sent = Dhcp4to6IpcTest::callback_sent_pkt_;
+#if 0
+    ASSERT_TRUE(pkt4_sent);
+    Pkt4o6Ptr pkt_sent = boost::dynamic_pointer_cast<Pkt4o6>(pkt4_sent);
+    ASSERT_TRUE(pkt_sent);
+    Pkt6Ptr pkt6_sent = pkt_sent->getPkt6();
+    ASSERT_TRUE(pkt6_sent);
+    EXPECT_EQ("eth0", pkt6_sent->getIface());
+    EXPECT_EQ("2001:db8:1::123", pkt6_sent->getRemoteAddr().toText());
+    // more tests
+#endif
 }
 }
 
 
 } // end of anonymous namespace
 } // end of anonymous namespace