1 | /*
|
---|
2 | * Copyright (c) 2000 - 2002, 2005 Kungliga Tekniska Högskolan
|
---|
3 | * (Royal Institute of Technology, Stockholm, Sweden).
|
---|
4 | * All rights reserved.
|
---|
5 | *
|
---|
6 | * Redistribution and use in source and binary forms, with or without
|
---|
7 | * modification, are permitted provided that the following conditions
|
---|
8 | * are met:
|
---|
9 | *
|
---|
10 | * 1. Redistributions of source code must retain the above copyright
|
---|
11 | * notice, this list of conditions and the following disclaimer.
|
---|
12 | *
|
---|
13 | * 2. Redistributions in binary form must reproduce the above copyright
|
---|
14 | * notice, this list of conditions and the following disclaimer in the
|
---|
15 | * documentation and/or other materials provided with the distribution.
|
---|
16 | *
|
---|
17 | * 3. Neither the name of the Institute nor the names of its contributors
|
---|
18 | * may be used to endorse or promote products derived from this software
|
---|
19 | * without specific prior written permission.
|
---|
20 | *
|
---|
21 | * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
---|
22 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
---|
23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
---|
24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
---|
25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
---|
26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
---|
27 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
---|
28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
---|
29 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
---|
30 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
---|
31 | * SUCH DAMAGE.
|
---|
32 | */
|
---|
33 |
|
---|
34 | #include <config.h>
|
---|
35 | #include "roken.h"
|
---|
36 |
|
---|
37 | #ifdef __osf__
|
---|
38 | /* hate */
|
---|
39 | struct rtentry;
|
---|
40 | struct mbuf;
|
---|
41 | #endif
|
---|
42 | #ifdef HAVE_NET_IF_H
|
---|
43 | #include <net/if.h>
|
---|
44 | #endif
|
---|
45 |
|
---|
46 | #ifdef HAVE_SYS_SOCKIO_H
|
---|
47 | #include <sys/sockio.h>
|
---|
48 | #endif /* HAVE_SYS_SOCKIO_H */
|
---|
49 |
|
---|
50 | #ifdef HAVE_NETINET_IN6_VAR_H
|
---|
51 | #include <netinet/in6_var.h>
|
---|
52 | #endif /* HAVE_NETINET_IN6_VAR_H */
|
---|
53 |
|
---|
54 | #include <ifaddrs.h>
|
---|
55 |
|
---|
56 | #ifdef __hpux
|
---|
57 | #define lifconf if_laddrconf
|
---|
58 | #define lifc_len iflc_len
|
---|
59 | #define lifc_buf iflc_buf
|
---|
60 | #define lifc_req iflc_req
|
---|
61 |
|
---|
62 | #define lifreq if_laddrreq
|
---|
63 | #define lifr_addr iflr_addr
|
---|
64 | #define lifr_name iflr_name
|
---|
65 | #define lifr_dstaddr iflr_dstaddr
|
---|
66 | #define lifr_broadaddr iflr_broadaddr
|
---|
67 | #define lifr_flags iflr_flags
|
---|
68 | #define lifr_index iflr_index
|
---|
69 | #endif
|
---|
70 |
|
---|
71 | #ifdef AF_NETLINK
|
---|
72 |
|
---|
73 | /*
|
---|
74 | * The linux - AF_NETLINK version of getifaddrs - from Usagi.
|
---|
75 | * Linux does not return v6 addresses from SIOCGIFCONF.
|
---|
76 | */
|
---|
77 |
|
---|
78 | /* $USAGI: ifaddrs.c,v 1.18 2002/03/06 01:50:46 yoshfuji Exp $ */
|
---|
79 |
|
---|
80 | /**************************************************************************
|
---|
81 | * ifaddrs.c
|
---|
82 | * Copyright (C)2000 Hideaki YOSHIFUJI, All Rights Reserved.
|
---|
83 | *
|
---|
84 | * Redistribution and use in source and binary forms, with or without
|
---|
85 | * modification, are permitted provided that the following conditions
|
---|
86 | * are met:
|
---|
87 | * 1. Redistributions of source code must retain the above copyright
|
---|
88 | * notice, this list of conditions and the following disclaimer.
|
---|
89 | * 2. Redistributions in binary form must reproduce the above copyright
|
---|
90 | * notice, this list of conditions and the following disclaimer in the
|
---|
91 | * documentation and/or other materials provided with the distribution.
|
---|
92 | * 3. Neither the name of the author nor the names of its contributors
|
---|
93 | * may be used to endorse or promote products derived from this software
|
---|
94 | * without specific prior written permission.
|
---|
95 | *
|
---|
96 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
---|
97 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
---|
98 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
---|
99 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
---|
100 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
---|
101 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
---|
102 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
---|
103 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
---|
104 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
---|
105 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
---|
106 | * SUCH DAMAGE.
|
---|
107 | */
|
---|
108 |
|
---|
109 | #include "config.h"
|
---|
110 |
|
---|
111 | #include <string.h>
|
---|
112 | #include <time.h>
|
---|
113 | #include <malloc.h>
|
---|
114 | #include <errno.h>
|
---|
115 | #include <unistd.h>
|
---|
116 |
|
---|
117 | #include <sys/socket.h>
|
---|
118 | #include <asm/types.h>
|
---|
119 | #include <linux/netlink.h>
|
---|
120 | #include <linux/rtnetlink.h>
|
---|
121 | #include <sys/types.h>
|
---|
122 | #include <sys/socket.h>
|
---|
123 | #include <sys/poll.h>
|
---|
124 | #include <netpacket/packet.h>
|
---|
125 | #include <net/ethernet.h> /* the L2 protocols */
|
---|
126 | #include <sys/uio.h>
|
---|
127 | #include <net/if.h>
|
---|
128 | #include <net/if_arp.h>
|
---|
129 | #include <ifaddrs.h>
|
---|
130 | #include <netinet/in.h>
|
---|
131 |
|
---|
132 | #define __set_errno(e) (errno = (e))
|
---|
133 | #define __close(fd) (close(fd))
|
---|
134 | #undef ifa_broadaddr
|
---|
135 | #define ifa_broadaddr ifa_dstaddr
|
---|
136 | #define IFA_NETMASK
|
---|
137 |
|
---|
138 | /* ====================================================================== */
|
---|
139 | struct nlmsg_list{
|
---|
140 | struct nlmsg_list *nlm_next;
|
---|
141 | struct nlmsghdr *nlh;
|
---|
142 | int size;
|
---|
143 | time_t seq;
|
---|
144 | };
|
---|
145 |
|
---|
146 | struct rtmaddr_ifamap {
|
---|
147 | void *address;
|
---|
148 | void *local;
|
---|
149 | #ifdef IFA_NETMASK
|
---|
150 | void *netmask;
|
---|
151 | #endif
|
---|
152 | void *broadcast;
|
---|
153 | #ifdef HAVE_IFADDRS_IFA_ANYCAST
|
---|
154 | void *anycast;
|
---|
155 | #endif
|
---|
156 | int address_len;
|
---|
157 | int local_len;
|
---|
158 | #ifdef IFA_NETMASK
|
---|
159 | int netmask_len;
|
---|
160 | #endif
|
---|
161 | int broadcast_len;
|
---|
162 | #ifdef HAVE_IFADDRS_IFA_ANYCAST
|
---|
163 | int anycast_len;
|
---|
164 | #endif
|
---|
165 | };
|
---|
166 |
|
---|
167 | /* ====================================================================== */
|
---|
168 | static size_t
|
---|
169 | ifa_sa_len(sa_family_t family, int len)
|
---|
170 | {
|
---|
171 | size_t size;
|
---|
172 | switch(family){
|
---|
173 | case AF_INET:
|
---|
174 | size = sizeof(struct sockaddr_in);
|
---|
175 | break;
|
---|
176 | case AF_INET6:
|
---|
177 | size = sizeof(struct sockaddr_in6);
|
---|
178 | break;
|
---|
179 | case AF_PACKET:
|
---|
180 | size = (size_t)(((struct sockaddr_ll *)NULL)->sll_addr) + len;
|
---|
181 | if (size < sizeof(struct sockaddr_ll))
|
---|
182 | size = sizeof(struct sockaddr_ll);
|
---|
183 | break;
|
---|
184 | default:
|
---|
185 | size = (size_t)(((struct sockaddr *)NULL)->sa_data) + len;
|
---|
186 | if (size < sizeof(struct sockaddr))
|
---|
187 | size = sizeof(struct sockaddr);
|
---|
188 | break;
|
---|
189 | }
|
---|
190 | return size;
|
---|
191 | }
|
---|
192 |
|
---|
193 | static void
|
---|
194 | ifa_make_sockaddr(sa_family_t family,
|
---|
195 | struct sockaddr *sa,
|
---|
196 | void *p, size_t len,
|
---|
197 | uint32_t scope, uint32_t scopeid)
|
---|
198 | {
|
---|
199 | if (sa == NULL) return;
|
---|
200 | switch(family){
|
---|
201 | case AF_INET:
|
---|
202 | memcpy(&((struct sockaddr_in*)sa)->sin_addr, (char *)p, len);
|
---|
203 | break;
|
---|
204 | case AF_INET6:
|
---|
205 | memcpy(&((struct sockaddr_in6*)sa)->sin6_addr, (char *)p, len);
|
---|
206 | if (IN6_IS_ADDR_LINKLOCAL(p) ||
|
---|
207 | IN6_IS_ADDR_MC_LINKLOCAL(p)){
|
---|
208 | ((struct sockaddr_in6*)sa)->sin6_scope_id = scopeid;
|
---|
209 | }
|
---|
210 | break;
|
---|
211 | case AF_PACKET:
|
---|
212 | memcpy(((struct sockaddr_ll*)sa)->sll_addr, (char *)p, len);
|
---|
213 | ((struct sockaddr_ll*)sa)->sll_halen = len;
|
---|
214 | break;
|
---|
215 | default:
|
---|
216 | memcpy(sa->sa_data, p, len); /*XXX*/
|
---|
217 | break;
|
---|
218 | }
|
---|
219 | sa->sa_family = family;
|
---|
220 | #ifdef HAVE_SOCKADDR_SA_LEN
|
---|
221 | sa->sa_len = ifa_sa_len(family, len);
|
---|
222 | #endif
|
---|
223 | }
|
---|
224 |
|
---|
225 | #ifndef IFA_NETMASK
|
---|
226 | static struct sockaddr *
|
---|
227 | ifa_make_sockaddr_mask(sa_family_t family,
|
---|
228 | struct sockaddr *sa,
|
---|
229 | uint32_t prefixlen)
|
---|
230 | {
|
---|
231 | int i;
|
---|
232 | char *p = NULL, c;
|
---|
233 | uint32_t max_prefixlen = 0;
|
---|
234 |
|
---|
235 | if (sa == NULL) return NULL;
|
---|
236 | switch(family){
|
---|
237 | case AF_INET:
|
---|
238 | memset(&((struct sockaddr_in*)sa)->sin_addr, 0, sizeof(((struct sockaddr_in*)sa)->sin_addr));
|
---|
239 | p = (char *)&((struct sockaddr_in*)sa)->sin_addr;
|
---|
240 | max_prefixlen = 32;
|
---|
241 | break;
|
---|
242 | case AF_INET6:
|
---|
243 | memset(&((struct sockaddr_in6*)sa)->sin6_addr, 0, sizeof(((struct sockaddr_in6*)sa)->sin6_addr));
|
---|
244 | p = (char *)&((struct sockaddr_in6*)sa)->sin6_addr;
|
---|
245 | #if 0 /* XXX: fill scope-id? */
|
---|
246 | if (IN6_IS_ADDR_LINKLOCAL(p) ||
|
---|
247 | IN6_IS_ADDR_MC_LINKLOCAL(p)){
|
---|
248 | ((struct sockaddr_in6*)sa)->sin6_scope_id = scopeid;
|
---|
249 | }
|
---|
250 | #endif
|
---|
251 | max_prefixlen = 128;
|
---|
252 | break;
|
---|
253 | default:
|
---|
254 | return NULL;
|
---|
255 | }
|
---|
256 | sa->sa_family = family;
|
---|
257 | #ifdef HAVE_SOCKADDR_SA_LEN
|
---|
258 | sa->sa_len = ifa_sa_len(family, len);
|
---|
259 | #endif
|
---|
260 | if (p){
|
---|
261 | if (prefixlen > max_prefixlen)
|
---|
262 | prefixlen = max_prefixlen;
|
---|
263 | for (i=0; i<(prefixlen / 8); i++)
|
---|
264 | *p++ = 0xff;
|
---|
265 | c = 0xff;
|
---|
266 | c <<= (8 - (prefixlen % 8));
|
---|
267 | *p = c;
|
---|
268 | }
|
---|
269 | return sa;
|
---|
270 | }
|
---|
271 | #endif
|
---|
272 |
|
---|
273 | /* ====================================================================== */
|
---|
274 | static int
|
---|
275 | nl_sendreq(int sd, int request, int flags, int *seq)
|
---|
276 | {
|
---|
277 | char reqbuf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
|
---|
278 | NLMSG_ALIGN(sizeof(struct rtgenmsg))];
|
---|
279 | struct sockaddr_nl nladdr;
|
---|
280 | struct nlmsghdr *req_hdr;
|
---|
281 | struct rtgenmsg *req_msg;
|
---|
282 | time_t t = time(NULL);
|
---|
283 |
|
---|
284 | if (seq) *seq = t;
|
---|
285 | memset(&reqbuf, 0, sizeof(reqbuf));
|
---|
286 | req_hdr = (struct nlmsghdr *)reqbuf;
|
---|
287 | req_msg = (struct rtgenmsg *)NLMSG_DATA(req_hdr);
|
---|
288 | req_hdr->nlmsg_len = NLMSG_LENGTH(sizeof(*req_msg));
|
---|
289 | req_hdr->nlmsg_type = request;
|
---|
290 | req_hdr->nlmsg_flags = flags | NLM_F_REQUEST;
|
---|
291 | req_hdr->nlmsg_pid = 0;
|
---|
292 | req_hdr->nlmsg_seq = t;
|
---|
293 | req_msg->rtgen_family = AF_UNSPEC;
|
---|
294 | memset(&nladdr, 0, sizeof(nladdr));
|
---|
295 | nladdr.nl_family = AF_NETLINK;
|
---|
296 | return (sendto(sd, (void *)req_hdr, req_hdr->nlmsg_len, 0,
|
---|
297 | (struct sockaddr *)&nladdr, sizeof(nladdr)));
|
---|
298 | }
|
---|
299 |
|
---|
300 | static int
|
---|
301 | nl_recvmsg(int sd, int request, int seq,
|
---|
302 | void *buf, size_t buflen,
|
---|
303 | int *flags)
|
---|
304 | {
|
---|
305 | struct msghdr msg;
|
---|
306 | struct iovec iov = { buf, buflen };
|
---|
307 | struct sockaddr_nl nladdr;
|
---|
308 | int read_len;
|
---|
309 |
|
---|
310 | for (;;){
|
---|
311 | msg.msg_name = (void *)&nladdr;
|
---|
312 | msg.msg_namelen = sizeof(nladdr);
|
---|
313 | msg.msg_iov = &iov;
|
---|
314 | msg.msg_iovlen = 1;
|
---|
315 | msg.msg_control = NULL;
|
---|
316 | msg.msg_controllen = 0;
|
---|
317 | msg.msg_flags = 0;
|
---|
318 | read_len = recvmsg(sd, &msg, 0);
|
---|
319 | if ((read_len < 0 && errno == EINTR) || (msg.msg_flags & MSG_TRUNC))
|
---|
320 | continue;
|
---|
321 | if (flags) *flags = msg.msg_flags;
|
---|
322 | break;
|
---|
323 | }
|
---|
324 | return read_len;
|
---|
325 | }
|
---|
326 |
|
---|
327 | static int
|
---|
328 | nl_getmsg(int sd, int request, int seq,
|
---|
329 | struct nlmsghdr **nlhp,
|
---|
330 | int *done)
|
---|
331 | {
|
---|
332 | struct nlmsghdr *nh;
|
---|
333 | size_t bufsize = 65536, lastbufsize = 0;
|
---|
334 | void *buff = NULL;
|
---|
335 | int result = 0, read_size;
|
---|
336 | int msg_flags;
|
---|
337 | pid_t pid = getpid();
|
---|
338 | for (;;){
|
---|
339 | void *newbuff = realloc(buff, bufsize);
|
---|
340 | if (newbuff == NULL || bufsize < lastbufsize) {
|
---|
341 | result = -1;
|
---|
342 | break;
|
---|
343 | }
|
---|
344 | buff = newbuff;
|
---|
345 | result = read_size = nl_recvmsg(sd, request, seq, buff, bufsize, &msg_flags);
|
---|
346 | if (read_size < 0 || (msg_flags & MSG_TRUNC)){
|
---|
347 | lastbufsize = bufsize;
|
---|
348 | bufsize *= 2;
|
---|
349 | continue;
|
---|
350 | }
|
---|
351 | if (read_size == 0) break;
|
---|
352 | nh = (struct nlmsghdr *)buff;
|
---|
353 | for (nh = (struct nlmsghdr *)buff;
|
---|
354 | NLMSG_OK(nh, read_size);
|
---|
355 | nh = (struct nlmsghdr *)NLMSG_NEXT(nh, read_size)){
|
---|
356 | if (nh->nlmsg_pid != pid ||
|
---|
357 | nh->nlmsg_seq != seq)
|
---|
358 | continue;
|
---|
359 | if (nh->nlmsg_type == NLMSG_DONE){
|
---|
360 | (*done)++;
|
---|
361 | break; /* ok */
|
---|
362 | }
|
---|
363 | if (nh->nlmsg_type == NLMSG_ERROR){
|
---|
364 | struct nlmsgerr *nlerr = (struct nlmsgerr *)NLMSG_DATA(nh);
|
---|
365 | result = -1;
|
---|
366 | if (nh->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr)))
|
---|
367 | __set_errno(EIO);
|
---|
368 | else
|
---|
369 | __set_errno(-nlerr->error);
|
---|
370 | break;
|
---|
371 | }
|
---|
372 | }
|
---|
373 | break;
|
---|
374 | }
|
---|
375 | if (result < 0)
|
---|
376 | if (buff){
|
---|
377 | int saved_errno = errno;
|
---|
378 | free(buff);
|
---|
379 | __set_errno(saved_errno);
|
---|
380 | }
|
---|
381 | *nlhp = (struct nlmsghdr *)buff;
|
---|
382 | return result;
|
---|
383 | }
|
---|
384 |
|
---|
385 | static int
|
---|
386 | nl_getlist(int sd, int seq,
|
---|
387 | int request,
|
---|
388 | struct nlmsg_list **nlm_list,
|
---|
389 | struct nlmsg_list **nlm_end)
|
---|
390 | {
|
---|
391 | struct nlmsghdr *nlh = NULL;
|
---|
392 | int status;
|
---|
393 | int done = 0;
|
---|
394 | int tries = 3;
|
---|
395 |
|
---|
396 | try_again:
|
---|
397 | status = nl_sendreq(sd, request, NLM_F_ROOT|NLM_F_MATCH, &seq);
|
---|
398 | if (status < 0)
|
---|
399 | return status;
|
---|
400 | if (seq == 0)
|
---|
401 | seq = (int)time(NULL);
|
---|
402 | while(!done){
|
---|
403 | struct pollfd pfd;
|
---|
404 |
|
---|
405 | pfd.fd = sd;
|
---|
406 | pfd.events = POLLIN | POLLPRI;
|
---|
407 | pfd.revents = 0;
|
---|
408 | status = poll(&pfd, 1, 1000);
|
---|
409 | if (status < 0)
|
---|
410 | return status;
|
---|
411 | else if (status == 0) {
|
---|
412 | seq++;
|
---|
413 | if (tries-- > 0)
|
---|
414 | goto try_again;
|
---|
415 | return -1;
|
---|
416 | }
|
---|
417 |
|
---|
418 | status = nl_getmsg(sd, request, seq, &nlh, &done);
|
---|
419 | if (status < 0)
|
---|
420 | return status;
|
---|
421 | if (nlh){
|
---|
422 | struct nlmsg_list *nlm_next = (struct nlmsg_list *)malloc(sizeof(struct nlmsg_list));
|
---|
423 | if (nlm_next == NULL){
|
---|
424 | int saved_errno = errno;
|
---|
425 | free(nlh);
|
---|
426 | __set_errno(saved_errno);
|
---|
427 | status = -1;
|
---|
428 | } else {
|
---|
429 | nlm_next->nlm_next = NULL;
|
---|
430 | nlm_next->nlh = (struct nlmsghdr *)nlh;
|
---|
431 | nlm_next->size = status;
|
---|
432 | nlm_next->seq = seq;
|
---|
433 | if (*nlm_list == NULL){
|
---|
434 | *nlm_list = nlm_next;
|
---|
435 | *nlm_end = nlm_next;
|
---|
436 | } else {
|
---|
437 | (*nlm_end)->nlm_next = nlm_next;
|
---|
438 | *nlm_end = nlm_next;
|
---|
439 | }
|
---|
440 | }
|
---|
441 | }
|
---|
442 | }
|
---|
443 | return status >= 0 ? seq : status;
|
---|
444 | }
|
---|
445 |
|
---|
446 | /* ---------------------------------------------------------------------- */
|
---|
447 | static void
|
---|
448 | free_nlmsglist(struct nlmsg_list *nlm0)
|
---|
449 | {
|
---|
450 | struct nlmsg_list *nlm, *nlm_next;
|
---|
451 | int saved_errno;
|
---|
452 | if (!nlm0)
|
---|
453 | return;
|
---|
454 | saved_errno = errno;
|
---|
455 | for (nlm=nlm0; nlm; nlm=nlm_next){
|
---|
456 | if (nlm->nlh)
|
---|
457 | free(nlm->nlh);
|
---|
458 | nlm_next=nlm->nlm_next;
|
---|
459 | free(nlm);
|
---|
460 | }
|
---|
461 | __set_errno(saved_errno);
|
---|
462 | }
|
---|
463 |
|
---|
464 | static void
|
---|
465 | free_data(void *data, void *ifdata)
|
---|
466 | {
|
---|
467 | int saved_errno = errno;
|
---|
468 | if (data != NULL) free(data);
|
---|
469 | if (ifdata != NULL) free(ifdata);
|
---|
470 | __set_errno(saved_errno);
|
---|
471 | }
|
---|
472 |
|
---|
473 | /* ---------------------------------------------------------------------- */
|
---|
474 | static void
|
---|
475 | nl_close(int sd)
|
---|
476 | {
|
---|
477 | int saved_errno = errno;
|
---|
478 | if (sd >= 0) __close(sd);
|
---|
479 | __set_errno(saved_errno);
|
---|
480 | }
|
---|
481 |
|
---|
482 | /* ---------------------------------------------------------------------- */
|
---|
483 | static int
|
---|
484 | nl_open(void)
|
---|
485 | {
|
---|
486 | struct sockaddr_nl nladdr;
|
---|
487 | int sd;
|
---|
488 |
|
---|
489 | sd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
|
---|
490 | if (sd < 0) return -1;
|
---|
491 | memset(&nladdr, 0, sizeof(nladdr));
|
---|
492 | nladdr.nl_family = AF_NETLINK;
|
---|
493 | if (bind(sd, (struct sockaddr*)&nladdr, sizeof(nladdr)) < 0){
|
---|
494 | nl_close(sd);
|
---|
495 | return -1;
|
---|
496 | }
|
---|
497 | return sd;
|
---|
498 | }
|
---|
499 |
|
---|
500 | /* ====================================================================== */
|
---|
501 | ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
|
---|
502 | rk_getifaddrs(struct ifaddrs **ifap)
|
---|
503 | {
|
---|
504 | int sd;
|
---|
505 | struct nlmsg_list *nlmsg_list, *nlmsg_end, *nlm;
|
---|
506 | /* - - - - - - - - - - - - - - - */
|
---|
507 | int icnt;
|
---|
508 | size_t dlen, xlen, nlen;
|
---|
509 | uint32_t max_ifindex = 0;
|
---|
510 |
|
---|
511 | pid_t pid = getpid();
|
---|
512 | int seq;
|
---|
513 | int result;
|
---|
514 | int build ; /* 0 or 1 */
|
---|
515 |
|
---|
516 | /* ---------------------------------- */
|
---|
517 | /* initialize */
|
---|
518 | icnt = dlen = xlen = nlen = 0;
|
---|
519 | nlmsg_list = nlmsg_end = NULL;
|
---|
520 |
|
---|
521 | if (ifap)
|
---|
522 | *ifap = NULL;
|
---|
523 |
|
---|
524 | /* ---------------------------------- */
|
---|
525 | /* open socket and bind */
|
---|
526 | sd = nl_open();
|
---|
527 | if (sd < 0)
|
---|
528 | return -1;
|
---|
529 |
|
---|
530 | /* ---------------------------------- */
|
---|
531 | /* gather info */
|
---|
532 | if ((seq = nl_getlist(sd, 0, RTM_GETLINK,
|
---|
533 | &nlmsg_list, &nlmsg_end)) < 0){
|
---|
534 | free_nlmsglist(nlmsg_list);
|
---|
535 | nl_close(sd);
|
---|
536 | return -1;
|
---|
537 | }
|
---|
538 | if ((seq = nl_getlist(sd, seq+1, RTM_GETADDR,
|
---|
539 | &nlmsg_list, &nlmsg_end)) < 0){
|
---|
540 | free_nlmsglist(nlmsg_list);
|
---|
541 | nl_close(sd);
|
---|
542 | return -1;
|
---|
543 | }
|
---|
544 |
|
---|
545 | /* ---------------------------------- */
|
---|
546 | /* Estimate size of result buffer and fill it */
|
---|
547 | for (build=0; build<=1; build++){
|
---|
548 | struct ifaddrs *ifl = NULL, *ifa = NULL;
|
---|
549 | struct nlmsghdr *nlh, *nlh0;
|
---|
550 | char *data = NULL, *xdata = NULL;
|
---|
551 | void *ifdata = NULL;
|
---|
552 | char *ifname = NULL, **iflist = NULL;
|
---|
553 | uint16_t *ifflist = NULL;
|
---|
554 | struct rtmaddr_ifamap ifamap;
|
---|
555 |
|
---|
556 | if (build){
|
---|
557 | data = calloc(1,
|
---|
558 | NLMSG_ALIGN(sizeof(struct ifaddrs[icnt]))
|
---|
559 | + dlen + xlen + nlen);
|
---|
560 | ifa = (struct ifaddrs *)data;
|
---|
561 | ifdata = calloc(1,
|
---|
562 | NLMSG_ALIGN(sizeof(char *[max_ifindex+1]))
|
---|
563 | + NLMSG_ALIGN(sizeof(uint16_t [max_ifindex+1])));
|
---|
564 | if (ifap != NULL)
|
---|
565 | *ifap = (ifdata != NULL) ? ifa : NULL;
|
---|
566 | else{
|
---|
567 | free_data(data, ifdata);
|
---|
568 | result = 0;
|
---|
569 | break;
|
---|
570 | }
|
---|
571 | if (data == NULL || ifdata == NULL){
|
---|
572 | free_data(data, ifdata);
|
---|
573 | result = -1;
|
---|
574 | break;
|
---|
575 | }
|
---|
576 | ifl = NULL;
|
---|
577 | data += NLMSG_ALIGN(sizeof(struct ifaddrs)) * icnt;
|
---|
578 | xdata = data + dlen;
|
---|
579 | ifname = xdata + xlen;
|
---|
580 | iflist = ifdata;
|
---|
581 | ifflist = (uint16_t *)(((char *)iflist) + NLMSG_ALIGN(sizeof(char *[max_ifindex+1])));
|
---|
582 | }
|
---|
583 |
|
---|
584 | for (nlm=nlmsg_list; nlm; nlm=nlm->nlm_next){
|
---|
585 | int nlmlen = nlm->size;
|
---|
586 | if (!(nlh0 = nlm->nlh))
|
---|
587 | continue;
|
---|
588 | for (nlh = nlh0;
|
---|
589 | NLMSG_OK(nlh, nlmlen);
|
---|
590 | nlh=NLMSG_NEXT(nlh,nlmlen)){
|
---|
591 | struct ifinfomsg *ifim = NULL;
|
---|
592 | struct ifaddrmsg *ifam = NULL;
|
---|
593 | struct rtattr *rta;
|
---|
594 |
|
---|
595 | size_t nlm_struct_size = 0;
|
---|
596 | sa_family_t nlm_family = 0;
|
---|
597 | uint32_t nlm_scope = 0, nlm_index = 0;
|
---|
598 | size_t sockaddr_size = 0;
|
---|
599 | uint32_t nlm_prefixlen = 0;
|
---|
600 | size_t rtasize;
|
---|
601 |
|
---|
602 | memset(&ifamap, 0, sizeof(ifamap));
|
---|
603 |
|
---|
604 | /* check if the message is what we want */
|
---|
605 | if (nlh->nlmsg_pid != pid ||
|
---|
606 | nlh->nlmsg_seq != nlm->seq)
|
---|
607 | continue;
|
---|
608 | if (nlh->nlmsg_type == NLMSG_DONE){
|
---|
609 | break; /* ok */
|
---|
610 | }
|
---|
611 | switch (nlh->nlmsg_type){
|
---|
612 | case RTM_NEWLINK:
|
---|
613 | ifim = (struct ifinfomsg *)NLMSG_DATA(nlh);
|
---|
614 | nlm_struct_size = sizeof(*ifim);
|
---|
615 | nlm_family = ifim->ifi_family;
|
---|
616 | nlm_scope = 0;
|
---|
617 | nlm_index = ifim->ifi_index;
|
---|
618 | nlm_prefixlen = 0;
|
---|
619 | if (build)
|
---|
620 | ifflist[nlm_index] = ifa->ifa_flags = ifim->ifi_flags;
|
---|
621 | break;
|
---|
622 | case RTM_NEWADDR:
|
---|
623 | ifam = (struct ifaddrmsg *)NLMSG_DATA(nlh);
|
---|
624 | nlm_struct_size = sizeof(*ifam);
|
---|
625 | nlm_family = ifam->ifa_family;
|
---|
626 | nlm_scope = ifam->ifa_scope;
|
---|
627 | nlm_index = ifam->ifa_index;
|
---|
628 | nlm_prefixlen = ifam->ifa_prefixlen;
|
---|
629 | if (build)
|
---|
630 | ifa->ifa_flags = ifflist[nlm_index];
|
---|
631 | break;
|
---|
632 | default:
|
---|
633 | continue;
|
---|
634 | }
|
---|
635 |
|
---|
636 | if (!build){
|
---|
637 | if (max_ifindex < nlm_index)
|
---|
638 | max_ifindex = nlm_index;
|
---|
639 | } else {
|
---|
640 | if (ifl != NULL)
|
---|
641 | ifl->ifa_next = ifa;
|
---|
642 | }
|
---|
643 |
|
---|
644 | rtasize = NLMSG_PAYLOAD(nlh, nlmlen) - NLMSG_ALIGN(nlm_struct_size);
|
---|
645 | for (rta = (struct rtattr *)(((char *)NLMSG_DATA(nlh)) + NLMSG_ALIGN(nlm_struct_size));
|
---|
646 | RTA_OK(rta, rtasize);
|
---|
647 | rta = RTA_NEXT(rta, rtasize)){
|
---|
648 | struct sockaddr **sap = NULL;
|
---|
649 | void *rtadata = RTA_DATA(rta);
|
---|
650 | size_t rtapayload = RTA_PAYLOAD(rta);
|
---|
651 | socklen_t sa_len;
|
---|
652 |
|
---|
653 | switch(nlh->nlmsg_type){
|
---|
654 | case RTM_NEWLINK:
|
---|
655 | switch(rta->rta_type){
|
---|
656 | case IFLA_ADDRESS:
|
---|
657 | case IFLA_BROADCAST:
|
---|
658 | if (build){
|
---|
659 | sap = (rta->rta_type == IFLA_ADDRESS) ? &ifa->ifa_addr : &ifa->ifa_broadaddr;
|
---|
660 | *sap = (struct sockaddr *)data;
|
---|
661 | }
|
---|
662 | sa_len = ifa_sa_len(AF_PACKET, rtapayload);
|
---|
663 | if (rta->rta_type == IFLA_ADDRESS)
|
---|
664 | sockaddr_size = NLMSG_ALIGN(sa_len);
|
---|
665 | if (!build){
|
---|
666 | dlen += NLMSG_ALIGN(sa_len);
|
---|
667 | } else {
|
---|
668 | memset(*sap, 0, sa_len);
|
---|
669 | ifa_make_sockaddr(AF_PACKET, *sap, rtadata,rtapayload, 0,0);
|
---|
670 | ((struct sockaddr_ll *)*sap)->sll_ifindex = nlm_index;
|
---|
671 | ((struct sockaddr_ll *)*sap)->sll_hatype = ifim->ifi_type;
|
---|
672 | data += NLMSG_ALIGN(sa_len);
|
---|
673 | }
|
---|
674 | break;
|
---|
675 | case IFLA_IFNAME:/* Name of Interface */
|
---|
676 | if (!build)
|
---|
677 | nlen += NLMSG_ALIGN(rtapayload + 1);
|
---|
678 | else{
|
---|
679 | ifa->ifa_name = ifname;
|
---|
680 | if (iflist[nlm_index] == NULL)
|
---|
681 | iflist[nlm_index] = ifa->ifa_name;
|
---|
682 | strncpy(ifa->ifa_name, rtadata, rtapayload);
|
---|
683 | ifa->ifa_name[rtapayload] = '\0';
|
---|
684 | ifname += NLMSG_ALIGN(rtapayload + 1);
|
---|
685 | }
|
---|
686 | break;
|
---|
687 | case IFLA_STATS:/* Statistics of Interface */
|
---|
688 | if (!build)
|
---|
689 | xlen += NLMSG_ALIGN(rtapayload);
|
---|
690 | else{
|
---|
691 | ifa->ifa_data = xdata;
|
---|
692 | memcpy(ifa->ifa_data, rtadata, rtapayload);
|
---|
693 | xdata += NLMSG_ALIGN(rtapayload);
|
---|
694 | }
|
---|
695 | break;
|
---|
696 | case IFLA_UNSPEC:
|
---|
697 | break;
|
---|
698 | case IFLA_MTU:
|
---|
699 | break;
|
---|
700 | case IFLA_LINK:
|
---|
701 | break;
|
---|
702 | case IFLA_QDISC:
|
---|
703 | break;
|
---|
704 | default:
|
---|
705 | break;
|
---|
706 | }
|
---|
707 | break;
|
---|
708 | case RTM_NEWADDR:
|
---|
709 | if (nlm_family == AF_PACKET) break;
|
---|
710 | switch(rta->rta_type){
|
---|
711 | case IFA_ADDRESS:
|
---|
712 | ifamap.address = rtadata;
|
---|
713 | ifamap.address_len = rtapayload;
|
---|
714 | break;
|
---|
715 | case IFA_LOCAL:
|
---|
716 | ifamap.local = rtadata;
|
---|
717 | ifamap.local_len = rtapayload;
|
---|
718 | break;
|
---|
719 | case IFA_BROADCAST:
|
---|
720 | ifamap.broadcast = rtadata;
|
---|
721 | ifamap.broadcast_len = rtapayload;
|
---|
722 | break;
|
---|
723 | #ifdef HAVE_IFADDRS_IFA_ANYCAST
|
---|
724 | case IFA_ANYCAST:
|
---|
725 | ifamap.anycast = rtadata;
|
---|
726 | ifamap.anycast_len = rtapayload;
|
---|
727 | break;
|
---|
728 | #endif
|
---|
729 | case IFA_LABEL:
|
---|
730 | if (!build)
|
---|
731 | nlen += NLMSG_ALIGN(rtapayload + 1);
|
---|
732 | else{
|
---|
733 | ifa->ifa_name = ifname;
|
---|
734 | if (iflist[nlm_index] == NULL)
|
---|
735 | iflist[nlm_index] = ifname;
|
---|
736 | strncpy(ifa->ifa_name, rtadata, rtapayload);
|
---|
737 | ifa->ifa_name[rtapayload] = '\0';
|
---|
738 | ifname += NLMSG_ALIGN(rtapayload + 1);
|
---|
739 | }
|
---|
740 | break;
|
---|
741 | case IFA_UNSPEC:
|
---|
742 | break;
|
---|
743 | case IFA_CACHEINFO:
|
---|
744 | break;
|
---|
745 | default:
|
---|
746 | break;
|
---|
747 | }
|
---|
748 | }
|
---|
749 | }
|
---|
750 | if (nlh->nlmsg_type == RTM_NEWADDR &&
|
---|
751 | nlm_family != AF_PACKET) {
|
---|
752 | if (!ifamap.local) {
|
---|
753 | ifamap.local = ifamap.address;
|
---|
754 | ifamap.local_len = ifamap.address_len;
|
---|
755 | }
|
---|
756 | if (!ifamap.address) {
|
---|
757 | ifamap.address = ifamap.local;
|
---|
758 | ifamap.address_len = ifamap.local_len;
|
---|
759 | }
|
---|
760 | if (ifamap.address_len != ifamap.local_len ||
|
---|
761 | (ifamap.address != NULL &&
|
---|
762 | memcmp(ifamap.address, ifamap.local, ifamap.address_len))) {
|
---|
763 | /* p2p; address is peer and local is ours */
|
---|
764 | ifamap.broadcast = ifamap.address;
|
---|
765 | ifamap.broadcast_len = ifamap.address_len;
|
---|
766 | ifamap.address = ifamap.local;
|
---|
767 | ifamap.address_len = ifamap.local_len;
|
---|
768 | }
|
---|
769 | if (ifamap.address) {
|
---|
770 | #ifndef IFA_NETMASK
|
---|
771 | sockaddr_size = NLMSG_ALIGN(ifa_sa_len(nlm_family,ifamap.address_len));
|
---|
772 | #endif
|
---|
773 | if (!build)
|
---|
774 | dlen += NLMSG_ALIGN(ifa_sa_len(nlm_family,ifamap.address_len));
|
---|
775 | else {
|
---|
776 | ifa->ifa_addr = (struct sockaddr *)data;
|
---|
777 | ifa_make_sockaddr(nlm_family, ifa->ifa_addr, ifamap.address, ifamap.address_len,
|
---|
778 | nlm_scope, nlm_index);
|
---|
779 | data += NLMSG_ALIGN(ifa_sa_len(nlm_family, ifamap.address_len));
|
---|
780 | }
|
---|
781 | }
|
---|
782 | #ifdef IFA_NETMASK
|
---|
783 | if (ifamap.netmask) {
|
---|
784 | if (!build)
|
---|
785 | dlen += NLMSG_ALIGN(ifa_sa_len(nlm_family,ifamap.netmask_len));
|
---|
786 | else {
|
---|
787 | ifa->ifa_netmask = (struct sockaddr *)data;
|
---|
788 | ifa_make_sockaddr(nlm_family, ifa->ifa_netmask, ifamap.netmask, ifamap.netmask_len,
|
---|
789 | nlm_scope, nlm_index);
|
---|
790 | data += NLMSG_ALIGN(ifa_sa_len(nlm_family, ifamap.netmask_len));
|
---|
791 | }
|
---|
792 | }
|
---|
793 | #endif
|
---|
794 | if (ifamap.broadcast) {
|
---|
795 | if (!build)
|
---|
796 | dlen += NLMSG_ALIGN(ifa_sa_len(nlm_family,ifamap.broadcast_len));
|
---|
797 | else {
|
---|
798 | ifa->ifa_broadaddr = (struct sockaddr *)data;
|
---|
799 | ifa_make_sockaddr(nlm_family, ifa->ifa_broadaddr, ifamap.broadcast, ifamap.broadcast_len,
|
---|
800 | nlm_scope, nlm_index);
|
---|
801 | data += NLMSG_ALIGN(ifa_sa_len(nlm_family, ifamap.broadcast_len));
|
---|
802 | }
|
---|
803 | }
|
---|
804 | #ifdef HAVE_IFADDRS_IFA_ANYCAST
|
---|
805 | if (ifamap.anycast) {
|
---|
806 | if (!build)
|
---|
807 | dlen += NLMSG_ALIGN(ifa_sa_len(nlm_family,ifamap.anycast_len));
|
---|
808 | else {
|
---|
809 | ifa->ifa_anycast = (struct sockaddr *)data;
|
---|
810 | ifa_make_sockaddr(nlm_family, ifa->ifa_anyaddr, ifamap.anycast, ifamap.anycast_len,
|
---|
811 | nlm_scope, nlm_index);
|
---|
812 | data += NLMSG_ALIGN(ifa_sa_len(nlm_family, ifamap.anycast_len));
|
---|
813 | }
|
---|
814 | }
|
---|
815 | #endif
|
---|
816 | }
|
---|
817 | if (!build){
|
---|
818 | #ifndef IFA_NETMASK
|
---|
819 | dlen += sockaddr_size;
|
---|
820 | #endif
|
---|
821 | icnt++;
|
---|
822 | } else {
|
---|
823 | if (ifa->ifa_name == NULL)
|
---|
824 | ifa->ifa_name = iflist[nlm_index];
|
---|
825 | #ifndef IFA_NETMASK
|
---|
826 | if (ifa->ifa_addr &&
|
---|
827 | ifa->ifa_addr->sa_family != AF_UNSPEC &&
|
---|
828 | ifa->ifa_addr->sa_family != AF_PACKET){
|
---|
829 | ifa->ifa_netmask = (struct sockaddr *)data;
|
---|
830 | ifa_make_sockaddr_mask(ifa->ifa_addr->sa_family, ifa->ifa_netmask, nlm_prefixlen);
|
---|
831 | }
|
---|
832 | data += sockaddr_size;
|
---|
833 | #endif
|
---|
834 | ifl = ifa++;
|
---|
835 | }
|
---|
836 | }
|
---|
837 | }
|
---|
838 | if (!build){
|
---|
839 | if (icnt == 0 && (dlen + nlen + xlen == 0)){
|
---|
840 | if (ifap != NULL)
|
---|
841 | *ifap = NULL;
|
---|
842 | break; /* cannot found any addresses */
|
---|
843 | }
|
---|
844 | }
|
---|
845 | else
|
---|
846 | free_data(NULL, ifdata);
|
---|
847 | }
|
---|
848 |
|
---|
849 | /* ---------------------------------- */
|
---|
850 | /* Finalize */
|
---|
851 | free_nlmsglist(nlmsg_list);
|
---|
852 | nl_close(sd);
|
---|
853 | return 0;
|
---|
854 | }
|
---|
855 |
|
---|
856 | void ROKEN_LIB_FUNCTION
|
---|
857 | rk_freeifaddrs(struct ifaddrs *ifp)
|
---|
858 | {
|
---|
859 | /* AF_NETLINK method uses a single allocation for all interfaces */
|
---|
860 | free(ifp);
|
---|
861 | }
|
---|
862 |
|
---|
863 | #else /* !AF_NETLINK */
|
---|
864 |
|
---|
865 | /*
|
---|
866 | * The generic SIOCGIFCONF version.
|
---|
867 | */
|
---|
868 |
|
---|
869 | static int
|
---|
870 | getifaddrs2(struct ifaddrs **ifap,
|
---|
871 | int af, int siocgifconf, int siocgifflags,
|
---|
872 | size_t ifreq_sz)
|
---|
873 | {
|
---|
874 | int ret;
|
---|
875 | int fd;
|
---|
876 | size_t buf_size;
|
---|
877 | char *buf;
|
---|
878 | struct ifconf ifconf;
|
---|
879 | char *p;
|
---|
880 | size_t sz;
|
---|
881 | struct sockaddr sa_zero;
|
---|
882 | struct ifreq *ifr;
|
---|
883 | struct ifaddrs *start = NULL, **end = &start;
|
---|
884 |
|
---|
885 | buf = NULL;
|
---|
886 |
|
---|
887 | memset (&sa_zero, 0, sizeof(sa_zero));
|
---|
888 | fd = socket(af, SOCK_DGRAM, 0);
|
---|
889 | if (fd < 0)
|
---|
890 | return -1;
|
---|
891 |
|
---|
892 | buf_size = 8192;
|
---|
893 | for (;;) {
|
---|
894 | buf = calloc(1, buf_size);
|
---|
895 | if (buf == NULL) {
|
---|
896 | ret = ENOMEM;
|
---|
897 | goto error_out;
|
---|
898 | }
|
---|
899 | ifconf.ifc_len = buf_size;
|
---|
900 | ifconf.ifc_buf = buf;
|
---|
901 |
|
---|
902 | /*
|
---|
903 | * Solaris returns EINVAL when the buffer is too small.
|
---|
904 | */
|
---|
905 | if (ioctl (fd, siocgifconf, &ifconf) < 0 && errno != EINVAL) {
|
---|
906 | ret = errno;
|
---|
907 | goto error_out;
|
---|
908 | }
|
---|
909 | /*
|
---|
910 | * Can the difference between a full and a overfull buf
|
---|
911 | * be determined?
|
---|
912 | */
|
---|
913 |
|
---|
914 | if (ifconf.ifc_len < buf_size)
|
---|
915 | break;
|
---|
916 | free (buf);
|
---|
917 | buf_size *= 2;
|
---|
918 | }
|
---|
919 |
|
---|
920 | for (p = ifconf.ifc_buf;
|
---|
921 | p < ifconf.ifc_buf + ifconf.ifc_len;
|
---|
922 | p += sz) {
|
---|
923 | struct ifreq ifreq;
|
---|
924 | struct sockaddr *sa;
|
---|
925 | size_t salen;
|
---|
926 |
|
---|
927 | ifr = (struct ifreq *)p;
|
---|
928 | sa = &ifr->ifr_addr;
|
---|
929 |
|
---|
930 | sz = ifreq_sz;
|
---|
931 | salen = sizeof(struct sockaddr);
|
---|
932 | #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
|
---|
933 | salen = sa->sa_len;
|
---|
934 | sz = max(sz, sizeof(ifr->ifr_name) + sa->sa_len);
|
---|
935 | #endif
|
---|
936 | #ifdef SA_LEN
|
---|
937 | salen = SA_LEN(sa);
|
---|
938 | sz = max(sz, sizeof(ifr->ifr_name) + SA_LEN(sa));
|
---|
939 | #endif
|
---|
940 | memset (&ifreq, 0, sizeof(ifreq));
|
---|
941 | memcpy (ifreq.ifr_name, ifr->ifr_name, sizeof(ifr->ifr_name));
|
---|
942 |
|
---|
943 | if (ioctl(fd, siocgifflags, &ifreq) < 0) {
|
---|
944 | ret = errno;
|
---|
945 | goto error_out;
|
---|
946 | }
|
---|
947 |
|
---|
948 | *end = malloc(sizeof(**end));
|
---|
949 | if (*end == NULL) {
|
---|
950 | ret = ENOMEM;
|
---|
951 | goto error_out;
|
---|
952 | }
|
---|
953 |
|
---|
954 | (*end)->ifa_next = NULL;
|
---|
955 | (*end)->ifa_name = strdup(ifr->ifr_name);
|
---|
956 | if ((*end)->ifa_name == NULL) {
|
---|
957 | ret = ENOMEM;
|
---|
958 | goto error_out;
|
---|
959 | }
|
---|
960 | (*end)->ifa_flags = ifreq.ifr_flags;
|
---|
961 | (*end)->ifa_addr = malloc(salen);
|
---|
962 | if ((*end)->ifa_addr == NULL) {
|
---|
963 | ret = ENOMEM;
|
---|
964 | goto error_out;
|
---|
965 | }
|
---|
966 | memcpy((*end)->ifa_addr, sa, salen);
|
---|
967 | (*end)->ifa_netmask = NULL;
|
---|
968 |
|
---|
969 | #if 0
|
---|
970 | /* fix these when we actually need them */
|
---|
971 | if(ifreq.ifr_flags & IFF_BROADCAST) {
|
---|
972 | (*end)->ifa_broadaddr = malloc(sizeof(ifr->ifr_broadaddr));
|
---|
973 | if ((*end)->ifa_broadaddr == NULL) {
|
---|
974 | ret = ENOMEM;
|
---|
975 | goto error_out;
|
---|
976 | }
|
---|
977 | memcpy((*end)->ifa_broadaddr, &ifr->ifr_broadaddr,
|
---|
978 | sizeof(ifr->ifr_broadaddr));
|
---|
979 | } else if(ifreq.ifr_flags & IFF_POINTOPOINT) {
|
---|
980 | (*end)->ifa_dstaddr = malloc(sizeof(ifr->ifr_dstaddr));
|
---|
981 | if ((*end)->ifa_dstaddr == NULL) {
|
---|
982 | ret = ENOMEM;
|
---|
983 | goto error_out;
|
---|
984 | }
|
---|
985 | memcpy((*end)->ifa_dstaddr, &ifr->ifr_dstaddr,
|
---|
986 | sizeof(ifr->ifr_dstaddr));
|
---|
987 | } else
|
---|
988 | (*end)->ifa_dstaddr = NULL;
|
---|
989 | #else
|
---|
990 | (*end)->ifa_dstaddr = NULL;
|
---|
991 | #endif
|
---|
992 |
|
---|
993 | (*end)->ifa_data = NULL;
|
---|
994 |
|
---|
995 | end = &(*end)->ifa_next;
|
---|
996 |
|
---|
997 | }
|
---|
998 | *ifap = start;
|
---|
999 | close(fd);
|
---|
1000 | free(buf);
|
---|
1001 | return 0;
|
---|
1002 | error_out:
|
---|
1003 | rk_freeifaddrs(start);
|
---|
1004 | close(fd);
|
---|
1005 | free(buf);
|
---|
1006 | errno = ret;
|
---|
1007 | return -1;
|
---|
1008 | }
|
---|
1009 |
|
---|
1010 | #if defined(HAVE_IPV6) && defined(SIOCGLIFCONF) && defined(SIOCGLIFFLAGS)
|
---|
1011 | static int
|
---|
1012 | getlifaddrs2(struct ifaddrs **ifap,
|
---|
1013 | int af, int siocgifconf, int siocgifflags,
|
---|
1014 | size_t ifreq_sz)
|
---|
1015 | {
|
---|
1016 | int ret;
|
---|
1017 | int fd;
|
---|
1018 | size_t buf_size;
|
---|
1019 | char *buf;
|
---|
1020 | struct lifconf ifconf;
|
---|
1021 | char *p;
|
---|
1022 | size_t sz;
|
---|
1023 | struct sockaddr sa_zero;
|
---|
1024 | struct lifreq *ifr;
|
---|
1025 | struct ifaddrs *start = NULL, **end = &start;
|
---|
1026 |
|
---|
1027 | buf = NULL;
|
---|
1028 |
|
---|
1029 | memset (&sa_zero, 0, sizeof(sa_zero));
|
---|
1030 | fd = socket(af, SOCK_DGRAM, 0);
|
---|
1031 | if (fd < 0)
|
---|
1032 | return -1;
|
---|
1033 |
|
---|
1034 | buf_size = 8192;
|
---|
1035 | for (;;) {
|
---|
1036 | buf = calloc(1, buf_size);
|
---|
1037 | if (buf == NULL) {
|
---|
1038 | ret = ENOMEM;
|
---|
1039 | goto error_out;
|
---|
1040 | }
|
---|
1041 | #ifndef __hpux
|
---|
1042 | ifconf.lifc_family = af;
|
---|
1043 | ifconf.lifc_flags = 0;
|
---|
1044 | #endif
|
---|
1045 | ifconf.lifc_len = buf_size;
|
---|
1046 | ifconf.lifc_buf = buf;
|
---|
1047 |
|
---|
1048 | /*
|
---|
1049 | * Solaris returns EINVAL when the buffer is too small.
|
---|
1050 | */
|
---|
1051 | if (ioctl (fd, siocgifconf, &ifconf) < 0 && errno != EINVAL) {
|
---|
1052 | ret = errno;
|
---|
1053 | goto error_out;
|
---|
1054 | }
|
---|
1055 | /*
|
---|
1056 | * Can the difference between a full and a overfull buf
|
---|
1057 | * be determined?
|
---|
1058 | */
|
---|
1059 |
|
---|
1060 | if (ifconf.lifc_len < buf_size)
|
---|
1061 | break;
|
---|
1062 | free (buf);
|
---|
1063 | buf_size *= 2;
|
---|
1064 | }
|
---|
1065 |
|
---|
1066 | for (p = ifconf.lifc_buf;
|
---|
1067 | p < ifconf.lifc_buf + ifconf.lifc_len;
|
---|
1068 | p += sz) {
|
---|
1069 | struct lifreq ifreq;
|
---|
1070 | struct sockaddr_storage *sa;
|
---|
1071 | size_t salen;
|
---|
1072 |
|
---|
1073 | ifr = (struct lifreq *)p;
|
---|
1074 | sa = &ifr->lifr_addr;
|
---|
1075 |
|
---|
1076 | sz = ifreq_sz;
|
---|
1077 | salen = sizeof(struct sockaddr_storage);
|
---|
1078 | #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
|
---|
1079 | salen = sa->sa_len;
|
---|
1080 | sz = max(sz, sizeof(ifr->ifr_name) + sa->sa_len);
|
---|
1081 | #endif
|
---|
1082 | #ifdef SA_LEN
|
---|
1083 | salen = SA_LEN(sa);
|
---|
1084 | sz = max(sz, sizeof(ifr->ifr_name) + SA_LEN(sa));
|
---|
1085 | #endif
|
---|
1086 | memset (&ifreq, 0, sizeof(ifreq));
|
---|
1087 | memcpy (ifreq.lifr_name, ifr->lifr_name, sizeof(ifr->lifr_name));
|
---|
1088 |
|
---|
1089 | if (ioctl(fd, siocgifflags, &ifreq) < 0) {
|
---|
1090 | ret = errno;
|
---|
1091 | goto error_out;
|
---|
1092 | }
|
---|
1093 |
|
---|
1094 | *end = malloc(sizeof(**end));
|
---|
1095 | if (*end == NULL) {
|
---|
1096 | ret = ENOMEM;
|
---|
1097 | goto error_out;
|
---|
1098 | }
|
---|
1099 |
|
---|
1100 | (*end)->ifa_next = NULL;
|
---|
1101 | (*end)->ifa_name = strdup(ifr->lifr_name);
|
---|
1102 | if ((*end)->ifa_name == NULL) {
|
---|
1103 | ret = ENOMEM;
|
---|
1104 | goto error_out;
|
---|
1105 | }
|
---|
1106 | (*end)->ifa_flags = ifreq.lifr_flags;
|
---|
1107 | (*end)->ifa_addr = malloc(salen);
|
---|
1108 | if ((*end)->ifa_addr == NULL) {
|
---|
1109 | ret = ENOMEM;
|
---|
1110 | goto error_out;
|
---|
1111 | }
|
---|
1112 | memcpy((*end)->ifa_addr, sa, salen);
|
---|
1113 | (*end)->ifa_netmask = NULL;
|
---|
1114 |
|
---|
1115 | #if 0
|
---|
1116 | /* fix these when we actually need them */
|
---|
1117 | if(ifreq.ifr_flags & IFF_BROADCAST) {
|
---|
1118 | (*end)->ifa_broadaddr = malloc(sizeof(ifr->ifr_broadaddr));
|
---|
1119 | if ((*end)->ifa_broadaddr == NULL) {
|
---|
1120 | ret = ENOMEM;
|
---|
1121 | goto error_out;
|
---|
1122 | }
|
---|
1123 | memcpy((*end)->ifa_broadaddr, &ifr->ifr_broadaddr,
|
---|
1124 | sizeof(ifr->ifr_broadaddr));
|
---|
1125 | } else if(ifreq.ifr_flags & IFF_POINTOPOINT) {
|
---|
1126 | (*end)->ifa_dstaddr = malloc(sizeof(ifr->ifr_dstaddr));
|
---|
1127 | if ((*end)->ifa_dstaddr == NULL) {
|
---|
1128 | ret = ENOMEM;
|
---|
1129 | goto error_out;
|
---|
1130 | }
|
---|
1131 | memcpy((*end)->ifa_dstaddr, &ifr->ifr_dstaddr,
|
---|
1132 | sizeof(ifr->ifr_dstaddr));
|
---|
1133 | } else
|
---|
1134 | (*end)->ifa_dstaddr = NULL;
|
---|
1135 | #else
|
---|
1136 | (*end)->ifa_dstaddr = NULL;
|
---|
1137 | #endif
|
---|
1138 |
|
---|
1139 | (*end)->ifa_data = NULL;
|
---|
1140 |
|
---|
1141 | end = &(*end)->ifa_next;
|
---|
1142 |
|
---|
1143 | }
|
---|
1144 | *ifap = start;
|
---|
1145 | close(fd);
|
---|
1146 | free(buf);
|
---|
1147 | return 0;
|
---|
1148 | error_out:
|
---|
1149 | rk_freeifaddrs(start);
|
---|
1150 | close(fd);
|
---|
1151 | free(buf);
|
---|
1152 | errno = ret;
|
---|
1153 | return -1;
|
---|
1154 | }
|
---|
1155 | #endif /* defined(HAVE_IPV6) && defined(SIOCGLIFCONF) && defined(SIOCGLIFFLAGS) */
|
---|
1156 |
|
---|
1157 | /**
|
---|
1158 | * Join two struct ifaddrs lists by appending supp to base.
|
---|
1159 | * Either may be NULL. The new list head (usually base) will be
|
---|
1160 | * returned.
|
---|
1161 | */
|
---|
1162 | static struct ifaddrs *
|
---|
1163 | append_ifaddrs(struct ifaddrs *base, struct ifaddrs *supp) {
|
---|
1164 | if (!base)
|
---|
1165 | return supp;
|
---|
1166 |
|
---|
1167 | if (!supp)
|
---|
1168 | return base;
|
---|
1169 |
|
---|
1170 | while (base->ifa_next)
|
---|
1171 | base = base->ifa_next;
|
---|
1172 |
|
---|
1173 | base->ifa_next = supp;
|
---|
1174 |
|
---|
1175 | return base;
|
---|
1176 | }
|
---|
1177 |
|
---|
1178 | ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
|
---|
1179 | rk_getifaddrs(struct ifaddrs **ifap)
|
---|
1180 | {
|
---|
1181 | int ret = -1;
|
---|
1182 | errno = ENXIO;
|
---|
1183 | #if defined(AF_INET6) && defined(SIOCGIF6CONF) && defined(SIOCGIF6FLAGS)
|
---|
1184 | if (ret)
|
---|
1185 | ret = getifaddrs2 (ifap, AF_INET6, SIOCGIF6CONF, SIOCGIF6FLAGS,
|
---|
1186 | sizeof(struct in6_ifreq));
|
---|
1187 | #endif
|
---|
1188 | #if defined(HAVE_IPV6) && defined(SIOCGLIFCONF) && defined(SIOCGLIFFLAGS)
|
---|
1189 | /* Do IPv6 and IPv4 queries separately then join the result.
|
---|
1190 | *
|
---|
1191 | * HP-UX only returns IPv6 addresses using SIOCGLIFCONF,
|
---|
1192 | * SIOCGIFCONF has to be used for IPv4 addresses. The result is then
|
---|
1193 | * merged.
|
---|
1194 | *
|
---|
1195 | * Solaris needs particular care, because a SIOCGLIFCONF lookup using
|
---|
1196 | * AF_UNSPEC can fail in a Zone requiring an AF_INET lookup, so we just
|
---|
1197 | * do them separately the same as for HP-UX. See
|
---|
1198 | * http://repo.or.cz/w/heimdal.git/commitdiff/76afc31e9ba2f37e64c70adc006ade9e37e9ef73
|
---|
1199 | */
|
---|
1200 | if (ret) {
|
---|
1201 | int v6err, v4err;
|
---|
1202 | struct ifaddrs *v6addrs, *v4addrs;
|
---|
1203 |
|
---|
1204 | v6err = getlifaddrs2 (&v6addrs, AF_INET6, SIOCGLIFCONF, SIOCGLIFFLAGS,
|
---|
1205 | sizeof(struct lifreq));
|
---|
1206 | v4err = getifaddrs2 (&v4addrs, AF_INET, SIOCGIFCONF, SIOCGIFFLAGS,
|
---|
1207 | sizeof(struct ifreq));
|
---|
1208 | if (v6err)
|
---|
1209 | v6addrs = NULL;
|
---|
1210 | if (v4err)
|
---|
1211 | v4addrs = NULL;
|
---|
1212 |
|
---|
1213 | if (v6addrs) {
|
---|
1214 | if (v4addrs)
|
---|
1215 | *ifap = append_ifaddrs(v6addrs, v4addrs);
|
---|
1216 | else
|
---|
1217 | *ifap = v6addrs;
|
---|
1218 | } else if (v4addrs) {
|
---|
1219 | *ifap = v4addrs;
|
---|
1220 | } else {
|
---|
1221 | *ifap = NULL;
|
---|
1222 | }
|
---|
1223 |
|
---|
1224 | ret = (v6err || v4err) ? -1 : 0;
|
---|
1225 | }
|
---|
1226 | #endif
|
---|
1227 | #if defined(HAVE_IPV6) && defined(SIOCGIFCONF)
|
---|
1228 | if (ret)
|
---|
1229 | ret = getifaddrs2 (ifap, AF_INET6, SIOCGIFCONF, SIOCGIFFLAGS,
|
---|
1230 | sizeof(struct ifreq));
|
---|
1231 | #endif
|
---|
1232 | #if defined(AF_INET) && defined(SIOCGIFCONF) && defined(SIOCGIFFLAGS)
|
---|
1233 | if (ret)
|
---|
1234 | ret = getifaddrs2 (ifap, AF_INET, SIOCGIFCONF, SIOCGIFFLAGS,
|
---|
1235 | sizeof(struct ifreq));
|
---|
1236 | #endif
|
---|
1237 | return ret;
|
---|
1238 | }
|
---|
1239 |
|
---|
1240 | ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
|
---|
1241 | rk_freeifaddrs(struct ifaddrs *ifp)
|
---|
1242 | {
|
---|
1243 | struct ifaddrs *p, *q;
|
---|
1244 |
|
---|
1245 | for(p = ifp; p; ) {
|
---|
1246 | free(p->ifa_name);
|
---|
1247 | if(p->ifa_addr)
|
---|
1248 | free(p->ifa_addr);
|
---|
1249 | if(p->ifa_dstaddr)
|
---|
1250 | free(p->ifa_dstaddr);
|
---|
1251 | if(p->ifa_netmask)
|
---|
1252 | free(p->ifa_netmask);
|
---|
1253 | if(p->ifa_data)
|
---|
1254 | free(p->ifa_data);
|
---|
1255 | q = p;
|
---|
1256 | p = p->ifa_next;
|
---|
1257 | free(q);
|
---|
1258 | }
|
---|
1259 | }
|
---|
1260 |
|
---|
1261 | #endif /* !AF_NETLINK */
|
---|
1262 |
|
---|
1263 | #ifdef TEST
|
---|
1264 |
|
---|
1265 | void
|
---|
1266 | print_addr(const char *s, struct sockaddr *sa)
|
---|
1267 | {
|
---|
1268 | int i;
|
---|
1269 | printf(" %s=%d/", s, sa->sa_family);
|
---|
1270 | #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
|
---|
1271 | for(i = 0; i < sa->sa_len - ((long)sa->sa_data - (long)&sa->sa_family); i++)
|
---|
1272 | printf("%02x", ((unsigned char*)sa->sa_data)[i]);
|
---|
1273 | #else
|
---|
1274 | for(i = 0; i < sizeof(sa->sa_data); i++)
|
---|
1275 | printf("%02x", ((unsigned char*)sa->sa_data)[i]);
|
---|
1276 | #endif
|
---|
1277 | printf("\n");
|
---|
1278 | }
|
---|
1279 |
|
---|
1280 | void
|
---|
1281 | print_ifaddrs(struct ifaddrs *x)
|
---|
1282 | {
|
---|
1283 | struct ifaddrs *p;
|
---|
1284 |
|
---|
1285 | for(p = x; p; p = p->ifa_next) {
|
---|
1286 | printf("%s\n", p->ifa_name);
|
---|
1287 | printf(" flags=%x\n", p->ifa_flags);
|
---|
1288 | if(p->ifa_addr)
|
---|
1289 | print_addr("addr", p->ifa_addr);
|
---|
1290 | if(p->ifa_dstaddr)
|
---|
1291 | print_addr("dstaddr", p->ifa_dstaddr);
|
---|
1292 | if(p->ifa_netmask)
|
---|
1293 | print_addr("netmask", p->ifa_netmask);
|
---|
1294 | printf(" %p\n", p->ifa_data);
|
---|
1295 | }
|
---|
1296 | }
|
---|
1297 |
|
---|
1298 | int
|
---|
1299 | main()
|
---|
1300 | {
|
---|
1301 | struct ifaddrs *a = NULL, *b;
|
---|
1302 | getifaddrs2(&a, AF_INET, SIOCGIFCONF, SIOCGIFFLAGS, sizeof(struct ifreq));
|
---|
1303 | print_ifaddrs(a);
|
---|
1304 | printf("---\n");
|
---|
1305 | getifaddrs(&b);
|
---|
1306 | print_ifaddrs(b);
|
---|
1307 | return 0;
|
---|
1308 | }
|
---|
1309 | #endif
|
---|