blob: 5d022a79a9040b9fc9eec3a14357ecdd71e7bb62 [file] [log] [blame]
henrike@webrtc.orgf7795df2014-05-13 18:00:26 +00001/*
2 * Copyright 2012 The WebRTC Project Authors. All rights reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11#if defined(WEBRTC_ANDROID)
12#include "webrtc/base/ifaddrs-android.h"
13#include <stdlib.h>
14#include <string.h>
15#include <sys/types.h>
16#include <sys/socket.h>
17#include <sys/utsname.h>
18#include <sys/ioctl.h>
19#include <netinet/in.h>
20#include <net/if.h>
21#include <unistd.h>
22#include <errno.h>
23#include <linux/netlink.h>
24#include <linux/rtnetlink.h>
25
26struct netlinkrequest {
27 nlmsghdr header;
28 ifaddrmsg msg;
29};
30
31namespace {
32const int kMaxReadSize = 4096;
33};
34
35int set_ifname(struct ifaddrs* ifaddr, int interface) {
36 char buf[IFNAMSIZ] = {0};
37 char* name = if_indextoname(interface, buf);
38 if (name == NULL) {
39 return -1;
40 }
41 ifaddr->ifa_name = new char[strlen(name) + 1];
42 strncpy(ifaddr->ifa_name, name, strlen(name) + 1);
43 return 0;
44}
45
46int set_flags(struct ifaddrs* ifaddr) {
47 int fd = socket(AF_INET, SOCK_DGRAM, 0);
48 if (fd == -1) {
49 return -1;
50 }
51 ifreq ifr;
52 memset(&ifr, 0, sizeof(ifr));
53 strncpy(ifr.ifr_name, ifaddr->ifa_name, IFNAMSIZ - 1);
54 int rc = ioctl(fd, SIOCGIFFLAGS, &ifr);
55 close(fd);
56 if (rc == -1) {
57 return -1;
58 }
59 ifaddr->ifa_flags = ifr.ifr_flags;
60 return 0;
61}
62
63int set_addresses(struct ifaddrs* ifaddr, ifaddrmsg* msg, void* data,
64 size_t len) {
65 if (msg->ifa_family == AF_INET) {
66 sockaddr_in* sa = new sockaddr_in;
67 sa->sin_family = AF_INET;
68 memcpy(&sa->sin_addr, data, len);
69 ifaddr->ifa_addr = reinterpret_cast<sockaddr*>(sa);
70 } else if (msg->ifa_family == AF_INET6) {
71 sockaddr_in6* sa = new sockaddr_in6;
72 sa->sin6_family = AF_INET6;
73 sa->sin6_scope_id = msg->ifa_index;
74 memcpy(&sa->sin6_addr, data, len);
75 ifaddr->ifa_addr = reinterpret_cast<sockaddr*>(sa);
76 } else {
77 return -1;
78 }
79 return 0;
80}
81
82int make_prefixes(struct ifaddrs* ifaddr, int family, int prefixlen) {
83 char* prefix = NULL;
84 if (family == AF_INET) {
85 sockaddr_in* mask = new sockaddr_in;
86 mask->sin_family = AF_INET;
87 memset(&mask->sin_addr, 0, sizeof(in_addr));
88 ifaddr->ifa_netmask = reinterpret_cast<sockaddr*>(mask);
89 if (prefixlen > 32) {
90 prefixlen = 32;
91 }
92 prefix = reinterpret_cast<char*>(&mask->sin_addr);
93 } else if (family == AF_INET6) {
94 sockaddr_in6* mask = new sockaddr_in6;
95 mask->sin6_family = AF_INET6;
96 memset(&mask->sin6_addr, 0, sizeof(in6_addr));
97 ifaddr->ifa_netmask = reinterpret_cast<sockaddr*>(mask);
98 if (prefixlen > 128) {
99 prefixlen = 128;
100 }
101 prefix = reinterpret_cast<char*>(&mask->sin6_addr);
102 } else {
103 return -1;
104 }
105 for (int i = 0; i < (prefixlen / 8); i++) {
106 *prefix++ = 0xFF;
107 }
108 char remainder = 0xff;
109 remainder <<= (8 - prefixlen % 8);
110 *prefix = remainder;
111 return 0;
112}
113
114int populate_ifaddrs(struct ifaddrs* ifaddr, ifaddrmsg* msg, void* bytes,
115 size_t len) {
116 if (set_ifname(ifaddr, msg->ifa_index) != 0) {
117 return -1;
118 }
119 if (set_flags(ifaddr) != 0) {
120 return -1;
121 }
122 if (set_addresses(ifaddr, msg, bytes, len) != 0) {
123 return -1;
124 }
125 if (make_prefixes(ifaddr, msg->ifa_family, msg->ifa_prefixlen) != 0) {
126 return -1;
127 }
128 return 0;
129}
130
131int getifaddrs(struct ifaddrs** result) {
132 int fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
133 if (fd < 0) {
134 return -1;
135 }
136
137 netlinkrequest ifaddr_request;
138 memset(&ifaddr_request, 0, sizeof(ifaddr_request));
139 ifaddr_request.header.nlmsg_flags = NLM_F_ROOT | NLM_F_REQUEST;
140 ifaddr_request.header.nlmsg_type = RTM_GETADDR;
141 ifaddr_request.header.nlmsg_len = NLMSG_LENGTH(sizeof(ifaddrmsg));
142
143 ssize_t count = send(fd, &ifaddr_request, ifaddr_request.header.nlmsg_len, 0);
144 if (static_cast<size_t>(count) != ifaddr_request.header.nlmsg_len) {
145 close(fd);
146 return -1;
147 }
148 struct ifaddrs* start = NULL;
149 struct ifaddrs* current = NULL;
150 char buf[kMaxReadSize];
151 ssize_t amount_read = recv(fd, &buf, kMaxReadSize, 0);
152 while (amount_read > 0) {
153 nlmsghdr* header = reinterpret_cast<nlmsghdr*>(&buf[0]);
154 size_t header_size = static_cast<size_t>(amount_read);
155 for ( ; NLMSG_OK(header, header_size);
156 header = NLMSG_NEXT(header, header_size)) {
157 switch (header->nlmsg_type) {
158 case NLMSG_DONE:
159 // Success. Return.
160 *result = start;
161 close(fd);
162 return 0;
163 case NLMSG_ERROR:
164 close(fd);
165 freeifaddrs(start);
166 return -1;
167 case RTM_NEWADDR: {
168 ifaddrmsg* address_msg =
169 reinterpret_cast<ifaddrmsg*>(NLMSG_DATA(header));
170 rtattr* rta = IFA_RTA(address_msg);
171 ssize_t payload_len = IFA_PAYLOAD(header);
172 while (RTA_OK(rta, payload_len)) {
173 if (rta->rta_type == IFA_ADDRESS) {
174 int family = address_msg->ifa_family;
175 if (family == AF_INET || family == AF_INET6) {
176 ifaddrs* newest = new ifaddrs;
177 memset(newest, 0, sizeof(ifaddrs));
178 if (current) {
179 current->ifa_next = newest;
180 } else {
181 start = newest;
182 }
183 if (populate_ifaddrs(newest, address_msg, RTA_DATA(rta),
184 RTA_PAYLOAD(rta)) != 0) {
185 freeifaddrs(start);
186 *result = NULL;
187 return -1;
188 }
189 current = newest;
190 }
191 }
192 rta = RTA_NEXT(rta, payload_len);
193 }
194 break;
195 }
196 }
197 }
198 amount_read = recv(fd, &buf, kMaxReadSize, 0);
199 }
200 close(fd);
201 freeifaddrs(start);
202 return -1;
203}
204
205void freeifaddrs(struct ifaddrs* addrs) {
206 struct ifaddrs* last = NULL;
207 struct ifaddrs* cursor = addrs;
208 while (cursor) {
209 delete[] cursor->ifa_name;
210 delete cursor->ifa_addr;
211 delete cursor->ifa_netmask;
212 last = cursor;
213 cursor = cursor->ifa_next;
214 delete last;
215 }
216}
217#endif // defined(WEBRTC_ANDROID)