// Copyright (c) 2014 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/net_util_mac.h" #include #include #include #include #include #include "base/files/file_path.h" #include "base/logging.h" #include "base/memory/scoped_ptr.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 "net/base/escape.h" #include "net/base/ip_endpoint.h" #include "net/base/net_errors.h" #include "net/base/net_util_posix.h" #include "url/gurl.h" #if !defined(OS_IOS) #include #include #include #endif // !OS_IOS namespace net { namespace { #if !defined(OS_IOS) // MacOSX implementation of IPAttributesGetterMac which calls ioctl on socket to // retrieve IP attributes. class IPAttributesGetterMacImpl : public internal::IPAttributesGetterMac { public: IPAttributesGetterMacImpl(); ~IPAttributesGetterMacImpl() override; bool IsInitialized() const override; bool GetIPAttributes(const char* ifname, const sockaddr* sock_addr, int* native_attributes) override; private: int ioctl_socket_; }; IPAttributesGetterMacImpl::IPAttributesGetterMacImpl() : ioctl_socket_(socket(AF_INET6, SOCK_DGRAM, 0)) { DCHECK_GE(ioctl_socket_, 0); } bool IPAttributesGetterMacImpl::IsInitialized() const { return ioctl_socket_ >= 0; } IPAttributesGetterMacImpl::~IPAttributesGetterMacImpl() { if (ioctl_socket_ >= 0) { close(ioctl_socket_); } } bool IPAttributesGetterMacImpl::GetIPAttributes(const char* ifname, const sockaddr* sock_addr, int* native_attributes) { struct in6_ifreq ifr = {}; strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name) - 1); memcpy(&ifr.ifr_ifru.ifru_addr, sock_addr, sock_addr->sa_len); int rv = ioctl(ioctl_socket_, SIOCGIFAFLAG_IN6, &ifr); if (rv >= 0) { *native_attributes = ifr.ifr_ifru.ifru_flags; } return (rv >= 0); } // When returning true, the platform native IPv6 address attributes were // successfully converted to net IP address attributes. Otherwise, returning // false and the caller should drop the IP address which can't be used by the // application layer. bool TryConvertNativeToNetIPAttributes(int native_attributes, int* net_attributes) { // For MacOSX, we disallow addresses with attributes IN6_IFF_ANYCASE, // IN6_IFF_DUPLICATED, IN6_IFF_TENTATIVE, and IN6_IFF_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 (native_attributes & (IN6_IFF_ANYCAST | IN6_IFF_DUPLICATED | IN6_IFF_TENTATIVE | IN6_IFF_DETACHED)) { return false; } if (native_attributes & IN6_IFF_TEMPORARY) { *net_attributes |= IP_ADDRESS_ATTRIBUTE_TEMPORARY; } if (native_attributes & IN6_IFF_DEPRECATED) { *net_attributes |= IP_ADDRESS_ATTRIBUTE_DEPRECATED; } return true; } NetworkChangeNotifier::ConnectionType GetNetworkInterfaceType( int addr_family, const std::string& interface_name) { NetworkChangeNotifier::ConnectionType type = NetworkChangeNotifier::CONNECTION_UNKNOWN; struct ifmediareq ifmr = {}; strncpy(ifmr.ifm_name, interface_name.c_str(), sizeof(ifmr.ifm_name) - 1); int s = socket(addr_family, SOCK_DGRAM, 0); if (s == -1) { return type; } if (ioctl(s, SIOCGIFMEDIA, &ifmr) != -1) { if (ifmr.ifm_current & IFM_IEEE80211) { type = NetworkChangeNotifier::CONNECTION_WIFI; } else if (ifmr.ifm_current & IFM_ETHER) { type = NetworkChangeNotifier::CONNECTION_ETHERNET; } } close(s); return type; } #endif // !OS_IOS } // namespace namespace internal { bool GetNetworkListImpl(NetworkInterfaceList* networks, int policy, const ifaddrs* interfaces, IPAttributesGetterMac* ip_attributes_getter) { // 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; const 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; #if !defined(OS_IOS) // Retrieve native ip attributes and convert to net version if a getter is // given. if (ip_attributes_getter && ip_attributes_getter->IsInitialized()) { int native_attributes = 0; if (addr->sa_family == AF_INET6 && ip_attributes_getter->GetIPAttributes( interface->ifa_name, interface->ifa_addr, &native_attributes)) { if (!TryConvertNativeToNetIPAttributes(native_attributes, &ip_attributes)) { continue; } } } connection_type = GetNetworkInterfaceType(addr->sa_family, name); #endif // !OS_IOS 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 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; } scoped_ptr ip_attributes_getter; #if !defined(OS_IOS) ip_attributes_getter.reset(new IPAttributesGetterMacImpl()); #endif bool result = internal::GetNetworkListImpl(networks, policy, interfaces, ip_attributes_getter.get()); freeifaddrs(interfaces); return result; } std::string GetWifiSSID() { NOTIMPLEMENTED(); return ""; } } // namespace net