addrs-netlink.c (9376B)
1 /* 2 Copyright (c) 2011, The WebRTC project authors. All rights reserved. 3 4 Redistribution and use in source and binary forms, with or without 5 modification, are permitted provided that the following conditions are 6 met: 7 8 * Redistributions of source code must retain the above copyright 9 notice, this list of conditions and the following disclaimer. 10 11 * Redistributions in binary form must reproduce the above copyright 12 notice, this list of conditions and the following disclaimer in 13 the documentation and/or other materials provided with the 14 distribution. 15 16 * Neither the name of Google nor the names of its contributors may 17 be used to endorse or promote products derived from this software 18 without specific prior written permission. 19 20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 #if defined(LINUX) 34 #include <net/if.h> 35 #include "addrs-netlink.h" 36 #include <csi_platform.h> 37 #include <assert.h> 38 #include <string.h> 39 #include "util.h" 40 #include "stun_util.h" 41 #include "util.h" 42 #include <r_macros.h> 43 44 #include <stdlib.h> 45 #include <sys/types.h> 46 #include <sys/socket.h> 47 #include <sys/utsname.h> 48 #include <sys/ioctl.h> 49 #include <netinet/in.h> 50 #include <unistd.h> 51 #include <errno.h> 52 #include <linux/netlink.h> 53 #include <linux/rtnetlink.h> 54 55 #ifdef ANDROID 56 /* Work around an Android NDK < r8c bug */ 57 #undef __unused 58 #else 59 #include <linux/if.h> /* struct ifreq, IFF_POINTTOPOINT */ 60 #include <linux/wireless.h> /* struct iwreq */ 61 #include <linux/ethtool.h> /* struct ethtool_cmd */ 62 #include <linux/sockios.h> /* SIOCETHTOOL */ 63 #endif /* ANDROID */ 64 65 66 struct netlinkrequest { 67 struct nlmsghdr header; 68 struct ifaddrmsg msg; 69 }; 70 71 static const int kMaxReadSize = 4096; 72 73 static void set_ifname(nr_local_addr *addr, struct ifaddrmsg* msg) { 74 assert(sizeof(addr->addr.ifname) > IF_NAMESIZE); 75 if_indextoname(msg->ifa_index, addr->addr.ifname); 76 } 77 78 static int get_siocgifflags(nr_local_addr *addr) { 79 int fd = socket(AF_INET, SOCK_DGRAM, 0); 80 if (fd == -1) { 81 assert(0); 82 return 0; 83 } 84 struct ifreq ifr; 85 memset(&ifr, 0, sizeof(ifr)); 86 strncpy(ifr.ifr_name, addr->addr.ifname, IFNAMSIZ - 1); 87 int rc = ioctl(fd, SIOCGIFFLAGS, &ifr); 88 close(fd); 89 if (rc == -1) { 90 assert(0); 91 return 0; 92 } 93 return ifr.ifr_flags; 94 } 95 96 static int set_sockaddr(nr_local_addr *addr, struct ifaddrmsg* msg, struct rtattr* rta) { 97 assert(rta->rta_type == IFA_ADDRESS || rta->rta_type == IFA_LOCAL); 98 void *data = RTA_DATA(rta); 99 size_t len = RTA_PAYLOAD(rta); 100 if (msg->ifa_family == AF_INET) { 101 struct sockaddr_in sa; 102 memset(&sa, 0, sizeof(struct sockaddr_in)); 103 sa.sin_family = AF_INET; 104 memcpy(&sa.sin_addr, data, len); 105 return nr_sockaddr_to_transport_addr((struct sockaddr*)&sa, IPPROTO_UDP, 0, &(addr->addr)); 106 } else if (msg->ifa_family == AF_INET6) { 107 struct sockaddr_in6 sa; 108 memset(&sa, 0, sizeof(struct sockaddr_in6)); 109 sa.sin6_family = AF_INET6; 110 /* We do not set sin6_scope_id to ifa_index, because that is only valid for 111 * link local addresses, and we don't use those anyway */ 112 memcpy(&sa.sin6_addr, data, len); 113 return nr_sockaddr_to_transport_addr((struct sockaddr*)&sa, IPPROTO_UDP, 0, &(addr->addr)); 114 } 115 116 return R_BAD_ARGS; 117 } 118 119 static int 120 stun_ifaddr_is_disallowed_v6(int flags) { 121 return flags & (IFA_F_TENTATIVE | IFA_F_OPTIMISTIC | IFA_F_DADFAILED | IFA_F_DEPRECATED); 122 } 123 124 static int 125 stun_convert_netlink(nr_local_addr *addr, struct ifaddrmsg *address_msg, struct rtattr* rta) 126 { 127 int r = set_sockaddr(addr, address_msg, rta); 128 if (r) { 129 r_log(NR_LOG_STUN, LOG_ERR, "set_sockaddr error r = %d", r); 130 return r; 131 } 132 133 set_ifname(addr, address_msg); 134 135 if (address_msg->ifa_flags & IFA_F_TEMPORARY) { 136 addr->flags |= NR_ADDR_FLAG_TEMPORARY; 137 } 138 139 int flags = get_siocgifflags(addr); 140 if (flags & IFF_POINTOPOINT) 141 { 142 addr->iface.type = NR_INTERFACE_TYPE_UNKNOWN | NR_INTERFACE_TYPE_VPN; 143 /* TODO (Bug 896913): find backend network type of this VPN */ 144 } 145 146 #if defined(LINUX) && !defined(ANDROID) 147 struct ethtool_cmd ecmd; 148 struct ifreq ifr; 149 struct iwreq wrq; 150 int e; 151 int s = socket(AF_INET, SOCK_DGRAM, 0); 152 153 strncpy(ifr.ifr_name, addr->addr.ifname, sizeof(ifr.ifr_name)); 154 /* TODO (Bug 896851): interface property for Android */ 155 /* Getting ethtool for ethernet information. */ 156 ecmd.cmd = ETHTOOL_GSET; 157 /* In/out param */ 158 ifr.ifr_data = (void*)&ecmd; 159 160 e = ioctl(s, SIOCETHTOOL, &ifr); 161 if (e == 0) 162 { 163 /* For wireless network, we won't get ethtool, it's a wired 164 * connection */ 165 addr->iface.type = NR_INTERFACE_TYPE_WIRED; 166 #ifdef DONT_HAVE_ETHTOOL_SPEED_HI 167 addr->iface.estimated_speed = ecmd.speed; 168 #else 169 addr->iface.estimated_speed = ((ecmd.speed_hi << 16) | ecmd.speed) * 1000; 170 #endif 171 } 172 173 strncpy(wrq.ifr_name, addr->addr.ifname, sizeof(wrq.ifr_name)); 174 e = ioctl(s, SIOCGIWRATE, &wrq); 175 if (e == 0) 176 { 177 addr->iface.type = NR_INTERFACE_TYPE_WIFI; 178 addr->iface.estimated_speed = wrq.u.bitrate.value / 1000; 179 } 180 181 close(s); 182 183 #else 184 addr->iface.type = NR_INTERFACE_TYPE_UNKNOWN; 185 addr->iface.estimated_speed = 0; 186 #endif 187 return 0; 188 } 189 190 int 191 stun_getaddrs_filtered(nr_local_addr addrs[], int maxaddrs, int *count) 192 { 193 int _status; 194 int fd = 0; 195 196 /* Scope everything else since we're using ABORT. */ 197 { 198 *count = 0; 199 200 if (maxaddrs <= 0) 201 ABORT(R_BAD_ARGS); 202 203 fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); 204 if (fd < 0) { 205 ABORT(R_INTERNAL); 206 } 207 208 struct netlinkrequest ifaddr_request; 209 memset(&ifaddr_request, 0, sizeof(ifaddr_request)); 210 ifaddr_request.header.nlmsg_flags = NLM_F_ROOT | NLM_F_REQUEST; 211 ifaddr_request.header.nlmsg_type = RTM_GETADDR; 212 ifaddr_request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); 213 214 ssize_t bytes = send(fd, &ifaddr_request, ifaddr_request.header.nlmsg_len, 0); 215 if ((size_t)bytes != ifaddr_request.header.nlmsg_len) { 216 ABORT(R_INTERNAL); 217 } 218 219 char buf[kMaxReadSize]; 220 ssize_t amount_read = recv(fd, &buf, kMaxReadSize, 0); 221 while ((amount_read > 0) && (*count != maxaddrs)) { 222 struct nlmsghdr* header = (struct nlmsghdr*)&buf[0]; 223 size_t header_size = (size_t)amount_read; 224 for ( ; NLMSG_OK(header, header_size) && (*count != maxaddrs); 225 header = NLMSG_NEXT(header, header_size)) { 226 switch (header->nlmsg_type) { 227 case NLMSG_DONE: 228 /* Success. Return. */ 229 close(fd); 230 return 0; 231 case NLMSG_ERROR: 232 ABORT(R_INTERNAL); 233 case RTM_NEWADDR: { 234 struct ifaddrmsg* address_msg = 235 (struct ifaddrmsg*)NLMSG_DATA(header); 236 struct rtattr* rta = IFA_RTA(address_msg); 237 ssize_t payload_len = IFA_PAYLOAD(header); 238 bool found = false; 239 while (RTA_OK(rta, payload_len)) { 240 // This is a bit convoluted. IFA_ADDRESS and IFA_LOCAL are the 241 // same thing except when using a POINTTOPOINT interface, in 242 // which case IFA_LOCAL is the local address, and IFA_ADDRESS is 243 // the remote address. In a reasonable world, that would mean we 244 // could just use IFA_LOCAL all the time. Sadly, IFA_LOCAL is not 245 // always set (IPv6 in particular). So, we have to be on the 246 // lookout for both, and prefer IFA_LOCAL when present. 247 if (rta->rta_type == IFA_ADDRESS || rta->rta_type == IFA_LOCAL) { 248 int family = address_msg->ifa_family; 249 if ((family == AF_INET || family == AF_INET6) && 250 !stun_ifaddr_is_disallowed_v6(address_msg->ifa_flags) && 251 !stun_convert_netlink(&addrs[*count], address_msg, rta)) { 252 found = true; 253 if (rta->rta_type == IFA_LOCAL) { 254 // IFA_LOCAL is what we really want; if we find it we're 255 // done. If this is IFA_ADDRESS instead, we do not proceed 256 // yet, and allow a subsequent IFA_LOCAL to overwrite what 257 // we just put in |addrs|. 258 break; 259 } 260 } 261 } 262 /* TODO: Use IFA_LABEL instead of if_indextoname? We would need 263 * to remember how many nr_local_addr we've converted for this 264 * ifaddrmsg, and set the label on all of them. */ 265 rta = RTA_NEXT(rta, payload_len); 266 } 267 268 if (found) { 269 ++(*count); 270 } 271 break; 272 } 273 } 274 } 275 amount_read = recv(fd, &buf, kMaxReadSize, 0); 276 } 277 } 278 279 _status=0; 280 abort: 281 close(fd); 282 return(_status); 283 } 284 285 #endif /* defined(LINUX) */