getifaddrs.c revision 103423
172445Sassar/*
2103423Snectar * Copyright (c) 2000 - 2002 Kungliga Tekniska H�gskolan
372445Sassar * (Royal Institute of Technology, Stockholm, Sweden).
472445Sassar * All rights reserved.
572445Sassar *
672445Sassar * Redistribution and use in source and binary forms, with or without
772445Sassar * modification, are permitted provided that the following conditions
872445Sassar * are met:
972445Sassar *
1072445Sassar * 1. Redistributions of source code must retain the above copyright
1172445Sassar *    notice, this list of conditions and the following disclaimer.
1272445Sassar *
1372445Sassar * 2. Redistributions in binary form must reproduce the above copyright
1472445Sassar *    notice, this list of conditions and the following disclaimer in the
1572445Sassar *    documentation and/or other materials provided with the distribution.
1672445Sassar *
1772445Sassar * 3. Neither the name of the Institute nor the names of its contributors
1872445Sassar *    may be used to endorse or promote products derived from this software
1972445Sassar *    without specific prior written permission.
2072445Sassar *
2172445Sassar * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
2272445Sassar * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2372445Sassar * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2472445Sassar * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
2572445Sassar * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2672445Sassar * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2772445Sassar * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2872445Sassar * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2972445Sassar * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3072445Sassar * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3172445Sassar * SUCH DAMAGE.
3272445Sassar */
3372445Sassar
3472445Sassar#ifdef HAVE_CONFIG_H
3572445Sassar#include <config.h>
36103423SnectarRCSID("$Id: getifaddrs.c,v 1.9 2002/09/05 03:36:23 assar Exp $");
3772445Sassar#endif
3872445Sassar#include "roken.h"
3972445Sassar
4072445Sassar#ifdef __osf__
4172445Sassar/* hate */
4272445Sassarstruct rtentry;
4372445Sassarstruct mbuf;
4472445Sassar#endif
4572445Sassar#ifdef HAVE_NET_IF_H
4672445Sassar#include <net/if.h>
4772445Sassar#endif
4872445Sassar
4972445Sassar#ifdef HAVE_SYS_SOCKIO_H
5072445Sassar#include <sys/sockio.h>
5172445Sassar#endif /* HAVE_SYS_SOCKIO_H */
5272445Sassar
5372445Sassar#ifdef HAVE_NETINET_IN6_VAR_H
5472445Sassar#include <netinet/in6_var.h>
5572445Sassar#endif /* HAVE_NETINET_IN6_VAR_H */
5672445Sassar
5772445Sassar#include <ifaddrs.h>
5872445Sassar
59103423Snectar#ifdef AF_NETLINK
60103423Snectar
61103423Snectar/*
62103423Snectar * The linux - AF_NETLINK version of getifaddrs - from Usagi.
63103423Snectar * Linux does not return v6 addresses from SIOCGIFCONF.
64103423Snectar */
65103423Snectar
66103423Snectar/* $USAGI: ifaddrs.c,v 1.18 2002/03/06 01:50:46 yoshfuji Exp $ */
67103423Snectar
68103423Snectar/**************************************************************************
69103423Snectar * ifaddrs.c
70103423Snectar * Copyright (C)2000 Hideaki YOSHIFUJI, All Rights Reserved.
71103423Snectar *
72103423Snectar * Redistribution and use in source and binary forms, with or without
73103423Snectar * modification, are permitted provided that the following conditions
74103423Snectar * are met:
75103423Snectar * 1. Redistributions of source code must retain the above copyright
76103423Snectar *    notice, this list of conditions and the following disclaimer.
77103423Snectar * 2. Redistributions in binary form must reproduce the above copyright
78103423Snectar *    notice, this list of conditions and the following disclaimer in the
79103423Snectar *    documentation and/or other materials provided with the distribution.
80103423Snectar * 3. Neither the name of the author nor the names of its contributors
81103423Snectar *    may be used to endorse or promote products derived from this software
82103423Snectar *    without specific prior written permission.
83103423Snectar *
84103423Snectar * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
85103423Snectar * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
86103423Snectar * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
87103423Snectar * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
88103423Snectar * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
89103423Snectar * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
90103423Snectar * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
91103423Snectar * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
92103423Snectar * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
93103423Snectar * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
94103423Snectar * SUCH DAMAGE.
95103423Snectar */
96103423Snectar
97103423Snectar#include "config.h"
98103423Snectar
99103423Snectar#include <string.h>
100103423Snectar#include <time.h>
101103423Snectar#include <malloc.h>
102103423Snectar#include <errno.h>
103103423Snectar#include <unistd.h>
104103423Snectar
105103423Snectar#include <sys/socket.h>
106103423Snectar#include <asm/types.h>
107103423Snectar#include <linux/netlink.h>
108103423Snectar#include <linux/rtnetlink.h>
109103423Snectar#include <sys/types.h>
110103423Snectar#include <sys/socket.h>
111103423Snectar#include <netpacket/packet.h>
112103423Snectar#include <net/ethernet.h>     /* the L2 protocols */
113103423Snectar#include <sys/uio.h>
114103423Snectar#include <net/if.h>
115103423Snectar#include <net/if_arp.h>
116103423Snectar#include <ifaddrs.h>
117103423Snectar#include <netinet/in.h>
118103423Snectar
119103423Snectar#define __set_errno(e) (errno = (e))
120103423Snectar#define __close(fd) (close(fd))
121103423Snectar#undef ifa_broadaddr
122103423Snectar#define ifa_broadaddr ifa_dstaddr
123103423Snectar#define IFA_NETMASK
124103423Snectar
125103423Snectar/* ====================================================================== */
126103423Snectarstruct nlmsg_list{
127103423Snectar    struct nlmsg_list *nlm_next;
128103423Snectar    struct nlmsghdr *nlh;
129103423Snectar    int size;
130103423Snectar    time_t seq;
131103423Snectar};
132103423Snectar
133103423Snectarstruct rtmaddr_ifamap {
134103423Snectar  void *address;
135103423Snectar  void *local;
136103423Snectar#ifdef IFA_NETMASK
137103423Snectar  void *netmask;
138103423Snectar#endif
139103423Snectar  void *broadcast;
140103423Snectar#ifdef HAVE_IFADDRS_IFA_ANYCAST
141103423Snectar  void *anycast;
142103423Snectar#endif
143103423Snectar  int address_len;
144103423Snectar  int local_len;
145103423Snectar#ifdef IFA_NETMASK
146103423Snectar  int netmask_len;
147103423Snectar#endif
148103423Snectar  int broadcast_len;
149103423Snectar#ifdef HAVE_IFADDRS_IFA_ANYCAST
150103423Snectar  int anycast_len;
151103423Snectar#endif
152103423Snectar};
153103423Snectar
154103423Snectar/* ====================================================================== */
155103423Snectarstatic size_t
156103423Snectarifa_sa_len(sa_family_t family, int len)
157103423Snectar{
158103423Snectar  size_t size;
159103423Snectar  switch(family){
160103423Snectar  case AF_INET:
161103423Snectar    size = sizeof(struct sockaddr_in);
162103423Snectar    break;
163103423Snectar  case AF_INET6:
164103423Snectar    size = sizeof(struct sockaddr_in6);
165103423Snectar    break;
166103423Snectar  case AF_PACKET:
167103423Snectar    size = (size_t)(((struct sockaddr_ll *)NULL)->sll_addr) + len;
168103423Snectar    if (size < sizeof(struct sockaddr_ll))
169103423Snectar      size = sizeof(struct sockaddr_ll);
170103423Snectar    break;
171103423Snectar  default:
172103423Snectar    size = (size_t)(((struct sockaddr *)NULL)->sa_data) + len;
173103423Snectar    if (size < sizeof(struct sockaddr))
174103423Snectar      size = sizeof(struct sockaddr);
175103423Snectar  }
176103423Snectar  return size;
177103423Snectar}
178103423Snectar
179103423Snectarstatic void
180103423Snectarifa_make_sockaddr(sa_family_t family,
181103423Snectar		  struct sockaddr *sa,
182103423Snectar		  void *p, size_t len,
183103423Snectar		  uint32_t scope, uint32_t scopeid)
184103423Snectar{
185103423Snectar  if (sa == NULL) return;
186103423Snectar  switch(family){
187103423Snectar  case AF_INET:
188103423Snectar    memcpy(&((struct sockaddr_in*)sa)->sin_addr, (char *)p, len);
189103423Snectar    break;
190103423Snectar  case AF_INET6:
191103423Snectar    memcpy(&((struct sockaddr_in6*)sa)->sin6_addr, (char *)p, len);
192103423Snectar    if (IN6_IS_ADDR_LINKLOCAL(p) ||
193103423Snectar	IN6_IS_ADDR_MC_LINKLOCAL(p)){
194103423Snectar      ((struct sockaddr_in6*)sa)->sin6_scope_id = scopeid;
195103423Snectar    }
196103423Snectar    break;
197103423Snectar  case AF_PACKET:
198103423Snectar    memcpy(((struct sockaddr_ll*)sa)->sll_addr, (char *)p, len);
199103423Snectar    ((struct sockaddr_ll*)sa)->sll_halen = len;
200103423Snectar    break;
201103423Snectar  default:
202103423Snectar    memcpy(sa->sa_data, p, len);	/*XXX*/
203103423Snectar    break;
204103423Snectar  }
205103423Snectar  sa->sa_family = family;
206103423Snectar#ifdef HAVE_SOCKADDR_SA_LEN
207103423Snectar  sa->sa_len = ifa_sa_len(family, len);
208103423Snectar#endif
209103423Snectar}
210103423Snectar
211103423Snectar#ifndef IFA_NETMASK
212103423Snectarstatic struct sockaddr *
213103423Snectarifa_make_sockaddr_mask(sa_family_t family,
214103423Snectar		       struct sockaddr *sa,
215103423Snectar		       uint32_t prefixlen)
216103423Snectar{
217103423Snectar  int i;
218103423Snectar  char *p = NULL, c;
219103423Snectar  uint32_t max_prefixlen = 0;
220103423Snectar
221103423Snectar  if (sa == NULL) return NULL;
222103423Snectar  switch(family){
223103423Snectar  case AF_INET:
224103423Snectar    memset(&((struct sockaddr_in*)sa)->sin_addr, 0, sizeof(((struct sockaddr_in*)sa)->sin_addr));
225103423Snectar    p = (char *)&((struct sockaddr_in*)sa)->sin_addr;
226103423Snectar    max_prefixlen = 32;
227103423Snectar    break;
228103423Snectar  case AF_INET6:
229103423Snectar    memset(&((struct sockaddr_in6*)sa)->sin6_addr, 0, sizeof(((struct sockaddr_in6*)sa)->sin6_addr));
230103423Snectar    p = (char *)&((struct sockaddr_in6*)sa)->sin6_addr;
231103423Snectar#if 0	/* XXX: fill scope-id? */
232103423Snectar    if (IN6_IS_ADDR_LINKLOCAL(p) ||
233103423Snectar	IN6_IS_ADDR_MC_LINKLOCAL(p)){
234103423Snectar      ((struct sockaddr_in6*)sa)->sin6_scope_id = scopeid;
235103423Snectar    }
236103423Snectar#endif
237103423Snectar    max_prefixlen = 128;
238103423Snectar    break;
239103423Snectar  default:
240103423Snectar    return NULL;
241103423Snectar  }
242103423Snectar  sa->sa_family = family;
243103423Snectar#ifdef HAVE_SOCKADDR_SA_LEN
244103423Snectar  sa->sa_len = ifa_sa_len(family, len);
245103423Snectar#endif
246103423Snectar  if (p){
247103423Snectar    if (prefixlen > max_prefixlen)
248103423Snectar      prefixlen = max_prefixlen;
249103423Snectar    for (i=0; i<(prefixlen / 8); i++)
250103423Snectar      *p++ = 0xff;
251103423Snectar    c = 0xff;
252103423Snectar    c <<= (8 - (prefixlen % 8));
253103423Snectar    *p = c;
254103423Snectar  }
255103423Snectar  return sa;
256103423Snectar}
257103423Snectar#endif
258103423Snectar
259103423Snectar/* ====================================================================== */
260103423Snectarstatic int
261103423Snectarnl_sendreq(int sd, int request, int flags, int *seq)
262103423Snectar{
263103423Snectar  char reqbuf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
264103423Snectar	      NLMSG_ALIGN(sizeof(struct rtgenmsg))];
265103423Snectar  struct sockaddr_nl nladdr;
266103423Snectar  struct nlmsghdr *req_hdr;
267103423Snectar  struct rtgenmsg *req_msg;
268103423Snectar  time_t t = time(NULL);
269103423Snectar
270103423Snectar  if (seq) *seq = t;
271103423Snectar  memset(&reqbuf, 0, sizeof(reqbuf));
272103423Snectar  req_hdr = (struct nlmsghdr *)reqbuf;
273103423Snectar  req_msg = (struct rtgenmsg *)NLMSG_DATA(req_hdr);
274103423Snectar  req_hdr->nlmsg_len = NLMSG_LENGTH(sizeof(*req_msg));
275103423Snectar  req_hdr->nlmsg_type = request;
276103423Snectar  req_hdr->nlmsg_flags = flags | NLM_F_REQUEST;
277103423Snectar  req_hdr->nlmsg_pid = 0;
278103423Snectar  req_hdr->nlmsg_seq = t;
279103423Snectar  req_msg->rtgen_family = AF_UNSPEC;
280103423Snectar  memset(&nladdr, 0, sizeof(nladdr));
281103423Snectar  nladdr.nl_family = AF_NETLINK;
282103423Snectar  return (sendto(sd, (void *)req_hdr, req_hdr->nlmsg_len, 0,
283103423Snectar		 (struct sockaddr *)&nladdr, sizeof(nladdr)));
284103423Snectar}
285103423Snectar
286103423Snectarstatic int
287103423Snectarnl_recvmsg(int sd, int request, int seq,
288103423Snectar	   void *buf, size_t buflen,
289103423Snectar	   int *flags)
290103423Snectar{
291103423Snectar  struct msghdr msg;
292103423Snectar  struct iovec iov = { buf, buflen };
293103423Snectar  struct sockaddr_nl nladdr;
294103423Snectar  int read_len;
295103423Snectar
296103423Snectar  for (;;){
297103423Snectar    msg.msg_name = (void *)&nladdr;
298103423Snectar    msg.msg_namelen = sizeof(nladdr);
299103423Snectar    msg.msg_iov = &iov;
300103423Snectar    msg.msg_iovlen = 1;
301103423Snectar    msg.msg_control = NULL;
302103423Snectar    msg.msg_controllen = 0;
303103423Snectar    msg.msg_flags = 0;
304103423Snectar    read_len = recvmsg(sd, &msg, 0);
305103423Snectar    if ((read_len < 0 && errno == EINTR) || (msg.msg_flags & MSG_TRUNC))
306103423Snectar      continue;
307103423Snectar    if (flags) *flags = msg.msg_flags;
308103423Snectar    break;
309103423Snectar  }
310103423Snectar  return read_len;
311103423Snectar}
312103423Snectar
313103423Snectarstatic int
314103423Snectarnl_getmsg(int sd, int request, int seq,
315103423Snectar	  struct nlmsghdr **nlhp,
316103423Snectar	  int *done)
317103423Snectar{
318103423Snectar  struct nlmsghdr *nh;
319103423Snectar  size_t bufsize = 65536, lastbufsize = 0;
320103423Snectar  void *buff = NULL;
321103423Snectar  int result = 0, read_size;
322103423Snectar  int msg_flags;
323103423Snectar  pid_t pid = getpid();
324103423Snectar  for (;;){
325103423Snectar    void *newbuff = realloc(buff, bufsize);
326103423Snectar    if (newbuff == NULL || bufsize < lastbufsize) {
327103423Snectar      result = -1;
328103423Snectar      break;
329103423Snectar    }
330103423Snectar    buff = newbuff;
331103423Snectar    result = read_size = nl_recvmsg(sd, request, seq, buff, bufsize, &msg_flags);
332103423Snectar    if (read_size < 0 || (msg_flags & MSG_TRUNC)){
333103423Snectar      lastbufsize = bufsize;
334103423Snectar      bufsize *= 2;
335103423Snectar      continue;
336103423Snectar    }
337103423Snectar    if (read_size == 0) break;
338103423Snectar    nh = (struct nlmsghdr *)buff;
339103423Snectar    for (nh = (struct nlmsghdr *)buff;
340103423Snectar	 NLMSG_OK(nh, read_size);
341103423Snectar	 nh = (struct nlmsghdr *)NLMSG_NEXT(nh, read_size)){
342103423Snectar      if (nh->nlmsg_pid != pid ||
343103423Snectar	  nh->nlmsg_seq != seq)
344103423Snectar	continue;
345103423Snectar      if (nh->nlmsg_type == NLMSG_DONE){
346103423Snectar	(*done)++;
347103423Snectar	break; /* ok */
348103423Snectar      }
349103423Snectar      if (nh->nlmsg_type == NLMSG_ERROR){
350103423Snectar	struct nlmsgerr *nlerr = (struct nlmsgerr *)NLMSG_DATA(nh);
351103423Snectar	result = -1;
352103423Snectar	if (nh->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr)))
353103423Snectar	  __set_errno(EIO);
354103423Snectar	else
355103423Snectar	  __set_errno(-nlerr->error);
356103423Snectar	break;
357103423Snectar      }
358103423Snectar    }
359103423Snectar    break;
360103423Snectar  }
361103423Snectar  if (result < 0)
362103423Snectar    if (buff){
363103423Snectar      int saved_errno = errno;
364103423Snectar      free(buff);
365103423Snectar      __set_errno(saved_errno);
366103423Snectar    }
367103423Snectar  *nlhp = (struct nlmsghdr *)buff;
368103423Snectar  return result;
369103423Snectar}
370103423Snectar
37172445Sassarstatic int
372103423Snectarnl_getlist(int sd, int seq,
373103423Snectar	   int request,
374103423Snectar	   struct nlmsg_list **nlm_list,
375103423Snectar	   struct nlmsg_list **nlm_end)
376103423Snectar{
377103423Snectar  struct nlmsghdr *nlh = NULL;
378103423Snectar  int status;
379103423Snectar  int done = 0;
380103423Snectar
381103423Snectar  status = nl_sendreq(sd, request, NLM_F_ROOT|NLM_F_MATCH, &seq);
382103423Snectar  if (status < 0)
383103423Snectar    return status;
384103423Snectar  if (seq == 0)
385103423Snectar    seq = (int)time(NULL);
386103423Snectar  while(!done){
387103423Snectar    status = nl_getmsg(sd, request, seq, &nlh, &done);
388103423Snectar    if (status < 0)
389103423Snectar      return status;
390103423Snectar    if (nlh){
391103423Snectar      struct nlmsg_list *nlm_next = (struct nlmsg_list *)malloc(sizeof(struct nlmsg_list));
392103423Snectar      if (nlm_next == NULL){
393103423Snectar	int saved_errno = errno;
394103423Snectar	free(nlh);
395103423Snectar	__set_errno(saved_errno);
396103423Snectar	status = -1;
397103423Snectar      } else {
398103423Snectar	nlm_next->nlm_next = NULL;
399103423Snectar	nlm_next->nlh = (struct nlmsghdr *)nlh;
400103423Snectar	nlm_next->size = status;
401103423Snectar	nlm_next->seq = seq;
402103423Snectar	if (*nlm_list == NULL){
403103423Snectar	  *nlm_list = nlm_next;
404103423Snectar	  *nlm_end = nlm_next;
405103423Snectar	} else {
406103423Snectar	  (*nlm_end)->nlm_next = nlm_next;
407103423Snectar	  *nlm_end = nlm_next;
408103423Snectar	}
409103423Snectar      }
410103423Snectar    }
411103423Snectar  }
412103423Snectar  return status >= 0 ? seq : status;
413103423Snectar}
414103423Snectar
415103423Snectar/* ---------------------------------------------------------------------- */
416103423Snectarstatic void
417103423Snectarfree_nlmsglist(struct nlmsg_list *nlm0)
418103423Snectar{
419103423Snectar  struct nlmsg_list *nlm;
420103423Snectar  int saved_errno;
421103423Snectar  if (!nlm0)
422103423Snectar    return;
423103423Snectar  saved_errno = errno;
424103423Snectar  for (nlm=nlm0; nlm; nlm=nlm->nlm_next){
425103423Snectar    if (nlm->nlh)
426103423Snectar      free(nlm->nlh);
427103423Snectar  }
428103423Snectar  free(nlm0);
429103423Snectar  __set_errno(saved_errno);
430103423Snectar}
431103423Snectar
432103423Snectarstatic void
433103423Snectarfree_data(void *data, void *ifdata)
434103423Snectar{
435103423Snectar  int saved_errno = errno;
436103423Snectar  if (data != NULL) free(data);
437103423Snectar  if (ifdata != NULL) free(ifdata);
438103423Snectar  __set_errno(saved_errno);
439103423Snectar}
440103423Snectar
441103423Snectar/* ---------------------------------------------------------------------- */
442103423Snectarstatic void
443103423Snectarnl_close(int sd)
444103423Snectar{
445103423Snectar  int saved_errno = errno;
446103423Snectar  if (sd >= 0) __close(sd);
447103423Snectar  __set_errno(saved_errno);
448103423Snectar}
449103423Snectar
450103423Snectar/* ---------------------------------------------------------------------- */
451103423Snectarstatic int
452103423Snectarnl_open(void)
453103423Snectar{
454103423Snectar  struct sockaddr_nl nladdr;
455103423Snectar  int sd;
456103423Snectar
457103423Snectar  sd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
458103423Snectar  if (sd < 0) return -1;
459103423Snectar  memset(&nladdr, 0, sizeof(nladdr));
460103423Snectar  nladdr.nl_family = AF_NETLINK;
461103423Snectar  if (bind(sd, (struct sockaddr*)&nladdr, sizeof(nladdr)) < 0){
462103423Snectar    nl_close(sd);
463103423Snectar    return -1;
464103423Snectar  }
465103423Snectar  return sd;
466103423Snectar}
467103423Snectar
468103423Snectar/* ====================================================================== */
469103423Snectarint getifaddrs(struct ifaddrs **ifap)
470103423Snectar{
471103423Snectar  int sd;
472103423Snectar  struct nlmsg_list *nlmsg_list, *nlmsg_end, *nlm;
473103423Snectar  /* - - - - - - - - - - - - - - - */
474103423Snectar  int icnt;
475103423Snectar  size_t dlen, xlen, nlen;
476103423Snectar  uint32_t max_ifindex = 0;
477103423Snectar
478103423Snectar  pid_t pid = getpid();
479103423Snectar  int seq;
480103423Snectar  int result;
481103423Snectar  int build     ; /* 0 or 1 */
482103423Snectar
483103423Snectar/* ---------------------------------- */
484103423Snectar  /* initialize */
485103423Snectar  icnt = dlen = xlen = nlen = 0;
486103423Snectar  nlmsg_list = nlmsg_end = NULL;
487103423Snectar
488103423Snectar  if (ifap)
489103423Snectar    *ifap = NULL;
490103423Snectar
491103423Snectar/* ---------------------------------- */
492103423Snectar  /* open socket and bind */
493103423Snectar  sd = nl_open();
494103423Snectar  if (sd < 0)
495103423Snectar    return -1;
496103423Snectar
497103423Snectar/* ---------------------------------- */
498103423Snectar   /* gather info */
499103423Snectar  if ((seq = nl_getlist(sd, 0, RTM_GETLINK,
500103423Snectar			&nlmsg_list, &nlmsg_end)) < 0){
501103423Snectar    free_nlmsglist(nlmsg_list);
502103423Snectar    nl_close(sd);
503103423Snectar    return -1;
504103423Snectar  }
505103423Snectar  if ((seq = nl_getlist(sd, seq+1, RTM_GETADDR,
506103423Snectar			&nlmsg_list, &nlmsg_end)) < 0){
507103423Snectar    free_nlmsglist(nlmsg_list);
508103423Snectar    nl_close(sd);
509103423Snectar    return -1;
510103423Snectar  }
511103423Snectar
512103423Snectar/* ---------------------------------- */
513103423Snectar  /* Estimate size of result buffer and fill it */
514103423Snectar  for (build=0; build<=1; build++){
515103423Snectar    struct ifaddrs *ifl = NULL, *ifa = NULL;
516103423Snectar    struct nlmsghdr *nlh, *nlh0;
517103423Snectar    char *data = NULL, *xdata = NULL;
518103423Snectar    void *ifdata = NULL;
519103423Snectar    char *ifname = NULL, **iflist = NULL;
520103423Snectar    uint16_t *ifflist = NULL;
521103423Snectar    struct rtmaddr_ifamap ifamap;
522103423Snectar
523103423Snectar    if (build){
524103423Snectar      data = calloc(1,
525103423Snectar		    NLMSG_ALIGN(sizeof(struct ifaddrs[icnt]))
526103423Snectar		    + dlen + xlen + nlen);
527103423Snectar      ifa = (struct ifaddrs *)data;
528103423Snectar      ifdata = calloc(1,
529103423Snectar		      NLMSG_ALIGN(sizeof(char *[max_ifindex+1]))
530103423Snectar		      + NLMSG_ALIGN(sizeof(uint16_t [max_ifindex+1])));
531103423Snectar      if (ifap != NULL)
532103423Snectar	*ifap = (ifdata != NULL) ? ifa : NULL;
533103423Snectar      else{
534103423Snectar	free_data(data, ifdata);
535103423Snectar	result = 0;
536103423Snectar	break;
537103423Snectar      }
538103423Snectar      if (data == NULL || ifdata == NULL){
539103423Snectar	free_data(data, ifdata);
540103423Snectar	result = -1;
541103423Snectar	break;
542103423Snectar      }
543103423Snectar      ifl = NULL;
544103423Snectar      data += NLMSG_ALIGN(sizeof(struct ifaddrs)) * icnt;
545103423Snectar      xdata = data + dlen;
546103423Snectar      ifname = xdata + xlen;
547103423Snectar      iflist = ifdata;
548103423Snectar      ifflist = (uint16_t *)(((char *)iflist) + NLMSG_ALIGN(sizeof(char *[max_ifindex+1])));
549103423Snectar    }
550103423Snectar
551103423Snectar    for (nlm=nlmsg_list; nlm; nlm=nlm->nlm_next){
552103423Snectar      int nlmlen = nlm->size;
553103423Snectar      if (!(nlh0 = nlm->nlh))
554103423Snectar	continue;
555103423Snectar      for (nlh = nlh0;
556103423Snectar	   NLMSG_OK(nlh, nlmlen);
557103423Snectar	   nlh=NLMSG_NEXT(nlh,nlmlen)){
558103423Snectar	struct ifinfomsg *ifim = NULL;
559103423Snectar	struct ifaddrmsg *ifam = NULL;
560103423Snectar	struct rtattr *rta;
561103423Snectar
562103423Snectar	size_t nlm_struct_size = 0;
563103423Snectar	sa_family_t nlm_family = 0;
564103423Snectar	uint32_t nlm_scope = 0, nlm_index = 0;
565103423Snectar	size_t sockaddr_size = 0;
566103423Snectar	uint32_t nlm_prefixlen = 0;
567103423Snectar	size_t rtasize;
568103423Snectar
569103423Snectar	memset(&ifamap, 0, sizeof(ifamap));
570103423Snectar
571103423Snectar	/* check if the message is what we want */
572103423Snectar	if (nlh->nlmsg_pid != pid ||
573103423Snectar	    nlh->nlmsg_seq != nlm->seq)
574103423Snectar	  continue;
575103423Snectar	if (nlh->nlmsg_type == NLMSG_DONE){
576103423Snectar	  break; /* ok */
577103423Snectar	}
578103423Snectar	switch (nlh->nlmsg_type){
579103423Snectar	case RTM_NEWLINK:
580103423Snectar	  ifim = (struct ifinfomsg *)NLMSG_DATA(nlh);
581103423Snectar	  nlm_struct_size = sizeof(*ifim);
582103423Snectar	  nlm_family = ifim->ifi_family;
583103423Snectar	  nlm_scope = 0;
584103423Snectar	  nlm_index = ifim->ifi_index;
585103423Snectar	  nlm_prefixlen = 0;
586103423Snectar	  if (build)
587103423Snectar	    ifflist[nlm_index] = ifa->ifa_flags = ifim->ifi_flags;
588103423Snectar	  break;
589103423Snectar	case RTM_NEWADDR:
590103423Snectar	  ifam = (struct ifaddrmsg *)NLMSG_DATA(nlh);
591103423Snectar	  nlm_struct_size = sizeof(*ifam);
592103423Snectar	  nlm_family = ifam->ifa_family;
593103423Snectar	  nlm_scope = ifam->ifa_scope;
594103423Snectar	  nlm_index = ifam->ifa_index;
595103423Snectar	  nlm_prefixlen = ifam->ifa_prefixlen;
596103423Snectar	  if (build)
597103423Snectar	    ifa->ifa_flags = ifflist[nlm_index];
598103423Snectar	  break;
599103423Snectar	default:
600103423Snectar	  continue;
601103423Snectar	}
602103423Snectar
603103423Snectar	if (!build){
604103423Snectar	  if (max_ifindex < nlm_index)
605103423Snectar	    max_ifindex = nlm_index;
606103423Snectar	} else {
607103423Snectar	  if (ifl != NULL)
608103423Snectar	    ifl->ifa_next = ifa;
609103423Snectar	}
610103423Snectar
611103423Snectar	rtasize = NLMSG_PAYLOAD(nlh, nlmlen) - NLMSG_ALIGN(nlm_struct_size);
612103423Snectar	for (rta = (struct rtattr *)(((char *)NLMSG_DATA(nlh)) + NLMSG_ALIGN(nlm_struct_size));
613103423Snectar	     RTA_OK(rta, rtasize);
614103423Snectar	     rta = RTA_NEXT(rta, rtasize)){
615103423Snectar	  struct sockaddr **sap = NULL;
616103423Snectar	  void *rtadata = RTA_DATA(rta);
617103423Snectar	  size_t rtapayload = RTA_PAYLOAD(rta);
618103423Snectar	  socklen_t sa_len;
619103423Snectar
620103423Snectar	  switch(nlh->nlmsg_type){
621103423Snectar	  case RTM_NEWLINK:
622103423Snectar	    switch(rta->rta_type){
623103423Snectar	    case IFLA_ADDRESS:
624103423Snectar	    case IFLA_BROADCAST:
625103423Snectar	      if (build){
626103423Snectar		sap = (rta->rta_type == IFLA_ADDRESS) ? &ifa->ifa_addr : &ifa->ifa_broadaddr;
627103423Snectar		*sap = (struct sockaddr *)data;
628103423Snectar	      }
629103423Snectar	      sa_len = ifa_sa_len(AF_PACKET, rtapayload);
630103423Snectar	      if (rta->rta_type == IFLA_ADDRESS)
631103423Snectar		sockaddr_size = NLMSG_ALIGN(sa_len);
632103423Snectar	      if (!build){
633103423Snectar		dlen += NLMSG_ALIGN(sa_len);
634103423Snectar	      } else {
635103423Snectar		memset(*sap, 0, sa_len);
636103423Snectar		ifa_make_sockaddr(AF_PACKET, *sap, rtadata,rtapayload, 0,0);
637103423Snectar		((struct sockaddr_ll *)*sap)->sll_ifindex = nlm_index;
638103423Snectar		((struct sockaddr_ll *)*sap)->sll_hatype = ifim->ifi_type;
639103423Snectar		data += NLMSG_ALIGN(sa_len);
640103423Snectar	      }
641103423Snectar	      break;
642103423Snectar	    case IFLA_IFNAME:/* Name of Interface */
643103423Snectar	      if (!build)
644103423Snectar		nlen += NLMSG_ALIGN(rtapayload + 1);
645103423Snectar	      else{
646103423Snectar		ifa->ifa_name = ifname;
647103423Snectar		if (iflist[nlm_index] == NULL)
648103423Snectar		  iflist[nlm_index] = ifa->ifa_name;
649103423Snectar		strncpy(ifa->ifa_name, rtadata, rtapayload);
650103423Snectar		ifa->ifa_name[rtapayload] = '\0';
651103423Snectar		ifname += NLMSG_ALIGN(rtapayload + 1);
652103423Snectar	      }
653103423Snectar	      break;
654103423Snectar	    case IFLA_STATS:/* Statistics of Interface */
655103423Snectar	      if (!build)
656103423Snectar		xlen += NLMSG_ALIGN(rtapayload);
657103423Snectar	      else{
658103423Snectar		ifa->ifa_data = xdata;
659103423Snectar		memcpy(ifa->ifa_data, rtadata, rtapayload);
660103423Snectar		xdata += NLMSG_ALIGN(rtapayload);
661103423Snectar	      }
662103423Snectar	      break;
663103423Snectar	    case IFLA_UNSPEC:
664103423Snectar	      break;
665103423Snectar	    case IFLA_MTU:
666103423Snectar	      break;
667103423Snectar	    case IFLA_LINK:
668103423Snectar	      break;
669103423Snectar	    case IFLA_QDISC:
670103423Snectar	      break;
671103423Snectar	    default:
672103423Snectar	    }
673103423Snectar	    break;
674103423Snectar	  case RTM_NEWADDR:
675103423Snectar	    if (nlm_family == AF_PACKET) break;
676103423Snectar	    switch(rta->rta_type){
677103423Snectar	    case IFA_ADDRESS:
678103423Snectar		ifamap.address = rtadata;
679103423Snectar		ifamap.address_len = rtapayload;
680103423Snectar		break;
681103423Snectar	    case IFA_LOCAL:
682103423Snectar		ifamap.local = rtadata;
683103423Snectar		ifamap.local_len = rtapayload;
684103423Snectar		break;
685103423Snectar	    case IFA_BROADCAST:
686103423Snectar		ifamap.broadcast = rtadata;
687103423Snectar		ifamap.broadcast_len = rtapayload;
688103423Snectar		break;
689103423Snectar#ifdef HAVE_IFADDRS_IFA_ANYCAST
690103423Snectar	    case IFA_ANYCAST:
691103423Snectar		ifamap.anycast = rtadata;
692103423Snectar		ifamap.anycast_len = rtapayload;
693103423Snectar		break;
694103423Snectar#endif
695103423Snectar	    case IFA_LABEL:
696103423Snectar	      if (!build)
697103423Snectar		nlen += NLMSG_ALIGN(rtapayload + 1);
698103423Snectar	      else{
699103423Snectar		ifa->ifa_name = ifname;
700103423Snectar		if (iflist[nlm_index] == NULL)
701103423Snectar		  iflist[nlm_index] = ifname;
702103423Snectar		strncpy(ifa->ifa_name, rtadata, rtapayload);
703103423Snectar		ifa->ifa_name[rtapayload] = '\0';
704103423Snectar		ifname += NLMSG_ALIGN(rtapayload + 1);
705103423Snectar	      }
706103423Snectar	      break;
707103423Snectar	    case IFA_UNSPEC:
708103423Snectar	      break;
709103423Snectar	    case IFA_CACHEINFO:
710103423Snectar	      break;
711103423Snectar	    default:
712103423Snectar	    }
713103423Snectar	  }
714103423Snectar	}
715103423Snectar	if (nlh->nlmsg_type == RTM_NEWADDR &&
716103423Snectar	    nlm_family != AF_PACKET) {
717103423Snectar	  if (!ifamap.local) {
718103423Snectar	    ifamap.local = ifamap.address;
719103423Snectar	    ifamap.local_len = ifamap.address_len;
720103423Snectar	  }
721103423Snectar	  if (!ifamap.address) {
722103423Snectar	    ifamap.address = ifamap.local;
723103423Snectar	    ifamap.address_len = ifamap.local_len;
724103423Snectar	  }
725103423Snectar	  if (ifamap.address_len != ifamap.local_len ||
726103423Snectar	      (ifamap.address != NULL &&
727103423Snectar	       memcmp(ifamap.address, ifamap.local, ifamap.address_len))) {
728103423Snectar	    /* p2p; address is peer and local is ours */
729103423Snectar	    ifamap.broadcast = ifamap.address;
730103423Snectar	    ifamap.broadcast_len = ifamap.address_len;
731103423Snectar	    ifamap.address = ifamap.local;
732103423Snectar	    ifamap.address_len = ifamap.local_len;
733103423Snectar	  }
734103423Snectar	  if (ifamap.address) {
735103423Snectar#ifndef IFA_NETMASK
736103423Snectar	    sockaddr_size = NLMSG_ALIGN(ifa_sa_len(nlm_family,ifamap.address_len));
737103423Snectar#endif
738103423Snectar	    if (!build)
739103423Snectar	      dlen += NLMSG_ALIGN(ifa_sa_len(nlm_family,ifamap.address_len));
740103423Snectar	    else {
741103423Snectar	      ifa->ifa_addr = (struct sockaddr *)data;
742103423Snectar	      ifa_make_sockaddr(nlm_family, ifa->ifa_addr, ifamap.address, ifamap.address_len,
743103423Snectar				nlm_scope, nlm_index);
744103423Snectar	      data += NLMSG_ALIGN(ifa_sa_len(nlm_family, ifamap.address_len));
745103423Snectar	    }
746103423Snectar	  }
747103423Snectar#ifdef IFA_NETMASK
748103423Snectar	  if (ifamap.netmask) {
749103423Snectar	    if (!build)
750103423Snectar	      dlen += NLMSG_ALIGN(ifa_sa_len(nlm_family,ifamap.netmask_len));
751103423Snectar	    else {
752103423Snectar	      ifa->ifa_netmask = (struct sockaddr *)data;
753103423Snectar	      ifa_make_sockaddr(nlm_family, ifa->ifa_netmask, ifamap.netmask, ifamap.netmask_len,
754103423Snectar				nlm_scope, nlm_index);
755103423Snectar	      data += NLMSG_ALIGN(ifa_sa_len(nlm_family, ifamap.netmask_len));
756103423Snectar	    }
757103423Snectar	  }
758103423Snectar#endif
759103423Snectar	  if (ifamap.broadcast) {
760103423Snectar	    if (!build)
761103423Snectar	      dlen += NLMSG_ALIGN(ifa_sa_len(nlm_family,ifamap.broadcast_len));
762103423Snectar	    else {
763103423Snectar	      ifa->ifa_broadaddr = (struct sockaddr *)data;
764103423Snectar	      ifa_make_sockaddr(nlm_family, ifa->ifa_broadaddr, ifamap.broadcast, ifamap.broadcast_len,
765103423Snectar				nlm_scope, nlm_index);
766103423Snectar	      data += NLMSG_ALIGN(ifa_sa_len(nlm_family, ifamap.broadcast_len));
767103423Snectar	    }
768103423Snectar	  }
769103423Snectar#ifdef HAVE_IFADDRS_IFA_ANYCAST
770103423Snectar	  if (ifamap.anycast) {
771103423Snectar	    if (!build)
772103423Snectar	      dlen += NLMSG_ALIGN(ifa_sa_len(nlm_family,ifamap.anycast_len));
773103423Snectar	    else {
774103423Snectar	      ifa->ifa_anycast = (struct sockaddr *)data;
775103423Snectar	      ifa_make_sockaddr(nlm_family, ifa->ifa_anyaddr, ifamap.anycast, ifamap.anycast_len,
776103423Snectar				nlm_scope, nlm_index);
777103423Snectar	      data += NLMSG_ALIGN(ifa_sa_len(nlm_family, ifamap.anycast_len));
778103423Snectar	    }
779103423Snectar	  }
780103423Snectar#endif
781103423Snectar	}
782103423Snectar	if (!build){
783103423Snectar#ifndef IFA_NETMASK
784103423Snectar	  dlen += sockaddr_size;
785103423Snectar#endif
786103423Snectar	  icnt++;
787103423Snectar	} else {
788103423Snectar	  if (ifa->ifa_name == NULL)
789103423Snectar	    ifa->ifa_name = iflist[nlm_index];
790103423Snectar#ifndef IFA_NETMASK
791103423Snectar	  if (ifa->ifa_addr &&
792103423Snectar	      ifa->ifa_addr->sa_family != AF_UNSPEC &&
793103423Snectar	      ifa->ifa_addr->sa_family != AF_PACKET){
794103423Snectar	    ifa->ifa_netmask = (struct sockaddr *)data;
795103423Snectar	    ifa_make_sockaddr_mask(ifa->ifa_addr->sa_family, ifa->ifa_netmask, nlm_prefixlen);
796103423Snectar	  }
797103423Snectar	  data += sockaddr_size;
798103423Snectar#endif
799103423Snectar	  ifl = ifa++;
800103423Snectar	}
801103423Snectar      }
802103423Snectar    }
803103423Snectar    if (!build){
804103423Snectar      if (icnt == 0 && (dlen + nlen + xlen == 0)){
805103423Snectar	if (ifap != NULL)
806103423Snectar	  *ifap = NULL;
807103423Snectar	break; /* cannot found any addresses */
808103423Snectar      }
809103423Snectar    }
810103423Snectar    else
811103423Snectar      free_data(NULL, ifdata);
812103423Snectar  }
813103423Snectar
814103423Snectar/* ---------------------------------- */
815103423Snectar  /* Finalize */
816103423Snectar  free_nlmsglist(nlmsg_list);
817103423Snectar  nl_close(sd);
818103423Snectar  return 0;
819103423Snectar}
820103423Snectar
821103423Snectar/* ---------------------------------------------------------------------- */
822103423Snectarvoid
823103423Snectarfreeifaddrs(struct ifaddrs *ifa)
824103423Snectar{
825103423Snectar  free(ifa);
826103423Snectar}
827103423Snectar
828103423Snectar
829103423Snectar#else /* !AF_NETLINK */
830103423Snectar
831103423Snectar/*
832103423Snectar * The generic SIOCGIFCONF version.
833103423Snectar */
834103423Snectar
835103423Snectarstatic int
83672445Sassargetifaddrs2(struct ifaddrs **ifap,
83772445Sassar	    int af, int siocgifconf, int siocgifflags,
83872445Sassar	    size_t ifreq_sz)
83972445Sassar{
84072445Sassar    int ret;
84172445Sassar    int fd;
84272445Sassar    size_t buf_size;
84372445Sassar    char *buf;
84472445Sassar    struct ifconf ifconf;
84572445Sassar    char *p;
84672445Sassar    size_t sz;
84772445Sassar    struct sockaddr sa_zero;
84872445Sassar    struct ifreq *ifr;
84990926Snectar    struct ifaddrs *start = NULL, **end = &start;
85072445Sassar
85172445Sassar    buf = NULL;
85272445Sassar
85372445Sassar    memset (&sa_zero, 0, sizeof(sa_zero));
85472445Sassar    fd = socket(af, SOCK_DGRAM, 0);
85572445Sassar    if (fd < 0)
85672445Sassar	return -1;
85772445Sassar
85872445Sassar    buf_size = 8192;
85972445Sassar    for (;;) {
86072445Sassar	buf = calloc(1, buf_size);
86172445Sassar	if (buf == NULL) {
86272445Sassar	    ret = ENOMEM;
86372445Sassar	    goto error_out;
86472445Sassar	}
86572445Sassar	ifconf.ifc_len = buf_size;
86672445Sassar	ifconf.ifc_buf = buf;
86772445Sassar
86872445Sassar	/*
86972445Sassar	 * Solaris returns EINVAL when the buffer is too small.
87072445Sassar	 */
87172445Sassar	if (ioctl (fd, siocgifconf, &ifconf) < 0 && errno != EINVAL) {
87272445Sassar	    ret = errno;
87372445Sassar	    goto error_out;
87472445Sassar	}
87572445Sassar	/*
87672445Sassar	 * Can the difference between a full and a overfull buf
87772445Sassar	 * be determined?
87872445Sassar	 */
87972445Sassar
88072445Sassar	if (ifconf.ifc_len < buf_size)
88172445Sassar	    break;
88272445Sassar	free (buf);
88372445Sassar	buf_size *= 2;
88472445Sassar    }
88572445Sassar
88672445Sassar    for (p = ifconf.ifc_buf;
88772445Sassar	 p < ifconf.ifc_buf + ifconf.ifc_len;
88872445Sassar	 p += sz) {
88972445Sassar	struct ifreq ifreq;
89072445Sassar	struct sockaddr *sa;
89172445Sassar	size_t salen;
89272445Sassar
89372445Sassar	ifr = (struct ifreq *)p;
89472445Sassar	sa  = &ifr->ifr_addr;
89572445Sassar
89672445Sassar	sz = ifreq_sz;
89772445Sassar	salen = sizeof(struct sockaddr);
89872445Sassar#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
89972445Sassar	salen = sa->sa_len;
90072445Sassar	sz = max(sz, sizeof(ifr->ifr_name) + sa->sa_len);
90172445Sassar#endif
90272445Sassar#ifdef SA_LEN
90372445Sassar	salen = SA_LEN(sa);
90472445Sassar	sz = max(sz, sizeof(ifr->ifr_name) + SA_LEN(sa));
90572445Sassar#endif
90672445Sassar	memset (&ifreq, 0, sizeof(ifreq));
90772445Sassar	memcpy (ifreq.ifr_name, ifr->ifr_name, sizeof(ifr->ifr_name));
90872445Sassar
90972445Sassar	if (ioctl(fd, siocgifflags, &ifreq) < 0) {
91072445Sassar	    ret = errno;
91172445Sassar	    goto error_out;
91272445Sassar	}
91372445Sassar
91472445Sassar	*end = malloc(sizeof(**end));
91590926Snectar	if (*end == NULL) {
91690926Snectar	    ret = ENOMEM;
91790926Snectar	    goto error_out;
91890926Snectar	}
91972445Sassar
92072445Sassar	(*end)->ifa_next = NULL;
92172445Sassar	(*end)->ifa_name = strdup(ifr->ifr_name);
92272445Sassar	(*end)->ifa_flags = ifreq.ifr_flags;
92372445Sassar	(*end)->ifa_addr = malloc(salen);
92472445Sassar	memcpy((*end)->ifa_addr, sa, salen);
92572445Sassar	(*end)->ifa_netmask = NULL;
92672445Sassar
92772445Sassar#if 0
92872445Sassar	/* fix these when we actually need them */
92972445Sassar	if(ifreq.ifr_flags & IFF_BROADCAST) {
93072445Sassar	    (*end)->ifa_broadaddr = malloc(sizeof(ifr->ifr_broadaddr));
93172445Sassar	    memcpy((*end)->ifa_broadaddr, &ifr->ifr_broadaddr,
93272445Sassar		   sizeof(ifr->ifr_broadaddr));
93372445Sassar	} else if(ifreq.ifr_flags & IFF_POINTOPOINT) {
93472445Sassar	    (*end)->ifa_dstaddr = malloc(sizeof(ifr->ifr_dstaddr));
93572445Sassar	    memcpy((*end)->ifa_dstaddr, &ifr->ifr_dstaddr,
93672445Sassar		   sizeof(ifr->ifr_dstaddr));
93772445Sassar	} else
93872445Sassar	    (*end)->ifa_dstaddr = NULL;
93972445Sassar#else
94072445Sassar	    (*end)->ifa_dstaddr = NULL;
94172445Sassar#endif
94272445Sassar
94372445Sassar	(*end)->ifa_data = NULL;
94472445Sassar
94572445Sassar	end = &(*end)->ifa_next;
94672445Sassar
94772445Sassar    }
94872445Sassar    *ifap = start;
94978527Sassar    close(fd);
95072445Sassar    free(buf);
95172445Sassar    return 0;
95272445Sassar  error_out:
95390926Snectar    freeifaddrs(start);
95478527Sassar    close(fd);
95572445Sassar    free(buf);
95672445Sassar    errno = ret;
95772445Sassar    return -1;
95872445Sassar}
95972445Sassar
96090926Snectar#if defined(HAVE_IPV6) && defined(SIOCGLIFCONF) && defined(SIOCGLIFFLAGS)
96190926Snectarstatic int
96290926Snectargetlifaddrs2(struct ifaddrs **ifap,
96390926Snectar	     int af, int siocgifconf, int siocgifflags,
96490926Snectar	     size_t ifreq_sz)
96590926Snectar{
96690926Snectar    int ret;
96790926Snectar    int fd;
96890926Snectar    size_t buf_size;
96990926Snectar    char *buf;
97090926Snectar    struct lifconf ifconf;
97190926Snectar    char *p;
97290926Snectar    size_t sz;
97390926Snectar    struct sockaddr sa_zero;
97490926Snectar    struct lifreq *ifr;
97590926Snectar    struct ifaddrs *start = NULL, **end = &start;
97690926Snectar
97790926Snectar    buf = NULL;
97890926Snectar
97990926Snectar    memset (&sa_zero, 0, sizeof(sa_zero));
98090926Snectar    fd = socket(af, SOCK_DGRAM, 0);
98190926Snectar    if (fd < 0)
98290926Snectar	return -1;
98390926Snectar
98490926Snectar    buf_size = 8192;
98590926Snectar    for (;;) {
98690926Snectar	buf = calloc(1, buf_size);
98790926Snectar	if (buf == NULL) {
98890926Snectar	    ret = ENOMEM;
98990926Snectar	    goto error_out;
99090926Snectar	}
99190926Snectar	ifconf.lifc_family = AF_UNSPEC;
99290926Snectar	ifconf.lifc_flags  = 0;
99390926Snectar	ifconf.lifc_len    = buf_size;
99490926Snectar	ifconf.lifc_buf    = buf;
99590926Snectar
99690926Snectar	/*
99790926Snectar	 * Solaris returns EINVAL when the buffer is too small.
99890926Snectar	 */
99990926Snectar	if (ioctl (fd, siocgifconf, &ifconf) < 0 && errno != EINVAL) {
100090926Snectar	    ret = errno;
100190926Snectar	    goto error_out;
100290926Snectar	}
100390926Snectar	/*
100490926Snectar	 * Can the difference between a full and a overfull buf
100590926Snectar	 * be determined?
100690926Snectar	 */
100790926Snectar
100890926Snectar	if (ifconf.lifc_len < buf_size)
100990926Snectar	    break;
101090926Snectar	free (buf);
101190926Snectar	buf_size *= 2;
101290926Snectar    }
101390926Snectar
101490926Snectar    for (p = ifconf.lifc_buf;
101590926Snectar	 p < ifconf.lifc_buf + ifconf.lifc_len;
101690926Snectar	 p += sz) {
101790926Snectar	struct lifreq ifreq;
101890926Snectar	struct sockaddr_storage *sa;
101990926Snectar	size_t salen;
102090926Snectar
102190926Snectar	ifr = (struct lifreq *)p;
102290926Snectar	sa  = &ifr->lifr_addr;
102390926Snectar
102490926Snectar	sz = ifreq_sz;
102590926Snectar	salen = sizeof(struct sockaddr_storage);
102690926Snectar#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
102790926Snectar	salen = sa->sa_len;
102890926Snectar	sz = max(sz, sizeof(ifr->ifr_name) + sa->sa_len);
102990926Snectar#endif
103090926Snectar#ifdef SA_LEN
103190926Snectar	salen = SA_LEN(sa);
103290926Snectar	sz = max(sz, sizeof(ifr->ifr_name) + SA_LEN(sa));
103390926Snectar#endif
103490926Snectar	memset (&ifreq, 0, sizeof(ifreq));
103590926Snectar	memcpy (ifreq.lifr_name, ifr->lifr_name, sizeof(ifr->lifr_name));
103690926Snectar
103790926Snectar	if (ioctl(fd, siocgifflags, &ifreq) < 0) {
103890926Snectar	    ret = errno;
103990926Snectar	    goto error_out;
104090926Snectar	}
104190926Snectar
104290926Snectar	*end = malloc(sizeof(**end));
104390926Snectar
104490926Snectar	(*end)->ifa_next = NULL;
104590926Snectar	(*end)->ifa_name = strdup(ifr->lifr_name);
104690926Snectar	(*end)->ifa_flags = ifreq.lifr_flags;
104790926Snectar	(*end)->ifa_addr = malloc(salen);
104890926Snectar	memcpy((*end)->ifa_addr, sa, salen);
104990926Snectar	(*end)->ifa_netmask = NULL;
105090926Snectar
105190926Snectar#if 0
105290926Snectar	/* fix these when we actually need them */
105390926Snectar	if(ifreq.ifr_flags & IFF_BROADCAST) {
105490926Snectar	    (*end)->ifa_broadaddr = malloc(sizeof(ifr->ifr_broadaddr));
105590926Snectar	    memcpy((*end)->ifa_broadaddr, &ifr->ifr_broadaddr,
105690926Snectar		   sizeof(ifr->ifr_broadaddr));
105790926Snectar	} else if(ifreq.ifr_flags & IFF_POINTOPOINT) {
105890926Snectar	    (*end)->ifa_dstaddr = malloc(sizeof(ifr->ifr_dstaddr));
105990926Snectar	    memcpy((*end)->ifa_dstaddr, &ifr->ifr_dstaddr,
106090926Snectar		   sizeof(ifr->ifr_dstaddr));
106190926Snectar	} else
106290926Snectar	    (*end)->ifa_dstaddr = NULL;
106390926Snectar#else
106490926Snectar	    (*end)->ifa_dstaddr = NULL;
106590926Snectar#endif
106690926Snectar
106790926Snectar	(*end)->ifa_data = NULL;
106890926Snectar
106990926Snectar	end = &(*end)->ifa_next;
107090926Snectar
107190926Snectar    }
107290926Snectar    *ifap = start;
107390926Snectar    close(fd);
107490926Snectar    free(buf);
107590926Snectar    return 0;
107690926Snectar  error_out:
107790926Snectar    freeifaddrs(start);
107890926Snectar    close(fd);
107990926Snectar    free(buf);
108090926Snectar    errno = ret;
108190926Snectar    return -1;
108290926Snectar}
108390926Snectar#endif /* defined(HAVE_IPV6) && defined(SIOCGLIFCONF) && defined(SIOCGLIFFLAGS) */
108490926Snectar
108572445Sassarint
108672445Sassargetifaddrs(struct ifaddrs **ifap)
108772445Sassar{
108872445Sassar    int ret = -1;
108972445Sassar    errno = ENXIO;
109072445Sassar#if defined(AF_INET6) && defined(SIOCGIF6CONF) && defined(SIOCGIF6FLAGS)
109172445Sassar    if (ret)
109272445Sassar	ret = getifaddrs2 (ifap, AF_INET6, SIOCGIF6CONF, SIOCGIF6FLAGS,
109372445Sassar			   sizeof(struct in6_ifreq));
109472445Sassar#endif
109590926Snectar#if defined(HAVE_IPV6) && defined(SIOCGLIFCONF) && defined(SIOCGLIFFLAGS)
109690926Snectar    if (ret)
109790926Snectar	ret = getlifaddrs2 (ifap, AF_INET6, SIOCGLIFCONF, SIOCGLIFFLAGS,
109890926Snectar			    sizeof(struct lifreq));
109990926Snectar#endif
110072445Sassar#if defined(HAVE_IPV6) && defined(SIOCGIFCONF)
110172445Sassar    if (ret)
110272445Sassar	ret = getifaddrs2 (ifap, AF_INET6, SIOCGIFCONF, SIOCGIFFLAGS,
110372445Sassar			   sizeof(struct ifreq));
110472445Sassar#endif
110572445Sassar#if defined(AF_INET) && defined(SIOCGIFCONF) && defined(SIOCGIFFLAGS)
110672445Sassar    if (ret)
110772445Sassar	ret = getifaddrs2 (ifap, AF_INET, SIOCGIFCONF, SIOCGIFFLAGS,
110872445Sassar			   sizeof(struct ifreq));
110972445Sassar#endif
111072445Sassar    return ret;
111172445Sassar}
111272445Sassar
111372445Sassarvoid
111472445Sassarfreeifaddrs(struct ifaddrs *ifp)
111572445Sassar{
111672445Sassar    struct ifaddrs *p, *q;
111772445Sassar
111872445Sassar    for(p = ifp; p; ) {
111972445Sassar	free(p->ifa_name);
112072445Sassar	if(p->ifa_addr)
112172445Sassar	    free(p->ifa_addr);
112272445Sassar	if(p->ifa_dstaddr)
112372445Sassar	    free(p->ifa_dstaddr);
112472445Sassar	if(p->ifa_netmask)
112572445Sassar	    free(p->ifa_netmask);
112672445Sassar	if(p->ifa_data)
112772445Sassar	    free(p->ifa_data);
112872445Sassar	q = p;
112972445Sassar	p = p->ifa_next;
113072445Sassar	free(q);
113172445Sassar    }
113272445Sassar}
113372445Sassar
1134103423Snectar#endif /* !AF_NETLINK */
1135103423Snectar
113672445Sassar#ifdef TEST
113772445Sassar
113872445Sassarvoid
113972445Sassarprint_addr(const char *s, struct sockaddr *sa)
114072445Sassar{
114172445Sassar    int i;
114272445Sassar    printf("  %s=%d/", s, sa->sa_family);
114372445Sassar#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
114472445Sassar    for(i = 0; i < sa->sa_len - ((long)sa->sa_data - (long)&sa->sa_family); i++)
114572445Sassar	printf("%02x", ((unsigned char*)sa->sa_data)[i]);
114672445Sassar#else
114772445Sassar    for(i = 0; i < sizeof(sa->sa_data); i++)
114872445Sassar	printf("%02x", ((unsigned char*)sa->sa_data)[i]);
114972445Sassar#endif
115072445Sassar    printf("\n");
115172445Sassar}
115272445Sassar
115372445Sassarvoid
115472445Sassarprint_ifaddrs(struct ifaddrs *x)
115572445Sassar{
115672445Sassar    struct ifaddrs *p;
115772445Sassar
115872445Sassar    for(p = x; p; p = p->ifa_next) {
115972445Sassar	printf("%s\n", p->ifa_name);
116072445Sassar	printf("  flags=%x\n", p->ifa_flags);
116172445Sassar	if(p->ifa_addr)
116272445Sassar	    print_addr("addr", p->ifa_addr);
116372445Sassar	if(p->ifa_dstaddr)
116472445Sassar	    print_addr("dstaddr", p->ifa_dstaddr);
116572445Sassar	if(p->ifa_netmask)
116672445Sassar	    print_addr("netmask", p->ifa_netmask);
116772445Sassar	printf("  %p\n", p->ifa_data);
116872445Sassar    }
116972445Sassar}
117072445Sassar
117172445Sassarint
117272445Sassarmain()
117372445Sassar{
117472445Sassar    struct ifaddrs *a = NULL, *b;
117572445Sassar    getifaddrs2(&a, AF_INET, SIOCGIFCONF, SIOCGIFFLAGS, sizeof(struct ifreq));
117672445Sassar    print_ifaddrs(a);
117772445Sassar    printf("---\n");
117872445Sassar    getifaddrs(&b);
117972445Sassar    print_ifaddrs(b);
118072445Sassar    return 0;
118172445Sassar}
118272445Sassar#endif
1183