diff --git a/clients/cli/settings.c b/clients/cli/settings.c index 974fa97f61..0c10cf533e 100644 --- a/clients/cli/settings.c +++ b/clients/cli/settings.c @@ -59,6 +59,7 @@ NmcOutputField nmc_fields_setting_connection[] = { SETTING_FIELD (NM_SETTING_CONNECTION_SECONDARIES), /* 14 */ SETTING_FIELD (NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT), /* 15 */ SETTING_FIELD (NM_SETTING_CONNECTION_METERED), /* 16 */ + SETTING_FIELD (NM_SETTING_CONNECTION_LLDP), /* 17 */ {NULL, NULL, 0, NULL, FALSE, FALSE, 0} }; #define NMC_FIELDS_SETTING_CONNECTION_ALL "name"","\ @@ -77,7 +78,8 @@ NmcOutputField nmc_fields_setting_connection[] = { NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES","\ NM_SETTING_CONNECTION_SECONDARIES","\ NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT","\ - NM_SETTING_CONNECTION_METERED + NM_SETTING_CONNECTION_METERED","\ + NM_SETTING_CONNECTION_LLDP #define NMC_FIELDS_SETTING_CONNECTION_COMMON NMC_FIELDS_SETTING_CONNECTION_ALL /* Available fields for NM_SETTING_WIRED_SETTING_NAME */ @@ -2929,6 +2931,56 @@ nmc_property_connection_describe_metered (NMSetting *setting, const char *prop) "'unknown' to let NetworkManager choose a value using some heuristics\n"); } +/* 'lldp' */ +static char * +nmc_property_connection_get_lldp (NMSetting *setting, NmcPropertyGetType get_type) +{ + NMSettingConnection *s_conn = NM_SETTING_CONNECTION (setting); + NMSettingConnectionLldp lldp; + char *tmp, *str; + + lldp = nm_setting_connection_get_lldp (s_conn); + tmp = nm_utils_enum_to_str (nm_setting_connection_lldp_get_type (), lldp); + if (get_type == NMC_PROPERTY_GET_PARSABLE) + str = g_strdup_printf ("%s", tmp && *tmp ? tmp : "default"); + else + str = g_strdup_printf ("%d (%s)", lldp, tmp && *tmp ? tmp : "default"); + g_free (tmp); + return str; +} + +static gboolean +nmc_property_connection_set_lldp (NMSetting *setting, const char *prop, + const char *val, GError **error) +{ + NMSettingConnectionLldp lldp; + gboolean ret; + long int t; + + if (nmc_string_to_int_base (val, 0, TRUE, + NM_SETTING_CONNECTION_LLDP_DEFAULT, + NM_SETTING_CONNECTION_LLDP_ENABLE_RX, + &t)) + lldp = t; + else { + ret = nm_utils_enum_from_str (nm_setting_connection_lldp_get_type (), val, + (int *) &lldp, NULL); + + if (!ret) { + if (g_ascii_strcasecmp (val, "enable") == 0) + lldp = NM_SETTING_CONNECTION_LLDP_ENABLE_RX; + else { + g_set_error (error, 1, 0, _("invalid option '%s', use one of [%s]"), + val, "default,disable,enable-rx,enable"); + return FALSE; + } + } + } + + g_object_set (setting, prop, lldp, NULL); + return TRUE; +} + /* --- NM_SETTING_802_1X_SETTING_NAME property setter functions --- */ #define DEFINE_SETTER_STR_LIST(def_func, set_func) \ static gboolean \ @@ -5624,6 +5676,13 @@ nmc_properties_init (void) nmc_property_connection_describe_metered, NULL, NULL); + nmc_add_prop_funcs (GLUE (CONNECTION, LLDP), + nmc_property_connection_get_lldp, + nmc_property_connection_set_lldp, + NULL, + NULL, + NULL, + NULL); /* Add editable properties for NM_SETTING_DCB_SETTING_NAME */ nmc_add_prop_funcs (GLUE (DCB, APP_FCOE_FLAGS), @@ -7035,6 +7094,7 @@ setting_connection_details (NMSetting *setting, NmCli *nmc, const char *one_pro set_val_str (arr, 14, nmc_property_connection_get_secondaries (setting, NMC_PROPERTY_GET_PRETTY)); set_val_str (arr, 15, nmc_property_connection_get_gateway_ping_timeout (setting, NMC_PROPERTY_GET_PRETTY)); set_val_str (arr, 16, nmc_property_connection_get_metered (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 17, nmc_property_connection_get_lldp (setting, NMC_PROPERTY_GET_PRETTY)); g_ptr_array_add (nmc->output_data, arr); print_data (nmc); /* Print all data */ diff --git a/examples/python/gi/get-lldp-neighbors.py b/examples/python/gi/get-lldp-neighbors.py new file mode 100755 index 0000000000..03690d5a78 --- /dev/null +++ b/examples/python/gi/get-lldp-neighbors.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python +# +# vim: ft=python ts=4 sts=4 sw=4 et ai +# -*- Mode: Python; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Copyright (C) 2015 Red Hat, Inc. +# + +import sys +from gi.repository import GLib, NM + +# This example shows how to get a list of LLDP neighbors for a given interface. + +main_loop = None + +if __name__ == "__main__": + if len(sys.argv) != 2: + sys.exit('Usage: %s ' % sys.argv[0]) + dev_iface = sys.argv[1] + + c = NM.Client.new(None) + dev = c.get_device_by_iface(dev_iface) + if dev is None: + sys.exit('Device \'%s\' not found' % dev_iface) + + neighbors = dev.get_lldp_neighbors() + for neighbor in neighbors: + ret, chassis = neighbor.get_attr_string_value('chassis-id') + ret, port = neighbor.get_attr_string_value('port-id') + print "Neighbor: %s - %s" % (chassis, port) + for attr in neighbor.get_attr_names(): + attr_type = neighbor.get_attr_type(attr) + if attr_type.equal(GLib.VariantType.new('s')): + ret, value = neighbor.get_attr_string_value(attr) + print " %-32s: %s" % (attr, value) + elif attr_type.equal(GLib.VariantType.new('u')): + ret, value = neighbor.get_attr_uint_value(attr) + print " %-32s: %u" % (attr, value) diff --git a/include/nm-macros-internal.h b/include/nm-macros-internal.h index 80e9abf01b..5c1d61dbaf 100644 --- a/include/nm-macros-internal.h +++ b/include/nm-macros-internal.h @@ -218,6 +218,17 @@ nm_clear_g_signal_handler (gpointer self, guint *id) return FALSE; } +static inline gboolean +nm_clear_g_variant (GVariant **variant) +{ + if (variant && *variant) { + g_variant_unref (*variant); + *variant = NULL; + return TRUE; + } + return FALSE; +} + /*****************************************************************************/ /* Determine whether @x is a power of two (@x being an integer type). diff --git a/introspection/nm-device.xml b/introspection/nm-device.xml index 909254002f..fd377a07eb 100644 --- a/introspection/nm-device.xml +++ b/introspection/nm-device.xml @@ -155,6 +155,12 @@ subject to limitations, for example set by service providers. + + + Array of LLDP neighbors; each element is a dictionary mapping + LLDP TLV names to variant boxed values. + + diff --git a/libnm-core/nm-dbus-interface.h b/libnm-core/nm-dbus-interface.h index 0dda998f79..68b0592e72 100644 --- a/libnm-core/nm-dbus-interface.h +++ b/libnm-core/nm-dbus-interface.h @@ -647,4 +647,23 @@ typedef enum /*< flags >*/ { #undef NM_AVAILABLE_IN_1_2 #endif +#define NM_LLDP_ATTR_DESTINATION "destination" +#define NM_LLDP_ATTR_CHASSIS_ID_TYPE "chassis-id-type" +#define NM_LLDP_ATTR_CHASSIS_ID "chassis-id" +#define NM_LLDP_ATTR_PORT_ID_TYPE "port-id-type" +#define NM_LLDP_ATTR_PORT_ID "port-id" +#define NM_LLDP_ATTR_PORT_DESCRIPTION "port-description" +#define NM_LLDP_ATTR_SYSTEM_NAME "system-name" +#define NM_LLDP_ATTR_SYSTEM_DESCRIPTION "system-description" +#define NM_LLDP_ATTR_SYSTEM_CAPABILITIES "system-capabilities" +#define NM_LLDP_ATTR_IEEE_802_1_PVID "ieee-802-1-pvid" +#define NM_LLDP_ATTR_IEEE_802_1_PPVID "ieee-802-1-ppvid" +#define NM_LLDP_ATTR_IEEE_802_1_PPVID_FLAGS "ieee-802-1-ppvid-flags" +#define NM_LLDP_ATTR_IEEE_802_1_VID "ieee-802-1-pvid" +#define NM_LLDP_ATTR_IEEE_802_1_VLAN_NAME "ieee-802-1-vlan-name" + +#define NM_LLDP_DEST_NEAREST_BRIDGE "nearest-bridge" +#define NM_LLDP_DEST_NEAREST_NON_TPMR_BRIDGE "nearest-non-tpmr-bridge" +#define NM_LLDP_DEST_NEAREST_CUSTOMER_BRIDGE "nearest-customer-bridge" + #endif /* __NM_DBUS_INTERFACE_H__ */ diff --git a/libnm-core/nm-setting-connection.c b/libnm-core/nm-setting-connection.c index 0217c358c1..f341a3987d 100644 --- a/libnm-core/nm-setting-connection.c +++ b/libnm-core/nm-setting-connection.c @@ -77,6 +77,7 @@ typedef struct { GSList *secondaries; /* secondary connections to activate with the base connection */ guint gateway_ping_timeout; NMMetered metered; + NMSettingConnectionLldp lldp; } NMSettingConnectionPrivate; enum { @@ -97,6 +98,7 @@ enum { PROP_SECONDARIES, PROP_GATEWAY_PING_TIMEOUT, PROP_METERED, + PROP_LLDP, LAST_PROP }; @@ -786,6 +788,24 @@ NM_BACKPORT_SYMBOL (libnm_1_0_6, NMMetered, nm_setting_connection_get_metered, ( NM_BACKPORT_SYMBOL (libnm_1_0_6, GType, nm_metered_get_type, (void), ()); +/** + * nm_setting_connection_get_lldp: + * @setting: the #NMSettingConnection + * + * Returns the #NMSettingConnection:lldp property of the connection. + * + * Returns: a %NMSettingConnectionLldp which indicates whether LLDP must be + * enabled for the connection. + * + * Since: 1.2 + **/ +NMSettingConnectionLldp +nm_setting_connection_get_lldp (NMSettingConnection *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), NM_SETTING_CONNECTION_LLDP_DEFAULT); + + return NM_SETTING_CONNECTION_GET_PRIVATE (setting)->lldp; +} static void _set_error_missing_base_setting (GError **error, const char *type) @@ -1196,6 +1216,9 @@ set_property (GObject *object, guint prop_id, case PROP_METERED: priv->metered = g_value_get_enum (value); break; + case PROP_LLDP: + priv->lldp = g_value_get_int (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1272,6 +1295,9 @@ get_property (GObject *object, guint prop_id, case PROP_METERED: g_value_set_enum (value, priv->metered); break; + case PROP_LLDP: + g_value_set_int (value, priv->lldp); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1702,4 +1728,29 @@ nm_setting_connection_class_init (NMSettingConnectionClass *setting_class) G_PARAM_READWRITE | NM_SETTING_PARAM_REAPPLY_IMMEDIATELY | G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingConnection:lldp: + * + * Whether LLDP is enabled for the connection. + * + * Since: 1.2 + **/ + /* ---ifcfg-rh--- + * property: lldp + * variable: LLDP + * values: boolean value or 'rx' + * default: missing variable means global default + * description: whether LLDP is enabled for the connection + * example: LLDP=no + * ---end--- + */ + g_object_class_install_property + (object_class, PROP_LLDP, + g_param_spec_int (NM_SETTING_CONNECTION_LLDP, "", "", + G_MININT32, G_MAXINT32, NM_SETTING_CONNECTION_LLDP_DEFAULT, + NM_SETTING_PARAM_FUZZY_IGNORE | + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); } diff --git a/libnm-core/nm-setting-connection.h b/libnm-core/nm-setting-connection.h index 0f502c955e..0d4966f05a 100644 --- a/libnm-core/nm-setting-connection.h +++ b/libnm-core/nm-setting-connection.h @@ -60,6 +60,7 @@ G_BEGIN_DECLS #define NM_SETTING_CONNECTION_SECONDARIES "secondaries" #define NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT "gateway-ping-timeout" #define NM_SETTING_CONNECTION_METERED "metered" +#define NM_SETTING_CONNECTION_LLDP "lldp" /* Types for property values */ /** @@ -79,6 +80,19 @@ typedef enum { NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_YES = 1, } NMSettingConnectionAutoconnectSlaves; +/** + * NMSettingConnectionLldp: + * @NM_SETTING_CONNECTION_LLDP_DEFAULT: default value + * @NM_SETTING_CONNECTION_LLDP_DISABLE: disable LLDP + * @NM_SETTING_CONNECTION_LLDP_ENABLE_RX: enable reception of LLDP frames + * + * #NMSettingConnectionLldp values indicate whether LLDP should be enabled. + */ +typedef enum { + NM_SETTING_CONNECTION_LLDP_DEFAULT = -1, + NM_SETTING_CONNECTION_LLDP_DISABLE = 0, + NM_SETTING_CONNECTION_LLDP_ENABLE_RX = 1, +} NMSettingConnectionLldp; /** * NMSettingConnection: @@ -144,6 +158,8 @@ gboolean nm_setting_connection_remove_secondary_by_value (NMSettingConnection guint32 nm_setting_connection_get_gateway_ping_timeout (NMSettingConnection *setting); NM_AVAILABLE_IN_1_2 NMMetered nm_setting_connection_get_metered (NMSettingConnection *setting); +NM_AVAILABLE_IN_1_2 +NMSettingConnectionLldp nm_setting_connection_get_lldp (NMSettingConnection *setting); G_END_DECLS diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c index bb2d6cb66d..7bd488b4e8 100644 --- a/libnm-core/tests/test-general.c +++ b/libnm-core/tests/test-general.c @@ -1972,6 +1972,7 @@ test_connection_diff_a_only (void) { NM_SETTING_CONNECTION_SECONDARIES, NM_SETTING_DIFF_RESULT_IN_A }, { NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT, NM_SETTING_DIFF_RESULT_IN_A }, { NM_SETTING_CONNECTION_METERED, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_CONNECTION_LLDP, NM_SETTING_DIFF_RESULT_IN_A }, { NULL, NM_SETTING_DIFF_RESULT_UNKNOWN } } }, { NM_SETTING_WIRED_SETTING_NAME, { diff --git a/libnm/libnm.ver b/libnm/libnm.ver index 2e58887d06..a3f9282822 100644 --- a/libnm/libnm.ver +++ b/libnm/libnm.ver @@ -859,17 +859,27 @@ libnm_1_2_0 { global: nm_access_point_get_last_seen; nm_device_ethernet_get_s390_subchannels; + nm_device_get_lldp_neighbors; nm_device_get_metered; nm_device_get_nm_plugin_missing; nm_device_set_managed; nm_device_wifi_request_scan_options; nm_device_wifi_request_scan_options_async; + nm_lldp_neighbor_get_attr_names; + nm_lldp_neighbor_get_attr_string_value; + nm_lldp_neighbor_get_attr_type; + nm_lldp_neighbor_get_attr_uint_value; + nm_lldp_neighbor_new; + nm_lldp_neighbor_ref; + nm_lldp_neighbor_unref; nm_metered_get_type; nm_setting_802_1x_check_cert_scheme; nm_setting_bridge_get_multicast_snooping; nm_setting_connection_autoconnect_slaves_get_type; nm_setting_connection_get_autoconnect_slaves; + nm_setting_connection_get_lldp; nm_setting_connection_get_metered; + nm_setting_connection_lldp_get_type; nm_setting_ip4_config_get_dhcp_timeout; nm_setting_ip_config_add_dns_option; nm_setting_ip_config_clear_dns_options; diff --git a/libnm/nm-device.c b/libnm/nm-device.c index 725762de61..bb4231ec52 100644 --- a/libnm/nm-device.c +++ b/libnm/nm-device.c @@ -105,6 +105,7 @@ typedef struct { char *physical_port_id; guint32 mtu; + GPtrArray *lldp_neighbors; } NMDevicePrivate; enum { @@ -134,6 +135,7 @@ enum { PROP_PHYSICAL_PORT_ID, PROP_MTU, PROP_METERED, + PROP_LLDP_NEIGHBORS, LAST_PROP }; @@ -146,6 +148,11 @@ enum { static guint signals[LAST_SIGNAL] = { 0 }; +struct _NMLldpNeighbor { + guint refcount; + GHashTable *attrs; +}; + static void nm_device_init (NMDevice *device) { @@ -153,6 +160,7 @@ nm_device_init (NMDevice *device) priv->state = NM_DEVICE_STATE_UNKNOWN; priv->reason = NM_DEVICE_STATE_REASON_NONE; + priv->lldp_neighbors = g_ptr_array_new (); } static gboolean @@ -165,6 +173,38 @@ demarshal_state_reason (NMObject *object, GParamSpec *pspec, GVariant *value, gp return TRUE; } +static gboolean +demarshal_lldp_neighbors (NMObject *object, GParamSpec *pspec, GVariant *value, gpointer field) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (object); + GVariantIter iter, attrs_iter; + GVariant *variant, *attr_variant; + const char *attr_name; + + g_return_val_if_fail (g_variant_is_of_type (value, G_VARIANT_TYPE ("aa{sv}")), FALSE); + + g_ptr_array_unref (priv->lldp_neighbors); + priv->lldp_neighbors = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_lldp_neighbor_unref); + g_variant_iter_init (&iter, value); + + while (g_variant_iter_next (&iter, "@a{sv}", &variant)) { + NMLldpNeighbor *neigh; + + neigh = nm_lldp_neighbor_new (); + g_variant_iter_init (&attrs_iter, variant); + + while (g_variant_iter_next (&attrs_iter, "{&sv}", &attr_name, &attr_variant)) + g_hash_table_insert (neigh->attrs, g_strdup (attr_name), attr_variant); + + g_variant_unref (variant); + g_ptr_array_add (priv->lldp_neighbors, neigh); + } + + _nm_object_queue_notify (object, NM_DEVICE_LLDP_NEIGHBORS); + + return TRUE; +} + static void device_state_changed (NMDBusDevice *proxy, guint new_state, @@ -199,6 +239,7 @@ init_dbus (NMObject *object) { NM_DEVICE_PHYSICAL_PORT_ID, &priv->physical_port_id }, { NM_DEVICE_MTU, &priv->mtu }, { NM_DEVICE_METERED, &priv->metered }, + { NM_DEVICE_LLDP_NEIGHBORS, &priv->lldp_neighbors, demarshal_lldp_neighbors }, /* Properties that exist in D-Bus but that we don't track */ { "ip4-address", NULL }, @@ -350,6 +391,7 @@ dispose (GObject *object) g_clear_object (&priv->active_connection); g_clear_pointer (&priv->available_connections, g_ptr_array_unref); + g_clear_pointer (&priv->lldp_neighbors, g_ptr_array_unref); G_OBJECT_CLASS (nm_device_parent_class)->dispose (object); } @@ -461,6 +503,9 @@ get_property (GObject *object, case PROP_METERED: g_value_set_uint (value, nm_device_get_metered (device)); break; + case PROP_LLDP_NEIGHBORS: + g_value_set_boxed (value, nm_device_get_lldp_neighbors (device)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -839,6 +884,18 @@ nm_device_class_init (NMDeviceClass *device_class) G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + /** + * NMDevice:lldp-neighbors: + * + * The LLDP neighbors. + **/ + g_object_class_install_property + (object_class, PROP_LLDP_NEIGHBORS, + g_param_spec_boxed (NM_DEVICE_LLDP_NEIGHBORS, "", "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + /* signals */ /** @@ -2010,6 +2067,27 @@ nm_device_get_metered (NMDevice *device) NM_BACKPORT_SYMBOL (libnm_1_0_6, NMMetered, nm_device_get_metered, (NMDevice *device), (device)); +/** + * nm_device_get_lldp_neighbors: + * @device: a #NMDevice + * + * Gets the list of neighbors discovered through LLDP. + * + * Returns: (element-type NMLldpNeighbor) (transfer none): the #GPtrArray + * containing #NMLldpNeighbors. This is the internal copy used by the + * device and must not be modified. The library never modifies the returned + * array and thus it is safe for callers to reference and keep using it. + * + * Since: 1.2 + **/ +GPtrArray * +nm_device_get_lldp_neighbors (NMDevice *device) +{ + g_return_val_if_fail (NM_IS_DEVICE (device), NULL); + + return NM_DEVICE_GET_PRIVATE (device)->lldp_neighbors; +} + /** * nm_device_is_software: * @device: a #NMDevice @@ -2360,3 +2438,182 @@ nm_device_get_setting_type (NMDevice *device) return NM_DEVICE_GET_CLASS (device)->get_setting_type (device); } + +/** + * nm_lldp_neighbor_new + * + * Creates a new #NMLldpNeighbor object. + * + * Returns: (transfer full): the new #NMLldpNeighbor object. + * + * Since: 1.2 + **/ +NMLldpNeighbor * +nm_lldp_neighbor_new (void) +{ + NMLldpNeighbor *neigh; + + neigh = g_new0 (NMLldpNeighbor, 1); + neigh->refcount = 1; + neigh->attrs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, + (GDestroyNotify) g_variant_unref); + + return neigh; +} + +/** + * nm_lldp_neighbor_ref + * @neighbor: the #NMLldpNeighbor + * + * Increases the reference count of the object. + * + * Since: 1.2 + **/ +void +nm_lldp_neighbor_ref (NMLldpNeighbor *neighbor) +{ + g_return_if_fail (neighbor); + g_return_if_fail (neighbor->refcount > 0); + + neighbor->refcount++; +} + +/** + * nm_lldp_neighbor_unref: + * @neighbor: the #NMLldpNeighbor + * + * Decreases the reference count of the object. If the reference count + * reaches zero, the object will be destroyed. + * + * Since: 1.2 + **/ +void +nm_lldp_neighbor_unref (NMLldpNeighbor *neighbor) +{ + g_return_if_fail (neighbor); + g_return_if_fail (neighbor->refcount > 0); + + if (--neighbor->refcount == 0) { + g_return_if_fail (neighbor->attrs); + g_hash_table_unref (neighbor->attrs); + g_free (neighbor); + } +} + +/** + * nm_lldp_neighbor_get_attr_names: + * @neighbor: the #NMLldpNeighbor + * + * Gets an array of attribute names available for @neighbor. + * + * Returns: (transfer full): a %NULL-terminated array of attribute names. + * + * Since: 1.2 + **/ +char ** +nm_lldp_neighbor_get_attr_names (NMLldpNeighbor *neighbor) +{ + GHashTableIter iter; + const char *key; + GPtrArray *names; + + g_return_val_if_fail (neighbor, NULL); + g_return_val_if_fail (neighbor->attrs, NULL); + + names = g_ptr_array_new (); + + g_hash_table_iter_init (&iter, neighbor->attrs); + while (g_hash_table_iter_next (&iter, (gpointer *) &key, NULL)) + g_ptr_array_add (names, g_strdup (key)); + + g_ptr_array_add (names, NULL); + + return (char **) g_ptr_array_free (names, FALSE); +} + +/** + * nm_lldp_neighbor_get_attr_string_value: + * @neighbor: the #NMLldpNeighbor + * @name: the attribute name + * @out_value: (out) (allow-none) (transfer none): on return, the attribute value + * + * Gets the string value of attribute with name @name on @neighbor + * + * Returns: %TRUE if a string attribute with name @name was found, %FALSE otherwise + * + * Since: 1.2 + **/ +gboolean +nm_lldp_neighbor_get_attr_string_value (NMLldpNeighbor *neighbor, char *name, + const char **out_value) +{ + GVariant *variant; + + g_return_val_if_fail (neighbor, FALSE); + g_return_val_if_fail (name && name[0], FALSE); + + variant = g_hash_table_lookup (neighbor->attrs, name); + if (variant && g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING)) { + if (out_value) + *out_value = g_variant_get_string (variant, NULL); + return TRUE; + } else + return FALSE; +} + +/** + * nm_lldp_neighbor_get_attr_uint_value: + * @neighbor: the #NMLldpNeighbor + * @name: the attribute name + * @out_value: (out) (allow-none) on return, the attribute value + * + * Gets the uint value of attribute with name @name on @neighbor + * + * Returns: %TRUE if a uint attribute with name @name was found, %FALSE otherwise + * + * Since: 1.2 + **/ +gboolean +nm_lldp_neighbor_get_attr_uint_value (NMLldpNeighbor *neighbor, char *name, + guint *out_value) +{ + GVariant *variant; + + g_return_val_if_fail (neighbor, FALSE); + g_return_val_if_fail (name && name[0], FALSE); + + variant = g_hash_table_lookup (neighbor->attrs, name); + if (variant && g_variant_is_of_type (variant, G_VARIANT_TYPE_UINT32)) { + if (out_value) + *out_value = g_variant_get_uint32 (variant); + return TRUE; + } else + return FALSE; +} + +/** + * nm_lldp_neighbor_get_attr_type: + * @neighbor: the #NMLldpNeighbor + * @name: the attribute name + * + * Get the type of an attribute. + * + * Returns: the #GVariantType of the attribute with name @name + * + * Since: 1.2 + **/ +const GVariantType * +nm_lldp_neighbor_get_attr_type (NMLldpNeighbor *neighbor, char *name) +{ + GVariant *variant; + + g_return_val_if_fail (neighbor, NULL); + g_return_val_if_fail (name && name[0], NULL); + + variant = g_hash_table_lookup (neighbor->attrs, name); + if (variant) + return g_variant_get_type (variant); + else + return NULL; + +} diff --git a/libnm/nm-device.h b/libnm/nm-device.h index 4453bfeb2c..7b76cf2d0b 100644 --- a/libnm/nm-device.h +++ b/libnm/nm-device.h @@ -62,6 +62,7 @@ G_BEGIN_DECLS #define NM_DEVICE_PHYSICAL_PORT_ID "physical-port-id" #define NM_DEVICE_MTU "mtu" #define NM_DEVICE_METERED "metered" +#define NM_DEVICE_LLDP_NEIGHBORS "lldp-neighbors" struct _NMDevice { NMObject parent; @@ -90,6 +91,8 @@ typedef struct { gpointer padding[8]; } NMDeviceClass; +typedef struct _NMLldpNeighbor NMLldpNeighbor; + GType nm_device_get_type (void); const char * nm_device_get_iface (NMDevice *device); @@ -127,6 +130,8 @@ const char * nm_device_get_vendor (NMDevice *device); const char * nm_device_get_description (NMDevice *device); NM_AVAILABLE_IN_1_2 NMMetered nm_device_get_metered (NMDevice *device); +NM_AVAILABLE_IN_1_2 +GPtrArray * nm_device_get_lldp_neighbors (NMDevice *device); char ** nm_device_disambiguate_names (NMDevice **devices, int num_devices); @@ -164,6 +169,24 @@ gboolean nm_device_connection_compatible (NMDevice *device, GType nm_device_get_setting_type (NMDevice *device); +NM_AVAILABLE_IN_1_2 +NMLldpNeighbor *nm_lldp_neighbor_new (void); +NM_AVAILABLE_IN_1_2 +void nm_lldp_neighbor_ref (NMLldpNeighbor *neighbor); +NM_AVAILABLE_IN_1_2 +void nm_lldp_neighbor_unref (NMLldpNeighbor *neighbor); +NM_AVAILABLE_IN_1_2 +char **nm_lldp_neighbor_get_attr_names (NMLldpNeighbor *neighbor); +NM_AVAILABLE_IN_1_2 +gboolean nm_lldp_neighbor_get_attr_string_value (NMLldpNeighbor *neighbor, char *name, + const char **out_value); +NM_AVAILABLE_IN_1_2 +gboolean nm_lldp_neighbor_get_attr_uint_value (NMLldpNeighbor *neighbor, char *name, + guint *out_value); + +NM_AVAILABLE_IN_1_2 +const GVariantType *nm_lldp_neighbor_get_attr_type (NMLldpNeighbor *neighbor, char *name); + G_END_DECLS #endif /* __NM_DEVICE_H__ */ diff --git a/libnm/nm-ip-config.c b/libnm/nm-ip-config.c index d057903e27..fc4567c420 100644 --- a/libnm/nm-ip-config.c +++ b/libnm/nm-ip-config.c @@ -406,7 +406,8 @@ nm_ip_config_get_gateway (NMIPConfig *config) * * Returns: (element-type NMIPAddress) (transfer none): the #GPtrArray * containing #NMIPAddresses. This is the internal copy used by the - * configuration and must not be modified. + * configuration and must not be modified. The library never modifies the + * returned array and thus it is safe for callers to reference and keep using it. **/ GPtrArray * nm_ip_config_get_addresses (NMIPConfig *config) @@ -491,7 +492,9 @@ nm_ip_config_get_wins_servers (NMIPConfig *config) * * Returns: (element-type NMIPRoute) (transfer none): the #GPtrArray containing * #NMIPRoutes. This is the internal copy used by the configuration, and must - * not be modified. + * not be modified. The library never modifies the returned array and thus it is + * safe for callers to reference and keep using it. + * **/ GPtrArray * nm_ip_config_get_routes (NMIPConfig *config) diff --git a/man/NetworkManager.conf.xml.in b/man/NetworkManager.conf.xml.in index f7bcd1a359..9bf1388bea 100644 --- a/man/NetworkManager.conf.xml.in +++ b/man/NetworkManager.conf.xml.in @@ -574,6 +574,9 @@ ipv6.ip6-privacy=1 connection.autoconnect-slaves + + connection.lldp + ethernet.wake-on-lan diff --git a/src/Makefile.am b/src/Makefile.am index d23cbf81a7..b845958a6e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -221,6 +221,8 @@ libNetworkManager_la_SOURCES = \ $(nm_dhcp_client_headers) \ devices/nm-device.c \ devices/nm-device.h \ + devices/nm-lldp-listener.c \ + devices/nm-lldp-listener.h \ devices/nm-device-ethernet-utils.c \ devices/nm-device-ethernet-utils.h \ devices/nm-device-factory.c \ diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 46bf08b514..fcd5dfe576 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -60,6 +60,7 @@ #include "nm-core-internal.h" #include "nm-default-route-manager.h" #include "nm-route-manager.h" +#include "nm-lldp-listener.h" #include "sd-ipv4ll.h" #include "nm-audit-manager.h" @@ -126,6 +127,7 @@ enum { PROP_HW_ADDRESS, PROP_HAS_PENDING_ACTION, PROP_METERED, + PROP_LLDP_NEIGHBORS, LAST_PROP }; @@ -351,6 +353,7 @@ typedef struct { NMMetered metered; NMConnectionProvider *con_provider; + NMLldpListener *lldp_listener; } NMDevicePrivate; static gboolean nm_device_set_ip4_config (NMDevice *self, @@ -1182,6 +1185,7 @@ static void update_dynamic_ip_setup (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + GError *error; g_hash_table_remove_all (priv->ip6_saved_properties); @@ -1207,6 +1211,16 @@ update_dynamic_ip_setup (NMDevice *self) if (priv->dnsmasq_manager) { /* FIXME: todo */ } + + if (priv->lldp_listener && nm_lldp_listener_is_running (priv->lldp_listener)) { + nm_lldp_listener_stop (priv->lldp_listener); + if (!nm_lldp_listener_start (priv->lldp_listener, nm_device_get_ifindex (self), + nm_device_get_iface (self), &error)) { + _LOGD (LOGD_DEVICE, "LLDP listener %p could not be restarted: %s", + priv->lldp_listener, error->message); + g_clear_error (&error); + } + } } static void @@ -2969,6 +2983,45 @@ master_ready_cb (NMActiveConnection *active, nm_device_activate_schedule_stage2_device_config (self); } +static void +lldp_neighbors_changed (NMLldpListener *lldp_listener, GParamSpec *pspec, + gpointer user_data) +{ + NMDevice *self = NM_DEVICE (user_data); + + g_object_notify (G_OBJECT (self), NM_DEVICE_LLDP_NEIGHBORS); +} + +static gboolean +lldp_rx_enabled (NMDevice *self) +{ + NMConnection *connection; + NMSettingConnection *s_con; + NMSettingConnectionLldp lldp = NM_SETTING_CONNECTION_LLDP_DEFAULT; + + connection = nm_device_get_applied_connection (self); + g_return_val_if_fail (connection, FALSE); + + s_con = nm_connection_get_setting_connection (connection); + g_return_val_if_fail (s_con, FALSE); + + lldp = nm_setting_connection_get_lldp (s_con); + if (lldp == NM_SETTING_CONNECTION_LLDP_DEFAULT) { + gs_free char *value = NULL; + + value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, + "connection.lldp", + self); + lldp = _nm_utils_ascii_str_to_int64 (value, 10, + NM_SETTING_CONNECTION_LLDP_DEFAULT, + NM_SETTING_CONNECTION_LLDP_ENABLE_RX, + NM_SETTING_CONNECTION_LLDP_DEFAULT); + if (lldp == NM_SETTING_CONNECTION_LLDP_DEFAULT) + lldp = NM_SETTING_CONNECTION_LLDP_DISABLE; + } + return lldp == NM_SETTING_CONNECTION_LLDP_ENABLE_RX; +} + static NMActStageReturn act_stage1_prepare (NMDevice *self, NMDeviceStateReason *reason) { @@ -3089,6 +3142,28 @@ activate_stage2_device_config (NMDevice *self) nm_device_queue_recheck_assume (info->slave); } + if (lldp_rx_enabled (self)) { + gs_free_error GError *error = NULL; + + if (priv->lldp_listener) + nm_lldp_listener_stop (priv->lldp_listener); + else { + priv->lldp_listener = nm_lldp_listener_new (); + g_signal_connect (priv->lldp_listener, + "notify::" NM_LLDP_LISTENER_NEIGHBORS, + G_CALLBACK (lldp_neighbors_changed), + self); + } + + if (nm_lldp_listener_start (priv->lldp_listener, nm_device_get_ifindex (self), + nm_device_get_iface (self), &error)) + _LOGD (LOGD_DEVICE, "LLDP listener %p started", priv->lldp_listener); + else { + _LOGD (LOGD_DEVICE, "LLDP listener %p could not be started: %s", + priv->lldp_listener, error->message); + } + } + nm_device_activate_schedule_stage3_ip_config_start (self); } @@ -8526,6 +8601,9 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, CleanupType clean nm_platform_address_flush (NM_PLATFORM_GET, ifindex); } + if (priv->lldp_listener) + nm_lldp_listener_stop (priv->lldp_listener); + nm_device_update_metered (self); _cleanup_generic_post (self, cleanup_type); } @@ -9556,6 +9634,14 @@ 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); + g_clear_object (&priv->lldp_listener); + } + G_OBJECT_CLASS (nm_device_parent_class)->dispose (object); } @@ -9707,6 +9793,7 @@ get_property (GObject *object, guint prop_id, GPtrArray *array; GHashTableIter iter; NMConnection *connection; + GVariantBuilder array_builder; switch (prop_id) { case PROP_UDI: @@ -9814,6 +9901,14 @@ get_property (GObject *object, guint prop_id, case PROP_METERED: g_value_set_uint (value, priv->metered); break; + case PROP_LLDP_NEIGHBORS: + if (priv->lldp_listener) + g_value_set_variant (value, nm_lldp_listener_get_neighbors (priv->lldp_listener)); + else { + g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("aa{sv}")); + g_value_take_variant (value, g_variant_builder_end (&array_builder)); + } + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -10096,6 +10191,14 @@ nm_device_class_init (NMDeviceClass *klass) G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property + (object_class, PROP_LLDP_NEIGHBORS, + g_param_spec_variant (NM_DEVICE_LLDP_NEIGHBORS, "", "", + G_VARIANT_TYPE ("aa{sv}"), + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + /* Signals */ signals[STATE_CHANGED] = g_signal_new ("state-changed", diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index 441ae06b73..15858194a7 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -58,6 +58,7 @@ #define NM_DEVICE_MTU "mtu" #define NM_DEVICE_HW_ADDRESS "hw-address" #define NM_DEVICE_METERED "metered" +#define NM_DEVICE_LLDP_NEIGHBORS "lldp-neighbors" #define NM_DEVICE_TYPE_DESC "type-desc" /* Internal only */ #define NM_DEVICE_RFKILL_TYPE "rfkill-type" /* Internal only */ diff --git a/src/devices/nm-lldp-listener.c b/src/devices/nm-lldp-listener.c new file mode 100644 index 0000000000..6d29a67cac --- /dev/null +++ b/src/devices/nm-lldp-listener.c @@ -0,0 +1,651 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2015 Red Hat, Inc. + */ + +#include "config.h" + +#include + +#include "sd-lldp.h" +#include "lldp.h" +#include "nm-lldp-listener.h" +#include "nm-platform.h" +#include "nm-utils.h" + +#define MAX_NEIGHBORS 4096 +#define MIN_UPDATE_INTERVAL 2 + +typedef struct { + char *iface; + int ifindex; + sd_lldp *lldp_handle; + GHashTable *lldp_neighbors; + guint timer; + guint num_pending_events; + GVariant *variant; +} NMLldpListenerPrivate; + +enum { + PROP_0, + PROP_NEIGHBORS, + + LAST_PROP +}; + +G_DEFINE_TYPE (NMLldpListener, nm_lldp_listener, G_TYPE_OBJECT) + +#define NM_LLDP_LISTENER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_LLDP_LISTENER, NMLldpListenerPrivate)) + +typedef struct { + guint8 chassis_id_type; + guint8 port_id_type; + char *chassis_id; + char *port_id; + + int dest; + + GHashTable *tlvs; +} LLDPNeighbor; + +static void process_lldp_neighbors (NMLldpListener *self); + +static void +gvalue_destroy (gpointer data) +{ + GValue *value = (GValue *) data; + + g_value_unset (value); + g_slice_free (GValue, value); +} + +static GValue * +gvalue_new_nstr (const char *str, guint16 len) +{ + GValue *value; + + value = g_slice_new0 (GValue); + g_value_init (value, G_TYPE_STRING); + g_value_take_string (value, strndup (str, len)); + return value; +} + +static GValue * +gvalue_new_uint (guint val) +{ + GValue *value; + + value = g_slice_new0 (GValue); + g_value_init (value, G_TYPE_UINT); + g_value_set_uint (value, val); + return value; +} + +static guint +lldp_neighbor_id_hash (gconstpointer ptr) +{ + const LLDPNeighbor *neigh = ptr; + + return g_str_hash (neigh->chassis_id) ^ + g_str_hash (neigh->port_id) ^ + neigh->chassis_id_type ^ + (neigh->port_id_type * 33); +} + +static gboolean +lldp_neighbor_id_equal (gconstpointer a, gconstpointer b) +{ + const LLDPNeighbor *x = a, *y = b; + + return x->chassis_id_type == y->chassis_id_type && + x->port_id_type == y->port_id_type && + !g_strcmp0 (x->chassis_id, y->chassis_id) && + !g_strcmp0 (x->port_id, y->port_id); +} + +static void +lldp_neighbor_free (gpointer data) +{ + LLDPNeighbor *neighbor = data; + + if (neighbor) { + g_free (neighbor->chassis_id); + g_free (neighbor->port_id); + g_hash_table_unref (neighbor->tlvs); + g_free (neighbor); + } +} + +static gboolean +lldp_neighbor_equal (LLDPNeighbor *a, LLDPNeighbor *b) +{ + GHashTableIter iter; + gpointer k, v; + + g_return_val_if_fail (a && a->tlvs, FALSE); + g_return_val_if_fail (b && b->tlvs, FALSE); + + if ( a->chassis_id_type != b->chassis_id_type + || a->port_id_type != b->port_id_type + || a->dest != b->dest + || g_strcmp0 (a->chassis_id, b->chassis_id) + || g_strcmp0 (a->port_id, b->port_id)) + return FALSE; + + if (g_hash_table_size (a->tlvs) != g_hash_table_size (b->tlvs)) + return FALSE; + + g_hash_table_iter_init (&iter, a->tlvs); + while (g_hash_table_iter_next (&iter, &k, &v)) { + GValue *value_a, *value_b; + + value_a = v; + value_b = g_hash_table_lookup (b->tlvs, k); + + if (!value_b) + return FALSE; + + g_return_val_if_fail (G_VALUE_TYPE (value_a) == G_VALUE_TYPE (value_b), FALSE); + + if (G_VALUE_HOLDS_STRING (value_a)) { + if (g_strcmp0 (g_value_get_string (value_a), g_value_get_string (value_b))) + return FALSE; + } else if (G_VALUE_HOLDS_UINT (value_a)) { + if (g_value_get_uint (value_a) != g_value_get_uint (value_b)) + return FALSE; + } else + g_return_val_if_reached (FALSE); + } + + return TRUE; +} + +static gboolean +lldp_hash_table_equal (GHashTable *a, GHashTable *b) +{ + GHashTableIter iter; + gpointer val; + + g_return_val_if_fail (a, FALSE); + g_return_val_if_fail (b, FALSE); + + if (g_hash_table_size (a) != g_hash_table_size (b)) + return FALSE; + + g_hash_table_iter_init (&iter, a); + while (g_hash_table_iter_next (&iter, NULL, &val)) { + LLDPNeighbor *neigh_a, *neigh_b; + + neigh_a = val; + neigh_b = g_hash_table_lookup (b, val); + + if (!neigh_b) + return FALSE; + + if (!lldp_neighbor_equal (neigh_a, neigh_b)) + return FALSE; + } + + return TRUE; +} + +static gboolean +lldp_timeout (gpointer user_data) +{ + NMLldpListener *self = NM_LLDP_LISTENER (user_data); + NMLldpListenerPrivate *priv = NM_LLDP_LISTENER_GET_PRIVATE (self); + + priv->timer = 0; + + if (priv->num_pending_events) + process_lldp_neighbors (self); + + return G_SOURCE_REMOVE; +} + +static void +process_lldp_neighbors (NMLldpListener *self) +{ + NMLldpListenerPrivate *priv = NM_LLDP_LISTENER_GET_PRIVATE (self); + sd_lldp_packet **packets = NULL; + GHashTable *hash; + int num, i; + + num = sd_lldp_get_packets (priv->lldp_handle, &packets); + if (num < 0) { + nm_log_dbg (LOGD_DEVICE, "LLDP: error %d retrieving neighbor packets for %s", + num, priv->iface); + return; + } + + hash = g_hash_table_new_full (lldp_neighbor_id_hash, lldp_neighbor_id_equal, + lldp_neighbor_free, NULL); + + for (i = 0; packets && i < num; i++) { + uint8_t chassis_id_type, port_id_type, *chassis_id, *port_id, data8; + uint16_t chassis_id_len, port_id_len, len, data16; + LLDPNeighbor *neigh; + GValue *value; + char *str; + int r; + + if (i >= MAX_NEIGHBORS) + goto next_packet; + + r = sd_lldp_packet_read_chassis_id (packets[i], &chassis_id_type, + &chassis_id, &chassis_id_len); + if (r < 0) + goto next_packet; + + r = sd_lldp_packet_read_port_id (packets[i], &port_id_type, + &port_id, &port_id_len); + if (r < 0) + goto next_packet; + + neigh = g_malloc0 (sizeof (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) { + lldp_neighbor_free (neigh); + goto next_packet; + } + + switch (chassis_id_type) { + case LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS: + case LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME: + case LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED: + case LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT: + neigh->chassis_id = strndup ((char *) chassis_id, chassis_id_len); + break; + case LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS: + neigh->chassis_id = nm_utils_hwaddr_ntoa (chassis_id, chassis_id_len); + break; + default: + nm_log_dbg (LOGD_DEVICE, "LLDP: unsupported chassis ID type %d", chassis_id_type); + lldp_neighbor_free (neigh); + goto next_packet; + } + + if (port_id_len < 1) { + lldp_neighbor_free (neigh); + goto next_packet; + } + + switch (port_id_type) { + case LLDP_PORT_SUBTYPE_INTERFACE_ALIAS: + case LLDP_PORT_SUBTYPE_INTERFACE_NAME: + case LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED: + case LLDP_PORT_SUBTYPE_PORT_COMPONENT: + neigh->port_id = strndup ((char *) port_id, port_id_len); + break; + case LLDP_PORT_SUBTYPE_MAC_ADDRESS: + neigh->port_id = nm_utils_hwaddr_ntoa (port_id, port_id_len); + break; + default: + nm_log_dbg (LOGD_DEVICE, "LLDP: unsupported port ID type %d", port_id_type); + lldp_neighbor_free (neigh); + goto next_packet; + } + + if (sd_lldp_packet_read_port_description (packets[i], &str, &len) == 0) { + value = gvalue_new_nstr (str, len); + 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); + 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); + g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_SYSTEM_DESCRIPTION, value); + } + + if (sd_lldp_packet_read_system_capability (packets[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_packet_read_port_protocol_vlan_id (packets[i], &data8, &data16) == 0) { + value = gvalue_new_uint (data16); + g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_IEEE_802_1_PPVID, value); + + value = gvalue_new_uint (data8); + g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_IEEE_802_1_PPVID_FLAGS, value); + } + + if (sd_lldp_packet_read_vlan_name (packets[i], &data16, &str, &len) == 0) { + value = gvalue_new_uint (data16); + g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_IEEE_802_1_VID, value); + + value = gvalue_new_nstr (str, len); + g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_IEEE_802_1_VLAN_NAME, value); + } + + nm_log_dbg (LOGD_DEVICE, "LLDP: new neigh: CHASSIS='%s' PORT='%s'", + neigh->chassis_id, neigh->port_id); + + g_hash_table_add (hash, neigh); +next_packet: + sd_lldp_packet_unref (packets[i]); + } + + g_free (packets); + + if (lldp_hash_table_equal (priv->lldp_neighbors, hash)) { + g_hash_table_destroy (hash); + } else { + g_hash_table_destroy (priv->lldp_neighbors); + priv->lldp_neighbors = hash; + nm_clear_g_variant (&priv->variant); + g_object_notify (G_OBJECT (self), NM_LLDP_LISTENER_NEIGHBORS); + } + + /* Since the processing of the neighbor list is potentially + * expensive when there are many neighbors, coalesce multiple + * events arriving in short time. + */ + priv->timer = g_timeout_add_seconds (MIN_UPDATE_INTERVAL, lldp_timeout, self); + priv->num_pending_events = 0; +} + +static void +lldp_event_handler (sd_lldp *lldp, int event, void *userdata) +{ + NMLldpListener *self = userdata; + NMLldpListenerPrivate *priv; + + g_return_if_fail (NM_IS_LLDP_LISTENER (self)); + priv = NM_LLDP_LISTENER_GET_PRIVATE (self); + + if (priv->timer > 0) { + priv->num_pending_events++; + return; + } + + process_lldp_neighbors (self); +} + +gboolean +nm_lldp_listener_start (NMLldpListener *self, int ifindex, const char *iface, + GError **error) +{ + NMLldpListenerPrivate *priv; + const struct ether_addr *addr; + size_t addr_len; + 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); + + if (priv->lldp_handle) { + g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, + "already running"); + return FALSE; + } + + addr = nm_platform_link_get_address (NM_PLATFORM_GET, ifindex, &addr_len); + if (!addr || addr_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, addr, &priv->lldp_handle); + if (ret) { + 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) { + 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) { + 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) { + g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, + "start failed"); + goto err; + } + + priv->ifindex = ifindex; + priv->iface = strdup (iface); + return TRUE; + +err: + sd_lldp_detach_event (priv->lldp_handle); +err_free: + sd_lldp_free (priv->lldp_handle); + priv->lldp_handle = NULL; + return FALSE; +} + +void +nm_lldp_listener_stop (NMLldpListener *self) +{ + NMLldpListenerPrivate *priv; + guint size; + + g_return_if_fail (NM_IS_LLDP_LISTENER (self)); + priv = NM_LLDP_LISTENER_GET_PRIVATE (self); + + if (priv->lldp_handle) { + sd_lldp_stop (priv->lldp_handle); + sd_lldp_detach_event (priv->lldp_handle); + sd_lldp_free (priv->lldp_handle); + g_clear_pointer (&priv->iface, g_free); + priv->lldp_handle = NULL; + + size = g_hash_table_size (priv->lldp_neighbors); + g_hash_table_remove_all (priv->lldp_neighbors); + if (size) { + nm_clear_g_variant (&priv->variant); + g_object_notify (G_OBJECT (self), NM_LLDP_LISTENER_NEIGHBORS); + } + } + + nm_clear_g_source (&priv->timer); +} + +gboolean +nm_lldp_listener_is_running (NMLldpListener *self) +{ + NMLldpListenerPrivate *priv; + + g_return_val_if_fail (NM_IS_LLDP_LISTENER (self), FALSE); + + priv = NM_LLDP_LISTENER_GET_PRIVATE (self); + return !!priv->lldp_handle; +} + +GVariant * +nm_lldp_listener_get_neighbors (NMLldpListener *self) +{ + GVariantBuilder array_builder, neigh_builder; + GHashTableIter iter; + NMLldpListenerPrivate *priv; + LLDPNeighbor *neigh; + char *dest_str = NULL; + + priv = NM_LLDP_LISTENER_GET_PRIVATE (self); + + if (priv->variant) + goto out; + + g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("aa{sv}")); + g_hash_table_iter_init (&iter, priv->lldp_neighbors); + + while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &neigh)) { + GHashTableIter val_iter; + gpointer key, val; + + g_variant_builder_init (&neigh_builder, G_VARIANT_TYPE ("a{sv}")); + + g_variant_builder_add (&neigh_builder, "{sv}", + NM_LLDP_ATTR_CHASSIS_ID_TYPE, + g_variant_new_uint32 (neigh->chassis_id_type)); + g_variant_builder_add (&neigh_builder, "{sv}", + NM_LLDP_ATTR_CHASSIS_ID, + g_variant_new_string (neigh->chassis_id)); + g_variant_builder_add (&neigh_builder, "{sv}", + NM_LLDP_ATTR_PORT_ID_TYPE, + g_variant_new_uint32 (neigh->port_id_type)); + g_variant_builder_add (&neigh_builder, "{sv}", + NM_LLDP_ATTR_PORT_ID, + g_variant_new_string (neigh->port_id)); + + switch (neigh->dest) { + case SD_LLDP_DESTINATION_TYPE_NEAREST_BRIDGE: + dest_str = NM_LLDP_DEST_NEAREST_BRIDGE; + break; + case SD_LLDP_DESTINATION_TYPE_NEAREST_NON_TPMR_BRIDGE: + dest_str = NM_LLDP_DEST_NEAREST_NON_TPMR_BRIDGE; + break; + case SD_LLDP_DESTINATION_TYPE_NEAREST_CUSTOMER_BRIDGE: + dest_str = NM_LLDP_DEST_NEAREST_CUSTOMER_BRIDGE; + break; + } + + if (dest_str) { + g_variant_builder_add (&neigh_builder, "{sv}", + NM_LLDP_ATTR_DESTINATION, + g_variant_new_string (dest_str)); + } + + g_hash_table_iter_init (&val_iter, neigh->tlvs); + while (g_hash_table_iter_next (&val_iter, &key, &val)) { + GValue *item = val; + + if (G_VALUE_HOLDS_STRING (item)) { + g_variant_builder_add (&neigh_builder, "{sv}", + key, + g_variant_new_string (g_value_get_string (item))); + } else if (G_VALUE_HOLDS_UINT (item)) { + g_variant_builder_add (&neigh_builder, "{sv}", + key, + g_variant_new_uint32 (g_value_get_uint (item))); + } + } + + g_variant_builder_add (&array_builder, "a{sv}", &neigh_builder); + } + + priv->variant = g_variant_ref_sink (g_variant_builder_end (&array_builder)); + +out: + return priv->variant; +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMLldpListener *self = NM_LLDP_LISTENER (object); + + switch (prop_id) { + case PROP_NEIGHBORS: + g_value_set_variant (value, nm_lldp_listener_get_neighbors (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_lldp_listener_init (NMLldpListener *self) +{ + NMLldpListenerPrivate *priv = NM_LLDP_LISTENER_GET_PRIVATE (self); + + priv->lldp_neighbors = g_hash_table_new_full (lldp_neighbor_id_hash, + lldp_neighbor_id_equal, + lldp_neighbor_free, NULL); +} + +NMLldpListener * +nm_lldp_listener_new (void) +{ + return (NMLldpListener *) g_object_new (NM_TYPE_LLDP_LISTENER, NULL); +} + +static void +dispose (GObject *object) +{ + nm_lldp_listener_stop (NM_LLDP_LISTENER (object)); + + G_OBJECT_CLASS (nm_lldp_listener_parent_class)->dispose (object); +} + +static void +finalize (GObject *object) +{ + NMLldpListener *self = NM_LLDP_LISTENER (object); + NMLldpListenerPrivate *priv = NM_LLDP_LISTENER_GET_PRIVATE (self); + + nm_lldp_listener_stop (self); + g_hash_table_unref (priv->lldp_neighbors); + + nm_clear_g_variant (&priv->variant); + + G_OBJECT_CLASS (nm_lldp_listener_parent_class)->finalize (object); +} + +static void +nm_lldp_listener_class_init (NMLldpListenerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (NMLldpListenerPrivate)); + + object_class->dispose = dispose; + object_class->finalize = finalize; + object_class->get_property = get_property; + + g_object_class_install_property + (object_class, PROP_NEIGHBORS, + g_param_spec_variant (NM_LLDP_LISTENER_NEIGHBORS, "", "", + G_VARIANT_TYPE ("aa{sv}"), + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); +} + diff --git a/src/devices/nm-lldp-listener.h b/src/devices/nm-lldp-listener.h new file mode 100644 index 0000000000..7e8fa2051b --- /dev/null +++ b/src/devices/nm-lldp-listener.h @@ -0,0 +1,56 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2015 Red Hat, Inc. + */ + +#ifndef __NM_LLDP_LISTENER__ +#define __NM_LLDP_LISTENER__ + +#include "nm-glib.h" +#include "nm-types.h" + +G_BEGIN_DECLS + +#define NM_TYPE_LLDP_LISTENER (nm_lldp_listener_get_type ()) +#define NM_LLDP_LISTENER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_LLDP_LISTENER, NMLldpListener)) +#define NM_LLDP_LISTENER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_LLDP_LISTENER, NMLldpListenerClass)) +#define NM_IS_LLDP_LISTENER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_LLDP_LISTENER)) +#define NM_IS_LLDP_LISTENER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_LLDP_LISTENER)) +#define NM_LLDP_LISTENER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_LLDP_LISTENER, NMLldpListenerClass)) + +#define NM_LLDP_LISTENER_NEIGHBORS "neighbors" + +struct _NMLldpListener { + GObject parent; +}; + +typedef struct { + GObjectClass parent; +} NMLldpListenerClass; + +GType nm_lldp_listener_get_type (void); +NMLldpListener *nm_lldp_listener_new (void); +gboolean nm_lldp_listener_start (NMLldpListener *self, int ifindex, const char *iface, GError **error); +void nm_lldp_listener_stop (NMLldpListener *self); +gboolean nm_lldp_listener_is_running (NMLldpListener *self); + +GVariant *nm_lldp_listener_get_neighbors (NMLldpListener *self); + +G_END_DECLS + +#endif /* __NM_LLDP_LISTENER__ */ diff --git a/src/nm-types.h b/src/nm-types.h index 8fd4c36aef..2554d47e98 100644 --- a/src/nm-types.h +++ b/src/nm-types.h @@ -49,6 +49,7 @@ typedef struct _NMRfkillManager NMRfkillManager; typedef struct _NMRouteManager NMRouteManager; typedef struct _NMSessionMonitor NMSessionMonitor; typedef struct _NMSleepMonitor NMSleepMonitor; +typedef struct _NMLldpListener NMLldpListener; typedef enum { /* In priority order; higher number == higher priority */ diff --git a/src/settings/plugins/ifcfg-rh/reader.c b/src/settings/plugins/ifcfg-rh/reader.c index 564ed1248a..5a29e924ea 100644 --- a/src/settings/plugins/ifcfg-rh/reader.c +++ b/src/settings/plugins/ifcfg-rh/reader.c @@ -128,6 +128,7 @@ make_connection_setting (const char *file, const char *prefix) { NMSettingConnection *s_con; + NMSettingConnectionLldp lldp; const char *ifcfg_name = NULL; char *new_id, *uuid = NULL, *zone = NULL, *value; @@ -165,17 +166,24 @@ make_connection_setting (const char *file, g_free (value); } + value = svGetValue (ifcfg, "LLDP", FALSE); + if (!g_strcmp0 (value, "rx")) + lldp = NM_SETTING_CONNECTION_LLDP_ENABLE_RX; + else + lldp = svParseBoolean (value, NM_SETTING_CONNECTION_LLDP_DEFAULT); + /* Missing ONBOOT is treated as "ONBOOT=true" by the old network service */ g_object_set (s_con, NM_SETTING_CONNECTION_AUTOCONNECT, - svTrueValue (ifcfg, "ONBOOT", TRUE), + svGetValueBoolean (ifcfg, "ONBOOT", TRUE), NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY, (gint) svGetValueInt64 (ifcfg, "AUTOCONNECT_PRIORITY", 10, NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_MIN, NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_MAX, NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_DEFAULT), NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES, - svTrueValue (ifcfg, "AUTOCONNECT_SLAVES", NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_DEFAULT), + svGetValueBoolean (ifcfg, "AUTOCONNECT_SLAVES", NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_DEFAULT), + NM_SETTING_CONNECTION_LLDP, lldp, NULL); value = svGetValue (ifcfg, "USERS", FALSE); @@ -247,7 +255,7 @@ make_connection_setting (const char *file, g_free (value); } - switch (svTrueValue (ifcfg, "CONNECTION_METERED", -1)) { + switch (svGetValueBoolean (ifcfg, "CONNECTION_METERED", -1)) { case TRUE: g_object_set (s_con, NM_SETTING_CONNECTION_METERED, NM_METERED_YES, NULL); break; @@ -923,7 +931,7 @@ make_ip4_setting (shvarFile *ifcfg, * specified is DEFROUTE=yes which means that this connection can be used * as a default route */ - never_default = !svTrueValue (ifcfg, "DEFROUTE", TRUE); + never_default = !svGetValueBoolean (ifcfg, "DEFROUTE", TRUE); /* Then check if GATEWAYDEV; it's global and overrides DEFROUTE */ network_ifcfg = svOpenFile (network_file, NULL); @@ -998,10 +1006,10 @@ make_ip4_setting (shvarFile *ifcfg, g_object_set (s_ip4, NM_SETTING_IP_CONFIG_METHOD, method, - NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS, !svTrueValue (ifcfg, "PEERDNS", TRUE), - NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES, !svTrueValue (ifcfg, "PEERROUTES", TRUE), + NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS, !svGetValueBoolean (ifcfg, "PEERDNS", TRUE), + NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES, !svGetValueBoolean (ifcfg, "PEERROUTES", TRUE), NM_SETTING_IP_CONFIG_NEVER_DEFAULT, never_default, - NM_SETTING_IP_CONFIG_MAY_FAIL, !svTrueValue (ifcfg, "IPV4_FAILURE_FATAL", FALSE), + NM_SETTING_IP_CONFIG_MAY_FAIL, !svGetValueBoolean (ifcfg, "IPV4_FAILURE_FATAL", FALSE), NM_SETTING_IP_CONFIG_ROUTE_METRIC, svGetValueInt64 (ifcfg, "IPV4_ROUTE_METRIC", 10, -1, G_MAXUINT32, -1), NULL); @@ -1017,7 +1025,7 @@ make_ip4_setting (shvarFile *ifcfg, g_free (value); g_object_set (s_ip4, - NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME, svTrueValue (ifcfg, "DHCP_SEND_HOSTNAME", TRUE), + NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME, svGetValueBoolean (ifcfg, "DHCP_SEND_HOSTNAME", TRUE), NM_SETTING_IP4_CONFIG_DHCP_TIMEOUT, svGetValueInt64 (ifcfg, "IPV4_DHCP_TIMEOUT", 10, 0, G_MAXUINT32, 0), NULL); @@ -1318,7 +1326,7 @@ make_ip6_setting (shvarFile *ifcfg, * specified is IPV6_DEFROUTE=yes which means that this connection can be used * as a default route */ - never_default = !svTrueValue (ifcfg, "IPV6_DEFROUTE", TRUE); + never_default = !svGetValueBoolean (ifcfg, "IPV6_DEFROUTE", TRUE); /* Then check if IPV6_DEFAULTGW or IPV6_DEFAULTDEV is specified; * they are global and override IPV6_DEFROUTE @@ -1358,11 +1366,11 @@ make_ip6_setting (shvarFile *ifcfg, /* Find out method property */ /* Is IPV6 enabled? Set method to "ignored", when not enabled */ str_value = svGetValue (ifcfg, "IPV6INIT", FALSE); - ipv6init = svTrueValue (ifcfg, "IPV6INIT", FALSE); + ipv6init = svGetValueBoolean (ifcfg, "IPV6INIT", FALSE); if (!str_value) { network_ifcfg = svOpenFile (network_file, NULL); if (network_ifcfg) { - ipv6init = svTrueValue (network_ifcfg, "IPV6INIT", FALSE); + ipv6init = svGetValueBoolean (network_ifcfg, "IPV6INIT", FALSE); svCloseFile (network_ifcfg); } } @@ -1371,9 +1379,9 @@ make_ip6_setting (shvarFile *ifcfg, if (!ipv6init) method = NM_SETTING_IP6_CONFIG_METHOD_IGNORE; /* IPv6 is disabled */ else { - ipv6forwarding = svTrueValue (ifcfg, "IPV6FORWARDING", FALSE); - ipv6_autoconf = svTrueValue (ifcfg, "IPV6_AUTOCONF", !ipv6forwarding); - dhcp6 = svTrueValue (ifcfg, "DHCPV6C", FALSE); + ipv6forwarding = svGetValueBoolean (ifcfg, "IPV6FORWARDING", FALSE); + ipv6_autoconf = svGetValueBoolean (ifcfg, "IPV6_AUTOCONF", !ipv6forwarding); + dhcp6 = svGetValueBoolean (ifcfg, "DHCPV6C", FALSE); if (ipv6_autoconf) method = NM_SETTING_IP6_CONFIG_METHOD_AUTO; @@ -1395,12 +1403,12 @@ make_ip6_setting (shvarFile *ifcfg, /* Read IPv6 Privacy Extensions configuration */ ip6_privacy_str = svGetValue (ifcfg, "IPV6_PRIVACY", FALSE); if (ip6_privacy_str) { - ip6_privacy = svTrueValue (ifcfg, "IPV6_PRIVACY", FALSE); + ip6_privacy = svGetValueBoolean (ifcfg, "IPV6_PRIVACY", FALSE); if (!ip6_privacy) ip6_privacy = g_strcmp0 (ip6_privacy_str, "rfc4941") == 0 || g_strcmp0 (ip6_privacy_str, "rfc3041") == 0; } - ip6_privacy_prefer_public_ip = svTrueValue (ifcfg, "IPV6_PRIVACY_PREFER_PUBLIC_IP", FALSE); + ip6_privacy_prefer_public_ip = svGetValueBoolean (ifcfg, "IPV6_PRIVACY_PREFER_PUBLIC_IP", FALSE); ip6_privacy_val = ip6_privacy_str ? (ip6_privacy ? (ip6_privacy_prefer_public_ip ? NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR : NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR) : @@ -1410,10 +1418,10 @@ make_ip6_setting (shvarFile *ifcfg, g_object_set (s_ip6, NM_SETTING_IP_CONFIG_METHOD, method, - NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS, !svTrueValue (ifcfg, "IPV6_PEERDNS", TRUE), - NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES, !svTrueValue (ifcfg, "IPV6_PEERROUTES", TRUE), + NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS, !svGetValueBoolean (ifcfg, "IPV6_PEERDNS", TRUE), + NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES, !svGetValueBoolean (ifcfg, "IPV6_PEERROUTES", TRUE), NM_SETTING_IP_CONFIG_NEVER_DEFAULT, never_default, - NM_SETTING_IP_CONFIG_MAY_FAIL, !svTrueValue (ifcfg, "IPV6_FAILURE_FATAL", FALSE), + NM_SETTING_IP_CONFIG_MAY_FAIL, !svGetValueBoolean (ifcfg, "IPV6_FAILURE_FATAL", FALSE), NM_SETTING_IP_CONFIG_ROUTE_METRIC, svGetValueInt64 (ifcfg, "IPV6_ROUTE_METRIC", 10, -1, G_MAXUINT32, -1), NM_SETTING_IP6_CONFIG_IP6_PRIVACY, ip6_privacy_val, @@ -1617,11 +1625,11 @@ read_dcb_flags (shvarFile *ifcfg, DcbFlagsProperty *property) { NMSettingDcbFlags flags = NM_SETTING_DCB_FLAG_NONE; - if (svTrueValue (ifcfg, property->enable_key, FALSE)) + if (svGetValueBoolean (ifcfg, property->enable_key, FALSE)) flags |= NM_SETTING_DCB_FLAG_ENABLE; - if (svTrueValue (ifcfg, property->advertise_key, FALSE)) + if (svGetValueBoolean (ifcfg, property->advertise_key, FALSE)) flags |= NM_SETTING_DCB_FLAG_ADVERTISE; - if (svTrueValue (ifcfg, property->willing_key, FALSE)) + if (svGetValueBoolean (ifcfg, property->willing_key, FALSE)) flags |= NM_SETTING_DCB_FLAG_WILLING; return flags; @@ -1851,7 +1859,7 @@ make_dcb_setting (shvarFile *ifcfg, g_return_val_if_fail (out_setting != NULL, FALSE); - dcb_on = !!svTrueValue (ifcfg, "DCB", FALSE); + dcb_on = !!svGetValueBoolean (ifcfg, "DCB", FALSE); if (!dcb_on) return TRUE; @@ -2649,7 +2657,7 @@ eap_peap_reader (const char *eap_method, } } - if (svTrueValue (ifcfg, "IEEE_8021X_PEAP_FORCE_NEW_LABEL", FALSE)) + if (svGetValueBoolean (ifcfg, "IEEE_8021X_PEAP_FORCE_NEW_LABEL", FALSE)) g_object_set (s_8021x, NM_SETTING_802_1X_PHASE1_PEAPLABEL, "1", NULL); anon_ident = svGetValue (ifcfg, "IEEE_8021X_ANON_IDENTITY", FALSE); @@ -3090,9 +3098,9 @@ make_wpa_setting (shvarFile *ifcfg, allow_wpa = svGetValue (ifcfg, "WPA_ALLOW_WPA", FALSE); allow_rsn = svGetValue (ifcfg, "WPA_ALLOW_WPA2", FALSE); - if (allow_wpa && svTrueValue (ifcfg, "WPA_ALLOW_WPA", TRUE)) + if (allow_wpa && svGetValueBoolean (ifcfg, "WPA_ALLOW_WPA", TRUE)) nm_setting_wireless_security_add_proto (wsec, "wpa"); - if (allow_rsn && svTrueValue (ifcfg, "WPA_ALLOW_WPA2", TRUE)) + if (allow_rsn && svGetValueBoolean (ifcfg, "WPA_ALLOW_WPA2", TRUE)) nm_setting_wireless_security_add_proto (wsec, "rsn"); /* If neither WPA_ALLOW_WPA or WPA_ALLOW_WPA2 were present, default @@ -3462,12 +3470,12 @@ make_wireless_setting (shvarFile *ifcfg, g_object_set (s_wireless, NM_SETTING_WIRELESS_HIDDEN, - svTrueValue (ifcfg, "SSID_HIDDEN", FALSE), + svGetValueBoolean (ifcfg, "SSID_HIDDEN", FALSE), NULL); g_object_set (s_wireless, NM_SETTING_WIRELESS_POWERSAVE, - svTrueValue (ifcfg, "POWERSAVE", FALSE) ? 1 : 0, + svGetValueBoolean (ifcfg, "POWERSAVE", FALSE) ? 1 : 0, NULL); return NM_SETTING (s_wireless); @@ -3905,12 +3913,12 @@ make_infiniband_setting (shvarFile *ifcfg, g_free (value); } - if (svTrueValue (ifcfg, "CONNECTED_MODE", FALSE)) + if (svGetValueBoolean (ifcfg, "CONNECTED_MODE", FALSE)) g_object_set (s_infiniband, NM_SETTING_INFINIBAND_TRANSPORT_MODE, "connected", NULL); else g_object_set (s_infiniband, NM_SETTING_INFINIBAND_TRANSPORT_MODE, "datagram", NULL); - if (svTrueValue (ifcfg, "PKEY", FALSE)) { + if (svGetValueBoolean (ifcfg, "PKEY", FALSE)) { int p_key; char *parent; @@ -4447,7 +4455,7 @@ is_bond_device (const char *name, shvarFile *parsed) g_return_val_if_fail (name != NULL, FALSE); g_return_val_if_fail (parsed != NULL, FALSE); - if (svTrueValue (parsed, "BONDING_MASTER", FALSE)) + if (svGetValueBoolean (parsed, "BONDING_MASTER", FALSE)) return TRUE; /* XXX: Check for "bond[\d]+"? */ @@ -4461,7 +4469,7 @@ is_vlan_device (const char *name, shvarFile *parsed) g_return_val_if_fail (name != NULL, FALSE); g_return_val_if_fail (parsed != NULL, FALSE); - if (svTrueValue (parsed, "VLAN", FALSE)) + if (svGetValueBoolean (parsed, "VLAN", FALSE)) return TRUE; return FALSE; @@ -4595,7 +4603,7 @@ make_vlan_setting (shvarFile *ifcfg, g_object_set (s_vlan, NM_SETTING_VLAN_PARENT, parent, NULL); g_clear_pointer (&parent, g_free); - if (svTrueValue (ifcfg, "REORDER_HDR", FALSE)) + if (svGetValueBoolean (ifcfg, "REORDER_HDR", FALSE)) vlan_flags |= NM_VLAN_FLAG_REORDER_HEADERS; value = svGetValue (ifcfg, "VLAN_FLAGS", FALSE); @@ -4811,7 +4819,7 @@ connection_from_file_full (const char *filename, if (!parsed) return NULL; - if (!svTrueValue (parsed, "NM_CONTROLLED", TRUE)) { + if (!svGetValueBoolean (parsed, "NM_CONTROLLED", TRUE)) { g_assert (out_unhandled != NULL); connection = create_unhandled_connection (filename, parsed, "unmanaged", out_unhandled); @@ -4890,7 +4898,7 @@ connection_from_file_full (const char *filename, } } - if (svTrueValue (parsed, "BONDING_MASTER", FALSE) && + if (svGetValueBoolean (parsed, "BONDING_MASTER", FALSE) && strcasecmp (type, TYPE_BOND)) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "BONDING_MASTER=yes key only allowed in TYPE=bond connections"); diff --git a/src/settings/plugins/ifcfg-rh/shvar.c b/src/settings/plugins/ifcfg-rh/shvar.c index 225e3b995b..ffb0f8f7da 100644 --- a/src/settings/plugins/ifcfg-rh/shvar.c +++ b/src/settings/plugins/ifcfg-rh/shvar.c @@ -298,35 +298,55 @@ svGetValueFull (shvarFile *s, const char *key, gboolean verbatim) return value; } -/* return TRUE if resolves to any truth value (e.g. "yes", "true", "y", "t", "1") - * return FALSE if resolves to any non-truth value (e.g. "no", "false", "n", "f", "0") - * return otherwise +/** + * svParseBoolean: + * @value: the input string + * @fallback: the fallback value + * + * Parses a string and returns the boolean value it contains or, + * in case no valid value is found, the fallback value. Valid values + * are: "yes", "true", "t", "y", "1" and "no", "false", "f", "n", "0". + * + * Returns: the parsed boolean value or @fallback. */ gint -svTrueValue (shvarFile *s, const char *key, gint def) +svParseBoolean (const char *value, gint fallback) { - char *tmp; - gint returnValue = def; + if (!value) + return fallback; + + if ( !g_ascii_strcasecmp ("yes", value) + || !g_ascii_strcasecmp ("true", value) + || !g_ascii_strcasecmp ("t", value) + || !g_ascii_strcasecmp ("y", value) + || !g_ascii_strcasecmp ("1", value)) + return TRUE; + else if ( !g_ascii_strcasecmp ("no", value) + || !g_ascii_strcasecmp ("false", value) + || !g_ascii_strcasecmp ("f", value) + || !g_ascii_strcasecmp ("n", value) + || !g_ascii_strcasecmp ("0", value)) + return FALSE; + + return fallback; +} + +/* svGetValueBoolean: + * @s: fhe file + * @key: the name of the key to read + * @fallback: the fallback value in any error case + * + * Reads a value @key and converts it to a boolean using svParseBoolean(). + * + * Returns: the parsed boolean value or @fallback. + */ +gint +svGetValueBoolean (shvarFile *s, const char *key, gint fallback) +{ + gs_free char *tmp = NULL; tmp = svGetValue (s, key, FALSE); - if (!tmp) - return returnValue; - - if ( !g_ascii_strcasecmp ("yes", tmp) - || !g_ascii_strcasecmp ("true", tmp) - || !g_ascii_strcasecmp ("t", tmp) - || !g_ascii_strcasecmp ("y", tmp) - || !g_ascii_strcasecmp ("1", tmp)) - returnValue = TRUE; - else if ( !g_ascii_strcasecmp ("no", tmp) - || !g_ascii_strcasecmp ("false", tmp) - || !g_ascii_strcasecmp ("f", tmp) - || !g_ascii_strcasecmp ("n", tmp) - || !g_ascii_strcasecmp ("0", tmp)) - returnValue = FALSE; - - g_free (tmp); - return returnValue; + return svParseBoolean (tmp, fallback); } /* svGetValueInt64: diff --git a/src/settings/plugins/ifcfg-rh/shvar.h b/src/settings/plugins/ifcfg-rh/shvar.h index 876be3ade0..d51e07685f 100644 --- a/src/settings/plugins/ifcfg-rh/shvar.h +++ b/src/settings/plugins/ifcfg-rh/shvar.h @@ -58,11 +58,13 @@ shvarFile *svOpenFile (const char *name, GError **error); char *svGetValue (shvarFile *s, const char *key, gboolean verbatim); char *svGetValueFull (shvarFile *s, const char *key, gboolean verbatim); +gint svParseBoolean (const char *value, gint def); + /* return TRUE if resolves to any truth value (e.g. "yes", "y", "true") * return FALSE if resolves to any non-truth value (e.g. "no", "n", "false") * return otherwise */ -gint svTrueValue (shvarFile *s, const char *key, gint def); +gint svGetValueBoolean (shvarFile *s, const char *key, gint def); gint64 svGetValueInt64 (shvarFile *s, const char *key, guint base, gint64 min, gint64 max, gint64 fallback); diff --git a/src/settings/plugins/ifcfg-rh/writer.c b/src/settings/plugins/ifcfg-rh/writer.c index e6e68bce5f..376f38c0bd 100644 --- a/src/settings/plugins/ifcfg-rh/writer.c +++ b/src/settings/plugins/ifcfg-rh/writer.c @@ -1772,6 +1772,18 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg) FALSE); } + switch (nm_setting_connection_get_lldp (s_con)) { + case NM_SETTING_CONNECTION_LLDP_ENABLE_RX: + tmp = "rx"; + break; + case NM_SETTING_CONNECTION_LLDP_DISABLE: + tmp = "no"; + break; + default: + tmp = NULL; + } + svSetValue (ifcfg, "LLDP", tmp, FALSE); + /* Permissions */ svSetValue (ifcfg, "USERS", NULL, FALSE); n = nm_setting_connection_get_num_permissions (s_con);