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
This commit is contained in:
parent
195b85a80f
commit
fdb2963e0a
|
@ -29,6 +29,7 @@
|
|||
#include <ifaddrs.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <linux/if_packet.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <net/if.h>
|
||||
|
@ -93,6 +94,13 @@ struct ifaddrs_storage {
|
|||
ifa.ifa_netmask = reinterpret_cast<sockaddr*>(&netmask);
|
||||
}
|
||||
|
||||
void SetPacketAttributes(int ifindex, unsigned short hatype, unsigned char halen) {
|
||||
sockaddr_ll* sll = reinterpret_cast<sockaddr_ll*>(&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<sockaddr_in6*>(ss);
|
||||
return reinterpret_cast<uint8_t*>(&ss6->sin6_addr);
|
||||
} else if (family == AF_PACKET) {
|
||||
sockaddr_ll* sll = reinterpret_cast<sockaddr_ll*>(ss);
|
||||
return reinterpret_cast<uint8_t*>(&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<ifaddrmsg*>(NLMSG_DATA(hdr));
|
||||
|
||||
// We should already know about this from an RTM_NEWLINK message.
|
||||
ifaddrs_storage* addr = reinterpret_cast<ifaddrs_storage*>(*out);
|
||||
const ifaddrs_storage* addr = reinterpret_cast<const ifaddrs_storage*>(*out);
|
||||
while (addr != nullptr && addr->interface_index != static_cast<int>(msg->ifa_index)) {
|
||||
addr = reinterpret_cast<ifaddrs_storage*>(addr->ifa.ifa_next);
|
||||
addr = reinterpret_cast<const ifaddrs_storage*>(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);
|
||||
|
|
|
@ -18,6 +18,9 @@
|
|||
|
||||
#include <ifaddrs.h>
|
||||
|
||||
#include <linux/if_packet.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
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<const sockaddr_in*>(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<const sockaddr_in6*>(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<const sockaddr_ll*>(lo_packet->ifa_addr);
|
||||
ASSERT_EQ(6, sa_ll->sll_halen);
|
||||
|
||||
freeifaddrs(addrs);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue