From: Francis Dupont Date: Fri, 17 Jun 2016 00:24:29 +0000 (+0200) Subject: [4110a] Rebased previous code (checkpoint/not finished) X-Git-Tag: trac4273_base~1^2~9 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5bde5c0def6a88307c273533f3778e93dc60b684;p=thirdparty%2Fkea.git [4110a] Rebased previous code (checkpoint/not finished) --- diff --git a/src/bin/dhcp4/dhcp4_messages.mes b/src/bin/dhcp4/dhcp4_messages.mes index cc39af7892..e1128c98e9 100644 --- a/src/bin/dhcp4/dhcp4_messages.mes +++ b/src/bin/dhcp4/dhcp4_messages.mes @@ -641,6 +641,12 @@ stopping IO between the DHCPv4 server and the DHCP_DDNS server. This is probably due to a programmatic error is not likely to impact either server upon restart. The reason for the failure is given within the message. +% DHCP4_SRV_DHCP4O6_ERROR error stopping IO with DHCPv4o6 during shutdown: %1 +This error message indicates that during shutdown, an error occurred while +stopping IO between the DHCPv4 server and the DHCPv6o6 server. This is +probably due to a programmatic error is not likely to impact either server +upon restart. The reason for the failure is given within the message. + % DHCP4_STARTED Kea DHCPv4 server version %1 started This informational message indicates that the DHCPv4 server has processed all configuration information and is ready to process diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc index e2189d5242..2c14b93f23 100644 --- a/src/bin/dhcp4/dhcp4_srv.cc +++ b/src/bin/dhcp4/dhcp4_srv.cc @@ -16,7 +16,10 @@ #include #include #include +#include +#include #include +#include #include #include #include @@ -170,9 +173,33 @@ Dhcpv4Exchange::initResponse() { resp_.reset(new Pkt4(resp_type, getQuery()->getTransid())); copyDefaultFields(); copyDefaultOptions(); + + if (getQuery()->isDhcp4o6()) { + initResponse4o6(); + } } } +void +Dhcpv4Exchange::initResponse4o6() { + Pkt4o6Ptr query = boost::dynamic_pointer_cast(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()); @@ -357,6 +384,13 @@ Dhcpv4Srv::~Dhcpv4Srv() { LOG_ERROR(dhcp4_logger, DHCP4_SRV_D2STOP_ERROR).arg(ex.what()); } + try { + Dhcp4to6Ipc::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(); // The lease manager was instantiated during DHCPv4Srv configuration, @@ -376,6 +410,11 @@ Dhcpv4Srv::shutdown() { 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; @@ -478,6 +517,140 @@ Dhcpv4Srv::selectSubnet(const Pkt4Ptr& query) const { return (subnet); } +isc::dhcp::Subnet4Ptr +Dhcpv4Srv::selectSubnet4o6(const Pkt4Ptr& query) const { + // Here begin by the same thing than selectSubnet, i.e., the DHCPv4 part + + Subnet4Ptr subnet; + + SubnetSelector selector; + selector.ciaddr_ = query->getCiaddr(); + selector.giaddr_ = query->getGiaddr(); + selector.local_address_ = query->getLocalAddr(); + selector.client_classes_ = query->classes_; + // moved selector.remote_address_ as it is IPv6 + selector.iface_name_ = query->getIface(); + + // If the link-selection sub-option is present, extract its value. + // "The link-selection sub-option is used by any DHCP relay agent + // that desires to specify a subnet/link for a DHCP client request + // that it is relaying but needs the subnet/link specification to + // be different from the IP address the DHCP server should use + // when communicating with the relay agent." (RFC 3257) + // + // Try first Relay Agent Link Selection sub-option + OptionPtr rai = query->getOption(DHO_DHCP_AGENT_OPTIONS); + if (rai) { + OptionCustomPtr rai_custom = + boost::dynamic_pointer_cast(rai); + if (rai_custom) { + OptionPtr link_select = + rai_custom->getOption(RAI_OPTION_LINK_SELECTION); + if (link_select) { + OptionBuffer link_select_buf = link_select->getData(); + if (link_select_buf.size() == sizeof(uint32_t)) { + selector.option_select_ = + IOAddress::fromBytes(AF_INET, &link_select_buf[0]); + } + } + } + } else { + // Or Subnet Selection option + OptionPtr sbnsel = query->getOption(DHO_SUBNET_SELECTION); + if (sbnsel) { + OptionCustomPtr oc = + boost::dynamic_pointer_cast(sbnsel); + if (oc) { + selector.option_select_ = oc->readAddress(); + } + } + } + + // Mark it as DHCPv4-over-DHCPv6 + selector.dhcp4o6_ = true; + + // Now the DHCPv6 part + selector.remote_address_ = query->getRemoteAddr(); + selector.first_relay_linkaddr_ = IOAddress("::"); + + // Handle a DHCPv6 relayed query + Pkt4o6Ptr query4o6 = boost::dynamic_pointer_cast(query); + if (!query4o6) { + isc_throw(Unexpected, "Can't get DHCP4o6 message"); + } + const Pkt6Ptr& query6 = query4o6->getPkt6(); + + // Initialize fields specific to relayed messages. + if (query6 && !query6->relay_info_.empty()) { + BOOST_REVERSE_FOREACH(Pkt6::RelayInfo relay, query6->relay_info_) { + if (!relay.linkaddr_.isV6Zero() && + !relay.linkaddr_.isV6LinkLocal()) { + selector.first_relay_linkaddr_ = relay.linkaddr_; + break; + } + } + selector.interface_id_ = + query6->getAnyRelayOption(D6O_INTERFACE_ID, + Pkt6::RELAY_GET_FIRST); + } + + CfgMgr& cfgmgr = CfgMgr::instance(); + subnet = cfgmgr.getCurrentCfg()->getCfgSubnets4()->selectSubnet(selector); + + // Let's execute all callouts registered for subnet4_select + if (HooksManager::calloutsPresent(hook_index_subnet4_select_)) { + CalloutHandlePtr callout_handle = getCalloutHandle(query); + + // We're reusing callout_handle from previous calls + callout_handle->deleteAllArguments(); + + // Set new arguments + callout_handle->setArgument("query4", query); + callout_handle->setArgument("subnet4", subnet); + callout_handle->setArgument("subnet4collection", + cfgmgr.getCurrentCfg()-> + getCfgSubnets4()->getAll()); + + // Call user (and server-side) callouts + HooksManager::callCallouts(hook_index_subnet4_select_, + *callout_handle); + + // Callouts decided to skip this step. This means that no subnet + // will be selected. Packet processing will continue, but it will + // be severely limited (i.e. only global options will be assigned) + if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) { + LOG_DEBUG(hooks_logger, DBG_DHCP4_HOOKS, + DHCP4_HOOK_SUBNET4_SELECT_SKIP) + .arg(query->getLabel()); + return (Subnet4Ptr()); + } + + /// @todo: Add support for DROP status + + // Use whatever subnet was specified by the callout + callout_handle->getArgument("subnet4", subnet); + } + + 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)); @@ -1638,7 +1811,8 @@ Dhcpv4Srv::adjustIfaceData(Dhcpv4Exchange& ex) { // 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.isV4Bcast()) { + // Do the same for DHCPv4-over-DHCPv6 exchanges. + if (local_addr.isV4Bcast() || query->isDhcp4o6()) { SocketInfo sock_info = IfaceMgr::instance().getSocket(*query); local_addr = sock_info.addr_; } @@ -1670,6 +1844,12 @@ Dhcpv4Srv::adjustRemoteAddr(Dhcpv4Exchange& ex) { 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 @@ -2191,6 +2371,12 @@ Dhcpv4Srv::acceptDirectRequest(const Pkt4Ptr& pkt) const { 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. diff --git a/src/bin/dhcp4/dhcp4_srv.h b/src/bin/dhcp4/dhcp4_srv.h index 29a0c3f8af..907a5b95de 100644 --- a/src/bin/dhcp4/dhcp4_srv.h +++ b/src/bin/dhcp4/dhcp4_srv.h @@ -9,6 +9,8 @@ #include #include +#include +#include #include #include #include @@ -81,6 +83,11 @@ public: /// 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_); @@ -713,6 +720,12 @@ protected: /// @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_;