// Copyright 2017 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/base/network_interfaces_getifaddrs.h" #include #include #include #include #include #include #include "base/files/file_path.h" #include "base/logging.h" #include "base/posix/eintr_wrapper.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/ip_endpoint.h" #include "net/base/net_errors.h" #include "net/base/network_interfaces_posix.h" #if defined(OS_MACOSX) && !defined(OS_IOS) #include #include #include #endif // !OS_IOS namespace net { namespace internal { #if defined(OS_MACOSX) && !defined(OS_IOS) // MacOSX implementation of IPAttributesGetter which calls ioctl() on socket to // retrieve IP attributes. class IPAttributesGetterMac : public internal::IPAttributesGetter { public: IPAttributesGetterMac(); ~IPAttributesGetterMac() override; bool IsInitialized() const override; bool GetAddressAttributes(const ifaddrs* if_addr, int* attributes) override; NetworkChangeNotifier::ConnectionType GetNetworkInterfaceType( const ifaddrs* if_addr) override; private: int ioctl_socket_; }; IPAttributesGetterMac::IPAttributesGetterMac() : ioctl_socket_(socket(AF_INET6, SOCK_DGRAM, 0)) { DCHECK_GE(ioctl_socket_, 0); } IPAttributesGetterMac::~IPAttributesGetterMac() { if (IsInitialized()) { PCHECK(IGNORE_EINTR(close(ioctl_socket_)) == 0); } } bool IPAttributesGetterMac::IsInitialized() const { return ioctl_socket_ >= 0; } int AddressFlagsToNetAddressAttributes(int flags) { int result = 0; if (flags & IN6_IFF_TEMPORARY) { result |= IP_ADDRESS_ATTRIBUTE_TEMPORARY; } if (flags & IN6_IFF_DEPRECATED) { result |= IP_ADDRESS_ATTRIBUTE_DEPRECATED; } if (flags & IN6_IFF_ANYCAST) { result |= IP_ADDRESS_ATTRIBUTE_ANYCAST; } if (flags & IN6_IFF_TENTATIVE) { result |= IP_ADDRESS_ATTRIBUTE_TENTATIVE; } if (flags & IN6_IFF_DUPLICATED) { result |= IP_ADDRESS_ATTRIBUTE_DUPLICATED; } if (flags & IN6_IFF_DETACHED) { result |= IP_ADDRESS_ATTRIBUTE_DETACHED; } return result; } bool IPAttributesGetterMac::GetAddressAttributes(const ifaddrs* if_addr, int* attributes) { struct in6_ifreq ifr = {}; strncpy(ifr.ifr_name, if_addr->ifa_name, sizeof(ifr.ifr_name) - 1); memcpy(&ifr.ifr_ifru.ifru_addr, if_addr->ifa_addr, if_addr->ifa_addr->sa_len); int rv = ioctl(ioctl_socket_, SIOCGIFAFLAG_IN6, &ifr); if (rv >= 0) { *attributes = AddressFlagsToNetAddressAttributes(ifr.ifr_ifru.ifru_flags); } return (rv >= 0); } NetworkChangeNotifier::ConnectionType IPAttributesGetterMac::GetNetworkInterfaceType(const ifaddrs* if_addr) { if (!IsInitialized()) return NetworkChangeNotifier::CONNECTION_UNKNOWN; struct ifmediareq ifmr = {}; strncpy(ifmr.ifm_name, if_addr->ifa_name, sizeof(ifmr.ifm_name) - 1); if (ioctl(ioctl_socket_, SIOCGIFMEDIA, &ifmr) != -1) { if (ifmr.ifm_current & IFM_IEEE80211) { return NetworkChangeNotifier::CONNECTION_WIFI; } if (ifmr.ifm_current & IFM_ETHER) { return NetworkChangeNotifier::CONNECTION_ETHERNET; } } return NetworkChangeNotifier::CONNECTION_UNKNOWN; } #endif // defined(OS_MACOSX) && !defined(OS_IOS) bool IfaddrsToNetworkInterfaceList(int policy, const ifaddrs* interfaces, IPAttributesGetter* ip_attributes_getter, NetworkInterfaceList* networks) { // Enumerate the addresses assigned to network interfaces which are up. for (const ifaddrs* interface = interfaces; interface != NULL; interface = interface->ifa_next) { // Skip loopback interfaces, and ones which are down. if (!(IFF_RUNNING & interface->ifa_flags)) continue; if (IFF_LOOPBACK & interface->ifa_flags) continue; // Skip interfaces with no address configured. struct sockaddr* addr = interface->ifa_addr; if (!addr) continue; // Skip unspecified addresses (i.e. made of zeroes) and loopback addresses // configured on non-loopback interfaces. if (IsLoopbackOrUnspecifiedAddress(addr)) continue; std::string name = interface->ifa_name; // Filter out VMware interfaces, typically named vmnet1 and vmnet8. if (ShouldIgnoreInterface(name, policy)) { continue; } NetworkChangeNotifier::ConnectionType connection_type = NetworkChangeNotifier::CONNECTION_UNKNOWN; int ip_attributes = IP_ADDRESS_ATTRIBUTE_NONE; // Retrieve native ip attributes and convert to net version if a getter is // given. if (ip_attributes_getter && ip_attributes_getter->IsInitialized()) { if (addr->sa_family == AF_INET6 && ip_attributes_getter->GetAddressAttributes(interface, &ip_attributes)) { // Disallow addresses with attributes ANYCASE, DUPLICATED, TENTATIVE, // and DETACHED as these are still progressing through duplicated // address detection (DAD) or are not suitable to be used in an // one-to-one communication and shouldn't be used by the application // layer. if (ip_attributes & (IP_ADDRESS_ATTRIBUTE_ANYCAST | IP_ADDRESS_ATTRIBUTE_DUPLICATED | IP_ADDRESS_ATTRIBUTE_TENTATIVE | IP_ADDRESS_ATTRIBUTE_DETACHED)) { continue; } } connection_type = ip_attributes_getter->GetNetworkInterfaceType(interface); } IPEndPoint address; int addr_size = 0; if (addr->sa_family == AF_INET6) { addr_size = sizeof(sockaddr_in6); } else if (addr->sa_family == AF_INET) { addr_size = sizeof(sockaddr_in); } if (address.FromSockAddr(addr, addr_size)) { uint8_t prefix_length = 0; if (interface->ifa_netmask) { // If not otherwise set, assume the same sa_family as ifa_addr. if (interface->ifa_netmask->sa_family == 0) { interface->ifa_netmask->sa_family = addr->sa_family; } IPEndPoint netmask; if (netmask.FromSockAddr(interface->ifa_netmask, addr_size)) { prefix_length = MaskPrefixLength(netmask.address()); } } networks->push_back(NetworkInterface( name, name, if_nametoindex(name.c_str()), connection_type, address.address(), prefix_length, ip_attributes)); } } return true; } } // namespace internal bool GetNetworkList(NetworkInterfaceList* networks, int policy) { if (networks == NULL) return false; // getifaddrs() may require IO operations. base::ThreadRestrictions::AssertIOAllowed(); ifaddrs* interfaces; if (getifaddrs(&interfaces) < 0) { PLOG(ERROR) << "getifaddrs"; return false; } std::unique_ptr ip_attributes_getter; #if defined(OS_MACOSX) && !defined(OS_IOS) ip_attributes_getter = std::make_unique(); #endif bool result = internal::IfaddrsToNetworkInterfaceList( policy, interfaces, ip_attributes_getter.get(), networks); freeifaddrs(interfaces); return result; } std::string GetWifiSSID() { NOTIMPLEMENTED(); return std::string(); } } // namespace net