]> git.ipfire.org Git - thirdparty/lldpd.git/commitdiff
netlink: use libnl3 instead of custom netlink code
authorVincent Bernat <vincent@bernat.im>
Sun, 16 Aug 2015 23:19:52 +0000 (01:19 +0200)
committerVincent Bernat <vincent@bernat.im>
Mon, 17 Aug 2015 06:49:04 +0000 (08:49 +0200)
The main goal of this change is to implement a caching system for
netlink and avoiding the full scan done when a change was
triggered. Implementing a netlink cache is not as funny as it seems and
therefore, it seems just better to use libnl3.

Licensing issues are explained in README. People concerned with that
should just use dynamic linking. An embedded copy of libnl3 is also
provided, just like libevent.

17 files changed:
.gitmodules
NEWS
README.md
configure.ac
debian/control
debian/copyright
libnl [new submodule]
m4/libnl3.m4 [new file with mode: 0644]
src/daemon/Makefile.am
src/daemon/event.c
src/daemon/interfaces-bsd.c
src/daemon/interfaces-linux.c
src/daemon/interfaces-solaris.c
src/daemon/lldpd.c
src/daemon/lldpd.h
src/daemon/netlink.c
src/daemon/priv.c

index 2119f124a0af19567ae2a20af4c4772bc7422bea..ec1560acfcf3db83bc9a3dfe7232c37b8ef2e226 100644 (file)
@@ -1,3 +1,6 @@
 [submodule "libevent"]
        path = libevent
        url = https://github.com/libevent/libevent.git
+[submodule "libnl"]
+       path = libnl
+       url = https://github.com/thom311/libnl.git
diff --git a/NEWS b/NEWS
index 52e8ca0d27c1258e65733f21818bc2a51b01cd0e..7a997aaed15d5689da1635259932dc7d040195cf 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,8 @@
+lldpd (0.8.0)
+  * Change:
+    + For Linux, switch to libnl3. Be aware of the licensing issues in
+      case of static linking.
+
 lldpd (0.7.16)
   * Change:
     + For Linux, 2.6.32 is now the minimal required kernel. When using
index b4e4d1179781e6c4b8170906feba311bd7bdead3..fae2094220841f03239d967e35bb8f7b0d453347 100644 (file)
--- a/README.md
+++ b/README.md
@@ -263,3 +263,7 @@ lldpd is distributed under the ISC license:
 Also, `lldpcli` will be linked to GNU Readline (which is GPL licensed)
 if available. To avoid this, use `--without-readline` as a configure
 option.
+
+libnl is LGPL licensed. To avoid any licensing issue, be sure to use
+dynamic linking (`--with-embedded-libnl=no`). If you use static
+linking, ensure that you understand the license implication of LGPL.
index e1ddb011e819730dbcd23ef34adc778b4781e70b..0de8274486f4be92e46c09ae1ccb895f5e06687f 100644 (file)
@@ -164,8 +164,9 @@ AC_CACHE_SAVE
 ## Unit tests wich check
 PKG_CHECK_MODULES([CHECK], [check >= 0.9.4], [have_check=yes], [have_check=no])
 
-# Libevent
+# Third-party libraries
 lldp_CHECK_LIBEVENT
+lldp_CHECK_LIBNL
 
 # Compatibility with pkg.m4 < 0.27
 m4_ifdef([PKG_INSTALLDIR], [PKG_INSTALLDIR],
@@ -319,6 +320,11 @@ if test x"$LIBEVENT_EMBEDDED" = x; then
 else
    libevent=embedded
 fi
+if test x"$LIBNL_EMBEDDED" = x; then
+   libnl="system (if needed)"
+else
+   libnl=embedded
+fi
 
 cat <<EOF
 
@@ -329,6 +335,7 @@ cat <<EOF
   C Compiler.....: $CC $LLDP_CFLAGS $LLDP_CPPFLAGS $CFLAGS $CPPFLAGS
   Linker.........: $LD $LLDP_LDFLAGS $LLDP_BIN_LDFLAGS $LDFLAGS $LIBS
   Libevent.......: $libevent
+  Libnl..........: $libnl
   Readline.......: ${ax_cv_lib_readline}
  Optional features:
   SNMP support...: ${with_snmp-no}
index 86c331f4746abe58918f468c3f9d393a256ea2ee..5046d418c22d6f6c853a49eafa8da8e99e1e3987 100644 (file)
@@ -10,6 +10,7 @@ Build-Depends: debhelper (>= 5),
                libxml2-dev,
                libjansson-dev | libjson-c-dev | libjson0-dev (>= 0.10),
                libevent-dev,
+               libnl-3-dev,
                libreadline-dev,
                libbsd-dev,
                pkg-config 
index 2eec55cd8aff49123102249b5194d34edd772a3d..cda0013601eaad776d65749de1861bfc315d1fa5 100644 (file)
@@ -18,6 +18,111 @@ License: ISC
  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
+Files: libnl/*
+Copyright: Copyright (c) Thomas Graf <tgraf@suug.ch>
+                         Baruch Even <baruch@ev-en.org>
+                         Petr Gotthard <petr.gotthard@siemens.com>
+                         Siemens AG Oesterreich
+                         Philip Craig <philipc@snapgear.com>
+                         Patrick McHardy <kaber@trash.net>
+                         Secure Computing Corporation
+License: LGPL-2.1
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published 
+ by the Free Software Foundation version 2.1 of the License.
+ .
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+ .
+ On Debian GNU/Linux systems, the complete text of the GNU Lesser General
+ Public License can be found in /usr/share/common-licenses/LGPL-2.1 
+Comment:
+ The content of this directory is shipped with lldpd but not used for
+ compilation. The system libnl3 is used in place of this embedded
+ copy.
+
+Files: libnl/src/idiag-socket-details.c
+       libnl/src/lib/utils.c
+       libnl/src/nl-addr-add.c
+       libnl/src/nl-addr-delete.c
+       libnl/src/nl-addr-list.c
+       libnl/src/nl-cls-add.c
+       libnl/src/nl-addr-add.c
+Copyright: (c) 2013 Sassano Systems LLC <joe@sassanosystems.com>
+           (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
+License: GPL-2
+Comment:
+ Those files are userland tools part of libnl-3. They are never
+ compiled.
+
+Files: libnl/include/linux-private/linux/*
+Copyright: 1991-2012 Linus Torvalds and many others
+License: GPL-2
+Comment:
+ It is believed that header files are an interface for user space and
+ therefore cannot be covered by copyright.
+
+License: GPL-2
+ This package is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2 as
+ published by the Free Software Foundation.
+ .
+ This package is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ .
+ You should have received a copy of the GNU General Public License
+ along with this package; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ .
+ On Debian systems, the complete text of the GNU General Public
+ License version 2 can be found in `/usr/share/common-licenses/GPL-2'.
+
+Files: libnl/include/netlink/xfrm/selector.h
+       libnl/include/netlink/xfrm/sa.h
+       libnl/include/netlink/xfrm/ae.h
+       libnl/include/netlink/xfrm/sp.h
+       libnl/include/netlink/xfrm/template.h
+       libnl/include/netlink/xfrm/lifetime.h
+       libnl/lib/xfrm/sa.c
+       libnl/lib/xfrm/template.c
+       libnl/lib/xfrm/ae.c
+       libnl/lib/xfrm/sp.c
+       libnl/lib/xfrm/selector.c
+       libnl/lib/xfrm/lifetime.c
+Copyright: Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+License: BSD-3-clause
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ .
+   Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+ .
+   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.
+ .
+   Neither the name of Texas Instruments Incorporated 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 COPYRIGHT HOLDERS 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 COPYRIGHT
+ OWNER 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.
+
 Files: libevent/*
 Copyright: Copyright 2000-2007 Niels Provos <provos@citi.umich.edu>
            Copyright 2007-2012 Niels Provos and Nick Mathewson
diff --git a/libnl b/libnl
new file mode 160000 (submodule)
index 0000000..8f5c65b
--- /dev/null
+++ b/libnl
@@ -0,0 +1 @@
+Subproject commit 8f5c65beee10598eb3264494963773e7b44a85e5
diff --git a/m4/libnl3.m4 b/m4/libnl3.m4
new file mode 100644 (file)
index 0000000..efe6e07
--- /dev/null
@@ -0,0 +1,46 @@
+#
+# lldp_CHECK_LIBNL
+#
+
+AC_DEFUN([lldp_CHECK_LIBNL], [
+  # Do we require embedded libnl?
+  AC_ARG_WITH([embedded-libnl],
+    AS_HELP_STRING(
+      [--with-embedded-libnl],
+      [Use embedded libnl @<:@default=auto@:>@]
+  ), [], [with_embedded_libnl=auto])
+  if test x"$with_embedded_libnl" = x"yes"; then
+     LIBNL_EMBEDDED=1
+  else
+    # Check with pkg-config (3.2.7 is needed for nl_cache_mngr_alloc)
+    PKG_CHECK_MODULES([LIBNL], [libnl >= 3.2.7 libnl-route >= 3.2.7], [], [
+      # No appropriate version, let's use the shipped copy if possible
+      if test x"$with_embedded_libnl" = x"auto"; then
+        AC_MSG_NOTICE([using shipped libnl])
+        LIBNL_EMBEDDED=1
+      else
+        AC_MSG_ERROR([*** libnl not found])
+      fi
+    ])
+  fi
+
+  if test x"$LIBNL_EMBEDDED" != x; then
+    unset LIBNL_LIBS
+    LIBNL_CFLAGS="-I\$(top_srcdir)/libnl/include -I\$(top_builddir)/libnl/include"
+    LIBNL_LDFLAGS="\$(top_builddir)/libnl/lib/libnl-3.la \$(top_builddir)/libnl/lib/libnl-route-3.la"
+  fi
+
+  # Call ./configure in libnl. Need it for make dist...
+  libnl_configure_args="$libnl_configure_args --disable-pthreads"
+  libnl_configure_args="$libnl_configure_args --disable-cli"
+  libnl_configure_args="$libnl_configure_args --disable-debug"
+  libnl_configure_args="$libnl_configure_args --disable-shared"
+  libnl_configure_args="$libnl_configure_args --with-pic"
+  libnl_configure_args="$libnl_configure_args --enable-static"
+  lldp_CONFIG_SUBDIRS([libnl], [$libnl_configure_args])
+
+  AM_CONDITIONAL([LIBNL_EMBEDDED], [test x"$LIBNL_EMBEDDED" != x])
+  AC_SUBST([LIBNL_LIBS])
+  AC_SUBST([LIBNL_CFLAGS])
+  AC_SUBST([LIBNL_LDFLAGS])
+])
index 71de01af5c760a0078a61019e57c6fadd8472574..4fb515f531e3a0eb58e38218a9b9cde70e0631ba 100644 (file)
@@ -33,6 +33,11 @@ liblldpd_la_LIBADD   = \
        $(top_builddir)/src/libcommon-daemon-client.la \
        $(top_builddir)/src/libcommon-daemon-lib.la @LIBEVENT_LIBS@
 
+## lldpd
+lldpd_SOURCES = main.c
+lldpd_LDFLAGS = $(AM_LDFLAGS) $(LLDP_BIN_LDFLAGS)
+lldpd_LDADD   = liblldpd.la @LIBEVENT_LDFLAGS@
+
 if HOST_OS_LINUX
 liblldpd_la_SOURCES += \
        forward-linux.c \
@@ -40,6 +45,9 @@ liblldpd_la_SOURCES += \
        netlink.c \
        dmi-linux.c \
        priv-linux.c
+liblldpd_la_LIBADD += @LIBNL_LIBS@
+liblldpd_la_CFLAGS += @LIBNL_CFLAGS@
+lldpd_LDADD        += @LIBNL_LDFLAGS@
 endif
 if HOST_OS_DRAGONFLY
 liblldpd_la_SOURCES += \
@@ -94,11 +102,6 @@ liblldpd_la_SOURCES += \
        priv-bsd.c
 endif
 
-## lldpd
-lldpd_SOURCES = main.c
-lldpd_LDFLAGS = $(AM_LDFLAGS) $(LLDP_BIN_LDFLAGS)
-lldpd_LDADD   = liblldpd.la @LIBEVENT_LDFLAGS@
-
 # Add SNMP support if needed
 if USE_SNMP
 liblldpd_la_SOURCES += agent.c agent_priv.c agent.h
@@ -148,6 +151,15 @@ $(top_builddir)/libevent/libevent.la: $(top_srcdir)/libevent/*.c $(top_srcdir)/l
        (cd $(top_builddir)/libevent && $(MAKE))
 endif
 
+## libnl
+if HOST_OS_LINUX
+if LIBNL_EMBEDDED
+netlink.c: $(top_builddir)/libnl/lib/libnl-3.la $(top_builddir)/libnl/lib/libnl-route-3.la
+$(top_builddir)/libnl/lib/libnl-3.la $(top_builddir)/libnl/lib/libnl-route-3.la: $(top_srcdir)/libnl/lib/*.c $(top_srcdir)/libnl/lib/route/*.c
+       (cd $(top_builddir)/libnl && $(MAKE))
+endif
+endif
+
 ## systemd service file
 if HAVE_SYSTEMDSYSTEMUNITDIR
 systemdsystemunit_DATA = lldpd.service
index 3ee8f769f646b2dd1ebb7be43ca6a524e6254da0..50f7977f598f421cf2297fe46317150a2c40611a 100644 (file)
@@ -657,22 +657,26 @@ levent_iface_recv(evutil_socket_t fd, short what, void *arg)
        char buffer[EVENT_BUFFER];
        int n;
 
-       /* Discard the message */
-       while (1) {
-               n = read(fd, buffer, sizeof(buffer));
-               if (n == -1 &&
-                   (errno == EWOULDBLOCK ||
-                       errno == EAGAIN)) break;
-               if (n == -1) {
-                       log_warn("event",
-                           "unable to receive interface change notification message");
-                       return;
-               }
-               if (n == 0) {
-                       log_warnx("event",
-                           "end of file reached while getting interface change notification message");
-                       return;
+       if (cfg->g_iface_cb == NULL) {
+               /* Discard the message */
+               while (1) {
+                       n = read(fd, buffer, sizeof(buffer));
+                       if (n == -1 &&
+                           (errno == EWOULDBLOCK ||
+                               errno == EAGAIN)) break;
+                       if (n == -1) {
+                               log_warn("event",
+                                   "unable to receive interface change notification message");
+                               return;
+                       }
+                       if (n == 0) {
+                               log_warnx("event",
+                                   "end of file reached while getting interface change notification message");
+                               return;
+                       }
                }
+       } else {
+               cfg->g_iface_cb(cfg);
        }
 
        /* Schedule local port update. We don't run it right away because we may
index 7db99cf77a0900c863ab98c38eaabeb9fa968eee..4084d53b8f67a74f7254701949457cdf1639a0c8 100644 (file)
@@ -685,3 +685,8 @@ end:
        interfaces_free_addresses(addresses);
        if (ifaddrs) freeifaddrs(ifaddrs);
 }
+
+void
+interfaces_cleanup(struct lldpd *)
+{
+}
index c9342d48a77e3737119985f95b27b41e1052282c..30e4b8e21ba62205891d42986f08a038b5b310b8 100644 (file)
@@ -782,8 +782,8 @@ interfaces_update(struct lldpd *cfg)
        struct lldpd_hardware *hardware;
        struct interfaces_device_list *interfaces;
        struct interfaces_address_list *addresses;
-       interfaces = netlink_get_interfaces();
-       addresses = netlink_get_addresses();
+       interfaces = netlink_get_interfaces(cfg);
+       addresses = netlink_get_addresses(cfg);
        if (interfaces == NULL || addresses == NULL) {
                log_warnx("interfaces", "cannot update the list of local interfaces");
                goto end;
@@ -817,21 +817,13 @@ interfaces_update(struct lldpd *cfg)
                interfaces_helper_promisc(cfg, hardware);
        }
 
-       if (cfg->g_iface_event == NULL) {
-               int s;
-               log_debug("interfaces", "subscribe to netlink notifications");
-               s = netlink_subscribe_changes();
-               if (s == -1) {
-                       log_warnx("interfaces", "unable to subscribe to netlink notifications");
-                       goto end;
-               }
-               if (levent_iface_subscribe(cfg, s) == -1)
-                       close(s);
-               /* coverity[leaked_handle]
-                  s has been saved by levent_iface_subscribe */
-       }
-
 end:
        interfaces_free_devices(interfaces);
        interfaces_free_addresses(addresses);
 }
+
+void
+interfaces_cleanup(struct lldpd *cfg)
+{
+       netlink_cleanup(cfg);
+}
index 9a4f0049691e5935fcd4edd41aa3d2732b512c5c..f6f67639c3a4833b36213ce75fef3e0f14f421e5 100644 (file)
@@ -184,3 +184,8 @@ end:
        interfaces_free_devices(interfaces);
        interfaces_free_addresses(addresses);
 }
+
+void
+interfaces_cleanup(struct lldpd *)
+{
+}
index 876fcc9057cc44e256ab43ec6dd444caa2fbf149..f32cd05b45df039a268e3e04dc1bd4c193c868c6 100644 (file)
@@ -1154,6 +1154,9 @@ lldpd_exit(struct lldpd *cfg)
                lldpd_remote_cleanup(hardware, NULL, 1);
                lldpd_hardware_cleanup(cfg, hardware);
        }
+       interfaces_cleanup(cfg);
+
+       free(cfg->g_config.c_platform);
 }
 
 /**
index fded75f4249e13c671b06cede28aa3b762cfdcbe..cf6f2fa9953e96cdbebf27f30d09e01d0a03cfb5 100644 (file)
@@ -97,37 +97,7 @@ struct protocol {
 
 #define SMART_HIDDEN(port) (port->p_hidden_in)
 
-struct lldpd {
-       int                      g_sock;
-       struct event_base       *g_base;
-#ifdef USE_SNMP
-#endif
-
-       struct lldpd_config      g_config;
-
-       struct protocol         *g_protocols;
-       int                      g_lastrid;
-       struct event            *g_main_loop;
-       struct event            *g_cleanup_timer;
-#ifdef USE_SNMP
-       int                      g_snmp;
-       struct event            *g_snmp_timeout;
-       void                    *g_snmp_fds;
-       const char              *g_snmp_agentx;
-#endif /* USE_SNMP */
-
-       /* Unix socket handling */
-       const char              *g_ctlname;
-       int                      g_ctl;
-       struct event            *g_iface_event; /* Triggered when there is an interface change */
-       struct event            *g_iface_timer_event; /* Triggered one second after last interface change */
-
-       char                    *g_lsb_release;
-
-#define LOCAL_CHASSIS(cfg) ((struct lldpd_chassis *)(TAILQ_FIRST(&cfg->g_chassis)))
-       TAILQ_HEAD(, lldpd_chassis) g_chassis;
-       TAILQ_HEAD(, lldpd_hardware) g_hardware;
-};
+struct lldpd;
 
 /* lldpd.c */
 struct lldpd_hardware  *lldpd_get_hardware(struct lldpd *,
@@ -398,12 +368,14 @@ int interfaces_send_helper(struct lldpd *,
 
 void interfaces_setup_multicast(struct lldpd *, const char *, int);
 int interfaces_routing_enabled(struct lldpd *);
+void interfaces_cleanup(struct lldpd *);
 
 #ifdef HOST_OS_LINUX
 /* netlink.c */
-struct interfaces_device_list  *netlink_get_interfaces(void);
-struct interfaces_address_list *netlink_get_addresses(void);
-int netlink_subscribe_changes(void);
+struct interfaces_device_list  *netlink_get_interfaces(struct lldpd *);
+struct interfaces_address_list *netlink_get_addresses(struct lldpd *);
+void netlink_cleanup(struct lldpd *);
+struct lldpd_netlink;
 #endif
 
 #ifndef HOST_OS_LINUX
@@ -413,4 +385,41 @@ int ifbpf_phys_init(struct lldpd *, struct lldpd_hardware *);
 /* pattern.c */
 int pattern_match(char *, char *, int);
 
+struct lldpd {
+       int                      g_sock;
+       struct event_base       *g_base;
+#ifdef USE_SNMP
+#endif
+
+       struct lldpd_config      g_config;
+
+       struct protocol         *g_protocols;
+       int                      g_lastrid;
+       struct event            *g_main_loop;
+       struct event            *g_cleanup_timer;
+#ifdef USE_SNMP
+       int                      g_snmp;
+       struct event            *g_snmp_timeout;
+       void                    *g_snmp_fds;
+       const char              *g_snmp_agentx;
+#endif /* USE_SNMP */
+
+       /* Unix socket handling */
+       const char              *g_ctlname;
+       int                      g_ctl;
+       struct event            *g_iface_event; /* Triggered when there is an interface change */
+       struct event            *g_iface_timer_event; /* Triggered one second after last interface change */
+       void(*g_iface_cb)(struct lldpd *);            /* Called when there is an interface change */
+
+       char                    *g_lsb_release;
+
+#ifdef HOST_OS_LINUX
+       struct lldpd_netlink    *g_netlink;
+#endif
+
+#define LOCAL_CHASSIS(cfg) ((struct lldpd_chassis *)(TAILQ_FIRST(&cfg->g_chassis)))
+       TAILQ_HEAD(, lldpd_chassis) g_chassis;
+       TAILQ_HEAD(, lldpd_hardware) g_hardware;
+};
+
 #endif /* _LLDPD_H */
index 0e3dba8a73c36ac8bca600357a501066761502e2..694e4e34f0266a21d92feca9b2c0da457460a72c 100644 (file)
 
 #include "lldpd.h"
 
-#include <stdio.h>
-#include <unistd.h>
-#include <sys/types.h>
+#include <asm/types.h>
 #include <sys/socket.h>
-#include <net/if_arp.h>
 #include <linux/netlink.h>
-#include <linux/rtnetlink.h>
-
-#define NETLINK_BUFFER 4096
-
-struct netlink_req {
-    struct nlmsghdr hdr;
-    struct rtgenmsg gen;
+#include <net/if_arp.h>
+#include <netlink/socket.h>
+#include <netlink/route/addr.h>
+#include <netlink/route/link.h>
+#include <netlink/route/link/vlan.h>
+
+struct lldpd_netlink {
+       struct nl_cache_mngr *mngr;
+       struct nl_cache *addr;
+       struct nl_cache *link;
 };
 
 /**
- * Connect to netlink.
- *
- * Open a Netlink socket and connect to it.
- *
- * @param protocol Which protocol to use (eg NETLINK_ROUTE).
- * @param groups   Which groups we want to subscribe to
- * @return The opened socket or -1 on error.
+ * Callback when we get netlink updates.
  */
-static int
-netlink_connect(int protocol, unsigned groups)
+static void
+netlink_change_cb(struct lldpd *cfg)
 {
-    int s;
-    struct sockaddr_nl local = {
-        .nl_family = AF_NETLINK,
-        .nl_pid = getpid(),
-        .nl_groups = groups
-    };
-
-    /* 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 (groups && bind(s, (struct sockaddr *)&local, sizeof(struct sockaddr_nl)) < 0) {
-        log_warn("netlink", "unable to bind netlink socket");
-        close(s);
-        return -1;
-    }
-    return s;
+       int err;
+       log_debug("netlink", "netlink update received");
+       if ((err = nl_cache_mngr_data_ready(cfg->g_netlink->mngr)) < 0) {
+               log_warn("netlink", "unable to parse incoming netlink messages: %s",
+                   nl_geterror(err));
+       }
 }
 
 /**
- * Send a netlink message.
+ * Initialize netlink subsystem.
  *
- * The type of the message can be chosen as well the route family. The
- * mesage will always be NLM_F_REQUEST | NLM_F_DUMP.
+ * This can be called several times but will have effect only the first time.
  *
- * @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)
+netlink_initialize(struct lldpd *cfg)
 {
-    struct netlink_req req = {
-        .hdr = {
-            .nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)),
-            .nlmsg_type = type,
-            .nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP,
-            .nlmsg_seq = 1,
-            .nlmsg_pid = getpid() },
-        .gen = { .rtgen_family = family }
-    };
-    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;
-}
+       int err;
+       if (cfg->g_netlink) return 0;
 
-static void
-netlink_parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
-{
-       while (RTA_OK(rta, len)) {
-               if ((rta->rta_type <= max) && (!tb[rta->rta_type]))
-                       tb[rta->rta_type] = rta;
-               rta = RTA_NEXT(rta,len);
+       log_debug("netlink", "initialize netlink subsystem");
+       if ((cfg->g_netlink = calloc(sizeof(struct lldpd_netlink), 1)) == NULL) {
+               log_warn("netlink", "unable to allocate memory for netlink subsystem");
+               goto end;
        }
-}
 
-/**
- * Parse a `linkinfo` attributes.
- *
- * @param iff where to put the result
- * @param rta linkinfo attribute
- * @param len length of attributes
- */
-static void
-netlink_parse_linkinfo(struct interfaces_device *iff, struct rtattr *rta, int len)
-{
-       struct rtattr *link_info_attrs[IFLA_INFO_MAX+1] = {};
-       char *kind = NULL;
-
-       netlink_parse_rtattr(link_info_attrs, IFLA_INFO_MAX, rta, len);
-
-       if (link_info_attrs[IFLA_INFO_KIND]) {
-               kind = strdup(RTA_DATA(link_info_attrs[IFLA_INFO_KIND]));
-               if (kind) {
-                       if (!strcmp(kind, "vlan")) {
-                               log_debug("netlink", "interface %s is a VLAN",
-                                   iff->name);
-                               iff->type |= IFACE_VLAN_T;
-                       } else if (!strcmp(kind, "bridge")) {
-                               log_debug("netlink", "interface %s is a bridge",
-                                   iff->name);
-                               iff->type |= IFACE_BRIDGE_T;
-                       } else if (!strcmp(kind, "bond")) {
-                               log_debug("netlink", "interface %s is a bond",
-                                   iff->name);
-                               iff->type |= IFACE_BOND_T;
-                       }
-               }
+       if ((err = nl_cache_mngr_alloc(NULL, NETLINK_ROUTE, NL_AUTO_PROVIDE,
+                   &cfg->g_netlink->mngr)) < 0) {
+               log_warn("netlink", "unable to allocate cache manager: %s",
+                   nl_geterror(err));
+               goto end;
        }
 
-       if (kind && !strcmp(kind, "vlan") && link_info_attrs[IFLA_INFO_DATA]) {
-               struct rtattr *vlan_link_info_data_attrs[IFLA_VLAN_MAX+1] = {};
-               netlink_parse_rtattr(vlan_link_info_data_attrs, IFLA_VLAN_MAX,
-                   RTA_DATA(link_info_attrs[IFLA_INFO_DATA]),
-                   RTA_PAYLOAD(link_info_attrs[IFLA_INFO_DATA]));
+       if ((err = nl_cache_mngr_add(cfg->g_netlink->mngr,
+                   "route/link", NULL, NULL, &cfg->g_netlink->link)) < 0) {
+               log_warn("netlink", "unable to allocate route/link cache");
+               goto end;
+       }
+       if ((err = nl_cache_mngr_add(cfg->g_netlink->mngr,
+                   "route/addr", NULL, NULL, &cfg->g_netlink->addr)) < 0) {
+               log_warn("netlink", "unable to allocate route/addr cache");
+               goto end;
+       }
 
-               if (vlan_link_info_data_attrs[IFLA_VLAN_ID]) {
-                       iff->vlanid = *(uint16_t *)RTA_DATA(vlan_link_info_data_attrs[IFLA_VLAN_ID]);
-                       log_debug("netlink", "VLAN ID for interface %s is %d",
-                           iff->name, iff->vlanid);
-               }
+       cfg->g_iface_cb = netlink_change_cb;
+       if (levent_iface_subscribe(cfg, nl_cache_mngr_get_fd(cfg->g_netlink->mngr)) == -1) {
+               goto end;
        }
 
-       free(kind);
+       return 0;
+end:
+       netlink_cleanup(cfg);
+       return -1;
 }
 
 /**
- * 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
+ * Cleanup netlink subsystem.
  */
-static int
-netlink_parse_link(struct nlmsghdr *msg,
-    struct interfaces_device *iff)
+void
+netlink_cleanup(struct lldpd *cfg)
 {
-    struct ifinfomsg *ifi;
-    struct rtattr *attribute;
-    int len;
-    ifi = NLMSG_DATA(msg);
-    len = msg->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifinfomsg));
-
-    if (!((ifi->ifi_flags & IFF_UP) && (ifi->ifi_flags & IFF_RUNNING))) {
-        log_debug("netlink", "skip down interface at index %d",
-          ifi->ifi_index);
-        return -1;
-    }
-    if (ifi->ifi_type != ARPHRD_ETHER) {
-        log_debug("netlink", "skip non Ethernet interface at index %d",
-          ifi->ifi_index);
-        return -1;
-    }
-
-    iff->index = ifi->ifi_index;
-    iff->flags = ifi->ifi_flags;
-    iff->lower_idx = -1;
-    iff->upper_idx = -1;
-
-    for (attribute = IFLA_RTA(ifi);
-         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 "lower" interface */
-            iff->lower_idx = *(int*)RTA_DATA(attribute);
-            break;
-        case IFLA_MASTER:
-            /* Index of master interface */
-            iff->upper_idx = *(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;
-        case IFLA_LINKINFO:
-            netlink_parse_linkinfo(iff, RTA_DATA(attribute), RTA_PAYLOAD(attribute));
-            break;
-        default:
-            log_debug("netlink", "unhandled link 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;
+       if (cfg->g_netlink == NULL) return;
+       if (cfg->g_netlink->mngr != NULL) nl_cache_mngr_free(cfg->g_netlink->mngr);
+
+       free(cfg->g_netlink);
+       cfg->g_netlink = NULL;
 }
 
 /**
- * Parse a `address` netlink message.
+ * Parse a `link` 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
+ * @param link link object from cache
+ * @return parsed interface
  */
-static int
-netlink_parse_address(struct nlmsghdr *msg,
-    struct interfaces_address *ifa)
+static struct interfaces_device *
+netlink_parse_link(struct rtnl_link *link)
 {
-    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 address 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;
+       const char *name = rtnl_link_get_name(link);
+       if (name == NULL) {
+               log_debug("netlink", "skip unnamed interface");
+               return NULL;
+       }
+
+       unsigned int flags = rtnl_link_get_flags(link);
+       if (!((flags & IFF_UP) && (flags & IFF_RUNNING))) {
+               log_debug("netlink", "skip down interface %s", name);
+               return NULL;
+       }
+       if (rtnl_link_get_arptype(link) != ARPHRD_ETHER) {
+               log_debug("netlink", "skip non Ethernet interface %s", name);
+               return NULL;
+       }
+
+       struct interfaces_device *iff = calloc(1, sizeof(struct interfaces_device));
+       if (iff == NULL) {
+               log_warn("netlink", "no memory for a new interface");
+               return NULL;
+       }
+       iff->index = rtnl_link_get_ifindex(link);;
+       iff->flags = flags;
+       iff->lower_idx = rtnl_link_get_link(link);
+       iff->upper_idx = rtnl_link_get_master(link);
+       iff->name = strdup(name);
+       if (rtnl_link_get_ifalias(link) != NULL)
+               iff->alias = strdup(rtnl_link_get_ifalias(link));
+
+       struct nl_addr *mac = rtnl_link_get_addr(link);
+       if (mac) {
+               iff->address = malloc(nl_addr_get_len(mac));
+               if (iff->address)
+                       memcpy(iff->address,
+                           nl_addr_get_binary_addr(mac),
+                           nl_addr_get_len(mac));
+       }
+       if (!iff->address) {
+               log_info("netlink", "interface %d does not have a name or an address, skip",
+                   iff->index);
+               interfaces_free_device(iff);
+               return NULL;
+       }
+       iff->txqueue = rtnl_link_get_txqlen(link);
+       iff->mtu = rtnl_link_get_mtu(link);
+
+       const char *kind = rtnl_link_get_type(link);
+       if (kind) {
+               if (!strcmp(kind, "vlan")) {
+                       iff->type |= IFACE_VLAN_T;
+                       iff->vlanid = rtnl_link_vlan_get_id(link);
+                       log_debug("netlink", "interface %s is a VLAN (id=%d)",
+                           name, iff->vlanid);
+               } else if (!strcmp(kind, "bridge")) {
+                       iff->type |= IFACE_BRIDGE_T;
+                       log_debug("netlink", "interface %s is a bridge",
+                           name);
+               } else if (!strcmp(kind, "bond")) {
+                       iff->type |= IFACE_BOND_T;
+                       log_debug("netlink", "interface %s is a bond",
+                           name);
+               }
+       }
+
+       return iff;
 }
 
 /**
- * Receive netlink answer from the kernel.
+ * Parse a `address` netlink message.
  *
- * @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
+ * @param addr address object from cache
+ * @return parsed address
  */
-static int
-netlink_recv(int s,
-  struct interfaces_device_list *ifs,
-  struct interfaces_address_list *ifas)
+static struct interfaces_address *
+netlink_parse_address(struct rtnl_addr *addr)
 {
-    char reply[NETLINK_BUFFER] __attribute__ ((aligned));
-    int  end = 0;
-
-    struct interfaces_device *iff;
-    struct interfaces_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*)(void*)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 interfaces_device));
-                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
-                    interfaces_free_device(iff);
-                break;
-            case RTM_NEWADDR:
-                if (!ifas) break;
-                log_debug("netlink", "received address information");
-                ifa = calloc(1, sizeof(struct interfaces_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
-                    interfaces_free_address(ifa);
-                break;
-            default:
-                log_debug("netlink",
-                          "received unhandled message type %d (len: %d)",
-                          msg->nlmsg_type, msg->nlmsg_len);
-            }
-        }
-    }
-    return 0;
+       int family = rtnl_addr_get_family(addr);
+       switch (family) {
+       case AF_INET:
+       case AF_INET6: break;
+       default:
+               log_debug("netlink", "got a non IP address on if %d (family: %d)",
+                   rtnl_addr_get_ifindex(addr), family);
+               return NULL;
+       }
+
+       struct interfaces_address *ifa = calloc(1, sizeof(struct interfaces_address));
+       if (ifa == NULL) {
+               log_warn("netlink", "no memory for a new address");
+               return NULL;
+       }
+       ifa->index = rtnl_addr_get_ifindex(addr);
+       ifa->flags = rtnl_addr_get_flags(addr);
+
+       socklen_t len = sizeof(ifa->address);
+       int err = nl_addr_fill_sockaddr(rtnl_addr_get_local(addr),
+           (struct sockaddr *)&ifa->address, &len);
+       if (err < 0 || ifa->address.ss_family == AF_UNSPEC) {
+               log_debug("netlink", "no IP for interface %d",
+                   ifa->index);
+               interfaces_free_address(ifa);
+               return NULL;
+       }
+       return ifa;
 }
 
 /**
@@ -408,49 +230,52 @@ netlink_recv(int s,
  * @return a list of interfaces.
  */
 struct interfaces_device_list*
-netlink_get_interfaces()
+netlink_get_interfaces(struct lldpd *cfg)
 {
-    int s;
-    struct interfaces_device_list *ifs;
-    struct interfaces_device *iface1, *iface2;
-
-    if ((s = netlink_connect(NETLINK_ROUTE, 0)) == -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 interfaces_device_list));
-    if (ifs == NULL) {
-        log_warn("netlink", "not enough memory for interface list");
-        close(s);
-        return NULL;
-    }
-    TAILQ_INIT(ifs);
-    netlink_recv(s, ifs, NULL);
-
-    /* Fill out lower/upper */
-    TAILQ_FOREACH(iface1, ifs, next) {
-        if (iface1->upper_idx != -1 && iface1->upper_idx != iface1->index)
-            TAILQ_FOREACH(iface2, ifs, next) {
-                if (iface1->upper_idx == iface2->index) {
-                    iface1->upper = iface2;
-                    break;
-                }
-            }
-        if (iface1->lower_idx != -1 && iface1->lower_idx != iface1->index)
-            TAILQ_FOREACH(iface2, ifs, next) {
-                if (iface1->lower_idx == iface2->index) {
-                    iface1->lower = iface2;
-                    break;
-                }
-            }
-    }
-
-    close(s);
-    return ifs;
+       if (netlink_initialize(cfg) == -1) return NULL;
+
+       struct interfaces_device_list *ifs;
+
+       log_debug("netlink", "get the list of available interfaces");
+       ifs = malloc(sizeof(struct interfaces_device_list));
+       if (ifs == NULL) {
+               log_warn("netlink", "not enough memory for interface list");
+               return NULL;
+       }
+       TAILQ_INIT(ifs);
+
+       for (struct nl_object *link = nl_cache_get_first(cfg->g_netlink->link);
+            link != NULL;
+            link = nl_cache_get_next(link)) {
+               nl_object_get(link);
+               struct interfaces_device *iff = netlink_parse_link((struct rtnl_link *)link);
+               if (iff) TAILQ_INSERT_TAIL(ifs, iff, next);
+               nl_object_put(link);
+       }
+
+       struct interfaces_device *iface1, *iface2;
+       TAILQ_FOREACH(iface1, ifs, next) {
+               if (iface1->upper_idx != 0 && iface1->upper_idx != iface1->index)
+                       TAILQ_FOREACH(iface2, ifs, next) {
+                               if (iface1->upper_idx == iface2->index) {
+                                       log_debug("netlink", "%s is upper iface for %s",
+                                           iface2->name, iface1->name);
+                                       iface1->upper = iface2;
+                                       break;
+                               }
+                       }
+               if (iface1->lower_idx != 0 && iface1->lower_idx != iface1->index)
+                       TAILQ_FOREACH(iface2, ifs, next) {
+                               if (iface1->lower_idx == iface2->index) {
+                                       log_debug("netlink", "%s is lower iface for %s",
+                                           iface2->name, iface1->name);
+                                       iface1->lower = iface2;
+                                       break;
+                               }
+                       }
+       }
+
+       return ifs;
 }
 
 /**
@@ -459,53 +284,28 @@ netlink_get_interfaces()
  * @return a list of addresses.
  */
 struct interfaces_address_list*
-netlink_get_addresses()
+netlink_get_addresses(struct lldpd *cfg)
 {
-    int s;
-    struct interfaces_address_list *ifaddrs;
-
-    if ((s = netlink_connect(NETLINK_ROUTE, 0)) == -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 interfaces_address_list));
-    if (ifaddrs == NULL) {
-        log_warn("netlink", "not enough memory for address list");
-        close(s);
-        return NULL;
-    }
-    TAILQ_INIT(ifaddrs);
-    netlink_recv(s, NULL, ifaddrs);
-
-    close(s);
-    return ifaddrs;
-}
+       if (netlink_initialize(cfg) == -1) return NULL;
 
-static int
-netlink_group_mask(int group)
-{
-       return group ? (1 << (group - 1)) : 0;
-}
+       struct interfaces_address_list *ifaddrs;
 
-/**
- * Subscribe to link changes.
- *
- * @return The socket we should listen to for changes.
- */
-int
-netlink_subscribe_changes()
-{
-    unsigned int groups;
-
-    log_debug("netlink", "listening on interface changes");
-
-    groups = netlink_group_mask(RTNLGRP_LINK) |
-               netlink_group_mask(RTNLGRP_IPV4_IFADDR) |
-               netlink_group_mask(RTNLGRP_IPV6_IFADDR);
+       log_debug("netlink", "get the list of available addresses");
+       ifaddrs = malloc(sizeof(struct interfaces_address_list));
+       if (ifaddrs == NULL) {
+               log_warn("netlink", "not enough memory for address list");
+               return NULL;
+       }
+       TAILQ_INIT(ifaddrs);
+
+       for (struct nl_object *addr = nl_cache_get_first(cfg->g_netlink->addr);
+            addr != NULL;
+            addr = nl_cache_get_next(addr)) {
+               nl_object_get(addr);
+               struct interfaces_address *ifa = netlink_parse_address((struct rtnl_addr *)addr);
+               if (ifa) TAILQ_INSERT_TAIL(ifaddrs, ifa, next);
+               nl_object_put(addr);
+       }
 
-    return netlink_connect(NETLINK_ROUTE, groups);
+       return ifaddrs;
 }
index 7b3c5eca93f6a3c20fb08afd67486d219ba9a13f..5345f230f34069db49ec2b95284f0f6e70d1893d 100644 (file)
@@ -117,7 +117,7 @@ int
 priv_iface_init(int index, char *iface)
 {
        int rc;
-       char dev[IFNAMSIZ];
+       char dev[IFNAMSIZ] = {};
        enum priv_cmd cmd = PRIV_IFACE_INIT;
        must_write(PRIV_UNPRIVILEGED, &cmd, sizeof(enum priv_cmd));
        must_write(PRIV_UNPRIVILEGED, &index, sizeof(int));