Browse Source

[4106] Ported DHCPv4-over-DHCPv6 IPC code from fd4o6 private branch

Francis Dupont 9 years ago
parent
commit
2192caa2c2

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

@@ -62,6 +62,7 @@ libdhcp4_la_SOURCES += ctrl_dhcp4_srv.cc ctrl_dhcp4_srv.h
 libdhcp4_la_SOURCES += json_config_parser.cc json_config_parser.h
 libdhcp4_la_SOURCES += dhcp4_log.cc dhcp4_log.h
 libdhcp4_la_SOURCES += dhcp4_srv.cc dhcp4_srv.h
+libdhcp4_la_SOURCES += dhcp4_dhcp4o6_ipc.cc dhcp4_dhcp4o6_ipc.h
 
 libdhcp4_la_SOURCES += kea_controller.cc
 

+ 11 - 0
src/bin/dhcp4/ctrl_dhcp4_srv.cc

@@ -8,6 +8,7 @@
 #include <cc/data.h>
 #include <dhcp4/ctrl_dhcp4_srv.h>
 #include <dhcp4/dhcp4_log.h>
+#include <dhcp4/dhcp4_dhcp4o6_ipc.h>
 #include <hooks/hooks_manager.h>
 #include <dhcp4/json_config_parser.h>
 #include <dhcpsrv/cfgmgr.h>
@@ -189,6 +190,16 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) {
         return (isc::config::createAnswer(1, err.str()));
     }
 
+    // Setup DHCPv4-over-DHCPv6 IPC
+    try {
+        Dhcp4o6Ipc::instance().open();
+    } catch (const std::exception& ex) {
+        std::ostringstream err;
+        err << "error starting DHCPv4-over-DHCPv6 IPC "
+               " after server reconfiguration: " << ex.what();
+        return (isc::config::createAnswer(1, err.str()));
+    }
+
     // Configuration may change active interfaces. Therefore, we have to reopen
     // sockets according to new configuration. It is possible that this
     // operation will fail for some interfaces but the openSockets function

+ 76 - 0
src/bin/dhcp4/dhcp4_dhcp4o6_ipc.cc

@@ -0,0 +1,76 @@
+// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+
+#include <util/buffer.h>
+#include <dhcp/iface_mgr.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcp4/dhcp4_dhcp4o6_ipc.h>
+
+using namespace std;
+
+namespace isc {
+namespace dhcp {
+
+Dhcp4o6Ipc::Dhcp4o6Ipc() : Dhcp4o6IpcBase() {}
+
+Dhcp4o6Ipc& Dhcp4o6Ipc::instance() {
+    static Dhcp4o6Ipc dhcp4o6_ipc;
+    return (dhcp4o6_ipc);
+}
+
+void Dhcp4o6Ipc::open() {
+    uint32_t port = CfgMgr::instance().getStagingCfg()->getDhcp4o6Port();
+    if (port == 0) {
+        Dhcp4o6IpcBase::close();
+        return;
+    }
+    if (port > 65534) {
+        isc_throw(OutOfRange, "DHCP4o6 port " << port);
+    }
+
+    int old_fd = socket_fd_;
+    socket_fd_ = Dhcp4o6IpcBase::open(static_cast<uint16_t>(port), 4);
+    if ((old_fd == -1) && (socket_fd_ != old_fd)) {
+        IfaceMgr::instance().addExternalSocket(socket_fd_, Dhcp4o6Ipc::handler);
+    }
+}
+
+void Dhcp4o6Ipc::handler() {
+    Dhcp4o6Ipc& ipc = Dhcp4o6Ipc::instance();
+    Pkt6Ptr pkt = ipc.receive();
+    if (!pkt) {
+        return;
+    }
+
+    pkt->unpack();
+    OptionCollection msgs = pkt->getOptions(D6O_DHCPV4_MSG);
+    if (msgs.size() != 1) {
+        return;
+    }
+    OptionPtr msg = pkt->getOption(D6O_DHCPV4_MSG);
+    if (!msg) {
+        isc_throw(Unexpected, "Can't get DHCPv4 message option");
+    }
+    instance().received_.reset(new Pkt4o6(msg->getData(), pkt));
+}
+
+Pkt4o6Ptr& Dhcp4o6Ipc::getReceived() {
+    return (received_);
+}
+
+};  // namespace dhcp
+
+};  // namespace isc

+ 74 - 0
src/bin/dhcp4/dhcp4_dhcp4o6_ipc.h

@@ -0,0 +1,74 @@
+// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef DHCP4_DHCP4O6_IPC_H
+#define DHCP4_DHCP4O6_IPC_H
+
+/// @file dhcp4_dhcp4o6_ipc.h Defines the Dhcp4o6Ipc class.
+/// This file defines the class Kea uses to act as the DHCPv4 server
+/// side of DHCPv4-over-DHCPv6 communication between servers.
+///
+
+#include <dhcp/pkt4o6.h>
+#include <dhcpsrv/dhcp4o6_ipc.h>
+#include <boost/shared_ptr.hpp>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Handles DHCPv4-over-DHCPv6 IPC on the DHCPv4 server side
+class Dhcp4o6Ipc : public Dhcp4o6IpcBase {
+protected:
+    /// @brief Constructor
+    ///
+    /// Default constructor
+    Dhcp4o6Ipc();
+
+    /// @brief Destructor.
+    virtual ~Dhcp4o6Ipc() { }
+
+public:
+    /// @brief Returns pointer to the sole instance of Dhcp4o6Ipc
+    ///
+    /// Dhcp4o6Ipc is a singleton class
+    ///
+    /// @return the only existing instance of DHCP4o6 IPC
+    static Dhcp4o6Ipc& instance();
+
+    /// @brief Open communication socket
+    ///
+    /// Call base open method and sets the handler/callback when needed
+    virtual void open();
+
+    /// @brief On receive handler
+    ///
+    /// The handler processes the DHCPv4-query DHCPv6 packet and
+    /// sends the DHCPv4-response DHCPv6 packet back to the DHCPv6 server
+    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 dhcp
+
+#endif

+ 13 - 2
src/bin/dhcp4/json_config_parser.cc

@@ -415,9 +415,10 @@ DhcpConfigParser* createGlobalDhcp4ConfigParser(const std::string& config_id,
     if ((config_id.compare("valid-lifetime") == 0)  ||
         (config_id.compare("renew-timer") == 0)  ||
         (config_id.compare("rebind-timer") == 0) ||
-        (config_id.compare("decline-probation-period") == 0) )  {
+        (config_id.compare("decline-probation-period") == 0) ||
+        (config_id.compare("dhcp4o6-port") == 0) )  {
         parser = new Uint32Parser(config_id,
-                                 globalContext()->uint32_values_);
+                                  globalContext()->uint32_values_);
     } else if (config_id.compare("interfaces-config") == 0) {
         parser = new IfacesConfigParser4();
     } else if (config_id.compare("subnet4") == 0) {
@@ -465,6 +466,7 @@ DhcpConfigParser* createGlobalDhcp4ConfigParser(const std::string& config_id,
 ///
 /// - echo-client-id
 /// - decline-probation-period
+/// - dhcp4o6-port
 void setGlobalParameters4() {
     // Although the function is modest for now, it is certain that the number
     // of global switches will increase over time, hence the name.
@@ -487,6 +489,15 @@ void setGlobalParameters4() {
     } catch (...) {
         // That's not really needed.
     }
+
+    // Set the DHCPv4-over-DHCPv6 interserver port.
+    try {
+        uint32_t dhcp4o6_port = globalContext()->uint32_values_
+            ->getOptionalParam("dhcp4o6-port", 0);
+        CfgMgr::instance().getStagingCfg()->setDhcp4o6Port(dhcp4o6_port);
+    } catch (...) {
+        // Ignore errors. This flag is optional
+    }
 }
 
 isc::data::ConstElementPtr

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

@@ -63,6 +63,7 @@ libdhcp6_la_SOURCES += dhcp6_log.cc dhcp6_log.h
 libdhcp6_la_SOURCES += dhcp6_srv.cc dhcp6_srv.h
 libdhcp6_la_SOURCES += ctrl_dhcp6_srv.cc ctrl_dhcp6_srv.h
 libdhcp6_la_SOURCES += json_config_parser.cc json_config_parser.h
+libdhcp6_la_SOURCES += dhcp6_dhcp4o6_ipc.cc dhcp6_dhcp4o6_ipc.h
 
 libdhcp6_la_SOURCES += kea_controller.cc
 

+ 11 - 0
src/bin/dhcp6/ctrl_dhcp6_srv.cc

@@ -11,6 +11,7 @@
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/cfg_db_access.h>
 #include <dhcp6/ctrl_dhcp6_srv.h>
+#include <dhcp6/dhcp6_dhcp4o6_ipc.h>
 #include <dhcp6/dhcp6_log.h>
 #include <dhcp6/json_config_parser.h>
 #include <hooks/hooks_manager.h>
@@ -213,6 +214,16 @@ ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) {
         return (isc::config::createAnswer(1, err.str()));
     }
 
+    // Setup DHCPv4-over-DHCPv6 IPC
+    try {
+        Dhcp4o6Ipc::instance().open();
+    } catch (const std::exception& ex) {
+        std::ostringstream err;
+        err << "error starting DHCPv4-over-DHCPv6 IPC "
+               " after server reconfiguration: " << ex.what();
+        return (isc::config::createAnswer(1, err.str()));
+    }
+
     // Configuration may change active interfaces. Therefore, we have to reopen
     // sockets according to new configuration. It is possible that this
     // operation will fail for some interfaces but the openSockets function

+ 71 - 0
src/bin/dhcp6/dhcp6_dhcp4o6_ipc.cc

@@ -0,0 +1,71 @@
+// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+
+#include <util/buffer.h>
+#include <dhcp/iface_mgr.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcp6/dhcp6_dhcp4o6_ipc.h>
+
+using namespace std;
+
+namespace isc {
+namespace dhcp {
+
+Dhcp4o6Ipc::Dhcp4o6Ipc() : Dhcp4o6IpcBase() {}
+
+Dhcp4o6Ipc& Dhcp4o6Ipc::instance() {
+    static Dhcp4o6Ipc dhcp4o6_ipc;
+    return (dhcp4o6_ipc);
+}
+
+void Dhcp4o6Ipc::open() {
+    uint32_t port = CfgMgr::instance().getStagingCfg()->getDhcp4o6Port();
+    if (port == 0) {
+        Dhcp4o6IpcBase::close();
+        return;
+    }
+    if (port > 65534) {
+        isc_throw(OutOfRange, "DHCP4o6 port " << port);
+    }
+
+    int old_fd = socket_fd_;
+    socket_fd_ = Dhcp4o6IpcBase::open(static_cast<uint16_t>(port), 6);
+    if ((old_fd == -1) && (socket_fd_ != old_fd)) {
+        IfaceMgr::instance().addExternalSocket(socket_fd_, Dhcp4o6Ipc::handler);
+    }
+}
+
+void Dhcp4o6Ipc::handler() {
+    Dhcp4o6Ipc& ipc = Dhcp4o6Ipc::instance();
+    Pkt6Ptr pkt = ipc.receive();
+    if (!pkt) {
+        return;
+    }
+    isc::util::OutputBuffer& buf = pkt->getBuffer();
+    pkt->repack();
+    uint8_t msg_type = buf[0];
+    if ((msg_type == DHCPV6_RELAY_FORW) || (msg_type == DHCPV6_RELAY_REPL)) {
+        pkt->setRemotePort(DHCP6_SERVER_PORT);
+    } else {
+        pkt->setRemotePort(DHCP6_CLIENT_PORT);
+    }
+    IfaceMgr::instance().send(pkt);
+    // processStatsSent(pkt);
+}
+
+};  // namespace dhcp
+
+};  // namespace isc

+ 61 - 0
src/bin/dhcp6/dhcp6_dhcp4o6_ipc.h

@@ -0,0 +1,61 @@
+// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef DHCP6_DHCP4O6_IPC_H
+#define DHCP6_DHCP4O6_IPC_H
+
+/// @file dhcp6_dhcp4o6_ipc.h Defines the Dhcp4o6Ipc class.
+/// This file defines the class Kea uses to act as the DHCPv6 server
+/// side of DHCPv4-over-DHCPv6 communication between servers.
+///
+#include <dhcpsrv/dhcp4o6_ipc.h>
+#include <boost/shared_ptr.hpp>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Handles DHCPv4-over-DHCPv6 IPC on the DHCPv6 server side
+class Dhcp4o6Ipc : public Dhcp4o6IpcBase {
+protected:
+    /// @brief Constructor
+    ///
+    /// Default constructor
+    Dhcp4o6Ipc();
+
+    /// @brief Destructor.
+    virtual ~Dhcp4o6Ipc() { }
+
+public:
+    /// @brief Returns pointer to the sole instance of Dhcp4o6Ipc
+    ///
+    /// Dhcp4o6Ipc is a singleton class
+    ///
+    /// @return the only existing instance of DHCP4o6 IPC
+    static Dhcp4o6Ipc& instance();
+
+    /// @brief Open communication socket
+    ///
+    /// Call base open method and sets the handler/callback when needed
+    virtual void open();
+
+    /// @brief On receive handler
+    ///
+    /// The handler sends the DHCPv6 packet back to the remote address
+    static void handler();
+};
+
+} // namespace isc
+} // namespace dhcp
+
+#endif

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

@@ -25,6 +25,7 @@
 #include <dhcp/option_vendor_class.h>
 #include <dhcp/option_int_array.h>
 #include <dhcp/pkt6.h>
+#include <dhcp6/dhcp6_dhcp4o6_ipc.h>
 #include <dhcp6/dhcp6_log.h>
 #include <dhcp6/dhcp6_srv.h>
 #include <dhcpsrv/callout_handle_store.h>
@@ -211,6 +212,13 @@ Dhcpv6Srv::~Dhcpv6Srv() {
         LOG_ERROR(dhcp6_logger, DHCP6_SRV_D2STOP_ERROR).arg(ex.what());
     }
 
+    try {
+        Dhcp4o6Ipc::instance().close();
+    } catch(const std::exception& ex) {
+        // Highly unlikely, but lets Report it but go on
+        // LOG_ERROR(dhcp6_logger, DHCP6_SRV_DHCP4O6_ERROR).arg(ex.what());
+    }
+
     IfaceMgr::instance().closeSockets();
 
     LeaseMgrFactory::destroy();

+ 12 - 1
src/bin/dhcp6/json_config_parser.cc

@@ -667,7 +667,8 @@ DhcpConfigParser* createGlobal6DhcpConfigParser(const std::string& config_id,
         (config_id.compare("valid-lifetime") == 0)  ||
         (config_id.compare("renew-timer") == 0)  ||
         (config_id.compare("rebind-timer") == 0) ||
-        (config_id.compare("decline-probation-period") == 0) )  {
+        (config_id.compare("decline-probation-period") == 0) ||
+        (config_id.compare("dhcp4o6-port") == 0) )  {
         parser = new Uint32Parser(config_id,
                                  globalContext()->uint32_values_);
     } else if (config_id.compare("interfaces-config") == 0) {
@@ -718,6 +719,7 @@ DhcpConfigParser* createGlobal6DhcpConfigParser(const std::string& config_id,
 /// Currently this method sets the following global parameters:
 ///
 /// - decline-probation-period
+/// - dhcp4o6-port
 void setGlobalParameters6() {
 
     // Set the probation period for decline handling.
@@ -729,6 +731,15 @@ void setGlobalParameters6() {
     } catch (...) {
         // That's not really needed.
     }
+
+    // Set the DHCPv4-over-DHCPv6 interserver port.
+    try {
+        uint32_t dhcp4o6_port = globalContext()->uint32_values_
+            ->getOptionalParam("dhcp4o6-port", 0);
+        CfgMgr::instance().getStagingCfg()->setDhcp4o6Port(dhcp4o6_port);
+    } catch (...) {
+        // Ignore errors. This flag is optional
+    }
 }
 
 isc::data::ConstElementPtr

+ 1 - 1
src/lib/dhcp/Makefile.am

@@ -15,7 +15,7 @@ CLEANFILES = *.gcno *.gcda
 lib_LTLIBRARIES = libkea-dhcp++.la
 libkea_dhcp___la_SOURCES  =
 libkea_dhcp___la_SOURCES += classify.cc classify.h
-libkea_dhcp___la_SOURCES += dhcp6.h dhcp4.h
+libkea_dhcp___la_SOURCES += dhcp6.h dhcp4.h dhcp4o6.h
 libkea_dhcp___la_SOURCES += duid.cc duid.h
 libkea_dhcp___la_SOURCES += duid_factory.cc duid_factory.h
 libkea_dhcp___la_SOURCES += hwaddr.cc hwaddr.h

+ 25 - 0
src/lib/dhcp/dhcp4o6.h

@@ -0,0 +1,25 @@
+// Copyright (C) 2015  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef DHCP4O6_H
+#define DHCP4O6_H
+
+/* Offsets and sizes into interserver messages. */
+#define DHCP4O6_IFNAME_OFFSET  0
+#define DHCP4O6_IFNAME_SIZE   16
+#define DHCP4O6_RADDR_OFFSET  16
+#define DHCP4O6_RADDR_SIZE    16
+#define DHCP4O6_DHCP6_OFFSET  32
+
+#endif /* DHCP4O6_H */

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

@@ -107,6 +107,7 @@ libkea_dhcpsrv_la_SOURCES += d2_client_mgr.cc d2_client_mgr.h
 libkea_dhcpsrv_la_SOURCES += daemon.cc daemon.h
 libkea_dhcpsrv_la_SOURCES += database_connection.cc database_connection.h
 libkea_dhcpsrv_la_SOURCES += db_exceptions.h
+libkea_dhcpsrv_la_SOURCES += dhcp4o6_ipc.cc dhcp4o6_ipc.h
 libkea_dhcpsrv_la_SOURCES += dhcpsrv_log.cc dhcpsrv_log.h
 libkea_dhcpsrv_la_SOURCES += host.cc host.h
 libkea_dhcpsrv_la_SOURCES += host_container.h

+ 206 - 0
src/lib/dhcpsrv/dhcp4o6_ipc.cc

@@ -0,0 +1,206 @@
+// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+
+#include <dhcp/dhcp4o6.h>
+#include <dhcp/iface_mgr.h>
+#include <dhcpsrv/dhcp4o6_ipc.h>
+
+#include <netinet/in.h>
+#include <string>
+
+using namespace isc::asiolink;
+using namespace isc::util;
+using namespace std;
+
+namespace isc {
+namespace dhcp {
+
+Dhcp4o6IpcBase::Dhcp4o6IpcBase() : port_(0), socket_fd_(-1) {}
+
+Dhcp4o6IpcBase::~Dhcp4o6IpcBase() {
+    close();
+}
+
+int Dhcp4o6IpcBase::open(uint16_t port, int side) {
+    if (port == port_) {
+        // No change: nothing to do
+        return (socket_fd_);
+    }
+
+    // Port 0: closing
+    if (port == 0) {
+        port_ = 0;
+        if (socket_fd_ != -1) {
+            IfaceMgr::instance().deleteExternalSocket(socket_fd_);
+            ::close(socket_fd_);
+            socket_fd_ = -1;
+        }
+        return (socket_fd_);
+    }
+
+    // Open socket
+    int sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+    if (sock < 0) {
+        isc_throw(Unexpected, "Failed to create DHCP4o6 socket.");
+    }
+
+    // Set reuse address
+    int flag = 1;
+    if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
+                   (char *)&flag, sizeof(flag)) < 0) {
+        ::close(sock);
+        isc_throw(Unexpected, "Failed to set SO_REUSEADDR on DHCP4o6 socket.");
+    }
+
+    // Set no blocking
+    if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
+        ::close(sock);
+        isc_throw(Unexpected, "Failed to set O_NONBLOCK on DHCP4o6 socket.");
+    }
+
+    // Bind to the local address
+    struct sockaddr_in6 local6;
+    memset(&local6, 0, sizeof(local6));
+    local6.sin6_family = AF_INET6;
+#ifdef HAVE_SA_LEN
+    local6.sin6_len = sizeof(local6);
+#endif
+    if (side == 6) {
+        local6.sin6_port = htons(port);
+    } else {
+        local6.sin6_port = htons(port + 1);
+    }
+    if (bind(sock, (struct sockaddr *)&local6, sizeof(local6)) < 0) {
+        ::close(sock);
+        isc_throw(Unexpected, "Failed to bind DHCP4o6 socket.");
+    }
+
+    // Connect to the remote address
+    struct sockaddr_in6 remote6;
+    memset(&remote6, 0, sizeof(remote6));
+    remote6.sin6_family = AF_INET6;
+#ifdef HAVE_SA_LEN
+    remote6.sin6_len = sizeof(remote6);
+#endif
+    if (side == 6) {
+        remote6.sin6_port = htons(port + 1);
+    } else {
+        remote6.sin6_port = htons(port);
+    }
+    if (connect(sock, (struct sockaddr *)&remote6, sizeof(remote6)) < 0) {
+        ::close(sock);
+        isc_throw(Unexpected, "Failed to connect DHCP4o6 socket.");
+    }
+
+    if (socket_fd_ != -1) {
+        if (dup2(sock, socket_fd_) == -1) {
+            ::close(sock);
+            isc_throw(Unexpected, "Failed to duplicate DHCP4o6 socket.");
+        }
+        if (sock != socket_fd_) {
+            ::close(sock);
+            sock = socket_fd_;
+        }
+    }
+
+    // Success
+    port_ = port;
+    socket_fd_ = sock;
+    return (socket_fd_);
+}
+
+void Dhcp4o6IpcBase::close() {
+    static_cast<void>(open(0, 0));
+}
+
+Pkt6Ptr Dhcp4o6IpcBase::receive() {
+    uint8_t buf[65536];
+    ssize_t cc = recv(socket_fd_, buf, sizeof(buf), 0);
+    if (cc < 0) {
+        isc_throw(Unexpected, "Failed to receive on DHCP4o6 socket.");
+    }
+    if (cc < DHCP4O6_DHCP6_OFFSET + Pkt6::DHCPV6_PKT_HDR_LEN) {
+        // Ignore too short messages
+        return (Pkt6Ptr());
+    }
+    char name[DHCP4O6_IFNAME_SIZE + 1];
+    memcpy(name, buf, DHCP4O6_IFNAME_SIZE);
+    IfacePtr iface = IfaceMgr::instance().getIface(string(name));
+    if (!iface) {
+        // Can't get the interface: ignore
+        return (Pkt6Ptr());
+    }
+    uint8_t raddr[DHCP4O6_RADDR_SIZE];
+    memcpy(raddr, buf + DHCP4O6_RADDR_OFFSET, DHCP4O6_RADDR_SIZE);
+    IOAddress from = IOAddress::fromBytes(AF_INET6, raddr);
+    Pkt6Ptr pkt;
+    pkt = Pkt6Ptr(new Pkt6(buf + DHCP4O6_DHCP6_OFFSET,
+                           cc - DHCP4O6_DHCP6_OFFSET));
+    pkt->updateTimestamp();
+    pkt->setRemoteAddr(from);
+    pkt->setIface(iface->getName());
+    pkt->setIndex(iface->getIndex());
+    return (pkt);
+}
+
+void Dhcp4o6IpcBase::send(Pkt6Ptr pkt) {
+    // No packet: nothing to send
+    if (!pkt) {
+        return;
+    }
+
+    // Disabled: nowhere to send
+    if (socket_fd_ == -1) {
+        return;
+    }
+
+    // Get interface name
+    string name = pkt->getIface();
+    if (name.empty() || name.size() > DHCP4O6_IFNAME_SIZE) {
+        // Bad interface name: ignore
+        return;
+    }
+    name.resize(DHCP4O6_IFNAME_SIZE);
+
+    // Get remote address
+    IOAddress from = pkt->getRemoteAddr();
+    vector<uint8_t> raddr = from.toBytes();
+    if (raddr.size() != DHCP4O6_RADDR_SIZE) {
+        // Bad remote address: ignore
+        return;
+    }
+
+    // Get packet content
+    OutputBuffer& pbuf = pkt->getBuffer();
+    if (!pbuf.getLength()) {
+        // Empty buffer: content is not (yet) here, get it
+        pkt->repack();
+    }
+
+    // Fill buffer
+    vector<uint8_t> buf(DHCP4O6_DHCP6_OFFSET + pbuf.getLength());
+    memcpy(&buf[0], name.c_str(), DHCP4O6_IFNAME_SIZE);
+    memcpy(&buf[0] + DHCP4O6_RADDR_OFFSET, &raddr[0], DHCP4O6_RADDR_SIZE);
+    memcpy(&buf[0] + DHCP4O6_DHCP6_OFFSET, pbuf.getData(), pbuf.getLength());
+
+    // Send
+    static_cast<void>(::send(socket_fd_, &buf[0], buf.size(), 0));
+    return;
+}
+
+};  // namespace dhcp
+
+};  // namespace isc

+ 81 - 0
src/lib/dhcpsrv/dhcp4o6_ipc.h

@@ -0,0 +1,81 @@
+// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef DHCP4O6_IPC_H
+#define DHCP4O6_IPC_H
+
+/// @file dhcp4o6_ipc.h Defines the Dhcp4o6IpcBase class.
+/// This file defines the class Kea uses as a base for
+/// DHCPv4-over-DHCPv6 communication between servers.
+///
+#include <dhcp/pkt6.h>
+
+#include <boost/noncopyable.hpp>
+
+#include <stdint.h>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief 
+///
+class Dhcp4o6IpcBase : public boost::noncopyable {
+protected:
+    /// @brief Constructor
+    ///
+    /// Default constructor
+    Dhcp4o6IpcBase();
+
+    /// @brief Destructor.
+    virtual ~Dhcp4o6IpcBase();
+
+    /// @brief Open communication socket (from base class)
+    ///
+    /// @param port port number to use (0 for disabled)
+    /// @param side side of the server (4 or 6)
+    ///
+    /// @return new socket descriptor
+    int open(uint16_t port, int side);
+
+public:
+    /// @brief Open communication socket (for derived classes)
+    virtual void open() = 0;
+
+    /// @brief Close communication socket
+    void close();
+
+    /// @brief Receive IPC message
+    ///
+    /// @return a pointer to a DHCPv6 message with interface and remote
+    /// address set from the IPC message
+    Pkt6Ptr receive();
+
+    /// @brief Send IPC message
+    ///
+    /// @param a pointer to a DHCPv6 message with interface and remote
+    /// address set for the IPC message
+    void send(Pkt6Ptr pkt);
+
+protected:
+    /// @brief Port number
+    uint16_t port_;
+
+    /// @brief Socket descriptor
+    int socket_fd_;
+};
+
+} // namespace isc
+} // namespace dhcp
+
+#endif

+ 2 - 2
src/lib/dhcpsrv/srv_config.cc

@@ -28,7 +28,7 @@ SrvConfig::SrvConfig()
       cfg_host_operations4_(CfgHostOperations::createConfig4()),
       cfg_host_operations6_(CfgHostOperations::createConfig6()),
       class_dictionary_(new ClientClassDictionary()),
-      decline_timer_(0) {
+      decline_timer_(0), dhcp4o6_port_(0) {
 }
 
 SrvConfig::SrvConfig(const uint32_t sequence)
@@ -41,7 +41,7 @@ SrvConfig::SrvConfig(const uint32_t sequence)
       cfg_host_operations4_(CfgHostOperations::createConfig4()),
       cfg_host_operations6_(CfgHostOperations::createConfig6()),
       class_dictionary_(new ClientClassDictionary()),
-      decline_timer_(0) {
+      decline_timer_(0), dhcp4o6_port_(0) {
 }
 
 std::string

+ 24 - 0
src/lib/dhcpsrv/srv_config.h

@@ -469,6 +469,24 @@ public:
         return (decline_timer_);
     }
 
+    /// @brief Sets DHCP4o6 IPC port
+    ///
+    /// DHCPv4-over-DHCPv6 uses a UDP socket for interserver communication,
+    /// this socket is bound and connected to this port and port + 1
+    ///
+    /// @param port port and port + 1 to use
+    void setDhcp4o6Port(uint32_t port) {
+        dhcp4o6_port_ = port;
+    }
+
+    /// @brief Returns DHCP4o6 IPC port
+    ///
+    /// See @ref setDhcp4o6Port or brief discussion.                         
+    /// @return value of DHCP4o6 IPC port
+    uint32_t getDhcp4o6Port() {
+        return (dhcp4o6_port_);
+    }
+
 private:
 
     /// @brief Sequence number identifying the configuration.
@@ -546,6 +564,12 @@ private:
     /// This timer specifies decline probation period, the time after a declined
     /// lease is recovered back to available state. Expressed in seconds.
     uint32_t decline_timer_;
+
+    /// @brief DHCP4o6 IPC port
+    ///
+    /// DHCPv4-over-DHCPv6 uses a UDP socket for interserver communication,
+    /// this socket is bound and connected to this port and port + 1
+    uint32_t dhcp4o6_port_;
 };
 
 /// @name Pointers to the @c SrvConfig object.