• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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