From: Tomek Mrugalski Date: Tue, 7 Oct 2014 17:55:10 +0000 (+0200) Subject: [3549] getMACFromIPv6LinkLocal implemented (direct version, relay tbd) X-Git-Tag: trac3629a_base~8^2~5 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=0a83a5837cb316464bb0d5834435fcf3dda65463;p=thirdparty%2Fkea.git [3549] getMACFromIPv6LinkLocal implemented (direct version, relay tbd) --- diff --git a/src/lib/dhcp/pkt.cc b/src/lib/dhcp/pkt.cc index b4dc70a935..caff4914e1 100644 --- a/src/lib/dhcp/pkt.cc +++ b/src/lib/dhcp/pkt.cc @@ -14,6 +14,8 @@ #include #include +#include +#include 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 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))); +} + + }; }; diff --git a/src/lib/dhcp/pkt.h b/src/lib/dhcp/pkt.h index 9ee081f26d..ff0618457d 100644 --- a/src/lib/dhcp/pkt.h +++ b/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_; diff --git a/src/lib/dhcp/tests/pkt6_unittest.cc b/src/lib/dhcp/tests/pkt6_unittest.cc index 72ec201e22..c090dfc433 100644 --- a/src/lib/dhcp/tests/pkt6_unittest.cc +++ b/src/lib/dhcp/tests/pkt6_unittest.cc @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -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)); +} + }