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