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_UP & interface->ifa_flags)) {
145 continue;
146 }
147 if (!(IFF_RUNNING & interface->ifa_flags))
148 continue;
149 if (IFF_LOOPBACK & interface->ifa_flags)
150 continue;
151 // Skip interfaces with no address configured.
152 struct sockaddr* addr = interface->ifa_addr;
153 if (!addr)
154 continue;
155
156 // Skip unspecified addresses (i.e. made of zeroes) and loopback addresses
157 // configured on non-loopback interfaces.
158 if (IsLoopbackOrUnspecifiedAddress(addr))
159 continue;
160
161 std::string name = interface->ifa_name;
162 // Filter out VMware interfaces, typically named vmnet1 and vmnet8.
163 if (ShouldIgnoreInterface(name, policy)) {
164 continue;
165 }
166
167 NetworkChangeNotifier::ConnectionType connection_type =
168 NetworkChangeNotifier::CONNECTION_UNKNOWN;
169
170 int ip_attributes = IP_ADDRESS_ATTRIBUTE_NONE;
171
172 // Retrieve native ip attributes and convert to net version if a getter is
173 // given.
174 if (ip_attributes_getter && ip_attributes_getter->IsInitialized()) {
175 if (addr->sa_family == AF_INET6 &&
176 ip_attributes_getter->GetAddressAttributes(interface,
177 &ip_attributes)) {
178 // Disallow addresses with attributes ANYCASE, DUPLICATED, TENTATIVE,
179 // and DETACHED as these are still progressing through duplicated
180 // address detection (DAD) or are not suitable to be used in an
181 // one-to-one communication and shouldn't be used by the application
182 // layer.
183 if (ip_attributes &
184 (IP_ADDRESS_ATTRIBUTE_ANYCAST | IP_ADDRESS_ATTRIBUTE_DUPLICATED |
185 IP_ADDRESS_ATTRIBUTE_TENTATIVE | IP_ADDRESS_ATTRIBUTE_DETACHED)) {
186 continue;
187 }
188 }
189
190 connection_type =
191 ip_attributes_getter->GetNetworkInterfaceType(interface);
192 }
193
194 IPEndPoint address;
195
196 int addr_size = 0;
197 if (addr->sa_family == AF_INET6) {
198 addr_size = sizeof(sockaddr_in6);
199 } else if (addr->sa_family == AF_INET) {
200 addr_size = sizeof(sockaddr_in);
201 }
202
203 if (address.FromSockAddr(addr, addr_size)) {
204 uint8_t prefix_length = 0;
205 if (interface->ifa_netmask) {
206 // If not otherwise set, assume the same sa_family as ifa_addr.
207 if (interface->ifa_netmask->sa_family == 0) {
208 interface->ifa_netmask->sa_family = addr->sa_family;
209 }
210 IPEndPoint netmask;
211 if (netmask.FromSockAddr(interface->ifa_netmask, addr_size)) {
212 prefix_length = MaskPrefixLength(netmask.address());
213 }
214 }
215 networks->push_back(NetworkInterface(
216 name, name, if_nametoindex(name.c_str()), connection_type,
217 address.address(), prefix_length, ip_attributes));
218 }
219 }
220
221 return true;
222 }
223
224 } // namespace internal
225
226 // This version of GetNetworkList() can only be called on Android N+, so give it
227 // a different and internal name so it isn't invoked mistakenly.
228 #if BUILDFLAG(IS_ANDROID)
229 namespace internal {
GetNetworkListUsingGetifaddrs(NetworkInterfaceList * networks,int policy,bool use_alternative_getifaddrs)230 bool GetNetworkListUsingGetifaddrs(NetworkInterfaceList* networks,
231 int policy,
232 bool use_alternative_getifaddrs) {
233 DCHECK_GE(base::android::BuildInfo::GetInstance()->sdk_int(),
234 base::android::SDK_VERSION_NOUGAT);
235 DCHECK(getifaddrs);
236 DCHECK(freeifaddrs);
237 #else
238 bool GetNetworkList(NetworkInterfaceList* networks, int policy) {
239 constexpr bool use_alternative_getifaddrs = false;
240 #endif
241 if (networks == nullptr)
242 return false;
243
244 // getifaddrs() may require IO operations.
245 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
246 base::BlockingType::MAY_BLOCK);
247
248 ifaddrs* interfaces;
249 int getifaddrs_result;
250 if (use_alternative_getifaddrs) {
251 #if BUILDFLAG(IS_ANDROID)
252 // Chromium ships its own implementation of getifaddrs()
253 // under the name Getifaddrs.
254 getifaddrs_result = Getifaddrs(&interfaces);
255 #else
256 NOTREACHED();
257 #endif
258 } else {
259 getifaddrs_result = getifaddrs(&interfaces);
260 }
261 if (getifaddrs_result < 0) {
262 PLOG(ERROR) << "getifaddrs";
263 return false;
264 }
265
266 std::unique_ptr<internal::IPAttributesGetter> ip_attributes_getter;
267
268 #if BUILDFLAG(IS_MAC)
269 ip_attributes_getter = std::make_unique<internal::IPAttributesGetterMac>();
270 #endif
271
272 bool result = internal::IfaddrsToNetworkInterfaceList(
273 policy, interfaces, ip_attributes_getter.get(), networks);
274
275 if (use_alternative_getifaddrs) {
276 #if BUILDFLAG(IS_ANDROID)
277 Freeifaddrs(interfaces);
278 #else
279 NOTREACHED();
280 #endif
281 } else {
282 freeifaddrs(interfaces);
283 }
284 return result;
285 }
286
287 #if BUILDFLAG(IS_ANDROID)
288 } // namespace internal
289 // For Android use GetWifiSSID() impl in network_interfaces_linux.cc.
290 #else
291 std::string GetWifiSSID() {
292 NOTIMPLEMENTED();
293 return std::string();
294 }
295 #endif
296
297 } // namespace net
298