Define F_DUPFD_CLOEXEC.
[glibc-ports.git] / sysdeps / unix / sysv / linux / arm / check_pf.c
blobdfca75d374c1995ef52b29497bab16accaf657be
1 /* Determine protocol families for which interfaces exist. Linux version.
2 Copyright (C) 2003, 2006, 2007 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
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.
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.
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, write to the Free
17 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18 02111-1307 USA. */
20 #include <assert.h>
21 #include <errno.h>
22 #include <ifaddrs.h>
23 #include <netdb.h>
24 #include <stddef.h>
25 #include <string.h>
26 #include <time.h>
27 #include <unistd.h>
28 #include <sys/socket.h>
30 #include <asm/types.h>
31 #include <linux/netlink.h>
32 #include <linux/rtnetlink.h>
34 #include <not-cancel.h>
35 #include <kernel-features.h>
38 #ifndef IFA_F_TEMPORARY
39 # define IFA_F_TEMPORARY IFA_F_SECONDARY
40 #endif
41 #ifndef IFA_F_HOMEADDRESS
42 # define IFA_F_HOMEADDRESS 0
43 #endif
46 static int
47 make_request (int fd, pid_t pid, bool *seen_ipv4, bool *seen_ipv6,
48 struct in6addrinfo **in6ai, size_t *in6ailen)
50 struct req
52 struct nlmsghdr nlh;
53 struct rtgenmsg g;
54 } req;
55 struct sockaddr_nl nladdr;
57 /* struct rtgenmsg consists of a single byte but the ARM ABI rounds
58 it up to a word. Clear the padding explicitly here. */
59 assert (sizeof (req.g) == 4);
60 memset (&req.g, '\0', sizeof (req.g));
62 req.nlh.nlmsg_len = sizeof (req);
63 req.nlh.nlmsg_type = RTM_GETADDR;
64 req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
65 req.nlh.nlmsg_pid = 0;
66 req.nlh.nlmsg_seq = time (NULL);
67 req.g.rtgen_family = AF_UNSPEC;
69 memset (&nladdr, '\0', sizeof (nladdr));
70 nladdr.nl_family = AF_NETLINK;
72 #ifdef PAGE_SIZE
73 /* Help the compiler optimize out the malloc call if PAGE_SIZE
74 is constant and smaller or equal to PTHREAD_STACK_MIN/4. */
75 const size_t buf_size = PAGE_SIZE;
76 #else
77 const size_t buf_size = __getpagesize ();
78 #endif
79 bool use_malloc = false;
80 char *buf;
82 if (__libc_use_alloca (buf_size))
83 buf = alloca (buf_size);
84 else
86 buf = malloc (buf_size);
87 if (buf != NULL)
88 use_malloc = true;
89 else
90 goto out_fail;
93 struct iovec iov = { buf, buf_size };
95 if (TEMP_FAILURE_RETRY (__sendto (fd, (void *) &req, sizeof (req), 0,
96 (struct sockaddr *) &nladdr,
97 sizeof (nladdr))) < 0)
98 goto out_fail;
100 *seen_ipv4 = false;
101 *seen_ipv6 = false;
103 bool done = false;
104 struct in6ailist
106 struct in6addrinfo info;
107 struct in6ailist *next;
108 } *in6ailist = NULL;
109 size_t in6ailistlen = 0;
113 struct msghdr msg =
115 (void *) &nladdr, sizeof (nladdr),
116 &iov, 1,
117 NULL, 0,
121 ssize_t read_len = TEMP_FAILURE_RETRY (__recvmsg (fd, &msg, 0));
122 if (read_len < 0)
123 goto out_fail;
125 if (msg.msg_flags & MSG_TRUNC)
126 goto out_fail;
128 struct nlmsghdr *nlmh;
129 for (nlmh = (struct nlmsghdr *) buf;
130 NLMSG_OK (nlmh, (size_t) read_len);
131 nlmh = (struct nlmsghdr *) NLMSG_NEXT (nlmh, read_len))
133 if (nladdr.nl_pid != 0 || (pid_t) nlmh->nlmsg_pid != pid
134 || nlmh->nlmsg_seq != req.nlh.nlmsg_seq)
135 continue;
137 if (nlmh->nlmsg_type == RTM_NEWADDR)
139 struct ifaddrmsg *ifam = (struct ifaddrmsg *) NLMSG_DATA (nlmh);
140 struct rtattr *rta = IFA_RTA (ifam);
141 size_t len = nlmh->nlmsg_len - NLMSG_LENGTH (sizeof (*ifam));
143 switch (ifam->ifa_family)
145 const void *local;
146 const void *address;
148 case AF_INET:
149 local = NULL;
150 address = NULL;
151 while (RTA_OK (rta, len))
153 switch (rta->rta_type)
155 case IFA_LOCAL:
156 local = RTA_DATA (rta);
157 break;
159 case IFA_ADDRESS:
160 address = RTA_DATA (rta);
161 goto out_v4;
164 rta = RTA_NEXT (rta, len);
167 if (local != NULL)
169 out_v4:
170 if (*(const in_addr_t *) (address ?: local)
171 != htonl (INADDR_LOOPBACK))
172 *seen_ipv4 = true;
174 break;
176 case AF_INET6:
177 local = NULL;
178 address = NULL;
179 while (RTA_OK (rta, len))
181 switch (rta->rta_type)
183 case IFA_LOCAL:
184 local = RTA_DATA (rta);
185 break;
187 case IFA_ADDRESS:
188 address = RTA_DATA (rta);
189 goto out_v6;
192 rta = RTA_NEXT (rta, len);
195 if (local != NULL)
197 out_v6:
198 if (!IN6_IS_ADDR_LOOPBACK (address ?: local))
199 *seen_ipv6 = true;
202 if (ifam->ifa_flags & (IFA_F_DEPRECATED
203 | IFA_F_TEMPORARY
204 | IFA_F_HOMEADDRESS))
206 struct in6ailist *newp = alloca (sizeof (*newp));
207 newp->info.flags = (((ifam->ifa_flags & IFA_F_DEPRECATED)
208 ? in6ai_deprecated : 0)
209 | ((ifam->ifa_flags
210 & IFA_F_TEMPORARY)
211 ? in6ai_temporary : 0)
212 | ((ifam->ifa_flags
213 & IFA_F_HOMEADDRESS)
214 ? in6ai_homeaddress : 0));
215 memcpy (newp->info.addr, address ?: local,
216 sizeof (newp->info.addr));
217 newp->next = in6ailist;
218 in6ailist = newp;
219 ++in6ailistlen;
221 break;
222 default:
223 /* Ignore. */
224 break;
227 else if (nlmh->nlmsg_type == NLMSG_DONE)
228 /* We found the end, leave the loop. */
229 done = true;
232 while (! done);
234 close_not_cancel_no_status (fd);
236 if (*seen_ipv6 && in6ailist != NULL)
238 *in6ai = malloc (in6ailistlen * sizeof (**in6ai));
239 if (*in6ai == NULL)
240 goto out_fail;
242 *in6ailen = in6ailistlen;
246 (*in6ai)[--in6ailistlen] = in6ailist->info;
247 in6ailist = in6ailist->next;
249 while (in6ailist != NULL);
252 if (use_malloc)
253 free (buf);
254 return 0;
256 out_fail:
257 if (use_malloc)
258 free (buf);
259 return -1;
263 /* We don't know if we have NETLINK support compiled in in our
264 Kernel. */
265 #if __ASSUME_NETLINK_SUPPORT == 0
266 /* Define in ifaddrs.h. */
267 extern int __no_netlink_support attribute_hidden;
268 #else
269 # define __no_netlink_support 0
270 #endif
273 void
274 attribute_hidden
275 __check_pf (bool *seen_ipv4, bool *seen_ipv6,
276 struct in6addrinfo **in6ai, size_t *in6ailen)
278 *in6ai = NULL;
279 *in6ailen = 0;
281 if (! __no_netlink_support)
283 int fd = __socket (PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
285 struct sockaddr_nl nladdr;
286 memset (&nladdr, '\0', sizeof (nladdr));
287 nladdr.nl_family = AF_NETLINK;
289 socklen_t addr_len = sizeof (nladdr);
291 if (fd >= 0
292 && __bind (fd, (struct sockaddr *) &nladdr, sizeof (nladdr)) == 0
293 && __getsockname (fd, (struct sockaddr *) &nladdr, &addr_len) == 0
294 && make_request (fd, nladdr.nl_pid, seen_ipv4, seen_ipv6,
295 in6ai, in6ailen) == 0)
296 /* It worked. */
297 return;
299 if (fd >= 0)
300 __close (fd);
302 #if __ASSUME_NETLINK_SUPPORT == 0
303 /* Remember that there is no netlink support. */
304 __no_netlink_support = 1;
305 #else
306 /* We cannot determine what interfaces are available. Be
307 pessimistic. */
308 *seen_ipv4 = true;
309 *seen_ipv6 = true;
310 #endif
313 #if __ASSUME_NETLINK_SUPPORT == 0
314 /* No netlink. Get the interface list via getifaddrs. */
315 struct ifaddrs *ifa = NULL;
316 if (getifaddrs (&ifa) != 0)
318 /* We cannot determine what interfaces are available. Be
319 pessimistic. */
320 *seen_ipv4 = true;
321 *seen_ipv6 = true;
322 return;
325 struct ifaddrs *runp;
326 for (runp = ifa; runp != NULL; runp = runp->ifa_next)
327 if (runp->ifa_addr->sa_family == PF_INET)
328 *seen_ipv4 = true;
329 else if (runp->ifa_addr->sa_family == PF_INET6)
330 *seen_ipv6 = true;
332 (void) freeifaddrs (ifa);
333 #endif