1 /* Determine protocol families for which interfaces exist.  Linux version.
2    Copyright (C) 2003-2022 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4 
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9 
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, see
17    <https://www.gnu.org/licenses/>.  */
18 
19 #include <assert.h>
20 #include <errno.h>
21 #include <ifaddrs.h>
22 #include <netdb.h>
23 #include <stddef.h>
24 #include <string.h>
25 #include <time.h>
26 #include <unistd.h>
27 #include <stdint.h>
28 #include <sys/socket.h>
29 
30 #include <asm/types.h>
31 #include <linux/netlink.h>
32 #include <linux/rtnetlink.h>
33 
34 #include <not-cancel.h>
35 #include <libc-lock.h>
36 #include <atomic.h>
37 #include <nscd/nscd-client.h>
38 
39 #include "netlinkaccess.h"
40 
41 #ifndef IFA_F_HOMEADDRESS
42 # define IFA_F_HOMEADDRESS 0
43 #endif
44 #ifndef IFA_F_OPTIMISTIC
45 # define IFA_F_OPTIMISTIC 0
46 #endif
47 
48 
49 struct cached_data
50 {
51   uint32_t timestamp;
52   uint32_t usecnt;
53   bool seen_ipv4;
54   bool seen_ipv6;
55   size_t in6ailen;
56   struct in6addrinfo in6ai[0];
57 };
58 
59 static struct cached_data noai6ai_cached =
60   {
61     .usecnt = 1,	/* Make sure we never try to delete this entry.  */
62     .in6ailen = 0
63   };
64 
65 static struct cached_data *cache;
66 __libc_lock_define_initialized (static, lock);
67 
68 
69 #if IS_IN (nscd)
70 static uint32_t nl_timestamp;
71 
72 uint32_t
__bump_nl_timestamp(void)73 __bump_nl_timestamp (void)
74 {
75   if (atomic_increment_val (&nl_timestamp) == 0)
76     atomic_increment (&nl_timestamp);
77 
78   return nl_timestamp;
79 }
80 #endif
81 
82 static inline uint32_t
get_nl_timestamp(void)83 get_nl_timestamp (void)
84 {
85 #if IS_IN (nscd)
86   return nl_timestamp;
87 #elif defined USE_NSCD
88   return __nscd_get_nl_timestamp ();
89 #else
90   return 0;
91 #endif
92 }
93 
94 static inline bool
cache_valid_p(void)95 cache_valid_p (void)
96 {
97   if (cache != NULL)
98     {
99       uint32_t timestamp = get_nl_timestamp ();
100       return timestamp != 0 && cache->timestamp == timestamp;
101     }
102   return false;
103 }
104 
105 
106 static struct cached_data *
make_request(int fd,pid_t pid)107 make_request (int fd, pid_t pid)
108 {
109   struct cached_data *result = NULL;
110 
111   size_t result_len = 0;
112   size_t result_cap = 32;
113 
114   struct req
115   {
116     struct nlmsghdr nlh;
117     struct rtgenmsg g;
118     /* struct rtgenmsg consists of a single byte.  This means there
119        are three bytes of padding included in the REQ definition.
120        We make them explicit here.  */
121     char pad[3];
122   } req;
123   struct sockaddr_nl nladdr;
124 
125   req.nlh.nlmsg_len = sizeof (req);
126   req.nlh.nlmsg_type = RTM_GETADDR;
127   req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
128   req.nlh.nlmsg_pid = 0;
129   req.nlh.nlmsg_seq = time_now ();
130   req.g.rtgen_family = AF_UNSPEC;
131 
132   assert (sizeof (req) - offsetof (struct req, pad) == 3);
133   memset (req.pad, '\0', sizeof (req.pad));
134 
135   memset (&nladdr, '\0', sizeof (nladdr));
136   nladdr.nl_family = AF_NETLINK;
137 
138 #ifdef PAGE_SIZE
139   const size_t buf_size = PAGE_SIZE;
140 #else
141   const size_t buf_size = 4096;
142 #endif
143   char buf[buf_size];
144 
145   struct iovec iov = { buf, buf_size };
146 
147   if (TEMP_FAILURE_RETRY (__sendto (fd, (void *) &req, sizeof (req), 0,
148 				    (struct sockaddr *) &nladdr,
149 				    sizeof (nladdr))) < 0)
150     goto out_fail;
151 
152   bool done = false;
153 
154   bool seen_ipv4 = false;
155   bool seen_ipv6 = false;
156 
157   do
158     {
159       struct msghdr msg =
160 	{
161 	  .msg_name = (void *) &nladdr,
162 	  .msg_namelen =  sizeof (nladdr),
163 	  .msg_iov = &iov,
164 	  .msg_iovlen = 1,
165 	  .msg_control = NULL,
166 	  .msg_controllen = 0,
167 	  .msg_flags = 0
168 	};
169 
170       ssize_t read_len = TEMP_FAILURE_RETRY (__recvmsg (fd, &msg, 0));
171       __netlink_assert_response (fd, read_len);
172       if (read_len < 0)
173 	goto out_fail;
174 
175       if (msg.msg_flags & MSG_TRUNC)
176 	goto out_fail;
177 
178       struct nlmsghdr *nlmh;
179       for (nlmh = (struct nlmsghdr *) buf;
180 	   NLMSG_OK (nlmh, (size_t) read_len);
181 	   nlmh = (struct nlmsghdr *) NLMSG_NEXT (nlmh, read_len))
182 	{
183 	  if (nladdr.nl_pid != 0 || (pid_t) nlmh->nlmsg_pid != pid
184 	      || nlmh->nlmsg_seq != req.nlh.nlmsg_seq)
185 	    continue;
186 
187 	  if (nlmh->nlmsg_type == RTM_NEWADDR)
188 	    {
189 	      struct ifaddrmsg *ifam = (struct ifaddrmsg *) NLMSG_DATA (nlmh);
190 	      struct rtattr *rta = IFA_RTA (ifam);
191 	      size_t len = nlmh->nlmsg_len - NLMSG_LENGTH (sizeof (*ifam));
192 
193 	      if (ifam->ifa_family != AF_INET
194 		  && ifam->ifa_family != AF_INET6)
195 		continue;
196 
197 	      const void *local = NULL;
198 	      const void *address = NULL;
199 	      while (RTA_OK (rta, len))
200 		{
201 		  switch (rta->rta_type)
202 		    {
203 		    case IFA_LOCAL:
204 		      local = RTA_DATA (rta);
205 		      break;
206 
207 		    case IFA_ADDRESS:
208 		      address = RTA_DATA (rta);
209 		      goto out;
210 		    }
211 
212 		  rta = RTA_NEXT (rta, len);
213 		}
214 
215 	      if (local != NULL)
216 		{
217 		  address = local;
218 		out:
219 		  if (ifam->ifa_family == AF_INET)
220 		    {
221 		      if (*(const in_addr_t *) address
222 			  != htonl (INADDR_LOOPBACK))
223 			seen_ipv4 = true;
224 		    }
225 		  else
226 		    {
227 		      if (!IN6_IS_ADDR_LOOPBACK (address))
228 			seen_ipv6 = true;
229 		    }
230 		}
231 
232 	      if (result_len == 0 || result_len == result_cap)
233 		{
234 		  result_cap = 2 * result_cap;
235 		  result = realloc (result, sizeof (*result)
236 				    + result_cap
237 				      * sizeof (struct in6addrinfo));
238 		}
239 
240 	      if (!result)
241 		goto out_fail;
242 
243 	      struct in6addrinfo *info = &result->in6ai[result_len++];
244 
245 	      info->flags = (((ifam->ifa_flags
246 			       & (IFA_F_DEPRECATED | IFA_F_OPTIMISTIC))
247 			      ? in6ai_deprecated : 0)
248 			     | ((ifam->ifa_flags & IFA_F_HOMEADDRESS)
249 			         ? in6ai_homeaddress : 0));
250 	      info->prefixlen = ifam->ifa_prefixlen;
251 	      info->index = ifam->ifa_index;
252 	      if (ifam->ifa_family == AF_INET)
253 		{
254 		  info->addr[0] = 0;
255 		  info->addr[1] = 0;
256 		  info->addr[2] = htonl (0xffff);
257 		  info->addr[3] = *(const in_addr_t *) address;
258 		}
259 	      else
260 		memcpy (info->addr, address, sizeof (info->addr));
261 	    }
262 	  else if (nlmh->nlmsg_type == NLMSG_DONE)
263 	    /* We found the end, leave the loop.  */
264 	    done = true;
265 	}
266     }
267   while (! done);
268 
269   if (seen_ipv6 && result != NULL)
270     {
271       result->timestamp = get_nl_timestamp ();
272       result->usecnt = 2;
273       result->seen_ipv4 = seen_ipv4;
274       result->seen_ipv6 = true;
275       result->in6ailen = result_len;
276     }
277   else
278     {
279       free (result);
280 
281       atomic_add (&noai6ai_cached.usecnt, 2);
282       noai6ai_cached.seen_ipv4 = seen_ipv4;
283       noai6ai_cached.seen_ipv6 = seen_ipv6;
284       result = &noai6ai_cached;
285     }
286 
287   return result;
288 
289  out_fail:
290 
291   free (result);
292   return NULL;
293 }
294 
295 
296 void
297 attribute_hidden
__check_pf(bool * seen_ipv4,bool * seen_ipv6,struct in6addrinfo ** in6ai,size_t * in6ailen)298 __check_pf (bool *seen_ipv4, bool *seen_ipv6,
299 	    struct in6addrinfo **in6ai, size_t *in6ailen)
300 {
301   *in6ai = NULL;
302   *in6ailen = 0;
303 
304   struct cached_data *olddata = NULL;
305   struct cached_data *data = NULL;
306 
307   __libc_lock_lock (lock);
308 
309   if (cache_valid_p ())
310     {
311       data = cache;
312       atomic_increment (&cache->usecnt);
313     }
314   else
315     {
316       int fd = __socket (PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
317 
318       if (__glibc_likely (fd >= 0))
319 	{
320 	  struct sockaddr_nl nladdr;
321 	  memset (&nladdr, '\0', sizeof (nladdr));
322 	  nladdr.nl_family = AF_NETLINK;
323 
324 	  socklen_t addr_len = sizeof (nladdr);
325 
326 	  if (__bind (fd, (struct sockaddr *) &nladdr, sizeof (nladdr)) == 0
327 	      && __getsockname (fd, (struct sockaddr *) &nladdr,
328 				&addr_len) == 0)
329 	    data = make_request (fd, nladdr.nl_pid);
330 
331 	  __close_nocancel_nostatus (fd);
332 	}
333 
334       if (data != NULL)
335 	{
336 	  olddata = cache;
337 	  cache = data;
338 	}
339     }
340 
341   __libc_lock_unlock (lock);
342 
343   if (data != NULL)
344     {
345       /* It worked.  */
346       *seen_ipv4 = data->seen_ipv4;
347       *seen_ipv6 = data->seen_ipv6;
348       *in6ailen = data->in6ailen;
349       *in6ai = data->in6ai;
350 
351       if (olddata != NULL && olddata->usecnt > 0
352 	  && atomic_add_zero (&olddata->usecnt, -1))
353 	free (olddata);
354 
355       return;
356     }
357 
358   /* We cannot determine what interfaces are available.  Be
359      pessimistic.  */
360   *seen_ipv4 = true;
361   *seen_ipv6 = true;
362 }
363 
364 /* Free the cache if it has been allocated.  */
libc_freeres_fn(freecache)365 libc_freeres_fn (freecache)
366 {
367   if (cache)
368     __free_in6ai (cache->in6ai);
369 }
370 
371 void
__free_in6ai(struct in6addrinfo * ai)372 __free_in6ai (struct in6addrinfo *ai)
373 {
374   if (ai != NULL)
375     {
376       struct cached_data *data =
377 	(struct cached_data *) ((char *) ai
378 				- offsetof (struct cached_data, in6ai));
379 
380       if (atomic_add_zero (&data->usecnt, -1))
381 	{
382 	  __libc_lock_lock (lock);
383 
384 	  if (data->usecnt == 0)
385 	    /* Still unused.  */
386 	    free (data);
387 
388 	  __libc_lock_unlock (lock);
389 	}
390     }
391 }
392