#include "net/base/network_interfaces_linux.h"
#include <memory>
#include "build/build_config.h"
#if !BUILDFLAG(IS_ANDROID)
#include <linux/ethtool.h>
#endif
#include <linux/if.h>
#include <linux/sockios.h>
#include <linux/wireless.h>
#include <set>
#include <sys/ioctl.h>
#include <sys/types.h>
#include "base/feature_list.h"
#include "base/files/file_path.h"
#include "base/files/scoped_file.h"
#include "base/strings/escape.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_tokenizer.h"
#include "base/strings/string_util.h"
#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
#include "net/base/address_map_linux.h"
#include "net/base/address_tracker_linux.h"
#include "net/base/features.h"
#include "net/base/ip_endpoint.h"
#include "net/base/net_errors.h"
#include "net/base/network_interfaces_posix.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "url/gurl.h"
#if BUILDFLAG(IS_ANDROID)
#include "base/android/build_info.h"
#include "base/strings/string_piece.h"
#include "net/android/network_library.h"
#include "net/base/network_interfaces_getifaddrs.h"
#endif
#if BUILDFLAG(IS_OHOS)
#include "net/base/network_interfaces_getifaddrs.h"
#endif
namespace net {
namespace {
bool TryConvertNativeToNetIPAttributes(int native_attributes,
int* net_attributes) {
if (native_attributes & (
#if !BUILDFLAG(IS_ANDROID)
IFA_F_OPTIMISTIC | IFA_F_DADFAILED |
#endif
IFA_F_TENTATIVE)) {
return false;
}
if (native_attributes & IFA_F_TEMPORARY) {
*net_attributes |= IP_ADDRESS_ATTRIBUTE_TEMPORARY;
}
if (native_attributes & IFA_F_DEPRECATED) {
*net_attributes |= IP_ADDRESS_ATTRIBUTE_DEPRECATED;
}
return true;
}
}
namespace internal {
NetworkChangeNotifier::ConnectionType GetInterfaceConnectionType(
const std::string& ifname) {
base::ScopedFD s = GetSocketForIoctl();
if (!s.is_valid())
return NetworkChangeNotifier::CONNECTION_UNKNOWN;
struct iwreq pwrq = {};
strncpy(pwrq.ifr_name, ifname.c_str(), IFNAMSIZ - 1);
if (ioctl(s.get(), SIOCGIWNAME, &pwrq) != -1)
return NetworkChangeNotifier::CONNECTION_WIFI;
#if !BUILDFLAG(IS_ANDROID)
struct ethtool_cmd ecmd = {};
ecmd.cmd = ETHTOOL_GSET;
struct ifreq ifr = {};
ifr.ifr_data = &ecmd;
strncpy(ifr.ifr_name, ifname.c_str(), IFNAMSIZ - 1);
if (ioctl(s.get(), SIOCETHTOOL, &ifr) != -1)
return NetworkChangeNotifier::CONNECTION_ETHERNET;
#endif
return NetworkChangeNotifier::CONNECTION_UNKNOWN;
}
std::string GetInterfaceSSID(const std::string& ifname) {
base::ScopedFD ioctl_socket = GetSocketForIoctl();
if (!ioctl_socket.is_valid())
return std::string();
struct iwreq wreq = {};
strncpy(wreq.ifr_name, ifname.c_str(), IFNAMSIZ - 1);
char ssid[IW_ESSID_MAX_SIZE + 1] = {0};
wreq.u.essid.pointer = ssid;
wreq.u.essid.length = IW_ESSID_MAX_SIZE;
if (ioctl(ioctl_socket.get(), SIOCGIWESSID, &wreq) != -1)
return ssid;
return std::string();
}
bool GetNetworkListImpl(
NetworkInterfaceList* networks,
int policy,
const std::unordered_set<int>& online_links,
const internal::AddressTrackerLinux::AddressMap& address_map,
GetInterfaceNameFunction get_interface_name) {
std::map<int, std::string> ifnames;
for (const auto& it : address_map) {
if (online_links.find(it.second.ifa_index) == online_links.end())
continue;
sockaddr_storage sock_addr;
socklen_t sock_len = sizeof(sockaddr_storage);
if (!IPEndPoint(it.first, 0)
.ToSockAddr(reinterpret_cast<sockaddr*>(&sock_addr), &sock_len)) {
continue;
}
if (IsLoopbackOrUnspecifiedAddress(reinterpret_cast<sockaddr*>(&sock_addr)))
continue;
int ip_attributes = IP_ADDRESS_ATTRIBUTE_NONE;
if (it.second.ifa_family == AF_INET6) {
if (!TryConvertNativeToNetIPAttributes(it.second.ifa_flags,
&ip_attributes))
continue;
}
std::map<int, std::string>::const_iterator itname =
ifnames.find(it.second.ifa_index);
std::string ifname;
if (itname == ifnames.end()) {
char buffer[IFNAMSIZ] = {0};
ifname.assign(get_interface_name(it.second.ifa_index, buffer));
if (ifname.empty())
continue;
ifnames[it.second.ifa_index] = ifname;
} else {
ifname = itname->second;
}
if (ShouldIgnoreInterface(ifname, policy))
continue;
NetworkChangeNotifier::ConnectionType type =
GetInterfaceConnectionType(ifname);
networks->push_back(
NetworkInterface(ifname, ifname, it.second.ifa_index, type, it.first,
it.second.ifa_prefixlen, ip_attributes));
}
return true;
}
std::string GetWifiSSIDFromInterfaceListInternal(
const NetworkInterfaceList& interfaces,
internal::GetInterfaceSSIDFunction get_interface_ssid) {
std::string connected_ssid;
for (size_t i = 0; i < interfaces.size(); ++i) {
if (interfaces[i].type != NetworkChangeNotifier::CONNECTION_WIFI)
return std::string();
std::string ssid = get_interface_ssid(interfaces[i].name);
if (i == 0) {
connected_ssid = ssid;
} else if (ssid != connected_ssid) {
return std::string();
}
}
return connected_ssid;
}
base::ScopedFD GetSocketForIoctl() {
base::ScopedFD ioctl_socket(socket(AF_INET6, SOCK_DGRAM, 0));
if (ioctl_socket.is_valid())
return ioctl_socket;
return base::ScopedFD(socket(AF_INET, SOCK_DGRAM, 0));
}
}
bool GetNetworkList(NetworkInterfaceList* networks, int policy) {
if (networks == nullptr)
return false;
#if BUILDFLAG(IS_ANDROID)
base::android::BuildInfo* build_info =
base::android::BuildInfo::GetInstance();
if (build_info->sdk_int() >= base::android::SDK_VERSION_NOUGAT) {
bool use_alternative_getifaddrs =
base::StringPiece(build_info->brand()) == "samsung" &&
base::StartsWith(build_info->hardware(), "mt");
bool ret = internal::GetNetworkListUsingGetifaddrs(
networks, policy, use_alternative_getifaddrs);
for (NetworkInterface& network : *networks)
network.type = internal::GetInterfaceConnectionType(network.name);
return ret;
}
#endif
#if BUILDFLAG(IS_OHOS)
bool ret = internal::GetNetworkListUsingGetifaddrs(networks, policy);
for (NetworkInterface& network : *networks)
network.type = internal::GetInterfaceConnectionType(network.name);
return ret;
#else
const AddressMapOwnerLinux* map_owner = nullptr;
absl::optional<internal::AddressTrackerLinux> temp_tracker;
#if BUILDFLAG(IS_LINUX)
if (base::FeatureList::IsEnabled(features::kAddressTrackerLinuxIsProxied)) {
map_owner = NetworkChangeNotifier::GetAddressMapOwner();
}
#endif
if (!map_owner) {
temp_tracker.emplace();
temp_tracker->Init();
map_owner = &temp_tracker.value();
}
return internal::GetNetworkListImpl(
networks, policy, map_owner->GetOnlineLinks(), map_owner->GetAddressMap(),
&internal::AddressTrackerLinux::GetInterfaceName);
#endif
}
std::string GetWifiSSID() {
#if BUILDFLAG(IS_ANDROID)
return android::GetWifiSSID();
#else
NetworkInterfaceList networks;
if (GetNetworkList(&networks, INCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES)) {
return internal::GetWifiSSIDFromInterfaceListInternal(
networks, internal::GetInterfaceSSID);
}
return std::string();
#endif
}
}