systemd: merge branch systemd into th/systemd-lldp-bgo763384
authorThomas Haller <thaller@redhat.com>
Mon, 29 Feb 2016 13:30:15 +0000 (14:30 +0100)
committerThomas Haller <thaller@redhat.com>
Fri, 11 Mar 2016 08:28:10 +0000 (09:28 +0100)
As the lldp API changed, adjust "nm-lldp-listener.c".

Note that the commit is not yet functional due to missing
sd_event_source_set_enabled() and sd_event_source_set_time().

38 files changed:
1  2 
src/Makefile.am
src/devices/nm-device.c
src/devices/nm-lldp-listener.c
src/devices/nm-lldp-listener.h
src/devices/tests/test-lldp.c
src/systemd/nm-sd-adapt.c
src/systemd/nm-sd-adapt.h
src/systemd/src/basic/escape.c
src/systemd/src/basic/escape.h
src/systemd/src/basic/ether-addr-util.c
src/systemd/src/basic/fileio.c
src/systemd/src/basic/fs-util.c
src/systemd/src/basic/hashmap.c
src/systemd/src/basic/hexdecoct.c
src/systemd/src/basic/hostname-util.c
src/systemd/src/basic/io-util.c
src/systemd/src/basic/log.h
src/systemd/src/basic/macro.h
src/systemd/src/basic/parse-util.c
src/systemd/src/basic/socket-util.c
src/systemd/src/basic/string-util.c
src/systemd/src/basic/strv.c
src/systemd/src/basic/time-util.c
src/systemd/src/basic/utf8.c
src/systemd/src/basic/util.h
src/systemd/src/libsystemd-network/dhcp-option.c
src/systemd/src/libsystemd-network/dhcp-packet.c
src/systemd/src/libsystemd-network/dhcp6-option.c
src/systemd/src/libsystemd-network/lldp-neighbor.c
src/systemd/src/libsystemd-network/lldp-network.c
src/systemd/src/libsystemd-network/network-internal.c
src/systemd/src/libsystemd-network/sd-dhcp-client.c
src/systemd/src/libsystemd-network/sd-dhcp6-client.c
src/systemd/src/libsystemd-network/sd-ipv4acd.c
src/systemd/src/libsystemd-network/sd-ipv4ll.c
src/systemd/src/libsystemd-network/sd-lldp.c
src/systemd/src/shared/dns-domain.c
src/tests/test-systemd.c

diff --cc src/Makefile.am
@@@ -48,173 -48,6 +48,173 @@@ AM_CPPFLAGS =                             
  # primarily for its side effect of removing duplicates.
  AM_CPPFLAGS += $(foreach d,$(sort $(dir $(libNetworkManager_la_SOURCES))),-I$(top_srcdir)/src/$d)
  
-       systemd/src/libsystemd-network/lldp-internal.c \
 +noinst_LTLIBRARIES = \
 +      libNetworkManager.la \
 +      libnm-iface-helper.la \
 +      libsystemd-nm.la
 +
 +######################
 +# libsystemd-nm
 +######################
 +
 +SYSTEMD_NM_CFLAGS_PATHS = \
 +      -I$(top_srcdir)/src/systemd/src/systemd \
 +      -I$(top_srcdir)/src/systemd/src/libsystemd-network \
 +      -I$(top_srcdir)/src/systemd/src/basic \
 +      -I$(top_srcdir)/src/systemd
 +
 +libsystemd_nm_la_SOURCES = \
 +      systemd/nm-sd-adapt.c \
 +      systemd/nm-sd-adapt.h \
 +      systemd/src/basic/alloc-util.c \
 +      systemd/src/basic/alloc-util.h \
 +      systemd/src/basic/async.h \
 +      systemd/src/basic/escape.c \
 +      systemd/src/basic/escape.h \
++      systemd/src/basic/ether-addr-util.c \
++      systemd/src/basic/ether-addr-util.h \
 +      systemd/src/basic/fd-util.c \
 +      systemd/src/basic/fd-util.h \
 +      systemd/src/basic/fileio.c \
 +      systemd/src/basic/fileio.h \
 +      systemd/src/basic/fs-util.c \
 +      systemd/src/basic/fs-util.h \
 +      systemd/src/basic/hash-funcs.c \
 +      systemd/src/basic/hash-funcs.h \
 +      systemd/src/basic/hashmap.c \
 +      systemd/src/basic/hashmap.h \
 +      systemd/src/basic/hexdecoct.c \
 +      systemd/src/basic/hexdecoct.h \
 +      systemd/src/basic/hostname-util.c \
 +      systemd/src/basic/hostname-util.h \
 +      systemd/src/basic/in-addr-util.c \
 +      systemd/src/basic/in-addr-util.h \
 +      systemd/src/basic/io-util.c \
 +      systemd/src/basic/io-util.h \
 +      systemd/src/basic/list.h \
 +      systemd/src/basic/log.h \
 +      systemd/src/basic/macro.h \
 +      systemd/src/basic/mempool.c \
 +      systemd/src/basic/mempool.h \
 +      systemd/src/basic/parse-util.c \
 +      systemd/src/basic/parse-util.h \
 +      systemd/src/basic/path-util.c \
 +      systemd/src/basic/path-util.h \
 +      systemd/src/basic/prioq.c \
 +      systemd/src/basic/prioq.h \
 +      systemd/src/basic/random-util.c \
 +      systemd/src/basic/random-util.h \
 +      systemd/src/basic/refcnt.h \
 +      systemd/src/basic/set.h \
 +      systemd/src/basic/siphash24.c \
 +      systemd/src/basic/siphash24.h \
++      systemd/src/basic/socket-util.c \
 +      systemd/src/basic/socket-util.h \
 +      systemd/src/basic/sparse-endian.h \
 +      systemd/src/basic/stdio-util.h \
 +      systemd/src/basic/string-table.c \
 +      systemd/src/basic/string-table.h \
 +      systemd/src/basic/string-util.c \
 +      systemd/src/basic/string-util.h \
 +      systemd/src/basic/strv.c \
 +      systemd/src/basic/strv.h \
 +      systemd/src/basic/time-util.c \
 +      systemd/src/basic/time-util.h \
 +      systemd/src/basic/umask-util.h \
 +      systemd/src/basic/unaligned.h \
 +      systemd/src/basic/utf8.c \
 +      systemd/src/basic/utf8.h \
 +      systemd/src/basic/util.c \
 +      systemd/src/basic/util.h \
 +      systemd/src/libsystemd-network/arp-util.c \
 +      systemd/src/libsystemd-network/arp-util.h \
 +      systemd/src/libsystemd-network/dhcp-identifier.c \
 +      systemd/src/libsystemd-network/dhcp-identifier.h \
 +      systemd/src/libsystemd-network/dhcp-internal.h \
 +      systemd/src/libsystemd-network/dhcp-lease-internal.h \
 +      systemd/src/libsystemd-network/dhcp-network.c \
 +      systemd/src/libsystemd-network/dhcp-option.c \
 +      systemd/src/libsystemd-network/dhcp-packet.c \
 +      systemd/src/libsystemd-network/dhcp-protocol.h \
 +      systemd/src/libsystemd-network/dhcp6-internal.h \
 +      systemd/src/libsystemd-network/dhcp6-lease-internal.h \
 +      systemd/src/libsystemd-network/dhcp6-network.c \
 +      systemd/src/libsystemd-network/dhcp6-option.c \
 +      systemd/src/libsystemd-network/dhcp6-protocol.h \
-       systemd/src/libsystemd-network/lldp-port.c \
-       systemd/src/libsystemd-network/lldp-port.h \
-       systemd/src/libsystemd-network/lldp-tlv.c \
-       systemd/src/libsystemd-network/lldp-tlv.h \
 +      systemd/src/libsystemd-network/lldp-internal.h \
++      systemd/src/libsystemd-network/lldp-neighbor.c \
++      systemd/src/libsystemd-network/lldp-neighbor.h \
 +      systemd/src/libsystemd-network/lldp-network.c \
 +      systemd/src/libsystemd-network/lldp-network.h \
 +      systemd/src/libsystemd-network/lldp.h \
 +      systemd/src/libsystemd-network/network-internal.c \
 +      systemd/src/libsystemd-network/network-internal.h \
 +      systemd/src/libsystemd-network/sd-dhcp-client.c \
 +      systemd/src/libsystemd-network/sd-dhcp-lease.c \
 +      systemd/src/libsystemd-network/sd-dhcp6-client.c \
 +      systemd/src/libsystemd-network/sd-dhcp6-lease.c \
 +      systemd/src/libsystemd-network/sd-ipv4acd.c \
 +      systemd/src/libsystemd-network/sd-ipv4ll.c \
 +      systemd/src/libsystemd-network/sd-lldp.c \
 +      systemd/src/libsystemd/sd-id128/sd-id128.c \
 +      systemd/src/shared/dns-domain.c \
 +      systemd/src/shared/dns-domain.h \
 +      systemd/src/systemd/_sd-common.h \
 +      systemd/src/systemd/sd-dhcp-client.h \
 +      systemd/src/systemd/sd-dhcp-lease.h \
 +      systemd/src/systemd/sd-dhcp6-client.h \
 +      systemd/src/systemd/sd-dhcp6-lease.h \
 +      systemd/src/systemd/sd-event.h \
 +      systemd/src/systemd/sd-id128.h \
 +      systemd/src/systemd/sd-ipv4acd.h \
 +      systemd/src/systemd/sd-ipv4ll.h \
 +      systemd/src/systemd/sd-lldp.h \
 +      systemd/src/systemd/sd-ndisc.h
 +
 +libsystemd_nm_la_CPPFLAGS = \
 +      -I$(top_srcdir)/shared \
 +      -I$(top_builddir)/shared \
 +      -I$(top_srcdir)/libnm-core \
 +      -I$(top_builddir)/libnm-core \
 +      $(SYSTEMD_NM_CFLAGS_PATHS) \
 +      -I$(top_srcdir)/src/systemd/src/shared \
 +      -DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_SYSTEMD \
 +      $(GLIB_CFLAGS)
 +
 +libsystemd_nm_la_LIBADD = \
 +      $(GLIB_LIBS)
 +
 +######################
 +# libsystemd-nm-base
 +######################
 +
 +if ENABLE_TESTS
 +noinst_LTLIBRARIES += \
 +      libNetworkManager-base.la
 +
 +libNetworkManager_base_la_SOURCES = \
 +      nm-core-utils.c \
 +      nm-core-utils.h \
 +      nm-logging.c \
 +      nm-logging.h
 +
 +libNetworkManager_base_la_CPPFLAGS = \
 +      -I$(top_srcdir)/shared \
 +      -I$(top_builddir)/shared \
 +      -I$(top_srcdir)/libnm-core \
 +      -I$(top_builddir)/libnm-core \
 +      -I$(top_srcdir)/src/platform \
 +      -DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_INSIDE_DAEMON \
 +      -DNO_SYSTEMD_JOURNAL \
 +      -DPREFIX=\"$(prefix)\" \
 +      -DNMSTATEDIR=\"$(nmstatedir)\" \
 +      $(GLIB_CFLAGS)
 +
 +libNetworkManager_base_la_LIBADD = \
 +      $(top_builddir)/libnm-core/libnm-core.la \
 +      $(GLIB_LIBS)
 +endif
 +
  ###########################################
  # NetworkManager
  ###########################################
@@@ -1182,66 -877,8 +1182,65 @@@ nm_device_master_release_one_slave (NMD
         * when slaves change.
         */
        nm_device_update_hw_address (self);
 +      nm_device_set_unmanaged_by_flags (slave, NM_UNMANAGED_IS_SLAVE, NM_UNMAN_FLAG_OP_FORGET, NM_DEVICE_STATE_REASON_REMOVED);
 +}
  
 -      return success;
 +/**
 + * can_unmanaged_external_down:
 + * @self: the device
 + *
 + * Check whether the device should stay NM_UNMANAGED_EXTERNAL_DOWN unless
 + * IFF_UP-ed externally.
 + */
 +static gboolean
 +can_unmanaged_external_down (NMDevice *self)
 +{
 +      return nm_device_is_software (self) && !NM_DEVICE_GET_PRIVATE (self)->is_nm_owned;
 +}
 +
 +static void
 +update_dynamic_ip_setup (NMDevice *self)
 +{
 +      NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 +      GError *error;
 +      gconstpointer addr;
 +      size_t addr_length;
 +
 +      g_hash_table_remove_all (priv->ip6_saved_properties);
 +
 +      if (priv->dhcp4_client) {
 +              if (!nm_device_dhcp4_renew (self, FALSE)) {
 +                      nm_device_state_changed (self,
 +                                               NM_DEVICE_STATE_FAILED,
 +                                               NM_DEVICE_STATE_REASON_DHCP_FAILED);
 +                      return;
 +              }
 +      }
 +      if (priv->dhcp6_client) {
 +              if (!nm_device_dhcp6_renew (self, FALSE)) {
 +                      nm_device_state_changed (self,
 +                                               NM_DEVICE_STATE_FAILED,
 +                                               NM_DEVICE_STATE_REASON_DHCP_FAILED);
 +                      return;
 +              }
 +      }
 +      if (priv->rdisc) {
 +              /* FIXME: todo */
 +      }
 +      if (priv->dnsmasq_manager) {
 +              /* FIXME: todo */
 +      }
 +
 +      if (priv->lldp_listener && nm_lldp_listener_is_running (priv->lldp_listener)) {
 +              nm_lldp_listener_stop (priv->lldp_listener);
 +              addr = nm_platform_link_get_address (NM_PLATFORM_GET, priv->ifindex, &addr_length);
 +
-               if (!nm_lldp_listener_start (priv->lldp_listener, nm_device_get_ifindex (self),
-                                            nm_device_get_iface (self), addr, addr_length, &error)) {
++              if (!nm_lldp_listener_start (priv->lldp_listener, nm_device_get_ifindex (self), &error)) {
 +                      _LOGD (LOGD_DEVICE, "LLDP listener %p could not be restarted: %s",
 +                             priv->lldp_listener, error->message);
 +                      g_clear_error (&error);
 +              }
 +      }
  }
  
  static void
@@@ -3547,33 -2310,13 +3546,32 @@@ activate_stage2_device_config (NMDevic
                        nm_device_queue_recheck_assume (info->slave);
        }
  
 -      _LOGI (LOGD_DEVICE, "Activation: Stage 2 of 5 (Device Configure) successful.");
 +      if (lldp_rx_enabled (self)) {
 +              gs_free_error GError *error = NULL;
 +              gconstpointer addr;
 +              size_t addr_length;
  
 -      nm_device_activate_schedule_stage3_ip_config_start (self);
 +              if (priv->lldp_listener)
 +                      nm_lldp_listener_stop (priv->lldp_listener);
 +              else {
 +                      priv->lldp_listener = nm_lldp_listener_new ();
 +                      g_signal_connect (priv->lldp_listener,
 +                                        "notify::" NM_LLDP_LISTENER_NEIGHBORS,
 +                                        G_CALLBACK (lldp_neighbors_changed),
 +                                        self);
 +              }
  
 -out:
 -      _LOGI (LOGD_DEVICE, "Activation: Stage 2 of 5 (Device Configure) complete.");
 -      return FALSE;
 +              addr = nm_platform_link_get_address (NM_PLATFORM_GET, priv->ifindex, &addr_length);
 +
-               if (nm_lldp_listener_start (priv->lldp_listener, nm_device_get_ifindex (self),
-                                           nm_device_get_iface (self), addr, addr_length, &error))
++              if (nm_lldp_listener_start (priv->lldp_listener, nm_device_get_ifindex (self), &error))
 +                      _LOGD (LOGD_DEVICE, "LLDP listener %p started", priv->lldp_listener);
 +              else {
 +                      _LOGD (LOGD_DEVICE, "LLDP listener %p could not be started: %s",
 +                             priv->lldp_listener, error->message);
 +              }
 +      }
 +
 +      nm_device_activate_schedule_stage3_ip_config_start (self);
  }
  
  
index c9202c4,0000000..39e7a40
mode 100644,000000..100644
--- /dev/null
@@@ -1,689 -1,0 +1,795 @@@
-       int dest;
 +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
 +/* NetworkManager -- Network link manager
 + *
 + * This program is free software; you can redistribute it and/or modify
 + * it under the terms of the GNU General Public License as published by
 + * the Free Software Foundation; either version 2 of the License, or
 + * (at your option) any later version.
 + *
 + * 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.
 + *
 + * You should have received a copy of the GNU General Public License along
 + * with this program; if not, write to the Free Software Foundation, Inc.,
 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 + *
 + * Copyright (C) 2015 Red Hat, Inc.
 + */
 +
 +#include "nm-default.h"
 +
 +#include <net/ethernet.h>
++#include <errno.h>
 +
 +#include "sd-lldp.h"
 +#include "lldp.h"
 +#include "nm-lldp-listener.h"
 +#include "nm-platform.h"
 +#include "nm-utils.h"
 +
 +#define MAX_NEIGHBORS         4096
 +#define MIN_UPDATE_INTERVAL   2
 +
++#define LLDP_MAC_NEAREST_BRIDGE          ((const struct ether_addr *) ((uint8_t[ETH_ALEN]) { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e }))
++#define LLDP_MAC_NEAREST_NON_TPMR_BRIDGE ((const struct ether_addr *) ((uint8_t[ETH_ALEN]) { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }))
++#define LLDP_MAC_NEAREST_CUSTOMER_BRIDGE ((const struct ether_addr *) ((uint8_t[ETH_ALEN]) { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }))
++
 +typedef struct {
 +      char         *iface;
 +      int           ifindex;
 +      sd_lldp      *lldp_handle;
 +      GHashTable   *lldp_neighbors;
 +      guint         timer;
 +      guint         num_pending_events;
 +      GVariant     *variant;
 +} NMLldpListenerPrivate;
 +
 +enum {
 +      PROP_0,
 +      PROP_NEIGHBORS,
 +
 +      LAST_PROP
 +};
 +
 +G_DEFINE_TYPE (NMLldpListener, nm_lldp_listener, G_TYPE_OBJECT)
 +
 +#define NM_LLDP_LISTENER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_LLDP_LISTENER, NMLldpListenerPrivate))
 +
 +typedef struct {
 +      guint8 chassis_id_type;
 +      guint8 port_id_type;
 +      char *chassis_id;
 +      char *port_id;
 +
- gvalue_new_nstr (const char *str, guint16 len)
++      struct ether_addr destination_address;
 +
 +      GHashTable *tlvs;
 +} LLDPNeighbor;
 +
 +static void process_lldp_neighbors (NMLldpListener *self);
 +
 +/*****************************************************************************/
 +
 +#define _NMLOG_PREFIX_NAME                "lldp"
 +#define _NMLOG_DOMAIN                     LOGD_DEVICE
 +#define _NMLOG(level, ...) \
 +    G_STMT_START { \
 +        const NMLogLevel _level = (level); \
 +        \
 +        if (nm_logging_enabled (_level, _NMLOG_DOMAIN)) { \
 +            char _sbuf[64]; \
 +            int _ifindex = (self) ? NM_LLDP_LISTENER_GET_PRIVATE (self)->ifindex : 0; \
 +            \
 +            _nm_log (_level, _NMLOG_DOMAIN, 0, \
 +                     "%s%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
 +                     _NMLOG_PREFIX_NAME, \
 +                     ((_ifindex > 0) \
 +                        ? nm_sprintf_buf (_sbuf, "[%p,%d]", (self), _ifindex) \
 +                        : ((self) \
 +                            ? nm_sprintf_buf (_sbuf, "[%p]", (self)) \
 +                            : "")) \
 +                     _NM_UTILS_MACRO_REST (__VA_ARGS__)); \
 +        } \
 +    } G_STMT_END \
 +
 +/*****************************************************************************/
 +
++static gboolean
++ether_addr_equal (const struct ether_addr *a1, const struct ether_addr *a2)
++{
++      nm_assert (a1);
++      nm_assert (a2);
++
++      G_STATIC_ASSERT_EXPR (sizeof (*a1) == ETH_ALEN);
++      return memcmp (a1, a2, ETH_ALEN) == 0;
++}
++
 +static void
 +gvalue_destroy (gpointer data)
 +{
 +      GValue *value = (GValue *) data;
 +
 +      g_value_unset (value);
 +      g_slice_free (GValue, value);
 +}
 +
 +static GValue *
-       g_value_take_string (value, strndup (str, len));
++gvalue_new_str (const char *str)
 +{
 +      GValue *value;
 +
 +      value = g_slice_new0 (GValue);
 +      g_value_init (value, G_TYPE_STRING);
-           || a->dest != b->dest
++      g_value_set_string (value, str ?: "");
 +      return value;
 +}
 +
++static GValue *
++gvalue_new_str_ptr (const void *str, gsize len)
++{
++      const char *s = str;
++      const char *tmp;
++      gsize len0 = len;
++      gs_free char *str_free = NULL;
++      gs_free char *str_escaped = NULL;
++
++      /* truncate at first NUL, including removing trailing NULs*/
++      tmp = memchr (s, '\0', len);
++      if (tmp)
++              len = tmp - s;
++
++      if (!len)
++              return gvalue_new_str ("");
++
++      if (len0 <= len || s[len] != '\0') {
++              /* hmpf, g_strescape needs a trailing NUL. Need to clone */
++              s = str_free = g_strndup (s, len);
++      }
++
++      str_escaped = g_strescape (s, NULL);
++      return gvalue_new_str (str_escaped);
++}
++
 +static GValue *
 +gvalue_new_uint (guint val)
 +{
 +      GValue *value;
 +
 +      value = g_slice_new0 (GValue);
 +      g_value_init (value, G_TYPE_UINT);
 +      g_value_set_uint (value, val);
 +      return value;
 +}
 +
++static GValue *
++gvalue_new_uint_u8 (const void *data)
++{
++      return gvalue_new_uint (*((const guint8 *) data));
++}
++
++static GValue *
++gvalue_new_uint_u16 (const void *data)
++{
++      guint16 v;
++
++      memcpy (&v, data, sizeof (v));
++      return gvalue_new_uint (ntohs (v));
++}
++
 +static guint
 +lldp_neighbor_id_hash (gconstpointer ptr)
 +{
 +      const LLDPNeighbor *neigh = ptr;
 +      guint hash;
 +
 +      hash =   23423423u  + ((guint) (neigh->chassis_id ? g_str_hash (neigh->chassis_id) : 12321u));
 +      hash = (hash * 33u) + ((guint) (neigh->port_id ? g_str_hash (neigh->port_id) : 34342343u));
 +      hash = (hash * 33u) + ((guint) neigh->chassis_id_type);
 +      hash = (hash * 33u) + ((guint) neigh->port_id_type);
 +      return hash;
 +}
 +
 +static gboolean
 +lldp_neighbor_id_equal (gconstpointer a, gconstpointer b)
 +{
 +      const LLDPNeighbor *x = a, *y = b;
 +
 +      return x->chassis_id_type == y->chassis_id_type &&
 +             x->port_id_type == y->port_id_type &&
 +             !g_strcmp0 (x->chassis_id, y->chassis_id) &&
 +             !g_strcmp0 (x->port_id, y->port_id);
 +}
 +
 +static void
 +lldp_neighbor_free (LLDPNeighbor *neighbor)
 +{
 +      if (neighbor) {
 +              g_free (neighbor->chassis_id);
 +              g_free (neighbor->port_id);
 +              g_hash_table_unref (neighbor->tlvs);
 +              g_slice_free (LLDPNeighbor, neighbor);
 +      }
 +}
 +
 +static void
 +lldp_neighbor_freep (LLDPNeighbor **ptr)
 +{
 +      lldp_neighbor_free (*ptr);
 +}
 +
 +static gboolean
 +lldp_neighbor_equal (LLDPNeighbor *a, LLDPNeighbor *b)
 +{
 +      GHashTableIter iter;
 +      gpointer k, v;
 +
 +      g_return_val_if_fail (a && a->tlvs, FALSE);
 +      g_return_val_if_fail (b && b->tlvs, FALSE);
 +
 +      if (   a->chassis_id_type != b->chassis_id_type
 +          || a->port_id_type != b->port_id_type
-       nm_auto_free sd_lldp_packet **packets = NULL;
++          || ether_addr_equal (&a->destination_address, &b->destination_address)
 +          || g_strcmp0 (a->chassis_id, b->chassis_id)
 +          || g_strcmp0 (a->port_id, b->port_id))
 +              return FALSE;
 +
 +      if (g_hash_table_size (a->tlvs) != g_hash_table_size (b->tlvs))
 +              return FALSE;
 +
 +      g_hash_table_iter_init (&iter, a->tlvs);
 +      while (g_hash_table_iter_next (&iter, &k, &v)) {
 +              GValue *value_a, *value_b;
 +
 +              value_a = v;
 +              value_b = g_hash_table_lookup (b->tlvs, k);
 +
 +              if (!value_b)
 +                      return FALSE;
 +
 +              g_return_val_if_fail (G_VALUE_TYPE (value_a) == G_VALUE_TYPE (value_b), FALSE);
 +
 +              if (G_VALUE_HOLDS_STRING (value_a)) {
 +                      if (g_strcmp0 (g_value_get_string (value_a), g_value_get_string (value_b)))
 +                              return FALSE;
 +              } else if (G_VALUE_HOLDS_UINT (value_a)) {
 +                      if (g_value_get_uint (value_a) != g_value_get_uint (value_b))
 +                              return FALSE;
 +              } else
 +                      g_return_val_if_reached (FALSE);
 +      }
 +
 +      return TRUE;
 +}
 +
 +static gboolean
 +lldp_hash_table_equal (GHashTable *a, GHashTable *b)
 +{
 +      GHashTableIter iter;
 +      gpointer val;
 +
 +      g_return_val_if_fail (a, FALSE);
 +      g_return_val_if_fail (b, FALSE);
 +
 +      if (g_hash_table_size (a) != g_hash_table_size (b))
 +              return FALSE;
 +
 +      g_hash_table_iter_init (&iter, a);
 +      while (g_hash_table_iter_next (&iter, NULL, &val)) {
 +              LLDPNeighbor *neigh_a, *neigh_b;
 +
 +              neigh_a = val;
 +              neigh_b = g_hash_table_lookup (b, val);
 +
 +              if (!neigh_b)
 +                      return FALSE;
 +
 +              if (!lldp_neighbor_equal (neigh_a, neigh_b))
 +                      return FALSE;
 +      }
 +
 +      return TRUE;
 +}
 +
 +static gboolean
 +lldp_timeout (gpointer user_data)
 +{
 +      NMLldpListener *self = user_data;
 +      NMLldpListenerPrivate *priv;
 +
 +      g_return_val_if_fail (NM_IS_LLDP_LISTENER (self), G_SOURCE_REMOVE);
 +
 +      priv = NM_LLDP_LISTENER_GET_PRIVATE (self);
 +
 +      priv->timer = 0;
 +
 +      if (priv->num_pending_events)
 +              process_lldp_neighbors (self);
 +
 +      return G_SOURCE_REMOVE;
 +}
 +
 +static void
 +process_lldp_neighbors (NMLldpListener *self)
 +{
 +      NMLldpListenerPrivate *priv = NM_LLDP_LISTENER_GET_PRIVATE (self);
-       int num, i;
++      nm_auto_free sd_lldp_neighbor **neighbors = NULL;
 +      GHashTable *hash;
-       num = sd_lldp_get_packets (priv->lldp_handle, &packets);
++      int num, i, r;
 +
 +      g_return_if_fail (priv->lldp_handle);
 +
-       for (i = 0; packets && i < num; i++) {
++      num = sd_lldp_get_neighbors (priv->lldp_handle, &neighbors);
 +      if (num < 0) {
 +              _LOGD ("process: error %d retrieving neighbor packets for %s",
 +                      num, priv->iface);
 +              return;
 +      }
 +
 +      hash = g_hash_table_new_full (lldp_neighbor_id_hash, lldp_neighbor_id_equal,
 +                                    (GDestroyNotify) lldp_neighbor_free, NULL);
 +
-               uint8_t chassis_id_type, port_id_type, *chassis_id, *port_id, data8;
-               uint16_t chassis_id_len, port_id_len, len, data16;
++      for (i = 0; neighbors && i < num; i++) {
 +              nm_auto (lldp_neighbor_freep) LLDPNeighbor *neigh = NULL;
-               char *str;
-               int r;
++              uint8_t chassis_id_type, port_id_type;
++              uint16_t data16;
++              uint8_t *data8;
++              const void *chassis_id, *port_id;
++              gsize chassis_id_len, port_id_len, len;
 +              GValue *value;
-                       goto next_packet;
++              const char *str;
 +
 +              if (i >= MAX_NEIGHBORS)
-               r = sd_lldp_packet_read_chassis_id (packets[i], &chassis_id_type,
-                                                   &chassis_id, &chassis_id_len);
++                      break;
 +
-                       goto next_packet;
++              r = sd_lldp_neighbor_get_chassis_id (neighbors[i], &chassis_id_type,
++                                                   &chassis_id, &chassis_id_len);
 +              if (r < 0)
-               r = sd_lldp_packet_read_port_id (packets[i], &port_id_type,
-                                                &port_id, &port_id_len);
++                      goto next_neighbor;
++              if (chassis_id_len < 1)
++                      goto next_neighbor;
 +
-                       goto next_packet;
++              r = sd_lldp_neighbor_get_port_id (neighbors[i], &port_id_type,
++                                                &port_id, &port_id_len);
 +              if (r < 0)
-               sd_lldp_packet_get_destination_type (packets[i], &neigh->dest);
++                      goto next_neighbor;
++              if (port_id_len < 1)
++                      goto next_neighbor;
 +
 +              neigh = g_slice_new0 (LLDPNeighbor);
 +              neigh->tlvs = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, gvalue_destroy);
 +              neigh->chassis_id_type = chassis_id_type;
 +              neigh->port_id_type = port_id_type;
-               if (chassis_id_len < 1)
-                       goto next_packet;
 +
-                       goto next_packet;
++              if (sd_lldp_neighbor_get_destination_address (neighbors[i], &neigh->destination_address) < 0)
++                      goto next_neighbor;
 +
 +              switch (chassis_id_type) {
 +              case LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS:
 +              case LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME:
 +              case LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED:
 +              case LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT:
 +                      neigh->chassis_id = g_strndup ((const char *) chassis_id, chassis_id_len);
 +                      break;
 +              case LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS:
 +                      neigh->chassis_id = nm_utils_hwaddr_ntoa (chassis_id, chassis_id_len);
 +                      break;
 +              default:
 +                      _LOGD ("process: unsupported chassis ID type %d", chassis_id_type);
-               if (port_id_len < 1)
-                       goto next_packet;
++                      goto next_neighbor;
 +              }
 +
-                       goto next_packet;
 +              switch (port_id_type) {
 +              case LLDP_PORT_SUBTYPE_INTERFACE_ALIAS:
 +              case LLDP_PORT_SUBTYPE_INTERFACE_NAME:
 +              case LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED:
 +              case LLDP_PORT_SUBTYPE_PORT_COMPONENT:
 +                      neigh->port_id = strndup ((char *) port_id, port_id_len);
 +                      break;
 +              case LLDP_PORT_SUBTYPE_MAC_ADDRESS:
 +                      neigh->port_id = nm_utils_hwaddr_ntoa (port_id, port_id_len);
 +                      break;
 +              default:
 +                      _LOGD ("process: unsupported port ID type %d", port_id_type);
-               if (sd_lldp_packet_read_port_description (packets[i], &str, &len) == 0) {
-                       value = gvalue_new_nstr (str, len);
++                      goto next_neighbor;
 +              }
 +
-               if (sd_lldp_packet_read_system_name (packets[i], &str, &len) == 0) {
-                       value = gvalue_new_nstr (str, len);
++              if (sd_lldp_neighbor_get_port_description (neighbors[i], &str) == 0) {
++                      value = gvalue_new_str (str);
 +                      g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_PORT_DESCRIPTION, value);
 +              }
 +
-               if (sd_lldp_packet_read_system_description (packets[i], &str, &len) == 0) {
-                       value = gvalue_new_nstr (str, len);
++              if (sd_lldp_neighbor_get_system_name (neighbors[i], &str) == 0) {
++                      value = gvalue_new_str (str);
 +                      g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_SYSTEM_NAME, value);
 +              }
 +
-               if (sd_lldp_packet_read_system_capability (packets[i], &data16) == 0) {
++              if (sd_lldp_neighbor_get_system_description (neighbors[i], &str) == 0) {
++                      value = gvalue_new_str (str);
 +                      g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_SYSTEM_DESCRIPTION, value);
 +              }
 +
-               if (sd_lldp_packet_read_port_vlan_id (packets[i], &data16) == 0) {
-                       value = gvalue_new_uint (data16);
-                       g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_IEEE_802_1_PVID, value);
-               }
-               if (sd_lldp_packet_read_port_protocol_vlan_id (packets[i], &data8, &data16) == 0) {
-                       value = gvalue_new_uint (data16);
-                       g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_IEEE_802_1_PPVID, value);
-                       value = gvalue_new_uint (data8);
-                       g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_IEEE_802_1_PPVID_FLAGS, value);
-               }
-               if (sd_lldp_packet_read_vlan_name (packets[i], &data16, &str, &len) == 0) {
-                       value = gvalue_new_uint (data16);
-                       g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_IEEE_802_1_VID, value);
++              if (sd_lldp_neighbor_get_system_capabilities (neighbors[i], &data16) == 0) {
 +                      value = gvalue_new_uint (data16);
 +                      g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_SYSTEM_CAPABILITIES, value);
 +              }
 +
-                       value = gvalue_new_nstr (str, len);
-                       g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_IEEE_802_1_VLAN_NAME, value);
-               }
++              if (sd_lldp_neighbor_tlv_rewind (neighbors[i]) < 0)
++                      goto next_neighbor;
++              do {
++                      guint8 oui[3];
++                      guint8 subtype;
++
++                      r = sd_lldp_neighbor_tlv_get_oui (neighbors[i], oui, &subtype);
++                      if (r < 0) {
++                              if (r == -ENXIO)
++                                      continue;
++                              goto next_neighbor;
++                      }
 +
- next_packet:
-               sd_lldp_packet_unref (packets[i]);
++                      if (!(   memcmp (oui, LLDP_OUI_802_1, sizeof (oui)) == 0
++                            && NM_IN_SET (subtype,
++                                          LLDP_OUI_802_1_SUBTYPE_PORT_PROTOCOL_VLAN_ID,
++                                          LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID,
++                                          LLDP_OUI_802_1_SUBTYPE_VLAN_NAME)))
++                              continue;
++
++                      if (sd_lldp_neighbor_tlv_get_raw (neighbors[i], (void *) &data8, &len) < 0)
++                              continue;
++
++                      /* skip over leading TLV, OUI and subtype */
++#ifdef WITH_MORE_ASSERTS
++                      {
++                              guint8 check_hdr[] = {
++                                      0xfe | (((len - 2) >> 8) & 0x01), ((len - 2) & 0xFF),
++                                      oui[0], oui[1], oui[2],
++                                      subtype
++                              };
++
++                              nm_assert (len > 2 + 3 +1);
++                              nm_assert (memcmp (data8, check_hdr, sizeof check_hdr) == 0);
++                      }
++#endif
++                      if (len <= 6)
++                              goto next_neighbor;
++                      data8 += 6;
++                      len -= 6;
++
++                      /*if (memcmp (oui, LLDP_OUI_802_1, sizeof (oui)) == 0)*/
++                      {
++                              switch (subtype) {
++                              case LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID:
++                                      if (len != 2)
++                                              goto next_neighbor;
++                                      g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_IEEE_802_1_PVID,
++                                                           gvalue_new_uint_u16 (data8));
++                                      break;
++                              case LLDP_OUI_802_1_SUBTYPE_PORT_PROTOCOL_VLAN_ID:
++                                      if (len != 3)
++                                              goto next_neighbor;
++                                      g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_IEEE_802_1_PPVID_FLAGS,
++                                                           gvalue_new_uint_u8 (&data8[0]));
++                                      g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_IEEE_802_1_PPVID,
++                                                           gvalue_new_uint_u16 (&data8[1]));
++                                      break;
++                              case LLDP_OUI_802_1_SUBTYPE_VLAN_NAME: {
++                                      int l;
++
++                                      if (len <= 3)
++                                              goto next_neighbor;
++
++                                      l = data8[2];
++                                      if (len != 3 + l)
++                                              goto next_neighbor;
++
++                                      g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_IEEE_802_1_VID,
++                                                           gvalue_new_uint_u16 (&data8[0]));
++                                      g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_IEEE_802_1_VLAN_NAME,
++                                                           gvalue_new_str_ptr (&data8[3], len));
++                                      break;
++                              }
++                              default:
++                                      g_assert_not_reached ();
++                              }
++                      }
++              } while (sd_lldp_neighbor_tlv_next (neighbors[i]) > 0);
 +
 +              _LOGD ("process: new neigh: CHASSIS='%s' PORT='%s'",
 +                      neigh->chassis_id, neigh->port_id);
 +
 +              g_hash_table_add (hash, neigh);
 +              neigh = NULL;
- lldp_event_handler (sd_lldp *lldp, int event, void *userdata)
++next_neighbor:
++              ;
 +      }
 +
++      for (i = 0; neighbors && i < num; i++)
++              sd_lldp_neighbor_unref (neighbors[i]);
++
 +      if (lldp_hash_table_equal (priv->lldp_neighbors, hash)) {
 +              g_hash_table_destroy (hash);
 +      } else {
 +              g_hash_table_destroy (priv->lldp_neighbors);
 +              priv->lldp_neighbors = hash;
 +              nm_clear_g_variant (&priv->variant);
 +              g_object_notify (G_OBJECT (self), NM_LLDP_LISTENER_NEIGHBORS);
 +      }
 +
 +      /* Since the processing of the neighbor list is potentially
 +       * expensive when there are many neighbors, coalesce multiple
 +       * events arriving in short time.
 +       */
 +      priv->timer = g_timeout_add_seconds (MIN_UPDATE_INTERVAL, lldp_timeout, self);
 +      priv->num_pending_events = 0;
 +}
 +
 +static void
- nm_lldp_listener_start (NMLldpListener *self, int ifindex, const char *iface,
-                         const guint8 *mac, guint mac_len, GError **error)
++lldp_event_handler (sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n, void *userdata)
 +{
 +      NMLldpListener *self = userdata;
 +      NMLldpListenerPrivate *priv;
 +
 +      g_return_if_fail (NM_IS_LLDP_LISTENER (self));
 +
 +      priv = NM_LLDP_LISTENER_GET_PRIVATE (self);
 +
 +      if (priv->timer > 0) {
 +              priv->num_pending_events++;
 +              return;
 +      }
 +
 +      process_lldp_neighbors (self);
 +}
 +
 +gboolean
-       g_return_val_if_fail (iface, FALSE);
++nm_lldp_listener_start (NMLldpListener *self, int ifindex, GError **error)
 +{
 +      NMLldpListenerPrivate *priv;
 +      int ret;
 +
 +      g_return_val_if_fail (NM_IS_LLDP_LISTENER (self), FALSE);
 +      g_return_val_if_fail (ifindex > 0, FALSE);
-       if (!mac || mac_len != ETH_ALEN) {
-               g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED,
-                                    "unsupported device");
-               return FALSE;
-       }
-       ret = sd_lldp_new (ifindex, iface, (struct ether_addr *) mac, &priv->lldp_handle);
-       if (ret) {
 +      g_return_val_if_fail (!error || !*error, FALSE);
 +
 +      priv = NM_LLDP_LISTENER_GET_PRIVATE (self);
 +
 +      if (priv->lldp_handle) {
 +              g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED,
 +                                   "already running");
 +              return FALSE;
 +      }
 +
-       if (ret) {
++      ret = sd_lldp_new (&priv->lldp_handle, ifindex);
++      if (ret < 0) {
 +              g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED,
 +                                   "initialization failed");
 +              return FALSE;
 +      }
 +
 +      ret = sd_lldp_attach_event (priv->lldp_handle, NULL, 0);
-       if (ret) {
++      if (ret < 0) {
 +              g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED,
 +                                   "attach event failed");
 +              goto err_free;
 +      }
 +
 +      ret = sd_lldp_set_callback (priv->lldp_handle, lldp_event_handler, self);
-       if (ret) {
++      if (ret < 0) {
 +              g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED,
 +                                   "set callback failed");
 +              goto err;
 +      }
 +
 +      ret = sd_lldp_start (priv->lldp_handle);
-       priv->iface = strdup (iface);
++      if (ret < 0) {
 +              g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED,
 +                                   "start failed");
 +              goto err;
 +      }
 +
 +      priv->ifindex = ifindex;
-               g_clear_pointer (&priv->iface, g_free);
 +      _LOGD ("start");
 +      return TRUE;
 +
 +err:
 +      sd_lldp_detach_event (priv->lldp_handle);
 +err_free:
 +      sd_lldp_unref (priv->lldp_handle);
 +      priv->lldp_handle = NULL;
 +      return FALSE;
 +}
 +
 +void
 +nm_lldp_listener_stop (NMLldpListener *self)
 +{
 +      NMLldpListenerPrivate *priv;
 +      guint size;
 +
 +      g_return_if_fail (NM_IS_LLDP_LISTENER (self));
 +      priv = NM_LLDP_LISTENER_GET_PRIVATE (self);
 +
 +      if (priv->lldp_handle) {
 +              _LOGD ("stop");
 +              sd_lldp_stop (priv->lldp_handle);
 +              sd_lldp_detach_event (priv->lldp_handle);
 +              sd_lldp_unref (priv->lldp_handle);
-       char *dest_str = NULL;
 +              priv->lldp_handle = NULL;
 +
 +              size = g_hash_table_size (priv->lldp_neighbors);
 +              g_hash_table_remove_all (priv->lldp_neighbors);
 +              if (size) {
 +                      nm_clear_g_variant (&priv->variant);
 +                      g_object_notify (G_OBJECT (self), NM_LLDP_LISTENER_NEIGHBORS);
 +              }
 +      }
 +
 +      nm_clear_g_source (&priv->timer);
 +      priv->ifindex = 0;
 +}
 +
 +gboolean
 +nm_lldp_listener_is_running (NMLldpListener *self)
 +{
 +      NMLldpListenerPrivate *priv;
 +
 +      g_return_val_if_fail (NM_IS_LLDP_LISTENER (self), FALSE);
 +
 +      priv = NM_LLDP_LISTENER_GET_PRIVATE (self);
 +      return !!priv->lldp_handle;
 +}
 +
 +GVariant *
 +nm_lldp_listener_get_neighbors (NMLldpListener *self)
 +{
 +      GVariantBuilder array_builder, neigh_builder;
 +      GHashTableIter iter;
 +      NMLldpListenerPrivate *priv;
 +      LLDPNeighbor *neigh;
-               switch (neigh->dest) {
-               case SD_LLDP_DESTINATION_TYPE_NEAREST_BRIDGE:
 +
 +      g_return_val_if_fail (NM_IS_LLDP_LISTENER (self), FALSE);
 +
 +      priv = NM_LLDP_LISTENER_GET_PRIVATE (self);
 +
 +      if (priv->variant)
 +              goto out;
 +
 +      g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("aa{sv}"));
 +      g_hash_table_iter_init (&iter, priv->lldp_neighbors);
 +
 +      while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &neigh)) {
 +              GHashTableIter val_iter;
 +              gpointer key, val;
++              const char *dest_str;
 +
 +              g_variant_builder_init (&neigh_builder, G_VARIANT_TYPE ("a{sv}"));
 +
 +              g_variant_builder_add (&neigh_builder, "{sv}",
 +                                     NM_LLDP_ATTR_CHASSIS_ID_TYPE,
 +                                     g_variant_new_uint32 (neigh->chassis_id_type));
 +              g_variant_builder_add (&neigh_builder, "{sv}",
 +                                     NM_LLDP_ATTR_CHASSIS_ID,
 +                                     g_variant_new_string (neigh->chassis_id));
 +              g_variant_builder_add (&neigh_builder, "{sv}",
 +                                     NM_LLDP_ATTR_PORT_ID_TYPE,
 +                                     g_variant_new_uint32 (neigh->port_id_type));
 +              g_variant_builder_add (&neigh_builder, "{sv}",
 +                                     NM_LLDP_ATTR_PORT_ID,
 +                                     g_variant_new_string (neigh->port_id));
 +
-                       break;
-               case SD_LLDP_DESTINATION_TYPE_NEAREST_NON_TPMR_BRIDGE:
++              if (ether_addr_equal (&neigh->destination_address, LLDP_MAC_NEAREST_BRIDGE))
 +                      dest_str = NM_LLDP_DEST_NEAREST_BRIDGE;
-                       break;
-               case SD_LLDP_DESTINATION_TYPE_NEAREST_CUSTOMER_BRIDGE:
++              else if (ether_addr_equal (&neigh->destination_address, LLDP_MAC_NEAREST_NON_TPMR_BRIDGE))
 +                      dest_str = NM_LLDP_DEST_NEAREST_NON_TPMR_BRIDGE;
-                       break;
-               }
++              else if (ether_addr_equal (&neigh->destination_address, LLDP_MAC_NEAREST_CUSTOMER_BRIDGE))
 +                      dest_str = NM_LLDP_DEST_NEAREST_CUSTOMER_BRIDGE;
++              else
++                      dest_str = NULL;
 +              if (dest_str) {
 +                      g_variant_builder_add (&neigh_builder, "{sv}",
 +                                             NM_LLDP_ATTR_DESTINATION,
 +                                             g_variant_new_string (dest_str));
 +              }
 +
 +              g_hash_table_iter_init (&val_iter, neigh->tlvs);
 +              while (g_hash_table_iter_next (&val_iter, &key, &val)) {
 +                      GValue *item = val;
 +
 +                      if (G_VALUE_HOLDS_STRING (item)) {
 +                              g_variant_builder_add (&neigh_builder, "{sv}",
 +                                                     key,
 +                                                     g_variant_new_string (g_value_get_string (item)));
 +                      } else if (G_VALUE_HOLDS_UINT (item)) {
 +                              g_variant_builder_add (&neigh_builder, "{sv}",
 +                                                     key,
 +                                                     g_variant_new_uint32 (g_value_get_uint (item)));
 +                      }
 +              }
 +
 +              g_variant_builder_add (&array_builder, "a{sv}", &neigh_builder);
 +      }
 +
 +      priv->variant = g_variant_ref_sink (g_variant_builder_end (&array_builder));
 +
 +out:
 +      return priv->variant;
 +}
 +
 +static void
 +get_property (GObject *object, guint prop_id,
 +              GValue *value, GParamSpec *pspec)
 +{
 +      NMLldpListener *self = NM_LLDP_LISTENER (object);
 +
 +      switch (prop_id) {
 +      case PROP_NEIGHBORS:
 +              g_value_set_variant (value, nm_lldp_listener_get_neighbors (self));
 +              break;
 +      default:
 +              G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 +              break;
 +      }
 +}
 +
 +static void
 +nm_lldp_listener_init (NMLldpListener *self)
 +{
 +      NMLldpListenerPrivate *priv = NM_LLDP_LISTENER_GET_PRIVATE (self);
 +
 +      priv->lldp_neighbors = g_hash_table_new_full (lldp_neighbor_id_hash,
 +                                                    lldp_neighbor_id_equal,
 +                                                    (GDestroyNotify) lldp_neighbor_free, NULL);
 +
 +      _LOGT ("lldp listener created");
 +}
 +
 +NMLldpListener *
 +nm_lldp_listener_new (void)
 +{
 +      return (NMLldpListener *) g_object_new (NM_TYPE_LLDP_LISTENER, NULL);
 +}
 +
 +static void
 +dispose (GObject *object)
 +{
 +      nm_lldp_listener_stop (NM_LLDP_LISTENER (object));
 +
 +      G_OBJECT_CLASS (nm_lldp_listener_parent_class)->dispose (object);
 +}
 +
 +static void
 +finalize (GObject *object)
 +{
 +      NMLldpListener *self = NM_LLDP_LISTENER (object);
 +      NMLldpListenerPrivate *priv = NM_LLDP_LISTENER_GET_PRIVATE (self);
 +
 +      nm_lldp_listener_stop (self);
 +      g_hash_table_unref (priv->lldp_neighbors);
 +
 +      nm_clear_g_variant (&priv->variant);
 +
 +      _LOGT ("lldp listener destroyed");
 +
 +      G_OBJECT_CLASS (nm_lldp_listener_parent_class)->finalize (object);
 +}
 +
 +static void
 +nm_lldp_listener_class_init (NMLldpListenerClass *klass)
 +{
 +      GObjectClass *object_class = G_OBJECT_CLASS (klass);
 +
 +      g_type_class_add_private (klass, sizeof (NMLldpListenerPrivate));
 +
 +      object_class->dispose = dispose;
 +      object_class->finalize = finalize;
 +      object_class->get_property = get_property;
 +
 +      g_object_class_install_property
 +              (object_class, PROP_NEIGHBORS,
 +               g_param_spec_variant (NM_LLDP_LISTENER_NEIGHBORS, "", "",
 +                                     G_VARIANT_TYPE ("aa{sv}"),
 +                                     NULL,
 +                                     G_PARAM_READABLE |
 +                                     G_PARAM_STATIC_STRINGS));
 +}
 +
index 10c43e7,0000000..df02fd6
mode 100644,000000..100644
--- /dev/null
@@@ -1,54 -1,0 +1,53 @@@
- gboolean nm_lldp_listener_start (NMLldpListener *self, int ifindex, const char *iface,
-                                  const guint8 *mac, guint mac_len, GError **error);
 +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
 +/* NetworkManager -- Network link manager
 + *
 + * This program is free software; you can redistribute it and/or modify
 + * it under the terms of the GNU General Public License as published by
 + * the Free Software Foundation; either version 2 of the License, or
 + * (at your option) any later version.
 + *
 + * 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.
 + *
 + * You should have received a copy of the GNU General Public License along
 + * with this program; if not, write to the Free Software Foundation, Inc.,
 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 + *
 + * Copyright (C) 2015 Red Hat, Inc.
 + */
 +
 +#ifndef __NM_LLDP_LISTENER__
 +#define __NM_LLDP_LISTENER__
 +
 +G_BEGIN_DECLS
 +
 +#define NM_TYPE_LLDP_LISTENER            (nm_lldp_listener_get_type ())
 +#define NM_LLDP_LISTENER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_LLDP_LISTENER, NMLldpListener))
 +#define NM_LLDP_LISTENER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_LLDP_LISTENER, NMLldpListenerClass))
 +#define NM_IS_LLDP_LISTENER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_LLDP_LISTENER))
 +#define NM_IS_LLDP_LISTENER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_LLDP_LISTENER))
 +#define NM_LLDP_LISTENER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_LLDP_LISTENER, NMLldpListenerClass))
 +
 +#define NM_LLDP_LISTENER_NEIGHBORS "neighbors"
 +
 +struct _NMLldpListener {
 +      GObject parent;
 +};
 +
 +typedef struct {
 +      GObjectClass parent;
 +} NMLldpListenerClass;
 +
 +GType nm_lldp_listener_get_type (void);
 +NMLldpListener *nm_lldp_listener_new (void);
++gboolean nm_lldp_listener_start (NMLldpListener *self, int ifindex, GError **error);
 +void nm_lldp_listener_stop (NMLldpListener *self);
 +gboolean nm_lldp_listener_is_running (NMLldpListener *self);
 +
 +GVariant *nm_lldp_listener_get_neighbors (NMLldpListener *self);
 +
 +G_END_DECLS
 +
 +#endif /* __NM_LLDP_LISTENER__ */
index 8be52fe,0000000..80ba725
mode 100644,000000..100644
--- /dev/null
@@@ -1,449 -1,0 +1,448 @@@
-       g_test_skip ("the test is known to fail");
-       return;
 +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
 +/* nm-platform.c - Handle runtime kernel networking configuration
 + *
 + * This program is free software; you can redistribute it and/or modify
 + * it under the terms of the GNU General Public License as published by
 + * the Free Software Foundation; either version 2, or (at your option)
 + * any later version.
 + *
 + * 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.
 + *
 + * You should have received a copy of the GNU General Public License along
 + * with this program; if not, write to the Free Software Foundation, Inc.,
 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 + *
 + * Copyright (C) 2015 Red Hat, Inc.
 + */
 +
 +#include "nm-default.h"
 +
 +#include <fcntl.h>
 +#include <linux/if_tun.h>
 +#include <sys/ioctl.h>
 +#include <sys/stat.h>
 +#include <sys/types.h>
 +
 +#include "nm-lldp-listener.h"
 +
 +#include "lldp.h"
 +
 +#include "test-common.h"
 +
 +#include "nm-test-utils.h"
 +
 +/*****************************************************************************/
 +
 +static GVariant *
 +get_lldp_neighbor (GVariant *neighbors,
 +                   int chassis_id_type,
 +                   const char *chassis_id,
 +                   int port_id_type,
 +                   const char *port_id)
 +{
 +      GVariantIter iter;
 +      GVariant *variant;
 +      GVariant *result = NULL;
 +
 +      nmtst_assert_variant_is_of_type (neighbors, G_VARIANT_TYPE ("aa{sv}"));
 +
 +      g_assert (chassis_id_type >= -1 && chassis_id_type <= G_MAXUINT8);
 +      g_assert (port_id_type >= -1 && port_id_type <= G_MAXUINT8);
 +
 +      g_variant_iter_init (&iter, neighbors);
 +      while (g_variant_iter_next (&iter, "@a{sv}", &variant)) {
 +              gs_unref_variant GVariant *v_chassis_id_type = NULL;
 +              gs_unref_variant GVariant *v_chassis_id = NULL;
 +              gs_unref_variant GVariant *v_port_id_type = NULL;
 +              gs_unref_variant GVariant *v_port_id = NULL;
 +
 +              v_chassis_id_type = g_variant_lookup_value (variant, NM_LLDP_ATTR_CHASSIS_ID_TYPE, G_VARIANT_TYPE_UINT32);
 +              g_assert (v_chassis_id_type);
 +
 +              v_chassis_id = g_variant_lookup_value (variant, NM_LLDP_ATTR_CHASSIS_ID, G_VARIANT_TYPE_STRING);
 +              g_assert (v_chassis_id);
 +
 +              v_port_id_type = g_variant_lookup_value (variant, NM_LLDP_ATTR_PORT_ID_TYPE, G_VARIANT_TYPE_UINT32);
 +              g_assert (v_port_id_type);
 +
 +              v_port_id = g_variant_lookup_value (variant, NM_LLDP_ATTR_PORT_ID, G_VARIANT_TYPE_STRING);
 +              g_assert (v_port_id);
 +
 +              if (   nm_streq (g_variant_get_string (v_chassis_id, NULL), chassis_id)
 +                  && nm_streq (g_variant_get_string (v_port_id, NULL), port_id)
 +                  && NM_IN_SET (chassis_id_type, -1, g_variant_get_uint32 (v_chassis_id_type))
 +                  && NM_IN_SET (port_id_type, -1, g_variant_get_uint32 (v_port_id_type))) {
 +                      g_assert (!result);
 +                      result = variant;
 +              } else
 +                      g_variant_unref (variant);
 +      }
 +
 +      return result;
 +}
 +
 +typedef struct {
 +      int ifindex;
 +      int fd;
 +      guint8 mac[ETH_ALEN];
 +} TestRecvFixture;
 +
 +typedef struct {
 +      gsize frame_len;
 +      const uint8_t *frame;
 +} TestRecvFrame;
 +#define TEST_RECV_FRAME_DEFINE(name, ...) \
 +      static const guint8 _##name##_v[] = { __VA_ARGS__ }; \
 +      static const TestRecvFrame name = { \
 +              .frame_len = sizeof (_##name##_v), \
 +              .frame = _##name##_v, \
 +      }
 +
 +typedef struct {
 +      guint expected_num_called;
 +      gsize frames_len;
 +      const TestRecvFrame *frames[10];
 +      void (*check) (GMainLoop *loop, NMLldpListener *listener);
 +} TestRecvData;
 +#define TEST_RECV_DATA_DEFINE(name, _expected_num_called, _check, ...) \
 +      static const TestRecvData name = { \
 +              .expected_num_called = _expected_num_called, \
 +              .check = _check, \
 +              .frames_len = NM_NARG (__VA_ARGS__), \
 +              .frames = { __VA_ARGS__ }, \
 +      }
 +
 +#define TEST_IFNAME "nm-tap-test0"
 +
 +TEST_RECV_FRAME_DEFINE (_test_recv_data0_frame0,
 +      /* Ethernet header */
 +      0x01, 0x80, 0xc2, 0x00, 0x00, 0x03,     /* Destination MAC */
 +      0x01, 0x02, 0x03, 0x04, 0x05, 0x06,     /* Source MAC */
 +      0x88, 0xcc,                             /* Ethertype */
 +      /* LLDP mandatory TLVs */
 +      0x02, 0x07, 0x04, 0x00, 0x01, 0x02,     /* Chassis: MAC, 00:01:02:03:04:05 */
 +      0x03, 0x04, 0x05,
 +      0x04, 0x04, 0x05, 0x31, 0x2f, 0x33,     /* Port: interface name, "1/3" */
 +      0x06, 0x02, 0x00, 0x78,                 /* TTL: 120 seconds */
 +      /* LLDP optional TLVs */
 +      0x08, 0x04, 0x50, 0x6f, 0x72, 0x74,     /* Port Description: "Port" */
 +      0x0a, 0x03, 0x53, 0x59, 0x53,           /* System Name: "SYS" */
 +      0x0c, 0x04, 0x66, 0x6f, 0x6f, 0x00,     /* System Description: "foo" (NULL-terminated) */
 +      0x00, 0x00                              /* End Of LLDPDU */
 +);
 +
 +static void
 +_test_recv_data0_check (GMainLoop *loop, NMLldpListener *listener)
 +{
 +      GVariant *neighbors, *attr;
 +      gs_unref_variant GVariant *neighbor = NULL;
 +
 +      neighbors = nm_lldp_listener_get_neighbors (listener);
 +      nmtst_assert_variant_is_of_type (neighbors, G_VARIANT_TYPE ("aa{sv}"));
 +      g_assert_cmpint (g_variant_n_children (neighbors), ==, 1);
 +
 +      neighbor = get_lldp_neighbor (neighbors,
 +                                    LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS, "00:01:02:03:04:05",
 +                                    LLDP_PORT_SUBTYPE_INTERFACE_NAME, "1/3");
 +      g_assert (neighbor);
 +      g_assert_cmpint (g_variant_n_children (neighbor), ==, 4 + 4);
 +
 +      attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_PORT_DESCRIPTION, G_VARIANT_TYPE_STRING);
 +      nmtst_assert_variant_string (attr, "Port");
 +      nm_clear_g_variant (&attr);
 +
 +      attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_SYSTEM_NAME, G_VARIANT_TYPE_STRING);
 +      nmtst_assert_variant_string (attr, "SYS");
 +      nm_clear_g_variant (&attr);
 +
 +      attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_DESTINATION, G_VARIANT_TYPE_STRING);
 +      nmtst_assert_variant_string (attr, NM_LLDP_DEST_NEAREST_NON_TPMR_BRIDGE);
 +      nm_clear_g_variant (&attr);
 +
 +      attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_SYSTEM_DESCRIPTION, G_VARIANT_TYPE_STRING);
 +      nmtst_assert_variant_string (attr, "foo");
 +      nm_clear_g_variant (&attr);
 +}
 +
 +TEST_RECV_DATA_DEFINE (_test_recv_data0,       1, _test_recv_data0_check,  &_test_recv_data0_frame0);
 +TEST_RECV_DATA_DEFINE (_test_recv_data0_twice, 1, _test_recv_data0_check,  &_test_recv_data0_frame0, &_test_recv_data0_frame0);
 +
 +
 +TEST_RECV_FRAME_DEFINE (_test_recv_data1_frame0,
 +      /* lldp.detailed.pcap from
 +       * https://wiki.wireshark.org/SampleCaptures#Link_Layer_Discovery_Protocol_.28LLDP.29 */
 +
 +      /* ethernet header */
 +      0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e, /* destination mac */
 +      0x00, 0x01, 0x30, 0xf9, 0xad, 0xa0, /* source mac */
 +      0x88, 0xcc,                         /* ethernet type */
 +
 +      0x02, 0x07, 0x04, 0x00, 0x01, 0x30, /* Chassis Subtype */
 +      0xf9, 0xad, 0xa0,
 +      0x04, 0x04, 0x05, 0x31, 0x2f, 0x31, /* Port Subtype */
 +      0x06, 0x02, 0x00, 0x78,             /* Time To Live */
 +      0x08, 0x17, 0x53, 0x75, 0x6d, 0x6d, /* Port Description */
 +      0x69, 0x74, 0x33, 0x30, 0x30, 0x2d,
 +      0x34, 0x38, 0x2d, 0x50, 0x6f, 0x72,
 +      0x74, 0x20, 0x31, 0x30, 0x30, 0x31,
 +      0x00,
 +      0x0a, 0x0d, 0x53, 0x75, 0x6d, 0x6d, /* System Name */
 +      0x69, 0x74, 0x33, 0x30, 0x30, 0x2d,
 +      0x34, 0x38, 0x00,
 +      0x0c, 0x4c, 0x53, 0x75, 0x6d, 0x6d, /* System Description */
 +      0x69, 0x74, 0x33, 0x30, 0x30, 0x2d,
 +      0x34, 0x38, 0x20, 0x2d, 0x20, 0x56,
 +      0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
 +      0x20, 0x37, 0x2e, 0x34, 0x65, 0x2e,
 +      0x31, 0x20, 0x28, 0x42, 0x75, 0x69,
 +      0x6c, 0x64, 0x20, 0x35, 0x29, 0x20,
 +      0x62, 0x79, 0x20, 0x52, 0x65, 0x6c,
 +      0x65, 0x61, 0x73, 0x65, 0x5f, 0x4d,
 +      0x61, 0x73, 0x74, 0x65, 0x72, 0x20,
 +      0x30, 0x35, 0x2f, 0x32, 0x37, 0x2f,
 +      0x30, 0x35, 0x20, 0x30, 0x34, 0x3a,
 +      0x35, 0x33, 0x3a, 0x31, 0x31, 0x00,
 +      0x0e, 0x04, 0x00, 0x14, 0x00, 0x14, /* Capabilities */
 +      0x10, 0x0e, 0x07, 0x06, 0x00, 0x01, /* Management Address */
 +      0x30, 0xf9, 0xad, 0xa0, 0x02, 0x00,
 +      0x00, 0x03, 0xe9, 0x00,
 +      0xfe, 0x07, 0x00, 0x12, 0x0f, 0x02, /* IEEE 802.3 - Power Via MDI */
 +      0x07, 0x01, 0x00,
 +      0xfe, 0x09, 0x00, 0x12, 0x0f, 0x01, /* IEEE 802.3 - MAC/PHY Configuration/Status */
 +      0x03, 0x6c, 0x00, 0x00, 0x10,
 +      0xfe, 0x09, 0x00, 0x12, 0x0f, 0x03, /* IEEE 802.3 - Link Aggregation */
 +      0x01, 0x00, 0x00, 0x00, 0x00,
 +      0xfe, 0x06, 0x00, 0x12, 0x0f, 0x04, /* IEEE 802.3 - Maximum Frame Size */
 +      0x05, 0xf2,
 +      0xfe, 0x06, 0x00, 0x80, 0xc2, 0x01, /* IEEE 802.1 - Port VLAN ID */
 +      0x01, 0xe8,
 +      0xfe, 0x07, 0x00, 0x80, 0xc2, 0x02, /* IEEE 802.1 - Port and Protocol VLAN ID */
 +      0x01, 0x00, 0x00,
 +      0xfe, 0x17, 0x00, 0x80, 0xc2, 0x03, /* IEEE 802.1 - VLAN Name */
 +      0x01, 0xe8, 0x10, 0x76, 0x32, 0x2d,
 +      0x30, 0x34, 0x38, 0x38, 0x2d, 0x30,
 +      0x33, 0x2d, 0x30, 0x35, 0x30, 0x35,
 +      0x00,
 +      0xfe, 0x05, 0x00, 0x80, 0xc2, 0x04, /* IEEE 802.1 - Protocol Identity */
 +      0x00,
 +      0x00, 0x00                          /* End of LLDPDU */
 +);
 +
 +static void
 +_test_recv_data1_check (GMainLoop *loop, NMLldpListener *listener)
 +{
 +      GVariant *neighbors, *attr;
 +      gs_unref_variant GVariant *neighbor = NULL;
 +
 +      neighbors = nm_lldp_listener_get_neighbors (listener);
 +      nmtst_assert_variant_is_of_type (neighbors, G_VARIANT_TYPE ("aa{sv}"));
 +      g_assert_cmpint (g_variant_n_children (neighbors), ==, 1);
 +
 +      neighbor = get_lldp_neighbor (neighbors,
 +                                    LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS, "00:01:30:F9:AD:A0",
 +                                    LLDP_PORT_SUBTYPE_INTERFACE_NAME, "1/1");
 +      g_assert (neighbor);
 +      g_assert_cmpint (g_variant_n_children (neighbor), ==, 4 + 10);
 +
 +      attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_DESTINATION, G_VARIANT_TYPE_STRING);
 +      nmtst_assert_variant_string (attr, NM_LLDP_DEST_NEAREST_BRIDGE);
 +      nm_clear_g_variant (&attr);
 +
 +      /* unsupported: Time To Live */
 +
 +      /* Port Description */
 +      attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_PORT_DESCRIPTION, G_VARIANT_TYPE_STRING);
 +      nmtst_assert_variant_string (attr, "Summit300-48-Port 1001");
 +      nm_clear_g_variant (&attr);
 +
 +      /* System Name */
 +      attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_SYSTEM_NAME, G_VARIANT_TYPE_STRING);
 +      nmtst_assert_variant_string (attr, "Summit300-48");
 +      nm_clear_g_variant (&attr);
 +
 +      /* System Description */
 +      attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_SYSTEM_DESCRIPTION, G_VARIANT_TYPE_STRING);
 +      nmtst_assert_variant_string (attr, "Summit300-48 - Version 7.4e.1 (Build 5) by Release_Master 05/27/05 04:53:11");
 +      nm_clear_g_variant (&attr);
 +
 +      /* Capabilities */
 +      attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_SYSTEM_CAPABILITIES, G_VARIANT_TYPE_UINT32);
 +      nmtst_assert_variant_uint32 (attr, 20);
 +      nm_clear_g_variant (&attr);
 +
 +      /* unsupported: Management Address */
 +      /* unsupported: IEEE 802.3 - Power Via MDI */
 +      /* unsupported: IEEE 802.3 - MAC/PHY Configuration/Status */
 +      /* unsupported: IEEE 802.3 - Link Aggregation */
 +      /* unsupported: IEEE 802.3 - Maximum Frame Size*/
 +
 +      /* IEEE 802.1 - Port VLAN ID */
 +      attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_IEEE_802_1_PVID, G_VARIANT_TYPE_UINT32);
 +      nmtst_assert_variant_uint32 (attr, 488);
 +      nm_clear_g_variant (&attr);
 +
 +      /* IEEE 802.1 - Port and Protocol VLAN ID */
 +      attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_IEEE_802_1_PPVID, G_VARIANT_TYPE_UINT32);
 +      nmtst_assert_variant_uint32 (attr, 0);
 +      nm_clear_g_variant (&attr);
 +      attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_IEEE_802_1_PPVID_FLAGS, G_VARIANT_TYPE_UINT32);
 +      nmtst_assert_variant_uint32 (attr, 1);
 +      nm_clear_g_variant (&attr);
 +
 +      /* IEEE 802.1 - VLAN Name */
 +      attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_IEEE_802_1_VLAN_NAME, G_VARIANT_TYPE_STRING);
 +      nmtst_assert_variant_string (attr, "v2-0488-03-0505");
 +      nm_clear_g_variant (&attr);
 +      attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_IEEE_802_1_VID, G_VARIANT_TYPE_UINT32);
 +      nmtst_assert_variant_uint32 (attr, 488);
 +      nm_clear_g_variant (&attr);
 +
 +      /* unsupported: IEEE 802.1 - Protocol Identity */
 +}
 +
 +TEST_RECV_DATA_DEFINE (_test_recv_data1,       1, _test_recv_data1_check,  &_test_recv_data1_frame0);
 +
 +TEST_RECV_FRAME_DEFINE (_test_recv_data2_frame0_ttl1,
 +      /* Ethernet header */
 +      0x01, 0x80, 0xc2, 0x00, 0x00, 0x03,     /* Destination MAC */
 +      0x01, 0x02, 0x03, 0x04, 0x05, 0x06,     /* Source MAC */
 +      0x88, 0xcc,                             /* Ethertype */
 +      /* LLDP mandatory TLVs */
 +      0x02, 0x07, 0x04, 0x00, 0x01, 0x02,     /* Chassis: MAC, 00:01:02:03:04:05 */
 +      0x03, 0x04, 0x05,
 +      0x04, 0x04, 0x05, 0x31, 0x2f, 0x33,     /* Port: interface name, "1/3" */
 +      0x06, 0x02, 0x00, 0x01,                 /* TTL: 1 seconds */
 +      /* LLDP optional TLVs */
 +      0x08, 0x04, 0x50, 0x6f, 0x72, 0x74,     /* Port Description: "Port" */
 +      0x0a, 0x03, 0x53, 0x59, 0x53,           /* System Name: "SYS" */
 +      0x0c, 0x04, 0x66, 0x6f, 0x6f, 0x00,     /* System Description: "foo" (NULL-terminated) */
 +      0x00, 0x00                              /* End Of LLDPDU */
 +);
 +
 +static void
 +_test_recv_data2_ttl1_check (GMainLoop *loop, NMLldpListener *listener)
 +{
 +      gulong notify_id;
 +      GVariant *neighbors;
 +
 +      _test_recv_data0_check (loop, listener);
 +
-       g_assert (nm_lldp_listener_start (listener, fixture->ifindex, TEST_IFNAME, fixture->mac, ETH_ALEN, NULL));
 +      /* wait for signal. */
 +      notify_id = g_signal_connect (listener, "notify::" NM_LLDP_LISTENER_NEIGHBORS,
 +                                    nmtst_main_loop_quit_on_notify, loop);
 +      if (!nmtst_main_loop_run (loop, 20000))
 +              g_assert_not_reached ();
 +      nm_clear_g_signal_handler (listener, &notify_id);
 +
 +      neighbors = nm_lldp_listener_get_neighbors (listener);
 +      nmtst_assert_variant_is_of_type (neighbors, G_VARIANT_TYPE ("aa{sv}"));
 +      g_assert_cmpint (g_variant_n_children (neighbors), ==, 0);
 +}
 +
 +TEST_RECV_DATA_DEFINE (_test_recv_data2_ttl1, 1, _test_recv_data2_ttl1_check,  &_test_recv_data2_frame0_ttl1);
 +
 +static void
 +_test_recv_fixture_setup (TestRecvFixture *fixture, gconstpointer user_data)
 +{
 +      const NMPlatformLink *link;
 +      struct ifreq ifr = { };
 +      int fd, s;
 +
 +      fd = open ("/dev/net/tun", O_RDWR);
 +      g_assert (fd >= 0);
 +
 +      ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
 +      nm_utils_ifname_cpy (ifr.ifr_name, TEST_IFNAME);
 +      g_assert (ioctl (fd, TUNSETIFF, &ifr) >= 0);
 +
 +      /* Bring the interface up */
 +      s = socket (AF_INET, SOCK_DGRAM, 0);
 +      g_assert (s >= 0);
 +      ifr.ifr_flags |= IFF_UP;
 +      g_assert (ioctl (s, SIOCSIFFLAGS, &ifr) >= 0);
 +      close (s);
 +
 +      link = nmtstp_assert_wait_for_link (TEST_IFNAME, NM_LINK_TYPE_TAP, 100);
 +      fixture->ifindex = link->ifindex;
 +      fixture->fd = fd;
 +      memcpy (fixture->mac, link->addr.data, ETH_ALEN);
 +}
 +
 +typedef struct {
 +      int num_called;
 +} TestRecvCallbackInfo;
 +
 +static void
 +lldp_neighbors_changed (NMLldpListener *lldp_listener, GParamSpec *pspec,
 +                        gpointer user_data)
 +{
 +      TestRecvCallbackInfo *info = user_data;
 +
 +      info->num_called++;
 +}
 +
 +static void
 +test_recv (TestRecvFixture *fixture, gconstpointer user_data)
 +{
 +      const TestRecvData *data = user_data;
 +      gs_unref_object NMLldpListener *listener = NULL;
 +      GMainLoop *loop;
 +      TestRecvCallbackInfo info = { };
 +      gsize i_frames;
 +      gulong notify_id;
++      GError *error = NULL;
 +
 +      listener = nm_lldp_listener_new ();
 +      g_assert (listener != NULL);
++      g_assert (nm_lldp_listener_start (listener, fixture->ifindex, &error));
++      g_assert_no_error (error);
 +
 +      notify_id = g_signal_connect (listener, "notify::" NM_LLDP_LISTENER_NEIGHBORS,
 +                                    (GCallback) lldp_neighbors_changed, &info);
 +      loop = g_main_loop_new (NULL, FALSE);
 +
 +      for (i_frames = 0; i_frames < data->frames_len; i_frames++) {
 +              const TestRecvFrame *f = data->frames[i_frames];
 +
 +              g_assert (write (fixture->fd, f->frame, f->frame_len) == f->frame_len);
 +      }
 +
 +      if (nmtst_main_loop_run (loop, 500))
 +              g_assert_not_reached ();
 +
 +      g_assert_cmpint (info.num_called, ==, data->expected_num_called);
 +
 +      nm_clear_g_signal_handler (listener, &notify_id);
 +
 +      data->check (loop, listener);
 +
 +      g_clear_pointer (&loop, g_main_loop_unref);
 +}
 +
 +static void
 +_test_recv_fixture_teardown (TestRecvFixture *fixture, gconstpointer user_data)
 +{
 +      nm_platform_link_delete (NM_PLATFORM_GET, fixture->ifindex);
 +}
 +
 +/*****************************************************************************/
 +
 +void
 +init_tests (int *argc, char ***argv)
 +{
 +      nmtst_init_assert_logging (argc, argv, "WARN", "ALL");
 +}
 +
 +void
 +setup_tests (void)
 +{
 +#define _TEST_ADD_RECV(testpath, testdata) \
 +      g_test_add (testpath, TestRecvFixture, testdata, _test_recv_fixture_setup, test_recv, _test_recv_fixture_teardown)
 +      _TEST_ADD_RECV ("/lldp/recv/0",       &_test_recv_data0);
 +      _TEST_ADD_RECV ("/lldp/recv/0_twice", &_test_recv_data0_twice);
 +      _TEST_ADD_RECV ("/lldp/recv/1",       &_test_recv_data1);
 +      _TEST_ADD_RECV ("/lldp/recv/2_ttl1",  &_test_recv_data2_ttl1);
 +}
index fa7cd93,0000000..40d52e7
mode 100644,000000..100644
--- /dev/null
@@@ -1,238 -1,0 +1,252 @@@
 +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
 +/* This program is free software; you can redistribute it and/or modify
 + * it under the terms of the GNU General Public License as published by
 + * the Free Software Foundation; either version 2, or (at your option)
 + * any later version.
 + *
 + * 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.
 + *
 + * You should have received a copy of the GNU General Public License along
 + * with this program; if not, write to the Free Software Foundation, Inc.,
 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 + *
 + * Copyright (C) 2014 Red Hat, Inc.
 + */
 +
 +#include "nm-default.h"
 +
 +#include "nm-sd-adapt.h"
 +
 +#include <unistd.h>
 +#include <errno.h>
 +
 +#include "sd-event.h"
 +#include "fd-util.h"
 +#include "time-util.h"
 +
 +struct sd_event_source {
 +      guint refcount;
 +      guint id;
 +      gpointer user_data;
 +
 +      GIOChannel *channel;
 +
 +      union {
 +              struct {
 +                      sd_event_io_handler_t cb;
 +              } io;
 +              struct {
 +                      sd_event_time_handler_t cb;
 +                      uint64_t usec;
 +              } time;
 +      };
 +};
 +
 +static struct sd_event_source *
 +source_new (void)
 +{
 +      struct sd_event_source *source;
 +
 +      source = g_slice_new0 (struct sd_event_source);
 +      source->refcount = 1;
 +      return source;
 +}
 +
 +int
 +sd_event_source_set_priority (sd_event_source *s, int64_t priority)
 +{
 +      return 0;
 +}
 +
++int
++sd_event_source_set_enabled (sd_event_source *s, int m)
++{
++      /* TODO */
++      g_return_val_if_reached (-EINVAL);
++}
++
++int
++sd_event_source_set_time (sd_event_source *s, uint64_t usec)
++{
++      /* TODO */
++      g_return_val_if_reached (-EINVAL);
++}
++
 +sd_event_source*
 +sd_event_source_unref (sd_event_source *s)
 +{
 +
 +      if (!s)
 +              return NULL;
 +
 +      g_return_val_if_fail (s->refcount, NULL);
 +
 +      s->refcount--;
 +      if (s->refcount == 0) {
 +              if (s->id)
 +                      g_source_remove (s->id);
 +              if (s->channel) {
 +                      /* Don't shut down the channel since systemd will soon close
 +                       * the file descriptor itself, which would cause -EBADF.
 +                       */
 +                      g_io_channel_unref (s->channel);
 +              }
 +              g_slice_free (struct sd_event_source, s);
 +      }
 +      return NULL;
 +}
 +
 +int
 +sd_event_source_set_description(sd_event_source *s, const char *description)
 +{
 +      if (!s)
 +              return -EINVAL;
 +
 +      g_source_set_name_by_id (s->id, description);
 +      return 0;
 +}
 +
 +static gboolean
 +io_ready (GIOChannel *channel, GIOCondition condition, struct sd_event_source *source)
 +{
 +      int r, revents = 0;
 +      gboolean result;
 +
 +      if (condition & G_IO_IN)
 +              revents |= EPOLLIN;
 +      if (condition & G_IO_OUT)
 +              revents |= EPOLLOUT;
 +      if (condition & G_IO_PRI)
 +              revents |= EPOLLPRI;
 +      if (condition & G_IO_ERR)
 +              revents |= EPOLLERR;
 +      if (condition & G_IO_HUP)
 +              revents |= EPOLLHUP;
 +
 +      source->refcount++;
 +
 +      r = source->io.cb (source, g_io_channel_unix_get_fd (channel), revents, source->user_data);
 +      if (r < 0 || source->refcount <= 1) {
 +              source->id = 0;
 +              result = G_SOURCE_REMOVE;
 +      } else
 +              result = G_SOURCE_CONTINUE;
 +
 +      sd_event_source_unref (source);
 +
 +      return result;
 +}
 +
 +int
 +sd_event_add_io (sd_event *e, sd_event_source **s, int fd, uint32_t events, sd_event_io_handler_t callback, void *userdata)
 +{
 +      struct sd_event_source *source;
 +      GIOChannel *channel;
 +      GIOCondition condition = 0;
 +
 +      /* systemd supports floating sd_event_source by omitting the @s argument.
 +       * We don't have such users and don't implement floating references. */
 +      g_return_val_if_fail (s, -EINVAL);
 +
 +      channel = g_io_channel_unix_new (fd);
 +      if (!channel)
 +              return -EINVAL;
 +
 +      source = source_new ();
 +      source->io.cb = callback;
 +      source->user_data = userdata;
 +      source->channel = channel;
 +
 +      if (events & EPOLLIN)
 +              condition |= G_IO_IN;
 +      if (events & EPOLLOUT)
 +              condition |= G_IO_OUT;
 +      if (events & EPOLLPRI)
 +              condition |= G_IO_PRI;
 +      if (events & EPOLLERR)
 +              condition |= G_IO_ERR;
 +      if (events & EPOLLHUP)
 +              condition |= G_IO_HUP;
 +
 +      g_io_channel_set_encoding (source->channel, NULL, NULL);
 +      g_io_channel_set_buffered (source->channel, FALSE);
 +      source->id = g_io_add_watch (source->channel, condition, (GIOFunc) io_ready, source);
 +
 +      *s = source;
 +      return 0;
 +}
 +
 +static gboolean
 +time_ready (struct sd_event_source *source)
 +{
 +      source->refcount++;
 +
 +      source->time.cb (source, source->time.usec, source->user_data);
 +      source->id = 0;
 +
 +      sd_event_source_unref (source);
 +
 +      return G_SOURCE_REMOVE;
 +}
 +
 +int
 +sd_event_add_time(sd_event *e, sd_event_source **s, clockid_t clock, uint64_t usec, uint64_t accuracy, sd_event_time_handler_t callback, void *userdata)
 +{
 +      struct sd_event_source *source;
 +      uint64_t n = now (clock);
 +
 +      /* systemd supports floating sd_event_source by omitting the @s argument.
 +       * We don't have such users and don't implement floating references. */
 +      g_return_val_if_fail (s, -EINVAL);
 +
 +      source = source_new ();
 +      source->time.cb = callback;
 +      source->user_data = userdata;
 +      source->time.usec = usec;
 +
 +      if (usec > 1000)
 +              usec = n < usec - 1000 ? usec - n : 1000;
 +      source->id = g_timeout_add (usec / 1000, (GSourceFunc) time_ready, source);
 +
 +      *s = source;
 +      return 0;
 +}
 +
 +/* sd_event is basically a GMainContext; but since we only
 + * ever use the default context, nothing to do here.
 + */
 +
 +int
 +sd_event_default (sd_event **e)
 +{
 +      *e = GUINT_TO_POINTER (1);
 +      return 0;
 +}
 +
 +sd_event*
 +sd_event_ref (sd_event *e)
 +{
 +      return e;
 +}
 +
 +sd_event*
 +sd_event_unref (sd_event *e)
 +{
 +      return NULL;
 +}
 +
 +int
 +sd_event_now (sd_event *e, clockid_t clock, uint64_t *usec)
 +{
 +      *usec = now (clock);
 +      return 0;
 +}
 +
 +int asynchronous_close(int fd) {
 +      safe_close(fd);
 +      return -1;
 +}
 +
index f48c86f,0000000..1547f12
mode 100644,000000..100644
--- /dev/null
@@@ -1,156 -1,0 +1,161 @@@
 +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
 +/* This program is free software; you can redistribute it and/or modify
 + * it under the terms of the GNU General Public License as published by
 + * the Free Software Foundation; either version 2, or (at your option)
 + * any later version.
 + *
 + * 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.
 + *
 + * You should have received a copy of the GNU General Public License along
 + * with this program; if not, write to the Free Software Foundation, Inc.,
 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 + *
 + * Copyright (C) 2014 - 2015 Red Hat, Inc.
 + */
 +
 +#ifndef NM_SD_ADAPT_H
 +#define NM_SD_ADAPT_H
 +
 +#include "nm-default.h"
 +
 +#include <stdbool.h>
 +#include <syslog.h>
 +#include <sys/resource.h>
 +#include <time.h>
 +
 +#define noreturn G_GNUC_NORETURN
 +
 +#ifndef CLOCK_BOOTTIME
 +#define CLOCK_BOOTTIME 7
 +#endif
 +
 +/*****************************************************************************/
 +
 +static inline NMLogLevel
 +_slog_level_to_nm (int slevel)
 +{
 +    switch (slevel) {
 +    case LOG_DEBUG:   return LOGL_DEBUG;
 +      case LOG_WARNING: return LOGL_WARN;
 +      case LOG_CRIT:
 +      case LOG_ERR:     return LOGL_ERR;
 +      case LOG_INFO:
 +      case LOG_NOTICE:
 +      default:          return LOGL_INFO;
 +      }
 +}
 +
 +#define log_internal(level, error, file, line, func, format, ...) \
 +({ \
 +      const int _nm_e = (error); \
 +      const NMLogLevel _nm_l = _slog_level_to_nm ((level)); \
 +      \
 +      if (nm_logging_enabled (_nm_l, LOGD_DHCP)) { \
 +              const char *_nm_location = strrchr ((""file), '/'); \
 +              \
 +              _nm_log_impl (_nm_location ? _nm_location + 1 : (""file), (line), (func), _nm_l, LOGD_DHCP, _nm_e, ("%s"format), "libsystemd: ", ## __VA_ARGS__); \
 +      } \
 +      (_nm_e > 0 ? -_nm_e : _nm_e); \
 +})
 +
 +#define log_full_errno(level, error, ...) \
 +({ \
 +      log_internal(level, error, __FILE__, __LINE__, __func__, __VA_ARGS__); \
 +})
 +
 +#define log_assert_failed(text, file, line, func) \
 +G_STMT_START { \
 +      log_internal (LOG_CRIT, 0, file, line, func, "Assertion '%s' failed at %s:%u, function %s(). Aborting.", text, file, line, func); \
 +      g_assert_not_reached (); \
 +} G_STMT_END
 +
 +#define log_assert_failed_unreachable(text, file, line, func) \
 +G_STMT_START { \
 +      log_internal (LOG_CRIT, 0, file, line, func, "Code should not be reached '%s' at %s:%u, function %s(). Aborting.", text, file, line, func); \
 +      g_assert_not_reached (); \
 +} G_STMT_END
 +
 +#define log_assert_failed_return(text, file, line, func) \
 +({ \
 +      log_internal (LOG_DEBUG, 0, file, line, func, "Assertion '%s' failed at %s:%u, function %s(). Ignoring.", text, file, line, func); \
 +      g_return_if_fail_warning (G_LOG_DOMAIN, G_STRFUNC, text); \
 +      (void) 0; \
 +})
 +
 +/*****************************************************************************
 + * The remainder of the header is only enabled when building the systemd code
 + * itself.
 + *****************************************************************************/
 +
 +#if (NETWORKMANAGER_COMPILATION) == NM_NETWORKMANAGER_COMPILATION_SYSTEMD
 +
 +#include <netinet/in.h>
 +#include <string.h>
 +#include <stdio.h>
 +#include <errno.h>
 +#include <elf.h>
 +#ifdef HAVE_SYS_AUXV_H
 +#include <sys/auxv.h>
 +#endif
 +#include <unistd.h>
 +#include <sys/syscall.h>
++#include <sys/ioctl.h>
 +
 +#include <net/if_arp.h>
 +
 +/* Missing in Linux 3.2.0, in Ubuntu 12.04 */
 +#ifndef BPF_XOR
 +#define BPF_XOR 0xa0
 +#endif
 +
++#ifndef ETHERTYPE_LLDP
++#define ETHERTYPE_LLDP 0x88cc
++#endif
++
 +/*****************************************************************************/
 +
 +/* work around missing uchar.h */
 +typedef guint16 char16_t;
 +typedef guint32 char32_t;
 +
 +/*****************************************************************************/
 +
 +/* Can't include both net/if.h and linux/if.h; so have to define this here */
 +#ifndef IFNAMSIZ
 +#define IFNAMSIZ 16
 +#endif
 +
 +#ifndef MAX_HANDLE_SZ
 +#define MAX_HANDLE_SZ 128
 +#endif
 +
 +
 +/*
 + * Some toolchains (E.G. uClibc 0.9.33 and earlier) don't export
 + * CLOCK_BOOTTIME even though the kernel supports it, so provide a
 + * local definition
 + */
 +#ifndef CLOCK_BOOTTIME
 +#define CLOCK_BOOTTIME 7
 +#endif
 +
 +#include "sd-id128.h"
 +#include "sparse-endian.h"
 +#include "async.h"
 +#include "util.h"
 +
 +static inline pid_t gettid(void) {
 +        return (pid_t) syscall(SYS_gettid);
 +}
 +
 +static inline bool is_main_thread(void) {
 +        return TRUE;
 +}
 +
 +#endif /* (NETWORKMANAGER_COMPILATION) == NM_NETWORKMANAGER_COMPILATION_SYSTEMD */
 +
 +#endif /* NM_SD_ADAPT_H */
 +
Simple merge
Simple merge
index 0000000,a4d8d65..0219300
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,56 +1,58 @@@
+ /***
+   This file is part of systemd.
+   Copyright 2014 Tom Gundersen
+   systemd 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; either version 2.1 of the License, or
+   (at your option) any later version.
+   systemd 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
+   Lesser General Public License for more details.
+   You should have received a copy of the GNU Lesser General Public License
+   along with systemd; If not, see <http://www.gnu.org/licenses/>.
+ ***/
++#include "nm-sd-adapt.h"
++
+ #include <net/ethernet.h>
+ #include <stdio.h>
+ #include <sys/types.h>
+ #include "ether-addr-util.h"
+ #include "macro.h"
+ char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]) {
+         assert(addr);
+         assert(buffer);
+         /* Like ether_ntoa() but uses %02x instead of %x to print
+          * ethernet addresses, which makes them look less funny. Also,
+          * doesn't use a static buffer. */
+         sprintf(buffer, "%02x:%02x:%02x:%02x:%02x:%02x",
+                 addr->ether_addr_octet[0],
+                 addr->ether_addr_octet[1],
+                 addr->ether_addr_octet[2],
+                 addr->ether_addr_octet[3],
+                 addr->ether_addr_octet[4],
+                 addr->ether_addr_octet[5]);
+         return buffer;
+ }
+ bool ether_addr_equal(const struct ether_addr *a, const struct ether_addr *b) {
+         assert(a);
+         assert(b);
+         return  a->ether_addr_octet[0] == b->ether_addr_octet[0] &&
+                 a->ether_addr_octet[1] == b->ether_addr_octet[1] &&
+                 a->ether_addr_octet[2] == b->ether_addr_octet[2] &&
+                 a->ether_addr_octet[3] == b->ether_addr_octet[3] &&
+                 a->ether_addr_octet[4] == b->ether_addr_octet[4] &&
+                 a->ether_addr_octet[5] == b->ether_addr_octet[5];
+ }
Simple merge
@@@ -291,25 -282,6 +291,7 @@@ int chmod_and_chown(const char *path, m
  
          return 0;
  }
- int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid) {
-         assert(fd >= 0);
-         /* Under the assumption that we are running privileged we
-          * first change the access mode and only then hand out
-          * ownership to avoid a window where access is too open. */
-         if (mode != MODE_INVALID)
-                 if (fchmod(fd, mode) < 0)
-                         return -errno;
-         if (uid != UID_INVALID || gid != GID_INVALID)
-                 if (fchown(fd, uid, gid) < 0)
-                         return -errno;
-         return 0;
- }
 +#endif /* NM_IGNORED */
  
  int fchmod_umask(int fd, mode_t m) {
          mode_t u;
Simple merge
Simple merge
@@@ -17,9 -17,6 +17,8 @@@
    along with systemd; If not, see <http://www.gnu.org/licenses/>.
  ***/
  
- #include <bits/local_lim.h>
 +#include "nm-sd-adapt.h"
 +
  #include <errno.h>
  #include <limits.h>
  #include <stdio.h>
Simple merge
Simple merge
Simple merge
Simple merge
index 0000000,0f38f9a..c68db7c
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,972 +1,982 @@@
+ /***
+   This file is part of systemd.
+   Copyright 2010 Lennart Poettering
+   systemd 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; either version 2.1 of the License, or
+   (at your option) any later version.
+   systemd 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
+   Lesser General Public License for more details.
+   You should have received a copy of the GNU Lesser General Public License
+   along with systemd; If not, see <http://www.gnu.org/licenses/>.
+ ***/
++#include "nm-sd-adapt.h"
++
+ #include <arpa/inet.h>
+ #include <errno.h>
+ #include <limits.h>
+ #include <net/if.h>
+ #include <netdb.h>
+ #include <netinet/ip.h>
+ #include <stddef.h>
+ #include <stdint.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+ #include <unistd.h>
+ #include "alloc-util.h"
+ #include "fd-util.h"
+ #include "fileio.h"
++#if 0 /* NM_IGNORED */
+ #include "formats-util.h"
++#endif /* NM_IGNORED */
+ #include "log.h"
+ #include "macro.h"
++#if 0 /* NM_IGNORED */
+ #include "missing.h"
++#endif /* NM_IGNORED */
+ #include "parse-util.h"
+ #include "path-util.h"
+ #include "socket-util.h"
+ #include "string-table.h"
+ #include "string-util.h"
++#if 0 /* NM_IGNORED */
+ #include "user-util.h"
++#endif /* NM_IGNORED */
+ #include "util.h"
++#if 0 /* NM_IGNORED */
+ int socket_address_parse(SocketAddress *a, const char *s) {
+         char *e, *n;
+         unsigned u;
+         int r;
+         assert(a);
+         assert(s);
+         zero(*a);
+         a->type = SOCK_STREAM;
+         if (*s == '[') {
+                 /* IPv6 in [x:.....:z]:p notation */
+                 e = strchr(s+1, ']');
+                 if (!e)
+                         return -EINVAL;
+                 n = strndupa(s+1, e-s-1);
+                 errno = 0;
+                 if (inet_pton(AF_INET6, n, &a->sockaddr.in6.sin6_addr) <= 0)
+                         return errno > 0 ? -errno : -EINVAL;
+                 e++;
+                 if (*e != ':')
+                         return -EINVAL;
+                 e++;
+                 r = safe_atou(e, &u);
+                 if (r < 0)
+                         return r;
+                 if (u <= 0 || u > 0xFFFF)
+                         return -EINVAL;
+                 a->sockaddr.in6.sin6_family = AF_INET6;
+                 a->sockaddr.in6.sin6_port = htons((uint16_t) u);
+                 a->size = sizeof(struct sockaddr_in6);
+         } else if (*s == '/') {
+                 /* AF_UNIX socket */
+                 size_t l;
+                 l = strlen(s);
+                 if (l >= sizeof(a->sockaddr.un.sun_path))
+                         return -EINVAL;
+                 a->sockaddr.un.sun_family = AF_UNIX;
+                 memcpy(a->sockaddr.un.sun_path, s, l);
+                 a->size = offsetof(struct sockaddr_un, sun_path) + l + 1;
+         } else if (*s == '@') {
+                 /* Abstract AF_UNIX socket */
+                 size_t l;
+                 l = strlen(s+1);
+                 if (l >= sizeof(a->sockaddr.un.sun_path) - 1)
+                         return -EINVAL;
+                 a->sockaddr.un.sun_family = AF_UNIX;
+                 memcpy(a->sockaddr.un.sun_path+1, s+1, l);
+                 a->size = offsetof(struct sockaddr_un, sun_path) + 1 + l;
+         } else {
+                 e = strchr(s, ':');
+                 if (e) {
+                         r = safe_atou(e+1, &u);
+                         if (r < 0)
+                                 return r;
+                         if (u <= 0 || u > 0xFFFF)
+                                 return -EINVAL;
+                         n = strndupa(s, e-s);
+                         /* IPv4 in w.x.y.z:p notation? */
+                         r = inet_pton(AF_INET, n, &a->sockaddr.in.sin_addr);
+                         if (r < 0)
+                                 return -errno;
+                         if (r > 0) {
+                                 /* Gotcha, it's a traditional IPv4 address */
+                                 a->sockaddr.in.sin_family = AF_INET;
+                                 a->sockaddr.in.sin_port = htons((uint16_t) u);
+                                 a->size = sizeof(struct sockaddr_in);
+                         } else {
+                                 unsigned idx;
+                                 if (strlen(n) > IF_NAMESIZE-1)
+                                         return -EINVAL;
+                                 /* Uh, our last resort, an interface name */
+                                 idx = if_nametoindex(n);
+                                 if (idx == 0)
+                                         return -EINVAL;
+                                 a->sockaddr.in6.sin6_family = AF_INET6;
+                                 a->sockaddr.in6.sin6_port = htons((uint16_t) u);
+                                 a->sockaddr.in6.sin6_scope_id = idx;
+                                 a->sockaddr.in6.sin6_addr = in6addr_any;
+                                 a->size = sizeof(struct sockaddr_in6);
+                         }
+                 } else {
+                         /* Just a port */
+                         r = safe_atou(s, &u);
+                         if (r < 0)
+                                 return r;
+                         if (u <= 0 || u > 0xFFFF)
+                                 return -EINVAL;
+                         if (socket_ipv6_is_supported()) {
+                                 a->sockaddr.in6.sin6_family = AF_INET6;
+                                 a->sockaddr.in6.sin6_port = htons((uint16_t) u);
+                                 a->sockaddr.in6.sin6_addr = in6addr_any;
+                                 a->size = sizeof(struct sockaddr_in6);
+                         } else {
+                                 a->sockaddr.in.sin_family = AF_INET;
+                                 a->sockaddr.in.sin_port = htons((uint16_t) u);
+                                 a->sockaddr.in.sin_addr.s_addr = INADDR_ANY;
+                                 a->size = sizeof(struct sockaddr_in);
+                         }
+                 }
+         }
+         return 0;
+ }
+ int socket_address_parse_and_warn(SocketAddress *a, const char *s) {
+         SocketAddress b;
+         int r;
+         /* Similar to socket_address_parse() but warns for IPv6 sockets when we don't support them. */
+         r = socket_address_parse(&b, s);
+         if (r < 0)
+                 return r;
+         if (!socket_ipv6_is_supported() && b.sockaddr.sa.sa_family == AF_INET6) {
+                 log_warning("Binding to IPv6 address not available since kernel does not support IPv6.");
+                 return -EAFNOSUPPORT;
+         }
+         *a = b;
+         return 0;
+ }
+ int socket_address_parse_netlink(SocketAddress *a, const char *s) {
+         int family;
+         unsigned group = 0;
+         _cleanup_free_ char *sfamily = NULL;
+         assert(a);
+         assert(s);
+         zero(*a);
+         a->type = SOCK_RAW;
+         errno = 0;
+         if (sscanf(s, "%ms %u", &sfamily, &group) < 1)
+                 return errno > 0 ? -errno : -EINVAL;
+         family = netlink_family_from_string(sfamily);
+         if (family < 0)
+                 return -EINVAL;
+         a->sockaddr.nl.nl_family = AF_NETLINK;
+         a->sockaddr.nl.nl_groups = group;
+         a->type = SOCK_RAW;
+         a->size = sizeof(struct sockaddr_nl);
+         a->protocol = family;
+         return 0;
+ }
+ int socket_address_verify(const SocketAddress *a) {
+         assert(a);
+         switch (socket_address_family(a)) {
+         case AF_INET:
+                 if (a->size != sizeof(struct sockaddr_in))
+                         return -EINVAL;
+                 if (a->sockaddr.in.sin_port == 0)
+                         return -EINVAL;
+                 if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM)
+                         return -EINVAL;
+                 return 0;
+         case AF_INET6:
+                 if (a->size != sizeof(struct sockaddr_in6))
+                         return -EINVAL;
+                 if (a->sockaddr.in6.sin6_port == 0)
+                         return -EINVAL;
+                 if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM)
+                         return -EINVAL;
+                 return 0;
+         case AF_UNIX:
+                 if (a->size < offsetof(struct sockaddr_un, sun_path))
+                         return -EINVAL;
+                 if (a->size > offsetof(struct sockaddr_un, sun_path)) {
+                         if (a->sockaddr.un.sun_path[0] != 0) {
+                                 char *e;
+                                 /* path */
+                                 e = memchr(a->sockaddr.un.sun_path, 0, sizeof(a->sockaddr.un.sun_path));
+                                 if (!e)
+                                         return -EINVAL;
+                                 if (a->size != offsetof(struct sockaddr_un, sun_path) + (e - a->sockaddr.un.sun_path) + 1)
+                                         return -EINVAL;
+                         }
+                 }
+                 if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM && a->type != SOCK_SEQPACKET)
+                         return -EINVAL;
+                 return 0;
+         case AF_NETLINK:
+                 if (a->size != sizeof(struct sockaddr_nl))
+                         return -EINVAL;
+                 if (a->type != SOCK_RAW && a->type != SOCK_DGRAM)
+                         return -EINVAL;
+                 return 0;
+         default:
+                 return -EAFNOSUPPORT;
+         }
+ }
+ int socket_address_print(const SocketAddress *a, char **ret) {
+         int r;
+         assert(a);
+         assert(ret);
+         r = socket_address_verify(a);
+         if (r < 0)
+                 return r;
+         if (socket_address_family(a) == AF_NETLINK) {
+                 _cleanup_free_ char *sfamily = NULL;
+                 r = netlink_family_to_string_alloc(a->protocol, &sfamily);
+                 if (r < 0)
+                         return r;
+                 r = asprintf(ret, "%s %u", sfamily, a->sockaddr.nl.nl_groups);
+                 if (r < 0)
+                         return -ENOMEM;
+                 return 0;
+         }
+         return sockaddr_pretty(&a->sockaddr.sa, a->size, false, true, ret);
+ }
+ bool socket_address_can_accept(const SocketAddress *a) {
+         assert(a);
+         return
+                 a->type == SOCK_STREAM ||
+                 a->type == SOCK_SEQPACKET;
+ }
+ bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) {
+         assert(a);
+         assert(b);
+         /* Invalid addresses are unequal to all */
+         if (socket_address_verify(a) < 0 ||
+             socket_address_verify(b) < 0)
+                 return false;
+         if (a->type != b->type)
+                 return false;
+         if (socket_address_family(a) != socket_address_family(b))
+                 return false;
+         switch (socket_address_family(a)) {
+         case AF_INET:
+                 if (a->sockaddr.in.sin_addr.s_addr != b->sockaddr.in.sin_addr.s_addr)
+                         return false;
+                 if (a->sockaddr.in.sin_port != b->sockaddr.in.sin_port)
+                         return false;
+                 break;
+         case AF_INET6:
+                 if (memcmp(&a->sockaddr.in6.sin6_addr, &b->sockaddr.in6.sin6_addr, sizeof(a->sockaddr.in6.sin6_addr)) != 0)
+                         return false;
+                 if (a->sockaddr.in6.sin6_port != b->sockaddr.in6.sin6_port)
+                         return false;
+                 break;
+         case AF_UNIX:
+                 if (a->size <= offsetof(struct sockaddr_un, sun_path) ||
+                     b->size <= offsetof(struct sockaddr_un, sun_path))
+                         return false;
+                 if ((a->sockaddr.un.sun_path[0] == 0) != (b->sockaddr.un.sun_path[0] == 0))
+                         return false;
+                 if (a->sockaddr.un.sun_path[0]) {
+                         if (!path_equal_or_files_same(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path))
+                                 return false;
+                 } else {
+                         if (a->size != b->size)
+                                 return false;
+                         if (memcmp(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, a->size) != 0)
+                                 return false;
+                 }
+                 break;
+         case AF_NETLINK:
+                 if (a->protocol != b->protocol)
+                         return false;
+                 if (a->sockaddr.nl.nl_groups != b->sockaddr.nl.nl_groups)
+                         return false;
+                 break;
+         default:
+                 /* Cannot compare, so we assume the addresses are different */
+                 return false;
+         }
+         return true;
+ }
+ bool socket_address_is(const SocketAddress *a, const char *s, int type) {
+         struct SocketAddress b;
+         assert(a);
+         assert(s);
+         if (socket_address_parse(&b, s) < 0)
+                 return false;
+         b.type = type;
+         return socket_address_equal(a, &b);
+ }
+ bool socket_address_is_netlink(const SocketAddress *a, const char *s) {
+         struct SocketAddress b;
+         assert(a);
+         assert(s);
+         if (socket_address_parse_netlink(&b, s) < 0)
+                 return false;
+         return socket_address_equal(a, &b);
+ }
+ const char* socket_address_get_path(const SocketAddress *a) {
+         assert(a);
+         if (socket_address_family(a) != AF_UNIX)
+                 return NULL;
+         if (a->sockaddr.un.sun_path[0] == 0)
+                 return NULL;
+         return a->sockaddr.un.sun_path;
+ }
+ bool socket_ipv6_is_supported(void) {
+         if (access("/proc/net/sockstat6", F_OK) != 0)
+                 return false;
+         return true;
+ }
+ bool socket_address_matches_fd(const SocketAddress *a, int fd) {
+         SocketAddress b;
+         socklen_t solen;
+         assert(a);
+         assert(fd >= 0);
+         b.size = sizeof(b.sockaddr);
+         if (getsockname(fd, &b.sockaddr.sa, &b.size) < 0)
+                 return false;
+         if (b.sockaddr.sa.sa_family != a->sockaddr.sa.sa_family)
+                 return false;
+         solen = sizeof(b.type);
+         if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &b.type, &solen) < 0)
+                 return false;
+         if (b.type != a->type)
+                 return false;
+         if (a->protocol != 0)  {
+                 solen = sizeof(b.protocol);
+                 if (getsockopt(fd, SOL_SOCKET, SO_PROTOCOL, &b.protocol, &solen) < 0)
+                         return false;
+                 if (b.protocol != a->protocol)
+                         return false;
+         }
+         return socket_address_equal(a, &b);
+ }
+ int sockaddr_port(const struct sockaddr *_sa) {
+         union sockaddr_union *sa = (union sockaddr_union*) _sa;
+         assert(sa);
+         if (!IN_SET(sa->sa.sa_family, AF_INET, AF_INET6))
+                 return -EAFNOSUPPORT;
+         return ntohs(sa->sa.sa_family == AF_INET6 ?
+                        sa->in6.sin6_port :
+                        sa->in.sin_port);
+ }
+ int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret) {
+         union sockaddr_union *sa = (union sockaddr_union*) _sa;
+         char *p;
+         int r;
+         assert(sa);
+         assert(salen >= sizeof(sa->sa.sa_family));
+         switch (sa->sa.sa_family) {
+         case AF_INET: {
+                 uint32_t a;
+                 a = ntohl(sa->in.sin_addr.s_addr);
+                 if (include_port)
+                         r = asprintf(&p,
+                                      "%u.%u.%u.%u:%u",
+                                      a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF,
+                                      ntohs(sa->in.sin_port));
+                 else
+                         r = asprintf(&p,
+                                      "%u.%u.%u.%u",
+                                      a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF);
+                 if (r < 0)
+                         return -ENOMEM;
+                 break;
+         }
+         case AF_INET6: {
+                 static const unsigned char ipv4_prefix[] = {
+                         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF
+                 };
+                 if (translate_ipv6 &&
+                     memcmp(&sa->in6.sin6_addr, ipv4_prefix, sizeof(ipv4_prefix)) == 0) {
+                         const uint8_t *a = sa->in6.sin6_addr.s6_addr+12;
+                         if (include_port)
+                                 r = asprintf(&p,
+                                              "%u.%u.%u.%u:%u",
+                                              a[0], a[1], a[2], a[3],
+                                              ntohs(sa->in6.sin6_port));
+                         else
+                                 r = asprintf(&p,
+                                              "%u.%u.%u.%u",
+                                              a[0], a[1], a[2], a[3]);
+                         if (r < 0)
+                                 return -ENOMEM;
+                 } else {
+                         char a[INET6_ADDRSTRLEN];
+                         inet_ntop(AF_INET6, &sa->in6.sin6_addr, a, sizeof(a));
+                         if (include_port) {
+                                 r = asprintf(&p,
+                                              "[%s]:%u",
+                                              a,
+                                              ntohs(sa->in6.sin6_port));
+                                 if (r < 0)
+                                         return -ENOMEM;
+                         } else {
+                                 p = strdup(a);
+                                 if (!p)
+                                         return -ENOMEM;
+                         }
+                 }
+                 break;
+         }
+         case AF_UNIX:
+                 if (salen <= offsetof(struct sockaddr_un, sun_path)) {
+                         p = strdup("<unnamed>");
+                         if (!p)
+                                 return -ENOMEM;
+                 } else if (sa->un.sun_path[0] == 0) {
+                         /* abstract */
+                         /* FIXME: We assume we can print the
+                          * socket path here and that it hasn't
+                          * more than one NUL byte. That is
+                          * actually an invalid assumption */
+                         p = new(char, sizeof(sa->un.sun_path)+1);
+                         if (!p)
+                                 return -ENOMEM;
+                         p[0] = '@';
+                         memcpy(p+1, sa->un.sun_path+1, sizeof(sa->un.sun_path)-1);
+                         p[sizeof(sa->un.sun_path)] = 0;
+                 } else {
+                         p = strndup(sa->un.sun_path, sizeof(sa->un.sun_path));
+                         if (!p)
+                                 return -ENOMEM;
+                 }
+                 break;
+         default:
+                 return -EOPNOTSUPP;
+         }
+         *ret = p;
+         return 0;
+ }
+ int getpeername_pretty(int fd, bool include_port, char **ret) {
+         union sockaddr_union sa;
+         socklen_t salen = sizeof(sa);
+         int r;
+         assert(fd >= 0);
+         assert(ret);
+         if (getpeername(fd, &sa.sa, &salen) < 0)
+                 return -errno;
+         if (sa.sa.sa_family == AF_UNIX) {
+                 struct ucred ucred = {};
+                 /* UNIX connection sockets are anonymous, so let's use
+                  * PID/UID as pretty credentials instead */
+                 r = getpeercred(fd, &ucred);
+                 if (r < 0)
+                         return r;
+                 if (asprintf(ret, "PID "PID_FMT"/UID "UID_FMT, ucred.pid, ucred.uid) < 0)
+                         return -ENOMEM;
+                 return 0;
+         }
+         /* For remote sockets we translate IPv6 addresses back to IPv4
+          * if applicable, since that's nicer. */
+         return sockaddr_pretty(&sa.sa, salen, true, include_port, ret);
+ }
+ int getsockname_pretty(int fd, char **ret) {
+         union sockaddr_union sa;
+         socklen_t salen = sizeof(sa);
+         assert(fd >= 0);
+         assert(ret);
+         if (getsockname(fd, &sa.sa, &salen) < 0)
+                 return -errno;
+         /* For local sockets we do not translate IPv6 addresses back
+          * to IPv6 if applicable, since this is usually used for
+          * listening sockets where the difference between IPv4 and
+          * IPv6 matters. */
+         return sockaddr_pretty(&sa.sa, salen, false, true, ret);
+ }
+ int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret) {
+         int r;
+         char host[NI_MAXHOST], *ret;
+         assert(_ret);
+         r = getnameinfo(&sa->sa, salen, host, sizeof(host), NULL, 0,
+                         NI_IDN|NI_IDN_USE_STD3_ASCII_RULES);
+         if (r != 0) {
+                 int saved_errno = errno;
+                 r = sockaddr_pretty(&sa->sa, salen, true, true, &ret);
+                 if (r < 0)
+                         return r;
+                 log_debug_errno(saved_errno, "getnameinfo(%s) failed: %m", ret);
+         } else {
+                 ret = strdup(host);
+                 if (!ret)
+                         return -ENOMEM;
+         }
+         *_ret = ret;
+         return 0;
+ }
+ int getnameinfo_pretty(int fd, char **ret) {
+         union sockaddr_union sa;
+         socklen_t salen = sizeof(sa);
+         assert(fd >= 0);
+         assert(ret);
+         if (getsockname(fd, &sa.sa, &salen) < 0)
+                 return -errno;
+         return socknameinfo_pretty(&sa, salen, ret);
+ }
+ int socket_address_unlink(SocketAddress *a) {
+         assert(a);
+         if (socket_address_family(a) != AF_UNIX)
+                 return 0;
+         if (a->sockaddr.un.sun_path[0] == 0)
+                 return 0;
+         if (unlink(a->sockaddr.un.sun_path) < 0)
+                 return -errno;
+         return 1;
+ }
+ static const char* const netlink_family_table[] = {
+         [NETLINK_ROUTE] = "route",
+         [NETLINK_FIREWALL] = "firewall",
+         [NETLINK_INET_DIAG] = "inet-diag",
+         [NETLINK_NFLOG] = "nflog",
+         [NETLINK_XFRM] = "xfrm",
+         [NETLINK_SELINUX] = "selinux",
+         [NETLINK_ISCSI] = "iscsi",
+         [NETLINK_AUDIT] = "audit",
+         [NETLINK_FIB_LOOKUP] = "fib-lookup",
+         [NETLINK_CONNECTOR] = "connector",
+         [NETLINK_NETFILTER] = "netfilter",
+         [NETLINK_IP6_FW] = "ip6-fw",
+         [NETLINK_DNRTMSG] = "dnrtmsg",
+         [NETLINK_KOBJECT_UEVENT] = "kobject-uevent",
+         [NETLINK_GENERIC] = "generic",
+         [NETLINK_SCSITRANSPORT] = "scsitransport",
+         [NETLINK_ECRYPTFS] = "ecryptfs"
+ };
+ DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(netlink_family, int, INT_MAX);
+ static const char* const socket_address_bind_ipv6_only_table[_SOCKET_ADDRESS_BIND_IPV6_ONLY_MAX] = {
+         [SOCKET_ADDRESS_DEFAULT] = "default",
+         [SOCKET_ADDRESS_BOTH] = "both",
+         [SOCKET_ADDRESS_IPV6_ONLY] = "ipv6-only"
+ };
+ DEFINE_STRING_TABLE_LOOKUP(socket_address_bind_ipv6_only, SocketAddressBindIPv6Only);
+ bool sockaddr_equal(const union sockaddr_union *a, const union sockaddr_union *b) {
+         assert(a);
+         assert(b);
+         if (a->sa.sa_family != b->sa.sa_family)
+                 return false;
+         if (a->sa.sa_family == AF_INET)
+                 return a->in.sin_addr.s_addr == b->in.sin_addr.s_addr;
+         if (a->sa.sa_family == AF_INET6)
+                 return memcmp(&a->in6.sin6_addr, &b->in6.sin6_addr, sizeof(a->in6.sin6_addr)) == 0;
+         return false;
+ }
+ int fd_inc_sndbuf(int fd, size_t n) {
+         int r, value;
+         socklen_t l = sizeof(value);
+         r = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, &l);
+         if (r >= 0 && l == sizeof(value) && (size_t) value >= n*2)
+                 return 0;
+         /* If we have the privileges we will ignore the kernel limit. */
+         value = (int) n;
+         if (setsockopt(fd, SOL_SOCKET, SO_SNDBUFFORCE, &value, sizeof(value)) < 0)
+                 if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value)) < 0)
+                         return -errno;
+         return 1;
+ }
+ int fd_inc_rcvbuf(int fd, size_t n) {
+         int r, value;
+         socklen_t l = sizeof(value);
+         r = getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, &l);
+         if (r >= 0 && l == sizeof(value) && (size_t) value >= n*2)
+                 return 0;
+         /* If we have the privileges we will ignore the kernel limit. */
+         value = (int) n;
+         if (setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &value, sizeof(value)) < 0)
+                 if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value)) < 0)
+                         return -errno;
+         return 1;
+ }
+ static const char* const ip_tos_table[] = {
+         [IPTOS_LOWDELAY] = "low-delay",
+         [IPTOS_THROUGHPUT] = "throughput",
+         [IPTOS_RELIABILITY] = "reliability",
+         [IPTOS_LOWCOST] = "low-cost",
+ };
+ DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ip_tos, int, 0xff);
+ int getpeercred(int fd, struct ucred *ucred) {
+         socklen_t n = sizeof(struct ucred);
+         struct ucred u;
+         int r;
+         assert(fd >= 0);
+         assert(ucred);
+         r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &u, &n);
+         if (r < 0)
+                 return -errno;
+         if (n != sizeof(struct ucred))
+                 return -EIO;
+         /* Check if the data is actually useful and not suppressed due
+          * to namespacing issues */
+         if (u.pid <= 0)
+                 return -ENODATA;
+         if (u.uid == UID_INVALID)
+                 return -ENODATA;
+         if (u.gid == GID_INVALID)
+                 return -ENODATA;
+         *ucred = u;
+         return 0;
+ }
+ int getpeersec(int fd, char **ret) {
+         socklen_t n = 64;
+         char *s;
+         int r;
+         assert(fd >= 0);
+         assert(ret);
+         s = new0(char, n);
+         if (!s)
+                 return -ENOMEM;
+         r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n);
+         if (r < 0) {
+                 free(s);
+                 if (errno != ERANGE)
+                         return -errno;
+                 s = new0(char, n);
+                 if (!s)
+                         return -ENOMEM;
+                 r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n);
+                 if (r < 0) {
+                         free(s);
+                         return -errno;
+                 }
+         }
+         if (isempty(s)) {
+                 free(s);
+                 return -EOPNOTSUPP;
+         }
+         *ret = s;
+         return 0;
+ }
+ int send_one_fd_sa(
+                 int transport_fd,
+                 int fd,
+                 const struct sockaddr *sa, socklen_t len,
+                 int flags) {
+         union {
+                 struct cmsghdr cmsghdr;
+                 uint8_t buf[CMSG_SPACE(sizeof(int))];
+         } control = {};
+         struct msghdr mh = {
+                 .msg_name = (struct sockaddr*) sa,
+                 .msg_namelen = len,
+                 .msg_control = &control,
+                 .msg_controllen = sizeof(control),
+         };
+         struct cmsghdr *cmsg;
+         assert(transport_fd >= 0);
+         assert(fd >= 0);
+         cmsg = CMSG_FIRSTHDR(&mh);
+         cmsg->cmsg_level = SOL_SOCKET;
+         cmsg->cmsg_type = SCM_RIGHTS;
+         cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+         memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
+         mh.msg_controllen = CMSG_SPACE(sizeof(int));
+         if (sendmsg(transport_fd, &mh, MSG_NOSIGNAL | flags) < 0)
+                 return -errno;
+         return 0;
+ }
+ int receive_one_fd(int transport_fd, int flags) {
+         union {
+                 struct cmsghdr cmsghdr;
+                 uint8_t buf[CMSG_SPACE(sizeof(int))];
+         } control = {};
+         struct msghdr mh = {
+                 .msg_control = &control,
+                 .msg_controllen = sizeof(control),
+         };
+         struct cmsghdr *cmsg, *found = NULL;
+         assert(transport_fd >= 0);
+         /*
+          * Receive a single FD via @transport_fd. We don't care for
+          * the transport-type. We retrieve a single FD at most, so for
+          * packet-based transports, the caller must ensure to send
+          * only a single FD per packet.  This is best used in
+          * combination with send_one_fd().
+          */
+         if (recvmsg(transport_fd, &mh, MSG_NOSIGNAL | MSG_CMSG_CLOEXEC | flags) < 0)
+                 return -errno;
+         CMSG_FOREACH(cmsg, &mh) {
+                 if (cmsg->cmsg_level == SOL_SOCKET &&
+                     cmsg->cmsg_type == SCM_RIGHTS &&
+                     cmsg->cmsg_len == CMSG_LEN(sizeof(int))) {
+                         assert(!found);
+                         found = cmsg;
+                         break;
+                 }
+         }
+         if (!found) {
+                 cmsg_close_all(&mh);
+                 return -EIO;
+         }
+         return *(int*) CMSG_DATA(found);
+ }
++#endif /* NM_IGNORED */
+ ssize_t next_datagram_size_fd(int fd) {
+         ssize_t l;
+         int k;
+         /* This is a bit like FIONREAD/SIOCINQ, however a bit more powerful. The difference being: recv(MSG_PEEK) will
+          * actually cause the next datagram in the queue to be validated regarding checksums, which FIONREAD doesn't
+          * do. This difference is actually of major importance as we need to be sure that the size returned here
+          * actually matches what we will read with recvmsg() next, as otherwise we might end up allocating a buffer of
+          * the wrong size. */
+         l = recv(fd, NULL, 0, MSG_PEEK|MSG_TRUNC);
+         if (l < 0) {
+                 if (errno == EOPNOTSUPP)
+                         goto fallback;
+                 return -errno;
+         }
+         if (l == 0)
+                 goto fallback;
+         return l;
+ fallback:
+         k = 0;
+         /* Some sockets (AF_PACKET) do not support null-sized recv() with MSG_TRUNC set, let's fall back to FIONREAD
+          * for them. Checksums don't matter for raw sockets anyway, hence this should be fine. */
+         if (ioctl(fd, FIONREAD, &k) < 0)
+                 return -errno;
+         return (ssize_t) k;
+ }
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
index 0000000,c61941c..fb6bbe2
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,792 +1,794 @@@
+ /***
+   This file is part of systemd.
+   Copyright 2016 Lennart Poettering
+   systemd 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; either version 2.1 of the License, or
+   (at your option) any later version.
+   systemd 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
+   Lesser General Public License for more details.
+   You should have received a copy of the GNU Lesser General Public License
+   along with systemd; If not, see <http://www.gnu.org/licenses/>.
+ ***/
++#include "nm-sd-adapt.h"
++
+ #include "alloc-util.h"
+ #include "escape.h"
+ #include "ether-addr-util.h"
+ #include "hexdecoct.h"
+ #include "in-addr-util.h"
+ #include "lldp-internal.h"
+ #include "lldp-neighbor.h"
+ #include "lldp.h"
+ #include "unaligned.h"
+ static void lldp_neighbor_id_hash_func(const void *p, struct siphash *state) {
+         const LLDPNeighborID *id = p;
+         siphash24_compress(id->chassis_id, id->chassis_id_size, state);
+         siphash24_compress(&id->chassis_id_size, sizeof(id->chassis_id_size), state);
+         siphash24_compress(id->port_id, id->port_id_size, state);
+         siphash24_compress(&id->port_id_size, sizeof(id->port_id_size), state);
+ }
+ static int lldp_neighbor_id_compare_func(const void *a, const void *b) {
+         const LLDPNeighborID *x = a, *y = b;
+         int r;
+         r = memcmp(x->chassis_id, y->chassis_id, MIN(x->chassis_id_size, y->chassis_id_size));
+         if (r != 0)
+                 return r;
+         if (x->chassis_id_size < y->chassis_id_size)
+                 return -1;
+         if (x->chassis_id_size > y->chassis_id_size)
+                 return 1;
+         r = memcmp(x->port_id, y->port_id, MIN(x->port_id_size, y->port_id_size));
+         if (r != 0)
+                 return r;
+         if (x->port_id_size < y->port_id_size)
+                 return -1;
+         if (x->port_id_size > y->port_id_size)
+                 return 1;
+         return 0;
+ }
+ const struct hash_ops lldp_neighbor_id_hash_ops = {
+         .hash = lldp_neighbor_id_hash_func,
+         .compare = lldp_neighbor_id_compare_func
+ };
+ int lldp_neighbor_prioq_compare_func(const void *a, const void *b) {
+         const sd_lldp_neighbor *x = a, *y = b;
+         if (x->until < y->until)
+                 return -1;
+         if (x->until > y->until)
+                 return 1;
+         return 0;
+ }
+ _public_ sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n) {
+         if (!n)
+                 return NULL;
+         assert(n->n_ref > 0 || n->lldp);
+         n->n_ref++;
+         return n;
+ }
+ static void lldp_neighbor_free(sd_lldp_neighbor *n) {
+         assert(n);
+         free(n->id.port_id);
+         free(n->id.chassis_id);
+         free(n->port_description);
+         free(n->system_name);
+         free(n->system_description);
+         free(n->chassis_id_as_string);
+         free(n->port_id_as_string);
+         free(n);
+ }
+ _public_ sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n) {
+         /* Drops one reference from the neighbor. Note that the object is not freed unless it is already unlinked from
+          * the sd_lldp object. */
+         if (!n)
+                 return NULL;
+         assert(n->n_ref > 0);
+         n->n_ref--;
+         if (n->n_ref <= 0 && !n->lldp)
+                 lldp_neighbor_free(n);
+         return NULL;
+ }
+ sd_lldp_neighbor *lldp_neighbor_unlink(sd_lldp_neighbor *n) {
+         /* Removes the neighbor object from the LLDP object, and frees it if it also has no other reference. */
+         if (!n)
+                 return NULL;
+         if (!n->lldp)
+                 return NULL;
+         assert_se(hashmap_remove(n->lldp->neighbor_by_id, &n->id) == n);
+         assert_se(prioq_remove(n->lldp->neighbor_by_expiry, n, &n->prioq_idx) >= 0);
+         n->lldp = NULL;
+         if (n->n_ref <= 0)
+                 lldp_neighbor_free(n);
+         return NULL;
+ }
+ sd_lldp_neighbor *lldp_neighbor_new(size_t raw_size) {
+         sd_lldp_neighbor *n;
+         n = malloc0(ALIGN(sizeof(sd_lldp_neighbor)) + raw_size);
+         if (!n)
+                 return NULL;
+         n->raw_size = raw_size;
+         n->n_ref = 1;
+         return n;
+ }
+ static int parse_string(char **s, const void *q, size_t n) {
+         const char *p = q;
+         char *k;
+         assert(s);
+         assert(p || n == 0);
+         if (*s) {
+                 log_lldp("Found duplicate string, ignoring field.");
+                 return 0;
+         }
+         /* Strip trailing NULs, just to be nice */
+         while (n > 0 && p[n-1] == 0)
+                 n--;
+         if (n <= 0) /* Ignore empty strings */
+                 return 0;
+         /* Look for inner NULs */
+         if (memchr(p, 0, n)) {
+                 log_lldp("Found inner NUL in string, ignoring field.");
+                 return 0;
+         }
+         /* Let's escape weird chars, for security reasons */
+         k = cescape_length(p, n);
+         if (!k)
+                 return -ENOMEM;
+         free(*s);
+         *s = k;
+         return 1;
+ }
+ int lldp_neighbor_parse(sd_lldp_neighbor *n) {
+         struct ether_header h;
+         const uint8_t *p;
+         size_t left;
+         int r;
+         assert(n);
+         if (n->raw_size < sizeof(struct ether_header)) {
+                 log_lldp("Recieved truncated packet, ignoring.");
+                 return -EBADMSG;
+         }
+         memcpy(&h, LLDP_NEIGHBOR_RAW(n), sizeof(h));
+         if (h.ether_type != htobe16(ETHERTYPE_LLDP)) {
+                 log_lldp("Received packet with wrong type, ignoring.");
+                 return -EBADMSG;
+         }
+         if (h.ether_dhost[0] != 0x01 ||
+             h.ether_dhost[1] != 0x80 ||
+             h.ether_dhost[2] != 0xc2 ||
+             h.ether_dhost[3] != 0x00 ||
+             h.ether_dhost[4] != 0x00 ||
+             !IN_SET(h.ether_dhost[5], 0x00, 0x03, 0x0e)) {
+                 log_lldp("Received packet with wrong destination address, ignoring.");
+                 return -EBADMSG;
+         }
+         memcpy(&n->source_address, h.ether_shost, sizeof(struct ether_addr));
+         memcpy(&n->destination_address, h.ether_dhost, sizeof(struct ether_addr));
+         p = (const uint8_t*) LLDP_NEIGHBOR_RAW(n) + sizeof(struct ether_header);
+         left = n->raw_size - sizeof(struct ether_header);
+         for (;;) {
+                 uint8_t type;
+                 uint16_t length;
+                 if (left < 2) {
+                         log_lldp("TLV lacks header, ignoring.");
+                         return -EBADMSG;
+                 }
+                 type = p[0] >> 1;
+                 length = p[1] + (((uint16_t) (p[0] & 1)) << 8);
+                 p += 2, left -= 2;
+                 if (left < length) {
+                         log_lldp("TLV truncated, ignoring datagram.");
+                         return -EBADMSG;
+                 }
+                 switch (type) {
+                 case LLDP_TYPE_END:
+                         if (length != 0) {
+                                 log_lldp("End marker TLV not zero-sized, ignoring datagram.");
+                                 return -EBADMSG;
+                         }
+                         if (left != 0) {
+                                 log_lldp("Trailing garbage in datagram, ignoring datagram.");
+                                 return -EBADMSG;
+                         }
+                         goto end_marker;
+                 case LLDP_TYPE_CHASSIS_ID:
+                         if (length < 2 || length > 256) { /* includes the chassis subtype, hence one extra byte */
+                                 log_lldp("Chassis ID field size out of range, ignoring datagram.");
+                                 return -EBADMSG;
+                         }
+                         if (n->id.chassis_id) {
+                                 log_lldp("Duplicate chassis ID field, ignoring datagram.");
+                                 return -EBADMSG;
+                         }
+                         n->id.chassis_id = memdup(p, length);
+                         if (!n->id.chassis_id)
+                                 return -ENOMEM;
+                         n->id.chassis_id_size = length;
+                         break;
+                 case LLDP_TYPE_PORT_ID:
+                         if (length < 2 || length > 256) { /* includes the port subtype, hence one extra byte */
+                                 log_lldp("Port ID field size out of range, ignoring datagram.");
+                                 return -EBADMSG;
+                         }
+                         if (n->id.port_id) {
+                                 log_lldp("Duplicate port ID field, ignoring datagram.");
+                                 return -EBADMSG;
+                         }
+                         n->id.port_id = memdup(p, length);
+                         if (!n->id.port_id)
+                                 return -ENOMEM;
+                         n->id.port_id_size = length;
+                         break;
+                 case LLDP_TYPE_TTL:
+                         if (length != 2) {
+                                 log_lldp("TTL field has wrong size, ignoring datagram.");
+                                 return -EBADMSG;
+                         }
+                         if (n->has_ttl) {
+                                 log_lldp("Duplicate TTL field, ignoring datagram.");
+                                 return -EBADMSG;
+                         }
+                         n->ttl = unaligned_read_be16(p);
+                         n->has_ttl = true;
+                         break;
+                 case LLDP_TYPE_PORT_DESCRIPTION:
+                         r = parse_string(&n->port_description, p, length);
+                         if (r < 0)
+                                 return r;
+                         break;
+                 case LLDP_TYPE_SYSTEM_NAME:
+                         r = parse_string(&n->system_name, p, length);
+                         if (r < 0)
+                                 return r;
+                         break;
+                 case LLDP_TYPE_SYSTEM_DESCRIPTION:
+                         r = parse_string(&n->system_description, p, length);
+                         if (r < 0)
+                                 return r;
+                         break;
+                 case LLDP_TYPE_SYSTEM_CAPABILITIES:
+                         if (length != 4)
+                                 log_lldp("System capabilities field has wrong size, ignoring.");
+                         else {
+                                 n->system_capabilities = unaligned_read_be16(p);
+                                 n->enabled_capabilities = unaligned_read_be16(p + 2);
+                                 n->has_capabilities = true;
+                         }
+                         break;
+                 case LLDP_TYPE_PRIVATE:
+                         if (length < 4)
+                                 log_lldp("Found private TLV that is too short, ignoring.");
+                         break;
+                 }
+                 p += length, left -= length;
+         }
+ end_marker:
+         if (!n->id.chassis_id || !n->id.port_id || !n->has_ttl) {
+                 log_lldp("One or more mandatory TLV missing in datagram. Ignoring.");
+                 return -EBADMSG;
+         }
+         n->rindex = sizeof(struct ether_header);
+         return 0;
+ }
+ void lldp_neighbor_start_ttl(sd_lldp_neighbor *n) {
+         assert(n);
+         if (n->ttl > 0)
+                 n->until = usec_add(now(clock_boottime_or_monotonic()), n->ttl * USEC_PER_SEC);
+         else
+                 n->until = 0;
+         if (n->lldp)
+                 prioq_reshuffle(n->lldp->neighbor_by_expiry, n, &n->prioq_idx);
+ }
+ bool lldp_neighbor_equal(const sd_lldp_neighbor *a, const sd_lldp_neighbor *b) {
+         if (a == b)
+                 return true;
+         if (!a || !b)
+                 return false;
+         if (a->raw_size != b->raw_size)
+                 return false;
+         return memcmp(LLDP_NEIGHBOR_RAW(a), LLDP_NEIGHBOR_RAW(b), a->raw_size) == 0;
+ }
+ _public_ int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct ether_addr* address) {
+         assert_return(n, -EINVAL);
+         assert_return(address, -EINVAL);
+         *address = n->source_address;
+         return 0;
+ }
+ _public_ int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_addr* address) {
+         assert_return(n, -EINVAL);
+         assert_return(address, -EINVAL);
+         *address = n->destination_address;
+         return 0;
+ }
+ _public_ int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) {
+         assert_return(n, -EINVAL);
+         assert_return(ret, -EINVAL);
+         assert_return(size, -EINVAL);
+         *ret = LLDP_NEIGHBOR_RAW(n);
+         *size = n->raw_size;
+         return 0;
+ }
+ _public_ int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) {
+         assert_return(n, -EINVAL);
+         assert_return(type, -EINVAL);
+         assert_return(ret, -EINVAL);
+         assert_return(size, -EINVAL);
+         assert(n->id.chassis_id_size > 0);
+         *type = *(uint8_t*) n->id.chassis_id;
+         *ret = (uint8_t*) n->id.chassis_id + 1;
+         *size = n->id.chassis_id_size - 1;
+         return 0;
+ }
+ static int format_mac_address(const void *data, size_t sz, char **ret) {
+         struct ether_addr a;
+         char *k;
+         assert(data || sz <= 0);
+         if (sz != 7)
+                 return 0;
+         memcpy(&a, (uint8_t*) data + 1, sizeof(a));
+         k = new(char, ETHER_ADDR_TO_STRING_MAX);
+         if (!k)
+                 return -ENOMEM;
+         *ret = ether_addr_to_string(&a, k);
+         return 1;
+ }
+ static int format_network_address(const void *data, size_t sz, char **ret) {
+         union in_addr_union a;
+         int family;
+         if (sz == 6 && ((uint8_t*) data)[1] == 1) {
+                 memcpy(&a.in, (uint8_t*) data + 2, sizeof(a.in));
+                 family = AF_INET;
+         } else if (sz == 18 && ((uint8_t*) data)[1] == 2) {
+                 memcpy(&a.in6, (uint8_t*) data + 2, sizeof(a.in6));
+                 family = AF_INET6;
+         } else
+                 return 0;
+         return in_addr_to_string(family, &a, ret);
+ }
+ _public_ int sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor *n, const char **ret) {
+         char *k;
+         int r;
+         assert_return(n, -EINVAL);
+         assert_return(ret, -EINVAL);
+         if (n->chassis_id_as_string) {
+                 *ret = n->chassis_id_as_string;
+                 return 0;
+         }
+         assert(n->id.chassis_id_size > 0);
+         switch (*(uint8_t*) n->id.chassis_id) {
+         case LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT:
+         case LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS:
+         case LLDP_CHASSIS_SUBTYPE_PORT_COMPONENT:
+         case LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME:
+         case LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED:
+                 k = cescape_length((char*) n->id.chassis_id + 1, n->id.chassis_id_size - 1);
+                 if (!k)
+                         return -ENOMEM;
+                 goto done;
+         case LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS:
+                 r = format_mac_address(n->id.chassis_id, n->id.chassis_id_size, &k);
+                 if (r < 0)
+                         return r;
+                 if (r > 0)
+                         goto done;
+                 break;
+         case LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS:
+                 r = format_network_address(n->id.chassis_id, n->id.chassis_id_size, &k);
+                 if (r < 0)
+                         return r;
+                 if (r > 0)
+                         goto done;
+                 break;
+         }
+         /* Generic fallback */
+         k = hexmem(n->id.chassis_id, n->id.chassis_id_size);
+         if (!k)
+                 return -ENOMEM;
+ done:
+         *ret = n->chassis_id_as_string = k;
+         return 0;
+ }
+ _public_ int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) {
+         assert_return(n, -EINVAL);
+         assert_return(type, -EINVAL);
+         assert_return(ret, -EINVAL);
+         assert_return(size, -EINVAL);
+         assert(n->id.port_id_size > 0);
+         *type = *(uint8_t*) n->id.port_id;
+         *ret = (uint8_t*) n->id.port_id + 1;
+         *size = n->id.port_id_size - 1;
+         return 0;
+ }
+ _public_ int sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor *n, const char **ret) {
+         char *k;
+         int r;
+         assert_return(n, -EINVAL);
+         assert_return(ret, -EINVAL);
+         if (n->port_id_as_string) {
+                 *ret = n->port_id_as_string;
+                 return 0;
+         }
+         assert(n->id.port_id_size > 0);
+         switch (*(uint8_t*) n->id.port_id) {
+         case LLDP_PORT_SUBTYPE_INTERFACE_ALIAS:
+         case LLDP_PORT_SUBTYPE_PORT_COMPONENT:
+         case LLDP_PORT_SUBTYPE_INTERFACE_NAME:
+         case LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED:
+                 k = cescape_length((char*) n->id.port_id + 1, n->id.port_id_size - 1);
+                 if (!k)
+                         return -ENOMEM;
+                 goto done;
+         case LLDP_PORT_SUBTYPE_MAC_ADDRESS:
+                 r = format_mac_address(n->id.port_id, n->id.port_id_size, &k);
+                 if (r < 0)
+                         return r;
+                 if (r > 0)
+                         goto done;
+                 break;
+         case LLDP_PORT_SUBTYPE_NETWORK_ADDRESS:
+                 r = format_network_address(n->id.port_id, n->id.port_id_size, &k);
+                 if (r < 0)
+                         return r;
+                 if (r > 0)
+                         goto done;
+                 break;
+         }
+         /* Generic fallback */
+         k = hexmem(n->id.port_id, n->id.port_id_size);
+         if (!k)
+                 return -ENOMEM;
+ done:
+         *ret = n->port_id_as_string = k;
+         return 0;
+ }
+ _public_ int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret) {
+         assert_return(n, -EINVAL);
+         assert_return(ret, -EINVAL);
+         *ret = n->ttl;
+         return 0;
+ }
+ _public_ int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret) {
+         assert_return(n, -EINVAL);
+         assert_return(ret, -EINVAL);
+         if (!n->system_name)
+                 return -ENODATA;
+         *ret = n->system_name;
+         return 0;
+ }
+ _public_ int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret) {
+         assert_return(n, -EINVAL);
+         assert_return(ret, -EINVAL);
+         if (!n->system_description)
+                 return -ENODATA;
+         *ret = n->system_description;
+         return 0;
+ }
+ _public_ int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret) {
+         assert_return(n, -EINVAL);
+         assert_return(ret, -EINVAL);
+         if (!n->port_description)
+                 return -ENODATA;
+         *ret = n->port_description;
+         return 0;
+ }
+ _public_ int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret) {
+         assert_return(n, -EINVAL);
+         assert_return(ret, -EINVAL);
+         if (!n->has_capabilities)
+                 return -ENODATA;
+         *ret = n->system_capabilities;
+         return 0;
+ }
+ _public_ int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret) {
+         assert_return(n, -EINVAL);
+         assert_return(ret, -EINVAL);
+         if (!n->has_capabilities)
+                 return -ENODATA;
+         *ret = n->enabled_capabilities;
+         return 0;
+ }
+ int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size) {
+         _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
+         int r;
+         assert_return(ret, -EINVAL);
+         assert_return(raw || raw_size <= 0, -EINVAL);
+         n = lldp_neighbor_new(raw_size);
+         if (!n)
+                 return -ENOMEM;
+         memcpy(LLDP_NEIGHBOR_RAW(n), raw, raw_size);
+         r = lldp_neighbor_parse(n);
+         if (r < 0)
+                 return r;
+         *ret = n;
+         n = 0;
+         return r;
+ }
+ _public_ int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n) {
+         assert_return(n, -EINVAL);
+         assert(n->raw_size >= sizeof(struct ether_header));
+         n->rindex = sizeof(struct ether_header);
+         return 0;
+ }
+ _public_ int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n) {
+         size_t length;
+         assert_return(n, -EINVAL);
+         if (n->rindex == n->raw_size) /* EOF */
+                 return -ESPIPE;
+         if (n->rindex + 2 > n->raw_size) /* Truncated message */
+                 return -EBADMSG;
+         length = LLDP_NEIGHBOR_LENGTH(n);
+         if (n->rindex + 2 + length > n->raw_size)
+                 return -EBADMSG;
+         n->rindex += 2 + length;
+         return n->rindex < n->raw_size;
+ }
+ _public_ int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type) {
+         assert_return(n, -EINVAL);
+         assert_return(type, -EINVAL);
+         if (n->rindex == n->raw_size) /* EOF */
+                 return -ESPIPE;
+         if (n->rindex + 2 > n->raw_size)
+                 return -EBADMSG;
+         *type = LLDP_NEIGHBOR_TYPE(n);
+         return 0;
+ }
+ _public_ int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type) {
+         uint8_t k;
+         int r;
+         assert_return(n, -EINVAL);
+         r = sd_lldp_neighbor_tlv_get_type(n, &k);
+         if (r < 0)
+                 return r;
+         return type == k;
+ }
+ _public_ int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[3], uint8_t *subtype) {
+         const uint8_t *d;
+         size_t length;
+         int r;
+         assert_return(n, -EINVAL);
+         assert_return(oui, -EINVAL);
+         assert_return(subtype, -EINVAL);
+         r = sd_lldp_neighbor_tlv_is_type(n, LLDP_TYPE_PRIVATE);
+         if (r < 0)
+                 return r;
+         if (r == 0)
+                 return -ENXIO;
+         length = LLDP_NEIGHBOR_LENGTH(n);
+         if (length < 4)
+                 return -EBADMSG;
+         if (n->rindex + 2 + length > n->raw_size)
+                 return -EBADMSG;
+         d = LLDP_NEIGHBOR_DATA(n);
+         memcpy(oui, d, 3);
+         *subtype = d[3];
+         return 0;
+ }
+ _public_ int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[3], uint8_t subtype) {
+         uint8_t k[3], st;
+         int r;
+         r = sd_lldp_neighbor_tlv_get_oui(n, k, &st);
+         if (r == -ENXIO)
+                 return 0;
+         if (r < 0)
+                 return r;
+         return memcmp(k, oui, 3) == 0 && st == subtype;
+ }
+ _public_ int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) {
+         size_t length;
+         assert_return(n, -EINVAL);
+         assert_return(ret, -EINVAL);
+         assert_return(size, -EINVAL);
+         /* Note that this returns the full TLV, including the TLV header */
+         if (n->rindex + 2 > n->raw_size)
+                 return -EBADMSG;
+         length = LLDP_NEIGHBOR_LENGTH(n);
+         if (n->rindex + 2 + length > n->raw_size)
+                 return -EBADMSG;
+         *ret = (uint8_t*) LLDP_NEIGHBOR_RAW(n) + n->rindex;
+         *size = length + 2;
+         return 0;
+ }
    along with systemd; If not, see <http://www.gnu.org/licenses/>.
  ***/
  
 +#include "nm-sd-adapt.h"
 +
  #include <linux/filter.h>
- #include <linux/if_ether.h>
+ #include <netinet/if_ether.h>
  
  #include "fd-util.h"
- #include "lldp-internal.h"
  #include "lldp-network.h"
- #include "lldp-tlv.h"
  #include "socket-util.h"
  
  int lldp_network_bind_raw_socket(int ifindex) {
@@@ -1,25 -1,23 +1,25 @@@
  /***
-     This file is part of systemd.
+   This file is part of systemd.
  
-     Copyright (C) 2014 Tom Gundersen
-     Copyright (C) 2014 Susant Sahani
+   Copyright (C) 2014 Tom Gundersen
+   Copyright (C) 2014 Susant Sahani
  
-     systemd 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; either version 2.1 of the License, or
-     (at your option) any later version.
+   systemd 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; either version 2.1 of the License, or
+   (at your option) any later version.
  
-     systemd 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
-     Lesser General Public License for more details.
+   systemd 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
+   Lesser General Public License for more details.
  
-     You should have received a copy of the GNU Lesser General Public License
-     along with systemd; If not, see <http://www.gnu.org/licenses/>.
+   You should have received a copy of the GNU Lesser General Public License
+   along with systemd; If not, see <http://www.gnu.org/licenses/>.
  ***/
  
 +#include "nm-sd-adapt.h"
 +
  #include <arpa/inet.h>
  
  #include "sd-lldp.h"
Simple merge
index 557a631,0000000..2f6c479
mode 100644,000000..100644
--- /dev/null
@@@ -1,71 -1,0 +1,71 @@@
-       r = sd_lldp_new (ifindex, "lo", (struct ether_addr *) ((guint8[]) { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }), &lldp);
 +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
 +/*
 + * This program is free software; you can redistribute it and/or modify
 + * it under the terms of the GNU General Public License as published by
 + * the Free Software Foundation; either version 2, or (at your option)
 + * any later version.
 + *
 + * 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.
 + *
 + * You should have received a copy of the GNU General Public License along
 + * with this program; if not, write to the Free Software Foundation, Inc.,
 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 + *
 + * Copyright (C) 2016 Red Hat, Inc.
 + */
 +
 +#include "nm-default.h"
 +
 +#include "sd-dhcp-client.h"
 +#include "sd-lldp.h"
 +
 +#include "nm-test-utils.h"
 +
 +/*****************************************************************************/
 +
 +static void
 +test_dhcp_create (void)
 +{
 +      sd_dhcp_client *client4 = NULL;
 +      int r;
 +
 +      r = sd_dhcp_client_new (&client4);
 +      g_assert (r == 0);
 +      g_assert (client4);
 +
 +      sd_dhcp_client_unref (client4);
 +}
 +
 +/*****************************************************************************/
 +
 +static void
 +test_lldp_create (void)
 +{
 +      sd_lldp *lldp = NULL;
 +      int ifindex = 1;
 +      int r;
 +
++      r = sd_lldp_new (&lldp, ifindex);
 +      g_assert (r == 0);
 +      g_assert (lldp);
 +
 +      sd_lldp_unref (lldp);
 +}
 +
 +/*****************************************************************************/
 +
 +NMTST_DEFINE ();
 +
 +int
 +main (int argc, char **argv)
 +{
 +      nmtst_init_assert_logging (&argc, &argv, "INFO", "ALL");
 +
 +      g_test_add_func ("/systemd/dhcp/create", test_dhcp_create);
 +      g_test_add_func ("/systemd/lldp/create", test_lldp_create);
 +
 +      return g_test_run ();
 +}