From: Dave Watson Date: Fri, 8 Aug 2014 18:28:15 +0000 (-0700) Subject: Add link-local scope handling X-Git-Tag: v0.22.0~389 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=4547b603051edf32b219d357fe1ded184348003e;p=folly.git Add link-local scope handling Summary: Also save the link-local scope in the V6 address. See D1479365 for more details Test Plan: fbconfig folly/test:network_address_test; fbmake runtests Reviewed By: simpkins@fb.com Subscribers: marccelani, doug, ps, bmatheny FB internal diff: D1486435 --- diff --git a/folly/IPAddress.cpp b/folly/IPAddress.cpp index b49b0dc8..e59caf3b 100644 --- a/folly/IPAddress.cpp +++ b/folly/IPAddress.cpp @@ -146,12 +146,20 @@ IPAddress::IPAddress(StringPiece addr) // need to check for V4 address second, since IPv4-mapped IPv6 addresses may // contain a period if (ip.find(':') != string::npos) { - in6_addr ipAddr; - if (inet_pton(AF_INET6, ip.c_str(), &ipAddr) != 1) { - throwFormatException("inet_pton failed for V6 address"); + struct addrinfo* result; + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_NUMERICHOST; + if (!getaddrinfo(ip.c_str(), nullptr, &hints, &result)) { + struct sockaddr_in6* ipAddr = (struct sockaddr_in6*)result->ai_addr; + addr_ = IPAddressV46(IPAddressV6(*ipAddr)); + family_ = AF_INET6; + freeaddrinfo(result); + } else { + throwFormatException("getsockaddr failed for V6 address"); } - addr_ = IPAddressV46(IPAddressV6(ipAddr)); - family_ = AF_INET6; } else if (ip.find('.') != string::npos) { in_addr ipAddr; if (inet_pton(AF_INET, ip.c_str(), &ipAddr) != 1) { @@ -181,7 +189,7 @@ IPAddress::IPAddress(const sockaddr* addr) } case AF_INET6: { const sockaddr_in6 *v6addr = reinterpret_cast(addr); - addr_.ipV6Addr = IPAddressV6(v6addr->sin6_addr); + addr_.ipV6Addr = IPAddressV6(*v6addr); break; } default: diff --git a/folly/IPAddress.h b/folly/IPAddress.h index 6446836f..7820fa47 100644 --- a/folly/IPAddress.h +++ b/folly/IPAddress.h @@ -206,6 +206,7 @@ class IPAddress : boost::totally_ordered { sockaddr_in6 *sin = reinterpret_cast(dest); sin->sin6_addr = asV6().toAddr(); sin->sin6_port = port; + sin->sin6_scope_id = asV6().getScopeId(); return sizeof(*sin); } else { throw InvalidAddressFamilyException(family()); diff --git a/folly/IPAddressV6.cpp b/folly/IPAddressV6.cpp index 188a4776..849c6d63 100644 --- a/folly/IPAddressV6.cpp +++ b/folly/IPAddressV6.cpp @@ -65,7 +65,18 @@ IPAddressV6::IPAddressV6(StringPiece addr) { ip = ip.substr(1, ip.size() - 2); } - if (inet_pton(AF_INET6, ip.c_str(), &addr_.in6Addr_) != 1) { + struct addrinfo* result; + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_NUMERICHOST; + if (!getaddrinfo(ip.c_str(), nullptr, &hints, &result)) { + struct sockaddr_in6* ipAddr = (struct sockaddr_in6*)result->ai_addr; + addr_.in6Addr_ = ipAddr->sin6_addr; + scope_ = ipAddr->sin6_scope_id; + freeaddrinfo(result); + } else { throw IPAddressFormatException("Invalid IPv6 address '", ip, "'"); } } @@ -76,6 +87,13 @@ IPAddressV6::IPAddressV6(const in6_addr& src) { } +// sockaddr_in6 constructor +IPAddressV6::IPAddressV6(const sockaddr_in6& src) + : addr_(src.sin6_addr) + , scope_(src.sin6_scope_id) +{ +} + // ByteArray16 constructor IPAddressV6::IPAddressV6(const ByteArray16& src) : addr_(src) @@ -108,6 +126,7 @@ void IPAddressV6::setFromBinary(ByteRange bytes) { "be 16 bytes, got ", bytes.size()); } memcpy(&addr_.in6Addr_.s6_addr, bytes.data(), sizeof(in6_addr)); + scope_ = 0; } // public @@ -309,12 +328,17 @@ IPAddressV6 IPAddressV6::mask(size_t numBits) const { // public string IPAddressV6::str() const { char buffer[INET6_ADDRSTRLEN] = {0}; - if (!inet_ntop(AF_INET6, &addr_.in6Addr_, buffer, INET6_ADDRSTRLEN)) { + sockaddr_in6 sock = toSockAddr(); + if (!getnameinfo( + (sockaddr*)&sock, sizeof(sock), + buffer, INET6_ADDRSTRLEN, + nullptr, 0, NI_NUMERICHOST)) { + string ip(buffer); + return std::move(ip); + } else { throw IPAddressFormatException("Invalid address with hex ", "'", detail::Bytes::toHex(bytes(), 16), "'"); } - string ip(buffer); - return std::move(ip); } // public diff --git a/folly/IPAddressV6.h b/folly/IPAddressV6.h index 6ef438d5..1620fb11 100644 --- a/folly/IPAddressV6.h +++ b/folly/IPAddressV6.h @@ -51,6 +51,17 @@ typedef std::array ByteArray16; * isTeredo, isIPv4Mapped, tryCreateIPv4, type * * @see IPAddress + * + * Notes on scope ID parsing: + * + * getaddrinfo() uses if_nametoindex() to convert interface names + * into a numerical index. For instance, + * "fe80::202:c9ff:fec1:ee08%eth0" may return scope ID 2 on some + * hosts, but other numbers on other hosts. It will fail entirely on + * hosts without an eth0 interface. + * + * Serializing / Deserializing IPAddressB6's on different hosts + * that use link-local scoping probably won't work. */ class IPAddressV6 : boost::totally_ordered { public: @@ -90,6 +101,7 @@ class IPAddressV6 : boost::totally_ordered { // Create an IPAddressV6 from a string // @throws IPAddressFormatException + // explicit IPAddressV6(StringPiece ip); // ByteArray16 constructor @@ -98,6 +110,9 @@ class IPAddressV6 : boost::totally_ordered { // in6_addr constructor explicit IPAddressV6(const in6_addr& src); + // sockaddr_in6 constructor + explicit IPAddressV6(const sockaddr_in6& src); + /** * Create a link-local IPAddressV6 from the specified ethernet MAC address. */ @@ -209,10 +224,16 @@ class IPAddressV6 : boost::totally_ordered { // return underlying in6_addr structure in6_addr toAddr() const { return addr_.in6Addr_; } + uint16_t getScopeId() const { return scope_; } + void setScopeId(uint16_t scope) { + scope_ = scope; + } + sockaddr_in6 toSockAddr() const { sockaddr_in6 addr; memset(&addr, 0, sizeof(sockaddr_in6)); addr.sin6_family = AF_INET6; + addr.sin6_scope_id = scope_; memcpy(&addr.sin6_addr, &addr_.in6Addr_, sizeof(in6_addr)); return addr; } @@ -294,6 +315,10 @@ class IPAddressV6 : boost::totally_ordered { explicit AddressStorage(MacAddress mac); } addr_; + // Link-local scope id. This should always be 0 for IPAddresses that + // are *not* link-local. + uint16_t scope_{0}; + static const std::array masks_; /** @@ -315,11 +340,18 @@ void toAppend(IPAddressV6 addr, fbstring* result); * Return true if two addresses are equal. */ inline bool operator==(const IPAddressV6& addr1, const IPAddressV6& addr2) { - return (std::memcmp(addr1.toAddr().s6_addr, addr2.toAddr().s6_addr, 16) == 0); + return (std::memcmp(addr1.toAddr().s6_addr, addr2.toAddr().s6_addr, 16) == 0) + && addr1.getScopeId() == addr2.getScopeId(); } // Return true if addr1 < addr2 inline bool operator<(const IPAddressV6& addr1, const IPAddressV6& addr2) { - return (std::memcmp(addr1.toAddr().s6_addr, addr2.toAddr().s6_addr, 16) < 0); + auto cmp = std::memcmp(addr1.toAddr().s6_addr, + addr2.toAddr().s6_addr, 16) < 0; + if (!cmp) { + return addr1.getScopeId() < addr2.getScopeId(); + } else { + return cmp; + } } } // folly diff --git a/folly/detail/IPAddress.h b/folly/detail/IPAddress.h index 18d53a64..ba43bf06 100644 --- a/folly/detail/IPAddress.h +++ b/folly/detail/IPAddress.h @@ -40,6 +40,7 @@ extern "C" { #endif #include +#include } #include diff --git a/folly/test/IPAddressTest.cpp b/folly/test/IPAddressTest.cpp index 952cf4aa..bdda3663 100644 --- a/folly/test/IPAddressTest.cpp +++ b/folly/test/IPAddressTest.cpp @@ -29,8 +29,8 @@ using namespace std; // tests code example TEST(IPAddress, CodeExample) { EXPECT_EQ(4, sizeof(IPAddressV4)); - EXPECT_EQ(16, sizeof(IPAddressV6)); - EXPECT_EQ(20, sizeof(IPAddress)); + EXPECT_EQ(20, sizeof(IPAddressV6)); + EXPECT_EQ(24, sizeof(IPAddress)); IPAddress uninitaddr; IPAddress v4addr("192.0.2.129"); IPAddress v6map("::ffff:192.0.2.129"); @@ -53,6 +53,24 @@ TEST(IPAddress, CodeExample) { EXPECT_TRUE(IPAddress::createIPv6(v4addr) == v6map.asV6()); } +TEST(IPAddress, Scope) { + // Test that link-local scope is saved + auto str = "fe80::62eb:69ff:fe9b:ba60%eth0"; + IPAddressV6 a2(str); + EXPECT_EQ(str, a2.str()); + + sockaddr_in6 sock = a2.toSockAddr(); + EXPECT_NE(0, sock.sin6_scope_id); + + IPAddress a1(str); + EXPECT_EQ(str, a1.str()); + + a2.setScopeId(0); + EXPECT_NE(a1, a2); + + EXPECT_TRUE(a2 < a1); +} + TEST(IPAddress, Ordering) { IPAddress a1("0.1.1.1"); IPAddress a2("1.1.1.0"); @@ -272,6 +290,7 @@ TEST(IPAddress, CtorSockaddr) { { // setup sockaddr_in6 addr; + memset(&addr, 0, sizeof(addr)); in6_addr sin_addr; ByteArray16 sec{{ // 2620:0:1cfe:face:b00c::3