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