From d9143d851de1870c46058d95656dde11b9bf892b Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Wed, 20 Mar 2019 09:50:06 +0100 Subject: [PATCH 01/13] libnm-core: add missing notify in sriov setting --- libnm-core/nm-setting-sriov.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libnm-core/nm-setting-sriov.c b/libnm-core/nm-setting-sriov.c index 9c7306e011..8fe4961135 100644 --- a/libnm-core/nm-setting-sriov.c +++ b/libnm-core/nm-setting-sriov.c @@ -894,8 +894,10 @@ _nm_setting_sriov_sort_vfs (NMSettingSriov *setting) } } - if (need_sort) + if (need_sort) { g_ptr_array_sort (setting->vfs, vf_index_compare); + _notify (setting, PROP_VFS); + } return need_sort; } From 2691fd279d0213efd680689fc7e787bbd95a84af Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Tue, 19 Mar 2019 15:04:54 +0100 Subject: [PATCH 02/13] libnm-core: fix typos in API documentation --- libnm-core/nm-setting-sriov.c | 2 +- libnm-core/nm-utils.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libnm-core/nm-setting-sriov.c b/libnm-core/nm-setting-sriov.c index 8fe4961135..b662ca2cf6 100644 --- a/libnm-core/nm-setting-sriov.c +++ b/libnm-core/nm-setting-sriov.c @@ -94,7 +94,7 @@ _vf_vlan_create_hash (void) } /** - * nm_srio_vf_new: + * nm_sriov_vf_new: * @index: the VF index * * Creates a new #NMSriovVF object. diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index 9f3b209ccf..d662ce7556 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -2439,7 +2439,7 @@ _tc_read_common_opts (const char *str, * @str: the string representation of a qdisc * @error: location of the error * - * Parces the tc style string qdisc representation of the queueing + * Parses the tc style string qdisc representation of the queueing * discipline to a %NMTCQdisc instance. Supports a subset of the tc language. * * Returns: the %NMTCQdisc or %NULL @@ -2554,7 +2554,7 @@ nm_utils_tc_action_to_str (NMTCAction *action, GError **error) * @str: the string representation of a action * @error: location of the error * - * Parces the tc style string action representation of the queueing + * Parses the tc style string action representation of the queueing * discipline to a %NMTCAction instance. Supports a subset of the tc language. * * Returns: the %NMTCAction or %NULL @@ -2707,7 +2707,7 @@ static const NMVariantAttributeSpec * const tc_tfilter_attribute_spec[] = { * @str: the string representation of a tfilter * @error: location of the error * - * Parces the tc style string tfilter representation of the queueing + * Parses the tc style string tfilter representation of the queueing * discipline to a %NMTCTfilter instance. Supports a subset of the tc language. * * Returns: the %NMTCTfilter or %NULL From 6f8beac4cfd7648a7729f78c5641fc8c1399cdd2 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Tue, 19 Mar 2019 18:38:31 +0100 Subject: [PATCH 03/13] keyfile: fix memory leaks The GPtrArray was leaked when the group did not have keys. --- libnm-core/nm-keyfile.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/libnm-core/nm-keyfile.c b/libnm-core/nm-keyfile.c index 05c6bf9710..a4b0064396 100644 --- a/libnm-core/nm-keyfile.c +++ b/libnm-core/nm-keyfile.c @@ -1522,17 +1522,17 @@ static void qdisc_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key) { const char *setting_name = nm_setting_get_name (setting); - GPtrArray *qdiscs; + gs_unref_ptrarray GPtrArray *qdiscs = NULL; gs_strfreev char **keys = NULL; gsize n_keys = 0; int i; - qdiscs = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_tc_qdisc_unref); - keys = nm_keyfile_plugin_kf_get_keys (info->keyfile, setting_name, &n_keys, NULL); if (n_keys == 0) return; + qdiscs = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_tc_qdisc_unref); + for (i = 0; i < n_keys; i++) { NMTCQdisc *qdisc; const char *qdisc_parent; @@ -1562,25 +1562,23 @@ qdisc_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key) if (qdiscs->len >= 1) g_object_set (setting, key, qdiscs, NULL); - - g_ptr_array_unref (qdiscs); } static void tfilter_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key) { const char *setting_name = nm_setting_get_name (setting); - GPtrArray *tfilters; + gs_unref_ptrarray GPtrArray *tfilters = NULL; gs_strfreev char **keys = NULL; gsize n_keys = 0; int i; - tfilters = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_tc_tfilter_unref); - keys = nm_keyfile_plugin_kf_get_keys (info->keyfile, setting_name, &n_keys, NULL); if (n_keys == 0) return; + tfilters = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_tc_tfilter_unref); + for (i = 0; i < n_keys; i++) { NMTCTfilter *tfilter; const char *tfilter_parent; @@ -1610,8 +1608,6 @@ tfilter_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key) if (tfilters->len >= 1) g_object_set (setting, key, tfilters, NULL); - - g_ptr_array_unref (tfilters); } /*****************************************************************************/ From 96fab7b46258e2c292deae15cfbd380a0a255208 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Fri, 15 Mar 2019 08:53:37 +0100 Subject: [PATCH 04/13] all: add vlan-filtering and vlan-default-pvid bridge properties --- clients/common/nm-meta-setting-desc.c | 6 + clients/common/settings-docs.h.in | 2 + libnm-core/nm-setting-bridge.c | 95 +++++++++++++++ libnm-core/nm-setting-bridge.h | 6 + libnm/libnm.ver | 6 + src/devices/nm-device-bridge.c | 108 +++++++++++++++--- .../plugins/ifcfg-rh/nms-ifcfg-rh-reader.c | 2 + .../plugins/ifcfg-rh/nms-ifcfg-rh-writer.c | 14 +++ .../network-scripts/ifcfg-test-bridge-main | 2 +- .../plugins/ifcfg-rh/tests/test-ifcfg-rh.c | 4 + 10 files changed, 228 insertions(+), 17 deletions(-) diff --git a/clients/common/nm-meta-setting-desc.c b/clients/common/nm-meta-setting-desc.c index 0529420118..bc2cdafc30 100644 --- a/clients/common/nm-meta-setting-desc.c +++ b/clients/common/nm-meta-setting-desc.c @@ -4808,6 +4808,12 @@ static const NMMetaPropertyInfo *const property_infos_BRIDGE[] = { .prompt = N_("Enable IGMP snooping [no]"), .property_type = &_pt_gobject_bool, ), + PROPERTY_INFO_WITH_DESC (NM_SETTING_BRIDGE_VLAN_FILTERING, + .property_type = &_pt_gobject_bool, + ), + PROPERTY_INFO_WITH_DESC (NM_SETTING_BRIDGE_VLAN_DEFAULT_PVID, + .property_type = &_pt_gobject_int, + ), NULL }; diff --git a/clients/common/settings-docs.h.in b/clients/common/settings-docs.h.in index 7b958faae9..4a0da22fac 100644 --- a/clients/common/settings-docs.h.in +++ b/clients/common/settings-docs.h.in @@ -118,6 +118,8 @@ #define DESCRIBE_DOC_NM_SETTING_BRIDGE_MULTICAST_SNOOPING N_("Controls whether IGMP snooping is enabled for this bridge. Note that if snooping was automatically disabled due to hash collisions, the system may refuse to enable the feature until the collisions are resolved.") #define DESCRIBE_DOC_NM_SETTING_BRIDGE_PRIORITY N_("Sets the Spanning Tree Protocol (STP) priority for this bridge. Lower values are \"better\"; the lowest priority bridge will be elected the root bridge.") #define DESCRIBE_DOC_NM_SETTING_BRIDGE_STP N_("Controls whether Spanning Tree Protocol (STP) is enabled for this bridge.") +#define DESCRIBE_DOC_NM_SETTING_BRIDGE_VLAN_DEFAULT_PVID N_("The default PVID for the ports of the bridge, that is the VLAN id assigned to incoming untagged frames.") +#define DESCRIBE_DOC_NM_SETTING_BRIDGE_VLAN_FILTERING N_("Control whether VLAN filtering is enabled on the bridge.") #define DESCRIBE_DOC_NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE N_("Enables or disables \"hairpin mode\" for the port, which allows frames to be sent back out through the port the frame was received on.") #define DESCRIBE_DOC_NM_SETTING_BRIDGE_PORT_PATH_COST N_("The Spanning Tree Protocol (STP) port cost for destinations via this port.") #define DESCRIBE_DOC_NM_SETTING_BRIDGE_PORT_PRIORITY N_("The Spanning Tree Protocol (STP) priority of this bridge port.") diff --git a/libnm-core/nm-setting-bridge.c b/libnm-core/nm-setting-bridge.c index e418013cb6..93c1f102d1 100644 --- a/libnm-core/nm-setting-bridge.c +++ b/libnm-core/nm-setting-bridge.c @@ -50,6 +50,8 @@ NM_GOBJECT_PROPERTIES_DEFINE_BASE ( PROP_AGEING_TIME, PROP_GROUP_FORWARD_MASK, PROP_MULTICAST_SNOOPING, + PROP_VLAN_FILTERING, + PROP_VLAN_DEFAULT_PVID, ); typedef struct { @@ -62,6 +64,8 @@ typedef struct { guint32 ageing_time; guint16 group_forward_mask; gboolean multicast_snooping; + gboolean vlan_filtering; + guint16 vlan_default_pvid; } NMSettingBridgePrivate; G_DEFINE_TYPE (NMSettingBridge, nm_setting_bridge, NM_TYPE_SETTING) @@ -200,6 +204,38 @@ nm_setting_bridge_get_multicast_snooping (NMSettingBridge *setting) return NM_SETTING_BRIDGE_GET_PRIVATE (setting)->multicast_snooping; } +/** + * nm_setting_bridge_get_vlan_filtering: + * @setting: the #NMSettingBridge + * + * Returns: the #NMSettingBridge:vlan-filtering property of the setting + * + * Since: 1.18 + **/ +gboolean +nm_setting_bridge_get_vlan_filtering (NMSettingBridge *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_BRIDGE (setting), FALSE); + + return NM_SETTING_BRIDGE_GET_PRIVATE (setting)->vlan_filtering; +} + +/** + * nm_setting_bridge_get_vlan_default_pvid: + * @setting: the #NMSettingBridge + * + * Returns: the #NMSettingBridge:vlan-default-pvid property of the setting + * + * Since: 1.18 + **/ +guint16 +nm_setting_bridge_get_vlan_default_pvid (NMSettingBridge *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_BRIDGE (setting), 1); + + return NM_SETTING_BRIDGE_GET_PRIVATE (setting)->vlan_default_pvid; +} + static gboolean check_range (guint32 val, guint32 min, @@ -318,6 +354,12 @@ get_property (GObject *object, guint prop_id, case PROP_MULTICAST_SNOOPING: g_value_set_boolean (value, priv->multicast_snooping); break; + case PROP_VLAN_FILTERING: + g_value_set_boolean (value, priv->vlan_filtering); + break; + case PROP_VLAN_DEFAULT_PVID: + g_value_set_uint (value, priv->vlan_default_pvid); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -360,6 +402,12 @@ set_property (GObject *object, guint prop_id, case PROP_MULTICAST_SNOOPING: priv->multicast_snooping = g_value_get_boolean (value); break; + case PROP_VLAN_FILTERING: + priv->vlan_filtering = g_value_get_boolean (value); + break; + case PROP_VLAN_DEFAULT_PVID: + priv->vlan_default_pvid = g_value_get_uint (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -627,6 +675,53 @@ nm_setting_bridge_class_init (NMSettingBridgeClass *klass) NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS); + /** + * NMSettingBridge:vlan-filtering: + * + * Control whether VLAN filtering is enabled on the bridge. + * + * Since: 1.18 + **/ + /* ---ifcfg-rh--- + * property: vlan-filtering + * variable: BRIDGING_OPTS: vlan_filtering= + * values: 0 or 1 + * default: 0 + * description: VLAN filtering support. + * ---end--- + */ + obj_properties[PROP_VLAN_FILTERING] = + g_param_spec_boolean (NM_SETTING_BRIDGE_VLAN_FILTERING, "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS); + + /** + * NMSettingBridge:vlan-default-pvid: + * + * The default PVID for the ports of the bridge, that is the VLAN id + * assigned to incoming untagged frames. + * + * Since: 1.18 + **/ + /* ---ifcfg-rh--- + * property: vlan-default-pvid + * variable: BRIDGING_OPTS: default_pvid= + * values: 0 - 4094 + * default: 1 + * description: default VLAN PVID. + * ---end--- + */ + obj_properties[PROP_VLAN_DEFAULT_PVID] = + g_param_spec_uint (NM_SETTING_BRIDGE_VLAN_DEFAULT_PVID, "", "", + 0, 4094, 1, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS); + /* ---dbus--- * property: interface-name * format: string diff --git a/libnm-core/nm-setting-bridge.h b/libnm-core/nm-setting-bridge.h index d640dde3d2..53bb5ad822 100644 --- a/libnm-core/nm-setting-bridge.h +++ b/libnm-core/nm-setting-bridge.h @@ -48,6 +48,8 @@ G_BEGIN_DECLS #define NM_SETTING_BRIDGE_AGEING_TIME "ageing-time" #define NM_SETTING_BRIDGE_GROUP_FORWARD_MASK "group-forward-mask" #define NM_SETTING_BRIDGE_MULTICAST_SNOOPING "multicast-snooping" +#define NM_SETTING_BRIDGE_VLAN_FILTERING "vlan-filtering" +#define NM_SETTING_BRIDGE_VLAN_DEFAULT_PVID "vlan-default-pvid" /** * NMSettingBridge: @@ -86,6 +88,10 @@ NM_AVAILABLE_IN_1_10 guint16 nm_setting_bridge_get_group_forward_mask (NMSettingBridge *setting); gboolean nm_setting_bridge_get_multicast_snooping (NMSettingBridge *setting); +NM_AVAILABLE_IN_1_18 +gboolean nm_setting_bridge_get_vlan_filtering (NMSettingBridge *setting); +NM_AVAILABLE_IN_1_18 +guint16 nm_setting_bridge_get_vlan_default_pvid (NMSettingBridge *setting); G_END_DECLS diff --git a/libnm/libnm.ver b/libnm/libnm.ver index ece9686efa..e3a146c21f 100644 --- a/libnm/libnm.ver +++ b/libnm/libnm.ver @@ -1518,3 +1518,9 @@ global: nm_wireguard_peer_set_public_key; nm_wireguard_peer_unref; } libnm_1_14_0; + +libnm_1_18_0 { +global: + nm_setting_bridge_get_vlan_filtering; + nm_setting_bridge_get_vlan_default_pvid; +} libnm_1_16_0; diff --git a/src/devices/nm-device-bridge.c b/src/devices/nm-device-bridge.c index 4c8921c071..9528a3af90 100644 --- a/src/devices/nm-device-bridge.c +++ b/src/devices/nm-device-bridge.c @@ -37,6 +37,7 @@ _LOG_DECLARE_SELF(NMDeviceBridge); struct _NMDeviceBridge { NMDevice parent; + bool vlan_configured:1; }; struct _NMDeviceBridgeClass { @@ -267,16 +268,6 @@ commit_option (NMDevice *device, NMSetting *setting, const Option *option, gbool nm_platform_sysctl_master_set_option (nm_device_get_platform (device), ifindex, option->sysname, value); } -static void -commit_master_options (NMDevice *device, NMSettingBridge *setting) -{ - const Option *option; - NMSetting *s = NM_SETTING (setting); - - for (option = master_options; option->name; option++) - commit_option (device, s, option, FALSE); -} - static void commit_slave_options (NMDevice *device, NMSettingBridgePort *setting) { @@ -396,22 +387,97 @@ master_update_slave_connection (NMDevice *device, return TRUE; } +static gboolean +bridge_set_vlan_options (NMDevice *device, NMSettingBridge *s_bridge) +{ + NMDeviceBridge *self = NM_DEVICE_BRIDGE (device); + gconstpointer hwaddr; + size_t length; + gboolean enabled; + guint16 pvid; + NMPlatform *plat; + int ifindex; + + if (self->vlan_configured) + return TRUE; + + plat = nm_device_get_platform (device); + ifindex = nm_device_get_ifindex (device); + enabled = nm_setting_bridge_get_vlan_filtering (s_bridge); + + if (!enabled) { + nm_platform_sysctl_master_set_option (plat, ifindex, "vlan_filtering", "0"); + nm_platform_sysctl_master_set_option (plat, ifindex, "default_pvid", "1"); + return TRUE; + } + + hwaddr = nm_platform_link_get_address (plat, ifindex, &length); + g_return_val_if_fail (length == ETH_ALEN, FALSE); + if (nm_utils_hwaddr_matches (hwaddr, ETH_ALEN, nm_ip_addr_zero.addr_eth, ETH_ALEN)) { + /* We need a non-zero MAC address to set the default pvid. + * Retry later. */ + return TRUE; + } + + self->vlan_configured = TRUE; + + /* Filtering must be disabled to change the default PVID */ + if (!nm_platform_sysctl_master_set_option (plat, ifindex, "vlan_filtering", "0")) + return FALSE; + + /* Clear the default PVID so that we later can force the re-creation of + * default PVID VLANs by writing the option again. */ + if (!nm_platform_sysctl_master_set_option (plat, ifindex, "default_pvid", "0")) + return FALSE; + + /* Now set the default PVID. After this point the kernel creates + * a PVID VLAN on each port, including the bridge itself. */ + pvid = nm_setting_bridge_get_vlan_default_pvid (s_bridge); + if (pvid) { + char value[32]; + + nm_sprintf_buf (value, "%u", pvid); + if (!nm_platform_sysctl_master_set_option (plat, ifindex, "default_pvid", value)) + return FALSE; + } + + if (!nm_platform_sysctl_master_set_option (plat, ifindex, "vlan_filtering", "1")) + return FALSE; + + return TRUE; +} + static NMActStageReturn act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason) { NMActStageReturn ret; - NMConnection *connection = nm_device_get_applied_connection (device); + NMConnection *connection; + NMSetting *s_bridge; + const Option *option; - g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE); + NM_DEVICE_BRIDGE (device)->vlan_configured = FALSE; ret = NM_DEVICE_CLASS (nm_device_bridge_parent_class)->act_stage1_prepare (device, out_failure_reason); if (ret != NM_ACT_STAGE_RETURN_SUCCESS) return ret; - if (!nm_device_hw_addr_set_cloned (device, nm_device_get_applied_connection (device), FALSE)) - return NM_ACT_STAGE_RETURN_FAILURE; + connection = nm_device_get_applied_connection (device); + g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE); + s_bridge = (NMSetting *) nm_connection_get_setting_bridge (connection); + g_return_val_if_fail (s_bridge, NM_ACT_STAGE_RETURN_FAILURE); - commit_master_options (device, nm_connection_get_setting_bridge (connection)); + if (!nm_device_hw_addr_set_cloned (device, connection, FALSE)) { + NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_CONFIG_FAILED); + return NM_ACT_STAGE_RETURN_FAILURE; + } + + for (option = master_options; option->name; option++) + commit_option (device, s_bridge, option, FALSE); + + if (!bridge_set_vlan_options (device, (NMSettingBridge *) s_bridge)) { + NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_CONFIG_FAILED); + return NM_ACT_STAGE_RETURN_FAILURE; + } return NM_ACT_STAGE_RETURN_SUCCESS; } @@ -457,12 +523,22 @@ enslave_slave (NMDevice *device, gboolean configure) { NMDeviceBridge *self = NM_DEVICE_BRIDGE (device); + NMConnection *master_connection; + NMSettingBridge *s_bridge; + NMSettingBridgePort *s_port; if (configure) { if (!nm_platform_link_enslave (nm_device_get_platform (device), nm_device_get_ip_ifindex (device), nm_device_get_ip_ifindex (slave))) return FALSE; - commit_slave_options (slave, nm_connection_get_setting_bridge_port (connection)); + master_connection = nm_device_get_applied_connection (device); + nm_assert (master_connection); + s_bridge = nm_connection_get_setting_bridge (master_connection); + nm_assert (s_bridge); + s_port = nm_connection_get_setting_bridge_port (connection); + + bridge_set_vlan_options (device, s_bridge); + commit_slave_options (slave, s_port); _LOGI (LOGD_BRIDGE, "attached bridge port %s", nm_device_get_ip_iface (slave)); diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c index 7779319b7f..d0f37ea95a 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c @@ -4958,6 +4958,8 @@ handle_bridge_option (NMSetting *setting, { "max_age", NM_SETTING_BRIDGE_MAX_AGE, BRIDGE_OPT_TYPE_OPTION, .only_with_stp = TRUE }, { "ageing_time", NM_SETTING_BRIDGE_AGEING_TIME, BRIDGE_OPT_TYPE_OPTION }, { "multicast_snooping", NM_SETTING_BRIDGE_MULTICAST_SNOOPING, BRIDGE_OPT_TYPE_OPTION }, + { "vlan_filtering", NM_SETTING_BRIDGE_VLAN_FILTERING, BRIDGE_OPT_TYPE_OPTION }, + { "default_pvid", NM_SETTING_BRIDGE_VLAN_DEFAULT_PVID, BRIDGE_OPT_TYPE_OPTION }, { "group_fwd_mask", NM_SETTING_BRIDGE_GROUP_FORWARD_MASK, BRIDGE_OPT_TYPE_OPTION }, { "priority", NM_SETTING_BRIDGE_PORT_PRIORITY, BRIDGE_OPT_TYPE_PORT_OPTION }, { "path_cost", NM_SETTING_BRIDGE_PORT_PATH_COST, BRIDGE_OPT_TYPE_PORT_OPTION }, diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c index ee7fd16157..f7fc36bfd6 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c @@ -1534,6 +1534,20 @@ write_bridge_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wire g_string_append_printf (opts, "multicast_snooping=%u", (guint32) b); } + b = nm_setting_bridge_get_vlan_filtering (s_bridge); + if (b != get_setting_default_boolean (NM_SETTING (s_bridge), NM_SETTING_BRIDGE_VLAN_FILTERING)) { + if (opts->len) + g_string_append_c (opts, ' '); + g_string_append_printf (opts, "vlan_filtering=%u", (guint32) b); + } + + i = nm_setting_bridge_get_vlan_default_pvid (s_bridge); + if (i != get_setting_default_uint (NM_SETTING (s_bridge), NM_SETTING_BRIDGE_VLAN_DEFAULT_PVID)) { + if (opts->len) + g_string_append_c (opts, ' '); + g_string_append_printf (opts, "default_pvid=%u", i); + } + if (opts->len) svSetValueStr (ifcfg, "BRIDGING_OPTS", opts->str); g_string_free (opts, TRUE); diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bridge-main b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bridge-main index 2bc987c240..1788efe263 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bridge-main +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bridge-main @@ -4,5 +4,5 @@ TYPE=Bridge BOOTPROTO=dhcp STP=on DELAY=2 -BRIDGING_OPTS="priority=32744 hello_time=7 max_age=39 ageing_time=235352 multicast_snooping=0 group_fwd_mask=24" +BRIDGING_OPTS="priority=32744 hello_time=7 max_age=39 ageing_time=235352 multicast_snooping=0 group_fwd_mask=24 vlan_filtering=1 default_pvid=99" MACADDR=00:16:41:11:22:33 diff --git a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c index b352fbfc7c..e504c7bbdf 100644 --- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c +++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c @@ -7507,6 +7507,8 @@ test_read_bridge_main (void) g_assert_cmpuint (nm_setting_bridge_get_ageing_time (s_bridge), ==, 235352); g_assert_cmpuint (nm_setting_bridge_get_group_forward_mask (s_bridge), ==, 24); g_assert (!nm_setting_bridge_get_multicast_snooping (s_bridge)); + g_assert_cmpint (nm_setting_bridge_get_vlan_filtering (s_bridge), ==, TRUE); + g_assert_cmpint (nm_setting_bridge_get_vlan_default_pvid (s_bridge), ==, 99); /* MAC address */ s_wired = nm_connection_get_setting_wired (connection); @@ -7554,6 +7556,8 @@ test_write_bridge_main (void) g_object_set (s_bridge, NM_SETTING_BRIDGE_MAC_ADDRESS, mac, NM_SETTING_BRIDGE_GROUP_FORWARD_MASK, 19008, + NM_SETTING_BRIDGE_VLAN_FILTERING, TRUE, + NM_SETTING_BRIDGE_VLAN_DEFAULT_PVID, 4000, NULL); /* IP4 setting */ From 1e5b0788bcc91e7998dfffda3a116735f90ed239 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Sat, 16 Mar 2019 17:21:35 +0100 Subject: [PATCH 05/13] libnm-core: add vlans property to bridge-port setting --- clients/common/settings-docs.h.in | 1 + libnm-core/nm-connection.c | 13 ++ libnm-core/nm-core-internal.h | 1 + libnm-core/nm-setting-bridge-port.c | 298 +++++++++++++++++++++++++++- libnm-core/nm-setting-bridge-port.h | 16 ++ libnm-core/nm-setting-bridge.c | 287 ++++++++++++++++++++++++++- libnm-core/nm-setting-bridge.h | 32 +++ libnm-core/nm-setting-private.h | 4 + libnm-core/nm-utils-private.h | 13 ++ libnm-core/nm-utils.c | 140 +++++++++++++ libnm/libnm.ver | 19 ++ shared/nm-libnm-core-utils.h | 4 + 12 files changed, 825 insertions(+), 3 deletions(-) diff --git a/clients/common/settings-docs.h.in b/clients/common/settings-docs.h.in index 4a0da22fac..3a66ae73ad 100644 --- a/clients/common/settings-docs.h.in +++ b/clients/common/settings-docs.h.in @@ -123,6 +123,7 @@ #define DESCRIBE_DOC_NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE N_("Enables or disables \"hairpin mode\" for the port, which allows frames to be sent back out through the port the frame was received on.") #define DESCRIBE_DOC_NM_SETTING_BRIDGE_PORT_PATH_COST N_("The Spanning Tree Protocol (STP) port cost for destinations via this port.") #define DESCRIBE_DOC_NM_SETTING_BRIDGE_PORT_PRIORITY N_("The Spanning Tree Protocol (STP) priority of this bridge port.") +#define DESCRIBE_DOC_NM_SETTING_BRIDGE_PORT_VLANS N_("Array of bridge VLAN objects. In addition to the VLANs specified here, the port will also have the default-pvid VLAN configured on the bridge by the bridge.vlan-default-pvid property. In nmcli the VLAN list can be specified with the following syntax: $vid [pvid] [untagged] [, $vid [pvid] [untagged]]...") #define DESCRIBE_DOC_NM_SETTING_CDMA_MTU N_("If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple frames.") #define DESCRIBE_DOC_NM_SETTING_CDMA_NUMBER N_("The number to dial to establish the connection to the CDMA-based mobile broadband network, if any. If not specified, the default number (#777) is used when required.") #define DESCRIBE_DOC_NM_SETTING_CDMA_PASSWORD N_("The password used to authenticate with the network, if required. Many providers do not require a password, or accept any password. But if a password is required, it is specified here.") diff --git a/libnm-core/nm-connection.c b/libnm-core/nm-connection.c index ea7730bf91..312898368e 100644 --- a/libnm-core/nm-connection.c +++ b/libnm-core/nm-connection.c @@ -1297,6 +1297,18 @@ _normalize_sriov_vf_order (NMConnection *self, GHashTable *parameters) return _nm_setting_sriov_sort_vfs (s_sriov); } +static gboolean +_normalize_bridge_port_vlan_order (NMConnection *self, GHashTable *parameters) +{ + NMSettingBridgePort *s_port; + + s_port = nm_connection_get_setting_bridge_port (self); + if (!s_port) + return FALSE; + + return _nm_setting_bridge_port_sort_vlans (s_port); +} + static gboolean _normalize_required_settings (NMConnection *self, GHashTable *parameters) { @@ -1645,6 +1657,7 @@ nm_connection_normalize (NMConnection *connection, was_modified |= _normalize_ovs_interface_type (connection, parameters); was_modified |= _normalize_ip_tunnel_wired_setting (connection, parameters); was_modified |= _normalize_sriov_vf_order (connection, parameters); + was_modified |= _normalize_bridge_port_vlan_order (connection, parameters); /* Verify anew. */ success = _nm_connection_verify (connection, error); diff --git a/libnm-core/nm-core-internal.h b/libnm-core/nm-core-internal.h index 394ecb5677..3859c2bb40 100644 --- a/libnm-core/nm-core-internal.h +++ b/libnm-core/nm-core-internal.h @@ -578,6 +578,7 @@ gboolean _nm_utils_dhcp_duid_valid (const char *duid, GBytes **out_duid_bin); /*****************************************************************************/ gboolean _nm_setting_sriov_sort_vfs (NMSettingSriov *setting); +gboolean _nm_setting_bridge_port_sort_vlans (NMSettingBridgePort *setting); /*****************************************************************************/ diff --git a/libnm-core/nm-setting-bridge-port.c b/libnm-core/nm-setting-bridge-port.c index 614c883cbd..404a486251 100644 --- a/libnm-core/nm-setting-bridge-port.c +++ b/libnm-core/nm-setting-bridge-port.c @@ -42,22 +42,60 @@ /*****************************************************************************/ -NM_GOBJECT_PROPERTIES_DEFINE_BASE ( +NM_GOBJECT_PROPERTIES_DEFINE (NMSettingBridgePort, PROP_PRIORITY, PROP_PATH_COST, PROP_HAIRPIN_MODE, + PROP_VLANS, ); typedef struct { guint16 priority; guint16 path_cost; gboolean hairpin_mode; + GPtrArray *vlans; } NMSettingBridgePortPrivate; G_DEFINE_TYPE (NMSettingBridgePort, nm_setting_bridge_port, NM_TYPE_SETTING) #define NM_SETTING_BRIDGE_PORT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_BRIDGE_PORT, NMSettingBridgePortPrivate)) +static int +vlan_ptr_cmp (gconstpointer a, gconstpointer b) +{ + const NMBridgeVlan *vlan_a = *(const NMBridgeVlan **) a; + const NMBridgeVlan *vlan_b = *(const NMBridgeVlan **) b; + + return nm_bridge_vlan_cmp (vlan_a, vlan_b); +} + +gboolean +_nm_setting_bridge_port_sort_vlans (NMSettingBridgePort *setting) +{ + NMSettingBridgePortPrivate *priv; + gboolean need_sort = FALSE; + guint i; + + priv = NM_SETTING_BRIDGE_PORT_GET_PRIVATE (setting); + + for (i = 1; i < priv->vlans->len; i++) { + NMBridgeVlan *vlan_prev = priv->vlans->pdata[i - 1]; + NMBridgeVlan *vlan = priv->vlans->pdata[i]; + + if (nm_bridge_vlan_cmp (vlan_prev, vlan) > 0) { + need_sort = TRUE; + break; + } + } + + if (need_sort) { + g_ptr_array_sort (priv->vlans, vlan_ptr_cmp); + _notify (setting, PROP_VLANS); + } + + return need_sort; +} + /*****************************************************************************/ /** @@ -102,11 +140,160 @@ nm_setting_bridge_port_get_hairpin_mode (NMSettingBridgePort *setting) return NM_SETTING_BRIDGE_PORT_GET_PRIVATE (setting)->hairpin_mode; } +/** + * nm_setting_bridge_port_add_vlan: + * @setting: the #NMSettingBridgePort + * @vlan: the vlan to add + * + * Appends a new vlan and associated information to the setting. The + * given vlan gets sealed and a reference to it is added. + * + * Since: 1.18 + **/ +void +nm_setting_bridge_port_add_vlan (NMSettingBridgePort *setting, + NMBridgeVlan *vlan) +{ + NMSettingBridgePortPrivate *priv; + + g_return_if_fail (NM_IS_SETTING_BRIDGE_PORT (setting)); + g_return_if_fail (vlan); + + priv = NM_SETTING_BRIDGE_PORT_GET_PRIVATE (setting); + + nm_bridge_vlan_seal (vlan); + nm_bridge_vlan_ref (vlan); + + g_ptr_array_add (priv->vlans, vlan); + _notify (setting, PROP_VLANS); +} + +/** + * nm_setting_bridge_port_get_num_vlans: + * @setting: the #NMSettingBridgePort + * + * Returns: the number of VLANs + * + * Since: 1.18 + **/ +guint +nm_setting_bridge_port_get_num_vlans (NMSettingBridgePort *setting) +{ + NMSettingBridgePortPrivate *priv; + + g_return_val_if_fail (NM_IS_SETTING_BRIDGE_PORT (setting), 0); + priv = NM_SETTING_BRIDGE_PORT_GET_PRIVATE (setting); + + return priv->vlans->len; +} + +/** + * nm_setting_bridge_port_get_vlan: + * @setting: the #NMSettingBridgePort + * @idx: index number of the VLAN to return + * + * Returns: (transfer none): the VLAN at index @idx + * + * Since: 1.18 + **/ +NMBridgeVlan * +nm_setting_bridge_port_get_vlan (NMSettingBridgePort *setting, guint idx) +{ + NMSettingBridgePortPrivate *priv; + + g_return_val_if_fail (NM_IS_SETTING_BRIDGE_PORT (setting), NULL); + priv = NM_SETTING_BRIDGE_PORT_GET_PRIVATE (setting); + + g_return_val_if_fail (idx < priv->vlans->len, NULL); + + return priv->vlans->pdata[idx]; +} + +/** + * nm_setting_bridge_port_remove_vlan: + * @setting: the #NMSettingBridgePort + * @idx: index number of the VLAN. + * + * Removes the vlan at index @idx. + * + * Since: 1.18 + **/ +void +nm_setting_bridge_port_remove_vlan (NMSettingBridgePort *setting, guint idx) +{ + NMSettingBridgePortPrivate *priv; + + g_return_if_fail (NM_IS_SETTING_BRIDGE_PORT (setting)); + priv = NM_SETTING_BRIDGE_PORT_GET_PRIVATE (setting); + + g_return_if_fail (idx < priv->vlans->len); + + g_ptr_array_remove_index (priv->vlans, idx); + _notify (setting, PROP_VLANS); +} + +/** + * nm_setting_bridge_port_remove_vlan_by_vid: + * @setting: the #NMSettingBridgePort + * @vid: the vlan index of the vlan to remove + * + * Removes the vlan vith id @vid. + * + * Returns: %TRUE if the vlan was found and removed; %FALSE otherwise + * + * Since: 1.18 + **/ +gboolean +nm_setting_bridge_port_remove_vlan_by_vid (NMSettingBridgePort *setting, + guint16 vid) +{ + NMSettingBridgePortPrivate *priv; + guint i; + + g_return_val_if_fail (NM_IS_SETTING_BRIDGE_PORT (setting), FALSE); + priv = NM_SETTING_BRIDGE_PORT_GET_PRIVATE (setting); + + for (i = 0; i < priv->vlans->len; i++) { + if (nm_bridge_vlan_get_vid (priv->vlans->pdata[i]) == vid) { + g_ptr_array_remove_index (priv->vlans, i); + _notify (setting, PROP_VLANS); + return TRUE; + } + } + return FALSE; +} + +/** + * nm_setting_bridge_port_clear_vlans: + * @setting: the #NMSettingBridgePort + * + * Removes all configured VLANs. + * + * Since: 1.18 + **/ +void +nm_setting_bridge_port_clear_vlans (NMSettingBridgePort *setting) +{ + NMSettingBridgePortPrivate *priv; + + g_return_if_fail (NM_IS_SETTING_BRIDGE_PORT (setting)); + priv = NM_SETTING_BRIDGE_PORT_GET_PRIVATE (setting); + + if (priv->vlans->len != 0) { + g_ptr_array_set_size (priv->vlans, 0); + _notify (setting, PROP_VLANS); + } +} + /*****************************************************************************/ static gboolean verify (NMSetting *setting, NMConnection *connection, GError **error) { + NMSettingBridgePortPrivate *priv; + + priv = NM_SETTING_BRIDGE_PORT_GET_PRIVATE (setting); + if (connection) { NMSettingConnection *s_con; const char *slave_type; @@ -136,9 +323,58 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) } } + if (!_nm_utils_bridge_vlan_verify_list (priv->vlans, + FALSE, + error, + NM_SETTING_BRIDGE_PORT_SETTING_NAME, + NM_SETTING_BRIDGE_PORT_VLANS)) + return FALSE; + + /* Failures from here on are NORMALIZABLE... */ + + if (!_nm_utils_bridge_vlan_verify_list (priv->vlans, + TRUE, + error, + NM_SETTING_BRIDGE_PORT_SETTING_NAME, + NM_SETTING_BRIDGE_PORT_VLANS)) + return NM_SETTING_VERIFY_NORMALIZABLE; + return TRUE; } +static NMTernary +compare_property (const NMSettInfoSetting *sett_info, + guint property_idx, + NMSetting *setting, + NMSetting *other, + NMSettingCompareFlags flags) +{ + NMSettingBridgePortPrivate *priv_a; + NMSettingBridgePortPrivate *priv_b; + guint i; + + if (nm_streq (sett_info->property_infos[property_idx].name, NM_SETTING_BRIDGE_PORT_VLANS)) { + if (other) { + priv_a = NM_SETTING_BRIDGE_PORT_GET_PRIVATE (setting); + priv_b = NM_SETTING_BRIDGE_PORT_GET_PRIVATE (other); + + if (priv_a->vlans->len != priv_b->vlans->len) + return FALSE; + for (i = 0; i < priv_a->vlans->len; i++) { + if (nm_bridge_vlan_cmp (priv_a->vlans->pdata[i], priv_b->vlans->pdata[i])) + return FALSE; + } + } + return TRUE; + } + + return NM_SETTING_CLASS (nm_setting_bridge_port_parent_class)->compare_property (sett_info, + property_idx, + setting, + other, + flags); +} + /*****************************************************************************/ static void @@ -157,6 +393,11 @@ get_property (GObject *object, guint prop_id, case PROP_HAIRPIN_MODE: g_value_set_boolean (value, priv->hairpin_mode); break; + case PROP_VLANS: + g_value_take_boxed (value, _nm_utils_copy_array (priv->vlans, + (NMUtilsCopyFunc) nm_bridge_vlan_ref, + (GDestroyNotify) nm_bridge_vlan_unref)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -179,6 +420,12 @@ set_property (GObject *object, guint prop_id, case PROP_HAIRPIN_MODE: priv->hairpin_mode = g_value_get_boolean (value); break; + case PROP_VLANS: + g_ptr_array_unref (priv->vlans); + priv->vlans = _nm_utils_copy_array (g_value_get_boxed (value), + (NMUtilsCopyFunc) _nm_bridge_vlan_dup_and_seal, + (GDestroyNotify) nm_bridge_vlan_unref); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -190,6 +437,9 @@ set_property (GObject *object, guint prop_id, static void nm_setting_bridge_port_init (NMSettingBridgePort *setting) { + NMSettingBridgePortPrivate *priv = NM_SETTING_BRIDGE_PORT_GET_PRIVATE (setting); + + priv->vlans = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_bridge_vlan_unref); } /** @@ -205,17 +455,30 @@ nm_setting_bridge_port_new (void) return (NMSetting *) g_object_new (NM_TYPE_SETTING_BRIDGE_PORT, NULL); } +static void +finalize (GObject *object) +{ + NMSettingBridgePortPrivate *priv = NM_SETTING_BRIDGE_PORT_GET_PRIVATE (object); + + g_ptr_array_unref (priv->vlans); + + G_OBJECT_CLASS (nm_setting_bridge_port_parent_class)->finalize (object); +} + static void nm_setting_bridge_port_class_init (NMSettingBridgePortClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); NMSettingClass *setting_class = NM_SETTING_CLASS (klass); + GArray *properties_override = _nm_sett_info_property_override_create_array (); g_type_class_add_private (klass, sizeof (NMSettingBridgePortPrivate)); + object_class->finalize = finalize; object_class->get_property = get_property; object_class->set_property = set_property; + setting_class->compare_property = compare_property; setting_class->verify = verify; /** @@ -280,7 +543,38 @@ nm_setting_bridge_port_class_init (NMSettingBridgePortClass *klass) NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS); + /** + * NMSettingBridgePort:vlans: (type GPtrArray(NMBridgeVlan)) + * + * Array of bridge VLAN objects. In addition to the VLANs + * specified here, the port will also have the default-pvid + * VLAN configured on the bridge by the bridge.vlan-default-pvid + * property. + * + * In nmcli the VLAN list can be specified with the following + * syntax: + * + * $vid [pvid] [untagged] [, $vid [pvid] [untagged]]... + * + * Since: 1.18 + **/ + obj_properties[PROP_VLANS] = + g_param_spec_boxed (NM_SETTING_BRIDGE_PORT_VLANS, "", "", + G_TYPE_PTR_ARRAY, + G_PARAM_READWRITE | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS); + + _properties_override_add_override (properties_override, + obj_properties[PROP_VLANS], + G_VARIANT_TYPE ("aa{sv}"), + _nm_utils_bridge_vlans_to_dbus, + _nm_utils_bridge_vlans_from_dbus, + NULL); + g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); - _nm_setting_class_commit (setting_class, NM_META_SETTING_TYPE_BRIDGE_PORT); + _nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_BRIDGE_PORT, + NULL, properties_override); + } diff --git a/libnm-core/nm-setting-bridge-port.h b/libnm-core/nm-setting-bridge-port.h index 0ff6d9f13b..b1b1c8f40c 100644 --- a/libnm-core/nm-setting-bridge-port.h +++ b/libnm-core/nm-setting-bridge-port.h @@ -27,6 +27,7 @@ #endif #include "nm-setting.h" +#include "nm-setting-bridge.h" G_BEGIN_DECLS @@ -42,6 +43,7 @@ G_BEGIN_DECLS #define NM_SETTING_BRIDGE_PORT_PRIORITY "priority" #define NM_SETTING_BRIDGE_PORT_PATH_COST "path-cost" #define NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE "hairpin-mode" +#define NM_SETTING_BRIDGE_PORT_VLANS "vlans" /** * NMSettingBridgePort: @@ -69,6 +71,20 @@ guint16 nm_setting_bridge_port_get_path_cost (NMSettingBridgePort *settin gboolean nm_setting_bridge_port_get_hairpin_mode (NMSettingBridgePort *setting); +NM_AVAILABLE_IN_1_18 +void nm_setting_bridge_port_add_vlan (NMSettingBridgePort *setting, + NMBridgeVlan *vlan); +NM_AVAILABLE_IN_1_18 +guint nm_setting_bridge_port_get_num_vlans (NMSettingBridgePort *setting); +NM_AVAILABLE_IN_1_18 +NMBridgeVlan *nm_setting_bridge_port_get_vlan (NMSettingBridgePort *setting, guint idx); +NM_AVAILABLE_IN_1_18 +void nm_setting_bridge_port_remove_vlan (NMSettingBridgePort *setting, guint idx); +NM_AVAILABLE_IN_1_18 +gboolean nm_setting_bridge_port_remove_vlan_by_vid (NMSettingBridgePort *setting, guint16 vid); +NM_AVAILABLE_IN_1_18 +void nm_setting_bridge_port_clear_vlans (NMSettingBridgePort *setting); + G_END_DECLS #endif /* __NM_SETTING_BRIDGE_PORT_H__ */ diff --git a/libnm-core/nm-setting-bridge.c b/libnm-core/nm-setting-bridge.c index 93c1f102d1..26f2fe305d 100644 --- a/libnm-core/nm-setting-bridge.c +++ b/libnm-core/nm-setting-bridge.c @@ -74,6 +74,291 @@ G_DEFINE_TYPE (NMSettingBridge, nm_setting_bridge, NM_TYPE_SETTING) /*****************************************************************************/ +G_DEFINE_BOXED_TYPE (NMBridgeVlan, nm_bridge_vlan, _nm_bridge_vlan_dup, nm_bridge_vlan_unref) + +struct _NMBridgeVlan { + guint refcount; + guint16 vid; + bool untagged:1; + bool pvid:1; + bool sealed:1; +}; + +static gboolean +NM_IS_BRIDGE_VLAN (const NMBridgeVlan *self, gboolean also_sealed) +{ + return self + && self->refcount > 0 + && (also_sealed || !self->sealed); +} + +/** + * nm_bridge_vlan_new: + * @vid: the VLAN id, must be between 1 and 4094. + * + * Creates a new #NMBridgeVlan object. + * + * Returns: (transfer full): the new #NMBridgeVlan object. + * + * Since: 1.18 + **/ +NMBridgeVlan * +nm_bridge_vlan_new (guint16 vid) +{ + NMBridgeVlan *vlan; + + g_return_val_if_fail (vid >= NM_BRIDGE_VLAN_VID_MIN, NULL); + g_return_val_if_fail (vid <= NM_BRIDGE_VLAN_VID_MAX, NULL); + + vlan = g_slice_new0 (NMBridgeVlan); + vlan->refcount = 1; + vlan->vid = vid; + + return vlan; +} + +/** + * nm_bridge_vlan_ref: + * @vlan: the #NMBridgeVlan + * + * Increases the reference count of the object. + * + * Returns: the input argument @vlan object. + * + * Since: 1.18 + **/ +NMBridgeVlan * +nm_bridge_vlan_ref (NMBridgeVlan *vlan) +{ + g_return_val_if_fail (NM_IS_BRIDGE_VLAN (vlan, TRUE), NULL); + + nm_assert (vlan->refcount < G_MAXUINT); + + vlan->refcount++; + return vlan; +} + +/** + * nm_bridge_vlan_unref: + * @vlan: the #NMBridgeVlan + * + * Decreases the reference count of the object. If the reference count + * reaches zero the object will be destroyed. + * + * Since: 1.18 + **/ +void +nm_bridge_vlan_unref (NMBridgeVlan *vlan) +{ + g_return_if_fail (NM_IS_BRIDGE_VLAN (vlan, TRUE)); + + if (--vlan->refcount == 0) + g_slice_free (NMBridgeVlan, vlan); +} + +/** + * nm_bridge_vlan_cmp: + * @a: a #NMBridgeVlan + * @b: another #NMBridgeVlan + * + * Compare two bridge VLAN objects. + * + * Returns: zero of the two instances are equivalent or + * a non-zero integer otherwise. This defines a total ordering + * over the VLANs. Whether a VLAN is sealed or not does not + * affect the comparison. + * + * Since: 1.18 + **/ +int +nm_bridge_vlan_cmp (const NMBridgeVlan *a, const NMBridgeVlan *b) +{ + g_return_val_if_fail (NM_IS_BRIDGE_VLAN (a, TRUE), 0); + g_return_val_if_fail (NM_IS_BRIDGE_VLAN (b, TRUE), 0); + + NM_CMP_SELF (a, b); + NM_CMP_FIELD (a, b, vid); + NM_CMP_FIELD_BOOL (a, b, untagged); + NM_CMP_FIELD_BOOL (a, b, pvid); + + return 0; +} + +NMBridgeVlan * +_nm_bridge_vlan_dup (const NMBridgeVlan *vlan) +{ + g_return_val_if_fail (NM_IS_BRIDGE_VLAN (vlan, TRUE), NULL); + + if (vlan->sealed) { + nm_bridge_vlan_ref ((NMBridgeVlan *) vlan); + return (NMBridgeVlan *) vlan; + } + + return nm_bridge_vlan_new_clone (vlan); +} + +NMBridgeVlan * +_nm_bridge_vlan_dup_and_seal (const NMBridgeVlan *vlan) +{ + NMBridgeVlan *new; + + g_return_val_if_fail (NM_IS_BRIDGE_VLAN (vlan, TRUE), NULL); + + new = _nm_bridge_vlan_dup (vlan); + nm_bridge_vlan_seal (new); + + return new; +} + +/** + * nm_bridge_vlan_get_vid: + * @vlan: the #NMBridgeVlan + * + * Gets the VLAN id of the object. + * + * Returns: the VLAN id + * + * Since: 1.18 + **/ +guint16 +nm_bridge_vlan_get_vid (const NMBridgeVlan *vlan) +{ + g_return_val_if_fail (NM_IS_BRIDGE_VLAN (vlan, TRUE), 0); + + return vlan->vid; +} + +/** + * nm_bridge_vlan_is_untagged: + * @vlan: the #NMBridgeVlan + * + * Returns whether the VLAN is untagged. + * + * Returns: %TRUE if the VLAN is untagged, %FALSE otherwise + * + * Since: 1.18 + **/ +gboolean +nm_bridge_vlan_is_untagged (const NMBridgeVlan *vlan) +{ + g_return_val_if_fail (NM_IS_BRIDGE_VLAN (vlan, TRUE), FALSE); + + return vlan->untagged; +} + +/** + * nm_bridge_vlan_is_pvid: + * @vlan: the #NMBridgeVlan + * + * Returns whether the VLAN is the PVID for the port. + * + * Returns: %TRUE if the VLAN is the PVID + * + * Since: 1.18 + **/ +gboolean +nm_bridge_vlan_is_pvid (const NMBridgeVlan *vlan) +{ + g_return_val_if_fail (NM_IS_BRIDGE_VLAN (vlan, TRUE), FALSE); + + return vlan->pvid; +} + +/** + * nm_bridge_vlan_set_untagged: + * @vlan: the #NMBridgeVlan + * @value: the new value + * + * Change the value of the untagged property of the VLAN. + * + * Since: 1.18 + **/ +void +nm_bridge_vlan_set_untagged (NMBridgeVlan *vlan, gboolean value) +{ + g_return_if_fail (NM_IS_BRIDGE_VLAN (vlan, FALSE)); + + vlan->untagged = value; +} + +/** + * nm_bridge_vlan_set_pvid: + * @vlan: the #NMBridgeVlan + * @value: the new value + * + * Change the value of the PVID property of the VLAN. + * + * Since: 1.18 + **/ +void +nm_bridge_vlan_set_pvid (NMBridgeVlan *vlan, gboolean value) +{ + g_return_if_fail (NM_IS_BRIDGE_VLAN (vlan, FALSE)); + + vlan->pvid = value; +} + +/** + * nm_bridge_vlan_is_sealed: + * @vlan: the #NMBridgeVlan instance + * + * Returns: whether @self is sealed or not. + * + * Since: 1.18 + */ +gboolean +nm_bridge_vlan_is_sealed (const NMBridgeVlan *vlan) +{ + g_return_val_if_fail (NM_IS_BRIDGE_VLAN (vlan, TRUE), FALSE); + + return vlan->sealed; +} + +/** + * nm_bridge_vlan_seal: + * @vlan: the #NMBridgeVlan instance + * + * Seal the #NMBridgeVlan instance. Afterwards, it is a bug + * to call all functions that modify the instance (except ref/unref). + * A sealed instance cannot be unsealed again, but you can create + * an unsealed copy with nm_bridge_vlan_new_clone(). + * + * Since: 1.18 + */ +void +nm_bridge_vlan_seal (NMBridgeVlan *vlan) +{ + g_return_if_fail (NM_IS_BRIDGE_VLAN (vlan, TRUE)); + + vlan->sealed = TRUE; +} + +/** + * nm_bridge_vlan_new_clone: + * @vlan: the #NMBridgeVlan instance to copy + * + * Returns: (transfer full): a clone of @vlan. This instance + * is always unsealed. + * + * Since: 1.18 + */ +NMBridgeVlan * +nm_bridge_vlan_new_clone (const NMBridgeVlan *vlan) +{ + NMBridgeVlan *copy; + + g_return_val_if_fail (NM_IS_BRIDGE_VLAN (vlan, TRUE), NULL); + + copy = nm_bridge_vlan_new (vlan->vid); + copy->untagged = vlan->untagged; + copy->pvid = vlan->pvid; + + return copy; +} + +/*****************************************************************************/ + + /** * nm_setting_bridge_get_mac_address: * @setting: the #NMSettingBridge @@ -716,7 +1001,7 @@ nm_setting_bridge_class_init (NMSettingBridgeClass *klass) */ obj_properties[PROP_VLAN_DEFAULT_PVID] = g_param_spec_uint (NM_SETTING_BRIDGE_VLAN_DEFAULT_PVID, "", "", - 0, 4094, 1, + 0, NM_BRIDGE_VLAN_VID_MAX, 1, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_INFERRABLE | diff --git a/libnm-core/nm-setting-bridge.h b/libnm-core/nm-setting-bridge.h index 53bb5ad822..d277675f1a 100644 --- a/libnm-core/nm-setting-bridge.h +++ b/libnm-core/nm-setting-bridge.h @@ -51,6 +51,9 @@ G_BEGIN_DECLS #define NM_SETTING_BRIDGE_VLAN_FILTERING "vlan-filtering" #define NM_SETTING_BRIDGE_VLAN_DEFAULT_PVID "vlan-default-pvid" +#define NM_BRIDGE_VLAN_VID_MIN 1 +#define NM_BRIDGE_VLAN_VID_MAX 4094 + /** * NMSettingBridge: * @@ -93,6 +96,35 @@ gboolean nm_setting_bridge_get_vlan_filtering (NMSettingBridge *setting); NM_AVAILABLE_IN_1_18 guint16 nm_setting_bridge_get_vlan_default_pvid (NMSettingBridge *setting); +typedef struct _NMBridgeVlan NMBridgeVlan; + +NM_AVAILABLE_IN_1_18 +GType nm_bridge_vlan_get_type (void); +NM_AVAILABLE_IN_1_18 +NMBridgeVlan * nm_bridge_vlan_new (guint16 vid); +NM_AVAILABLE_IN_1_18 +NMBridgeVlan * nm_bridge_vlan_ref (NMBridgeVlan *vlan); +NM_AVAILABLE_IN_1_18 +void nm_bridge_vlan_unref (NMBridgeVlan *vlan); +NM_AVAILABLE_IN_1_18 +NMBridgeVlan * nm_bridge_vlan_new_clone (const NMBridgeVlan *vlan); +NM_AVAILABLE_IN_1_18 +int nm_bridge_vlan_cmp (const NMBridgeVlan *a, const NMBridgeVlan *b); +NM_AVAILABLE_IN_1_18 +void nm_bridge_vlan_seal (NMBridgeVlan *vlan); +NM_AVAILABLE_IN_1_18 +gboolean nm_bridge_vlan_is_sealed (const NMBridgeVlan *vlan); +NM_AVAILABLE_IN_1_18 +void nm_bridge_vlan_set_untagged (NMBridgeVlan *vlan, gboolean value); +NM_AVAILABLE_IN_1_18 +void nm_bridge_vlan_set_pvid (NMBridgeVlan *vlan, gboolean value); +NM_AVAILABLE_IN_1_18 +guint16 nm_bridge_vlan_get_vid (const NMBridgeVlan *vlan); +NM_AVAILABLE_IN_1_18 +gboolean nm_bridge_vlan_is_untagged (const NMBridgeVlan *vlan); +NM_AVAILABLE_IN_1_18 +gboolean nm_bridge_vlan_is_pvid (const NMBridgeVlan *vlan); + G_END_DECLS #endif /* __NM_SETTING_BRIDGE_H__ */ diff --git a/libnm-core/nm-setting-private.h b/libnm-core/nm-setting-private.h index 2af74ec4ce..b423eb5c69 100644 --- a/libnm-core/nm-setting-private.h +++ b/libnm-core/nm-setting-private.h @@ -26,6 +26,7 @@ #endif #include "nm-setting.h" +#include "nm-setting-bridge.h" #include "nm-connection.h" #include "nm-core-enum-types.h" @@ -204,6 +205,9 @@ gboolean _nm_setting_should_compare_secret_property (NMSetting *setting, const char *secret_name, NMSettingCompareFlags flags); +NMBridgeVlan *_nm_bridge_vlan_dup (const NMBridgeVlan *vlan); +NMBridgeVlan *_nm_bridge_vlan_dup_and_seal (const NMBridgeVlan *vlan); + /*****************************************************************************/ #endif /* NM_SETTING_PRIVATE_H */ diff --git a/libnm-core/nm-utils-private.h b/libnm-core/nm-utils-private.h index f730d3d365..a1a1369a39 100644 --- a/libnm-core/nm-utils-private.h +++ b/libnm-core/nm-utils-private.h @@ -103,6 +103,19 @@ void _nm_utils_format_variant_attributes_full (GString *str, char key_value_separator); gboolean _nm_sriov_vf_parse_vlans (NMSriovVF *vf, const char *str, GError **error); +GVariant * _nm_utils_bridge_vlans_to_dbus (NMSetting *setting, const char *property); +gboolean _nm_utils_bridge_vlans_from_dbus (NMSetting *setting, + GVariant *connection_dict, + const char *property, + GVariant *value, + NMSettingParseFlags parse_flags, + GError **error); +gboolean _nm_utils_bridge_vlan_verify_list (GPtrArray *vlans, + gboolean check_normalizable, + GError **error, + const char *setting, + const char *property); + /* JSON to GValue conversion macros */ static inline void diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index d662ce7556..882dfd838c 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -46,6 +46,7 @@ #include "nm-crypto.h" #include "nm-setting-bond.h" #include "nm-setting-bridge.h" +#include "nm-setting-bridge-port.h" #include "nm-setting-infiniband.h" #include "nm-setting-ip6-config.h" #include "nm-setting-team.h" @@ -6710,3 +6711,142 @@ nm_utils_base64secret_normalize (const char *base64_key, nm_explicit_bzero (buf, required_key_len); return TRUE; } + +GVariant * +_nm_utils_bridge_vlans_to_dbus (NMSetting *setting, const char *property) +{ + gs_unref_ptrarray GPtrArray *vlans = NULL; + GVariantBuilder builder; + guint i; + + g_object_get (setting, property, &vlans, NULL); + g_variant_builder_init (&builder, G_VARIANT_TYPE ("aa{sv}")); + + if (vlans) { + for (i = 0; i < vlans->len; i++) { + NMBridgeVlan *vlan = vlans->pdata[i]; + GVariantBuilder vlan_builder; + + g_variant_builder_init (&vlan_builder, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add (&vlan_builder, "{sv}", "vid", + g_variant_new_uint16 (nm_bridge_vlan_get_vid (vlan))); + g_variant_builder_add (&vlan_builder, "{sv}", "pvid", + g_variant_new_boolean (nm_bridge_vlan_is_pvid (vlan))); + g_variant_builder_add (&vlan_builder, "{sv}", "untagged", + g_variant_new_boolean (nm_bridge_vlan_is_untagged (vlan))); + g_variant_builder_add (&builder, "a{sv}", &vlan_builder); + } + } + + return g_variant_builder_end (&builder); +} + +gboolean +_nm_utils_bridge_vlans_from_dbus (NMSetting *setting, + GVariant *connection_dict, + const char *property, + GVariant *value, + NMSettingParseFlags parse_flags, + GError **error) +{ + gs_unref_ptrarray GPtrArray *vlans = NULL; + GVariantIter vlan_iter; + GVariant *vlan_var; + + g_return_val_if_fail (g_variant_is_of_type (value, G_VARIANT_TYPE ("aa{sv}")), FALSE); + + vlans = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_bridge_vlan_unref); + g_variant_iter_init (&vlan_iter, value); + while (g_variant_iter_next (&vlan_iter, "@a{sv}", &vlan_var)) { + gs_unref_variant GVariant *var_unref = vlan_var; + NMBridgeVlan *vlan; + guint16 vid; + gboolean pvid = FALSE, untagged = FALSE; + + if (!g_variant_lookup (vlan_var, "vid", "q", &vid)) + continue; + if ( vid < NM_BRIDGE_VLAN_VID_MIN + || vid > NM_BRIDGE_VLAN_VID_MAX) + continue; + + g_variant_lookup (vlan_var, "pvid", "b", &pvid); + g_variant_lookup (vlan_var, "untagged", "b", &untagged); + + vlan = nm_bridge_vlan_new (vid); + nm_bridge_vlan_set_untagged (vlan, untagged); + nm_bridge_vlan_set_pvid (vlan, pvid); + g_ptr_array_add (vlans, vlan); + } + + g_object_set (setting, property, vlans, NULL); + + return TRUE; +} + +gboolean +_nm_utils_bridge_vlan_verify_list (GPtrArray *vlans, + gboolean check_normalizable, + GError **error, + const char *setting, + const char *property) +{ + guint i; + gs_unref_hashtable GHashTable *h = NULL; + gboolean pvid_found = FALSE; + + if (!vlans || !vlans->len) + return TRUE; + + if (check_normalizable) { + for (i = 1; i < vlans->len; i++) { + NMBridgeVlan *vlan_prev = vlans->pdata[i - 1]; + NMBridgeVlan *vlan = vlans->pdata[i]; + + if (nm_bridge_vlan_get_vid (vlan_prev) > nm_bridge_vlan_get_vid (vlan)) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("Bridge VLANs %d and %d are not sorted by ascending vid"), + nm_bridge_vlan_get_vid (vlan_prev), + nm_bridge_vlan_get_vid (vlan)); + g_prefix_error (error, "%s.%s: ", setting, property); + return FALSE; + } + } + return TRUE; + } + + h = g_hash_table_new (nm_direct_hash, NULL); + for (i = 0; i < vlans->len; i++) { + NMBridgeVlan *vlan = vlans->pdata[i]; + guint vid; + + vid = nm_bridge_vlan_get_vid (vlan); + + if (g_hash_table_contains (h, GUINT_TO_POINTER (vid))) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("duplicate bridge VLAN vid %u"), vid); + g_prefix_error (error, "%s.%s: ", setting, property); + return FALSE; + } + + if (nm_bridge_vlan_is_pvid (vlan)) { + if (pvid_found) { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("only one VLAN can be the PVID")); + g_prefix_error (error, "%s.%s: ", setting, property); + return FALSE; + } + pvid_found = TRUE; + } + + g_hash_table_add (h, GUINT_TO_POINTER (vid)); + } + + return TRUE; +} + diff --git a/libnm/libnm.ver b/libnm/libnm.ver index e3a146c21f..444b10459b 100644 --- a/libnm/libnm.ver +++ b/libnm/libnm.ver @@ -1521,6 +1521,25 @@ global: libnm_1_18_0 { global: + nm_bridge_vlan_cmp; + nm_bridge_vlan_get_type; + nm_bridge_vlan_get_vid; + nm_bridge_vlan_is_pvid; + nm_bridge_vlan_is_sealed; + nm_bridge_vlan_is_untagged; + nm_bridge_vlan_new; + nm_bridge_vlan_new_clone; + nm_bridge_vlan_ref; + nm_bridge_vlan_seal; + nm_bridge_vlan_set_pvid; + nm_bridge_vlan_set_untagged; + nm_bridge_vlan_unref; nm_setting_bridge_get_vlan_filtering; nm_setting_bridge_get_vlan_default_pvid; + nm_setting_bridge_port_add_vlan; + nm_setting_bridge_port_clear_vlans; + nm_setting_bridge_port_get_num_vlans; + nm_setting_bridge_port_get_vlan; + nm_setting_bridge_port_remove_vlan; + nm_setting_bridge_port_remove_vlan_by_vid; } libnm_1_16_0; diff --git a/shared/nm-libnm-core-utils.h b/shared/nm-libnm-core-utils.h index 3d06de00a2..85dd3b9b2c 100644 --- a/shared/nm-libnm-core-utils.h +++ b/shared/nm-libnm-core-utils.h @@ -20,6 +20,7 @@ /****************************************************************************/ +#include "nm-setting-bridge.h" #include "nm-setting-connection.h" #include "nm-setting-ip-config.h" #include "nm-setting-ip4-config.h" @@ -46,6 +47,9 @@ NM_AUTO_DEFINE_FCN0 (NMTCQdisc *, _nm_auto_unref_tc_qdisc, nm_tc_qdisc_unref) #define nm_auto_unref_tc_tfilter nm_auto (_nm_auto_unref_tc_tfilter) NM_AUTO_DEFINE_FCN0 (NMTCTfilter *, _nm_auto_unref_tc_tfilter, nm_tc_tfilter_unref) +#define nm_auto_unref_bridge_vlan nm_auto (_nm_auto_unref_bridge_vlan) +NM_AUTO_DEFINE_FCN0 (NMBridgeVlan *, _nm_auto_unref_bridge_vlan, nm_bridge_vlan_unref) + #define nm_auto_unref_team_link_watcher nm_auto (_nm_auto_unref_team_link_watcher) NM_AUTO_DEFINE_FCN0 (NMTeamLinkWatcher *, _nm_auto_unref_team_link_watcher, nm_team_link_watcher_unref) From a680bedf94b98a454e9d4ae085d72edee69dade7 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Thu, 21 Mar 2019 16:49:55 +0100 Subject: [PATCH 06/13] libnm-core: add vlans property to bridge setting --- clients/common/settings-docs.h.in | 1 + libnm-core/nm-connection.c | 13 ++ libnm-core/nm-core-internal.h | 1 + libnm-core/nm-setting-bridge.c | 286 +++++++++++++++++++++++++++++- libnm-core/nm-setting-bridge.h | 18 +- libnm/libnm.ver | 6 + 6 files changed, 321 insertions(+), 4 deletions(-) diff --git a/clients/common/settings-docs.h.in b/clients/common/settings-docs.h.in index 3a66ae73ad..59ca4cc8a6 100644 --- a/clients/common/settings-docs.h.in +++ b/clients/common/settings-docs.h.in @@ -120,6 +120,7 @@ #define DESCRIBE_DOC_NM_SETTING_BRIDGE_STP N_("Controls whether Spanning Tree Protocol (STP) is enabled for this bridge.") #define DESCRIBE_DOC_NM_SETTING_BRIDGE_VLAN_DEFAULT_PVID N_("The default PVID for the ports of the bridge, that is the VLAN id assigned to incoming untagged frames.") #define DESCRIBE_DOC_NM_SETTING_BRIDGE_VLAN_FILTERING N_("Control whether VLAN filtering is enabled on the bridge.") +#define DESCRIBE_DOC_NM_SETTING_BRIDGE_VLANS N_("Array of bridge VLAN objects. In addition to the VLANs specified here, the bridge will also have the default-pvid VLAN configured by the bridge.vlan-default-pvid property. In nmcli the VLAN list can be specified with the following syntax: $vid [pvid] [untagged] [, $vid [pvid] [untagged]]...") #define DESCRIBE_DOC_NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE N_("Enables or disables \"hairpin mode\" for the port, which allows frames to be sent back out through the port the frame was received on.") #define DESCRIBE_DOC_NM_SETTING_BRIDGE_PORT_PATH_COST N_("The Spanning Tree Protocol (STP) port cost for destinations via this port.") #define DESCRIBE_DOC_NM_SETTING_BRIDGE_PORT_PRIORITY N_("The Spanning Tree Protocol (STP) priority of this bridge port.") diff --git a/libnm-core/nm-connection.c b/libnm-core/nm-connection.c index 312898368e..e91052a35b 100644 --- a/libnm-core/nm-connection.c +++ b/libnm-core/nm-connection.c @@ -1297,6 +1297,18 @@ _normalize_sriov_vf_order (NMConnection *self, GHashTable *parameters) return _nm_setting_sriov_sort_vfs (s_sriov); } +static gboolean +_normalize_bridge_vlan_order (NMConnection *self, GHashTable *parameters) +{ + NMSettingBridge *s_bridge; + + s_bridge = nm_connection_get_setting_bridge (self); + if (!s_bridge) + return FALSE; + + return _nm_setting_bridge_sort_vlans (s_bridge); +} + static gboolean _normalize_bridge_port_vlan_order (NMConnection *self, GHashTable *parameters) { @@ -1657,6 +1669,7 @@ nm_connection_normalize (NMConnection *connection, was_modified |= _normalize_ovs_interface_type (connection, parameters); was_modified |= _normalize_ip_tunnel_wired_setting (connection, parameters); was_modified |= _normalize_sriov_vf_order (connection, parameters); + was_modified |= _normalize_bridge_vlan_order (connection, parameters); was_modified |= _normalize_bridge_port_vlan_order (connection, parameters); /* Verify anew. */ diff --git a/libnm-core/nm-core-internal.h b/libnm-core/nm-core-internal.h index 3859c2bb40..8510699da4 100644 --- a/libnm-core/nm-core-internal.h +++ b/libnm-core/nm-core-internal.h @@ -579,6 +579,7 @@ gboolean _nm_utils_dhcp_duid_valid (const char *duid, GBytes **out_duid_bin); gboolean _nm_setting_sriov_sort_vfs (NMSettingSriov *setting); gboolean _nm_setting_bridge_port_sort_vlans (NMSettingBridgePort *setting); +gboolean _nm_setting_bridge_sort_vlans (NMSettingBridge *setting); /*****************************************************************************/ diff --git a/libnm-core/nm-setting-bridge.c b/libnm-core/nm-setting-bridge.c index 26f2fe305d..04a4f8c2ff 100644 --- a/libnm-core/nm-setting-bridge.c +++ b/libnm-core/nm-setting-bridge.c @@ -40,7 +40,7 @@ /*****************************************************************************/ -NM_GOBJECT_PROPERTIES_DEFINE_BASE ( +NM_GOBJECT_PROPERTIES_DEFINE (NMSettingBridge, PROP_MAC_ADDRESS, PROP_STP, PROP_PRIORITY, @@ -52,6 +52,7 @@ NM_GOBJECT_PROPERTIES_DEFINE_BASE ( PROP_MULTICAST_SNOOPING, PROP_VLAN_FILTERING, PROP_VLAN_DEFAULT_PVID, + PROP_VLANS, ); typedef struct { @@ -66,6 +67,7 @@ typedef struct { gboolean multicast_snooping; gboolean vlan_filtering; guint16 vlan_default_pvid; + GPtrArray *vlans; } NMSettingBridgePrivate; G_DEFINE_TYPE (NMSettingBridge, nm_setting_bridge, NM_TYPE_SETTING) @@ -358,6 +360,43 @@ nm_bridge_vlan_new_clone (const NMBridgeVlan *vlan) /*****************************************************************************/ +static int +vlan_ptr_cmp (gconstpointer a, gconstpointer b) +{ + const NMBridgeVlan *vlan_a = *(const NMBridgeVlan **) a; + const NMBridgeVlan *vlan_b = *(const NMBridgeVlan **) b; + + return nm_bridge_vlan_cmp (vlan_a, vlan_b); +} + +gboolean +_nm_setting_bridge_sort_vlans (NMSettingBridge *setting) +{ + NMSettingBridgePrivate *priv; + gboolean need_sort = FALSE; + guint i; + + priv = NM_SETTING_BRIDGE_GET_PRIVATE (setting); + + for (i = 1; i < priv->vlans->len; i++) { + NMBridgeVlan *vlan_prev = priv->vlans->pdata[i - 1]; + NMBridgeVlan *vlan = priv->vlans->pdata[i]; + + if (nm_bridge_vlan_cmp (vlan_prev, vlan) > 0) { + need_sort = TRUE; + break; + } + } + + if (need_sort) { + g_ptr_array_sort (priv->vlans, vlan_ptr_cmp); + _notify (setting, PROP_VLANS); + } + + return need_sort; +} + +/*****************************************************************************/ /** * nm_setting_bridge_get_mac_address: @@ -521,6 +560,153 @@ nm_setting_bridge_get_vlan_default_pvid (NMSettingBridge *setting) return NM_SETTING_BRIDGE_GET_PRIVATE (setting)->vlan_default_pvid; } +/** + * nm_setting_bridge_add_vlan: + * @setting: the #NMSettingBridge + * @vlan: the vlan to add + * + * Appends a new vlan and associated information to the setting. The + * given vlan gets sealed and a reference to it is added. + * + * Since: 1.18 + **/ +void +nm_setting_bridge_add_vlan (NMSettingBridge *setting, + NMBridgeVlan *vlan) +{ + NMSettingBridgePrivate *priv; + + g_return_if_fail (NM_IS_SETTING_BRIDGE (setting)); + g_return_if_fail (vlan); + + priv = NM_SETTING_BRIDGE_GET_PRIVATE (setting); + + nm_bridge_vlan_seal (vlan); + nm_bridge_vlan_ref (vlan); + + g_ptr_array_add (priv->vlans, vlan); + _notify (setting, PROP_VLANS); +} + +/** + * nm_setting_bridge_get_num_vlans: + * @setting: the #NMSettingBridge + * + * Returns: the number of VLANs + * + * Since: 1.18 + **/ +guint +nm_setting_bridge_get_num_vlans (NMSettingBridge *setting) +{ + NMSettingBridgePrivate *priv; + + g_return_val_if_fail (NM_IS_SETTING_BRIDGE (setting), 0); + priv = NM_SETTING_BRIDGE_GET_PRIVATE (setting); + + return priv->vlans->len; +} + +/** + * nm_setting_bridge_get_vlan: + * @setting: the #NMSettingBridge + * @idx: index number of the VLAN to return + * + * Returns: (transfer none): the VLAN at index @idx + * + * Since: 1.18 + **/ +NMBridgeVlan * +nm_setting_bridge_get_vlan (NMSettingBridge *setting, guint idx) +{ + NMSettingBridgePrivate *priv; + + g_return_val_if_fail (NM_IS_SETTING_BRIDGE (setting), NULL); + priv = NM_SETTING_BRIDGE_GET_PRIVATE (setting); + + g_return_val_if_fail (idx < priv->vlans->len, NULL); + + return priv->vlans->pdata[idx]; +} + +/** + * nm_setting_bridge_remove_vlan: + * @setting: the #NMSettingBridge + * @idx: index number of the VLAN. + * + * Removes the vlan at index @idx. + * + * Since: 1.18 + **/ +void +nm_setting_bridge_remove_vlan (NMSettingBridge *setting, guint idx) +{ + NMSettingBridgePrivate *priv; + + g_return_if_fail (NM_IS_SETTING_BRIDGE (setting)); + priv = NM_SETTING_BRIDGE_GET_PRIVATE (setting); + + g_return_if_fail (idx < priv->vlans->len); + + g_ptr_array_remove_index (priv->vlans, idx); + _notify (setting, PROP_VLANS); +} + +/** + * nm_setting_bridge_remove_vlan_by_vid: + * @setting: the #NMSettingBridge + * @vid: the vlan index of the vlan to remove + * + * Removes the vlan vith id @vid. + * + * Returns: %TRUE if the vlan was found and removed; %FALSE otherwise + * + * Since: 1.18 + **/ +gboolean +nm_setting_bridge_remove_vlan_by_vid (NMSettingBridge *setting, + guint16 vid) +{ + NMSettingBridgePrivate *priv; + guint i; + + g_return_val_if_fail (NM_IS_SETTING_BRIDGE (setting), FALSE); + priv = NM_SETTING_BRIDGE_GET_PRIVATE (setting); + + for (i = 0; i < priv->vlans->len; i++) { + if (nm_bridge_vlan_get_vid (priv->vlans->pdata[i]) == vid) { + g_ptr_array_remove_index (priv->vlans, i); + _notify (setting, PROP_VLANS); + return TRUE; + } + } + return FALSE; +} + +/** + * nm_setting_bridge_clear_vlans: + * @setting: the #NMSettingBridge + * + * Removes all configured VLANs. + * + * Since: 1.18 + **/ +void +nm_setting_bridge_clear_vlans (NMSettingBridge *setting) +{ + NMSettingBridgePrivate *priv; + + g_return_if_fail (NM_IS_SETTING_BRIDGE (setting)); + priv = NM_SETTING_BRIDGE_GET_PRIVATE (setting); + + if (priv->vlans->len != 0) { + g_ptr_array_set_size (priv->vlans, 0); + _notify (setting, PROP_VLANS); + } +} + +/*****************************************************************************/ + static gboolean check_range (guint32 val, guint32 min, @@ -599,7 +785,59 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) return FALSE; } - return _nm_connection_verify_required_interface_name (connection, error); + if (!_nm_connection_verify_required_interface_name (connection, error)) + return FALSE; + + if (!_nm_utils_bridge_vlan_verify_list (priv->vlans, + FALSE, + error, + NM_SETTING_BRIDGE_SETTING_NAME, + NM_SETTING_BRIDGE_VLANS)) + return FALSE; + + /* Failures from here on are NORMALIZABLE... */ + + if (!_nm_utils_bridge_vlan_verify_list (priv->vlans, + TRUE, + error, + NM_SETTING_BRIDGE_SETTING_NAME, + NM_SETTING_BRIDGE_VLANS)) + return NM_SETTING_VERIFY_NORMALIZABLE; + + return TRUE; +} + +static NMTernary +compare_property (const NMSettInfoSetting *sett_info, + guint property_idx, + NMSetting *setting, + NMSetting *other, + NMSettingCompareFlags flags) +{ + NMSettingBridgePrivate *priv_a; + NMSettingBridgePrivate *priv_b; + guint i; + + if (nm_streq (sett_info->property_infos[property_idx].name, NM_SETTING_BRIDGE_VLANS)) { + if (other) { + priv_a = NM_SETTING_BRIDGE_GET_PRIVATE (setting); + priv_b = NM_SETTING_BRIDGE_GET_PRIVATE (other); + + if (priv_a->vlans->len != priv_b->vlans->len) + return FALSE; + for (i = 0; i < priv_a->vlans->len; i++) { + if (nm_bridge_vlan_cmp (priv_a->vlans->pdata[i], priv_b->vlans->pdata[i])) + return FALSE; + } + } + return TRUE; + } + + return NM_SETTING_CLASS (nm_setting_bridge_parent_class)->compare_property (sett_info, + property_idx, + setting, + other, + flags); } /*****************************************************************************/ @@ -645,6 +883,11 @@ get_property (GObject *object, guint prop_id, case PROP_VLAN_DEFAULT_PVID: g_value_set_uint (value, priv->vlan_default_pvid); break; + case PROP_VLANS: + g_value_take_boxed (value, _nm_utils_copy_array (priv->vlans, + (NMUtilsCopyFunc) nm_bridge_vlan_ref, + (GDestroyNotify) nm_bridge_vlan_unref)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -693,6 +936,12 @@ set_property (GObject *object, guint prop_id, case PROP_VLAN_DEFAULT_PVID: priv->vlan_default_pvid = g_value_get_uint (value); break; + case PROP_VLANS: + g_ptr_array_unref (priv->vlans); + priv->vlans = _nm_utils_copy_array (g_value_get_boxed (value), + (NMUtilsCopyFunc) _nm_bridge_vlan_dup_and_seal, + (GDestroyNotify) nm_bridge_vlan_unref); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -704,6 +953,9 @@ set_property (GObject *object, guint prop_id, static void nm_setting_bridge_init (NMSettingBridge *setting) { + NMSettingBridgePrivate *priv = NM_SETTING_BRIDGE_GET_PRIVATE (setting); + + priv->vlans = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_bridge_vlan_unref); } /** @@ -725,6 +977,7 @@ finalize (GObject *object) NMSettingBridgePrivate *priv = NM_SETTING_BRIDGE_GET_PRIVATE (object); g_free (priv->mac_address); + g_ptr_array_unref (priv->vlans); G_OBJECT_CLASS (nm_setting_bridge_parent_class)->finalize (object); } @@ -742,6 +995,7 @@ nm_setting_bridge_class_init (NMSettingBridgeClass *klass) object_class->set_property = set_property; object_class->finalize = finalize; + setting_class->compare_property = compare_property; setting_class->verify = verify; /** @@ -1007,6 +1261,34 @@ nm_setting_bridge_class_init (NMSettingBridgeClass *klass) NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS); + /** + * NMSettingBridge:vlans: (type GPtrArray(NMBridgeVlan)) + * + * Array of bridge VLAN objects. In addition to the VLANs + * specified here, the bridge will also have the default-pvid + * VLAN configured by the bridge.vlan-default-pvid property. + * + * In nmcli the VLAN list can be specified with the following + * syntax: + * + * $vid [pvid] [untagged] [, $vid [pvid] [untagged]]... + * + * Since: 1.18 + **/ + obj_properties[PROP_VLANS] = + g_param_spec_boxed (NM_SETTING_BRIDGE_VLANS, "", "", + G_TYPE_PTR_ARRAY, + G_PARAM_READWRITE | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS); + + _properties_override_add_override (properties_override, + obj_properties[PROP_VLANS], + G_VARIANT_TYPE ("aa{sv}"), + _nm_utils_bridge_vlans_to_dbus, + _nm_utils_bridge_vlans_from_dbus, + NULL); + /* ---dbus--- * property: interface-name * format: string diff --git a/libnm-core/nm-setting-bridge.h b/libnm-core/nm-setting-bridge.h index d277675f1a..2afd1bff61 100644 --- a/libnm-core/nm-setting-bridge.h +++ b/libnm-core/nm-setting-bridge.h @@ -50,6 +50,7 @@ G_BEGIN_DECLS #define NM_SETTING_BRIDGE_MULTICAST_SNOOPING "multicast-snooping" #define NM_SETTING_BRIDGE_VLAN_FILTERING "vlan-filtering" #define NM_SETTING_BRIDGE_VLAN_DEFAULT_PVID "vlan-default-pvid" +#define NM_SETTING_BRIDGE_VLANS "vlans" #define NM_BRIDGE_VLAN_VID_MIN 1 #define NM_BRIDGE_VLAN_VID_MAX 4094 @@ -70,6 +71,8 @@ typedef struct { gpointer padding[4]; } NMSettingBridgeClass; +typedef struct _NMBridgeVlan NMBridgeVlan; + GType nm_setting_bridge_get_type (void); NMSetting * nm_setting_bridge_new (void); @@ -95,8 +98,19 @@ NM_AVAILABLE_IN_1_18 gboolean nm_setting_bridge_get_vlan_filtering (NMSettingBridge *setting); NM_AVAILABLE_IN_1_18 guint16 nm_setting_bridge_get_vlan_default_pvid (NMSettingBridge *setting); - -typedef struct _NMBridgeVlan NMBridgeVlan; +NM_AVAILABLE_IN_1_18 +void nm_setting_bridge_add_vlan (NMSettingBridge *setting, + NMBridgeVlan *vlan); +NM_AVAILABLE_IN_1_18 +guint nm_setting_bridge_get_num_vlans (NMSettingBridge *setting); +NM_AVAILABLE_IN_1_18 +NMBridgeVlan *nm_setting_bridge_get_vlan (NMSettingBridge *setting, guint idx); +NM_AVAILABLE_IN_1_18 +void nm_setting_bridge_remove_vlan (NMSettingBridge *setting, guint idx); +NM_AVAILABLE_IN_1_18 +gboolean nm_setting_bridge_remove_vlan_by_vid (NMSettingBridge *setting, guint16 vid); +NM_AVAILABLE_IN_1_18 +void nm_setting_bridge_clear_vlans (NMSettingBridge *setting); NM_AVAILABLE_IN_1_18 GType nm_bridge_vlan_get_type (void); diff --git a/libnm/libnm.ver b/libnm/libnm.ver index 444b10459b..dcf43921dc 100644 --- a/libnm/libnm.ver +++ b/libnm/libnm.ver @@ -1534,6 +1534,10 @@ global: nm_bridge_vlan_set_pvid; nm_bridge_vlan_set_untagged; nm_bridge_vlan_unref; + nm_setting_bridge_add_vlan; + nm_setting_bridge_clear_vlans; + nm_setting_bridge_get_num_vlans; + nm_setting_bridge_get_vlan; nm_setting_bridge_get_vlan_filtering; nm_setting_bridge_get_vlan_default_pvid; nm_setting_bridge_port_add_vlan; @@ -1542,4 +1546,6 @@ global: nm_setting_bridge_port_get_vlan; nm_setting_bridge_port_remove_vlan; nm_setting_bridge_port_remove_vlan_by_vid; + nm_setting_bridge_remove_vlan; + nm_setting_bridge_remove_vlan_by_vid; } libnm_1_16_0; From cf71860183dcabd47846ff7f07e0ae28ddab9123 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Tue, 19 Mar 2019 15:35:28 +0100 Subject: [PATCH 07/13] libnm-core: add bridge vlan conversion utils --- libnm-core/nm-core-internal.h | 4 ++ libnm-core/nm-setting-bridge.c | 118 ++++++++++++++++++++++++++++++++ libnm-core/nm-setting-bridge.h | 5 ++ libnm-core/nm-utils.c | 1 - libnm-core/tests/test-setting.c | 59 ++++++++++++++++ libnm/libnm.ver | 2 + 6 files changed, 188 insertions(+), 1 deletion(-) diff --git a/libnm-core/nm-core-internal.h b/libnm-core/nm-core-internal.h index 8510699da4..60774c49b3 100644 --- a/libnm-core/nm-core-internal.h +++ b/libnm-core/nm-core-internal.h @@ -752,4 +752,8 @@ gboolean nm_utils_base64secret_normalize (const char *base64_key, /*****************************************************************************/ +void _nm_bridge_vlan_str_append_rest (const NMBridgeVlan *vlan, + GString *string, + gboolean leading_space); + #endif diff --git a/libnm-core/nm-setting-bridge.c b/libnm-core/nm-setting-bridge.c index 04a4f8c2ff..ae70942c09 100644 --- a/libnm-core/nm-setting-bridge.c +++ b/libnm-core/nm-setting-bridge.c @@ -358,6 +358,124 @@ nm_bridge_vlan_new_clone (const NMBridgeVlan *vlan) return copy; } +void +_nm_bridge_vlan_str_append_rest (const NMBridgeVlan *vlan, + GString *string, + gboolean leading_space) +{ + if (nm_bridge_vlan_is_pvid (vlan)) { + if (leading_space) + g_string_append_c (string, ' '); + g_string_append (string, "pvid"); + leading_space = TRUE; + } + if (nm_bridge_vlan_is_untagged (vlan)) { + if (leading_space) + g_string_append_c (string, ' '); + g_string_append (string, "untagged"); + leading_space = TRUE; + } +} + +/** + * nm_bridge_vlan_to_str: + * @vlan: the %NMBridgeVlan + * @error: location of the error + * + * Convert a %NMBridgeVlan to a string. + * + * Returns: formatted string or %NULL + * + * Since: 1.18 + */ +char * +nm_bridge_vlan_to_str (const NMBridgeVlan *vlan, GError **error) +{ + GString *string; + + g_return_val_if_fail (vlan, NULL); + g_return_val_if_fail (!error || !*error, NULL); + + /* The function never fails at the moment, but it might in the + * future if more parameters are added to the object that could + * make it invalid. */ + + string = g_string_sized_new (20); + + g_string_append_printf (string, "%u", nm_bridge_vlan_get_vid (vlan)); + _nm_bridge_vlan_str_append_rest (vlan, string, TRUE); + + return g_string_free (string, FALSE); +} + +/** + * nm_bridge_vlan_from_str: + * @str: the string representation of a bridge VLAN + * @error: location of the error + * + * Parses the string representation of the queueing + * discipline to a %NMBridgeVlan instance. + * + * Returns: the %NMBridgeVlan or %NULL + * + * Since: 1.18 + */ +NMBridgeVlan * +nm_bridge_vlan_from_str (const char *str, GError **error) +{ + NMBridgeVlan *vlan = NULL; + gs_unref_hashtable GHashTable *ht = NULL; + gs_free const char **tokens = NULL; + guint i, vid; + gboolean pvid = FALSE; + gboolean untagged = FALSE; + + g_return_val_if_fail (str, NULL); + g_return_val_if_fail (!error || !*error, NULL); + + tokens = nm_utils_strsplit_set (str, " ", FALSE); + if (!tokens || !tokens[0]) { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + "missing VLAN id"); + return NULL; + } + + vid = _nm_utils_ascii_str_to_uint64 (tokens[0], + 10, + NM_BRIDGE_VLAN_VID_MIN, + NM_BRIDGE_VLAN_VID_MAX, + G_MAXUINT); + if (vid == G_MAXUINT) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + "invalid VLAN id '%s', must be in [1,4094]", tokens[0]); + return NULL; + } + + for (i = 1; tokens[i]; i++) { + if (nm_streq (tokens[i], "pvid")) + pvid = TRUE; + else if (nm_streq (tokens[i], "untagged")) + untagged = TRUE; + else { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + "invalid option '%s'", tokens[i]); + return NULL; + } + } + + vlan = nm_bridge_vlan_new (vid); + nm_bridge_vlan_set_pvid (vlan, pvid); + nm_bridge_vlan_set_untagged (vlan, untagged); + + return vlan; +} + /*****************************************************************************/ static int diff --git a/libnm-core/nm-setting-bridge.h b/libnm-core/nm-setting-bridge.h index 2afd1bff61..0ec0b2a8e3 100644 --- a/libnm-core/nm-setting-bridge.h +++ b/libnm-core/nm-setting-bridge.h @@ -139,6 +139,11 @@ gboolean nm_bridge_vlan_is_untagged (const NMBridgeVlan *vlan); NM_AVAILABLE_IN_1_18 gboolean nm_bridge_vlan_is_pvid (const NMBridgeVlan *vlan); +NM_AVAILABLE_IN_1_18 +char * nm_bridge_vlan_to_str (const NMBridgeVlan *vlan, GError **error); +NM_AVAILABLE_IN_1_18 +NMBridgeVlan * nm_bridge_vlan_from_str (const char *str, GError **error); + G_END_DECLS #endif /* __NM_SETTING_BRIDGE_H__ */ diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index 882dfd838c..88cdee36e3 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -6849,4 +6849,3 @@ _nm_utils_bridge_vlan_verify_list (GPtrArray *vlans, return TRUE; } - diff --git a/libnm-core/tests/test-setting.c b/libnm-core/tests/test-setting.c index 2011273a2b..dc624d1ba3 100644 --- a/libnm-core/tests/test-setting.c +++ b/libnm-core/tests/test-setting.c @@ -1658,6 +1658,63 @@ test_sriov_parse_vlans (void) test_sriov_parse_vlan_one ("1.2.ad;2.0.q;5;3", TRUE, {1, 2, 1}, {2, 0, 0}, {3, 0, 0}, {5, 0, 0}); } +static void +test_bridge_vlans (void) +{ + NMBridgeVlan *v1, *v2; + GError *error = NULL; + char *str; + + v1 = nm_bridge_vlan_from_str ("1 foobar", &error); + nmtst_assert_no_success (v1, error); + g_clear_error (&error); + + v1 = nm_bridge_vlan_from_str ("4095", &error); + nmtst_assert_no_success (v1, error); + g_clear_error (&error); + + /* test comparison (1) */ + v1 = nm_bridge_vlan_from_str ("10 untagged", &error); + nmtst_assert_success (v1, error); + + g_assert_cmpuint (nm_bridge_vlan_get_vid (v1), ==, 10); + g_assert_cmpint (nm_bridge_vlan_is_sealed (v1), ==, FALSE); + g_assert_cmpint (nm_bridge_vlan_is_pvid (v1), ==, FALSE); + g_assert_cmpint (nm_bridge_vlan_is_untagged (v1), ==, TRUE); + + nm_bridge_vlan_set_pvid (v1, TRUE); + nm_bridge_vlan_set_untagged (v1, FALSE); + nm_bridge_vlan_seal (v1); + + g_assert_cmpint (nm_bridge_vlan_is_sealed (v1), ==, TRUE); + g_assert_cmpint (nm_bridge_vlan_is_pvid (v1), ==, TRUE); + g_assert_cmpint (nm_bridge_vlan_is_untagged (v1), ==, FALSE); + + str = nm_bridge_vlan_to_str (v1, &error); + nmtst_assert_success (str, error); + g_assert_cmpstr (str, ==, "10 pvid"); + g_clear_pointer (&str, g_free); + + v2 = nm_bridge_vlan_from_str (" 10 pvid ", &error); + nmtst_assert_success (v2, error); + + g_assert_cmpint (nm_bridge_vlan_cmp (v1, v2), ==, 0); + + nm_bridge_vlan_unref (v1); + nm_bridge_vlan_unref (v2); + + /* test comparison (2) */ + v1 = nm_bridge_vlan_from_str ("10", &error); + nmtst_assert_success (v1, error); + v2 = nm_bridge_vlan_from_str ("20", &error); + nmtst_assert_success (v2, error); + + g_assert_cmpint (nm_bridge_vlan_cmp (v1, v2), <, 0); + + nm_bridge_vlan_unref (v1); + nm_bridge_vlan_unref (v2); +} + /*****************************************************************************/ static void @@ -2663,6 +2720,8 @@ main (int argc, char **argv) g_test_add_func ("/libnm/settings/tc_config/setting/duplicates", test_tc_config_setting_duplicates); g_test_add_func ("/libnm/settings/tc_config/dbus", test_tc_config_dbus); + g_test_add_func ("/libnm/settings/bridge/vlans", test_bridge_vlans); + #if WITH_JSON_VALIDATION g_test_add_func ("/libnm/settings/team/sync_runner_from_config_roundrobin", test_runner_roundrobin_sync_from_config); diff --git a/libnm/libnm.ver b/libnm/libnm.ver index dcf43921dc..b705ca4589 100644 --- a/libnm/libnm.ver +++ b/libnm/libnm.ver @@ -1522,6 +1522,7 @@ global: libnm_1_18_0 { global: nm_bridge_vlan_cmp; + nm_bridge_vlan_from_str; nm_bridge_vlan_get_type; nm_bridge_vlan_get_vid; nm_bridge_vlan_is_pvid; @@ -1533,6 +1534,7 @@ global: nm_bridge_vlan_seal; nm_bridge_vlan_set_pvid; nm_bridge_vlan_set_untagged; + nm_bridge_vlan_to_str; nm_bridge_vlan_unref; nm_setting_bridge_add_vlan; nm_setting_bridge_clear_vlans; From c66d17d4fc3fd06e4e9853ff0929c3844fbed1b6 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Tue, 19 Mar 2019 17:33:40 +0100 Subject: [PATCH 08/13] keyfile: support bridge vlans --- libnm-core/nm-keyfile.c | 93 +++++++++++++++++++++++++++++++++ libnm-core/tests/test-keyfile.c | 89 +++++++++++++++++++++++++++++++ 2 files changed, 182 insertions(+) diff --git a/libnm-core/nm-keyfile.c b/libnm-core/nm-keyfile.c index a4b0064396..0d833752ef 100644 --- a/libnm-core/nm-keyfile.c +++ b/libnm-core/nm-keyfile.c @@ -1518,6 +1518,57 @@ team_config_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key g_object_set (G_OBJECT (setting), key, conf, NULL); } +static void +bridge_vlan_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key) +{ + const char *setting_name = nm_setting_get_name (setting); + gs_unref_ptrarray GPtrArray *vlans = NULL; + gs_strfreev char **keys = NULL; + gsize n_keys = 0; + int i; + + keys = nm_keyfile_plugin_kf_get_keys (info->keyfile, setting_name, &n_keys, NULL); + if (n_keys == 0) + return; + + vlans = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_bridge_vlan_unref); + + for (i = 0; i < n_keys; i++) { + NMBridgeVlan *vlan; + const char *index; + gs_free char *vlan_rest = NULL; + gs_free char *vlan_str = NULL; + gs_free_error GError *err = NULL; + + if (!g_str_has_prefix (keys[i], "vlan.")) + continue; + + index = keys[i] + NM_STRLEN("vlan."); + + if (index[0] == '\0') + continue; + if (index[0] == '0' && index[1] != '\0') + continue; + if (!NM_STRCHAR_ALL (index, ch, g_ascii_isdigit (ch))) + continue; + + vlan_rest = nm_keyfile_plugin_kf_get_string (info->keyfile, setting_name, keys[i], NULL); + vlan_str = g_strdup_printf ("%s %s", index, vlan_rest); + + vlan = nm_bridge_vlan_from_str (vlan_str, &err); + if (!vlan) { + handle_warn (info, keys[i], NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid bridge vlan: %s"), + err->message); + continue; + } + g_ptr_array_add (vlans, vlan); + } + + if (vlans->len >= 1) + g_object_set (setting, key, vlans, NULL); +} + static void qdisc_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key) { @@ -1842,6 +1893,34 @@ route_writer (KeyfileWriterInfo *info, write_ip_values (info->keyfile, setting_name, array, NULL, TRUE); } +static void +bridge_vlan_writer (KeyfileWriterInfo *info, + NMSetting *setting, + const char *key, + const GValue *value) +{ + gsize i; + GPtrArray *array; + nm_auto_free_gstring GString *value_str = NULL; + + array = (GPtrArray *) g_value_get_boxed (value); + if (!array || !array->len) + return; + + for (i = 0; i < array->len; i++) { + NMBridgeVlan *vlan = array->pdata[i]; + char key_name[32]; + + nm_sprintf_buf (key_name, "vlan.%u", nm_bridge_vlan_get_vid (vlan)); + nm_gstring_prepare (&value_str); + _nm_bridge_vlan_str_append_rest (vlan, value_str, FALSE); + nm_keyfile_plugin_kf_set_string (info->keyfile, + nm_setting_get_name (setting), + key_name, + value_str->str); + } +} + static void qdisc_writer (KeyfileWriterInfo *info, NMSetting *setting, @@ -2272,6 +2351,20 @@ static const ParseInfoSetting *const parse_infos[_NM_META_SETTING_TYPE_NUM] = { PARSE_INFO_PROPERTY (NM_SETTING_BRIDGE_MAC_ADDRESS, .parser = mac_address_parser_ETHER, ), + PARSE_INFO_PROPERTY (NM_SETTING_BRIDGE_VLANS, + .parser_no_check_key = TRUE, + .parser = bridge_vlan_parser, + .writer = bridge_vlan_writer, + ), + ), + ), + PARSE_INFO_SETTING (NM_META_SETTING_TYPE_BRIDGE_PORT, + PARSE_INFO_PROPERTIES ( + PARSE_INFO_PROPERTY (NM_SETTING_BRIDGE_PORT_VLANS, + .parser_no_check_key = TRUE, + .parser = bridge_vlan_parser, + .writer = bridge_vlan_writer, + ), ), ), PARSE_INFO_SETTING (NM_META_SETTING_TYPE_CONNECTION, diff --git a/libnm-core/tests/test-keyfile.c b/libnm-core/tests/test-keyfile.c index 9b9d1c0415..fbdd7ab27b 100644 --- a/libnm-core/tests/test-keyfile.c +++ b/libnm-core/tests/test-keyfile.c @@ -744,6 +744,93 @@ test_vpn_1 (void) /*****************************************************************************/ +static void +test_bridge_vlans (void) +{ + gs_unref_keyfile GKeyFile *keyfile = NULL; + gs_unref_object NMConnection *con = NULL; + NMSettingBridge *s_bridge; + NMBridgeVlan *vlan; + + con = nmtst_create_connection_from_keyfile ( + "[connection]\n" + "id=t\n" + "type=bridge\n" + "interface-name=br4\n" + "\n" + "[bridge]\n" + "vlan.9=untagged\n" + "vlan.1=pvid untagged\n" + "", + "/test_bridge_port/vlans"); + s_bridge = NM_SETTING_BRIDGE (nm_connection_get_setting (con, NM_TYPE_SETTING_BRIDGE)); + g_assert (s_bridge); + g_assert_cmpuint (nm_setting_bridge_get_num_vlans (s_bridge), ==, 2); + + vlan = nm_setting_bridge_get_vlan (s_bridge, 0); + g_assert (vlan); + g_assert_cmpuint (nm_bridge_vlan_get_vid (vlan), ==, 1); + g_assert_cmpint (nm_bridge_vlan_is_pvid (vlan), ==, TRUE); + g_assert_cmpint (nm_bridge_vlan_is_untagged (vlan), ==, TRUE); + + vlan = nm_setting_bridge_get_vlan (s_bridge, 1); + g_assert (vlan); + g_assert_cmpuint (nm_bridge_vlan_get_vid (vlan), ==, 9); + g_assert_cmpint (nm_bridge_vlan_is_pvid (vlan), ==, FALSE); + g_assert_cmpint (nm_bridge_vlan_is_untagged (vlan), ==, TRUE); + + CLEAR (&con, &keyfile); +} + +static void +test_bridge_port_vlans (void) +{ + gs_unref_keyfile GKeyFile *keyfile = NULL; + gs_unref_object NMConnection *con = NULL; + NMSettingBridgePort *s_port; + NMBridgeVlan *vlan; + + con = nmtst_create_connection_from_keyfile ( + "[connection]\n" + "id=t\n" + "type=dummy\n" + "interface-name=dummy1\n" + "master=br0\n" + "slave-type=bridge\n" + "\n" + "[bridge-port]\n" + "vlan.4000=\n" + "vlan.10=untagged\n" + "vlan.20=pvid untagged" + "", + "/test_bridge_port/vlans"); + s_port = NM_SETTING_BRIDGE_PORT (nm_connection_get_setting (con, NM_TYPE_SETTING_BRIDGE_PORT)); + g_assert (s_port); + g_assert_cmpuint (nm_setting_bridge_port_get_num_vlans (s_port), ==, 3); + + vlan = nm_setting_bridge_port_get_vlan (s_port, 0); + g_assert (vlan); + g_assert_cmpuint (nm_bridge_vlan_get_vid (vlan), ==, 10); + g_assert_cmpint (nm_bridge_vlan_is_pvid (vlan), ==, FALSE); + g_assert_cmpint (nm_bridge_vlan_is_untagged (vlan), ==, TRUE); + + vlan = nm_setting_bridge_port_get_vlan (s_port, 1); + g_assert (vlan); + g_assert_cmpuint (nm_bridge_vlan_get_vid (vlan), ==, 20); + g_assert_cmpint (nm_bridge_vlan_is_pvid (vlan), ==, TRUE); + g_assert_cmpint (nm_bridge_vlan_is_untagged (vlan), ==, TRUE); + + vlan = nm_setting_bridge_port_get_vlan (s_port, 2); + g_assert (vlan); + g_assert_cmpuint (nm_bridge_vlan_get_vid (vlan), ==, 4000); + g_assert_cmpint (nm_bridge_vlan_is_pvid (vlan), ==, FALSE); + g_assert_cmpint (nm_bridge_vlan_is_untagged (vlan), ==, FALSE); + + CLEAR (&con, &keyfile); +} + +/*****************************************************************************/ + NMTST_DEFINE (); int main (int argc, char **argv) @@ -757,6 +844,8 @@ int main (int argc, char **argv) g_test_add_func ("/core/keyfile/test_team_conf_read/invalid", test_team_conf_read_invalid); g_test_add_func ("/core/keyfile/test_user/1", test_user_1); g_test_add_func ("/core/keyfile/test_vpn/1", test_vpn_1); + g_test_add_func ("/core/keyfile/bridge/vlans", test_bridge_vlans); + g_test_add_func ("/core/keyfile/bridge-port/vlans", test_bridge_port_vlans); return g_test_run (); } From 56d193e68661889606a106b288a49e25ad389777 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Sat, 23 Mar 2019 16:58:57 +0100 Subject: [PATCH 09/13] ifcfg-rh: add bridge vlans support --- libnm-core/nm-setting-bridge-port.c | 7 ++ libnm-core/nm-setting-bridge.c | 7 ++ .../plugins/ifcfg-rh/nms-ifcfg-rh-reader.c | 47 ++++++++++++ .../plugins/ifcfg-rh/nms-ifcfg-rh-writer.c | 75 ++++++++++++++++--- ...fcfg-Test_Write_Bridge_Component.cexpected | 1 + .../plugins/ifcfg-rh/tests/test-ifcfg-rh.c | 28 +++++++ 6 files changed, 153 insertions(+), 12 deletions(-) diff --git a/libnm-core/nm-setting-bridge-port.c b/libnm-core/nm-setting-bridge-port.c index 404a486251..ebac2fc2c3 100644 --- a/libnm-core/nm-setting-bridge-port.c +++ b/libnm-core/nm-setting-bridge-port.c @@ -558,6 +558,13 @@ nm_setting_bridge_port_class_init (NMSettingBridgePortClass *klass) * * Since: 1.18 **/ + /* ---ifcfg-rh--- + * property: vlans + * variable: BRIDGE_PORT_VLANS + * description: List of VLANs on the bridge port + * example: BRIDGE_PORT_VLANS="1 pvid untagged,20,40 untagged" + * ---end--- + */ obj_properties[PROP_VLANS] = g_param_spec_boxed (NM_SETTING_BRIDGE_PORT_VLANS, "", "", G_TYPE_PTR_ARRAY, diff --git a/libnm-core/nm-setting-bridge.c b/libnm-core/nm-setting-bridge.c index ae70942c09..18ed5f54db 100644 --- a/libnm-core/nm-setting-bridge.c +++ b/libnm-core/nm-setting-bridge.c @@ -1393,6 +1393,13 @@ nm_setting_bridge_class_init (NMSettingBridgeClass *klass) * * Since: 1.18 **/ + /* ---ifcfg-rh--- + * property: vlans + * variable: BRIDGE_VLANS + * description: List of VLANs on the bridge + * example: BRIDGE_VLANS="1 pvid untagged,20,40 untagged" + * ---end--- + */ obj_properties[PROP_VLANS] = g_param_spec_boxed (NM_SETTING_BRIDGE_VLANS, "", "", G_TYPE_PTR_ARRAY, diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c index d0f37ea95a..de45c084ef 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c @@ -5054,6 +5054,43 @@ handle_bridging_opts (NMSetting *setting, } } +static void +read_bridge_vlans (shvarFile *ifcfg, + const char *key, + NMSetting *setting, + const char *property) +{ + gs_unref_ptrarray GPtrArray *array = NULL; + gs_free char *value_to_free = NULL; + const char *value; + + value = svGetValueStr (ifcfg, key, &value_to_free); + if (value) { + gs_free const char **strv = NULL; + const char *const *iter; + GError *local = NULL; + NMBridgeVlan *vlan; + + array = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_bridge_vlan_unref); + + strv = nm_utils_strsplit_set (value, ",", FALSE); + if (strv) { + for (iter = strv; *iter; iter++) { + vlan = nm_bridge_vlan_from_str (*iter, &local); + if (!vlan) { + PARSE_WARNING ("invalid bridge VLAN: %s", local->message); + g_clear_error (&local); + continue; + } + g_ptr_array_add (array, vlan); + } + } + nm_clear_g_free (&value_to_free); + } + + g_object_set (setting, property, array, NULL); +} + static NMSetting * make_bridge_setting (shvarFile *ifcfg, const char *file, @@ -5112,6 +5149,11 @@ make_bridge_setting (shvarFile *ifcfg, nm_clear_g_free (&value_to_free); } + read_bridge_vlans (ifcfg, + "BRIDGE_VLANS", + NM_SETTING (s_bridge), + NM_SETTING_BRIDGE_VLANS); + return (NMSetting *) g_steal_pointer (&s_bridge); } @@ -5181,6 +5223,11 @@ make_bridge_port_setting (shvarFile *ifcfg) handle_bridging_opts (s_port, FALSE, value, handle_bridge_option, BRIDGE_OPT_TYPE_PORT_OPTION); nm_clear_g_free (&value_to_free); } + + read_bridge_vlans (ifcfg, + "BRIDGE_PORT_VLANS", + s_port, + NM_SETTING_BRIDGE_PORT_VLANS); } return s_port; diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c index f7fc36bfd6..f6b8f8c21a 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c @@ -1463,6 +1463,43 @@ get_setting_default_boolean (NMSetting *setting, const char *prop) return ret; } +static gboolean +write_bridge_vlans (NMSetting *setting, + const char *property_name, + shvarFile *ifcfg, + const char *key, + GError **error) +{ + gs_unref_ptrarray GPtrArray *vlans = NULL; + NMBridgeVlan *vlan; + GString *string; + guint i; + + g_object_get (setting, property_name, &vlans, NULL); + + if (!vlans || !vlans->len) { + svUnsetValue (ifcfg, key); + return TRUE; + } + + string = g_string_new (""); + for (i = 0; i < vlans->len; i++) { + gs_free char *vlan_str = NULL; + + vlan = vlans->pdata[i]; + vlan_str = nm_bridge_vlan_to_str (vlan, error); + if (!vlan_str) + return FALSE; + if (string->len > 0) + g_string_append (string, ", "); + g_string_append (string, vlan_str); + } + + svSetValueStr (ifcfg, key, string->str); + g_string_free (string, TRUE); + return TRUE; +} + static gboolean write_bridge_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wired, GError **error) { @@ -1552,6 +1589,13 @@ write_bridge_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wire svSetValueStr (ifcfg, "BRIDGING_OPTS", opts->str); g_string_free (opts, TRUE); + if (!write_bridge_vlans ((NMSetting *) s_bridge, + NM_SETTING_BRIDGE_VLANS, + ifcfg, + "BRIDGE_VLANS", + error)) + return FALSE; + svSetValueStr (ifcfg, "TYPE", TYPE_BRIDGE); *wired = write_wired_for_virtual (connection, ifcfg); @@ -1564,7 +1608,7 @@ write_bridge_port_setting (NMConnection *connection, shvarFile *ifcfg, GError ** { NMSettingBridgePort *s_port; guint32 i; - GString *opts; + GString *string; s_port = nm_connection_get_setting_bridge_port (connection); if (!s_port) @@ -1573,28 +1617,35 @@ write_bridge_port_setting (NMConnection *connection, shvarFile *ifcfg, GError ** svUnsetValue (ifcfg, "BRIDGING_OPTS"); /* Bridge options */ - opts = g_string_sized_new (32); + string = g_string_sized_new (32); i = nm_setting_bridge_port_get_priority (s_port); if (i != get_setting_default_uint (NM_SETTING (s_port), NM_SETTING_BRIDGE_PORT_PRIORITY)) - g_string_append_printf (opts, "priority=%u", i); + g_string_append_printf (string, "priority=%u", i); i = nm_setting_bridge_port_get_path_cost (s_port); if (i != get_setting_default_uint (NM_SETTING (s_port), NM_SETTING_BRIDGE_PORT_PATH_COST)) { - if (opts->len) - g_string_append_c (opts, ' '); - g_string_append_printf (opts, "path_cost=%u", i); + if (string->len) + g_string_append_c (string, ' '); + g_string_append_printf (string, "path_cost=%u", i); } if (nm_setting_bridge_port_get_hairpin_mode (s_port)) { - if (opts->len) - g_string_append_c (opts, ' '); - g_string_append_printf (opts, "hairpin_mode=1"); + if (string->len) + g_string_append_c (string, ' '); + g_string_append_printf (string, "hairpin_mode=1"); } - if (opts->len) - svSetValueStr (ifcfg, "BRIDGING_OPTS", opts->str); - g_string_free (opts, TRUE); + if (string->len) + svSetValueStr (ifcfg, "BRIDGING_OPTS", string->str); + g_string_free (string, TRUE); + + if (!write_bridge_vlans ((NMSetting *) s_port, + NM_SETTING_BRIDGE_PORT_VLANS, + ifcfg, + "BRIDGE_PORT_VLANS", + error)) + return FALSE; return TRUE; } diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bridge_Component.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bridge_Component.cexpected index c478db38cc..0b4db7c1ed 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bridge_Component.cexpected +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bridge_Component.cexpected @@ -2,6 +2,7 @@ HWADDR=31:33:33:37:BE:CD MTU=1492 TYPE=Ethernet BRIDGING_OPTS="priority=50 path_cost=33" +BRIDGE_PORT_VLANS="1 untagged, 2 pvid untagged, 4" NAME="Test Write Bridge Component" UUID=${UUID} ONBOOT=yes diff --git a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c index e504c7bbdf..99ffae403b 100644 --- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c +++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c @@ -7533,6 +7533,8 @@ test_write_bridge_main (void) NMIPAddress *addr; static const char *mac = "31:33:33:37:be:cd"; GError *error = NULL; + gs_unref_ptrarray GPtrArray *vlans = NULL; + NMBridgeVlan *vlan; connection = nm_simple_connection_new (); g_assert (connection); @@ -7553,11 +7555,23 @@ test_write_bridge_main (void) s_bridge = (NMSettingBridge *) nm_setting_bridge_new (); nm_connection_add_setting (connection, NM_SETTING (s_bridge)); + vlans = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_bridge_vlan_unref); + vlan = nm_bridge_vlan_new (11); + nm_bridge_vlan_set_untagged (vlan, TRUE); + g_ptr_array_add (vlans, vlan); + vlan = nm_bridge_vlan_new (22); + nm_bridge_vlan_set_pvid (vlan, TRUE); + nm_bridge_vlan_set_untagged (vlan, TRUE); + g_ptr_array_add (vlans, vlan); + vlan = nm_bridge_vlan_new (44); + g_ptr_array_add (vlans, vlan); + g_object_set (s_bridge, NM_SETTING_BRIDGE_MAC_ADDRESS, mac, NM_SETTING_BRIDGE_GROUP_FORWARD_MASK, 19008, NM_SETTING_BRIDGE_VLAN_FILTERING, TRUE, NM_SETTING_BRIDGE_VLAN_DEFAULT_PVID, 4000, + NM_SETTING_BRIDGE_VLANS, vlans, NULL); /* IP4 setting */ @@ -7635,6 +7649,8 @@ test_write_bridge_component (void) NMSetting *s_port; static const char *mac = "31:33:33:37:be:cd"; guint32 mtu = 1492; + gs_unref_ptrarray GPtrArray *vlans = NULL; + NMBridgeVlan *vlan; connection = nm_simple_connection_new (); g_assert (connection); @@ -7662,11 +7678,23 @@ test_write_bridge_component (void) NULL); /* Bridge port */ + vlans = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_bridge_vlan_unref); + vlan = nm_bridge_vlan_new (1); + nm_bridge_vlan_set_untagged (vlan, TRUE); + g_ptr_array_add (vlans, vlan); + vlan = nm_bridge_vlan_new (2); + nm_bridge_vlan_set_pvid (vlan, TRUE); + nm_bridge_vlan_set_untagged (vlan, TRUE); + g_ptr_array_add (vlans, vlan); + vlan = nm_bridge_vlan_new (4); + g_ptr_array_add (vlans, vlan); + s_port = nm_setting_bridge_port_new (); nm_connection_add_setting (connection, s_port); g_object_set (s_port, NM_SETTING_BRIDGE_PORT_PRIORITY, 50, NM_SETTING_BRIDGE_PORT_PATH_COST, 33, + NM_SETTING_BRIDGE_PORT_VLANS, vlans, NULL); nmtst_assert_connection_verifies (connection); From 4848ad7c77e6e73a183815c313993ff20f547f23 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Sat, 23 Mar 2019 17:00:13 +0100 Subject: [PATCH 10/13] nmcli: add bridge vlans support --- clients/common/nm-meta-setting-desc.c | 78 +++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/clients/common/nm-meta-setting-desc.c b/clients/common/nm-meta-setting-desc.c index bc2cdafc30..7a20196a0d 100644 --- a/clients/common/nm-meta-setting-desc.c +++ b/clients/common/nm-meta-setting-desc.c @@ -3434,6 +3434,25 @@ _objlist_obj_to_str_fcn_tc_config_qdiscs (NMMetaAccessorGetType get_type, g_string_append (str, s); } +static void +_objlist_obj_to_str_fcn_bridge_vlans (NMMetaAccessorGetType get_type, + NMSetting *setting, + guint idx, + GString *str) +{ + gs_free char *s = NULL; + NMBridgeVlan *vlan; + + if (NM_IS_SETTING_BRIDGE (setting)) + vlan = nm_setting_bridge_get_vlan (NM_SETTING_BRIDGE (setting), idx); + else + vlan = nm_setting_bridge_port_get_vlan (NM_SETTING_BRIDGE_PORT (setting), idx); + + s = nm_bridge_vlan_to_str (vlan, NULL); + if (s) + g_string_append (str, s); +} + static gboolean _objlist_set_fcn_sriov_vfs (NMSetting *setting, gboolean do_add, @@ -3482,6 +3501,43 @@ _objlist_set_fcn_tc_config_qdiscs (NMSetting *setting, return TRUE; } +static gboolean +_objlist_set_fcn_bridge_vlans (NMSetting *setting, + gboolean do_add, + const char *value, + GError **error) +{ + nm_auto_unref_bridge_vlan NMBridgeVlan *vlan = NULL; + gs_free_error GError *local = NULL; + + vlan = nm_bridge_vlan_from_str (value, &local); + if (!vlan) { + nm_utils_error_set (error, NM_UTILS_ERROR_INVALID_ARGUMENT, + "%s %s", + local->message, + _("The valid syntax is: ' [pvid] [untagged]")); + return FALSE; + } + + if (NM_IS_SETTING_BRIDGE (setting)) { + if (do_add) + nm_setting_bridge_add_vlan (NM_SETTING_BRIDGE (setting), vlan); + else { + nm_setting_bridge_remove_vlan_by_vid (NM_SETTING_BRIDGE (setting), + nm_bridge_vlan_get_vid (vlan)); + } + } else { + if (do_add) + nm_setting_bridge_port_add_vlan (NM_SETTING_BRIDGE_PORT (setting), vlan); + else { + nm_setting_bridge_port_remove_vlan_by_vid (NM_SETTING_BRIDGE_PORT (setting), + nm_bridge_vlan_get_vid (vlan)); + } + } + + return TRUE; +} + static void _objlist_obj_to_str_fcn_tc_config_tfilters (NMMetaAccessorGetType get_type, NMSetting *setting, @@ -4814,6 +4870,17 @@ static const NMMetaPropertyInfo *const property_infos_BRIDGE[] = { PROPERTY_INFO_WITH_DESC (NM_SETTING_BRIDGE_VLAN_DEFAULT_PVID, .property_type = &_pt_gobject_int, ), + PROPERTY_INFO_WITH_DESC (NM_SETTING_BRIDGE_VLANS, + .property_type = &_pt_objlist, + .property_typ_data = DEFINE_PROPERTY_TYP_DATA ( + PROPERTY_TYP_DATA_SUBTYPE (objlist, + .get_num_fcn = OBJLIST_GET_NUM_FCN (NMSettingBridge, nm_setting_bridge_get_num_vlans), + .clear_all_fcn = OBJLIST_CLEAR_ALL_FCN (NMSettingBridge, nm_setting_bridge_clear_vlans), + .obj_to_str_fcn = _objlist_obj_to_str_fcn_bridge_vlans, + .set_fcn = _objlist_set_fcn_bridge_vlans, + ), + ), + ), NULL }; @@ -4838,6 +4905,17 @@ static const NMMetaPropertyInfo *const property_infos_BRIDGE_PORT[] = { .prompt = N_("Hairpin [no]"), .property_type = &_pt_gobject_bool, ), + PROPERTY_INFO_WITH_DESC (NM_SETTING_BRIDGE_PORT_VLANS, + .property_type = &_pt_objlist, + .property_typ_data = DEFINE_PROPERTY_TYP_DATA ( + PROPERTY_TYP_DATA_SUBTYPE (objlist, + .get_num_fcn = OBJLIST_GET_NUM_FCN (NMSettingBridgePort, nm_setting_bridge_port_get_num_vlans), + .clear_all_fcn = OBJLIST_CLEAR_ALL_FCN (NMSettingBridgePort, nm_setting_bridge_port_clear_vlans), + .obj_to_str_fcn = _objlist_obj_to_str_fcn_bridge_vlans, + .set_fcn = _objlist_set_fcn_bridge_vlans, + ), + ), + ), NULL }; From be2fbd0c694cefe68c51ba463ba6f543120a640d Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Sat, 16 Mar 2019 17:22:14 +0100 Subject: [PATCH 11/13] platform: add @family argument to new-link function This will be used to create bridge vlans, which require an AF_BRIDGE message family. --- src/platform/nm-linux-platform.c | 122 ++++++++++++------------------- 1 file changed, 46 insertions(+), 76 deletions(-) diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 977ee885df..1c74137534 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -3842,15 +3842,17 @@ nla_put_failure: } static struct nl_msg * -_nl_msg_new_link (int nlmsg_type, - int nlmsg_flags, - int ifindex, - const char *ifname, - unsigned flags_mask, - unsigned flags_set) +_nl_msg_new_link_full (int nlmsg_type, + int nlmsg_flags, + int ifindex, + const char *ifname, + guint8 family, + unsigned flags_mask, + unsigned flags_set) { nm_auto_nlmsg struct nl_msg *msg = NULL; const struct ifinfomsg ifi = { + .ifi_family = family, .ifi_change = flags_mask, .ifi_flags = flags_set, .ifi_index = ifindex, @@ -3872,6 +3874,15 @@ nla_put_failure: g_return_val_if_reached (NULL); } +static struct nl_msg * +_nl_msg_new_link (int nlmsg_type, + int nlmsg_flags, + int ifindex, + const char *ifname) +{ + return _nl_msg_new_link_full (nlmsg_type, nlmsg_flags, ifindex, ifname, AF_UNSPEC, 0, 0); +} + /* Copied and modified from libnl3's build_addr_msg(). */ static struct nl_msg * _nl_msg_new_address (int nlmsg_type, @@ -5574,9 +5585,7 @@ do_request_link_no_delayed_actions (NMPlatform *platform, int ifindex, const cha nlmsg = _nl_msg_new_link (RTM_GETLINK, 0, ifindex, - name, - 0, - 0); + name); if (nlmsg) { nle = _nl_send_nlmsg (platform, nlmsg, NULL, NULL, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); if (nle < 0) { @@ -6274,9 +6283,7 @@ link_add (NMPlatform *platform, nlmsg = _nl_msg_new_link (RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, 0, - name, - 0, - 0); + name); if (!nlmsg) return -NME_UNSPEC; @@ -6305,9 +6312,7 @@ link_delete (NMPlatform *platform, int ifindex) nlmsg = _nl_msg_new_link (RTM_DELLINK, 0, ifindex, - NULL, - 0, - 0); + NULL); nmp_object_stackinit_id_link (&obj_id, ifindex); return do_delete_object (platform, &obj_id, nlmsg); @@ -6330,9 +6335,7 @@ link_set_netns (NMPlatform *platform, nlmsg = _nl_msg_new_link (RTM_NEWLINK, 0, ifindex, - NULL, - 0, - 0); + NULL); if (!nlmsg) return FALSE; @@ -6359,12 +6362,13 @@ link_change_flags (NMPlatform *platform, nm_platform_link_flags2str (flags_set, s_flags, sizeof (s_flags)), nm_platform_link_flags2str (flags_mask, NULL, 0)); - nlmsg = _nl_msg_new_link (RTM_NEWLINK, - 0, - ifindex, - NULL, - flags_mask, - flags_set); + nlmsg = _nl_msg_new_link_full (RTM_NEWLINK, + 0, + ifindex, + NULL, + AF_UNSPEC, + flags_mask, + flags_set); if (!nlmsg) return -NME_UNSPEC; return do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL); @@ -6428,9 +6432,7 @@ link_set_user_ipv6ll_enabled (NMPlatform *platform, int ifindex, gboolean enable nlmsg = _nl_msg_new_link (RTM_NEWLINK, 0, ifindex, - NULL, - 0, - 0); + NULL); if ( !nlmsg || !_nl_msg_new_link_set_afspec (nlmsg, mode, NULL)) g_return_val_if_reached (-NME_BUG); @@ -6447,7 +6449,7 @@ link_set_token (NMPlatform *platform, int ifindex, NMUtilsIPv6IfaceId iid) _LOGD ("link: change %d: token: set IPv6 address generation token to %s", ifindex, nm_utils_inet6_interface_identifier_to_token (iid, sbuf)); - nlmsg = _nl_msg_new_link (RTM_NEWLINK, 0, ifindex, NULL, 0, 0); + nlmsg = _nl_msg_new_link (RTM_NEWLINK, 0, ifindex, NULL); if (!nlmsg || !_nl_msg_new_link_set_afspec (nlmsg, -1, &iid)) g_return_val_if_reached (FALSE); @@ -6528,9 +6530,7 @@ link_set_address (NMPlatform *platform, int ifindex, gconstpointer address, size nlmsg = _nl_msg_new_link (RTM_NEWLINK, 0, ifindex, - NULL, - 0, - 0); + NULL); if (!nlmsg) g_return_val_if_reached (-NME_BUG); @@ -6549,9 +6549,7 @@ link_set_name (NMPlatform *platform, int ifindex, const char *name) nlmsg = _nl_msg_new_link (RTM_NEWLINK, 0, ifindex, - NULL, - 0, - 0); + NULL); if (!nlmsg) g_return_val_if_reached (-NME_BUG); @@ -6584,9 +6582,7 @@ link_set_mtu (NMPlatform *platform, int ifindex, guint32 mtu) nlmsg = _nl_msg_new_link (RTM_NEWLINK, 0, ifindex, - NULL, - 0, - 0); + NULL); if (!nlmsg) return FALSE; @@ -6702,9 +6698,7 @@ link_set_sriov_vfs (NMPlatform *platform, int ifindex, const NMPlatformVF *const nlmsg = _nl_msg_new_link (RTM_NEWLINK, 0, ifindex, - NULL, - 0, - 0); + NULL); if (!nlmsg) g_return_val_if_reached (-NME_BUG); @@ -6832,9 +6826,7 @@ vlan_add (NMPlatform *platform, nlmsg = _nl_msg_new_link (RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, 0, - name, - 0, - 0); + name); if (!nlmsg) return FALSE; @@ -6868,9 +6860,7 @@ link_gre_add (NMPlatform *platform, nlmsg = _nl_msg_new_link (RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, 0, - name, - 0, - 0); + name); if (!nlmsg) return FALSE; @@ -6920,9 +6910,7 @@ link_ip6tnl_add (NMPlatform *platform, nlmsg = _nl_msg_new_link (RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, 0, - name, - 0, - 0); + name); if (!nlmsg) return FALSE; @@ -6976,9 +6964,7 @@ link_ip6gre_add (NMPlatform *platform, nlmsg = _nl_msg_new_link (RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, 0, - name, - 0, - 0); + name); if (!nlmsg) return FALSE; @@ -7035,9 +7021,7 @@ link_ipip_add (NMPlatform *platform, nlmsg = _nl_msg_new_link (RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, 0, - name, - 0, - 0); + name); if (!nlmsg) return FALSE; @@ -7079,9 +7063,7 @@ link_macsec_add (NMPlatform *platform, nlmsg = _nl_msg_new_link (RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, 0, - name, - 0, - 0); + name); if (!nlmsg) return FALSE; @@ -7136,9 +7118,7 @@ link_macvlan_add (NMPlatform *platform, nlmsg = _nl_msg_new_link (RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, 0, - name, - 0, - 0); + name); if (!nlmsg) return FALSE; @@ -7178,9 +7158,7 @@ link_sit_add (NMPlatform *platform, nlmsg = _nl_msg_new_link (RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, 0, - name, - 0, - 0); + name); if (!nlmsg) return FALSE; @@ -7280,9 +7258,7 @@ link_vxlan_add (NMPlatform *platform, nlmsg = _nl_msg_new_link (RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, 0, - name, - 0, - 0); + name); if (!nlmsg) return FALSE; @@ -7346,9 +7322,7 @@ link_6lowpan_add (NMPlatform *platform, nlmsg = _nl_msg_new_link (RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, 0, - name, - 0, - 0); + name); if (!nlmsg) return FALSE; @@ -7496,9 +7470,7 @@ link_vlan_change (NMPlatform *platform, nlmsg = _nl_msg_new_link (RTM_NEWLINK, 0, ifindex, - NULL, - 0, - 0); + NULL); if ( !nlmsg || !_nl_msg_new_link_set_linkinfo_vlan (nlmsg, -1, @@ -7522,9 +7494,7 @@ link_enslave (NMPlatform *platform, int master, int slave) nlmsg = _nl_msg_new_link (RTM_NEWLINK, 0, ifindex, - NULL, - 0, - 0); + NULL); if (!nlmsg) return FALSE; From fafde171ea67fc66c1db38454fb278c2e9763726 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Sat, 16 Mar 2019 17:22:35 +0100 Subject: [PATCH 12/13] platform: add support for bridge vlans --- src/platform/nm-linux-platform.c | 73 +++++++++++++++++++++++++++++++- src/platform/nm-platform.c | 40 +++++++++++++++++ src/platform/nm-platform.h | 9 ++++ 3 files changed, 121 insertions(+), 1 deletion(-) diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 1c74137534..57a017cd4a 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -277,6 +278,14 @@ struct _ifla_vf_vlan_info { /*****************************************************************************/ +/* Appeared in in kernel 4.0 dated April 12, 2015 */ +#ifndef BRIDGE_VLAN_INFO_RANGE_BEGIN +#define BRIDGE_VLAN_INFO_RANGE_BEGIN (1 << 3) /* VLAN is start of vlan range */ +#define BRIDGE_VLAN_INFO_RANGE_END (1 << 4) /* VLAN is end of vlan range */ +#endif + +/*****************************************************************************/ + typedef enum { INFINIBAND_ACTION_CREATE_CHILD, INFINIBAND_ACTION_DELETE_CHILD, @@ -3858,7 +3867,7 @@ _nl_msg_new_link_full (int nlmsg_type, .ifi_index = ifindex, }; - nm_assert (NM_IN_SET (nlmsg_type, RTM_DELLINK, RTM_NEWLINK, RTM_GETLINK)); + nm_assert (NM_IN_SET (nlmsg_type, RTM_DELLINK, RTM_NEWLINK, RTM_GETLINK, RTM_SETLINK)); msg = nlmsg_alloc_simple (nlmsg_type, nlmsg_flags); @@ -6781,6 +6790,67 @@ nla_put_failure: g_return_val_if_reached (FALSE); } +static gboolean +link_set_bridge_vlans (NMPlatform *platform, + int ifindex, + gboolean on_master, + const NMPlatformBridgeVlan *const *vlans) +{ + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + struct nlattr *list; + struct bridge_vlan_info vinfo = { }; + guint i; + + nlmsg = _nl_msg_new_link_full (vlans ? RTM_SETLINK : RTM_DELLINK, + 0, + ifindex, + NULL, + AF_BRIDGE, + 0, + 0); + if (!nlmsg) + g_return_val_if_reached (-NME_BUG); + + if (!(list = nla_nest_start (nlmsg, IFLA_AF_SPEC))) + goto nla_put_failure; + + NLA_PUT_U16 (nlmsg, + IFLA_BRIDGE_FLAGS, + on_master ? BRIDGE_FLAGS_MASTER : BRIDGE_FLAGS_SELF); + + if (vlans) { + /* Add VLANs */ + for (i = 0; vlans[i]; i++) { + const NMPlatformBridgeVlan *vlan = vlans[i]; + + vinfo.vid = vlan->vid; + vinfo.flags = 0; + + if (vlan->untagged) + vinfo.flags |= BRIDGE_VLAN_INFO_UNTAGGED; + if (vlan->pvid) + vinfo.flags |= BRIDGE_VLAN_INFO_PVID; + + NLA_PUT (nlmsg, IFLA_BRIDGE_VLAN_INFO, sizeof (vinfo), &vinfo); + } + } else { + /* Flush existing VLANs */ + vinfo.vid = 1; + vinfo.flags = BRIDGE_VLAN_INFO_RANGE_BEGIN; + NLA_PUT (nlmsg, IFLA_BRIDGE_VLAN_INFO, sizeof (vinfo), &vinfo); + + vinfo.vid = 4094; + vinfo.flags = BRIDGE_VLAN_INFO_RANGE_END; + NLA_PUT (nlmsg, IFLA_BRIDGE_VLAN_INFO, sizeof (vinfo), &vinfo); + } + + nla_nest_end (nlmsg, list); + + return (do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) >= 0); +nla_put_failure: + g_return_val_if_reached (FALSE); +} + static char * link_get_physical_port_id (NMPlatform *platform, int ifindex) { @@ -8921,6 +8991,7 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->link_set_name = link_set_name; platform_class->link_set_sriov_params = link_set_sriov_params; platform_class->link_set_sriov_vfs = link_set_sriov_vfs; + platform_class->link_set_bridge_vlans = link_set_bridge_vlans; platform_class->link_get_physical_port_id = link_get_physical_port_id; platform_class->link_get_dev_id = link_get_dev_id; diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 8500c2faa4..ffab6ac28c 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -1518,6 +1518,28 @@ nm_platform_link_set_sriov_vfs (NMPlatform *self, int ifindex, const NMPlatformV return klass->link_set_sriov_vfs (self, ifindex, vfs); } +gboolean +nm_platform_link_set_bridge_vlans (NMPlatform *self, int ifindex, gboolean on_master, const NMPlatformBridgeVlan *const *vlans) +{ + guint i; + _CHECK_SELF (self, klass, FALSE); + + g_return_val_if_fail (ifindex > 0, FALSE); + + _LOG3D ("link: %s bridge VLANs on %s", + vlans ? "setting" : "clearing", + on_master ? "master" : "self"); + if (vlans) { + for (i = 0; vlans[i]; i++) { + const NMPlatformBridgeVlan *vlan = vlans[i]; + + _LOG3D ("link: bridge VLAN %s", nm_platform_bridge_vlan_to_string (vlan, NULL, 0)); + } + } + + return klass->link_set_bridge_vlans (self, ifindex, on_master, vlans); +} + /** * nm_platform_link_set_up: * @self: platform instance @@ -6506,6 +6528,24 @@ nm_platform_vf_to_string (const NMPlatformVF *vf, char *buf, gsize len) return buf; } + +const char * +nm_platform_bridge_vlan_to_string (const NMPlatformBridgeVlan *vlan, char *buf, gsize len) +{ + if (!nm_utils_to_string_buffer_init_null (vlan, &buf, &len)) + return buf; + + g_snprintf (buf, len, + "%u" + "%s" + "%s", + vlan->vid, + vlan->pvid ? " PVID" : "", + vlan->untagged ? " untagged" : ""); + + return buf; +} + void nm_platform_link_hash_update (const NMPlatformLink *obj, NMHashState *h) { diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 06231a1dc9..2e30f8b9ce 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -670,6 +670,12 @@ typedef struct { gint8 trust; } NMPlatformVF; +typedef struct { + guint16 vid; + bool untagged:1; + bool pvid:1; +} NMPlatformBridgeVlan; + typedef struct { in_addr_t local; in_addr_t remote; @@ -893,6 +899,7 @@ typedef struct { gboolean (*link_set_name) (NMPlatform *, int ifindex, const char *name); gboolean (*link_set_sriov_params) (NMPlatform *, int ifindex, guint num_vfs, int autoprobe); gboolean (*link_set_sriov_vfs) (NMPlatform *self, int ifindex, const NMPlatformVF *const *vfs); + gboolean (*link_set_bridge_vlans) (NMPlatform *self, int ifindex, gboolean on_master, const NMPlatformBridgeVlan *const *vlans); char * (*link_get_physical_port_id) (NMPlatform *, int ifindex); guint (*link_get_dev_id) (NMPlatform *, int ifindex); @@ -1326,6 +1333,7 @@ int nm_platform_link_set_mtu (NMPlatform *self, int ifindex, guint32 mtu); gboolean nm_platform_link_set_name (NMPlatform *self, int ifindex, const char *name); gboolean nm_platform_link_set_sriov_params (NMPlatform *self, int ifindex, guint num_vfs, int autoprobe); gboolean nm_platform_link_set_sriov_vfs (NMPlatform *self, int ifindex, const NMPlatformVF *const *vfs); +gboolean nm_platform_link_set_bridge_vlans (NMPlatform *self, int ifindex, gboolean on_master, const NMPlatformBridgeVlan *const *vlans); char *nm_platform_link_get_physical_port_id (NMPlatform *self, int ifindex); guint nm_platform_link_get_dev_id (NMPlatform *self, int ifindex); @@ -1585,6 +1593,7 @@ const char *nm_platform_routing_rule_to_string (const NMPlatformRoutingRule *rou const char *nm_platform_qdisc_to_string (const NMPlatformQdisc *qdisc, char *buf, gsize len); const char *nm_platform_tfilter_to_string (const NMPlatformTfilter *tfilter, char *buf, gsize len); const char *nm_platform_vf_to_string (const NMPlatformVF *vf, char *buf, gsize len); +const char *nm_platform_bridge_vlan_to_string (const NMPlatformBridgeVlan *vlan, char *buf, gsize len); const char *nm_platform_vlan_qos_mapping_to_string (const char *name, const NMVlanQosMapping *map, From 494f78440c6abb398fd4aaaeeaa624a968f4d6e6 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Sat, 16 Mar 2019 17:22:57 +0100 Subject: [PATCH 13/13] device: support bridge vlans --- src/devices/nm-device-bridge.c | 67 ++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 3 deletions(-) diff --git a/src/devices/nm-device-bridge.c b/src/devices/nm-device-bridge.c index 9528a3af90..1d2f61e6c2 100644 --- a/src/devices/nm-device-bridge.c +++ b/src/devices/nm-device-bridge.c @@ -268,11 +268,39 @@ commit_option (NMDevice *device, NMSetting *setting, const Option *option, gbool nm_platform_sysctl_master_set_option (nm_device_get_platform (device), ifindex, option->sysname, value); } +static NMPlatformBridgeVlan ** +setting_vlans_to_platform (GPtrArray *array) +{ + GPtrArray *plat_vlans; + guint i; + + if (!array || !array->len) + return NULL; + + plat_vlans = g_ptr_array_sized_new (array->len + 1); + + for (i = 0; i < array->len; i++) { + NMBridgeVlan *vlan = array->pdata[i]; + NMPlatformBridgeVlan *plat_vlan; + + plat_vlan = g_new0 (NMPlatformBridgeVlan, 1); + plat_vlan->vid = nm_bridge_vlan_get_vid (vlan); + plat_vlan->pvid = nm_bridge_vlan_is_pvid (vlan); + plat_vlan->untagged = nm_bridge_vlan_is_untagged (vlan); + + g_ptr_array_add (plat_vlans, plat_vlan); + } + g_ptr_array_add (plat_vlans, NULL); + + return (NMPlatformBridgeVlan **) g_ptr_array_free (plat_vlans, FALSE); +} + static void commit_slave_options (NMDevice *device, NMSettingBridgePort *setting) { const Option *option; - NMSetting *s, *s_clear = NULL; + NMSetting *s; + gs_unref_object NMSetting *s_clear = NULL; if (setting) s = NM_SETTING (setting); @@ -281,8 +309,6 @@ commit_slave_options (NMDevice *device, NMSettingBridgePort *setting) for (option = slave_options; option->name; option++) commit_option (device, s, option, TRUE); - - g_clear_object (&s_clear); } static void @@ -397,6 +423,8 @@ bridge_set_vlan_options (NMDevice *device, NMSettingBridge *s_bridge) guint16 pvid; NMPlatform *plat; int ifindex; + gs_unref_ptrarray GPtrArray *vlans = NULL; + nm_auto_freev NMPlatformBridgeVlan **plat_vlans = NULL; if (self->vlan_configured) return TRUE; @@ -408,6 +436,7 @@ bridge_set_vlan_options (NMDevice *device, NMSettingBridge *s_bridge) if (!enabled) { nm_platform_sysctl_master_set_option (plat, ifindex, "vlan_filtering", "0"); nm_platform_sysctl_master_set_option (plat, ifindex, "default_pvid", "1"); + nm_platform_link_set_bridge_vlans (plat, ifindex, FALSE, NULL); return TRUE; } @@ -430,6 +459,10 @@ bridge_set_vlan_options (NMDevice *device, NMSettingBridge *s_bridge) if (!nm_platform_sysctl_master_set_option (plat, ifindex, "default_pvid", "0")) return FALSE; + /* Clear all existing VLANs */ + if (!nm_platform_link_set_bridge_vlans (plat, ifindex, FALSE, NULL)) + return FALSE; + /* Now set the default PVID. After this point the kernel creates * a PVID VLAN on each port, including the bridge itself. */ pvid = nm_setting_bridge_get_vlan_default_pvid (s_bridge); @@ -441,6 +474,15 @@ bridge_set_vlan_options (NMDevice *device, NMSettingBridge *s_bridge) return FALSE; } + /* Create VLANs only after setting the default PVID, so that + * any PVID VLAN overrides the bridge's default PVID. */ + g_object_get (s_bridge, NM_SETTING_BRIDGE_VLANS, &vlans, NULL); + plat_vlans = setting_vlans_to_platform (vlans); + if ( plat_vlans + && !nm_platform_link_set_bridge_vlans (plat, ifindex, FALSE, + (const NMPlatformBridgeVlan *const *) plat_vlans)) + return FALSE; + if (!nm_platform_sysctl_master_set_option (plat, ifindex, "vlan_filtering", "1")) return FALSE; @@ -526,6 +568,8 @@ enslave_slave (NMDevice *device, NMConnection *master_connection; NMSettingBridge *s_bridge; NMSettingBridgePort *s_port; + gs_unref_ptrarray GPtrArray *vlans = NULL; + nm_auto_freev NMPlatformBridgeVlan **plat_vlans = NULL; if (configure) { if (!nm_platform_link_enslave (nm_device_get_platform (device), nm_device_get_ip_ifindex (device), nm_device_get_ip_ifindex (slave))) @@ -538,6 +582,23 @@ enslave_slave (NMDevice *device, s_port = nm_connection_get_setting_bridge_port (connection); bridge_set_vlan_options (device, s_bridge); + + if (nm_setting_bridge_get_vlan_filtering (s_bridge)) { + if (s_port) + g_object_get (s_port, NM_SETTING_BRIDGE_PORT_VLANS, &vlans, NULL); + plat_vlans = setting_vlans_to_platform (vlans); + + /* Since the link was just enslaved, there are no existing VLANs + * (except for the default one) and so there's no need to flush. */ + + if ( plat_vlans + && !nm_platform_link_set_bridge_vlans (nm_device_get_platform (slave), + nm_device_get_ifindex (slave), + TRUE, + (const NMPlatformBridgeVlan *const *) plat_vlans)) + return FALSE; + } + commit_slave_options (slave, s_port); _LOGI (LOGD_BRIDGE, "attached bridge port %s",