]> git.ipfire.org Git - thirdparty/lldpd.git/commitdiff
netlink: remove use of libnl3
authorVincent Bernat <vincent@bernat.im>
Fri, 1 Jan 2016 07:54:32 +0000 (08:54 +0100)
committerVincent Bernat <vincent@bernat.im>
Fri, 1 Jan 2016 18:11:48 +0000 (19:11 +0100)
Use netlink implementation from 0.7.19 instead but manage a cache
ourselves. The changes are quite minimal compared to the implementation
in 0.7.19. We handle deletion and updates. The use of linked list may be
problematic performance-wise.

When an interface goes down then up, no PDU is scheduled to be sent
again. This bug was already present in the previous implementation and
should be a regression of 36080c.

18 files changed:
.gitmodules
Makefile.am
NEWS
README.md
configure.ac
debian/control
debian/copyright
libnl [deleted submodule]
m4/libnl3.m4 [deleted file]
redhat/lldpd.spec
src/daemon/Makefile.am
src/daemon/event.c
src/daemon/interfaces-bsd.c
src/daemon/interfaces-linux.c
src/daemon/interfaces.c
src/daemon/lldpd.h
src/daemon/netlink.c
tests/Makefile.am

index ec1560acfcf3db83bc9a3dfe7232c37b8ef2e226..2119f124a0af19567ae2a20af4c4772bc7422bea 100644 (file)
@@ -1,6 +1,3 @@
 [submodule "libevent"]
        path = libevent
        url = https://github.com/libevent/libevent.git
-[submodule "libnl"]
-       path = libnl
-       url = https://github.com/thom311/libnl.git
index 78978d054e1615b70d8598b8f1ad6e98782d9bfa..ea936ec1f021c352ba4d30781f58c1b24c760432 100644 (file)
@@ -7,10 +7,6 @@ EXTRA_DIST   = $(DX_CONFIG) include get-version autogen.sh
 DIST_SUBDIRS = $(SUBDIRS) libevent
 DISTCLEANFILES = ChangeLog
 
-if HOST_OS_LINUX
-DIST_SUBDIRS += libnl
-endif
-
 dist_doc_DATA = README.md NEWS CONTRIBUTE.md
 doc_DATA = ChangeLog
 
diff --git a/NEWS b/NEWS
index 5fd6be2facd8c2a8c5273cd645e61762c2110aba..7069dba2bcfd44da9ed2c74ba390f325a96c0e94 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,8 @@
+lldpd (0.9.0)
+  * Change:
+    + Don't rely on libnl3 for netlink. Reimplement a minimal,
+      API-compatible, version.
+
 lldpd (0.8.0, never released)
   * Fix:
     + Fix a buffer overflow when receiving a too large management
index 64477cc6a5c9b90e0911b794df2c0caa7d03f98a..dde80a97c41f162e22f5f7bcd0e7a2302e976132 100644 (file)
--- a/README.md
+++ b/README.md
@@ -285,7 +285,3 @@ 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 3f7dd0ce856b5f8d0ecfa82d4db16b09af666e3b..796cd042243df7dad97f28ca5c538309fdf01632 100644 (file)
@@ -181,7 +181,6 @@ PKG_CHECK_MODULES([CHECK], [check >= 0.9.4], [have_check=yes], [have_check=no])
 
 # Third-party libraries
 lldp_CHECK_LIBEVENT
-lldp_CHECK_LIBNL
 
 # Compatibility with pkg.m4 < 0.27
 m4_ifdef([PKG_INSTALLDIR], [PKG_INSTALLDIR],
@@ -350,11 +349,6 @@ 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
 
@@ -365,7 +359,6 @@ 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 259f69d593926118e1c119abc96944d67aa1f657..2fbdf85fe25224aa83b3953d56611b9079ae5226 100644 (file)
@@ -10,8 +10,6 @@ Build-Depends: debhelper (>= 5),
                libxml2-dev,
                libjansson-dev | libjson-c-dev | libjson0-dev (>= 0.10),
                libevent-dev (>= 2.0.5),
-               libnl-3-dev (>= 3.2.7), libnl-route-3-dev (>= 3.2.7),
-               bison, flex,
                libreadline-dev,
                libbsd-dev,
                pkg-config,
index cda0013601eaad776d65749de1861bfc315d1fa5..2eec55cd8aff49123102249b5194d34edd772a3d 100644 (file)
@@ -18,111 +18,6 @@ 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
deleted file mode 160000 (submodule)
index 7b9671c..0000000
--- a/libnl
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 7b9671c6d88fce7e5261e4caa1a9a17f7136eba3
diff --git a/m4/libnl3.m4 b/m4/libnl3.m4
deleted file mode 100644 (file)
index 486198e..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-#
-# lldp_CHECK_LIBNL
-#
-
-AC_DEFUN([lldp_CHECK_LIBNL], [
- # Do we require embedded libnl?
- if test x"$os" = x"Linux"; then
-  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.0 >= 3.2.7 libnl-route-3.0 >= 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])
- fi
-
- AM_CONDITIONAL([LIBNL_EMBEDDED], [test x"$LIBNL_EMBEDDED" != x])
- AC_SUBST([LIBNL_LIBS])
- AC_SUBST([LIBNL_CFLAGS])
- AC_SUBST([LIBNL_LDFLAGS])
-])
index 07d11cc5b1c44b478f8b08aa678bd0ce74120143..0bda7eff7c67608ce31ac43b8194bd766e9577d3 100644 (file)
 %bcond_without system_libevent
 %endif
 
-# On RHEL < 7, use embedded libnl.
-%if 0%{?rhel_version} > 0 && 0%{?rhel_version} < 700 || 0%{?centos_version} > 0 && 0%{?centos_version} < 600 || 0%{?suse_version} > 0 && 0%{?suse_version} < 1200
-%bcond_with system_libnl
-%else
-%bcond_without system_libnl
-%endif
-
 %define lldpd_user _lldpd
 %define lldpd_group _lldpd
 %define lldpd_chroot /var/run/lldpd
@@ -65,11 +58,6 @@ BuildRequires: pkgconfig
 %if %{with system_libevent}
 BuildRequires: libevent-devel
 %endif
-%if %{with system_libnl}
-BuildRequires: libnl3-devel
-%endif
-BuildRequires: flex
-BuildRequires: bison
 BuildRequires: readline-devel
 %if %{with snmp}
 BuildRequires: net-snmp-devel
index c14d83aafa2abd611b640785b1d152dbb84ee560..b4734d4ae4f8ffa677bae29c98d0547439d9c439 100644 (file)
@@ -45,9 +45,6 @@ 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 += \
@@ -151,15 +148,6 @@ $(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) CFLAGS="$(CFLAGS) -Wno-error=unused-variable")
-endif
-endif
-
 ## systemd service file
 if HAVE_SYSTEMDSYSTEMUNITDIR
 systemdsystemunit_DATA = lldpd.service
index bfcc7bb32f09ab3d8f0f37e14da9d375dcd37b38..4846478ff4f6396d141f37665b6c80deff8b7584 100644 (file)
@@ -732,7 +732,8 @@ levent_iface_subscribe(struct lldpd *cfg, int socket)
 {
        log_debug("event", "subscribe to interface changes from socket %d",
            socket);
-       levent_make_socket_nonblocking(socket);
+       if (cfg->g_iface_cb == NULL)
+               levent_make_socket_nonblocking(socket);
        cfg->g_iface_event = event_new(cfg->g_base, socket,
            EV_READ | EV_PERSIST, levent_iface_recv, cfg);
        if (cfg->g_iface_event == NULL) {
index 5b45c33aee1d18dfcbbf9ec73498ee1e8f207682..9cee2e6a48fecd892d6b8a842af7507cd585f588 100644 (file)
@@ -351,7 +351,7 @@ ifbsd_blacklist(struct lldpd *cfg,
                if (iface->name[i] == '\0') {
                        log_debug("interfaces", "skip %s: AirDrop interface",
                            iface->name);
-                       iface->flags = 0;
+                       iface->ignore = 1;
                }
        }
 #endif
index 1e5ba9a48dd595c4d09e276de66943537c9ab369..9803d30f22f6fb0b76d7bf8bd687e6185e6335bc 100644 (file)
@@ -539,7 +539,7 @@ iflinux_handle_bond(struct lldpd *cfg, struct interfaces_device_list *interfaces
        struct bond_master *bmaster;
        TAILQ_FOREACH(iface, interfaces, next) {
                if (!(iface->type & IFACE_PHYSICAL_T)) continue;
-               if (!iface->flags) continue;
+               if (iface->ignore) continue;
                if (!iface->upper || !(iface->upper->type & IFACE_BOND_T)) continue;
 
                master = iface->upper;
@@ -583,7 +583,7 @@ iflinux_handle_bond(struct lldpd *cfg, struct interfaces_device_list *interfaces
                }
 
                hardware->h_flags = iface->flags;
-               iface->flags = 0;
+               iface->ignore = 1;
 
                /* Get local address */
                memcpy(&hardware->h_lladdr, iface->address, ETHER_ADDR_LEN);
@@ -776,7 +776,7 @@ interfaces_update(struct lldpd *cfg)
        addresses = netlink_get_addresses(cfg);
        if (interfaces == NULL || addresses == NULL) {
                log_warnx("interfaces", "cannot update the list of local interfaces");
-               goto end;
+               return;
        }
 
        /* Add missing bits to list of interfaces */
@@ -806,10 +806,6 @@ interfaces_update(struct lldpd *cfg)
                iflinux_macphy(hardware);
                interfaces_helper_promisc(cfg, hardware);
        }
-
-end:
-       interfaces_free_devices(interfaces);
-       interfaces_free_addresses(addresses);
 }
 
 void
index 73f949a5fc2104757c234cf60d2479f6ba164f3f..ec817213eb9b17555d08f302f702ac3266782c07 100644 (file)
@@ -170,7 +170,7 @@ interfaces_helper_whitelist(struct lldpd *cfg,
                switch (m) {
                case 0:
                        log_debug("interfaces", "blacklist %s", iface->name);
-                       iface->flags = 0;
+                       iface->ignore = 1;
                        continue;
                case 2:
                        log_debug("interfaces", "whitelist %s (consider it as a physical interface)",
@@ -273,7 +273,7 @@ interfaces_helper_vlan(struct lldpd *cfg,
        struct interfaces_device *iface;
 
        TAILQ_FOREACH(iface, interfaces, next) {
-               if (!iface->flags)
+               if (iface->ignore)
                        continue;
                if (!(iface->type & IFACE_VLAN_T))
                        continue;
@@ -563,7 +563,7 @@ interfaces_helper_physical(struct lldpd *cfg,
 
        TAILQ_FOREACH(iface, interfaces, next) {
                if (!(iface->type & IFACE_PHYSICAL_T)) continue;
-               if (!iface->flags) continue;
+               if (iface->ignore) continue;
 
                log_debug("interfaces", "%s is an acceptable ethernet device",
                    iface->name);
@@ -595,7 +595,7 @@ interfaces_helper_physical(struct lldpd *cfg,
                }
 
                hardware->h_flags = iface->flags;   /* Should be non-zero */
-               iface->flags = 0;                   /* Future handlers
+               iface->ignore = 1;                  /* Future handlers
                                                       don't have to
                                                       care about this
                                                       interface. */
index 06ecf3bf8b14f1f8693f4d3201e5a8a0f49420f3..fd0d09e411281a4ed18bbfffbd5b9a101132fb13 100644 (file)
@@ -304,6 +304,7 @@ void     interfaces_update(struct lldpd *);
 #define IFACE_WIRELESS_T (1 << 4) /* Wireless interface */
 struct interfaces_device {
        TAILQ_ENTRY(interfaces_device) next;
+       int   ignore;           /* Ignore this interface */
        int   index;            /* Index */
        char *name;             /* Name */
        char *alias;            /* Alias */
index 06a370da24ecb4be3b0e8eeb00bc1bf174cea87e..720af3838d970a76faa164dfa157cf3ce60daea1 100644 (file)
 
 #include "lldpd.h"
 
-#include <asm/types.h>
+#include <errno.h>
 #include <sys/socket.h>
-#include <linux/netlink.h>
+#include <netdb.h>
 #include <net/if_arp.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
+#define NETLINK_BUFFER 4096
 
-#if defined(__clang__)
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdocumentation"
-#endif
-#include <netlink/socket.h>
-#include <netlink/route/addr.h>
-#include <netlink/route/link.h>
-#include <netlink/route/link/vlan.h>
-#if defined(__clang__)
-#pragma clang diagnostic pop
-#endif
+struct netlink_req {
+       struct nlmsghdr hdr;
+       struct rtgenmsg gen;
+};
 
 struct lldpd_netlink {
-       struct nl_cache_mngr *mngr;
-       struct nl_cache *addr;
-       struct nl_cache *link;
+       int nl_socket;
+       /* Cache */
+       struct interfaces_device_list *devices;
+       struct interfaces_address_list *addresses;
 };
 
 /**
- * Callback when we get netlink updates.
+ * 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.
  */
-static void
-netlink_change_cb(struct lldpd *cfg)
+static int
+netlink_connect(int protocol, unsigned groups)
 {
-       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));
+       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;
 }
 
 /**
- * Initialize netlink subsystem.
+ * Send a netlink message.
  *
- * This can be called several times but will have effect only the first time.
+ * 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_initialize(struct lldpd *cfg)
+netlink_send(int s, int type, int family, int seq)
 {
-       int err;
-       if (cfg->g_netlink) return 0;
-
-       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;
-       }
-
-       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;
+       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 = seq,
+                       .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;
        }
 
-       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;
-       }
+       return 0;
+}
 
-       cfg->g_iface_cb = netlink_change_cb;
-       if (levent_iface_subscribe(cfg, nl_cache_mngr_get_fd(cfg->g_netlink->mngr)) == -1) {
-               goto end;
+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);
        }
-
-       return 0;
-end:
-       netlink_cleanup(cfg);
-       return -1;
 }
 
 /**
- * Cleanup netlink subsystem.
+ * Parse a `linkinfo` attributes.
+ *
+ * @param iff where to put the result
+ * @param rta linkinfo attribute
+ * @param len length of attributes
  */
-void
-netlink_cleanup(struct lldpd *cfg)
+static void
+netlink_parse_linkinfo(struct interfaces_device *iff, struct rtattr *rta, int len)
 {
-       if (cfg->g_netlink == NULL) return;
-       if (cfg->g_netlink->mngr != NULL) nl_cache_mngr_free(cfg->g_netlink->mngr);
+       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;
+                       }
+               }
+       }
 
-       free(cfg->g_netlink);
-       cfg->g_netlink = NULL;
+       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 (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);
+               }
+       }
+
+       free(kind);
 }
 
 /**
  * Parse a `link` netlink message.
  *
- * @param link link object from cache
- * @return parsed interface
+ * @param msg  message to be parsed
+ * @param iff  where to put the result
+ * return 0 if the interface is worth it, -1 otherwise
  */
-static struct interfaces_device *
-netlink_parse_link(struct rtnl_link *link)
+static int
+netlink_parse_link(struct nlmsghdr *msg,
+    struct interfaces_device *iff)
 {
-       const char *name = rtnl_link_get_name(link);
-       if (name == NULL) {
-               log_debug("netlink", "skip unnamed interface");
-               return NULL;
+       struct ifinfomsg *ifi;
+       struct rtattr *attribute;
+       int len;
+       ifi = NLMSG_DATA(msg);
+       len = msg->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifinfomsg));
+
+       if (ifi->ifi_type != ARPHRD_ETHER) {
+               log_debug("netlink", "skip non Ethernet interface at index %d",
+                   ifi->ifi_index);
+               return -1;
        }
 
-       unsigned int flags = rtnl_link_get_flags(link);
-       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));
+       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->address) {
-               log_info("netlink", "interface %d does not have a MAC address, skip",
+       if (!iff->name || !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 -1;
        }
 
-       return iff;
+       log_debug("netlink", "parsed link %d (%s, flags: %d)",
+           iff->index, iff->name, iff->flags);
+       return 0;
 }
 
 /**
  * Parse a `address` netlink message.
  *
- * @param addr address object from cache
- * @return parsed address
+ * @param msg  message to be parsed
+ * @param ifa  where to put the result
+ * return 0 if the address is worth it, -1 otherwise
  */
-static struct interfaces_address *
-netlink_parse_address(struct rtnl_addr *addr)
+static int
+netlink_parse_address(struct nlmsghdr *msg,
+    struct interfaces_address *ifa)
 {
-       int family = rtnl_addr_get_family(addr);
-       switch (family) {
+       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)",
-                   rtnl_addr_get_ifindex(addr), family);
-               return NULL;
+                   ifa->index, ifi->ifa_family);
+               return -1;
        }
 
-       struct interfaces_address *ifa = calloc(1, sizeof(struct interfaces_address));
-       if (ifa == NULL) {
-               log_warn("netlink", "no memory for a new address");
-               return NULL;
+       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;
+                               memset(&ip, 0, sizeof(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;
+                               memset(&ip6, 0, sizeof(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;
+               }
        }
-       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) {
+       if (ifa->address.ss_family == AF_UNSPEC) {
                log_debug("netlink", "no IP for interface %d",
                    ifa->index);
-               interfaces_free_address(ifa);
-               return NULL;
+               return -1;
        }
-       return ifa;
+       return 0;
 }
 
 /**
- * Receive the list of interfaces.
+ * Receive netlink answer from the kernel.
  *
- * @return a list of interfaces.
+ * @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
  */
-struct interfaces_device_list*
-netlink_get_interfaces(struct lldpd *cfg)
+static int
+netlink_recv(int s,
+    struct interfaces_device_list *ifs,
+    struct interfaces_address_list *ifas)
 {
-       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);
+       char reply[NETLINK_BUFFER] __attribute__ ((aligned));
+       int end = 0;
+       int link_update = 0;
+
+       struct interfaces_device *ifdold;
+       struct interfaces_device *ifdnew;
+       struct interfaces_address *ifaold;
+       struct interfaces_address *ifanew;
+       char addr[INET6_ADDRSTRLEN + 1];
+
+       while (!end) {
+               ssize_t 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) {
+                       if (errno == EAGAIN || errno == EWOULDBLOCK) {
+                               log_debug("netlink", "should have received something, but didn't");
+                               return 0;
+                       }
+                       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)) {
+                       if (!(msg->nlmsg_flags & NLM_F_MULTI))
+                               end = 1;
+                       switch (msg->nlmsg_type) {
+                       case NLMSG_DONE:
+                               log_debug("netlink", "received done message");
+                               end = 1;
+                               break;
+                       case RTM_NEWLINK:
+                       case RTM_DELLINK:
+                               if (!ifs) break;
+                               log_debug("netlink", "received link information");
+                               ifdnew = calloc(1, sizeof(struct interfaces_device));
+                               if (ifdnew == NULL) {
+                                       log_warn("netlink", "not enough memory for another interface, give up what we have");
+                                       goto end;
+                               }
+                               if (netlink_parse_link(msg, ifdnew) == 0) {
+                                       /* We need to find if we already have this interface */
+                                       TAILQ_FOREACH(ifdold, ifs, next) {
+                                               if (ifdold->index == ifdnew->index) break;
+                                       }
+                                       if (msg->nlmsg_type == RTM_NEWLINK) {
+                                               if (ifdold == NULL) {
+                                                       log_debug("netlink", "interface %s is new",
+                                                           ifdnew->name);
+                                                       TAILQ_INSERT_TAIL(ifs, ifdnew, next);
+                                               } else {
+                                                       log_debug("netlink", "interface %s/%s is updated",
+                                                           ifdold->name, ifdnew->name);
+                                                       TAILQ_INSERT_AFTER(ifs, ifdold, ifdnew, next);
+                                                       TAILQ_REMOVE(ifs, ifdold, next);
+                                                       interfaces_free_device(ifdold);
+                                               }
+                                       } else {
+                                               if (ifdold == NULL) {
+                                                       log_warnx("netlink",
+                                                           "removal request for %s, but no knowledge of it",
+                                                               ifdnew->name);
+                                               } else {
+                                                       log_debug("netlink", "interface %s is to be removed",
+                                                           ifdold->name);
+                                                       TAILQ_REMOVE(ifs, ifdold, next);
+                                                       interfaces_free_device(ifdold);
+                                               }
+                                               interfaces_free_device(ifdnew);
+                                       }
+                                       link_update = 1;
+                               } else {
+                                       interfaces_free_device(ifdnew);
+                               }
+                               break;
+                       case RTM_NEWADDR:
+                       case RTM_DELADDR:
+                               if (!ifas) break;
+                               log_debug("netlink", "received address information");
+                               ifanew = calloc(1, sizeof(struct interfaces_address));
+                               if (ifanew == NULL) {
+                                       log_warn("netlink", "not enough memory for another address, give what we have");
+                                       goto end;
+                               }
+                               if (netlink_parse_address(msg, ifanew) == 0) {
+                                       TAILQ_FOREACH(ifaold, ifas, next) {
+                                               if ((ifaold->index == ifanew->index) &&
+                                                   !memcmp(&ifaold->address, &ifanew->address,
+                                                       sizeof(ifaold->address))) continue;
+                                       }
+                                       if (getnameinfo((struct sockaddr *)&ifanew->address,
+                                               sizeof(ifanew->address),
+                                               addr, sizeof(addr),
+                                               NULL, 0, NI_NUMERICHOST) != 0) {
+                                               strlcpy(addr, "(unknown)", sizeof(addr));
+                                       }
 
-       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);
+                                       if (msg->nlmsg_type == RTM_NEWADDR) {
+                                               if (ifaold == NULL) {
+                                                       log_debug("netlink", "new address %s%%%d",
+                                                           addr, ifanew->index);
+                                                       TAILQ_INSERT_TAIL(ifas, ifanew, next);
+                                               } else {
+                                                       log_debug("netlink", "updated address %s%%%d",
+                                                           addr, ifaold->index);
+                                                       TAILQ_INSERT_AFTER(ifas, ifaold, ifanew, next);
+                                                       TAILQ_REMOVE(ifas, ifaold, next);
+                                                       interfaces_free_address(ifaold);
+                                               }
+                                       } else {
+                                               if (ifaold == NULL) {
+                                                       log_warnx("netlink",
+                                                           "removal request for address of %s%%%d, but no knowledge of it",
+                                                           addr, ifanew->index);
+                                               } else {
+                                                       log_debug("netlink", "address %s%%%d is to be removed",
+                                                           addr, ifaold->index);
+                                                       TAILQ_REMOVE(ifas, ifaold, next);
+                                                       interfaces_free_address(ifaold);
+                                               }
+                                               interfaces_free_address(ifanew);
+                                       }
+                               } else {
+                                       interfaces_free_address(ifanew);
+                               }
+                               break;
+                       default:
+                               log_debug("netlink",
+                                   "received unhandled message type %d (len: %d)",
+                                   msg->nlmsg_type, msg->nlmsg_len);
+                       }
+               }
        }
-
-       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;
+end:
+       if (link_update) {
+               /* Fill out lower/upper */
+               struct interfaces_device *iface1, *iface2;
+               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;
+                                       }
                                }
+                       } else {
+                               iface1->upper = NULL;
                        }
-               if (iface1->lower_idx != 0 && iface1->lower_idx != iface1->index)
-                       TAILQ_FOREACH(iface2, ifs, next) {
-                               if (iface1->lower_idx == iface2->index) {
-                                       if (iface2->lower_idx == iface1->index) {
-                                               log_debug("netlink", "%s and %s are peered together",
-                                                   iface1->name, iface2->name);
-                                               /* Workaround a bug introduced in Linux 4.1 */
-                                               iface2->lower_idx = iface2->index;
-                                               iface1->lower_idx = iface1->index;
-                                       } else {
-                                               log_debug("netlink", "%s is lower iface for %s",
-                                                   iface2->name, iface1->name);
-                                               iface1->lower = iface2;
+                       if (iface1->lower_idx != -1 && iface1->lower_idx != iface1->index) {
+                               TAILQ_FOREACH(iface2, ifs, next) {
+                                       if (iface1->lower_idx == iface2->index) {
+                                               if (iface2->lower_idx == iface1->index) {
+                                                       /* Workaround a bug introduced in Linux 4.1 */
+                                                       iface2->lower_idx = iface2->index;
+                                                       iface1->lower_idx = iface1->index;
+                                               } else iface1->lower = iface2;
+                                               break;
                                        }
-                                       break;
                                }
+                       } else {
+                               iface1->lower = NULL;
                        }
+               }
        }
+       return 0;
+}
 
-       return ifs;
+static int
+netlink_group_mask(int group)
+{
+       return group ? (1 << (group - 1)) : 0;
 }
 
 /**
- * Receive the list of addresses.
+ * Subscribe to link changes.
  *
- * @return a list of addresses.
+ * @return The socket we should listen to for changes.
  */
-struct interfaces_address_list*
-netlink_get_addresses(struct lldpd *cfg)
+int
+netlink_subscribe_changes()
 {
-       if (netlink_initialize(cfg) == -1) return NULL;
+       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);
+
+       return netlink_connect(NETLINK_ROUTE, groups);
+}
+
+/**
+ * Receive changes from netlink */
+static void
+netlink_change_cb(struct lldpd *cfg)
+{
+       if (cfg->g_netlink == NULL)
+               return;
+       netlink_recv(cfg->g_netlink->nl_socket,
+           cfg->g_netlink->devices,
+           cfg->g_netlink->addresses);
+}
 
-       struct interfaces_address_list *ifaddrs;
+/**
+ * Initialize netlink subsystem.
+ *
+ * This can be called several times but will have effect only the first time.
+ *
+ * @return 0 on success, -1 otherwise
+ */
+static int
+netlink_initialize(struct lldpd *cfg)
+{
+       if (cfg->g_netlink) return 0;
 
-       log_debug("netlink", "get the list of available addresses");
-       ifaddrs = malloc(sizeof(struct interfaces_address_list));
+       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;
+       }
+
+       /* Connect to netlink (by requesting to get notified on updates) and
+        * request updated information right now */
+       int s = cfg->g_netlink->nl_socket = netlink_subscribe_changes();
+
+       struct interfaces_address_list *ifaddrs = cfg->g_netlink->addresses =
+           malloc(sizeof(struct interfaces_address_list));
        if (ifaddrs == NULL) {
                log_warn("netlink", "not enough memory for address list");
-               return NULL;
+               goto end;
        }
        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);
+       struct interfaces_device_list *ifs = cfg->g_netlink->devices =
+           malloc(sizeof(struct interfaces_device_list));
+       if (ifs == NULL) {
+               log_warn("netlink", "not enough memory for interface list");
+               goto end;
+       }
+       TAILQ_INIT(ifs);
+
+       if (netlink_send(s, RTM_GETADDR, AF_UNSPEC, 1) == -1)
+               goto end;
+       netlink_recv(s, NULL, ifaddrs);
+       if (netlink_send(s, RTM_GETLINK, AF_PACKET, 2) == -1)
+               goto end;
+       netlink_recv(s, ifs, NULL);
+
+       /* Listen to any future change */
+       cfg->g_iface_cb = netlink_change_cb;
+       if (levent_iface_subscribe(cfg, s) == -1) {
+               goto end;
+       }
+
+       return 0;
+end:
+       netlink_cleanup(cfg);
+       return -1;
+}
+
+/**
+ * Cleanup netlink subsystem.
+ */
+void
+netlink_cleanup(struct lldpd *cfg)
+{
+       if (cfg->g_netlink == NULL) return;
+       if (cfg->g_netlink->nl_socket != -1)
+               close(cfg->g_netlink->nl_socket);
+       interfaces_free_devices(cfg->g_netlink->devices);
+       interfaces_free_addresses(cfg->g_netlink->addresses);
+
+       free(cfg->g_netlink);
+       cfg->g_netlink = NULL;
+}
+
+/**
+ * Receive the list of interfaces.
+ *
+ * @return a list of interfaces.
+ */
+struct interfaces_device_list*
+netlink_get_interfaces(struct lldpd *cfg)
+{
+       if (netlink_initialize(cfg) == -1) return NULL;
+       struct interfaces_device *ifd;
+       TAILQ_FOREACH(ifd, cfg->g_netlink->devices, next) {
+               ifd->ignore = 0;
        }
+       return cfg->g_netlink->devices;
+}
 
-       return ifaddrs;
+/**
+ * Receive the list of addresses.
+ *
+ * @return a list of addresses.
+ */
+struct interfaces_address_list*
+netlink_get_addresses(struct lldpd *cfg)
+{
+       if (netlink_initialize(cfg) == -1) return NULL;
+       return cfg->g_netlink->addresses;
 }
index 2278b4ea8eb4c19d7e00e2451c4384869ef90046..b06d7f81a8b099cf37c612fab896aad7c0478b05 100644 (file)
@@ -8,10 +8,6 @@ TESTS = check_marshal check_pattern check_lldp check_cdp check_sonmp check_edp c
 AM_CFLAGS += @CHECK_CFLAGS@
 LDADD = $(top_builddir)/src/daemon/liblldpd.la @CHECK_LIBS@ @LIBEVENT_LDFLAGS@
 
-if HOST_OS_LINUX
-LDADD += @LIBNL_LDFLAGS@
-endif
-
 check_marshal_SOURCES = check_marshal.c \
        $(top_srcdir)/src/marshal.h \
        check-compat.h