]> git.ipfire.org Git - thirdparty/lldpd.git/commitdiff
interfaces: switch to Netlink to gather interface-related information
authorVincent Bernat <bernat@luffy.cx>
Thu, 20 Dec 2012 07:28:29 +0000 (08:28 +0100)
committerVincent Bernat <bernat@luffy.cx>
Sat, 22 Dec 2012 19:25:59 +0000 (20:25 +0100)
Instead of using getifaddrs() which gives incomplete information, we
use Netlink which is supported since a long time. Many information are
supported by Netlink: bonding, bridging, VLAN... Unfortunately, the
degree of support depends on the version of the kernel:

 - IFLA_VLAN_* stuff is available since 2.6.23 only.
 - IFLA_MASTER seems to be supported since ages (at least
   2.4.32). However, it does not allow to differentiate a bridge and a
   bonding. It does not apply to a VLAN.
 - IFLA_LINK is not sent for a VLAN before 2.6.23.
 - IFLA_BRPORT_* has just landed in Linux.
 - IFLA_LINKINFO and IFLA_INFO_KIND which could help detect the kind
   of device are only available since 2.6.23.

So, in summary, we can't reliably use Netlink to gather VLAN, bridging
and bonding information if we want to support 2.6.18 kernels. When
2.6.23 will be the minimal version, we could do some interesting
things. Also, it seems that ethtool stuff is not available through
Netlink.

This commit also happens to prepare the support of additional OS. Only
interfaces-linux.c (and netlink.c) are now Linux specific. Adding
support for KFreeBSD should be easy enough. More code factorisation
will happen then.

19 files changed:
NEWS
README.md
configure.ac
m4/os.m4 [new file with mode: 0644]
src/compat/compat.h
src/compat/getifaddrs.c [deleted file]
src/daemon/Makefile.am
src/daemon/dmi.c
src/daemon/interfaces-linux.c [moved from src/daemon/interfaces.c with 75% similarity]
src/daemon/lldp.c
src/daemon/lldpd.8
src/daemon/lldpd.c
src/daemon/lldpd.h
src/daemon/netlink.c [new file with mode: 0644]
src/daemon/priv.c
src/lldpd-structs.c
src/lldpd-structs.h
tests/Makefile.am
tests/check_ifaddrs.c [deleted file]

diff --git a/NEWS b/NEWS
index a58772448a50058e3403db81b77c13cbd05d84b3..f6561c75f3e4b720df2ea4dbb26e59a1120df077 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -4,6 +4,7 @@ lldpd (0.6.2)
       first enabled protocol will be used when no neighbor is detected.
     + Allow to filter debug logs using tokens. Add more debug logs.
     + lldpctl can now output JSON.
+    + Use netlink to gather interface information.
 
 lldpd (0.6.1)
   * Features:
index 152a47e339987950c67ebf4684fec1deceedce52..68db0c1effe78ce871de9ddf81e96444b08e8e15 100644 (file)
--- a/README.md
+++ b/README.md
@@ -126,16 +126,10 @@ To embed lldpd into an existing system, there are two point of entries:
 
  1. If your system does not use standard Linux interface, you can
     support additional interfaces by implementing the appropriate
-    `struct lldpd_ops`. You can look at `src/daemon/interfaces.c` for
-    examples. You also need to implement an interface handler which is
-    a function that will take care of some interface (including the
-    regular ones if they need special treatment). This handler will be
-    responsible to instantiate the appropriate `struct lldpd_hardware`
-    structure and associate it to the appropriate `struct
-    lldpd_ops`. Again, there are examples in
-    `src/daemon/interfaces.c`. The handler should be added to the list
-    of handlers in `src/daemon/lldpd.c` in
-    `lldpd_update_localports()`.
+    `struct lldpd_ops`. You can look at
+    `src/daemon/interfaces-linux.c` for examples. Also, have a look at
+    `interfaces_update()` which is responsible for discovering and
+    registering interfaces.
 
  2. `lldpctl` provides a convenient way to query `lldpd`. It also
     comes with various outputs, including XML which allows one to
index 79875ac768352e326fbe8d12b032cb696c3fd1df..442bddb2f698f0669799704638e684b2cea0e4b9 100644 (file)
@@ -70,6 +70,9 @@ AX_CFLAGS_GCC_OPTION([-Wno-sign-compare]) dnl Should be fixed later
 AX_LDFLAGS_OPTION([-Wl,-z,relro])
 AX_LDFLAGS_OPTION([-Wl,-z,now])
 
+# OS
+lldp_CHECK_OS
+
 AC_CACHE_SAVE
 
 # Checks for header files.
@@ -131,7 +134,6 @@ AC_CONFIG_LIBOBJ_DIR([src/compat])
 AC_FUNC_MALLOC
 AC_FUNC_REALLOC
 AC_REPLACE_FUNCS([strlcpy])
-AC_REPLACE_FUNCS([getifaddrs])
 AC_CACHE_SAVE
 
 ## Unit tests wich check
diff --git a/m4/os.m4 b/m4/os.m4
new file mode 100644 (file)
index 0000000..d40027b
--- /dev/null
+++ b/m4/os.m4
@@ -0,0 +1,27 @@
+#
+# lldp_CHECK_OS
+#
+# List of supported OS.
+#
+AC_DEFUN([lldp_DEFINE_OS], [dnl
+  case $host_os in
+    $1)
+      os="$2"
+      AC_DEFINE_UNQUOTED(HOST_OS_$3, 1, [Host operating system is $2])
+      ;;
+  esac
+  AM_CONDITIONAL(HOST_OS_$3, test x$os = x$2)dnl
+])
+
+AC_DEFUN([lldp_CHECK_OS], [
+  AC_CANONICAL_HOST
+  AC_MSG_CHECKING([if host OS is supported])
+
+  lldp_DEFINE_OS(linux*, Linux, LINUX)
+
+  if test x$os = x; then
+     AC_MSG_RESULT(no)
+     AC_MSG_ERROR([*** unsupported OS $host_os])
+  fi
+  AC_MSG_RESULT([yes ($os)])
+])
index 957f3992af33df1685ccbe888e77dd9850f7e101..3c1ec40aa94be7880a1e645d072adf6214835a3b 100644 (file)
 #define GET_VLAN_VID_CMD (GET_VLAN_REALDEV_NAME_CMD + 1)
 #endif
 
-#if !HAVE_GETIFADDRS
-struct ifaddrs {
-       struct ifaddrs  *ifa_next;    /* Next item in list */
-       char            *ifa_name;    /* Name of interface */
-       unsigned int     ifa_flags;   /* Flags from SIOCGIFFLAGS */
-       struct sockaddr *ifa_addr;    /* Address of interface */
-       struct sockaddr *ifa_netmask; /* Netmask of interface */
-       /* At most one of the following two is valid.  If the IFF_BROADCAST
-          bit is set in `ifa_flags', then `ifa_broadaddr' is valid.  If the
-          IFF_POINTOPOINT bit is set, then `ifa_dstaddr' is valid.
-          It is never the case that both these bits are set at once.  */
-       union {
-               struct sockaddr *ifu_broadaddr;
-               /* Broadcast address of interface */
-               struct sockaddr *ifu_dstaddr;
-               /* Point-to-point destination address */
-       } ifa_ifu;
-# ifndef ifa_broadaddr
-#  define              ifa_broadaddr ifa_ifu.ifu_broadaddr
-# endif
-# ifndef ifa_dstaddr
-#  define              ifa_dstaddr   ifa_ifu.ifu_dstaddr
-# endif
-       void            *ifa_data;    /* Address-specific data */
-};
-
-int getifaddrs(struct ifaddrs **ifap);
-void freeifaddrs(struct ifaddrs *ifa);
-#endif
-
 #if !HAVE_STRLCPY
 size_t strlcpy(char *, const char *, size_t);
 #endif
diff --git a/src/compat/getifaddrs.c b/src/compat/getifaddrs.c
deleted file mode 100644 (file)
index b2fea1e..0000000
+++ /dev/null
@@ -1,763 +0,0 @@
-/* -*- mode: c; c-file-style: "openbsd" -*- */
-/* 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.
- *
- * 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 "compat.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 <netpacket/packet.h>
-#include <net/ethernet.h>     /* the L2 protocols */
-#include <sys/uio.h>
-#include <net/if.h>
-#include <net/if_arp.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)
-{
-  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 status >= 0 ? seq : status;
-}
-
-/* ---------------------------------------------------------------------- */
-static void 
-free_nlmsglist(struct nlmsg_list *nlm0)
-{
-  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 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);
-       break;
-      }
-      if (data == NULL || ifdata == NULL){
-       free_data(data, ifdata);
-       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 75b44cf27931d7a104c53f23ce3a4af53b696ac2..022ac5e868a14821694e348da26044f6b85a5138 100644 (file)
@@ -10,13 +10,21 @@ liblldpd_la_SOURCES  = \
        cdp.c cdp.h \
        sonmp.c sonmp.h \
        edp.c edp.h \
-       interfaces.c client.c priv.c privsep_fdpass.c dmi.c \
+       client.c \
+       priv.c privsep_fdpass.c \
+       dmi.c \
        event.c lldpd.c
 liblldpd_la_CFLAGS   = @LIBEVENT_CFLAGS@
 liblldpd_la_LIBADD   = \
        $(top_builddir)/src/libcommon-daemon-client.la \
        $(top_builddir)/src/libcommon-daemon-lib.la @LIBEVENT_LIBS@
 
+if HOST_OS_LINUX
+liblldpd_la_SOURCES += \
+       interfaces-linux.c \
+       netlink.c
+endif
+
 # Add SNMP support if needed
 if USE_SNMP
 liblldpd_la_SOURCES += agent.c agent_priv.c agent.h
index 54e6b583a5dc4d71a14d4010d6bf804c3de6385c..b8d242f3673d1f1f7b72eb4e77040f258412cbc9 100644 (file)
@@ -15,7 +15,6 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#define INCLUDE_LINUX_IF_H
 #include "lldpd.h"
 #include <unistd.h>
 
@@ -30,7 +29,7 @@
            - asset: /sys/class/dmi/id/chassis_asset_tag
        */
 
-#if __i386__ || __amd64__
+#ifdef HOST_OS_LINUX
 char*
 dmi_get(char *file)
 {
@@ -56,6 +55,14 @@ dmi_get(char *file)
                return strdup(buffer);
        return NULL;
 }
+#else
+char *
+dmi_get(char *file)
+{
+       return NULL;
+}
+#endif
+
 
 char*
 dmi_hw()
@@ -93,4 +100,3 @@ dmi_asset()
        return dmi_get(SYSFS_CLASS_DMI "chassis_asset_tag");
 }
 #endif
-#endif
similarity index 75%
rename from src/daemon/interfaces.c
rename to src/daemon/interfaces-linux.c
index 7978d5260bdfc60c0ce53085ac84950e71e17193..6c99d8778d2ea12e0b522922e953bf0d70eb14ec 100644 (file)
@@ -15,7 +15,6 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#define INCLUDE_LINUX_IF_H
 #include "lldpd.h"
 
 #include <stdio.h>
@@ -37,6 +36,7 @@
 #include <linux/sockios.h>
 #include <linux/filter.h>
 #include <linux/if_packet.h>
+#include <linux/ethtool.h>
 
 #define SYSFS_PATH_MAX 256
 #define MAX_PORTS 1024
 
 static struct sock_filter lldpd_filter_f[] = { LLDPD_FILTER_F };
 
-/* net/if.h */
-extern unsigned int if_nametoindex (__const char *__ifname) __THROW;
-extern char *if_indextoname (unsigned int __ifindex, char *__ifname) __THROW;
-
 struct lldpd_ops eth_ops;
 struct lldpd_ops bond_ops;
 
@@ -122,7 +118,39 @@ pattern_match(char *iface, char *list, int found)
 }
 
 static int
-old_iface_is_bridge(struct lldpd *cfg, const char *name)
+iface_nametoindex(struct netlink_interface_list *interfaces,
+    const char *device)
+{
+       struct netlink_interface *iface;
+       TAILQ_FOREACH(iface, interfaces, next) {
+               if (!strcmp(iface->name, device))
+                       return iface->index;
+       }
+       log_debug("interfaces", "cannot get interface index for %s",
+           device);
+       return 0;
+}
+
+static int
+iface_indextoname(struct netlink_interface_list *interfaces,
+    int index, char *name)
+{
+       struct netlink_interface *iface;
+       TAILQ_FOREACH(iface, interfaces, next) {
+               if (iface->index == index) {
+                       strncpy(name, iface->name, IFNAMSIZ);
+                       return 0;
+               }
+       }
+       log_debug("interfaces", "cannot get interface name for index %d",
+           index);
+       return -1;
+}
+
+static int
+old_iface_is_bridge(struct lldpd *cfg,
+    struct netlink_interface_list *interfaces,
+    const char *name)
 {
        int ifindices[MAX_BRIDGES];
        char ifname[IFNAMSIZ];
@@ -135,7 +163,7 @@ old_iface_is_bridge(struct lldpd *cfg, const char *name)
                   to fill logs. */
                return 0;
        for (i = 0; i < num; i++) {
-               if (if_indextoname(ifindices[i], ifname) == NULL)
+               if (iface_indextoname(interfaces, ifindices[i], ifname) == -1)
                        log_info("interfaces", "unable to get name of interface %d",
                            ifindices[i]);
                else if (strncmp(name, ifname, IFNAMSIZ) == 0)
@@ -145,7 +173,9 @@ old_iface_is_bridge(struct lldpd *cfg, const char *name)
 }
 
 static int
-iface_is_bridge(struct lldpd *cfg, const char *name)
+iface_is_bridge(struct lldpd *cfg,
+    struct netlink_interface_list *interfaces,
+    const char *name)
 {
 #ifdef SYSFS_BRIDGE_FDB
        char path[SYSFS_PATH_MAX];
@@ -155,24 +185,27 @@ iface_is_bridge(struct lldpd *cfg, const char *name)
                    SYSFS_CLASS_NET "%s/" SYSFS_BRIDGE_FDB, name)) >= SYSFS_PATH_MAX)
                log_warnx("interfaces", "path truncated");
        if ((f = priv_open(path)) < 0) {
-               return old_iface_is_bridge(cfg, name);
+               return old_iface_is_bridge(cfg, interfaces, name);
        }
        close(f);
        return 1;
 #else
-       return old_iface_is_bridge(cfg, name);
+       return old_iface_is_bridge(cfg, interfaces, name);
 #endif
 }
 
 #ifdef ENABLE_DOT1
 static int
-old_iface_is_bridged_to(struct lldpd *cfg, const char *slave, const char *master)
+old_iface_is_bridged_to(struct lldpd *cfg,
+    struct netlink_interface_list *interfaces,
+    const char *slave, const char *master)
 {
-       int j, index = if_nametoindex(slave);
+       int j, index = iface_nametoindex(interfaces, slave);
        int ifptindices[MAX_PORTS];
        unsigned long args2[4] = { BRCTL_GET_PORT_LIST,
                                   (unsigned long)ifptindices, MAX_PORTS, 0 };
        struct ifreq ifr;
+       if (index == 0) return 0;
 
        strncpy(ifr.ifr_name, master, IFNAMSIZ);
        memset(ifptindices, 0, sizeof(ifptindices));
@@ -193,26 +226,28 @@ old_iface_is_bridged_to(struct lldpd *cfg, const char *slave, const char *master
 }
 
 static int
-iface_is_bridged_to(struct lldpd *cfg, const char *slave, const char *master)
+iface_is_bridged_to(struct lldpd *cfg,
+    struct netlink_interface_list *interfaces,
+    const char *slave, const char *master)
 {
 #ifdef SYSFS_BRIDGE_PORT_SUBDIR
        char path[SYSFS_PATH_MAX];
        int f;
 
        /* Master should be a bridge, first */
-       if (!iface_is_bridge(cfg, master)) return 0;
+       if (!iface_is_bridge(cfg, interfaces, master)) return 0;
 
        if (snprintf(path, SYSFS_PATH_MAX,
                SYSFS_CLASS_NET "%s/" SYSFS_BRIDGE_PORT_SUBDIR "/%s/port_no",
                master, slave) >= SYSFS_PATH_MAX)
                log_warnx("interfaces", "path truncated");
        if ((f = priv_open(path)) < 0) {
-               return old_iface_is_bridged_to(cfg, slave, master);
+               return old_iface_is_bridged_to(cfg, interfaces, slave, master);
        }
        close(f);
        return 1;
 #else
-       return old_iface_is_bridged_to(cfg, slave, master);
+       return old_iface_is_bridged_to(cfg, interfaces, slave, master);
 #endif
 }
 #endif
@@ -285,28 +320,26 @@ iface_is_bond_slave(struct lldpd *cfg, const char *slave, const char *master,
 }
 
 static int
-iface_is_enslaved(struct lldpd *cfg, const char *name)
+iface_is_enslaved(struct lldpd *cfg,
+    struct netlink_interface_list *interfaces,
+    const char *name)
 {
-       struct ifaddrs *ifap, *ifa;
+       struct netlink_interface *iface;
        int master;
 
-       if (getifaddrs(&ifap) != 0) {
-               log_warn("interfaces", "unable to get interface list");
-               return -1;
-       }
-       for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
-               if (iface_is_bond_slave(cfg, name, ifa->ifa_name, NULL)) {
-                       master = if_nametoindex(ifa->ifa_name);
-                       freeifaddrs(ifap);
+       TAILQ_FOREACH(iface, interfaces, next) {
+               if (iface_is_bond_slave(cfg, name, iface->name, NULL)) {
+                       master = iface->index;
                        return master;
                }
        }
-       freeifaddrs(ifap);
        return -1;
 }
 
 static void
-iface_get_permanent_mac(struct lldpd *cfg, struct lldpd_hardware *hardware)
+iface_get_permanent_mac(struct lldpd *cfg,
+    struct netlink_interface_list *interfaces,
+    struct lldpd_hardware *hardware)
 {
        int master, f, state = 0;
        FILE *netbond;
@@ -320,10 +353,11 @@ iface_get_permanent_mac(struct lldpd *cfg, struct lldpd_hardware *hardware)
        log_debug("interfaces", "get MAC address for %s",
            hardware->h_ifname);
 
-       if ((master = iface_is_enslaved(cfg, hardware->h_ifname)) == -1)
+       if ((master = iface_is_enslaved(cfg, interfaces,
+                   hardware->h_ifname)) == -1)
                return;
        /* We have a bond, we need to query it to get real MAC addresses */
-       if ((if_indextoname(master, bond)) == NULL) {
+       if ((iface_indextoname(interfaces, master, bond)) == -1) {
                log_warnx("interfaces", "unable to get bond name");
                return;
        }
@@ -399,9 +433,10 @@ iface_get_permanent_mac(struct lldpd *cfg, struct lldpd_hardware *hardware)
 
 /* Generic minimal checks to handle a given interface. */
 static int
-iface_minimal_checks(struct lldpd *cfg, struct ifaddrs *ifa)
+iface_minimal_checks(struct lldpd *cfg,
+    struct netlink_interface_list *interfaces,
+    struct netlink_interface *iface)
 {
-       struct sockaddr_ll sdl;
        struct ifreq ifr;
        struct ethtool_drvinfo ethc;
        const char * const *rif;
@@ -413,57 +448,53 @@ iface_minimal_checks(struct lldpd *cfg, struct ifaddrs *ifa)
                NULL
        };
 
-       int is_bridge = iface_is_bridge(cfg, ifa->ifa_name);
+       int is_bridge = iface_is_bridge(cfg, interfaces, iface->name);
 
-       log_debug("interfaces", "minimal checks for %s", ifa->ifa_name);
+       log_debug("interfaces", "minimal checks for %s", iface->name);
 
        if (!(LOCAL_CHASSIS(cfg)->c_cap_enabled & LLDP_CAP_BRIDGE) &&
            is_bridge) {
                log_debug("interfaces", "skip %s: is a bridge",
-                   ifa->ifa_name);
+                   iface->name);
                LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_BRIDGE;
                return 0;
        }
 
        if (!(LOCAL_CHASSIS(cfg)->c_cap_enabled & LLDP_CAP_WLAN) &&
-           iface_is_wireless(cfg, ifa->ifa_name))
+           iface_is_wireless(cfg, iface->name))
                LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_WLAN;
 
        /* First, check if this interface has already been handled */
-       if (!ifa->ifa_flags) {
+       if (!iface->flags) {
                log_debug("interfaces", "skip %s: already been handled",
-                   ifa->ifa_name);
+                   iface->name);
                return 0;
        }
 
-       if (ifa->ifa_addr == NULL) {
-               if (ifa->ifa_addr->sa_family != PF_PACKET) {
-                       log_debug("interfaces", "skip %s: address family is %d and not PF_PACKET",
-                           ifa->ifa_name, ifa->ifa_addr->sa_family);
-                       return 0;
-               }
-               log_debug("interfaces", "skip %s: no address",
-                   ifa->ifa_name);
-       }
-
-       memcpy(&sdl, ifa->ifa_addr, sizeof(struct sockaddr_ll));
-       if (sdl.sll_hatype != ARPHRD_ETHER || !sdl.sll_halen) {
+       if (iface->type != ARPHRD_ETHER) {
                log_debug("interfaces", "skip %s: not an Ethernet device",
-                   ifa->ifa_name);
+                   iface->name);
                return 0;
        }
 
        /* We request that the interface is able to do either multicast
         * or broadcast to be able to send discovery frames. */
-       if (!(ifa->ifa_flags & (IFF_MULTICAST|IFF_BROADCAST))) {
+       if (!(iface->flags & (IFF_MULTICAST|IFF_BROADCAST))) {
                log_debug("interfaces", "skip %s: not able to do multicast nor broadcast",
-                   ifa->ifa_name);
+                   iface->name);
+               return 0;
+       }
+
+       /* If the interface is linked to another one, skip it too. */
+       if (iface->link != -1 && iface->index != iface->link) {
+               log_debug("interfaces", "skip %s: there is a lower interface (%d)",
+                   iface->name, iface->link);
                return 0;
        }
 
        /* Check if the driver is whitelisted */
        memset(&ifr, 0, sizeof(ifr));
-       strcpy(ifr.ifr_name, ifa->ifa_name);
+       strcpy(ifr.ifr_name, iface->name);
        memset(&ethc, 0, sizeof(ethc));
        ifr.ifr_data = (caddr_t) &ethc;
        ethc.cmd = ETHTOOL_GDRVINFO;
@@ -472,43 +503,41 @@ iface_minimal_checks(struct lldpd *cfg, struct ifaddrs *ifa)
                        if (strcmp(ethc.driver, *rif) == 0) {
                                /* White listed! */
                                log_debug("interfaces", "accept %s: whitelisted",
-                                   ifa->ifa_name);
+                                   iface->name);
                                return 1;
                        }
                }
        }
-       log_debug("interfaces", "keep %s: not whitelisted",
-           ifa->ifa_name);
+       log_debug("interfaces", "keep checking %s: not whitelisted",
+           iface->name);
 
        /* Check queue len. If no queue, this usually means that this
           is not a "real" interface. */
-       memset(&ifr, 0, sizeof(ifr));
-       strcpy(ifr.ifr_name, ifa->ifa_name);
-       if ((ioctl(cfg->g_sock, SIOCGIFTXQLEN, &ifr) < 0) || !ifr.ifr_qlen) {
+       if (iface->txqueue == 0) {
                log_debug("interfaces", "skip %s: no queue",
-                   ifa->ifa_name);
+                   iface->name);
                return 0;
        }
 
        /* Don't handle bond and VLAN, nor bridge  */
-       if (iface_is_vlan(cfg, ifa->ifa_name)) {
+       if (iface_is_vlan(cfg, iface->name)) {
                log_debug("interfaces", "skip %s: is a VLAN",
-                   ifa->ifa_name);
+                   iface->name);
                return 0;
        }
-       if (iface_is_bond(cfg, ifa->ifa_name)) {
+       if (iface_is_bond(cfg, iface->name)) {
                log_debug("interfaces", "skip %s: is a bond",
-                   ifa->ifa_name);
+                   iface->name);
                return 0;
        }
        if (is_bridge) {
                log_debug("interfaces", "skip %s: is a bridge",
-                   ifa->ifa_name);
+                   iface->name);
                return 0;
        }
 
        log_debug("interfaces", "%s passes the minimal checks",
-           ifa->ifa_name);
+           iface->name);
        return 1;
 }
 
@@ -532,12 +561,9 @@ iface_set_filter(const char *name, int fd)
 
 /* Fill up port name and description */
 static void
-iface_port_name_desc(struct lldpd_hardware *hardware)
+iface_port_name_desc(struct lldpd_hardware *hardware, struct netlink_interface *iface)
 {
        struct lldpd_port *port = &hardware->h_lport;
-       char buffer[256];       /* 256 = IFALIASZ */
-       char path[SYSFS_PATH_MAX];
-       int f;
 
        /* There are two cases:
 
@@ -549,13 +575,8 @@ iface_port_name_desc(struct lldpd_hardware *hardware)
             port name in description.
        */
 
-       if ((snprintf(path, SYSFS_PATH_MAX,
-                   SYSFS_CLASS_NET "%s/ifalias", hardware->h_ifname)) >= SYSFS_PATH_MAX)
-               log_warnx("interfaces", "path truncated");
-       memset(buffer, 0, sizeof(buffer));
-       if (((f = priv_open(path)) < 0) || (read(f, buffer, sizeof(buffer)-1) < 1)) {
+       if (iface->alias == NULL || strlen(iface->alias) == 0) {
                /* Case 2: MAC address and port name */
-               close(f);
                log_debug("interfaces", "use ifname and MAC address for %s",
                    hardware->h_ifname);
                port->p_id_subtype = LLDP_PORTID_SUBTYPE_LLADDR;
@@ -569,7 +590,6 @@ iface_port_name_desc(struct lldpd_hardware *hardware)
                return;
        }
        /* Case 1: port name and port description */
-       close(f);
        log_debug("interfaces", "use ifname and ifalias for %s",
            hardware->h_ifname);
        port->p_id_subtype = LLDP_PORTID_SUBTYPE_IFNAME;
@@ -578,9 +598,7 @@ iface_port_name_desc(struct lldpd_hardware *hardware)
                calloc(1, port->p_id_len)) == NULL)
                fatal("interfaces", NULL);
        memcpy(port->p_id, hardware->h_ifname, port->p_id_len);
-       if (buffer[strlen(buffer) - 1] == '\n')
-               buffer[strlen(buffer) - 1] = '\0';
-       port->p_descr = strdup(buffer);
+       port->p_descr = strdup(iface->alias);
 }
 
 /* Fill up MAC/PHY for a given hardware port */
@@ -606,7 +624,7 @@ iface_macphy(struct lldpd_hardware *hardware)
 
        log_debug("interfaces", "ask ethtool for the appropriate MAC/PHY for %s",
            hardware->h_ifname);
-       if (priv_ethtool(hardware->h_ifname, &ethc) == 0) {
+       if (priv_ethtool(hardware->h_ifname, &ethc, sizeof(struct ethtool_cmd)) == 0) {
                port->p_macphy.autoneg_support = (ethc.supported & SUPPORTED_Autoneg) ? 1 : 0;
                port->p_macphy.autoneg_enabled = (ethc.autoneg == AUTONEG_DISABLE) ? 0 : 1;
                for (j=0; advertised_ethtool_to_rfc3636[j][0]; j++) {
@@ -650,21 +668,6 @@ iface_macphy(struct lldpd_hardware *hardware)
 #endif
 }
 
-static void
-iface_mtu(struct lldpd *cfg, struct lldpd_hardware *hardware)
-{
-       struct ifreq ifr;
-
-       /* get MTU */
-       memset(&ifr, 0, sizeof(ifr));
-       strlcpy(ifr.ifr_name, hardware->h_ifname, sizeof(ifr.ifr_name));
-       if (ioctl(cfg->g_sock, SIOCGIFMTU, (char*)&ifr) == -1) {
-               log_warn("interfaces", "unable to get MTU of %s, using 1500", hardware->h_ifname);
-               hardware->h_mtu = 1500;
-       } else
-               hardware->h_mtu = ifr.ifr_mtu;
-}
-
 static void
 iface_multicast(struct lldpd *cfg, const char *name, int remove)
 {
@@ -691,7 +694,7 @@ iface_eth_init(struct lldpd *cfg, struct lldpd_hardware *hardware)
 
        log_debug("interfaces", "initialize ethernet device %s",
            hardware->h_ifname);
-       if ((fd = priv_iface_init(hardware->h_ifname)) == -1)
+       if ((fd = priv_iface_init(hardware->h_ifindex)) == -1)
                return -1;
        hardware->h_sendfd = fd; /* Send */
 
@@ -713,8 +716,8 @@ static int
 iface_eth_send(struct lldpd *cfg, struct lldpd_hardware *hardware,
     char *buffer, size_t size)
 {
-       log_debug("interfaces", "send PDU to ethernet device %s",
-           hardware->h_ifname);
+       log_debug("interfaces", "send PDU to ethernet device %s (fd=%d)",
+           hardware->h_ifname, hardware->h_sendfd);
        return write(hardware->h_sendfd,
            buffer, size);
 }
@@ -754,26 +757,29 @@ iface_eth_close(struct lldpd *cfg, struct lldpd_hardware *hardware)
        return 0;
 }
 
-void
-lldpd_ifh_eth(struct lldpd *cfg, struct ifaddrs *ifap)
+static void
+lldpd_ifh_eth(struct lldpd *cfg, struct netlink_interface_list *interfaces)
 {
-       struct ifaddrs *ifa;
+       struct netlink_interface *iface;
        struct lldpd_hardware *hardware;
 
-       for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
-               if (!iface_minimal_checks(cfg, ifa))
+       TAILQ_FOREACH(iface, interfaces, next) {
+               log_debug("interfaces", "check if %s is a real ethernet device",
+                   iface->name);
+               if (!iface_minimal_checks(cfg, interfaces, iface))
                        continue;
 
                log_debug("interfaces", "%s is an acceptable ethernet device",
-                   ifa->ifa_name);
+                   iface->name);
                if ((hardware = lldpd_get_hardware(cfg,
-                           ifa->ifa_name,
-                           if_nametoindex(ifa->ifa_name),
+                           iface->name,
+                           iface->index,
                            &eth_ops)) == NULL) {
                        if  ((hardware = lldpd_alloc_hardware(cfg,
-                                   ifa->ifa_name)) == NULL) {
+                                   iface->name,
+                                   iface->index)) == NULL) {
                                log_warnx("interfaces", "Unable to allocate space for %s",
-                                   ifa->ifa_name);
+                                   iface->name);
                                continue;
                        }
                        if (iface_eth_init(cfg, hardware) != 0) {
@@ -782,66 +788,67 @@ lldpd_ifh_eth(struct lldpd *cfg, struct ifaddrs *ifap)
                                continue;
                        }
                        hardware->h_ops = &eth_ops;
-                       hardware->h_ifindex = if_nametoindex(ifa->ifa_name);
                        TAILQ_INSERT_TAIL(&cfg->g_hardware, hardware, h_entries);
                } else {
                        if (hardware->h_flags) continue; /* Already seen this time */
                        lldpd_port_cleanup(&hardware->h_lport, 0);
                }
 
-               hardware->h_flags = ifa->ifa_flags; /* Should be non-zero */
-               ifa->ifa_flags = 0;                 /* Future handlers
+               hardware->h_flags = iface->flags;   /* Should be non-zero */
+               iface->flags = 0;                   /* Future handlers
                                                       don't have to
                                                       care about this
                                                       interface. */
 
                /* Get local address */
-               memcpy(&hardware->h_lladdr,
-                   (u_int8_t*)((u_int8_t*)ifa->ifa_addr +
-                       offsetof(struct sockaddr_ll, sll_addr)),
-                   sizeof(hardware->h_lladdr));
+               memcpy(&hardware->h_lladdr, iface->address, ETHER_ADDR_LEN);
 
                /* Fill information about port */
-               iface_port_name_desc(hardware);
+               iface_port_name_desc(hardware, iface);
 
                /* Fill additional info */
                iface_macphy(hardware);
-               iface_mtu(cfg, hardware);
+               hardware->h_mtu = iface->mtu ? iface->mtu : 1500;
        }
 }
 
-void
-lldpd_ifh_whitelist(struct lldpd *cfg, struct ifaddrs *ifap)
+static void
+lldpd_ifh_whitelist(struct lldpd *cfg, struct netlink_interface_list *interfaces)
 {
-       struct ifaddrs *ifa;
+       struct netlink_interface *iface;
 
        if (!cfg->g_config.c_iface_pattern)
                return;
 
-       for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
-               if (ifa->ifa_flags == 0) continue; /* Already handled by someone else */
-               if (!pattern_match(ifa->ifa_name, cfg->g_config.c_iface_pattern, 0)) {
+       TAILQ_FOREACH(iface, interfaces, next) {
+               if (iface->flags == 0) continue; /* Already handled by someone else */
+               if (!pattern_match(iface->name, cfg->g_config.c_iface_pattern, 0)) {
                        /* This interface was not found. We flag it. */
-                       log_debug("interfaces", "blacklist %s", ifa->ifa_name);
-                       ifa->ifa_flags = 0;
+                       log_debug("interfaces", "blacklist %s", iface->name);
+                       iface->flags = 0;
                }
        }
 }
 
+struct bond_master {
+       char name[IFNAMSIZ];
+       int  index;
+};
+
 static int
 iface_bond_init(struct lldpd *cfg, struct lldpd_hardware *hardware)
 {
-       char *mastername = (char *)hardware->h_data; /* Master name */
+       struct bond_master *master = hardware->h_data;
        int fd, status;
        int un = 1;
 
-       if (!mastername) return -1;
+       if (!master) return -1;
 
        log_debug("interfaces", "initialize bonded device %s",
            hardware->h_ifname);
 
        /* First, we get a socket to the raw physical interface */
-       if ((fd = priv_iface_init(hardware->h_ifname)) == -1)
+       if ((fd = priv_iface_init(hardware->h_ifindex)) == -1)
                return -1;
        hardware->h_sendfd = fd;
        if ((status = iface_set_filter(hardware->h_ifname, fd)) != 0) {
@@ -851,11 +858,11 @@ iface_bond_init(struct lldpd *cfg, struct lldpd_hardware *hardware)
        iface_multicast(cfg, hardware->h_ifname, 0);
 
        /* Then, we open a raw interface for the master */
-       if ((fd = priv_iface_init(mastername)) == -1) {
+       if ((fd = priv_iface_init(master->index)) == -1) {
                close(hardware->h_sendfd);
                return -1;
        }
-       if ((status = iface_set_filter(mastername, fd)) != 0) {
+       if ((status = iface_set_filter(master->name, fd)) != 0) {
                close(hardware->h_sendfd);
                close(fd);
                return status;
@@ -865,19 +872,19 @@ iface_bond_init(struct lldpd *cfg, struct lldpd_hardware *hardware)
         * physical device instead of bond device (works with >=
         * 2.6.24). */
        if (setsockopt(fd, SOL_PACKET,
-                      PACKET_ORIGDEV, &un, sizeof(un)) == -1) {
-               log_debug("interfaces", "unable to setsockopt for master bonding device of %s. "
-                          "You will get inaccurate results",
-                          hardware->h_ifname);
+               PACKET_ORIGDEV, &un, sizeof(un)) == -1) {
+               log_info("interfaces", "unable to setsockopt for master bonding device of %s. "
+                   "You will get inaccurate results",
+                   hardware->h_ifname);
        }
-       iface_multicast(cfg, mastername, 0);
+       iface_multicast(cfg, master->name, 0);
 
        levent_hardware_add_fd(hardware, hardware->h_sendfd);
        levent_hardware_add_fd(hardware, fd);
        log_debug("interfaces", "interface %s initialized (fd=%d,master=%s[%d])",
            hardware->h_ifname,
            hardware->h_sendfd,
-           mastername, fd);
+           master->name, fd);
        return 0;
 }
 
@@ -888,11 +895,11 @@ iface_bond_send(struct lldpd *cfg, struct lldpd_hardware *hardware,
        /* With bonds, we have duplicate MAC address on different physical
         * interfaces. We need to alter the source MAC address when we send on
         * an inactive slave. */
-       char *master = (char*)hardware->h_data;
+       struct bond_master *master = hardware->h_data;
        int active;
        log_debug("interfaces", "send PDU to bonded device %s",
            hardware->h_ifname);
-       if (!iface_is_bond_slave(cfg, hardware->h_ifname, master, &active)) {
+       if (!iface_is_bond_slave(cfg, hardware->h_ifname, master->name, &active)) {
                log_warnx("interfaces", "%s seems to not be enslaved anymore?",
                    hardware->h_ifname);
                return 0;
@@ -917,6 +924,7 @@ iface_bond_recv(struct lldpd *cfg, struct lldpd_hardware *hardware,
        int n;
        struct sockaddr_ll from;
        socklen_t fromlen;
+       struct bond_master *master = hardware->h_data;
 
        log_debug("interfaces", "receive PDU from bonded device %s",
            hardware->h_ifname);
@@ -938,7 +946,7 @@ iface_bond_recv(struct lldpd *cfg, struct lldpd_hardware *hardware,
        if (from.sll_ifindex == hardware->h_ifindex)
                /* This is for us */
                return n;
-       if (from.sll_ifindex == if_nametoindex((char*)hardware->h_data))
+       if (from.sll_ifindex == master->index)
                /* We don't know from which physical interface it comes (kernel
                 * < 2.6.24). In doubt, this is for us. */
                return n;
@@ -948,40 +956,55 @@ iface_bond_recv(struct lldpd *cfg, struct lldpd_hardware *hardware,
 static int
 iface_bond_close(struct lldpd *cfg, struct lldpd_hardware *hardware)
 {
+       struct bond_master *master = hardware->h_data;
        log_debug("interfaces", "closing bonded device %s",
            hardware->h_ifname);
        iface_multicast(cfg, hardware->h_ifname, 1);
-       iface_multicast(cfg, (char*)hardware->h_data, 1);
+       iface_multicast(cfg, master->name, 1);
        free(hardware->h_data);
        return 0;
 }
 
-void
-lldpd_ifh_bond(struct lldpd *cfg, struct ifaddrs *ifap)
+static void
+lldpd_ifh_bond(struct lldpd *cfg, struct netlink_interface_list *interfaces)
 {
-       struct ifaddrs *ifa;
+       struct netlink_interface *iface;
        struct lldpd_hardware *hardware;
-       int master;
-       for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
-               if (!iface_minimal_checks(cfg, ifa))
+       struct bond_master *master;
+       int idx;
+       TAILQ_FOREACH(iface, interfaces, next) {
+               log_debug("interfaces", "check if %s is part of a bond",
+                   iface->name);
+               if (!iface_minimal_checks(cfg, interfaces, iface))
                        continue;
-               if ((master = iface_is_enslaved(cfg, ifa->ifa_name)) == -1)
+               if ((idx = iface_is_enslaved(cfg, interfaces,
+                           iface->name)) == -1)
                        continue;
 
                log_debug("interfaces", "%s is an acceptable bonded device (master=%d)",
-                   ifa->ifa_name, master);
+                   iface->name, idx);
                if ((hardware = lldpd_get_hardware(cfg,
-                           ifa->ifa_name,
-                           if_nametoindex(ifa->ifa_name),
+                           iface->name,
+                           iface->index,
                            &bond_ops)) == NULL) {
                        if  ((hardware = lldpd_alloc_hardware(cfg,
-                                   ifa->ifa_name)) == NULL) {
+                                   iface->name,
+                                   iface->index)) == NULL) {
                                log_warnx("interfaces", "Unable to allocate space for %s",
-                                   ifa->ifa_name);
+                                   iface->name);
+                               continue;
+                       }
+                       hardware->h_data = master = calloc(1, sizeof(struct bond_master));
+                       if (!hardware->h_data) {
+                               log_warn("interfaces", "not enough memory");
+                               lldpd_hardware_cleanup(cfg, hardware);
+                               continue;
+                       }
+                       master->index = idx;
+                       if (iface_indextoname(interfaces, master->index, master->name) == -1) {
+                               lldpd_hardware_cleanup(cfg, hardware);
                                continue;
                        }
-                       hardware->h_data = (char *)calloc(1, IFNAMSIZ);
-                       if_indextoname(master, hardware->h_data);
                        if (iface_bond_init(cfg, hardware) != 0) {
                                log_warn("interfaces", "unable to initialize %s",
                                    hardware->h_ifname);
@@ -989,37 +1012,38 @@ lldpd_ifh_bond(struct lldpd *cfg, struct ifaddrs *ifap)
                                continue;
                        }
                        hardware->h_ops = &bond_ops;
-                       hardware->h_ifindex = if_nametoindex(ifa->ifa_name);
                        TAILQ_INSERT_TAIL(&cfg->g_hardware, hardware, h_entries);
                } else {
                        if (hardware->h_flags) continue; /* Already seen this time */
-                       memset(hardware->h_data, 0, IFNAMSIZ);
-                       if_indextoname(master, hardware->h_data);
+                       master = hardware->h_data;
+                       memset(hardware->h_data, 0, sizeof(struct bond_master));
+                       master->index = idx;
+                       iface_indextoname(interfaces, master->index, master->name);
                        lldpd_port_cleanup(&hardware->h_lport, 0);
                }
-               
-               hardware->h_flags = ifa->ifa_flags;
-               ifa->ifa_flags = 0;
+
+               hardware->h_flags = iface->flags;
+               iface->flags = 0;
 
                /* Get local address */
-               iface_get_permanent_mac(cfg, hardware);
-               
+               iface_get_permanent_mac(cfg, interfaces, hardware);
+
                /* Fill information about port */
-               iface_port_name_desc(hardware);
-               
+               iface_port_name_desc(hardware, iface);
+
                /* Fill additional info */
 #ifdef ENABLE_DOT3
-               hardware->h_lport.p_aggregid = master;
+               hardware->h_lport.p_aggregid = master->index;
 #endif
                iface_macphy(hardware);
-               iface_mtu(cfg, hardware);
+               hardware->h_mtu = iface->mtu ? iface->mtu : 1500;
        }
 }
 
 #ifdef ENABLE_DOT1
 static void
 iface_append_vlan(struct lldpd *cfg,
-    struct lldpd_hardware *hardware, struct ifaddrs *ifa)
+    struct lldpd_hardware *hardware, struct netlink_interface *iface)
 {
        struct lldpd_port *port = &hardware->h_lport;
        struct lldpd_vlan *vlan;
@@ -1027,18 +1051,18 @@ iface_append_vlan(struct lldpd *cfg,
 
        /* Check if the VLAN is already here. */
        TAILQ_FOREACH(vlan, &port->p_vlans, v_entries)
-           if (strncmp(ifa->ifa_name, vlan->v_name, IFNAMSIZ) == 0)
+           if (strncmp(iface->name, vlan->v_name, IFNAMSIZ) == 0)
                    return;
        if ((vlan = (struct lldpd_vlan *)
                calloc(1, sizeof(struct lldpd_vlan))) == NULL)
                return;
-       if ((vlan->v_name = strdup(ifa->ifa_name)) == NULL) {
+       if ((vlan->v_name = strdup(iface->name)) == NULL) {
                free(vlan);
                return;
        }
        memset(&ifv, 0, sizeof(ifv));
        ifv.cmd = GET_VLAN_VID_CMD;
-       strlcpy(ifv.device1, ifa->ifa_name, sizeof(ifv.device1));
+       strlcpy(ifv.device1, iface->name, sizeof(ifv.device1));
        if (ioctl(cfg->g_sock, SIOCGIFVLAN, &ifv) < 0) {
                /* Dunno what happened */
                free(vlan->v_name);
@@ -1052,35 +1076,38 @@ iface_append_vlan(struct lldpd *cfg,
        TAILQ_INSERT_TAIL(&port->p_vlans, vlan, v_entries);
 }
 
-void
-lldpd_ifh_vlan(struct lldpd *cfg, struct ifaddrs *ifap)
+static void
+lldpd_ifh_vlan(struct lldpd *cfg, struct netlink_interface_list *interfaces)
 {
-       struct ifaddrs *ifa;
+       struct netlink_interface *iface;
        struct vlan_ioctl_args ifv;
        struct lldpd_hardware *hardware;
-       
-       for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
-               if (!ifa->ifa_flags)
+
+       TAILQ_FOREACH(iface, interfaces, next) {
+               if (!iface->flags)
                        continue;
-               if (!iface_is_vlan(cfg, ifa->ifa_name))
+               if (!iface_is_vlan(cfg, iface->name))
                        continue;
 
                /* We need to find the physical interfaces of this
                   vlan, through bonds and bridges. */
+               /* TODO: use iflink instead? Possible since 2.6.17 only. */
                log_debug("interfaces", "search physical interface for VLAN interface %s",
-                   ifa->ifa_name);
+                   iface->name);
                memset(&ifv, 0, sizeof(ifv));
                ifv.cmd = GET_VLAN_REALDEV_NAME_CMD;
-               strlcpy(ifv.device1, ifa->ifa_name, sizeof(ifv.device1));
+               strlcpy(ifv.device1, iface->name, sizeof(ifv.device1));
                if (ioctl(cfg->g_sock, SIOCGIFVLAN, &ifv) >= 0) {
                        /* Three cases:
                            1. we get a real device
                            2. we get a bond
                            3. we get a bridge
                        */
+                       int idx = iface_nametoindex(interfaces, ifv.u.device2);
+                       if (idx == 0) continue;
                        if ((hardware = lldpd_get_hardware(cfg,
                                    ifv.u.device2,
-                                   if_nametoindex(ifv.u.device2),
+                                   idx,
                                    NULL)) == NULL) {
                                if (iface_is_bond(cfg, ifv.u.device2)) {
                                        TAILQ_FOREACH(hardware, &cfg->g_hardware,
@@ -1093,12 +1120,13 @@ lldpd_ifh_vlan(struct lldpd *cfg, struct ifaddrs *ifap)
                                                        hardware->h_ifname,
                                                        ifv.u.device2);
                                                    iface_append_vlan(cfg,
-                                                       hardware, ifa);
+                                                       hardware, iface);
                                            }
-                               } else if (iface_is_bridge(cfg, ifv.u.device2)) {
+                               } else if (iface_is_bridge(cfg, interfaces, ifv.u.device2)) {
                                        TAILQ_FOREACH(hardware, &cfg->g_hardware,
                                            h_entries)
                                            if (iface_is_bridged_to(cfg,
+                                                   interfaces,
                                                    hardware->h_ifname,
                                                    ifv.u.device2)) {
                                                    log_debug("interfaces",
@@ -1106,11 +1134,11 @@ lldpd_ifh_vlan(struct lldpd *cfg, struct ifaddrs *ifap)
                                                        hardware->h_ifname,
                                                        ifv.u.device2);
                                                    iface_append_vlan(cfg,
-                                                       hardware, ifa);
+                                                       hardware, iface);
                                            }
                                }
                        } else iface_append_vlan(cfg,
-                           hardware, ifa);
+                           hardware, iface);
                }
        }
 }
@@ -1123,18 +1151,18 @@ lldpd_ifh_vlan(struct lldpd *cfg, struct ifaddrs *ifap)
 #define IN_IS_ADDR_GLOBAL(a) (!IN_IS_ADDR_LOOPBACK(a))
 #endif
 #ifndef IN6_IS_ADDR_GLOBAL
-#define IN6_IS_ADDR_GLOBAL(a) (!IN6_IS_ADDR_LOOPBACK(a) && \
-                                                               !IN6_IS_ADDR_LINKLOCAL(a))
+#define IN6_IS_ADDR_GLOBAL(a) \
+       (!IN6_IS_ADDR_LOOPBACK(a) && !IN6_IS_ADDR_LINKLOCAL(a))
 #endif
 
 /* Find a management address in all available interfaces, even those that were
    already handled. This is a special interface handler because it does not
    really handle interface related information (management address is attached
    to the local chassis). */
-void
-lldpd_ifh_mgmt(struct lldpd *cfg, struct ifaddrs *ifap)
+static void
+lldpd_ifh_mgmt(struct lldpd *cfg, struct netlink_address_list *addrs)
 {
-       struct ifaddrs *ifa;
+       struct netlink_address *addr;
        char addrstrbuf[INET6_ADDRSTRLEN];
        struct lldpd_mgmt *mgmt;
        void *sin_addr_ptr;
@@ -1162,23 +1190,19 @@ lldpd_ifh_mgmt(struct lldpd *cfg, struct ifaddrs *ifap)
                   example !*:*,!10.* will only blacklist
                   addresses. We will pick the first IPv4 address not
                   matching 10.*. */
-               for (ifa = ifap;
-                    ifa != NULL;
-                    ifa = ifa->ifa_next) {
-                       if (ifa->ifa_addr == NULL)
-                               continue;
-                       if (ifa->ifa_addr->sa_family != lldpd_af(af))
+               TAILQ_FOREACH(addr, addrs, next) {
+                       if (addr->address.ss_family != lldpd_af(af))
                                continue;
 
                        switch (af) {
                        case LLDPD_AF_IPV4:
-                               sin_addr_ptr = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
+                               sin_addr_ptr = &((struct sockaddr_in *)&addr->address)->sin_addr;
                                sin_addr_size = sizeof(struct in_addr);
                                if (!IN_IS_ADDR_GLOBAL((struct in_addr *)sin_addr_ptr))
                                        continue;
                                break;
                        case LLDPD_AF_IPV6:
-                               sin_addr_ptr = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
+                               sin_addr_ptr = &((struct sockaddr_in6 *)&addr->address)->sin6_addr;
                                sin_addr_size = sizeof(struct in6_addr);
                                if (!IN6_IS_ADDR_GLOBAL((struct in6_addr *)sin_addr_ptr))
                                        continue;
@@ -1188,14 +1212,14 @@ lldpd_ifh_mgmt(struct lldpd *cfg, struct ifaddrs *ifap)
                                continue;
                        }
                        if (inet_ntop(lldpd_af(af), sin_addr_ptr,
-                                     addrstrbuf, sizeof(addrstrbuf)) == NULL) {
+                               addrstrbuf, sizeof(addrstrbuf)) == NULL) {
                                log_warn("interfaces", "unable to convert IP address to a string");
                                continue;
                        }
                        if (cfg->g_config.c_mgmt_pattern == NULL ||
                            pattern_match(addrstrbuf, cfg->g_config.c_mgmt_pattern, allnegative)) {
                                mgmt = lldpd_alloc_mgmt(af, sin_addr_ptr, sin_addr_size,
-                                                       if_nametoindex(ifa->ifa_name));
+                                                       addr->index);
                                if (mgmt == NULL) {
                                        assert(errno == ENOMEM); /* anything else is a bug */
                                        log_warn("interfaces", "out of memory error");
@@ -1214,10 +1238,10 @@ lldpd_ifh_mgmt(struct lldpd *cfg, struct ifaddrs *ifap)
 
 /* Fill out chassis ID if not already done. This handler is special
    because we will only handle interfaces that are already handled. */
-void
-lldpd_ifh_chassis(struct lldpd *cfg, struct ifaddrs *ifap)
+static void
+lldpd_ifh_chassis(struct lldpd *cfg, struct netlink_interface_list *interfaces)
 {
-       struct ifaddrs *ifa;
+       struct netlink_interface *iface;
        struct lldpd_hardware *hardware;
        char *name = NULL;
 
@@ -1225,14 +1249,14 @@ lldpd_ifh_chassis(struct lldpd *cfg, struct ifaddrs *ifap)
            LOCAL_CHASSIS(cfg)->c_id_subtype == LLDP_CHASSISID_SUBTYPE_LLADDR)
                return;         /* We already have one */
 
-       for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
-               if (ifa->ifa_flags) continue;
+       TAILQ_FOREACH(iface, interfaces, next) {
+               if (iface->flags) continue;
                if (cfg->g_config.c_cid_pattern &&
-                   !pattern_match(ifa->ifa_name, cfg->g_config.c_cid_pattern, 0)) continue;
+                   !pattern_match(iface->name, cfg->g_config.c_cid_pattern, 0)) continue;
 
                if ((hardware = lldpd_get_hardware(cfg,
-                           ifa->ifa_name,
-                           if_nametoindex(ifa->ifa_name),
+                           iface->name,
+                           iface->index,
                            NULL)) == NULL)
                        /* That's odd. Let's skip. */
                        continue;
@@ -1261,3 +1285,31 @@ struct lldpd_ops bond_ops = {
        .recv = iface_bond_recv,
        .cleanup = iface_bond_close,
 };
+
+void
+interfaces_update(struct lldpd *cfg)
+{
+       struct netlink_interface_list *interfaces = NULL;
+       struct netlink_address_list *addresses = NULL;
+       interfaces = netlink_get_interfaces();
+       addresses = netlink_get_addresses();
+       if (interfaces == NULL || addresses == NULL) {
+               log_warnx("interfaces", "cannot update the list of local interfaces");
+               goto end;
+       }
+
+       lldpd_ifh_whitelist(cfg, interfaces);
+       lldpd_ifh_bond(cfg, interfaces);
+       lldpd_ifh_eth(cfg, interfaces);
+#ifdef ENABLE_DOT1
+       lldpd_ifh_vlan(cfg, interfaces);
+#endif
+       lldpd_ifh_mgmt(cfg, addresses);
+       lldpd_ifh_chassis(cfg, interfaces);
+
+end:
+       netlink_free_interfaces(interfaces);
+       netlink_free_addresses(addresses);
+       return;
+
+}
index 0b979cb601b8ccd35aa05b8ea82ad4fcc8fb72e0..592741ae7772b84773d73679f61c0f607091d9f2 100644 (file)
@@ -26,7 +26,6 @@
 #include <sys/socket.h>
 #include <sys/ioctl.h>
 #include <netpacket/packet.h>
-#include <linux/sockios.h>
 
 inline static int
 lldpd_af_to_lldp_proto(int af)
index 5c7cd7ec5137439fdd379a1cc9609e3a7160535c..614304ef630f715a2d0923f73665b87984db5741 100644 (file)
@@ -115,6 +115,8 @@ Receiving PDU from some interface.
 Main loop.
 .It Sy smartfilter
 Smart filtering of different protocols on the same port.
+.It Sy netlink
+Netlink subsystem.
 .El
 .It Fl k
 Disable advertising of kernel release, version and machine. Kernel name
index 9ad0efcf045b9582e4c7958d9197ac9a2cf13c42..7832dbf9d841b94ef31664bf7a616a57b542425d 100644 (file)
@@ -34,7 +34,6 @@
 #include <sys/time.h>
 #include <sys/ioctl.h>
 #include <arpa/inet.h>
-#include <net/if_arp.h>
 #include <pwd.h>
 #include <grp.h>
 
@@ -138,7 +137,7 @@ lldpd_get_hardware(struct lldpd *cfg, char *name, int index, struct lldpd_ops *o
 }
 
 struct lldpd_hardware *
-lldpd_alloc_hardware(struct lldpd *cfg, char *name)
+lldpd_alloc_hardware(struct lldpd *cfg, char *name, int index)
 {
        struct lldpd_hardware *hardware;
 
@@ -150,6 +149,7 @@ lldpd_alloc_hardware(struct lldpd *cfg, char *name)
 
        hardware->h_cfg = cfg;
        strlcpy(hardware->h_ifname, name, sizeof(hardware->h_ifname));
+       hardware->h_ifindex = index;
        hardware->h_lport.p_chassis = LOCAL_CHASSIS(cfg);
        hardware->h_lport.p_chassis->c_refcount++;
        TAILQ_INIT(&hardware->h_rports);
@@ -785,7 +785,6 @@ lldpd_send_all(struct lldpd *cfg)
 static void
 lldpd_med(struct lldpd_chassis *chassis)
 {
-#if __i386__ || __amd64__
        static short int once = 0;
        if (!once) {
                chassis->c_med_hw = dmi_hw();
@@ -796,7 +795,6 @@ lldpd_med(struct lldpd_chassis *chassis)
                chassis->c_med_asset = dmi_asset();
                once = 1;
        }
-#endif
 }
 #endif
 
@@ -877,20 +875,7 @@ lldpd_update_localchassis(struct lldpd *cfg)
 static void
 lldpd_update_localports(struct lldpd *cfg)
 {
-       struct ifaddrs *ifap;
        struct lldpd_hardware *hardware;
-       lldpd_ifhandlers ifhs[] = {
-               lldpd_ifh_whitelist, /* Is the interface whitelisted? */
-               lldpd_ifh_bond, /* Handle bond */
-               lldpd_ifh_eth,  /* Handle classic ethernet interfaces */
-#ifdef ENABLE_DOT1
-               lldpd_ifh_vlan, /* Handle VLAN */
-#endif
-               lldpd_ifh_mgmt, /* Handle management address (if not already handled) */
-               lldpd_ifh_chassis, /* Handle chassis ID (if not already handled) */
-               NULL
-       };
-       lldpd_ifhandlers *ifh;
 
        log_debug("localchassis", "update information for local ports");
 
@@ -900,20 +885,7 @@ lldpd_update_localports(struct lldpd *cfg)
        TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries)
            hardware->h_flags = 0;
 
-       if (getifaddrs(&ifap) != 0)
-               fatal("localchassis", "failed to get interface list");
-
-       /* We will run the list of interfaces through a list of interface
-        * handlers. Each handler will create or update some hardware port (and
-        * will set h_flags to a non zero value. The handler can use the list of
-        * interfaces but this is not mandatory. If the interface handler
-        * handles an interface from the list, it should set ifa_flags to 0 to
-        * let know the other handlers that it took care of this interface. This
-        * means that more specific handlers should be before less specific
-        * ones. */
-       for (ifh = ifhs; *ifh != NULL; ifh++)
-               (*ifh)(cfg, ifap);
-       freeifaddrs(ifap);
+       interfaces_update(cfg);
 }
 
 void
index 9327024c7e915081279eed462c6537866a6202fc..95653ca9764dcdf0efeaa87b5d238206d53c3312 100644 (file)
 #include <stddef.h>
 #include <string.h>
 #include <sys/queue.h>
-#ifdef HAVE_SYS_TYPES_H
-#  include <sys/types.h>
-#endif
-#ifndef INCLUDE_LINUX_IF_H
-#  include <net/if.h>
-#else
-#  include <arpa/inet.h>
-#  include <linux/if.h>
-#endif
-#if HAVE_GETIFADDRS
-#  include <ifaddrs.h>
-#endif
+#include <sys/types.h>
 #include <net/ethernet.h>
 #include <netinet/in.h>
-#include <linux/ethtool.h>
 #include <sys/un.h>
 
 #include "lldp-tlv.h"
@@ -128,12 +116,10 @@ struct lldpd {
        TAILQ_HEAD(, lldpd_hardware) g_hardware;
 };
 
-typedef void(*lldpd_ifhandlers)(struct lldpd *, struct ifaddrs *);
-
 /* lldpd.c */
 struct lldpd_hardware  *lldpd_get_hardware(struct lldpd *,
     char *, int, struct lldpd_ops *);
-struct lldpd_hardware  *lldpd_alloc_hardware(struct lldpd *, char *);
+struct lldpd_hardware  *lldpd_alloc_hardware(struct lldpd *, char *, int);
 void    lldpd_hardware_cleanup(struct lldpd*, struct lldpd_hardware *);
 struct lldpd_mgmt *lldpd_alloc_mgmt(int family, void *addr, size_t addrsize, u_int32_t iface);
 void    lldpd_recv(struct lldpd *, struct lldpd_hardware *, int);
@@ -178,19 +164,8 @@ int         edp_send(PROTO_SEND_SIG);
 int     edp_decode(PROTO_DECODE_SIG);
 #endif
 
-/* interfaces.c */
-void    lldpd_ifh_whitelist(struct lldpd *, struct ifaddrs *);
-void    lldpd_ifh_bond(struct lldpd *, struct ifaddrs *);
-void    lldpd_ifh_eth(struct lldpd *, struct ifaddrs *);
-#ifdef ENABLE_DOT1
-void    lldpd_ifh_vlan(struct lldpd *, struct ifaddrs *);
-#endif
-void    lldpd_ifh_mgmt(struct lldpd *, struct ifaddrs *);
-void    lldpd_ifh_chassis(struct lldpd *, struct ifaddrs *);
-
 /* dmi.c */
 #ifdef ENABLE_LLDPMED
-#if __i386__ || __amd64__
 char   *dmi_hw(void);
 char   *dmi_fw(void);
 char   *dmi_sn(void);
@@ -198,7 +173,6 @@ char        *dmi_manuf(void);
 char   *dmi_model(void);
 char   *dmi_asset(void);
 #endif
-#endif
 
 #ifdef USE_SNMP
 /* agent.c */
@@ -223,8 +197,10 @@ void        priv_init(char*, int, uid_t, gid_t);
 void    priv_ctl_cleanup(void);
 char           *priv_gethostbyname(void);
 int             priv_open(char*);
-int             priv_ethtool(char*, struct ethtool_cmd*);
-int             priv_iface_init(const char *);
+#ifdef HOST_OS_LINUX
+int             priv_ethtool(char*, void*, size_t);
+#endif
+int             priv_iface_init(int);
 int     priv_iface_multicast(const char *, u_int8_t *, int);
 int     priv_snmp_socket(struct sockaddr_un *);
 
@@ -232,4 +208,36 @@ int         priv_snmp_socket(struct sockaddr_un *);
 int     receive_fd(int);
 void    send_fd(int, int);
 
+/* interfaces-*.c */
+void     interfaces_update(struct lldpd *);
+
+#ifdef HOST_OS_LINUX
+/* netlink stuff */
+struct netlink_interface {
+       TAILQ_ENTRY(netlink_interface) next;
+       int   index;            /* Index */
+       char *name;             /* Name */
+       char *alias;            /* Alias */
+       int   flags;            /* Flags */
+       int   mtu;              /* MTU */
+       char *address;          /* MAC address */
+       int   type;             /* Type (ARPHDR_*) */
+       int   link;             /* Support interface */
+       int   master;           /* Master interface */
+       int   txqueue;          /* TX queue len */
+};
+struct netlink_address {
+       TAILQ_ENTRY(netlink_address) next;
+       int index;              /* Index */
+       int flags;              /* Flags */
+       struct sockaddr_storage address; /* Address */
+};
+TAILQ_HEAD(netlink_interface_list, netlink_interface);
+TAILQ_HEAD(netlink_address_list, netlink_address);
+struct netlink_interface_list *netlink_get_interfaces(void);
+struct netlink_address_list *netlink_get_addresses(void);
+void netlink_free_interfaces(struct netlink_interface_list *);
+void netlink_free_addresses(struct netlink_address_list *);
+#endif
+
 #endif /* _LLDPD_H */
diff --git a/src/daemon/netlink.c b/src/daemon/netlink.c
new file mode 100644 (file)
index 0000000..9c3f8bc
--- /dev/null
@@ -0,0 +1,440 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and/or 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.
+ *
+ * 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.
+ */
+
+#include "lldpd.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
+#define NETLINK_BUFFER 4096
+
+struct netlink_req {
+    struct nlmsghdr hdr;
+    struct rtgenmsg gen;
+};
+
+/**
+ * Connect to netlink.
+ *
+ * Open a Netlink socket and connect to it.
+ *
+ * @param protocol Which protocol to use (eg NETLINK_ROUTE).
+ * @return The opened socket or -1 on error.
+ */
+static int
+netlink_connect(int protocol)
+{
+    int s = 0;
+    struct sockaddr_nl local = {
+        .nl_family = AF_NETLINK,
+        .nl_pid = getpid(),
+        .nl_groups = 0
+    };
+
+    /* Open Netlink socket */
+    log_debug("netlink", "opening netlink socket");
+    s = socket(AF_NETLINK, SOCK_RAW, protocol);
+    if (s == -1) {
+        log_warn("netlink", "unable to open netlink socket");
+        return -1;
+    }
+    if (bind(s, (struct sockaddr *)&local, sizeof(struct sockaddr_nl)) < 0) {
+        log_warn("netlink", "unable to bind netlink socket");
+        close(s);
+        return -1;
+    }
+    return s;
+}
+
+/**
+ * Send a netlink message.
+ *
+ * The type of the message can be chosen as well the route family. The
+ * mesage will always be NLM_F_REQUEST | NLM_F_DUMP.
+ *
+ * @param s      the netlink socket
+ * @param type   the request type (eg RTM_GETLINK)
+ * @param family the rt family (eg AF_PACKET)
+ * @return 0 on success, -1 otherwise
+ */
+static int
+netlink_send(int s, int type, int family)
+{
+    struct netlink_req req = {
+        .hdr = {
+            .nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)),
+            .nlmsg_type = RTM_GETLINK,
+            .nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP,
+            .nlmsg_seq = 1,
+            .nlmsg_pid = getpid() },
+        .gen = { .rtgen_family = AF_PACKET }
+    };
+    struct iovec iov = {
+        .iov_base = &req,
+        .iov_len = req.hdr.nlmsg_len
+    };
+    struct sockaddr_nl peer = { .nl_family = AF_NETLINK };
+    struct msghdr rtnl_msg = {
+        .msg_iov = &iov,
+        .msg_iovlen = 1,
+        .msg_name = &peer,
+        .msg_namelen = sizeof(struct sockaddr_nl)
+    };
+
+    /* Send netlink message. This is synchronous but we are guaranteed
+     * to not block. */
+    log_debug("netlink", "sending netlink message");
+    if (sendmsg(s, (struct msghdr *)&rtnl_msg, 0) == -1) {
+        log_warn("netlink", "unable to send netlink message");
+        return -1;
+    }
+
+    return 0;
+}
+
+/**
+ * Free an interface.
+ *
+ * @param iff interface to be freed
+ */
+static void
+netlink_free_interface(struct netlink_interface *iff)
+{
+    if (!iff) return;
+    free(iff->name);
+    free(iff->alias);
+    free(iff->address);
+    free(iff);
+}
+
+/**
+ * Free a list of interfaces.
+ *
+ * @param ifs list of interfaces to be freed
+ */
+void
+netlink_free_interfaces(struct netlink_interface_list *ifs)
+{
+    struct netlink_interface *iff, *iff_next;
+    if (!ifs) return;
+    for (iff = TAILQ_FIRST(ifs);
+         iff != NULL;
+         iff = iff_next) {
+        iff_next = TAILQ_NEXT(iff, next);
+        netlink_free_interface(iff);
+    }
+    free(ifs);
+}
+
+/**
+ * Free a list of addresses.
+ *
+ * @param ifaddrs list of addresses
+ */
+void
+netlink_free_addresses(struct netlink_address_list *ifaddrs)
+{
+    struct netlink_address *ifa, *ifa_next;
+    if (!ifaddrs) return;
+    for (ifa = TAILQ_FIRST(ifaddrs);
+         ifa != NULL;
+         ifa = ifa_next) {
+        ifa_next = TAILQ_NEXT(ifa, next);
+        free(ifa);
+    }
+    free(ifaddrs);
+}
+
+/**
+ * Parse a `link` netlink message.
+ *
+ * @param msg  message to be parsed
+ * @param iff  where to put the result
+ * return 0 if the interface is worth it, -1 otherwise
+ */
+static int
+netlink_parse_link(struct nlmsghdr *msg,
+    struct netlink_interface *iff)
+{
+    struct ifinfomsg *iface;
+    struct rtattr *attribute;
+    int len;
+    iface = NLMSG_DATA(msg);
+    len = msg->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifinfomsg));
+
+    iff->index = iface->ifi_index;
+    iff->type  = iface->ifi_type;
+    iff->flags = iface->ifi_flags;
+    iff->link  = -1;
+
+    for (attribute = IFLA_RTA(iface);
+         RTA_OK(attribute, len);
+         attribute = RTA_NEXT(attribute, len)) {
+        switch(attribute->rta_type) {
+        case IFLA_IFNAME:
+            /* Interface name */
+            iff->name = strdup(RTA_DATA(attribute));
+            break;
+        case IFLA_IFALIAS:
+            /* Interface alias */
+            iff->alias = strdup(RTA_DATA(attribute));
+            break;
+        case IFLA_ADDRESS:
+            /* Interface MAC address */
+            iff->address = malloc(RTA_PAYLOAD(attribute));
+            if (iff->address)
+                memcpy(iff->address, RTA_DATA(attribute), RTA_PAYLOAD(attribute));
+            break;
+        case IFLA_LINK:
+            /* Index of "inferior" interface */
+            iff->link = *(int*)RTA_DATA(attribute);
+            break;
+        case IFLA_MASTER:
+            /* Index of master interface */
+            iff->master = *(int*)RTA_DATA(attribute);
+            break;
+        case IFLA_TXQLEN:
+            /* Transmit queue length */
+            iff->txqueue = *(int*)RTA_DATA(attribute);
+            break;
+        case IFLA_MTU:
+            /* Maximum Transmission Unit */
+            iff->mtu = *(int*)RTA_DATA(attribute);
+            break;
+        default:
+            log_debug("netlink", "unhandled attribute type %d for iface %s",
+                      attribute->rta_type, iff->name ? iff->name : "(unknown)");
+            break;
+        }
+    }
+    if (!iff->name || !iff->address) {
+        log_info("netlink", "interface %d does not have a name or an address, skip",
+          iff->index);
+        return -1;
+    }
+    return 0;
+}
+
+/**
+ * Parse a `address` netlink message.
+ *
+ * @param msg  message to be parsed
+ * @param ifa  where to put the result
+ * return 0 if the address is worth it, -1 otherwise
+ */
+static int
+netlink_parse_address(struct nlmsghdr *msg,
+    struct netlink_address *ifa)
+{
+    struct ifaddrmsg *ifi;
+    struct rtattr *attribute;
+    int len;
+    ifi = NLMSG_DATA(msg);
+    len = msg->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifaddrmsg));
+
+    ifa->index = ifi->ifa_index;
+    ifa->flags = ifi->ifa_flags;
+    switch (ifi->ifa_family) {
+    case AF_INET:
+    case AF_INET6: break;
+    default:
+        log_debug("netlink", "got a non IP address on if %d (family: %d)",
+          ifa->index, ifi->ifa_family);
+        return -1;
+    }
+
+    for (attribute = IFA_RTA(ifi);
+         RTA_OK(attribute, len);
+         attribute = RTA_NEXT(attribute, len)) {
+        switch(attribute->rta_type) {
+        case IFA_ADDRESS:
+            /* Address */
+            if (ifi->ifa_family == AF_INET) {
+                struct sockaddr_in ip = { .sin_family = AF_INET };
+                memcpy(&ip.sin_addr, RTA_DATA(attribute),
+                  sizeof(struct in_addr));
+                memcpy(&ifa->address, &ip, sizeof(struct sockaddr_in));
+            } else {
+                struct sockaddr_in6 ip6 = { .sin6_family = AF_INET6 };
+                memcpy(&ip6.sin6_addr, RTA_DATA(attribute),
+                  sizeof(struct in6_addr));
+                memcpy(&ifa->address, &ip6, sizeof(struct sockaddr_in6));
+            }
+            break;
+        default:
+            log_debug("netlink", "unhandled attribute type %d for iface %d",
+                      attribute->rta_type, ifa->index);
+            break;
+        }
+    }
+    if (ifa->address.ss_family == AF_UNSPEC) {
+        log_debug("netlink", "no IP for interface %d",
+          ifa->index);
+        return -1;
+    }
+    return 0;
+}
+
+/**
+ * Receive netlink answer from the kernel.
+ *
+ * @param s    the netlink socket
+ * @param ifs  list to store interface list or NULL if we don't
+ * @param ifas list to store address list or NULL if we don't
+ * @return     0 on success, -1 on error
+ */
+static int
+netlink_recv(int s,
+  struct netlink_interface_list *ifs,
+  struct netlink_address_list *ifas)
+{
+    char reply[NETLINK_BUFFER];
+    int  end = 0;
+
+    struct netlink_interface *iff;
+    struct netlink_address *ifa;
+
+    while (!end) {
+        int len;
+        struct nlmsghdr *msg;
+        struct iovec iov = {
+            .iov_base = reply,
+            .iov_len = NETLINK_BUFFER
+        };
+        struct sockaddr_nl peer = { .nl_family = AF_NETLINK };
+        struct msghdr rtnl_reply = {
+            .msg_iov = &iov,
+            .msg_iovlen = 1,
+            .msg_name = &peer,
+            .msg_namelen = sizeof(struct sockaddr_nl)
+        };
+
+        len = recvmsg(s, &rtnl_reply, 0);
+        if (len == -1) {
+            log_warnx("netlink", "unable to receive netlink answer");
+            return -1;
+        }
+        if (!len) return 0;
+        for (msg = (struct nlmsghdr*)reply;
+             NLMSG_OK(msg, len);
+             msg = NLMSG_NEXT(msg, len)) {
+            switch (msg->nlmsg_type) {
+            case NLMSG_DONE:
+                log_debug("netlink", "received end of dump message");
+                end = 1;
+                break;
+            case RTM_NEWLINK:
+                if (!ifs) break;
+                log_debug("netlink", "received link information");
+                iff = calloc(1, sizeof(struct netlink_interface));
+                if (iff == NULL) {
+                    log_warn("netlink", "not enough memory for another interface, give what we have");
+                    return 0;
+                }
+                if (netlink_parse_link(msg, iff) == 0)
+                    TAILQ_INSERT_TAIL(ifs, iff, next);
+                else
+                    netlink_free_interface(iff);
+                break;
+            case RTM_NEWADDR:
+                if (!ifas) break;
+                log_debug("netlink", "received address information");
+                ifa = calloc(1, sizeof(struct netlink_address));
+                if (ifa == NULL) {
+                    log_warn("netlink", "not enough memory for another address, give what we have");
+                    return 0;
+                }
+                if (netlink_parse_address(msg, ifa) == 0)
+                    TAILQ_INSERT_TAIL(ifas, ifa, next);
+                else
+                    free(ifa);
+                break;
+            default:
+                log_debug("netlink",
+                          "received unhandled message type %d (len: %d)",
+                          msg->nlmsg_type, msg->nlmsg_len);
+            }
+        }
+    }
+    return 0;
+}
+
+/**
+ * Receive the list of interfaces.
+ *
+ * @return a list of interfaces.
+ */
+struct netlink_interface_list*
+netlink_get_interfaces()
+{
+    int s;
+    struct netlink_interface_list *ifs;
+
+    if ((s = netlink_connect(NETLINK_ROUTE)) == -1)
+        return NULL;
+    if (netlink_send(s, RTM_GETLINK, AF_PACKET) == -1) {
+        close(s);
+        return NULL;
+    }
+
+    log_debug("netlink", "get the list of available interfaces");
+    ifs = malloc(sizeof(struct netlink_interface_list));
+    if (ifs == NULL) {
+        log_warn("netlink", "not enough memory for interface list");
+        return NULL;
+    }
+    TAILQ_INIT(ifs);
+    netlink_recv(s, ifs, NULL);
+
+    close(s);
+    return ifs;
+}
+
+/**
+ * Receive the list of addresses.
+ *
+ * @return a list of addresses.
+ */
+struct netlink_address_list*
+netlink_get_addresses()
+{
+    int s;
+    struct netlink_address_list *ifaddrs;
+
+    if ((s = netlink_connect(NETLINK_ROUTE)) == -1)
+        return NULL;
+    if (netlink_send(s, RTM_GETADDR, AF_UNSPEC) == -1) {
+        close(s);
+        return NULL;
+    }
+
+    log_debug("netlink", "get the list of available addresses");
+    ifaddrs = malloc(sizeof(struct netlink_address_list));
+    if (ifaddrs == NULL) {
+        log_warn("netlink", "not enough memory for address list");
+        return NULL;
+    }
+    TAILQ_INIT(ifaddrs);
+    netlink_recv(s, NULL, ifaddrs);
+
+    close(s);
+    return ifaddrs;
+}
index 3014e6293826a43e22bacca7d2a6351159517a62..3c62ce6e0128c2ab6e3e92ca126993b2739210a5 100644 (file)
@@ -35,8 +35,8 @@
 #include <sys/utsname.h>
 #include <sys/ioctl.h>
 #include <netdb.h>
-#include <linux/sockios.h>
-#include <linux/if_packet.h>
+#include <netpacket/packet.h>
+#include <net/ethernet.h>
 
 /* Use resolv.h */
 #ifdef HAVE_SYS_TYPES_H
@@ -125,9 +125,10 @@ priv_open(char *file)
        return receive_fd(remote);
 }
 
+#ifdef HOST_OS_LINUX
 /* Proxy for ethtool ioctl */
 int
-priv_ethtool(char *ifname, struct ethtool_cmd *ethc)
+priv_ethtool(char *ifname, void *ethc, size_t length)
 {
        int cmd, rc, len;
        cmd = PRIV_ETHTOOL;
@@ -138,17 +139,18 @@ priv_ethtool(char *ifname, struct ethtool_cmd *ethc)
        must_read(remote, &rc, sizeof(int));
        if (rc != 0)
                return rc;
-       must_read(remote, ethc, sizeof(struct ethtool_cmd));
+       must_read(remote, ethc, length);
        return rc;
 }
+#endif
 
 int
-priv_iface_init(const char *name)
+priv_iface_init(int index)
 {
        int cmd, rc;
        cmd = PRIV_IFACE_INIT;
        must_write(remote, &cmd, sizeof(int));
-       must_write(remote, name, IFNAMSIZ);
+       must_write(remote, &index, sizeof(int));
        must_read(remote, &rc, sizeof(int));
        if (rc != 0) return -1;
        return receive_fd(remote);
@@ -228,7 +230,6 @@ asroot_open()
                SYSFS_CLASS_NET "[^.][^/]*/brforward",
                SYSFS_CLASS_NET "[^.][^/]*/brport",
                SYSFS_CLASS_NET "[^.][^/]*/brif/[^.][^/]*/port_no",
-               SYSFS_CLASS_NET "[^.][^/]*/ifalias",
                SYSFS_CLASS_DMI "product_version",
                SYSFS_CLASS_DMI "product_serial",
                SYSFS_CLASS_DMI "product_name",
@@ -277,6 +278,9 @@ asroot_open()
        close(fd);
 }
 
+#ifdef HOST_OS_LINUX
+#include <linux/ethtool.h>
+#include <linux/sockios.h>
 static void
 asroot_ethtool()
 {
@@ -303,16 +307,16 @@ asroot_ethtool()
        must_write(remote, &rc, sizeof(int));
        must_write(remote, &ethc, sizeof(struct ethtool_cmd));
 }
+#endif
 
 static void
 asroot_iface_init()
 {
        struct sockaddr_ll sa;
        int s, rc = 0;
-       char ifname[IFNAMSIZ];
+       int ifindex;
 
-       must_read(remote, ifname, IFNAMSIZ);
-       ifname[IFNAMSIZ-1] = '\0';
+       must_read(remote, &ifindex, sizeof(ifindex));
 
        /* Open listening socket to receive/send frames */
        if ((s = socket(PF_PACKET, SOCK_RAW,
@@ -324,7 +328,7 @@ asroot_iface_init()
        memset(&sa, 0, sizeof(sa));
        sa.sll_family = AF_PACKET;
        sa.sll_protocol = 0;
-       sa.sll_ifindex = if_nametoindex(ifname);
+       sa.sll_ifindex = ifindex;
        if (bind(s, (struct sockaddr*)&sa, sizeof(sa)) < 0) {
                rc = errno;
                must_write(remote, &rc, sizeof(rc));
index cef86056c4cd189b7cd94d1fc1a84f296211dcd6..1ce898e8affb0f5aec48964755fab75c203b4c38 100644 (file)
@@ -41,6 +41,7 @@ lldpd_chassis_mgmt_cleanup(struct lldpd_chassis *chassis)
 void
 lldpd_chassis_cleanup(struct lldpd_chassis *chassis, int all)
 {
+       lldpd_chassis_mgmt_cleanup(chassis);
        log_debug("alloc", "cleanup chassis %s",
            chassis->c_name ? chassis->c_name : "(unknwon)");
 #ifdef ENABLE_LLDPMED
@@ -55,7 +56,6 @@ lldpd_chassis_cleanup(struct lldpd_chassis *chassis, int all)
        free(chassis->c_id);
        free(chassis->c_name);
        free(chassis->c_descr);
-       lldpd_chassis_mgmt_cleanup(chassis);
        if (all)
                free(chassis);
 }
index b440392b3331657f3ae43a220b5c363a19457a79..37e6e247806e07cfa174917148b8038a1d1f7bbf 100644 (file)
 
 #include <sys/types.h>
 #include <sys/socket.h>
-#ifndef INCLUDE_LINUX_IF_H
-#  include <net/if.h>
+
+/* This is not very convenient, but we need net/if.h for IFNAMSIZ and others but
+ * we may also need linux/if.h in some modules. And they conflict each others.
+ */
+#ifdef HOST_OS_LINUX
+# include <linux/if.h>
 #else
-#  include <arpa/inet.h>
-#  include <linux/if.h>
+# include <net/if.h>
 #endif
+
 #include <net/ethernet.h>
 #include <netinet/in.h>
 #include <sys/queue.h>
index 2fb3565bff834686693160c8319d78ab5b9e1781..532ad35d6afaf74ff9a3c4e8775fd205127fef77 100644 (file)
@@ -1,4 +1,4 @@
-TESTS = check_marshal check_lldp check_cdp check_sonmp check_edp check_ifaddrs
+TESTS = check_marshal check_lldp check_cdp check_sonmp check_edp
 
 if HAVE_CHECK
 
@@ -21,10 +21,6 @@ check_edp_SOURCES = check_edp.c \
        $(top_srcdir)/src/daemon/lldpd.h \
        common.h common.c
 
-check_ifaddrs_SOURCES = check_ifaddrs.c \
-       $(top_srcdir)/src/daemon/lldpd.h \
-       $(top_srcdir)/src/compat/getifaddrs.c
-
 AM_CFLAGS = @CHECK_CFLAGS@
 LDADD = $(top_builddir)/src/daemon/liblldpd.la @CHECK_LIBS@ @LIBEVENT_LDFLAGS@
 
diff --git a/tests/check_ifaddrs.c b/tests/check_ifaddrs.c
deleted file mode 100644 (file)
index fba6275..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-#include <stdio.h>
-#include <arpa/inet.h>
-#include <check.h>
-#include "common.h"
-
-#define DUMP "ifdump.txt"
-
-/* This is not a real test. It should dump into a file the list of interfaces */
-
-static const char *
-addr_string (struct sockaddr *sa) {
-       static char buf[64];
-       const char *res;
-       if (sa == NULL)
-               return "NULL";
-       switch (sa->sa_family) {
-       case AF_INET:
-               res = inet_ntop(AF_INET,
-                   &((struct sockaddr_in *)sa)->sin_addr,
-                   buf, sizeof(buf));
-               break;
-       case AF_INET6:
-               res = inet_ntop(AF_INET6,
-                   &((struct sockaddr_in6 *)sa)->sin6_addr,
-                   buf, sizeof(buf));
-               break;
-       case AF_UNSPEC:
-               return "<--->";
-       case AF_PACKET:
-               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;
-}
-
-START_TEST (test_ifaddrs)
-{
-       struct ifaddrs *ifap, *ifa;
-       FILE* dump;
-
-       if (getifaddrs(&ifap) < 0) {
-               fail("unable to get interface list");
-               return;
-       }
-       dump = fopen(DUMP, "w+");
-       if (dump == NULL) {
-               fail("unable to open dump file " DUMP);
-               return;
-       }
-       fprintf(dump,
-           "Name           Flags    Address                    Netmask                    Broadcast/Destination\n");
-       for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
-               fprintf(dump, "%-15s%#.5x  ",
-                   ifa->ifa_name, ifa->ifa_flags);
-               fprintf(dump, "%-26s ",
-                   addr_string(ifa->ifa_addr));
-               fprintf(dump, "%-26s ",
-                   addr_string(ifa->ifa_netmask));
-               fprintf(dump, "%-26s\n",
-                   addr_string(ifa->ifa_broadaddr));
-       }
-       fclose(dump);
-}
-END_TEST
-
-Suite *
-ifaddrs_suite(void)
-{
-       Suite *s = suite_create("getifaddrs");
-
-       /* Single objects packing/unpacking */
-       TCase *tc_core = tcase_create("getifaddrs");
-       tcase_add_test(tc_core, test_ifaddrs);
-       suite_add_tcase(s, tc_core);
-
-       return s;
-}
-
-int
-main()
-{
-       int number_failed;
-       Suite *s = ifaddrs_suite ();
-       SRunner *sr = srunner_create (s);
-       srunner_run_all (sr, CK_ENV);
-       number_failed = srunner_ntests_failed (sr);
-       srunner_free (sr);
-       return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
-}