]> git.ipfire.org Git - thirdparty/lldpd.git/commitdiff
Provide another getifaddrs implementation, stolen from USAGI project
authorVincent Bernat <bernat@luffy.cx>
Thu, 9 Jul 2009 16:05:02 +0000 (18:05 +0200)
committerVincent Bernat <bernat@luffy.cx>
Thu, 9 Jul 2009 16:05:02 +0000 (18:05 +0200)
src/getifaddrs.c
tests/check_ifaddrs.c

index 80ecbeb1d39f56196b560526aa2ea0839e6febbb..bad0cf05da8aaeac7246fe7f0abf707f6106398b 100644 (file)
-/*
- * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
+/* Stolen from:
+   http://www.linux-ipv6.org/cvsweb/usagi/usagi/libinet6/ifaddrs.c?rev=1.17.4.2;content-type=text%2Fplain
+*/
+
+/* $USAGI: ifaddrs.c,v 1.17.4.2 2002-12-08 08:22:22 yoshfuji Exp $ */
+
+/**************************************************************************
+ * ifaddrs.c
+ * Copyright (C)2000 Hideaki YOSHIFUJI, All Rights Reserved.
  *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
  */
 
 #include "lldpd.h"
+
+/* For compatibility */
+#undef IFA_NETMASK
+#undef HAVE_IFADDRS_IFA_ANYCAST
+#undef HAVE_SOCKADDR_SA_LEN
+#define __set_errno(X) errno = (X)
+#define __close(X) close(X)
+
+#include <string.h>
+#include <time.h>
+#include <malloc.h>
+#include <errno.h>
 #include <unistd.h>
+
+#include <sys/socket.h>
+#include <asm/types.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <sys/types.h>
 #include <sys/socket.h>
-#include <sys/ioctl.h>
-
-/* This implementation uses ioctl and not netlink. This should work with many
- * earlier Linux. However, because we use an AF_INET socket, we only get IPv4
- * addresses. Since lldpd only handles IPv4 for now, this is not a
- * problem. Moreover, IPv6 + libc not having getifaddrs should be pretty
- * rare. */
-int
-getifaddrs(struct ifaddrs **ifap)
+#include <netpacket/packet.h>
+#include <net/ethernet.h>     /* the L2 protocols */
+#include <sys/uio.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <ifaddrs.h>
+#include <netinet/in.h>
+
+/* ====================================================================== */
+struct nlmsg_list{
+    struct nlmsg_list *nlm_next;
+    struct nlmsghdr *nlh;
+    int size;
+    time_t seq;
+};
+
+struct rtmaddr_ifamap {
+  void *address;
+  void *local;
+#ifdef IFA_NETMASK
+  void *netmask;
+#endif
+  void *broadcast;
+#ifdef HAVE_IFADDRS_IFA_ANYCAST
+  void *anycast;
+#endif
+  int address_len;
+  int local_len;
+#ifdef IFA_NETMASK
+  int netmask_len;
+#endif
+  int broadcast_len;
+#ifdef HAVE_IFADDRS_IFA_ANYCAST
+  int anycast_len;
+#endif
+};
+
+/* ====================================================================== */
+static size_t
+ifa_sa_len(sa_family_t family, int len)
 {
-       int sock, n, i;
-       struct ifconf ifc;
-       struct ifreq *ifr;
-       char buffer[8192];
-       struct ifaddrs *ifa = NULL, *lifa = NULL;
-
-       *ifap = NULL;
-       if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
-               return -1;
-
-       ifc.ifc_len = sizeof(buffer);
-       ifc.ifc_buf = buffer;
-       if (ioctl(sock, SIOCGIFCONF, &ifc) == -1)
-               goto fault;
-       ifr = ifc.ifc_req;
-       n = ifc.ifc_len / sizeof(struct ifreq);
-       for (i = 0; i < n; i++) {
-               if (ioctl(sock, SIOCGIFFLAGS, &ifr[i]) == -1)
-                       goto fault;
-               ifa = (struct ifaddrs*)calloc(1, sizeof(struct ifaddrs));
-               if ((ifa->ifa_name = strdup(ifr[i].ifr_name)) == NULL)
-                       goto fault;
-               ifa->ifa_flags = ifr[i].ifr_flags;
-               /* Address */
-               if (ioctl(sock, SIOCGIFADDR, &ifr[i]) != -1) {
-                       if ((ifa->ifa_addr =
-                               (struct  sockaddr *)malloc(
-                                   sizeof(struct sockaddr_storage))) == NULL)
-                               goto fault;
-                       memcpy(ifa->ifa_addr, &ifr[i].ifr_addr,
-                           sizeof(struct sockaddr_storage));
-               }
-               /* Netmask */
-               if (ioctl(sock, SIOCGIFNETMASK, &ifr[i]) != -1) {
-                       if ((ifa->ifa_netmask =
-                               (struct sockaddr *)malloc(
-                                   sizeof(struct sockaddr_storage))) == NULL)
-                               goto fault;
-                       memcpy(ifa->ifa_netmask, &ifr[i].ifr_addr,
-                           sizeof(struct sockaddr_storage));
-               }
-               /* Broadcast or point to point */
-               if (ifr[i].ifr_flags & IFF_BROADCAST) {
-                       if (ioctl(sock, SIOCGIFBRDADDR, &ifr[i]) != -1) {
-                               if ((ifa->ifa_ifu.ifu_broadaddr =
-                                       (struct sockaddr *)malloc(
-                                               sizeof(struct sockaddr_storage))) == NULL)
-                                       goto fault;
-                               memcpy(ifa->ifa_ifu.ifu_broadaddr,
-                                   &ifr[i].ifr_addr,
-                                   sizeof(struct sockaddr_storage));
-                       }
-               } else if (ifr[i].ifr_flags & IFF_POINTOPOINT) {
-                       if (ioctl(sock, SIOCGIFDSTADDR, &ifr[i]) != -1) {
-                               if ((ifa->ifa_ifu.ifu_dstaddr =
-                                       (struct sockaddr *)malloc(
-                                               sizeof(struct sockaddr_storage))) == NULL)
-                                       goto fault;
-                               memcpy(ifa->ifa_ifu.ifu_dstaddr,
-                                   &ifr[i].ifr_addr,
-                                   sizeof(struct sockaddr_storage));
-                       }
-               }
-               /* Link them together */
-               if (lifa)
-                       lifa->ifa_next = ifa;
-               else
-                       *ifap = ifa;
-               lifa = ifa;
-               ifa = NULL;
+  size_t size;
+  switch(family){
+  case AF_INET:
+    size = sizeof(struct sockaddr_in);
+    break;
+  case AF_INET6:
+    size = sizeof(struct sockaddr_in6);
+    break;
+  case AF_PACKET:
+    size = (size_t)(((struct sockaddr_ll *)NULL)->sll_addr) + len;
+    if (size < sizeof(struct sockaddr_ll))
+      size = sizeof(struct sockaddr_ll);
+    break;
+  default:
+    size = (size_t)(((struct sockaddr *)NULL)->sa_data) + len;
+    if (size < sizeof(struct sockaddr))
+      size = sizeof(struct sockaddr);
+  }
+  return size;
+}
+
+static void 
+ifa_make_sockaddr(sa_family_t family, 
+                 struct sockaddr *sa, 
+                 void *p, size_t len,
+                 uint32_t scope, uint32_t scopeid)
+{
+  if (sa == NULL) return;
+  switch(family){
+  case AF_INET:
+    memcpy(&((struct sockaddr_in*)sa)->sin_addr, (char *)p, len);
+    break;
+  case AF_INET6:
+    memcpy(&((struct sockaddr_in6*)sa)->sin6_addr, (char *)p, len);
+    if (IN6_IS_ADDR_LINKLOCAL(p) ||
+       IN6_IS_ADDR_MC_LINKLOCAL(p)){
+      ((struct sockaddr_in6*)sa)->sin6_scope_id = scopeid;
+    }
+    break;
+  case AF_PACKET:
+    memcpy(((struct sockaddr_ll*)sa)->sll_addr, (char *)p, len);
+    ((struct sockaddr_ll*)sa)->sll_halen = len;
+    break;
+  default:
+    memcpy(sa->sa_data, p, len);       /*XXX*/
+    break;
+  }
+  sa->sa_family = family;
+#ifdef HAVE_SOCKADDR_SA_LEN
+  sa->sa_len = ifa_sa_len(family, len);
+#endif
+}
+
+static struct sockaddr *
+ifa_make_sockaddr_mask(sa_family_t family, 
+                      struct sockaddr *sa, 
+                      uint32_t prefixlen)
+{
+  int i;
+  char *p = NULL, c;
+  uint32_t max_prefixlen = 0;
+
+  if (sa == NULL) return NULL;
+  switch(family){
+  case AF_INET:
+    memset(&((struct sockaddr_in*)sa)->sin_addr, 0, sizeof(((struct sockaddr_in*)sa)->sin_addr));
+    p = (char *)&((struct sockaddr_in*)sa)->sin_addr;
+    max_prefixlen = 32;
+    break;
+  case AF_INET6:
+    memset(&((struct sockaddr_in6*)sa)->sin6_addr, 0, sizeof(((struct sockaddr_in6*)sa)->sin6_addr));
+    p = (char *)&((struct sockaddr_in6*)sa)->sin6_addr;
+#if 0  /* XXX: fill scope-id? */
+    if (IN6_IS_ADDR_LINKLOCAL(p) ||
+       IN6_IS_ADDR_MC_LINKLOCAL(p)){
+      ((struct sockaddr_in6*)sa)->sin6_scope_id = scopeid;
+    }
+#endif
+    max_prefixlen = 128;
+    break;
+  default:
+    return NULL;
+  }
+  sa->sa_family = family;
+#ifdef HAVE_SOCKADDR_SA_LEN
+  sa->sa_len = ifa_sa_len(family, len);
+#endif
+  if (p){
+    if (prefixlen > max_prefixlen)
+      prefixlen = max_prefixlen;
+    for (i=0; i<(prefixlen / 8); i++)
+      *p++ = 0xff;
+    c = 0xff;
+    c <<= (8 - (prefixlen % 8));
+    *p = c;
+  }
+  return sa;
+}
+
+/* ====================================================================== */
+static int 
+nl_sendreq(int sd, int request, int flags, int *seq)
+{
+  char reqbuf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
+             NLMSG_ALIGN(sizeof(struct rtgenmsg))];
+  struct sockaddr_nl nladdr;
+  struct nlmsghdr *req_hdr;
+  struct rtgenmsg *req_msg;
+  time_t t = time(NULL);
+
+  if (seq) *seq = t;
+  memset(&reqbuf, 0, sizeof(reqbuf));
+  req_hdr = (struct nlmsghdr *)reqbuf;
+  req_msg = (struct rtgenmsg *)NLMSG_DATA(req_hdr);
+  req_hdr->nlmsg_len = NLMSG_LENGTH(sizeof(*req_msg));
+  req_hdr->nlmsg_type = request;
+  req_hdr->nlmsg_flags = flags | NLM_F_REQUEST;
+  req_hdr->nlmsg_pid = 0;
+  req_hdr->nlmsg_seq = t;
+  req_msg->rtgen_family = AF_UNSPEC;
+  memset(&nladdr, 0, sizeof(nladdr));
+  nladdr.nl_family = AF_NETLINK;
+  return (sendto(sd, (void *)req_hdr, req_hdr->nlmsg_len, 0,
+                (struct sockaddr *)&nladdr, sizeof(nladdr)));
+}
+
+static int 
+nl_recvmsg(int sd, int request, int seq, 
+          void *buf, size_t buflen, 
+          int *flags)
+{
+  struct msghdr msg;
+  struct iovec iov = { buf, buflen };
+  struct sockaddr_nl nladdr;
+  int read_len;
+
+  for (;;){
+    msg.msg_name = (void *)&nladdr;
+    msg.msg_namelen = sizeof(nladdr);
+    msg.msg_iov = &iov;
+    msg.msg_iovlen = 1;
+    msg.msg_control = NULL;
+    msg.msg_controllen = 0;
+    msg.msg_flags = 0;
+    read_len = recvmsg(sd, &msg, 0);
+    if ((read_len < 0 && errno == EINTR) || (msg.msg_flags & MSG_TRUNC))
+      continue;
+    if (flags) *flags = msg.msg_flags;
+    break;
+  }
+  return read_len;
+}
+
+static int 
+nl_getmsg(int sd, int request, int seq, 
+         struct nlmsghdr **nlhp,
+         int *done)
+{
+  struct nlmsghdr *nh;
+  size_t bufsize = 65536, lastbufsize = 0;
+  void *buff = NULL;
+  int result = 0, read_size;
+  int msg_flags;
+  pid_t pid = getpid();
+  for (;;){
+    void *newbuff = realloc(buff, bufsize);
+    if (newbuff == NULL || bufsize < lastbufsize) {
+      result = -1;
+      break;
+    }
+    buff = newbuff;
+    result = read_size = nl_recvmsg(sd, request, seq, buff, bufsize, &msg_flags);
+    if (read_size < 0 || (msg_flags & MSG_TRUNC)){
+      lastbufsize = bufsize;
+      bufsize *= 2;
+      continue;
+    }
+    if (read_size == 0) break;
+    nh = (struct nlmsghdr *)buff;
+    for (nh = (struct nlmsghdr *)buff;
+        NLMSG_OK(nh, read_size);
+        nh = (struct nlmsghdr *)NLMSG_NEXT(nh, read_size)){
+      if (nh->nlmsg_pid != pid ||
+         nh->nlmsg_seq != seq)
+       continue;
+      if (nh->nlmsg_type == NLMSG_DONE){
+       (*done)++;
+       break; /* ok */
+      }
+      if (nh->nlmsg_type == NLMSG_ERROR){
+       struct nlmsgerr *nlerr = (struct nlmsgerr *)NLMSG_DATA(nh);
+       result = -1;
+       if (nh->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr)))
+         __set_errno(EIO);
+       else
+         __set_errno(-nlerr->error);
+       break;
+      }
+    }
+    break;
+  }
+  if (result < 0)
+    if (buff){
+      int saved_errno = errno;
+      free(buff);
+      __set_errno(saved_errno);
+    }
+  *nlhp = (struct nlmsghdr *)buff;
+  return result;
+}
+
+static int
+nl_getlist(int sd, int seq,
+          int request,
+          struct nlmsg_list **nlm_list,
+          struct nlmsg_list **nlm_end)
+{
+  struct nlmsghdr *nlh = NULL;
+  int status;
+  int done = 0;
+
+  status = nl_sendreq(sd, request, NLM_F_ROOT|NLM_F_MATCH, &seq);
+  if (status < 0)
+    return status;
+  if (seq == 0)
+    seq = (int)time(NULL);
+  while(!done){
+    status = nl_getmsg(sd, request, seq, &nlh, &done);
+    if (status < 0)
+      return status;
+    if (nlh){
+      struct nlmsg_list *nlm_next = (struct nlmsg_list *)malloc(sizeof(struct nlmsg_list));
+      if (nlm_next == NULL){
+       int saved_errno = errno;
+       free(nlh);
+       __set_errno(saved_errno);
+       status = -1;
+      } else {
+       nlm_next->nlm_next = NULL;
+       nlm_next->nlh = (struct nlmsghdr *)nlh;
+       nlm_next->size = status;
+       nlm_next->seq = seq;
+       if (*nlm_list == NULL){
+         *nlm_list = nlm_next;
+         *nlm_end = nlm_next;
+       } else {
+         (*nlm_end)->nlm_next = nlm_next;
+         *nlm_end = nlm_next;
        }
-       return 0;
-fault:
-       freeifaddrs(ifa);       /* It is not linked at anything if not NULL */
-       freeifaddrs(*ifap);
-       close(sock);
-       return -1;
+      }
+    }
+  }
+  return status >= 0 ? seq : status;
 }
 
-void
-freeifaddrs(struct ifaddrs *ifa)
+/* ---------------------------------------------------------------------- */
+static void 
+free_nlmsglist(struct nlmsg_list *nlm0)
 {
-       struct ifaddrs *pifa;
-       while (ifa) {
-               pifa = ifa;
-               ifa = ifa->ifa_next;
-               free(pifa->ifa_name);
-               free(pifa->ifa_netmask);
-               free(pifa->ifa_addr);
-               if (pifa->ifa_flags & IFF_BROADCAST)
-                       free(pifa->ifa_ifu.ifu_broadaddr);
-               else if (pifa->ifa_flags & IFF_POINTOPOINT)
-                       free(pifa->ifa_ifu.ifu_dstaddr);
-               free(pifa->ifa_data);
-               free(pifa);
+  struct nlmsg_list *nlm, *nlm_next;
+  int saved_errno;
+  if (!nlm0)
+    return;
+  saved_errno = errno;
+  nlm = nlm0;
+  while (nlm)
+  {
+    if (nlm->nlh)
+      free(nlm->nlh);
+    nlm_next = nlm->nlm_next;
+    free(nlm);
+    nlm = nlm_next;
+  }
+  __set_errno(saved_errno);
+}
+
+static void 
+free_data(void *data, void *ifdata)
+{
+  int saved_errno = errno;
+  if (data != NULL) free(data);
+  if (ifdata != NULL) free(ifdata);
+  __set_errno(saved_errno);
+}
+
+/* ---------------------------------------------------------------------- */
+static void 
+nl_close(int sd)
+{
+  int saved_errno = errno;
+  if (sd >= 0) __close(sd);
+  __set_errno(saved_errno);
+}
+
+/* ---------------------------------------------------------------------- */
+static int 
+nl_open(void)
+{
+  struct sockaddr_nl nladdr;
+  int sd;
+
+  sd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+  if (sd < 0) return -1;
+  memset(&nladdr, 0, sizeof(nladdr));
+  nladdr.nl_family = AF_NETLINK;
+  if (bind(sd, (struct sockaddr*)&nladdr, sizeof(nladdr)) < 0){
+    nl_close(sd);
+    return -1;
+  }
+  return sd;
+}
+
+/* ====================================================================== */
+int getifaddrs(struct ifaddrs **ifap)
+{
+  int sd;
+  struct nlmsg_list *nlmsg_list, *nlmsg_end, *nlm;
+  /* - - - - - - - - - - - - - - - */
+  int icnt;
+  size_t dlen, xlen, nlen;
+  uint32_t max_ifindex = 0;
+
+  pid_t pid = getpid();
+  int seq;
+  int result;
+  int build     ; /* 0 or 1 */
+
+/* ---------------------------------- */
+  /* initialize */
+  icnt = dlen = xlen = nlen = 0;
+  nlmsg_list = nlmsg_end = NULL;
+
+  if (ifap)
+    *ifap = NULL;
+
+/* ---------------------------------- */
+  /* open socket and bind */
+  sd = nl_open();
+  if (sd < 0)
+    return -1;
+
+/* ---------------------------------- */
+   /* gather info */
+  if ((seq = nl_getlist(sd, 0, RTM_GETLINK,
+                       &nlmsg_list, &nlmsg_end)) < 0){
+    free_nlmsglist(nlmsg_list);
+    nl_close(sd);
+    return -1;
+  }
+  if ((seq = nl_getlist(sd, seq+1, RTM_GETADDR,
+                       &nlmsg_list, &nlmsg_end)) < 0){
+    free_nlmsglist(nlmsg_list);
+    nl_close(sd);
+    return -1;
+  }
+
+/* ---------------------------------- */
+  /* Estimate size of result buffer and fill it */
+  for (build=0; build<=1; build++){
+    struct ifaddrs *ifl = NULL, *ifa = NULL;
+    struct nlmsghdr *nlh, *nlh0;
+    void *data = NULL, *xdata = NULL, *ifdata = NULL;
+    char *ifname = NULL, **iflist = NULL;
+    uint16_t *ifflist = NULL;
+    struct rtmaddr_ifamap ifamap;
+
+    if (build){
+      ifa = data = calloc(1,
+                         NLMSG_ALIGN(sizeof(struct ifaddrs[icnt]))
+                         + dlen + xlen + nlen);
+      ifdata = calloc(1, 
+                     NLMSG_ALIGN(sizeof(char *[max_ifindex+1]))
+                     + NLMSG_ALIGN(sizeof(uint16_t [max_ifindex+1])));
+      if (ifap != NULL)
+       *ifap = (ifdata != NULL) ? ifa : NULL;
+      else{
+       free_data(data, ifdata);
+       result = 0;
+       break;
+      }
+      if (data == NULL || ifdata == NULL){
+       free_data(data, ifdata);
+       result = -1;
+       break;
+      }
+      ifl = NULL;
+      data += NLMSG_ALIGN(sizeof(struct ifaddrs)) * icnt;
+      xdata = data + dlen;
+      ifname = xdata + xlen;
+      iflist = ifdata;
+      ifflist = ((void *)iflist) + NLMSG_ALIGN(sizeof(char *[max_ifindex+1]));
+    }
+
+    for (nlm=nlmsg_list; nlm; nlm=nlm->nlm_next){
+      int nlmlen = nlm->size;
+      if (!(nlh0 = nlm->nlh))
+       continue;
+      for (nlh = nlh0; 
+          NLMSG_OK(nlh, nlmlen); 
+          nlh=NLMSG_NEXT(nlh,nlmlen)){
+       struct ifinfomsg *ifim = NULL;
+       struct ifaddrmsg *ifam = NULL;
+       struct rtattr *rta;
+
+       size_t nlm_struct_size = 0;
+       sa_family_t nlm_family = 0;
+       uint32_t nlm_scope = 0, nlm_index = 0;
+#ifndef IFA_NETMASK
+       size_t sockaddr_size = 0;
+       uint32_t nlm_prefixlen = 0;
+#endif
+       size_t rtasize;
+
+       memset(&ifamap, 0, sizeof(ifamap));
+
+       /* check if the message is what we want */
+       if (nlh->nlmsg_pid != pid ||
+           nlh->nlmsg_seq != nlm->seq)
+         continue;
+       if (nlh->nlmsg_type == NLMSG_DONE){
+         break; /* ok */
        }
+       switch (nlh->nlmsg_type){
+       case RTM_NEWLINK:
+         ifim = (struct ifinfomsg *)NLMSG_DATA(nlh);
+         nlm_struct_size = sizeof(*ifim);
+         nlm_family = ifim->ifi_family;
+         nlm_scope = 0;
+         nlm_index = ifim->ifi_index;
+         nlm_prefixlen = 0;
+         if (build)
+           ifflist[nlm_index] = ifa->ifa_flags = ifim->ifi_flags;
+         break;
+       case RTM_NEWADDR:
+         ifam = (struct ifaddrmsg *)NLMSG_DATA(nlh);
+         nlm_struct_size = sizeof(*ifam);
+         nlm_family = ifam->ifa_family;
+         nlm_scope = ifam->ifa_scope;
+         nlm_index = ifam->ifa_index;
+         nlm_prefixlen = ifam->ifa_prefixlen;
+         if (build)
+           ifa->ifa_flags = ifflist[nlm_index];
+         break;
+       default:
+         continue;
+       }
+       
+       if (!build){
+         if (max_ifindex < nlm_index)
+           max_ifindex = nlm_index;
+       } else {
+         if (ifl != NULL)
+           ifl->ifa_next = ifa;
+       }
+
+       rtasize = NLMSG_PAYLOAD(nlh, nlmlen) - NLMSG_ALIGN(nlm_struct_size);
+       for (rta = (struct rtattr *)(((char *)NLMSG_DATA(nlh)) + NLMSG_ALIGN(nlm_struct_size));
+            RTA_OK(rta, rtasize);
+            rta = RTA_NEXT(rta, rtasize)){
+         struct sockaddr **sap = NULL;
+         void *rtadata = RTA_DATA(rta);
+         size_t rtapayload = RTA_PAYLOAD(rta);
+         socklen_t sa_len;
+
+         switch(nlh->nlmsg_type){
+         case RTM_NEWLINK:
+           switch(rta->rta_type){
+           case IFLA_ADDRESS:
+           case IFLA_BROADCAST:
+             if (build){
+               sap = (rta->rta_type == IFLA_ADDRESS) ? &ifa->ifa_addr : &ifa->ifa_broadaddr;
+               *sap = (struct sockaddr *)data;
+             }
+             sa_len = ifa_sa_len(AF_PACKET, rtapayload);
+             if (rta->rta_type == IFLA_ADDRESS)
+               sockaddr_size = NLMSG_ALIGN(sa_len);
+             if (!build){
+               dlen += NLMSG_ALIGN(sa_len);
+             } else {
+               memset(*sap, 0, sa_len);
+               ifa_make_sockaddr(AF_PACKET, *sap, rtadata,rtapayload, 0,0);
+               ((struct sockaddr_ll *)*sap)->sll_ifindex = nlm_index;
+               ((struct sockaddr_ll *)*sap)->sll_hatype = ifim->ifi_type;
+               data += NLMSG_ALIGN(sa_len);
+             }
+             break;
+           case IFLA_IFNAME:/* Name of Interface */
+             if (!build)
+               nlen += NLMSG_ALIGN(rtapayload + 1);
+             else{
+               ifa->ifa_name = ifname;
+               if (iflist[nlm_index] == NULL)
+                 iflist[nlm_index] = ifa->ifa_name;
+               strncpy(ifa->ifa_name, rtadata, rtapayload);
+               ifa->ifa_name[rtapayload] = '\0';
+               ifname += NLMSG_ALIGN(rtapayload + 1);
+             }
+             break;
+           case IFLA_STATS:/* Statistics of Interface */
+             if (!build)
+               xlen += NLMSG_ALIGN(rtapayload);
+             else{
+               ifa->ifa_data = xdata;
+               memcpy(ifa->ifa_data, rtadata, rtapayload);
+               xdata += NLMSG_ALIGN(rtapayload);
+             }
+             break;
+           case IFLA_UNSPEC:
+             break;
+           case IFLA_MTU:
+             break;
+           case IFLA_LINK:
+             break;
+           case IFLA_QDISC:
+             break;
+           }
+           break;
+         case RTM_NEWADDR:
+           if (nlm_family == AF_PACKET) break;
+           switch(rta->rta_type){
+           case IFA_ADDRESS:
+               ifamap.address = rtadata;
+               ifamap.address_len = rtapayload;
+               break;
+           case IFA_LOCAL:
+               ifamap.local = rtadata;
+               ifamap.local_len = rtapayload;
+               break;
+           case IFA_BROADCAST:
+               ifamap.broadcast = rtadata;
+               ifamap.broadcast_len = rtapayload;
+               break;
+#ifdef HAVE_IFADDRS_IFA_ANYCAST
+           case IFA_ANYCAST:
+               ifamap.anycast = rtadata;
+               ifamap.anycast_len = rtapayload;
+               break;
+#endif
+           case IFA_LABEL:
+             if (!build)
+               nlen += NLMSG_ALIGN(rtapayload + 1);
+             else{
+               ifa->ifa_name = ifname;
+               if (iflist[nlm_index] == NULL)
+                 iflist[nlm_index] = ifname;
+               strncpy(ifa->ifa_name, rtadata, rtapayload);
+               ifa->ifa_name[rtapayload] = '\0';
+               ifname += NLMSG_ALIGN(rtapayload + 1);
+             }
+             break;
+           case IFA_UNSPEC:
+             break;
+           case IFA_CACHEINFO:
+             break;
+           }
+         }
+       }
+       if (nlh->nlmsg_type == RTM_NEWADDR &&
+           nlm_family != AF_PACKET) {
+         if (!ifamap.local) {
+           ifamap.local = ifamap.address;
+           ifamap.local_len = ifamap.address_len;
+         }
+         if (!ifamap.address) {
+           ifamap.address = ifamap.local;
+           ifamap.address_len = ifamap.local_len;
+         }
+         if (ifamap.address_len != ifamap.local_len ||
+             (ifamap.address != NULL &&
+              memcmp(ifamap.address, ifamap.local, ifamap.address_len))) {
+           /* p2p; address is peer and local is ours */
+           ifamap.broadcast = ifamap.address;
+           ifamap.broadcast_len = ifamap.address_len;
+           ifamap.address = ifamap.local;
+           ifamap.address_len = ifamap.local_len;
+         }
+         if (ifamap.address) {
+#ifndef IFA_NETMASK
+           sockaddr_size = NLMSG_ALIGN(ifa_sa_len(nlm_family,ifamap.address_len));
+#endif
+           if (!build)
+             dlen += NLMSG_ALIGN(ifa_sa_len(nlm_family,ifamap.address_len));
+           else {
+             ifa->ifa_addr = (struct sockaddr *)data;
+             ifa_make_sockaddr(nlm_family, ifa->ifa_addr, ifamap.address, ifamap.address_len,
+                               nlm_scope, nlm_index);
+             data += NLMSG_ALIGN(ifa_sa_len(nlm_family, ifamap.address_len));
+           }
+         }
+#ifdef IFA_NETMASK
+         if (ifamap.netmask) {
+           if (!build)
+             dlen += NLMSG_ALIGN(ifa_sa_len(nlm_family,ifamap.netmask_len));
+           else {
+             ifa->ifa_netmask = (struct sockaddr *)data;
+             ifa_make_sockaddr(nlm_family, ifa->ifa_netmask, ifamap.netmask, ifamap.netmask_len,
+                               nlm_scope, nlm_index);
+             data += NLMSG_ALIGN(ifa_sa_len(nlm_family, ifamap.netmask_len));
+           }
+         }
+#endif
+         if (ifamap.broadcast) {
+           if (!build)
+             dlen += NLMSG_ALIGN(ifa_sa_len(nlm_family,ifamap.broadcast_len));
+           else {
+             ifa->ifa_broadaddr = (struct sockaddr *)data;
+             ifa_make_sockaddr(nlm_family, ifa->ifa_broadaddr, ifamap.broadcast, ifamap.broadcast_len,
+                               nlm_scope, nlm_index);
+             data += NLMSG_ALIGN(ifa_sa_len(nlm_family, ifamap.broadcast_len));
+           }
+         }
+#ifdef HAVE_IFADDRS_IFA_ANYCAST
+         if (ifamap.anycast) {
+           if (!build)
+             dlen += NLMSG_ALIGN(ifa_sa_len(nlm_family,ifamap.anycast_len));
+           else {
+             ifa->ifa_anycast = (struct sockaddr *)data;
+             ifa_make_sockaddr(nlm_family, ifa->ifa_anyaddr, ifamap.anycast, ifamap.anycast_len,
+                               nlm_scope, nlm_index);
+             data += NLMSG_ALIGN(ifa_sa_len(nlm_family, ifamap.anycast_len));
+           }
+         }
+#endif
+       }
+       if (!build){
+#ifndef IFA_NETMASK
+         dlen += sockaddr_size;
+#endif
+         icnt++;
+       } else {
+         if (ifa->ifa_name == NULL)
+           ifa->ifa_name = iflist[nlm_index];
+#ifndef IFA_NETMASK
+         if (ifa->ifa_addr && 
+             ifa->ifa_addr->sa_family != AF_UNSPEC && 
+             ifa->ifa_addr->sa_family != AF_PACKET){
+           ifa->ifa_netmask = (struct sockaddr *)data;
+           ifa_make_sockaddr_mask(ifa->ifa_addr->sa_family, ifa->ifa_netmask, nlm_prefixlen);
+         }
+         data += sockaddr_size;
+#endif
+         ifl = ifa++;
+       }
+      }
+    }
+    if (!build){
+      if (icnt == 0 && (dlen + nlen + xlen == 0)){
+       if (ifap != NULL)
+         *ifap = NULL;
+       break; /* cannot found any addresses */
+      }
+    }
+    else
+      free_data(NULL, ifdata);
+  }
+
+/* ---------------------------------- */
+  /* Finalize */
+  free_nlmsglist(nlmsg_list);
+  nl_close(sd);
+  return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+void 
+freeifaddrs(struct ifaddrs *ifa)
+{
+  free(ifa);
 }
index eb9feb74991564e5c7c78f1e842967ec91c536a4..47f6b841240208bd2b51cb445f9980186e3925f5 100644 (file)
 static const char *
 addr_string (struct sockaddr *sa) {
        static char buf[64];
+       const char *res;
        if (sa == NULL)
-               return "<0000>";
+               return "NULL";
        switch (sa->sa_family) {
        case AF_INET:
-               return inet_ntop(AF_INET,
+               res = inet_ntop(AF_INET,
                    &((struct sockaddr_in *)sa)->sin_addr,
                    buf, sizeof(buf));
+               break;
        case AF_INET6:
-               return "<ipv6>";
+               res = inet_ntop(AF_INET6,
+                   &((struct sockaddr_in6 *)sa)->sin6_addr,
+                   buf, sizeof(buf));
+               break;
        case AF_UNSPEC:
-               return "<---->";
+               return "<--->";
        case AF_PACKET:
-               return "<pckt>";
+               return "<pkt>";
        default:
                snprintf(buf, 64, "<%4d>", sa->sa_family);
+               return buf;
        }
+       strcpy(buf, res);
+       if (strlen(buf) > 26)
+               memcpy(buf + 21, "[...]", strlen("[...]") + 1);
        return buf;
 }
 
@@ -44,15 +53,15 @@ START_TEST (test_ifaddrs)
                return;
        }
        fprintf(dump,
-           "Name           Flags   Address         Netmask         Broadcast/Destination\n");
+           "Name           Flags    Address                    Netmask                    Broadcast/Destination\n");
        for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
-               fprintf(dump, "%-15s%#.4x  ",
+               fprintf(dump, "%-15s%#.5x  ",
                    ifa->ifa_name, ifa->ifa_flags);
-               fprintf(dump, "%-15s ",
+               fprintf(dump, "%-26s ",
                    addr_string(ifa->ifa_addr));
-               fprintf(dump, "%-15s ",
+               fprintf(dump, "%-26s ",
                    addr_string(ifa->ifa_netmask));
-               fprintf(dump, "%-15s\n",
+               fprintf(dump, "%-26s\n",
                    addr_string(ifa->ifa_broadaddr));
        }
        fclose(dump);