lldp: merge branch 'th/lldp'
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/1426
This commit is contained in:
41
Makefile.am
41
Makefile.am
@@ -454,6 +454,8 @@ src_libnm_glib_aux_libnm_glib_aux_la_SOURCES = \
|
|||||||
src/libnm-glib-aux/nm-logging-syslog.h \
|
src/libnm-glib-aux/nm-logging-syslog.h \
|
||||||
src/libnm-glib-aux/nm-macros-internal.h \
|
src/libnm-glib-aux/nm-macros-internal.h \
|
||||||
src/libnm-glib-aux/nm-obj.h \
|
src/libnm-glib-aux/nm-obj.h \
|
||||||
|
src/libnm-glib-aux/nm-prioq.c \
|
||||||
|
src/libnm-glib-aux/nm-prioq.h \
|
||||||
src/libnm-glib-aux/nm-random-utils.c \
|
src/libnm-glib-aux/nm-random-utils.c \
|
||||||
src/libnm-glib-aux/nm-random-utils.h \
|
src/libnm-glib-aux/nm-random-utils.h \
|
||||||
src/libnm-glib-aux/nm-ref-string.c \
|
src/libnm-glib-aux/nm-ref-string.c \
|
||||||
@@ -510,6 +512,36 @@ EXTRA_DIST += src/libnm-udev-aux/meson.build
|
|||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
|
noinst_LTLIBRARIES += src/libnm-lldp/libnm-lldp.la
|
||||||
|
|
||||||
|
src_libnm_lldp_libnm_lldp_la_CFLAGS = \
|
||||||
|
$(src_libnm_glib_aux_cppflags) \
|
||||||
|
-I$(srcdir)/src \
|
||||||
|
-I$(builddir)/src \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
|
src_libnm_lldp_libnm_lldp_la_CPPFLAGS = \
|
||||||
|
$(CODE_COVERAGE_CFLAGS) \
|
||||||
|
$(SANITIZER_LIB_CFLAGS) \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
|
src_libnm_lldp_libnm_lldp_la_LDFLAGS = \
|
||||||
|
$(SANITIZER_LIB_LDFLAGS) \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
|
src_libnm_lldp_libnm_lldp_la_SOURCES = \
|
||||||
|
src/libnm-lldp/nm-lldp-neighbor.c \
|
||||||
|
src/libnm-lldp/nm-lldp-neighbor.h \
|
||||||
|
src/libnm-lldp/nm-lldp-network.c \
|
||||||
|
src/libnm-lldp/nm-lldp-network.h \
|
||||||
|
src/libnm-lldp/nm-lldp-rx-internal.h \
|
||||||
|
src/libnm-lldp/nm-lldp-rx.c \
|
||||||
|
src/libnm-lldp/nm-lldp-rx.h \
|
||||||
|
src/libnm-lldp/nm-lldp.h \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
noinst_LTLIBRARIES += src/libnm-base/libnm-base.la
|
noinst_LTLIBRARIES += src/libnm-base/libnm-base.la
|
||||||
|
|
||||||
src_libnm_base_libnm_base_la_CPPFLAGS = \
|
src_libnm_base_libnm_base_la_CPPFLAGS = \
|
||||||
@@ -2389,16 +2421,10 @@ src_libnm_systemd_core_libnm_systemd_core_la_SOURCES = \
|
|||||||
src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.h \
|
src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.h \
|
||||||
src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.c \
|
src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.c \
|
||||||
src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.h \
|
src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.h \
|
||||||
src/libnm-systemd-core/src/libsystemd-network/lldp-neighbor.c \
|
|
||||||
src/libnm-systemd-core/src/libsystemd-network/lldp-neighbor.h \
|
|
||||||
src/libnm-systemd-core/src/libsystemd-network/lldp-network.c \
|
|
||||||
src/libnm-systemd-core/src/libsystemd-network/lldp-network.h \
|
|
||||||
src/libnm-systemd-core/src/libsystemd-network/lldp-rx-internal.h \
|
|
||||||
src/libnm-systemd-core/src/libsystemd-network/network-common.c \
|
src/libnm-systemd-core/src/libsystemd-network/network-common.c \
|
||||||
src/libnm-systemd-core/src/libsystemd-network/network-common.h \
|
src/libnm-systemd-core/src/libsystemd-network/network-common.h \
|
||||||
src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c \
|
src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c \
|
||||||
src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c \
|
src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c \
|
||||||
src/libnm-systemd-core/src/libsystemd-network/sd-lldp-rx.c \
|
|
||||||
src/libnm-systemd-core/src/libsystemd/sd-event/event-source.h \
|
src/libnm-systemd-core/src/libsystemd/sd-event/event-source.h \
|
||||||
src/libnm-systemd-core/src/libsystemd/sd-event/event-util.c \
|
src/libnm-systemd-core/src/libsystemd/sd-event/event-util.c \
|
||||||
src/libnm-systemd-core/src/libsystemd/sd-event/event-util.h \
|
src/libnm-systemd-core/src/libsystemd/sd-event/event-util.h \
|
||||||
@@ -2412,8 +2438,6 @@ src_libnm_systemd_core_libnm_systemd_core_la_SOURCES = \
|
|||||||
src/libnm-systemd-core/src/systemd/sd-dhcp6-option.h \
|
src/libnm-systemd-core/src/systemd/sd-dhcp6-option.h \
|
||||||
src/libnm-systemd-core/src/systemd/sd-event.h \
|
src/libnm-systemd-core/src/systemd/sd-event.h \
|
||||||
src/libnm-systemd-core/src/systemd/sd-id128.h \
|
src/libnm-systemd-core/src/systemd/sd-id128.h \
|
||||||
src/libnm-systemd-core/src/systemd/sd-lldp-rx.h \
|
|
||||||
src/libnm-systemd-core/src/systemd/sd-lldp.h \
|
|
||||||
src/libnm-systemd-core/src/systemd/sd-ndisc.h \
|
src/libnm-systemd-core/src/systemd/sd-ndisc.h \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
@@ -2684,6 +2708,7 @@ src_core_libNetworkManager_la_LIBADD = \
|
|||||||
src/libnm-platform/libnm-platform.la \
|
src/libnm-platform/libnm-platform.la \
|
||||||
src/libnm-base/libnm-base.la \
|
src/libnm-base/libnm-base.la \
|
||||||
src/libnm-log-core/libnm-log-core.la \
|
src/libnm-log-core/libnm-log-core.la \
|
||||||
|
src/libnm-lldp/libnm-lldp.la \
|
||||||
src/libnm-udev-aux/libnm-udev-aux.la \
|
src/libnm-udev-aux/libnm-udev-aux.la \
|
||||||
src/libnm-glib-aux/libnm-glib-aux.la \
|
src/libnm-glib-aux/libnm-glib-aux.la \
|
||||||
src/libnm-std-aux/libnm-std-aux.la \
|
src/libnm-std-aux/libnm-std-aux.la \
|
||||||
|
@@ -15,24 +15,20 @@
|
|||||||
#include "libnm-std-aux/unaligned.h"
|
#include "libnm-std-aux/unaligned.h"
|
||||||
#include "libnm-platform/nm-platform.h"
|
#include "libnm-platform/nm-platform.h"
|
||||||
#include "libnm-glib-aux/nm-c-list.h"
|
#include "libnm-glib-aux/nm-c-list.h"
|
||||||
|
#include "libnm-lldp/nm-lldp-rx.h"
|
||||||
#include "nm-utils.h"
|
#include "nm-utils.h"
|
||||||
|
|
||||||
#include "libnm-systemd-core/nm-sd.h"
|
|
||||||
|
|
||||||
#define MAX_NEIGHBORS 128
|
#define MAX_NEIGHBORS 128
|
||||||
#define MIN_UPDATE_INTERVAL_NSEC (2 * NM_UTILS_NSEC_PER_SEC)
|
#define MIN_UPDATE_INTERVAL_NSEC (2 * NM_UTILS_NSEC_PER_SEC)
|
||||||
|
|
||||||
#define LLDP_MAC_NEAREST_BRIDGE \
|
#define LLDP_MAC_NEAREST_BRIDGE (&NM_ETHER_ADDR_INIT(0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e))
|
||||||
(&((struct ether_addr){.ether_addr_octet = {0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e}}))
|
#define LLDP_MAC_NEAREST_NON_TPMR_BRIDGE (&NM_ETHER_ADDR_INIT(0x01, 0x80, 0xc2, 0x00, 0x00, 0x03))
|
||||||
#define LLDP_MAC_NEAREST_NON_TPMR_BRIDGE \
|
#define LLDP_MAC_NEAREST_CUSTOMER_BRIDGE (&NM_ETHER_ADDR_INIT(0x01, 0x80, 0xc2, 0x00, 0x00, 0x00))
|
||||||
(&((struct ether_addr){.ether_addr_octet = {0x01, 0x80, 0xc2, 0x00, 0x00, 0x03}}))
|
|
||||||
#define LLDP_MAC_NEAREST_CUSTOMER_BRIDGE \
|
|
||||||
(&((struct ether_addr){.ether_addr_octet = {0x01, 0x80, 0xc2, 0x00, 0x00, 0x00}}))
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
struct _NMLldpListener {
|
struct _NMLldpListener {
|
||||||
sd_lldp_rx *lldp_handle;
|
NMLldpRX *lldp_rx;
|
||||||
GHashTable *lldp_neighbors;
|
GHashTable *lldp_neighbors;
|
||||||
GVariant *variant;
|
GVariant *variant;
|
||||||
|
|
||||||
@@ -50,7 +46,7 @@ struct _NMLldpListener {
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
GVariant *variant;
|
GVariant *variant;
|
||||||
sd_lldp_neighbor *neighbor_sd;
|
NMLldpNeighbor *neighbor_nm;
|
||||||
char *chassis_id;
|
char *chassis_id;
|
||||||
char *port_id;
|
char *port_id;
|
||||||
guint8 chassis_id_type;
|
guint8 chassis_id_type;
|
||||||
@@ -100,7 +96,7 @@ lldp_neighbor_get_raw(LldpNeighbor *neigh, const guint8 **out_raw_data, gsize *o
|
|||||||
|
|
||||||
nm_assert(neigh);
|
nm_assert(neigh);
|
||||||
|
|
||||||
r = sd_lldp_neighbor_get_raw(neigh->neighbor_sd, &raw_data, &raw_len);
|
r = nm_lldp_neighbor_get_raw(neigh->neighbor_nm, &raw_data, &raw_len);
|
||||||
|
|
||||||
nm_assert(r >= 0);
|
nm_assert(r >= 0);
|
||||||
nm_assert(raw_data);
|
nm_assert(raw_data);
|
||||||
@@ -111,7 +107,7 @@ lldp_neighbor_get_raw(LldpNeighbor *neigh, const guint8 **out_raw_data, gsize *o
|
|||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
lldp_neighbor_id_get(struct sd_lldp_neighbor *neighbor_sd,
|
lldp_neighbor_id_get(NMLldpNeighbor *neighbor_nm,
|
||||||
guint8 *out_chassis_id_type,
|
guint8 *out_chassis_id_type,
|
||||||
const guint8 **out_chassis_id,
|
const guint8 **out_chassis_id,
|
||||||
gsize *out_chassis_id_len,
|
gsize *out_chassis_id_len,
|
||||||
@@ -121,14 +117,14 @@ lldp_neighbor_id_get(struct sd_lldp_neighbor *neighbor_sd,
|
|||||||
{
|
{
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
r = sd_lldp_neighbor_get_chassis_id(neighbor_sd,
|
r = nm_lldp_neighbor_get_chassis_id(neighbor_nm,
|
||||||
out_chassis_id_type,
|
out_chassis_id_type,
|
||||||
(gconstpointer *) out_chassis_id,
|
(gconstpointer *) out_chassis_id,
|
||||||
out_chassis_id_len);
|
out_chassis_id_len);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
r = sd_lldp_neighbor_get_port_id(neighbor_sd,
|
r = nm_lldp_neighbor_get_port_id(neighbor_nm,
|
||||||
out_port_id_type,
|
out_port_id_type,
|
||||||
(gconstpointer *) out_port_id,
|
(gconstpointer *) out_port_id,
|
||||||
out_port_id_len);
|
out_port_id_len);
|
||||||
@@ -139,82 +135,16 @@ lldp_neighbor_id_get(struct sd_lldp_neighbor *neighbor_sd,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static guint
|
static guint
|
||||||
lldp_neighbor_id_hash(gconstpointer ptr)
|
lldp_neighbor_id_hash(const LldpNeighbor *neigh)
|
||||||
{
|
{
|
||||||
const LldpNeighbor *neigh = ptr;
|
return nm_lldp_neighbor_id_hash(nm_lldp_neighbor_get_id(neigh->neighbor_nm));
|
||||||
guint8 chassis_id_type;
|
|
||||||
guint8 port_id_type;
|
|
||||||
const guint8 *chassis_id;
|
|
||||||
const guint8 *port_id;
|
|
||||||
gsize chassis_id_len;
|
|
||||||
gsize port_id_len;
|
|
||||||
NMHashState h;
|
|
||||||
|
|
||||||
if (!lldp_neighbor_id_get(neigh->neighbor_sd,
|
|
||||||
&chassis_id_type,
|
|
||||||
&chassis_id,
|
|
||||||
&chassis_id_len,
|
|
||||||
&port_id_type,
|
|
||||||
&port_id,
|
|
||||||
&port_id_len)) {
|
|
||||||
nm_assert_not_reached();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
nm_hash_init(&h, 23423423u);
|
|
||||||
nm_hash_update_vals(&h, chassis_id_len, port_id_len, chassis_id_type, port_id_type);
|
|
||||||
nm_hash_update(&h, chassis_id, chassis_id_len);
|
|
||||||
nm_hash_update(&h, port_id, port_id_len);
|
|
||||||
return nm_hash_complete(&h);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
lldp_neighbor_id_cmp(const LldpNeighbor *a, const LldpNeighbor *b)
|
lldp_neighbor_id_cmp(const LldpNeighbor *a, const LldpNeighbor *b)
|
||||||
{
|
{
|
||||||
guint8 a_chassis_id_type;
|
return nm_lldp_neighbor_id_cmp(nm_lldp_neighbor_get_id(a->neighbor_nm),
|
||||||
guint8 b_chassis_id_type;
|
nm_lldp_neighbor_get_id(b->neighbor_nm));
|
||||||
guint8 a_port_id_type;
|
|
||||||
guint8 b_port_id_type;
|
|
||||||
const guint8 *a_chassis_id;
|
|
||||||
const guint8 *b_chassis_id;
|
|
||||||
const guint8 *a_port_id;
|
|
||||||
const guint8 *b_port_id;
|
|
||||||
gsize a_chassis_id_len;
|
|
||||||
gsize b_chassis_id_len;
|
|
||||||
gsize a_port_id_len;
|
|
||||||
gsize b_port_id_len;
|
|
||||||
|
|
||||||
NM_CMP_SELF(a, b);
|
|
||||||
|
|
||||||
if (!lldp_neighbor_id_get(a->neighbor_sd,
|
|
||||||
&a_chassis_id_type,
|
|
||||||
&a_chassis_id,
|
|
||||||
&a_chassis_id_len,
|
|
||||||
&a_port_id_type,
|
|
||||||
&a_port_id,
|
|
||||||
&a_port_id_len)) {
|
|
||||||
nm_assert_not_reached();
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!lldp_neighbor_id_get(b->neighbor_sd,
|
|
||||||
&b_chassis_id_type,
|
|
||||||
&b_chassis_id,
|
|
||||||
&b_chassis_id_len,
|
|
||||||
&b_port_id_type,
|
|
||||||
&b_port_id,
|
|
||||||
&b_port_id_len)) {
|
|
||||||
nm_assert_not_reached();
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
NM_CMP_DIRECT(a_chassis_id_type, b_chassis_id_type);
|
|
||||||
NM_CMP_DIRECT(a_port_id_type, b_port_id_type);
|
|
||||||
NM_CMP_DIRECT(a_chassis_id_len, b_chassis_id_len);
|
|
||||||
NM_CMP_DIRECT(a_port_id_len, b_port_id_len);
|
|
||||||
NM_CMP_DIRECT_MEMCMP(a_chassis_id, b_chassis_id, a_chassis_id_len);
|
|
||||||
NM_CMP_DIRECT_MEMCMP(a_port_id, b_port_id, a_port_id_len);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
@@ -225,7 +155,7 @@ lldp_neighbor_id_cmp_p(gconstpointer a, gconstpointer b, gpointer user_data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
lldp_neighbor_id_equal(gconstpointer a, gconstpointer b)
|
lldp_neighbor_id_equal(const LldpNeighbor *a, const LldpNeighbor *b)
|
||||||
{
|
{
|
||||||
return lldp_neighbor_id_cmp(a, b) == 0;
|
return lldp_neighbor_id_cmp(a, b) == 0;
|
||||||
}
|
}
|
||||||
@@ -239,7 +169,7 @@ lldp_neighbor_free(LldpNeighbor *neighbor)
|
|||||||
g_free(neighbor->chassis_id);
|
g_free(neighbor->chassis_id);
|
||||||
g_free(neighbor->port_id);
|
g_free(neighbor->port_id);
|
||||||
nm_g_variant_unref(neighbor->variant);
|
nm_g_variant_unref(neighbor->variant);
|
||||||
sd_lldp_neighbor_unref(neighbor->neighbor_sd);
|
nm_lldp_neighbor_unref(neighbor->neighbor_nm);
|
||||||
nm_g_slice_free(neighbor);
|
nm_g_slice_free(neighbor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,7 +187,7 @@ lldp_neighbor_equal(LldpNeighbor *a, LldpNeighbor *b)
|
|||||||
gsize raw_len_a;
|
gsize raw_len_a;
|
||||||
gsize raw_len_b;
|
gsize raw_len_b;
|
||||||
|
|
||||||
if (a->neighbor_sd == b->neighbor_sd)
|
if (a->neighbor_nm == b->neighbor_nm)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
lldp_neighbor_get_raw(a, &raw_data_a, &raw_len_a);
|
lldp_neighbor_get_raw(a, &raw_data_a, &raw_len_a);
|
||||||
@@ -293,7 +223,7 @@ parse_management_address_tlv(const uint8_t *data, gsize len)
|
|||||||
if (len < 11)
|
if (len < 11)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
nm_assert((data[0] >> 1) == SD_LLDP_TYPE_MGMT_ADDRESS);
|
nm_assert((data[0] >> 1) == NM_LLDP_TYPE_MGMT_ADDRESS);
|
||||||
nm_assert((((data[0] & 1) << 8) + data[1]) + 2 == len);
|
nm_assert((((data[0] & 1) << 8) + data[1]) + 2 == len);
|
||||||
|
|
||||||
data += 2;
|
data += 2;
|
||||||
@@ -402,7 +332,7 @@ format_string_cp(const guint8 *data, gsize len, gboolean allow_trim)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static LldpNeighbor *
|
static LldpNeighbor *
|
||||||
lldp_neighbor_new(sd_lldp_neighbor *neighbor_sd)
|
lldp_neighbor_new(NMLldpNeighbor *neighbor_nm)
|
||||||
{
|
{
|
||||||
LldpNeighbor *neigh;
|
LldpNeighbor *neigh;
|
||||||
guint8 chassis_id_type;
|
guint8 chassis_id_type;
|
||||||
@@ -414,7 +344,7 @@ lldp_neighbor_new(sd_lldp_neighbor *neighbor_sd)
|
|||||||
gs_free char *s_chassis_id = NULL;
|
gs_free char *s_chassis_id = NULL;
|
||||||
gs_free char *s_port_id = NULL;
|
gs_free char *s_port_id = NULL;
|
||||||
|
|
||||||
if (!lldp_neighbor_id_get(neighbor_sd,
|
if (!lldp_neighbor_id_get(neighbor_nm,
|
||||||
&chassis_id_type,
|
&chassis_id_type,
|
||||||
&chassis_id,
|
&chassis_id,
|
||||||
&chassis_id_len,
|
&chassis_id_len,
|
||||||
@@ -424,17 +354,17 @@ lldp_neighbor_new(sd_lldp_neighbor *neighbor_sd)
|
|||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
switch (chassis_id_type) {
|
switch (chassis_id_type) {
|
||||||
case SD_LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT:
|
case NM_LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT:
|
||||||
case SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS:
|
case NM_LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS:
|
||||||
case SD_LLDP_CHASSIS_SUBTYPE_PORT_COMPONENT:
|
case NM_LLDP_CHASSIS_SUBTYPE_PORT_COMPONENT:
|
||||||
case SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME:
|
case NM_LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME:
|
||||||
case SD_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED:
|
case NM_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED:
|
||||||
s_chassis_id = format_string_cp(chassis_id, chassis_id_len, FALSE);
|
s_chassis_id = format_string_cp(chassis_id, chassis_id_len, FALSE);
|
||||||
break;
|
break;
|
||||||
case SD_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS:
|
case NM_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS:
|
||||||
s_chassis_id = nm_utils_hwaddr_ntoa(chassis_id, chassis_id_len);
|
s_chassis_id = nm_utils_hwaddr_ntoa(chassis_id, chassis_id_len);
|
||||||
break;
|
break;
|
||||||
case SD_LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS:
|
case NM_LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS:
|
||||||
s_chassis_id = format_network_address(chassis_id, chassis_id_len);
|
s_chassis_id = format_network_address(chassis_id, chassis_id_len);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -446,16 +376,16 @@ lldp_neighbor_new(sd_lldp_neighbor *neighbor_sd)
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (port_id_type) {
|
switch (port_id_type) {
|
||||||
case SD_LLDP_PORT_SUBTYPE_INTERFACE_ALIAS:
|
case NM_LLDP_PORT_SUBTYPE_INTERFACE_ALIAS:
|
||||||
case SD_LLDP_PORT_SUBTYPE_PORT_COMPONENT:
|
case NM_LLDP_PORT_SUBTYPE_PORT_COMPONENT:
|
||||||
case SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME:
|
case NM_LLDP_PORT_SUBTYPE_INTERFACE_NAME:
|
||||||
case SD_LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED:
|
case NM_LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED:
|
||||||
s_port_id = format_string_cp(port_id, port_id_len, FALSE);
|
s_port_id = format_string_cp(port_id, port_id_len, FALSE);
|
||||||
break;
|
break;
|
||||||
case SD_LLDP_PORT_SUBTYPE_MAC_ADDRESS:
|
case NM_LLDP_PORT_SUBTYPE_MAC_ADDRESS:
|
||||||
s_port_id = nm_utils_hwaddr_ntoa(port_id, port_id_len);
|
s_port_id = nm_utils_hwaddr_ntoa(port_id, port_id_len);
|
||||||
break;
|
break;
|
||||||
case SD_LLDP_PORT_SUBTYPE_NETWORK_ADDRESS:
|
case NM_LLDP_PORT_SUBTYPE_NETWORK_ADDRESS:
|
||||||
s_port_id = format_network_address(port_id, port_id_len);
|
s_port_id = format_network_address(port_id, port_id_len);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -468,7 +398,7 @@ lldp_neighbor_new(sd_lldp_neighbor *neighbor_sd)
|
|||||||
|
|
||||||
neigh = g_slice_new(LldpNeighbor);
|
neigh = g_slice_new(LldpNeighbor);
|
||||||
*neigh = (LldpNeighbor){
|
*neigh = (LldpNeighbor){
|
||||||
.neighbor_sd = sd_lldp_neighbor_ref(neighbor_sd),
|
.neighbor_nm = nm_lldp_neighbor_ref(neighbor_nm),
|
||||||
.chassis_id_type = chassis_id_type,
|
.chassis_id_type = chassis_id_type,
|
||||||
.chassis_id = g_steal_pointer(&s_chassis_id),
|
.chassis_id = g_steal_pointer(&s_chassis_id),
|
||||||
.port_id_type = port_id_type,
|
.port_id_type = port_id_type,
|
||||||
@@ -480,7 +410,7 @@ lldp_neighbor_new(sd_lldp_neighbor *neighbor_sd)
|
|||||||
static GVariant *
|
static GVariant *
|
||||||
lldp_neighbor_to_variant(LldpNeighbor *neigh)
|
lldp_neighbor_to_variant(LldpNeighbor *neigh)
|
||||||
{
|
{
|
||||||
struct ether_addr destination_address;
|
NMEtherAddr destination_address;
|
||||||
GVariantBuilder builder;
|
GVariantBuilder builder;
|
||||||
const char *str;
|
const char *str;
|
||||||
const guint8 *raw_data;
|
const guint8 *raw_data;
|
||||||
@@ -505,33 +435,33 @@ lldp_neighbor_to_variant(LldpNeighbor *neigh)
|
|||||||
nm_g_variant_builder_add_sv_uint32(&builder, NM_LLDP_ATTR_PORT_ID_TYPE, neigh->port_id_type);
|
nm_g_variant_builder_add_sv_uint32(&builder, NM_LLDP_ATTR_PORT_ID_TYPE, neigh->port_id_type);
|
||||||
nm_g_variant_builder_add_sv_str(&builder, NM_LLDP_ATTR_PORT_ID, neigh->port_id);
|
nm_g_variant_builder_add_sv_str(&builder, NM_LLDP_ATTR_PORT_ID, neigh->port_id);
|
||||||
|
|
||||||
r = sd_lldp_neighbor_get_destination_address(neigh->neighbor_sd, &destination_address);
|
r = nm_lldp_neighbor_get_destination_address(neigh->neighbor_nm, &destination_address);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
str = NULL;
|
str = NULL;
|
||||||
else if (nm_utils_ether_addr_equal(&destination_address, LLDP_MAC_NEAREST_BRIDGE))
|
else if (nm_ether_addr_equal(&destination_address, LLDP_MAC_NEAREST_BRIDGE))
|
||||||
str = NM_LLDP_DEST_NEAREST_BRIDGE;
|
str = NM_LLDP_DEST_NEAREST_BRIDGE;
|
||||||
else if (nm_utils_ether_addr_equal(&destination_address, LLDP_MAC_NEAREST_NON_TPMR_BRIDGE))
|
else if (nm_ether_addr_equal(&destination_address, LLDP_MAC_NEAREST_NON_TPMR_BRIDGE))
|
||||||
str = NM_LLDP_DEST_NEAREST_NON_TPMR_BRIDGE;
|
str = NM_LLDP_DEST_NEAREST_NON_TPMR_BRIDGE;
|
||||||
else if (nm_utils_ether_addr_equal(&destination_address, LLDP_MAC_NEAREST_CUSTOMER_BRIDGE))
|
else if (nm_ether_addr_equal(&destination_address, LLDP_MAC_NEAREST_CUSTOMER_BRIDGE))
|
||||||
str = NM_LLDP_DEST_NEAREST_CUSTOMER_BRIDGE;
|
str = NM_LLDP_DEST_NEAREST_CUSTOMER_BRIDGE;
|
||||||
else
|
else
|
||||||
str = NULL;
|
str = NULL;
|
||||||
if (str)
|
if (str)
|
||||||
nm_g_variant_builder_add_sv_str(&builder, NM_LLDP_ATTR_DESTINATION, str);
|
nm_g_variant_builder_add_sv_str(&builder, NM_LLDP_ATTR_DESTINATION, str);
|
||||||
|
|
||||||
if (sd_lldp_neighbor_get_port_description(neigh->neighbor_sd, &str) == 0)
|
if (nm_lldp_neighbor_get_port_description(neigh->neighbor_nm, &str) == 0)
|
||||||
nm_g_variant_builder_add_sv_str(&builder, NM_LLDP_ATTR_PORT_DESCRIPTION, str);
|
nm_g_variant_builder_add_sv_str(&builder, NM_LLDP_ATTR_PORT_DESCRIPTION, str);
|
||||||
|
|
||||||
if (sd_lldp_neighbor_get_system_name(neigh->neighbor_sd, &str) == 0)
|
if (nm_lldp_neighbor_get_system_name(neigh->neighbor_nm, &str) == 0)
|
||||||
nm_g_variant_builder_add_sv_str(&builder, NM_LLDP_ATTR_SYSTEM_NAME, str);
|
nm_g_variant_builder_add_sv_str(&builder, NM_LLDP_ATTR_SYSTEM_NAME, str);
|
||||||
|
|
||||||
if (sd_lldp_neighbor_get_system_description(neigh->neighbor_sd, &str) == 0)
|
if (nm_lldp_neighbor_get_system_description(neigh->neighbor_nm, &str) == 0)
|
||||||
nm_g_variant_builder_add_sv_str(&builder, NM_LLDP_ATTR_SYSTEM_DESCRIPTION, str);
|
nm_g_variant_builder_add_sv_str(&builder, NM_LLDP_ATTR_SYSTEM_DESCRIPTION, str);
|
||||||
|
|
||||||
if (sd_lldp_neighbor_get_system_capabilities(neigh->neighbor_sd, &u16) == 0)
|
if (nm_lldp_neighbor_get_system_capabilities(neigh->neighbor_nm, &u16) == 0)
|
||||||
nm_g_variant_builder_add_sv_uint32(&builder, NM_LLDP_ATTR_SYSTEM_CAPABILITIES, u16);
|
nm_g_variant_builder_add_sv_uint32(&builder, NM_LLDP_ATTR_SYSTEM_CAPABILITIES, u16);
|
||||||
|
|
||||||
r = sd_lldp_neighbor_tlv_rewind(neigh->neighbor_sd);
|
r = nm_lldp_neighbor_tlv_rewind(neigh->neighbor_nm);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
nm_assert_not_reached();
|
nm_assert_not_reached();
|
||||||
else {
|
else {
|
||||||
@@ -556,14 +486,14 @@ lldp_neighbor_to_variant(LldpNeighbor *neigh)
|
|||||||
guint8 type;
|
guint8 type;
|
||||||
guint8 subtype;
|
guint8 subtype;
|
||||||
|
|
||||||
if (sd_lldp_neighbor_tlv_get_type(neigh->neighbor_sd, &type) < 0)
|
if (nm_lldp_neighbor_tlv_get_type(neigh->neighbor_nm, &type) < 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (sd_lldp_neighbor_tlv_get_raw(neigh->neighbor_sd, (void *) &data8, &len) < 0)
|
if (nm_lldp_neighbor_tlv_get_raw(neigh->neighbor_nm, (void *) &data8, &len) < 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case SD_LLDP_TYPE_MGMT_ADDRESS:
|
case NM_LLDP_TYPE_MGMT_ADDRESS:
|
||||||
tmp_variant = parse_management_address_tlv(data8, len);
|
tmp_variant = parse_management_address_tlv(data8, len);
|
||||||
if (tmp_variant) {
|
if (tmp_variant) {
|
||||||
if (!v_management_addresses_has) {
|
if (!v_management_addresses_has) {
|
||||||
@@ -573,13 +503,13 @@ lldp_neighbor_to_variant(LldpNeighbor *neigh)
|
|||||||
g_variant_builder_add_value(&v_management_addresses, tmp_variant);
|
g_variant_builder_add_value(&v_management_addresses, tmp_variant);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
case SD_LLDP_TYPE_PRIVATE:
|
case NM_LLDP_TYPE_PRIVATE:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = sd_lldp_neighbor_tlv_get_oui(neigh->neighbor_sd, oui, &subtype);
|
r = nm_lldp_neighbor_tlv_get_oui(neigh->neighbor_nm, oui, &subtype);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
if (r == -ENXIO)
|
if (r == -ENXIO)
|
||||||
continue;
|
continue;
|
||||||
@@ -609,15 +539,15 @@ lldp_neighbor_to_variant(LldpNeighbor *neigh)
|
|||||||
data8 += 6;
|
data8 += 6;
|
||||||
len -= 6;
|
len -= 6;
|
||||||
|
|
||||||
if (memcmp(oui, SD_LLDP_OUI_802_1, sizeof(oui)) == 0) {
|
if (memcmp(oui, NM_LLDP_OUI_802_1, sizeof(oui)) == 0) {
|
||||||
switch (subtype) {
|
switch (subtype) {
|
||||||
case SD_LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID:
|
case NM_LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID:
|
||||||
if (len != 2)
|
if (len != 2)
|
||||||
continue;
|
continue;
|
||||||
if (!v_ieee_802_1_pvid)
|
if (!v_ieee_802_1_pvid)
|
||||||
v_ieee_802_1_pvid = g_variant_new_uint32(unaligned_read_be16(data8));
|
v_ieee_802_1_pvid = g_variant_new_uint32(unaligned_read_be16(data8));
|
||||||
break;
|
break;
|
||||||
case SD_LLDP_OUI_802_1_SUBTYPE_PORT_PROTOCOL_VLAN_ID:
|
case NM_LLDP_OUI_802_1_SUBTYPE_PORT_PROTOCOL_VLAN_ID:
|
||||||
if (len != 3)
|
if (len != 3)
|
||||||
continue;
|
continue;
|
||||||
if (!v_ieee_802_1_ppvid) {
|
if (!v_ieee_802_1_ppvid) {
|
||||||
@@ -633,7 +563,7 @@ lldp_neighbor_to_variant(LldpNeighbor *neigh)
|
|||||||
g_variant_builder_add_value(&v_ieee_802_1_ppvids,
|
g_variant_builder_add_value(&v_ieee_802_1_ppvids,
|
||||||
g_variant_builder_end(&tmp_builder));
|
g_variant_builder_end(&tmp_builder));
|
||||||
break;
|
break;
|
||||||
case SD_LLDP_OUI_802_1_SUBTYPE_VLAN_NAME:
|
case NM_LLDP_OUI_802_1_SUBTYPE_VLAN_NAME:
|
||||||
{
|
{
|
||||||
gs_free char *name_to_free = NULL;
|
gs_free char *name_to_free = NULL;
|
||||||
const char *name;
|
const char *name;
|
||||||
@@ -669,9 +599,9 @@ lldp_neighbor_to_variant(LldpNeighbor *neigh)
|
|||||||
default:
|
default:
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} else if (memcmp(oui, SD_LLDP_OUI_802_3, sizeof(oui)) == 0) {
|
} else if (memcmp(oui, NM_LLDP_OUI_802_3, sizeof(oui)) == 0) {
|
||||||
switch (subtype) {
|
switch (subtype) {
|
||||||
case SD_LLDP_OUI_802_3_SUBTYPE_MAC_PHY_CONFIG_STATUS:
|
case NM_LLDP_OUI_802_3_SUBTYPE_MAC_PHY_CONFIG_STATUS:
|
||||||
if (len != 5)
|
if (len != 5)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -687,7 +617,7 @@ lldp_neighbor_to_variant(LldpNeighbor *neigh)
|
|||||||
v_ieee_802_3_mac_phy_conf = g_variant_builder_end(&tmp_builder);
|
v_ieee_802_3_mac_phy_conf = g_variant_builder_end(&tmp_builder);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SD_LLDP_OUI_802_3_SUBTYPE_POWER_VIA_MDI:
|
case NM_LLDP_OUI_802_3_SUBTYPE_POWER_VIA_MDI:
|
||||||
if (len != 3)
|
if (len != 3)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -703,7 +633,7 @@ lldp_neighbor_to_variant(LldpNeighbor *neigh)
|
|||||||
v_ieee_802_3_power_via_mdi = g_variant_builder_end(&tmp_builder);
|
v_ieee_802_3_power_via_mdi = g_variant_builder_end(&tmp_builder);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SD_LLDP_OUI_802_3_SUBTYPE_MAXIMUM_FRAME_SIZE:
|
case NM_LLDP_OUI_802_3_SUBTYPE_MAXIMUM_FRAME_SIZE:
|
||||||
if (len != 2)
|
if (len != 2)
|
||||||
continue;
|
continue;
|
||||||
if (!v_ieee_802_3_max_frame_size)
|
if (!v_ieee_802_3_max_frame_size)
|
||||||
@@ -711,9 +641,9 @@ lldp_neighbor_to_variant(LldpNeighbor *neigh)
|
|||||||
g_variant_new_uint32(unaligned_read_be16(data8));
|
g_variant_new_uint32(unaligned_read_be16(data8));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if (memcmp(oui, SD_LLDP_OUI_IANA, sizeof(oui)) == 0) {
|
} else if (memcmp(oui, NM_LLDP_OUI_IANA, sizeof(oui)) == 0) {
|
||||||
switch (subtype) {
|
switch (subtype) {
|
||||||
case SD_LLDP_OUI_IANA_SUBTYPE_MUD:
|
case NM_LLDP_OUI_IANA_SUBTYPE_MUD:
|
||||||
if (!v_mud_url) {
|
if (!v_mud_url) {
|
||||||
gs_free char *s_free = NULL;
|
gs_free char *s_free = NULL;
|
||||||
const char *s;
|
const char *s;
|
||||||
@@ -725,7 +655,7 @@ lldp_neighbor_to_variant(LldpNeighbor *neigh)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} while (sd_lldp_neighbor_tlv_next(neigh->neighbor_sd) > 0);
|
} while (nm_lldp_neighbor_tlv_next(neigh->neighbor_nm) > 0);
|
||||||
|
|
||||||
if (v_management_addresses_has)
|
if (v_management_addresses_has)
|
||||||
nm_g_variant_builder_add_sv(&builder,
|
nm_g_variant_builder_add_sv(&builder,
|
||||||
@@ -777,18 +707,17 @@ lldp_neighbor_to_variant(LldpNeighbor *neigh)
|
|||||||
GVariant *
|
GVariant *
|
||||||
nmtst_lldp_parse_from_raw(const guint8 *raw_data, gsize raw_len)
|
nmtst_lldp_parse_from_raw(const guint8 *raw_data, gsize raw_len)
|
||||||
{
|
{
|
||||||
nm_auto(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *neighbor_sd = NULL;
|
nm_auto(nm_lldp_neighbor_unrefp) NMLldpNeighbor *neighbor_nm = NULL;
|
||||||
nm_auto(lldp_neighbor_freep) LldpNeighbor *neigh = NULL;
|
nm_auto(lldp_neighbor_freep) LldpNeighbor *neigh = NULL;
|
||||||
GVariant *variant;
|
GVariant *variant;
|
||||||
int r;
|
|
||||||
|
|
||||||
g_assert(raw_data);
|
g_assert(raw_data);
|
||||||
g_assert(raw_len > 0);
|
g_assert(raw_len > 0);
|
||||||
|
|
||||||
r = sd_lldp_neighbor_from_raw(&neighbor_sd, raw_data, raw_len);
|
neighbor_nm = nm_lldp_neighbor_new_from_raw(raw_data, raw_len);
|
||||||
g_assert(r >= 0);
|
g_assert(neighbor_nm);
|
||||||
|
|
||||||
neigh = lldp_neighbor_new(neighbor_sd);
|
neigh = lldp_neighbor_new(neighbor_nm);
|
||||||
g_assert(neigh);
|
g_assert(neigh);
|
||||||
|
|
||||||
variant = lldp_neighbor_to_variant(neigh);
|
variant = lldp_neighbor_to_variant(neigh);
|
||||||
@@ -843,18 +772,18 @@ data_changed_schedule(NMLldpListener *self)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
process_lldp_neighbor(NMLldpListener *self, sd_lldp_neighbor *neighbor_sd, gboolean remove)
|
process_lldp_neighbor(NMLldpListener *self, NMLldpNeighbor *neighbor_nm, gboolean remove)
|
||||||
{
|
{
|
||||||
nm_auto(lldp_neighbor_freep) LldpNeighbor *neigh = NULL;
|
nm_auto(lldp_neighbor_freep) LldpNeighbor *neigh = NULL;
|
||||||
LldpNeighbor *neigh_old;
|
LldpNeighbor *neigh_old;
|
||||||
|
|
||||||
nm_assert(self);
|
nm_assert(self);
|
||||||
nm_assert(self->lldp_handle);
|
nm_assert(self->lldp_rx);
|
||||||
nm_assert(self->lldp_neighbors);
|
nm_assert(self->lldp_neighbors);
|
||||||
|
|
||||||
g_return_if_fail(neighbor_sd);
|
g_return_if_fail(neighbor_nm);
|
||||||
|
|
||||||
neigh = lldp_neighbor_new(neighbor_sd);
|
neigh = lldp_neighbor_new(neighbor_nm);
|
||||||
if (!neigh) {
|
if (!neigh) {
|
||||||
_LOGT("process: failed to parse neighbor");
|
_LOGT("process: failed to parse neighbor");
|
||||||
return;
|
return;
|
||||||
@@ -884,14 +813,17 @@ handle_changed:
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
lldp_event_handler(sd_lldp_rx *lldp, sd_lldp_rx_event_t event, sd_lldp_neighbor *n, void *userdata)
|
lldp_event_handler(NMLldpRX *lldp, NMLldpRXEvent event, NMLldpNeighbor *n, void *user_data)
|
||||||
{
|
{
|
||||||
process_lldp_neighbor(userdata,
|
NMLldpListener *self = user_data;
|
||||||
|
|
||||||
|
_LOGD("event: %s", nm_lldp_rx_event_to_string(event));
|
||||||
|
process_lldp_neighbor(self,
|
||||||
n,
|
n,
|
||||||
!NM_IN_SET(event,
|
!NM_IN_SET(event,
|
||||||
SD_LLDP_RX_EVENT_ADDED,
|
NM_LLDP_RX_EVENT_ADDED,
|
||||||
SD_LLDP_RX_EVENT_UPDATED,
|
NM_LLDP_RX_EVENT_UPDATED,
|
||||||
SD_LLDP_RX_EVENT_REFRESHED));
|
NM_LLDP_RX_EVENT_REFRESHED));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
@@ -936,34 +868,13 @@ nm_lldp_listener_new(int ifindex,
|
|||||||
GError **error)
|
GError **error)
|
||||||
{
|
{
|
||||||
NMLldpListener *self = NULL;
|
NMLldpListener *self = NULL;
|
||||||
sd_lldp_rx *lldp_handle;
|
nm_auto(nm_lldp_rx_unrefp) NMLldpRX *lldp_rx = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
g_return_val_if_fail(ifindex > 0, FALSE);
|
g_return_val_if_fail(ifindex > 0, FALSE);
|
||||||
g_return_val_if_fail(!error || !*error, FALSE);
|
g_return_val_if_fail(!error || !*error, FALSE);
|
||||||
g_return_val_if_fail(notify_callback, FALSE);
|
g_return_val_if_fail(notify_callback, FALSE);
|
||||||
|
|
||||||
r = sd_lldp_rx_new(&lldp_handle);
|
|
||||||
if (r < 0) {
|
|
||||||
g_set_error_literal(error,
|
|
||||||
NM_DEVICE_ERROR,
|
|
||||||
NM_DEVICE_ERROR_FAILED,
|
|
||||||
"initialization failed");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
r = sd_lldp_rx_set_ifindex(lldp_handle, ifindex);
|
|
||||||
if (r < 0) {
|
|
||||||
g_set_error_literal(error,
|
|
||||||
NM_DEVICE_ERROR,
|
|
||||||
NM_DEVICE_ERROR_FAILED,
|
|
||||||
"failed setting ifindex");
|
|
||||||
goto fail_handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
r = sd_lldp_rx_set_neighbors_max(lldp_handle, MAX_NEIGHBORS);
|
|
||||||
nm_assert(r == 0);
|
|
||||||
|
|
||||||
self = g_slice_new(NMLldpListener);
|
self = g_slice_new(NMLldpListener);
|
||||||
*self = (NMLldpListener){
|
*self = (NMLldpListener){
|
||||||
.ifindex = ifindex,
|
.ifindex = ifindex,
|
||||||
@@ -971,39 +882,34 @@ nm_lldp_listener_new(int ifindex,
|
|||||||
.notify_user_data = notify_user_data,
|
.notify_user_data = notify_user_data,
|
||||||
};
|
};
|
||||||
|
|
||||||
r = sd_lldp_rx_set_callback(lldp_handle, lldp_event_handler, self);
|
nm_assert(nm_g_main_context_is_thread_default(g_main_context_default()));
|
||||||
if (r < 0) {
|
|
||||||
g_set_error_literal(error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, "set callback failed");
|
|
||||||
goto fail_handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
r = sd_lldp_rx_attach_event(lldp_handle, NULL, 0);
|
lldp_rx = nm_lldp_rx_new(&((NMLldpRXConfig){
|
||||||
if (r < 0) {
|
.ifindex = ifindex,
|
||||||
g_set_error_literal(error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, "attach event failed");
|
.neighbors_max = MAX_NEIGHBORS,
|
||||||
goto fail_attached;
|
.callback = lldp_event_handler,
|
||||||
}
|
.userdata = self,
|
||||||
|
}));
|
||||||
|
|
||||||
r = sd_lldp_rx_start(lldp_handle);
|
r = nm_lldp_rx_start(lldp_rx);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
g_set_error_literal(error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, "start failed");
|
g_set_error_literal(error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, "start failed");
|
||||||
goto fail_attached;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
self->lldp_neighbors = g_hash_table_new_full(lldp_neighbor_id_hash,
|
self->lldp_neighbors = g_hash_table_new_full((GHashFunc) lldp_neighbor_id_hash,
|
||||||
lldp_neighbor_id_equal,
|
(GEqualFunc) lldp_neighbor_id_equal,
|
||||||
(GDestroyNotify) lldp_neighbor_free,
|
(GDestroyNotify) lldp_neighbor_free,
|
||||||
NULL);
|
NULL);
|
||||||
self->lldp_handle = lldp_handle;
|
|
||||||
|
self->lldp_rx = g_steal_pointer(&lldp_rx);
|
||||||
|
|
||||||
_LOGD("start lldp listener");
|
_LOGD("start lldp listener");
|
||||||
return self;
|
return self;
|
||||||
|
|
||||||
fail_attached:
|
fail:
|
||||||
sd_lldp_rx_detach_event(lldp_handle);
|
|
||||||
fail_handle:
|
|
||||||
if (self)
|
if (self)
|
||||||
nm_g_slice_free(self);
|
nm_g_slice_free(self);
|
||||||
sd_lldp_rx_unref(lldp_handle);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1012,9 +918,8 @@ nm_lldp_listener_destroy(NMLldpListener *self)
|
|||||||
{
|
{
|
||||||
g_return_if_fail(self);
|
g_return_if_fail(self);
|
||||||
|
|
||||||
sd_lldp_rx_stop(self->lldp_handle);
|
nm_lldp_rx_stop(self->lldp_rx);
|
||||||
sd_lldp_rx_detach_event(self->lldp_handle);
|
nm_lldp_rx_unref(self->lldp_rx);
|
||||||
sd_lldp_rx_unref(self->lldp_handle);
|
|
||||||
|
|
||||||
nm_clear_g_source_inst(&self->ratelimit_source);
|
nm_clear_g_source_inst(&self->ratelimit_source);
|
||||||
|
|
||||||
|
@@ -12,9 +12,8 @@
|
|||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include "libnm-lldp/nm-lldp.h"
|
||||||
#include "devices/nm-lldp-listener.h"
|
#include "devices/nm-lldp-listener.h"
|
||||||
#include "libnm-systemd-core/nm-sd.h"
|
|
||||||
|
|
||||||
#include "platform/tests/test-common.h"
|
#include "platform/tests/test-common.h"
|
||||||
|
|
||||||
#include "nm-test-utils-core.h"
|
#include "nm-test-utils-core.h"
|
||||||
@@ -191,9 +190,9 @@ _test_recv_data0_check_do(GMainLoop *loop, NMLldpListener *listener, const TestR
|
|||||||
g_assert_cmpint(g_variant_n_children(neighbors), ==, 1);
|
g_assert_cmpint(g_variant_n_children(neighbors), ==, 1);
|
||||||
|
|
||||||
neighbor = get_lldp_neighbor(neighbors,
|
neighbor = get_lldp_neighbor(neighbors,
|
||||||
SD_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS,
|
NM_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS,
|
||||||
"00:01:02:03:04:05",
|
"00:01:02:03:04:05",
|
||||||
SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME,
|
NM_LLDP_PORT_SUBTYPE_INTERFACE_NAME,
|
||||||
"1/3");
|
"1/3");
|
||||||
g_assert(neighbor);
|
g_assert(neighbor);
|
||||||
g_assert_cmpint(g_variant_n_children(neighbor), ==, 1 + 4 + 4);
|
g_assert_cmpint(g_variant_n_children(neighbor), ==, 1 + 4 + 4);
|
||||||
@@ -547,9 +546,9 @@ _test_recv_data1_check(GMainLoop *loop, NMLldpListener *listener, TestRecvCallba
|
|||||||
g_assert_cmpint(g_variant_n_children(neighbors), ==, 1);
|
g_assert_cmpint(g_variant_n_children(neighbors), ==, 1);
|
||||||
|
|
||||||
neighbor = get_lldp_neighbor(neighbors,
|
neighbor = get_lldp_neighbor(neighbors,
|
||||||
SD_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS,
|
NM_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS,
|
||||||
"00:01:30:F9:AD:A0",
|
"00:01:30:F9:AD:A0",
|
||||||
SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME,
|
NM_LLDP_PORT_SUBTYPE_INTERFACE_NAME,
|
||||||
"1/1");
|
"1/1");
|
||||||
g_assert(neighbor);
|
g_assert(neighbor);
|
||||||
g_assert_cmpint(g_variant_n_children(neighbor), ==, 1 + 4 + 16);
|
g_assert_cmpint(g_variant_n_children(neighbor), ==, 1 + 4 + 16);
|
||||||
@@ -874,7 +873,6 @@ test_recv(TestRecvFixture *fixture, gconstpointer user_data)
|
|||||||
TestRecvCallbackInfo info = {};
|
TestRecvCallbackInfo info = {};
|
||||||
gsize i_frames;
|
gsize i_frames;
|
||||||
GError *error = NULL;
|
GError *error = NULL;
|
||||||
guint sd_id;
|
|
||||||
|
|
||||||
if (fixture->ifindex == 0) {
|
if (fixture->ifindex == 0) {
|
||||||
g_test_skip("Tun device not available");
|
g_test_skip("Tun device not available");
|
||||||
@@ -885,7 +883,6 @@ test_recv(TestRecvFixture *fixture, gconstpointer user_data)
|
|||||||
nmtst_assert_success(listener, error);
|
nmtst_assert_success(listener, error);
|
||||||
|
|
||||||
loop = g_main_loop_new(NULL, FALSE);
|
loop = g_main_loop_new(NULL, FALSE);
|
||||||
sd_id = nm_sd_event_attach_default();
|
|
||||||
|
|
||||||
for (i_frames = 0; i_frames < data->frames_len; i_frames++) {
|
for (i_frames = 0; i_frames < data->frames_len; i_frames++) {
|
||||||
const TestRecvFrame *f = data->frames[i_frames];
|
const TestRecvFrame *f = data->frames[i_frames];
|
||||||
@@ -902,7 +899,6 @@ test_recv(TestRecvFixture *fixture, gconstpointer user_data)
|
|||||||
|
|
||||||
nm_clear_pointer(&listener, nm_lldp_listener_destroy);
|
nm_clear_pointer(&listener, nm_lldp_listener_destroy);
|
||||||
|
|
||||||
nm_clear_g_source(&sd_id);
|
|
||||||
nm_clear_pointer(&loop, g_main_loop_unref);
|
nm_clear_pointer(&loop, g_main_loop_unref);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -205,6 +205,7 @@ if enable_tests
|
|||||||
libnm_core_aux_intern,
|
libnm_core_aux_intern,
|
||||||
libnm_core_impl,
|
libnm_core_impl,
|
||||||
libnm_crypto,
|
libnm_crypto,
|
||||||
|
libnm_lldp,
|
||||||
libnm_platform,
|
libnm_platform,
|
||||||
libnm_base,
|
libnm_base,
|
||||||
libnm_systemd_shared,
|
libnm_systemd_shared,
|
||||||
@@ -256,6 +257,7 @@ NetworkManager_all_sym = executable(
|
|||||||
libnm_core_aux_intern,
|
libnm_core_aux_intern,
|
||||||
libnm_core_impl,
|
libnm_core_impl,
|
||||||
libnm_crypto,
|
libnm_crypto,
|
||||||
|
libnm_lldp,
|
||||||
libnm_platform,
|
libnm_platform,
|
||||||
libnm_base,
|
libnm_base,
|
||||||
libnm_log_core,
|
libnm_log_core,
|
||||||
@@ -296,6 +298,7 @@ NetworkManager = executable(
|
|||||||
libnm_core_aux_intern,
|
libnm_core_aux_intern,
|
||||||
libnm_core_impl,
|
libnm_core_impl,
|
||||||
libnm_crypto,
|
libnm_crypto,
|
||||||
|
libnm_lldp,
|
||||||
libnm_platform,
|
libnm_platform,
|
||||||
libnm_base,
|
libnm_base,
|
||||||
libnm_udev_aux,
|
libnm_udev_aux,
|
||||||
|
@@ -12,21 +12,6 @@
|
|||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
static void
|
|
||||||
test_lldp_create(void)
|
|
||||||
{
|
|
||||||
sd_lldp_rx *lldp = NULL;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
r = sd_lldp_rx_new(&lldp);
|
|
||||||
g_assert(r == 0);
|
|
||||||
g_assert(lldp);
|
|
||||||
|
|
||||||
sd_lldp_rx_unref(lldp);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
GMainLoop *mainloop;
|
GMainLoop *mainloop;
|
||||||
sd_event_source *event_source;
|
sd_event_source *event_source;
|
||||||
@@ -105,7 +90,6 @@ main(int argc, char **argv)
|
|||||||
{
|
{
|
||||||
nmtst_init(&argc, &argv, TRUE);
|
nmtst_init(&argc, &argv, TRUE);
|
||||||
|
|
||||||
g_test_add_func("/systemd/lldp/create", test_lldp_create);
|
|
||||||
g_test_add_func("/systemd/sd-event", test_sd_event);
|
g_test_add_func("/systemd/sd-event", test_sd_event);
|
||||||
|
|
||||||
return g_test_run();
|
return g_test_run();
|
||||||
|
@@ -13,6 +13,7 @@ libnm_glib_aux = static_library(
|
|||||||
'nm-json-aux.c',
|
'nm-json-aux.c',
|
||||||
'nm-keyfile-aux.c',
|
'nm-keyfile-aux.c',
|
||||||
'nm-logging-base.c',
|
'nm-logging-base.c',
|
||||||
|
'nm-prioq.c',
|
||||||
'nm-random-utils.c',
|
'nm-random-utils.c',
|
||||||
'nm-ref-string.c',
|
'nm-ref-string.c',
|
||||||
'nm-secret-utils.c',
|
'nm-secret-utils.c',
|
||||||
|
@@ -51,10 +51,6 @@ typedef struct _NMIPAddr {
|
|||||||
|
|
||||||
extern const NMIPAddr nm_ip_addr_zero;
|
extern const NMIPAddr nm_ip_addr_zero;
|
||||||
|
|
||||||
/* This doesn't really belong here, but since it's convenient to re-use nm_ip_addr_zero.ether_addr
|
|
||||||
* for NMEtherAddr, it is. */
|
|
||||||
#define nm_ether_addr_zero (nm_ip_addr_zero.ether_addr)
|
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
nm_ip_addr_cmp(int addr_family, gconstpointer a, gconstpointer b)
|
nm_ip_addr_cmp(int addr_family, gconstpointer a, gconstpointer b)
|
||||||
{
|
{
|
||||||
|
@@ -12,6 +12,7 @@
|
|||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/un.h>
|
#include <sys/un.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
|
||||||
#include "nm-str-buf.h"
|
#include "nm-str-buf.h"
|
||||||
#include "nm-shared-utils.h"
|
#include "nm-shared-utils.h"
|
||||||
@@ -496,6 +497,45 @@ nm_utils_fd_read(int fd, NMStrBuf *out_string)
|
|||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
/* Taken from systemd's next_datagram_size_fd(). */
|
||||||
|
gssize
|
||||||
|
nm_fd_next_datagram_size(int fd)
|
||||||
|
{
|
||||||
|
gssize 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 (NM_IN_SET(errno, EOPNOTSUPP, EFAULT))
|
||||||
|
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 (gssize) k;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
GSubprocess *subprocess;
|
GSubprocess *subprocess;
|
||||||
GSource *timeout_source;
|
GSource *timeout_source;
|
||||||
|
@@ -52,10 +52,55 @@ struct _NMStrBuf;
|
|||||||
|
|
||||||
gssize nm_utils_fd_read(int fd, struct _NMStrBuf *out_string);
|
gssize nm_utils_fd_read(int fd, struct _NMStrBuf *out_string);
|
||||||
|
|
||||||
|
gssize nm_fd_next_datagram_size(int fd);
|
||||||
|
|
||||||
struct stat;
|
struct stat;
|
||||||
|
|
||||||
int nm_utils_file_stat(const char *filename, struct stat *out_st);
|
int nm_utils_file_stat(const char *filename, struct stat *out_st);
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
/* From systemd's ERRNO_IS_TRANSIENT().
|
||||||
|
*
|
||||||
|
* For send()/recv() or read()/write(). */
|
||||||
|
static inline gboolean
|
||||||
|
NM_ERRNO_IS_TRANSIENT(int r)
|
||||||
|
{
|
||||||
|
return NM_IN_SET((r < 0 ? -r : r), EAGAIN, EINTR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* From systemd's ERRNO_IS_DISCONNECT().
|
||||||
|
*
|
||||||
|
* Hint #1: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5.
|
||||||
|
*
|
||||||
|
* Hint #2: The kernel sends e.g., EHOSTUNREACH or ENONET to userspace in some ICMP error cases. See the
|
||||||
|
* icmp_err_convert[] in net/ipv4/icmp.c in the kernel sources.
|
||||||
|
*
|
||||||
|
* Hint #3: When asynchronous connect() on TCP fails because the host never acknowledges a single packet,
|
||||||
|
* kernel tells us that with ETIMEDOUT, see tcp(7). */
|
||||||
|
static inline gboolean
|
||||||
|
NM_ERRNO_IS_DISCONNECT(int r)
|
||||||
|
{
|
||||||
|
return NM_IN_SET((r < 0 ? -r : r),
|
||||||
|
ECONNABORTED,
|
||||||
|
ECONNREFUSED,
|
||||||
|
ECONNRESET,
|
||||||
|
EHOSTDOWN,
|
||||||
|
EHOSTUNREACH,
|
||||||
|
ENETDOWN,
|
||||||
|
ENETRESET,
|
||||||
|
ENETUNREACH,
|
||||||
|
ENONET,
|
||||||
|
ENOPROTOOPT,
|
||||||
|
ENOTCONN,
|
||||||
|
EPIPE,
|
||||||
|
EPROTO,
|
||||||
|
ESHUTDOWN,
|
||||||
|
ETIMEDOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
void nm_g_subprocess_terminate_in_background(GSubprocess *subprocess, int timeout_msec_before_kill);
|
void nm_g_subprocess_terminate_in_background(GSubprocess *subprocess, int timeout_msec_before_kill);
|
||||||
|
|
||||||
char **nm_utils_find_mkstemp_files(const char *dirname, const char *filename);
|
char **nm_utils_find_mkstemp_files(const char *dirname, const char *filename);
|
||||||
|
323
src/libnm-glib-aux/nm-prioq.c
Normal file
323
src/libnm-glib-aux/nm-prioq.c
Normal file
@@ -0,0 +1,323 @@
|
|||||||
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Taken from systemd's Prioq.
|
||||||
|
*
|
||||||
|
* Priority Queue
|
||||||
|
* The prioq object implements a priority queue. That is, it orders objects by
|
||||||
|
* their priority and allows O(1) access to the object with the highest
|
||||||
|
* priority. Insertion and removal are Θ(log n). Optionally, the caller can
|
||||||
|
* provide a pointer to an index which will be kept up-to-date by the prioq.
|
||||||
|
*
|
||||||
|
* The underlying algorithm used in this implementation is a Heap.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "libnm-glib-aux/nm-default-glib-i18n-lib.h"
|
||||||
|
|
||||||
|
#include "nm-prioq.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
struct _NMPrioqItem {
|
||||||
|
void *data;
|
||||||
|
unsigned *idx;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
void
|
||||||
|
nm_prioq_init(NMPrioq *q, GCompareFunc compare_func)
|
||||||
|
{
|
||||||
|
nm_assert(q);
|
||||||
|
nm_assert(compare_func);
|
||||||
|
|
||||||
|
*q = (NMPrioq){
|
||||||
|
._priv =
|
||||||
|
{
|
||||||
|
.compare_func = compare_func,
|
||||||
|
.compare_data = NULL,
|
||||||
|
.compare_with_data = FALSE,
|
||||||
|
.items = NULL,
|
||||||
|
.n_items = 0,
|
||||||
|
.n_allocated = 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nm_prioq_init_with_data(NMPrioq *q, GCompareDataFunc compare_func, gpointer compare_data)
|
||||||
|
{
|
||||||
|
nm_assert(q);
|
||||||
|
nm_assert(compare_func);
|
||||||
|
|
||||||
|
*q = (NMPrioq){
|
||||||
|
._priv =
|
||||||
|
{
|
||||||
|
.compare_data_func = compare_func,
|
||||||
|
.compare_data = compare_data,
|
||||||
|
.compare_with_data = TRUE,
|
||||||
|
.items = NULL,
|
||||||
|
.n_items = 0,
|
||||||
|
.n_allocated = 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nm_prioq_destroy(NMPrioq *q)
|
||||||
|
{
|
||||||
|
if (!q || !q->_priv.compare_func)
|
||||||
|
return;
|
||||||
|
|
||||||
|
free(q->_priv.items);
|
||||||
|
q->_priv.compare_func = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
static int
|
||||||
|
compare(NMPrioq *q, unsigned a, unsigned b)
|
||||||
|
{
|
||||||
|
nm_assert(q);
|
||||||
|
nm_assert(q->_priv.compare_func);
|
||||||
|
nm_assert(a != b);
|
||||||
|
nm_assert(a < q->_priv.n_items);
|
||||||
|
nm_assert(b < q->_priv.n_items);
|
||||||
|
|
||||||
|
if (q->_priv.compare_with_data) {
|
||||||
|
return q->_priv.compare_data_func(q->_priv.items[a].data,
|
||||||
|
q->_priv.items[b].data,
|
||||||
|
q->_priv.compare_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return q->_priv.compare_func(q->_priv.items[a].data, q->_priv.items[b].data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
swap(NMPrioq *q, unsigned j, unsigned k)
|
||||||
|
{
|
||||||
|
nm_assert(q);
|
||||||
|
nm_assert(j < q->_priv.n_items);
|
||||||
|
nm_assert(k < q->_priv.n_items);
|
||||||
|
|
||||||
|
nm_assert(!q->_priv.items[j].idx || *(q->_priv.items[j].idx) == j);
|
||||||
|
nm_assert(!q->_priv.items[k].idx || *(q->_priv.items[k].idx) == k);
|
||||||
|
|
||||||
|
NM_SWAP(&q->_priv.items[j].data, &q->_priv.items[k].data);
|
||||||
|
NM_SWAP(&q->_priv.items[j].idx, &q->_priv.items[k].idx);
|
||||||
|
|
||||||
|
if (q->_priv.items[j].idx)
|
||||||
|
*q->_priv.items[j].idx = j;
|
||||||
|
|
||||||
|
if (q->_priv.items[k].idx)
|
||||||
|
*q->_priv.items[k].idx = k;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned
|
||||||
|
shuffle_up(NMPrioq *q, unsigned idx)
|
||||||
|
{
|
||||||
|
nm_assert(q);
|
||||||
|
nm_assert(idx < q->_priv.n_items);
|
||||||
|
|
||||||
|
while (idx > 0) {
|
||||||
|
unsigned k;
|
||||||
|
|
||||||
|
k = (idx - 1) / 2;
|
||||||
|
|
||||||
|
if (compare(q, k, idx) <= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
swap(q, idx, k);
|
||||||
|
idx = k;
|
||||||
|
}
|
||||||
|
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned
|
||||||
|
shuffle_down(NMPrioq *q, unsigned idx)
|
||||||
|
{
|
||||||
|
nm_assert(q);
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
unsigned j;
|
||||||
|
unsigned k;
|
||||||
|
unsigned s;
|
||||||
|
|
||||||
|
k = (idx + 1) * 2; /* right child */
|
||||||
|
j = k - 1; /* left child */
|
||||||
|
|
||||||
|
if (j >= q->_priv.n_items)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (compare(q, j, idx) < 0) {
|
||||||
|
/* So our left child is smaller than we are, let's
|
||||||
|
* remember this fact */
|
||||||
|
s = j;
|
||||||
|
} else
|
||||||
|
s = idx;
|
||||||
|
|
||||||
|
if ((k < q->_priv.n_items) && compare(q, k, s) < 0) {
|
||||||
|
/* So our right child is smaller than we are, let's
|
||||||
|
* remember this fact */
|
||||||
|
s = k;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* s now points to the smallest of the three items */
|
||||||
|
|
||||||
|
if (s == idx)
|
||||||
|
/* No swap necessary, we're done */
|
||||||
|
break;
|
||||||
|
|
||||||
|
swap(q, idx, s);
|
||||||
|
idx = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nm_prioq_put(NMPrioq *q, void *data, unsigned *idx)
|
||||||
|
{
|
||||||
|
unsigned k;
|
||||||
|
|
||||||
|
nm_assert(q);
|
||||||
|
|
||||||
|
if (q->_priv.n_items >= q->_priv.n_allocated) {
|
||||||
|
q->_priv.n_allocated = NM_MAX((q->_priv.n_items + 1u) * 2u, 16u);
|
||||||
|
q->_priv.items = g_renew(struct _NMPrioqItem, q->_priv.items, q->_priv.n_allocated);
|
||||||
|
}
|
||||||
|
|
||||||
|
k = q->_priv.n_items++;
|
||||||
|
|
||||||
|
q->_priv.items[k] = (struct _NMPrioqItem){
|
||||||
|
.data = data,
|
||||||
|
.idx = idx,
|
||||||
|
};
|
||||||
|
if (idx)
|
||||||
|
*idx = k;
|
||||||
|
|
||||||
|
shuffle_up(q, k);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
remove_item(NMPrioq *q, struct _NMPrioqItem *i)
|
||||||
|
{
|
||||||
|
struct _NMPrioqItem *l;
|
||||||
|
unsigned k;
|
||||||
|
|
||||||
|
nm_assert(q);
|
||||||
|
nm_assert(i);
|
||||||
|
nm_assert(q->_priv.n_items > 0);
|
||||||
|
nm_assert(i >= q->_priv.items);
|
||||||
|
nm_assert(i < &q->_priv.items[q->_priv.n_items]);
|
||||||
|
|
||||||
|
l = &q->_priv.items[q->_priv.n_items - 1u];
|
||||||
|
|
||||||
|
if (i == l) {
|
||||||
|
/* Last entry, let's just remove it */
|
||||||
|
q->_priv.n_items--;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Not last entry, let's replace the last entry with
|
||||||
|
* this one, and reshuffle */
|
||||||
|
k = i - q->_priv.items;
|
||||||
|
|
||||||
|
*i = *l;
|
||||||
|
if (i->idx)
|
||||||
|
*i->idx = k;
|
||||||
|
q->_priv.n_items--;
|
||||||
|
|
||||||
|
k = shuffle_down(q, k);
|
||||||
|
shuffle_up(q, k);
|
||||||
|
}
|
||||||
|
|
||||||
|
_nm_pure static struct _NMPrioqItem *
|
||||||
|
find_item(NMPrioq *q, void *data, unsigned *idx)
|
||||||
|
{
|
||||||
|
struct _NMPrioqItem *i;
|
||||||
|
|
||||||
|
nm_assert(q);
|
||||||
|
|
||||||
|
if (q->_priv.n_items <= 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (idx) {
|
||||||
|
if (*idx == NM_PRIOQ_IDX_NULL || *idx >= q->_priv.n_items)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
i = &q->_priv.items[*idx];
|
||||||
|
if (i->data == data)
|
||||||
|
return i;
|
||||||
|
} else {
|
||||||
|
for (i = q->_priv.items; i < &q->_priv.items[q->_priv.n_items]; i++) {
|
||||||
|
if (i->data == data)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
nm_prioq_remove(NMPrioq *q, void *data, unsigned *idx)
|
||||||
|
{
|
||||||
|
struct _NMPrioqItem *i;
|
||||||
|
|
||||||
|
nm_assert(q);
|
||||||
|
|
||||||
|
i = find_item(q, data, idx);
|
||||||
|
if (!i)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
remove_item(q, i);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
nm_prioq_reshuffle(NMPrioq *q, void *data, unsigned *idx)
|
||||||
|
{
|
||||||
|
struct _NMPrioqItem *i;
|
||||||
|
unsigned k;
|
||||||
|
|
||||||
|
nm_assert(q);
|
||||||
|
|
||||||
|
i = find_item(q, data, idx);
|
||||||
|
if (!i)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
k = i - q->_priv.items;
|
||||||
|
k = shuffle_down(q, k);
|
||||||
|
shuffle_up(q, k);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
nm_prioq_peek_by_index(NMPrioq *q, unsigned idx)
|
||||||
|
{
|
||||||
|
nm_assert(q);
|
||||||
|
|
||||||
|
if (idx >= q->_priv.n_items)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return q->_priv.items[idx].data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
nm_prioq_pop(NMPrioq *q)
|
||||||
|
{
|
||||||
|
void *data;
|
||||||
|
|
||||||
|
nm_assert(q);
|
||||||
|
|
||||||
|
if (q->_priv.n_items <= 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
data = q->_priv.items[0].data;
|
||||||
|
remove_item(q, &q->_priv.items[0]);
|
||||||
|
return data;
|
||||||
|
}
|
71
src/libnm-glib-aux/nm-prioq.h
Normal file
71
src/libnm-glib-aux/nm-prioq.h
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
|
||||||
|
#ifndef __NM_PRIOQ_H__
|
||||||
|
#define __NM_PRIOQ_H__
|
||||||
|
|
||||||
|
#define NM_PRIOQ_IDX_NULL G_MAXUINT
|
||||||
|
|
||||||
|
struct _NMPrioqItem;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
struct {
|
||||||
|
union {
|
||||||
|
GCompareDataFunc compare_data_func;
|
||||||
|
GCompareFunc compare_func;
|
||||||
|
};
|
||||||
|
|
||||||
|
gpointer compare_data;
|
||||||
|
|
||||||
|
struct _NMPrioqItem *items;
|
||||||
|
|
||||||
|
unsigned n_items;
|
||||||
|
unsigned n_allocated;
|
||||||
|
|
||||||
|
bool compare_with_data;
|
||||||
|
} _priv;
|
||||||
|
} NMPrioq;
|
||||||
|
|
||||||
|
#define NM_PRIOQ_ZERO \
|
||||||
|
{ \
|
||||||
|
._priv = { \
|
||||||
|
.compare_func = NULL, \
|
||||||
|
}, \
|
||||||
|
}
|
||||||
|
|
||||||
|
void nm_prioq_init(NMPrioq *q, GCompareFunc compare_func);
|
||||||
|
void nm_prioq_init_with_data(NMPrioq *q, GCompareDataFunc compare_func, gpointer compare_data);
|
||||||
|
|
||||||
|
void nm_prioq_destroy(NMPrioq *q);
|
||||||
|
|
||||||
|
#define nm_auto_prioq nm_auto(nm_prioq_destroy)
|
||||||
|
|
||||||
|
void nm_prioq_put(NMPrioq *q, void *data, unsigned *idx);
|
||||||
|
gboolean nm_prioq_remove(NMPrioq *q, void *data, unsigned *idx);
|
||||||
|
gboolean nm_prioq_reshuffle(NMPrioq *q, void *data, unsigned *idx);
|
||||||
|
|
||||||
|
void *nm_prioq_peek_by_index(NMPrioq *q, unsigned idx) _nm_pure;
|
||||||
|
|
||||||
|
static inline void *
|
||||||
|
nm_prioq_peek(NMPrioq *q)
|
||||||
|
{
|
||||||
|
return nm_prioq_peek_by_index(q, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *nm_prioq_pop(NMPrioq *q);
|
||||||
|
|
||||||
|
#define NM_PRIOQ_FOREACH_ITEM(q, p) for (unsigned _i = 0; (p = nm_prioq_peek_by_index(q, _i)); _i++)
|
||||||
|
|
||||||
|
_nm_pure static inline unsigned
|
||||||
|
nm_prioq_size(NMPrioq *q)
|
||||||
|
{
|
||||||
|
nm_assert(q);
|
||||||
|
return q->_priv.n_items;
|
||||||
|
}
|
||||||
|
|
||||||
|
_nm_pure static inline gboolean
|
||||||
|
nm_prioq_isempty(NMPrioq *q)
|
||||||
|
{
|
||||||
|
return nm_prioq_size(q) <= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* __NM_PRIOQ_H__ */
|
@@ -198,6 +198,13 @@ typedef struct {
|
|||||||
|
|
||||||
#define NM_ETHER_ADDR_INIT(...) ((NMEtherAddr) _NM_ETHER_ADDR_INIT(__VA_ARGS__))
|
#define NM_ETHER_ADDR_INIT(...) ((NMEtherAddr) _NM_ETHER_ADDR_INIT(__VA_ARGS__))
|
||||||
|
|
||||||
|
struct _NMIPAddr;
|
||||||
|
extern const struct _NMIPAddr nm_ip_addr_zero;
|
||||||
|
|
||||||
|
/* Let's reuse nm_ip_addr_zero also for nm_ether_addr_zero. It's a union that
|
||||||
|
* also contains a NMEtherAddr field. */
|
||||||
|
#define nm_ether_addr_zero (*((const NMEtherAddr *) ((gconstpointer) &nm_ip_addr_zero)))
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
nm_ether_addr_cmp(const NMEtherAddr *a, const NMEtherAddr *b)
|
nm_ether_addr_cmp(const NMEtherAddr *a, const NMEtherAddr *b)
|
||||||
{
|
{
|
||||||
@@ -212,6 +219,12 @@ nm_ether_addr_equal(const NMEtherAddr *a, const NMEtherAddr *b)
|
|||||||
return nm_ether_addr_cmp(a, b) == 0;
|
return nm_ether_addr_cmp(a, b) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline gboolean
|
||||||
|
nm_ether_addr_is_zero(const NMEtherAddr *a)
|
||||||
|
{
|
||||||
|
return nm_memeq(a, &nm_ether_addr_zero, sizeof(NMEtherAddr));
|
||||||
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
struct ether_addr;
|
struct ether_addr;
|
||||||
@@ -221,6 +234,7 @@ nm_utils_ether_addr_cmp(const struct ether_addr *a1, const struct ether_addr *a2
|
|||||||
{
|
{
|
||||||
nm_assert(a1);
|
nm_assert(a1);
|
||||||
nm_assert(a2);
|
nm_assert(a2);
|
||||||
|
|
||||||
return memcmp(a1, a2, 6 /*ETH_ALEN*/);
|
return memcmp(a1, a2, 6 /*ETH_ALEN*/);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2599,6 +2613,9 @@ nm_ether_addr_to_string(const NMEtherAddr *ether_addr, char sbuf[static(sizeof(N
|
|||||||
#define nm_ether_addr_to_string_a(ether_addr) \
|
#define nm_ether_addr_to_string_a(ether_addr) \
|
||||||
nm_ether_addr_to_string((ether_addr), g_alloca(sizeof(NMEtherAddr) * 3))
|
nm_ether_addr_to_string((ether_addr), g_alloca(sizeof(NMEtherAddr) * 3))
|
||||||
|
|
||||||
|
#define nm_ether_addr_to_string_dup(ether_addr) \
|
||||||
|
((char *) nm_ether_addr_to_string((ether_addr), g_malloc(sizeof(NMEtherAddr) * 3)))
|
||||||
|
|
||||||
NMEtherAddr *nm_ether_addr_from_string(NMEtherAddr *addr, const char *str);
|
NMEtherAddr *nm_ether_addr_from_string(NMEtherAddr *addr, const char *str);
|
||||||
|
|
||||||
guint8 *nm_utils_hexstr2bin_full(const char *hexstr,
|
guint8 *nm_utils_hexstr2bin_full(const char *hexstr,
|
||||||
|
@@ -301,8 +301,13 @@ nm_utils_monotonic_timestamp_from_boottime(guint64 boottime, gint64 timestamp_ns
|
|||||||
|
|
||||||
nm_assert(offset <= 0 && offset > G_MININT64);
|
nm_assert(offset <= 0 && offset > G_MININT64);
|
||||||
|
|
||||||
/* check for overflow (note that offset is non-positive). */
|
if (boottime >= (guint64) G_MAXINT64) {
|
||||||
g_return_val_if_fail(boottime < G_MAXINT64, G_MAXINT64);
|
/* This indicates infinity. We keep it at such. */
|
||||||
|
return G_MAXINT64;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Note that overflow cannot happen, because bootime is non-negative, and
|
||||||
|
* offset is non-positive. */
|
||||||
|
|
||||||
return (gint64) boottime + offset;
|
return (gint64) boottime + offset;
|
||||||
}
|
}
|
||||||
@@ -317,6 +322,16 @@ nm_utils_clock_gettime_nsec(clockid_t clockid)
|
|||||||
return nm_utils_timespec_to_nsec(&tp);
|
return nm_utils_timespec_to_nsec(&tp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gint64
|
||||||
|
nm_utils_clock_gettime_usec(clockid_t clockid)
|
||||||
|
{
|
||||||
|
struct timespec tp;
|
||||||
|
|
||||||
|
if (clock_gettime(clockid, &tp) != 0)
|
||||||
|
return -NM_ERRNO_NATIVE(errno);
|
||||||
|
return nm_utils_timespec_to_usec(&tp);
|
||||||
|
}
|
||||||
|
|
||||||
gint64
|
gint64
|
||||||
nm_utils_clock_gettime_msec(clockid_t clockid)
|
nm_utils_clock_gettime_msec(clockid_t clockid)
|
||||||
{
|
{
|
||||||
@@ -326,3 +341,36 @@ nm_utils_clock_gettime_msec(clockid_t clockid)
|
|||||||
return -NM_ERRNO_NATIVE(errno);
|
return -NM_ERRNO_NATIVE(errno);
|
||||||
return nm_utils_timespec_to_msec(&tp);
|
return nm_utils_timespec_to_msec(&tp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
/* Taken from systemd's map_clock_usec_internal(). */
|
||||||
|
gint64
|
||||||
|
nm_time_map_clock(gint64 from, gint64 from_base, gint64 to_base)
|
||||||
|
{
|
||||||
|
/* Maps the time 'from' between two clocks, based on a common reference point where the first clock
|
||||||
|
* is at 'from_base' and the second clock at 'to_base'. Basically calculates:
|
||||||
|
*
|
||||||
|
* from - from_base + to_base
|
||||||
|
*
|
||||||
|
* But takes care of overflows/underflows and avoids signed operations. */
|
||||||
|
|
||||||
|
if (from >= from_base) {
|
||||||
|
gint64 delta = from - from_base;
|
||||||
|
|
||||||
|
/* In the future */
|
||||||
|
if (to_base >= G_MAXINT64 - delta)
|
||||||
|
return G_MAXINT64;
|
||||||
|
|
||||||
|
return to_base + delta;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
gint64 delta = from_base - from;
|
||||||
|
|
||||||
|
/* In the past */
|
||||||
|
if (to_base <= G_MININT64 + delta)
|
||||||
|
return G_MININT64;
|
||||||
|
|
||||||
|
return to_base - delta;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -8,17 +8,38 @@
|
|||||||
|
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
|
_nm_always_inline static inline gint64
|
||||||
|
_nm_utils_timespec_to_xsec(const struct timespec *ts, gint64 xsec_per_sec)
|
||||||
|
{
|
||||||
|
nm_assert(ts);
|
||||||
|
|
||||||
|
if (ts->tv_sec < 0 || ts->tv_nsec < 0)
|
||||||
|
return G_MAXINT64;
|
||||||
|
|
||||||
|
if (ts->tv_sec > ((guint64) G_MAXINT64) || ts->tv_nsec > ((guint64) G_MAXINT64)
|
||||||
|
|| ts->tv_sec >= (G_MAXINT64 - ((gint64) ts->tv_nsec)) / xsec_per_sec)
|
||||||
|
return G_MAXINT64;
|
||||||
|
|
||||||
|
return (((gint64) ts->tv_sec) * xsec_per_sec)
|
||||||
|
+ (((gint64) ts->tv_nsec) / (NM_UTILS_NSEC_PER_SEC / xsec_per_sec));
|
||||||
|
}
|
||||||
|
|
||||||
static inline gint64
|
static inline gint64
|
||||||
nm_utils_timespec_to_nsec(const struct timespec *ts)
|
nm_utils_timespec_to_nsec(const struct timespec *ts)
|
||||||
{
|
{
|
||||||
return (((gint64) ts->tv_sec) * ((gint64) NM_UTILS_NSEC_PER_SEC)) + ((gint64) ts->tv_nsec);
|
return _nm_utils_timespec_to_xsec(ts, NM_UTILS_NSEC_PER_SEC);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline gint64
|
||||||
|
nm_utils_timespec_to_usec(const struct timespec *ts)
|
||||||
|
{
|
||||||
|
return _nm_utils_timespec_to_xsec(ts, NM_UTILS_USEC_PER_SEC);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline gint64
|
static inline gint64
|
||||||
nm_utils_timespec_to_msec(const struct timespec *ts)
|
nm_utils_timespec_to_msec(const struct timespec *ts)
|
||||||
{
|
{
|
||||||
return (((gint64) ts->tv_sec) * ((gint64) 1000))
|
return _nm_utils_timespec_to_xsec(ts, NM_UTILS_MSEC_PER_SEC);
|
||||||
+ (((gint64) ts->tv_nsec) / ((gint64) NM_UTILS_NSEC_PER_SEC / 1000));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gint64 nm_utils_get_monotonic_timestamp_nsec(void);
|
gint64 nm_utils_get_monotonic_timestamp_nsec(void);
|
||||||
@@ -35,6 +56,12 @@ nm_utils_get_monotonic_timestamp_nsec_cached(gint64 *cache_now)
|
|||||||
return (*cache_now) ?: (*cache_now = nm_utils_get_monotonic_timestamp_nsec());
|
return (*cache_now) ?: (*cache_now = nm_utils_get_monotonic_timestamp_nsec());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline gint64
|
||||||
|
nm_utils_get_monotonic_timestamp_usec_cached(gint64 *cache_now)
|
||||||
|
{
|
||||||
|
return (*cache_now) ?: (*cache_now = nm_utils_get_monotonic_timestamp_usec());
|
||||||
|
}
|
||||||
|
|
||||||
static inline gint64
|
static inline gint64
|
||||||
nm_utils_get_monotonic_timestamp_msec_cached(gint64 *cache_now)
|
nm_utils_get_monotonic_timestamp_msec_cached(gint64 *cache_now)
|
||||||
{
|
{
|
||||||
@@ -48,6 +75,9 @@ nm_utils_get_monotonic_timestamp_sec_cached(gint32 *cache_now)
|
|||||||
}
|
}
|
||||||
|
|
||||||
gint64 nm_utils_clock_gettime_nsec(clockid_t clockid);
|
gint64 nm_utils_clock_gettime_nsec(clockid_t clockid);
|
||||||
|
gint64 nm_utils_clock_gettime_usec(clockid_t clockid);
|
||||||
gint64 nm_utils_clock_gettime_msec(clockid_t clockid);
|
gint64 nm_utils_clock_gettime_msec(clockid_t clockid);
|
||||||
|
|
||||||
|
gint64 nm_time_map_clock(gint64 from, gint64 from_base, gint64 to_base);
|
||||||
|
|
||||||
#endif /* __NM_TIME_UTILS_H__ */
|
#endif /* __NM_TIME_UTILS_H__ */
|
||||||
|
@@ -11,6 +11,7 @@
|
|||||||
#include "libnm-glib-aux/nm-time-utils.h"
|
#include "libnm-glib-aux/nm-time-utils.h"
|
||||||
#include "libnm-glib-aux/nm-ref-string.h"
|
#include "libnm-glib-aux/nm-ref-string.h"
|
||||||
#include "libnm-glib-aux/nm-io-utils.h"
|
#include "libnm-glib-aux/nm-io-utils.h"
|
||||||
|
#include "libnm-glib-aux/nm-prioq.h"
|
||||||
|
|
||||||
#include "libnm-glib-aux/nm-test-utils.h"
|
#include "libnm-glib-aux/nm-test-utils.h"
|
||||||
|
|
||||||
@@ -89,6 +90,38 @@ test_monotonic_timestamp(void)
|
|||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_timespect_to(void)
|
||||||
|
{
|
||||||
|
struct timespec ts;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 1000; i++) {
|
||||||
|
gint64 t_msec;
|
||||||
|
gint64 t_usec;
|
||||||
|
gint64 t_nsec;
|
||||||
|
|
||||||
|
nmtst_rand_buf(NULL, &ts, sizeof(ts));
|
||||||
|
ts.tv_sec = llabs(ts.tv_sec % 100000);
|
||||||
|
ts.tv_nsec = llabs(ts.tv_nsec % NM_UTILS_NSEC_PER_SEC);
|
||||||
|
|
||||||
|
t_msec = nm_utils_timespec_to_msec(&ts);
|
||||||
|
t_usec = nm_utils_timespec_to_usec(&ts);
|
||||||
|
t_nsec = nm_utils_timespec_to_nsec(&ts);
|
||||||
|
|
||||||
|
g_assert_cmpint(t_msec, <=, t_usec / 1000);
|
||||||
|
g_assert_cmpint(t_msec + 1, >=, t_usec / 1000);
|
||||||
|
|
||||||
|
g_assert_cmpint(t_msec, <=, t_nsec / 1000000);
|
||||||
|
g_assert_cmpint(t_msec + 1, >=, t_nsec / 1000000);
|
||||||
|
|
||||||
|
g_assert_cmpint(t_usec, <=, t_nsec / 1000);
|
||||||
|
g_assert_cmpint(t_usec + 1, >=, t_nsec / 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
static void
|
static void
|
||||||
test_nmhash(void)
|
test_nmhash(void)
|
||||||
{
|
{
|
||||||
@@ -2260,6 +2293,79 @@ test_garray(void)
|
|||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
static int
|
||||||
|
_prioq_cmp(gconstpointer a, gconstpointer b)
|
||||||
|
{
|
||||||
|
NM_CMP_DIRECT(GPOINTER_TO_UINT(a), GPOINTER_TO_UINT(b));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
_prioq_cmp_with_data(gconstpointer a, gconstpointer b, gpointer user_data)
|
||||||
|
{
|
||||||
|
return _prioq_cmp(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_nm_prioq(void)
|
||||||
|
{
|
||||||
|
nm_auto_prioq NMPrioq q = NM_PRIOQ_ZERO;
|
||||||
|
gpointer data[200];
|
||||||
|
gpointer data_pop[200];
|
||||||
|
guint data_idx[G_N_ELEMENTS(data)];
|
||||||
|
guint i;
|
||||||
|
guint n;
|
||||||
|
gpointer p;
|
||||||
|
|
||||||
|
if (nmtst_get_rand_one_case_in(10))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (nmtst_get_rand_bool())
|
||||||
|
nm_prioq_init(&q, _prioq_cmp);
|
||||||
|
else
|
||||||
|
nm_prioq_init_with_data(&q, _prioq_cmp_with_data, NULL);
|
||||||
|
|
||||||
|
g_assert(nm_prioq_size(&q) == 0);
|
||||||
|
|
||||||
|
if (nmtst_get_rand_one_case_in(10))
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (i = 0; i < G_N_ELEMENTS(data); i++) {
|
||||||
|
data[i] = GUINT_TO_POINTER((nmtst_get_rand_uint32() % G_N_ELEMENTS(data)) + 1u);
|
||||||
|
data_idx[i] = NM_PRIOQ_IDX_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
nm_prioq_put(&q, data[0], NULL);
|
||||||
|
g_assert(nm_prioq_size(&q) == 1);
|
||||||
|
|
||||||
|
p = nm_prioq_pop(&q);
|
||||||
|
g_assert(p == data[0]);
|
||||||
|
g_assert(nm_prioq_size(&q) == 0);
|
||||||
|
|
||||||
|
g_assert(!nm_prioq_pop(&q));
|
||||||
|
|
||||||
|
n = nmtst_get_rand_uint32() % G_N_ELEMENTS(data);
|
||||||
|
for (i = 0; i < n; i++)
|
||||||
|
nm_prioq_put(&q, data[i], &data_idx[i]);
|
||||||
|
|
||||||
|
g_assert_cmpint(nm_prioq_size(&q), ==, n);
|
||||||
|
|
||||||
|
if (nmtst_get_rand_one_case_in(10))
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
data_pop[i] = nm_prioq_pop(&q);
|
||||||
|
g_assert(data_pop[i]);
|
||||||
|
if (i > 0)
|
||||||
|
g_assert(_prioq_cmp(data_pop[i - 1], data_pop[i]) <= 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_assert(!nm_prioq_pop(&q));
|
||||||
|
g_assert(nm_prioq_size(&q) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
NMTST_DEFINE();
|
NMTST_DEFINE();
|
||||||
|
|
||||||
int
|
int
|
||||||
@@ -2270,6 +2376,7 @@ main(int argc, char **argv)
|
|||||||
g_test_add_func("/general/test_nm_static_assert", test_nm_static_assert);
|
g_test_add_func("/general/test_nm_static_assert", test_nm_static_assert);
|
||||||
g_test_add_func("/general/test_gpid", test_gpid);
|
g_test_add_func("/general/test_gpid", test_gpid);
|
||||||
g_test_add_func("/general/test_monotonic_timestamp", test_monotonic_timestamp);
|
g_test_add_func("/general/test_monotonic_timestamp", test_monotonic_timestamp);
|
||||||
|
g_test_add_func("/general/test_timespect_to", test_timespect_to);
|
||||||
g_test_add_func("/general/test_nmhash", test_nmhash);
|
g_test_add_func("/general/test_nmhash", test_nmhash);
|
||||||
g_test_add_func("/general/test_nm_make_strv", test_make_strv);
|
g_test_add_func("/general/test_nm_make_strv", test_make_strv);
|
||||||
g_test_add_func("/general/test_nm_strdup_int", test_nm_strdup_int);
|
g_test_add_func("/general/test_nm_strdup_int", test_nm_strdup_int);
|
||||||
@@ -2306,6 +2413,7 @@ main(int argc, char **argv)
|
|||||||
g_test_add_func("/general/test_hostname_is_valid", test_hostname_is_valid);
|
g_test_add_func("/general/test_hostname_is_valid", test_hostname_is_valid);
|
||||||
g_test_add_func("/general/test_inet_utils", test_inet_utils);
|
g_test_add_func("/general/test_inet_utils", test_inet_utils);
|
||||||
g_test_add_func("/general/test_garray", test_garray);
|
g_test_add_func("/general/test_garray", test_garray);
|
||||||
|
g_test_add_func("/general/test_nm_prioq", test_nm_prioq);
|
||||||
|
|
||||||
return g_test_run();
|
return g_test_run();
|
||||||
}
|
}
|
||||||
|
18
src/libnm-lldp/meson.build
Normal file
18
src/libnm-lldp/meson.build
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
|
||||||
|
libnm_lldp = static_library(
|
||||||
|
'nm-lldp',
|
||||||
|
sources: [
|
||||||
|
'nm-lldp-neighbor.c',
|
||||||
|
'nm-lldp-network.c',
|
||||||
|
'nm-lldp-rx.c',
|
||||||
|
],
|
||||||
|
include_directories: [
|
||||||
|
src_inc,
|
||||||
|
top_inc,
|
||||||
|
],
|
||||||
|
dependencies: [
|
||||||
|
glib_dep,
|
||||||
|
libudev_dep,
|
||||||
|
],
|
||||||
|
)
|
842
src/libnm-lldp/nm-lldp-neighbor.c
Normal file
842
src/libnm-lldp/nm-lldp-neighbor.c
Normal file
@@ -0,0 +1,842 @@
|
|||||||
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
|
||||||
|
#include "libnm-glib-aux/nm-default-glib-i18n-lib.h"
|
||||||
|
|
||||||
|
#include "nm-lldp-neighbor.h"
|
||||||
|
|
||||||
|
#include <net/ethernet.h>
|
||||||
|
|
||||||
|
#include "libnm-std-aux/unaligned.h"
|
||||||
|
#include "libnm-glib-aux/nm-time-utils.h"
|
||||||
|
#include "nm-lldp-network.h"
|
||||||
|
#include "nm-lldp.h"
|
||||||
|
#include "nm-lldp-rx-internal.h"
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
guint
|
||||||
|
nm_lldp_neighbor_id_hash(const NMLldpNeighborID *id)
|
||||||
|
{
|
||||||
|
NMHashState h;
|
||||||
|
|
||||||
|
nm_assert(id);
|
||||||
|
|
||||||
|
nm_hash_init(&h, 1925469911u);
|
||||||
|
nm_hash_update_mem(&h, id->chassis_id, id->chassis_id_size);
|
||||||
|
nm_hash_update_mem(&h, id->port_id, id->port_id_size);
|
||||||
|
return nm_hash_complete(&h);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
nm_lldp_neighbor_id_cmp(const NMLldpNeighborID *x, const NMLldpNeighborID *y)
|
||||||
|
{
|
||||||
|
nm_assert(x);
|
||||||
|
nm_assert(y);
|
||||||
|
|
||||||
|
NM_CMP_SELF(x, y);
|
||||||
|
NM_CMP_RETURN_DIRECT(
|
||||||
|
nm_memcmp_n(x->chassis_id, x->chassis_id_size, y->chassis_id, y->chassis_id_size, 1));
|
||||||
|
NM_CMP_RETURN_DIRECT(nm_memcmp_n(x->port_id, x->port_id_size, y->port_id, y->port_id_size, 1));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
nm_lldp_neighbor_id_equal(const NMLldpNeighborID *x, const NMLldpNeighborID *y)
|
||||||
|
{
|
||||||
|
return nm_lldp_neighbor_id_cmp(x, y) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
nm_lldp_neighbor_prioq_compare_func(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const NMLldpNeighbor *x = a;
|
||||||
|
const NMLldpNeighbor *y = b;
|
||||||
|
|
||||||
|
nm_assert(x);
|
||||||
|
nm_assert(y);
|
||||||
|
|
||||||
|
NM_CMP_FIELD(x, y, until_usec);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
parse_string(NMLldpRX *lldp_rx, char **s, const void *q, size_t n)
|
||||||
|
{
|
||||||
|
const char *p = q;
|
||||||
|
char *k;
|
||||||
|
|
||||||
|
nm_assert(s);
|
||||||
|
nm_assert(p || n == 0);
|
||||||
|
|
||||||
|
if (*s) {
|
||||||
|
_LOG2D(lldp_rx, "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)) {
|
||||||
|
_LOG2D(lldp_rx, "Found inner NUL in string, ignoring field.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Let's escape weird chars, for security reasons */
|
||||||
|
k = nm_utils_buf_utf8safe_escape_cp(p,
|
||||||
|
n,
|
||||||
|
NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL
|
||||||
|
| NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII);
|
||||||
|
|
||||||
|
g_free(*s);
|
||||||
|
*s = k;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
nm_lldp_neighbor_parse(NMLldpNeighbor *n)
|
||||||
|
{
|
||||||
|
struct ether_header h;
|
||||||
|
const uint8_t *p;
|
||||||
|
size_t left;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
nm_assert(n);
|
||||||
|
|
||||||
|
if (n->raw_size < sizeof(struct ether_header)) {
|
||||||
|
_LOG2D(n->lldp_rx, "Received truncated packet, ignoring.");
|
||||||
|
return -NME_UNSPEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(&h, NM_LLDP_NEIGHBOR_RAW(n), sizeof(h));
|
||||||
|
|
||||||
|
if (h.ether_type != htobe16(NM_ETHERTYPE_LLDP)) {
|
||||||
|
_LOG2D(n->lldp_rx, "Received packet with wrong type, ignoring.");
|
||||||
|
return -NME_UNSPEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|| !NM_IN_SET(h.ether_dhost[5], 0x00, 0x03, 0x0e)) {
|
||||||
|
_LOG2D(n->lldp_rx, "Received packet with wrong destination address, ignoring.");
|
||||||
|
return -NME_UNSPEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(&n->source_address, h.ether_shost, sizeof(NMEtherAddr));
|
||||||
|
memcpy(&n->destination_address, h.ether_dhost, sizeof(NMEtherAddr));
|
||||||
|
|
||||||
|
p = (const uint8_t *) NM_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) {
|
||||||
|
_LOG2D(n->lldp_rx, "TLV lacks header, ignoring.");
|
||||||
|
return -NME_UNSPEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
type = p[0] >> 1;
|
||||||
|
length = p[1] + (((uint16_t) (p[0] & 1)) << 8);
|
||||||
|
p += 2, left -= 2;
|
||||||
|
|
||||||
|
if (left < length) {
|
||||||
|
_LOG2D(n->lldp_rx, "TLV truncated, ignoring datagram.");
|
||||||
|
return -NME_UNSPEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case NM_LLDP_TYPE_END:
|
||||||
|
if (length != 0) {
|
||||||
|
_LOG2D(n->lldp_rx, "End marker TLV not zero-sized, ignoring datagram.");
|
||||||
|
return -NME_UNSPEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Note that after processing the NM_LLDP_TYPE_END left could still be > 0
|
||||||
|
* as the message may contain padding (see IEEE 802.1AB-2016, sec. 8.5.12) */
|
||||||
|
|
||||||
|
goto end_marker;
|
||||||
|
|
||||||
|
case NM_LLDP_TYPE_CHASSIS_ID:
|
||||||
|
if (length < 2 || length > 256) {
|
||||||
|
/* includes the chassis subtype, hence one extra byte */
|
||||||
|
_LOG2D(n->lldp_rx, "Chassis ID field size out of range, ignoring datagram.");
|
||||||
|
return -NME_UNSPEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n->id.chassis_id) {
|
||||||
|
_LOG2D(n->lldp_rx, "Duplicate chassis ID field, ignoring datagram.");
|
||||||
|
return -NME_UNSPEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
n->id.chassis_id = nm_memdup(p, length);
|
||||||
|
n->id.chassis_id_size = length;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NM_LLDP_TYPE_PORT_ID:
|
||||||
|
if (length < 2 || length > 256) {
|
||||||
|
/* includes the port subtype, hence one extra byte */
|
||||||
|
_LOG2D(n->lldp_rx, "Port ID field size out of range, ignoring datagram.");
|
||||||
|
return -NME_UNSPEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n->id.port_id) {
|
||||||
|
_LOG2D(n->lldp_rx, "Duplicate port ID field, ignoring datagram.");
|
||||||
|
return -NME_UNSPEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
n->id.port_id = nm_memdup(p, length);
|
||||||
|
n->id.port_id_size = length;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NM_LLDP_TYPE_TTL:
|
||||||
|
if (length != 2) {
|
||||||
|
_LOG2D(n->lldp_rx, "TTL field has wrong size, ignoring datagram.");
|
||||||
|
return -NME_UNSPEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n->has_ttl) {
|
||||||
|
_LOG2D(n->lldp_rx, "Duplicate TTL field, ignoring datagram.");
|
||||||
|
return -NME_UNSPEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
n->ttl = unaligned_read_be16(p);
|
||||||
|
n->has_ttl = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NM_LLDP_TYPE_PORT_DESCRIPTION:
|
||||||
|
r = parse_string(n->lldp_rx, &n->port_description, p, length);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NM_LLDP_TYPE_SYSTEM_NAME:
|
||||||
|
r = parse_string(n->lldp_rx, &n->system_name, p, length);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NM_LLDP_TYPE_SYSTEM_DESCRIPTION:
|
||||||
|
r = parse_string(n->lldp_rx, &n->system_description, p, length);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NM_LLDP_TYPE_SYSTEM_CAPABILITIES:
|
||||||
|
if (length != 4) {
|
||||||
|
_LOG2D(n->lldp_rx, "System capabilities field has wrong size.");
|
||||||
|
return -NME_UNSPEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
n->system_capabilities = unaligned_read_be16(p);
|
||||||
|
n->enabled_capabilities = unaligned_read_be16(p + 2);
|
||||||
|
n->has_capabilities = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NM_LLDP_TYPE_PRIVATE:
|
||||||
|
if (length < 4) {
|
||||||
|
_LOG2D(n->lldp_rx, "Found private TLV that is too short, ignoring.");
|
||||||
|
return -NME_UNSPEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* RFC 8520: MUD URL */
|
||||||
|
if (memcmp(p, NM_LLDP_OUI_IANA_MUD, sizeof(NM_LLDP_OUI_IANA_MUD)) == 0) {
|
||||||
|
r = parse_string(n->lldp_rx,
|
||||||
|
&n->mud_url,
|
||||||
|
p + sizeof(NM_LLDP_OUI_IANA_MUD),
|
||||||
|
length - sizeof(NM_LLDP_OUI_IANA_MUD));
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
p += length, left -= length;
|
||||||
|
}
|
||||||
|
|
||||||
|
end_marker:
|
||||||
|
if (!n->id.chassis_id || !n->id.port_id || !n->has_ttl) {
|
||||||
|
_LOG2D(n->lldp_rx, "One or more mandatory TLV missing in datagram. Ignoring.");
|
||||||
|
return -NME_UNSPEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
n->rindex = sizeof(struct ether_header);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nm_lldp_neighbor_start_ttl(NMLldpNeighbor *n)
|
||||||
|
{
|
||||||
|
nm_assert(n);
|
||||||
|
|
||||||
|
if (n->ttl > 0) {
|
||||||
|
/* Use the packet's timestamp if there is one known */
|
||||||
|
if (n->timestamp_usec <= 0) {
|
||||||
|
/* Otherwise, take the current time */
|
||||||
|
n->timestamp_usec = nm_utils_get_monotonic_timestamp_usec();
|
||||||
|
}
|
||||||
|
|
||||||
|
n->until_usec = n->timestamp_usec + (n->ttl * NM_UTILS_USEC_PER_SEC);
|
||||||
|
} else
|
||||||
|
n->until_usec = 0;
|
||||||
|
|
||||||
|
if (n->lldp_rx)
|
||||||
|
nm_prioq_reshuffle(&n->lldp_rx->neighbor_by_expiry, n, &n->prioq_idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
nm_lldp_neighbor_cmp(const NMLldpNeighbor *a, const NMLldpNeighbor *b)
|
||||||
|
{
|
||||||
|
NM_CMP_SELF(a, b);
|
||||||
|
NM_CMP_FIELD(a, b, raw_size);
|
||||||
|
NM_CMP_DIRECT_MEMCMP(NM_LLDP_NEIGHBOR_RAW(a), NM_LLDP_NEIGHBOR_RAW(b), a->raw_size);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
nm_lldp_neighbor_get_source_address(NMLldpNeighbor *n, NMEtherAddr *address)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(n, -EINVAL);
|
||||||
|
g_return_val_if_fail(address, -EINVAL);
|
||||||
|
|
||||||
|
*address = n->source_address;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
nm_lldp_neighbor_get_destination_address(NMLldpNeighbor *n, NMEtherAddr *address)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(n, -EINVAL);
|
||||||
|
g_return_val_if_fail(address, -EINVAL);
|
||||||
|
|
||||||
|
*address = n->destination_address;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
nm_lldp_neighbor_get_raw(NMLldpNeighbor *n, const void **ret, size_t *size)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(n, -EINVAL);
|
||||||
|
g_return_val_if_fail(ret, -EINVAL);
|
||||||
|
g_return_val_if_fail(size, -EINVAL);
|
||||||
|
|
||||||
|
*ret = NM_LLDP_NEIGHBOR_RAW(n);
|
||||||
|
*size = n->raw_size;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
nm_lldp_neighbor_get_chassis_id(NMLldpNeighbor *n, uint8_t *type, const void **ret, size_t *size)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(n, -EINVAL);
|
||||||
|
g_return_val_if_fail(type, -EINVAL);
|
||||||
|
g_return_val_if_fail(ret, -EINVAL);
|
||||||
|
g_return_val_if_fail(size, -EINVAL);
|
||||||
|
|
||||||
|
nm_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 char *
|
||||||
|
format_mac_address(const void *data, size_t sz)
|
||||||
|
{
|
||||||
|
NMEtherAddr a;
|
||||||
|
|
||||||
|
nm_assert(data || sz <= 0);
|
||||||
|
|
||||||
|
if (sz != 7)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
memcpy(&a, (uint8_t *) data + 1, sizeof(a));
|
||||||
|
return nm_ether_addr_to_string_dup(&a);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
format_network_address(const void *data, size_t sz)
|
||||||
|
{
|
||||||
|
int addr_family;
|
||||||
|
NMIPAddr a;
|
||||||
|
|
||||||
|
if (sz == 6 && ((uint8_t *) data)[1] == 1) {
|
||||||
|
memcpy(&a.addr4, (uint8_t *) data + 2, sizeof(a.addr4));
|
||||||
|
addr_family = AF_INET;
|
||||||
|
} else if (sz == 18 && ((uint8_t *) data)[1] == 2) {
|
||||||
|
memcpy(&a.addr6, (uint8_t *) data + 2, sizeof(a.addr6));
|
||||||
|
addr_family = AF_INET6;
|
||||||
|
} else
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return nm_inet_ntop_dup(addr_family, &a);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
nm_lldp_neighbor_get_chassis_id_as_string(NMLldpNeighbor *n)
|
||||||
|
{
|
||||||
|
char *k;
|
||||||
|
|
||||||
|
g_return_val_if_fail(n, NULL);
|
||||||
|
|
||||||
|
if (n->chassis_id_as_string)
|
||||||
|
return n->chassis_id_as_string;
|
||||||
|
|
||||||
|
nm_assert(n->id.chassis_id_size > 0);
|
||||||
|
|
||||||
|
switch (*(uint8_t *) n->id.chassis_id) {
|
||||||
|
case NM_LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT:
|
||||||
|
case NM_LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS:
|
||||||
|
case NM_LLDP_CHASSIS_SUBTYPE_PORT_COMPONENT:
|
||||||
|
case NM_LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME:
|
||||||
|
case NM_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED:
|
||||||
|
k = nm_utils_buf_utf8safe_escape_cp((char *) n->id.chassis_id + 1,
|
||||||
|
n->id.chassis_id_size - 1,
|
||||||
|
NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL
|
||||||
|
| NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII);
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
case NM_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS:
|
||||||
|
k = format_mac_address(n->id.chassis_id, n->id.chassis_id_size);
|
||||||
|
if (k)
|
||||||
|
goto done;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NM_LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS:
|
||||||
|
k = format_network_address(n->id.chassis_id, n->id.chassis_id_size);
|
||||||
|
if (k)
|
||||||
|
goto done;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Generic fallback */
|
||||||
|
k = nm_utils_bin2hexstr_full(n->id.chassis_id, n->id.chassis_id_size, '\0', FALSE, NULL);
|
||||||
|
|
||||||
|
done:
|
||||||
|
nm_assert(k);
|
||||||
|
return (n->chassis_id_as_string = k);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
nm_lldp_neighbor_get_port_id(NMLldpNeighbor *n, uint8_t *type, const void **ret, size_t *size)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(n, -EINVAL);
|
||||||
|
g_return_val_if_fail(type, -EINVAL);
|
||||||
|
g_return_val_if_fail(ret, -EINVAL);
|
||||||
|
g_return_val_if_fail(size, -EINVAL);
|
||||||
|
|
||||||
|
nm_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
nm_lldp_neighbor_get_port_id_as_string(NMLldpNeighbor *n)
|
||||||
|
{
|
||||||
|
char *k;
|
||||||
|
|
||||||
|
g_return_val_if_fail(n, NULL);
|
||||||
|
|
||||||
|
if (n->port_id_as_string)
|
||||||
|
return n->port_id_as_string;
|
||||||
|
|
||||||
|
nm_assert(n->id.port_id_size > 0);
|
||||||
|
|
||||||
|
switch (*(uint8_t *) n->id.port_id) {
|
||||||
|
case NM_LLDP_PORT_SUBTYPE_INTERFACE_ALIAS:
|
||||||
|
case NM_LLDP_PORT_SUBTYPE_PORT_COMPONENT:
|
||||||
|
case NM_LLDP_PORT_SUBTYPE_INTERFACE_NAME:
|
||||||
|
case NM_LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED:
|
||||||
|
k = nm_utils_buf_utf8safe_escape_cp((char *) n->id.port_id + 1,
|
||||||
|
n->id.port_id_size - 1,
|
||||||
|
NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL
|
||||||
|
| NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII);
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
case NM_LLDP_PORT_SUBTYPE_MAC_ADDRESS:
|
||||||
|
k = format_mac_address(n->id.port_id, n->id.port_id_size);
|
||||||
|
if (k)
|
||||||
|
goto done;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NM_LLDP_PORT_SUBTYPE_NETWORK_ADDRESS:
|
||||||
|
k = format_network_address(n->id.port_id, n->id.port_id_size);
|
||||||
|
if (k)
|
||||||
|
goto done;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Generic fallback */
|
||||||
|
k = nm_utils_bin2hexstr_full(n->id.port_id, n->id.port_id_size, '\0', FALSE, NULL);
|
||||||
|
|
||||||
|
done:
|
||||||
|
nm_assert(k);
|
||||||
|
return (n->port_id_as_string = k);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
nm_lldp_neighbor_get_ttl(NMLldpNeighbor *n, uint16_t *ret_sec)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(n, -EINVAL);
|
||||||
|
g_return_val_if_fail(ret_sec, -EINVAL);
|
||||||
|
|
||||||
|
*ret_sec = n->ttl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
nm_lldp_neighbor_get_system_name(NMLldpNeighbor *n, const char **ret)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(n, -EINVAL);
|
||||||
|
g_return_val_if_fail(ret, -EINVAL);
|
||||||
|
|
||||||
|
if (!n->system_name)
|
||||||
|
return -ENODATA;
|
||||||
|
|
||||||
|
*ret = n->system_name;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
nm_lldp_neighbor_get_system_description(NMLldpNeighbor *n, const char **ret)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(n, -EINVAL);
|
||||||
|
g_return_val_if_fail(ret, -EINVAL);
|
||||||
|
|
||||||
|
if (!n->system_description)
|
||||||
|
return -ENODATA;
|
||||||
|
|
||||||
|
*ret = n->system_description;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
nm_lldp_neighbor_get_port_description(NMLldpNeighbor *n, const char **ret)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(n, -EINVAL);
|
||||||
|
g_return_val_if_fail(ret, -EINVAL);
|
||||||
|
|
||||||
|
if (!n->port_description)
|
||||||
|
return -ENODATA;
|
||||||
|
|
||||||
|
*ret = n->port_description;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
nm_lldp_neighbor_get_mud_url(NMLldpNeighbor *n, const char **ret)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(n, -EINVAL);
|
||||||
|
g_return_val_if_fail(ret, -EINVAL);
|
||||||
|
|
||||||
|
if (!n->mud_url)
|
||||||
|
return -ENODATA;
|
||||||
|
|
||||||
|
*ret = n->mud_url;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
nm_lldp_neighbor_get_system_capabilities(NMLldpNeighbor *n, uint16_t *ret)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(n, -EINVAL);
|
||||||
|
g_return_val_if_fail(ret, -EINVAL);
|
||||||
|
|
||||||
|
if (!n->has_capabilities)
|
||||||
|
return -ENODATA;
|
||||||
|
|
||||||
|
*ret = n->system_capabilities;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
nm_lldp_neighbor_get_enabled_capabilities(NMLldpNeighbor *n, uint16_t *ret)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(n, -EINVAL);
|
||||||
|
g_return_val_if_fail(ret, -EINVAL);
|
||||||
|
|
||||||
|
if (!n->has_capabilities)
|
||||||
|
return -ENODATA;
|
||||||
|
|
||||||
|
*ret = n->enabled_capabilities;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
nm_lldp_neighbor_tlv_rewind(NMLldpNeighbor *n)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(n, -EINVAL);
|
||||||
|
|
||||||
|
nm_assert(n->raw_size >= sizeof(struct ether_header));
|
||||||
|
|
||||||
|
n->rindex = sizeof(struct ether_header);
|
||||||
|
|
||||||
|
return n->rindex < n->raw_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
nm_lldp_neighbor_tlv_next(NMLldpNeighbor *n)
|
||||||
|
{
|
||||||
|
size_t length;
|
||||||
|
|
||||||
|
g_return_val_if_fail(n, -EINVAL);
|
||||||
|
|
||||||
|
if (n->rindex == n->raw_size) /* EOF */
|
||||||
|
return -ESPIPE;
|
||||||
|
|
||||||
|
if (n->rindex + 2 > n->raw_size) /* Truncated message */
|
||||||
|
return -EBADMSG;
|
||||||
|
|
||||||
|
length = NM_LLDP_NEIGHBOR_TLV_LENGTH(n);
|
||||||
|
if (n->rindex + 2 + length > n->raw_size)
|
||||||
|
return -EBADMSG;
|
||||||
|
|
||||||
|
n->rindex += 2 + length;
|
||||||
|
return n->rindex < n->raw_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
nm_lldp_neighbor_tlv_get_type(NMLldpNeighbor *n, uint8_t *type)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(n, -EINVAL);
|
||||||
|
g_return_val_if_fail(type, -EINVAL);
|
||||||
|
|
||||||
|
if (n->rindex == n->raw_size) /* EOF */
|
||||||
|
return -ESPIPE;
|
||||||
|
|
||||||
|
if (n->rindex + 2 > n->raw_size)
|
||||||
|
return -EBADMSG;
|
||||||
|
|
||||||
|
*type = NM_LLDP_NEIGHBOR_TLV_TYPE(n);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
nm_lldp_neighbor_tlv_is_type(NMLldpNeighbor *n, uint8_t type)
|
||||||
|
{
|
||||||
|
uint8_t k;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
g_return_val_if_fail(n, -EINVAL);
|
||||||
|
|
||||||
|
r = nm_lldp_neighbor_tlv_get_type(n, &k);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
return type == k;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
nm_lldp_neighbor_tlv_get_oui(NMLldpNeighbor *n, uint8_t oui[static 3], uint8_t *subtype)
|
||||||
|
{
|
||||||
|
const uint8_t *d;
|
||||||
|
size_t length;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
g_return_val_if_fail(n, -EINVAL);
|
||||||
|
g_return_val_if_fail(oui, -EINVAL);
|
||||||
|
g_return_val_if_fail(subtype, -EINVAL);
|
||||||
|
|
||||||
|
r = nm_lldp_neighbor_tlv_is_type(n, NM_LLDP_TYPE_PRIVATE);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
if (r == 0)
|
||||||
|
return -ENXIO;
|
||||||
|
|
||||||
|
length = NM_LLDP_NEIGHBOR_TLV_LENGTH(n);
|
||||||
|
if (length < 4)
|
||||||
|
return -EBADMSG;
|
||||||
|
|
||||||
|
if (n->rindex + 2 + length > n->raw_size)
|
||||||
|
return -EBADMSG;
|
||||||
|
|
||||||
|
d = NM_LLDP_NEIGHBOR_TLV_DATA(n);
|
||||||
|
memcpy(oui, d, 3);
|
||||||
|
*subtype = d[3];
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
nm_lldp_neighbor_tlv_is_oui(NMLldpNeighbor *n, const uint8_t oui[static 3], uint8_t subtype)
|
||||||
|
{
|
||||||
|
uint8_t k[3], st;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = nm_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
nm_lldp_neighbor_tlv_get_raw(NMLldpNeighbor *n, const void **ret, size_t *size)
|
||||||
|
{
|
||||||
|
size_t length;
|
||||||
|
|
||||||
|
g_return_val_if_fail(n, -EINVAL);
|
||||||
|
g_return_val_if_fail(ret, -EINVAL);
|
||||||
|
g_return_val_if_fail(size, -EINVAL);
|
||||||
|
|
||||||
|
/* Note that this returns the full TLV, including the TLV header */
|
||||||
|
|
||||||
|
if (n->rindex + 2 > n->raw_size)
|
||||||
|
return -EBADMSG;
|
||||||
|
|
||||||
|
length = NM_LLDP_NEIGHBOR_TLV_LENGTH(n);
|
||||||
|
if (n->rindex + 2 + length > n->raw_size)
|
||||||
|
return -EBADMSG;
|
||||||
|
|
||||||
|
*ret = (uint8_t *) NM_LLDP_NEIGHBOR_RAW(n) + n->rindex;
|
||||||
|
*size = length + 2;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
nm_lldp_neighbor_get_timestamp_usec(NMLldpNeighbor *n, gint64 *out_usec)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(n, -EINVAL);
|
||||||
|
|
||||||
|
if (n->timestamp_usec == 0)
|
||||||
|
return -ENODATA;
|
||||||
|
|
||||||
|
NM_SET_OUT(out_usec, n->timestamp_usec);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
NMLldpNeighbor *
|
||||||
|
nm_lldp_neighbor_new(size_t raw_size)
|
||||||
|
{
|
||||||
|
NMLldpNeighbor *n;
|
||||||
|
|
||||||
|
nm_assert(raw_size < SIZE_MAX - NM_ALIGN(sizeof(NMLldpNeighbor)));
|
||||||
|
|
||||||
|
n = g_malloc0(NM_ALIGN(sizeof(NMLldpNeighbor)) + raw_size);
|
||||||
|
|
||||||
|
n->raw_size = raw_size;
|
||||||
|
n->ref_count = 1;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
NMLldpNeighbor *
|
||||||
|
nm_lldp_neighbor_new_from_raw(const void *raw, size_t raw_size)
|
||||||
|
{
|
||||||
|
nm_auto(nm_lldp_neighbor_unrefp) NMLldpNeighbor *n = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
g_return_val_if_fail(raw || raw_size <= 0, NULL);
|
||||||
|
|
||||||
|
n = nm_lldp_neighbor_new(raw_size);
|
||||||
|
|
||||||
|
nm_memcpy(NM_LLDP_NEIGHBOR_RAW(n), raw, raw_size);
|
||||||
|
|
||||||
|
r = nm_lldp_neighbor_parse(n);
|
||||||
|
if (r < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return g_steal_pointer(&n);
|
||||||
|
}
|
||||||
|
|
||||||
|
NMLldpNeighbor *
|
||||||
|
nm_lldp_neighbor_ref(NMLldpNeighbor *n)
|
||||||
|
{
|
||||||
|
if (!n)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
nm_assert(n->ref_count > 0 || n->lldp_rx);
|
||||||
|
|
||||||
|
n->ref_count++;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_lldp_neighbor_free(NMLldpNeighbor *n)
|
||||||
|
{
|
||||||
|
if (!n)
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_free((gpointer) n->id.port_id);
|
||||||
|
g_free((gpointer) n->id.chassis_id);
|
||||||
|
g_free(n->port_description);
|
||||||
|
g_free(n->system_name);
|
||||||
|
g_free(n->system_description);
|
||||||
|
g_free(n->mud_url);
|
||||||
|
g_free(n->chassis_id_as_string);
|
||||||
|
g_free(n->port_id_as_string);
|
||||||
|
g_free(n);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NMLldpNeighbor *
|
||||||
|
nm_lldp_neighbor_unref(NMLldpNeighbor *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;
|
||||||
|
|
||||||
|
nm_assert(n->ref_count > 0);
|
||||||
|
n->ref_count--;
|
||||||
|
|
||||||
|
if (n->ref_count <= 0 && !n->lldp_rx)
|
||||||
|
_lldp_neighbor_free(n);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nm_lldp_neighbor_unlink(NMLldpNeighbor *n)
|
||||||
|
{
|
||||||
|
gpointer old_key;
|
||||||
|
gpointer old_val;
|
||||||
|
|
||||||
|
/* Removes the neighbor object from the LLDP object, and frees it if it also has no other reference. */
|
||||||
|
|
||||||
|
if (!n)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!n->lldp_rx)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Only remove the neighbor object from the hash table if it's in there, don't complain if it isn't. This is
|
||||||
|
* because we are used as destructor call for hashmap_clear() and thus sometimes are called to de-register
|
||||||
|
* ourselves from the hashtable and sometimes are called after we already are de-registered. */
|
||||||
|
|
||||||
|
if (g_hash_table_steal_extended(n->lldp_rx->neighbor_by_id, n, &old_key, &old_val)) {
|
||||||
|
nm_assert(NM_IN_SET(old_val, NULL, old_key));
|
||||||
|
if (old_key != n) {
|
||||||
|
/* it wasn't the right key. Add it again. */
|
||||||
|
g_hash_table_add(n->lldp_rx->neighbor_by_id, old_key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nm_prioq_remove(&n->lldp_rx->neighbor_by_expiry, n, &n->prioq_idx);
|
||||||
|
|
||||||
|
n->lldp_rx = NULL;
|
||||||
|
|
||||||
|
if (n->ref_count <= 0)
|
||||||
|
_lldp_neighbor_free(n);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
85
src/libnm-lldp/nm-lldp-neighbor.h
Normal file
85
src/libnm-lldp/nm-lldp-neighbor.h
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
|
||||||
|
#ifndef __NM_LLDP_NEIGHBOR_H__
|
||||||
|
#define __NM_LLDP_NEIGHBOR_H__
|
||||||
|
|
||||||
|
#include "nm-lldp-rx.h"
|
||||||
|
|
||||||
|
struct _NMLldpNeighbor {
|
||||||
|
NMLldpNeighborID id;
|
||||||
|
|
||||||
|
/* Neighbor objects stay around as long as they are linked into an "NMLldpRX" object or n_ref > 0. */
|
||||||
|
struct _NMLldpRX *lldp_rx;
|
||||||
|
|
||||||
|
gint64 timestamp_usec;
|
||||||
|
|
||||||
|
gint64 until_usec;
|
||||||
|
|
||||||
|
int ref_count;
|
||||||
|
unsigned prioq_idx;
|
||||||
|
|
||||||
|
NMEtherAddr source_address;
|
||||||
|
NMEtherAddr destination_address;
|
||||||
|
|
||||||
|
/* The raw packet size. The data is appended to the object, accessible via LLDP_NEIGHBOR_RAW() */
|
||||||
|
size_t raw_size;
|
||||||
|
|
||||||
|
/* The current read index for the iterative TLV interface */
|
||||||
|
size_t rindex;
|
||||||
|
|
||||||
|
/* And a couple of fields parsed out. */
|
||||||
|
bool has_ttl : 1;
|
||||||
|
bool has_capabilities : 1;
|
||||||
|
bool has_port_vlan_id : 1;
|
||||||
|
|
||||||
|
uint16_t ttl;
|
||||||
|
|
||||||
|
uint16_t system_capabilities;
|
||||||
|
uint16_t enabled_capabilities;
|
||||||
|
|
||||||
|
char *port_description;
|
||||||
|
char *system_name;
|
||||||
|
char *system_description;
|
||||||
|
char *mud_url;
|
||||||
|
|
||||||
|
uint16_t port_vlan_id;
|
||||||
|
|
||||||
|
char *chassis_id_as_string;
|
||||||
|
char *port_id_as_string;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline void *
|
||||||
|
NM_LLDP_NEIGHBOR_RAW(const NMLldpNeighbor *n)
|
||||||
|
{
|
||||||
|
return (uint8_t *) n + NM_ALIGN(sizeof(NMLldpNeighbor));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint8_t
|
||||||
|
NM_LLDP_NEIGHBOR_TLV_TYPE(const NMLldpNeighbor *n)
|
||||||
|
{
|
||||||
|
return ((uint8_t *) NM_LLDP_NEIGHBOR_RAW(n))[n->rindex] >> 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline size_t
|
||||||
|
NM_LLDP_NEIGHBOR_TLV_LENGTH(const NMLldpNeighbor *n)
|
||||||
|
{
|
||||||
|
uint8_t *p;
|
||||||
|
|
||||||
|
p = (uint8_t *) NM_LLDP_NEIGHBOR_RAW(n) + n->rindex;
|
||||||
|
return p[1] + (((size_t) (p[0] & 1)) << 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void *
|
||||||
|
NM_LLDP_NEIGHBOR_TLV_DATA(const NMLldpNeighbor *n)
|
||||||
|
{
|
||||||
|
return ((uint8_t *) NM_LLDP_NEIGHBOR_RAW(n)) + n->rindex + 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nm_lldp_neighbor_prioq_compare_func(const void *a, const void *b);
|
||||||
|
|
||||||
|
void nm_lldp_neighbor_unlink(NMLldpNeighbor *n);
|
||||||
|
NMLldpNeighbor *nm_lldp_neighbor_new(size_t raw_size);
|
||||||
|
int nm_lldp_neighbor_parse(NMLldpNeighbor *n);
|
||||||
|
void nm_lldp_neighbor_start_ttl(NMLldpNeighbor *n);
|
||||||
|
|
||||||
|
#endif /* __NM_LLDP_NEIGHBOR_H__ */
|
74
src/libnm-lldp/nm-lldp-network.c
Normal file
74
src/libnm-lldp/nm-lldp-network.c
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
|
||||||
|
#include "libnm-glib-aux/nm-default-glib-i18n-lib.h"
|
||||||
|
|
||||||
|
#include "nm-lldp-network.h"
|
||||||
|
|
||||||
|
#include <linux/filter.h>
|
||||||
|
#include <linux/if_packet.h>
|
||||||
|
#include <netinet/if_ether.h>
|
||||||
|
|
||||||
|
int
|
||||||
|
nm_lldp_network_bind_raw_socket(int ifindex)
|
||||||
|
{
|
||||||
|
static const struct sock_filter filter[] = {
|
||||||
|
BPF_STMT(BPF_LD + BPF_W + BPF_ABS,
|
||||||
|
offsetof(struct ethhdr, h_dest)), /* A <- 4 bytes of destination MAC */
|
||||||
|
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0180c200, 1, 0), /* A != 01:80:c2:00 */
|
||||||
|
BPF_STMT(BPF_RET + BPF_K, 0), /* drop packet */
|
||||||
|
BPF_STMT(BPF_LD + BPF_H + BPF_ABS,
|
||||||
|
offsetof(struct ethhdr, h_dest)
|
||||||
|
+ 4), /* A <- remaining 2 bytes of destination MAC */
|
||||||
|
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0000, 3, 0), /* A != 00:00 */
|
||||||
|
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0003, 2, 0), /* A != 00:03 */
|
||||||
|
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x000e, 1, 0), /* A != 00:0e */
|
||||||
|
BPF_STMT(BPF_RET + BPF_K, 0), /* drop packet */
|
||||||
|
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ethhdr, h_proto)), /* A <- protocol */
|
||||||
|
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, NM_ETHERTYPE_LLDP, 1, 0), /* A != NM_ETHERTYPE_LLDP */
|
||||||
|
BPF_STMT(BPF_RET + BPF_K, 0), /* drop packet */
|
||||||
|
BPF_STMT(BPF_RET + BPF_K, UINT32_MAX), /* accept packet */
|
||||||
|
};
|
||||||
|
static const struct sock_fprog fprog = {
|
||||||
|
.len = G_N_ELEMENTS(filter),
|
||||||
|
.filter = (struct sock_filter *) filter,
|
||||||
|
};
|
||||||
|
struct packet_mreq mreq = {
|
||||||
|
.mr_ifindex = ifindex,
|
||||||
|
.mr_type = PACKET_MR_MULTICAST,
|
||||||
|
.mr_alen = ETH_ALEN,
|
||||||
|
.mr_address = {0x01, 0x80, 0xC2, 0x00, 0x00, 0x00},
|
||||||
|
};
|
||||||
|
struct sockaddr_ll saddrll = {
|
||||||
|
.sll_family = AF_PACKET,
|
||||||
|
.sll_ifindex = ifindex,
|
||||||
|
};
|
||||||
|
nm_auto_close int fd = -1;
|
||||||
|
|
||||||
|
assert(ifindex > 0);
|
||||||
|
|
||||||
|
fd = socket(AF_PACKET, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, htobe16(NM_ETHERTYPE_LLDP));
|
||||||
|
if (fd < 0)
|
||||||
|
return -errno;
|
||||||
|
|
||||||
|
if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)) < 0)
|
||||||
|
return -errno;
|
||||||
|
|
||||||
|
/* customer bridge */
|
||||||
|
if (setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
|
||||||
|
return -errno;
|
||||||
|
|
||||||
|
/* non TPMR bridge */
|
||||||
|
mreq.mr_address[ETH_ALEN - 1] = 0x03;
|
||||||
|
if (setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
|
||||||
|
return -errno;
|
||||||
|
|
||||||
|
/* nearest bridge */
|
||||||
|
mreq.mr_address[ETH_ALEN - 1] = 0x0E;
|
||||||
|
if (setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
|
||||||
|
return -errno;
|
||||||
|
|
||||||
|
if (bind(fd, (const struct sockaddr *) &saddrll, sizeof(saddrll)) < 0)
|
||||||
|
return -errno;
|
||||||
|
|
||||||
|
return nm_steal_fd(&fd);
|
||||||
|
}
|
9
src/libnm-lldp/nm-lldp-network.h
Normal file
9
src/libnm-lldp/nm-lldp-network.h
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
#ifndef __NM_LLDP_NETWORK_H__
|
||||||
|
#define __NM_LLDP_NETWORK_H__
|
||||||
|
|
||||||
|
#define NM_ETHERTYPE_LLDP 0x88cc
|
||||||
|
|
||||||
|
int nm_lldp_network_bind_raw_socket(int ifindex);
|
||||||
|
|
||||||
|
#endif /* __NM_LLDP_NETWORK_H__ */
|
55
src/libnm-lldp/nm-lldp-rx-internal.h
Normal file
55
src/libnm-lldp/nm-lldp-rx-internal.h
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
|
||||||
|
#ifndef __NM_LLDP_RX_INTERNAL_H__
|
||||||
|
#define __NM_LLDP_RX_INTERNAL_H__
|
||||||
|
|
||||||
|
#include "libnm-glib-aux/nm-prioq.h"
|
||||||
|
#include "libnm-log-core/nm-logging.h"
|
||||||
|
|
||||||
|
#include "nm-lldp-rx.h"
|
||||||
|
|
||||||
|
struct _NMLldpRX {
|
||||||
|
int ref_count;
|
||||||
|
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
NMLldpRXConfig config;
|
||||||
|
|
||||||
|
GMainContext *main_context;
|
||||||
|
|
||||||
|
GSource *io_event_source;
|
||||||
|
GSource *timer_event_source;
|
||||||
|
|
||||||
|
GHashTable *neighbor_by_id;
|
||||||
|
NMPrioq neighbor_by_expiry;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
#define _NMLOG2_DOMAIN LOGD_PLATFORM
|
||||||
|
#define _NMLOG2(level, lldp_rx, ...) \
|
||||||
|
G_STMT_START \
|
||||||
|
{ \
|
||||||
|
const NMLogLevel _level = (level); \
|
||||||
|
NMLldpRX *_lldp_rx = (lldp_rx); \
|
||||||
|
\
|
||||||
|
if (_NMLOG2_ENABLED(_level)) { \
|
||||||
|
_nm_log(level, \
|
||||||
|
_NMLOG2_DOMAIN, \
|
||||||
|
0, \
|
||||||
|
_lldp_rx->config.log_ifname, \
|
||||||
|
_lldp_rx->config.log_uuid, \
|
||||||
|
"lldp-rx[" NM_HASH_OBFUSCATE_PTR_FMT \
|
||||||
|
"%s%s]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
|
||||||
|
NM_HASH_OBFUSCATE_PTR(_lldp_rx), \
|
||||||
|
NM_PRINT_FMT_QUOTED2(_lldp_rx->config.log_ifname, \
|
||||||
|
", ", \
|
||||||
|
_lldp_rx->config.log_ifname, \
|
||||||
|
"") _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
G_STMT_END
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
#endif /* __NM_LLDP_RX_INTERNAL_H__ */
|
469
src/libnm-lldp/nm-lldp-rx.c
Normal file
469
src/libnm-lldp/nm-lldp-rx.c
Normal file
@@ -0,0 +1,469 @@
|
|||||||
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
|
||||||
|
#include "libnm-glib-aux/nm-default-glib-i18n-lib.h"
|
||||||
|
|
||||||
|
#include "nm-lldp-rx.h"
|
||||||
|
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <linux/sockios.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
|
||||||
|
#include "libnm-glib-aux/nm-io-utils.h"
|
||||||
|
#include "libnm-glib-aux/nm-time-utils.h"
|
||||||
|
#include "nm-lldp-network.h"
|
||||||
|
#include "nm-lldp-neighbor.h"
|
||||||
|
#include "nm-lldp-rx-internal.h"
|
||||||
|
|
||||||
|
#define LLDP_DEFAULT_NEIGHBORS_MAX 128U
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
static void lldp_rx_start_timer(NMLldpRX *lldp_rx, NMLldpNeighbor *neighbor);
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
NM_UTILS_LOOKUP_STR_DEFINE(nm_lldp_rx_event_to_string,
|
||||||
|
NMLldpRXEvent,
|
||||||
|
NM_UTILS_LOOKUP_DEFAULT_WARN("<unknown>"),
|
||||||
|
NM_UTILS_LOOKUP_STR_ITEM(NM_LLDP_RX_EVENT_ADDED, "added"),
|
||||||
|
NM_UTILS_LOOKUP_STR_ITEM(NM_LLDP_RX_EVENT_REMOVED, "removed"),
|
||||||
|
NM_UTILS_LOOKUP_STR_ITEM(NM_LLDP_RX_EVENT_UPDATED, "updated"),
|
||||||
|
NM_UTILS_LOOKUP_STR_ITEM(NM_LLDP_RX_EVENT_REFRESHED, "refreshed"),
|
||||||
|
NM_UTILS_LOOKUP_ITEM_IGNORE_OTHER());
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
#define nm_assert_is_lldp_rx(lldp_rx) \
|
||||||
|
G_STMT_START \
|
||||||
|
{ \
|
||||||
|
NMLldpRX *_lldp_rx = (lldp_rx); \
|
||||||
|
\
|
||||||
|
nm_assert(_lldp_rx); \
|
||||||
|
nm_assert(_lldp_rx->ref_count > 0); \
|
||||||
|
} \
|
||||||
|
G_STMT_END
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
/* This needs to be first. Check nm_lldp_rx_get_id(). */
|
||||||
|
G_STATIC_ASSERT(G_STRUCT_OFFSET(NMLldpNeighbor, id) == 0);
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
static void
|
||||||
|
lldp_rx_callback(NMLldpRX *lldp_rx, NMLldpRXEvent event, NMLldpNeighbor *n)
|
||||||
|
{
|
||||||
|
nm_assert_is_lldp_rx(lldp_rx);
|
||||||
|
nm_assert(event >= 0 && event < _NM_LLDP_RX_EVENT_MAX);
|
||||||
|
|
||||||
|
_LOG2D(lldp_rx, "invoking callback for '%s' event", nm_lldp_rx_event_to_string(event));
|
||||||
|
lldp_rx->config.callback(lldp_rx, event, n, lldp_rx->config.userdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
lldp_rx_make_space(NMLldpRX *lldp_rx, gboolean flush, size_t extra)
|
||||||
|
{
|
||||||
|
nm_auto(nm_lldp_rx_unrefp) NMLldpRX *lldp_rx_alive = NULL;
|
||||||
|
gint64 now_usec = 0;
|
||||||
|
gboolean changed = FALSE;
|
||||||
|
size_t max;
|
||||||
|
|
||||||
|
/* Remove all entries that are past their TTL, and more until at least the specified number of extra entries
|
||||||
|
* are free. */
|
||||||
|
|
||||||
|
max = (!flush && lldp_rx->config.neighbors_max > extra)
|
||||||
|
? (lldp_rx->config.neighbors_max - extra)
|
||||||
|
: 0u;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
NMLldpNeighbor *n;
|
||||||
|
|
||||||
|
nm_assert(g_hash_table_size(lldp_rx->neighbor_by_id)
|
||||||
|
== nm_prioq_size(&lldp_rx->neighbor_by_expiry));
|
||||||
|
|
||||||
|
n = nm_prioq_peek(&lldp_rx->neighbor_by_expiry);
|
||||||
|
if (!n)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (nm_prioq_size(&lldp_rx->neighbor_by_expiry) > max) {
|
||||||
|
/* drop it. */
|
||||||
|
} else {
|
||||||
|
if (n->until_usec > nm_utils_get_monotonic_timestamp_usec_cached(&now_usec))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flush) {
|
||||||
|
changed = TRUE;
|
||||||
|
nm_lldp_neighbor_unlink(n);
|
||||||
|
} else {
|
||||||
|
nm_auto(nm_lldp_neighbor_unrefp) NMLldpNeighbor *n_alive = NULL;
|
||||||
|
|
||||||
|
if (!changed) {
|
||||||
|
lldp_rx_alive = nm_lldp_rx_ref(lldp_rx);
|
||||||
|
changed = TRUE;
|
||||||
|
}
|
||||||
|
n_alive = nm_lldp_neighbor_ref(n);
|
||||||
|
nm_lldp_neighbor_unlink(n);
|
||||||
|
lldp_rx_callback(lldp_rx, NM_LLDP_RX_EVENT_REMOVED, n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
lldp_rx_keep_neighbor(NMLldpRX *lldp_rx, NMLldpNeighbor *n)
|
||||||
|
{
|
||||||
|
nm_assert_is_lldp_rx(lldp_rx);
|
||||||
|
nm_assert(n);
|
||||||
|
|
||||||
|
/* Don't keep data with a zero TTL */
|
||||||
|
if (n->ttl <= 0)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* Filter out data from the filter address */
|
||||||
|
if (!nm_ether_addr_is_zero(&lldp_rx->config.filter_address)
|
||||||
|
&& nm_ether_addr_equal(&lldp_rx->config.filter_address, &n->source_address))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* Only add if the neighbor has a capability we are interested in. Note that we also store all neighbors with
|
||||||
|
* no caps field set. */
|
||||||
|
if (n->has_capabilities && (n->enabled_capabilities & lldp_rx->config.capability_mask) == 0)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* Keep everything else */
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
lldp_rx_add_neighbor(NMLldpRX *lldp_rx, NMLldpNeighbor *n)
|
||||||
|
{
|
||||||
|
nm_auto(nm_lldp_neighbor_unrefp) NMLldpNeighbor *old_alive = NULL;
|
||||||
|
NMLldpNeighbor *old;
|
||||||
|
gboolean keep;
|
||||||
|
|
||||||
|
nm_assert_is_lldp_rx(lldp_rx);
|
||||||
|
nm_assert(n);
|
||||||
|
nm_assert(!n->lldp_rx);
|
||||||
|
|
||||||
|
keep = lldp_rx_keep_neighbor(lldp_rx, n);
|
||||||
|
|
||||||
|
/* First retrieve the old entry for this MSAP */
|
||||||
|
old = g_hash_table_lookup(lldp_rx->neighbor_by_id, n);
|
||||||
|
if (old) {
|
||||||
|
old_alive = nm_lldp_neighbor_ref(old);
|
||||||
|
|
||||||
|
if (!keep) {
|
||||||
|
nm_lldp_neighbor_unlink(old);
|
||||||
|
lldp_rx_callback(lldp_rx, NM_LLDP_RX_EVENT_REMOVED, old);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nm_lldp_neighbor_equal(n, old)) {
|
||||||
|
/* Is this equal, then restart the TTL counter, but don't do anything else. */
|
||||||
|
old->timestamp_usec = n->timestamp_usec;
|
||||||
|
lldp_rx_start_timer(lldp_rx, old);
|
||||||
|
lldp_rx_callback(lldp_rx, NM_LLDP_RX_EVENT_REFRESHED, old);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Data changed, remove the old entry, and add a new one */
|
||||||
|
nm_lldp_neighbor_unlink(old);
|
||||||
|
|
||||||
|
} else if (!keep)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Then, make room for at least one new neighbor */
|
||||||
|
lldp_rx_make_space(lldp_rx, FALSE, 1);
|
||||||
|
|
||||||
|
if (!g_hash_table_add(lldp_rx->neighbor_by_id, n))
|
||||||
|
nm_assert_not_reached();
|
||||||
|
|
||||||
|
nm_prioq_put(&lldp_rx->neighbor_by_expiry, n, &n->prioq_idx);
|
||||||
|
|
||||||
|
n->lldp_rx = lldp_rx;
|
||||||
|
|
||||||
|
lldp_rx_start_timer(lldp_rx, n);
|
||||||
|
lldp_rx_callback(lldp_rx, old ? NM_LLDP_RX_EVENT_UPDATED : NM_LLDP_RX_EVENT_ADDED, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
lldp_rx_receive_datagram(int fd, GIOCondition condition, gpointer user_data)
|
||||||
|
|
||||||
|
{
|
||||||
|
NMLldpRX *lldp_rx = user_data;
|
||||||
|
nm_auto(nm_lldp_neighbor_unrefp) NMLldpNeighbor *n = NULL;
|
||||||
|
ssize_t space;
|
||||||
|
ssize_t length;
|
||||||
|
struct timespec ts;
|
||||||
|
gint64 ts_usec;
|
||||||
|
gint64 now_usec;
|
||||||
|
gint64 now_usec_rt;
|
||||||
|
gint64 now_usec_bt;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
nm_assert_is_lldp_rx(lldp_rx);
|
||||||
|
nm_assert(lldp_rx->fd == fd);
|
||||||
|
|
||||||
|
_LOG2T(lldp_rx, "fd ready");
|
||||||
|
|
||||||
|
space = nm_fd_next_datagram_size(lldp_rx->fd);
|
||||||
|
if (space < 0) {
|
||||||
|
if (!NM_ERRNO_IS_TRANSIENT(space) && !NM_ERRNO_IS_DISCONNECT(space)) {
|
||||||
|
_LOG2D(lldp_rx,
|
||||||
|
"Failed to determine datagram size to read, ignoring: %s",
|
||||||
|
nm_strerror_native(-space));
|
||||||
|
}
|
||||||
|
return G_SOURCE_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
n = nm_lldp_neighbor_new(space);
|
||||||
|
|
||||||
|
length = recv(lldp_rx->fd, NM_LLDP_NEIGHBOR_RAW(n), n->raw_size, MSG_DONTWAIT);
|
||||||
|
if (length < 0) {
|
||||||
|
if (!NM_ERRNO_IS_TRANSIENT(errno) && !NM_ERRNO_IS_DISCONNECT(errno)) {
|
||||||
|
_LOG2D(lldp_rx,
|
||||||
|
"Failed to read LLDP datagram, ignoring: %s",
|
||||||
|
nm_strerror_native(errno));
|
||||||
|
}
|
||||||
|
return G_SOURCE_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((size_t) length != n->raw_size) {
|
||||||
|
_LOG2D(lldp_rx, "Packet size mismatch, ignoring");
|
||||||
|
return G_SOURCE_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Try to get the timestamp of this packet if it is known */
|
||||||
|
if (ioctl(lldp_rx->fd, SIOCGSTAMPNS, &ts) >= 0
|
||||||
|
&& (ts_usec = nm_utils_timespec_to_usec(&ts)) < G_MAXINT64
|
||||||
|
&& (now_usec_bt = nm_utils_clock_gettime_usec(CLOCK_BOOTTIME)) >= 0
|
||||||
|
&& (now_usec_rt = nm_utils_clock_gettime_usec(CLOCK_REALTIME)) >= 0) {
|
||||||
|
gint64 t;
|
||||||
|
|
||||||
|
now_usec = nm_utils_monotonic_timestamp_from_boottime(now_usec_bt, 1000);
|
||||||
|
ts_usec = nm_time_map_clock(ts_usec, now_usec_rt, now_usec_bt);
|
||||||
|
|
||||||
|
t = now_usec;
|
||||||
|
if (ts_usec >= 0) {
|
||||||
|
ts_usec = nm_utils_monotonic_timestamp_from_boottime(ts_usec, 1000);
|
||||||
|
if (ts_usec > NM_UTILS_USEC_PER_SEC && ts_usec < now_usec)
|
||||||
|
t = ts_usec;
|
||||||
|
}
|
||||||
|
|
||||||
|
n->timestamp_usec = t;
|
||||||
|
} else
|
||||||
|
n->timestamp_usec = nm_utils_get_monotonic_timestamp_usec();
|
||||||
|
|
||||||
|
r = nm_lldp_neighbor_parse(n);
|
||||||
|
if (r < 0) {
|
||||||
|
_LOG2D(lldp_rx, "Failure parsing invalid LLDP datagram.");
|
||||||
|
return G_SOURCE_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
_LOG2D(lldp_rx, "Successfully processed LLDP datagram.");
|
||||||
|
lldp_rx_add_neighbor(lldp_rx, n);
|
||||||
|
|
||||||
|
return G_SOURCE_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
lldp_rx_reset(NMLldpRX *lldp_rx)
|
||||||
|
{
|
||||||
|
nm_clear_g_source_inst(&lldp_rx->timer_event_source);
|
||||||
|
nm_clear_g_source_inst(&lldp_rx->io_event_source);
|
||||||
|
nm_clear_fd(&lldp_rx->fd);
|
||||||
|
|
||||||
|
lldp_rx_make_space(lldp_rx, TRUE, 0);
|
||||||
|
|
||||||
|
nm_assert(g_hash_table_size(lldp_rx->neighbor_by_id) == 0);
|
||||||
|
nm_assert(nm_prioq_size(&lldp_rx->neighbor_by_expiry) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
nm_lldp_rx_is_running(NMLldpRX *lldp_rx)
|
||||||
|
{
|
||||||
|
if (!lldp_rx)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
return lldp_rx->fd >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
nm_lldp_rx_start(NMLldpRX *lldp_rx)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
|
||||||
|
g_return_val_if_fail(lldp_rx, -EINVAL);
|
||||||
|
nm_assert(lldp_rx->main_context);
|
||||||
|
nm_assert(lldp_rx->config.ifindex > 0);
|
||||||
|
|
||||||
|
if (nm_lldp_rx_is_running(lldp_rx))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
nm_assert(!lldp_rx->io_event_source);
|
||||||
|
|
||||||
|
r = nm_lldp_network_bind_raw_socket(lldp_rx->config.ifindex);
|
||||||
|
if (r < 0) {
|
||||||
|
_LOG2D(lldp_rx, "start failed to bind socket (%s)", nm_strerror_native(-r));
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
lldp_rx->fd = r;
|
||||||
|
|
||||||
|
lldp_rx->io_event_source = nm_g_source_attach(nm_g_unix_fd_source_new(lldp_rx->fd,
|
||||||
|
G_IO_IN,
|
||||||
|
G_PRIORITY_DEFAULT,
|
||||||
|
lldp_rx_receive_datagram,
|
||||||
|
lldp_rx,
|
||||||
|
NULL),
|
||||||
|
lldp_rx->main_context);
|
||||||
|
|
||||||
|
_LOG2D(lldp_rx, "started (fd %d)", lldp_rx->fd);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
nm_lldp_rx_stop(NMLldpRX *lldp_rx)
|
||||||
|
{
|
||||||
|
if (!nm_lldp_rx_is_running(lldp_rx))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
_LOG2D(lldp_rx, "stopping");
|
||||||
|
|
||||||
|
lldp_rx_reset(lldp_rx);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
on_timer_event(gpointer user_data)
|
||||||
|
{
|
||||||
|
NMLldpRX *lldp_rx = user_data;
|
||||||
|
|
||||||
|
lldp_rx_make_space(lldp_rx, FALSE, 0);
|
||||||
|
lldp_rx_start_timer(lldp_rx, NULL);
|
||||||
|
return G_SOURCE_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
lldp_rx_start_timer(NMLldpRX *lldp_rx, NMLldpNeighbor *neighbor)
|
||||||
|
{
|
||||||
|
NMLldpNeighbor *n;
|
||||||
|
gint64 timeout_msec;
|
||||||
|
|
||||||
|
nm_assert_is_lldp_rx(lldp_rx);
|
||||||
|
|
||||||
|
nm_clear_g_source_inst(&lldp_rx->timer_event_source);
|
||||||
|
|
||||||
|
if (neighbor)
|
||||||
|
nm_lldp_neighbor_start_ttl(neighbor);
|
||||||
|
|
||||||
|
n = nm_prioq_peek(&lldp_rx->neighbor_by_expiry);
|
||||||
|
if (!n)
|
||||||
|
return;
|
||||||
|
|
||||||
|
timeout_msec = (n->until_usec / 1000) - nm_utils_get_monotonic_timestamp_msec();
|
||||||
|
|
||||||
|
lldp_rx->timer_event_source =
|
||||||
|
nm_g_source_attach(nm_g_timeout_source_new(NM_CLAMP(timeout_msec, 0, G_MAXUINT),
|
||||||
|
G_PRIORITY_DEFAULT,
|
||||||
|
on_timer_event,
|
||||||
|
lldp_rx,
|
||||||
|
NULL),
|
||||||
|
lldp_rx->main_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
neighbor_compare_func(gconstpointer p_a, gconstpointer p_b, gpointer user_data)
|
||||||
|
{
|
||||||
|
NMLldpNeighbor *const *a = p_a;
|
||||||
|
NMLldpNeighbor *const *b = p_b;
|
||||||
|
|
||||||
|
nm_assert(a);
|
||||||
|
nm_assert(b);
|
||||||
|
nm_assert(*a);
|
||||||
|
nm_assert(*b);
|
||||||
|
|
||||||
|
return nm_lldp_neighbor_id_cmp(&(*a)->id, &(*b)->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
NMLldpNeighbor **
|
||||||
|
nm_lldp_rx_get_neighbors(NMLldpRX *lldp_rx, guint *out_len)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(lldp_rx, NULL);
|
||||||
|
|
||||||
|
return (NMLldpNeighbor **)
|
||||||
|
nm_utils_hash_keys_to_array(lldp_rx->neighbor_by_id, neighbor_compare_func, NULL, out_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
NMLldpRX *
|
||||||
|
nm_lldp_rx_new(const NMLldpRXConfig *config)
|
||||||
|
{
|
||||||
|
NMLldpRX *lldp_rx;
|
||||||
|
|
||||||
|
nm_assert(config);
|
||||||
|
nm_assert(config->ifindex > 0);
|
||||||
|
nm_assert(config->callback);
|
||||||
|
|
||||||
|
/* This needs to be first, see neighbor_by_id hash. */
|
||||||
|
G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(NMLldpNeighbor, id) == 0);
|
||||||
|
|
||||||
|
lldp_rx = g_slice_new(NMLldpRX);
|
||||||
|
*lldp_rx = (NMLldpRX){
|
||||||
|
.ref_count = 1,
|
||||||
|
.fd = -1,
|
||||||
|
.main_context = g_main_context_ref_thread_default(),
|
||||||
|
.config = *config,
|
||||||
|
.neighbor_by_id = g_hash_table_new((GHashFunc) nm_lldp_neighbor_id_hash,
|
||||||
|
(GEqualFunc) nm_lldp_neighbor_id_equal),
|
||||||
|
};
|
||||||
|
lldp_rx->config.log_ifname = g_strdup(lldp_rx->config.log_ifname);
|
||||||
|
lldp_rx->config.log_uuid = g_strdup(lldp_rx->config.log_uuid);
|
||||||
|
if (lldp_rx->config.neighbors_max == 0)
|
||||||
|
lldp_rx->config.neighbors_max = LLDP_DEFAULT_NEIGHBORS_MAX;
|
||||||
|
if (!lldp_rx->config.has_capability_mask && lldp_rx->config.capability_mask == 0)
|
||||||
|
lldp_rx->config.capability_mask = UINT16_MAX;
|
||||||
|
|
||||||
|
nm_prioq_init(&lldp_rx->neighbor_by_expiry, (GCompareFunc) nm_lldp_neighbor_prioq_compare_func);
|
||||||
|
|
||||||
|
return lldp_rx;
|
||||||
|
}
|
||||||
|
|
||||||
|
NMLldpRX *
|
||||||
|
nm_lldp_rx_ref(NMLldpRX *lldp_rx)
|
||||||
|
{
|
||||||
|
if (!lldp_rx)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
nm_assert_is_lldp_rx(lldp_rx);
|
||||||
|
nm_assert(lldp_rx->ref_count < G_MAXINT);
|
||||||
|
|
||||||
|
lldp_rx->ref_count++;
|
||||||
|
return lldp_rx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nm_lldp_rx_unref(NMLldpRX *lldp_rx)
|
||||||
|
{
|
||||||
|
if (!lldp_rx)
|
||||||
|
return;
|
||||||
|
|
||||||
|
nm_assert_is_lldp_rx(lldp_rx);
|
||||||
|
|
||||||
|
if (--lldp_rx->ref_count > 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
lldp_rx_reset(lldp_rx);
|
||||||
|
|
||||||
|
g_hash_table_unref(lldp_rx->neighbor_by_id);
|
||||||
|
nm_prioq_destroy(&lldp_rx->neighbor_by_expiry);
|
||||||
|
|
||||||
|
free((char *) lldp_rx->config.log_ifname);
|
||||||
|
free((char *) lldp_rx->config.log_uuid);
|
||||||
|
|
||||||
|
g_main_context_unref(lldp_rx->main_context);
|
||||||
|
|
||||||
|
nm_g_slice_free(lldp_rx);
|
||||||
|
}
|
124
src/libnm-lldp/nm-lldp-rx.h
Normal file
124
src/libnm-lldp/nm-lldp-rx.h
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
#ifndef __NM_LLDP_RX_H__
|
||||||
|
#define __NM_LLDP_RX_H__
|
||||||
|
|
||||||
|
#include "nm-lldp.h"
|
||||||
|
|
||||||
|
typedef struct _NMLldpRX NMLldpRX;
|
||||||
|
typedef struct _NMLldpNeighbor NMLldpNeighbor;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
NM_LLDP_RX_EVENT_ADDED,
|
||||||
|
NM_LLDP_RX_EVENT_REMOVED,
|
||||||
|
NM_LLDP_RX_EVENT_UPDATED,
|
||||||
|
NM_LLDP_RX_EVENT_REFRESHED,
|
||||||
|
_NM_LLDP_RX_EVENT_MAX,
|
||||||
|
_NM_LLDP_RX_EVENT_INVALID = -EINVAL,
|
||||||
|
} NMLldpRXEvent;
|
||||||
|
|
||||||
|
const char *nm_lldp_rx_event_to_string(NMLldpRXEvent e) _nm_pure;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
/* The spec calls this an "MSAP identifier" */
|
||||||
|
const void *chassis_id;
|
||||||
|
size_t chassis_id_size;
|
||||||
|
|
||||||
|
const void *port_id;
|
||||||
|
size_t port_id_size;
|
||||||
|
} NMLldpNeighborID;
|
||||||
|
|
||||||
|
guint nm_lldp_neighbor_id_hash(const NMLldpNeighborID *id);
|
||||||
|
int nm_lldp_neighbor_id_cmp(const NMLldpNeighborID *x, const NMLldpNeighborID *y);
|
||||||
|
gboolean nm_lldp_neighbor_id_equal(const NMLldpNeighborID *x, const NMLldpNeighborID *y);
|
||||||
|
|
||||||
|
typedef void (*NMLldpRXCallback)(NMLldpRX *lldp_rx,
|
||||||
|
NMLldpRXEvent event,
|
||||||
|
NMLldpNeighbor *n,
|
||||||
|
void *userdata);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int ifindex;
|
||||||
|
guint neighbors_max;
|
||||||
|
const char *log_ifname;
|
||||||
|
const char *log_uuid;
|
||||||
|
NMLldpRXCallback callback;
|
||||||
|
void *userdata;
|
||||||
|
|
||||||
|
/* In order to deal nicely with bridges that send back our own packets, allow one address to be filtered, so
|
||||||
|
* that our own can be filtered out here. */
|
||||||
|
NMEtherAddr filter_address;
|
||||||
|
|
||||||
|
uint16_t capability_mask;
|
||||||
|
bool has_capability_mask : 1;
|
||||||
|
} NMLldpRXConfig;
|
||||||
|
|
||||||
|
NMLldpRX *nm_lldp_rx_new(const NMLldpRXConfig *config);
|
||||||
|
NMLldpRX *nm_lldp_rx_ref(NMLldpRX *lldp_rx);
|
||||||
|
void nm_lldp_rx_unref(NMLldpRX *lldp_rx);
|
||||||
|
|
||||||
|
NM_AUTO_DEFINE_FCN(NMLldpRX *, nm_lldp_rx_unrefp, nm_lldp_rx_unref);
|
||||||
|
|
||||||
|
int nm_lldp_rx_start(NMLldpRX *lldp_rx);
|
||||||
|
int nm_lldp_rx_stop(NMLldpRX *lldp_rx);
|
||||||
|
gboolean nm_lldp_rx_is_running(NMLldpRX *lldp_rx);
|
||||||
|
|
||||||
|
/* Controls how much and what to store in the neighbors database */
|
||||||
|
|
||||||
|
NMLldpNeighbor **nm_lldp_rx_get_neighbors(NMLldpRX *lldp_rx, guint *out_len);
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
NMLldpNeighbor *nm_lldp_neighbor_new_from_raw(const void *raw, size_t raw_size);
|
||||||
|
|
||||||
|
NMLldpNeighbor *nm_lldp_neighbor_ref(NMLldpNeighbor *n);
|
||||||
|
NMLldpNeighbor *nm_lldp_neighbor_unref(NMLldpNeighbor *n);
|
||||||
|
|
||||||
|
NM_AUTO_DEFINE_FCN(NMLldpNeighbor *, nm_lldp_neighbor_unrefp, nm_lldp_neighbor_unref);
|
||||||
|
|
||||||
|
int nm_lldp_neighbor_cmp(const NMLldpNeighbor *a, const NMLldpNeighbor *b);
|
||||||
|
|
||||||
|
static inline gboolean
|
||||||
|
nm_lldp_neighbor_equal(const NMLldpNeighbor *a, const NMLldpNeighbor *b)
|
||||||
|
{
|
||||||
|
return nm_lldp_neighbor_cmp(a, b) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
static inline const NMLldpNeighborID *
|
||||||
|
nm_lldp_neighbor_get_id(NMLldpNeighbor *lldp_neigbor)
|
||||||
|
{
|
||||||
|
return (const NMLldpNeighborID *) ((gconstpointer) lldp_neigbor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Access to LLDP frame metadata */
|
||||||
|
int nm_lldp_neighbor_get_source_address(NMLldpNeighbor *n, NMEtherAddr *address);
|
||||||
|
int nm_lldp_neighbor_get_destination_address(NMLldpNeighbor *n, NMEtherAddr *address);
|
||||||
|
int nm_lldp_neighbor_get_timestamp_usec(NMLldpNeighbor *n, gint64 *out_usec);
|
||||||
|
int nm_lldp_neighbor_get_raw(NMLldpNeighbor *n, const void **ret, size_t *size);
|
||||||
|
|
||||||
|
/* High-level, direct, parsed out field access. These fields exist at most once, hence may be queried directly. */
|
||||||
|
int
|
||||||
|
nm_lldp_neighbor_get_chassis_id(NMLldpNeighbor *n, uint8_t *type, const void **ret, size_t *size);
|
||||||
|
const char *nm_lldp_neighbor_get_chassis_id_as_string(NMLldpNeighbor *n);
|
||||||
|
int nm_lldp_neighbor_get_port_id(NMLldpNeighbor *n, uint8_t *type, const void **ret, size_t *size);
|
||||||
|
const char *nm_lldp_neighbor_get_port_id_as_string(NMLldpNeighbor *n);
|
||||||
|
int nm_lldp_neighbor_get_ttl(NMLldpNeighbor *n, uint16_t *ret_sec);
|
||||||
|
int nm_lldp_neighbor_get_system_name(NMLldpNeighbor *n, const char **ret);
|
||||||
|
int nm_lldp_neighbor_get_system_description(NMLldpNeighbor *n, const char **ret);
|
||||||
|
int nm_lldp_neighbor_get_port_description(NMLldpNeighbor *n, const char **ret);
|
||||||
|
int nm_lldp_neighbor_get_mud_url(NMLldpNeighbor *n, const char **ret);
|
||||||
|
int nm_lldp_neighbor_get_system_capabilities(NMLldpNeighbor *n, uint16_t *ret);
|
||||||
|
int nm_lldp_neighbor_get_enabled_capabilities(NMLldpNeighbor *n, uint16_t *ret);
|
||||||
|
|
||||||
|
/* Low-level, iterative TLV access. This is for everything else, it iteratively goes through all available TLVs
|
||||||
|
* (including the ones covered with the calls above), and allows multiple TLVs for the same fields. */
|
||||||
|
int nm_lldp_neighbor_tlv_rewind(NMLldpNeighbor *n);
|
||||||
|
int nm_lldp_neighbor_tlv_next(NMLldpNeighbor *n);
|
||||||
|
int nm_lldp_neighbor_tlv_get_type(NMLldpNeighbor *n, uint8_t *type);
|
||||||
|
int nm_lldp_neighbor_tlv_is_type(NMLldpNeighbor *n, uint8_t type);
|
||||||
|
int nm_lldp_neighbor_tlv_get_oui(NMLldpNeighbor *n, uint8_t oui[static 3], uint8_t *subtype);
|
||||||
|
int nm_lldp_neighbor_tlv_is_oui(NMLldpNeighbor *n, const uint8_t oui[static 3], uint8_t subtype);
|
||||||
|
int nm_lldp_neighbor_tlv_get_raw(NMLldpNeighbor *n, const void **ret, size_t *size);
|
||||||
|
|
||||||
|
#endif /* __NM_LLDP_RX_H__ */
|
110
src/libnm-lldp/nm-lldp.h
Normal file
110
src/libnm-lldp/nm-lldp.h
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
#ifndef __NM_LLDP_H__
|
||||||
|
#define __NM_LLDP_H__
|
||||||
|
|
||||||
|
/* IEEE 802.1AB-2009 Clause 8: TLV Types */
|
||||||
|
enum {
|
||||||
|
NM_LLDP_TYPE_END = 0,
|
||||||
|
NM_LLDP_TYPE_CHASSIS_ID = 1,
|
||||||
|
NM_LLDP_TYPE_PORT_ID = 2,
|
||||||
|
NM_LLDP_TYPE_TTL = 3,
|
||||||
|
NM_LLDP_TYPE_PORT_DESCRIPTION = 4,
|
||||||
|
NM_LLDP_TYPE_SYSTEM_NAME = 5,
|
||||||
|
NM_LLDP_TYPE_SYSTEM_DESCRIPTION = 6,
|
||||||
|
NM_LLDP_TYPE_SYSTEM_CAPABILITIES = 7,
|
||||||
|
NM_LLDP_TYPE_MGMT_ADDRESS = 8,
|
||||||
|
NM_LLDP_TYPE_PRIVATE = 127
|
||||||
|
};
|
||||||
|
|
||||||
|
/* IEEE 802.1AB-2009 Clause 8.5.2: Chassis subtypes */
|
||||||
|
enum {
|
||||||
|
NM_LLDP_CHASSIS_SUBTYPE_RESERVED = 0,
|
||||||
|
NM_LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT = 1,
|
||||||
|
NM_LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS = 2,
|
||||||
|
NM_LLDP_CHASSIS_SUBTYPE_PORT_COMPONENT = 3,
|
||||||
|
NM_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS = 4,
|
||||||
|
NM_LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS = 5,
|
||||||
|
NM_LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME = 6,
|
||||||
|
NM_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED = 7
|
||||||
|
};
|
||||||
|
|
||||||
|
/* IEEE 802.1AB-2009 Clause 8.5.3: Port subtype */
|
||||||
|
enum {
|
||||||
|
NM_LLDP_PORT_SUBTYPE_RESERVED = 0,
|
||||||
|
NM_LLDP_PORT_SUBTYPE_INTERFACE_ALIAS = 1,
|
||||||
|
NM_LLDP_PORT_SUBTYPE_PORT_COMPONENT = 2,
|
||||||
|
NM_LLDP_PORT_SUBTYPE_MAC_ADDRESS = 3,
|
||||||
|
NM_LLDP_PORT_SUBTYPE_NETWORK_ADDRESS = 4,
|
||||||
|
NM_LLDP_PORT_SUBTYPE_INTERFACE_NAME = 5,
|
||||||
|
NM_LLDP_PORT_SUBTYPE_AGENT_CIRCUIT_ID = 6,
|
||||||
|
NM_LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED = 7
|
||||||
|
};
|
||||||
|
|
||||||
|
/* IEEE 802.1AB-2009 Clause 8.5.8: System capabilities */
|
||||||
|
enum {
|
||||||
|
NM_LLDP_SYSTEM_CAPABILITIES_OTHER = 1 << 0,
|
||||||
|
NM_LLDP_SYSTEM_CAPABILITIES_REPEATER = 1 << 1,
|
||||||
|
NM_LLDP_SYSTEM_CAPABILITIES_BRIDGE = 1 << 2,
|
||||||
|
NM_LLDP_SYSTEM_CAPABILITIES_WLAN_AP = 1 << 3,
|
||||||
|
NM_LLDP_SYSTEM_CAPABILITIES_ROUTER = 1 << 4,
|
||||||
|
NM_LLDP_SYSTEM_CAPABILITIES_PHONE = 1 << 5,
|
||||||
|
NM_LLDP_SYSTEM_CAPABILITIES_DOCSIS = 1 << 6,
|
||||||
|
NM_LLDP_SYSTEM_CAPABILITIES_STATION = 1 << 7,
|
||||||
|
NM_LLDP_SYSTEM_CAPABILITIES_CVLAN = 1 << 8,
|
||||||
|
NM_LLDP_SYSTEM_CAPABILITIES_SVLAN = 1 << 9,
|
||||||
|
NM_LLDP_SYSTEM_CAPABILITIES_TPMR = 1 << 10
|
||||||
|
};
|
||||||
|
|
||||||
|
#define NM_LLDP_SYSTEM_CAPABILITIES_ALL UINT16_MAX
|
||||||
|
|
||||||
|
#define NM_LLDP_SYSTEM_CAPABILITIES_ALL_ROUTERS \
|
||||||
|
((uint16_t) (NM_LLDP_SYSTEM_CAPABILITIES_REPEATER | NM_LLDP_SYSTEM_CAPABILITIES_BRIDGE \
|
||||||
|
| NM_LLDP_SYSTEM_CAPABILITIES_WLAN_AP | NM_LLDP_SYSTEM_CAPABILITIES_ROUTER \
|
||||||
|
| NM_LLDP_SYSTEM_CAPABILITIES_DOCSIS | NM_LLDP_SYSTEM_CAPABILITIES_CVLAN \
|
||||||
|
| NM_LLDP_SYSTEM_CAPABILITIES_SVLAN | NM_LLDP_SYSTEM_CAPABILITIES_TPMR))
|
||||||
|
|
||||||
|
#define NM_LLDP_OUI_802_1 \
|
||||||
|
(const uint8_t[]) \
|
||||||
|
{ \
|
||||||
|
0x00, 0x80, 0xc2 \
|
||||||
|
}
|
||||||
|
#define NM_LLDP_OUI_802_3 \
|
||||||
|
(const uint8_t[]) \
|
||||||
|
{ \
|
||||||
|
0x00, 0x12, 0x0f \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define _SD_LLDP_OUI_IANA 0x00, 0x00, 0x5E
|
||||||
|
#define NM_LLDP_OUI_IANA \
|
||||||
|
(const uint8_t[]) \
|
||||||
|
{ \
|
||||||
|
_SD_LLDP_OUI_IANA \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define NM_LLDP_OUI_IANA_SUBTYPE_MUD 0x01
|
||||||
|
#define NM_LLDP_OUI_IANA_MUD \
|
||||||
|
(const uint8_t[]) \
|
||||||
|
{ \
|
||||||
|
_SD_LLDP_OUI_IANA, NM_LLDP_OUI_IANA_SUBTYPE_MUD \
|
||||||
|
}
|
||||||
|
|
||||||
|
/* IEEE 802.1AB-2009 Annex E */
|
||||||
|
enum {
|
||||||
|
NM_LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID = 1,
|
||||||
|
NM_LLDP_OUI_802_1_SUBTYPE_PORT_PROTOCOL_VLAN_ID = 2,
|
||||||
|
NM_LLDP_OUI_802_1_SUBTYPE_VLAN_NAME = 3,
|
||||||
|
NM_LLDP_OUI_802_1_SUBTYPE_PROTOCOL_IDENTITY = 4,
|
||||||
|
NM_LLDP_OUI_802_1_SUBTYPE_VID_USAGE_DIGEST = 5,
|
||||||
|
NM_LLDP_OUI_802_1_SUBTYPE_MANAGEMENT_VID = 6,
|
||||||
|
NM_LLDP_OUI_802_1_SUBTYPE_LINK_AGGREGATION = 7
|
||||||
|
};
|
||||||
|
|
||||||
|
/* IEEE 802.1AB-2009 Annex F */
|
||||||
|
enum {
|
||||||
|
NM_LLDP_OUI_802_3_SUBTYPE_MAC_PHY_CONFIG_STATUS = 1,
|
||||||
|
NM_LLDP_OUI_802_3_SUBTYPE_POWER_VIA_MDI = 2,
|
||||||
|
NM_LLDP_OUI_802_3_SUBTYPE_LINK_AGGREGATION = 3,
|
||||||
|
NM_LLDP_OUI_802_3_SUBTYPE_MAXIMUM_FRAME_SIZE = 4
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* __NM_LLDP_H__ */
|
@@ -410,6 +410,24 @@ nm_mult_clamped_u(unsigned a, unsigned b)
|
|||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
static inline size_t
|
||||||
|
NM_ALIGN_TO(size_t l, size_t ali)
|
||||||
|
{
|
||||||
|
nm_assert(nm_utils_is_power_of_two(ali));
|
||||||
|
|
||||||
|
if (l > SIZE_MAX - (ali - 1))
|
||||||
|
return SIZE_MAX; /* indicate overflow */
|
||||||
|
|
||||||
|
return ((l + ali - 1) & ~(ali - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
#define NM_ALIGN4(l) NM_ALIGN_TO(l, 4)
|
||||||
|
#define NM_ALIGN8(l) NM_ALIGN_TO(l, 8)
|
||||||
|
#define NM_ALIGN(l) NM_ALIGN_TO(l, sizeof(void *))
|
||||||
|
#define NM_ALIGN_PTR(p) ((void *) NM_ALIGN((uintptr_t) (p)))
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
#define NM_SWAP(p_a, p_b) \
|
#define NM_SWAP(p_a, p_b) \
|
||||||
do { \
|
do { \
|
||||||
typeof(*(p_a)) *const _p_a = (p_a); \
|
typeof(*(p_a)) *const _p_a = (p_a); \
|
||||||
|
@@ -7,12 +7,9 @@ libnm_systemd_core = static_library(
|
|||||||
'src/libsystemd-network/dhcp6-network.c',
|
'src/libsystemd-network/dhcp6-network.c',
|
||||||
'src/libsystemd-network/dhcp6-option.c',
|
'src/libsystemd-network/dhcp6-option.c',
|
||||||
'src/libsystemd-network/dhcp6-protocol.c',
|
'src/libsystemd-network/dhcp6-protocol.c',
|
||||||
'src/libsystemd-network/lldp-neighbor.c',
|
|
||||||
'src/libsystemd-network/lldp-network.c',
|
|
||||||
'src/libsystemd-network/network-common.c',
|
'src/libsystemd-network/network-common.c',
|
||||||
'src/libsystemd-network/sd-dhcp6-client.c',
|
'src/libsystemd-network/sd-dhcp6-client.c',
|
||||||
'src/libsystemd-network/sd-dhcp6-lease.c',
|
'src/libsystemd-network/sd-dhcp6-lease.c',
|
||||||
'src/libsystemd-network/sd-lldp-rx.c',
|
|
||||||
'src/libsystemd/sd-event/event-util.c',
|
'src/libsystemd/sd-event/event-util.c',
|
||||||
'src/libsystemd/sd-event/sd-event.c',
|
'src/libsystemd/sd-event/sd-event.c',
|
||||||
'src/libsystemd/sd-id128/id128-util.c',
|
'src/libsystemd/sd-id128/id128-util.c',
|
||||||
|
@@ -7,7 +7,6 @@
|
|||||||
#define __NM_SD_H__
|
#define __NM_SD_H__
|
||||||
|
|
||||||
#include "src/systemd/sd-dhcp6-client.h"
|
#include "src/systemd/sd-dhcp6-client.h"
|
||||||
#include "src/systemd/sd-lldp-rx.h"
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
@@ -1,798 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
||||||
|
|
||||||
#include "nm-sd-adapt-core.h"
|
|
||||||
|
|
||||||
#include "alloc-util.h"
|
|
||||||
#include "escape.h"
|
|
||||||
#include "ether-addr-util.h"
|
|
||||||
#include "hexdecoct.h"
|
|
||||||
#include "in-addr-util.h"
|
|
||||||
#include "lldp-neighbor.h"
|
|
||||||
#include "memory-util.h"
|
|
||||||
#include "missing_network.h"
|
|
||||||
#include "unaligned.h"
|
|
||||||
|
|
||||||
static void lldp_neighbor_id_hash_func(const LLDPNeighborID *id, struct siphash *state) {
|
|
||||||
assert(id);
|
|
||||||
assert(state);
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
int lldp_neighbor_id_compare_func(const LLDPNeighborID *x, const LLDPNeighborID *y) {
|
|
||||||
assert(x);
|
|
||||||
assert(y);
|
|
||||||
|
|
||||||
return memcmp_nn(x->chassis_id, x->chassis_id_size, y->chassis_id, y->chassis_id_size)
|
|
||||||
?: memcmp_nn(x->port_id, x->port_id_size, y->port_id, y->port_id_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
|
|
||||||
lldp_neighbor_hash_ops,
|
|
||||||
LLDPNeighborID,
|
|
||||||
lldp_neighbor_id_hash_func,
|
|
||||||
lldp_neighbor_id_compare_func,
|
|
||||||
sd_lldp_neighbor,
|
|
||||||
lldp_neighbor_unlink);
|
|
||||||
|
|
||||||
int lldp_neighbor_prioq_compare_func(const void *a, const void *b) {
|
|
||||||
const sd_lldp_neighbor *x = a, *y = b;
|
|
||||||
|
|
||||||
assert(x);
|
|
||||||
assert(y);
|
|
||||||
|
|
||||||
return CMP(x->until, y->until);
|
|
||||||
}
|
|
||||||
|
|
||||||
sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n) {
|
|
||||||
if (!n)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
assert(n->n_ref > 0 || n->lldp_rx);
|
|
||||||
n->n_ref++;
|
|
||||||
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
static sd_lldp_neighbor *lldp_neighbor_free(sd_lldp_neighbor *n) {
|
|
||||||
if (!n)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
free(n->id.port_id);
|
|
||||||
free(n->id.chassis_id);
|
|
||||||
free(n->port_description);
|
|
||||||
free(n->system_name);
|
|
||||||
free(n->system_description);
|
|
||||||
free(n->mud_url);
|
|
||||||
free(n->chassis_id_as_string);
|
|
||||||
free(n->port_id_as_string);
|
|
||||||
return mfree(n);
|
|
||||||
}
|
|
||||||
|
|
||||||
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_rx)
|
|
||||||
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_rx)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
/* Only remove the neighbor object from the hash table if it's in there, don't complain if it isn't. This is
|
|
||||||
* because we are used as destructor call for hashmap_clear() and thus sometimes are called to de-register
|
|
||||||
* ourselves from the hashtable and sometimes are called after we already are de-registered. */
|
|
||||||
|
|
||||||
(void) hashmap_remove_value(n->lldp_rx->neighbor_by_id, &n->id, n);
|
|
||||||
|
|
||||||
assert_se(prioq_remove(n->lldp_rx->neighbor_by_expiry, n, &n->prioq_idx) >= 0);
|
|
||||||
|
|
||||||
n->lldp_rx = 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;
|
|
||||||
|
|
||||||
if (raw_size > SIZE_MAX - ALIGN(sizeof(sd_lldp_neighbor)))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
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(sd_lldp_rx *lldp_rx, char **s, const void *q, size_t n) {
|
|
||||||
const char *p = q;
|
|
||||||
char *k;
|
|
||||||
|
|
||||||
assert(s);
|
|
||||||
assert(p || n == 0);
|
|
||||||
|
|
||||||
if (*s) {
|
|
||||||
log_lldp_rx(lldp_rx, "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_rx(lldp_rx, "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 log_oom_debug();
|
|
||||||
|
|
||||||
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))
|
|
||||||
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
|
|
||||||
"Received truncated packet, ignoring.");
|
|
||||||
|
|
||||||
memcpy(&h, LLDP_NEIGHBOR_RAW(n), sizeof(h));
|
|
||||||
|
|
||||||
if (h.ether_type != htobe16(ETHERTYPE_LLDP))
|
|
||||||
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
|
|
||||||
"Received packet with wrong type, ignoring.");
|
|
||||||
|
|
||||||
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))
|
|
||||||
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
|
|
||||||
"Received packet with wrong destination address, ignoring.");
|
|
||||||
|
|
||||||
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)
|
|
||||||
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
|
|
||||||
"TLV lacks header, ignoring.");
|
|
||||||
|
|
||||||
type = p[0] >> 1;
|
|
||||||
length = p[1] + (((uint16_t) (p[0] & 1)) << 8);
|
|
||||||
p += 2, left -= 2;
|
|
||||||
|
|
||||||
if (left < length)
|
|
||||||
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
|
|
||||||
"TLV truncated, ignoring datagram.");
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
|
|
||||||
case SD_LLDP_TYPE_END:
|
|
||||||
if (length != 0)
|
|
||||||
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
|
|
||||||
"End marker TLV not zero-sized, ignoring datagram.");
|
|
||||||
|
|
||||||
/* Note that after processing the SD_LLDP_TYPE_END left could still be > 0
|
|
||||||
* as the message may contain padding (see IEEE 802.1AB-2016, sec. 8.5.12) */
|
|
||||||
|
|
||||||
goto end_marker;
|
|
||||||
|
|
||||||
case SD_LLDP_TYPE_CHASSIS_ID:
|
|
||||||
if (length < 2 || length > 256)
|
|
||||||
/* includes the chassis subtype, hence one extra byte */
|
|
||||||
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
|
|
||||||
"Chassis ID field size out of range, ignoring datagram.");
|
|
||||||
|
|
||||||
if (n->id.chassis_id)
|
|
||||||
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
|
|
||||||
"Duplicate chassis ID field, ignoring datagram.");
|
|
||||||
|
|
||||||
n->id.chassis_id = memdup(p, length);
|
|
||||||
if (!n->id.chassis_id)
|
|
||||||
return log_oom_debug();
|
|
||||||
|
|
||||||
n->id.chassis_id_size = length;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SD_LLDP_TYPE_PORT_ID:
|
|
||||||
if (length < 2 || length > 256)
|
|
||||||
/* includes the port subtype, hence one extra byte */
|
|
||||||
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
|
|
||||||
"Port ID field size out of range, ignoring datagram.");
|
|
||||||
|
|
||||||
if (n->id.port_id)
|
|
||||||
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
|
|
||||||
"Duplicate port ID field, ignoring datagram.");
|
|
||||||
|
|
||||||
n->id.port_id = memdup(p, length);
|
|
||||||
if (!n->id.port_id)
|
|
||||||
return log_oom_debug();
|
|
||||||
|
|
||||||
n->id.port_id_size = length;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SD_LLDP_TYPE_TTL:
|
|
||||||
if (length != 2)
|
|
||||||
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
|
|
||||||
"TTL field has wrong size, ignoring datagram.");
|
|
||||||
|
|
||||||
if (n->has_ttl)
|
|
||||||
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
|
|
||||||
"Duplicate TTL field, ignoring datagram.");
|
|
||||||
|
|
||||||
n->ttl = unaligned_read_be16(p);
|
|
||||||
n->has_ttl = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SD_LLDP_TYPE_PORT_DESCRIPTION:
|
|
||||||
r = parse_string(n->lldp_rx, &n->port_description, p, length);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SD_LLDP_TYPE_SYSTEM_NAME:
|
|
||||||
r = parse_string(n->lldp_rx, &n->system_name, p, length);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SD_LLDP_TYPE_SYSTEM_DESCRIPTION:
|
|
||||||
r = parse_string(n->lldp_rx, &n->system_description, p, length);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SD_LLDP_TYPE_SYSTEM_CAPABILITIES:
|
|
||||||
if (length != 4)
|
|
||||||
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
|
|
||||||
"System capabilities field has wrong size.");
|
|
||||||
|
|
||||||
n->system_capabilities = unaligned_read_be16(p);
|
|
||||||
n->enabled_capabilities = unaligned_read_be16(p + 2);
|
|
||||||
n->has_capabilities = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SD_LLDP_TYPE_PRIVATE:
|
|
||||||
if (length < 4)
|
|
||||||
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
|
|
||||||
"Found private TLV that is too short, ignoring.");
|
|
||||||
|
|
||||||
/* RFC 8520: MUD URL */
|
|
||||||
if (memcmp(p, SD_LLDP_OUI_IANA_MUD, sizeof(SD_LLDP_OUI_IANA_MUD)) == 0) {
|
|
||||||
r = parse_string(n->lldp_rx, &n->mud_url, p + sizeof(SD_LLDP_OUI_IANA_MUD),
|
|
||||||
length - sizeof(SD_LLDP_OUI_IANA_MUD));
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
p += length, left -= length;
|
|
||||||
}
|
|
||||||
|
|
||||||
end_marker:
|
|
||||||
if (!n->id.chassis_id || !n->id.port_id || !n->has_ttl)
|
|
||||||
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
|
|
||||||
"One or more mandatory TLV missing in datagram. Ignoring.");
|
|
||||||
|
|
||||||
n->rindex = sizeof(struct ether_header);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void lldp_neighbor_start_ttl(sd_lldp_neighbor *n) {
|
|
||||||
assert(n);
|
|
||||||
|
|
||||||
if (n->ttl > 0) {
|
|
||||||
usec_t base;
|
|
||||||
|
|
||||||
/* Use the packet's timestamp if there is one known */
|
|
||||||
base = triple_timestamp_by_clock(&n->timestamp, CLOCK_BOOTTIME);
|
|
||||||
if (!timestamp_is_set(base))
|
|
||||||
base = now(CLOCK_BOOTTIME); /* Otherwise, take the current time */
|
|
||||||
|
|
||||||
n->until = usec_add(base, n->ttl * USEC_PER_SEC);
|
|
||||||
} else
|
|
||||||
n->until = 0;
|
|
||||||
|
|
||||||
if (n->lldp_rx)
|
|
||||||
prioq_reshuffle(n->lldp_rx->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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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, r;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
r = in_addr_to_string(family, &a, ret);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 SD_LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT:
|
|
||||||
case SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS:
|
|
||||||
case SD_LLDP_CHASSIS_SUBTYPE_PORT_COMPONENT:
|
|
||||||
case SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME:
|
|
||||||
case SD_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 SD_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 SD_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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 SD_LLDP_PORT_SUBTYPE_INTERFACE_ALIAS:
|
|
||||||
case SD_LLDP_PORT_SUBTYPE_PORT_COMPONENT:
|
|
||||||
case SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME:
|
|
||||||
case SD_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 SD_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 SD_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;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret_sec) {
|
|
||||||
assert_return(n, -EINVAL);
|
|
||||||
assert_return(ret_sec, -EINVAL);
|
|
||||||
|
|
||||||
*ret_sec = n->ttl;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sd_lldp_neighbor_get_mud_url(sd_lldp_neighbor *n, const char **ret) {
|
|
||||||
assert_return(n, -EINVAL);
|
|
||||||
assert_return(ret, -EINVAL);
|
|
||||||
|
|
||||||
if (!n->mud_url)
|
|
||||||
return -ENODATA;
|
|
||||||
|
|
||||||
*ret = n->mud_url;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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_safe(LLDP_NEIGHBOR_RAW(n), raw, raw_size);
|
|
||||||
|
|
||||||
r = lldp_neighbor_parse(n);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
*ret = TAKE_PTR(n);
|
|
||||||
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 n->rindex < n->raw_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
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_TLV_LENGTH(n);
|
|
||||||
if (n->rindex + 2 + length > n->raw_size)
|
|
||||||
return -EBADMSG;
|
|
||||||
|
|
||||||
n->rindex += 2 + length;
|
|
||||||
return n->rindex < n->raw_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
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_TLV_TYPE(n);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[_SD_ARRAY_STATIC 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, SD_LLDP_TYPE_PRIVATE);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
if (r == 0)
|
|
||||||
return -ENXIO;
|
|
||||||
|
|
||||||
length = LLDP_NEIGHBOR_TLV_LENGTH(n);
|
|
||||||
if (length < 4)
|
|
||||||
return -EBADMSG;
|
|
||||||
|
|
||||||
if (n->rindex + 2 + length > n->raw_size)
|
|
||||||
return -EBADMSG;
|
|
||||||
|
|
||||||
d = LLDP_NEIGHBOR_TLV_DATA(n);
|
|
||||||
memcpy(oui, d, 3);
|
|
||||||
*subtype = d[3];
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[_SD_ARRAY_STATIC 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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_TLV_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;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor *n, clockid_t clock, uint64_t *ret) {
|
|
||||||
assert_return(n, -EINVAL);
|
|
||||||
assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock), -EOPNOTSUPP);
|
|
||||||
assert_return(clock_supported(clock), -EOPNOTSUPP);
|
|
||||||
assert_return(ret, -EINVAL);
|
|
||||||
|
|
||||||
if (!triple_timestamp_is_set(&n->timestamp))
|
|
||||||
return -ENODATA;
|
|
||||||
|
|
||||||
*ret = triple_timestamp_by_clock(&n->timestamp, clock);
|
|
||||||
return 0;
|
|
||||||
}
|
|
@@ -1,92 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#include "sd-lldp-rx.h"
|
|
||||||
|
|
||||||
#include "hash-funcs.h"
|
|
||||||
#include "lldp-rx-internal.h"
|
|
||||||
#include "time-util.h"
|
|
||||||
|
|
||||||
typedef struct LLDPNeighborID {
|
|
||||||
/* The spec calls this an "MSAP identifier" */
|
|
||||||
void *chassis_id;
|
|
||||||
size_t chassis_id_size;
|
|
||||||
|
|
||||||
void *port_id;
|
|
||||||
size_t port_id_size;
|
|
||||||
} LLDPNeighborID;
|
|
||||||
|
|
||||||
struct sd_lldp_neighbor {
|
|
||||||
/* Neighbor objects stay around as long as they are linked into an "sd_lldp_rx" object or n_ref > 0. */
|
|
||||||
sd_lldp_rx *lldp_rx;
|
|
||||||
unsigned n_ref;
|
|
||||||
|
|
||||||
triple_timestamp timestamp;
|
|
||||||
|
|
||||||
usec_t until;
|
|
||||||
unsigned prioq_idx;
|
|
||||||
|
|
||||||
struct ether_addr source_address;
|
|
||||||
struct ether_addr destination_address;
|
|
||||||
|
|
||||||
LLDPNeighborID id;
|
|
||||||
|
|
||||||
/* The raw packet size. The data is appended to the object, accessible via LLDP_NEIGHBOR_RAW() */
|
|
||||||
size_t raw_size;
|
|
||||||
|
|
||||||
/* The current read index for the iterative TLV interface */
|
|
||||||
size_t rindex;
|
|
||||||
|
|
||||||
/* And a couple of fields parsed out. */
|
|
||||||
bool has_ttl:1;
|
|
||||||
bool has_capabilities:1;
|
|
||||||
bool has_port_vlan_id:1;
|
|
||||||
|
|
||||||
uint16_t ttl;
|
|
||||||
|
|
||||||
uint16_t system_capabilities;
|
|
||||||
uint16_t enabled_capabilities;
|
|
||||||
|
|
||||||
char *port_description;
|
|
||||||
char *system_name;
|
|
||||||
char *system_description;
|
|
||||||
char *mud_url;
|
|
||||||
|
|
||||||
uint16_t port_vlan_id;
|
|
||||||
|
|
||||||
char *chassis_id_as_string;
|
|
||||||
char *port_id_as_string;
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline void *LLDP_NEIGHBOR_RAW(const sd_lldp_neighbor *n) {
|
|
||||||
return (uint8_t*) n + ALIGN(sizeof(sd_lldp_neighbor));
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint8_t LLDP_NEIGHBOR_TLV_TYPE(const sd_lldp_neighbor *n) {
|
|
||||||
return ((uint8_t*) LLDP_NEIGHBOR_RAW(n))[n->rindex] >> 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline size_t LLDP_NEIGHBOR_TLV_LENGTH(const sd_lldp_neighbor *n) {
|
|
||||||
uint8_t *p;
|
|
||||||
|
|
||||||
p = (uint8_t*) LLDP_NEIGHBOR_RAW(n) + n->rindex;
|
|
||||||
return p[1] + (((size_t) (p[0] & 1)) << 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void* LLDP_NEIGHBOR_TLV_DATA(const sd_lldp_neighbor *n) {
|
|
||||||
return ((uint8_t*) LLDP_NEIGHBOR_RAW(n)) + n->rindex + 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern const struct hash_ops lldp_neighbor_hash_ops;
|
|
||||||
int lldp_neighbor_id_compare_func(const LLDPNeighborID *x, const LLDPNeighborID *y);
|
|
||||||
int lldp_neighbor_prioq_compare_func(const void *a, const void *b);
|
|
||||||
|
|
||||||
sd_lldp_neighbor *lldp_neighbor_unlink(sd_lldp_neighbor *n);
|
|
||||||
sd_lldp_neighbor *lldp_neighbor_new(size_t raw_size);
|
|
||||||
int lldp_neighbor_parse(sd_lldp_neighbor *n);
|
|
||||||
void lldp_neighbor_start_ttl(sd_lldp_neighbor *n);
|
|
||||||
bool lldp_neighbor_equal(const sd_lldp_neighbor *a, const sd_lldp_neighbor *b);
|
|
@@ -1,72 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
||||||
|
|
||||||
#include "nm-sd-adapt-core.h"
|
|
||||||
|
|
||||||
#include <linux/filter.h>
|
|
||||||
#include <netinet/if_ether.h>
|
|
||||||
|
|
||||||
#include "fd-util.h"
|
|
||||||
#include "lldp-network.h"
|
|
||||||
#include "missing_network.h"
|
|
||||||
#include "socket-util.h"
|
|
||||||
|
|
||||||
int lldp_network_bind_raw_socket(int ifindex) {
|
|
||||||
static const struct sock_filter filter[] = {
|
|
||||||
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ethhdr, h_dest)), /* A <- 4 bytes of destination MAC */
|
|
||||||
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0180c200, 1, 0), /* A != 01:80:c2:00 */
|
|
||||||
BPF_STMT(BPF_RET + BPF_K, 0), /* drop packet */
|
|
||||||
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ethhdr, h_dest) + 4), /* A <- remaining 2 bytes of destination MAC */
|
|
||||||
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0000, 3, 0), /* A != 00:00 */
|
|
||||||
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0003, 2, 0), /* A != 00:03 */
|
|
||||||
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x000e, 1, 0), /* A != 00:0e */
|
|
||||||
BPF_STMT(BPF_RET + BPF_K, 0), /* drop packet */
|
|
||||||
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ethhdr, h_proto)), /* A <- protocol */
|
|
||||||
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_LLDP, 1, 0), /* A != ETHERTYPE_LLDP */
|
|
||||||
BPF_STMT(BPF_RET + BPF_K, 0), /* drop packet */
|
|
||||||
BPF_STMT(BPF_RET + BPF_K, UINT32_MAX), /* accept packet */
|
|
||||||
};
|
|
||||||
static const struct sock_fprog fprog = {
|
|
||||||
.len = ELEMENTSOF(filter),
|
|
||||||
.filter = (struct sock_filter*) filter,
|
|
||||||
};
|
|
||||||
struct packet_mreq mreq = {
|
|
||||||
.mr_ifindex = ifindex,
|
|
||||||
.mr_type = PACKET_MR_MULTICAST,
|
|
||||||
.mr_alen = ETH_ALEN,
|
|
||||||
.mr_address = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x00 }
|
|
||||||
};
|
|
||||||
union sockaddr_union saddrll = {
|
|
||||||
.ll.sll_family = AF_PACKET,
|
|
||||||
.ll.sll_ifindex = ifindex,
|
|
||||||
};
|
|
||||||
_cleanup_close_ int fd = -1;
|
|
||||||
|
|
||||||
assert(ifindex > 0);
|
|
||||||
|
|
||||||
fd = socket(AF_PACKET, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK,
|
|
||||||
htobe16(ETHERTYPE_LLDP));
|
|
||||||
if (fd < 0)
|
|
||||||
return -errno;
|
|
||||||
|
|
||||||
if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)) < 0)
|
|
||||||
return -errno;
|
|
||||||
|
|
||||||
/* customer bridge */
|
|
||||||
if (setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
|
|
||||||
return -errno;
|
|
||||||
|
|
||||||
/* non TPMR bridge */
|
|
||||||
mreq.mr_address[ETH_ALEN - 1] = 0x03;
|
|
||||||
if (setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
|
|
||||||
return -errno;
|
|
||||||
|
|
||||||
/* nearest bridge */
|
|
||||||
mreq.mr_address[ETH_ALEN - 1] = 0x0E;
|
|
||||||
if (setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
|
|
||||||
return -errno;
|
|
||||||
|
|
||||||
if (bind(fd, &saddrll.sa, sizeof(saddrll.ll)) < 0)
|
|
||||||
return -errno;
|
|
||||||
|
|
||||||
return TAKE_FD(fd);
|
|
||||||
}
|
|
@@ -1,6 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "sd-event.h"
|
|
||||||
|
|
||||||
int lldp_network_bind_raw_socket(int ifindex);
|
|
@@ -1,48 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "sd-event.h"
|
|
||||||
#include "sd-lldp-rx.h"
|
|
||||||
|
|
||||||
#include "hashmap.h"
|
|
||||||
#include "network-common.h"
|
|
||||||
#include "prioq.h"
|
|
||||||
|
|
||||||
struct sd_lldp_rx {
|
|
||||||
unsigned n_ref;
|
|
||||||
|
|
||||||
int ifindex;
|
|
||||||
char *ifname;
|
|
||||||
int fd;
|
|
||||||
|
|
||||||
sd_event *event;
|
|
||||||
int64_t event_priority;
|
|
||||||
sd_event_source *io_event_source;
|
|
||||||
sd_event_source *timer_event_source;
|
|
||||||
|
|
||||||
Prioq *neighbor_by_expiry;
|
|
||||||
Hashmap *neighbor_by_id;
|
|
||||||
|
|
||||||
uint64_t neighbors_max;
|
|
||||||
|
|
||||||
sd_lldp_rx_callback_t callback;
|
|
||||||
void *userdata;
|
|
||||||
|
|
||||||
uint16_t capability_mask;
|
|
||||||
|
|
||||||
struct ether_addr filter_address;
|
|
||||||
};
|
|
||||||
|
|
||||||
const char* lldp_rx_event_to_string(sd_lldp_rx_event_t e) _const_;
|
|
||||||
sd_lldp_rx_event_t lldp_rx_event_from_string(const char *s) _pure_;
|
|
||||||
|
|
||||||
#define log_lldp_rx_errno(lldp_rx, error, fmt, ...) \
|
|
||||||
log_interface_prefix_full_errno( \
|
|
||||||
"LLDP Rx: ", \
|
|
||||||
sd_lldp_rx, lldp_rx, \
|
|
||||||
error, fmt, ##__VA_ARGS__)
|
|
||||||
#define log_lldp_rx(lldp_rx, fmt, ...) \
|
|
||||||
log_interface_prefix_full_errno_zerook( \
|
|
||||||
"LLDP Rx: ", \
|
|
||||||
sd_lldp_rx, lldp_rx, \
|
|
||||||
0, fmt, ##__VA_ARGS__)
|
|
@@ -1,527 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
||||||
|
|
||||||
#include "nm-sd-adapt-core.h"
|
|
||||||
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
#include <linux/sockios.h>
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
|
|
||||||
#include "sd-lldp-rx.h"
|
|
||||||
|
|
||||||
#include "alloc-util.h"
|
|
||||||
#include "ether-addr-util.h"
|
|
||||||
#include "event-util.h"
|
|
||||||
#include "fd-util.h"
|
|
||||||
#include "lldp-neighbor.h"
|
|
||||||
#include "lldp-network.h"
|
|
||||||
#include "lldp-rx-internal.h"
|
|
||||||
#include "memory-util.h"
|
|
||||||
#include "network-common.h"
|
|
||||||
#include "socket-util.h"
|
|
||||||
#include "sort-util.h"
|
|
||||||
#include "string-table.h"
|
|
||||||
|
|
||||||
#define LLDP_DEFAULT_NEIGHBORS_MAX 128U
|
|
||||||
|
|
||||||
static const char * const lldp_rx_event_table[_SD_LLDP_RX_EVENT_MAX] = {
|
|
||||||
[SD_LLDP_RX_EVENT_ADDED] = "added",
|
|
||||||
[SD_LLDP_RX_EVENT_REMOVED] = "removed",
|
|
||||||
[SD_LLDP_RX_EVENT_UPDATED] = "updated",
|
|
||||||
[SD_LLDP_RX_EVENT_REFRESHED] = "refreshed",
|
|
||||||
};
|
|
||||||
|
|
||||||
DEFINE_STRING_TABLE_LOOKUP(lldp_rx_event, sd_lldp_rx_event_t);
|
|
||||||
|
|
||||||
static void lldp_rx_flush_neighbors(sd_lldp_rx *lldp_rx) {
|
|
||||||
assert(lldp_rx);
|
|
||||||
|
|
||||||
hashmap_clear(lldp_rx->neighbor_by_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void lldp_rx_callback(sd_lldp_rx *lldp_rx, sd_lldp_rx_event_t event, sd_lldp_neighbor *n) {
|
|
||||||
assert(lldp_rx);
|
|
||||||
assert(event >= 0 && event < _SD_LLDP_RX_EVENT_MAX);
|
|
||||||
|
|
||||||
if (!lldp_rx->callback)
|
|
||||||
return (void) log_lldp_rx(lldp_rx, "Received '%s' event.", lldp_rx_event_to_string(event));
|
|
||||||
|
|
||||||
log_lldp_rx(lldp_rx, "Invoking callback for '%s' event.", lldp_rx_event_to_string(event));
|
|
||||||
lldp_rx->callback(lldp_rx, event, n, lldp_rx->userdata);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int lldp_rx_make_space(sd_lldp_rx *lldp_rx, size_t extra) {
|
|
||||||
usec_t t = USEC_INFINITY;
|
|
||||||
bool changed = false;
|
|
||||||
|
|
||||||
assert(lldp_rx);
|
|
||||||
|
|
||||||
/* Remove all entries that are past their TTL, and more until at least the specified number of extra entries
|
|
||||||
* are free. */
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
_cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
|
|
||||||
|
|
||||||
n = prioq_peek(lldp_rx->neighbor_by_expiry);
|
|
||||||
if (!n)
|
|
||||||
break;
|
|
||||||
|
|
||||||
sd_lldp_neighbor_ref(n);
|
|
||||||
|
|
||||||
if (hashmap_size(lldp_rx->neighbor_by_id) > LESS_BY(lldp_rx->neighbors_max, extra))
|
|
||||||
goto remove_one;
|
|
||||||
|
|
||||||
if (t == USEC_INFINITY)
|
|
||||||
t = now(CLOCK_BOOTTIME);
|
|
||||||
|
|
||||||
if (n->until > t)
|
|
||||||
break;
|
|
||||||
|
|
||||||
remove_one:
|
|
||||||
lldp_neighbor_unlink(n);
|
|
||||||
lldp_rx_callback(lldp_rx, SD_LLDP_RX_EVENT_REMOVED, n);
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return changed;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool lldp_rx_keep_neighbor(sd_lldp_rx *lldp_rx, sd_lldp_neighbor *n) {
|
|
||||||
assert(lldp_rx);
|
|
||||||
assert(n);
|
|
||||||
|
|
||||||
/* Don't keep data with a zero TTL */
|
|
||||||
if (n->ttl <= 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
/* Filter out data from the filter address */
|
|
||||||
if (!ether_addr_is_null(&lldp_rx->filter_address) &&
|
|
||||||
ether_addr_equal(&lldp_rx->filter_address, &n->source_address))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
/* Only add if the neighbor has a capability we are interested in. Note that we also store all neighbors with
|
|
||||||
* no caps field set. */
|
|
||||||
if (n->has_capabilities &&
|
|
||||||
(n->enabled_capabilities & lldp_rx->capability_mask) == 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
/* Keep everything else */
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int lldp_rx_start_timer(sd_lldp_rx *lldp_rx, sd_lldp_neighbor *neighbor);
|
|
||||||
|
|
||||||
static int lldp_rx_add_neighbor(sd_lldp_rx *lldp_rx, sd_lldp_neighbor *n) {
|
|
||||||
_cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *old = NULL;
|
|
||||||
bool keep;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
assert(lldp_rx);
|
|
||||||
assert(n);
|
|
||||||
assert(!n->lldp_rx);
|
|
||||||
|
|
||||||
keep = lldp_rx_keep_neighbor(lldp_rx, n);
|
|
||||||
|
|
||||||
/* First retrieve the old entry for this MSAP */
|
|
||||||
old = hashmap_get(lldp_rx->neighbor_by_id, &n->id);
|
|
||||||
if (old) {
|
|
||||||
sd_lldp_neighbor_ref(old);
|
|
||||||
|
|
||||||
if (!keep) {
|
|
||||||
lldp_neighbor_unlink(old);
|
|
||||||
lldp_rx_callback(lldp_rx, SD_LLDP_RX_EVENT_REMOVED, old);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lldp_neighbor_equal(n, old)) {
|
|
||||||
/* Is this equal, then restart the TTL counter, but don't do anything else. */
|
|
||||||
old->timestamp = n->timestamp;
|
|
||||||
lldp_rx_start_timer(lldp_rx, old);
|
|
||||||
lldp_rx_callback(lldp_rx, SD_LLDP_RX_EVENT_REFRESHED, old);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Data changed, remove the old entry, and add a new one */
|
|
||||||
lldp_neighbor_unlink(old);
|
|
||||||
|
|
||||||
} else if (!keep)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* Then, make room for at least one new neighbor */
|
|
||||||
lldp_rx_make_space(lldp_rx, 1);
|
|
||||||
|
|
||||||
r = hashmap_ensure_put(&lldp_rx->neighbor_by_id, &lldp_neighbor_hash_ops, &n->id, n);
|
|
||||||
if (r < 0)
|
|
||||||
goto finish;
|
|
||||||
|
|
||||||
r = prioq_ensure_put(&lldp_rx->neighbor_by_expiry, lldp_neighbor_prioq_compare_func, n, &n->prioq_idx);
|
|
||||||
if (r < 0) {
|
|
||||||
assert_se(hashmap_remove(lldp_rx->neighbor_by_id, &n->id) == n);
|
|
||||||
goto finish;
|
|
||||||
}
|
|
||||||
|
|
||||||
n->lldp_rx = lldp_rx;
|
|
||||||
|
|
||||||
lldp_rx_start_timer(lldp_rx, n);
|
|
||||||
lldp_rx_callback(lldp_rx, old ? SD_LLDP_RX_EVENT_UPDATED : SD_LLDP_RX_EVENT_ADDED, n);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
finish:
|
|
||||||
if (old)
|
|
||||||
lldp_rx_callback(lldp_rx, SD_LLDP_RX_EVENT_REMOVED, old);
|
|
||||||
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int lldp_rx_handle_datagram(sd_lldp_rx *lldp_rx, sd_lldp_neighbor *n) {
|
|
||||||
int r;
|
|
||||||
|
|
||||||
assert(lldp_rx);
|
|
||||||
assert(n);
|
|
||||||
|
|
||||||
r = lldp_neighbor_parse(n);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
r = lldp_rx_add_neighbor(lldp_rx, n);
|
|
||||||
if (r < 0)
|
|
||||||
return log_lldp_rx_errno(lldp_rx, r, "Failed to add datagram. Ignoring.");
|
|
||||||
|
|
||||||
log_lldp_rx(lldp_rx, "Successfully processed LLDP datagram.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int lldp_rx_receive_datagram(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
|
|
||||||
_cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
|
|
||||||
ssize_t space, length;
|
|
||||||
sd_lldp_rx *lldp_rx = ASSERT_PTR(userdata);
|
|
||||||
struct timespec ts;
|
|
||||||
|
|
||||||
assert(fd >= 0);
|
|
||||||
|
|
||||||
space = next_datagram_size_fd(fd);
|
|
||||||
if (space < 0) {
|
|
||||||
if (ERRNO_IS_TRANSIENT(space) || ERRNO_IS_DISCONNECT(space))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
log_lldp_rx_errno(lldp_rx, space, "Failed to determine datagram size to read, ignoring: %m");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
n = lldp_neighbor_new(space);
|
|
||||||
if (!n) {
|
|
||||||
log_oom_debug();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
length = recv(fd, LLDP_NEIGHBOR_RAW(n), n->raw_size, MSG_DONTWAIT);
|
|
||||||
if (length < 0) {
|
|
||||||
if (ERRNO_IS_TRANSIENT(errno) || ERRNO_IS_DISCONNECT(errno))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
log_lldp_rx_errno(lldp_rx, errno, "Failed to read LLDP datagram, ignoring: %m");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((size_t) length != n->raw_size) {
|
|
||||||
log_lldp_rx(lldp_rx, "Packet size mismatch, ignoring");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Try to get the timestamp of this packet if it is known */
|
|
||||||
if (ioctl(fd, SIOCGSTAMPNS, &ts) >= 0)
|
|
||||||
triple_timestamp_from_realtime(&n->timestamp, timespec_load(&ts));
|
|
||||||
else
|
|
||||||
triple_timestamp_get(&n->timestamp);
|
|
||||||
|
|
||||||
(void) lldp_rx_handle_datagram(lldp_rx, n);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void lldp_rx_reset(sd_lldp_rx *lldp_rx) {
|
|
||||||
assert(lldp_rx);
|
|
||||||
|
|
||||||
(void) event_source_disable(lldp_rx->timer_event_source);
|
|
||||||
lldp_rx->io_event_source = sd_event_source_disable_unref(lldp_rx->io_event_source);
|
|
||||||
lldp_rx->fd = safe_close(lldp_rx->fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
int sd_lldp_rx_is_running(sd_lldp_rx *lldp_rx) {
|
|
||||||
if (!lldp_rx)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return lldp_rx->fd >= 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sd_lldp_rx_start(sd_lldp_rx *lldp_rx) {
|
|
||||||
int r;
|
|
||||||
|
|
||||||
assert_return(lldp_rx, -EINVAL);
|
|
||||||
assert_return(lldp_rx->event, -EINVAL);
|
|
||||||
assert_return(lldp_rx->ifindex > 0, -EINVAL);
|
|
||||||
|
|
||||||
if (sd_lldp_rx_is_running(lldp_rx))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
assert(!lldp_rx->io_event_source);
|
|
||||||
|
|
||||||
lldp_rx->fd = lldp_network_bind_raw_socket(lldp_rx->ifindex);
|
|
||||||
if (lldp_rx->fd < 0)
|
|
||||||
return lldp_rx->fd;
|
|
||||||
|
|
||||||
r = sd_event_add_io(lldp_rx->event, &lldp_rx->io_event_source, lldp_rx->fd, EPOLLIN, lldp_rx_receive_datagram, lldp_rx);
|
|
||||||
if (r < 0)
|
|
||||||
goto fail;
|
|
||||||
|
|
||||||
r = sd_event_source_set_priority(lldp_rx->io_event_source, lldp_rx->event_priority);
|
|
||||||
if (r < 0)
|
|
||||||
goto fail;
|
|
||||||
|
|
||||||
(void) sd_event_source_set_description(lldp_rx->io_event_source, "lldp-rx-io");
|
|
||||||
|
|
||||||
log_lldp_rx(lldp_rx, "Started LLDP client");
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
fail:
|
|
||||||
lldp_rx_reset(lldp_rx);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sd_lldp_rx_stop(sd_lldp_rx *lldp_rx) {
|
|
||||||
if (!sd_lldp_rx_is_running(lldp_rx))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
log_lldp_rx(lldp_rx, "Stopping LLDP client");
|
|
||||||
|
|
||||||
lldp_rx_reset(lldp_rx);
|
|
||||||
lldp_rx_flush_neighbors(lldp_rx);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sd_lldp_rx_attach_event(sd_lldp_rx *lldp_rx, sd_event *event, int64_t priority) {
|
|
||||||
int r;
|
|
||||||
|
|
||||||
assert_return(lldp_rx, -EINVAL);
|
|
||||||
assert_return(!sd_lldp_rx_is_running(lldp_rx), -EBUSY);
|
|
||||||
assert_return(!lldp_rx->event, -EBUSY);
|
|
||||||
|
|
||||||
if (event)
|
|
||||||
lldp_rx->event = sd_event_ref(event);
|
|
||||||
else {
|
|
||||||
r = sd_event_default(&lldp_rx->event);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
lldp_rx->event_priority = priority;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sd_lldp_rx_detach_event(sd_lldp_rx *lldp_rx) {
|
|
||||||
assert_return(lldp_rx, -EINVAL);
|
|
||||||
assert_return(!sd_lldp_rx_is_running(lldp_rx), -EBUSY);
|
|
||||||
|
|
||||||
lldp_rx->io_event_source = sd_event_source_disable_unref(lldp_rx->io_event_source);
|
|
||||||
lldp_rx->timer_event_source = sd_event_source_disable_unref(lldp_rx->timer_event_source);
|
|
||||||
lldp_rx->event = sd_event_unref(lldp_rx->event);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
sd_event* sd_lldp_rx_get_event(sd_lldp_rx *lldp_rx) {
|
|
||||||
assert_return(lldp_rx, NULL);
|
|
||||||
|
|
||||||
return lldp_rx->event;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sd_lldp_rx_set_callback(sd_lldp_rx *lldp_rx, sd_lldp_rx_callback_t cb, void *userdata) {
|
|
||||||
assert_return(lldp_rx, -EINVAL);
|
|
||||||
|
|
||||||
lldp_rx->callback = cb;
|
|
||||||
lldp_rx->userdata = userdata;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sd_lldp_rx_set_ifindex(sd_lldp_rx *lldp_rx, int ifindex) {
|
|
||||||
assert_return(lldp_rx, -EINVAL);
|
|
||||||
assert_return(ifindex > 0, -EINVAL);
|
|
||||||
assert_return(!sd_lldp_rx_is_running(lldp_rx), -EBUSY);
|
|
||||||
|
|
||||||
lldp_rx->ifindex = ifindex;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sd_lldp_rx_set_ifname(sd_lldp_rx *lldp_rx, const char *ifname) {
|
|
||||||
assert_return(lldp_rx, -EINVAL);
|
|
||||||
assert_return(ifname, -EINVAL);
|
|
||||||
|
|
||||||
if (!ifname_valid_full(ifname, IFNAME_VALID_ALTERNATIVE))
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
return free_and_strdup(&lldp_rx->ifname, ifname);
|
|
||||||
}
|
|
||||||
|
|
||||||
int sd_lldp_rx_get_ifname(sd_lldp_rx *lldp_rx, const char **ret) {
|
|
||||||
int r;
|
|
||||||
|
|
||||||
assert_return(lldp_rx, -EINVAL);
|
|
||||||
|
|
||||||
r = get_ifname(lldp_rx->ifindex, &lldp_rx->ifname);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
if (ret)
|
|
||||||
*ret = lldp_rx->ifname;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static sd_lldp_rx *lldp_rx_free(sd_lldp_rx *lldp_rx) {
|
|
||||||
if (!lldp_rx)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
lldp_rx_reset(lldp_rx);
|
|
||||||
|
|
||||||
sd_lldp_rx_detach_event(lldp_rx);
|
|
||||||
|
|
||||||
lldp_rx_flush_neighbors(lldp_rx);
|
|
||||||
|
|
||||||
hashmap_free(lldp_rx->neighbor_by_id);
|
|
||||||
prioq_free(lldp_rx->neighbor_by_expiry);
|
|
||||||
free(lldp_rx->ifname);
|
|
||||||
return mfree(lldp_rx);
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_lldp_rx, sd_lldp_rx, lldp_rx_free);
|
|
||||||
|
|
||||||
int sd_lldp_rx_new(sd_lldp_rx **ret) {
|
|
||||||
_cleanup_(sd_lldp_rx_unrefp) sd_lldp_rx *lldp_rx = NULL;
|
|
||||||
|
|
||||||
assert_return(ret, -EINVAL);
|
|
||||||
|
|
||||||
lldp_rx = new(sd_lldp_rx, 1);
|
|
||||||
if (!lldp_rx)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
*lldp_rx = (sd_lldp_rx) {
|
|
||||||
.n_ref = 1,
|
|
||||||
.fd = -1,
|
|
||||||
.neighbors_max = LLDP_DEFAULT_NEIGHBORS_MAX,
|
|
||||||
.capability_mask = UINT16_MAX,
|
|
||||||
};
|
|
||||||
|
|
||||||
*ret = TAKE_PTR(lldp_rx);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int on_timer_event(sd_event_source *s, uint64_t usec, void *userdata) {
|
|
||||||
sd_lldp_rx *lldp_rx = userdata;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
r = lldp_rx_make_space(lldp_rx, 0);
|
|
||||||
if (r < 0) {
|
|
||||||
log_lldp_rx_errno(lldp_rx, r, "Failed to make space, ignoring: %m");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
r = lldp_rx_start_timer(lldp_rx, NULL);
|
|
||||||
if (r < 0) {
|
|
||||||
log_lldp_rx_errno(lldp_rx, r, "Failed to restart timer, ignoring: %m");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int lldp_rx_start_timer(sd_lldp_rx *lldp_rx, sd_lldp_neighbor *neighbor) {
|
|
||||||
sd_lldp_neighbor *n;
|
|
||||||
|
|
||||||
assert(lldp_rx);
|
|
||||||
assert(lldp_rx->event);
|
|
||||||
|
|
||||||
if (neighbor)
|
|
||||||
lldp_neighbor_start_ttl(neighbor);
|
|
||||||
|
|
||||||
n = prioq_peek(lldp_rx->neighbor_by_expiry);
|
|
||||||
if (!n)
|
|
||||||
return event_source_disable(lldp_rx->timer_event_source);
|
|
||||||
|
|
||||||
return event_reset_time(lldp_rx->event, &lldp_rx->timer_event_source,
|
|
||||||
CLOCK_BOOTTIME,
|
|
||||||
n->until, 0,
|
|
||||||
on_timer_event, lldp_rx,
|
|
||||||
lldp_rx->event_priority, "lldp-rx-timer", true);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int neighbor_compare_func(sd_lldp_neighbor * const *a, sd_lldp_neighbor * const *b) {
|
|
||||||
assert(a);
|
|
||||||
assert(b);
|
|
||||||
assert(*a);
|
|
||||||
assert(*b);
|
|
||||||
|
|
||||||
return lldp_neighbor_id_compare_func(&(*a)->id, &(*b)->id);
|
|
||||||
}
|
|
||||||
|
|
||||||
int sd_lldp_rx_get_neighbors(sd_lldp_rx *lldp_rx, sd_lldp_neighbor ***ret) {
|
|
||||||
_cleanup_free_ sd_lldp_neighbor **l = NULL;
|
|
||||||
sd_lldp_neighbor *n;
|
|
||||||
int k = 0;
|
|
||||||
|
|
||||||
assert_return(lldp_rx, -EINVAL);
|
|
||||||
assert_return(ret, -EINVAL);
|
|
||||||
|
|
||||||
if (hashmap_isempty(lldp_rx->neighbor_by_id)) { /* Special shortcut */
|
|
||||||
*ret = NULL;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
l = new0(sd_lldp_neighbor*, hashmap_size(lldp_rx->neighbor_by_id));
|
|
||||||
if (!l)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
HASHMAP_FOREACH(n, lldp_rx->neighbor_by_id)
|
|
||||||
l[k++] = sd_lldp_neighbor_ref(n);
|
|
||||||
|
|
||||||
assert((size_t) k == hashmap_size(lldp_rx->neighbor_by_id));
|
|
||||||
|
|
||||||
/* Return things in a stable order */
|
|
||||||
typesafe_qsort(l, k, neighbor_compare_func);
|
|
||||||
*ret = TAKE_PTR(l);
|
|
||||||
|
|
||||||
return k;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sd_lldp_rx_set_neighbors_max(sd_lldp_rx *lldp_rx, uint64_t m) {
|
|
||||||
assert_return(lldp_rx, -EINVAL);
|
|
||||||
assert_return(m > 0, -EINVAL);
|
|
||||||
|
|
||||||
lldp_rx->neighbors_max = m;
|
|
||||||
lldp_rx_make_space(lldp_rx, 0);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sd_lldp_rx_match_capabilities(sd_lldp_rx *lldp_rx, uint16_t mask) {
|
|
||||||
assert_return(lldp_rx, -EINVAL);
|
|
||||||
assert_return(mask != 0, -EINVAL);
|
|
||||||
|
|
||||||
lldp_rx->capability_mask = mask;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sd_lldp_rx_set_filter_address(sd_lldp_rx *lldp_rx, const struct ether_addr *addr) {
|
|
||||||
assert_return(lldp_rx, -EINVAL);
|
|
||||||
|
|
||||||
/* In order to deal nicely with bridges that send back our own packets, allow one address to be filtered, so
|
|
||||||
* that our own can be filtered out here. */
|
|
||||||
|
|
||||||
if (addr)
|
|
||||||
lldp_rx->filter_address = *addr;
|
|
||||||
else
|
|
||||||
zero(lldp_rx->filter_address);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
@@ -1,109 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
||||||
#ifndef foosdlldprxhfoo
|
|
||||||
#define foosdlldprxhfoo
|
|
||||||
|
|
||||||
/***
|
|
||||||
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 <https://www.gnu.org/licenses/>.
|
|
||||||
***/
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <net/ethernet.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#include "sd-event.h"
|
|
||||||
#include "sd-lldp.h"
|
|
||||||
|
|
||||||
#include "_sd-common.h"
|
|
||||||
|
|
||||||
_SD_BEGIN_DECLARATIONS;
|
|
||||||
|
|
||||||
typedef struct sd_lldp_rx sd_lldp_rx;
|
|
||||||
typedef struct sd_lldp_neighbor sd_lldp_neighbor;
|
|
||||||
|
|
||||||
__extension__ typedef enum sd_lldp_rx_event_t {
|
|
||||||
SD_LLDP_RX_EVENT_ADDED,
|
|
||||||
SD_LLDP_RX_EVENT_REMOVED,
|
|
||||||
SD_LLDP_RX_EVENT_UPDATED,
|
|
||||||
SD_LLDP_RX_EVENT_REFRESHED,
|
|
||||||
_SD_LLDP_RX_EVENT_MAX,
|
|
||||||
_SD_LLDP_RX_EVENT_INVALID = -EINVAL,
|
|
||||||
_SD_ENUM_FORCE_S64(LLDP_RX_EVENT)
|
|
||||||
} sd_lldp_rx_event_t;
|
|
||||||
|
|
||||||
typedef void (*sd_lldp_rx_callback_t)(sd_lldp_rx *lldp_rx, sd_lldp_rx_event_t event, sd_lldp_neighbor *n, void *userdata);
|
|
||||||
|
|
||||||
int sd_lldp_rx_new(sd_lldp_rx **ret);
|
|
||||||
sd_lldp_rx *sd_lldp_rx_ref(sd_lldp_rx *lldp_rx);
|
|
||||||
sd_lldp_rx *sd_lldp_rx_unref(sd_lldp_rx *lldp_rx);
|
|
||||||
|
|
||||||
int sd_lldp_rx_start(sd_lldp_rx *lldp_rx);
|
|
||||||
int sd_lldp_rx_stop(sd_lldp_rx *lldp_rx);
|
|
||||||
int sd_lldp_rx_is_running(sd_lldp_rx *lldp_rx);
|
|
||||||
|
|
||||||
int sd_lldp_rx_attach_event(sd_lldp_rx *lldp_rx, sd_event *event, int64_t priority);
|
|
||||||
int sd_lldp_rx_detach_event(sd_lldp_rx *lldp_rx);
|
|
||||||
sd_event *sd_lldp_rx_get_event(sd_lldp_rx *lldp_rx);
|
|
||||||
|
|
||||||
int sd_lldp_rx_set_callback(sd_lldp_rx *lldp_rx, sd_lldp_rx_callback_t cb, void *userdata);
|
|
||||||
int sd_lldp_rx_set_ifindex(sd_lldp_rx *lldp_rx, int ifindex);
|
|
||||||
int sd_lldp_rx_set_ifname(sd_lldp_rx *lldp_rx, const char *ifname);
|
|
||||||
int sd_lldp_rx_get_ifname(sd_lldp_rx *lldp_rx, const char **ret);
|
|
||||||
|
|
||||||
/* Controls how much and what to store in the neighbors database */
|
|
||||||
int sd_lldp_rx_set_neighbors_max(sd_lldp_rx *lldp_rx, uint64_t n);
|
|
||||||
int sd_lldp_rx_match_capabilities(sd_lldp_rx *lldp_rx, uint16_t mask);
|
|
||||||
int sd_lldp_rx_set_filter_address(sd_lldp_rx *lldp_rx, const struct ether_addr *address);
|
|
||||||
|
|
||||||
int sd_lldp_rx_get_neighbors(sd_lldp_rx *lldp_rx, sd_lldp_neighbor ***neighbors);
|
|
||||||
|
|
||||||
int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size);
|
|
||||||
sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n);
|
|
||||||
sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n);
|
|
||||||
|
|
||||||
/* Access to LLDP frame metadata */
|
|
||||||
int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct ether_addr* address);
|
|
||||||
int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_addr* address);
|
|
||||||
int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor *n, clockid_t clock, uint64_t *ret);
|
|
||||||
int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size);
|
|
||||||
|
|
||||||
/* High-level, direct, parsed out field access. These fields exist at most once, hence may be queried directly. */
|
|
||||||
int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size);
|
|
||||||
int sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor *n, const char **ret);
|
|
||||||
int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size);
|
|
||||||
int sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor *n, const char **ret);
|
|
||||||
int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret_sec);
|
|
||||||
int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret);
|
|
||||||
int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret);
|
|
||||||
int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret);
|
|
||||||
int sd_lldp_neighbor_get_mud_url(sd_lldp_neighbor *n, const char **ret);
|
|
||||||
int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret);
|
|
||||||
int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret);
|
|
||||||
|
|
||||||
/* Low-level, iterative TLV access. This is for everything else, it iteratively goes through all available TLVs
|
|
||||||
* (including the ones covered with the calls above), and allows multiple TLVs for the same fields. */
|
|
||||||
int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n);
|
|
||||||
int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n);
|
|
||||||
int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type);
|
|
||||||
int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type);
|
|
||||||
int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t *subtype);
|
|
||||||
int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t subtype);
|
|
||||||
int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size);
|
|
||||||
|
|
||||||
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_lldp_rx, sd_lldp_rx_unref);
|
|
||||||
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_lldp_neighbor, sd_lldp_neighbor_unref);
|
|
||||||
|
|
||||||
_SD_END_DECLARATIONS;
|
|
||||||
|
|
||||||
#endif
|
|
@@ -1,123 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
||||||
#ifndef foosdlldphfoo
|
|
||||||
#define foosdlldphfoo
|
|
||||||
|
|
||||||
/***
|
|
||||||
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 <https://www.gnu.org/licenses/>.
|
|
||||||
***/
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
|
|
||||||
#include "_sd-common.h"
|
|
||||||
|
|
||||||
_SD_BEGIN_DECLARATIONS;
|
|
||||||
|
|
||||||
/* IEEE 802.1AB-2009 Clause 8: TLV Types */
|
|
||||||
enum {
|
|
||||||
SD_LLDP_TYPE_END = 0,
|
|
||||||
SD_LLDP_TYPE_CHASSIS_ID = 1,
|
|
||||||
SD_LLDP_TYPE_PORT_ID = 2,
|
|
||||||
SD_LLDP_TYPE_TTL = 3,
|
|
||||||
SD_LLDP_TYPE_PORT_DESCRIPTION = 4,
|
|
||||||
SD_LLDP_TYPE_SYSTEM_NAME = 5,
|
|
||||||
SD_LLDP_TYPE_SYSTEM_DESCRIPTION = 6,
|
|
||||||
SD_LLDP_TYPE_SYSTEM_CAPABILITIES = 7,
|
|
||||||
SD_LLDP_TYPE_MGMT_ADDRESS = 8,
|
|
||||||
SD_LLDP_TYPE_PRIVATE = 127
|
|
||||||
};
|
|
||||||
|
|
||||||
/* IEEE 802.1AB-2009 Clause 8.5.2: Chassis subtypes */
|
|
||||||
enum {
|
|
||||||
SD_LLDP_CHASSIS_SUBTYPE_RESERVED = 0,
|
|
||||||
SD_LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT = 1,
|
|
||||||
SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS = 2,
|
|
||||||
SD_LLDP_CHASSIS_SUBTYPE_PORT_COMPONENT = 3,
|
|
||||||
SD_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS = 4,
|
|
||||||
SD_LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS = 5,
|
|
||||||
SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME = 6,
|
|
||||||
SD_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED = 7
|
|
||||||
};
|
|
||||||
|
|
||||||
/* IEEE 802.1AB-2009 Clause 8.5.3: Port subtype */
|
|
||||||
enum {
|
|
||||||
SD_LLDP_PORT_SUBTYPE_RESERVED = 0,
|
|
||||||
SD_LLDP_PORT_SUBTYPE_INTERFACE_ALIAS = 1,
|
|
||||||
SD_LLDP_PORT_SUBTYPE_PORT_COMPONENT = 2,
|
|
||||||
SD_LLDP_PORT_SUBTYPE_MAC_ADDRESS = 3,
|
|
||||||
SD_LLDP_PORT_SUBTYPE_NETWORK_ADDRESS = 4,
|
|
||||||
SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME = 5,
|
|
||||||
SD_LLDP_PORT_SUBTYPE_AGENT_CIRCUIT_ID = 6,
|
|
||||||
SD_LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED = 7
|
|
||||||
};
|
|
||||||
|
|
||||||
/* IEEE 802.1AB-2009 Clause 8.5.8: System capabilities */
|
|
||||||
enum {
|
|
||||||
SD_LLDP_SYSTEM_CAPABILITIES_OTHER = 1 << 0,
|
|
||||||
SD_LLDP_SYSTEM_CAPABILITIES_REPEATER = 1 << 1,
|
|
||||||
SD_LLDP_SYSTEM_CAPABILITIES_BRIDGE = 1 << 2,
|
|
||||||
SD_LLDP_SYSTEM_CAPABILITIES_WLAN_AP = 1 << 3,
|
|
||||||
SD_LLDP_SYSTEM_CAPABILITIES_ROUTER = 1 << 4,
|
|
||||||
SD_LLDP_SYSTEM_CAPABILITIES_PHONE = 1 << 5,
|
|
||||||
SD_LLDP_SYSTEM_CAPABILITIES_DOCSIS = 1 << 6,
|
|
||||||
SD_LLDP_SYSTEM_CAPABILITIES_STATION = 1 << 7,
|
|
||||||
SD_LLDP_SYSTEM_CAPABILITIES_CVLAN = 1 << 8,
|
|
||||||
SD_LLDP_SYSTEM_CAPABILITIES_SVLAN = 1 << 9,
|
|
||||||
SD_LLDP_SYSTEM_CAPABILITIES_TPMR = 1 << 10
|
|
||||||
};
|
|
||||||
|
|
||||||
#define SD_LLDP_SYSTEM_CAPABILITIES_ALL UINT16_MAX
|
|
||||||
|
|
||||||
#define SD_LLDP_SYSTEM_CAPABILITIES_ALL_ROUTERS \
|
|
||||||
((uint16_t) \
|
|
||||||
(SD_LLDP_SYSTEM_CAPABILITIES_REPEATER | \
|
|
||||||
SD_LLDP_SYSTEM_CAPABILITIES_BRIDGE | \
|
|
||||||
SD_LLDP_SYSTEM_CAPABILITIES_WLAN_AP | \
|
|
||||||
SD_LLDP_SYSTEM_CAPABILITIES_ROUTER | \
|
|
||||||
SD_LLDP_SYSTEM_CAPABILITIES_DOCSIS | \
|
|
||||||
SD_LLDP_SYSTEM_CAPABILITIES_CVLAN | \
|
|
||||||
SD_LLDP_SYSTEM_CAPABILITIES_SVLAN | \
|
|
||||||
SD_LLDP_SYSTEM_CAPABILITIES_TPMR))
|
|
||||||
|
|
||||||
#define SD_LLDP_OUI_802_1 (const uint8_t[]) { 0x00, 0x80, 0xc2 }
|
|
||||||
#define SD_LLDP_OUI_802_3 (const uint8_t[]) { 0x00, 0x12, 0x0f }
|
|
||||||
|
|
||||||
#define _SD_LLDP_OUI_IANA 0x00, 0x00, 0x5E
|
|
||||||
#define SD_LLDP_OUI_IANA (const uint8_t[]) { _SD_LLDP_OUI_IANA }
|
|
||||||
|
|
||||||
#define SD_LLDP_OUI_IANA_SUBTYPE_MUD 0x01
|
|
||||||
#define SD_LLDP_OUI_IANA_MUD \
|
|
||||||
(const uint8_t[]) { _SD_LLDP_OUI_IANA, SD_LLDP_OUI_IANA_SUBTYPE_MUD }
|
|
||||||
|
|
||||||
/* IEEE 802.1AB-2009 Annex E */
|
|
||||||
enum {
|
|
||||||
SD_LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID = 1,
|
|
||||||
SD_LLDP_OUI_802_1_SUBTYPE_PORT_PROTOCOL_VLAN_ID = 2,
|
|
||||||
SD_LLDP_OUI_802_1_SUBTYPE_VLAN_NAME = 3,
|
|
||||||
SD_LLDP_OUI_802_1_SUBTYPE_PROTOCOL_IDENTITY = 4,
|
|
||||||
SD_LLDP_OUI_802_1_SUBTYPE_VID_USAGE_DIGEST = 5,
|
|
||||||
SD_LLDP_OUI_802_1_SUBTYPE_MANAGEMENT_VID = 6,
|
|
||||||
SD_LLDP_OUI_802_1_SUBTYPE_LINK_AGGREGATION = 7
|
|
||||||
};
|
|
||||||
|
|
||||||
/* IEEE 802.1AB-2009 Annex F */
|
|
||||||
enum {
|
|
||||||
SD_LLDP_OUI_802_3_SUBTYPE_MAC_PHY_CONFIG_STATUS = 1,
|
|
||||||
SD_LLDP_OUI_802_3_SUBTYPE_POWER_VIA_MDI = 2,
|
|
||||||
SD_LLDP_OUI_802_3_SUBTYPE_LINK_AGGREGATION = 3,
|
|
||||||
SD_LLDP_OUI_802_3_SUBTYPE_MAXIMUM_FRAME_SIZE = 4
|
|
||||||
};
|
|
||||||
|
|
||||||
_SD_END_DECLARATIONS;
|
|
||||||
|
|
||||||
#endif
|
|
@@ -78,6 +78,7 @@ subdir('libnm-systemd-core')
|
|||||||
subdir('libnm-udev-aux')
|
subdir('libnm-udev-aux')
|
||||||
subdir('libnm-base')
|
subdir('libnm-base')
|
||||||
subdir('libnm-platform')
|
subdir('libnm-platform')
|
||||||
|
subdir('libnm-lldp')
|
||||||
subdir('libnm-crypto')
|
subdir('libnm-crypto')
|
||||||
subdir('libnm-core-public')
|
subdir('libnm-core-public')
|
||||||
subdir('libnm-core-intern')
|
subdir('libnm-core-intern')
|
||||||
|
Reference in New Issue
Block a user