172445Sassar/*
2233294Sstas * Copyright (c) 2000 - 2002, 2005 Kungliga Tekniska H��gskolan
372445Sassar * (Royal Institute of Technology, Stockholm, Sweden).
472445Sassar * All rights reserved.
5233294Sstas *
672445Sassar * Redistribution and use in source and binary forms, with or without
772445Sassar * modification, are permitted provided that the following conditions
872445Sassar * are met:
9233294Sstas *
1072445Sassar * 1. Redistributions of source code must retain the above copyright
1172445Sassar *    notice, this list of conditions and the following disclaimer.
12233294Sstas *
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.
16233294Sstas *
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.
20233294Sstas *
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#include <config.h>
3572445Sassar#include "roken.h"
3672445Sassar
3772445Sassar#ifdef __osf__
3872445Sassar/* hate */
3972445Sassarstruct rtentry;
4072445Sassarstruct mbuf;
4172445Sassar#endif
4272445Sassar#ifdef HAVE_NET_IF_H
4372445Sassar#include <net/if.h>
4472445Sassar#endif
4572445Sassar
4672445Sassar#ifdef HAVE_SYS_SOCKIO_H
4772445Sassar#include <sys/sockio.h>
4872445Sassar#endif /* HAVE_SYS_SOCKIO_H */
4972445Sassar
5072445Sassar#ifdef HAVE_NETINET_IN6_VAR_H
5172445Sassar#include <netinet/in6_var.h>
5272445Sassar#endif /* HAVE_NETINET_IN6_VAR_H */
5372445Sassar
5472445Sassar#include <ifaddrs.h>
5572445Sassar
56178825Sdfr#ifdef __hpux
57178825Sdfr#define lifconf if_laddrconf
58178825Sdfr#define lifc_len iflc_len
59178825Sdfr#define lifc_buf iflc_buf
60178825Sdfr#define lifc_req iflc_req
61178825Sdfr
62178825Sdfr#define lifreq if_laddrreq
63178825Sdfr#define lifr_addr iflr_addr
64178825Sdfr#define lifr_name iflr_name
65178825Sdfr#define lifr_dstaddr iflr_dstaddr
66178825Sdfr#define lifr_broadaddr iflr_broadaddr
67178825Sdfr#define lifr_flags iflr_flags
68178825Sdfr#define lifr_index iflr_index
69178825Sdfr#endif
70178825Sdfr
71103423Snectar#ifdef AF_NETLINK
72103423Snectar
73103423Snectar/*
74103423Snectar * The linux - AF_NETLINK version of getifaddrs - from Usagi.
75103423Snectar * Linux does not return v6 addresses from SIOCGIFCONF.
76103423Snectar */
77103423Snectar
78103423Snectar/* $USAGI: ifaddrs.c,v 1.18 2002/03/06 01:50:46 yoshfuji Exp $ */
79103423Snectar
80103423Snectar/**************************************************************************
81103423Snectar * ifaddrs.c
82103423Snectar * Copyright (C)2000 Hideaki YOSHIFUJI, All Rights Reserved.
83103423Snectar *
84103423Snectar * Redistribution and use in source and binary forms, with or without
85103423Snectar * modification, are permitted provided that the following conditions
86103423Snectar * are met:
87103423Snectar * 1. Redistributions of source code must retain the above copyright
88103423Snectar *    notice, this list of conditions and the following disclaimer.
89103423Snectar * 2. Redistributions in binary form must reproduce the above copyright
90103423Snectar *    notice, this list of conditions and the following disclaimer in the
91103423Snectar *    documentation and/or other materials provided with the distribution.
92103423Snectar * 3. Neither the name of the author nor the names of its contributors
93103423Snectar *    may be used to endorse or promote products derived from this software
94103423Snectar *    without specific prior written permission.
95233294Sstas *
96103423Snectar * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
97103423Snectar * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
98103423Snectar * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
99103423Snectar * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
100103423Snectar * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
101103423Snectar * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
102103423Snectar * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
103103423Snectar * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
104103423Snectar * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
105103423Snectar * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
106103423Snectar * SUCH DAMAGE.
107103423Snectar */
108103423Snectar
109103423Snectar#include "config.h"
110103423Snectar
111103423Snectar#include <string.h>
112103423Snectar#include <time.h>
113103423Snectar#include <malloc.h>
114103423Snectar#include <errno.h>
115103423Snectar#include <unistd.h>
116103423Snectar
117103423Snectar#include <sys/socket.h>
118103423Snectar#include <asm/types.h>
119103423Snectar#include <linux/netlink.h>
120103423Snectar#include <linux/rtnetlink.h>
121103423Snectar#include <sys/types.h>
122103423Snectar#include <sys/socket.h>
123178825Sdfr#include <sys/poll.h>
124103423Snectar#include <netpacket/packet.h>
125103423Snectar#include <net/ethernet.h>     /* the L2 protocols */
126103423Snectar#include <sys/uio.h>
127103423Snectar#include <net/if.h>
128103423Snectar#include <net/if_arp.h>
129103423Snectar#include <ifaddrs.h>
130103423Snectar#include <netinet/in.h>
131103423Snectar
132103423Snectar#define __set_errno(e) (errno = (e))
133103423Snectar#define __close(fd) (close(fd))
134103423Snectar#undef ifa_broadaddr
135103423Snectar#define ifa_broadaddr ifa_dstaddr
136103423Snectar#define IFA_NETMASK
137103423Snectar
138103423Snectar/* ====================================================================== */
139103423Snectarstruct nlmsg_list{
140103423Snectar    struct nlmsg_list *nlm_next;
141103423Snectar    struct nlmsghdr *nlh;
142103423Snectar    int size;
143103423Snectar    time_t seq;
144103423Snectar};
145103423Snectar
146103423Snectarstruct rtmaddr_ifamap {
147103423Snectar  void *address;
148103423Snectar  void *local;
149103423Snectar#ifdef IFA_NETMASK
150103423Snectar  void *netmask;
151103423Snectar#endif
152103423Snectar  void *broadcast;
153103423Snectar#ifdef HAVE_IFADDRS_IFA_ANYCAST
154103423Snectar  void *anycast;
155103423Snectar#endif
156103423Snectar  int address_len;
157103423Snectar  int local_len;
158103423Snectar#ifdef IFA_NETMASK
159103423Snectar  int netmask_len;
160103423Snectar#endif
161103423Snectar  int broadcast_len;
162103423Snectar#ifdef HAVE_IFADDRS_IFA_ANYCAST
163103423Snectar  int anycast_len;
164103423Snectar#endif
165103423Snectar};
166103423Snectar
167103423Snectar/* ====================================================================== */
168103423Snectarstatic size_t
169103423Snectarifa_sa_len(sa_family_t family, int len)
170103423Snectar{
171103423Snectar  size_t size;
172103423Snectar  switch(family){
173103423Snectar  case AF_INET:
174103423Snectar    size = sizeof(struct sockaddr_in);
175103423Snectar    break;
176103423Snectar  case AF_INET6:
177103423Snectar    size = sizeof(struct sockaddr_in6);
178103423Snectar    break;
179103423Snectar  case AF_PACKET:
180103423Snectar    size = (size_t)(((struct sockaddr_ll *)NULL)->sll_addr) + len;
181103423Snectar    if (size < sizeof(struct sockaddr_ll))
182103423Snectar      size = sizeof(struct sockaddr_ll);
183103423Snectar    break;
184103423Snectar  default:
185103423Snectar    size = (size_t)(((struct sockaddr *)NULL)->sa_data) + len;
186103423Snectar    if (size < sizeof(struct sockaddr))
187103423Snectar      size = sizeof(struct sockaddr);
188178825Sdfr    break;
189103423Snectar  }
190103423Snectar  return size;
191103423Snectar}
192103423Snectar
193233294Sstasstatic void
194233294Sstasifa_make_sockaddr(sa_family_t family,
195233294Sstas		  struct sockaddr *sa,
196103423Snectar		  void *p, size_t len,
197103423Snectar		  uint32_t scope, uint32_t scopeid)
198103423Snectar{
199103423Snectar  if (sa == NULL) return;
200103423Snectar  switch(family){
201103423Snectar  case AF_INET:
202103423Snectar    memcpy(&((struct sockaddr_in*)sa)->sin_addr, (char *)p, len);
203103423Snectar    break;
204103423Snectar  case AF_INET6:
205103423Snectar    memcpy(&((struct sockaddr_in6*)sa)->sin6_addr, (char *)p, len);
206103423Snectar    if (IN6_IS_ADDR_LINKLOCAL(p) ||
207103423Snectar	IN6_IS_ADDR_MC_LINKLOCAL(p)){
208103423Snectar      ((struct sockaddr_in6*)sa)->sin6_scope_id = scopeid;
209103423Snectar    }
210103423Snectar    break;
211103423Snectar  case AF_PACKET:
212103423Snectar    memcpy(((struct sockaddr_ll*)sa)->sll_addr, (char *)p, len);
213103423Snectar    ((struct sockaddr_ll*)sa)->sll_halen = len;
214103423Snectar    break;
215103423Snectar  default:
216103423Snectar    memcpy(sa->sa_data, p, len);	/*XXX*/
217103423Snectar    break;
218103423Snectar  }
219103423Snectar  sa->sa_family = family;
220103423Snectar#ifdef HAVE_SOCKADDR_SA_LEN
221103423Snectar  sa->sa_len = ifa_sa_len(family, len);
222103423Snectar#endif
223103423Snectar}
224103423Snectar
225103423Snectar#ifndef IFA_NETMASK
226103423Snectarstatic struct sockaddr *
227233294Sstasifa_make_sockaddr_mask(sa_family_t family,
228233294Sstas		       struct sockaddr *sa,
229103423Snectar		       uint32_t prefixlen)
230103423Snectar{
231103423Snectar  int i;
232103423Snectar  char *p = NULL, c;
233103423Snectar  uint32_t max_prefixlen = 0;
234103423Snectar
235103423Snectar  if (sa == NULL) return NULL;
236103423Snectar  switch(family){
237103423Snectar  case AF_INET:
238103423Snectar    memset(&((struct sockaddr_in*)sa)->sin_addr, 0, sizeof(((struct sockaddr_in*)sa)->sin_addr));
239103423Snectar    p = (char *)&((struct sockaddr_in*)sa)->sin_addr;
240103423Snectar    max_prefixlen = 32;
241103423Snectar    break;
242103423Snectar  case AF_INET6:
243103423Snectar    memset(&((struct sockaddr_in6*)sa)->sin6_addr, 0, sizeof(((struct sockaddr_in6*)sa)->sin6_addr));
244103423Snectar    p = (char *)&((struct sockaddr_in6*)sa)->sin6_addr;
245103423Snectar#if 0	/* XXX: fill scope-id? */
246103423Snectar    if (IN6_IS_ADDR_LINKLOCAL(p) ||
247103423Snectar	IN6_IS_ADDR_MC_LINKLOCAL(p)){
248103423Snectar      ((struct sockaddr_in6*)sa)->sin6_scope_id = scopeid;
249103423Snectar    }
250103423Snectar#endif
251103423Snectar    max_prefixlen = 128;
252103423Snectar    break;
253103423Snectar  default:
254103423Snectar    return NULL;
255103423Snectar  }
256103423Snectar  sa->sa_family = family;
257103423Snectar#ifdef HAVE_SOCKADDR_SA_LEN
258103423Snectar  sa->sa_len = ifa_sa_len(family, len);
259103423Snectar#endif
260103423Snectar  if (p){
261103423Snectar    if (prefixlen > max_prefixlen)
262103423Snectar      prefixlen = max_prefixlen;
263103423Snectar    for (i=0; i<(prefixlen / 8); i++)
264103423Snectar      *p++ = 0xff;
265103423Snectar    c = 0xff;
266103423Snectar    c <<= (8 - (prefixlen % 8));
267103423Snectar    *p = c;
268103423Snectar  }
269103423Snectar  return sa;
270103423Snectar}
271103423Snectar#endif
272103423Snectar
273103423Snectar/* ====================================================================== */
274233294Sstasstatic int
275103423Snectarnl_sendreq(int sd, int request, int flags, int *seq)
276103423Snectar{
277103423Snectar  char reqbuf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
278103423Snectar	      NLMSG_ALIGN(sizeof(struct rtgenmsg))];
279103423Snectar  struct sockaddr_nl nladdr;
280103423Snectar  struct nlmsghdr *req_hdr;
281103423Snectar  struct rtgenmsg *req_msg;
282103423Snectar  time_t t = time(NULL);
283103423Snectar
284103423Snectar  if (seq) *seq = t;
285103423Snectar  memset(&reqbuf, 0, sizeof(reqbuf));
286103423Snectar  req_hdr = (struct nlmsghdr *)reqbuf;
287103423Snectar  req_msg = (struct rtgenmsg *)NLMSG_DATA(req_hdr);
288103423Snectar  req_hdr->nlmsg_len = NLMSG_LENGTH(sizeof(*req_msg));
289103423Snectar  req_hdr->nlmsg_type = request;
290103423Snectar  req_hdr->nlmsg_flags = flags | NLM_F_REQUEST;
291103423Snectar  req_hdr->nlmsg_pid = 0;
292103423Snectar  req_hdr->nlmsg_seq = t;
293103423Snectar  req_msg->rtgen_family = AF_UNSPEC;
294103423Snectar  memset(&nladdr, 0, sizeof(nladdr));
295103423Snectar  nladdr.nl_family = AF_NETLINK;
296103423Snectar  return (sendto(sd, (void *)req_hdr, req_hdr->nlmsg_len, 0,
297103423Snectar		 (struct sockaddr *)&nladdr, sizeof(nladdr)));
298103423Snectar}
299103423Snectar
300233294Sstasstatic int
301233294Sstasnl_recvmsg(int sd, int request, int seq,
302233294Sstas	   void *buf, size_t buflen,
303103423Snectar	   int *flags)
304103423Snectar{
305103423Snectar  struct msghdr msg;
306103423Snectar  struct iovec iov = { buf, buflen };
307103423Snectar  struct sockaddr_nl nladdr;
308103423Snectar  int read_len;
309103423Snectar
310103423Snectar  for (;;){
311103423Snectar    msg.msg_name = (void *)&nladdr;
312103423Snectar    msg.msg_namelen = sizeof(nladdr);
313103423Snectar    msg.msg_iov = &iov;
314103423Snectar    msg.msg_iovlen = 1;
315103423Snectar    msg.msg_control = NULL;
316103423Snectar    msg.msg_controllen = 0;
317103423Snectar    msg.msg_flags = 0;
318103423Snectar    read_len = recvmsg(sd, &msg, 0);
319103423Snectar    if ((read_len < 0 && errno == EINTR) || (msg.msg_flags & MSG_TRUNC))
320103423Snectar      continue;
321103423Snectar    if (flags) *flags = msg.msg_flags;
322103423Snectar    break;
323103423Snectar  }
324103423Snectar  return read_len;
325103423Snectar}
326103423Snectar
327233294Sstasstatic int
328233294Sstasnl_getmsg(int sd, int request, int seq,
329103423Snectar	  struct nlmsghdr **nlhp,
330103423Snectar	  int *done)
331103423Snectar{
332103423Snectar  struct nlmsghdr *nh;
333103423Snectar  size_t bufsize = 65536, lastbufsize = 0;
334103423Snectar  void *buff = NULL;
335103423Snectar  int result = 0, read_size;
336103423Snectar  int msg_flags;
337103423Snectar  pid_t pid = getpid();
338103423Snectar  for (;;){
339103423Snectar    void *newbuff = realloc(buff, bufsize);
340103423Snectar    if (newbuff == NULL || bufsize < lastbufsize) {
341103423Snectar      result = -1;
342103423Snectar      break;
343103423Snectar    }
344103423Snectar    buff = newbuff;
345103423Snectar    result = read_size = nl_recvmsg(sd, request, seq, buff, bufsize, &msg_flags);
346103423Snectar    if (read_size < 0 || (msg_flags & MSG_TRUNC)){
347103423Snectar      lastbufsize = bufsize;
348103423Snectar      bufsize *= 2;
349103423Snectar      continue;
350103423Snectar    }
351103423Snectar    if (read_size == 0) break;
352103423Snectar    nh = (struct nlmsghdr *)buff;
353103423Snectar    for (nh = (struct nlmsghdr *)buff;
354103423Snectar	 NLMSG_OK(nh, read_size);
355103423Snectar	 nh = (struct nlmsghdr *)NLMSG_NEXT(nh, read_size)){
356103423Snectar      if (nh->nlmsg_pid != pid ||
357103423Snectar	  nh->nlmsg_seq != seq)
358103423Snectar	continue;
359103423Snectar      if (nh->nlmsg_type == NLMSG_DONE){
360103423Snectar	(*done)++;
361103423Snectar	break; /* ok */
362103423Snectar      }
363103423Snectar      if (nh->nlmsg_type == NLMSG_ERROR){
364103423Snectar	struct nlmsgerr *nlerr = (struct nlmsgerr *)NLMSG_DATA(nh);
365103423Snectar	result = -1;
366103423Snectar	if (nh->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr)))
367103423Snectar	  __set_errno(EIO);
368103423Snectar	else
369103423Snectar	  __set_errno(-nlerr->error);
370103423Snectar	break;
371103423Snectar      }
372103423Snectar    }
373103423Snectar    break;
374103423Snectar  }
375103423Snectar  if (result < 0)
376103423Snectar    if (buff){
377103423Snectar      int saved_errno = errno;
378103423Snectar      free(buff);
379103423Snectar      __set_errno(saved_errno);
380103423Snectar    }
381103423Snectar  *nlhp = (struct nlmsghdr *)buff;
382103423Snectar  return result;
383103423Snectar}
384103423Snectar
38572445Sassarstatic int
386103423Snectarnl_getlist(int sd, int seq,
387103423Snectar	   int request,
388103423Snectar	   struct nlmsg_list **nlm_list,
389103423Snectar	   struct nlmsg_list **nlm_end)
390103423Snectar{
391103423Snectar  struct nlmsghdr *nlh = NULL;
392103423Snectar  int status;
393103423Snectar  int done = 0;
394178825Sdfr  int tries = 3;
395103423Snectar
396178825Sdfr try_again:
397103423Snectar  status = nl_sendreq(sd, request, NLM_F_ROOT|NLM_F_MATCH, &seq);
398103423Snectar  if (status < 0)
399103423Snectar    return status;
400103423Snectar  if (seq == 0)
401103423Snectar    seq = (int)time(NULL);
402103423Snectar  while(!done){
403178825Sdfr    struct pollfd pfd;
404178825Sdfr
405178825Sdfr    pfd.fd = sd;
406178825Sdfr    pfd.events = POLLIN | POLLPRI;
407178825Sdfr    pfd.revents = 0;
408178825Sdfr    status = poll(&pfd, 1, 1000);
409178825Sdfr    if (status < 0)
410178825Sdfr	return status;
411178825Sdfr    else if (status == 0) {
412178825Sdfr	seq++;
413178825Sdfr	if (tries-- > 0)
414178825Sdfr	    goto try_again;
415178825Sdfr	return -1;
416178825Sdfr    }
417178825Sdfr
418103423Snectar    status = nl_getmsg(sd, request, seq, &nlh, &done);
419103423Snectar    if (status < 0)
420103423Snectar      return status;
421103423Snectar    if (nlh){
422103423Snectar      struct nlmsg_list *nlm_next = (struct nlmsg_list *)malloc(sizeof(struct nlmsg_list));
423103423Snectar      if (nlm_next == NULL){
424103423Snectar	int saved_errno = errno;
425103423Snectar	free(nlh);
426103423Snectar	__set_errno(saved_errno);
427103423Snectar	status = -1;
428103423Snectar      } else {
429103423Snectar	nlm_next->nlm_next = NULL;
430103423Snectar	nlm_next->nlh = (struct nlmsghdr *)nlh;
431103423Snectar	nlm_next->size = status;
432103423Snectar	nlm_next->seq = seq;
433103423Snectar	if (*nlm_list == NULL){
434103423Snectar	  *nlm_list = nlm_next;
435103423Snectar	  *nlm_end = nlm_next;
436103423Snectar	} else {
437103423Snectar	  (*nlm_end)->nlm_next = nlm_next;
438103423Snectar	  *nlm_end = nlm_next;
439103423Snectar	}
440103423Snectar      }
441103423Snectar    }
442103423Snectar  }
443103423Snectar  return status >= 0 ? seq : status;
444103423Snectar}
445103423Snectar
446103423Snectar/* ---------------------------------------------------------------------- */
447233294Sstasstatic void
448103423Snectarfree_nlmsglist(struct nlmsg_list *nlm0)
449103423Snectar{
450178825Sdfr  struct nlmsg_list *nlm, *nlm_next;
451103423Snectar  int saved_errno;
452103423Snectar  if (!nlm0)
453103423Snectar    return;
454103423Snectar  saved_errno = errno;
455178825Sdfr  for (nlm=nlm0; nlm; nlm=nlm_next){
456103423Snectar    if (nlm->nlh)
457103423Snectar      free(nlm->nlh);
458178825Sdfr    nlm_next=nlm->nlm_next;
459178825Sdfr    free(nlm);
460103423Snectar  }
461103423Snectar  __set_errno(saved_errno);
462103423Snectar}
463103423Snectar
464233294Sstasstatic void
465103423Snectarfree_data(void *data, void *ifdata)
466103423Snectar{
467103423Snectar  int saved_errno = errno;
468103423Snectar  if (data != NULL) free(data);
469103423Snectar  if (ifdata != NULL) free(ifdata);
470103423Snectar  __set_errno(saved_errno);
471103423Snectar}
472103423Snectar
473103423Snectar/* ---------------------------------------------------------------------- */
474233294Sstasstatic void
475103423Snectarnl_close(int sd)
476103423Snectar{
477103423Snectar  int saved_errno = errno;
478103423Snectar  if (sd >= 0) __close(sd);
479103423Snectar  __set_errno(saved_errno);
480103423Snectar}
481103423Snectar
482103423Snectar/* ---------------------------------------------------------------------- */
483233294Sstasstatic int
484103423Snectarnl_open(void)
485103423Snectar{
486103423Snectar  struct sockaddr_nl nladdr;
487103423Snectar  int sd;
488103423Snectar
489103423Snectar  sd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
490103423Snectar  if (sd < 0) return -1;
491103423Snectar  memset(&nladdr, 0, sizeof(nladdr));
492103423Snectar  nladdr.nl_family = AF_NETLINK;
493103423Snectar  if (bind(sd, (struct sockaddr*)&nladdr, sizeof(nladdr)) < 0){
494103423Snectar    nl_close(sd);
495103423Snectar    return -1;
496103423Snectar  }
497103423Snectar  return sd;
498103423Snectar}
499103423Snectar
500103423Snectar/* ====================================================================== */
501233294SstasROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
502178825Sdfrrk_getifaddrs(struct ifaddrs **ifap)
503103423Snectar{
504103423Snectar  int sd;
505103423Snectar  struct nlmsg_list *nlmsg_list, *nlmsg_end, *nlm;
506103423Snectar  /* - - - - - - - - - - - - - - - */
507103423Snectar  int icnt;
508103423Snectar  size_t dlen, xlen, nlen;
509103423Snectar  uint32_t max_ifindex = 0;
510103423Snectar
511103423Snectar  pid_t pid = getpid();
512103423Snectar  int seq;
513103423Snectar  int result;
514103423Snectar  int build     ; /* 0 or 1 */
515103423Snectar
516103423Snectar/* ---------------------------------- */
517103423Snectar  /* initialize */
518103423Snectar  icnt = dlen = xlen = nlen = 0;
519103423Snectar  nlmsg_list = nlmsg_end = NULL;
520103423Snectar
521103423Snectar  if (ifap)
522103423Snectar    *ifap = NULL;
523103423Snectar
524103423Snectar/* ---------------------------------- */
525103423Snectar  /* open socket and bind */
526103423Snectar  sd = nl_open();
527103423Snectar  if (sd < 0)
528103423Snectar    return -1;
529103423Snectar
530103423Snectar/* ---------------------------------- */
531103423Snectar   /* gather info */
532103423Snectar  if ((seq = nl_getlist(sd, 0, RTM_GETLINK,
533103423Snectar			&nlmsg_list, &nlmsg_end)) < 0){
534103423Snectar    free_nlmsglist(nlmsg_list);
535103423Snectar    nl_close(sd);
536103423Snectar    return -1;
537103423Snectar  }
538103423Snectar  if ((seq = nl_getlist(sd, seq+1, RTM_GETADDR,
539103423Snectar			&nlmsg_list, &nlmsg_end)) < 0){
540103423Snectar    free_nlmsglist(nlmsg_list);
541103423Snectar    nl_close(sd);
542103423Snectar    return -1;
543103423Snectar  }
544103423Snectar
545103423Snectar/* ---------------------------------- */
546103423Snectar  /* Estimate size of result buffer and fill it */
547103423Snectar  for (build=0; build<=1; build++){
548103423Snectar    struct ifaddrs *ifl = NULL, *ifa = NULL;
549103423Snectar    struct nlmsghdr *nlh, *nlh0;
550103423Snectar    char *data = NULL, *xdata = NULL;
551103423Snectar    void *ifdata = NULL;
552103423Snectar    char *ifname = NULL, **iflist = NULL;
553103423Snectar    uint16_t *ifflist = NULL;
554103423Snectar    struct rtmaddr_ifamap ifamap;
555103423Snectar
556103423Snectar    if (build){
557103423Snectar      data = calloc(1,
558103423Snectar		    NLMSG_ALIGN(sizeof(struct ifaddrs[icnt]))
559103423Snectar		    + dlen + xlen + nlen);
560103423Snectar      ifa = (struct ifaddrs *)data;
561233294Sstas      ifdata = calloc(1,
562103423Snectar		      NLMSG_ALIGN(sizeof(char *[max_ifindex+1]))
563103423Snectar		      + NLMSG_ALIGN(sizeof(uint16_t [max_ifindex+1])));
564103423Snectar      if (ifap != NULL)
565103423Snectar	*ifap = (ifdata != NULL) ? ifa : NULL;
566103423Snectar      else{
567103423Snectar	free_data(data, ifdata);
568103423Snectar	result = 0;
569103423Snectar	break;
570103423Snectar      }
571103423Snectar      if (data == NULL || ifdata == NULL){
572103423Snectar	free_data(data, ifdata);
573103423Snectar	result = -1;
574103423Snectar	break;
575103423Snectar      }
576103423Snectar      ifl = NULL;
577103423Snectar      data += NLMSG_ALIGN(sizeof(struct ifaddrs)) * icnt;
578103423Snectar      xdata = data + dlen;
579103423Snectar      ifname = xdata + xlen;
580103423Snectar      iflist = ifdata;
581103423Snectar      ifflist = (uint16_t *)(((char *)iflist) + NLMSG_ALIGN(sizeof(char *[max_ifindex+1])));
582103423Snectar    }
583103423Snectar
584103423Snectar    for (nlm=nlmsg_list; nlm; nlm=nlm->nlm_next){
585103423Snectar      int nlmlen = nlm->size;
586103423Snectar      if (!(nlh0 = nlm->nlh))
587103423Snectar	continue;
588233294Sstas      for (nlh = nlh0;
589233294Sstas	   NLMSG_OK(nlh, nlmlen);
590103423Snectar	   nlh=NLMSG_NEXT(nlh,nlmlen)){
591103423Snectar	struct ifinfomsg *ifim = NULL;
592103423Snectar	struct ifaddrmsg *ifam = NULL;
593103423Snectar	struct rtattr *rta;
594103423Snectar
595103423Snectar	size_t nlm_struct_size = 0;
596103423Snectar	sa_family_t nlm_family = 0;
597103423Snectar	uint32_t nlm_scope = 0, nlm_index = 0;
598103423Snectar	size_t sockaddr_size = 0;
599103423Snectar	uint32_t nlm_prefixlen = 0;
600103423Snectar	size_t rtasize;
601103423Snectar
602103423Snectar	memset(&ifamap, 0, sizeof(ifamap));
603103423Snectar
604103423Snectar	/* check if the message is what we want */
605103423Snectar	if (nlh->nlmsg_pid != pid ||
606103423Snectar	    nlh->nlmsg_seq != nlm->seq)
607103423Snectar	  continue;
608103423Snectar	if (nlh->nlmsg_type == NLMSG_DONE){
609103423Snectar	  break; /* ok */
610103423Snectar	}
611103423Snectar	switch (nlh->nlmsg_type){
612103423Snectar	case RTM_NEWLINK:
613103423Snectar	  ifim = (struct ifinfomsg *)NLMSG_DATA(nlh);
614103423Snectar	  nlm_struct_size = sizeof(*ifim);
615103423Snectar	  nlm_family = ifim->ifi_family;
616103423Snectar	  nlm_scope = 0;
617103423Snectar	  nlm_index = ifim->ifi_index;
618103423Snectar	  nlm_prefixlen = 0;
619103423Snectar	  if (build)
620103423Snectar	    ifflist[nlm_index] = ifa->ifa_flags = ifim->ifi_flags;
621103423Snectar	  break;
622103423Snectar	case RTM_NEWADDR:
623103423Snectar	  ifam = (struct ifaddrmsg *)NLMSG_DATA(nlh);
624103423Snectar	  nlm_struct_size = sizeof(*ifam);
625103423Snectar	  nlm_family = ifam->ifa_family;
626103423Snectar	  nlm_scope = ifam->ifa_scope;
627103423Snectar	  nlm_index = ifam->ifa_index;
628103423Snectar	  nlm_prefixlen = ifam->ifa_prefixlen;
629103423Snectar	  if (build)
630103423Snectar	    ifa->ifa_flags = ifflist[nlm_index];
631103423Snectar	  break;
632103423Snectar	default:
633103423Snectar	  continue;
634103423Snectar	}
635233294Sstas
636103423Snectar	if (!build){
637103423Snectar	  if (max_ifindex < nlm_index)
638103423Snectar	    max_ifindex = nlm_index;
639103423Snectar	} else {
640103423Snectar	  if (ifl != NULL)
641103423Snectar	    ifl->ifa_next = ifa;
642103423Snectar	}
643103423Snectar
644103423Snectar	rtasize = NLMSG_PAYLOAD(nlh, nlmlen) - NLMSG_ALIGN(nlm_struct_size);
645103423Snectar	for (rta = (struct rtattr *)(((char *)NLMSG_DATA(nlh)) + NLMSG_ALIGN(nlm_struct_size));
646103423Snectar	     RTA_OK(rta, rtasize);
647103423Snectar	     rta = RTA_NEXT(rta, rtasize)){
648103423Snectar	  struct sockaddr **sap = NULL;
649103423Snectar	  void *rtadata = RTA_DATA(rta);
650103423Snectar	  size_t rtapayload = RTA_PAYLOAD(rta);
651103423Snectar	  socklen_t sa_len;
652103423Snectar
653103423Snectar	  switch(nlh->nlmsg_type){
654103423Snectar	  case RTM_NEWLINK:
655103423Snectar	    switch(rta->rta_type){
656103423Snectar	    case IFLA_ADDRESS:
657103423Snectar	    case IFLA_BROADCAST:
658103423Snectar	      if (build){
659103423Snectar		sap = (rta->rta_type == IFLA_ADDRESS) ? &ifa->ifa_addr : &ifa->ifa_broadaddr;
660103423Snectar		*sap = (struct sockaddr *)data;
661103423Snectar	      }
662103423Snectar	      sa_len = ifa_sa_len(AF_PACKET, rtapayload);
663103423Snectar	      if (rta->rta_type == IFLA_ADDRESS)
664103423Snectar		sockaddr_size = NLMSG_ALIGN(sa_len);
665103423Snectar	      if (!build){
666103423Snectar		dlen += NLMSG_ALIGN(sa_len);
667103423Snectar	      } else {
668103423Snectar		memset(*sap, 0, sa_len);
669103423Snectar		ifa_make_sockaddr(AF_PACKET, *sap, rtadata,rtapayload, 0,0);
670103423Snectar		((struct sockaddr_ll *)*sap)->sll_ifindex = nlm_index;
671103423Snectar		((struct sockaddr_ll *)*sap)->sll_hatype = ifim->ifi_type;
672103423Snectar		data += NLMSG_ALIGN(sa_len);
673103423Snectar	      }
674103423Snectar	      break;
675103423Snectar	    case IFLA_IFNAME:/* Name of Interface */
676103423Snectar	      if (!build)
677103423Snectar		nlen += NLMSG_ALIGN(rtapayload + 1);
678103423Snectar	      else{
679103423Snectar		ifa->ifa_name = ifname;
680103423Snectar		if (iflist[nlm_index] == NULL)
681103423Snectar		  iflist[nlm_index] = ifa->ifa_name;
682103423Snectar		strncpy(ifa->ifa_name, rtadata, rtapayload);
683103423Snectar		ifa->ifa_name[rtapayload] = '\0';
684103423Snectar		ifname += NLMSG_ALIGN(rtapayload + 1);
685103423Snectar	      }
686103423Snectar	      break;
687103423Snectar	    case IFLA_STATS:/* Statistics of Interface */
688103423Snectar	      if (!build)
689103423Snectar		xlen += NLMSG_ALIGN(rtapayload);
690103423Snectar	      else{
691103423Snectar		ifa->ifa_data = xdata;
692103423Snectar		memcpy(ifa->ifa_data, rtadata, rtapayload);
693103423Snectar		xdata += NLMSG_ALIGN(rtapayload);
694103423Snectar	      }
695103423Snectar	      break;
696103423Snectar	    case IFLA_UNSPEC:
697103423Snectar	      break;
698103423Snectar	    case IFLA_MTU:
699103423Snectar	      break;
700103423Snectar	    case IFLA_LINK:
701103423Snectar	      break;
702103423Snectar	    case IFLA_QDISC:
703103423Snectar	      break;
704103423Snectar	    default:
705178825Sdfr	      break;
706103423Snectar	    }
707103423Snectar	    break;
708103423Snectar	  case RTM_NEWADDR:
709103423Snectar	    if (nlm_family == AF_PACKET) break;
710103423Snectar	    switch(rta->rta_type){
711103423Snectar	    case IFA_ADDRESS:
712103423Snectar		ifamap.address = rtadata;
713103423Snectar		ifamap.address_len = rtapayload;
714103423Snectar		break;
715103423Snectar	    case IFA_LOCAL:
716103423Snectar		ifamap.local = rtadata;
717103423Snectar		ifamap.local_len = rtapayload;
718103423Snectar		break;
719103423Snectar	    case IFA_BROADCAST:
720103423Snectar		ifamap.broadcast = rtadata;
721103423Snectar		ifamap.broadcast_len = rtapayload;
722103423Snectar		break;
723103423Snectar#ifdef HAVE_IFADDRS_IFA_ANYCAST
724103423Snectar	    case IFA_ANYCAST:
725103423Snectar		ifamap.anycast = rtadata;
726103423Snectar		ifamap.anycast_len = rtapayload;
727103423Snectar		break;
728103423Snectar#endif
729103423Snectar	    case IFA_LABEL:
730103423Snectar	      if (!build)
731103423Snectar		nlen += NLMSG_ALIGN(rtapayload + 1);
732103423Snectar	      else{
733103423Snectar		ifa->ifa_name = ifname;
734103423Snectar		if (iflist[nlm_index] == NULL)
735103423Snectar		  iflist[nlm_index] = ifname;
736103423Snectar		strncpy(ifa->ifa_name, rtadata, rtapayload);
737103423Snectar		ifa->ifa_name[rtapayload] = '\0';
738103423Snectar		ifname += NLMSG_ALIGN(rtapayload + 1);
739103423Snectar	      }
740103423Snectar	      break;
741103423Snectar	    case IFA_UNSPEC:
742103423Snectar	      break;
743103423Snectar	    case IFA_CACHEINFO:
744103423Snectar	      break;
745103423Snectar	    default:
746178825Sdfr	      break;
747103423Snectar	    }
748103423Snectar	  }
749103423Snectar	}
750103423Snectar	if (nlh->nlmsg_type == RTM_NEWADDR &&
751103423Snectar	    nlm_family != AF_PACKET) {
752103423Snectar	  if (!ifamap.local) {
753103423Snectar	    ifamap.local = ifamap.address;
754103423Snectar	    ifamap.local_len = ifamap.address_len;
755103423Snectar	  }
756103423Snectar	  if (!ifamap.address) {
757103423Snectar	    ifamap.address = ifamap.local;
758103423Snectar	    ifamap.address_len = ifamap.local_len;
759103423Snectar	  }
760103423Snectar	  if (ifamap.address_len != ifamap.local_len ||
761103423Snectar	      (ifamap.address != NULL &&
762103423Snectar	       memcmp(ifamap.address, ifamap.local, ifamap.address_len))) {
763103423Snectar	    /* p2p; address is peer and local is ours */
764103423Snectar	    ifamap.broadcast = ifamap.address;
765103423Snectar	    ifamap.broadcast_len = ifamap.address_len;
766103423Snectar	    ifamap.address = ifamap.local;
767103423Snectar	    ifamap.address_len = ifamap.local_len;
768103423Snectar	  }
769103423Snectar	  if (ifamap.address) {
770103423Snectar#ifndef IFA_NETMASK
771103423Snectar	    sockaddr_size = NLMSG_ALIGN(ifa_sa_len(nlm_family,ifamap.address_len));
772103423Snectar#endif
773103423Snectar	    if (!build)
774103423Snectar	      dlen += NLMSG_ALIGN(ifa_sa_len(nlm_family,ifamap.address_len));
775103423Snectar	    else {
776103423Snectar	      ifa->ifa_addr = (struct sockaddr *)data;
777103423Snectar	      ifa_make_sockaddr(nlm_family, ifa->ifa_addr, ifamap.address, ifamap.address_len,
778103423Snectar				nlm_scope, nlm_index);
779103423Snectar	      data += NLMSG_ALIGN(ifa_sa_len(nlm_family, ifamap.address_len));
780103423Snectar	    }
781103423Snectar	  }
782103423Snectar#ifdef IFA_NETMASK
783103423Snectar	  if (ifamap.netmask) {
784103423Snectar	    if (!build)
785103423Snectar	      dlen += NLMSG_ALIGN(ifa_sa_len(nlm_family,ifamap.netmask_len));
786103423Snectar	    else {
787103423Snectar	      ifa->ifa_netmask = (struct sockaddr *)data;
788103423Snectar	      ifa_make_sockaddr(nlm_family, ifa->ifa_netmask, ifamap.netmask, ifamap.netmask_len,
789103423Snectar				nlm_scope, nlm_index);
790103423Snectar	      data += NLMSG_ALIGN(ifa_sa_len(nlm_family, ifamap.netmask_len));
791103423Snectar	    }
792103423Snectar	  }
793103423Snectar#endif
794103423Snectar	  if (ifamap.broadcast) {
795103423Snectar	    if (!build)
796103423Snectar	      dlen += NLMSG_ALIGN(ifa_sa_len(nlm_family,ifamap.broadcast_len));
797103423Snectar	    else {
798103423Snectar	      ifa->ifa_broadaddr = (struct sockaddr *)data;
799103423Snectar	      ifa_make_sockaddr(nlm_family, ifa->ifa_broadaddr, ifamap.broadcast, ifamap.broadcast_len,
800103423Snectar				nlm_scope, nlm_index);
801103423Snectar	      data += NLMSG_ALIGN(ifa_sa_len(nlm_family, ifamap.broadcast_len));
802103423Snectar	    }
803103423Snectar	  }
804103423Snectar#ifdef HAVE_IFADDRS_IFA_ANYCAST
805103423Snectar	  if (ifamap.anycast) {
806103423Snectar	    if (!build)
807103423Snectar	      dlen += NLMSG_ALIGN(ifa_sa_len(nlm_family,ifamap.anycast_len));
808103423Snectar	    else {
809103423Snectar	      ifa->ifa_anycast = (struct sockaddr *)data;
810103423Snectar	      ifa_make_sockaddr(nlm_family, ifa->ifa_anyaddr, ifamap.anycast, ifamap.anycast_len,
811103423Snectar				nlm_scope, nlm_index);
812103423Snectar	      data += NLMSG_ALIGN(ifa_sa_len(nlm_family, ifamap.anycast_len));
813103423Snectar	    }
814103423Snectar	  }
815103423Snectar#endif
816103423Snectar	}
817103423Snectar	if (!build){
818103423Snectar#ifndef IFA_NETMASK
819103423Snectar	  dlen += sockaddr_size;
820103423Snectar#endif
821103423Snectar	  icnt++;
822103423Snectar	} else {
823103423Snectar	  if (ifa->ifa_name == NULL)
824103423Snectar	    ifa->ifa_name = iflist[nlm_index];
825103423Snectar#ifndef IFA_NETMASK
826233294Sstas	  if (ifa->ifa_addr &&
827233294Sstas	      ifa->ifa_addr->sa_family != AF_UNSPEC &&
828103423Snectar	      ifa->ifa_addr->sa_family != AF_PACKET){
829103423Snectar	    ifa->ifa_netmask = (struct sockaddr *)data;
830103423Snectar	    ifa_make_sockaddr_mask(ifa->ifa_addr->sa_family, ifa->ifa_netmask, nlm_prefixlen);
831103423Snectar	  }
832103423Snectar	  data += sockaddr_size;
833103423Snectar#endif
834103423Snectar	  ifl = ifa++;
835103423Snectar	}
836103423Snectar      }
837103423Snectar    }
838103423Snectar    if (!build){
839103423Snectar      if (icnt == 0 && (dlen + nlen + xlen == 0)){
840103423Snectar	if (ifap != NULL)
841103423Snectar	  *ifap = NULL;
842103423Snectar	break; /* cannot found any addresses */
843103423Snectar      }
844103423Snectar    }
845103423Snectar    else
846103423Snectar      free_data(NULL, ifdata);
847103423Snectar  }
848103423Snectar
849103423Snectar/* ---------------------------------- */
850103423Snectar  /* Finalize */
851103423Snectar  free_nlmsglist(nlmsg_list);
852103423Snectar  nl_close(sd);
853103423Snectar  return 0;
854103423Snectar}
855103423Snectar
856233294Sstasvoid ROKEN_LIB_FUNCTION
857233294Sstasrk_freeifaddrs(struct ifaddrs *ifp)
858233294Sstas{
859233294Sstas    /* AF_NETLINK method uses a single allocation for all interfaces */
860233294Sstas    free(ifp);
861233294Sstas}
862233294Sstas
863103423Snectar#else /* !AF_NETLINK */
864103423Snectar
865103423Snectar/*
866103423Snectar * The generic SIOCGIFCONF version.
867103423Snectar */
868103423Snectar
869103423Snectarstatic int
870233294Sstasgetifaddrs2(struct ifaddrs **ifap,
87172445Sassar	    int af, int siocgifconf, int siocgifflags,
87272445Sassar	    size_t ifreq_sz)
87372445Sassar{
87472445Sassar    int ret;
87572445Sassar    int fd;
87672445Sassar    size_t buf_size;
87772445Sassar    char *buf;
87872445Sassar    struct ifconf ifconf;
87972445Sassar    char *p;
88072445Sassar    size_t sz;
88172445Sassar    struct sockaddr sa_zero;
88272445Sassar    struct ifreq *ifr;
88390926Snectar    struct ifaddrs *start = NULL, **end = &start;
88472445Sassar
88572445Sassar    buf = NULL;
88672445Sassar
88772445Sassar    memset (&sa_zero, 0, sizeof(sa_zero));
88872445Sassar    fd = socket(af, SOCK_DGRAM, 0);
88972445Sassar    if (fd < 0)
89072445Sassar	return -1;
89172445Sassar
89272445Sassar    buf_size = 8192;
89372445Sassar    for (;;) {
89472445Sassar	buf = calloc(1, buf_size);
89572445Sassar	if (buf == NULL) {
89672445Sassar	    ret = ENOMEM;
89772445Sassar	    goto error_out;
89872445Sassar	}
89972445Sassar	ifconf.ifc_len = buf_size;
90072445Sassar	ifconf.ifc_buf = buf;
90172445Sassar
90272445Sassar	/*
90372445Sassar	 * Solaris returns EINVAL when the buffer is too small.
90472445Sassar	 */
90572445Sassar	if (ioctl (fd, siocgifconf, &ifconf) < 0 && errno != EINVAL) {
90672445Sassar	    ret = errno;
90772445Sassar	    goto error_out;
90872445Sassar	}
90972445Sassar	/*
91072445Sassar	 * Can the difference between a full and a overfull buf
91172445Sassar	 * be determined?
91272445Sassar	 */
91372445Sassar
91472445Sassar	if (ifconf.ifc_len < buf_size)
91572445Sassar	    break;
91672445Sassar	free (buf);
91772445Sassar	buf_size *= 2;
91872445Sassar    }
91972445Sassar
92072445Sassar    for (p = ifconf.ifc_buf;
92172445Sassar	 p < ifconf.ifc_buf + ifconf.ifc_len;
92272445Sassar	 p += sz) {
92372445Sassar	struct ifreq ifreq;
92472445Sassar	struct sockaddr *sa;
92572445Sassar	size_t salen;
92672445Sassar
92772445Sassar	ifr = (struct ifreq *)p;
92872445Sassar	sa  = &ifr->ifr_addr;
92972445Sassar
93072445Sassar	sz = ifreq_sz;
93172445Sassar	salen = sizeof(struct sockaddr);
93272445Sassar#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
93372445Sassar	salen = sa->sa_len;
93472445Sassar	sz = max(sz, sizeof(ifr->ifr_name) + sa->sa_len);
93572445Sassar#endif
93672445Sassar#ifdef SA_LEN
93772445Sassar	salen = SA_LEN(sa);
93872445Sassar	sz = max(sz, sizeof(ifr->ifr_name) + SA_LEN(sa));
93972445Sassar#endif
94072445Sassar	memset (&ifreq, 0, sizeof(ifreq));
94172445Sassar	memcpy (ifreq.ifr_name, ifr->ifr_name, sizeof(ifr->ifr_name));
94272445Sassar
94372445Sassar	if (ioctl(fd, siocgifflags, &ifreq) < 0) {
94472445Sassar	    ret = errno;
94572445Sassar	    goto error_out;
94672445Sassar	}
94772445Sassar
94872445Sassar	*end = malloc(sizeof(**end));
94990926Snectar	if (*end == NULL) {
95090926Snectar	    ret = ENOMEM;
95190926Snectar	    goto error_out;
95290926Snectar	}
95372445Sassar
95472445Sassar	(*end)->ifa_next = NULL;
95572445Sassar	(*end)->ifa_name = strdup(ifr->ifr_name);
956178825Sdfr	if ((*end)->ifa_name == NULL) {
957178825Sdfr	    ret = ENOMEM;
958178825Sdfr	    goto error_out;
959178825Sdfr	}
96072445Sassar	(*end)->ifa_flags = ifreq.ifr_flags;
96172445Sassar	(*end)->ifa_addr = malloc(salen);
962178825Sdfr	if ((*end)->ifa_addr == NULL) {
963178825Sdfr	    ret = ENOMEM;
964178825Sdfr	    goto error_out;
965178825Sdfr	}
96672445Sassar	memcpy((*end)->ifa_addr, sa, salen);
96772445Sassar	(*end)->ifa_netmask = NULL;
96872445Sassar
96972445Sassar#if 0
97072445Sassar	/* fix these when we actually need them */
97172445Sassar	if(ifreq.ifr_flags & IFF_BROADCAST) {
97272445Sassar	    (*end)->ifa_broadaddr = malloc(sizeof(ifr->ifr_broadaddr));
973178825Sdfr	    if ((*end)->ifa_broadaddr == NULL) {
974178825Sdfr		ret = ENOMEM;
975178825Sdfr		goto error_out;
976178825Sdfr	    }
977233294Sstas	    memcpy((*end)->ifa_broadaddr, &ifr->ifr_broadaddr,
97872445Sassar		   sizeof(ifr->ifr_broadaddr));
97972445Sassar	} else if(ifreq.ifr_flags & IFF_POINTOPOINT) {
98072445Sassar	    (*end)->ifa_dstaddr = malloc(sizeof(ifr->ifr_dstaddr));
981178825Sdfr	    if ((*end)->ifa_dstaddr == NULL) {
982178825Sdfr		ret = ENOMEM;
983178825Sdfr		goto error_out;
984178825Sdfr	    }
985233294Sstas	    memcpy((*end)->ifa_dstaddr, &ifr->ifr_dstaddr,
98672445Sassar		   sizeof(ifr->ifr_dstaddr));
98772445Sassar	} else
98872445Sassar	    (*end)->ifa_dstaddr = NULL;
98972445Sassar#else
99072445Sassar	    (*end)->ifa_dstaddr = NULL;
99172445Sassar#endif
99272445Sassar
99372445Sassar	(*end)->ifa_data = NULL;
99472445Sassar
99572445Sassar	end = &(*end)->ifa_next;
996233294Sstas
99772445Sassar    }
99872445Sassar    *ifap = start;
99978527Sassar    close(fd);
100072445Sassar    free(buf);
100172445Sassar    return 0;
100272445Sassar  error_out:
1003178825Sdfr    rk_freeifaddrs(start);
100478527Sassar    close(fd);
100572445Sassar    free(buf);
100672445Sassar    errno = ret;
100772445Sassar    return -1;
100872445Sassar}
100972445Sassar
101090926Snectar#if defined(HAVE_IPV6) && defined(SIOCGLIFCONF) && defined(SIOCGLIFFLAGS)
101190926Snectarstatic int
1012233294Sstasgetlifaddrs2(struct ifaddrs **ifap,
101390926Snectar	     int af, int siocgifconf, int siocgifflags,
101490926Snectar	     size_t ifreq_sz)
101590926Snectar{
101690926Snectar    int ret;
101790926Snectar    int fd;
101890926Snectar    size_t buf_size;
101990926Snectar    char *buf;
102090926Snectar    struct lifconf ifconf;
102190926Snectar    char *p;
102290926Snectar    size_t sz;
102390926Snectar    struct sockaddr sa_zero;
102490926Snectar    struct lifreq *ifr;
102590926Snectar    struct ifaddrs *start = NULL, **end = &start;
102690926Snectar
102790926Snectar    buf = NULL;
102890926Snectar
102990926Snectar    memset (&sa_zero, 0, sizeof(sa_zero));
103090926Snectar    fd = socket(af, SOCK_DGRAM, 0);
103190926Snectar    if (fd < 0)
103290926Snectar	return -1;
103390926Snectar
103490926Snectar    buf_size = 8192;
103590926Snectar    for (;;) {
103690926Snectar	buf = calloc(1, buf_size);
103790926Snectar	if (buf == NULL) {
103890926Snectar	    ret = ENOMEM;
103990926Snectar	    goto error_out;
104090926Snectar	}
1041178825Sdfr#ifndef __hpux
1042233294Sstas	ifconf.lifc_family = af;
104390926Snectar	ifconf.lifc_flags  = 0;
1044178825Sdfr#endif
104590926Snectar	ifconf.lifc_len    = buf_size;
104690926Snectar	ifconf.lifc_buf    = buf;
104790926Snectar
104890926Snectar	/*
104990926Snectar	 * Solaris returns EINVAL when the buffer is too small.
105090926Snectar	 */
105190926Snectar	if (ioctl (fd, siocgifconf, &ifconf) < 0 && errno != EINVAL) {
105290926Snectar	    ret = errno;
105390926Snectar	    goto error_out;
105490926Snectar	}
105590926Snectar	/*
105690926Snectar	 * Can the difference between a full and a overfull buf
105790926Snectar	 * be determined?
105890926Snectar	 */
105990926Snectar
106090926Snectar	if (ifconf.lifc_len < buf_size)
106190926Snectar	    break;
106290926Snectar	free (buf);
106390926Snectar	buf_size *= 2;
106490926Snectar    }
106590926Snectar
106690926Snectar    for (p = ifconf.lifc_buf;
106790926Snectar	 p < ifconf.lifc_buf + ifconf.lifc_len;
106890926Snectar	 p += sz) {
106990926Snectar	struct lifreq ifreq;
107090926Snectar	struct sockaddr_storage *sa;
107190926Snectar	size_t salen;
107290926Snectar
107390926Snectar	ifr = (struct lifreq *)p;
107490926Snectar	sa  = &ifr->lifr_addr;
107590926Snectar
107690926Snectar	sz = ifreq_sz;
107790926Snectar	salen = sizeof(struct sockaddr_storage);
107890926Snectar#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
107990926Snectar	salen = sa->sa_len;
108090926Snectar	sz = max(sz, sizeof(ifr->ifr_name) + sa->sa_len);
108190926Snectar#endif
108290926Snectar#ifdef SA_LEN
108390926Snectar	salen = SA_LEN(sa);
108490926Snectar	sz = max(sz, sizeof(ifr->ifr_name) + SA_LEN(sa));
108590926Snectar#endif
108690926Snectar	memset (&ifreq, 0, sizeof(ifreq));
108790926Snectar	memcpy (ifreq.lifr_name, ifr->lifr_name, sizeof(ifr->lifr_name));
108890926Snectar
108990926Snectar	if (ioctl(fd, siocgifflags, &ifreq) < 0) {
109090926Snectar	    ret = errno;
109190926Snectar	    goto error_out;
109290926Snectar	}
109390926Snectar
109490926Snectar	*end = malloc(sizeof(**end));
1095178825Sdfr	if (*end == NULL) {
1096178825Sdfr	    ret = ENOMEM;
1097178825Sdfr	    goto error_out;
1098178825Sdfr	}
109990926Snectar
110090926Snectar	(*end)->ifa_next = NULL;
110190926Snectar	(*end)->ifa_name = strdup(ifr->lifr_name);
1102178825Sdfr	if ((*end)->ifa_name == NULL) {
1103178825Sdfr	    ret = ENOMEM;
1104178825Sdfr	    goto error_out;
1105178825Sdfr	}
110690926Snectar	(*end)->ifa_flags = ifreq.lifr_flags;
110790926Snectar	(*end)->ifa_addr = malloc(salen);
1108178825Sdfr	if ((*end)->ifa_addr == NULL) {
1109178825Sdfr	    ret = ENOMEM;
1110178825Sdfr	    goto error_out;
1111178825Sdfr	}
111290926Snectar	memcpy((*end)->ifa_addr, sa, salen);
111390926Snectar	(*end)->ifa_netmask = NULL;
111490926Snectar
111590926Snectar#if 0
111690926Snectar	/* fix these when we actually need them */
111790926Snectar	if(ifreq.ifr_flags & IFF_BROADCAST) {
111890926Snectar	    (*end)->ifa_broadaddr = malloc(sizeof(ifr->ifr_broadaddr));
1119178825Sdfr	    if ((*end)->ifa_broadaddr == NULL) {
1120178825Sdfr		ret = ENOMEM;
1121178825Sdfr		goto error_out;
1122178825Sdfr	    }
1123233294Sstas	    memcpy((*end)->ifa_broadaddr, &ifr->ifr_broadaddr,
112490926Snectar		   sizeof(ifr->ifr_broadaddr));
112590926Snectar	} else if(ifreq.ifr_flags & IFF_POINTOPOINT) {
112690926Snectar	    (*end)->ifa_dstaddr = malloc(sizeof(ifr->ifr_dstaddr));
1127178825Sdfr	    if ((*end)->ifa_dstaddr == NULL) {
1128178825Sdfr		ret = ENOMEM;
1129178825Sdfr		goto error_out;
1130178825Sdfr	    }
1131233294Sstas	    memcpy((*end)->ifa_dstaddr, &ifr->ifr_dstaddr,
113290926Snectar		   sizeof(ifr->ifr_dstaddr));
113390926Snectar	} else
113490926Snectar	    (*end)->ifa_dstaddr = NULL;
113590926Snectar#else
113690926Snectar	    (*end)->ifa_dstaddr = NULL;
113790926Snectar#endif
113890926Snectar
113990926Snectar	(*end)->ifa_data = NULL;
114090926Snectar
114190926Snectar	end = &(*end)->ifa_next;
1142233294Sstas
114390926Snectar    }
114490926Snectar    *ifap = start;
114590926Snectar    close(fd);
114690926Snectar    free(buf);
114790926Snectar    return 0;
114890926Snectar  error_out:
1149178825Sdfr    rk_freeifaddrs(start);
115090926Snectar    close(fd);
115190926Snectar    free(buf);
115290926Snectar    errno = ret;
115390926Snectar    return -1;
115490926Snectar}
115590926Snectar#endif /* defined(HAVE_IPV6) && defined(SIOCGLIFCONF) && defined(SIOCGLIFFLAGS) */
115690926Snectar
1157233294Sstas/**
1158233294Sstas * Join two struct ifaddrs lists by appending supp to base.
1159233294Sstas * Either may be NULL. The new list head (usually base) will be
1160233294Sstas * returned.
1161233294Sstas */
1162233294Sstasstatic struct ifaddrs *
1163233294Sstasappend_ifaddrs(struct ifaddrs *base, struct ifaddrs *supp) {
1164233294Sstas    if (!base)
1165233294Sstas	return supp;
1166233294Sstas
1167233294Sstas    if (!supp)
1168233294Sstas	return base;
1169233294Sstas
1170233294Sstas    while (base->ifa_next)
1171233294Sstas	base = base->ifa_next;
1172233294Sstas
1173233294Sstas    base->ifa_next = supp;
1174233294Sstas
1175233294Sstas    return base;
1176233294Sstas}
1177233294Sstas
1178233294SstasROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
1179233294Sstasrk_getifaddrs(struct ifaddrs **ifap)
118072445Sassar{
118172445Sassar    int ret = -1;
118272445Sassar    errno = ENXIO;
118372445Sassar#if defined(AF_INET6) && defined(SIOCGIF6CONF) && defined(SIOCGIF6FLAGS)
118472445Sassar    if (ret)
118572445Sassar	ret = getifaddrs2 (ifap, AF_INET6, SIOCGIF6CONF, SIOCGIF6FLAGS,
118672445Sassar			   sizeof(struct in6_ifreq));
118772445Sassar#endif
118890926Snectar#if defined(HAVE_IPV6) && defined(SIOCGLIFCONF) && defined(SIOCGLIFFLAGS)
1189233294Sstas    /* Do IPv6 and IPv4 queries separately then join the result.
1190233294Sstas     *
1191233294Sstas     * HP-UX only returns IPv6 addresses using SIOCGLIFCONF,
1192233294Sstas     * SIOCGIFCONF has to be used for IPv4 addresses. The result is then
1193233294Sstas     * merged.
1194233294Sstas     *
1195233294Sstas     * Solaris needs particular care, because a SIOCGLIFCONF lookup using
1196233294Sstas     * AF_UNSPEC can fail in a Zone requiring an AF_INET lookup, so we just
1197233294Sstas     * do them separately the same as for HP-UX. See
1198233294Sstas     * http://repo.or.cz/w/heimdal.git/commitdiff/76afc31e9ba2f37e64c70adc006ade9e37e9ef73
1199233294Sstas     */
1200233294Sstas    if (ret) {
1201233294Sstas	int v6err, v4err;
1202233294Sstas	struct ifaddrs *v6addrs, *v4addrs;
1203233294Sstas
1204233294Sstas	v6err = getlifaddrs2 (&v6addrs, AF_INET6, SIOCGLIFCONF, SIOCGLIFFLAGS,
120590926Snectar			    sizeof(struct lifreq));
1206233294Sstas	v4err = getifaddrs2 (&v4addrs, AF_INET, SIOCGIFCONF, SIOCGIFFLAGS,
1207233294Sstas			    sizeof(struct ifreq));
1208233294Sstas	if (v6err)
1209233294Sstas	    v6addrs = NULL;
1210233294Sstas	if (v4err)
1211233294Sstas	    v4addrs = NULL;
1212233294Sstas
1213233294Sstas	if (v6addrs) {
1214233294Sstas	    if (v4addrs)
1215233294Sstas		*ifap = append_ifaddrs(v6addrs, v4addrs);
1216233294Sstas	    else
1217233294Sstas		*ifap = v6addrs;
1218233294Sstas	} else if (v4addrs) {
1219233294Sstas	    *ifap = v4addrs;
1220233294Sstas	} else {
1221233294Sstas	    *ifap = NULL;
1222233294Sstas	}
1223233294Sstas
1224233294Sstas	ret = (v6err || v4err) ? -1 : 0;
1225233294Sstas    }
122690926Snectar#endif
122772445Sassar#if defined(HAVE_IPV6) && defined(SIOCGIFCONF)
122872445Sassar    if (ret)
122972445Sassar	ret = getifaddrs2 (ifap, AF_INET6, SIOCGIFCONF, SIOCGIFFLAGS,
123072445Sassar			   sizeof(struct ifreq));
123172445Sassar#endif
123272445Sassar#if defined(AF_INET) && defined(SIOCGIFCONF) && defined(SIOCGIFFLAGS)
123372445Sassar    if (ret)
123472445Sassar	ret = getifaddrs2 (ifap, AF_INET, SIOCGIFCONF, SIOCGIFFLAGS,
123572445Sassar			   sizeof(struct ifreq));
123672445Sassar#endif
123772445Sassar    return ret;
123872445Sassar}
123972445Sassar
1240233294SstasROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
1241178825Sdfrrk_freeifaddrs(struct ifaddrs *ifp)
124272445Sassar{
124372445Sassar    struct ifaddrs *p, *q;
1244233294Sstas
124572445Sassar    for(p = ifp; p; ) {
124672445Sassar	free(p->ifa_name);
124772445Sassar	if(p->ifa_addr)
124872445Sassar	    free(p->ifa_addr);
1249233294Sstas	if(p->ifa_dstaddr)
125072445Sassar	    free(p->ifa_dstaddr);
1251233294Sstas	if(p->ifa_netmask)
125272445Sassar	    free(p->ifa_netmask);
125372445Sassar	if(p->ifa_data)
125472445Sassar	    free(p->ifa_data);
125572445Sassar	q = p;
125672445Sassar	p = p->ifa_next;
125772445Sassar	free(q);
125872445Sassar    }
125972445Sassar}
126072445Sassar
1261233294Sstas#endif /* !AF_NETLINK */
1262233294Sstas
126372445Sassar#ifdef TEST
126472445Sassar
126572445Sassarvoid
126672445Sassarprint_addr(const char *s, struct sockaddr *sa)
126772445Sassar{
126872445Sassar    int i;
126972445Sassar    printf("  %s=%d/", s, sa->sa_family);
127072445Sassar#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
127172445Sassar    for(i = 0; i < sa->sa_len - ((long)sa->sa_data - (long)&sa->sa_family); i++)
127272445Sassar	printf("%02x", ((unsigned char*)sa->sa_data)[i]);
127372445Sassar#else
1274233294Sstas    for(i = 0; i < sizeof(sa->sa_data); i++)
127572445Sassar	printf("%02x", ((unsigned char*)sa->sa_data)[i]);
127672445Sassar#endif
127772445Sassar    printf("\n");
127872445Sassar}
127972445Sassar
1280233294Sstasvoid
128172445Sassarprint_ifaddrs(struct ifaddrs *x)
128272445Sassar{
128372445Sassar    struct ifaddrs *p;
1284233294Sstas
128572445Sassar    for(p = x; p; p = p->ifa_next) {
128672445Sassar	printf("%s\n", p->ifa_name);
128772445Sassar	printf("  flags=%x\n", p->ifa_flags);
128872445Sassar	if(p->ifa_addr)
128972445Sassar	    print_addr("addr", p->ifa_addr);
1290233294Sstas	if(p->ifa_dstaddr)
129172445Sassar	    print_addr("dstaddr", p->ifa_dstaddr);
1292233294Sstas	if(p->ifa_netmask)
129372445Sassar	    print_addr("netmask", p->ifa_netmask);
129472445Sassar	printf("  %p\n", p->ifa_data);
129572445Sassar    }
129672445Sassar}
129772445Sassar
129872445Sassarint
129972445Sassarmain()
130072445Sassar{
130172445Sassar    struct ifaddrs *a = NULL, *b;
130272445Sassar    getifaddrs2(&a, AF_INET, SIOCGIFCONF, SIOCGIFFLAGS, sizeof(struct ifreq));
130372445Sassar    print_ifaddrs(a);
130472445Sassar    printf("---\n");
130572445Sassar    getifaddrs(&b);
130672445Sassar    print_ifaddrs(b);
130772445Sassar    return 0;
130872445Sassar}
130972445Sassar#endif
1310