• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 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_linux.h"
6 
7 #include <memory>
8 
9 #include "build/build_config.h"
10 
11 #if !BUILDFLAG(IS_ANDROID)
12 #include <linux/ethtool.h>
13 #endif  // !BUILDFLAG(IS_ANDROID)
14 #include <linux/if.h>
15 #include <linux/sockios.h>
16 #include <linux/wireless.h>
17 #include <set>
18 #include <sys/ioctl.h>
19 #include <sys/types.h>
20 
21 #include "base/feature_list.h"
22 #include "base/files/file_path.h"
23 #include "base/files/scoped_file.h"
24 #include "base/strings/escape.h"
25 #include "base/strings/string_number_conversions.h"
26 #include "base/strings/string_tokenizer.h"
27 #include "base/strings/string_util.h"
28 #include "base/threading/thread_restrictions.h"
29 #include "build/build_config.h"
30 #include "net/base/address_map_linux.h"
31 #include "net/base/address_tracker_linux.h"
32 #include "net/base/features.h"
33 #include "net/base/ip_endpoint.h"
34 #include "net/base/net_errors.h"
35 #include "net/base/network_interfaces_posix.h"
36 #include "third_party/abseil-cpp/absl/types/optional.h"
37 #include "url/gurl.h"
38 
39 #if BUILDFLAG(IS_ANDROID)
40 #include "base/android/build_info.h"
41 #include "base/strings/string_piece.h"
42 #include "net/android/network_library.h"
43 #include "net/base/network_interfaces_getifaddrs.h"
44 #endif
45 
46 namespace net {
47 
48 namespace {
49 
50 // When returning true, the platform native IPv6 address attributes were
51 // successfully converted to net IP address attributes. Otherwise, returning
52 // false and the caller should drop the IP address which can't be used by the
53 // application layer.
TryConvertNativeToNetIPAttributes(int native_attributes,int * net_attributes)54 bool TryConvertNativeToNetIPAttributes(int native_attributes,
55                                        int* net_attributes) {
56   // For Linux/ChromeOS/Android, we disallow addresses with attributes
57   // IFA_F_OPTIMISTIC, IFA_F_DADFAILED, and IFA_F_TENTATIVE as these
58   // are still progressing through duplicated address detection (DAD)
59   // and shouldn't be used by the application layer until DAD process
60   // is completed.
61   if (native_attributes & (
62 #if !BUILDFLAG(IS_ANDROID)
63                               IFA_F_OPTIMISTIC | IFA_F_DADFAILED |
64 #endif  // !BUILDFLAG(IS_ANDROID)
65                               IFA_F_TENTATIVE)) {
66     return false;
67   }
68 
69   if (native_attributes & IFA_F_TEMPORARY) {
70     *net_attributes |= IP_ADDRESS_ATTRIBUTE_TEMPORARY;
71   }
72 
73   if (native_attributes & IFA_F_DEPRECATED) {
74     *net_attributes |= IP_ADDRESS_ATTRIBUTE_DEPRECATED;
75   }
76 
77   return true;
78 }
79 
80 }  // namespace
81 
82 namespace internal {
83 
84 // Gets the connection type for interface |ifname| by checking for wireless
85 // or ethtool extensions.
GetInterfaceConnectionType(const std::string & ifname)86 NetworkChangeNotifier::ConnectionType GetInterfaceConnectionType(
87     const std::string& ifname) {
88   base::ScopedFD s = GetSocketForIoctl();
89   if (!s.is_valid())
90     return NetworkChangeNotifier::CONNECTION_UNKNOWN;
91 
92   // Test wireless extensions for CONNECTION_WIFI
93   struct iwreq pwrq = {};
94   strncpy(pwrq.ifr_name, ifname.c_str(), IFNAMSIZ - 1);
95   if (ioctl(s.get(), SIOCGIWNAME, &pwrq) != -1)
96     return NetworkChangeNotifier::CONNECTION_WIFI;
97 
98 #if !BUILDFLAG(IS_ANDROID)
99   // Test ethtool for CONNECTION_ETHERNET
100   struct ethtool_cmd ecmd = {};
101   ecmd.cmd = ETHTOOL_GSET;
102   struct ifreq ifr = {};
103   ifr.ifr_data = &ecmd;
104   strncpy(ifr.ifr_name, ifname.c_str(), IFNAMSIZ - 1);
105   if (ioctl(s.get(), SIOCETHTOOL, &ifr) != -1)
106     return NetworkChangeNotifier::CONNECTION_ETHERNET;
107 #endif  // !BUILDFLAG(IS_ANDROID)
108 
109   return NetworkChangeNotifier::CONNECTION_UNKNOWN;
110 }
111 
GetInterfaceSSID(const std::string & ifname)112 std::string GetInterfaceSSID(const std::string& ifname) {
113   base::ScopedFD ioctl_socket = GetSocketForIoctl();
114   if (!ioctl_socket.is_valid())
115     return std::string();
116   struct iwreq wreq = {};
117   strncpy(wreq.ifr_name, ifname.c_str(), IFNAMSIZ - 1);
118 
119   char ssid[IW_ESSID_MAX_SIZE + 1] = {0};
120   wreq.u.essid.pointer = ssid;
121   wreq.u.essid.length = IW_ESSID_MAX_SIZE;
122   if (ioctl(ioctl_socket.get(), SIOCGIWESSID, &wreq) != -1)
123     return ssid;
124   return std::string();
125 }
126 
GetNetworkListImpl(NetworkInterfaceList * networks,int policy,const std::unordered_set<int> & online_links,const internal::AddressTrackerLinux::AddressMap & address_map,GetInterfaceNameFunction get_interface_name)127 bool GetNetworkListImpl(
128     NetworkInterfaceList* networks,
129     int policy,
130     const std::unordered_set<int>& online_links,
131     const internal::AddressTrackerLinux::AddressMap& address_map,
132     GetInterfaceNameFunction get_interface_name) {
133   std::map<int, std::string> ifnames;
134 
135   for (const auto& it : address_map) {
136     // Ignore addresses whose links are not online.
137     if (online_links.find(it.second.ifa_index) == online_links.end())
138       continue;
139 
140     sockaddr_storage sock_addr;
141     socklen_t sock_len = sizeof(sockaddr_storage);
142 
143     // Convert to sockaddr for next check.
144     if (!IPEndPoint(it.first, 0)
145              .ToSockAddr(reinterpret_cast<sockaddr*>(&sock_addr), &sock_len)) {
146       continue;
147     }
148 
149     // Skip unspecified addresses (i.e. made of zeroes) and loopback addresses
150     if (IsLoopbackOrUnspecifiedAddress(reinterpret_cast<sockaddr*>(&sock_addr)))
151       continue;
152 
153     int ip_attributes = IP_ADDRESS_ATTRIBUTE_NONE;
154 
155     if (it.second.ifa_family == AF_INET6) {
156       // Ignore addresses whose attributes are not actionable by
157       // the application layer.
158       if (!TryConvertNativeToNetIPAttributes(it.second.ifa_flags,
159                                              &ip_attributes))
160         continue;
161     }
162 
163     // Find the name of this link.
164     std::map<int, std::string>::const_iterator itname =
165         ifnames.find(it.second.ifa_index);
166     std::string ifname;
167     if (itname == ifnames.end()) {
168       char buffer[IFNAMSIZ] = {0};
169       ifname.assign(get_interface_name(it.second.ifa_index, buffer));
170       // Ignore addresses whose interface name can't be retrieved.
171       if (ifname.empty())
172         continue;
173       ifnames[it.second.ifa_index] = ifname;
174     } else {
175       ifname = itname->second;
176     }
177 
178     // Based on the interface name and policy, determine whether we
179     // should ignore it.
180     if (ShouldIgnoreInterface(ifname, policy))
181       continue;
182 
183     NetworkChangeNotifier::ConnectionType type =
184         GetInterfaceConnectionType(ifname);
185 
186     networks->push_back(
187         NetworkInterface(ifname, ifname, it.second.ifa_index, type, it.first,
188                          it.second.ifa_prefixlen, ip_attributes));
189   }
190 
191   return true;
192 }
193 
GetWifiSSIDFromInterfaceListInternal(const NetworkInterfaceList & interfaces,internal::GetInterfaceSSIDFunction get_interface_ssid)194 std::string GetWifiSSIDFromInterfaceListInternal(
195     const NetworkInterfaceList& interfaces,
196     internal::GetInterfaceSSIDFunction get_interface_ssid) {
197   std::string connected_ssid;
198   for (size_t i = 0; i < interfaces.size(); ++i) {
199     if (interfaces[i].type != NetworkChangeNotifier::CONNECTION_WIFI)
200       return std::string();
201     std::string ssid = get_interface_ssid(interfaces[i].name);
202     if (i == 0) {
203       connected_ssid = ssid;
204     } else if (ssid != connected_ssid) {
205       return std::string();
206     }
207   }
208   return connected_ssid;
209 }
210 
GetSocketForIoctl()211 base::ScopedFD GetSocketForIoctl() {
212   base::ScopedFD ioctl_socket(socket(AF_INET6, SOCK_DGRAM, 0));
213   if (ioctl_socket.is_valid())
214     return ioctl_socket;
215   return base::ScopedFD(socket(AF_INET, SOCK_DGRAM, 0));
216 }
217 
218 }  // namespace internal
219 
GetNetworkList(NetworkInterfaceList * networks,int policy)220 bool GetNetworkList(NetworkInterfaceList* networks, int policy) {
221   if (networks == nullptr)
222     return false;
223 
224 #if BUILDFLAG(IS_ANDROID)
225   // On Android 11 RTM_GETLINK (used by AddressTrackerLinux) no longer works as
226   // per https://developer.android.com/preview/privacy/mac-address so instead
227   // use getifaddrs() which is supported since Android N.
228   base::android::BuildInfo* build_info =
229       base::android::BuildInfo::GetInstance();
230   if (build_info->sdk_int() >= base::android::SDK_VERSION_NOUGAT) {
231     // Some Samsung devices with MediaTek processors are with
232     // a buggy getifaddrs() implementation,
233     // so use a Chromium's own implementation to workaround.
234     // See https://crbug.com/1240237 for more context.
235     bool use_alternative_getifaddrs =
236         base::StringPiece(build_info->brand()) == "samsung" &&
237         base::StartsWith(build_info->hardware(), "mt");
238     bool ret = internal::GetNetworkListUsingGetifaddrs(
239         networks, policy, use_alternative_getifaddrs);
240     // Use GetInterfaceConnectionType() to sharpen up interface types.
241     for (NetworkInterface& network : *networks)
242       network.type = internal::GetInterfaceConnectionType(network.name);
243     return ret;
244   }
245 #endif  // BUILDFLAG(IS_ANDROID)
246 
247   const AddressMapOwnerLinux* map_owner = nullptr;
248   absl::optional<internal::AddressTrackerLinux> temp_tracker;
249 #if BUILDFLAG(IS_LINUX)
250   // If NetworkChangeNotifier already maintains a map owner in this process, use
251   // it.
252   if (base::FeatureList::IsEnabled(features::kAddressTrackerLinuxIsProxied)) {
253     map_owner = NetworkChangeNotifier::GetAddressMapOwner();
254   }
255 #endif  // BUILDFLAG(IS_LINUX)
256   if (!map_owner) {
257     // If there is no existing map_owner, create an AdressTrackerLinux and
258     // initialize it.
259     temp_tracker.emplace();
260     temp_tracker->Init();
261     map_owner = &temp_tracker.value();
262   }
263 
264   return internal::GetNetworkListImpl(
265       networks, policy, map_owner->GetOnlineLinks(), map_owner->GetAddressMap(),
266       &internal::AddressTrackerLinux::GetInterfaceName);
267 }
268 
GetWifiSSID()269 std::string GetWifiSSID() {
270 // On Android, obtain the SSID using the Android-specific APIs.
271 #if BUILDFLAG(IS_ANDROID)
272   return android::GetWifiSSID();
273 #else
274   NetworkInterfaceList networks;
275   if (GetNetworkList(&networks, INCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES)) {
276     return internal::GetWifiSSIDFromInterfaceListInternal(
277         networks, internal::GetInterfaceSSID);
278   }
279   return std::string();
280 #endif
281 }
282 
283 }  // namespace net
284