1 /*
2  *  Copyright 2015 The WebRTC Project Authors. All rights reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 #include <net/if.h>
12 #include <sys/ioctl.h>
13 #include <unistd.h>
14 
15 #include "webrtc/base/checks.h"
16 #include "webrtc/base/ifaddrs_converter.h"
17 #include "webrtc/base/logging.h"
18 #include "webrtc/base/scoped_ptr.h"
19 
20 #if !defined(WEBRTC_IOS)
21 #include <net/if_media.h>
22 #include <netinet/in_var.h>
23 #else  // WEBRTC_IOS
24 #define SCOPE6_ID_MAX 16
25 
26 struct in6_addrlifetime {
27   time_t ia6t_expire;    /* valid lifetime expiration time */
28   time_t ia6t_preferred; /* preferred lifetime expiration time */
29   u_int32_t ia6t_vltime; /* valid lifetime */
30   u_int32_t ia6t_pltime; /* prefix lifetime */
31 };
32 
33 struct in6_ifstat {
34   u_quad_t ifs6_in_receive;      /* # of total input datagram */
35   u_quad_t ifs6_in_hdrerr;       /* # of datagrams with invalid hdr */
36   u_quad_t ifs6_in_toobig;       /* # of datagrams exceeded MTU */
37   u_quad_t ifs6_in_noroute;      /* # of datagrams with no route */
38   u_quad_t ifs6_in_addrerr;      /* # of datagrams with invalid dst */
39   u_quad_t ifs6_in_protounknown; /* # of datagrams with unknown proto */
40                                  /* NOTE: increment on final dst if */
41   u_quad_t ifs6_in_truncated;    /* # of truncated datagrams */
42   u_quad_t ifs6_in_discard;      /* # of discarded datagrams */
43                                  /* NOTE: fragment timeout is not here */
44   u_quad_t ifs6_in_deliver;      /* # of datagrams delivered to ULP */
45                                  /* NOTE: increment on final dst if */
46   u_quad_t ifs6_out_forward;     /* # of datagrams forwarded */
47                                  /* NOTE: increment on outgoing if */
48   u_quad_t ifs6_out_request;     /* # of outgoing datagrams from ULP */
49                                  /* NOTE: does not include forwrads */
50   u_quad_t ifs6_out_discard;     /* # of discarded datagrams */
51   u_quad_t ifs6_out_fragok;      /* # of datagrams fragmented */
52   u_quad_t ifs6_out_fragfail;    /* # of datagrams failed on fragment */
53   u_quad_t ifs6_out_fragcreat;   /* # of fragment datagrams */
54                                  /* NOTE: this is # after fragment */
55   u_quad_t ifs6_reass_reqd;      /* # of incoming fragmented packets */
56                                  /* NOTE: increment on final dst if */
57   u_quad_t ifs6_reass_ok;        /* # of reassembled packets */
58                                  /* NOTE: this is # after reass */
59                                  /* NOTE: increment on final dst if */
60   u_quad_t ifs6_reass_fail;      /* # of reass failures */
61                                  /* NOTE: may not be packet count */
62                                  /* NOTE: increment on final dst if */
63   u_quad_t ifs6_in_mcast;        /* # of inbound multicast datagrams */
64   u_quad_t ifs6_out_mcast;       /* # of outbound multicast datagrams */
65 };
66 struct icmp6_ifstat {
67   /*
68    * Input statistics
69    */
70   /* ipv6IfIcmpInMsgs, total # of input messages */
71   u_quad_t ifs6_in_msg;
72   /* ipv6IfIcmpInErrors, # of input error messages */
73   u_quad_t ifs6_in_error;
74   /* ipv6IfIcmpInDestUnreachs, # of input dest unreach errors */
75   u_quad_t ifs6_in_dstunreach;
76   /* ipv6IfIcmpInAdminProhibs, # of input admin. prohibited errs */
77   u_quad_t ifs6_in_adminprohib;
78   /* ipv6IfIcmpInTimeExcds, # of input time exceeded errors */
79   u_quad_t ifs6_in_timeexceed;
80   /* ipv6IfIcmpInParmProblems, # of input parameter problem errors */
81   u_quad_t ifs6_in_paramprob;
82   /* ipv6IfIcmpInPktTooBigs, # of input packet too big errors */
83   u_quad_t ifs6_in_pkttoobig;
84   /* ipv6IfIcmpInEchos, # of input echo requests */
85   u_quad_t ifs6_in_echo;
86   /* ipv6IfIcmpInEchoReplies, # of input echo replies */
87   u_quad_t ifs6_in_echoreply;
88   /* ipv6IfIcmpInRouterSolicits, # of input router solicitations */
89   u_quad_t ifs6_in_routersolicit;
90   /* ipv6IfIcmpInRouterAdvertisements, # of input router advertisements */
91   u_quad_t ifs6_in_routeradvert;
92   /* ipv6IfIcmpInNeighborSolicits, # of input neighbor solicitations */
93   u_quad_t ifs6_in_neighborsolicit;
94   /* ipv6IfIcmpInNeighborAdvertisements, # of input neighbor advs. */
95   u_quad_t ifs6_in_neighboradvert;
96   /* ipv6IfIcmpInRedirects, # of input redirects */
97   u_quad_t ifs6_in_redirect;
98   /* ipv6IfIcmpInGroupMembQueries, # of input MLD queries */
99   u_quad_t ifs6_in_mldquery;
100   /* ipv6IfIcmpInGroupMembResponses, # of input MLD reports */
101   u_quad_t ifs6_in_mldreport;
102   /* ipv6IfIcmpInGroupMembReductions, # of input MLD done */
103   u_quad_t ifs6_in_mlddone;
104 
105   /*
106    * Output statistics. We should solve unresolved routing problem...
107    */
108   /* ipv6IfIcmpOutMsgs, total # of output messages */
109   u_quad_t ifs6_out_msg;
110   /* ipv6IfIcmpOutErrors, # of output error messages */
111   u_quad_t ifs6_out_error;
112   /* ipv6IfIcmpOutDestUnreachs, # of output dest unreach errors */
113   u_quad_t ifs6_out_dstunreach;
114   /* ipv6IfIcmpOutAdminProhibs, # of output admin. prohibited errs */
115   u_quad_t ifs6_out_adminprohib;
116   /* ipv6IfIcmpOutTimeExcds, # of output time exceeded errors */
117   u_quad_t ifs6_out_timeexceed;
118   /* ipv6IfIcmpOutParmProblems, # of output parameter problem errors */
119   u_quad_t ifs6_out_paramprob;
120   /* ipv6IfIcmpOutPktTooBigs, # of output packet too big errors */
121   u_quad_t ifs6_out_pkttoobig;
122   /* ipv6IfIcmpOutEchos, # of output echo requests */
123   u_quad_t ifs6_out_echo;
124   /* ipv6IfIcmpOutEchoReplies, # of output echo replies */
125   u_quad_t ifs6_out_echoreply;
126   /* ipv6IfIcmpOutRouterSolicits, # of output router solicitations */
127   u_quad_t ifs6_out_routersolicit;
128   /* ipv6IfIcmpOutRouterAdvertisements, # of output router advs. */
129   u_quad_t ifs6_out_routeradvert;
130   /* ipv6IfIcmpOutNeighborSolicits, # of output neighbor solicitations */
131   u_quad_t ifs6_out_neighborsolicit;
132   /* ipv6IfIcmpOutNeighborAdvertisements, # of output neighbor advs. */
133   u_quad_t ifs6_out_neighboradvert;
134   /* ipv6IfIcmpOutRedirects, # of output redirects */
135   u_quad_t ifs6_out_redirect;
136   /* ipv6IfIcmpOutGroupMembQueries, # of output MLD queries */
137   u_quad_t ifs6_out_mldquery;
138   /* ipv6IfIcmpOutGroupMembResponses, # of output MLD reports */
139   u_quad_t ifs6_out_mldreport;
140   /* ipv6IfIcmpOutGroupMembReductions, # of output MLD done */
141   u_quad_t ifs6_out_mlddone;
142 };
143 
144 struct in6_ifreq {
145   char ifr_name[IFNAMSIZ];
146   union {
147     struct sockaddr_in6 ifru_addr;
148     struct sockaddr_in6 ifru_dstaddr;
149     int ifru_flags;
150     int ifru_flags6;
151     int ifru_metric;
152     int ifru_intval;
153     caddr_t ifru_data;
154     struct in6_addrlifetime ifru_lifetime;
155     struct in6_ifstat ifru_stat;
156     struct icmp6_ifstat ifru_icmp6stat;
157     u_int32_t ifru_scope_id[SCOPE6_ID_MAX];
158   } ifr_ifru;
159 };
160 
161 #define SIOCGIFAFLAG_IN6 _IOWR('i', 73, struct in6_ifreq)
162 
163 #define IN6_IFF_ANYCAST 0x0001    /* anycast address */
164 #define IN6_IFF_TENTATIVE 0x0002  /* tentative address */
165 #define IN6_IFF_DUPLICATED 0x0004 /* DAD detected duplicate */
166 #define IN6_IFF_DETACHED 0x0008   /* may be detached from the link */
167 #define IN6_IFF_DEPRECATED 0x0010 /* deprecated address */
168 #define IN6_IFF_TEMPORARY 0x0080  /* temporary (anonymous) address. */
169 
170 #endif  // WEBRTC_IOS
171 
172 namespace rtc {
173 
174 namespace {
175 
176 class IPv6AttributesGetter {
177  public:
178   IPv6AttributesGetter();
179   virtual ~IPv6AttributesGetter();
180   bool IsInitialized() const;
181   bool GetIPAttributes(const char* ifname,
182                        const sockaddr* sock_addr,
183                        int* native_attributes);
184 
185  private:
186   // on MAC or IOS, we have to use ioctl with a socket to query an IPv6
187   // interface's attribute.
188   int ioctl_socket_;
189 };
190 
IPv6AttributesGetter()191 IPv6AttributesGetter::IPv6AttributesGetter()
192     : ioctl_socket_(
193           socket(AF_INET6, SOCK_DGRAM, 0 /* unspecified protocol */)) {
194   RTC_DCHECK_GE(ioctl_socket_, 0);
195 }
196 
IsInitialized() const197 bool IPv6AttributesGetter::IsInitialized() const {
198   return ioctl_socket_ >= 0;
199 }
200 
~IPv6AttributesGetter()201 IPv6AttributesGetter::~IPv6AttributesGetter() {
202   if (!IsInitialized()) {
203     return;
204   }
205   close(ioctl_socket_);
206 }
207 
GetIPAttributes(const char * ifname,const sockaddr * sock_addr,int * native_attributes)208 bool IPv6AttributesGetter::GetIPAttributes(const char* ifname,
209                                            const sockaddr* sock_addr,
210                                            int* native_attributes) {
211   if (!IsInitialized()) {
212     return false;
213   }
214 
215   struct in6_ifreq ifr = {};
216   strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name) - 1);
217   memcpy(&ifr.ifr_ifru.ifru_addr, sock_addr, sock_addr->sa_len);
218   int rv = ioctl(ioctl_socket_, SIOCGIFAFLAG_IN6, &ifr);
219   if (rv >= 0) {
220     *native_attributes = ifr.ifr_ifru.ifru_flags;
221   } else {
222     LOG(LS_ERROR) << "ioctl returns " << errno;
223   }
224   return (rv >= 0);
225 }
226 
227 // Converts native IPv6 address attributes to net IPv6 address attributes.  If
228 // it returns false, the IP address isn't suitable for one-to-one communications
229 // applications and should be ignored.
ConvertNativeToIPAttributes(int native_attributes,int * net_attributes)230 bool ConvertNativeToIPAttributes(int native_attributes, int* net_attributes) {
231   // For MacOSX, we disallow addresses with attributes IN6_IFF_ANYCASE,
232   // IN6_IFF_DUPLICATED, IN6_IFF_TENTATIVE, and IN6_IFF_DETACHED as these are
233   // still progressing through duplicated address detection (DAD) or are not
234   // suitable for one-to-one communication applications.
235   if (native_attributes & (IN6_IFF_ANYCAST | IN6_IFF_DUPLICATED |
236                            IN6_IFF_TENTATIVE | IN6_IFF_DETACHED)) {
237     return false;
238   }
239 
240   if (native_attributes & IN6_IFF_TEMPORARY) {
241     *net_attributes |= IPV6_ADDRESS_FLAG_TEMPORARY;
242   }
243 
244   if (native_attributes & IN6_IFF_DEPRECATED) {
245     *net_attributes |= IPV6_ADDRESS_FLAG_DEPRECATED;
246   }
247 
248   return true;
249 }
250 
251 class MacIfAddrsConverter : public IfAddrsConverter {
252  public:
MacIfAddrsConverter()253   MacIfAddrsConverter() : ip_attribute_getter_(new IPv6AttributesGetter()) {}
~MacIfAddrsConverter()254   ~MacIfAddrsConverter() override {}
255 
ConvertNativeAttributesToIPAttributes(const struct ifaddrs * interface,int * ip_attributes)256   bool ConvertNativeAttributesToIPAttributes(const struct ifaddrs* interface,
257                                              int* ip_attributes) override {
258     int native_attributes;
259     if (!ip_attribute_getter_->GetIPAttributes(
260             interface->ifa_name, interface->ifa_addr, &native_attributes)) {
261       return false;
262     }
263 
264     if (!ConvertNativeToIPAttributes(native_attributes, ip_attributes)) {
265       return false;
266     }
267 
268     return true;
269   }
270 
271  private:
272   rtc::scoped_ptr<IPv6AttributesGetter> ip_attribute_getter_;
273 };
274 
275 }  // namespace
276 
CreateIfAddrsConverter()277 IfAddrsConverter* CreateIfAddrsConverter() {
278   return new MacIfAddrsConverter();
279 }
280 
281 }  // namespace rtc
282