1 /* $USAGI: ni_ifaddrs.c,v 1.8 2007-10-11 06:25:21 yoshfuji Exp $ */
2 /*
3 * Copyright (C) 2002 USAGI/WIDE Project.
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 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the project nor the names of its contributors
15 * may be used to endorse or promote products derived from this software
16 * without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31 /* reformatted by indent -kr -i8 -l 1000 */
32 /* USAGI: ifaddrs.c,v 1.18 2002/03/06 01:50:46 yoshfuji Exp */
33
34 /**************************************************************************
35 * ifaddrs.c
36 * Copyright (C)2000 Hideaki YOSHIFUJI, All Rights Reserved.
37 *
38 * Redistribution and use in source and binary forms, with or without
39 * modification, are permitted provided that the following conditions
40 * are met:
41 * 1. Redistributions of source code must retain the above copyright
42 * notice, this list of conditions and the following disclaimer.
43 * 2. Redistributions in binary form must reproduce the above copyright
44 * notice, this list of conditions and the following disclaimer in the
45 * documentation and/or other materials provided with the distribution.
46 * 3. Neither the name of the author nor the names of its contributors
47 * may be used to endorse or promote products derived from this software
48 * without specific prior written permission.
49 *
50 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
51 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
52 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
53 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
54 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
55 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
56 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
57 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
58 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
59 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
60 * SUCH DAMAGE.
61 */
62
63 #include "config.h"
64
65 #include <string.h>
66 #include <time.h>
67 #include <malloc.h>
68 #include <errno.h>
69 #include <unistd.h>
70
71 #include <sys/socket.h>
72 #include <asm/types.h>
73 #include <linux/netlink.h>
74 #include <linux/rtnetlink.h>
75 #include <sys/types.h>
76 #include <sys/socket.h>
77 #include <netpacket/packet.h>
78 #include <net/ethernet.h> /* the L2 protocols */
79 #include <sys/uio.h>
80 #include <net/if.h>
81 #include <net/if_arp.h>
82 #include "ni_ifaddrs.h"
83 #include <netinet/in.h>
84
85 #ifdef _USAGI_LIBINET6
86 #include "libc-compat.h"
87 #endif
88
89 //#define IFA_LOCAL IFA_LOCAL
90
91 static const char *RCSID __attribute__ ((unused)) = "$USAGI: ni_ifaddrs.c,v 1.8 2007-10-11 06:25:21 yoshfuji Exp $ based on USAGI: ifaddrs.c,v 1.18 2002/03/06 01:50:46 yoshfuji Exp";
92
93 /* ====================================================================== */
94 struct nlmsg_list {
95 struct nlmsg_list *nlm_next;
96 struct nlmsghdr *nlh;
97 int size;
98 time_t seq;
99 };
100
101 #ifndef IFA_LOCAL
102 struct rtmaddr_ifamap {
103 void *address;
104 void *local;
105 void *broadcast;
106 int address_len;
107 int local_len;
108 int broadcast_len;
109 };
110 #endif
111
112 /* ====================================================================== */
nl_sendreq(int sd,int request,int flags,int * seq)113 static int nl_sendreq(int sd, int request, int flags, int *seq)
114 {
115 char reqbuf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + NLMSG_ALIGN(sizeof(struct rtgenmsg))];
116 struct sockaddr_nl nladdr;
117 struct nlmsghdr *req_hdr;
118 struct rtgenmsg *req_msg;
119 time_t t = time(NULL);
120
121 if (seq)
122 *seq = t;
123 memset(&reqbuf, 0, sizeof(reqbuf));
124 req_hdr = (struct nlmsghdr *) reqbuf;
125 req_msg = (struct rtgenmsg *) NLMSG_DATA(req_hdr);
126 req_hdr->nlmsg_len = NLMSG_LENGTH(sizeof(*req_msg));
127 req_hdr->nlmsg_type = request;
128 req_hdr->nlmsg_flags = flags | NLM_F_REQUEST;
129 req_hdr->nlmsg_pid = 0;
130 req_hdr->nlmsg_seq = t;
131 req_msg->rtgen_family = AF_UNSPEC;
132 memset(&nladdr, 0, sizeof(nladdr));
133 nladdr.nl_family = AF_NETLINK;
134 return (sendto(sd, (void *) req_hdr, req_hdr->nlmsg_len, 0, (struct sockaddr *) &nladdr, sizeof(nladdr)));
135 }
136
nl_recvmsg(int sd,int request,int seq,void * buf,size_t buflen,int * flags)137 static int nl_recvmsg(int sd, int request, int seq, void *buf, size_t buflen, int *flags)
138 {
139 struct msghdr msg;
140 struct iovec iov = { buf, buflen };
141 struct sockaddr_nl nladdr;
142 int read_len;
143
144 for (;;) {
145 msg.msg_name = (void *) &nladdr;
146 msg.msg_namelen = sizeof(nladdr);
147 msg.msg_iov = &iov;
148 msg.msg_iovlen = 1;
149 msg.msg_control = NULL;
150 msg.msg_controllen = 0;
151 msg.msg_flags = 0;
152 read_len = recvmsg(sd, &msg, 0);
153 if ((read_len < 0 && errno == EINTR)
154 || (msg.msg_flags & MSG_TRUNC))
155 continue;
156 if (flags)
157 *flags = msg.msg_flags;
158 break;
159 }
160 return read_len;
161 }
162
nl_getmsg(int sd,int request,int seq,struct nlmsghdr ** nlhp,int * done)163 static int nl_getmsg(int sd, int request, int seq, struct nlmsghdr **nlhp, int *done)
164 {
165 struct nlmsghdr *nh;
166 size_t bufsize = 65536, lastbufsize = 0;
167 void *buff = NULL;
168 int result = 0, read_size;
169 int msg_flags;
170 pid_t pid = getpid();
171 for (;;) {
172 void *newbuff = realloc(buff, bufsize);
173 if (newbuff == NULL || bufsize < lastbufsize) {
174 free(newbuff);
175 result = -1;
176 break;
177 }
178 buff = newbuff;
179 result = read_size = nl_recvmsg(sd, request, seq, buff, bufsize, &msg_flags);
180 if (read_size < 0 || (msg_flags & MSG_TRUNC)) {
181 lastbufsize = bufsize;
182 bufsize *= 2;
183 continue;
184 }
185 if (read_size == 0)
186 break;
187 nh = (struct nlmsghdr *) buff;
188 for (nh = (struct nlmsghdr *) buff; NLMSG_OK(nh, read_size); nh = (struct nlmsghdr *) NLMSG_NEXT(nh, read_size)) {
189 if (nh->nlmsg_pid != pid || nh->nlmsg_seq != seq)
190 continue;
191 if (nh->nlmsg_type == NLMSG_DONE) {
192 (*done)++;
193 break; /* ok */
194 }
195 if (nh->nlmsg_type == NLMSG_ERROR) {
196 struct nlmsgerr *nlerr = (struct nlmsgerr *) NLMSG_DATA(nh);
197 result = -1;
198 if (nh->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr)))
199 errno = EIO;
200 else
201 errno = -nlerr->error;
202 break;
203 }
204 }
205 break;
206 }
207 if (result < 0)
208 if (buff) {
209 int saved_errno = errno;
210 free(buff);
211 buff = NULL;
212 errno = saved_errno;
213 }
214 *nlhp = (struct nlmsghdr *) buff;
215 return result;
216 }
217
nl_getlist(int sd,int seq,int request,struct nlmsg_list ** nlm_list,struct nlmsg_list ** nlm_end)218 static int nl_getlist(int sd, int seq, int request, struct nlmsg_list **nlm_list, struct nlmsg_list **nlm_end)
219 {
220 struct nlmsghdr *nlh = NULL;
221 int status;
222 int done = 0;
223
224 status = nl_sendreq(sd, request, NLM_F_ROOT | NLM_F_MATCH, &seq);
225 if (status < 0)
226 return status;
227 if (seq == 0)
228 seq = (int) time(NULL);
229 while (!done) {
230 status = nl_getmsg(sd, request, seq, &nlh, &done);
231 if (status < 0)
232 return status;
233 if (nlh) {
234 struct nlmsg_list *nlm_next = (struct nlmsg_list *) malloc(sizeof(struct nlmsg_list));
235 if (nlm_next == NULL) {
236 int saved_errno = errno;
237 free(nlh);
238 errno = saved_errno;
239 status = -1;
240 } else {
241 nlm_next->nlm_next = NULL;
242 nlm_next->nlh = (struct nlmsghdr *) nlh;
243 nlm_next->size = status;
244 nlm_next->seq = seq;
245 if (*nlm_list == NULL) {
246 *nlm_list = nlm_next;
247 *nlm_end = nlm_next;
248 } else {
249 (*nlm_end)->nlm_next = nlm_next;
250 *nlm_end = nlm_next;
251 }
252 }
253 }
254 }
255 return status >= 0 ? seq : status;
256 }
257
258 /* ---------------------------------------------------------------------- */
free_nlmsglist(struct nlmsg_list * nlm0)259 static void free_nlmsglist(struct nlmsg_list *nlm0)
260 {
261 struct nlmsg_list *nlm, *nlm_next;
262 int saved_errno;
263 if (!nlm0)
264 return;
265 saved_errno = errno;
266 nlm = nlm0;
267 while(nlm) {
268 if(nlm->nlh)
269 free(nlm->nlh);
270 nlm_next = nlm->nlm_next;
271 free(nlm);
272 nlm = nlm_next;
273 }
274 errno = saved_errno;
275 }
276
free_data(void * data)277 static void free_data(void *data)
278 {
279 int saved_errno = errno;
280 if (data != NULL)
281 free(data);
282 errno = saved_errno;
283 }
284
285 /* ---------------------------------------------------------------------- */
nl_close(int sd)286 static void nl_close(int sd)
287 {
288 int saved_errno = errno;
289 if (sd >= 0)
290 close(sd);
291 errno = saved_errno;
292 }
293
294 /* ---------------------------------------------------------------------- */
nl_open(void)295 static int nl_open(void)
296 {
297 struct sockaddr_nl nladdr;
298 int sd;
299
300 sd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
301 if (sd < 0)
302 return -1;
303 memset(&nladdr, 0, sizeof(nladdr));
304 nladdr.nl_family = AF_NETLINK;
305 if (bind(sd, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {
306 nl_close(sd);
307 return -1;
308 }
309 return sd;
310 }
311
312 /* ====================================================================== */
ni_ifaddrs(struct ni_ifaddrs ** ifap,sa_family_t family)313 int ni_ifaddrs(struct ni_ifaddrs **ifap, sa_family_t family)
314 {
315 int sd;
316 struct nlmsg_list *nlmsg_list, *nlmsg_end, *nlm;
317 /* - - - - - - - - - - - - - - - */
318 int icnt;
319 size_t dlen, xlen;
320 uint32_t max_ifindex = 0;
321
322 pid_t pid = getpid();
323 int seq = 0;
324 int result;
325 int build; /* 0 or 1 */
326
327 /* ---------------------------------- */
328 /* initialize */
329 icnt = dlen = xlen = 0;
330 nlmsg_list = nlmsg_end = NULL;
331
332 if (ifap)
333 *ifap = NULL;
334
335 /* ---------------------------------- */
336 /* open socket and bind */
337 sd = nl_open();
338 if (sd < 0)
339 return -1;
340
341 /* ---------------------------------- */
342 /* gather info */
343 if ((seq = nl_getlist(sd, seq + 1, RTM_GETADDR, &nlmsg_list, &nlmsg_end)) < 0) {
344 free_nlmsglist(nlmsg_list);
345 nl_close(sd);
346 return -1;
347 }
348
349 /* ---------------------------------- */
350 /* Estimate size of result buffer and fill it */
351 for (build = 0; build <= 1; build++) {
352 struct ni_ifaddrs *ifl = NULL, *ifa = NULL;
353 struct nlmsghdr *nlh, *nlh0;
354 void *data = NULL, *xdata = NULL;
355 uint16_t *ifflist = NULL;
356 #ifndef IFA_LOCAL
357 struct rtmaddr_ifamap ifamap;
358 #endif
359
360 if (build) {
361 ifa = data = calloc(1, NLMSG_ALIGN(sizeof(struct ni_ifaddrs[icnt]))
362 + dlen + xlen);
363 if (ifap != NULL)
364 *ifap = ifa;
365 else {
366 free_data(data);
367 result = 0;
368 break;
369 }
370 if (data == NULL) {
371 free_data(data);
372 result = -1;
373 break;
374 }
375 ifl = NULL;
376 data += NLMSG_ALIGN(sizeof(struct ni_ifaddrs)) * icnt;
377 xdata = data + dlen;
378 ifflist = xdata + xlen;
379 }
380
381 for (nlm = nlmsg_list; nlm; nlm = nlm->nlm_next) {
382 int nlmlen = nlm->size;
383 if (!(nlh0 = nlm->nlh))
384 continue;
385 for (nlh = nlh0; NLMSG_OK(nlh, nlmlen); nlh = NLMSG_NEXT(nlh, nlmlen)) {
386 struct ifaddrmsg *ifam = NULL;
387 struct rtattr *rta;
388
389 size_t nlm_struct_size = 0;
390 sa_family_t nlm_family = 0;
391 uint32_t nlm_scope = 0, nlm_index = 0;
392 unsigned int nlm_flags;
393 size_t rtasize;
394
395 #ifndef IFA_LOCAL
396 memset(&ifamap, 0, sizeof(ifamap));
397 #endif
398
399 /* check if the message is what we want */
400 if (nlh->nlmsg_pid != pid || nlh->nlmsg_seq != nlm->seq)
401 continue;
402 if (nlh->nlmsg_type == NLMSG_DONE) {
403 break; /* ok */
404 }
405 switch (nlh->nlmsg_type) {
406 case RTM_NEWADDR:
407 ifam = (struct ifaddrmsg *) NLMSG_DATA(nlh);
408 nlm_struct_size = sizeof(*ifam);
409 nlm_family = ifam->ifa_family;
410 nlm_scope = ifam->ifa_scope;
411 nlm_index = ifam->ifa_index;
412 nlm_flags = ifam->ifa_flags;
413 if (family && nlm_family != family)
414 continue;
415 if (build) {
416 ifa->ifa_ifindex = nlm_index;
417 ifa->ifa_flags = nlm_flags;
418 }
419 break;
420 default:
421 continue;
422 }
423
424 if (!build) {
425 if (max_ifindex < nlm_index)
426 max_ifindex = nlm_index;
427 } else {
428 if (ifl != NULL)
429 ifl->ifa_next = ifa;
430 }
431
432 rtasize = NLMSG_PAYLOAD(nlh, nlmlen) - NLMSG_ALIGN(nlm_struct_size);
433 for (rta = (struct rtattr *) (((char *) NLMSG_DATA(nlh)) +
434 NLMSG_ALIGN(nlm_struct_size));
435 RTA_OK(rta, rtasize);
436 rta = RTA_NEXT(rta, rtasize)) {
437 void *rtadata = RTA_DATA(rta);
438 size_t rtapayload = RTA_PAYLOAD(rta);
439
440 switch (nlh->nlmsg_type) {
441 case RTM_NEWADDR:
442 if (nlm_family == AF_PACKET)
443 break;
444 switch (rta->rta_type) {
445 #ifndef IFA_LOCAL
446 case IFA_ADDRESS:
447 ifamap.address = rtadata;
448 ifamap.address_len = rtapayload;
449 break;
450 case IFA_LOCAL:
451 ifamap.local = rtadata;
452 ifamap.local_len = rtapayload;
453 break;
454 case IFA_BROADCAST:
455 ifamap.broadcast = rtadata;
456 ifamap.broadcast_len = rtapayload;
457 break;
458 case IFA_LABEL:
459 break;
460 case IFA_UNSPEC:
461 break;
462 #else
463 case IFA_LOCAL:
464 if (!build)
465 dlen += NLMSG_ALIGN(rtapayload);
466 else {
467 memcpy(data, rtadata, rtapayload);
468 ifa->ifa_addr = data;
469 data += NLMSG_ALIGN(rtapayload);
470 }
471 break;
472 #endif
473 case IFA_CACHEINFO:
474 if (!build)
475 xlen += NLMSG_ALIGN(rtapayload);
476 else {
477 memcpy(xdata, rtadata, rtapayload);
478 ifa->ifa_cacheinfo = xdata;
479 xdata += NLMSG_ALIGN(rtapayload);
480 }
481 break;
482 }
483 }
484 }
485 #ifndef IFA_LOCAL
486 if (nlh->nlmsg_type == RTM_NEWADDR && nlm_family != AF_PACKET) {
487 if (!ifamap.local) {
488 ifamap.local = ifamap.address;
489 ifamap.local_len = ifamap.address_len;
490 }
491 if (!ifamap.address) {
492 ifamap.address = ifamap.local;
493 ifamap.address_len = ifamap.local_len;
494 }
495 if (ifamap.address_len != ifamap.local_len ||
496 (ifamap.address != NULL &&
497 memcmp(ifamap.address, ifamap.local, ifamap.address_len))) {
498 /* p2p; address is peer and local is ours */
499 ifamap.broadcast = ifamap.address;
500 ifamap.broadcast_len = ifamap.address_len;
501 ifamap.address = ifamap.local;
502 ifamap.address_len = ifamap.local_len;
503 }
504 if (ifamap.address) {
505 if (!build)
506 dlen += NLMSG_ALIGN(ifamap.address_len);
507 else {
508 ifa->ifa_addr = (struct sockaddr *) data;
509 memcpy(ifa->ifa_addr, ifamap.address, ifamap.address_len);
510 data += NLMSG_ALIGN(ifamap.address_len);
511 }
512 }
513 }
514 #endif
515 if (!build) {
516 icnt++;
517 } else {
518 ifl = ifa++;
519 }
520 }
521 }
522 if (!build) {
523 if (icnt == 0 && (dlen + xlen == 0)) {
524 if (ifap != NULL)
525 *ifap = NULL;
526 break; /* cannot found any addresses */
527 }
528 }
529 }
530
531 /* ---------------------------------- */
532 /* Finalize */
533 free_nlmsglist(nlmsg_list);
534 nl_close(sd);
535 return 0;
536 }
537
538 /* ---------------------------------------------------------------------- */
ni_freeifaddrs(struct ni_ifaddrs * ifa)539 void ni_freeifaddrs(struct ni_ifaddrs *ifa)
540 {
541 free(ifa);
542 }
543
544