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
-// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-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
#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>
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
--- /dev/null
+// 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), 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;
+ }
+
+ 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
--- /dev/null
+// 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
This is an informational message reporting that the configuration has
been extended to include the specified IPv4 subnet.
+% DHCP4_CONFIG_NEW_SUBNET6 a new subnet has been added to configuration: %1
+This is an informational message reporting that the configuration has
+been extended to include the specified IPv6 subnet.
+
% DHCP4_CONFIG_OPTION_DUPLICATE multiple options with the code %1 added to the subnet %2
This warning message is issued on an attempt to configure multiple options
with the same option code for a particular subnet. Adding multiple options
#include <dhcp/option_vendor.h>
#include <dhcp/option_string.h>
#include <dhcp/pkt4.h>
+#include <dhcp/pkt4o6.h>
+#include <dhcp/pkt6.h>
#include <dhcp/docsis3_option_defs.h>
+#include <dhcp4/dhcp4_dhcp4o6_ipc.h>
#include <dhcp4/dhcp4_log.h>
#include <dhcp4/dhcp4_srv.h>
#include <dhcpsrv/addr_utilities.h>
if (resp_type > 0) {
resp_.reset(new Pkt4(resp_type, getQuery()->getTransid()));
copyDefaultFields();
+
+ if (getQuery()->isDhcp4o6()) {
+ initResponse4o6();
+ }
}
}
+void
+Dhcpv4Exchange::initResponse4o6() {
+ Pkt4o6Ptr query = boost::dynamic_pointer_cast<Pkt4o6>(getQuery());
+ if (!query) {
+ return;
+ }
+ const Pkt6Ptr& query6 = query->getPkt6();
+ Pkt6Ptr resp6(new Pkt6(DHCPV6_DHCPV4_RESPONSE, query6->getTransid()));
+ // Don't add client-id or server-id
+ // But copy relay info
+ if (!query6->relay_info_.empty()) {
+ resp6->copyRelayInfo(query6);
+ }
+ // Copy interface and remote address
+ resp6->setIface(query6->getIface());
+ resp6->setIndex(query6->getIndex());
+ resp6->setRemoteAddr(query6->getRemoteAddr());
+ resp_.reset(new Pkt4o6(resp_, resp6));
+}
+
void
Dhcpv4Exchange::copyDefaultFields() {
resp_->setIface(query_->getIface());
LOG_ERROR(dhcp4_logger, DHCP4_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(dhcp4_logger, DHCP4_SRV_DHCP4O6_ERROR).arg(ex.what());
+ }
+
IfaceMgr::instance().closeSockets();
}
isc::dhcp::Subnet4Ptr
Dhcpv4Srv::selectSubnet(const Pkt4Ptr& query) const {
+ // DHCPv4-over-DHCPv6 is a special (and complex) case
+ if (query->isDhcp4o6()) {
+ return (selectSubnet4o6(query));
+ }
+
Subnet4Ptr subnet;
SubnetSelector selector;
return (subnet);
}
+isc::dhcp::Subnet4Ptr
+Dhcpv4Srv::selectSubnet4o6(const Pkt4Ptr& query) const {
+
+ CfgMgr& cfgmgr = CfgMgr::instance();
+ Subnet4Ptr subnet;
+ Subnet6Ptr subnet6;
+ SubnetSelector selector;
+ selector.client_classes_ = query->classes_;
+
+ // DHCPv4 relay or option
+ selector.giaddr_ = query->getGiaddr();
+ if (!selector.giaddr_.isV4Zero()) {
+ subnet = cfgmgr.getCurrentCfg()->getCfgSubnets4()->selectSubnet(selector);
+ }
+
+ // DHCPv6 relay
+ if (!subnet) {
+ Pkt4o6Ptr query4o6 = boost::dynamic_pointer_cast<Pkt4o6>(query);
+ if (!query4o6) {
+ isc_throw(Unexpected, "Can't get DHCP4o6 message");
+ }
+ const Pkt6Ptr& query6 = query4o6->getPkt6();
+ if (query6 && !query6->relay_info_.empty()) {
+ selector.first_relay_linkaddr_ = query6->relay_info_.back().linkaddr_;
+ selector.interface_id_ =
+ query6->getAnyRelayOption(D6O_INTERFACE_ID, Pkt6::RELAY_GET_FIRST);
+ subnet6 =
+ cfgmgr.getCurrentCfg()->getCfgSubnets6()->selectSubnet(selector);
+ }
+ if (subnet6) {
+ // Rely on matching IDs
+ const Subnet4Collection* subnets =
+ cfgmgr.getCurrentCfg()->getCfgSubnets4()->getAll();
+ for (Subnet4Collection::const_iterator it = subnets->begin();
+ it != subnets->end(); ++it) {
+ if ((*it)->getID() == subnet6->getID()) {
+ subnet = *it;
+ }
+ }
+ }
+ }
+
+ // Applying default DHCPv4 rules
+ if (!subnet) {
+ selector.ciaddr_ = query->getCiaddr();
+ selector.remote_address_ = query->getRemoteAddr();
+ selector.iface_name_ = query->getIface();
+
+ subnet =
+ cfgmgr.getCurrentCfg()->getCfgSubnets4()->selectSubnet(selector);
+ }
+
+ // Let's execute all callouts registered for subnet4_select
+ // TODO
+
+ if (subnet) {
+ // Log at higher debug level that subnet has been found.
+ LOG_DEBUG(packet4_logger, DBG_DHCP4_BASIC_DATA, DHCP4_SUBNET_SELECTED)
+ .arg(query->getLabel())
+ .arg(subnet->getID());
+ // Log detailed information about the selected subnet at the
+ // lower debug level.
+ LOG_DEBUG(packet4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_SUBNET_DATA)
+ .arg(query->getLabel())
+ .arg(subnet->toText());
+
+ } else {
+ LOG_DEBUG(packet4_logger, DBG_DHCP4_DETAIL,
+ DHCP4_SUBNET_SELECTION_FAILED)
+ .arg(query->getLabel());
+ }
+
+ return (subnet);
+}
+
Pkt4Ptr
Dhcpv4Srv::receivePacket(int timeout) {
- return (IfaceMgr::instance().receive4(timeout));
+ Pkt4Ptr pkt = IfaceMgr::instance().receive4(timeout);
+ if (!pkt) {
+ Pkt4o6Ptr& pkt4o6 = Dhcp4o6Ipc::instance().getReceived();
+ if (pkt4o6) {
+ pkt = pkt4o6;
+ pkt4o6.reset();
+ }
+ }
+ return (pkt);
}
void
Dhcpv4Srv::sendPacket(const Pkt4Ptr& packet) {
- IfaceMgr::instance().send(packet);
+ if (!packet->isDhcp4o6()) {
+ IfaceMgr::instance().send(packet);
+ } else {
+ Pkt4o6Ptr pkt4o6 = boost::dynamic_pointer_cast<Pkt4o6>(packet);
+ // Should not happen
+ if (!pkt4o6) {
+ isc_throw(Unexpected, "Dhcp4o6 packet cast fail");
+ }
+ Dhcp4o6Ipc::instance().send(pkt4o6->getPkt6());
+ }
}
bool
// Instead we will need to use the address assigned to the interface
// on which the query has been received. In other cases, we will just
// use this address as a source address for the response.
- if (local_addr == IOAddress::IPV4_BCAST_ADDRESS()) {
+ // Do the same for DHCPv4-over-DHCPv6 exchanges.
+ if ((local_addr == IOAddress::IPV4_BCAST_ADDRESS()) ||
+ (query->isDhcp4o6())) {
SocketInfo sock_info = IfaceMgr::instance().getSocket(*query);
local_addr = sock_info.addr_;
}
Pkt4Ptr query = ex.getQuery();
Pkt4Ptr response = ex.getResponse();
+ // DHCPv4-over-DHCPv6 is simple
+ if (query->isDhcp4o6()) {
+ response->setRemoteAddr(query->getRemoteAddr());
+ return;
+ }
+
// The DHCPINFORM is slightly different than other messages in a sense
// that the server should always unicast the response to the ciaddr.
// It appears however that some clients don't set the ciaddr. We still
if (pkt->isRelayed()) {
return (true);
}
+
+ // Accept all DHCPv4-over-DHCPv6 messages.
+ if (pkt->isDhcp4o6()) {
+ return (true);
+ }
+
// The source address must not be zero for the DHCPINFORM message from
// the directly connected client because the server will not know where
// to respond if the ciaddr was not present.
#include <dhcp/dhcp4.h>
#include <dhcp/pkt4.h>
+#include <dhcp/pkt4o6.h>
+#include <dhcp/pkt6.h>
#include <dhcp/option.h>
#include <dhcp/option_string.h>
#include <dhcp/option4_client_fqdn.h>
/// response is not initialized.
void initResponse();
+ /// @brief Initializes the DHCPv6 part of the response message
+ ///
+ /// Called by initResponse() when the query is a DHCP4o6 message
+ void initResponse4o6();
+
/// @brief Returns the pointer to the query from the client.
Pkt4Ptr getQuery() const {
return (query_);
/// @return selected subnet (or NULL if no suitable subnet was found)
isc::dhcp::Subnet4Ptr selectSubnet(const Pkt4Ptr& query) const;
+ /// @brief Selects a subnet for a given client's DHCP4o6 packet.
+ ///
+ /// @param query client's message
+ /// @return selected subnet (or NULL if no suitable subnet was found)
+ isc::dhcp::Subnet4Ptr selectSubnet4o6(const Pkt4Ptr& query) const;
+
/// indicates if shutdown is in progress. Setting it to true will
/// initiate server shutdown procedure.
volatile bool shutdown_;
SubnetID subnet_id =
static_cast<SubnetID>(uint32_values_->getOptionalParam("id", 0));
+ Subnet4Ptr subnet4(new Subnet4(addr, len, t1, t2, valid, subnet_id));
+ subnet_id = subnet4->getID();
+
stringstream s;
s << addr << "/" << static_cast<int>(len) << " with params: ";
// t1 and t2 are optional may be not specified.
if (!t2.unspecified()) {
s << "t2=" << t2 << ", ";
}
- s <<"valid-lifetime=" << valid;
+ s << "valid-lifetime=" << valid;
+ s << ", id=" << subnet_id;
LOG_INFO(dhcp4_logger, DHCP4_CONFIG_NEW_SUBNET).arg(s.str());
- Subnet4Ptr subnet4(new Subnet4(addr, len, t1, t2, valid, subnet_id));
subnet_ = subnet4;
// match-client-id
}
};
-/// @brief this class parses list of DHCP4 subnets
+/// @brief this class parses list of IPv4 subnets
///
/// This is a wrapper parser that handles the whole list of Subnet4
/// definitions. It iterates over all entries and creates Subnet4ConfigParser
}
};
+/// @brief This class parses a single IPv6 subnet.
+///
+/// This is the IPv6 derivation of the SubnetConfigParser class and it parses
+/// the whole subnet definition. It creates parsersfor received configuration
+/// parameters as needed.
+class Subnet6ConfigParser : public SubnetConfigParser {
+public:
+
+ /// @brief Constructor
+ ///
+ /// @param ignored first parameter
+ /// stores global scope parameters, options, option definitions.
+ Subnet6ConfigParser(const std::string&)
+ :SubnetConfigParser("", globalContext(), IOAddress("::")) {
+ }
+
+ /// @brief Parses a single IPv6 subnet configuration and adds to the
+ /// Configuration Manager.
+ ///
+ /// @param subnet A new subnet being configured.
+ void build(ConstElementPtr subnet) {
+ SubnetConfigParser::build(subnet);
+
+ if (subnet_) {
+ Subnet6Ptr sub6ptr = boost::dynamic_pointer_cast<Subnet6>(subnet_);
+ if (!sub6ptr) {
+ // If we hit this, it is a programming error.
+ isc_throw(Unexpected,
+ "Invalid cast in Subnet6ConfigParser::commit");
+ }
+
+ // Set relay information if it was provided
+ if (relay_info_) {
+ sub6ptr->setRelayInfo(*relay_info_);
+ }
+
+ // Adding a subnet to the Configuration Manager may fail if the
+ // subnet id is invalid (duplicate). Thus, we catch exceptions
+ // here to append a position in the configuration string.
+ try {
+ CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->add(sub6ptr);
+ } catch (const std::exception& ex) {
+ isc_throw(DhcpConfigError, ex.what() << " ("
+ << subnet->getPosition() << ")");
+ }
+ }
+ }
+
+ /// @brief Commits subnet configuration.
+ ///
+ /// This function is currently no-op because subnet should already
+ /// be added into the Config Manager in the build().
+ void commit() { }
+
+protected:
+
+ /// @brief creates parsers for entries in subnet definition
+ ///
+ /// @param config_id name of the entry
+ ///
+ /// @return parser object for specified entry name. Note the caller is
+ /// responsible for deleting the parser created.
+ /// @throw isc::dhcp::DhcpConfigError if trying to create a parser
+ /// for unknown config element
+ DhcpConfigParser* createSubnetConfigParser(const std::string& config_id) {
+ DhcpConfigParser* parser = NULL;
+ if (config_id.compare("id") == 0) {
+ parser = new Uint32Parser(config_id, uint32_values_);
+ } else if ((config_id.compare("subnet") == 0) ||
+ (config_id.compare("interface") == 0) ||
+ (config_id.compare("client-class") == 0) ||
+ (config_id.compare("interface-id") == 0)) {
+ parser = new StringParser(config_id, string_values_);
+ } else if (config_id.compare("relay") == 0) {
+ parser = new RelayInfoParser(config_id, relay_info_, Option::V6);
+ } else {
+ isc_throw(NotImplemented, "unsupported parameter: " << config_id);
+ }
+
+ return (parser);
+ }
+
+ /// @brief Issues a DHCP server specific warning regarding duplicate subnet
+ /// options.
+ ///
+ /// @param code is the numeric option code of the duplicate option
+ /// @param addr is the subnet address
+ /// @todo A means to know the correct logger and perhaps a common
+ /// message would allow this message to be emitted by the base class.
+ /// Required to make Subnet6ConfigParser not abstract.
+ virtual void duplicate_option_warning(uint32_t code,
+ isc::asiolink::IOAddress& addr) {
+ LOG_WARN(dhcp4_logger, DHCP4_CONFIG_OPTION_DUPLICATE)
+ .arg(code).arg(addr.toText());
+ }
+
+ /// @brief Instantiates the IPv6 Subnet based on a given IPv6 address
+ /// and prefix length.
+ ///
+ /// @param addr is IPv6 prefix of the subnet.
+ /// @param len is the prefix length
+ void initSubnet(isc::asiolink::IOAddress addr, uint8_t len) {
+ // Subnet ID is not optional because without IPv6 subnets
+ // can't be used for DHCPv4-over-DHCPv6 subnet selection.
+ SubnetID subnet_id =
+ static_cast<SubnetID>(uint32_values_->getParam("id"));
+
+ // Get interface-id option content. For now we support string
+ // representation only
+ std::string ifaceid;
+ try {
+ ifaceid = string_values_->getParam("interface-id");
+ } catch (const DhcpConfigError &) {
+ // interface-id is not mandatory
+ }
+
+ // Specifying both interface for locally reachable subnets and
+ // interface id for relays is mutually exclusive. Need to test for
+ // this condition.
+ if (!ifaceid.empty()) {
+ std::string iface;
+ try {
+ iface = string_values_->getParam("interface");
+ } catch (const DhcpConfigError &) {
+ // iface not mandatory
+ }
+
+ if (!iface.empty()) {
+ isc_throw(isc::dhcp::DhcpConfigError,
+ "parser error: interface (defined for locally reachable "
+ "subnets) and interface-id (defined for subnets reachable"
+ " via relays) cannot be defined at the same time for "
+ "subnet " << addr << "/" << (int)len);
+ }
+ }
+
+ // Create a new subnet.
+ Triplet<uint32_t> t;
+ Subnet6* subnet6 = new Subnet6(addr, len, t, t, t, t, subnet_id);
+ subnet_id = subnet6->getID();
+
+ std::ostringstream output;
+ output << addr << "/" << static_cast<int>(len) << " id=" << subnet_id;
+
+ LOG_INFO(dhcp4_logger, DHCP4_CONFIG_NEW_SUBNET).arg(output.str());
+
+ // Configure interface-id for remote interfaces, if defined
+ if (!ifaceid.empty()) {
+ OptionBuffer tmp(ifaceid.begin(), ifaceid.end());
+ OptionPtr opt(new Option(Option::V6, D6O_INTERFACE_ID, tmp));
+ subnet6->setInterfaceId(opt);
+ }
+
+ // Try setting up client class (if specified)
+ try {
+ string client_class = string_values_->getParam("client-class");
+ subnet6->allowClientClass(client_class);
+ } catch (const DhcpConfigError&) {
+ // That's ok if it fails. client-class is optional.
+ }
+
+ subnet_.reset(subnet6);
+ }
+
+};
+
+
+/// @brief this class parses a list of IPv6 subnets
+///
+/// This is a wrapper parser that handles the whole list of Subnet6
+/// definitions. It iterates over all entries and creates Subnet6ConfigParser
+/// for each entry.
+class Subnets6ListConfigParser : public DhcpConfigParser {
+public:
+
+ /// @brief constructor
+ ///
+ /// @param dummy first argument, always ignored. All parsers accept a
+ /// string parameter "name" as their first argument.
+ Subnets6ListConfigParser(const std::string&) {
+ }
+
+ /// @brief parses contents of the list
+ ///
+ /// Iterates over all entries on the list and creates a Subnet6ConfigParser
+ /// for each entry.
+ ///
+ /// @param subnets_list pointer to a list of IPv6 subnets
+ void build(ConstElementPtr subnets_list) {
+ BOOST_FOREACH(ConstElementPtr subnet, subnets_list->listValue()) {
+ ParserPtr parser(new Subnet6ConfigParser("subnet"));
+ parser->build(subnet);
+ subnets_.push_back(parser);
+ }
+
+ }
+
+ /// @brief commits subnets definitions.
+ ///
+ /// Iterates over all Subnet6 parsers. Each parser contains definitions of
+ /// a single subnet and its parameters and commits each subnet separately.
+ void commit() {
+ BOOST_FOREACH(ParserPtr subnet, subnets_) {
+ subnet->commit();
+ }
+
+ }
+
+ /// @brief Returns Subnet6ListConfigParser object
+ /// @param param_name name of the parameter
+ /// @return Subnets6ListConfigParser object
+ static DhcpConfigParser* factory(const std::string& param_name) {
+ return (new Subnets6ListConfigParser(param_name));
+ }
+
+ /// @brief collection of subnet parsers.
+ ParserCollection subnets_;
+};
+
} // anonymous namespace
namespace isc {
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_);
} else if (config_id.compare("interfaces-config") == 0) {
parser = new IfacesConfigParser4();
} else if (config_id.compare("subnet4") == 0) {
parser = new Subnets4ListConfigParser(config_id);
+ } else if (config_id.compare("subnet6") == 0) {
+ parser = new Subnets6ListConfigParser(config_id);
} else if (config_id.compare("option-data") == 0) {
parser = new OptionDataListParser(config_id, CfgOptionPtr(), AF_INET);
} else if (config_id.compare("option-def") == 0) {
///
/// - 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.
} 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
// depend on the global values. Also, option values configuration
// must be performed after the option definitions configurations.
// Thus we group parsers and will fire them in the right order:
- // all parsers other than: lease-database, subnet4 and option-data parser,
- // then: option-data parser, subnet4 parser, lease-database parser.
+ // all parsers other than: lease-database, subnet and option-data parser,
+ // then: option-data parser, subnet parser, lease-database parser.
// Please do not change this order!
ParserCollection independent_parsers;
- ParserPtr subnet_parser;
+ ParserPtr subnet4_parser;
+ ParserPtr subnet6_parser;
ParserPtr option_parser;
ParserPtr iface_parser;
ParserPtr leases_parser;
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_PARSER_CREATED)
.arg(config_pair.first);
if (config_pair.first == "subnet4") {
- subnet_parser = parser;
+ subnet4_parser = parser;
+ } else if (config_pair.first == "subnet6") {
+ subnet6_parser = parser;
} else if (config_pair.first == "lease-database") {
leases_parser = parser;
} else if (config_pair.first == "option-data") {
independent_parsers.push_back(parser);
parser->build(config_pair.second);
// The commit operation here may modify the global storage
- // but we need it so as the subnet6 parser can access the
+ // but we need it so as subnet parsers can access the
// parsed data.
parser->commit();
}
option_parser->commit();
}
- // The subnet parser is the next one to be run.
- std::map<std::string, ConstElementPtr>::const_iterator subnet_config =
+ // Subnet parsers are the next to be run.
+ std::map<std::string, ConstElementPtr>::const_iterator subnet4_config =
values_map.find("subnet4");
- if (subnet_config != values_map.end()) {
+ if (subnet4_config != values_map.end()) {
config_pair.first = "subnet4";
- subnet_parser->build(subnet_config->second);
+ subnet4_parser->build(subnet4_config->second);
+ }
+ std::map<std::string, ConstElementPtr>::const_iterator subnet6_config =
+ values_map.find("subnet6");
+ if (subnet6_config != values_map.end()) {
+ config_pair.first = "subnet6";
+ subnet6_parser->build(subnet6_config->second);
}
// Get command socket configuration from the config file.
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
#include <config/command_mgr.h>
#include <dhcpsrv/cfgmgr.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>
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
--- /dev/null
+// 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
--- /dev/null
+// 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
#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>
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();
rsp = processInfRequest(query);
break;
+ case DHCPV6_DHCPV4_QUERY:
+ rsp = processDhcp4Query(query);
+ break;
+
default:
// We received a packet type that we do not recognize.
LOG_DEBUG(bad_packet6_logger, DBG_DHCP6_BASIC, DHCP6_UNKNOWN_MSG_RECEIVED)
return (reply);
}
+Pkt6Ptr
+Dhcpv6Srv::processDhcp4Query(const Pkt6Ptr& dhcp4_query) {
+
+ sanityCheck(dhcp4_query, OPTIONAL, OPTIONAL);
+
+ // flags are in transid
+ // uint32_t flags = dhcp4_query->getTransid();
+ // do nothing with DHCPV4_QUERY_FLAGS_UNICAST
+
+ // Get the DHCPv4 message option
+ OptionPtr dhcp4_msg = dhcp4_query->getOption(D6O_DHCPV4_MSG);
+ if (dhcp4_msg) {
+ // Forward the whole message to the DHCPv4 server via IPC
+ Dhcp4o6Ipc::instance().send(dhcp4_query);
+ }
+
+ // Our job is finished
+ return (Pkt6Ptr());
+}
+
size_t
Dhcpv6Srv::unpackOptions(const OptionBuffer& buf,
const std::string& option_space,
/// @return Reply message to be sent to the client.
Pkt6Ptr processInfRequest(const Pkt6Ptr& inf_request);
+ /// @brief Processes incoming DHCPv4-query message.
+ ///
+ /// @param dhcp4_query message received from client
+ /// @return Reply (empty) message to (not) be sent to the client.
+ Pkt6Ptr processDhcp4Query(const Pkt6Ptr& dhcp4_query);
+
/// @brief Selects a subnet for a given client's packet.
///
/// @param question client's message
(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) {
/// Currently this method sets the following global parameters:
///
/// - decline-probation-period
+/// - dhcp4o6-port
void setGlobalParameters6() {
// Set the probation period for decline handling.
} 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
/// (true) or non-relayed (false).
bool isRelayed() const;
+ /// @brief Checks if a DHCPv4 message has beeb transported over DHCPv6
+ ///
+ /// @return Boolean value which indicates whether the message is
+ /// transported over DHCPv6 (true) or native DHCPv4 (false)
+ virtual bool isDhcp4o6() const {
+ return (false);
+ }
+
private:
/// @brief Generic method that validates and sets HW address.
// PERFORMANCE OF THIS SOFTWARE.
#include <config.h>
-#include <asiolink/io_address.h>
-#include <dhcp/dhcp4.h>
-#include <dhcp/libdhcp++.h>
-#include <dhcp/option_int.h>
+
+#include <dhcp/dhcp6.h>
+#include <dhcp/option.h>
#include <dhcp/pkt4o6.h>
#include <exceptions/exceptions.h>
+#include <util/buffer.h>
-#include <algorithm>
-#include <iostream>
-#include <sstream>
-
-using namespace std;
-using namespace isc::dhcp;
using namespace isc::asiolink;
+using namespace isc::dhcp;
+using namespace isc::util;
+using namespace std;
namespace {
namespace isc {
namespace dhcp {
-Pkt4o6::Pkt4o6(const uint8_t* data, size_t len, const Pkt6Ptr& pkt6)
- :Pkt4(data, len), pkt6_(pkt6)
+Pkt4o6::Pkt4o6(const OptionBuffer& pkt4, const Pkt6Ptr& pkt6)
+ :Pkt4(&pkt4[0], pkt4.size()), pkt6_(pkt6)
{
+ static_cast<void>(pkt6->delOption(D6O_DHCPV4_MSG));
setIface(pkt6->getIface());
setIndex(pkt6->getIndex());
setRemoteAddr(pkt6->getRemoteAddr());
}
+Pkt4o6::Pkt4o6(const Pkt4Ptr& pkt4, const Pkt6Ptr& pkt6)
+ :Pkt4(*pkt4), pkt6_(pkt6) {
+}
+
+void Pkt4o6::pack() {
+ Pkt4::pack();
+ OutputBuffer& buf = getBuffer();
+ const uint8_t* ptr = static_cast<const uint8_t*>(buf.getData());
+ OptionBuffer msg(ptr, ptr + buf.getLength());
+ OptionPtr dhcp4_msg(new Option(Option::V6, D6O_DHCPV4_MSG, msg));
+ pkt6_->addOption(dhcp4_msg);
+ pkt6_->pack();
+}
+
} // end of namespace isc::dhcp
} // end of namespace isc
/// @brief Constructor, used in message reception.
///
- /// @param data pointer to received data
- /// @param len size of buffer to be allocated for this packet
- /// @param pkt6 encapsulating DHCPv6 message.
- Pkt4o6(const uint8_t* data, size_t len, const Pkt6Ptr& pkt6);
+ /// @param pkt4 DHCPv4 message
+ /// @param pkt6 encapsulating unpacked DHCPv6 message
+ /// the DHCPv4 message option will be removed
+ Pkt4o6(const OptionBuffer& pkt4, const Pkt6Ptr& pkt6);
+
+ /// @brief Constructor, used in replying to a message
+ ///
+ /// @param pkt4 DHCPv4 message
+ /// @param pkt6 DHCPv6 message
+ Pkt4o6(const Pkt4Ptr& pkt4, const Pkt6Ptr& pkt6);
/// @brief Returns encapsulating DHCPv6 message
- const Pkt6Ptr& getPkt6() { return (pkt6_); }
+ const Pkt6Ptr& getPkt6() const { return (pkt6_); }
+
+ /// @brief Prepares on-wire format of DHCPv4-over-DHCPv6 packet.
+ ///
+ /// Calls pack() on both DHCPv4 and DHCPv6 parts
+ /// Inserts the DHCPv4-message option
+ /// @ref pkt4::pack and @ref pkt6::pack
+ virtual void pack();
+
+ /// @brief Checks if a DHCPv4 message has beeb transported over DHCPv6
+ ///
+ /// @return Boolean value which indicates whether the message is
+ /// transported over DHCPv6 (true) or native DHCPv4 (false)
+ virtual bool isDhcp4o6() const {
+ return (true);
+ }
protected:
/// Encapsulating DHCPv6 message
// And the actual object type is the one that we expect.
// Note that for many options there are dedicated classes
// derived from Option class to represent them.
- const Option* optptr = option.get();
+ const Option* optptr = option.get();
EXPECT_TRUE(typeid(*optptr) == expected_type)
<< "Invalid class returned for option code " << code;
}
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 += 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
--- /dev/null
+// 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
--- /dev/null
+// 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
cfg_option_def_(new CfgOptionDef()), cfg_option_(new CfgOption()),
cfg_subnets4_(new CfgSubnets4()), cfg_subnets6_(new CfgSubnets6()),
cfg_hosts_(new CfgHosts()), cfg_rsoo_(new CfgRSOO()),
- decline_timer_(0) {
+ decline_timer_(0), dhcp4o6_port_(0) {
}
SrvConfig::SrvConfig(const uint32_t sequence)
cfg_option_def_(new CfgOptionDef()), cfg_option_(new CfgOption()),
cfg_subnets4_(new CfgSubnets4()), cfg_subnets6_(new CfgSubnets6()),
cfg_hosts_(new CfgHosts()), cfg_rsoo_(new CfgRSOO()),
- decline_timer_(0) {
+ decline_timer_(0), dhcp4o6_port_(0) {
}
std::string
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.
/// 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.