From dd71ee635cd1555e41398cbe48d6eb83ece8b88d Mon Sep 17 00:00:00 2001 From: Angelo Failla Date: Fri, 27 Jan 2017 00:35:27 -0800 Subject: [PATCH] Implemented IPAddressV6::getMacAddressFromLinkLocal Summary: It is possible to extract a MAC address from a link-local IPv6 address. This diff provides the logic for it. Reviewed By: simpkins Differential Revision: D4461970 fbshipit-source-id: cb4a5d774c3b4a20716d1742c961e02952ddf80d --- folly/IPAddressV6.cpp | 26 ++++++++++++++++++++++++++ folly/IPAddressV6.h | 11 +++++++++++ folly/test/IPAddressTest.cpp | 17 +++++++++++++++++ 3 files changed, 54 insertions(+) diff --git a/folly/IPAddressV6.cpp b/folly/IPAddressV6.cpp index 9a7758c0..5607a4bf 100644 --- a/folly/IPAddressV6.cpp +++ b/folly/IPAddressV6.cpp @@ -136,6 +136,32 @@ IPAddressV6::AddressStorage::AddressStorage(MacAddress mac) { bytes_[15] = macBytes[5]; } +Optional IPAddressV6::getMacAddressFromLinkLocal() const { + // Returned MacAddress must be constructed from a link-local IPv6 address. + if (!(addr_.bytes_[0] == 0xfe && addr_.bytes_[1] == 0x80 && + addr_.bytes_[2] == 0x00 && addr_.bytes_[3] == 0x00 && + addr_.bytes_[4] == 0x00 && addr_.bytes_[5] == 0x00 && + addr_.bytes_[6] == 0x00 && addr_.bytes_[7] == 0x00 && + addr_.bytes_[11] == 0xff && addr_.bytes_[12] == 0xfe)) { + return folly::none; + } + // The link-local address uses modified EUI-64 format, + // See RFC 4291 sections 2.5.1, 2.5.6, and Appendix A + std::array bytes; + // Step 1: first 8 bytes are fe:80:00:00:00:00:00:00, and can be stripped + // Step 2: invert the universal/local (U/L) flag (bit 7) + bytes[0] = addr_.bytes_[8] ^ 0x02; + // Step 3: copy thhese bytes are they are + bytes[1] = addr_.bytes_[9]; + bytes[2] = addr_.bytes_[10]; + // Step 4: strip bytes (0xfffe), which are bytes_[11] and bytes_[12] + // Step 5: copy the rest. + bytes[3] = addr_.bytes_[13]; + bytes[4] = addr_.bytes_[14]; + bytes[5] = addr_.bytes_[15]; + return Optional(MacAddress::fromBinary(range(bytes))); +} + void IPAddressV6::setFromBinary(ByteRange bytes) { if (bytes.size() != 16) { throw IPAddressFormatException(to( diff --git a/folly/IPAddressV6.h b/folly/IPAddressV6.h index 8e29f804..a1d0c7cb 100644 --- a/folly/IPAddressV6.h +++ b/folly/IPAddressV6.h @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -209,6 +210,16 @@ class IPAddressV6 { */ bool isLinkLocal() const; + /** + * Return the mac address if this is a link-local IPv6 address. + * + * @return an Optional union representing the mac address. + * + * If the address is not a link-local one it will return an empty Optional. + * You can use Optional::value() to check whether the mac address is not null. + */ + Optional getMacAddressFromLinkLocal() const; + /** * Return true if this is a multicast address. */ diff --git a/folly/test/IPAddressTest.cpp b/folly/test/IPAddressTest.cpp index 7fcd10d1..12a588b1 100644 --- a/folly/test/IPAddressTest.cpp +++ b/folly/test/IPAddressTest.cpp @@ -906,6 +906,23 @@ TEST(IPAddress, StringFormat) { EXPECT_EQ("1.2.3.4", detail::fastIpv4ToString(a4)); } +TEST(IPAddress, getMacAddressFromLinkLocal) { + IPAddressV6 ip6("fe80::f652:14ff:fec5:74d8"); + EXPECT_TRUE(ip6.getMacAddressFromLinkLocal().hasValue()); + EXPECT_EQ("f4:52:14:c5:74:d8", ip6.getMacAddressFromLinkLocal()->toString()); +} + +TEST(IPAddress, getMacAddressFromLinkLocal_Negative) { + IPAddressV6 no_link_local_ip6("2803:6082:a2:4447::1"); + EXPECT_FALSE(no_link_local_ip6.getMacAddressFromLinkLocal().hasValue()); + no_link_local_ip6 = IPAddressV6("fe80::f652:14ff:ccc5:74d8"); + EXPECT_FALSE(no_link_local_ip6.getMacAddressFromLinkLocal().hasValue()); + no_link_local_ip6 = IPAddressV6("fe80::f652:14ff:ffc5:74d8"); + EXPECT_FALSE(no_link_local_ip6.getMacAddressFromLinkLocal().hasValue()); + no_link_local_ip6 = IPAddressV6("fe81::f652:14ff:ffc5:74d8"); + EXPECT_FALSE(no_link_local_ip6.getMacAddressFromLinkLocal().hasValue()); +} + TEST(IPAddress, LongestCommonPrefix) { IPAddress ip10("10.0.0.0"); IPAddress ip11("11.0.0.0"); -- 2.34.1