]>
Commit | Line | Data |
---|---|---|
1dc869d1 | 1 | /* Determine protocol families for which interfaces exist. Linux version. |
32c075e1 | 2 | Copyright (C) 2003, 2006, 2007 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 | |
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. */ | |
19 | ||
b74a8c7f | 20 | #include <assert.h> |
1dc869d1 UD |
21 | #include <errno.h> |
22 | #include <ifaddrs.h> | |
23 | #include <netdb.h> | |
b74a8c7f | 24 | #include <stddef.h> |
1dc869d1 UD |
25 | #include <string.h> |
26 | #include <time.h> | |
27 | #include <unistd.h> | |
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> |
6ddd37a4 | 35 | #include <kernel-features.h> |
1dc869d1 UD |
36 | |
37 | ||
3af48b5b UD |
38 | #ifndef IFA_F_TEMPORARY |
39 | # define IFA_F_TEMPORARY IFA_F_SECONDARY | |
40 | #endif | |
457b559e UD |
41 | #ifndef IFA_F_HOMEADDRESS |
42 | # define IFA_F_HOMEADDRESS 0 | |
43 | #endif | |
3af48b5b UD |
44 | |
45 | ||
1dc869d1 | 46 | static int |
3af48b5b UD |
47 | make_request (int fd, pid_t pid, bool *seen_ipv4, bool *seen_ipv6, |
48 | struct in6addrinfo **in6ai, size_t *in6ailen) | |
1dc869d1 | 49 | { |
b74a8c7f | 50 | struct req |
1dc869d1 UD |
51 | { |
52 | struct nlmsghdr nlh; | |
53 | struct rtgenmsg g; | |
b74a8c7f UD |
54 | /* struct rtgenmsg consists of a single byte. This means there |
55 | are three bytes of padding included in the REQ definition. | |
56 | We make them explicit here. */ | |
57 | char pad[3]; | |
1dc869d1 UD |
58 | } req; |
59 | struct sockaddr_nl nladdr; | |
60 | ||
61 | req.nlh.nlmsg_len = sizeof (req); | |
62 | req.nlh.nlmsg_type = RTM_GETADDR; | |
63 | req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST; | |
64 | req.nlh.nlmsg_pid = 0; | |
65 | req.nlh.nlmsg_seq = time (NULL); | |
66 | req.g.rtgen_family = AF_UNSPEC; | |
67 | ||
b74a8c7f UD |
68 | assert (sizeof (req) - offsetof (struct req, pad) == 3); |
69 | memset (req.pad, '\0', sizeof (req.pad)); | |
70 | ||
1dc869d1 UD |
71 | memset (&nladdr, '\0', sizeof (nladdr)); |
72 | nladdr.nl_family = AF_NETLINK; | |
73 | ||
32c075e1 JJ |
74 | #ifdef PAGE_SIZE |
75 | /* Help the compiler optimize out the malloc call if PAGE_SIZE | |
76 | is constant and smaller or equal to PTHREAD_STACK_MIN/4. */ | |
77 | const size_t buf_size = PAGE_SIZE; | |
78 | #else | |
79 | const size_t buf_size = __getpagesize (); | |
80 | #endif | |
81 | bool use_malloc = false; | |
82 | char *buf; | |
83 | ||
84 | if (__libc_use_alloca (buf_size)) | |
85 | buf = alloca (buf_size); | |
86 | else | |
87 | { | |
88 | buf = malloc (buf_size); | |
89 | if (buf != NULL) | |
90 | use_malloc = true; | |
91 | else | |
92 | goto out_fail; | |
93 | } | |
94 | ||
95 | struct iovec iov = { buf, buf_size }; | |
96 | ||
1dc869d1 UD |
97 | if (TEMP_FAILURE_RETRY (__sendto (fd, (void *) &req, sizeof (req), 0, |
98 | (struct sockaddr *) &nladdr, | |
99 | sizeof (nladdr))) < 0) | |
32c075e1 | 100 | goto out_fail; |
1dc869d1 UD |
101 | |
102 | *seen_ipv4 = false; | |
103 | *seen_ipv6 = false; | |
104 | ||
105 | bool done = false; | |
3af48b5b UD |
106 | struct in6ailist |
107 | { | |
108 | struct in6addrinfo info; | |
109 | struct in6ailist *next; | |
110 | } *in6ailist = NULL; | |
111 | size_t in6ailistlen = 0; | |
1dc869d1 UD |
112 | |
113 | do | |
114 | { | |
115 | struct msghdr msg = | |
116 | { | |
117 | (void *) &nladdr, sizeof (nladdr), | |
118 | &iov, 1, | |
119 | NULL, 0, | |
120 | 0 | |
121 | }; | |
122 | ||
123 | ssize_t read_len = TEMP_FAILURE_RETRY (__recvmsg (fd, &msg, 0)); | |
124 | if (read_len < 0) | |
32c075e1 | 125 | goto out_fail; |
1dc869d1 UD |
126 | |
127 | if (msg.msg_flags & MSG_TRUNC) | |
32c075e1 | 128 | goto out_fail; |
1dc869d1 UD |
129 | |
130 | struct nlmsghdr *nlmh; | |
131 | for (nlmh = (struct nlmsghdr *) buf; | |
132 | NLMSG_OK (nlmh, (size_t) read_len); | |
133 | nlmh = (struct nlmsghdr *) NLMSG_NEXT (nlmh, read_len)) | |
134 | { | |
25ce4c6b | 135 | if (nladdr.nl_pid != 0 || (pid_t) nlmh->nlmsg_pid != pid |
1dc869d1 UD |
136 | || nlmh->nlmsg_seq != req.nlh.nlmsg_seq) |
137 | continue; | |
138 | ||
139 | if (nlmh->nlmsg_type == RTM_NEWADDR) | |
140 | { | |
141 | struct ifaddrmsg *ifam = (struct ifaddrmsg *) NLMSG_DATA (nlmh); | |
142 | ||
143 | switch (ifam->ifa_family) | |
144 | { | |
145 | case AF_INET: | |
146 | *seen_ipv4 = true; | |
147 | break; | |
148 | case AF_INET6: | |
149 | *seen_ipv6 = true; | |
3af48b5b | 150 | |
457b559e UD |
151 | if (ifam->ifa_flags & (IFA_F_DEPRECATED |
152 | | IFA_F_TEMPORARY | |
153 | | IFA_F_HOMEADDRESS)) | |
3af48b5b UD |
154 | { |
155 | struct rtattr *rta = IFA_RTA (ifam); | |
156 | size_t len = (nlmh->nlmsg_len | |
157 | - NLMSG_LENGTH (sizeof (*ifam))); | |
158 | void *local = NULL; | |
159 | void *address = NULL; | |
160 | while (RTA_OK (rta, len)) | |
161 | { | |
162 | switch (rta->rta_type) | |
163 | { | |
164 | case IFA_LOCAL: | |
165 | local = RTA_DATA (rta); | |
166 | break; | |
167 | ||
168 | case IFA_ADDRESS: | |
786dcb62 | 169 | address = RTA_DATA (rta); |
3af48b5b UD |
170 | break; |
171 | } | |
172 | ||
173 | rta = RTA_NEXT (rta, len); | |
174 | } | |
175 | ||
176 | struct in6ailist *newp = alloca (sizeof (*newp)); | |
177 | newp->info.flags = (((ifam->ifa_flags & IFA_F_DEPRECATED) | |
178 | ? in6ai_deprecated : 0) | |
179 | | ((ifam->ifa_flags | |
180 | & IFA_F_TEMPORARY) | |
457b559e UD |
181 | ? in6ai_temporary : 0) |
182 | | ((ifam->ifa_flags | |
183 | & IFA_F_HOMEADDRESS) | |
184 | ? in6ai_homeaddress : 0)); | |
3af48b5b UD |
185 | memcpy (newp->info.addr, address ?: local, |
186 | sizeof (newp->info.addr)); | |
187 | newp->next = in6ailist; | |
786dcb62 | 188 | in6ailist = newp; |
3af48b5b UD |
189 | ++in6ailistlen; |
190 | } | |
1dc869d1 UD |
191 | break; |
192 | default: | |
193 | /* Ignore. */ | |
194 | break; | |
195 | } | |
196 | } | |
197 | else if (nlmh->nlmsg_type == NLMSG_DONE) | |
198 | /* We found the end, leave the loop. */ | |
199 | done = true; | |
1dc869d1 UD |
200 | } |
201 | } | |
202 | while (! done); | |
203 | ||
3af48b5b UD |
204 | close_not_cancel_no_status (fd); |
205 | ||
206 | if (in6ailist != NULL) | |
207 | { | |
208 | *in6ai = malloc (in6ailistlen * sizeof (**in6ai)); | |
209 | if (*in6ai == NULL) | |
32c075e1 | 210 | goto out_fail; |
3af48b5b UD |
211 | |
212 | *in6ailen = in6ailistlen; | |
213 | ||
214 | do | |
215 | { | |
216 | (*in6ai)[--in6ailistlen] = in6ailist->info; | |
217 | in6ailist = in6ailist->next; | |
218 | } | |
219 | while (in6ailist != NULL); | |
220 | } | |
1dc869d1 | 221 | |
32c075e1 JJ |
222 | if (use_malloc) |
223 | free (buf); | |
1dc869d1 | 224 | return 0; |
32c075e1 JJ |
225 | |
226 | out_fail: | |
227 | if (use_malloc) | |
228 | free (buf); | |
229 | return -1; | |
1dc869d1 UD |
230 | } |
231 | ||
232 | ||
233 | /* We don't know if we have NETLINK support compiled in in our | |
234 | Kernel. */ | |
235 | #if __ASSUME_NETLINK_SUPPORT == 0 | |
236 | /* Define in ifaddrs.h. */ | |
237 | extern int __no_netlink_support attribute_hidden; | |
238 | #else | |
239 | # define __no_netlink_support 0 | |
240 | #endif | |
241 | ||
242 | ||
243 | void | |
244 | attribute_hidden | |
3af48b5b UD |
245 | __check_pf (bool *seen_ipv4, bool *seen_ipv6, |
246 | struct in6addrinfo **in6ai, size_t *in6ailen) | |
1dc869d1 | 247 | { |
3af48b5b UD |
248 | *in6ai = NULL; |
249 | *in6ailen = 0; | |
250 | ||
1dc869d1 UD |
251 | if (! __no_netlink_support) |
252 | { | |
253 | int fd = __socket (PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); | |
254 | ||
255 | struct sockaddr_nl nladdr; | |
256 | memset (&nladdr, '\0', sizeof (nladdr)); | |
257 | nladdr.nl_family = AF_NETLINK; | |
258 | ||
259 | socklen_t addr_len = sizeof (nladdr); | |
260 | ||
261 | if (fd >= 0 | |
262 | && __bind (fd, (struct sockaddr *) &nladdr, sizeof (nladdr)) == 0 | |
263 | && __getsockname (fd, (struct sockaddr *) &nladdr, &addr_len) == 0 | |
3af48b5b UD |
264 | && make_request (fd, nladdr.nl_pid, seen_ipv4, seen_ipv6, |
265 | in6ai, in6ailen) == 0) | |
1dc869d1 UD |
266 | /* It worked. */ |
267 | return; | |
268 | ||
269 | if (fd >= 0) | |
8ba1d429 | 270 | __close (fd); |
1dc869d1 UD |
271 | |
272 | #if __ASSUME_NETLINK_SUPPORT == 0 | |
273 | /* Remember that there is no netlink support. */ | |
274 | __no_netlink_support = 1; | |
275 | #else | |
276 | /* We cannot determine what interfaces are available. Be | |
277 | pessimistic. */ | |
278 | *seen_ipv4 = true; | |
279 | *seen_ipv6 = true; | |
280 | #endif | |
281 | } | |
282 | ||
283 | #if __ASSUME_NETLINK_SUPPORT == 0 | |
284 | /* No netlink. Get the interface list via getifaddrs. */ | |
285 | struct ifaddrs *ifa = NULL; | |
286 | if (getifaddrs (&ifa) != 0) | |
287 | { | |
288 | /* We cannot determine what interfaces are available. Be | |
289 | pessimistic. */ | |
290 | *seen_ipv4 = true; | |
291 | *seen_ipv6 = true; | |
292 | return; | |
293 | } | |
294 | ||
1dc869d1 UD |
295 | struct ifaddrs *runp; |
296 | for (runp = ifa; runp != NULL; runp = runp->ifa_next) | |
297 | if (runp->ifa_addr->sa_family == PF_INET) | |
298 | *seen_ipv4 = true; | |
299 | else if (runp->ifa_addr->sa_family == PF_INET6) | |
300 | *seen_ipv6 = true; | |
301 | ||
302 | (void) freeifaddrs (ifa); | |
303 | #endif | |
304 | } |