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().
# 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
###########################################
* 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
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);
}
--- /dev/null
- 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));
+}
+
--- /dev/null
- 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__ */
--- /dev/null
- 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, ¬ify_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, ¬ify_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);
+}
--- /dev/null
+/* -*- 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;
+}
+
--- /dev/null
+/* -*- 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 */
+
--- /dev/null
+ /***
+ 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];
+ }
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;
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>
--- /dev/null
+ /***
+ 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;
+ }
--- /dev/null
+ /***
+ 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) {
/***
- 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"
--- /dev/null
- 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 ();
+}