1 // Copyright 2017 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/base/network_interfaces_getifaddrs.h"
6
7 #include <ifaddrs.h>
8 #include <net/if.h>
9 #include <netinet/in.h>
10 #include <sys/types.h>
11
12 #include <memory>
13 #include <set>
14
15 #include "base/files/file_path.h"
16 #include "base/logging.h"
17 #include "base/posix/eintr_wrapper.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/strings/string_tokenizer.h"
20 #include "base/strings/string_util.h"
21 #include "base/threading/scoped_blocking_call.h"
22 #include "build/build_config.h"
23 #include "net/base/ip_endpoint.h"
24 #include "net/base/net_errors.h"
25 #include "net/base/network_interfaces_posix.h"
26
27 #if BUILDFLAG(IS_MAC)
28 #include <net/if_media.h>
29 #include <netinet/in_var.h>
30 #include <sys/ioctl.h>
31 #endif
32
33 #if BUILDFLAG(IS_ANDROID)
34 #include "base/android/build_info.h"
35 #include "net/base/network_interfaces_getifaddrs_android.h"
36 // Declare getifaddrs() and freeifaddrs() weakly as they're only available
37 // on Android N+.
38 extern "C" {
39 int getifaddrs(struct ifaddrs** __list_ptr) __attribute__((weak_import));
40 void freeifaddrs(struct ifaddrs* __ptr) __attribute__((weak_import));
41 }
42 #endif // BUILDFLAG(IS_ANDROID)
43
44 namespace net {
45 namespace internal {
46
47 #if BUILDFLAG(IS_MAC)
48
49 // MacOSX implementation of IPAttributesGetter which calls ioctl() on socket to
50 // retrieve IP attributes.
51 class IPAttributesGetterMac : public internal::IPAttributesGetter {
52 public:
53 IPAttributesGetterMac();
54 ~IPAttributesGetterMac() override;
55 bool IsInitialized() const override;
56 bool GetAddressAttributes(const ifaddrs* if_addr, int* attributes) override;
57 NetworkChangeNotifier::ConnectionType GetNetworkInterfaceType(
58 const ifaddrs* if_addr) override;
59
60 private:
61 int ioctl_socket_;
62 };
63
IPAttributesGetterMac()64 IPAttributesGetterMac::IPAttributesGetterMac()
65 : ioctl_socket_(socket(AF_INET6, SOCK_DGRAM, 0)) {
66 DCHECK_GE(ioctl_socket_, 0);
67 }
68
~IPAttributesGetterMac()69 IPAttributesGetterMac::~IPAttributesGetterMac() {
70 if (IsInitialized()) {
71 PCHECK(IGNORE_EINTR(close(ioctl_socket_)) == 0);
72 }
73 }
74
IsInitialized() const75 bool IPAttributesGetterMac::IsInitialized() const {
76 return ioctl_socket_ >= 0;
77 }
78
AddressFlagsToNetAddressAttributes(int flags)79 int AddressFlagsToNetAddressAttributes(int flags) {
80 int result = 0;
81 if (flags & IN6_IFF_TEMPORARY) {
82 result |= IP_ADDRESS_ATTRIBUTE_TEMPORARY;
83 }
84 if (flags & IN6_IFF_DEPRECATED) {
85 result |= IP_ADDRESS_ATTRIBUTE_DEPRECATED;
86 }
87 if (flags & IN6_IFF_ANYCAST) {
88 result |= IP_ADDRESS_ATTRIBUTE_ANYCAST;
89 }
90 if (flags & IN6_IFF_TENTATIVE) {
91 result |= IP_ADDRESS_ATTRIBUTE_TENTATIVE;
92 }
93 if (flags & IN6_IFF_DUPLICATED) {
94 result |= IP_ADDRESS_ATTRIBUTE_DUPLICATED;
95 }
96 if (flags & IN6_IFF_DETACHED) {
97 result |= IP_ADDRESS_ATTRIBUTE_DETACHED;
98 }
99 return result;
100 }
101
GetAddressAttributes(const ifaddrs * if_addr,int * attributes)102 bool IPAttributesGetterMac::GetAddressAttributes(const ifaddrs* if_addr,
103 int* attributes) {
104 struct in6_ifreq ifr = {};
105 strncpy(ifr.ifr_name, if_addr->ifa_name, sizeof(ifr.ifr_name) - 1);
106 memcpy(&ifr.ifr_ifru.ifru_addr, if_addr->ifa_addr, if_addr->ifa_addr->sa_len);
107 int rv = ioctl(ioctl_socket_, SIOCGIFAFLAG_IN6, &ifr);
108 if (rv >= 0) {
109 *attributes = AddressFlagsToNetAddressAttributes(ifr.ifr_ifru.ifru_flags);
110 }
111 return (rv >= 0);
112 }
113
114 NetworkChangeNotifier::ConnectionType
GetNetworkInterfaceType(const ifaddrs * if_addr)115 IPAttributesGetterMac::GetNetworkInterfaceType(const ifaddrs* if_addr) {
116 if (!IsInitialized())
117 return NetworkChangeNotifier::CONNECTION_UNKNOWN;
118
119 struct ifmediareq ifmr = {};
120 strncpy(ifmr.ifm_name, if_addr->ifa_name, sizeof(ifmr.ifm_name) - 1);
121
122 if (ioctl(ioctl_socket_, SIOCGIFMEDIA, &ifmr) != -1) {
123 if (ifmr.ifm_current & IFM_IEEE80211) {
124 return NetworkChangeNotifier::CONNECTION_WIFI;
125 }
126 if (ifmr.ifm_current & IFM_ETHER) {
127 return NetworkChangeNotifier::CONNECTION_ETHERNET;
128 }
129 }
130
131 return NetworkChangeNotifier::CONNECTION_UNKNOWN;
132 }
133
134 #endif // BUILDFLAG(IS_MAC)
135
IfaddrsToNetworkInterfaceList(int policy,const ifaddrs * interfaces,IPAttributesGetter * ip_attributes_getter,NetworkInterfaceList * networks)136 bool IfaddrsToNetworkInterfaceList(int policy,
137 const ifaddrs* interfaces,
138 IPAttributesGetter* ip_attributes_getter,
139 NetworkInterfaceList* networks) {
140 // Enumerate the addresses assigned to network interfaces which are up.
141 for (const ifaddrs* interface = interfaces; interface != nullptr;
142 interface = interface->ifa_next) {
143 // Skip loopback interfaces, and ones which are down.
144 if (!(IFF_RUNNING & interface->ifa_flags))
145 continue;
146 if (IFF_LOOPBACK & interface->ifa_flags)
147 continue;
148 // Skip interfaces with no address configured.
149 struct sockaddr* addr = interface->ifa_addr;
150 if (!addr)
151 continue;
152
153 // Skip unspecified addresses (i.e. made of zeroes) and loopback addresses
154 // configured on non-loopback interfaces.
155 if (IsLoopbackOrUnspecifiedAddress(addr))
156 continue;
157
158 std::string name = interface->ifa_name;
159 // Filter out VMware interfaces, typically named vmnet1 and vmnet8.
160 if (ShouldIgnoreInterface(name, policy)) {
161 continue;
162 }
163
164 NetworkChangeNotifier::ConnectionType connection_type =
165 NetworkChangeNotifier::CONNECTION_UNKNOWN;
166
167 int ip_attributes = IP_ADDRESS_ATTRIBUTE_NONE;
168
169 // Retrieve native ip attributes and convert to net version if a getter is
170 // given.
171 if (ip_attributes_getter && ip_attributes_getter->IsInitialized()) {
172 if (addr->sa_family == AF_INET6 &&
173 ip_attributes_getter->GetAddressAttributes(interface,
174 &ip_attributes)) {
175 // Disallow addresses with attributes ANYCASE, DUPLICATED, TENTATIVE,
176 // and DETACHED as these are still progressing through duplicated
177 // address detection (DAD) or are not suitable to be used in an
178 // one-to-one communication and shouldn't be used by the application
179 // layer.
180 if (ip_attributes &
181 (IP_ADDRESS_ATTRIBUTE_ANYCAST | IP_ADDRESS_ATTRIBUTE_DUPLICATED |
182 IP_ADDRESS_ATTRIBUTE_TENTATIVE | IP_ADDRESS_ATTRIBUTE_DETACHED)) {
183 continue;
184 }
185 }
186
187 connection_type =
188 ip_attributes_getter->GetNetworkInterfaceType(interface);
189 }
190
191 IPEndPoint address;
192
193 int addr_size = 0;
194 if (addr->sa_family == AF_INET6) {
195 addr_size = sizeof(sockaddr_in6);
196 } else if (addr->sa_family == AF_INET) {
197 addr_size = sizeof(sockaddr_in);
198 }
199
200 if (address.FromSockAddr(addr, addr_size)) {
201 uint8_t prefix_length = 0;
202 if (interface->ifa_netmask) {
203 // If not otherwise set, assume the same sa_family as ifa_addr.
204 if (interface->ifa_netmask->sa_family == 0) {
205 interface->ifa_netmask->sa_family = addr->sa_family;
206 }
207 IPEndPoint netmask;
208 if (netmask.FromSockAddr(interface->ifa_netmask, addr_size)) {
209 prefix_length = MaskPrefixLength(netmask.address());
210 }
211 }
212 networks->push_back(NetworkInterface(
213 name, name, if_nametoindex(name.c_str()), connection_type,
214 address.address(), prefix_length, ip_attributes));
215 }
216 }
217
218 return true;
219 }
220
221 } // namespace internal
222
223 // This version of GetNetworkList() can only be called on Android N+, so give it
224 // a different and internal name so it isn't invoked mistakenly.
225 #if BUILDFLAG(IS_ANDROID)
226 namespace internal {
GetNetworkListUsingGetifaddrs(NetworkInterfaceList * networks,int policy,bool use_alternative_getifaddrs)227 bool GetNetworkListUsingGetifaddrs(NetworkInterfaceList* networks,
228 int policy,
229 bool use_alternative_getifaddrs) {
230 DCHECK_GE(base::android::BuildInfo::GetInstance()->sdk_int(),
231 base::android::SDK_VERSION_NOUGAT);
232 DCHECK(getifaddrs);
233 DCHECK(freeifaddrs);
234 #else
235 bool GetNetworkList(NetworkInterfaceList* networks, int policy) {
236 constexpr bool use_alternative_getifaddrs = false;
237 #endif
238 if (networks == nullptr)
239 return false;
240
241 // getifaddrs() may require IO operations.
242 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
243 base::BlockingType::MAY_BLOCK);
244
245 ifaddrs* interfaces;
246 int getifaddrs_result;
247 if (use_alternative_getifaddrs) {
248 #if BUILDFLAG(IS_ANDROID)
249 // Chromium ships its own implementation of getifaddrs()
250 // under the name Getifaddrs.
251 getifaddrs_result = Getifaddrs(&interfaces);
252 #else
253 NOTREACHED();
254 #endif
255 } else {
256 getifaddrs_result = getifaddrs(&interfaces);
257 }
258 if (getifaddrs_result < 0) {
259 PLOG(ERROR) << "getifaddrs";
260 return false;
261 }
262
263 std::unique_ptr<internal::IPAttributesGetter> ip_attributes_getter;
264
265 #if BUILDFLAG(IS_MAC)
266 ip_attributes_getter = std::make_unique<internal::IPAttributesGetterMac>();
267 #endif
268
269 bool result = internal::IfaddrsToNetworkInterfaceList(
270 policy, interfaces, ip_attributes_getter.get(), networks);
271
272 if (use_alternative_getifaddrs) {
273 #if BUILDFLAG(IS_ANDROID)
274 Freeifaddrs(interfaces);
275 #else
276 NOTREACHED();
277 #endif
278 } else {
279 freeifaddrs(interfaces);
280 }
281 return result;
282 }
283
284 #if BUILDFLAG(IS_ANDROID)
285 } // namespace internal
286 // For Android use GetWifiSSID() impl in network_interfaces_linux.cc.
287 #else
288 std::string GetWifiSSID() {
289 NOTIMPLEMENTED();
290 return std::string();
291 }
292 #endif
293
294 } // namespace net
295