From: Francis Dupont Date: Sat, 31 Oct 2015 03:56:08 +0000 (+0100) Subject: [4106] Ported DHCPv4-over-DHCPv6 IPC code from fd4o6 private branch X-Git-Tag: trac4296_base~4^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=89c8c17a16b11b42e8c5135335f51a2d942d3612;p=thirdparty%2Fkea.git [4106] Ported DHCPv4-over-DHCPv6 IPC code from fd4o6 private branch --- diff --git a/src/bin/dhcp4/Makefile.am b/src/bin/dhcp4/Makefile.am index 0512bb3501..d2dcc5e8b7 100644 --- a/src/bin/dhcp4/Makefile.am +++ b/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 diff --git a/src/bin/dhcp4/ctrl_dhcp4_srv.cc b/src/bin/dhcp4/ctrl_dhcp4_srv.cc index 7d19451dc3..4ccff92992 100644 --- a/src/bin/dhcp4/ctrl_dhcp4_srv.cc +++ b/src/bin/dhcp4/ctrl_dhcp4_srv.cc @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -185,6 +186,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 diff --git a/src/bin/dhcp4/dhcp4_dhcp4o6_ipc.cc b/src/bin/dhcp4/dhcp4_dhcp4o6_ipc.cc new file mode 100644 index 0000000000..bf2644bfad --- /dev/null +++ b/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 + +#include +#include +#include +#include + +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(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 diff --git a/src/bin/dhcp4/dhcp4_dhcp4o6_ipc.h b/src/bin/dhcp4/dhcp4_dhcp4o6_ipc.h new file mode 100644 index 0000000000..5763e1264f --- /dev/null +++ b/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 +#include +#include + +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 diff --git a/src/bin/dhcp4/json_config_parser.cc b/src/bin/dhcp4/json_config_parser.cc index d62e03e39b..0b23cf27b3 100644 --- a/src/bin/dhcp4/json_config_parser.cc +++ b/src/bin/dhcp4/json_config_parser.cc @@ -373,15 +373,16 @@ namespace dhcp { /// @return parser for specified global DHCPv4 parameter /// @throw NotImplemented if trying to create a parser for unknown /// config element - DhcpConfigParser* createGlobalDhcp4ConfigParser(const std::string& config_id, - ConstElementPtr element) { +DhcpConfigParser* createGlobalDhcp4ConfigParser(const std::string& config_id, + ConstElementPtr element) { DhcpConfigParser* parser = NULL; 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) { @@ -423,6 +424,7 @@ namespace dhcp { /// /// - 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. @@ -445,6 +447,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 diff --git a/src/bin/dhcp6/Makefile.am b/src/bin/dhcp6/Makefile.am index 324aea80f2..7adc97e03b 100644 --- a/src/bin/dhcp6/Makefile.am +++ b/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 diff --git a/src/bin/dhcp6/ctrl_dhcp6_srv.cc b/src/bin/dhcp6/ctrl_dhcp6_srv.cc index e02f7be2cb..1ec1eeba74 100644 --- a/src/bin/dhcp6/ctrl_dhcp6_srv.cc +++ b/src/bin/dhcp6/ctrl_dhcp6_srv.cc @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -183,6 +184,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 diff --git a/src/bin/dhcp6/dhcp6_dhcp4o6_ipc.cc b/src/bin/dhcp6/dhcp6_dhcp4o6_ipc.cc new file mode 100644 index 0000000000..d3b758657f --- /dev/null +++ b/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 + +#include +#include +#include +#include + +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(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 diff --git a/src/bin/dhcp6/dhcp6_dhcp4o6_ipc.h b/src/bin/dhcp6/dhcp6_dhcp4o6_ipc.h new file mode 100644 index 0000000000..901a6f2d3e --- /dev/null +++ b/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 +#include + +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 diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc index 2f0687b60f..211a303c70 100644 --- a/src/bin/dhcp6/dhcp6_srv.cc +++ b/src/bin/dhcp6/dhcp6_srv.cc @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -239,6 +240,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(); diff --git a/src/bin/dhcp6/json_config_parser.cc b/src/bin/dhcp6/json_config_parser.cc index cc269ae91c..165c519d32 100644 --- a/src/bin/dhcp6/json_config_parser.cc +++ b/src/bin/dhcp6/json_config_parser.cc @@ -672,7 +672,8 @@ namespace dhcp { (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) { @@ -715,6 +716,7 @@ namespace dhcp { /// Currently this method sets the following global parameters: /// /// - decline-probation-period +/// - dhcp4o6-port void setGlobalParameters6() { // Set the probation period for decline handling. @@ -726,6 +728,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 diff --git a/src/lib/dhcp/dhcp4o6.h b/src/lib/dhcp/dhcp4o6.h new file mode 100644 index 0000000000..898080996b --- /dev/null +++ b/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 */ diff --git a/src/lib/dhcpsrv/Makefile.am b/src/lib/dhcpsrv/Makefile.am index 56efc8bbb2..bbf1ab403b 100644 --- a/src/lib/dhcpsrv/Makefile.am +++ b/src/lib/dhcpsrv/Makefile.am @@ -94,6 +94,7 @@ libkea_dhcpsrv_la_SOURCES += d2_client_cfg.cc d2_client_cfg.h 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 += 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 diff --git a/src/lib/dhcpsrv/dhcp4o6_ipc.cc b/src/lib/dhcpsrv/dhcp4o6_ipc.cc new file mode 100644 index 0000000000..9fcd1246ee --- /dev/null +++ b/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 + +#include +#include +#include + +#include +#include + +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(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 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 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(::send(socket_fd_, &buf[0], buf.size(), 0)); + return; +} + +}; // namespace dhcp + +}; // namespace isc diff --git a/src/lib/dhcpsrv/dhcp4o6_ipc.h b/src/lib/dhcpsrv/dhcp4o6_ipc.h new file mode 100644 index 0000000000..36b05babc0 --- /dev/null +++ b/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 + +#include + +#include + +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 diff --git a/src/lib/dhcpsrv/srv_config.cc b/src/lib/dhcpsrv/srv_config.cc index b614ff5d2d..027bce9c5f 100644 --- a/src/lib/dhcpsrv/srv_config.cc +++ b/src/lib/dhcpsrv/srv_config.cc @@ -32,7 +32,7 @@ SrvConfig::SrvConfig() cfg_subnets4_(new CfgSubnets4()), cfg_subnets6_(new CfgSubnets6()), cfg_hosts_(new CfgHosts()), cfg_rsoo_(new CfgRSOO()), cfg_expiration_(new CfgExpiration()), - 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_subnets4_(new CfgSubnets4()), cfg_subnets6_(new CfgSubnets6()), cfg_hosts_(new CfgHosts()), cfg_rsoo_(new CfgRSOO()), cfg_expiration_(new CfgExpiration()), - decline_timer_(0) { + decline_timer_(0), dhcp4o6_port_(0) { } std::string diff --git a/src/lib/dhcpsrv/srv_config.h b/src/lib/dhcpsrv/srv_config.h index da387c9a56..dbd9047e6f 100644 --- a/src/lib/dhcpsrv/srv_config.h +++ b/src/lib/dhcpsrv/srv_config.h @@ -407,6 +407,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. @@ -466,6 +484,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.