]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[5404] Implemented DHCPv4 port relay
authorFrancis Dupont <fdupont@isc.org>
Tue, 19 Dec 2017 22:29:58 +0000 (23:29 +0100)
committerFrancis Dupont <fdupont@isc.org>
Tue, 19 Dec 2017 22:29:58 +0000 (23:29 +0100)
src/bin/dhcp4/dhcp4_srv.cc
src/bin/dhcp4/dhcp4_srv.h
src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
src/lib/dhcp/dhcp4.h

index 97ecfa5cf3520bf49d43f27aefd32b7ffadae23a..a834e30987827a14f5a09a5abe44217464890bcf 100644 (file)
@@ -2035,6 +2035,19 @@ Dhcpv4Srv::assignLease(Dhcpv4Exchange& ex) {
     }
 }
 
+uint16_t
+Dhcpv4Srv::checkRelayPort(const Dhcpv4Exchange& ex) {
+
+    // Look for a relay-port RAI sub-option in the query.
+    const Pkt4Ptr& query = ex.getQuery();
+    const OptionPtr& rai = query->getOption(DHO_DHCP_AGENT_OPTIONS);
+    if (rai && rai->getOption(RAI_OPTION_RELAY_PORT)) {
+        // Got the sub-option so use the remote port set by the relay.
+        return (query->getRemotePort());
+    }
+    return (0);
+}
+
 void
 Dhcpv4Srv::adjustIfaceData(Dhcpv4Exchange& ex) {
     adjustRemoteAddr(ex);
@@ -2061,7 +2074,9 @@ Dhcpv4Srv::adjustIfaceData(Dhcpv4Exchange& ex) {
         response->setRemotePort(DHCP4_CLIENT_PORT);
 
     } else {
-        response->setRemotePort(DHCP4_SERVER_PORT);
+        // draft-ietf-dhc-relay-port-10.txt section 5.1
+        uint16_t relay_port = checkRelayPort(ex);
+        response->setRemotePort(relay_port ? relay_port : DHCP4_SERVER_PORT);
     }
 
     CfgIfacePtr cfg_iface = CfgMgr::instance().getCurrentCfg()->getCfgIface();
index e72af87c8415a8f763ff78efc7f1788c0263784e..765944531cd379228106a6e22cebc1d11bfcb228 100644 (file)
@@ -701,6 +701,12 @@ protected:
     /// server's response.
     static void appendServerID(Dhcpv4Exchange& ex);
 
+    /// @brief Check if the relay port RAI sub-option was set in the query.
+    ///
+    /// @param ex The exchange holding the client's message
+    /// @return the port to use to join the relay or 0 for the default
+    static uint16_t checkRelayPort(const Dhcpv4Exchange& ex);
+
     /// @brief Set IP/UDP and interface parameters for the DHCPv4 response.
     ///
     /// This method sets the following parameters for the DHCPv4 message being
index df1ae36fb93cbb02abc2a8f95c4e051673f81ee5..2b3e25abc95f10b5f34db49fe9f0c9d1a5d5235a 100644 (file)
@@ -161,6 +161,9 @@ TEST_F(Dhcpv4SrvTest, adjustIfaceDataRelay) {
     req->setIface("eth1");
     req->setIndex(1);
 
+    // Set remote port (it will be used in the next test).
+    req->setRemotePort(1234);
+
     // Create the exchange using the req.
     Dhcpv4Exchange ex = createExchange(req);
 
@@ -204,6 +207,71 @@ TEST_F(Dhcpv4SrvTest, adjustIfaceDataRelay) {
     EXPECT_EQ("192.0.1.50", resp->getRemoteAddr().toText());
 }
 
+// This test verifies that the remote port is adjusted when
+// the query carries a relay port RAI sub-option.
+TEST_F(Dhcpv4SrvTest, adjustIfaceDataRelayPort) {
+    IfaceMgrTestConfig test_config(true);
+    IfaceMgr::instance().openSockets4();
+
+    // Create the instance of the incoming packet.
+    boost::shared_ptr<Pkt4> req(new Pkt4(DHCPDISCOVER, 1234));
+    // Set the giaddr to non-zero address and hops to non-zero value
+    // as if it was relayed.
+    req->setGiaddr(IOAddress("192.0.1.1"));
+    req->setHops(2);
+    // Set ciaddr to zero. This simulates the client which applies
+    // for the new lease.
+    req->setCiaddr(IOAddress("0.0.0.0"));
+    // Clear broadcast flag.
+    req->setFlags(0x0000);
+
+    // Set local address, port and interface.
+    req->setLocalAddr(IOAddress("192.0.2.5"));
+    req->setLocalPort(1001);
+    req->setIface("eth1");
+    req->setIndex(1);
+
+    // Set remote port.
+    req->setRemotePort(1234);
+
+    // Add a RAI relay-port sub-option (the only difference with the previous test).
+    OptionDefinitionPtr rai_def =
+        LibDHCP::getOptionDef(DHCP4_OPTION_SPACE, DHO_DHCP_AGENT_OPTIONS);
+    ASSERT_TRUE(rai_def);
+    OptionCustomPtr rai(new OptionCustom(*rai_def, Option::V4));
+    ASSERT_TRUE(rai);
+    req->addOption(rai);
+    OptionPtr relay_port(new Option(Option::V4, RAI_OPTION_RELAY_PORT));
+    ASSERT_TRUE(relay_port);
+    rai->addOption(relay_port);
+
+    // Create the exchange using the req.
+    Dhcpv4Exchange ex = createExchange(req);
+
+    Pkt4Ptr resp = ex.getResponse();
+    resp->setYiaddr(IOAddress("192.0.1.100"));
+    // Clear the remote address.
+    resp->setRemoteAddr(IOAddress("0.0.0.0"));
+    // Set hops value for the response.
+    resp->setHops(req->getHops());
+
+    // This function never throws.
+    ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(ex));
+
+    // Now the destination address should be relay's address.
+    EXPECT_EQ("192.0.1.1", resp->getRemoteAddr().toText());
+    // The query has been relayed, so the response must be sent to the port 67.
+    EXPECT_EQ(1234, resp->getRemotePort());
+    // Local address should be the address assigned to interface eth1.
+    EXPECT_EQ("192.0.2.5", resp->getLocalAddr().toText());
+    // The local port is always DHCPv4 server port 67.
+    EXPECT_EQ(DHCP4_SERVER_PORT, resp->getLocalPort());
+    // We will send response over the same interface which was used to receive
+    // query.
+    EXPECT_EQ("eth1", resp->getIface());
+    EXPECT_EQ(1, resp->getIndex());
+}
+
 // This test verifies that it is possible to configure the server to use
 // routing information to determine the right outbound interface to sent
 // responses to a relayed client.
index 8fd8297ea9178477238671651dfe887b9fed1eed..9b8af7deceee84672fdf268e408c05293d9df5f6 100644 (file)
@@ -272,6 +272,7 @@ static const uint16_t RAI_OPTION_ACCESS_POINT_NAME = 15; // RFC7839
 static const uint16_t RAI_OPTION_ACCESS_POINT_BSSID = 16; // RFC7839
 static const uint16_t RAI_OPTION_OPERATOR_ID = 17; // RFC7839
 static const uint16_t RAI_OPTION_OPERATOR_REALM = 18; // RFC7839
+static const uint16_t RAI_OPTION_RELAY_PORT = 19; // I-D, to be assigned
 static const uint16_t RAI_OPTION_VIRTUAL_SUBNET_SELECT = 151; //RFC6607
 static const uint16_t RAI_OPTION_VIRTUAL_SUBNET_SELECT_CTRL = 152; //RFC6607