#include <utility>
#include <dhcp/pkt.h>
+#include <dhcp/iface_mgr.h>
+#include <vector>
namespace isc {
namespace dhcp {
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) {
}
}
+ // 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)));
+}
+
+
};
};
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_;
#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>
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
// 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));
+}
+
}