xref: /aosp_15_r20/external/webrtc/rtc_base/ifaddrs_android.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1 /*
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 "rtc_base/ifaddrs_android.h"
13 
14 #include <errno.h>
15 #include <linux/netlink.h>
16 #include <linux/rtnetlink.h>
17 #include <net/if.h>
18 #include <netinet/in.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <sys/ioctl.h>
22 #include <sys/socket.h>
23 #include <sys/types.h>
24 #include <sys/utsname.h>
25 #include <unistd.h>
26 
27 #include "absl/cleanup/cleanup.h"
28 
29 namespace {
30 
31 struct netlinkrequest {
32   nlmsghdr header;
33   ifaddrmsg msg;
34 };
35 
36 const int kMaxReadSize = 4096;
37 
38 }  // namespace
39 
40 namespace rtc {
41 
set_ifname(struct ifaddrs * ifaddr,int interface)42 int set_ifname(struct ifaddrs* ifaddr, int interface) {
43   char buf[IFNAMSIZ] = {0};
44   char* name = if_indextoname(interface, buf);
45   if (name == nullptr) {
46     return -1;
47   }
48   ifaddr->ifa_name = new char[strlen(name) + 1];
49   strncpy(ifaddr->ifa_name, name, strlen(name) + 1);
50   return 0;
51 }
52 
set_flags(struct ifaddrs * ifaddr)53 int set_flags(struct ifaddrs* ifaddr) {
54   int fd = socket(AF_INET, SOCK_DGRAM, 0);
55   if (fd == -1) {
56     return -1;
57   }
58   ifreq ifr;
59   memset(&ifr, 0, sizeof(ifr));
60   strncpy(ifr.ifr_name, ifaddr->ifa_name, IFNAMSIZ - 1);
61   int rc = ioctl(fd, SIOCGIFFLAGS, &ifr);
62   close(fd);
63   if (rc == -1) {
64     return -1;
65   }
66   ifaddr->ifa_flags = ifr.ifr_flags;
67   return 0;
68 }
69 
set_addresses(struct ifaddrs * ifaddr,ifaddrmsg * msg,void * data,size_t len)70 int set_addresses(struct ifaddrs* ifaddr,
71                   ifaddrmsg* msg,
72                   void* data,
73                   size_t len) {
74   if (msg->ifa_family == AF_INET) {
75     sockaddr_in* sa = new sockaddr_in;
76     sa->sin_family = AF_INET;
77     memcpy(&sa->sin_addr, data, len);
78     ifaddr->ifa_addr = reinterpret_cast<sockaddr*>(sa);
79   } else if (msg->ifa_family == AF_INET6) {
80     sockaddr_in6* sa = new sockaddr_in6;
81     sa->sin6_family = AF_INET6;
82     sa->sin6_scope_id = msg->ifa_index;
83     memcpy(&sa->sin6_addr, data, len);
84     ifaddr->ifa_addr = reinterpret_cast<sockaddr*>(sa);
85   } else {
86     return -1;
87   }
88   return 0;
89 }
90 
make_prefixes(struct ifaddrs * ifaddr,int family,int prefixlen)91 int make_prefixes(struct ifaddrs* ifaddr, int family, int prefixlen) {
92   char* prefix = nullptr;
93   if (family == AF_INET) {
94     sockaddr_in* mask = new sockaddr_in;
95     mask->sin_family = AF_INET;
96     memset(&mask->sin_addr, 0, sizeof(in_addr));
97     ifaddr->ifa_netmask = reinterpret_cast<sockaddr*>(mask);
98     if (prefixlen > 32) {
99       prefixlen = 32;
100     }
101     prefix = reinterpret_cast<char*>(&mask->sin_addr);
102   } else if (family == AF_INET6) {
103     sockaddr_in6* mask = new sockaddr_in6;
104     mask->sin6_family = AF_INET6;
105     memset(&mask->sin6_addr, 0, sizeof(in6_addr));
106     ifaddr->ifa_netmask = reinterpret_cast<sockaddr*>(mask);
107     if (prefixlen > 128) {
108       prefixlen = 128;
109     }
110     prefix = reinterpret_cast<char*>(&mask->sin6_addr);
111   } else {
112     return -1;
113   }
114   for (int i = 0; i < (prefixlen / 8); i++) {
115     *prefix++ = 0xFF;
116   }
117   char remainder = 0xff;
118   remainder <<= (8 - prefixlen % 8);
119   *prefix = remainder;
120   return 0;
121 }
122 
populate_ifaddrs(struct ifaddrs * ifaddr,ifaddrmsg * msg,void * bytes,size_t len)123 int populate_ifaddrs(struct ifaddrs* ifaddr,
124                      ifaddrmsg* msg,
125                      void* bytes,
126                      size_t len) {
127   if (set_ifname(ifaddr, msg->ifa_index) != 0) {
128     return -1;
129   }
130   if (set_flags(ifaddr) != 0) {
131     return -1;
132   }
133   if (set_addresses(ifaddr, msg, bytes, len) != 0) {
134     return -1;
135   }
136   if (make_prefixes(ifaddr, msg->ifa_family, msg->ifa_prefixlen) != 0) {
137     return -1;
138   }
139   return 0;
140 }
141 
getifaddrs(struct ifaddrs ** result)142 int getifaddrs(struct ifaddrs** result) {
143   *result = nullptr;
144   int fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
145   if (fd < 0) {
146     return -1;
147   }
148   absl::Cleanup close_file = [fd] { close(fd); };
149 
150   netlinkrequest ifaddr_request;
151   memset(&ifaddr_request, 0, sizeof(ifaddr_request));
152   ifaddr_request.header.nlmsg_flags = NLM_F_ROOT | NLM_F_REQUEST;
153   ifaddr_request.header.nlmsg_type = RTM_GETADDR;
154   ifaddr_request.header.nlmsg_len = NLMSG_LENGTH(sizeof(ifaddrmsg));
155 
156   ssize_t count = send(fd, &ifaddr_request, ifaddr_request.header.nlmsg_len, 0);
157   if (static_cast<size_t>(count) != ifaddr_request.header.nlmsg_len) {
158     return -1;
159   }
160   struct ifaddrs* start = nullptr;
161   absl::Cleanup cleanup_start = [&start] { freeifaddrs(start); };
162   struct ifaddrs* current = nullptr;
163   char buf[kMaxReadSize];
164   ssize_t amount_read = recv(fd, &buf, kMaxReadSize, 0);
165   while (amount_read > 0) {
166     nlmsghdr* header = reinterpret_cast<nlmsghdr*>(&buf[0]);
167     size_t header_size = static_cast<size_t>(amount_read);
168     for (; NLMSG_OK(header, header_size);
169          header = NLMSG_NEXT(header, header_size)) {
170       switch (header->nlmsg_type) {
171         case NLMSG_DONE:
172           // Success. Return `start`. Cancel `start` cleanup  because it
173           // becomes callers responsibility.
174           std::move(cleanup_start).Cancel();
175           *result = start;
176           return 0;
177         case NLMSG_ERROR:
178           return -1;
179         case RTM_NEWADDR: {
180           ifaddrmsg* address_msg =
181               reinterpret_cast<ifaddrmsg*>(NLMSG_DATA(header));
182           rtattr* rta = IFA_RTA(address_msg);
183           ssize_t payload_len = IFA_PAYLOAD(header);
184           while (RTA_OK(rta, payload_len)) {
185             if ((address_msg->ifa_family == AF_INET &&
186                  rta->rta_type == IFA_LOCAL) ||
187                 (address_msg->ifa_family == AF_INET6 &&
188                  rta->rta_type == IFA_ADDRESS)) {
189               ifaddrs* newest = new ifaddrs;
190               memset(newest, 0, sizeof(ifaddrs));
191               if (current) {
192                 current->ifa_next = newest;
193               } else {
194                 start = newest;
195               }
196               if (populate_ifaddrs(newest, address_msg, RTA_DATA(rta),
197                                    RTA_PAYLOAD(rta)) != 0) {
198                 return -1;
199               }
200               current = newest;
201             }
202             rta = RTA_NEXT(rta, payload_len);
203           }
204           break;
205         }
206       }
207     }
208     amount_read = recv(fd, &buf, kMaxReadSize, 0);
209   }
210   return -1;
211 }
212 
freeifaddrs(struct ifaddrs * addrs)213 void freeifaddrs(struct ifaddrs* addrs) {
214   struct ifaddrs* last = nullptr;
215   struct ifaddrs* cursor = addrs;
216   while (cursor) {
217     delete[] cursor->ifa_name;
218     delete cursor->ifa_addr;
219     delete cursor->ifa_netmask;
220     last = cursor;
221     cursor = cursor->ifa_next;
222     delete last;
223   }
224 }
225 
226 }  // namespace rtc
227 #endif  // defined(WEBRTC_ANDROID)
228