Browse Source

[master] Merge branch 'trac3551' (RFC6939 support)

Conflicts:
	ChangeLog
Tomek Mrugalski 10 years ago
parent
commit
23fca8ea6e

+ 3 - 0
AUTHORS

@@ -70,6 +70,9 @@ We have received the following contributions:
    2014-11: Export CalloutManager headers for testing statically linked
    2014-11: Export CalloutManager headers for testing statically linked
             libraries.
             libraries.
 
 
+ - David Gutierrez Rueda, CERN
+   2014-12: Support for client link-address option in DHCPv6 (RFC6939)
+
 Kea uses log4cplus (http://sourceforge.net/projects/log4cplus/) for logging,
 Kea uses log4cplus (http://sourceforge.net/projects/log4cplus/) for logging,
 Boost (http://www.boost.org/) library for almost everything, and can use Botan
 Boost (http://www.boost.org/) library for almost everything, and can use Botan
 (http://botan.randombit.net/) or OpenSSL (https://www.openssl.org/) for
 (http://botan.randombit.net/) or OpenSSL (https://www.openssl.org/) for

+ 5 - 0
ChangeLog

@@ -1,3 +1,8 @@
+862.	[func]		dgutier, tomek
+	Support for client link-layer address option (RFC6939) has
+	been added.
+	(Trac #3551, git dabdf965d92085f86d5e96c8dadce0f0a8f7c8e3)
+
 861.	[func]		marcin
 861.	[func]		marcin
 	The configuration parameters for a DHCPv4 and DHCPv6 options are now
 	The configuration parameters for a DHCPv4 and DHCPv6 options are now
 	optional.
 	optional.

+ 7 - 0
doc/guide/dhcp6-srv.xml

@@ -1869,6 +1869,13 @@ should include options from the isc option space:
             <ulink url="http://tools.ietf.org/html/rfc4704">RFC 4704</ulink>:
             <ulink url="http://tools.ietf.org/html/rfc4704">RFC 4704</ulink>:
             Supported option is CLIENT_FQDN.</simpara>
             Supported option is CLIENT_FQDN.</simpara>
           </listitem>
           </listitem>
+	  <listitem>
+	    <simpara><emphasis>Client Link-Layer Address Option in
+	    DHCPv6</emphasis>,
+	    <ulink url="http://tools.ietf.org/html/rfc6939">RFC
+	    6939</ulink>: Supported option is client link-layer
+	    address option.</simpara>
+	  </listitem>
       </itemizedlist>
       </itemizedlist>
     </section>
     </section>
 
 

+ 1 - 0
src/lib/dhcp/dhcp6.h

@@ -65,6 +65,7 @@
 #define D6O_CLT_TIME                            46 /* RFC5007 */
 #define D6O_CLT_TIME                            46 /* RFC5007 */
 #define D6O_LQ_RELAY_DATA                       47 /* RFC5007 */
 #define D6O_LQ_RELAY_DATA                       47 /* RFC5007 */
 #define D6O_LQ_CLIENT_LINK                      48 /* 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.
  * Status Codes, from RFC 3315 section 24.4, and RFC 3633, 5007.

+ 10 - 0
src/lib/dhcp/pkt.cc

@@ -155,6 +155,16 @@ Pkt::getMAC(uint32_t hw_addr_src) {
     }
     }
 
 
     // Method 4: From client link-layer address option inserted by a relay
     // 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
     // Method 5: From remote-id option inserted by a relay
 
 

+ 17 - 1
src/lib/dhcp/pkt.h

@@ -68,7 +68,7 @@ public:
     /// address option). Note that a skilled attacker can fake that by sending
     /// 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
     /// his request relayed, so the legitimate relay will think it's a second
     /// relay.
     /// 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
     /// A relay can insert remote-id. In some deployments it contains a MAC
     /// address (RFC4649).
     /// address (RFC4649).
@@ -524,6 +524,22 @@ protected:
     /// @return hardware address (or NULL)
     /// @return hardware address (or NULL)
     virtual HWAddrPtr getMACFromSrcLinkLocalAddr() = 0;
     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.
     /// @brief Attempts to convert IPv6 address into MAC.
     ///
     ///
     /// Utility method that attempts to convert link-local IPv6 address to the
     /// Utility method that attempts to convert link-local IPv6 address to the

+ 12 - 0
src/lib/dhcp/pkt4.h

@@ -402,6 +402,18 @@ protected:
         return (HWAddrPtr());
         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)
     /// local HW address (dst if receiving packet, src if sending packet)
     HWAddrPtr local_hwaddr_;
     HWAddrPtr local_hwaddr_;
 
 

+ 26 - 0
src/lib/dhcp/pkt6.cc

@@ -16,6 +16,7 @@
 #include <dhcp/libdhcp++.h>
 #include <dhcp/libdhcp++.h>
 #include <dhcp/option.h>
 #include <dhcp/option.h>
 #include <dhcp/pkt6.h>
 #include <dhcp/pkt6.h>
+#include <util/io_utilities.h>
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
 
 
 #include <iostream>
 #include <iostream>
@@ -553,6 +554,31 @@ Pkt6::getMACFromSrcLinkLocalAddr() {
     return (getMACFromIPv6(relay_info_[relay_info_.size() - 1].peeraddr_));
     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) {
+        const OptionBuffer data = opt->getData();
+        if (data.size() < 3) {
+            // This client link address option is trucnated. It's supposed to be
+            // 2 bytes of link-layer type followed by link-layer address.
+            return (HWAddrPtr());
+        }
+
+        // +2, -2 means to skip the initial 2 bytes which are hwaddress type
+        return (HWAddrPtr(new HWAddr(&data[0] + 2, data.size() - 2,
+                                     opt->getUint16())));
+    }
+    else {
+        return (HWAddrPtr());
+    }
+}
 
 
 } // end of isc::dhcp namespace
 } // end of isc::dhcp namespace
 } // end of isc namespace
 } // end of isc namespace

+ 10 - 0
src/lib/dhcp/pkt6.h

@@ -295,6 +295,16 @@ protected:
     /// @return Hardware address (or NULL)
     /// @return Hardware address (or NULL)
     virtual HWAddrPtr getMACFromSrcLinkLocalAddr();
     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.
     /// @brief Builds on wire packet for TCP transmission.
     ///
     ///
     /// @todo This function is not implemented yet.
     /// @todo This function is not implemented yet.

+ 3 - 1
src/lib/dhcp/std_option_defs.h

@@ -326,7 +326,9 @@ const OptionDefParams OPTION_DEF_PARAMS6[] = {
     { "lq-relay-data", D6O_LQ_RELAY_DATA, OPT_RECORD_TYPE, false,
     { "lq-relay-data", D6O_LQ_RELAY_DATA, OPT_RECORD_TYPE, false,
       RECORD_DEF(LQ_RELAY_DATA_RECORDS), "" },
       RECORD_DEF(LQ_RELAY_DATA_RECORDS), "" },
     { "lq-client-link", D6O_LQ_CLIENT_LINK, OPT_IPV6_ADDRESS_TYPE, true,
     { "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
     // @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
     // definitions but we don't do it because they are not really

+ 80 - 0
src/lib/dhcp/tests/pkt6_unittest.cc

@@ -1050,4 +1050,84 @@ TEST_F(Pkt6Test, getMACFromIPv6LinkLocal_multiRelay) {
     EXPECT_EQ(tmp.str(), found->toText(true));
     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 uint8_t 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;
+    uint8_t opt_data[] = {
+        0x00, 0x01,  // Ethertype
+        0x1a, 0x30, 0x0b, 0xfa, 0xc0, 0xfe // MAC
+    };
+    OptionPtr relay_opt1(new Option(Option::V6, D6O_CLIENT_LINKLAYER_ADDR,
+    		            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, D6O_CLIENT_LINKLAYER_ADDR,
+    		            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));
+}
 }
 }