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