]>
Commit | Line | Data |
---|---|---|
1dc869d1 | 1 | /* Determine protocol families for which interfaces exist. Linux version. |
b168057a | 2 | Copyright (C) 2003-2015 Free Software Foundation, Inc. |
1dc869d1 UD |
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 | |
59ba27a6 PE |
16 | License along with the GNU C Library; if not, see |
17 | <http://www.gnu.org/licenses/>. */ | |
1dc869d1 | 18 | |
b74a8c7f | 19 | #include <assert.h> |
1dc869d1 UD |
20 | #include <errno.h> |
21 | #include <ifaddrs.h> | |
22 | #include <netdb.h> | |
b74a8c7f | 23 | #include <stddef.h> |
1dc869d1 UD |
24 | #include <string.h> |
25 | #include <time.h> | |
26 | #include <unistd.h> | |
e054f494 | 27 | #include <stdint.h> |
1dc869d1 UD |
28 | #include <sys/socket.h> |
29 | ||
30 | #include <asm/types.h> | |
31 | #include <linux/netlink.h> | |
32 | #include <linux/rtnetlink.h> | |
33 | ||
3af48b5b | 34 | #include <not-cancel.h> |
3a2c0242 UD |
35 | #include <bits/libc-lock.h> |
36 | #include <atomic.h> | |
37 | #include <nscd/nscd-client.h> | |
1dc869d1 UD |
38 | |
39 | ||
457b559e UD |
40 | #ifndef IFA_F_HOMEADDRESS |
41 | # define IFA_F_HOMEADDRESS 0 | |
42 | #endif | |
de4d8563 UD |
43 | #ifndef IFA_F_OPTIMISTIC |
44 | # define IFA_F_OPTIMISTIC 0 | |
45 | #endif | |
3af48b5b UD |
46 | |
47 | ||
3a2c0242 UD |
48 | struct cached_data |
49 | { | |
50 | uint32_t timestamp; | |
51 | uint32_t usecnt; | |
52 | bool seen_ipv4; | |
53 | bool seen_ipv6; | |
54 | size_t in6ailen; | |
55 | struct in6addrinfo in6ai[0]; | |
56 | }; | |
57 | ||
58 | static struct cached_data noai6ai_cached = | |
59 | { | |
09f93bd3 | 60 | .usecnt = 1, /* Make sure we never try to delete this entry. */ |
3a2c0242 UD |
61 | .in6ailen = 0 |
62 | }; | |
63 | ||
d62aa75a | 64 | static struct cached_data *cache; |
3a2c0242 UD |
65 | __libc_lock_define_initialized (static, lock); |
66 | ||
67 | ||
85f36372 | 68 | #if IS_IN (nscd) |
3a2c0242 UD |
69 | static uint32_t nl_timestamp; |
70 | ||
71 | uint32_t | |
72 | __bump_nl_timestamp (void) | |
73 | { | |
74 | if (atomic_increment_val (&nl_timestamp) == 0) | |
75 | atomic_increment (&nl_timestamp); | |
76 | ||
77 | return nl_timestamp; | |
78 | } | |
79 | #endif | |
80 | ||
3cc3ef96 RM |
81 | static inline uint32_t |
82 | get_nl_timestamp (void) | |
83 | { | |
85f36372 | 84 | #if IS_IN (nscd) |
3cc3ef96 RM |
85 | return nl_timestamp; |
86 | #elif defined USE_NSCD | |
87 | return __nscd_get_nl_timestamp (); | |
88 | #else | |
89 | return 0; | |
90 | #endif | |
91 | } | |
92 | ||
93 | static inline bool | |
94 | cache_valid_p (void) | |
95 | { | |
96 | if (cache != NULL) | |
97 | { | |
98 | uint32_t timestamp = get_nl_timestamp (); | |
99 | return timestamp != 0 && cache->timestamp == timestamp; | |
100 | } | |
101 | return false; | |
102 | } | |
103 | ||
3a2c0242 UD |
104 | |
105 | static struct cached_data * | |
106 | make_request (int fd, pid_t pid) | |
1dc869d1 | 107 | { |
b74a8c7f | 108 | struct req |
1dc869d1 UD |
109 | { |
110 | struct nlmsghdr nlh; | |
111 | struct rtgenmsg g; | |
b74a8c7f UD |
112 | /* struct rtgenmsg consists of a single byte. This means there |
113 | are three bytes of padding included in the REQ definition. | |
114 | We make them explicit here. */ | |
115 | char pad[3]; | |
1dc869d1 UD |
116 | } req; |
117 | struct sockaddr_nl nladdr; | |
118 | ||
119 | req.nlh.nlmsg_len = sizeof (req); | |
120 | req.nlh.nlmsg_type = RTM_GETADDR; | |
121 | req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST; | |
122 | req.nlh.nlmsg_pid = 0; | |
123 | req.nlh.nlmsg_seq = time (NULL); | |
124 | req.g.rtgen_family = AF_UNSPEC; | |
125 | ||
b74a8c7f UD |
126 | assert (sizeof (req) - offsetof (struct req, pad) == 3); |
127 | memset (req.pad, '\0', sizeof (req.pad)); | |
128 | ||
1dc869d1 UD |
129 | memset (&nladdr, '\0', sizeof (nladdr)); |
130 | nladdr.nl_family = AF_NETLINK; | |
131 | ||
6cb988fa UD |
132 | #ifdef PAGE_SIZE |
133 | /* Help the compiler optimize out the malloc call if PAGE_SIZE | |
134 | is constant and smaller or equal to PTHREAD_STACK_MIN/4. */ | |
135 | const size_t buf_size = PAGE_SIZE; | |
136 | #else | |
137 | const size_t buf_size = __getpagesize (); | |
138 | #endif | |
139 | bool use_malloc = false; | |
140 | char *buf; | |
44152e4b | 141 | size_t alloca_used = 0; |
6cb988fa UD |
142 | |
143 | if (__libc_use_alloca (buf_size)) | |
44152e4b | 144 | buf = alloca_account (buf_size, alloca_used); |
6cb988fa UD |
145 | else |
146 | { | |
147 | buf = malloc (buf_size); | |
148 | if (buf != NULL) | |
149 | use_malloc = true; | |
150 | else | |
151 | goto out_fail; | |
152 | } | |
153 | ||
154 | struct iovec iov = { buf, buf_size }; | |
155 | ||
1dc869d1 UD |
156 | if (TEMP_FAILURE_RETRY (__sendto (fd, (void *) &req, sizeof (req), 0, |
157 | (struct sockaddr *) &nladdr, | |
158 | sizeof (nladdr))) < 0) | |
6cb988fa | 159 | goto out_fail; |
1dc869d1 | 160 | |
1dc869d1 | 161 | bool done = false; |
3af48b5b UD |
162 | struct in6ailist |
163 | { | |
164 | struct in6addrinfo info; | |
165 | struct in6ailist *next; | |
44152e4b | 166 | bool use_malloc; |
3af48b5b UD |
167 | } *in6ailist = NULL; |
168 | size_t in6ailistlen = 0; | |
3a2c0242 UD |
169 | bool seen_ipv4 = false; |
170 | bool seen_ipv6 = false; | |
1dc869d1 UD |
171 | |
172 | do | |
173 | { | |
174 | struct msghdr msg = | |
175 | { | |
176 | (void *) &nladdr, sizeof (nladdr), | |
177 | &iov, 1, | |
178 | NULL, 0, | |
179 | 0 | |
180 | }; | |
181 | ||
182 | ssize_t read_len = TEMP_FAILURE_RETRY (__recvmsg (fd, &msg, 0)); | |
fda389c8 | 183 | if (read_len <= 0) |
6da36183 | 184 | goto out_fail2; |
1dc869d1 UD |
185 | |
186 | if (msg.msg_flags & MSG_TRUNC) | |
6da36183 | 187 | goto out_fail2; |
1dc869d1 UD |
188 | |
189 | struct nlmsghdr *nlmh; | |
190 | for (nlmh = (struct nlmsghdr *) buf; | |
191 | NLMSG_OK (nlmh, (size_t) read_len); | |
192 | nlmh = (struct nlmsghdr *) NLMSG_NEXT (nlmh, read_len)) | |
193 | { | |
25ce4c6b | 194 | if (nladdr.nl_pid != 0 || (pid_t) nlmh->nlmsg_pid != pid |
1dc869d1 UD |
195 | || nlmh->nlmsg_seq != req.nlh.nlmsg_seq) |
196 | continue; | |
197 | ||
198 | if (nlmh->nlmsg_type == RTM_NEWADDR) | |
199 | { | |
200 | struct ifaddrmsg *ifam = (struct ifaddrmsg *) NLMSG_DATA (nlmh); | |
df18fdb9 UD |
201 | struct rtattr *rta = IFA_RTA (ifam); |
202 | size_t len = nlmh->nlmsg_len - NLMSG_LENGTH (sizeof (*ifam)); | |
1dc869d1 | 203 | |
6f3914d5 UD |
204 | if (ifam->ifa_family != AF_INET |
205 | && ifam->ifa_family != AF_INET6) | |
206 | continue; | |
df18fdb9 | 207 | |
6f3914d5 UD |
208 | const void *local = NULL; |
209 | const void *address = NULL; | |
210 | while (RTA_OK (rta, len)) | |
211 | { | |
212 | switch (rta->rta_type) | |
df18fdb9 | 213 | { |
6f3914d5 UD |
214 | case IFA_LOCAL: |
215 | local = RTA_DATA (rta); | |
216 | break; | |
217 | ||
218 | case IFA_ADDRESS: | |
219 | address = RTA_DATA (rta); | |
220 | goto out; | |
df18fdb9 UD |
221 | } |
222 | ||
6f3914d5 UD |
223 | rta = RTA_NEXT (rta, len); |
224 | } | |
225 | ||
226 | if (local != NULL) | |
227 | { | |
228 | address = local; | |
229 | out: | |
ad586879 | 230 | if (ifam->ifa_family == AF_INET) |
df18fdb9 | 231 | { |
6f3914d5 | 232 | if (*(const in_addr_t *) address |
df18fdb9 | 233 | != htonl (INADDR_LOOPBACK)) |
3a2c0242 | 234 | seen_ipv4 = true; |
df18fdb9 | 235 | } |
6f3914d5 | 236 | else |
df18fdb9 | 237 | { |
6f3914d5 | 238 | if (!IN6_IS_ADDR_LOOPBACK (address)) |
3a2c0242 | 239 | seen_ipv6 = true; |
df18fdb9 | 240 | } |
6f3914d5 | 241 | } |
3af48b5b | 242 | |
44152e4b AS |
243 | struct in6ailist *newp; |
244 | if (__libc_use_alloca (alloca_used + sizeof (*newp))) | |
245 | { | |
246 | newp = alloca_account (sizeof (*newp), alloca_used); | |
247 | newp->use_malloc = false; | |
248 | } | |
249 | else | |
250 | { | |
251 | newp = malloc (sizeof (*newp)); | |
252 | if (newp == NULL) | |
6da36183 | 253 | goto out_fail2; |
44152e4b AS |
254 | newp->use_malloc = true; |
255 | } | |
6f3914d5 UD |
256 | newp->info.flags = (((ifam->ifa_flags |
257 | & (IFA_F_DEPRECATED | |
258 | | IFA_F_OPTIMISTIC)) | |
259 | ? in6ai_deprecated : 0) | |
6f3914d5 UD |
260 | | ((ifam->ifa_flags |
261 | & IFA_F_HOMEADDRESS) | |
262 | ? in6ai_homeaddress : 0)); | |
263 | newp->info.prefixlen = ifam->ifa_prefixlen; | |
773e79b3 | 264 | newp->info.index = ifam->ifa_index; |
6f3914d5 UD |
265 | if (ifam->ifa_family == AF_INET) |
266 | { | |
267 | newp->info.addr[0] = 0; | |
268 | newp->info.addr[1] = 0; | |
269 | newp->info.addr[2] = htonl (0xffff); | |
270 | newp->info.addr[3] = *(const in_addr_t *) address; | |
1dc869d1 | 271 | } |
6f3914d5 UD |
272 | else |
273 | memcpy (newp->info.addr, address, sizeof (newp->info.addr)); | |
274 | newp->next = in6ailist; | |
275 | in6ailist = newp; | |
276 | ++in6ailistlen; | |
1dc869d1 UD |
277 | } |
278 | else if (nlmh->nlmsg_type == NLMSG_DONE) | |
279 | /* We found the end, leave the loop. */ | |
280 | done = true; | |
1dc869d1 UD |
281 | } |
282 | } | |
283 | while (! done); | |
284 | ||
3a2c0242 UD |
285 | struct cached_data *result; |
286 | if (seen_ipv6 && in6ailist != NULL) | |
3af48b5b | 287 | { |
3a2c0242 UD |
288 | result = malloc (sizeof (*result) |
289 | + in6ailistlen * sizeof (struct in6addrinfo)); | |
290 | if (result == NULL) | |
6da36183 | 291 | goto out_fail2; |
3af48b5b | 292 | |
3cc3ef96 | 293 | result->timestamp = get_nl_timestamp (); |
3a2c0242 UD |
294 | result->usecnt = 2; |
295 | result->seen_ipv4 = seen_ipv4; | |
296 | result->seen_ipv6 = true; | |
297 | result->in6ailen = in6ailistlen; | |
3af48b5b UD |
298 | |
299 | do | |
300 | { | |
3a2c0242 | 301 | result->in6ai[--in6ailistlen] = in6ailist->info; |
44152e4b AS |
302 | struct in6ailist *next = in6ailist->next; |
303 | if (in6ailist->use_malloc) | |
304 | free (in6ailist); | |
305 | in6ailist = next; | |
3af48b5b UD |
306 | } |
307 | while (in6ailist != NULL); | |
308 | } | |
3a2c0242 UD |
309 | else |
310 | { | |
a501a01e | 311 | atomic_add (&noai6ai_cached.usecnt, 2); |
3a2c0242 UD |
312 | noai6ai_cached.seen_ipv4 = seen_ipv4; |
313 | noai6ai_cached.seen_ipv6 = seen_ipv6; | |
314 | result = &noai6ai_cached; | |
315 | } | |
1dc869d1 | 316 | |
6cb988fa UD |
317 | if (use_malloc) |
318 | free (buf); | |
3a2c0242 | 319 | return result; |
6cb988fa | 320 | |
6da36183 | 321 | out_fail2: |
44152e4b AS |
322 | while (in6ailist != NULL) |
323 | { | |
324 | struct in6ailist *next = in6ailist->next; | |
325 | if (in6ailist->use_malloc) | |
326 | free (in6ailist); | |
327 | in6ailist = next; | |
328 | } | |
6da36183 | 329 | out_fail: |
6cb988fa UD |
330 | if (use_malloc) |
331 | free (buf); | |
3a2c0242 | 332 | return NULL; |
1dc869d1 UD |
333 | } |
334 | ||
335 | ||
1dc869d1 UD |
336 | void |
337 | attribute_hidden | |
3af48b5b UD |
338 | __check_pf (bool *seen_ipv4, bool *seen_ipv6, |
339 | struct in6addrinfo **in6ai, size_t *in6ailen) | |
1dc869d1 | 340 | { |
3af48b5b UD |
341 | *in6ai = NULL; |
342 | *in6ailen = 0; | |
343 | ||
89b4b02f JM |
344 | struct cached_data *olddata = NULL; |
345 | struct cached_data *data = NULL; | |
1dc869d1 | 346 | |
89b4b02f | 347 | __libc_lock_lock (lock); |
3a2c0242 | 348 | |
3cc3ef96 | 349 | if (cache_valid_p ()) |
89b4b02f JM |
350 | { |
351 | data = cache; | |
352 | atomic_increment (&cache->usecnt); | |
353 | } | |
354 | else | |
355 | { | |
356 | int fd = __socket (PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); | |
3a2c0242 | 357 | |
a1ffb40e | 358 | if (__glibc_likely (fd >= 0)) |
89b4b02f JM |
359 | { |
360 | struct sockaddr_nl nladdr; | |
361 | memset (&nladdr, '\0', sizeof (nladdr)); | |
362 | nladdr.nl_family = AF_NETLINK; | |
3a2c0242 | 363 | |
89b4b02f | 364 | socklen_t addr_len = sizeof (nladdr); |
3a2c0242 | 365 | |
89b4b02f JM |
366 | if (__bind (fd, (struct sockaddr *) &nladdr, sizeof (nladdr)) == 0 |
367 | && __getsockname (fd, (struct sockaddr *) &nladdr, | |
368 | &addr_len) == 0) | |
369 | data = make_request (fd, nladdr.nl_pid); | |
3a2c0242 | 370 | |
89b4b02f | 371 | close_not_cancel_no_status (fd); |
3a2c0242 | 372 | } |
1dc869d1 | 373 | |
3a2c0242 UD |
374 | if (data != NULL) |
375 | { | |
89b4b02f JM |
376 | olddata = cache; |
377 | cache = data; | |
636064eb | 378 | } |
1dc869d1 UD |
379 | } |
380 | ||
89b4b02f JM |
381 | __libc_lock_unlock (lock); |
382 | ||
383 | if (data != NULL) | |
1dc869d1 | 384 | { |
89b4b02f JM |
385 | /* It worked. */ |
386 | *seen_ipv4 = data->seen_ipv4; | |
387 | *seen_ipv6 = data->seen_ipv6; | |
388 | *in6ailen = data->in6ailen; | |
389 | *in6ai = data->in6ai; | |
390 | ||
391 | if (olddata != NULL && olddata->usecnt > 0 | |
392 | && atomic_add_zero (&olddata->usecnt, -1)) | |
393 | free (olddata); | |
394 | ||
1dc869d1 UD |
395 | return; |
396 | } | |
397 | ||
89b4b02f JM |
398 | /* We cannot determine what interfaces are available. Be |
399 | pessimistic. */ | |
400 | *seen_ipv4 = true; | |
401 | *seen_ipv6 = true; | |
1dc869d1 | 402 | } |
3a2c0242 | 403 | |
d62aa75a SP |
404 | /* Free the cache if it has been allocated. */ |
405 | libc_freeres_fn (freecache) | |
406 | { | |
407 | if (cache) | |
408 | __free_in6ai (cache->in6ai); | |
409 | } | |
3a2c0242 UD |
410 | |
411 | void | |
412 | __free_in6ai (struct in6addrinfo *ai) | |
413 | { | |
414 | if (ai != NULL) | |
415 | { | |
416 | struct cached_data *data = | |
417 | (struct cached_data *) ((char *) ai | |
418 | - offsetof (struct cached_data, in6ai)); | |
419 | ||
420 | if (atomic_add_zero (&data->usecnt, -1)) | |
421 | { | |
422 | __libc_lock_lock (lock); | |
423 | ||
424 | if (data->usecnt == 0) | |
425 | /* Still unused. */ | |
426 | free (data); | |
427 | ||
428 | __libc_lock_unlock (lock); | |
429 | } | |
430 | } | |
431 | } |