| // 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 <ifaddrs.h> |
| #include <net/if.h> |
| #include <netinet/in.h> |
| #include <sys/types.h> |
| |
| #include <memory> |
| #include <set> |
| |
| #include "starboard/types.h" |
| |
| #include "starboard/common/string.h" |
| |
| #include "starboard/memory.h" |
| |
| #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 <net/if_media.h> |
| #include <netinet/in_var.h> |
| #include <sys/ioctl.h> |
| #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 = {}; |
| SbStringCopy(ifr.ifr_name, if_addr->ifa_name, sizeof(ifr.ifr_name) - 1); |
| SbMemoryCopy(&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 = {}; |
| SbStringCopy(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::AssertBlockingAllowed(); |
| |
| ifaddrs* interfaces; |
| if (getifaddrs(&interfaces) < 0) { |
| PLOG(ERROR) << "getifaddrs"; |
| return false; |
| } |
| |
| std::unique_ptr<internal::IPAttributesGetter> ip_attributes_getter; |
| |
| #if defined(OS_MACOSX) && !defined(OS_IOS) |
| ip_attributes_getter = std::make_unique<internal::IPAttributesGetterMac>(); |
| #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 |