From: Francis Dupont Date: Sat, 31 Oct 2015 13:00:42 +0000 (+0100) Subject: [4110] Ported processing code (but no selectSubnet4o6) from fd4o6 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=203dc3b4c4384f5caca95f876916c125310cb169;p=thirdparty%2Fkea.git [4110] Ported processing code (but no selectSubnet4o6) from fd4o6 --- diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc index e3e1e85ebb..50d32f67e2 100644 --- a/src/bin/dhcp4/dhcp4_srv.cc +++ b/src/bin/dhcp4/dhcp4_srv.cc @@ -24,7 +24,10 @@ #include #include #include +#include +#include #include +#include #include #include #include @@ -168,9 +171,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()); @@ -290,6 +317,13 @@ Dhcpv4Srv::~Dhcpv4Srv() { 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(); // The lease manager was instantiated during DHCPv4Srv configuration, @@ -306,6 +340,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; @@ -408,14 +447,109 @@ Dhcpv4Srv::selectSubnet(const Pkt4Ptr& query) const { return (subnet); } +isc::dhcp::Subnet4Ptr +Dhcpv4Srv::selectSubnet4o6(const Pkt4Ptr& query) const { +#ifdef notyet + 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(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 (but not the IPv6 remote address) + 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); +#else + return (Subnet4Ptr()); +#endif +} + 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(packet); + // Should not happen + if (!pkt4o6) { + isc_throw(Unexpected, "Dhcp4o6 packet cast fail"); + } + Dhcp4o6Ipc::instance().send(pkt4o6->getPkt6()); + } } bool @@ -1440,7 +1574,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_; } @@ -1472,6 +1607,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 @@ -1990,6 +2131,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 b8b1077402..ccf148ce22 100644 --- a/src/bin/dhcp4/dhcp4_srv.h +++ b/src/bin/dhcp4/dhcp4_srv.h @@ -17,6 +17,8 @@ #include #include +#include +#include #include #include #include @@ -88,6 +90,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_); @@ -676,6 +683,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_; diff --git a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc index 184075e0ef..dbd74f720e 100644 --- a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc +++ b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc @@ -410,7 +410,7 @@ TEST_F(Dhcpv4SrvTest, initResponse) { OptionPtr resp_sbnsel = response->getOption(DHO_SUBNET_SELECTION); ASSERT_TRUE(resp_sbnsel); OptionCustomPtr resp_custom = - boost::dynamic_pointer_cast(resp_sbnsel); + boost::dynamic_pointer_cast(resp_sbnsel); ASSERT_TRUE(resp_custom); IOAddress subnet_addr("0.0.0.0"); ASSERT_NO_THROW(subnet_addr = resp_custom->readAddress());