remove gcc34
[dragonfly.git] / crypto / heimdal-0.6.3 / 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.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