1 // Copyright (c) 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/base/net_util_mac.h"
9 #include <netinet/in.h>
11 #include <sys/types.h>
13 #include "base/files/file_path.h"
14 #include "base/logging.h"
15 #include "base/memory/scoped_ptr.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/string_tokenizer.h"
18 #include "base/strings/string_util.h"
19 #include "base/threading/thread_restrictions.h"
20 #include "net/base/escape.h"
21 #include "net/base/ip_endpoint.h"
22 #include "net/base/net_errors.h"
23 #include "net/base/net_util_posix.h"
27 #include <net/if_media.h>
28 #include <netinet/in_var.h>
29 #include <sys/ioctl.h>
38 // MacOSX implementation of IPAttributesGetterMac which calls ioctl on socket to
39 // retrieve IP attributes.
40 class IPAttributesGetterMacImpl
: public internal::IPAttributesGetterMac
{
42 IPAttributesGetterMacImpl();
43 ~IPAttributesGetterMacImpl() override
;
44 bool IsInitialized() const override
;
45 bool GetIPAttributes(const char* ifname
,
46 const sockaddr
* sock_addr
,
47 int* native_attributes
) override
;
53 IPAttributesGetterMacImpl::IPAttributesGetterMacImpl()
54 : ioctl_socket_(socket(AF_INET6
, SOCK_DGRAM
, 0)) {
55 DCHECK_GE(ioctl_socket_
, 0);
58 bool IPAttributesGetterMacImpl::IsInitialized() const {
59 return ioctl_socket_
>= 0;
62 IPAttributesGetterMacImpl::~IPAttributesGetterMacImpl() {
63 if (ioctl_socket_
>= 0) {
68 bool IPAttributesGetterMacImpl::GetIPAttributes(const char* ifname
,
69 const sockaddr
* sock_addr
,
70 int* native_attributes
) {
71 struct in6_ifreq ifr
= {};
72 strncpy(ifr
.ifr_name
, ifname
, sizeof(ifr
.ifr_name
) - 1);
73 memcpy(&ifr
.ifr_ifru
.ifru_addr
, sock_addr
, sock_addr
->sa_len
);
74 int rv
= ioctl(ioctl_socket_
, SIOCGIFAFLAG_IN6
, &ifr
);
76 *native_attributes
= ifr
.ifr_ifru
.ifru_flags
;
81 // When returning true, the platform native IPv6 address attributes were
82 // successfully converted to net IP address attributes. Otherwise, returning
83 // false and the caller should drop the IP address which can't be used by the
85 bool TryConvertNativeToNetIPAttributes(int native_attributes
,
86 int* net_attributes
) {
87 // For MacOSX, we disallow addresses with attributes IN6_IFF_ANYCASE,
88 // IN6_IFF_DUPLICATED, IN6_IFF_TENTATIVE, and IN6_IFF_DETACHED as these are
89 // still progressing through duplicated address detection (DAD) or are not
90 // suitable to be used in an one-to-one communication and shouldn't be used
91 // by the application layer.
92 if (native_attributes
& (IN6_IFF_ANYCAST
| IN6_IFF_DUPLICATED
|
93 IN6_IFF_TENTATIVE
| IN6_IFF_DETACHED
)) {
97 if (native_attributes
& IN6_IFF_TEMPORARY
) {
98 *net_attributes
|= IP_ADDRESS_ATTRIBUTE_TEMPORARY
;
101 if (native_attributes
& IN6_IFF_DEPRECATED
) {
102 *net_attributes
|= IP_ADDRESS_ATTRIBUTE_DEPRECATED
;
108 NetworkChangeNotifier::ConnectionType
GetNetworkInterfaceType(
110 const std::string
& interface_name
) {
111 NetworkChangeNotifier::ConnectionType type
=
112 NetworkChangeNotifier::CONNECTION_UNKNOWN
;
114 struct ifmediareq ifmr
= {};
115 strncpy(ifmr
.ifm_name
, interface_name
.c_str(), sizeof(ifmr
.ifm_name
) - 1);
117 int s
= socket(addr_family
, SOCK_DGRAM
, 0);
122 if (ioctl(s
, SIOCGIFMEDIA
, &ifmr
) != -1) {
123 if (ifmr
.ifm_current
& IFM_IEEE80211
) {
124 type
= NetworkChangeNotifier::CONNECTION_WIFI
;
125 } else if (ifmr
.ifm_current
& IFM_ETHER
) {
126 type
= NetworkChangeNotifier::CONNECTION_ETHERNET
;
138 bool GetNetworkListImpl(NetworkInterfaceList
* networks
,
140 const ifaddrs
* interfaces
,
141 IPAttributesGetterMac
* ip_attributes_getter
) {
142 // Enumerate the addresses assigned to network interfaces which are up.
143 for (const ifaddrs
* interface
= interfaces
; interface
!= NULL
;
144 interface
= interface
->ifa_next
) {
145 // Skip loopback interfaces, and ones which are down.
146 if (!(IFF_RUNNING
& interface
->ifa_flags
))
148 if (IFF_LOOPBACK
& interface
->ifa_flags
)
150 // Skip interfaces with no address configured.
151 struct sockaddr
* addr
= interface
->ifa_addr
;
155 // Skip unspecified addresses (i.e. made of zeroes) and loopback addresses
156 // configured on non-loopback interfaces.
157 if (IsLoopbackOrUnspecifiedAddress(addr
))
160 const std::string
& name
= interface
->ifa_name
;
161 // Filter out VMware interfaces, typically named vmnet1 and vmnet8.
162 if (ShouldIgnoreInterface(name
, policy
)) {
166 NetworkChangeNotifier::ConnectionType connection_type
=
167 NetworkChangeNotifier::CONNECTION_UNKNOWN
;
169 int ip_attributes
= IP_ADDRESS_ATTRIBUTE_NONE
;
172 // Retrieve native ip attributes and convert to net version if a getter is
174 if (ip_attributes_getter
&& ip_attributes_getter
->IsInitialized()) {
175 int native_attributes
= 0;
176 if (addr
->sa_family
== AF_INET6
&&
177 ip_attributes_getter
->GetIPAttributes(
178 interface
->ifa_name
, interface
->ifa_addr
, &native_attributes
)) {
179 if (!TryConvertNativeToNetIPAttributes(native_attributes
,
186 connection_type
= GetNetworkInterfaceType(addr
->sa_family
, name
);
192 if (addr
->sa_family
== AF_INET6
) {
193 addr_size
= sizeof(sockaddr_in6
);
194 } else if (addr
->sa_family
== AF_INET
) {
195 addr_size
= sizeof(sockaddr_in
);
198 if (address
.FromSockAddr(addr
, addr_size
)) {
199 uint8 prefix_length
= 0;
200 if (interface
->ifa_netmask
) {
201 // If not otherwise set, assume the same sa_family as ifa_addr.
202 if (interface
->ifa_netmask
->sa_family
== 0) {
203 interface
->ifa_netmask
->sa_family
= addr
->sa_family
;
206 if (netmask
.FromSockAddr(interface
->ifa_netmask
, addr_size
)) {
207 prefix_length
= MaskPrefixLength(netmask
.address());
210 networks
->push_back(NetworkInterface(
211 name
, name
, if_nametoindex(name
.c_str()), connection_type
,
212 address
.address(), prefix_length
, ip_attributes
));
219 } // namespace internal
221 bool GetNetworkList(NetworkInterfaceList
* networks
, int policy
) {
222 if (networks
== NULL
)
225 // getifaddrs() may require IO operations.
226 base::ThreadRestrictions::AssertIOAllowed();
229 if (getifaddrs(&interfaces
) < 0) {
230 PLOG(ERROR
) << "getifaddrs";
234 scoped_ptr
<internal::IPAttributesGetterMac
> ip_attributes_getter
;
237 ip_attributes_getter
.reset(new IPAttributesGetterMacImpl());
240 bool result
= internal::GetNetworkListImpl(networks
, policy
, interfaces
,
241 ip_attributes_getter
.get());
242 freeifaddrs(interfaces
);