]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[3551] Patch as submitted by David (see comment 4 in ticket 3551)
authorTomek Mrugalski <tomasz@isc.org>
Tue, 9 Dec 2014 08:54:57 +0000 (09:54 +0100)
committerTomek Mrugalski <tomasz@isc.org>
Tue, 9 Dec 2014 08:54:57 +0000 (09:54 +0100)
src/lib/dhcp/dhcp6.h
src/lib/dhcp/pkt.cc
src/lib/dhcp/pkt.h
src/lib/dhcp/pkt4.h
src/lib/dhcp/pkt6.cc
src/lib/dhcp/pkt6.h
src/lib/dhcp/std_option_defs.h
src/lib/dhcp/tests/pkt6_unittest.cc

index d5201a97401e80c0b2703edbcb8c2a6a011a00c6..4dc485efd1fb5cf7e08ebbd2b8a40b0bcf52226b 100644 (file)
@@ -65,6 +65,7 @@
 #define D6O_CLT_TIME                            46 /* RFC5007 */
 #define D6O_LQ_RELAY_DATA                       47 /* RFC5007 */
 #define D6O_LQ_CLIENT_LINK                      48 /* RFC5007 */
+#define D6O_CLIENT_LINKLAYER_ADDR               79 /* RFC6939 */
 
 /*
  * Status Codes, from RFC 3315 section 24.4, and RFC 3633, 5007.
index 9632cae4a33499d86df81f13d9ca59c37c94d0e9..951229275eb81b88df8d0f3b071333ff71eb6bae 100644 (file)
@@ -155,6 +155,16 @@ Pkt::getMAC(uint32_t hw_addr_src) {
     }
 
     // Method 4: From client link-layer address option inserted by a relay
+    if (hw_addr_src & HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION) {
+               mac = getMACFromIPv6RelayOpt();
+               if (mac) {
+                       return (mac);
+               } else if (hw_addr_src ==  HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION) {
+            // If we're interested only in RFC6939 link layer address as source
+            // of that info, there's no point in trying other options.
+            return (HWAddrPtr());
+        }
+    }
 
     // Method 5: From remote-id option inserted by a relay
 
index 1b6a92299889933e0cc2f6f1a3ad5a41fe9dfb89..158d4e980ebfe44d3e54d0adb197670ee58132ee 100644 (file)
@@ -68,7 +68,7 @@ public:
     /// address option). Note that a skilled attacker can fake that by sending
     /// his request relayed, so the legitimate relay will think it's a second
     /// relay.
-    //static const uint32_t HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION = 0x0008;
+    static const uint32_t HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION = 0x0008;
 
     /// A relay can insert remote-id. In some deployments it contains a MAC
     /// address (RFC4649).
@@ -524,6 +524,22 @@ protected:
     /// @return hardware address (or NULL)
     virtual HWAddrPtr getMACFromSrcLinkLocalAddr() = 0;
 
+    /// @brief Attempts to obtain MAC address from relay option
+    /// client-linklayer-addr
+    ///
+    /// This method is called from getMAC(HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION)
+    /// and should not be called directly. It will extract the client's
+    //  MAC/Hardware address from option client_linklayer_addr (RFC6939)
+    //  inserted by the relay agent closest to the client.
+    /// If this method fails, it will return NULL.
+    ///
+    /// @note This is a pure virtual method and must be implemented in
+    /// the derived classes. The @c Pkt6 class have respective implementation.
+    /// This method is not applicable to DHCPv4.
+    ///
+    /// @return hardware address (or NULL)
+    virtual HWAddrPtr getMACFromIPv6RelayOpt() = 0;
+
     /// @brief Attempts to convert IPv6 address into MAC.
     ///
     /// Utility method that attempts to convert link-local IPv6 address to the
index 7786bed21ade4adc748fce5f10c7e36b0d8d47f9..ba459a837ce774c991203dcfb2a6bcc43f6df5ab 100644 (file)
@@ -402,6 +402,18 @@ protected:
         return (HWAddrPtr());
     }
 
+    /// @brief No-op
+    ///
+    /// This method returns hardware address extracted from an IPv6 relay agent.
+    /// option. As there is no IPv4-equivalent, it always returns NULL.
+    /// We need this stub implementation here, to keep all the get hardware
+    /// address logic in the base class.
+    ///
+    /// @return always NULL
+    virtual HWAddrPtr getMACFromIPv6RelayOpt() {
+        return (HWAddrPtr());
+    }
+
     /// local HW address (dst if receiving packet, src if sending packet)
     HWAddrPtr local_hwaddr_;
 
index 72a2ea56177c953934ea6b8cea82630a5f3ee503..c0fe5c737e353a8adfd4abf9a899b617b4664bf4 100644 (file)
@@ -553,6 +553,23 @@ Pkt6::getMACFromSrcLinkLocalAddr() {
     return (getMACFromIPv6(relay_info_[relay_info_.size() - 1].peeraddr_));
 }
 
+HWAddrPtr
+Pkt6::getMACFromIPv6RelayOpt() {
+    if (relay_info_.empty()) {
+       // This is a direct message
+        return (HWAddrPtr());
+    }
+    // RFC6969 Section 6: Look for the client_linklayer_addr option on the
+    // relay agent closest to the client
+    OptionPtr opt = getAnyRelayOption(D6O_CLIENT_LINKLAYER_ADDR, RELAY_GET_FIRST);
+    if (opt) {
+       return (HWAddrPtr(new HWAddr(&(opt->getData())[2], (opt->len()-opt->getHeaderLen()-2),
+                                             opt->getUint16())));
+    }
+    else {
+       return (HWAddrPtr());
+    }
+}
 
 } // end of isc::dhcp namespace
 } // end of isc namespace
index d79344f2b5af928f2a1b776d1415f81ff82901a9..f4b3391272a2b7a0be3e4baebb3612102a35a680 100644 (file)
@@ -295,6 +295,16 @@ protected:
     /// @return Hardware address (or NULL)
     virtual HWAddrPtr getMACFromSrcLinkLocalAddr();
 
+    /// @brief Extract MAC/Hardware address from client link-layer address
+    //         option inserted by a relay agent (RFC6939).
+    ///
+    /// This method extracts the client's hardware address from the
+    //  client-linklayer-addr option inserted by the relay agent closest to
+    //  the client.
+    ///
+    /// @return Hardware address (or NULL)
+    virtual HWAddrPtr getMACFromIPv6RelayOpt();
+
     /// @brief Builds on wire packet for TCP transmission.
     ///
     /// @todo This function is not implemented yet.
index 3f4fedf93a80e4d16797d3058cab0b6326df3bf3..b43dd1dba6d8d0b33230f0e5c7259e7d86823297 100644 (file)
@@ -326,7 +326,9 @@ const OptionDefParams OPTION_DEF_PARAMS6[] = {
     { "lq-relay-data", D6O_LQ_RELAY_DATA, OPT_RECORD_TYPE, false,
       RECORD_DEF(LQ_RELAY_DATA_RECORDS), "" },
     { "lq-client-link", D6O_LQ_CLIENT_LINK, OPT_IPV6_ADDRESS_TYPE, true,
-      NO_RECORD_DEF, "" }
+      NO_RECORD_DEF, "" },
+    { "client-linklayer-addr", D6O_CLIENT_LINKLAYER_ADDR, OPT_BINARY_TYPE, false,
+        NO_RECORD_DEF, "" }
 
     // @todo There is still a bunch of options for which we have to provide
     // definitions but we don't do it because they are not really
index b7b92ab4d4c27ee1efb703b73c84133e45804202..becd588d1aedddedfb8c3c9e254ae38e6ceced42 100644 (file)
@@ -1050,4 +1050,83 @@ TEST_F(Pkt6Test, getMACFromIPv6LinkLocal_multiRelay) {
     EXPECT_EQ(tmp.str(), found->toText(true));
 }
 
+// Test checks whether getMACFromIPv6RelayOpt() returns the hardware (MAC)
+// address properly from a single relayed message.
+TEST_F(Pkt6Test, getMACFromIPv6RelayOpt_singleRelay) {
+
+    // Let's create a Solicit first...
+    Pkt6 pkt(DHCPV6_SOLICIT, 1234);
+
+    // Packets that are not relayed should fail
+    EXPECT_FALSE(pkt.getMAC(Pkt::HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION));
+
+    // Now pretend it was relayed by a single relay.
+    Pkt6::RelayInfo info;
+
+    // generate options with code 79 and client link layer address
+    const char opt_data[] = {
+        0x00, 0x01,  // Ethertype
+        0x0a, 0x1b, 0x0b, 0x01, 0xca, 0xfe // MAC
+    };
+    OptionPtr relay_opt(new Option(Option::V6, 79,
+                           OptionBuffer(opt_data, opt_data + sizeof(opt_data))));
+    info.options_.insert(make_pair(relay_opt->getType(), relay_opt));
+
+    pkt.addRelayInfo(info);
+    ASSERT_EQ(1, pkt.relay_info_.size());
+
+    HWAddrPtr found = pkt.getMAC(Pkt::HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION);
+    ASSERT_TRUE(found);
+
+    stringstream tmp;
+    tmp << "hwtype=1 0a:1b:0b:01:ca:fe";
+    EXPECT_EQ(tmp.str(), found->toText(true));
+}
+
+// Test checks whether getMACFromIPv6RelayOpt() returns the hardware (MAC)
+// address properly from a message relayed by multiple servers.
+TEST_F(Pkt6Test, getMACFromIPv6RelayOpt_multipleRelay) {
+
+    // Let's create a Solicit first...
+    Pkt6 pkt(DHCPV6_SOLICIT, 1234);
+
+    // Now pretend it was relayed two times. The relay closest to the server
+    // adds link-layer-address information against the RFC, the process fails.
+    Pkt6::RelayInfo info1;
+    char opt_data[] = {
+        0x00, 0x01,  // Ethertype
+        0x1a, 0x30, 0x0b, 0xfa, 0xc0, 0xfe // MAC
+    };
+    OptionPtr relay_opt1(new Option(Option::V6, 79,
+                           OptionBuffer(opt_data, opt_data + sizeof(opt_data))));
+
+    info1.options_.insert(make_pair(relay_opt1->getType(), relay_opt1));
+    pkt.addRelayInfo(info1);
+
+    // Second relay, closest to the client has not implemented RFC6939
+    Pkt6::RelayInfo info2;
+    pkt.addRelayInfo(info2);
+    ASSERT_EQ(2, pkt.relay_info_.size());
+
+    EXPECT_FALSE(pkt.getMAC(Pkt::HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION));
+
+    // Let's envolve the packet with a third relay (now the closest to the client)
+    // that inserts the correct client_linklayer_addr option.
+    Pkt6::RelayInfo info3;
+    // We reuse the option and modify the MAC to be sure we get the right address
+    opt_data[2] = 0xfa;
+    OptionPtr relay_opt3(new Option(Option::V6, 79,
+                           OptionBuffer(opt_data, opt_data + sizeof(opt_data))));
+    info3.options_.insert(make_pair(relay_opt3->getType(), relay_opt3));
+    pkt.addRelayInfo(info3);
+    ASSERT_EQ(3, pkt.relay_info_.size());
+
+    // Now extract the MAC address from the relayed option
+    HWAddrPtr found = pkt.getMAC(Pkt::HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION);
+    ASSERT_TRUE(found);
+
+    stringstream tmp;
+    tmp << "hwtype=1 fa:30:0b:fa:c0:fe";
+    EXPECT_EQ(tmp.str(), found->toText(true));
+}
 }