]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[4110] Ported processing code (but no selectSubnet4o6) from fd4o6
authorFrancis Dupont <fdupont@isc.org>
Sat, 31 Oct 2015 13:00:42 +0000 (14:00 +0100)
committerFrancis Dupont <fdupont@isc.org>
Sat, 31 Oct 2015 13:00:42 +0000 (14:00 +0100)
src/bin/dhcp4/dhcp4_srv.cc
src/bin/dhcp4/dhcp4_srv.h
src/bin/dhcp4/tests/dhcp4_srv_unittest.cc

index e3e1e85ebbf312a8d1359d97a099a9a82b199431..50d32f67e282029df91155ede18a2fcf1280c830 100644 (file)
 #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>
@@ -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<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());
@@ -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<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 (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<Pkt4o6>(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.
index b8b1077402c048ccb6351e3ee5477bf5e17a20ce..ccf148ce223e6740b941627cb1e41735593397c0 100644 (file)
@@ -17,6 +17,8 @@
 
 #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>
@@ -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_;
index 184075e0efebdbd632cfd6051fd32cf07fc61dc5..dbd74f720e775fcd91f0742361e24249a59a0dac 100644 (file)
@@ -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<OptionCustom>(resp_sbnsel);
+        boost::dynamic_pointer_cast<OptionCustom>(resp_sbnsel);
     ASSERT_TRUE(resp_custom);
     IOAddress subnet_addr("0.0.0.0");
     ASSERT_NO_THROW(subnet_addr = resp_custom->readAddress());