Commit 4547b603 authored by Dave Watson's avatar Dave Watson Committed by Sara Golemon

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
parent 46e3ed29
......@@ -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");
}
addr_ = IPAddressV46(IPAddressV6(ipAddr));
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");
}
} 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<const sockaddr_in6*>(addr);
addr_.ipV6Addr = IPAddressV6(v6addr->sin6_addr);
addr_.ipV6Addr = IPAddressV6(*v6addr);
break;
}
default:
......
......@@ -206,6 +206,7 @@ class IPAddress : boost::totally_ordered<IPAddress> {
sockaddr_in6 *sin = reinterpret_cast<sockaddr_in6*>(dest);
sin->sin6_addr = asV6().toAddr();
sin->sin6_port = port;
sin->sin6_scope_id = asV6().getScopeId();
return sizeof(*sin);
} else {
throw InvalidAddressFamilyException(family());
......
......@@ -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
......
......@@ -51,6 +51,17 @@ typedef std::array<uint8_t, 16> 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<IPAddressV6> {
public:
......@@ -90,6 +101,7 @@ class IPAddressV6 : boost::totally_ordered<IPAddressV6> {
// Create an IPAddressV6 from a string
// @throws IPAddressFormatException
//
explicit IPAddressV6(StringPiece ip);
// ByteArray16 constructor
......@@ -98,6 +110,9 @@ class IPAddressV6 : boost::totally_ordered<IPAddressV6> {
// 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<IPAddressV6> {
// 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<IPAddressV6> {
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<ByteArray16, 129> 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
......
......@@ -40,6 +40,7 @@ extern "C" {
#endif
#include <sys/types.h>
#include <netdb.h>
}
#include <folly/Conv.h>
......
......@@ -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
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment