device/lldp: simplify NMLldpListener API

NMLldpListener API was a (refcounted) GObject with start/stop methods.
That means, a listener instance itself had state, namely whether it was
running and which ifindex was used. And this was not only internal
state, but the user had to care about this.

That is all entirely unnecessary. Beside requiring more code and having
more overhead (of a GObject), it is also harder to use. NMDevice not
only need to care whether priv->listener is set, it also needs to care
whether it is running.

Simplify this. The NMLldpListener is no longer ref-counted. As such, the
notify callback is set in the constructor, and the user will stop
receiving notifications by destroying the instance. Furthermore, the instance
can only use one ifindex, that is determined at construct time too.

The state that NMLldpListener now represents is simpler. This simplifies
the usage from NMDevice, which now only call lldp_setup() to enable and
disable the listener.

There is also no need to restart the LLDP listener. The only exception
is, if the ifindex changes. In that case, we throw away the old instance
and create a new one. Otherwise, the LLDP listener is itself responsible
to keep running. There is no excuse for it to fail, and if it does, it needs
to autorecover as good as it can.
This commit is contained in:
Thomas Haller 2021-04-14 09:39:54 +02:00
parent b0d45c88c3
commit 655dd13902
No known key found for this signature in database
GPG key ID: 29C2366E4DFC5728
4 changed files with 225 additions and 345 deletions

View file

@ -2625,12 +2625,15 @@ set_interface_flags_full(NMDevice * self,
}
static gboolean
set_interface_flags(NMDevice *self, NMDeviceInterfaceFlags interface_flags, gboolean set)
set_interface_flags(NMDevice * self,
NMDeviceInterfaceFlags interface_flags,
gboolean set,
gboolean notify)
{
return set_interface_flags_full(self,
interface_flags,
set ? interface_flags : NM_DEVICE_INTERFACE_FLAG_NONE,
TRUE);
notify);
}
void
@ -5051,11 +5054,7 @@ nm_device_set_carrier(NMDevice *self, gboolean carrier)
if (NM_FLAGS_ALL(priv->capabilities,
NM_DEVICE_CAP_CARRIER_DETECT | NM_DEVICE_CAP_NONSTANDARD_CARRIER)) {
notify_flags = set_interface_flags_full(self,
NM_DEVICE_INTERFACE_FLAG_CARRIER,
carrier ? NM_DEVICE_INTERFACE_FLAG_CARRIER
: NM_DEVICE_INTERFACE_FLAG_NONE,
FALSE);
notify_flags = set_interface_flags(self, NM_DEVICE_INTERFACE_FLAG_CARRIER, carrier, FALSE);
}
priv->carrier = carrier;
@ -7944,14 +7943,6 @@ master_ready_cb(NMActiveConnection *active, GParamSpec *pspec, NMDevice *self)
nm_device_activate_schedule_stage1_device_prepare(self, FALSE);
}
static void
lldp_neighbors_changed(NMLldpListener *lldp_listener, GParamSpec *pspec, gpointer user_data)
{
NMDevice *self = NM_DEVICE(user_data);
_notify(self, PROP_LLDP_NEIGHBORS);
}
static NMPlatformVF *
sriov_vf_config_to_platform(NMDevice *self, NMSriovVF *vf, GError **error)
{
@ -8239,43 +8230,56 @@ act_stage2_config(NMDevice *self, NMDeviceStateReason *out_failure_reason)
}
static void
lldp_init(NMDevice *self, gboolean restart)
_lldp_neighbors_changed_cb(NMLldpListener *lldp_listener, gpointer user_data)
{
_notify(user_data, PROP_LLDP_NEIGHBORS);
}
static void
lldp_setup(NMDevice *self, NMTernary enabled)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
int ifindex;
gboolean notify_lldp_neighbors = FALSE;
gboolean notify_interface_flags = FALSE;
if (priv->ifindex > 0 && _prop_get_connection_lldp(self)) {
gs_free_error GError *error = NULL;
ifindex = nm_device_get_ifindex(self);
if (priv->lldp_listener) {
if (restart && nm_lldp_listener_is_running(priv->lldp_listener)) {
nm_lldp_listener_stop(priv->lldp_listener);
set_interface_flags(self, NM_DEVICE_INTERFACE_FLAG_LLDP_CLIENT_ENABLED, FALSE);
}
} else {
priv->lldp_listener = nm_lldp_listener_new();
g_signal_connect(priv->lldp_listener,
"notify::" NM_LLDP_LISTENER_NEIGHBORS,
G_CALLBACK(lldp_neighbors_changed),
self);
}
if (ifindex <= 0)
enabled = FALSE;
else if (enabled == NM_TERNARY_DEFAULT)
enabled = _prop_get_connection_lldp(self);
if (!nm_lldp_listener_is_running(priv->lldp_listener)) {
if (nm_lldp_listener_start(priv->lldp_listener, nm_device_get_ifindex(self), &error)) {
_LOGD(LOGD_DEVICE, "LLDP listener %p started", priv->lldp_listener);
set_interface_flags(self, NM_DEVICE_INTERFACE_FLAG_LLDP_CLIENT_ENABLED, TRUE);
} else {
_LOGD(LOGD_DEVICE,
"LLDP listener %p could not be started: %s",
priv->lldp_listener,
error->message);
}
}
} else {
if (priv->lldp_listener) {
nm_lldp_listener_stop(priv->lldp_listener);
set_interface_flags(self, NM_DEVICE_INTERFACE_FLAG_LLDP_CLIENT_ENABLED, FALSE);
if (priv->lldp_listener) {
if (!enabled || nm_lldp_listener_get_ifindex(priv->lldp_listener) != ifindex) {
nm_clear_pointer(&priv->lldp_listener, nm_lldp_listener_destroy);
notify_lldp_neighbors = TRUE;
}
}
if (enabled && !priv->lldp_listener) {
gs_free_error GError *error = NULL;
priv->lldp_listener =
nm_lldp_listener_new(ifindex, _lldp_neighbors_changed_cb, self, &error);
if (!priv->lldp_listener) {
/* This really shouldn't happen. It's likely a bug. Investigate when this happens! */
_LOGW(LOGD_DEVICE,
"LLDP listener for ifindex %d could not be started: %s",
ifindex,
error->message);
} else
notify_lldp_neighbors = TRUE;
}
notify_interface_flags = set_interface_flags(self,
NM_DEVICE_INTERFACE_FLAG_LLDP_CLIENT_ENABLED,
!!priv->lldp_listener,
FALSE);
nm_gobject_notify_together(self,
notify_lldp_neighbors ? PROP_LLDP_NEIGHBORS : PROP_0,
notify_interface_flags ? PROP_INTERFACE_FLAGS : PROP_0);
}
/* set-mode can be:
@ -8488,7 +8492,7 @@ activate_stage2_device_config(NMDevice *self)
nm_device_queue_recheck_assume(info->slave);
}
lldp_init(self, TRUE);
lldp_setup(self, NM_TERNARY_DEFAULT);
nm_device_activate_schedule_stage3_ip_config_start(self);
}
@ -12720,7 +12724,7 @@ check_and_reapply_connection(NMDevice * self,
klass->reapply_connection(self, con_old, con_new);
if (priv->state >= NM_DEVICE_STATE_CONFIG)
lldp_init(self, FALSE);
lldp_setup(self, NM_TERNARY_DEFAULT);
if (priv->state >= NM_DEVICE_STATE_IP_CONFIG) {
s_ip4_old = nm_connection_get_setting_ip4_config(con_old);
@ -15960,10 +15964,7 @@ nm_device_cleanup(NMDevice *self, NMDeviceStateReason reason, CleanupType cleanu
FALSE,
NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
if (priv->lldp_listener) {
nm_lldp_listener_stop(priv->lldp_listener);
set_interface_flags(self, NM_DEVICE_INTERFACE_FLAG_LLDP_CLIENT_ENABLED, FALSE);
}
lldp_setup(self, NM_TERNARY_FALSE);
nm_device_update_metered(self);
@ -18396,14 +18397,7 @@ dispose(GObject *object)
nm_clear_g_source(&priv->device_link_changed_id);
nm_clear_g_source(&priv->device_ip_link_changed_id);
if (priv->lldp_listener) {
g_signal_handlers_disconnect_by_func(priv->lldp_listener,
G_CALLBACK(lldp_neighbors_changed),
self);
nm_lldp_listener_stop(priv->lldp_listener);
set_interface_flags(self, NM_DEVICE_INTERFACE_FLAG_LLDP_CLIENT_ENABLED, FALSE);
g_clear_object(&priv->lldp_listener);
}
lldp_setup(self, FALSE);
nm_clear_g_source(&priv->concheck_x[0].p_cur_id);
nm_clear_g_source(&priv->concheck_x[1].p_cur_id);

View file

@ -31,34 +31,21 @@
/*****************************************************************************/
NM_GOBJECT_PROPERTIES_DEFINE(NMLldpListener, PROP_NEIGHBORS, );
typedef struct {
struct _NMLldpListener {
sd_lldp * lldp_handle;
GHashTable *lldp_neighbors;
GVariant * variant;
NMLldpListenerNotify notify_callback;
gpointer notify_user_data;
/* the timestamp in nsec until which we delay updates. */
gint64 ratelimit_next_nsec;
guint ratelimit_id;
int ifindex;
} NMLldpListenerPrivate;
struct _NMLldpListener {
GObject parent;
NMLldpListenerPrivate _priv;
};
struct _NMLldpListenerClass {
GObjectClass parent;
};
G_DEFINE_TYPE(NMLldpListener, nm_lldp_listener, G_TYPE_OBJECT)
#define NM_LLDP_LISTENER_GET_PRIVATE(self) \
_NM_GET_PRIVATE(self, NMLldpListener, NM_IS_LLDP_LISTENER)
/*****************************************************************************/
typedef struct {
@ -74,27 +61,28 @@ typedef struct {
#define _NMLOG_PREFIX_NAME "lldp"
#define _NMLOG_DOMAIN LOGD_DEVICE
#define _NMLOG(level, ...) \
G_STMT_START \
{ \
const NMLogLevel _level = (level); \
\
if (nm_logging_enabled(_level, _NMLOG_DOMAIN)) { \
char _sbuf[64]; \
int _ifindex = (self) ? NM_LLDP_LISTENER_GET_PRIVATE(self)->ifindex : 0; \
\
_nm_log(_level, \
_NMLOG_DOMAIN, \
0, \
_ifindex > 0 ? nm_platform_link_get_name(NM_PLATFORM_GET, _ifindex) : NULL, \
NULL, \
"%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
_NMLOG_PREFIX_NAME, \
((_ifindex > 0) ? nm_sprintf_buf(_sbuf, "[%p,%d]", (self), _ifindex) \
: ((self) ? nm_sprintf_buf(_sbuf, "[%p]", (self)) : "")) \
_NM_UTILS_MACRO_REST(__VA_ARGS__)); \
} \
} \
#define _NMLOG(level, ...) \
G_STMT_START \
{ \
const NMLogLevel _level = (level); \
\
if (nm_logging_enabled(_level, _NMLOG_DOMAIN)) { \
char _sbuf[100]; \
\
_nm_log(_level, \
_NMLOG_DOMAIN, \
0, \
(self) ? nm_platform_link_get_name(NM_PLATFORM_GET, (self)->ifindex) : NULL, \
NULL, \
"%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
_NMLOG_PREFIX_NAME, \
((self) ? nm_sprintf_buf(_sbuf, \
"[" NM_HASH_OBFUSCATE_PTR_FMT ",%d]", \
NM_HASH_OBFUSCATE_PTR(self), \
(self)->ifindex) \
: "") _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
} \
} \
G_STMT_END
#define LOG_NEIGH_FMT "CHASSIS=%u/%s PORT=%u/%s"
@ -812,80 +800,71 @@ nmtst_lldp_parse_from_raw(const guint8 *raw_data, gsize raw_len)
/*****************************************************************************/
static void
data_changed_notify(NMLldpListener *self, NMLldpListenerPrivate *priv)
data_changed_notify(NMLldpListener *self)
{
nm_clear_g_variant(&priv->variant);
_notify(self, PROP_NEIGHBORS);
nm_clear_g_variant(&self->variant);
self->notify_callback(self, self->notify_user_data);
}
static gboolean
data_changed_timeout(gpointer user_data)
{
NMLldpListener * self = user_data;
NMLldpListenerPrivate *priv;
NMLldpListener *self = user_data;
g_return_val_if_fail(NM_IS_LLDP_LISTENER(self), G_SOURCE_REMOVE);
priv = NM_LLDP_LISTENER_GET_PRIVATE(self);
priv->ratelimit_id = 0;
priv->ratelimit_next_nsec = nm_utils_get_monotonic_timestamp_nsec() + MIN_UPDATE_INTERVAL_NSEC;
data_changed_notify(self, priv);
self->ratelimit_id = 0;
self->ratelimit_next_nsec = nm_utils_get_monotonic_timestamp_nsec() + MIN_UPDATE_INTERVAL_NSEC;
data_changed_notify(self);
return G_SOURCE_REMOVE;
}
static void
data_changed_schedule(NMLldpListener *self)
{
NMLldpListenerPrivate *priv = NM_LLDP_LISTENER_GET_PRIVATE(self);
gint64 now_nsec;
gint64 now_nsec;
if (priv->ratelimit_id != 0)
if (self->ratelimit_id != 0)
return;
now_nsec = nm_utils_get_monotonic_timestamp_nsec();
if (now_nsec < priv->ratelimit_next_nsec) {
priv->ratelimit_id =
if (now_nsec < self->ratelimit_next_nsec) {
self->ratelimit_id =
g_timeout_add_full(G_PRIORITY_LOW,
NM_UTILS_NSEC_TO_MSEC_CEIL(priv->ratelimit_next_nsec - now_nsec),
NM_UTILS_NSEC_TO_MSEC_CEIL(self->ratelimit_next_nsec - now_nsec),
data_changed_timeout,
self,
NULL);
return;
}
priv->ratelimit_id = g_idle_add_full(G_PRIORITY_LOW, data_changed_timeout, self, NULL);
self->ratelimit_id = g_idle_add_full(G_PRIORITY_LOW, data_changed_timeout, self, NULL);
}
static void
process_lldp_neighbor(NMLldpListener *self, sd_lldp_neighbor *neighbor_sd, gboolean remove)
{
NMLldpListenerPrivate * priv;
nm_auto(lldp_neighbor_freep) LldpNeighbor *neigh = NULL;
LldpNeighbor * neigh_old;
g_return_if_fail(NM_IS_LLDP_LISTENER(self));
nm_assert(self);
nm_assert(self->lldp_handle);
nm_assert(self->lldp_neighbors);
priv = NM_LLDP_LISTENER_GET_PRIVATE(self);
g_return_if_fail(priv->lldp_handle);
g_return_if_fail(neighbor_sd);
nm_assert(priv->lldp_neighbors);
neigh = lldp_neighbor_new(neighbor_sd);
if (!neigh) {
_LOGT("process: failed to parse neighbor");
return;
}
neigh_old = g_hash_table_lookup(priv->lldp_neighbors, neigh);
neigh_old = g_hash_table_lookup(self->lldp_neighbors, neigh);
if (remove) {
if (neigh_old) {
_LOGT("process: %s neigh: " LOG_NEIGH_FMT, "remove", LOG_NEIGH_ARG(neigh));
g_hash_table_remove(priv->lldp_neighbors, neigh_old);
g_hash_table_remove(self->lldp_neighbors, neigh_old);
goto handle_changed;
}
return;
@ -896,7 +875,7 @@ process_lldp_neighbor(NMLldpListener *self, sd_lldp_neighbor *neighbor_sd, gbool
_LOGD("process: %s neigh: " LOG_NEIGH_FMT, neigh_old ? "update" : "new", LOG_NEIGH_ARG(neigh));
g_hash_table_add(priv->lldp_neighbors, g_steal_pointer(&neigh));
g_hash_table_add(self->lldp_neighbors, g_steal_pointer(&neigh));
handle_changed:
data_changed_schedule(self);
@ -911,25 +890,57 @@ lldp_event_handler(sd_lldp *lldp, sd_lldp_event_t event, sd_lldp_neighbor *n, vo
!NM_IN_SET(event, SD_LLDP_EVENT_ADDED, SD_LLDP_EVENT_UPDATED, SD_LLDP_EVENT_REFRESHED));
}
gboolean
nm_lldp_listener_start(NMLldpListener *self, int ifindex, GError **error)
/*****************************************************************************/
int
nm_lldp_listener_get_ifindex(NMLldpListener *self)
{
NMLldpListenerPrivate *priv;
int ret;
g_return_val_if_fail(self, 0);
g_return_val_if_fail(NM_IS_LLDP_LISTENER(self), FALSE);
g_return_val_if_fail(ifindex > 0, FALSE);
g_return_val_if_fail(!error || !*error, FALSE);
return self->ifindex;
}
priv = NM_LLDP_LISTENER_GET_PRIVATE(self);
/*****************************************************************************/
if (priv->lldp_handle) {
g_set_error_literal(error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, "already running");
return FALSE;
GVariant *
nm_lldp_listener_get_neighbors(NMLldpListener *self)
{
g_return_val_if_fail(self, FALSE);
if (G_UNLIKELY(!self->variant)) {
gs_free LldpNeighbor **neighbors = NULL;
GVariantBuilder array_builder;
guint i, n;
g_variant_builder_init(&array_builder, G_VARIANT_TYPE("aa{sv}"));
neighbors = (LldpNeighbor **)
nm_utils_hash_keys_to_array(self->lldp_neighbors, lldp_neighbor_id_cmp_p, NULL, &n);
for (i = 0; i < n; i++)
g_variant_builder_add_value(&array_builder, lldp_neighbor_to_variant(neighbors[i]));
self->variant = g_variant_ref_sink(g_variant_builder_end(&array_builder));
}
ret = sd_lldp_new(&priv->lldp_handle);
if (ret < 0) {
return self->variant;
}
/*****************************************************************************/
NMLldpListener *
nm_lldp_listener_new(int ifindex,
NMLldpListenerNotify notify_callback,
gpointer notify_user_data,
GError ** error)
{
NMLldpListener *self = NULL;
sd_lldp * lldp_handle;
int r;
g_return_val_if_fail(ifindex > 0, FALSE);
g_return_val_if_fail(!error || !*error, FALSE);
g_return_val_if_fail(notify_callback, FALSE);
r = sd_lldp_new(&lldp_handle);
if (r < 0) {
g_set_error_literal(error,
NM_DEVICE_ERROR,
NM_DEVICE_ERROR_FAILED,
@ -937,189 +948,77 @@ nm_lldp_listener_start(NMLldpListener *self, int ifindex, GError **error)
return FALSE;
}
ret = sd_lldp_set_ifindex(priv->lldp_handle, ifindex);
if (ret < 0) {
r = sd_lldp_set_ifindex(lldp_handle, ifindex);
if (r < 0) {
g_set_error_literal(error,
NM_DEVICE_ERROR,
NM_DEVICE_ERROR_FAILED,
"failed setting ifindex");
goto err;
goto fail_handle;
}
ret = sd_lldp_set_callback(priv->lldp_handle, lldp_event_handler, self);
if (ret < 0) {
r = sd_lldp_set_neighbors_max(lldp_handle, MAX_NEIGHBORS);
nm_assert(r == 0);
self = g_slice_new(NMLldpListener);
*self = (NMLldpListener){
.ifindex = ifindex,
.notify_callback = notify_callback,
.notify_user_data = notify_user_data,
};
r = sd_lldp_set_callback(lldp_handle, lldp_event_handler, self);
if (r < 0) {
g_set_error_literal(error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, "set callback failed");
goto err;
goto fail_handle;
}
ret = sd_lldp_set_neighbors_max(priv->lldp_handle, MAX_NEIGHBORS);
nm_assert(ret == 0);
priv->ifindex = ifindex;
ret = sd_lldp_attach_event(priv->lldp_handle, NULL, 0);
if (ret < 0) {
r = sd_lldp_attach_event(lldp_handle, NULL, 0);
if (r < 0) {
g_set_error_literal(error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, "attach event failed");
goto err_free;
goto fail_attached;
}
ret = sd_lldp_start(priv->lldp_handle);
if (ret < 0) {
r = sd_lldp_start(lldp_handle);
if (r < 0) {
g_set_error_literal(error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, "start failed");
goto err;
goto fail_attached;
}
priv->lldp_neighbors = g_hash_table_new_full(lldp_neighbor_id_hash,
self->lldp_neighbors = g_hash_table_new_full(lldp_neighbor_id_hash,
lldp_neighbor_id_equal,
(GDestroyNotify) lldp_neighbor_free,
NULL);
self->lldp_handle = lldp_handle;
_LOGD("start");
_LOGD("start lldp listener");
return self;
return TRUE;
err:
sd_lldp_detach_event(priv->lldp_handle);
err_free:
sd_lldp_unref(priv->lldp_handle);
priv->lldp_handle = NULL;
priv->ifindex = 0;
return FALSE;
fail_attached:
sd_lldp_detach_event(lldp_handle);
fail_handle:
if (self)
nm_g_slice_free(self);
sd_lldp_unref(lldp_handle);
return NULL;
}
void
nm_lldp_listener_stop(NMLldpListener *self)
nm_lldp_listener_destroy(NMLldpListener *self)
{
NMLldpListenerPrivate *priv;
guint size;
gboolean changed = FALSE;
g_return_if_fail(self);
g_return_if_fail(NM_IS_LLDP_LISTENER(self));
priv = NM_LLDP_LISTENER_GET_PRIVATE(self);
sd_lldp_stop(self->lldp_handle);
sd_lldp_detach_event(self->lldp_handle);
sd_lldp_unref(self->lldp_handle);
if (priv->lldp_handle) {
_LOGD("stop");
sd_lldp_stop(priv->lldp_handle);
sd_lldp_detach_event(priv->lldp_handle);
sd_lldp_unref(priv->lldp_handle);
priv->lldp_handle = NULL;
nm_clear_g_source(&self->ratelimit_id);
size = g_hash_table_size(priv->lldp_neighbors);
g_hash_table_remove_all(priv->lldp_neighbors);
nm_clear_pointer(&priv->lldp_neighbors, g_hash_table_unref);
if (size > 0 || priv->ratelimit_id != 0)
changed = TRUE;
}
g_hash_table_destroy(self->lldp_neighbors);
nm_clear_g_source(&priv->ratelimit_id);
priv->ratelimit_next_nsec = 0;
priv->ifindex = 0;
if (changed)
data_changed_notify(self, priv);
}
gboolean
nm_lldp_listener_is_running(NMLldpListener *self)
{
NMLldpListenerPrivate *priv;
g_return_val_if_fail(NM_IS_LLDP_LISTENER(self), FALSE);
priv = NM_LLDP_LISTENER_GET_PRIVATE(self);
return !!priv->lldp_handle;
}
GVariant *
nm_lldp_listener_get_neighbors(NMLldpListener *self)
{
NMLldpListenerPrivate *priv;
g_return_val_if_fail(NM_IS_LLDP_LISTENER(self), FALSE);
priv = NM_LLDP_LISTENER_GET_PRIVATE(self);
if (G_UNLIKELY(!priv->variant)) {
gs_free LldpNeighbor **neighbors = NULL;
GVariantBuilder array_builder;
guint i, n;
g_variant_builder_init(&array_builder, G_VARIANT_TYPE("aa{sv}"));
neighbors = (LldpNeighbor **)
nm_utils_hash_keys_to_array(priv->lldp_neighbors, lldp_neighbor_id_cmp_p, NULL, &n);
for (i = 0; i < n; i++)
g_variant_builder_add_value(&array_builder, lldp_neighbor_to_variant(neighbors[i]));
priv->variant = g_variant_ref_sink(g_variant_builder_end(&array_builder));
}
return priv->variant;
}
static void
get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
{
NMLldpListener *self = NM_LLDP_LISTENER(object);
switch (prop_id) {
case PROP_NEIGHBORS:
g_value_set_variant(value, nm_lldp_listener_get_neighbors(self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}
static void
nm_lldp_listener_init(NMLldpListener *self)
{
_LOGT("lldp listener created");
}
NMLldpListener *
nm_lldp_listener_new(void)
{
return g_object_new(NM_TYPE_LLDP_LISTENER, NULL);
}
static void
dispose(GObject *object)
{
nm_lldp_listener_stop(NM_LLDP_LISTENER(object));
G_OBJECT_CLASS(nm_lldp_listener_parent_class)->dispose(object);
}
static void
finalize(GObject *object)
{
NMLldpListener * self = NM_LLDP_LISTENER(object);
NMLldpListenerPrivate *priv = NM_LLDP_LISTENER_GET_PRIVATE(self);
nm_lldp_listener_stop(self);
nm_clear_g_variant(&priv->variant);
nm_g_variant_unref(self->variant);
_LOGT("lldp listener destroyed");
G_OBJECT_CLASS(nm_lldp_listener_parent_class)->finalize(object);
}
static void
nm_lldp_listener_class_init(NMLldpListenerClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS(klass);
object_class->dispose = dispose;
object_class->finalize = finalize;
object_class->get_property = get_property;
obj_properties[PROP_NEIGHBORS] =
g_param_spec_variant(NM_LLDP_LISTENER_NEIGHBORS,
"",
"",
G_VARIANT_TYPE("aa{sv}"),
NULL,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
nm_g_slice_free(self);
}

View file

@ -6,28 +6,21 @@
#ifndef __NM_LLDP_LISTENER__
#define __NM_LLDP_LISTENER__
#define NM_TYPE_LLDP_LISTENER (nm_lldp_listener_get_type())
#define NM_LLDP_LISTENER(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_LLDP_LISTENER, NMLldpListener))
#define NM_LLDP_LISTENER_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_LLDP_LISTENER, NMLldpListenerClass))
#define NM_IS_LLDP_LISTENER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_LLDP_LISTENER))
#define NM_IS_LLDP_LISTENER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_LLDP_LISTENER))
#define NM_LLDP_LISTENER_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_LLDP_LISTENER, NMLldpListenerClass))
/*****************************************************************************/
#define NM_LLDP_LISTENER_NEIGHBORS "neighbors"
typedef void (*NMLldpListenerNotify)(NMLldpListener *self, gpointer user_data);
typedef struct _NMLldpListenerClass NMLldpListenerClass;
GType nm_lldp_listener_get_type(void);
NMLldpListener *nm_lldp_listener_new(void);
gboolean nm_lldp_listener_start(NMLldpListener *self, int ifindex, GError **error);
void nm_lldp_listener_stop(NMLldpListener *self);
gboolean nm_lldp_listener_is_running(NMLldpListener *self);
NMLldpListener *nm_lldp_listener_new(int ifindex,
NMLldpListenerNotify notify_callback,
gpointer notify_user_data,
GError ** error);
void nm_lldp_listener_destroy(NMLldpListener *self);
int nm_lldp_listener_get_ifindex(NMLldpListener *self);
GVariant *nm_lldp_listener_get_neighbors(NMLldpListener *self);
/*****************************************************************************/
GVariant *nmtst_lldp_parse_from_raw(const guint8 *raw_data, gsize raw_len);
#endif /* __NM_LLDP_LISTENER__ */

View file

@ -83,6 +83,7 @@ typedef struct {
const uint8_t *frame;
const char * as_variant;
} TestRecvFrame;
#define TEST_RECV_FRAME_DEFINE(name, _as_variant, ...) \
static const guint8 _##name##_v[] = {__VA_ARGS__}; \
static const TestRecvFrame name = { \
@ -91,12 +92,18 @@ typedef struct {
.frame = _##name##_v, \
}
typedef struct {
int num_called;
GMainLoop *loop_to_quit;
} TestRecvCallbackInfo;
typedef struct {
guint expected_num_called;
gsize frames_len;
const TestRecvFrame *frames[10];
void (*check)(GMainLoop *loop, NMLldpListener *listener);
void (*check)(GMainLoop *loop, NMLldpListener *listener, TestRecvCallbackInfo *info);
} TestRecvData;
#define TEST_RECV_DATA_DEFINE(name, _expected_num_called, _check, ...) \
static const TestRecvData name = { \
.expected_num_called = _expected_num_called, \
@ -213,7 +220,7 @@ _test_recv_data0_check_do(GMainLoop *loop, NMLldpListener *listener, const TestR
}
static void
_test_recv_data0_check(GMainLoop *loop, NMLldpListener *listener)
_test_recv_data0_check(GMainLoop *loop, NMLldpListener *listener, TestRecvCallbackInfo *info)
{
_test_recv_data0_check_do(loop, listener, &_test_recv_data0_frame0);
}
@ -528,7 +535,7 @@ TEST_RECV_FRAME_DEFINE(
);
static void
_test_recv_data1_check(GMainLoop *loop, NMLldpListener *listener)
_test_recv_data1_check(GMainLoop *loop, NMLldpListener *listener, TestRecvCallbackInfo *info)
{
GVariant * neighbors, *attr, *child;
gs_unref_variant GVariant *neighbor = NULL;
@ -756,21 +763,17 @@ TEST_RECV_FRAME_DEFINE(
);
static void
_test_recv_data2_ttl1_check(GMainLoop *loop, NMLldpListener *listener)
_test_recv_data2_ttl1_check(GMainLoop *loop, NMLldpListener *listener, TestRecvCallbackInfo *info)
{
gulong notify_id;
GVariant *neighbors;
_test_recv_data0_check_do(loop, listener, &_test_recv_data2_frame0_ttl1);
/* wait for signal. */
notify_id = g_signal_connect(listener,
"notify::" NM_LLDP_LISTENER_NEIGHBORS,
nmtst_main_loop_quit_on_notify,
loop);
info->loop_to_quit = loop;
if (!nmtst_main_loop_run(loop, 5000))
g_assert_not_reached();
nm_clear_g_signal_handler(listener, &notify_id);
info->loop_to_quit = NULL;
neighbors = nm_lldp_listener_get_neighbors(listener);
nmtst_assert_variant_is_of_type(neighbors, G_VARIANT_TYPE("aa{sv}"));
@ -852,46 +855,37 @@ again:
memcpy(fixture->mac, link->l_address.data, ETH_ALEN);
}
typedef struct {
int num_called;
} TestRecvCallbackInfo;
static void
lldp_neighbors_changed(NMLldpListener *lldp_listener, GParamSpec *pspec, gpointer user_data)
lldp_neighbors_changed(NMLldpListener *lldp_listener, gpointer user_data)
{
TestRecvCallbackInfo *info = user_data;
info->num_called++;
if (info->loop_to_quit)
g_main_loop_quit(info->loop_to_quit);
}
static void
test_recv(TestRecvFixture *fixture, gconstpointer user_data)
{
const TestRecvData *data = user_data;
gs_unref_object NMLldpListener *listener = NULL;
GMainLoop * loop;
TestRecvCallbackInfo info = {};
gsize i_frames;
gulong notify_id;
GError * error = NULL;
guint sd_id;
const TestRecvData * data = user_data;
NMLldpListener * listener;
GMainLoop * loop;
TestRecvCallbackInfo info = {};
gsize i_frames;
GError * error = NULL;
guint sd_id;
if (fixture->ifindex == 0) {
g_test_skip("Tun device not available");
return;
}
listener = nm_lldp_listener_new();
g_assert(listener != NULL);
g_assert(nm_lldp_listener_start(listener, fixture->ifindex, &error));
g_assert_no_error(error);
listener = nm_lldp_listener_new(fixture->ifindex, lldp_neighbors_changed, &info, &error);
nmtst_assert_success(listener, 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();
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];
@ -904,9 +898,9 @@ test_recv(TestRecvFixture *fixture, gconstpointer user_data)
g_assert_cmpint(info.num_called, ==, data->expected_num_called);
nm_clear_g_signal_handler(listener, &notify_id);
data->check(loop, listener, &info);
data->check(loop, listener);
nm_clear_pointer(&listener, nm_lldp_listener_destroy);
nm_clear_g_source(&sd_id);
nm_clear_pointer(&loop, g_main_loop_unref);