From fdb2963e0a65ececa89837eff64ffd8aca8027d2 Mon Sep 17 00:00:00 2001 From: Yi Kong Date: Tue, 22 Dec 2015 17:07:23 +0000 Subject: [PATCH] Handle AF_PACKET in getifaddr(3). Also fix a bug where we were mutating the address/broadcast address of an existing entry rather than the new entry, and use 'const' to ensure we don't make that mistake again. Change-Id: I31c127a5d21879b52c85cd0f7ed2e66554a21e39 --- libc/bionic/ifaddrs.cpp | 41 +++++++++++++++++++++++++++++++---------- tests/ifaddrs_test.cpp | 31 ++++++++++++++++++++++++++++--- 2 files changed, 59 insertions(+), 13 deletions(-) diff --git a/libc/bionic/ifaddrs.cpp b/libc/bionic/ifaddrs.cpp index c869420e6..b66883e77 100644 --- a/libc/bionic/ifaddrs.cpp +++ b/libc/bionic/ifaddrs.cpp @@ -29,6 +29,7 @@ #include #include +#include #include #include #include @@ -93,6 +94,13 @@ struct ifaddrs_storage { ifa.ifa_netmask = reinterpret_cast(&netmask); } + void SetPacketAttributes(int ifindex, unsigned short hatype, unsigned char halen) { + sockaddr_ll* sll = reinterpret_cast(&addr); + sll->sll_ifindex = ifindex; + sll->sll_hatype = hatype; + sll->sll_halen = halen; + } + private: // Returns a pointer to the first byte in the address data (which is // stored in network byte order). @@ -103,6 +111,9 @@ struct ifaddrs_storage { } else if (family == AF_INET6) { sockaddr_in6* ss6 = reinterpret_cast(ss); return reinterpret_cast(&ss6->sin6_addr); + } else if (family == AF_PACKET) { + sockaddr_ll* sll = reinterpret_cast(ss); + return reinterpret_cast(&sll->sll_addr); } return nullptr; } @@ -127,11 +138,21 @@ static void __handle_netlink_response(ifaddrs** out, nlmsghdr* hdr) { rtattr* rta = IFLA_RTA(ifi); size_t rta_len = IFLA_PAYLOAD(hdr); while (RTA_OK(rta, rta_len)) { - if (rta->rta_type == IFLA_IFNAME) { - if (RTA_PAYLOAD(rta) < sizeof(new_addr->name)) { - memcpy(new_addr->name, RTA_DATA(rta), RTA_PAYLOAD(rta)); - new_addr->ifa.ifa_name = new_addr->name; - } + if (rta->rta_type == IFLA_ADDRESS) { + if (RTA_PAYLOAD(rta) < sizeof(new_addr->addr)) { + new_addr->SetAddress(AF_PACKET, RTA_DATA(rta), RTA_PAYLOAD(rta)); + new_addr->SetPacketAttributes(ifi->ifi_index, ifi->ifi_type, RTA_PAYLOAD(rta)); + } + } else if (rta->rta_type == IFLA_BROADCAST) { + if (RTA_PAYLOAD(rta) < sizeof(new_addr->ifa_ifu)) { + new_addr->SetBroadcastAddress(AF_PACKET, RTA_DATA(rta), RTA_PAYLOAD(rta)); + new_addr->SetPacketAttributes(ifi->ifi_index, ifi->ifi_type, RTA_PAYLOAD(rta)); + } + } else if (rta->rta_type == IFLA_IFNAME) { + if (RTA_PAYLOAD(rta) < sizeof(new_addr->name)) { + memcpy(new_addr->name, RTA_DATA(rta), RTA_PAYLOAD(rta)); + new_addr->ifa.ifa_name = new_addr->name; + } } rta = RTA_NEXT(rta, rta_len); } @@ -139,9 +160,9 @@ static void __handle_netlink_response(ifaddrs** out, nlmsghdr* hdr) { ifaddrmsg* msg = reinterpret_cast(NLMSG_DATA(hdr)); // We should already know about this from an RTM_NEWLINK message. - ifaddrs_storage* addr = reinterpret_cast(*out); + const ifaddrs_storage* addr = reinterpret_cast(*out); while (addr != nullptr && addr->interface_index != static_cast(msg->ifa_index)) { - addr = reinterpret_cast(addr->ifa.ifa_next); + addr = reinterpret_cast(addr->ifa.ifa_next); } // If this is an unknown interface, ignore whatever we're being told about it. if (addr == nullptr) return; @@ -161,12 +182,12 @@ static void __handle_netlink_response(ifaddrs** out, nlmsghdr* hdr) { while (RTA_OK(rta, rta_len)) { if (rta->rta_type == IFA_ADDRESS) { if (msg->ifa_family == AF_INET || msg->ifa_family == AF_INET6) { - addr->SetAddress(msg->ifa_family, RTA_DATA(rta), RTA_PAYLOAD(rta)); - addr->SetNetmask(msg->ifa_family, msg->ifa_prefixlen); + new_addr->SetAddress(msg->ifa_family, RTA_DATA(rta), RTA_PAYLOAD(rta)); + new_addr->SetNetmask(msg->ifa_family, msg->ifa_prefixlen); } } else if (rta->rta_type == IFA_BROADCAST) { if (msg->ifa_family == AF_INET || msg->ifa_family == AF_INET6) { - addr->SetBroadcastAddress(msg->ifa_family, RTA_DATA(rta), RTA_PAYLOAD(rta)); + new_addr->SetBroadcastAddress(msg->ifa_family, RTA_DATA(rta), RTA_PAYLOAD(rta)); } } rta = RTA_NEXT(rta, rta_len); diff --git a/tests/ifaddrs_test.cpp b/tests/ifaddrs_test.cpp index 67857cbb9..bbaaec350 100644 --- a/tests/ifaddrs_test.cpp +++ b/tests/ifaddrs_test.cpp @@ -18,6 +18,9 @@ #include +#include +#include + TEST(ifaddrs, freeifaddrs_null) { freeifaddrs(nullptr); } @@ -28,11 +31,33 @@ TEST(ifaddrs, getifaddrs_smoke) { ASSERT_EQ(0, getifaddrs(&addrs)); ASSERT_TRUE(addrs != nullptr); - bool saw_loopback = false; + // We can't say much about what network interfaces are available, but we can be pretty + // sure there's a loopback interface, and that it has IPv4, IPv6, and AF_PACKET entries. + ifaddrs* lo_inet4 = nullptr; + ifaddrs* lo_inet6 = nullptr; + ifaddrs* lo_packet = nullptr; for (ifaddrs* addr = addrs; addr != nullptr; addr = addr->ifa_next) { - if (addr->ifa_name && strcmp(addr->ifa_name, "lo") == 0) saw_loopback = true; + if (addr->ifa_name && strcmp(addr->ifa_name, "lo") == 0) { + if (addr->ifa_addr && addr->ifa_addr->sa_family == AF_INET) lo_inet4 = addr; + else if (addr->ifa_addr && addr->ifa_addr->sa_family == AF_INET6) lo_inet6 = addr; + else if (addr->ifa_addr && addr->ifa_addr->sa_family == AF_PACKET) lo_packet = addr; + } } - ASSERT_TRUE(saw_loopback); + + // Does the IPv4 entry look right? + ASSERT_TRUE(lo_inet4 != nullptr); + const sockaddr_in* sa_inet4 = reinterpret_cast(lo_inet4->ifa_addr); + ASSERT_TRUE(ntohl(sa_inet4->sin_addr.s_addr) == INADDR_LOOPBACK); + + // Does the IPv6 entry look right? + ASSERT_TRUE(lo_inet6 != nullptr); + const sockaddr_in6* sa_inet6 = reinterpret_cast(lo_inet6->ifa_addr); + ASSERT_TRUE(IN6_IS_ADDR_LOOPBACK(&sa_inet6->sin6_addr)); + + // Does the AF_PACKET entry look right? + ASSERT_TRUE(lo_packet != nullptr); + const sockaddr_ll* sa_ll = reinterpret_cast(lo_packet->ifa_addr); + ASSERT_EQ(6, sa_ll->sll_halen); freeifaddrs(addrs); } -- 2.11.0