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