diff options
author | Elliott Hughes <enh@google.com> | 2016-02-17 14:19:48 -0800 |
---|---|---|
committer | Elliott Hughes <enh@google.com> | 2016-02-17 14:38:09 -0800 |
commit | 7dac8b8aabadbf2dcff20d3646e701728ba3777d (patch) | |
tree | bf5889601dc253dd00c736d1460e6cd1f324b322 | |
parent | 2de48bc809bba48a7f209e209b3822bad010166f (diff) | |
download | bionic-7dac8b8aabadbf2dcff20d3646e701728ba3777d.tar.gz |
Fix scope ids for link-local IPv6 addresses from getifaddrs(3).
Bug: http://b/27219454
Change-Id: I7a166ff5553565f7afdab18dd2c703af4d475ab4
-rw-r--r-- | libc/bionic/ifaddrs.cpp | 28 | ||||
-rw-r--r-- | libc/include/netinet/in6.h | 94 | ||||
-rw-r--r-- | tests/ifaddrs_test.cpp | 22 |
3 files changed, 81 insertions, 63 deletions
diff --git a/libc/bionic/ifaddrs.cpp b/libc/bionic/ifaddrs.cpp index d03d76f97..1fb16d4f1 100644 --- a/libc/bionic/ifaddrs.cpp +++ b/libc/bionic/ifaddrs.cpp @@ -67,20 +67,12 @@ struct ifaddrs_storage { *list = reinterpret_cast<ifaddrs*>(this); } - // Netlink gives us the address family in the header, and the - // sockaddr_in or sockaddr_in6 bytes as the payload. We need to - // stitch the two bits together into the sockaddr that's part of - // our portable interface. void SetAddress(int family, const void* data, size_t byteCount) { - addr.ss_family = family; - memcpy(SockaddrBytes(family, &addr), data, byteCount); - ifa.ifa_addr = reinterpret_cast<sockaddr*>(&addr); + ifa.ifa_addr = CopyAddress(family, data, byteCount, &addr); } void SetBroadcastAddress(int family, const void* data, size_t byteCount) { - ifa_ifu.ss_family = family; - memcpy(SockaddrBytes(family, &ifa_ifu), data, byteCount); - ifa.ifa_dstaddr = reinterpret_cast<sockaddr*>(&ifa_ifu); + ifa.ifa_dstaddr = CopyAddress(family, data, byteCount, &ifa_ifu); } // Netlink gives us the prefix length as a bit count. We need to turn @@ -104,6 +96,22 @@ struct ifaddrs_storage { } private: + sockaddr* CopyAddress(int family, const void* data, size_t byteCount, sockaddr_storage* ss) { + // Netlink gives us the address family in the header, and the + // sockaddr_in or sockaddr_in6 bytes as the payload. We need to + // stitch the two bits together into the sockaddr that's part of + // our portable interface. + ss->ss_family = family; + memcpy(SockaddrBytes(family, ss), data, byteCount); + + // For IPv6 we might also have to set the scope id. + if (family == AF_INET6 && (IN6_IS_ADDR_LINKLOCAL(data) || IN6_IS_ADDR_MC_LINKLOCAL(data))) { + reinterpret_cast<sockaddr_in6*>(ss)->sin6_scope_id = interface_index; + } + + return reinterpret_cast<sockaddr*>(ss); + } + // Returns a pointer to the first byte in the address data (which is // stored in network byte order). uint8_t* SockaddrBytes(int family, sockaddr_storage* ss) { diff --git a/libc/include/netinet/in6.h b/libc/include/netinet/in6.h index 7f3286ae9..879ac7536 100644 --- a/libc/include/netinet/in6.h +++ b/libc/include/netinet/in6.h @@ -25,48 +25,47 @@ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ + #ifndef _NETINET_IN6_H #define _NETINET_IN6_H #include <linux/in6.h> -#define IN6_IS_ADDR_UNSPECIFIED(a) \ - ((*(const uint32_t *)(const void *)(&(a)->s6_addr[0]) == 0) && \ - (*(const uint32_t *)(const void *)(&(a)->s6_addr[4]) == 0) && \ - (*(const uint32_t *)(const void *)(&(a)->s6_addr[8]) == 0) && \ - (*(const uint32_t *)(const void *)(&(a)->s6_addr[12]) == 0)) +#define IN6_IS_ADDR_UNSPECIFIED(a) \ + ((*(const uint32_t*)(&(a)->s6_addr[0]) == 0) && \ + (*(const uint32_t*)(&(a)->s6_addr[4]) == 0) && \ + (*(const uint32_t*)(&(a)->s6_addr[8]) == 0) && \ + (*(const uint32_t*)(&(a)->s6_addr[12]) == 0)) -#define IN6_IS_ADDR_LOOPBACK(a) \ - ((*(const uint32_t *)(const void *)(&(a)->s6_addr[0]) == 0) && \ - (*(const uint32_t *)(const void *)(&(a)->s6_addr[4]) == 0) && \ - (*(const uint32_t *)(const void *)(&(a)->s6_addr[8]) == 0) && \ - (*(const uint32_t *)(const void *)(&(a)->s6_addr[12]) == ntohl(1))) +#define IN6_IS_ADDR_LOOPBACK(a) \ + ((*(const uint32_t*)(&(a)->s6_addr[0]) == 0) && \ + (*(const uint32_t*)(&(a)->s6_addr[4]) == 0) && \ + (*(const uint32_t*)(&(a)->s6_addr[8]) == 0) && \ + (*(const uint32_t*)(&(a)->s6_addr[12]) == ntohl(1))) -#define IN6_IS_ADDR_V4COMPAT(a) \ - ((*(const uint32_t *)(const void *)(&(a)->s6_addr[0]) == 0) && \ - (*(const uint32_t *)(const void *)(&(a)->s6_addr[4]) == 0) && \ - (*(const uint32_t *)(const void *)(&(a)->s6_addr[8]) == 0) && \ - (*(const uint32_t *)(const void *)(&(a)->s6_addr[12]) != 0) && \ - (*(const uint32_t *)(const void *)(&(a)->s6_addr[12]) != ntohl(1))) +#define IN6_IS_ADDR_V4COMPAT(a) \ + ((*(const uint32_t*)(&(a)->s6_addr[0]) == 0) && \ + (*(const uint32_t*)(&(a)->s6_addr[4]) == 0) && \ + (*(const uint32_t*)(&(a)->s6_addr[8]) == 0) && \ + (*(const uint32_t*)(&(a)->s6_addr[12]) != 0) && \ + (*(const uint32_t*)(&(a)->s6_addr[12]) != ntohl(1))) -#define IN6_IS_ADDR_V4MAPPED(a) \ - ((*(const uint32_t *)(const void *)(&(a)->s6_addr[0]) == 0) && \ - (*(const uint32_t *)(const void *)(&(a)->s6_addr[4]) == 0) && \ - (*(const uint32_t *)(const void *)(&(a)->s6_addr[8]) == ntohl(0x0000ffff))) +#define IN6_IS_ADDR_V4MAPPED(a) \ + ((*(const uint32_t*)(&(a)->s6_addr[0]) == 0) && \ + (*(const uint32_t*)(&(a)->s6_addr[4]) == 0) && \ + (*(const uint32_t*)(&(a)->s6_addr[8]) == ntohl(0x0000ffff))) -#define IN6_IS_ADDR_LINKLOCAL(a) \ - (((a)->s6_addr[0] == 0xfe) && (((a)->s6_addr[1] & 0xc0) == 0x80)) +#define __bionic_s6_addr(a) ((const uint8_t*)(a)) -#define IN6_IS_ADDR_SITELOCAL(a) \ - (((a)->s6_addr[0] == 0xfe) && (((a)->s6_addr[1] & 0xc0) == 0xc0)) +#define IN6_IS_ADDR_LINKLOCAL(a) \ + ((__bionic_s6_addr(a)[0] == 0xfe) && ((__bionic_s6_addr(a)[1] & 0xc0) == 0x80)) -/* RFC 4193. */ -#define IN6_IS_ADDR_ULA(a) \ - (((a)->s6_addr[0] & 0xfe) == 0xfc) +#define IN6_IS_ADDR_SITELOCAL(a) \ + ((__bionic_s6_addr(a)[0] == 0xfe) && ((__bionic_s6_addr(a)[1] & 0xc0) == 0xc0)) -#define IN6_IS_ADDR_MULTICAST(a) \ - (((__const uint8_t *) (a))[0] == 0xff) +#define IN6_IS_ADDR_MULTICAST(a) (__bionic_s6_addr(a)[0] == 0xff) +#define IN6_IS_ADDR_ULA(a) ((__bionic_s6_addr(a)[0] & 0xfe) == 0xfc) #define IPV6_ADDR_SCOPE_NODELOCAL 0x01 #define IPV6_ADDR_SCOPE_INTFACELOCAL 0x01 @@ -75,27 +74,21 @@ #define IPV6_ADDR_SCOPE_ORGLOCAL 0x08 #define IPV6_ADDR_SCOPE_GLOBAL 0x0e -#define IPV6_ADDR_MC_SCOPE(a) \ - ((a)->s6_addr[1] & 0x0f) - -#define IN6_IS_ADDR_MC_NODELOCAL(a) \ - (IN6_IS_ADDR_MULTICAST(a) && \ - (IPV6_ADDR_MC_SCOPE(a) == IPV6_ADDR_SCOPE_NODELOCAL)) -#define IN6_IS_ADDR_MC_LINKLOCAL(a) \ - (IN6_IS_ADDR_MULTICAST(a) && \ - (IPV6_ADDR_MC_SCOPE(a) == IPV6_ADDR_SCOPE_LINKLOCAL)) -#define IN6_IS_ADDR_MC_SITELOCAL(a) \ - (IN6_IS_ADDR_MULTICAST(a) && \ - (IPV6_ADDR_MC_SCOPE(a) == IPV6_ADDR_SCOPE_SITELOCAL)) -#define IN6_IS_ADDR_MC_ORGLOCAL(a) \ - (IN6_IS_ADDR_MULTICAST(a) && \ - (IPV6_ADDR_MC_SCOPE(a) == IPV6_ADDR_SCOPE_ORGLOCAL)) -#define IN6_IS_ADDR_MC_GLOBAL(a) \ - (IN6_IS_ADDR_MULTICAST(a) && \ - (IPV6_ADDR_MC_SCOPE(a) == IPV6_ADDR_SCOPE_GLOBAL)) - -#define IN6_ARE_ADDR_EQUAL(a, b) \ - (memcmp(&(a)->s6_addr[0], &(b)->s6_addr[0], sizeof(struct in6_addr)) == 0) +#define IPV6_ADDR_MC_SCOPE(a) (__bionic_s6_addr(a)[1] & 0x0f) + +#define IN6_IS_ADDR_MC_NODELOCAL(a) \ + (IN6_IS_ADDR_MULTICAST(a) && (IPV6_ADDR_MC_SCOPE(a) == IPV6_ADDR_SCOPE_NODELOCAL)) +#define IN6_IS_ADDR_MC_LINKLOCAL(a) \ + (IN6_IS_ADDR_MULTICAST(a) && (IPV6_ADDR_MC_SCOPE(a) == IPV6_ADDR_SCOPE_LINKLOCAL)) +#define IN6_IS_ADDR_MC_SITELOCAL(a) \ + (IN6_IS_ADDR_MULTICAST(a) && (IPV6_ADDR_MC_SCOPE(a) == IPV6_ADDR_SCOPE_SITELOCAL)) +#define IN6_IS_ADDR_MC_ORGLOCAL(a) \ + (IN6_IS_ADDR_MULTICAST(a) && (IPV6_ADDR_MC_SCOPE(a) == IPV6_ADDR_SCOPE_ORGLOCAL)) +#define IN6_IS_ADDR_MC_GLOBAL(a) \ + (IN6_IS_ADDR_MULTICAST(a) && (IPV6_ADDR_MC_SCOPE(a) == IPV6_ADDR_SCOPE_GLOBAL)) + +#define IN6_ARE_ADDR_EQUAL(a, b) \ + (memcmp(&(a)->s6_addr[0], &(b)->s6_addr[0], sizeof(struct in6_addr)) == 0) #define INET6_ADDRSTRLEN 46 @@ -107,5 +100,4 @@ #define ipv6mr_interface ipv6mr_ifindex - #endif /* _NETINET_IN6_H */ diff --git a/tests/ifaddrs_test.cpp b/tests/ifaddrs_test.cpp index 2c34633b2..c3f027319 100644 --- a/tests/ifaddrs_test.cpp +++ b/tests/ifaddrs_test.cpp @@ -93,8 +93,7 @@ TEST(ifaddrs, getifaddrs_interfaces) { std::vector<std::string> sys_class_net; { - auto dir_deleter = [](DIR* handle) { if (handle) closedir(handle); }; - std::unique_ptr<DIR, decltype(dir_deleter)> d(opendir("/sys/class/net"), dir_deleter); + std::unique_ptr<DIR, decltype(&closedir)> d(opendir("/sys/class/net"), closedir); ASSERT_TRUE(d != nullptr); dirent* dir; while ((dir = readdir(d.get())) != nullptr) { @@ -150,6 +149,7 @@ TEST(ifaddrs, getifaddrs_INET) { for (auto ub = addrs.upper_bound(it->first); it != ub; ++it) { if (it->second == addr) { found = true; + break; } } EXPECT_TRUE(found) << if_name; @@ -220,3 +220,21 @@ TEST(ifaddrs, dump) { freeifaddrs(addrs); } + +TEST(ifaddrs, inet6_scope_ids) { + ifaddrs* addrs; + ASSERT_EQ(0, getifaddrs(&addrs)); + + for (ifaddrs* ifa = addrs; ifa != nullptr; ifa = ifa->ifa_next) { + if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET6) { + sockaddr_in6* sa6 = reinterpret_cast<sockaddr_in6*>(ifa->ifa_addr); + // Any link-local IPv6 address should have a scope id. (http://b/27219454.) + // 0 isn't a valid interface index, so that would mean the scope id wasn't set. + if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr) || IN6_IS_ADDR_MC_LINKLOCAL(&sa6->sin6_addr)) { + ASSERT_NE(sa6->sin6_scope_id, 0U); + } + } + } + + freeifaddrs(addrs); +} |