systemd: merge branch 'th/systemd-lldp-bgo763384' into master

https://bugzilla.gnome.org/show_bug.cgi?id=763384
This commit is contained in:
Thomas Haller 2016-01-26 17:22:54 +01:00
commit e1e428b21e
72 changed files with 6029 additions and 2546 deletions

View file

@ -86,6 +86,9 @@ AC_SUBST(nmdatadir, '${datadir}'/$PACKAGE, [NetworkManager shared data directory
AC_SUBST(nmstatedir, '${localstatedir}'/lib/$PACKAGE, [NetworkManager persistent state directory])
AC_SUBST(nmrundir, '${runstatedir}'/$PACKAGE, [NetworkManager runtime state directory])
AC_GNU_SOURCE
AC_CHECK_FUNCS([__secure_getenv secure_getenv])
# Alternative configuration plugins
AC_ARG_ENABLE(config-plugin-ibft, AS_HELP_STRING([--enable-config-plugin-ibft], [enable ibft configuration plugin]))
AC_ARG_ENABLE(ifcfg-rh, AS_HELP_STRING([--enable-ifcfg-rh], [enable ifcfg-rh configuration plugin (Fedora/RHEL)]))

View file

@ -71,6 +71,8 @@ libsystemd_nm_la_SOURCES = \
systemd/src/basic/async.h \
systemd/src/basic/escape.c \
systemd/src/basic/escape.h \
systemd/src/basic/ether-addr-util.c \
systemd/src/basic/ether-addr-util.h \
systemd/src/basic/fd-util.c \
systemd/src/basic/fd-util.h \
systemd/src/basic/fileio.c \
@ -106,6 +108,7 @@ libsystemd_nm_la_SOURCES = \
systemd/src/basic/set.h \
systemd/src/basic/siphash24.c \
systemd/src/basic/siphash24.h \
systemd/src/basic/socket-util.c \
systemd/src/basic/socket-util.h \
systemd/src/basic/sparse-endian.h \
systemd/src/basic/stdio-util.h \
@ -138,14 +141,11 @@ libsystemd_nm_la_SOURCES = \
systemd/src/libsystemd-network/dhcp6-network.c \
systemd/src/libsystemd-network/dhcp6-option.c \
systemd/src/libsystemd-network/dhcp6-protocol.h \
systemd/src/libsystemd-network/lldp-internal.c \
systemd/src/libsystemd-network/lldp-internal.h \
systemd/src/libsystemd-network/lldp-neighbor.c \
systemd/src/libsystemd-network/lldp-neighbor.h \
systemd/src/libsystemd-network/lldp-network.c \
systemd/src/libsystemd-network/lldp-network.h \
systemd/src/libsystemd-network/lldp-port.c \
systemd/src/libsystemd-network/lldp-port.h \
systemd/src/libsystemd-network/lldp-tlv.c \
systemd/src/libsystemd-network/lldp-tlv.h \
systemd/src/libsystemd-network/lldp.h \
systemd/src/libsystemd-network/network-internal.c \
systemd/src/libsystemd-network/network-internal.h \
@ -156,6 +156,7 @@ libsystemd_nm_la_SOURCES = \
systemd/src/libsystemd-network/sd-ipv4acd.c \
systemd/src/libsystemd-network/sd-ipv4ll.c \
systemd/src/libsystemd-network/sd-lldp.c \
systemd/src/libsystemd/sd-event/sd-event.c \
systemd/src/libsystemd/sd-id128/sd-id128.c \
systemd/src/shared/dns-domain.c \
systemd/src/shared/dns-domain.h \

View file

@ -1235,8 +1235,7 @@ update_dynamic_ip_setup (NMDevice *self)
nm_lldp_listener_stop (priv->lldp_listener);
addr = nm_platform_link_get_address (NM_PLATFORM_GET, priv->ifindex, &addr_length);
if (!nm_lldp_listener_start (priv->lldp_listener, nm_device_get_ifindex (self),
nm_device_get_iface (self), addr, addr_length, &error)) {
if (!nm_lldp_listener_start (priv->lldp_listener, nm_device_get_ifindex (self), &error)) {
_LOGD (LOGD_DEVICE, "LLDP listener %p could not be restarted: %s",
priv->lldp_listener, error->message);
g_clear_error (&error);
@ -3564,8 +3563,7 @@ activate_stage2_device_config (NMDevice *self)
addr = nm_platform_link_get_address (NM_PLATFORM_GET, priv->ifindex, &addr_length);
if (nm_lldp_listener_start (priv->lldp_listener, nm_device_get_ifindex (self),
nm_device_get_iface (self), addr, addr_length, &error))
if (nm_lldp_listener_start (priv->lldp_listener, nm_device_get_ifindex (self), &error))
_LOGD (LOGD_DEVICE, "LLDP listener %p started", priv->lldp_listener);
else {
_LOGD (LOGD_DEVICE, "LLDP listener %p could not be started: %s",

View file

@ -21,6 +21,7 @@
#include "nm-default.h"
#include <net/ethernet.h>
#include <errno.h>
#include "sd-lldp.h"
#include "lldp.h"
@ -31,6 +32,10 @@
#define MAX_NEIGHBORS 4096
#define MIN_UPDATE_INTERVAL 2
#define LLDP_MAC_NEAREST_BRIDGE ((const struct ether_addr *) ((uint8_t[ETH_ALEN]) { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e }))
#define LLDP_MAC_NEAREST_NON_TPMR_BRIDGE ((const struct ether_addr *) ((uint8_t[ETH_ALEN]) { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }))
#define LLDP_MAC_NEAREST_CUSTOMER_BRIDGE ((const struct ether_addr *) ((uint8_t[ETH_ALEN]) { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }))
typedef struct {
char *iface;
int ifindex;
@ -58,7 +63,7 @@ typedef struct {
char *chassis_id;
char *port_id;
int dest;
struct ether_addr destination_address;
GHashTable *tlvs;
} LLDPNeighbor;
@ -91,6 +96,16 @@ static void process_lldp_neighbors (NMLldpListener *self);
/*****************************************************************************/
static gboolean
ether_addr_equal (const struct ether_addr *a1, const struct ether_addr *a2)
{
nm_assert (a1);
nm_assert (a2);
G_STATIC_ASSERT_EXPR (sizeof (*a1) == ETH_ALEN);
return memcmp (a1, a2, ETH_ALEN) == 0;
}
static void
gvalue_destroy (gpointer data)
{
@ -101,16 +116,42 @@ gvalue_destroy (gpointer data)
}
static GValue *
gvalue_new_nstr (const char *str, guint16 len)
gvalue_new_str (const char *str)
{
GValue *value;
value = g_slice_new0 (GValue);
g_value_init (value, G_TYPE_STRING);
g_value_take_string (value, strndup (str, len));
g_value_set_string (value, str ?: "");
return value;
}
static GValue *
gvalue_new_str_ptr (const void *str, gsize len)
{
const char *s = str;
const char *tmp;
gsize len0 = len;
gs_free char *str_free = NULL;
gs_free char *str_escaped = NULL;
/* truncate at first NUL, including removing trailing NULs*/
tmp = memchr (s, '\0', len);
if (tmp)
len = tmp - s;
if (!len)
return gvalue_new_str ("");
if (len0 <= len || s[len] != '\0') {
/* hmpf, g_strescape needs a trailing NUL. Need to clone */
s = str_free = g_strndup (s, len);
}
str_escaped = g_strescape (s, NULL);
return gvalue_new_str (str_escaped);
}
static GValue *
gvalue_new_uint (guint val)
{
@ -122,6 +163,21 @@ gvalue_new_uint (guint val)
return value;
}
static GValue *
gvalue_new_uint_u8 (const void *data)
{
return gvalue_new_uint (*((const guint8 *) data));
}
static GValue *
gvalue_new_uint_u16 (const void *data)
{
guint16 v;
memcpy (&v, data, sizeof (v));
return gvalue_new_uint (ntohs (v));
}
static guint
lldp_neighbor_id_hash (gconstpointer ptr)
{
@ -174,7 +230,7 @@ lldp_neighbor_equal (LLDPNeighbor *a, LLDPNeighbor *b)
if ( a->chassis_id_type != b->chassis_id_type
|| a->port_id_type != b->port_id_type
|| a->dest != b->dest
|| ether_addr_equal (&a->destination_address, &b->destination_address)
|| g_strcmp0 (a->chassis_id, b->chassis_id)
|| g_strcmp0 (a->port_id, b->port_id))
return FALSE;
@ -258,13 +314,13 @@ static void
process_lldp_neighbors (NMLldpListener *self)
{
NMLldpListenerPrivate *priv = NM_LLDP_LISTENER_GET_PRIVATE (self);
nm_auto_free sd_lldp_packet **packets = NULL;
nm_auto_free sd_lldp_neighbor **neighbors = NULL;
GHashTable *hash;
int num, i;
int num, i, r;
g_return_if_fail (priv->lldp_handle);
num = sd_lldp_get_packets (priv->lldp_handle, &packets);
num = sd_lldp_get_neighbors (priv->lldp_handle, &neighbors);
if (num < 0) {
_LOGD ("process: error %d retrieving neighbor packets for %s",
num, priv->iface);
@ -274,35 +330,40 @@ process_lldp_neighbors (NMLldpListener *self)
hash = g_hash_table_new_full (lldp_neighbor_id_hash, lldp_neighbor_id_equal,
(GDestroyNotify) lldp_neighbor_free, NULL);
for (i = 0; packets && i < num; i++) {
for (i = 0; neighbors && i < num; i++) {
nm_auto (lldp_neighbor_freep) LLDPNeighbor *neigh = NULL;
uint8_t chassis_id_type, port_id_type, *chassis_id, *port_id, data8;
uint16_t chassis_id_len, port_id_len, len, data16;
uint8_t chassis_id_type, port_id_type;
uint16_t data16;
uint8_t *data8;
const void *chassis_id, *port_id;
gsize chassis_id_len, port_id_len, len;
GValue *value;
char *str;
int r;
const char *str;
if (i >= MAX_NEIGHBORS)
goto next_packet;
break;
r = sd_lldp_packet_read_chassis_id (packets[i], &chassis_id_type,
&chassis_id, &chassis_id_len);
r = sd_lldp_neighbor_get_chassis_id (neighbors[i], &chassis_id_type,
&chassis_id, &chassis_id_len);
if (r < 0)
goto next_packet;
goto next_neighbor;
if (chassis_id_len < 1)
goto next_neighbor;
r = sd_lldp_packet_read_port_id (packets[i], &port_id_type,
&port_id, &port_id_len);
r = sd_lldp_neighbor_get_port_id (neighbors[i], &port_id_type,
&port_id, &port_id_len);
if (r < 0)
goto next_packet;
goto next_neighbor;
if (port_id_len < 1)
goto next_neighbor;
neigh = g_slice_new0 (LLDPNeighbor);
neigh->tlvs = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, gvalue_destroy);
neigh->chassis_id_type = chassis_id_type;
neigh->port_id_type = port_id_type;
sd_lldp_packet_get_destination_type (packets[i], &neigh->dest);
if (chassis_id_len < 1)
goto next_packet;
if (sd_lldp_neighbor_get_destination_address (neighbors[i], &neigh->destination_address) < 0)
goto next_neighbor;
switch (chassis_id_type) {
case LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS:
@ -316,12 +377,9 @@ process_lldp_neighbors (NMLldpListener *self)
break;
default:
_LOGD ("process: unsupported chassis ID type %d", chassis_id_type);
goto next_packet;
goto next_neighbor;
}
if (port_id_len < 1)
goto next_packet;
switch (port_id_type) {
case LLDP_PORT_SUBTYPE_INTERFACE_ALIAS:
case LLDP_PORT_SUBTYPE_INTERFACE_NAME:
@ -334,59 +392,121 @@ process_lldp_neighbors (NMLldpListener *self)
break;
default:
_LOGD ("process: unsupported port ID type %d", port_id_type);
goto next_packet;
goto next_neighbor;
}
if (sd_lldp_packet_read_port_description (packets[i], &str, &len) == 0) {
value = gvalue_new_nstr (str, len);
if (sd_lldp_neighbor_get_port_description (neighbors[i], &str) == 0) {
value = gvalue_new_str (str);
g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_PORT_DESCRIPTION, value);
}
if (sd_lldp_packet_read_system_name (packets[i], &str, &len) == 0) {
value = gvalue_new_nstr (str, len);
if (sd_lldp_neighbor_get_system_name (neighbors[i], &str) == 0) {
value = gvalue_new_str (str);
g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_SYSTEM_NAME, value);
}
if (sd_lldp_packet_read_system_description (packets[i], &str, &len) == 0) {
value = gvalue_new_nstr (str, len);
if (sd_lldp_neighbor_get_system_description (neighbors[i], &str) == 0) {
value = gvalue_new_str (str);
g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_SYSTEM_DESCRIPTION, value);
}
if (sd_lldp_packet_read_system_capability (packets[i], &data16) == 0) {
if (sd_lldp_neighbor_get_system_capabilities (neighbors[i], &data16) == 0) {
value = gvalue_new_uint (data16);
g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_SYSTEM_CAPABILITIES, value);
}
if (sd_lldp_packet_read_port_vlan_id (packets[i], &data16) == 0) {
value = gvalue_new_uint (data16);
g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_IEEE_802_1_PVID, value);
}
if (sd_lldp_neighbor_tlv_rewind (neighbors[i]) < 0)
goto next_neighbor;
do {
guint8 oui[3];
guint8 subtype;
if (sd_lldp_packet_read_port_protocol_vlan_id (packets[i], &data8, &data16) == 0) {
value = gvalue_new_uint (data16);
g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_IEEE_802_1_PPVID, value);
r = sd_lldp_neighbor_tlv_get_oui (neighbors[i], oui, &subtype);
if (r < 0) {
if (r == -ENXIO)
continue;
goto next_neighbor;
}
value = gvalue_new_uint (data8);
g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_IEEE_802_1_PPVID_FLAGS, value);
}
if (!( memcmp (oui, LLDP_OUI_802_1, sizeof (oui)) == 0
&& NM_IN_SET (subtype,
LLDP_OUI_802_1_SUBTYPE_PORT_PROTOCOL_VLAN_ID,
LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID,
LLDP_OUI_802_1_SUBTYPE_VLAN_NAME)))
continue;
if (sd_lldp_packet_read_vlan_name (packets[i], &data16, &str, &len) == 0) {
value = gvalue_new_uint (data16);
g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_IEEE_802_1_VID, value);
if (sd_lldp_neighbor_tlv_get_raw (neighbors[i], (void *) &data8, &len) < 0)
continue;
value = gvalue_new_nstr (str, len);
g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_IEEE_802_1_VLAN_NAME, value);
}
/* skip over leading TLV, OUI and subtype */
#ifdef WITH_MORE_ASSERTS
{
guint8 check_hdr[] = {
0xfe | (((len - 2) >> 8) & 0x01), ((len - 2) & 0xFF),
oui[0], oui[1], oui[2],
subtype
};
nm_assert (len > 2 + 3 +1);
nm_assert (memcmp (data8, check_hdr, sizeof check_hdr) == 0);
}
#endif
if (len <= 6)
goto next_neighbor;
data8 += 6;
len -= 6;
/*if (memcmp (oui, LLDP_OUI_802_1, sizeof (oui)) == 0)*/
{
switch (subtype) {
case LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID:
if (len != 2)
goto next_neighbor;
g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_IEEE_802_1_PVID,
gvalue_new_uint_u16 (data8));
break;
case LLDP_OUI_802_1_SUBTYPE_PORT_PROTOCOL_VLAN_ID:
if (len != 3)
goto next_neighbor;
g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_IEEE_802_1_PPVID_FLAGS,
gvalue_new_uint_u8 (&data8[0]));
g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_IEEE_802_1_PPVID,
gvalue_new_uint_u16 (&data8[1]));
break;
case LLDP_OUI_802_1_SUBTYPE_VLAN_NAME: {
int l;
if (len <= 3)
goto next_neighbor;
l = data8[2];
if (len != 3 + l)
goto next_neighbor;
g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_IEEE_802_1_VID,
gvalue_new_uint_u16 (&data8[0]));
g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_IEEE_802_1_VLAN_NAME,
gvalue_new_str_ptr (&data8[3], len));
break;
}
default:
g_assert_not_reached ();
}
}
} while (sd_lldp_neighbor_tlv_next (neighbors[i]) > 0);
_LOGD ("process: new neigh: CHASSIS='%s' PORT='%s'",
neigh->chassis_id, neigh->port_id);
g_hash_table_add (hash, neigh);
neigh = NULL;
next_packet:
sd_lldp_packet_unref (packets[i]);
next_neighbor:
;
}
for (i = 0; neighbors && i < num; i++)
sd_lldp_neighbor_unref (neighbors[i]);
if (lldp_hash_table_equal (priv->lldp_neighbors, hash)) {
g_hash_table_destroy (hash);
} else {
@ -405,7 +525,7 @@ next_packet:
}
static void
lldp_event_handler (sd_lldp *lldp, int event, void *userdata)
lldp_event_handler (sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n, void *userdata)
{
NMLldpListener *self = userdata;
NMLldpListenerPrivate *priv;
@ -423,15 +543,13 @@ lldp_event_handler (sd_lldp *lldp, int event, void *userdata)
}
gboolean
nm_lldp_listener_start (NMLldpListener *self, int ifindex, const char *iface,
const guint8 *mac, guint mac_len, GError **error)
nm_lldp_listener_start (NMLldpListener *self, int ifindex, GError **error)
{
NMLldpListenerPrivate *priv;
int ret;
g_return_val_if_fail (NM_IS_LLDP_LISTENER (self), FALSE);
g_return_val_if_fail (ifindex > 0, FALSE);
g_return_val_if_fail (iface, FALSE);
g_return_val_if_fail (!error || !*error, FALSE);
priv = NM_LLDP_LISTENER_GET_PRIVATE (self);
@ -442,42 +560,35 @@ nm_lldp_listener_start (NMLldpListener *self, int ifindex, const char *iface,
return FALSE;
}
if (!mac || mac_len != ETH_ALEN) {
g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED,
"unsupported device");
return FALSE;
}
ret = sd_lldp_new (ifindex, iface, (struct ether_addr *) mac, &priv->lldp_handle);
if (ret) {
ret = sd_lldp_new (&priv->lldp_handle, ifindex);
if (ret < 0) {
g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED,
"initialization failed");
return FALSE;
}
ret = sd_lldp_attach_event (priv->lldp_handle, NULL, 0);
if (ret) {
if (ret < 0) {
g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED,
"attach event failed");
goto err_free;
}
ret = sd_lldp_set_callback (priv->lldp_handle, lldp_event_handler, self);
if (ret) {
if (ret < 0) {
g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED,
"set callback failed");
goto err;
}
ret = sd_lldp_start (priv->lldp_handle);
if (ret) {
if (ret < 0) {
g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED,
"start failed");
goto err;
}
priv->ifindex = ifindex;
priv->iface = strdup (iface);
_LOGD ("start");
return TRUE;
@ -503,7 +614,6 @@ nm_lldp_listener_stop (NMLldpListener *self)
sd_lldp_stop (priv->lldp_handle);
sd_lldp_detach_event (priv->lldp_handle);
sd_lldp_unref (priv->lldp_handle);
g_clear_pointer (&priv->iface, g_free);
priv->lldp_handle = NULL;
size = g_hash_table_size (priv->lldp_neighbors);
@ -536,7 +646,6 @@ nm_lldp_listener_get_neighbors (NMLldpListener *self)
GHashTableIter iter;
NMLldpListenerPrivate *priv;
LLDPNeighbor *neigh;
char *dest_str = NULL;
g_return_val_if_fail (NM_IS_LLDP_LISTENER (self), FALSE);
@ -551,6 +660,7 @@ nm_lldp_listener_get_neighbors (NMLldpListener *self)
while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &neigh)) {
GHashTableIter val_iter;
gpointer key, val;
const char *dest_str;
g_variant_builder_init (&neigh_builder, G_VARIANT_TYPE ("a{sv}"));
@ -567,18 +677,14 @@ nm_lldp_listener_get_neighbors (NMLldpListener *self)
NM_LLDP_ATTR_PORT_ID,
g_variant_new_string (neigh->port_id));
switch (neigh->dest) {
case SD_LLDP_DESTINATION_TYPE_NEAREST_BRIDGE:
if (ether_addr_equal (&neigh->destination_address, LLDP_MAC_NEAREST_BRIDGE))
dest_str = NM_LLDP_DEST_NEAREST_BRIDGE;
break;
case SD_LLDP_DESTINATION_TYPE_NEAREST_NON_TPMR_BRIDGE:
else if (ether_addr_equal (&neigh->destination_address, LLDP_MAC_NEAREST_NON_TPMR_BRIDGE))
dest_str = NM_LLDP_DEST_NEAREST_NON_TPMR_BRIDGE;
break;
case SD_LLDP_DESTINATION_TYPE_NEAREST_CUSTOMER_BRIDGE:
else if (ether_addr_equal (&neigh->destination_address, LLDP_MAC_NEAREST_CUSTOMER_BRIDGE))
dest_str = NM_LLDP_DEST_NEAREST_CUSTOMER_BRIDGE;
break;
}
else
dest_str = NULL;
if (dest_str) {
g_variant_builder_add (&neigh_builder, "{sv}",
NM_LLDP_ATTR_DESTINATION,

View file

@ -42,8 +42,7 @@ typedef struct {
GType nm_lldp_listener_get_type (void);
NMLldpListener *nm_lldp_listener_new (void);
gboolean nm_lldp_listener_start (NMLldpListener *self, int ifindex, const char *iface,
const guint8 *mac, guint mac_len, GError **error);
gboolean nm_lldp_listener_start (NMLldpListener *self, int ifindex, GError **error);
void nm_lldp_listener_stop (NMLldpListener *self);
gboolean nm_lldp_listener_is_running (NMLldpListener *self);

View file

@ -27,6 +27,7 @@
#include <sys/types.h>
#include "nm-lldp-listener.h"
#include "nm-sd-adapt.h"
#include "lldp.h"
@ -330,9 +331,6 @@ _test_recv_data2_ttl1_check (GMainLoop *loop, NMLldpListener *listener)
_test_recv_data0_check (loop, listener);
g_test_skip ("the test is known to fail");
return;
/* wait for signal. */
notify_id = g_signal_connect (listener, "notify::" NM_LLDP_LISTENER_NEIGHBORS,
nmtst_main_loop_quit_on_notify, loop);
@ -396,14 +394,18 @@ test_recv (TestRecvFixture *fixture, gconstpointer user_data)
TestRecvCallbackInfo info = { };
gsize i_frames;
gulong notify_id;
GError *error = NULL;
guint sd_id;
listener = nm_lldp_listener_new ();
g_assert (listener != NULL);
g_assert (nm_lldp_listener_start (listener, fixture->ifindex, TEST_IFNAME, fixture->mac, ETH_ALEN, NULL));
g_assert (nm_lldp_listener_start (listener, fixture->ifindex, &error));
g_assert_no_error (error);
notify_id = g_signal_connect (listener, "notify::" NM_LLDP_LISTENER_NEIGHBORS,
(GCallback) lldp_neighbors_changed, &info);
loop = g_main_loop_new (NULL, FALSE);
sd_id = nm_sd_event_attach_default ();
for (i_frames = 0; i_frames < data->frames_len; i_frames++) {
const TestRecvFrame *f = data->frames[i_frames];
@ -420,6 +422,7 @@ test_recv (TestRecvFixture *fixture, gconstpointer user_data)
data->check (loop, listener);
nm_clear_g_source (&sd_id);
g_clear_pointer (&loop, g_main_loop_unref);
}

View file

@ -50,6 +50,7 @@
#include "nm-auth-manager.h"
#include "nm-core-internal.h"
#include "nm-exported-object.h"
#include "nm-sd-adapt.h"
#if !defined(NM_DIST_VERSION)
# define NM_DIST_VERSION VERSION
@ -271,6 +272,7 @@ main (int argc, char *argv[])
gboolean wrote_pidfile = FALSE;
char *bad_domains = NULL;
NMConfigCmdLineOptions *config_cli;
guint sd_id = 0;
nm_g_type_init ();
@ -474,8 +476,11 @@ main (int argc, char *argv[])
success = TRUE;
if (configure_and_quit == FALSE)
if (configure_and_quit == FALSE) {
sd_id = nm_sd_event_attach_default ();
g_main_loop_run (main_loop);
}
done:
nm_exported_object_class_set_quitting ();
@ -486,5 +491,8 @@ done:
unlink (global_opt.pidfile);
nm_log_info (LOGD_CORE, "exiting (%s)", success ? "success" : "error");
nm_clear_g_source (&sd_id);
exit (success ? 0 : 1);
}

View file

@ -44,6 +44,7 @@ extern unsigned int if_nametoindex (const char *__ifname);
#include "nm-lndp-rdisc.h"
#include "nm-utils.h"
#include "nm-setting-ip6-config.h"
#include "nm-sd-adapt.h"
#if !defined(NM_DIST_VERSION)
# define NM_DIST_VERSION VERSION
@ -345,6 +346,7 @@ main (int argc, char *argv[])
size_t hwaddr_len = 0;
gconstpointer tmp;
gs_free NMUtilsIPv6IfaceId *iid = NULL;
guint sd_id;
nm_g_type_init ();
@ -494,6 +496,8 @@ main (int argc, char *argv[])
nm_rdisc_start (rdisc);
}
sd_id = nm_sd_event_attach_default ();
g_main_loop_run (main_loop);
g_clear_pointer (&hwaddr, g_byte_array_unref);
@ -502,6 +506,8 @@ main (int argc, char *argv[])
unlink (pidfile);
nm_log_info (LOGD_CORE, "exiting");
nm_clear_g_source (&sd_id);
exit (0);
}

View file

@ -20,219 +20,126 @@
#include "nm-sd-adapt.h"
#include <unistd.h>
#include <errno.h>
#include "sd-event.h"
#include "fd-util.h"
#include "time-util.h"
struct sd_event_source {
guint refcount;
guint id;
gpointer user_data;
GIOChannel *channel;
union {
struct {
sd_event_io_handler_t cb;
} io;
struct {
sd_event_time_handler_t cb;
uint64_t usec;
} time;
};
};
static struct sd_event_source *
source_new (void)
{
struct sd_event_source *source;
source = g_slice_new0 (struct sd_event_source);
source->refcount = 1;
return source;
}
/*****************************************************************************/
int
sd_event_source_set_priority (sd_event_source *s, int64_t priority)
{
return 0;
}
sd_event_source*
sd_event_source_unref (sd_event_source *s)
{
if (!s)
return NULL;
g_return_val_if_fail (s->refcount, NULL);
s->refcount--;
if (s->refcount == 0) {
if (s->id)
g_source_remove (s->id);
if (s->channel) {
/* Don't shut down the channel since systemd will soon close
* the file descriptor itself, which would cause -EBADF.
*/
g_io_channel_unref (s->channel);
}
g_slice_free (struct sd_event_source, s);
}
return NULL;
}
int
sd_event_source_set_description(sd_event_source *s, const char *description)
{
if (!s)
return -EINVAL;
g_source_set_name_by_id (s->id, description);
return 0;
}
static gboolean
io_ready (GIOChannel *channel, GIOCondition condition, struct sd_event_source *source)
{
int r, revents = 0;
gboolean result;
if (condition & G_IO_IN)
revents |= EPOLLIN;
if (condition & G_IO_OUT)
revents |= EPOLLOUT;
if (condition & G_IO_PRI)
revents |= EPOLLPRI;
if (condition & G_IO_ERR)
revents |= EPOLLERR;
if (condition & G_IO_HUP)
revents |= EPOLLHUP;
source->refcount++;
r = source->io.cb (source, g_io_channel_unix_get_fd (channel), revents, source->user_data);
if (r < 0 || source->refcount <= 1) {
source->id = 0;
result = G_SOURCE_REMOVE;
} else
result = G_SOURCE_CONTINUE;
sd_event_source_unref (source);
return result;
}
int
sd_event_add_io (sd_event *e, sd_event_source **s, int fd, uint32_t events, sd_event_io_handler_t callback, void *userdata)
{
struct sd_event_source *source;
GIOChannel *channel;
GIOCondition condition = 0;
/* systemd supports floating sd_event_source by omitting the @s argument.
* We don't have such users and don't implement floating references. */
g_return_val_if_fail (s, -EINVAL);
channel = g_io_channel_unix_new (fd);
if (!channel)
return -EINVAL;
source = source_new ();
source->io.cb = callback;
source->user_data = userdata;
source->channel = channel;
if (events & EPOLLIN)
condition |= G_IO_IN;
if (events & EPOLLOUT)
condition |= G_IO_OUT;
if (events & EPOLLPRI)
condition |= G_IO_PRI;
if (events & EPOLLERR)
condition |= G_IO_ERR;
if (events & EPOLLHUP)
condition |= G_IO_HUP;
g_io_channel_set_encoding (source->channel, NULL, NULL);
g_io_channel_set_buffered (source->channel, FALSE);
source->id = g_io_add_watch (source->channel, condition, (GIOFunc) io_ready, source);
*s = source;
return 0;
}
static gboolean
time_ready (struct sd_event_source *source)
{
source->refcount++;
source->time.cb (source, source->time.usec, source->user_data);
source->id = 0;
sd_event_source_unref (source);
return G_SOURCE_REMOVE;
}
int
sd_event_add_time(sd_event *e, sd_event_source **s, clockid_t clock, uint64_t usec, uint64_t accuracy, sd_event_time_handler_t callback, void *userdata)
{
struct sd_event_source *source;
uint64_t n = now (clock);
/* systemd supports floating sd_event_source by omitting the @s argument.
* We don't have such users and don't implement floating references. */
g_return_val_if_fail (s, -EINVAL);
source = source_new ();
source->time.cb = callback;
source->user_data = userdata;
source->time.usec = usec;
if (usec > 1000)
usec = n < usec - 1000 ? usec - n : 1000;
source->id = g_timeout_add (usec / 1000, (GSourceFunc) time_ready, source);
*s = source;
return 0;
}
/* sd_event is basically a GMainContext; but since we only
* ever use the default context, nothing to do here.
*/
int
sd_event_default (sd_event **e)
{
*e = GUINT_TO_POINTER (1);
return 0;
}
sd_event*
sd_event_ref (sd_event *e)
{
return e;
}
sd_event*
sd_event_unref (sd_event *e)
{
return NULL;
}
int
sd_event_now (sd_event *e, clockid_t clock, uint64_t *usec)
{
*usec = now (clock);
return 0;
}
int asynchronous_close(int fd) {
safe_close(fd);
asynchronous_close (int fd) {
safe_close (fd);
return -1;
}
/*****************************************************************************
* Integrating sd_event into glib. Taken and adjusted from
* https://www.freedesktop.org/software/systemd/man/sd_event_get_fd.html
*****************************************************************************/
typedef struct SDEventSource {
GSource source;
GPollFD pollfd;
sd_event *event;
guint *default_source_id;
} SDEventSource;
static gboolean
event_prepare (GSource *source, gint *timeout_)
{
return sd_event_prepare (((SDEventSource *) source)->event) > 0;
}
static gboolean
event_check (GSource *source)
{
return sd_event_wait (((SDEventSource *) source)->event, 0) > 0;
}
static gboolean
event_dispatch (GSource *source, GSourceFunc callback, gpointer user_data)
{
return sd_event_dispatch (((SDEventSource *)source)->event) > 0;
}
static void
event_finalize (GSource *source)
{
SDEventSource *s;
s = (SDEventSource *) source;
sd_event_unref (s->event);
if (s->default_source_id)
*s->default_source_id = 0;
}
static SDEventSource *
event_create_source (sd_event *event, guint *default_source_id)
{
static GSourceFuncs event_funcs = {
.prepare = event_prepare,
.check = event_check,
.dispatch = event_dispatch,
.finalize = event_finalize,
};
SDEventSource *source;
g_return_val_if_fail (event, NULL);
source = (SDEventSource *) g_source_new (&event_funcs, sizeof (SDEventSource));
source->event = sd_event_ref (event);
source->pollfd.fd = sd_event_get_fd (event);
source->pollfd.events = G_IO_IN | G_IO_HUP | G_IO_ERR;
source->default_source_id = default_source_id;
g_source_add_poll ((GSource *) source, &source->pollfd);
return source;
}
static guint
event_attach (sd_event *event, GMainContext *context)
{
SDEventSource *source;
guint id;
int r;
sd_event *e = event;
guint *p_default_source_id = NULL;
if (!e) {
static guint default_source_id = 0;
if (default_source_id) {
/* The default event cannot be registered multiple times. */
g_return_val_if_reached (0);
}
r = sd_event_default (&e);
if (r < 0)
g_return_val_if_reached (0);
p_default_source_id = &default_source_id;
}
source = event_create_source (e, p_default_source_id);
id = g_source_attach ((GSource *) source, context);
g_source_unref ((GSource *) source);
if (!event) {
*p_default_source_id = id;
sd_event_unref (e);
}
g_return_val_if_fail (id, 0);
return id;
}
guint
nm_sd_event_attach_default (void)
{
return event_attach (NULL, NULL);
}
/*****************************************************************************/

View file

@ -26,6 +26,8 @@
#include <sys/resource.h>
#include <time.h>
guint nm_sd_event_attach_default (void);
#define noreturn G_GNUC_NORETURN
#ifndef CLOCK_BOOTTIME
@ -102,6 +104,7 @@ G_STMT_START { \
#endif
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <net/if_arp.h>
@ -110,6 +113,18 @@ G_STMT_START { \
#define BPF_XOR 0xa0
#endif
#ifndef ETHERTYPE_LLDP
#define ETHERTYPE_LLDP 0x88cc
#endif
#ifndef HAVE_SECURE_GETENV
# ifdef HAVE___SECURE_GETENV
# define secure_getenv __secure_getenv
# else
# error neither secure_getenv nor __secure_getenv is available
# endif
#endif
/*****************************************************************************/
/* work around missing uchar.h */
@ -118,6 +133,14 @@ typedef guint32 char32_t;
/*****************************************************************************/
#define PID_TO_PTR(p) ((void*) ((uintptr_t) p))
static inline int
sd_notify (int unset_environment, const char *state)
{
return 0;
}
/* Can't include both net/if.h and linux/if.h; so have to define this here */
#ifndef IFNAMSIZ
#define IFNAMSIZ 16

View file

@ -51,25 +51,29 @@ static inline void freep(void *p) {
#define _cleanup_free_ _cleanup_(freep)
_malloc_ _alloc_(1, 2) static inline void *malloc_multiply(size_t a, size_t b) {
if (_unlikely_(b != 0 && a > ((size_t) -1) / b))
return NULL;
return malloc(a * b);
static inline bool size_multiply_overflow(size_t size, size_t need) {
return _unlikely_(need != 0 && size > (SIZE_MAX / need));
}
_alloc_(2, 3) static inline void *realloc_multiply(void *p, size_t a, size_t b) {
if (_unlikely_(b != 0 && a > ((size_t) -1) / b))
_malloc_ _alloc_(1, 2) static inline void *malloc_multiply(size_t size, size_t need) {
if (size_multiply_overflow(size, need))
return NULL;
return realloc(p, a * b);
return malloc(size * need);
}
_alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t a, size_t b) {
if (_unlikely_(b != 0 && a > ((size_t) -1) / b))
_alloc_(2, 3) static inline void *realloc_multiply(void *p, size_t size, size_t need) {
if (size_multiply_overflow(size, need))
return NULL;
return memdup(p, a * b);
return realloc(p, size * need);
}
_alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t size, size_t need) {
if (size_multiply_overflow(size, need))
return NULL;
return memdup(p, size * need);
}
void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size);

View file

@ -415,6 +415,34 @@ char *xescape(const char *s, const char *bad) {
return r;
}
char *octescape(const char *s, size_t len) {
char *r, *t;
const char *f;
/* Escapes all chars in bad, in addition to \ and " chars,
* in \nnn style escaping. */
r = new(char, len * 4 + 1);
if (!r)
return NULL;
for (f = s, t = r; f < s + len; f++) {
if (*f < ' ' || *f >= 127 || *f == '\\' || *f == '"') {
*(t++) = '\\';
*(t++) = '0' + (*f >> 6);
*(t++) = '0' + ((*f >> 3) & 8);
*(t++) = '0' + (*f & 8);
} else
*(t++) = *f;
}
*t = 0;
return r;
}
static char *strcpy_backslash_escaped(char *t, const char *s, const char *bad) {
assert(bad);

View file

@ -52,6 +52,7 @@ int cunescape_length_with_prefix(const char *s, size_t length, const char *prefi
int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit);
char *xescape(const char *s, const char *bad);
char *octescape(const char *s, size_t len);
char *shell_escape(const char *s, const char *bad);
char *shell_maybe_quote(const char *s);

View file

@ -0,0 +1,58 @@
/***
This file is part of systemd.
Copyright 2014 Tom Gundersen
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "nm-sd-adapt.h"
#include <net/ethernet.h>
#include <stdio.h>
#include <sys/types.h>
#include "ether-addr-util.h"
#include "macro.h"
char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]) {
assert(addr);
assert(buffer);
/* Like ether_ntoa() but uses %02x instead of %x to print
* ethernet addresses, which makes them look less funny. Also,
* doesn't use a static buffer. */
sprintf(buffer, "%02x:%02x:%02x:%02x:%02x:%02x",
addr->ether_addr_octet[0],
addr->ether_addr_octet[1],
addr->ether_addr_octet[2],
addr->ether_addr_octet[3],
addr->ether_addr_octet[4],
addr->ether_addr_octet[5]);
return buffer;
}
bool ether_addr_equal(const struct ether_addr *a, const struct ether_addr *b) {
assert(a);
assert(b);
return a->ether_addr_octet[0] == b->ether_addr_octet[0] &&
a->ether_addr_octet[1] == b->ether_addr_octet[1] &&
a->ether_addr_octet[2] == b->ether_addr_octet[2] &&
a->ether_addr_octet[3] == b->ether_addr_octet[3] &&
a->ether_addr_octet[4] == b->ether_addr_octet[4] &&
a->ether_addr_octet[5] == b->ether_addr_octet[5];
}

View file

@ -0,0 +1,37 @@
#pragma once
/***
This file is part of systemd.
Copyright 2014 Tom Gundersen
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <net/ethernet.h>
#include <stdbool.h>
#define ETHER_ADDR_FORMAT_STR "%02X%02X%02X%02X%02X%02X"
#define ETHER_ADDR_FORMAT_VAL(x) (x).ether_addr_octet[0], (x).ether_addr_octet[1], (x).ether_addr_octet[2], (x).ether_addr_octet[3], (x).ether_addr_octet[4], (x).ether_addr_octet[5]
#define ETHER_ADDR_TO_STRING_MAX (3*6)
char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]);
bool ether_addr_equal(const struct ether_addr *a, const struct ether_addr *b);
#define ETHER_ADDR_NULL ((const struct ether_addr){})
static inline bool ether_addr_is_null(const struct ether_addr *addr) {
return ether_addr_equal(addr, &ETHER_ADDR_NULL);
}

View file

@ -42,9 +42,7 @@
#include "parse-util.h"
#include "path-util.h"
#include "random-util.h"
#if 0 /* NM_IGNORED */
#include "stdio-util.h"
#endif /* NM_IGNORED */
#include "string-util.h"
#include "strv.h"
#include "time-util.h"
@ -356,7 +354,7 @@ static int parse_env_file_internal(
case KEY:
if (strchr(newline, c)) {
state = PRE_KEY;
line ++;
line++;
n_key = 0;
} else if (c == '=') {
state = PRE_VALUE;
@ -380,7 +378,7 @@ static int parse_env_file_internal(
case PRE_VALUE:
if (strchr(newline, c)) {
state = PRE_KEY;
line ++;
line++;
key[n_key] = 0;
if (value)
@ -420,7 +418,7 @@ static int parse_env_file_internal(
case VALUE:
if (strchr(newline, c)) {
state = PRE_KEY;
line ++;
line++;
key[n_key] = 0;
@ -539,7 +537,7 @@ static int parse_env_file_internal(
state = COMMENT_ESCAPE;
else if (strchr(newline, c)) {
state = PRE_KEY;
line ++;
line++;
}
break;
@ -912,7 +910,7 @@ int get_proc_field(const char *filename, const char *pattern, const char *termin
/* Back off one char if there's nothing but whitespace
and zeros */
if (!*t || isspace(*t))
t --;
t--;
}
len = strcspn(t, terminator);

View file

@ -291,24 +291,6 @@ int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
return 0;
}
int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid) {
assert(fd >= 0);
/* Under the assumption that we are running privileged we
* first change the access mode and only then hand out
* ownership to avoid a window where access is too open. */
if (mode != MODE_INVALID)
if (fchmod(fd, mode) < 0)
return -errno;
if (uid != UID_INVALID || gid != GID_INVALID)
if (fchown(fd, uid, gid) < 0)
return -errno;
return 0;
}
#endif /* NM_IGNORED */
int fchmod_umask(int fd, mode_t m) {

View file

@ -43,7 +43,6 @@ int readlink_and_canonicalize(const char *p, char **r);
int readlink_and_make_absolute_root(const char *root, const char *path, char **ret);
int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid);
int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid);
int fchmod_umask(int fd, mode_t mode);

View file

@ -180,7 +180,7 @@ enum HashmapType {
};
struct _packed_ indirect_storage {
char *storage; /* where buckets and DIBs are stored */
void *storage; /* where buckets and DIBs are stored */
uint8_t hash_key[HASH_KEY_SIZE]; /* hash key; changes during resize */
unsigned n_entries; /* number of stored entries */
@ -197,7 +197,7 @@ struct direct_storage {
/* This gives us 39 bytes on 64bit, or 35 bytes on 32bit.
* That's room for 4 set_entries + 4 DIB bytes + 3 unused bytes on 64bit,
* or 7 set_entries + 7 DIB bytes + 0 unused bytes on 32bit. */
char storage[sizeof(struct indirect_storage)];
uint8_t storage[sizeof(struct indirect_storage)];
};
#define DIRECT_BUCKETS(entry_t) \
@ -306,7 +306,7 @@ static void n_entries_dec(HashmapBase *h) {
h->n_direct_entries--;
}
static char *storage_ptr(HashmapBase *h) {
static void *storage_ptr(HashmapBase *h) {
return h->has_indirect ? h->indirect.storage
: h->direct.storage;
}
@ -351,7 +351,7 @@ static void get_hash_key(uint8_t hash_key[HASH_KEY_SIZE], bool reuse_is_ok) {
static struct hashmap_base_entry *bucket_at(HashmapBase *h, unsigned idx) {
return (struct hashmap_base_entry*)
(storage_ptr(h) + idx * hashmap_type_info[h->type].entry_size);
((uint8_t*) storage_ptr(h) + idx * hashmap_type_info[h->type].entry_size);
}
static struct plain_hashmap_entry *plain_bucket_at(Hashmap *h, unsigned idx) {
@ -385,7 +385,7 @@ static struct hashmap_base_entry *bucket_at_virtual(HashmapBase *h, struct swap_
static dib_raw_t *dib_raw_ptr(HashmapBase *h) {
return (dib_raw_t*)
(storage_ptr(h) + hashmap_type_info[h->type].entry_size * n_buckets(h));
((uint8_t*) storage_ptr(h) + hashmap_type_info[h->type].entry_size * n_buckets(h));
}
static unsigned bucket_distance(HashmapBase *h, unsigned idx, unsigned from) {
@ -1032,7 +1032,7 @@ static int hashmap_base_put_boldly(HashmapBase *h, unsigned idx,
*/
static int resize_buckets(HashmapBase *h, unsigned entries_add) {
struct swap_entries swap;
char *new_storage;
void *new_storage;
dib_raw_t *old_dibs, *new_dibs;
const struct hashmap_type_info *hi;
unsigned idx, optimal_idx;
@ -1099,7 +1099,7 @@ static int resize_buckets(HashmapBase *h, unsigned entries_add) {
h->indirect.n_buckets = (1U << new_shift) /
(hi->entry_size + sizeof(dib_raw_t));
old_dibs = (dib_raw_t*)(new_storage + hi->entry_size * old_n_buckets);
old_dibs = (dib_raw_t*)((uint8_t*) new_storage + hi->entry_size * old_n_buckets);
new_dibs = dib_raw_ptr(h);
/*

View file

@ -27,6 +27,7 @@
#include "alloc-util.h"
#include "hexdecoct.h"
#include "macro.h"
#include "util.h"
char octchar(int x) {
return '0' + (x & 7);
@ -277,8 +278,8 @@ int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *_l
if (padding) {
/* strip the padding */
while (l > 0 && p[l - 1] == '=' && pad < 7) {
pad ++;
l --;
pad++;
l--;
}
}
@ -506,7 +507,7 @@ int unbase64char(char c) {
if (c == '+')
return offset;
offset ++;
offset++;
if (c == '/')
return offset;
@ -574,7 +575,7 @@ static int base64_append_width(char **prefix, int plen,
if (!t)
return -ENOMEM;
memcpy(t + plen, sep, slen);
memcpy_safe(t + plen, sep, slen);
for (line = 0, s = t + plen + slen, avail = len; line < lines; line++) {
int act = MIN(width, avail);
@ -622,9 +623,9 @@ int unbase64mem(const char *p, size_t l, void **mem, size_t *_len) {
/* strip the padding */
if (l > 0 && p[l - 1] == '=')
l --;
l--;
if (l > 0 && p[l - 1] == '=')
l --;
l--;
/* a group of four input bytes needs three output bytes, in case of
padding we need to add two or three extra bytes */

View file

@ -19,7 +19,6 @@
#include "nm-sd-adapt.h"
#include <bits/local_lim.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
@ -51,6 +50,10 @@ bool hostname_is_set(void) {
char* gethostname_malloc(void) {
struct utsname u;
/* This call tries to return something useful, either the actual hostname
* or it makes something up. The only reason it might fail is OOM.
* It might even return "localhost" if that's set. */
assert_se(uname(&u) >= 0);
if (isempty(u.nodename) || streq(u.nodename, "(none)"))
@ -59,6 +62,31 @@ char* gethostname_malloc(void) {
return strdup(u.nodename);
}
int gethostname_strict(char **ret) {
struct utsname u;
char *k;
/* This call will rather fail than make up a name. It will not return "localhost" either. */
assert_se(uname(&u) >= 0);
if (isempty(u.nodename))
return -ENXIO;
if (streq(u.nodename, "(none)"))
return -ENXIO;
if (is_localhost(u.nodename))
return -ENXIO;
k = strdup(u.nodename);
if (!k)
return -ENOMEM;
*ret = k;
return 0;
}
static bool hostname_valid_char(char c) {
return
(c >= 'a' && c <= 'z') ||
@ -98,7 +126,7 @@ bool hostname_is_valid(const char *s, bool allow_trailing_dot) {
return false;
dot = true;
n_dots ++;
n_dots++;
} else {
if (!hostname_valid_char(*p))
return false;
@ -124,6 +152,8 @@ char* hostname_cleanup(char *s) {
assert(s);
strshorten(s, HOST_NAME_MAX);
for (p = s, d = s, dot = true; *p; p++) {
if (*p == '.') {
if (dot)
@ -143,8 +173,6 @@ char* hostname_cleanup(char *s) {
else
*d = 0;
strshorten(s, HOST_NAME_MAX);
return s;
}

View file

@ -26,6 +26,7 @@
bool hostname_is_set(void);
char* gethostname_malloc(void);
int gethostname_strict(char **ret);
bool hostname_is_valid(const char *s, bool allow_trailing_dot) _pure_;
char* hostname_cleanup(char *s);

View file

@ -251,7 +251,7 @@ ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length) {
} else if (n > 0)
q += n;
else
q ++;
q++;
}
if (q > w) {

View file

@ -46,7 +46,7 @@ ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length);
char *_s = (char *)(s); \
_i->iov_base = _s; \
_i->iov_len = strlen(_s); \
} while(false)
} while (false)
static inline size_t IOVEC_TOTAL_SIZE(const struct iovec *i, unsigned n) {
unsigned j;

View file

@ -32,7 +32,7 @@
#define LIST_HEAD_INIT(head) \
do { \
(head) = NULL; } \
while(false)
while (false)
/* Initialize a list item */
#define LIST_INIT(name,item) \
@ -40,7 +40,7 @@
typeof(*(item)) *_item = (item); \
assert(_item); \
_item->name##_prev = _item->name##_next = NULL; \
} while(false)
} while (false)
/* Prepend an item to the list */
#define LIST_PREPEND(name,head,item) \
@ -51,7 +51,7 @@
_item->name##_next->name##_prev = _item; \
_item->name##_prev = NULL; \
*_head = _item; \
} while(false)
} while (false)
/* Append an item to the list */
#define LIST_APPEND(name,head,item) \
@ -59,7 +59,7 @@
typeof(*(head)) *_tail; \
LIST_FIND_TAIL(name,head,_tail); \
LIST_INSERT_AFTER(name,head,_tail,item); \
} while(false)
} while (false)
/* Remove an item from the list */
#define LIST_REMOVE(name,head,item) \
@ -75,7 +75,7 @@
*_head = _item->name##_next; \
} \
_item->name##_next = _item->name##_prev = NULL; \
} while(false)
} while (false)
/* Find the head of the list */
#define LIST_FIND_HEAD(name,item,head) \
@ -119,7 +119,7 @@
_b->name##_prev = _a; \
_a->name##_next = _b; \
} \
} while(false)
} while (false)
/* Insert an item before another one (a = where, b = what) */
#define LIST_INSERT_BEFORE(name,head,a,b) \
@ -145,7 +145,7 @@
_b->name##_next = _a; \
_a->name##_prev = _b; \
} \
} while(false)
} while (false)
#define LIST_JUST_US(name,item) \
(!(item)->name##_prev && !(item)->name##_next) \

View file

@ -195,7 +195,7 @@ void log_assert_failed_return(
#ifdef LOG_TRACE
# define log_trace(...) log_debug(__VA_ARGS__)
#else
# define log_trace(...) do {} while(0)
# define log_trace(...) do {} while (0)
#endif
/* Structured logging */

View file

@ -233,7 +233,7 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) {
/* We override the glibc assert() here. */
#undef assert
#ifdef NDEBUG
#define assert(expr) do {} while(false)
#define assert(expr) do {} while (false)
#else
#define assert(expr) assert_message_se(expr, #expr)
#endif
@ -370,6 +370,12 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) {
_found; \
})
#define SWAP_TWO(x, y) do { \
typeof(x) _t = (x); \
(x) = (y); \
(y) = (_t); \
} while (false)
/* Define C11 thread_local attribute even on older gcc compiler
* version */
#ifndef thread_local

View file

@ -36,7 +36,7 @@ void* mempool_alloc0_tile(struct mempool *mp);
void mempool_free_tile(struct mempool *mp, void *p);
#define DEFINE_MEMPOOL(pool_name, tile_type, alloc_at_least) \
struct mempool pool_name = { \
static struct mempool pool_name = { \
.tile_size = sizeof(tile_type), \
.at_least = alloc_at_least, \
}

View file

@ -511,7 +511,7 @@ int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) {
s = *p;
/* accept any number of digits, strtoull is limted to 19 */
for(i=0; i < digits; i++,s++) {
for (i=0; i < digits; i++,s++) {
if (*s < '0' || *s > '9') {
if (i == 0)
return -EINVAL;

View file

@ -126,6 +126,9 @@ int set_put_strdupv(Set *s, char **l);
#define SET_FOREACH(e, s, i) \
for ((i) = ITERATOR_FIRST; set_iterate((s), &(i), (void**)&(e)); )
#define SET_FOREACH_MOVE(e, d, s) \
for (; ({ e = set_first(s); assert_se(!e || set_move_one(d, s, e) >= 0); e; }); )
DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free_free);

View file

@ -0,0 +1,982 @@
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "nm-sd-adapt.h"
#include <arpa/inet.h>
#include <errno.h>
#include <limits.h>
#include <net/if.h>
#include <netdb.h>
#include <netinet/ip.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "alloc-util.h"
#include "fd-util.h"
#include "fileio.h"
#if 0 /* NM_IGNORED */
#include "formats-util.h"
#endif /* NM_IGNORED */
#include "log.h"
#include "macro.h"
#if 0 /* NM_IGNORED */
#include "missing.h"
#endif /* NM_IGNORED */
#include "parse-util.h"
#include "path-util.h"
#include "socket-util.h"
#include "string-table.h"
#include "string-util.h"
#if 0 /* NM_IGNORED */
#include "user-util.h"
#endif /* NM_IGNORED */
#include "util.h"
#if 0 /* NM_IGNORED */
int socket_address_parse(SocketAddress *a, const char *s) {
char *e, *n;
unsigned u;
int r;
assert(a);
assert(s);
zero(*a);
a->type = SOCK_STREAM;
if (*s == '[') {
/* IPv6 in [x:.....:z]:p notation */
e = strchr(s+1, ']');
if (!e)
return -EINVAL;
n = strndupa(s+1, e-s-1);
errno = 0;
if (inet_pton(AF_INET6, n, &a->sockaddr.in6.sin6_addr) <= 0)
return errno > 0 ? -errno : -EINVAL;
e++;
if (*e != ':')
return -EINVAL;
e++;
r = safe_atou(e, &u);
if (r < 0)
return r;
if (u <= 0 || u > 0xFFFF)
return -EINVAL;
a->sockaddr.in6.sin6_family = AF_INET6;
a->sockaddr.in6.sin6_port = htons((uint16_t) u);
a->size = sizeof(struct sockaddr_in6);
} else if (*s == '/') {
/* AF_UNIX socket */
size_t l;
l = strlen(s);
if (l >= sizeof(a->sockaddr.un.sun_path))
return -EINVAL;
a->sockaddr.un.sun_family = AF_UNIX;
memcpy(a->sockaddr.un.sun_path, s, l);
a->size = offsetof(struct sockaddr_un, sun_path) + l + 1;
} else if (*s == '@') {
/* Abstract AF_UNIX socket */
size_t l;
l = strlen(s+1);
if (l >= sizeof(a->sockaddr.un.sun_path) - 1)
return -EINVAL;
a->sockaddr.un.sun_family = AF_UNIX;
memcpy(a->sockaddr.un.sun_path+1, s+1, l);
a->size = offsetof(struct sockaddr_un, sun_path) + 1 + l;
} else {
e = strchr(s, ':');
if (e) {
r = safe_atou(e+1, &u);
if (r < 0)
return r;
if (u <= 0 || u > 0xFFFF)
return -EINVAL;
n = strndupa(s, e-s);
/* IPv4 in w.x.y.z:p notation? */
r = inet_pton(AF_INET, n, &a->sockaddr.in.sin_addr);
if (r < 0)
return -errno;
if (r > 0) {
/* Gotcha, it's a traditional IPv4 address */
a->sockaddr.in.sin_family = AF_INET;
a->sockaddr.in.sin_port = htons((uint16_t) u);
a->size = sizeof(struct sockaddr_in);
} else {
unsigned idx;
if (strlen(n) > IF_NAMESIZE-1)
return -EINVAL;
/* Uh, our last resort, an interface name */
idx = if_nametoindex(n);
if (idx == 0)
return -EINVAL;
a->sockaddr.in6.sin6_family = AF_INET6;
a->sockaddr.in6.sin6_port = htons((uint16_t) u);
a->sockaddr.in6.sin6_scope_id = idx;
a->sockaddr.in6.sin6_addr = in6addr_any;
a->size = sizeof(struct sockaddr_in6);
}
} else {
/* Just a port */
r = safe_atou(s, &u);
if (r < 0)
return r;
if (u <= 0 || u > 0xFFFF)
return -EINVAL;
if (socket_ipv6_is_supported()) {
a->sockaddr.in6.sin6_family = AF_INET6;
a->sockaddr.in6.sin6_port = htons((uint16_t) u);
a->sockaddr.in6.sin6_addr = in6addr_any;
a->size = sizeof(struct sockaddr_in6);
} else {
a->sockaddr.in.sin_family = AF_INET;
a->sockaddr.in.sin_port = htons((uint16_t) u);
a->sockaddr.in.sin_addr.s_addr = INADDR_ANY;
a->size = sizeof(struct sockaddr_in);
}
}
}
return 0;
}
int socket_address_parse_and_warn(SocketAddress *a, const char *s) {
SocketAddress b;
int r;
/* Similar to socket_address_parse() but warns for IPv6 sockets when we don't support them. */
r = socket_address_parse(&b, s);
if (r < 0)
return r;
if (!socket_ipv6_is_supported() && b.sockaddr.sa.sa_family == AF_INET6) {
log_warning("Binding to IPv6 address not available since kernel does not support IPv6.");
return -EAFNOSUPPORT;
}
*a = b;
return 0;
}
int socket_address_parse_netlink(SocketAddress *a, const char *s) {
int family;
unsigned group = 0;
_cleanup_free_ char *sfamily = NULL;
assert(a);
assert(s);
zero(*a);
a->type = SOCK_RAW;
errno = 0;
if (sscanf(s, "%ms %u", &sfamily, &group) < 1)
return errno > 0 ? -errno : -EINVAL;
family = netlink_family_from_string(sfamily);
if (family < 0)
return -EINVAL;
a->sockaddr.nl.nl_family = AF_NETLINK;
a->sockaddr.nl.nl_groups = group;
a->type = SOCK_RAW;
a->size = sizeof(struct sockaddr_nl);
a->protocol = family;
return 0;
}
int socket_address_verify(const SocketAddress *a) {
assert(a);
switch (socket_address_family(a)) {
case AF_INET:
if (a->size != sizeof(struct sockaddr_in))
return -EINVAL;
if (a->sockaddr.in.sin_port == 0)
return -EINVAL;
if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM)
return -EINVAL;
return 0;
case AF_INET6:
if (a->size != sizeof(struct sockaddr_in6))
return -EINVAL;
if (a->sockaddr.in6.sin6_port == 0)
return -EINVAL;
if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM)
return -EINVAL;
return 0;
case AF_UNIX:
if (a->size < offsetof(struct sockaddr_un, sun_path))
return -EINVAL;
if (a->size > offsetof(struct sockaddr_un, sun_path)) {
if (a->sockaddr.un.sun_path[0] != 0) {
char *e;
/* path */
e = memchr(a->sockaddr.un.sun_path, 0, sizeof(a->sockaddr.un.sun_path));
if (!e)
return -EINVAL;
if (a->size != offsetof(struct sockaddr_un, sun_path) + (e - a->sockaddr.un.sun_path) + 1)
return -EINVAL;
}
}
if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM && a->type != SOCK_SEQPACKET)
return -EINVAL;
return 0;
case AF_NETLINK:
if (a->size != sizeof(struct sockaddr_nl))
return -EINVAL;
if (a->type != SOCK_RAW && a->type != SOCK_DGRAM)
return -EINVAL;
return 0;
default:
return -EAFNOSUPPORT;
}
}
int socket_address_print(const SocketAddress *a, char **ret) {
int r;
assert(a);
assert(ret);
r = socket_address_verify(a);
if (r < 0)
return r;
if (socket_address_family(a) == AF_NETLINK) {
_cleanup_free_ char *sfamily = NULL;
r = netlink_family_to_string_alloc(a->protocol, &sfamily);
if (r < 0)
return r;
r = asprintf(ret, "%s %u", sfamily, a->sockaddr.nl.nl_groups);
if (r < 0)
return -ENOMEM;
return 0;
}
return sockaddr_pretty(&a->sockaddr.sa, a->size, false, true, ret);
}
bool socket_address_can_accept(const SocketAddress *a) {
assert(a);
return
a->type == SOCK_STREAM ||
a->type == SOCK_SEQPACKET;
}
bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) {
assert(a);
assert(b);
/* Invalid addresses are unequal to all */
if (socket_address_verify(a) < 0 ||
socket_address_verify(b) < 0)
return false;
if (a->type != b->type)
return false;
if (socket_address_family(a) != socket_address_family(b))
return false;
switch (socket_address_family(a)) {
case AF_INET:
if (a->sockaddr.in.sin_addr.s_addr != b->sockaddr.in.sin_addr.s_addr)
return false;
if (a->sockaddr.in.sin_port != b->sockaddr.in.sin_port)
return false;
break;
case AF_INET6:
if (memcmp(&a->sockaddr.in6.sin6_addr, &b->sockaddr.in6.sin6_addr, sizeof(a->sockaddr.in6.sin6_addr)) != 0)
return false;
if (a->sockaddr.in6.sin6_port != b->sockaddr.in6.sin6_port)
return false;
break;
case AF_UNIX:
if (a->size <= offsetof(struct sockaddr_un, sun_path) ||
b->size <= offsetof(struct sockaddr_un, sun_path))
return false;
if ((a->sockaddr.un.sun_path[0] == 0) != (b->sockaddr.un.sun_path[0] == 0))
return false;
if (a->sockaddr.un.sun_path[0]) {
if (!path_equal_or_files_same(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path))
return false;
} else {
if (a->size != b->size)
return false;
if (memcmp(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, a->size) != 0)
return false;
}
break;
case AF_NETLINK:
if (a->protocol != b->protocol)
return false;
if (a->sockaddr.nl.nl_groups != b->sockaddr.nl.nl_groups)
return false;
break;
default:
/* Cannot compare, so we assume the addresses are different */
return false;
}
return true;
}
bool socket_address_is(const SocketAddress *a, const char *s, int type) {
struct SocketAddress b;
assert(a);
assert(s);
if (socket_address_parse(&b, s) < 0)
return false;
b.type = type;
return socket_address_equal(a, &b);
}
bool socket_address_is_netlink(const SocketAddress *a, const char *s) {
struct SocketAddress b;
assert(a);
assert(s);
if (socket_address_parse_netlink(&b, s) < 0)
return false;
return socket_address_equal(a, &b);
}
const char* socket_address_get_path(const SocketAddress *a) {
assert(a);
if (socket_address_family(a) != AF_UNIX)
return NULL;
if (a->sockaddr.un.sun_path[0] == 0)
return NULL;
return a->sockaddr.un.sun_path;
}
bool socket_ipv6_is_supported(void) {
if (access("/proc/net/sockstat6", F_OK) != 0)
return false;
return true;
}
bool socket_address_matches_fd(const SocketAddress *a, int fd) {
SocketAddress b;
socklen_t solen;
assert(a);
assert(fd >= 0);
b.size = sizeof(b.sockaddr);
if (getsockname(fd, &b.sockaddr.sa, &b.size) < 0)
return false;
if (b.sockaddr.sa.sa_family != a->sockaddr.sa.sa_family)
return false;
solen = sizeof(b.type);
if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &b.type, &solen) < 0)
return false;
if (b.type != a->type)
return false;
if (a->protocol != 0) {
solen = sizeof(b.protocol);
if (getsockopt(fd, SOL_SOCKET, SO_PROTOCOL, &b.protocol, &solen) < 0)
return false;
if (b.protocol != a->protocol)
return false;
}
return socket_address_equal(a, &b);
}
int sockaddr_port(const struct sockaddr *_sa) {
union sockaddr_union *sa = (union sockaddr_union*) _sa;
assert(sa);
if (!IN_SET(sa->sa.sa_family, AF_INET, AF_INET6))
return -EAFNOSUPPORT;
return ntohs(sa->sa.sa_family == AF_INET6 ?
sa->in6.sin6_port :
sa->in.sin_port);
}
int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret) {
union sockaddr_union *sa = (union sockaddr_union*) _sa;
char *p;
int r;
assert(sa);
assert(salen >= sizeof(sa->sa.sa_family));
switch (sa->sa.sa_family) {
case AF_INET: {
uint32_t a;
a = ntohl(sa->in.sin_addr.s_addr);
if (include_port)
r = asprintf(&p,
"%u.%u.%u.%u:%u",
a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF,
ntohs(sa->in.sin_port));
else
r = asprintf(&p,
"%u.%u.%u.%u",
a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF);
if (r < 0)
return -ENOMEM;
break;
}
case AF_INET6: {
static const unsigned char ipv4_prefix[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF
};
if (translate_ipv6 &&
memcmp(&sa->in6.sin6_addr, ipv4_prefix, sizeof(ipv4_prefix)) == 0) {
const uint8_t *a = sa->in6.sin6_addr.s6_addr+12;
if (include_port)
r = asprintf(&p,
"%u.%u.%u.%u:%u",
a[0], a[1], a[2], a[3],
ntohs(sa->in6.sin6_port));
else
r = asprintf(&p,
"%u.%u.%u.%u",
a[0], a[1], a[2], a[3]);
if (r < 0)
return -ENOMEM;
} else {
char a[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, &sa->in6.sin6_addr, a, sizeof(a));
if (include_port) {
r = asprintf(&p,
"[%s]:%u",
a,
ntohs(sa->in6.sin6_port));
if (r < 0)
return -ENOMEM;
} else {
p = strdup(a);
if (!p)
return -ENOMEM;
}
}
break;
}
case AF_UNIX:
if (salen <= offsetof(struct sockaddr_un, sun_path)) {
p = strdup("<unnamed>");
if (!p)
return -ENOMEM;
} else if (sa->un.sun_path[0] == 0) {
/* abstract */
/* FIXME: We assume we can print the
* socket path here and that it hasn't
* more than one NUL byte. That is
* actually an invalid assumption */
p = new(char, sizeof(sa->un.sun_path)+1);
if (!p)
return -ENOMEM;
p[0] = '@';
memcpy(p+1, sa->un.sun_path+1, sizeof(sa->un.sun_path)-1);
p[sizeof(sa->un.sun_path)] = 0;
} else {
p = strndup(sa->un.sun_path, sizeof(sa->un.sun_path));
if (!p)
return -ENOMEM;
}
break;
default:
return -EOPNOTSUPP;
}
*ret = p;
return 0;
}
int getpeername_pretty(int fd, bool include_port, char **ret) {
union sockaddr_union sa;
socklen_t salen = sizeof(sa);
int r;
assert(fd >= 0);
assert(ret);
if (getpeername(fd, &sa.sa, &salen) < 0)
return -errno;
if (sa.sa.sa_family == AF_UNIX) {
struct ucred ucred = {};
/* UNIX connection sockets are anonymous, so let's use
* PID/UID as pretty credentials instead */
r = getpeercred(fd, &ucred);
if (r < 0)
return r;
if (asprintf(ret, "PID "PID_FMT"/UID "UID_FMT, ucred.pid, ucred.uid) < 0)
return -ENOMEM;
return 0;
}
/* For remote sockets we translate IPv6 addresses back to IPv4
* if applicable, since that's nicer. */
return sockaddr_pretty(&sa.sa, salen, true, include_port, ret);
}
int getsockname_pretty(int fd, char **ret) {
union sockaddr_union sa;
socklen_t salen = sizeof(sa);
assert(fd >= 0);
assert(ret);
if (getsockname(fd, &sa.sa, &salen) < 0)
return -errno;
/* For local sockets we do not translate IPv6 addresses back
* to IPv6 if applicable, since this is usually used for
* listening sockets where the difference between IPv4 and
* IPv6 matters. */
return sockaddr_pretty(&sa.sa, salen, false, true, ret);
}
int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret) {
int r;
char host[NI_MAXHOST], *ret;
assert(_ret);
r = getnameinfo(&sa->sa, salen, host, sizeof(host), NULL, 0,
NI_IDN|NI_IDN_USE_STD3_ASCII_RULES);
if (r != 0) {
int saved_errno = errno;
r = sockaddr_pretty(&sa->sa, salen, true, true, &ret);
if (r < 0)
return r;
log_debug_errno(saved_errno, "getnameinfo(%s) failed: %m", ret);
} else {
ret = strdup(host);
if (!ret)
return -ENOMEM;
}
*_ret = ret;
return 0;
}
int getnameinfo_pretty(int fd, char **ret) {
union sockaddr_union sa;
socklen_t salen = sizeof(sa);
assert(fd >= 0);
assert(ret);
if (getsockname(fd, &sa.sa, &salen) < 0)
return -errno;
return socknameinfo_pretty(&sa, salen, ret);
}
int socket_address_unlink(SocketAddress *a) {
assert(a);
if (socket_address_family(a) != AF_UNIX)
return 0;
if (a->sockaddr.un.sun_path[0] == 0)
return 0;
if (unlink(a->sockaddr.un.sun_path) < 0)
return -errno;
return 1;
}
static const char* const netlink_family_table[] = {
[NETLINK_ROUTE] = "route",
[NETLINK_FIREWALL] = "firewall",
[NETLINK_INET_DIAG] = "inet-diag",
[NETLINK_NFLOG] = "nflog",
[NETLINK_XFRM] = "xfrm",
[NETLINK_SELINUX] = "selinux",
[NETLINK_ISCSI] = "iscsi",
[NETLINK_AUDIT] = "audit",
[NETLINK_FIB_LOOKUP] = "fib-lookup",
[NETLINK_CONNECTOR] = "connector",
[NETLINK_NETFILTER] = "netfilter",
[NETLINK_IP6_FW] = "ip6-fw",
[NETLINK_DNRTMSG] = "dnrtmsg",
[NETLINK_KOBJECT_UEVENT] = "kobject-uevent",
[NETLINK_GENERIC] = "generic",
[NETLINK_SCSITRANSPORT] = "scsitransport",
[NETLINK_ECRYPTFS] = "ecryptfs"
};
DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(netlink_family, int, INT_MAX);
static const char* const socket_address_bind_ipv6_only_table[_SOCKET_ADDRESS_BIND_IPV6_ONLY_MAX] = {
[SOCKET_ADDRESS_DEFAULT] = "default",
[SOCKET_ADDRESS_BOTH] = "both",
[SOCKET_ADDRESS_IPV6_ONLY] = "ipv6-only"
};
DEFINE_STRING_TABLE_LOOKUP(socket_address_bind_ipv6_only, SocketAddressBindIPv6Only);
bool sockaddr_equal(const union sockaddr_union *a, const union sockaddr_union *b) {
assert(a);
assert(b);
if (a->sa.sa_family != b->sa.sa_family)
return false;
if (a->sa.sa_family == AF_INET)
return a->in.sin_addr.s_addr == b->in.sin_addr.s_addr;
if (a->sa.sa_family == AF_INET6)
return memcmp(&a->in6.sin6_addr, &b->in6.sin6_addr, sizeof(a->in6.sin6_addr)) == 0;
return false;
}
int fd_inc_sndbuf(int fd, size_t n) {
int r, value;
socklen_t l = sizeof(value);
r = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, &l);
if (r >= 0 && l == sizeof(value) && (size_t) value >= n*2)
return 0;
/* If we have the privileges we will ignore the kernel limit. */
value = (int) n;
if (setsockopt(fd, SOL_SOCKET, SO_SNDBUFFORCE, &value, sizeof(value)) < 0)
if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value)) < 0)
return -errno;
return 1;
}
int fd_inc_rcvbuf(int fd, size_t n) {
int r, value;
socklen_t l = sizeof(value);
r = getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, &l);
if (r >= 0 && l == sizeof(value) && (size_t) value >= n*2)
return 0;
/* If we have the privileges we will ignore the kernel limit. */
value = (int) n;
if (setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &value, sizeof(value)) < 0)
if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value)) < 0)
return -errno;
return 1;
}
static const char* const ip_tos_table[] = {
[IPTOS_LOWDELAY] = "low-delay",
[IPTOS_THROUGHPUT] = "throughput",
[IPTOS_RELIABILITY] = "reliability",
[IPTOS_LOWCOST] = "low-cost",
};
DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ip_tos, int, 0xff);
int getpeercred(int fd, struct ucred *ucred) {
socklen_t n = sizeof(struct ucred);
struct ucred u;
int r;
assert(fd >= 0);
assert(ucred);
r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &u, &n);
if (r < 0)
return -errno;
if (n != sizeof(struct ucred))
return -EIO;
/* Check if the data is actually useful and not suppressed due
* to namespacing issues */
if (u.pid <= 0)
return -ENODATA;
if (u.uid == UID_INVALID)
return -ENODATA;
if (u.gid == GID_INVALID)
return -ENODATA;
*ucred = u;
return 0;
}
int getpeersec(int fd, char **ret) {
socklen_t n = 64;
char *s;
int r;
assert(fd >= 0);
assert(ret);
s = new0(char, n);
if (!s)
return -ENOMEM;
r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n);
if (r < 0) {
free(s);
if (errno != ERANGE)
return -errno;
s = new0(char, n);
if (!s)
return -ENOMEM;
r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n);
if (r < 0) {
free(s);
return -errno;
}
}
if (isempty(s)) {
free(s);
return -EOPNOTSUPP;
}
*ret = s;
return 0;
}
int send_one_fd_sa(
int transport_fd,
int fd,
const struct sockaddr *sa, socklen_t len,
int flags) {
union {
struct cmsghdr cmsghdr;
uint8_t buf[CMSG_SPACE(sizeof(int))];
} control = {};
struct msghdr mh = {
.msg_name = (struct sockaddr*) sa,
.msg_namelen = len,
.msg_control = &control,
.msg_controllen = sizeof(control),
};
struct cmsghdr *cmsg;
assert(transport_fd >= 0);
assert(fd >= 0);
cmsg = CMSG_FIRSTHDR(&mh);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
mh.msg_controllen = CMSG_SPACE(sizeof(int));
if (sendmsg(transport_fd, &mh, MSG_NOSIGNAL | flags) < 0)
return -errno;
return 0;
}
int receive_one_fd(int transport_fd, int flags) {
union {
struct cmsghdr cmsghdr;
uint8_t buf[CMSG_SPACE(sizeof(int))];
} control = {};
struct msghdr mh = {
.msg_control = &control,
.msg_controllen = sizeof(control),
};
struct cmsghdr *cmsg, *found = NULL;
assert(transport_fd >= 0);
/*
* Receive a single FD via @transport_fd. We don't care for
* the transport-type. We retrieve a single FD at most, so for
* packet-based transports, the caller must ensure to send
* only a single FD per packet. This is best used in
* combination with send_one_fd().
*/
if (recvmsg(transport_fd, &mh, MSG_NOSIGNAL | MSG_CMSG_CLOEXEC | flags) < 0)
return -errno;
CMSG_FOREACH(cmsg, &mh) {
if (cmsg->cmsg_level == SOL_SOCKET &&
cmsg->cmsg_type == SCM_RIGHTS &&
cmsg->cmsg_len == CMSG_LEN(sizeof(int))) {
assert(!found);
found = cmsg;
break;
}
}
if (!found) {
cmsg_close_all(&mh);
return -EIO;
}
return *(int*) CMSG_DATA(found);
}
#endif /* NM_IGNORED */
ssize_t next_datagram_size_fd(int fd) {
ssize_t l;
int k;
/* This is a bit like FIONREAD/SIOCINQ, however a bit more powerful. The difference being: recv(MSG_PEEK) will
* actually cause the next datagram in the queue to be validated regarding checksums, which FIONREAD doesn't
* do. This difference is actually of major importance as we need to be sure that the size returned here
* actually matches what we will read with recvmsg() next, as otherwise we might end up allocating a buffer of
* the wrong size. */
l = recv(fd, NULL, 0, MSG_PEEK|MSG_TRUNC);
if (l < 0) {
if (errno == EOPNOTSUPP)
goto fallback;
return -errno;
}
if (l == 0)
goto fallback;
return l;
fallback:
k = 0;
/* Some sockets (AF_PACKET) do not support null-sized recv() with MSG_TRUNC set, let's fall back to FIONREAD
* for them. Checksums don't matter for raw sockets anyway, hence this should be fine. */
if (ioctl(fd, FIONREAD, &k) < 0)
return -errno;
return (ssize_t) k;
}

View file

@ -133,5 +133,7 @@ int send_one_fd_sa(int transport_fd,
#define send_one_fd(transport_fd, fd, flags) send_one_fd_sa(transport_fd, fd, NULL, 0, flags)
int receive_one_fd(int transport_fd, int flags);
ssize_t next_datagram_size_fd(int fd);
#define CMSG_FOREACH(cmsg, mh) \
for ((cmsg) = CMSG_FIRSTHDR(mh); (cmsg); (cmsg) = CMSG_NXTHDR((mh), (cmsg)))

View file

@ -73,4 +73,4 @@ do { \
assert_not_reached("Unknown format string argument."); \
} \
} \
} while(false)
} while (false)

View file

@ -482,7 +482,7 @@ char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigne
}
if (k > x) /* last character was wide and went over quota */
x ++;
x++;
for (j = s + old_length; k < new_length && j > i; ) {
char32_t c;

View file

@ -377,7 +377,7 @@ char *strv_join(char **l, const char *separator) {
n = 0;
STRV_FOREACH(s, l) {
if (n != 0)
if (s != l)
n += k;
n += strlen(*s);
}
@ -388,7 +388,7 @@ char *strv_join(char **l, const char *separator) {
e = r;
STRV_FOREACH(s, l) {
if (e != r)
if (s != l)
e = stpcpy(e, separator);
e = stpcpy(e, *s);

View file

@ -44,10 +44,30 @@
static nsec_t timespec_load_nsec(const struct timespec *ts);
static clockid_t map_clock_id(clockid_t c) {
/* Some more exotic archs (s390, ppc, …) lack the "ALARM" flavour of the clocks. Thus, clock_gettime() will
* fail for them. Since they are essentially the same as their non-ALARM pendants (their only difference is
* when timers are set on them), let's just map them accordingly. This way, we can get the correct time even on
* those archs. */
switch (c) {
case CLOCK_BOOTTIME_ALARM:
return CLOCK_BOOTTIME;
case CLOCK_REALTIME_ALARM:
return CLOCK_REALTIME;
default:
return c;
}
}
usec_t now(clockid_t clock_id) {
struct timespec ts;
assert_se(clock_gettime(clock_id, &ts) == 0);
assert_se(clock_gettime(map_clock_id(clock_id), &ts) == 0);
return timespec_load(&ts);
}
@ -55,7 +75,7 @@ usec_t now(clockid_t clock_id) {
nsec_t now_nsec(clockid_t clock_id) {
struct timespec ts;
assert_se(clock_gettime(clock_id, &ts) == 0);
assert_se(clock_gettime(map_clock_id(clock_id), &ts) == 0);
return timespec_load_nsec(&ts);
}
@ -121,8 +141,7 @@ dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, us
usec_t timespec_load(const struct timespec *ts) {
assert(ts);
if (ts->tv_sec == (time_t) -1 &&
ts->tv_nsec == (long) -1)
if (ts->tv_sec == (time_t) -1 && ts->tv_nsec == (long) -1)
return USEC_INFINITY;
if ((usec_t) ts->tv_sec > (UINT64_MAX - (ts->tv_nsec / NSEC_PER_USEC)) / USEC_PER_SEC)
@ -136,13 +155,13 @@ usec_t timespec_load(const struct timespec *ts) {
static nsec_t timespec_load_nsec(const struct timespec *ts) {
assert(ts);
if (ts->tv_sec == (time_t) -1 &&
ts->tv_nsec == (long) -1)
if (ts->tv_sec == (time_t) -1 && ts->tv_nsec == (long) -1)
return NSEC_INFINITY;
return
(nsec_t) ts->tv_sec * NSEC_PER_SEC +
(nsec_t) ts->tv_nsec;
if ((nsec_t) ts->tv_sec >= (UINT64_MAX - ts->tv_nsec) / NSEC_PER_SEC)
return NSEC_INFINITY;
return (nsec_t) ts->tv_sec * NSEC_PER_SEC + (nsec_t) ts->tv_nsec;
}
struct timespec *timespec_store(struct timespec *ts, usec_t u) {
@ -434,7 +453,7 @@ int dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
assert(t);
if (sscanf(value, "%llu %llu", &a, &b) != 2) {
log_debug("Failed to parse finish timestamp value %s.", value);
log_debug("Failed to parse dual timestamp value \"%s\": %m", value);
return -EINVAL;
}
@ -444,6 +463,18 @@ int dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
return 0;
}
int timestamp_deserialize(const char *value, usec_t *timestamp) {
int r;
assert(value);
r = safe_atou64(value, timestamp);
if (r < 0)
return log_debug_errno(r, "Failed to parse timestamp value \"%s\": %m", value);
return r;
}
int parse_timestamp(const char *t, usec_t *usec) {
static const struct {
const char *name;
@ -550,12 +581,12 @@ int parse_timestamp(const char *t, usec_t *usec) {
goto from_tm;
} else if (streq(t, "yesterday")) {
tm.tm_mday --;
tm.tm_mday--;
tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
goto from_tm;
} else if (streq(t, "tomorrow")) {
tm.tm_mday ++;
tm.tm_mday++;
tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
goto from_tm;
}
@ -678,8 +709,7 @@ finish:
return 0;
}
int parse_time(const char *t, usec_t *usec, usec_t default_unit) {
static char* extract_multiplier(char *p, usec_t *multiplier) {
static const struct {
const char *suffix;
usec_t usec;
@ -713,7 +743,22 @@ int parse_time(const char *t, usec_t *usec, usec_t default_unit) {
{ "usec", 1ULL },
{ "us", 1ULL },
};
unsigned i;
for (i = 0; i < ELEMENTSOF(table); i++) {
char *e;
e = startswith(p, table[i].suffix);
if (e) {
*multiplier = table[i].usec;
return e;
}
}
return p;
}
int parse_time(const char *t, usec_t *usec, usec_t default_unit) {
const char *p, *s;
usec_t r = 0;
bool something = false;
@ -738,8 +783,8 @@ int parse_time(const char *t, usec_t *usec, usec_t default_unit) {
for (;;) {
long long l, z = 0;
char *e;
unsigned i, n = 0;
usec_t multiplier, k;
unsigned n = 0;
usec_t multiplier = default_unit, k;
p += strspn(p, WHITESPACE);
@ -752,10 +797,8 @@ int parse_time(const char *t, usec_t *usec, usec_t default_unit) {
errno = 0;
l = strtoll(p, &e, 10);
if (errno > 0)
return -errno;
if (l < 0)
return -ERANGE;
@ -779,18 +822,7 @@ int parse_time(const char *t, usec_t *usec, usec_t default_unit) {
return -EINVAL;
e += strspn(e, WHITESPACE);
for (i = 0; i < ELEMENTSOF(table); i++)
if (startswith(e, table[i].suffix)) {
multiplier = table[i].usec;
p = e + strlen(table[i].suffix);
break;
}
if (i >= ELEMENTSOF(table)) {
multiplier = default_unit;
p = e;
}
p = extract_multiplier(e, &multiplier);
something = true;

View file

@ -99,6 +99,7 @@ char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy);
void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t);
int dual_timestamp_deserialize(const char *value, dual_timestamp *t);
int timestamp_deserialize(const char *value, usec_t *timestamp);
int parse_timestamp(const char *t, usec_t *usec);

View file

@ -243,7 +243,7 @@ char *utf8_escape_non_printable(const char *str) {
*(s++) = hexchar((int) *str);
str += 1;
len --;
len--;
}
}
} else {

View file

@ -106,6 +106,16 @@ static inline void qsort_safe(void *base, size_t nmemb, size_t size, comparison_
qsort(base, nmemb, size, compar);
}
/**
* Normal memcpy requires src to be nonnull. We do nothing if n is 0.
*/
static inline void memcpy_safe(void *dst, const void *src, size_t n) {
if (n == 0)
return;
assert(src);
memcpy(dst, src, n);
}
int on_ac_power(void);
#define memzero(x,l) (memset((x), 0, (l)))

View file

@ -42,10 +42,10 @@ int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port,
int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset, uint8_t overload,
uint8_t code, size_t optlen, const void *optval);
typedef int (*dhcp_option_cb_t)(uint8_t code, uint8_t len,
typedef int (*dhcp_option_callback_t)(uint8_t code, uint8_t len,
const void *option, void *userdata);
int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_cb_t cb, void *userdata, char **error_message);
int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_callback_t cb, void *userdata, char **error_message);
int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid,
uint8_t type, uint16_t arp_type, size_t optlen,

View file

@ -36,7 +36,7 @@ static int option_append(uint8_t options[], size_t size, size_t *offset,
if (code != SD_DHCP_OPTION_END)
/* always make sure there is space for an END option */
size --;
size--;
switch (code) {
@ -56,12 +56,7 @@ static int option_append(uint8_t options[], size_t size, size_t *offset,
options[*offset] = code;
options[*offset + 1] = optlen;
if (optlen) {
assert(optval);
memcpy(&options[*offset + 2], optval, optlen);
}
memcpy_safe(&options[*offset + 2], optval, optlen);
*offset += optlen + 2;
break;
@ -142,7 +137,7 @@ int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset,
}
static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overload,
uint8_t *message_type, char **error_message, dhcp_option_cb_t cb,
uint8_t *message_type, char **error_message, dhcp_option_callback_t cb,
void *userdata) {
uint8_t code, len;
const uint8_t *option;
@ -228,7 +223,7 @@ static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overlo
return 0;
}
int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_cb_t cb, void *userdata, char **_error_message) {
int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_callback_t cb, void *userdata, char **_error_message) {
_cleanup_free_ char *error_message = NULL;
uint8_t overload = 0;
uint8_t message_type = 0;

View file

@ -68,7 +68,7 @@ uint16_t dhcp_packet_checksum(uint8_t *buf, size_t len) {
/* wrap around in one's complement */
sum++;
buf_64 ++;
buf_64++;
}
if (len % sizeof(uint64_t)) {

View file

@ -73,8 +73,7 @@ int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
if (r < 0)
return r;
if (optval)
memcpy(*buf, optval, optlen);
memcpy_safe(*buf, optval, optlen);
*buf += optlen;
*buflen -= optlen;

View file

@ -1,362 +0,0 @@
/***
This file is part of systemd.
Copyright (C) 2014 Tom Gundersen
Copyright (C) 2014 Susant Sahani
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "nm-sd-adapt.h"
#include "sd-lldp.h"
#include "alloc-util.h"
#include "lldp-internal.h"
/* We store maximum 1K chassis entries */
#define LLDP_MIB_MAX_CHASSIS 1024
/* Maximum Ports can be attached to any chassis */
#define LLDP_MIB_MAX_PORT_PER_CHASSIS 32
/* 10.5.5.2.2 mibUpdateObjects ()
* The mibUpdateObjects () procedure updates the MIB objects corresponding to
* the TLVs contained in the received LLDPDU for the LLDP remote system
* indicated by the LLDP remote systems update process defined in 10.3.5 */
int lldp_mib_update_objects(lldp_chassis *c, tlv_packet *tlv) {
lldp_neighbour_port *p;
uint16_t length, ttl;
uint8_t *data;
uint8_t type;
int r;
assert_return(c, -EINVAL);
assert_return(tlv, -EINVAL);
r = sd_lldp_packet_read_port_id(tlv, &type, &data, &length);
if (r < 0)
return r;
/* Update the packet if we already have */
LIST_FOREACH(port, p, c->ports) {
if ((p->type == type && p->length == length && !memcmp(p->data, data, p->length))) {
r = sd_lldp_packet_read_ttl(tlv, &ttl);
if (r < 0)
return r;
p->until = ttl * USEC_PER_SEC + now(clock_boottime_or_monotonic());
sd_lldp_packet_unref(p->packet);
p->packet = tlv;
prioq_reshuffle(p->c->by_expiry, p, &p->prioq_idx);
return 0;
}
}
return -1;
}
int lldp_mib_remove_objects(lldp_chassis *c, tlv_packet *tlv) {
lldp_neighbour_port *p, *q;
uint8_t *data;
uint16_t length;
uint8_t type;
int r;
assert_return(c, -EINVAL);
assert_return(tlv, -EINVAL);
r = sd_lldp_packet_read_port_id(tlv, &type, &data, &length);
if (r < 0)
return r;
LIST_FOREACH_SAFE(port, p, q, c->ports) {
/* Find the port */
if (p->type == type && p->length == length && !memcmp(p->data, data, p->length)) {
lldp_neighbour_port_remove_and_free(p);
break;
}
}
return 0;
}
int lldp_mib_add_objects(Prioq *by_expiry,
Hashmap *neighbour_mib,
tlv_packet *tlv) {
_cleanup_lldp_neighbour_port_free_ lldp_neighbour_port *p = NULL;
_cleanup_lldp_chassis_free_ lldp_chassis *c = NULL;
lldp_chassis_id chassis_id;
bool new_chassis = false;
uint8_t subtype, *data;
uint16_t ttl, length;
int r;
assert_return(by_expiry, -EINVAL);
assert_return(neighbour_mib, -EINVAL);
assert_return(tlv, -EINVAL);
r = sd_lldp_packet_read_chassis_id(tlv, &subtype, &data, &length);
if (r < 0)
goto drop;
r = sd_lldp_packet_read_ttl(tlv, &ttl);
if (r < 0)
goto drop;
/* Make hash key */
chassis_id.type = subtype;
chassis_id.length = length;
chassis_id.data = data;
/* Try to find the Chassis */
c = hashmap_get(neighbour_mib, &chassis_id);
if (!c) {
/* Don't create chassis if ttl 0 is received . Silently drop it */
if (ttl == 0) {
log_lldp("TTL value 0 received. Skiping Chassis creation.");
goto drop;
}
/* Admission Control: Can we store this packet ? */
if (hashmap_size(neighbour_mib) >= LLDP_MIB_MAX_CHASSIS) {
log_lldp("Exceeding number of chassie: %d. Dropping ...",
hashmap_size(neighbour_mib));
goto drop;
}
r = lldp_chassis_new(tlv, by_expiry, neighbour_mib, &c);
if (r < 0)
goto drop;
new_chassis = true;
r = hashmap_put(neighbour_mib, &c->chassis_id, c);
if (r < 0)
goto drop;
} else {
/* When the TTL field is set to zero, the receiving LLDP agent is notified all
* system information associated with the LLDP agent/port is to be deleted */
if (ttl == 0) {
log_lldp("TTL value 0 received . Deleting associated Port ...");
lldp_mib_remove_objects(c, tlv);
c = NULL;
goto drop;
}
/* if we already have this port just update it */
r = lldp_mib_update_objects(c, tlv);
if (r >= 0) {
c = NULL;
return r;
}
/* Admission Control: Can this port attached to the existing chassis ? */
if (c->n_ref >= LLDP_MIB_MAX_PORT_PER_CHASSIS) {
log_lldp("Port limit reached. Chassis has: %d ports. Dropping ...", c->n_ref);
c = NULL;
goto drop;
}
}
/* This is a new port */
r = lldp_neighbour_port_new(c, tlv, &p);
if (r < 0)
goto drop;
r = prioq_put(c->by_expiry, p, &p->prioq_idx);
if (r < 0)
goto drop;
/* Attach new port to chassis */
LIST_PREPEND(port, c->ports, p);
c->n_ref ++;
p = NULL;
c = NULL;
return 0;
drop:
sd_lldp_packet_unref(tlv);
if (new_chassis)
hashmap_remove(neighbour_mib, &c->chassis_id);
return r;
}
void lldp_neighbour_port_remove_and_free(lldp_neighbour_port *p) {
lldp_chassis *c;
assert(p);
assert(p->c);
c = p->c;
prioq_remove(c->by_expiry, p, &p->prioq_idx);
LIST_REMOVE(port, c->ports, p);
lldp_neighbour_port_free(p);
/* Drop the Chassis if no port is attached */
c->n_ref --;
if (c->n_ref <= 1) {
hashmap_remove(c->neighbour_mib, &c->chassis_id);
lldp_chassis_free(c);
}
}
void lldp_neighbour_port_free(lldp_neighbour_port *p) {
if(!p)
return;
sd_lldp_packet_unref(p->packet);
free(p->data);
free(p);
}
int lldp_neighbour_port_new(lldp_chassis *c,
tlv_packet *tlv,
lldp_neighbour_port **ret) {
_cleanup_lldp_neighbour_port_free_ lldp_neighbour_port *p = NULL;
uint16_t length, ttl;
uint8_t *data;
uint8_t type;
int r;
assert(tlv);
r = sd_lldp_packet_read_port_id(tlv, &type, &data, &length);
if (r < 0)
return r;
r = sd_lldp_packet_read_ttl(tlv, &ttl);
if (r < 0)
return r;
p = new0(lldp_neighbour_port, 1);
if (!p)
return -ENOMEM;
p->c = c;
p->type = type;
p->length = length;
p->packet = tlv;
p->prioq_idx = PRIOQ_IDX_NULL;
p->until = ttl * USEC_PER_SEC + now(clock_boottime_or_monotonic());
p->data = memdup(data, length);
if (!p->data)
return -ENOMEM;
*ret = p;
p = NULL;
return 0;
}
void lldp_chassis_free(lldp_chassis *c) {
if (!c)
return;
if (c->n_ref > 1)
return;
free(c->chassis_id.data);
free(c);
}
int lldp_chassis_new(tlv_packet *tlv,
Prioq *by_expiry,
Hashmap *neighbour_mib,
lldp_chassis **ret) {
_cleanup_lldp_chassis_free_ lldp_chassis *c = NULL;
uint16_t length;
uint8_t *data;
uint8_t type;
int r;
assert(tlv);
r = sd_lldp_packet_read_chassis_id(tlv, &type, &data, &length);
if (r < 0)
return r;
c = new0(lldp_chassis, 1);
if (!c)
return -ENOMEM;
c->n_ref = 1;
c->chassis_id.type = type;
c->chassis_id.length = length;
c->chassis_id.data = memdup(data, length);
if (!c->chassis_id.data)
return -ENOMEM;
LIST_HEAD_INIT(c->ports);
c->by_expiry = by_expiry;
c->neighbour_mib = neighbour_mib;
*ret = c;
c = NULL;
return 0;
}
int lldp_receive_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
_cleanup_(sd_lldp_packet_unrefp) tlv_packet *packet = NULL;
tlv_packet *p;
uint16_t length;
int r;
assert(fd);
assert(userdata);
r = tlv_packet_new(&packet);
if (r < 0)
return r;
length = read(fd, &packet->pdu, sizeof(packet->pdu));
/* Silently drop the packet */
if ((size_t) length > ETHER_MAX_LEN)
return 0;
packet->userdata = userdata;
p = packet;
packet = NULL;
return lldp_handle_packet(p, (uint16_t) length);
}

View file

@ -1,3 +1,5 @@
#pragma once
/***
This file is part of systemd.
@ -18,74 +20,34 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#pragma once
#include "sd-event.h"
#include "sd-lldp.h"
#include "list.h"
#include "lldp-tlv.h"
#include "hashmap.h"
#include "log.h"
#include "prioq.h"
typedef struct lldp_neighbour_port lldp_neighbour_port;
typedef struct lldp_chassis lldp_chassis;
typedef struct lldp_chassis_id lldp_chassis_id;
typedef struct lldp_agent_statistics lldp_agent_statistics;
struct sd_lldp {
int ifindex;
int fd;
struct lldp_neighbour_port {
uint8_t type;
uint8_t *data;
sd_event *event;
int64_t event_priority;
sd_event_source *io_event_source;
sd_event_source *timer_event_source;
uint16_t length;
usec_t until;
Prioq *neighbor_by_expiry;
Hashmap *neighbor_by_id;
unsigned prioq_idx;
uint64_t neighbors_max;
lldp_chassis *c;
tlv_packet *packet;
sd_lldp_callback_t callback;
void *userdata;
LIST_FIELDS(lldp_neighbour_port, port);
uint16_t capability_mask;
struct ether_addr filter_address;
};
int lldp_neighbour_port_new(lldp_chassis *c, tlv_packet *tlv, lldp_neighbour_port **ret);
void lldp_neighbour_port_free(lldp_neighbour_port *p);
void lldp_neighbour_port_remove_and_free(lldp_neighbour_port *p);
DEFINE_TRIVIAL_CLEANUP_FUNC(lldp_neighbour_port *, lldp_neighbour_port_free);
#define _cleanup_lldp_neighbour_port_free_ _cleanup_(lldp_neighbour_port_freep)
struct lldp_chassis_id {
uint8_t type;
uint16_t length;
uint8_t *data;
};
struct lldp_chassis {
unsigned n_ref;
lldp_chassis_id chassis_id;
Prioq *by_expiry;
Hashmap *neighbour_mib;
LIST_HEAD(lldp_neighbour_port, ports);
};
int lldp_chassis_new(tlv_packet *tlv,
Prioq *by_expiry,
Hashmap *neighbour_mib,
lldp_chassis **ret);
void lldp_chassis_free(lldp_chassis *c);
DEFINE_TRIVIAL_CLEANUP_FUNC(lldp_chassis *, lldp_chassis_free);
#define _cleanup_lldp_chassis_free_ _cleanup_(lldp_chassis_freep)
int lldp_mib_update_objects(lldp_chassis *c, tlv_packet *tlv);
int lldp_mib_add_objects(Prioq *by_expiry, Hashmap *neighbour_mib, tlv_packet *tlv);
int lldp_mib_remove_objects(lldp_chassis *c, tlv_packet *tlv);
int lldp_handle_packet(tlv_packet *m, uint16_t length);
int lldp_receive_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata);
#define log_lldp(fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "LLDP: " fmt, ##__VA_ARGS__)
#define log_lldp_errno(error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "LLDP: " fmt, ##__VA_ARGS__)
#define log_lldp(fmt, ...) log_lldp_errno(0, fmt, ##__VA_ARGS__)

View file

@ -0,0 +1,794 @@
/***
This file is part of systemd.
Copyright 2016 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "nm-sd-adapt.h"
#include "alloc-util.h"
#include "escape.h"
#include "ether-addr-util.h"
#include "hexdecoct.h"
#include "in-addr-util.h"
#include "lldp-internal.h"
#include "lldp-neighbor.h"
#include "lldp.h"
#include "unaligned.h"
static void lldp_neighbor_id_hash_func(const void *p, struct siphash *state) {
const LLDPNeighborID *id = p;
siphash24_compress(id->chassis_id, id->chassis_id_size, state);
siphash24_compress(&id->chassis_id_size, sizeof(id->chassis_id_size), state);
siphash24_compress(id->port_id, id->port_id_size, state);
siphash24_compress(&id->port_id_size, sizeof(id->port_id_size), state);
}
static int lldp_neighbor_id_compare_func(const void *a, const void *b) {
const LLDPNeighborID *x = a, *y = b;
int r;
r = memcmp(x->chassis_id, y->chassis_id, MIN(x->chassis_id_size, y->chassis_id_size));
if (r != 0)
return r;
if (x->chassis_id_size < y->chassis_id_size)
return -1;
if (x->chassis_id_size > y->chassis_id_size)
return 1;
r = memcmp(x->port_id, y->port_id, MIN(x->port_id_size, y->port_id_size));
if (r != 0)
return r;
if (x->port_id_size < y->port_id_size)
return -1;
if (x->port_id_size > y->port_id_size)
return 1;
return 0;
}
const struct hash_ops lldp_neighbor_id_hash_ops = {
.hash = lldp_neighbor_id_hash_func,
.compare = lldp_neighbor_id_compare_func
};
int lldp_neighbor_prioq_compare_func(const void *a, const void *b) {
const sd_lldp_neighbor *x = a, *y = b;
if (x->until < y->until)
return -1;
if (x->until > y->until)
return 1;
return 0;
}
_public_ sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n) {
if (!n)
return NULL;
assert(n->n_ref > 0 || n->lldp);
n->n_ref++;
return n;
}
static void lldp_neighbor_free(sd_lldp_neighbor *n) {
assert(n);
free(n->id.port_id);
free(n->id.chassis_id);
free(n->port_description);
free(n->system_name);
free(n->system_description);
free(n->chassis_id_as_string);
free(n->port_id_as_string);
free(n);
}
_public_ sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n) {
/* Drops one reference from the neighbor. Note that the object is not freed unless it is already unlinked from
* the sd_lldp object. */
if (!n)
return NULL;
assert(n->n_ref > 0);
n->n_ref--;
if (n->n_ref <= 0 && !n->lldp)
lldp_neighbor_free(n);
return NULL;
}
sd_lldp_neighbor *lldp_neighbor_unlink(sd_lldp_neighbor *n) {
/* Removes the neighbor object from the LLDP object, and frees it if it also has no other reference. */
if (!n)
return NULL;
if (!n->lldp)
return NULL;
assert_se(hashmap_remove(n->lldp->neighbor_by_id, &n->id) == n);
assert_se(prioq_remove(n->lldp->neighbor_by_expiry, n, &n->prioq_idx) >= 0);
n->lldp = NULL;
if (n->n_ref <= 0)
lldp_neighbor_free(n);
return NULL;
}
sd_lldp_neighbor *lldp_neighbor_new(size_t raw_size) {
sd_lldp_neighbor *n;
n = malloc0(ALIGN(sizeof(sd_lldp_neighbor)) + raw_size);
if (!n)
return NULL;
n->raw_size = raw_size;
n->n_ref = 1;
return n;
}
static int parse_string(char **s, const void *q, size_t n) {
const char *p = q;
char *k;
assert(s);
assert(p || n == 0);
if (*s) {
log_lldp("Found duplicate string, ignoring field.");
return 0;
}
/* Strip trailing NULs, just to be nice */
while (n > 0 && p[n-1] == 0)
n--;
if (n <= 0) /* Ignore empty strings */
return 0;
/* Look for inner NULs */
if (memchr(p, 0, n)) {
log_lldp("Found inner NUL in string, ignoring field.");
return 0;
}
/* Let's escape weird chars, for security reasons */
k = cescape_length(p, n);
if (!k)
return -ENOMEM;
free(*s);
*s = k;
return 1;
}
int lldp_neighbor_parse(sd_lldp_neighbor *n) {
struct ether_header h;
const uint8_t *p;
size_t left;
int r;
assert(n);
if (n->raw_size < sizeof(struct ether_header)) {
log_lldp("Recieved truncated packet, ignoring.");
return -EBADMSG;
}
memcpy(&h, LLDP_NEIGHBOR_RAW(n), sizeof(h));
if (h.ether_type != htobe16(ETHERTYPE_LLDP)) {
log_lldp("Received packet with wrong type, ignoring.");
return -EBADMSG;
}
if (h.ether_dhost[0] != 0x01 ||
h.ether_dhost[1] != 0x80 ||
h.ether_dhost[2] != 0xc2 ||
h.ether_dhost[3] != 0x00 ||
h.ether_dhost[4] != 0x00 ||
!IN_SET(h.ether_dhost[5], 0x00, 0x03, 0x0e)) {
log_lldp("Received packet with wrong destination address, ignoring.");
return -EBADMSG;
}
memcpy(&n->source_address, h.ether_shost, sizeof(struct ether_addr));
memcpy(&n->destination_address, h.ether_dhost, sizeof(struct ether_addr));
p = (const uint8_t*) LLDP_NEIGHBOR_RAW(n) + sizeof(struct ether_header);
left = n->raw_size - sizeof(struct ether_header);
for (;;) {
uint8_t type;
uint16_t length;
if (left < 2) {
log_lldp("TLV lacks header, ignoring.");
return -EBADMSG;
}
type = p[0] >> 1;
length = p[1] + (((uint16_t) (p[0] & 1)) << 8);
p += 2, left -= 2;
if (left < length) {
log_lldp("TLV truncated, ignoring datagram.");
return -EBADMSG;
}
switch (type) {
case LLDP_TYPE_END:
if (length != 0) {
log_lldp("End marker TLV not zero-sized, ignoring datagram.");
return -EBADMSG;
}
if (left != 0) {
log_lldp("Trailing garbage in datagram, ignoring datagram.");
return -EBADMSG;
}
goto end_marker;
case LLDP_TYPE_CHASSIS_ID:
if (length < 2 || length > 256) { /* includes the chassis subtype, hence one extra byte */
log_lldp("Chassis ID field size out of range, ignoring datagram.");
return -EBADMSG;
}
if (n->id.chassis_id) {
log_lldp("Duplicate chassis ID field, ignoring datagram.");
return -EBADMSG;
}
n->id.chassis_id = memdup(p, length);
if (!n->id.chassis_id)
return -ENOMEM;
n->id.chassis_id_size = length;
break;
case LLDP_TYPE_PORT_ID:
if (length < 2 || length > 256) { /* includes the port subtype, hence one extra byte */
log_lldp("Port ID field size out of range, ignoring datagram.");
return -EBADMSG;
}
if (n->id.port_id) {
log_lldp("Duplicate port ID field, ignoring datagram.");
return -EBADMSG;
}
n->id.port_id = memdup(p, length);
if (!n->id.port_id)
return -ENOMEM;
n->id.port_id_size = length;
break;
case LLDP_TYPE_TTL:
if (length != 2) {
log_lldp("TTL field has wrong size, ignoring datagram.");
return -EBADMSG;
}
if (n->has_ttl) {
log_lldp("Duplicate TTL field, ignoring datagram.");
return -EBADMSG;
}
n->ttl = unaligned_read_be16(p);
n->has_ttl = true;
break;
case LLDP_TYPE_PORT_DESCRIPTION:
r = parse_string(&n->port_description, p, length);
if (r < 0)
return r;
break;
case LLDP_TYPE_SYSTEM_NAME:
r = parse_string(&n->system_name, p, length);
if (r < 0)
return r;
break;
case LLDP_TYPE_SYSTEM_DESCRIPTION:
r = parse_string(&n->system_description, p, length);
if (r < 0)
return r;
break;
case LLDP_TYPE_SYSTEM_CAPABILITIES:
if (length != 4)
log_lldp("System capabilities field has wrong size, ignoring.");
else {
n->system_capabilities = unaligned_read_be16(p);
n->enabled_capabilities = unaligned_read_be16(p + 2);
n->has_capabilities = true;
}
break;
case LLDP_TYPE_PRIVATE:
if (length < 4)
log_lldp("Found private TLV that is too short, ignoring.");
break;
}
p += length, left -= length;
}
end_marker:
if (!n->id.chassis_id || !n->id.port_id || !n->has_ttl) {
log_lldp("One or more mandatory TLV missing in datagram. Ignoring.");
return -EBADMSG;
}
n->rindex = sizeof(struct ether_header);
return 0;
}
void lldp_neighbor_start_ttl(sd_lldp_neighbor *n) {
assert(n);
if (n->ttl > 0)
n->until = usec_add(now(clock_boottime_or_monotonic()), n->ttl * USEC_PER_SEC);
else
n->until = 0;
if (n->lldp)
prioq_reshuffle(n->lldp->neighbor_by_expiry, n, &n->prioq_idx);
}
bool lldp_neighbor_equal(const sd_lldp_neighbor *a, const sd_lldp_neighbor *b) {
if (a == b)
return true;
if (!a || !b)
return false;
if (a->raw_size != b->raw_size)
return false;
return memcmp(LLDP_NEIGHBOR_RAW(a), LLDP_NEIGHBOR_RAW(b), a->raw_size) == 0;
}
_public_ int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct ether_addr* address) {
assert_return(n, -EINVAL);
assert_return(address, -EINVAL);
*address = n->source_address;
return 0;
}
_public_ int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_addr* address) {
assert_return(n, -EINVAL);
assert_return(address, -EINVAL);
*address = n->destination_address;
return 0;
}
_public_ int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) {
assert_return(n, -EINVAL);
assert_return(ret, -EINVAL);
assert_return(size, -EINVAL);
*ret = LLDP_NEIGHBOR_RAW(n);
*size = n->raw_size;
return 0;
}
_public_ int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) {
assert_return(n, -EINVAL);
assert_return(type, -EINVAL);
assert_return(ret, -EINVAL);
assert_return(size, -EINVAL);
assert(n->id.chassis_id_size > 0);
*type = *(uint8_t*) n->id.chassis_id;
*ret = (uint8_t*) n->id.chassis_id + 1;
*size = n->id.chassis_id_size - 1;
return 0;
}
static int format_mac_address(const void *data, size_t sz, char **ret) {
struct ether_addr a;
char *k;
assert(data || sz <= 0);
if (sz != 7)
return 0;
memcpy(&a, (uint8_t*) data + 1, sizeof(a));
k = new(char, ETHER_ADDR_TO_STRING_MAX);
if (!k)
return -ENOMEM;
*ret = ether_addr_to_string(&a, k);
return 1;
}
static int format_network_address(const void *data, size_t sz, char **ret) {
union in_addr_union a;
int family;
if (sz == 6 && ((uint8_t*) data)[1] == 1) {
memcpy(&a.in, (uint8_t*) data + 2, sizeof(a.in));
family = AF_INET;
} else if (sz == 18 && ((uint8_t*) data)[1] == 2) {
memcpy(&a.in6, (uint8_t*) data + 2, sizeof(a.in6));
family = AF_INET6;
} else
return 0;
return in_addr_to_string(family, &a, ret);
}
_public_ int sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor *n, const char **ret) {
char *k;
int r;
assert_return(n, -EINVAL);
assert_return(ret, -EINVAL);
if (n->chassis_id_as_string) {
*ret = n->chassis_id_as_string;
return 0;
}
assert(n->id.chassis_id_size > 0);
switch (*(uint8_t*) n->id.chassis_id) {
case LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT:
case LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS:
case LLDP_CHASSIS_SUBTYPE_PORT_COMPONENT:
case LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME:
case LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED:
k = cescape_length((char*) n->id.chassis_id + 1, n->id.chassis_id_size - 1);
if (!k)
return -ENOMEM;
goto done;
case LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS:
r = format_mac_address(n->id.chassis_id, n->id.chassis_id_size, &k);
if (r < 0)
return r;
if (r > 0)
goto done;
break;
case LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS:
r = format_network_address(n->id.chassis_id, n->id.chassis_id_size, &k);
if (r < 0)
return r;
if (r > 0)
goto done;
break;
}
/* Generic fallback */
k = hexmem(n->id.chassis_id, n->id.chassis_id_size);
if (!k)
return -ENOMEM;
done:
*ret = n->chassis_id_as_string = k;
return 0;
}
_public_ int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) {
assert_return(n, -EINVAL);
assert_return(type, -EINVAL);
assert_return(ret, -EINVAL);
assert_return(size, -EINVAL);
assert(n->id.port_id_size > 0);
*type = *(uint8_t*) n->id.port_id;
*ret = (uint8_t*) n->id.port_id + 1;
*size = n->id.port_id_size - 1;
return 0;
}
_public_ int sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor *n, const char **ret) {
char *k;
int r;
assert_return(n, -EINVAL);
assert_return(ret, -EINVAL);
if (n->port_id_as_string) {
*ret = n->port_id_as_string;
return 0;
}
assert(n->id.port_id_size > 0);
switch (*(uint8_t*) n->id.port_id) {
case LLDP_PORT_SUBTYPE_INTERFACE_ALIAS:
case LLDP_PORT_SUBTYPE_PORT_COMPONENT:
case LLDP_PORT_SUBTYPE_INTERFACE_NAME:
case LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED:
k = cescape_length((char*) n->id.port_id + 1, n->id.port_id_size - 1);
if (!k)
return -ENOMEM;
goto done;
case LLDP_PORT_SUBTYPE_MAC_ADDRESS:
r = format_mac_address(n->id.port_id, n->id.port_id_size, &k);
if (r < 0)
return r;
if (r > 0)
goto done;
break;
case LLDP_PORT_SUBTYPE_NETWORK_ADDRESS:
r = format_network_address(n->id.port_id, n->id.port_id_size, &k);
if (r < 0)
return r;
if (r > 0)
goto done;
break;
}
/* Generic fallback */
k = hexmem(n->id.port_id, n->id.port_id_size);
if (!k)
return -ENOMEM;
done:
*ret = n->port_id_as_string = k;
return 0;
}
_public_ int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret) {
assert_return(n, -EINVAL);
assert_return(ret, -EINVAL);
*ret = n->ttl;
return 0;
}
_public_ int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret) {
assert_return(n, -EINVAL);
assert_return(ret, -EINVAL);
if (!n->system_name)
return -ENODATA;
*ret = n->system_name;
return 0;
}
_public_ int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret) {
assert_return(n, -EINVAL);
assert_return(ret, -EINVAL);
if (!n->system_description)
return -ENODATA;
*ret = n->system_description;
return 0;
}
_public_ int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret) {
assert_return(n, -EINVAL);
assert_return(ret, -EINVAL);
if (!n->port_description)
return -ENODATA;
*ret = n->port_description;
return 0;
}
_public_ int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret) {
assert_return(n, -EINVAL);
assert_return(ret, -EINVAL);
if (!n->has_capabilities)
return -ENODATA;
*ret = n->system_capabilities;
return 0;
}
_public_ int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret) {
assert_return(n, -EINVAL);
assert_return(ret, -EINVAL);
if (!n->has_capabilities)
return -ENODATA;
*ret = n->enabled_capabilities;
return 0;
}
int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size) {
_cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
int r;
assert_return(ret, -EINVAL);
assert_return(raw || raw_size <= 0, -EINVAL);
n = lldp_neighbor_new(raw_size);
if (!n)
return -ENOMEM;
memcpy(LLDP_NEIGHBOR_RAW(n), raw, raw_size);
r = lldp_neighbor_parse(n);
if (r < 0)
return r;
*ret = n;
n = 0;
return r;
}
_public_ int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n) {
assert_return(n, -EINVAL);
assert(n->raw_size >= sizeof(struct ether_header));
n->rindex = sizeof(struct ether_header);
return 0;
}
_public_ int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n) {
size_t length;
assert_return(n, -EINVAL);
if (n->rindex == n->raw_size) /* EOF */
return -ESPIPE;
if (n->rindex + 2 > n->raw_size) /* Truncated message */
return -EBADMSG;
length = LLDP_NEIGHBOR_LENGTH(n);
if (n->rindex + 2 + length > n->raw_size)
return -EBADMSG;
n->rindex += 2 + length;
return n->rindex < n->raw_size;
}
_public_ int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type) {
assert_return(n, -EINVAL);
assert_return(type, -EINVAL);
if (n->rindex == n->raw_size) /* EOF */
return -ESPIPE;
if (n->rindex + 2 > n->raw_size)
return -EBADMSG;
*type = LLDP_NEIGHBOR_TYPE(n);
return 0;
}
_public_ int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type) {
uint8_t k;
int r;
assert_return(n, -EINVAL);
r = sd_lldp_neighbor_tlv_get_type(n, &k);
if (r < 0)
return r;
return type == k;
}
_public_ int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[3], uint8_t *subtype) {
const uint8_t *d;
size_t length;
int r;
assert_return(n, -EINVAL);
assert_return(oui, -EINVAL);
assert_return(subtype, -EINVAL);
r = sd_lldp_neighbor_tlv_is_type(n, LLDP_TYPE_PRIVATE);
if (r < 0)
return r;
if (r == 0)
return -ENXIO;
length = LLDP_NEIGHBOR_LENGTH(n);
if (length < 4)
return -EBADMSG;
if (n->rindex + 2 + length > n->raw_size)
return -EBADMSG;
d = LLDP_NEIGHBOR_DATA(n);
memcpy(oui, d, 3);
*subtype = d[3];
return 0;
}
_public_ int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[3], uint8_t subtype) {
uint8_t k[3], st;
int r;
r = sd_lldp_neighbor_tlv_get_oui(n, k, &st);
if (r == -ENXIO)
return 0;
if (r < 0)
return r;
return memcmp(k, oui, 3) == 0 && st == subtype;
}
_public_ int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) {
size_t length;
assert_return(n, -EINVAL);
assert_return(ret, -EINVAL);
assert_return(size, -EINVAL);
/* Note that this returns the full TLV, including the TLV header */
if (n->rindex + 2 > n->raw_size)
return -EBADMSG;
length = LLDP_NEIGHBOR_LENGTH(n);
if (n->rindex + 2 + length > n->raw_size)
return -EBADMSG;
*ret = (uint8_t*) LLDP_NEIGHBOR_RAW(n) + n->rindex;
*size = length + 2;
return 0;
}

View file

@ -0,0 +1,106 @@
#pragma once
/***
This file is part of systemd.
Copyright 2016 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <inttypes.h>
#include <stdbool.h>
#include <sys/types.h>
#include "sd-lldp.h"
#include "hash-funcs.h"
#include "lldp-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" object or n_ref > 0. */
sd_lldp *lldp;
unsigned n_ref;
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;
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_TYPE(const sd_lldp_neighbor *n) {
return ((uint8_t*) LLDP_NEIGHBOR_RAW(n))[n->rindex] >> 1;
}
static inline size_t LLDP_NEIGHBOR_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_DATA(const sd_lldp_neighbor *n) {
return ((uint8_t*) LLDP_NEIGHBOR_RAW(n)) + n->rindex + 2;
}
extern const struct hash_ops lldp_neighbor_id_hash_ops;
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);

View file

@ -21,65 +21,58 @@
#include "nm-sd-adapt.h"
#include <linux/filter.h>
#include <linux/if_ether.h>
#include <netinet/if_ether.h>
#include "fd-util.h"
#include "lldp-internal.h"
#include "lldp-network.h"
#include "lldp-tlv.h"
#include "socket-util.h"
int lldp_network_bind_raw_socket(int ifindex) {
typedef struct LLDPFrame {
struct ethhdr hdr;
uint8_t tlvs[0];
} LLDPFrame;
struct sock_filter filter[] = {
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(LLDPFrame, hdr.h_dest)), /* A <- 4 bytes of destination MAC */
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(LLDPFrame, hdr.h_dest) + 4), /* A <- remaining 2 bytes of destination MAC */
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(LLDPFrame, hdr.h_proto)), /* A <- protocol */
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_t) -1), /* accept packet */
};
struct sock_fprog fprog = {
static const struct sock_fprog fprog = {
.len = ELEMENTSOF(filter),
.filter = filter
.filter = (struct sock_filter*) filter,
};
_cleanup_close_ int s = -1;
union sockaddr_union saddrll = {
.ll.sll_family = AF_PACKET,
.ll.sll_ifindex = ifindex,
};
_cleanup_close_ int fd = -1;
int r;
assert(ifindex > 0);
s = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (s < 0)
fd = socket(PF_PACKET, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, htons(ETHERTYPE_LLDP));
if (fd < 0)
return -errno;
r = setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog));
r = setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog));
if (r < 0)
return -errno;
r = bind(s, &saddrll.sa, sizeof(saddrll.ll));
r = bind(fd, &saddrll.sa, sizeof(saddrll.ll));
if (r < 0)
return -errno;
r = s;
s = -1;
r = fd;
fd = -1;
return r;
}

View file

@ -1,3 +1,5 @@
#pragma once
/***
This file is part of systemd.
@ -18,8 +20,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#pragma once
#include "sd-event.h"
int lldp_network_bind_raw_socket(int ifindex);

View file

@ -1,118 +0,0 @@
/***
This file is part of systemd.
Copyright (C) 2014 Tom Gundersen
Copyright (C) 2014 Susant Sahani
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "nm-sd-adapt.h"
#include "alloc-util.h"
#include "async.h"
#include "lldp-internal.h"
#include "lldp-network.h"
#include "lldp-port.h"
int lldp_port_start(lldp_port *p) {
int r;
assert_return(p, -EINVAL);
r = lldp_network_bind_raw_socket(p->ifindex);
if (r < 0)
return r;
p->rawfd = r;
r = sd_event_add_io(p->event, &p->lldp_port_rx,
p->rawfd, EPOLLIN, lldp_receive_packet, p);
if (r < 0) {
log_debug_errno(r, "Failed to allocate event source: %m");
goto fail;
}
r = sd_event_source_set_priority(p->lldp_port_rx, p->event_priority);
if (r < 0) {
log_debug_errno(r, "Failed to set event priority: %m");
goto fail;
}
r = sd_event_source_set_description(p->lldp_port_rx, "lldp-port-rx");
if (r < 0) {
log_debug_errno(r, "Failed to set event name: %m");
goto fail;
}
return 0;
fail:
lldp_port_stop(p);
return r;
}
int lldp_port_stop(lldp_port *p) {
assert_return(p, -EINVAL);
p->rawfd = asynchronous_close(p->rawfd);
p->lldp_port_rx = sd_event_source_unref(p->lldp_port_rx);
return 0;
}
void lldp_port_free(lldp_port *p) {
if (!p)
return;
lldp_port_stop(p);
free(p->ifname);
free(p);
}
int lldp_port_new(int ifindex,
const char *ifname,
const struct ether_addr *addr,
void *userdata,
lldp_port **ret) {
_cleanup_free_ lldp_port *p = NULL;
assert_return(ifindex, -EINVAL);
assert_return(ifname, -EINVAL);
assert_return(addr, -EINVAL);
p = new0(lldp_port, 1);
if (!p)
return -ENOMEM;
p->rawfd = -1;
p->ifindex = ifindex;
p->ifname = strdup(ifname);
if (!p->ifname)
return -ENOMEM;
memcpy(&p->mac, addr, ETH_ALEN);
p->userdata = userdata;
*ret = p;
p = NULL;
return 0;
}

View file

@ -1,69 +0,0 @@
/***
This file is part of systemd.
Copyright (C) 2014 Tom Gundersen
Copyright (C) 2014 Susant Sahani
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#pragma once
#include <net/ethernet.h>
#include "sd-event.h"
#include "sd-lldp.h"
#include "util.h"
typedef struct lldp_port lldp_port;
typedef enum LLDPPortStatus {
LLDP_PORT_STATUS_NONE,
LLDP_PORT_STATUS_ENABLED,
LLDP_PORT_STATUS_DISABLED,
_LLDP_PORT_STATUS_MAX,
_LLDP_PORT_STATUS_INVALID = -1,
} LLDPPortStatus;
struct lldp_port {
LLDPPortStatus status;
int ifindex;
char *ifname;
struct ether_addr mac;
int rawfd;
sd_event *event;
sd_event_source *lldp_port_rx;
int event_priority;
void *userdata;
};
int lldp_port_new(int ifindex,
const char *ifname,
const struct ether_addr *addr,
void *userdata,
lldp_port **ret);
void lldp_port_free(lldp_port *p);
DEFINE_TRIVIAL_CLEANUP_FUNC(lldp_port*, lldp_port_free);
#define _cleanup_lldp_port_free_ _cleanup_(lldp_port_freep)
int lldp_port_start(lldp_port *p);
int lldp_port_stop(lldp_port *p);

View file

@ -1,640 +0,0 @@
/***
This file is part of systemd.
Copyright (C) 2014 Tom Gundersen
Copyright (C) 2014 Susant Sahani
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "nm-sd-adapt.h"
#include <arpa/inet.h>
#include <net/ethernet.h>
#include "alloc-util.h"
#include "lldp-tlv.h"
#include "macro.h"
int tlv_section_new(tlv_section **ret) {
tlv_section *s;
s = new0(tlv_section, 1);
if (!s)
return -ENOMEM;
*ret = s;
return 0;
}
void tlv_section_free(tlv_section *m) {
if (!m)
return;
free(m);
}
int tlv_packet_new(tlv_packet **ret) {
tlv_packet *m;
m = new0(tlv_packet, 1);
if (!m)
return -ENOMEM;
LIST_HEAD_INIT(m->sections);
m->n_ref = 1;
*ret = m;
return 0;
}
tlv_packet *sd_lldp_packet_ref(tlv_packet *m) {
if (!m)
return NULL;
assert(m->n_ref > 0);
m->n_ref++;
return m;
}
tlv_packet *sd_lldp_packet_unref(tlv_packet *m) {
tlv_section *s, *n;
if (!m)
return NULL;
assert(m->n_ref > 0);
m->n_ref--;
if (m->n_ref > 0)
return m;
LIST_FOREACH_SAFE(section, s, n, m->sections)
tlv_section_free(s);
free(m);
return NULL;
}
int tlv_packet_append_bytes(tlv_packet *m, const void *data, size_t data_length) {
uint8_t *p;
assert_return(m, -EINVAL);
assert_return(data, -EINVAL);
assert_return(data_length, -EINVAL);
if (m->length + data_length > ETHER_MAX_LEN)
return -ENOMEM;
p = m->pdu + m->length;
memcpy(p, data, data_length);
m->length += data_length;
return 0;
}
int tlv_packet_append_u8(tlv_packet *m, uint8_t data) {
assert_return(m, -EINVAL);
return tlv_packet_append_bytes(m, &data, sizeof(uint8_t));
}
int tlv_packet_append_u16(tlv_packet *m, uint16_t data) {
uint16_t type;
assert_return(m, -EINVAL);
type = htons(data);
return tlv_packet_append_bytes(m, &type, sizeof(uint16_t));
}
int tlv_packet_append_u32(tlv_packet *m, uint32_t data) {
uint32_t type;
assert_return(m, -EINVAL);
type = htonl(data);
return tlv_packet_append_bytes(m, &type, sizeof(uint32_t));
}
int tlv_packet_append_string(tlv_packet *m, char *data, uint16_t size) {
assert_return(m, -EINVAL);
return tlv_packet_append_bytes(m, data, size);
}
int lldp_tlv_packet_open_container(tlv_packet *m, uint16_t type) {
assert_return(m, -EINVAL);
m->container_pos = m->pdu + m->length;
return tlv_packet_append_u16(m, type << 9);
}
int lldp_tlv_packet_close_container(tlv_packet *m) {
uint16_t type;
assert_return(m, -EINVAL);
assert_return(m->container_pos, -EINVAL);
memcpy(&type, m->container_pos, sizeof(uint16_t));
type |= htons(((m->pdu + m->length) - (m->container_pos + 2)) & 0x01ff);
memcpy(m->container_pos, &type, sizeof(uint16_t));
return 0;
}
static inline int tlv_packet_read_internal(tlv_section *m, void **data) {
assert_return(m->read_pos, -EINVAL);
*data = m->read_pos;
return 0;
}
int tlv_packet_read_u8(tlv_packet *m, uint8_t *data) {
void *val = NULL;
int r;
assert_return(m, -EINVAL);
r = tlv_packet_read_internal(m->container, &val);
if (r < 0)
return r;
memcpy(data, val, sizeof(uint8_t));
m->container->read_pos ++;
return 0;
}
int tlv_packet_read_u16(tlv_packet *m, uint16_t *data) {
uint16_t t;
void *val = NULL;
int r;
assert_return(m, -EINVAL);
r = tlv_packet_read_internal(m->container, &val);
if (r < 0)
return r;
memcpy(&t, val, sizeof(uint16_t));
*data = ntohs(t);
m->container->read_pos += 2;
return 0;
}
int tlv_packet_read_u32(tlv_packet *m, uint32_t *data) {
uint32_t t;
void *val;
int r;
assert_return(m, -EINVAL);
r = tlv_packet_read_internal(m->container, &val);
if (r < 0)
return r;
memcpy(&t, val, sizeof(uint32_t));
*data = ntohl(t);
m->container->read_pos += 4;
return r;
}
int tlv_packet_read_string(tlv_packet *m, char **data, uint16_t *data_length) {
void *val = NULL;
int r;
assert_return(m, -EINVAL);
r = tlv_packet_read_internal(m->container, &val);
if (r < 0)
return r;
*data = (char *) val;
*data_length = m->container->data + m->container->length - m->container->read_pos;
m->container->read_pos += *data_length;
return 0;
}
int tlv_packet_read_bytes(tlv_packet *m, uint8_t **data, uint16_t *data_length) {
void *val = NULL;
int r;
assert_return(m, -EINVAL);
r = tlv_packet_read_internal(m->container, &val);
if (r < 0)
return r;
*data = (uint8_t *) val;
*data_length = m->container->data + m->container->length - m->container->read_pos;
m->container->read_pos += *data_length;
return 0;
}
/* parse raw TLV packet */
int tlv_packet_parse_pdu(tlv_packet *m, uint16_t size) {
tlv_section *section, *tail;
uint16_t t, l;
uint8_t *p;
int r;
assert_return(m, -EINVAL);
assert_return(size, -EINVAL);
p = m->pdu;
/* extract Ethernet header */
memcpy(&m->mac, p, ETH_ALEN);
p += sizeof(struct ether_header);
for (l = 0; l <= size; ) {
r = tlv_section_new(&section);
if (r < 0)
return r;
memcpy(&t, p, sizeof(uint16_t));
section->type = ntohs(t) >> 9;
section->length = ntohs(t) & 0x01ff;
if (section->type == LLDP_TYPE_END || section->type >=_LLDP_TYPE_MAX) {
tlv_section_free(section);
break;
}
p += 2;
if (section->type == LLDP_TYPE_PRIVATE &&
section->length >= LLDP_OUI_LEN + 1) {
section->oui = p;
p += LLDP_OUI_LEN;
section->subtype = *p++;
section->length -= LLDP_OUI_LEN + 1;
l += LLDP_OUI_LEN + 1;
}
section->data = p;
LIST_FIND_TAIL(section, m->sections, tail);
LIST_INSERT_AFTER(section, m->sections, tail, section);
p += section->length;
l += (section->length + 2);
}
return 0;
}
int lldp_tlv_packet_enter_container(tlv_packet *m, uint16_t type) {
tlv_section *s;
assert_return(m, -EINVAL);
assert_return(type != LLDP_TYPE_PRIVATE, -EINVAL);
LIST_FOREACH(section, s, m->sections)
if (s->type == type)
break;
if (!s)
return -1;
m->container = s;
m->container->read_pos = s->data;
if (!m->container->read_pos) {
m->container = NULL;
return -1;
}
return 0;
}
int lldp_tlv_packet_enter_container_oui(tlv_packet *m, const uint8_t *oui, uint8_t subtype) {
tlv_section *s;
assert_return(m, -EINVAL);
assert_return(oui, -EINVAL);
LIST_FOREACH(section, s, m->sections) {
if (s->type == LLDP_TYPE_PRIVATE &&
s->oui &&
s->subtype == subtype &&
!memcmp(s->oui, oui, LLDP_OUI_LEN))
break;
}
if (!s)
return -1;
m->container = s;
m->container->read_pos = s->data;
if (!m->container->read_pos) {
m->container = NULL;
return -1;
}
return 0;
}
int lldp_tlv_packet_exit_container(tlv_packet *m) {
assert_return(m, -EINVAL);
m->container = 0;
return 0;
}
static int lldp_tlv_packet_read_u16_tlv(tlv_packet *tlv, uint16_t type, uint16_t *value) {
int r, r2;
assert_return(tlv, -EINVAL);
r = lldp_tlv_packet_enter_container(tlv, type);
if (r < 0)
return r;
r = tlv_packet_read_u16(tlv, value);
r2 = lldp_tlv_packet_exit_container(tlv);
return r < 0 ? r : r2;
}
static int lldp_tlv_packet_read_string_tlv(tlv_packet *tlv, uint16_t type, char **data, uint16_t *length) {
char *s;
int r, r2;
assert_return(tlv, -EINVAL);
r = lldp_tlv_packet_enter_container(tlv, type);
if (r < 0)
return r;
r = tlv_packet_read_string(tlv, &s, length);
if (r < 0)
goto out;
*data = (char *) s;
out:
r2 = lldp_tlv_packet_exit_container(tlv);
return r < 0 ? r : r2;
}
int sd_lldp_packet_read_chassis_id(tlv_packet *tlv,
uint8_t *type,
uint8_t **data,
uint16_t *length) {
uint8_t subtype;
int r, r2;
assert_return(tlv, -EINVAL);
r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_CHASSIS_ID);
if (r < 0)
return r;
r = tlv_packet_read_u8(tlv, &subtype);
if (r < 0)
goto out;
switch (subtype) {
case LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS:
r = tlv_packet_read_bytes(tlv, data, length);
if (r < 0)
goto out;
break;
default:
r = -EOPNOTSUPP;
break;
}
*type = subtype;
out:
r2 = lldp_tlv_packet_exit_container(tlv);
return r < 0 ? r : r2;
}
int sd_lldp_packet_read_port_id(tlv_packet *tlv,
uint8_t *type,
uint8_t **data,
uint16_t *length) {
uint8_t subtype;
char *s;
int r, r2;
assert_return(tlv, -EINVAL);
r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_PORT_ID);
if (r < 0)
return r;
r = tlv_packet_read_u8(tlv, &subtype);
if (r < 0)
goto out;
switch (subtype) {
case LLDP_PORT_SUBTYPE_PORT_COMPONENT:
case LLDP_PORT_SUBTYPE_INTERFACE_ALIAS:
case LLDP_PORT_SUBTYPE_INTERFACE_NAME:
case LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED:
r = tlv_packet_read_string(tlv, &s, length);
if (r < 0)
goto out;
*data = (uint8_t *) s;
break;
case LLDP_PORT_SUBTYPE_MAC_ADDRESS:
r = tlv_packet_read_bytes(tlv, data, length);
if (r < 0)
goto out;
break;
default:
r = -EOPNOTSUPP;
break;
}
*type = subtype;
out:
r2 = lldp_tlv_packet_exit_container(tlv);
return r < 0 ? r : r2;
}
int sd_lldp_packet_read_ttl(tlv_packet *tlv, uint16_t *ttl) {
return lldp_tlv_packet_read_u16_tlv(tlv, LLDP_TYPE_TTL, ttl);
}
int sd_lldp_packet_read_system_name(tlv_packet *tlv,
char **data,
uint16_t *length) {
return lldp_tlv_packet_read_string_tlv(tlv, LLDP_TYPE_SYSTEM_NAME, data, length);
}
int sd_lldp_packet_read_system_description(tlv_packet *tlv,
char **data,
uint16_t *length) {
return lldp_tlv_packet_read_string_tlv(tlv, LLDP_TYPE_SYSTEM_DESCRIPTION, data, length);
}
int sd_lldp_packet_read_port_description(tlv_packet *tlv,
char **data,
uint16_t *length) {
return lldp_tlv_packet_read_string_tlv(tlv, LLDP_TYPE_PORT_DESCRIPTION, data, length);
}
int sd_lldp_packet_read_system_capability(tlv_packet *tlv, uint16_t *data) {
return lldp_tlv_packet_read_u16_tlv(tlv, LLDP_TYPE_SYSTEM_CAPABILITIES, data);
}
int sd_lldp_packet_read_port_vlan_id(tlv_packet *tlv, uint16_t *id) {
int r, r2;
assert_return(tlv, -EINVAL);
r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_PORT_VLAN_ID);
if (r < 0)
return r;
r = tlv_packet_read_u16(tlv, id);
r2 = lldp_tlv_packet_exit_container(tlv);
return r < 0 ? r : r2;
}
int sd_lldp_packet_read_port_protocol_vlan_id(sd_lldp_packet *tlv, uint8_t *flags, uint16_t *id) {
int r, r2;
assert_return(tlv, -EINVAL);
r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_PORT_PROTOCOL_VLAN_ID);
if (r < 0)
return r;
r = tlv_packet_read_u8(tlv, flags);
if (r >= 0)
r = tlv_packet_read_u16(tlv, id);
r2 = lldp_tlv_packet_exit_container(tlv);
return r < 0 ? r : r2;
}
int sd_lldp_packet_read_vlan_name(tlv_packet *tlv, uint16_t *vlan_id, char **name, uint16_t *length) {
int r, r2;
uint8_t len = 0;
assert_return(tlv, -EINVAL);
r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_VLAN_NAME);
if (r < 0)
return r;
r = tlv_packet_read_u16(tlv, vlan_id);
if (r >= 0)
r = tlv_packet_read_u8(tlv, &len);
if (r >= 0)
r = tlv_packet_read_string(tlv, name, length);
if (r >= 0 && len < *length)
*length = len;
r2 = lldp_tlv_packet_exit_container(tlv);
return r < 0 ? r : r2;
}
int sd_lldp_packet_read_management_vid(tlv_packet *tlv, uint16_t *id) {
int r, r2;
assert_return(tlv, -EINVAL);
r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_MANAGEMENT_VID);
if (r < 0)
return r;
r = tlv_packet_read_u16(tlv, id);
r2 = lldp_tlv_packet_exit_container(tlv);
return r < 0 ? r : r2;
}
int sd_lldp_packet_read_link_aggregation(sd_lldp_packet *tlv, uint8_t *status, uint32_t *id) {
int r, r2;
assert_return(tlv, -EINVAL);
r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_LINK_AGGREGATION);
if (r < 0)
return r;
r = tlv_packet_read_u8(tlv, status);
if (r >= 0)
r = tlv_packet_read_u32(tlv, id);
r2 = lldp_tlv_packet_exit_container(tlv);
return r < 0 ? r : r2;
}
int sd_lldp_packet_get_destination_type(tlv_packet *tlv, int *dest) {
assert_return(tlv, -EINVAL);
assert_return(dest, -EINVAL);
/* 802.1AB-2009, Table 7-1 */
if (!memcmp(&tlv->mac, LLDP_MAC_NEAREST_BRIDGE, ETH_ALEN))
*dest = SD_LLDP_DESTINATION_TYPE_NEAREST_BRIDGE;
else if (!memcmp(&tlv->mac, LLDP_MAC_NEAREST_NON_TPMR_BRIDGE, ETH_ALEN))
*dest = SD_LLDP_DESTINATION_TYPE_NEAREST_NON_TPMR_BRIDGE;
else if (!memcmp(&tlv->mac, LLDP_MAC_NEAREST_CUSTOMER_BRIDGE, ETH_ALEN))
*dest = SD_LLDP_DESTINATION_TYPE_NEAREST_CUSTOMER_BRIDGE;
else
return -EINVAL;
return 0;
}

View file

@ -1,94 +0,0 @@
/***
This file is part of systemd.
Copyright (C) 2014 Tom Gundersen
Copyright (C) 2014 Susant Sahani
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#pragma once
#include <net/ethernet.h>
#include "sd-lldp.h"
#include "list.h"
#include "lldp.h"
#include "util.h"
typedef struct sd_lldp_packet tlv_packet;
typedef struct sd_lldp_section tlv_section;
#define LLDP_OUI_LEN 3
struct sd_lldp_section {
uint16_t type;
uint16_t length;
uint8_t *oui;
uint8_t subtype;
uint8_t *read_pos;
uint8_t *data;
LIST_FIELDS(tlv_section, section);
};
#define LLDP_MAC_NEAREST_BRIDGE (uint8_t[]) { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e }
#define LLDP_MAC_NEAREST_NON_TPMR_BRIDGE (uint8_t[]) { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }
#define LLDP_MAC_NEAREST_CUSTOMER_BRIDGE (uint8_t[]) { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }
int tlv_section_new(tlv_section **ret);
void tlv_section_free(tlv_section *ret);
struct sd_lldp_packet {
unsigned n_ref;
uint16_t type;
uint16_t length;
usec_t ts;
uint8_t *container_pos;
uint8_t pdu[ETHER_MAX_LEN];
void *userdata;
struct ether_addr mac;
tlv_section *container;
LIST_HEAD(tlv_section, sections);
};
int tlv_packet_new(tlv_packet **ret);
int lldp_tlv_packet_open_container(tlv_packet *m, uint16_t type);
int lldp_tlv_packet_close_container(tlv_packet *m);
int tlv_packet_append_bytes(tlv_packet *m, const void *data, size_t data_length);
int tlv_packet_append_u8(tlv_packet *m, uint8_t data);
int tlv_packet_append_u16(tlv_packet *m, uint16_t data);
int tlv_packet_append_u32(tlv_packet *m, uint32_t data);
int tlv_packet_append_string(tlv_packet *m, char *data, uint16_t size);
int lldp_tlv_packet_enter_container(tlv_packet *m, uint16_t type);
int lldp_tlv_packet_enter_container_oui(tlv_packet *m, const uint8_t *oui, uint8_t subtype);
int lldp_tlv_packet_exit_container(tlv_packet *m);
int tlv_packet_read_bytes(tlv_packet *m, uint8_t **data, uint16_t *data_length);
int tlv_packet_read_string(tlv_packet *m, char **data, uint16_t *data_length);
int tlv_packet_read_u8(tlv_packet *m, uint8_t *data);
int tlv_packet_read_u16(tlv_packet *m, uint16_t *data);
int tlv_packet_read_u32(tlv_packet *m, uint32_t *data);
int tlv_packet_parse_pdu(tlv_packet *t, uint16_t size);

View file

@ -1,3 +1,5 @@
#pragma once
/***
This file is part of systemd.
@ -18,14 +20,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#pragma once
#define LLDP_MULTICAST_ADDR { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e }
#define ETHERTYPE_LLDP 0x88cc
/* IEEE 802.3AB Clause 9: TLV Types */
typedef enum LLDPTypes {
enum {
LLDP_TYPE_END = 0,
LLDP_TYPE_CHASSIS_ID = 1,
LLDP_TYPE_PORT_ID = 2,
@ -36,12 +34,10 @@ typedef enum LLDPTypes {
LLDP_TYPE_SYSTEM_CAPABILITIES = 7,
LLDP_TYPE_MGMT_ADDRESS = 8,
LLDP_TYPE_PRIVATE = 127,
_LLDP_TYPE_MAX,
_LLDP_TYPE_INVALID = -1,
} LLDPTypes;
};
/* IEEE 802.3AB Clause 9.5.2: Chassis subtypes */
typedef enum LLDPChassisSubtypes {
enum {
LLDP_CHASSIS_SUBTYPE_RESERVED = 0,
LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT = 1,
LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS = 2,
@ -50,25 +46,21 @@ typedef enum LLDPChassisSubtypes {
LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS = 5,
LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME = 6,
LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED = 7,
_LLDP_CHASSIS_SUBTYPE_MAX,
_LLDP_CHASSIS_SUBTYPE_INVALID = -1,
} LLDPChassisSubtypes;
};
/* IEEE 802.3AB Clause 9.5.3: Port subtype */
typedef enum LLDPPortSubtypes {
enum {
LLDP_PORT_SUBTYPE_RESERVED = 0,
LLDP_PORT_SUBTYPE_INTERFACE_ALIAS = 1,
LLDP_PORT_SUBTYPE_PORT_COMPONENT = 2,
LLDP_PORT_SUBTYPE_MAC_ADDRESS = 3,
LLDP_PORT_SUBTYPE_NETWORK = 4,
LLDP_PORT_SUBTYPE_NETWORK_ADDRESS = 4,
LLDP_PORT_SUBTYPE_INTERFACE_NAME = 5,
LLDP_PORT_SUBTYPE_AGENT_CIRCUIT_ID = 6,
LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED = 7,
_LLDP_PORT_SUBTYPE_MAX,
_LLDP_PORT_SUBTYPE_INVALID = -1
} LLDPPortSubtypes;
};
typedef enum LLDPSystemCapabilities {
enum {
LLDP_SYSTEM_CAPABILITIES_OTHER = 1 << 0,
LLDP_SYSTEM_CAPABILITIES_REPEATER = 1 << 1,
LLDP_SYSTEM_CAPABILITIES_BRIDGE = 1 << 2,
@ -80,47 +72,31 @@ typedef enum LLDPSystemCapabilities {
LLDP_SYSTEM_CAPABILITIES_CVLAN = 1 << 8,
LLDP_SYSTEM_CAPABILITIES_SVLAN = 1 << 9,
LLDP_SYSTEM_CAPABILITIES_TPMR = 1 << 10,
_LLDP_SYSTEM_CAPABILITIES_MAX,
_LLDP_SYSTEM_CAPABILITIES_INVALID = -1,
} LLDPSystemCapabilities;
};
typedef enum LLDPMedSubtype {
LLDP_MED_SUBTYPE_RESERVED = 0,
LLDP_MED_SUBTYPE_CAPABILITIES = 1,
LLDP_MED_SUBTYPE_NETWORK_POLICY = 2,
LLDP_MED_SUBTYPE_LOCATION_ID = 3,
LLDP_MED_SUBTYPE_EXTENDED_PVMDI = 4,
LLDP_MED_SUBTYPE_INV_HWREV = 5,
LLDP_MED_SUBTYPE_INV_FWREV = 6,
LLDP_MED_SUBTYPE_INV_SWREV = 7,
LLDP_MED_SUBTYPE_INV_SERIAL = 8,
LLDP_MED_SUBTYPE_INV_MANUFACTURER = 9,
LLDP_MED_SUBTYPE_INV_MODELNAME = 10,
LLDP_MED_SUBTYPE_INV_ASSETID = 11,
_LLDP_MED_SUBTYPE_MAX,
_LLDP_MED_SUBTYPE_INVALID = -1,
} LLDPMedSubtype;
#define _LLDP_SYSTEM_CAPABILITIES_ALL ((uint16_t) -1)
#define _LLDP_SYSTEM_CAPABILITIES_ALL_ROUTERS \
((uint16_t) \
(LLDP_SYSTEM_CAPABILITIES_REPEATER| \
LLDP_SYSTEM_CAPABILITIES_BRIDGE| \
LLDP_SYSTEM_CAPABILITIES_WLAN_AP| \
LLDP_SYSTEM_CAPABILITIES_ROUTER| \
LLDP_SYSTEM_CAPABILITIES_DOCSIS| \
LLDP_SYSTEM_CAPABILITIES_CVLAN| \
LLDP_SYSTEM_CAPABILITIES_SVLAN| \
LLDP_SYSTEM_CAPABILITIES_TPMR))
typedef enum LLDPMedCapability {
LLDP_MED_CAPABILITY_CAPAPILITIES = 1 << 0,
LLDP_MED_CAPABILITY_NETWORK_POLICY = 1 << 1,
LLDP_MED_CAPABILITY_LOCATION_ID = 1 << 2,
LLDP_MED_CAPABILITY_EXTENDED_PSE = 1 << 3,
LLDP_MED_CAPABILITY_EXTENDED_PD = 1 << 4,
LLDP_MED_CAPABILITY_INVENTORY = 1 << 5,
LLDP_MED_CAPABILITY_MAX,
LLDP_MED_CAPABILITY_INVALID = -1,
} LLDPMedCapability;
#define LLDP_OUI_802_1 (uint8_t[]) { 0x00, 0x80, 0xc2 }
#define LLDP_OUI_802_3 (uint8_t[]) { 0x00, 0x12, 0x0f }
enum {
LLDP_OUI_SUBTYPE_802_1_PORT_VLAN_ID = 1,
LLDP_OUI_SUBTYPE_802_1_PORT_PROTOCOL_VLAN_ID = 2,
LLDP_OUI_SUBTYPE_802_1_VLAN_NAME = 3,
LLDP_OUI_SUBTYPE_802_1_PROTOCOL_IDENTITY = 4,
LLDP_OUI_SUBTYPE_802_1_VID_USAGE_DIGEST = 5,
LLDP_OUI_SUBTYPE_802_1_MANAGEMENT_VID = 6,
LLDP_OUI_SUBTYPE_802_1_LINK_AGGREGATION = 7,
LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID = 1,
LLDP_OUI_802_1_SUBTYPE_PORT_PROTOCOL_VLAN_ID = 2,
LLDP_OUI_802_1_SUBTYPE_VLAN_NAME = 3,
LLDP_OUI_802_1_SUBTYPE_PROTOCOL_IDENTITY = 4,
LLDP_OUI_802_1_SUBTYPE_VID_USAGE_DIGEST = 5,
LLDP_OUI_802_1_SUBTYPE_MANAGEMENT_VID = 6,
LLDP_OUI_802_1_SUBTYPE_LINK_AGGREGATION = 7,
};

View file

@ -381,7 +381,7 @@ int deserialize_in_addrs(struct in_addr **ret, const char *string) {
if (r <= 0)
continue;
size ++;
size++;
}
*ret = addresses;
@ -488,7 +488,7 @@ int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t
return -ENOMEM;
entry = strndup(word, len);
if(!entry)
if (!entry)
return -ENOMEM;
tok = entry;

View file

@ -103,7 +103,7 @@ struct sd_dhcp_client {
sd_event_source *timeout_t1;
sd_event_source *timeout_t2;
sd_event_source *timeout_expire;
sd_dhcp_client_cb_t cb;
sd_dhcp_client_callback_t cb;
void *userdata;
sd_dhcp_lease *lease;
usec_t start_delay;
@ -123,7 +123,7 @@ static int client_receive_message_udp(sd_event_source *s, int fd,
uint32_t revents, void *userdata);
static void client_stop(sd_dhcp_client *client, int error);
int sd_dhcp_client_set_callback(sd_dhcp_client *client, sd_dhcp_client_cb_t cb,
int sd_dhcp_client_set_callback(sd_dhcp_client *client, sd_dhcp_client_callback_t cb,
void *userdata) {
assert_return(client, -EINVAL);
@ -960,7 +960,7 @@ static int client_initialize_time_events(sd_dhcp_client *client) {
client->timeout_resend = sd_event_source_unref(client->timeout_resend);
if (client->start_delay) {
sd_event_now(client->event, clock_boottime_or_monotonic(), &usec);
assert_se(sd_event_now(client->event, clock_boottime_or_monotonic(), &usec) >= 0);
usec += client->start_delay;
}
@ -1527,20 +1527,17 @@ static int client_receive_message_udp(sd_event_source *s, int fd,
uint32_t revents, void *userdata) {
sd_dhcp_client *client = userdata;
_cleanup_free_ DHCPMessage *message = NULL;
int buflen = 0, len, r;
const struct ether_addr zero_mac = { { 0, 0, 0, 0, 0, 0 } };
const struct ether_addr *expected_chaddr = NULL;
uint8_t expected_hlen = 0;
ssize_t len, buflen;
assert(s);
assert(client);
r = ioctl(fd, FIONREAD, &buflen);
if (r < 0)
return -errno;
else if (buflen < 0)
/* this can't be right */
return -EIO;
buflen = next_datagram_size_fd(fd);
if (buflen < 0)
return buflen;
message = malloc0(buflen);
if (!message)
@ -1618,17 +1615,15 @@ static int client_receive_message_raw(sd_event_source *s, int fd,
};
struct cmsghdr *cmsg;
bool checksum = true;
int buflen = 0, len, r;
ssize_t buflen, len;
int r;
assert(s);
assert(client);
r = ioctl(fd, FIONREAD, &buflen);
if (r < 0)
return -errno;
else if (buflen < 0)
/* this can't be right */
return -EIO;
buflen = next_datagram_size_fd(fd);
if (buflen < 0)
return buflen;
packet = malloc0(buflen);
if (!packet)
@ -1698,8 +1693,7 @@ int sd_dhcp_client_stop(sd_dhcp_client *client) {
return 0;
}
int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event,
int priority) {
int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event, int64_t priority) {
int r;
assert_return(client, -EINVAL);

View file

@ -35,6 +35,7 @@
#include "in-addr-util.h"
#include "network-internal.h"
#include "random-util.h"
#include "socket-util.h"
#include "string-table.h"
#include "util.h"
@ -65,7 +66,7 @@ struct sd_dhcp6_client {
uint8_t retransmit_count;
sd_event_source *timeout_resend;
sd_event_source *timeout_resend_expire;
sd_dhcp6_client_cb_t cb;
sd_dhcp6_client_callback_t cb;
void *userdata;
struct duid duid;
size_t duid_len;
@ -112,7 +113,7 @@ DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
static int client_start(sd_dhcp6_client *client, enum DHCP6State state);
int sd_dhcp6_client_set_callback(sd_dhcp6_client *client, sd_dhcp6_client_cb_t cb, void *userdata) {
int sd_dhcp6_client_set_callback(sd_dhcp6_client *client, sd_dhcp6_client_callback_t cb, void *userdata) {
assert_return(client, -EINVAL);
client->cb = cb;
@ -893,18 +894,16 @@ static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
sd_dhcp6_client *client = userdata;
DHCP6_CLIENT_DONT_DESTROY(client);
_cleanup_free_ DHCP6Message *message = NULL;
int r, buflen, len;
ssize_t buflen, len;
int r = 0;
assert(s);
assert(client);
assert(client->event);
r = ioctl(fd, FIONREAD, &buflen);
if (r < 0)
return -errno;
else if (buflen < 0)
/* This really should not happen */
return -EIO;
buflen = next_datagram_size_fd(fd);
if (buflen < 0)
return buflen;
message = malloc(buflen);
if (!message)
@ -1207,7 +1206,7 @@ error:
return r;
}
int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int priority) {
int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int64_t priority) {
int r;
assert_return(client, -EINVAL);

View file

@ -94,7 +94,7 @@ struct sd_ipv4acd {
struct ether_addr mac_addr;
sd_event *event;
int event_priority;
sd_ipv4acd_cb_t cb;
sd_ipv4acd_callback_t cb;
void* userdata;
};
@ -430,7 +430,7 @@ int sd_ipv4acd_detach_event(sd_ipv4acd *ll) {
return 0;
}
int sd_ipv4acd_attach_event(sd_ipv4acd *ll, sd_event *event, int priority) {
int sd_ipv4acd_attach_event(sd_ipv4acd *ll, sd_event *event, int64_t priority) {
int r;
assert_return(ll, -EINVAL);
@ -449,7 +449,7 @@ int sd_ipv4acd_attach_event(sd_ipv4acd *ll, sd_event *event, int priority) {
return 0;
}
int sd_ipv4acd_set_callback(sd_ipv4acd *ll, sd_ipv4acd_cb_t cb, void *userdata) {
int sd_ipv4acd_set_callback(sd_ipv4acd *ll, sd_ipv4acd_callback_t cb, void *userdata) {
assert_return(ll, -EINVAL);
ll->cb = cb;
@ -458,7 +458,7 @@ int sd_ipv4acd_set_callback(sd_ipv4acd *ll, sd_ipv4acd_cb_t cb, void *userdata)
return 0;
}
int sd_ipv4acd_set_address(sd_ipv4acd *ll, const struct in_addr *address){
int sd_ipv4acd_set_address(sd_ipv4acd *ll, const struct in_addr *address) {
assert_return(ll, -EINVAL);
assert_return(address, -EINVAL);
assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);

View file

@ -54,7 +54,7 @@ struct sd_ipv4ll {
/* External */
be32_t claimed_address;
sd_ipv4ll_cb_t cb;
sd_ipv4ll_callback_t cb;
void* userdata;
};
@ -162,7 +162,7 @@ int sd_ipv4ll_detach_event(sd_ipv4ll *ll) {
return sd_ipv4acd_detach_event(ll->acd);
}
int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int priority) {
int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int64_t priority) {
int r;
assert_return(ll, -EINVAL);
@ -174,7 +174,7 @@ int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int priority) {
return 0;
}
int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_cb_t cb, void *userdata) {
int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_callback_t cb, void *userdata) {
assert_return(ll, -EINVAL);
ll->cb = cb;
@ -183,7 +183,7 @@ int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_cb_t cb, void *userdata) {
return 0;
}
int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address){
int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address) {
assert_return(ll, -EINVAL);
assert_return(address, -EINVAL);

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -155,7 +155,6 @@ _public_ int sd_id128_get_machine(sd_id128_t *ret) {
return 0;
}
#if 0 /* NM_IGNORED */
_public_ int sd_id128_get_boot(sd_id128_t *ret) {
static thread_local sd_id128_t saved_boot_id;
static thread_local bool saved_boot_id_valid = false;
@ -211,6 +210,7 @@ _public_ int sd_id128_get_boot(sd_id128_t *ret) {
return 0;
}
#if 0 /* NM_IGNORED */
_public_ int sd_id128_randomize(sd_id128_t *ret) {
sd_id128_t t;
int r;

View file

@ -182,7 +182,7 @@ int dns_label_unescape_suffix(const char *name, const char **label_terminal, cha
unsigned slashes = 0;
for (y = terminal - 1; y >= name && *y == '\\'; y--)
slashes ++;
slashes++;
if (slashes % 2 == 0) {
/* The '.' was not escaped */
@ -194,7 +194,7 @@ int dns_label_unescape_suffix(const char *name, const char **label_terminal, cha
}
}
terminal --;
terminal--;
}
r = dns_label_unescape(&name, dest, sz);
@ -333,7 +333,7 @@ int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded
l = strlen(buffer);
/* Verify that the the result is not longer than one DNS label. */
/* Verify that the result is not longer than one DNS label. */
if (l <= 0 || l > DNS_LABEL_MAX)
return -EINVAL;
if (l > decoded_max)
@ -1177,7 +1177,7 @@ int dns_name_skip(const char *a, unsigned n_labels, const char **ret) {
assert(a);
assert(ret);
for (; n_labels > 0; n_labels --) {
for (; n_labels > 0; n_labels--) {
r = dns_name_parent(&a);
if (r < 0)
return r;

View file

@ -1,3 +1,5 @@
#pragma once
/***
This file is part of systemd.
@ -17,9 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#pragma once
#include <errno.h>
#include <stdbool.h>
#include <stddef.h>

View file

@ -84,9 +84,9 @@ enum {
typedef struct sd_dhcp_client sd_dhcp_client;
typedef void (*sd_dhcp_client_cb_t)(sd_dhcp_client *client, int event,
typedef void (*sd_dhcp_client_callback_t)(sd_dhcp_client *client, int event,
void *userdata);
int sd_dhcp_client_set_callback(sd_dhcp_client *client, sd_dhcp_client_cb_t cb,
int sd_dhcp_client_set_callback(sd_dhcp_client *client, sd_dhcp_client_callback_t cb,
void *userdata);
int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option);
@ -113,7 +113,7 @@ sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client);
int sd_dhcp_client_new(sd_dhcp_client **ret);
int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event, int priority);
int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event, int64_t priority);
int sd_dhcp_client_detach_event(sd_dhcp_client *client);
sd_event *sd_dhcp_client_get_event(sd_dhcp_client *client);

View file

@ -76,10 +76,10 @@ enum {
typedef struct sd_dhcp6_client sd_dhcp6_client;
typedef void (*sd_dhcp6_client_cb_t)(sd_dhcp6_client *client, int event,
typedef void (*sd_dhcp6_client_callback_t)(sd_dhcp6_client *client, int event,
void *userdata);
int sd_dhcp6_client_set_callback(sd_dhcp6_client *client,
sd_dhcp6_client_cb_t cb, void *userdata);
sd_dhcp6_client_callback_t cb, void *userdata);
int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index);
int sd_dhcp6_client_set_local_address(sd_dhcp6_client *client, const struct in6_addr *local_address);
@ -97,8 +97,7 @@ int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret);
int sd_dhcp6_client_stop(sd_dhcp6_client *client);
int sd_dhcp6_client_start(sd_dhcp6_client *client);
int sd_dhcp6_client_is_running(sd_dhcp6_client *client);
int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
int priority);
int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int64_t priority);
int sd_dhcp6_client_detach_event(sd_dhcp6_client *client);
sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client);
sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client);

View file

@ -37,12 +37,12 @@ enum {
};
typedef struct sd_ipv4acd sd_ipv4acd;
typedef void (*sd_ipv4acd_cb_t)(sd_ipv4acd *ll, int event, void *userdata);
typedef void (*sd_ipv4acd_callback_t)(sd_ipv4acd *ll, int event, void *userdata);
int sd_ipv4acd_detach_event(sd_ipv4acd *ll);
int sd_ipv4acd_attach_event(sd_ipv4acd *ll, sd_event *event, int priority);
int sd_ipv4acd_attach_event(sd_ipv4acd *ll, sd_event *event, int64_t priority);
int sd_ipv4acd_get_address(sd_ipv4acd *ll, struct in_addr *address);
int sd_ipv4acd_set_callback(sd_ipv4acd *ll, sd_ipv4acd_cb_t cb, void *userdata);
int sd_ipv4acd_set_callback(sd_ipv4acd *ll, sd_ipv4acd_callback_t cb, void *userdata);
int sd_ipv4acd_set_mac(sd_ipv4acd *ll, const struct ether_addr *addr);
int sd_ipv4acd_set_index(sd_ipv4acd *ll, int interface_index);
int sd_ipv4acd_set_address(sd_ipv4acd *ll, const struct in_addr *address);

View file

@ -36,12 +36,12 @@ enum {
};
typedef struct sd_ipv4ll sd_ipv4ll;
typedef void (*sd_ipv4ll_cb_t)(sd_ipv4ll *ll, int event, void *userdata);
typedef void (*sd_ipv4ll_callback_t)(sd_ipv4ll *ll, int event, void *userdata);
int sd_ipv4ll_detach_event(sd_ipv4ll *ll);
int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int priority);
int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int64_t priority);
int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address);
int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_cb_t cb, void *userdata);
int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_callback_t cb, void *userdata);
int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr);
int sd_ipv4ll_set_index(sd_ipv4ll *ll, int interface_index);
int sd_ipv4ll_set_address(sd_ipv4ll *ll, const struct in_addr *address);

View file

@ -30,57 +30,69 @@
_SD_BEGIN_DECLARATIONS;
enum {
SD_LLDP_EVENT_UPDATE_INFO = 0,
};
enum {
SD_LLDP_DESTINATION_TYPE_NEAREST_BRIDGE,
SD_LLDP_DESTINATION_TYPE_NEAREST_NON_TPMR_BRIDGE,
SD_LLDP_DESTINATION_TYPE_NEAREST_CUSTOMER_BRIDGE,
};
typedef struct sd_lldp sd_lldp;
typedef struct sd_lldp_packet sd_lldp_packet;
typedef struct sd_lldp_neighbor sd_lldp_neighbor;
typedef void (*sd_lldp_cb_t)(sd_lldp *lldp, int event, void *userdata);
typedef enum sd_lldp_event {
SD_LLDP_EVENT_ADDED = 'a',
SD_LLDP_EVENT_REMOVED = 'r',
SD_LLDP_EVENT_UPDATED = 'u',
SD_LLDP_EVENT_REFRESHED = 'f',
} sd_lldp_event;
int sd_lldp_new(int ifindex, const char *ifname, const struct ether_addr *mac, sd_lldp **ret);
typedef void (*sd_lldp_callback_t)(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n, void *userdata);
int sd_lldp_new(sd_lldp **ret, int ifindex);
sd_lldp* sd_lldp_unref(sd_lldp *lldp);
int sd_lldp_start(sd_lldp *lldp);
int sd_lldp_stop(sd_lldp *lldp);
int sd_lldp_attach_event(sd_lldp *lldp, sd_event *event, int priority);
int sd_lldp_attach_event(sd_lldp *lldp, sd_event *event, int64_t priority);
int sd_lldp_detach_event(sd_lldp *lldp);
int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_cb_t cb, void *userdata);
int sd_lldp_save(sd_lldp *lldp, const char *file);
int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_callback_t cb, void *userdata);
int sd_lldp_packet_read_chassis_id(sd_lldp_packet *tlv, uint8_t *type, uint8_t **data, uint16_t *length);
int sd_lldp_packet_read_port_id(sd_lldp_packet *tlv, uint8_t *type, uint8_t **data, uint16_t *length);
int sd_lldp_packet_read_ttl(sd_lldp_packet *tlv, uint16_t *ttl);
int sd_lldp_packet_read_system_name(sd_lldp_packet *tlv, char **data, uint16_t *length);
int sd_lldp_packet_read_system_description(sd_lldp_packet *tlv, char **data, uint16_t *length);
int sd_lldp_packet_read_system_capability(sd_lldp_packet *tlv, uint16_t *data);
int sd_lldp_packet_read_port_description(sd_lldp_packet *tlv, char **data, uint16_t *length);
/* Controls how much and what to store in the neighbors database */
int sd_lldp_set_neighbors_max(sd_lldp *lldp, uint64_t n);
int sd_lldp_match_capabilities(sd_lldp *lldp, uint16_t mask);
int sd_lldp_set_filter_address(sd_lldp *lldp, const struct ether_addr *address);
/* IEEE 802.1 organizationally specific TLVs */
int sd_lldp_packet_read_port_vlan_id(sd_lldp_packet *tlv, uint16_t *id);
int sd_lldp_packet_read_port_protocol_vlan_id(sd_lldp_packet *tlv, uint8_t *flags, uint16_t *id);
int sd_lldp_packet_read_vlan_name(sd_lldp_packet *tlv, uint16_t *vlan_id, char **name, uint16_t *length);
int sd_lldp_packet_read_management_vid(sd_lldp_packet *tlv, uint16_t *id);
int sd_lldp_packet_read_link_aggregation(sd_lldp_packet *tlv, uint8_t *status, uint32_t *id);
int sd_lldp_get_neighbors(sd_lldp *lldp, sd_lldp_neighbor ***neighbors);
sd_lldp_packet *sd_lldp_packet_ref(sd_lldp_packet *tlv);
sd_lldp_packet *sd_lldp_packet_unref(sd_lldp_packet *tlv);
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);
int sd_lldp_packet_get_destination_type(sd_lldp_packet *tlv, int *dest);
/* 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_raw(sd_lldp_neighbor *n, const void **ret, size_t *size);
int sd_lldp_get_packets(sd_lldp *lldp, sd_lldp_packet ***tlvs);
/* 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);
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_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 evertyhing 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[3], uint8_t *subtype);
int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[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, sd_lldp_unref);
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_lldp_packet, sd_lldp_packet_unref);
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_lldp_neighbor, sd_lldp_neighbor_unref);
_SD_END_DECLARATIONS;

View file

@ -52,7 +52,7 @@ int sd_ndisc_set_callback(sd_ndisc *nd,
int sd_ndisc_set_index(sd_ndisc *nd, int interface_index);
int sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr);
int sd_ndisc_attach_event(sd_ndisc *nd, sd_event *event, int priority);
int sd_ndisc_attach_event(sd_ndisc *nd, sd_event *event, int64_t priority);
int sd_ndisc_detach_event(sd_ndisc *nd);
sd_event *sd_ndisc_get_event(sd_ndisc *nd);

View file

@ -117,6 +117,7 @@ test_wired_defname_LDADD = \
test_systemd_CFLAGS = \
"-I$(srcdir)/../" \
"-I$(srcdir)/../platform" \
"-I$(srcdir)/../systemd" \
"-I$(srcdir)/../systemd/src/systemd"
test_systemd_SOURCES = \

View file

@ -21,6 +21,8 @@
#include "sd-dhcp-client.h"
#include "sd-lldp.h"
#include "sd-event.h"
#include "nm-sd-adapt.h"
#include "nm-test-utils.h"
@ -48,7 +50,7 @@ test_lldp_create (void)
int ifindex = 1;
int r;
r = sd_lldp_new (ifindex, "lo", (struct ether_addr *) ((guint8[]) { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }), &lldp);
r = sd_lldp_new (&lldp, ifindex);
g_assert (r == 0);
g_assert (lldp);
@ -57,6 +59,71 @@ test_lldp_create (void)
/*****************************************************************************/
typedef struct {
GMainLoop *mainloop;
sd_event_source *event_source;
} TestSdEventData;
static int
_test_sd_event_timeout_cb (sd_event_source *s, uint64_t usec, void *userdata)
{
TestSdEventData *user_data = userdata;
g_assert (user_data);
g_assert (user_data->mainloop);
g_assert (user_data->event_source);
user_data->event_source = sd_event_source_unref (user_data->event_source);
g_main_loop_quit (user_data->mainloop);
return 0;
}
static void
test_sd_event (void)
{
int repeat;
for (repeat = 0; repeat < 2; repeat++) {
guint sd_id = 0;
int r;
int i, n;
sd_event *other_events[3] = { NULL }, *event = NULL;
TestSdEventData user_data = { 0 };
g_assert_cmpint (sd_event_default (NULL), ==, 0);
for (i = 0, n = (nmtst_get_rand_int () % (G_N_ELEMENTS (other_events) + 1)); i < n; i++) {
r = sd_event_default (&other_events[i]);
g_assert (r >= 0 && other_events[i]);
}
sd_id = nm_sd_event_attach_default ();
r = sd_event_default (&event);
g_assert (r >= 0 && event);
r = sd_event_add_time (event, &user_data.event_source, CLOCK_MONOTONIC, 1, 0, _test_sd_event_timeout_cb, &user_data);
g_assert (r >= 0 && user_data.event_source);
user_data.mainloop = g_main_loop_new (NULL, FALSE);
g_main_loop_run (user_data.mainloop);
g_main_loop_unref (user_data.mainloop);
g_assert (!user_data.event_source);
event = sd_event_unref (event);
for (i = 0, n = (nmtst_get_rand_int () % (G_N_ELEMENTS (other_events) + 1)); i < n; i++)
other_events[i] = sd_event_unref (other_events[i]);
nm_clear_g_source (&sd_id);
for (i = 0, n = G_N_ELEMENTS (other_events); i < n; i++)
other_events[i] = sd_event_unref (other_events[i]);
g_assert_cmpint (sd_event_default (NULL), ==, 0);
}
}
/*****************************************************************************/
NMTST_DEFINE ();
int
@ -66,6 +133,7 @@ main (int argc, char **argv)
g_test_add_func ("/systemd/dhcp/create", test_dhcp_create);
g_test_add_func ("/systemd/lldp/create", test_lldp_create);
g_test_add_func ("/systemd/sd-event", test_sd_event);
return g_test_run ();
}