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