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