// Copyright (c) 2011 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.h" #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 "url/gurl.h" #if !defined(OS_NACL) #if defined(OS_MACOSX) #include #else #include "net/base/address_tracker_linux.h" #include "net/base/net_util_posix.h" #endif // OS_MACOSX #include #include #endif // !defined(OS_NACL) #if defined(OS_MACOSX) && !defined(OS_IOS) #include #include #include #endif namespace net { namespace { // The application layer can pass |policy| defined in net_util.h to // request filtering out certain type of interfaces. bool ShouldIgnoreInterface(const std::string& name, int policy) { // Filter out VMware interfaces, typically named vmnet1 and vmnet8, // which might not be useful for use cases like WebRTC. if ((policy & EXCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES) && ((name.find("vmnet") != std::string::npos) || (name.find("vnic") != std::string::npos))) { return true; } return false; } // Check if the address is unspecified (i.e. made of zeroes) or loopback. bool IsLoopbackOrUnspecifiedAddress(const sockaddr* addr) { if (addr->sa_family == AF_INET6) { const struct sockaddr_in6* addr_in6 = reinterpret_cast(addr); const struct in6_addr* sin6_addr = &addr_in6->sin6_addr; if (IN6_IS_ADDR_LOOPBACK(sin6_addr) || IN6_IS_ADDR_UNSPECIFIED(sin6_addr)) { return true; } } else if (addr->sa_family == AF_INET) { const struct sockaddr_in* addr_in = reinterpret_cast(addr); if (addr_in->sin_addr.s_addr == INADDR_LOOPBACK || addr_in->sin_addr.s_addr == 0) { return true; } } else { // Skip non-IP addresses. return true; } return false; } #if defined(OS_MACOSX) struct NetworkInterfaceInfo { NetworkInterfaceInfo() : permanent(true) { } bool permanent; // IPv6 has notion of temporary address. If the address is // IPv6 and it's temporary this field will be false. NetworkInterface interface; }; // This method will remove permanent IPv6 addresses if a temporary address // is available for same network interface. void RemovePermanentIPv6AddressesWhereTemporaryExists( std::vector* infos) { if (!infos || infos->empty()) return; // Build a set containing the names of interfaces with a temp IPv6 address std::set ifaces_with_temp_addrs; std::vector::iterator i; for (i = infos->begin(); i != infos->end(); ++i) { if (!i->permanent && i->interface.address.size() == kIPv6AddressSize) { ifaces_with_temp_addrs.insert(i->interface.name); } } // If there are no such interfaces then there's no further work. if (ifaces_with_temp_addrs.empty()) return; // Search for permenent addresses belonging to same network interface. for (i = infos->begin(); i != infos->end(); ) { // If the address is IPv6 and it's permanent and there is temporary // address for it, then we can remove this address. if ((i->interface.address.size() == kIPv6AddressSize) && i->permanent && (ifaces_with_temp_addrs.find(i->interface.name) != ifaces_with_temp_addrs.end())) { i = infos->erase(i); } else { ++i; } } } #if !defined(OS_IOS) 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 // !defined(OS_IOS) #elif !defined(OS_NACL) // OS_MACOSX // Convert platform native IPv6 address attributes to net IP address // attributes and drop ones that can't be used by the application // layer. bool TryConvertNativeToNetIPAttributes(int native_attributes, int* net_attributes) { // For Linux/ChromeOS/Android, we disallow addresses with attributes // IFA_F_OPTIMISTIC, IFA_F_DADFAILED, and IFA_F_TENTATIVE as these // are still progressing through duplicated address detection (DAD) // and shouldn't be used by the application layer until DAD process // is completed. if (native_attributes & ( #if !defined(OS_ANDROID) IFA_F_OPTIMISTIC | IFA_F_DADFAILED | #endif // !OS_ANDROID 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; } #endif // OS_MACOSX } // namespace namespace internal { #if !defined(OS_MACOSX) && !defined(OS_NACL) inline const unsigned char* GetIPAddressData(const IPAddressNumber& ip) { #if defined(OS_ANDROID) return ip.begin(); #else return ip.data(); #endif } bool GetNetworkListImpl( NetworkInterfaceList* networks, int policy, const base::hash_set& online_links, const internal::AddressTrackerLinux::AddressMap& address_map, GetInterfaceNameFunction get_interface_name) { std::map ifnames; for (internal::AddressTrackerLinux::AddressMap::const_iterator it = address_map.begin(); it != address_map.end(); ++it) { // Ignore addresses whose links are not online. if (online_links.find(it->second.ifa_index) == online_links.end()) continue; sockaddr_storage sock_addr; socklen_t sock_len = sizeof(sockaddr_storage); // Convert to sockaddr for next check. if (!IPEndPoint(it->first, 0) .ToSockAddr(reinterpret_cast(&sock_addr), &sock_len)) { continue; } // Skip unspecified addresses (i.e. made of zeroes) and loopback addresses if (IsLoopbackOrUnspecifiedAddress(reinterpret_cast(&sock_addr))) continue; int ip_attributes = IP_ADDRESS_ATTRIBUTE_NONE; if (it->second.ifa_family == AF_INET6) { // Ignore addresses whose attributes are not actionable by // the application layer. if (!TryConvertNativeToNetIPAttributes(it->second.ifa_flags, &ip_attributes)) continue; } // Find the name of this link. std::map::const_iterator itname = ifnames.find(it->second.ifa_index); std::string ifname; if (itname == ifnames.end()) { char buffer[IF_NAMESIZE] = {0}; if (get_interface_name(it->second.ifa_index, buffer)) { ifname = ifnames[it->second.ifa_index] = buffer; } else { // Ignore addresses whose interface name can't be retrieved. continue; } } else { ifname = itname->second; } // Based on the interface name and policy, determine whether we // should ignore it. if (ShouldIgnoreInterface(ifname, policy)) continue; networks->push_back( NetworkInterface(ifname, ifname, it->second.ifa_index, NetworkChangeNotifier::CONNECTION_UNKNOWN, it->first, it->second.ifa_prefixlen, ip_attributes)); } return true; } #endif } // namespace internal bool GetNetworkList(NetworkInterfaceList* networks, int policy) { if (networks == NULL) return false; #if defined(OS_NACL) NOTIMPLEMENTED(); return false; #elif !defined(OS_MACOSX) internal::AddressTrackerLinux tracker; tracker.Init(); return internal::GetNetworkListImpl(networks, policy, tracker.GetOnlineLinks(), tracker.GetAddressMap(), &if_indextoname); #else // Only OS_MACOSX and OS_IOS will run the code below // getifaddrs() may require IO operations. base::ThreadRestrictions::AssertIOAllowed(); #if !defined(OS_IOS) int ioctl_socket = -1; if (policy & INCLUDE_ONLY_TEMP_IPV6_ADDRESS_IF_POSSIBLE) { // we need a socket to query information about temporary address. ioctl_socket = socket(AF_INET6, SOCK_DGRAM, 0); DCHECK_GT(ioctl_socket, 0); } #endif ifaddrs* interfaces; if (getifaddrs(&interfaces) < 0) { PLOG(ERROR) << "getifaddrs"; return false; } std::vector network_infos; // Enumerate the addresses assigned to network interfaces which are up. for (ifaddrs *interface = interfaces; interface != NULL; interface = interface->ifa_next) { // Skip loopback interfaces, and ones which are down. if (!(IFF_UP & 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; 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); } const std::string& name = interface->ifa_name; // Filter out VMware interfaces, typically named vmnet1 and vmnet8. if (ShouldIgnoreInterface(name, policy)) { continue; } NetworkInterfaceInfo network_info; NetworkChangeNotifier::ConnectionType connection_type = NetworkChangeNotifier::CONNECTION_UNKNOWN; #if !defined(OS_IOS) // Check if this is a temporary address. Currently this is only supported // on Mac. if ((policy & INCLUDE_ONLY_TEMP_IPV6_ADDRESS_IF_POSSIBLE) && ioctl_socket >= 0 && addr->sa_family == AF_INET6) { struct in6_ifreq ifr = {}; strncpy(ifr.ifr_name, interface->ifa_name, sizeof(ifr.ifr_name) - 1); memcpy(&ifr.ifr_ifru.ifru_addr, interface->ifa_addr, interface->ifa_addr->sa_len); int rv = ioctl(ioctl_socket, SIOCGIFAFLAG_IN6, &ifr); if (rv >= 0) { network_info.permanent = !(ifr.ifr_ifru.ifru_flags & IN6_IFF_TEMPORARY); } } connection_type = GetNetworkInterfaceType(addr->sa_family, name); #endif IPEndPoint address; if (address.FromSockAddr(addr, addr_size)) { uint8 net_mask = 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)) { net_mask = MaskPrefixLength(netmask.address()); } } network_info.interface = NetworkInterface(name, name, if_nametoindex(name.c_str()), connection_type, address.address(), net_mask, IP_ADDRESS_ATTRIBUTE_NONE); network_infos.push_back(NetworkInterfaceInfo(network_info)); } } freeifaddrs(interfaces); #if !defined(OS_IOS) if (ioctl_socket >= 0) { close(ioctl_socket); } #endif if (policy & INCLUDE_ONLY_TEMP_IPV6_ADDRESS_IF_POSSIBLE) { RemovePermanentIPv6AddressesWhereTemporaryExists(&network_infos); } for (size_t i = 0; i < network_infos.size(); ++i) { networks->push_back(network_infos[i].interface); } return true; #endif } WifiPHYLayerProtocol GetWifiPHYLayerProtocol() { return WIFI_PHY_LAYER_PROTOCOL_UNKNOWN; } scoped_ptr SetWifiOptions(int options) { return scoped_ptr(); } } // namespace net