ifaddrs.cpp revision 0945ed5cc5921243724fed4465d20881f4891a8d
1/* 2 * Copyright (C) 2015 The Android Open Source Project 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in 12 * the documentation and/or other materials provided with the 13 * distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29#include <ifaddrs.h> 30 31#include <errno.h> 32#include <linux/netlink.h> 33#include <linux/rtnetlink.h> 34#include <net/if.h> 35#include <netinet/in.h> 36#include <stdint.h> 37#include <stdio.h> 38#include <stdlib.h> 39#include <string.h> 40#include <unistd.h> 41 42// The public ifaddrs struct is full of pointers. Rather than track several 43// different allocations, we use a maximally-sized structure with the public 44// part at offset 0, and pointers into its hidden tail. 45struct ifaddrs_storage { 46 // Must come first, so that `ifaddrs_storage` is-a `ifaddrs`. 47 ifaddrs ifa; 48 49 // The interface index, so we can match RTM_NEWADDR messages with 50 // earlier RTM_NEWLINK messages (to copy the interface flags). 51 int interface_index; 52 53 // Storage for the pointers in `ifa`. 54 sockaddr_storage addr; 55 sockaddr_storage netmask; 56 sockaddr_storage ifa_ifu; 57 char name[IFNAMSIZ + 1]; 58 59 ifaddrs_storage(ifaddrs** list) { 60 memset(this, 0, sizeof(*this)); 61 62 // push_front onto `list`. 63 ifa.ifa_next = *list; 64 *list = reinterpret_cast<ifaddrs*>(this); 65 } 66 67 // Netlink gives us the address family in the header, and the 68 // sockaddr_in or sockaddr_in6 bytes as the payload. We need to 69 // stitch the two bits together into the sockaddr that's part of 70 // our portable interface. 71 void SetAddress(int family, const void* data, size_t byteCount) { 72 addr.ss_family = family; 73 memcpy(SockaddrBytes(family, &addr), data, byteCount); 74 ifa.ifa_addr = reinterpret_cast<sockaddr*>(&addr); 75 } 76 77 void SetBroadcastAddress(int family, const void* data, size_t byteCount) { 78 ifa_ifu.ss_family = family; 79 memcpy(SockaddrBytes(family, &ifa_ifu), data, byteCount); 80 ifa.ifa_dstaddr = reinterpret_cast<sockaddr*>(&ifa_ifu); 81 } 82 83 // Netlink gives us the prefix length as a bit count. We need to turn 84 // that into a BSD-compatible netmask represented by a sockaddr*. 85 void SetNetmask(int family, size_t prefix_length) { 86 // ...and work out the netmask from the prefix length. 87 netmask.ss_family = family; 88 uint8_t* dst = SockaddrBytes(family, &netmask); 89 memset(dst, 0xff, prefix_length / 8); 90 if ((prefix_length % 8) != 0) { 91 dst[prefix_length/8] = (0xff << (8 - (prefix_length % 8))); 92 } 93 ifa.ifa_netmask = reinterpret_cast<sockaddr*>(&netmask); 94 } 95 96 private: 97 // Returns a pointer to the first byte in the address data (which is 98 // stored in network byte order). 99 uint8_t* SockaddrBytes(int family, sockaddr_storage* ss) { 100 if (family == AF_INET) { 101 sockaddr_in* ss4 = reinterpret_cast<sockaddr_in*>(ss); 102 return reinterpret_cast<uint8_t*>(&ss4->sin_addr); 103 } else if (family == AF_INET6) { 104 sockaddr_in6* ss6 = reinterpret_cast<sockaddr_in6*>(ss); 105 return reinterpret_cast<uint8_t*>(&ss6->sin6_addr); 106 } 107 return nullptr; 108 } 109}; 110 111static void __handle_netlink_response(ifaddrs** out, nlmsghdr* hdr) { 112 if (hdr->nlmsg_type == RTM_NEWLINK) { 113 ifinfomsg* ifi = reinterpret_cast<ifinfomsg*>(NLMSG_DATA(hdr)); 114 115 // Create a new ifaddr entry, and set the interface index and flags. 116 ifaddrs_storage* new_addr = new ifaddrs_storage(out); 117 new_addr->interface_index = ifi->ifi_index; 118 new_addr->ifa.ifa_flags = ifi->ifi_flags; 119 120 // Go through the various bits of information and find the name. 121 rtattr* rta = IFLA_RTA(ifi); 122 size_t rta_len = IFLA_PAYLOAD(hdr); 123 while (RTA_OK(rta, rta_len)) { 124 if (rta->rta_type == IFLA_IFNAME) { 125 if (RTA_PAYLOAD(rta) < sizeof(new_addr->name)) { 126 memcpy(new_addr->name, RTA_DATA(rta), RTA_PAYLOAD(rta)); 127 new_addr->ifa.ifa_name = new_addr->name; 128 } 129 } 130 rta = RTA_NEXT(rta, rta_len); 131 } 132 } else if (hdr->nlmsg_type == RTM_NEWADDR) { 133 ifaddrmsg* msg = reinterpret_cast<ifaddrmsg*>(NLMSG_DATA(hdr)); 134 135 // We should already know about this from an RTM_NEWLINK message. 136 ifaddrs_storage* addr = reinterpret_cast<ifaddrs_storage*>(*out); 137 while (addr != nullptr && addr->interface_index != static_cast<int>(msg->ifa_index)) { 138 addr = reinterpret_cast<ifaddrs_storage*>(addr->ifa.ifa_next); 139 } 140 // If this is an unknown interface, ignore whatever we're being told about it. 141 if (addr == nullptr) return; 142 143 // Create a new ifaddr entry and copy what we already know. 144 ifaddrs_storage* new_addr = new ifaddrs_storage(out); 145 // We can just copy the name rather than look for IFA_LABEL. 146 strcpy(new_addr->name, addr->name); 147 new_addr->ifa.ifa_name = new_addr->name; 148 new_addr->ifa.ifa_flags = addr->ifa.ifa_flags; 149 new_addr->interface_index = addr->interface_index; 150 151 // Go through the various bits of information and find the address 152 // and any broadcast/destination address. 153 rtattr* rta = IFA_RTA(msg); 154 size_t rta_len = IFA_PAYLOAD(hdr); 155 while (RTA_OK(rta, rta_len)) { 156 if (rta->rta_type == IFA_ADDRESS) { 157 if (msg->ifa_family == AF_INET || msg->ifa_family == AF_INET6) { 158 addr->SetAddress(msg->ifa_family, RTA_DATA(rta), RTA_PAYLOAD(rta)); 159 addr->SetNetmask(msg->ifa_family, msg->ifa_prefixlen); 160 } 161 } else if (rta->rta_type == IFA_BROADCAST) { 162 if (msg->ifa_family == AF_INET || msg->ifa_family == AF_INET6) { 163 addr->SetBroadcastAddress(msg->ifa_family, RTA_DATA(rta), RTA_PAYLOAD(rta)); 164 } 165 } 166 rta = RTA_NEXT(rta, rta_len); 167 } 168 } 169} 170 171static bool __send_netlink_request(int fd, int type) { 172 struct NetlinkMessage { 173 nlmsghdr hdr; 174 rtgenmsg msg; 175 } request; 176 memset(&request, 0, sizeof(request)); 177 request.hdr.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; 178 request.hdr.nlmsg_type = type; 179 request.hdr.nlmsg_len = sizeof(request); 180 request.msg.rtgen_family = AF_UNSPEC; // All families. 181 return (TEMP_FAILURE_RETRY(send(fd, &request, sizeof(request), 0)) == sizeof(request)); 182} 183 184static bool __read_netlink_responses(int fd, ifaddrs** out, char* buf, size_t buf_len) { 185 ssize_t bytes_read; 186 // Read through all the responses, handing interesting ones to __handle_netlink_response. 187 while ((bytes_read = TEMP_FAILURE_RETRY(recv(fd, buf, buf_len, 0))) > 0) { 188 nlmsghdr* hdr = reinterpret_cast<nlmsghdr*>(buf); 189 for (; NLMSG_OK(hdr, static_cast<size_t>(bytes_read)); hdr = NLMSG_NEXT(hdr, bytes_read)) { 190 if (hdr->nlmsg_type == NLMSG_DONE) return true; 191 if (hdr->nlmsg_type == NLMSG_ERROR) return false; 192 __handle_netlink_response(out, hdr); 193 } 194 } 195 // We only get here if recv fails before we see a NLMSG_DONE. 196 return false; 197} 198 199int getifaddrs(ifaddrs** out) { 200 // Make cleanup easy. 201 *out = nullptr; 202 203 // The kernel keeps packets under 8KiB (NLMSG_GOODSIZE), 204 // but that's a bit too large to go on the stack. 205 size_t buf_len = 8192; 206 char* buf = new char[buf_len]; 207 if (buf == nullptr) return -1; 208 209 // Open the netlink socket and ask for all the links and addresses. 210 int fd = socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE); 211 bool okay = fd != -1 && 212 __send_netlink_request(fd, RTM_GETLINK) && __read_netlink_responses(fd, out, buf, buf_len) && 213 __send_netlink_request(fd, RTM_GETADDR) && __read_netlink_responses(fd, out, buf, buf_len); 214 215 if (!okay) { 216 freeifaddrs(*out); 217 // Ensure that callers crash if they forget to check for success. 218 *out = nullptr; 219 } 220 { 221 int saved_errno = errno; 222 close(fd); 223 delete[] buf; 224 errno = saved_errno; 225 } 226 return okay ? 0 : -1; 227} 228 229void freeifaddrs(ifaddrs* list) { 230 while (list != nullptr) { 231 ifaddrs* current = list; 232 list = list->ifa_next; 233 free(current); 234 } 235} 236