Browse Source

[3549] getMACFromIPv6LinkLocal implemented (direct version, relay tbd)

Tomek Mrugalski 10 years ago
parent
commit
0a83a5837c
3 changed files with 127 additions and 0 deletions
  1. 63 0
      src/lib/dhcp/pkt.cc
  2. 17 0
      src/lib/dhcp/pkt.h
  3. 47 0
      src/lib/dhcp/tests/pkt6_unittest.cc

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

@@ -14,6 +14,8 @@
 
 #include <utility>
 #include <dhcp/pkt.h>
+#include <dhcp/iface_mgr.h>
+#include <vector>
 
 namespace isc {
 namespace dhcp {
@@ -125,6 +127,8 @@ Pkt::setHWAddrMember(const uint8_t htype, const uint8_t,
 HWAddrPtr
 Pkt::getMAC(uint32_t hw_addr_src) {
     HWAddrPtr mac;
+
+    // Method 1: from raw sockets.
     if (hw_addr_src & HWADDR_SOURCE_RAW) {
         mac = getRemoteHWAddr();
         if (mac) {
@@ -136,11 +140,70 @@ Pkt::getMAC(uint32_t hw_addr_src) {
         }
     }
 
+    // Method 2: Extracted from DUID-LLT or DUID-LL
+
+    // Method 3: Extracted from source IPv6 link-local address
+    if (hw_addr_src & MAC_SOURCE_IPV6_LINK_LOCAL) {
+        mac = getMACFromSrcLinkLocalAddr();
+        if (mac) {
+            return (mac);
+        } else if (hw_addr_src ==  MAC_SOURCE_IPV6_LINK_LOCAL) {
+            // If we're interested only in link-local addr as source of that
+            // info, there's no point in trying other options.
+            return (HWAddrPtr());
+        }
+    }
+
+    // Method 4: From client link-layer address option inserted by a relay
+
+    // Method 5: From remote-id option inserted by a relay
+
+    // Method 6: From subscriber-id option inserted by a releay
+
+    // Method 7: From docsis options
+
     /// @todo: add other MAC acquisition methods here
 
     // Ok, none of the methods were suitable. Return NULL.
     return (HWAddrPtr());
 }
 
+HWAddrPtr
+Pkt::getMACFromSrcLinkLocalAddr() {
+    if (!remote_addr_.isV6LinkLocal()) {
+        return (HWAddrPtr());
+    }
+
+    std::vector<uint8_t> bin = remote_addr_.toBytes();
+
+    // Double check that it's of appropriate size
+    if ((bin.size() != isc::asiolink::V6ADDRESS_LEN) ||
+
+        // Check that it's link-local (starts with fe80).
+        (bin[0] != 0xfe) || (bin[1] != 0x80) ||
+
+        // And that the IID is of EUI-64 type.
+        (bin[11] != 0xff) || (bin[12] != 0xfe)) {
+        return (HWAddrPtr());
+    }
+
+    // Remove 8 most significant bytes
+    bin.erase(bin.begin(), bin.begin() + 8);
+
+    // Ok, we're down to EUI-64 only now: XX:XX:XX:ff:fe:XX:XX:XX
+    bin.erase(bin.begin() + 3, bin.begin() + 5);
+
+    // Let's get the interface this packet was received on. We need it to get
+    // hardware type
+    Iface* iface = IfaceMgr::instance().getIface(iface_);
+    uint16_t hwtype = 0; // not specified
+    if (iface) {
+        hwtype = iface->getHWType();
+    }
+
+    return (HWAddrPtr(new HWAddr(bin, hwtype)));
+}
+
+
 };
 };

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

@@ -503,6 +503,23 @@ public:
 
 protected:
 
+    /// @brief Attempts to obtain MAC address from source link-local IPv6 address
+    ///
+    /// This method is called from getMAC(MAC_SOURCE_IPV6_LINK_LOCAL) and should
+    /// not be called directly. It is not 100% reliable. The source IPv6 address
+    /// does not necessarily have to be link-local (may be global or ULA) and
+    /// even if it's link-local, it doesn't necessarily be based on EUI-64. For
+    /// example, Windows supports RFC4941, which randomized IID part of the
+    /// link-local address. If this method fails, it will return NULL.
+    ///
+    /// For direct message, it attempts to use remote_addr_ field. For relayed
+    /// message, it uses peer-addr of the first relay.
+    ///
+    /// This method is not applicable to IPv4.
+    ///
+    /// @param hardware address (or NULL)
+    HWAddrPtr getMACFromSrcLinkLocalAddr();
+
     /// Transaction-id (32 bits for v4, 24 bits for v6)
     uint32_t transid_;
 

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

@@ -21,6 +21,7 @@
 #include <dhcp/option6_ia.h>
 #include <dhcp/option_int.h>
 #include <dhcp/option_int_array.h>
+#include <dhcp/iface_mgr.h>
 #include <dhcp/pkt6.h>
 #include <dhcp/hwaddr.h>
 #include <dhcp/docsis3_option_defs.h>
@@ -877,6 +878,18 @@ TEST_F(Pkt6Test, getMAC) {
     EXPECT_FALSE(pkt.getMAC(Pkt::HWADDR_SOURCE_ANY));
     EXPECT_FALSE(pkt.getMAC(Pkt::HWADDR_SOURCE_RAW));
 
+    // We haven't specified source IPv6 address, so this method should fail, too
+    EXPECT_FALSE(pkt.getMAC(Pkt::HWADDR_SOURCE_IPV6_LINK_LOCAL));
+
+    // Let's check if setting IPv6 address improves the situation.
+    IOAddress linklocal_eui64("fe80::204:06ff:fe08:0a0c");
+    pkt.setRemoteAddr(linklocal_eui64);
+    EXPECT_TRUE(pkt.getMAC(Pkt::HWADDR_SOURCE_ANY));
+    EXPECT_TRUE(pkt.getMAC(Pkt::HWADDR_SOURCE_IPV6_LINK_LOCAL));
+    EXPECT_TRUE(pkt.getMAC(Pkt::HWADDR_SOURCE_IPV6_LINK_LOCAL |
+                           Pkt::HWADDR_SOURCE_RAW));
+    pkt.setRemoteAddr(IOAddress("::"));
+
     // Let's invent a MAC
     const uint8_t hw[] = { 2, 4, 6, 8, 10, 12 }; // MAC
     const uint8_t hw_type = 123; // hardware type
@@ -888,10 +901,44 @@ TEST_F(Pkt6Test, getMAC) {
     // Now we should be able to get something
     ASSERT_TRUE(pkt.getMAC(Pkt::HWADDR_SOURCE_ANY));
     ASSERT_TRUE(pkt.getMAC(Pkt::HWADDR_SOURCE_RAW));
+    EXPECT_TRUE(pkt.getMAC(Pkt::HWADDR_SOURCE_IPV6_LINK_LOCAL |
+                           Pkt::HWADDR_SOURCE_RAW));
 
     // Check that the returned MAC is indeed the expected one
     ASSERT_TRUE(*dummy_hwaddr == *pkt.getMAC(Pkt::HWADDR_SOURCE_ANY));
     ASSERT_TRUE(*dummy_hwaddr == *pkt.getMAC(Pkt::HWADDR_SOURCE_RAW));
 }
 
+// Test checks whether getMACFromIPv6LinkLocal() returns the hardware (MAC)
+// address properly.
+TEST_F(Pkt6Test, getMACFromIPv6LinkLocal) {
+    Pkt6 pkt(DHCPV6_ADVERTISE, 1234);
+
+    // Let's get the first interface
+    Iface* iface = IfaceMgr::instance().getIface(1);
+    ASSERT_TRUE(iface);
+
+    // and set source interface data properly. getMACFromIPv6LinkLocal attempts
+    // to use source interface to obtain hardware type
+    pkt.setIface(iface->getName());
+    pkt.setIndex(iface->getIndex());
+
+    IOAddress global("2001:db8::204:06ff:fe08:0a:0c");
+    IOAddress linklocal_eui64("fe80::204:06ff:fe08:0a0c");
+    IOAddress linklocal_noneui64("fe80::0204:0608:0a0c:0e10");
+
+    // If received from a global address, this method should fail
+    pkt.setRemoteAddr(global);
+    EXPECT_FALSE(pkt.getMAC(Pkt::HWADDR_SOURCE_IPV6_LINK_LOCAL));
+
+    // If received from link-local that is EUI-64 based, it should succeed
+    pkt.setRemoteAddr(linklocal_eui64);
+    HWAddrPtr found = pkt.getMAC(Pkt::HWADDR_SOURCE_IPV6_LINK_LOCAL);
+    ASSERT_TRUE(found);
+
+    stringstream tmp;
+    tmp << "hwtype=" << (int)iface->getHWType() << " 02:04:06:08:0a:0c";
+    EXPECT_EQ(tmp.str(), found->toText(true));
+}
+
 }