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