• 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_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