xref: /aosp_15_r20/external/cronet/net/base/network_interfaces_linux.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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