diff options
author | Beniamino Galvani <bgalvani@redhat.com> | 2019-04-18 09:53:52 +0200 |
---|---|---|
committer | Beniamino Galvani <bgalvani@redhat.com> | 2019-04-18 09:53:52 +0200 |
commit | 73e32e43c8ca08adc3500d603eacb9133e40ed3c (patch) | |
tree | 26e28318f0eee78ed25ecce6a0120e0e8c699c6f | |
parent | fd8b78dd6a75a6dedfd65bb5c3b523cdf64702b4 (diff) | |
parent | 05a547133b3e98c6c08f52df2923cfacc4ca8400 (diff) |
bridge: merge branch 'bg/bridge-vlan-ranges'
In some cases it is convenient to specify ranges of bridge vlans, as
already supported by iproute2 and natively by kernel.
https://bugzilla.redhat.com/show_bug.cgi?id=1652910
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/merge_requests/114
(cherry picked from commit 693252d049e76cc456f24425cc024b4a8ccd4e59)
-rw-r--r-- | clients/common/nm-meta-setting-desc.c | 24 | ||||
-rw-r--r-- | clients/common/settings-docs.h.in | 4 | ||||
-rw-r--r-- | libnm-core/nm-keyfile.c | 98 | ||||
-rw-r--r-- | libnm-core/nm-setting-bridge-port.c | 24 | ||||
-rw-r--r-- | libnm-core/nm-setting-bridge-port.h | 4 | ||||
-rw-r--r-- | libnm-core/nm-setting-bridge.c | 143 | ||||
-rw-r--r-- | libnm-core/nm-setting-bridge.h | 8 | ||||
-rw-r--r-- | libnm-core/nm-utils.c | 75 | ||||
-rw-r--r-- | libnm-core/tests/test-keyfile.c | 43 | ||||
-rw-r--r-- | libnm-core/tests/test-setting.c | 15 | ||||
-rw-r--r-- | libnm/libnm.ver | 2 | ||||
-rw-r--r-- | src/devices/nm-device-bridge.c | 43 | ||||
-rw-r--r-- | src/platform/nm-linux-platform.c | 11 | ||||
-rw-r--r-- | src/platform/nm-platform.c | 6 | ||||
-rw-r--r-- | src/platform/nm-platform.h | 3 | ||||
-rw-r--r-- | src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c | 2 | ||||
-rw-r--r-- | src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c | 4 | ||||
-rw-r--r-- | src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bridge_Component.cexpected | 2 | ||||
-rw-r--r-- | src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c | 14 |
19 files changed, 341 insertions, 184 deletions
diff --git a/clients/common/nm-meta-setting-desc.c b/clients/common/nm-meta-setting-desc.c index 2a4e5b8269..334b531f1b 100644 --- a/clients/common/nm-meta-setting-desc.c +++ b/clients/common/nm-meta-setting-desc.c @@ -167,8 +167,8 @@ _value_str_as_index_list (const char *value, gsize *out_len) #define MULTILIST_WITH_ESCAPE_CHARS NM_ASCII_SPACES"," -#define ESCAPED_TOKENS_DELIMTER ',' -#define ESCAPED_TOKENS_DELIMTERS "," +#define ESCAPED_TOKENS_DELIMITER ',' +#define ESCAPED_TOKENS_DELIMITERS "," typedef enum { VALUE_STRSPLIT_MODE_STRIPPED, @@ -216,7 +216,7 @@ _value_strsplit (const char *value, strv = nm_utils_strsplit_set_full (value, MULTILIST_WITH_ESCAPE_CHARS, NM_UTILS_STRSPLIT_SET_FLAGS_ALLOW_ESCAPING); break; case VALUE_STRSPLIT_MODE_ESCAPED_TOKENS: - strv = nm_utils_escaped_tokens_split (value, ESCAPED_TOKENS_DELIMTERS); + strv = nm_utils_escaped_tokens_split (value, ESCAPED_TOKENS_DELIMITERS); NM_SET_OUT (out_len, NM_PTRARRAY_LEN (strv)); return g_steal_pointer (&strv); default: @@ -3072,7 +3072,7 @@ _get_fcn_objlist (ARGS_GET_FCN) && property_info->property_typ_data->subtype.objlist.delimit_pretty_with_semicolon) g_string_append (str, "; "); else { - G_STATIC_ASSERT_EXPR (ESCAPED_TOKENS_DELIMTER == ','); + G_STATIC_ASSERT_EXPR (ESCAPED_TOKENS_DELIMITER == ','); g_string_append (str, ", "); } } @@ -3400,7 +3400,7 @@ _objlist_obj_to_str_fcn_ip_config_routing_rules (NMMetaAccessorGetType get_type, NULL, NULL); if (s) - nm_utils_escaped_tokens_escape_gstr (s, ESCAPED_TOKENS_DELIMTERS, str); + nm_utils_escaped_tokens_escape_gstr (s, ESCAPED_TOKENS_DELIMITERS, str); } static gboolean @@ -3559,7 +3559,7 @@ _objlist_obj_to_str_fcn_bridge_vlans (NMMetaAccessorGetType get_type, s = nm_bridge_vlan_to_str (vlan, NULL); if (s) - g_string_append (str, s); + nm_utils_escaped_tokens_escape_gstr_assert (s, ESCAPED_TOKENS_DELIMITERS, str); } static gboolean @@ -3618,13 +3618,14 @@ _objlist_set_fcn_bridge_vlans (NMSetting *setting, { nm_auto_unref_bridge_vlan NMBridgeVlan *vlan = NULL; gs_free_error GError *local = NULL; + guint16 vid_start, vid_end; 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: '<vid> [pvid] [untagged]")); + _("The valid syntax is: '<vid>[-<vid>] [pvid] [untagged]'")); return FALSE; } @@ -3632,15 +3633,18 @@ _objlist_set_fcn_bridge_vlans (NMSetting *setting, if (do_add) nm_setting_bridge_add_vlan (NM_SETTING_BRIDGE (setting), vlan); else { + nm_bridge_vlan_get_vid_range (vlan, &vid_start, &vid_end); nm_setting_bridge_remove_vlan_by_vid (NM_SETTING_BRIDGE (setting), - nm_bridge_vlan_get_vid (vlan)); + vid_start, vid_end); } } else { if (do_add) nm_setting_bridge_port_add_vlan (NM_SETTING_BRIDGE_PORT (setting), vlan); else { + nm_bridge_vlan_get_vid_range (vlan, &vid_start, &vid_end); nm_setting_bridge_port_remove_vlan_by_vid (NM_SETTING_BRIDGE_PORT (setting), - nm_bridge_vlan_get_vid (vlan)); + vid_start, + vid_end); } } @@ -4987,6 +4991,7 @@ static const NMMetaPropertyInfo *const property_infos_BRIDGE[] = { .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, + .strsplit_escaped_tokens = TRUE, ), ), ), @@ -5022,6 +5027,7 @@ static const NMMetaPropertyInfo *const property_infos_BRIDGE_PORT[] = { .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, + .strsplit_escaped_tokens = TRUE, ), ), ), diff --git a/clients/common/settings-docs.h.in b/clients/common/settings-docs.h.in index 59ca4cc8a6..811f2f16d6 100644 --- a/clients/common/settings-docs.h.in +++ b/clients/common/settings-docs.h.in @@ -120,11 +120,11 @@ #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_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]]... where $vid is either a single id between 1 and 4094 or a range, represented as a couple of ids separated by a dash.") #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_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]]... where $vid is either a single id between 1 and 4094 or a range, represented as a couple of ids separated by a dash.") #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-keyfile.c b/libnm-core/nm-keyfile.c index 773b4ba860..a12fd3dcdf 100644 --- a/libnm-core/nm-keyfile.c +++ b/libnm-core/nm-keyfile.c @@ -1626,51 +1626,37 @@ team_config_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key 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) + gs_free char *value = NULL; + gs_free const char **strv = NULL; + const char *const *iter; + GError *local = NULL; + NMBridgeVlan *vlan; + + value = nm_keyfile_plugin_kf_get_string (info->keyfile, + nm_setting_get_name (setting), + key, + NULL); + if (!value || !value[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; + strv = nm_utils_escaped_tokens_split (value, ","); + if (strv) { + for (iter = strv; *iter; iter++) { + vlan = nm_bridge_vlan_from_str (*iter, &local); + if (!vlan) { + handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, + "invalid bridge VLAN: %s", local->message); + g_clear_error (&local); + continue; + } + g_ptr_array_add (vlans, vlan); } - g_ptr_array_add (vlans, vlan); } - if (vlans->len >= 1) + if (vlans->len > 0) g_object_set (setting, key, vlans, NULL); } @@ -2004,26 +1990,34 @@ bridge_vlan_writer (KeyfileWriterInfo *info, const char *key, const GValue *value) { - gsize i; - GPtrArray *array; - nm_auto_free_gstring GString *value_str = NULL; + NMBridgeVlan *vlan; + GPtrArray *vlans; + GString *string; + guint i; - array = (GPtrArray *) g_value_get_boxed (value); - if (!array || !array->len) + vlans = (GPtrArray *) g_value_get_boxed (value); + if (!vlans || !vlans->len) return; - for (i = 0; i < array->len; i++) { - NMBridgeVlan *vlan = array->pdata[i]; - char key_name[32]; + string = g_string_new (""); + for (i = 0; i < vlans->len; i++) { + gs_free char *vlan_str = NULL; - 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); + vlan = vlans->pdata[i]; + vlan_str = nm_bridge_vlan_to_str (vlan, NULL); + if (!vlan_str) + continue; + if (string->len > 0) + g_string_append (string, ","); + nm_utils_escaped_tokens_escape_gstr_assert (vlan_str, ",", string); } + + nm_keyfile_plugin_kf_set_string (info->keyfile, + nm_setting_get_name (setting), + "vlans", + string->str); + + g_string_free (string, TRUE); } static void diff --git a/libnm-core/nm-setting-bridge-port.c b/libnm-core/nm-setting-bridge-port.c index ebac2fc2c3..7a8b345f01 100644 --- a/libnm-core/nm-setting-bridge-port.c +++ b/libnm-core/nm-setting-bridge-port.c @@ -235,9 +235,12 @@ nm_setting_bridge_port_remove_vlan (NMSettingBridgePort *setting, guint idx) /** * nm_setting_bridge_port_remove_vlan_by_vid: * @setting: the #NMSettingBridgePort - * @vid: the vlan index of the vlan to remove + * @vid_start: the vlan start index + * @vid_end: the vlan end index * - * Removes the vlan vith id @vid. + * Remove the VLAN with range @vid_start to @vid_end. + * If @vid_end is zero, it is assumed to be equal to @vid_start + * and so the single-id VLAN with id @vid_start is removed. * * Returns: %TRUE if the vlan was found and removed; %FALSE otherwise * @@ -245,16 +248,24 @@ nm_setting_bridge_port_remove_vlan (NMSettingBridgePort *setting, guint idx) **/ gboolean nm_setting_bridge_port_remove_vlan_by_vid (NMSettingBridgePort *setting, - guint16 vid) + guint16 vid_start, + guint16 vid_end) { NMSettingBridgePortPrivate *priv; + guint16 v_start, v_end; + NMBridgeVlan *vlan; guint i; + if (vid_end == 0) + vid_end = vid_start; + 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) { + vlan = (NMBridgeVlan *) priv->vlans->pdata[i]; + nm_bridge_vlan_get_vid_range (vlan, &v_start, &v_end); + if (v_start == vid_start && v_end == vid_end) { g_ptr_array_remove_index (priv->vlans, i); _notify (setting, PROP_VLANS); return TRUE; @@ -556,13 +567,16 @@ nm_setting_bridge_port_class_init (NMSettingBridgePortClass *klass) * * $vid [pvid] [untagged] [, $vid [pvid] [untagged]]... * + * where $vid is either a single id between 1 and 4094 or a + * range, represented as a couple of ids separated by a dash. + * * 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" + * example: BRIDGE_PORT_VLANS="1 pvid untagged,20,300-400 untagged" * ---end--- */ obj_properties[PROP_VLANS] = diff --git a/libnm-core/nm-setting-bridge-port.h b/libnm-core/nm-setting-bridge-port.h index b1b1c8f40c..5b75c1ecde 100644 --- a/libnm-core/nm-setting-bridge-port.h +++ b/libnm-core/nm-setting-bridge-port.h @@ -81,7 +81,9 @@ NMBridgeVlan *nm_setting_bridge_port_get_vlan (NMSettingBridgePort *setting, gui 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); +gboolean nm_setting_bridge_port_remove_vlan_by_vid (NMSettingBridgePort *setting, + guint16 vid_start, + guint16 vid_end); NM_AVAILABLE_IN_1_18 void nm_setting_bridge_port_clear_vlans (NMSettingBridgePort *setting); diff --git a/libnm-core/nm-setting-bridge.c b/libnm-core/nm-setting-bridge.c index 590f6ea2bd..c6aca0211a 100644 --- a/libnm-core/nm-setting-bridge.c +++ b/libnm-core/nm-setting-bridge.c @@ -80,7 +80,8 @@ G_DEFINE_BOXED_TYPE (NMBridgeVlan, nm_bridge_vlan, _nm_bridge_vlan_dup, nm_bridg struct _NMBridgeVlan { guint refcount; - guint16 vid; + guint16 vid_start; + guint16 vid_end; bool untagged:1; bool pvid:1; bool sealed:1; @@ -96,25 +97,33 @@ NM_IS_BRIDGE_VLAN (const NMBridgeVlan *self, gboolean also_sealed) /** * nm_bridge_vlan_new: - * @vid: the VLAN id, must be between 1 and 4094. + * @vid_start: the start VLAN id, must be between 1 and 4094. + * @vid_end: the end VLAN id, must be 0 or between @vid_start and 4094. * - * Creates a new #NMBridgeVlan object. + * Creates a new #NMBridgeVlan object for the given VLAN id range. + * Setting @vid_end to 0 is equivalent to setting it to @vid_start + * and creates a single-id VLAN. * * Returns: (transfer full): the new #NMBridgeVlan object. * * Since: 1.18 **/ NMBridgeVlan * -nm_bridge_vlan_new (guint16 vid) +nm_bridge_vlan_new (guint16 vid_start, guint16 vid_end) { 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); + if (vid_end == 0) + vid_end = vid_start; + + g_return_val_if_fail (vid_start >= NM_BRIDGE_VLAN_VID_MIN, NULL); + g_return_val_if_fail (vid_end <= NM_BRIDGE_VLAN_VID_MAX, NULL); + g_return_val_if_fail (vid_start <= vid_end, NULL); vlan = g_slice_new0 (NMBridgeVlan); vlan->refcount = 1; - vlan->vid = vid; + vlan->vid_start = vid_start; + vlan->vid_end = vid_end; return vlan; } @@ -179,7 +188,8 @@ nm_bridge_vlan_cmp (const NMBridgeVlan *a, const NMBridgeVlan *b) 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 (a, b, vid_start); + NM_CMP_FIELD (a, b, vid_end); NM_CMP_FIELD_BOOL (a, b, untagged); NM_CMP_FIELD_BOOL (a, b, pvid); @@ -213,21 +223,29 @@ _nm_bridge_vlan_dup_and_seal (const NMBridgeVlan *vlan) } /** - * nm_bridge_vlan_get_vid: + * nm_bridge_vlan_get_vid_range: * @vlan: the #NMBridgeVlan + * @vid_start: location to store the VLAN id range start. + * @vid_end: location to store the VLAN id range end * - * Gets the VLAN id of the object. + * Gets the VLAN id range. * - * Returns: the VLAN id + * Returns: %TRUE is the VLAN specifies a range, %FALSE if it is + * a single-id VLAN. * * Since: 1.18 **/ -guint16 -nm_bridge_vlan_get_vid (const NMBridgeVlan *vlan) +gboolean +nm_bridge_vlan_get_vid_range (const NMBridgeVlan *vlan, + guint16 *vid_start, + guint16 *vid_end) { g_return_val_if_fail (NM_IS_BRIDGE_VLAN (vlan, TRUE), 0); - return vlan->vid; + NM_SET_OUT (vid_start, vlan->vid_start); + NM_SET_OUT (vid_end, vlan->vid_end); + + return vlan->vid_start != vlan->vid_end; } /** @@ -288,7 +306,9 @@ nm_bridge_vlan_set_untagged (NMBridgeVlan *vlan, gboolean value) * @vlan: the #NMBridgeVlan * @value: the new value * - * Change the value of the PVID property of the VLAN. + * Change the value of the PVID property of the VLAN. It + * is invalid to set the value to %TRUE for non-single-id + * VLANs. * * Since: 1.18 **/ @@ -296,6 +316,7 @@ void nm_bridge_vlan_set_pvid (NMBridgeVlan *vlan, gboolean value) { g_return_if_fail (NM_IS_BRIDGE_VLAN (vlan, FALSE)); + g_return_if_fail (!value || vlan->vid_start == vlan->vid_end); vlan->pvid = value; } @@ -351,7 +372,7 @@ nm_bridge_vlan_new_clone (const NMBridgeVlan *vlan) g_return_val_if_fail (NM_IS_BRIDGE_VLAN (vlan, TRUE), NULL); - copy = nm_bridge_vlan_new (vlan->vid); + copy = nm_bridge_vlan_new (vlan->vid_start, vlan->vid_end); copy->untagged = vlan->untagged; copy->pvid = vlan->pvid; @@ -400,9 +421,13 @@ nm_bridge_vlan_to_str (const NMBridgeVlan *vlan, GError **error) * future if more parameters are added to the object that could * make it invalid. */ - string = g_string_sized_new (20); + string = g_string_sized_new (28); + + if (vlan->vid_start == vlan->vid_end) + g_string_append_printf (string, "%u", vlan->vid_start); + else + g_string_append_printf (string, "%u-%u", vlan->vid_start, vlan->vid_end); - 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); @@ -425,14 +450,15 @@ nm_bridge_vlan_from_str (const char *str, GError **error) { NMBridgeVlan *vlan = NULL; gs_free const char **tokens = NULL; - guint i, vid; + guint i, vid_start, vid_end = 0; gboolean pvid = FALSE; gboolean untagged = FALSE; + char *c; g_return_val_if_fail (str, NULL); g_return_val_if_fail (!error || !*error, NULL); - tokens = nm_utils_strsplit_set (str, " "); + tokens = nm_utils_escaped_tokens_split (str, NM_ASCII_SPACES); if (!tokens || !tokens[0]) { g_set_error_literal (error, NM_CONNECTION_ERROR, @@ -441,23 +467,58 @@ nm_bridge_vlan_from_str (const char *str, GError **error) 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) { + c = strchr (tokens[0], '-'); + if (c) + *c = '\0'; + + vid_start = _nm_utils_ascii_str_to_uint64 (tokens[0], + 10, + NM_BRIDGE_VLAN_VID_MIN, + NM_BRIDGE_VLAN_VID_MAX, + G_MAXUINT); + if (vid_start == G_MAXUINT) { g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED, - "invalid VLAN id '%s', must be in [1,4094]", tokens[0]); + "invalid VLAN id range start '%s', must be in [1,4094]", tokens[0]); return NULL; } + if (c) { + vid_end = _nm_utils_ascii_str_to_uint64 (c + 1, + 10, + NM_BRIDGE_VLAN_VID_MIN, + NM_BRIDGE_VLAN_VID_MAX, + G_MAXUINT); + if (vid_end == G_MAXUINT) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + "invalid VLAN id range end '%s', must be in [1,4094]", c + 1); + return NULL; + } + if (vid_end < vid_start) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + "invalid VLAN id range %u-%u, start VLAN id must be less than end VLAN id", + vid_start, vid_end); + return NULL; + } + } else + vid_end = vid_start; + for (i = 1; tokens[i]; i++) { - if (nm_streq (tokens[i], "pvid")) + if (nm_streq (tokens[i], "pvid")) { + if (vid_start != vid_end) { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + "a VLAN range can't be a PVID"); + return NULL; + } pvid = TRUE; - else if (nm_streq (tokens[i], "untagged")) + } else if (nm_streq (tokens[i], "untagged")) untagged = TRUE; else { g_set_error (error, @@ -468,7 +529,7 @@ nm_bridge_vlan_from_str (const char *str, GError **error) } } - vlan = nm_bridge_vlan_new (vid); + vlan = nm_bridge_vlan_new (vid_start, vid_end); nm_bridge_vlan_set_pvid (vlan, pvid); nm_bridge_vlan_set_untagged (vlan, untagged); @@ -772,9 +833,12 @@ nm_setting_bridge_remove_vlan (NMSettingBridge *setting, guint idx) /** * nm_setting_bridge_remove_vlan_by_vid: * @setting: the #NMSettingBridge - * @vid: the vlan index of the vlan to remove + * @vid_start: the vlan start index + * @vid_end: the vlan end index * - * Removes the vlan vith id @vid. + * Remove the VLAN with range @vid_start to @vid_end. + * If @vid_end is zero, it is assumed to be equal to @vid_start + * and so the single-id VLAN with id @vid_start is removed. * * Returns: %TRUE if the vlan was found and removed; %FALSE otherwise * @@ -782,16 +846,22 @@ nm_setting_bridge_remove_vlan (NMSettingBridge *setting, guint idx) **/ gboolean nm_setting_bridge_remove_vlan_by_vid (NMSettingBridge *setting, - guint16 vid) + guint16 vid_start, + guint16 vid_end) { NMSettingBridgePrivate *priv; + NMBridgeVlan *vlan; guint i; g_return_val_if_fail (NM_IS_SETTING_BRIDGE (setting), FALSE); priv = NM_SETTING_BRIDGE_GET_PRIVATE (setting); + if (vid_end == 0) + vid_end = vid_start; + for (i = 0; i < priv->vlans->len; i++) { - if (nm_bridge_vlan_get_vid (priv->vlans->pdata[i]) == vid) { + vlan = (NMBridgeVlan *) priv->vlans->pdata[i]; + if (vlan->vid_start == vid_start && vlan->vid_end == vid_end) { g_ptr_array_remove_index (priv->vlans, i); _notify (setting, PROP_VLANS); return TRUE; @@ -1390,13 +1460,16 @@ nm_setting_bridge_class_init (NMSettingBridgeClass *klass) * * $vid [pvid] [untagged] [, $vid [pvid] [untagged]]... * + * where $vid is either a single id between 1 and 4094 or a + * range, represented as a couple of ids separated by a dash. + * * 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" + * example: BRIDGE_VLANS="1 pvid untagged,20,300-400 untagged" * ---end--- */ obj_properties[PROP_VLANS] = diff --git a/libnm-core/nm-setting-bridge.h b/libnm-core/nm-setting-bridge.h index 0ec0b2a8e3..c01ab35c74 100644 --- a/libnm-core/nm-setting-bridge.h +++ b/libnm-core/nm-setting-bridge.h @@ -108,14 +108,16 @@ 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); +gboolean nm_setting_bridge_remove_vlan_by_vid (NMSettingBridge *setting, + guint16 vid_start, + guint16 vid_end); 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); NM_AVAILABLE_IN_1_18 -NMBridgeVlan * nm_bridge_vlan_new (guint16 vid); +NMBridgeVlan * nm_bridge_vlan_new (guint16 vid_start, guint16 vid_end); NM_AVAILABLE_IN_1_18 NMBridgeVlan * nm_bridge_vlan_ref (NMBridgeVlan *vlan); NM_AVAILABLE_IN_1_18 @@ -133,7 +135,7 @@ 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); +gboolean nm_bridge_vlan_get_vid_range (const NMBridgeVlan *vlan, guint16 *vid_start, guint16 *vid_end); NM_AVAILABLE_IN_1_18 gboolean nm_bridge_vlan_is_untagged (const NMBridgeVlan *vlan); NM_AVAILABLE_IN_1_18 diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index af05cdfb6e..28bc24b1e1 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -6800,10 +6800,15 @@ _nm_utils_bridge_vlans_to_dbus (NMSetting *setting, const char *property) for (i = 0; i < vlans->len; i++) { NMBridgeVlan *vlan = vlans->pdata[i]; GVariantBuilder vlan_builder; + guint16 vid_start, vid_end; + + nm_bridge_vlan_get_vid_range (vlan, &vid_start, &vid_end); 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}", "vid-start", + g_variant_new_uint16 (vid_start)); + g_variant_builder_add (&vlan_builder, "{sv}", "vid-end", + g_variant_new_uint16 (vid_end)); 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", @@ -6834,19 +6839,29 @@ _nm_utils_bridge_vlans_from_dbus (NMSetting *setting, while (g_variant_iter_next (&vlan_iter, "@a{sv}", &vlan_var)) { _nm_unused gs_unref_variant GVariant *var_unref = vlan_var; NMBridgeVlan *vlan; - guint16 vid; + guint16 vid_start, vid_end; gboolean pvid = FALSE, untagged = FALSE; - if (!g_variant_lookup (vlan_var, "vid", "q", &vid)) + if (!g_variant_lookup (vlan_var, "vid-start", "q", &vid_start)) + continue; + if ( vid_start < NM_BRIDGE_VLAN_VID_MIN + || vid_start > NM_BRIDGE_VLAN_VID_MAX) + continue; + + if (!g_variant_lookup (vlan_var, "vid-end", "q", &vid_end)) + continue; + if ( vid_end < NM_BRIDGE_VLAN_VID_MIN + || vid_end > NM_BRIDGE_VLAN_VID_MAX) continue; - if ( vid < NM_BRIDGE_VLAN_VID_MIN - || vid > NM_BRIDGE_VLAN_VID_MAX) + if (vid_start > vid_end) continue; g_variant_lookup (vlan_var, "pvid", "b", &pvid); + if (pvid && vid_start != vid_end) + continue; g_variant_lookup (vlan_var, "untagged", "b", &untagged); - vlan = nm_bridge_vlan_new (vid); + vlan = nm_bridge_vlan_new (vid_start, vid_end); nm_bridge_vlan_set_untagged (vlan, untagged); nm_bridge_vlan_set_pvid (vlan, pvid); g_ptr_array_add (vlans, vlan); @@ -6868,24 +6883,33 @@ _nm_utils_bridge_vlan_verify_list (GPtrArray *vlans, gs_unref_hashtable GHashTable *h = NULL; gboolean pvid_found = FALSE; - if (!vlans || !vlans->len) + if ( !vlans + || vlans->len <= 1) return TRUE; if (check_normalizable) { + guint16 vid_prev_end, vid_start, vid_end; + + nm_assert (_nm_utils_bridge_vlan_verify_list (vlans, FALSE, NULL, setting, property)); + + nm_bridge_vlan_get_vid_range (vlans->pdata[0], NULL, &vid_prev_end); for (i = 1; i < vlans->len; i++) { - NMBridgeVlan *vlan_prev = vlans->pdata[i - 1]; - NMBridgeVlan *vlan = vlans->pdata[i]; + const NMBridgeVlan *vlan = vlans->pdata[i]; - if (nm_bridge_vlan_get_vid (vlan_prev) > nm_bridge_vlan_get_vid (vlan)) { + nm_bridge_vlan_get_vid_range (vlan, &vid_start, &vid_end); + + if (vid_prev_end > vid_start) { 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)); + vid_prev_end, + vid_start); g_prefix_error (error, "%s.%s: ", setting, property); return FALSE; } + + vid_prev_end = vid_end; } return TRUE; } @@ -6893,21 +6917,24 @@ _nm_utils_bridge_vlan_verify_list (GPtrArray *vlans, h = g_hash_table_new (nm_direct_hash, NULL); for (i = 0; i < vlans->len; i++) { NMBridgeVlan *vlan = vlans->pdata[i]; - guint vid; + guint16 v, vid_start, vid_end; - vid = nm_bridge_vlan_get_vid (vlan); + nm_bridge_vlan_get_vid_range (vlan, &vid_start, &vid_end); - 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; + for (v = vid_start; v <= vid_end; v++) { + if (!nm_g_hash_table_add (h, GUINT_TO_POINTER (v))) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("duplicate bridge VLAN vid %u"), v); + g_prefix_error (error, "%s.%s: ", setting, property); + return FALSE; + } } if (nm_bridge_vlan_is_pvid (vlan)) { - if (pvid_found) { + if ( vid_start != vid_end + || pvid_found) { g_set_error_literal (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, @@ -6917,8 +6944,6 @@ _nm_utils_bridge_vlan_verify_list (GPtrArray *vlans, } pvid_found = TRUE; } - - g_hash_table_add (h, GUINT_TO_POINTER (vid)); } return TRUE; diff --git a/libnm-core/tests/test-keyfile.c b/libnm-core/tests/test-keyfile.c index fbdd7ab27b..157e8f1868 100644 --- a/libnm-core/tests/test-keyfile.c +++ b/libnm-core/tests/test-keyfile.c @@ -751,6 +751,7 @@ test_bridge_vlans (void) gs_unref_object NMConnection *con = NULL; NMSettingBridge *s_bridge; NMBridgeVlan *vlan; + guint16 vid, vid_end; con = nmtst_create_connection_from_keyfile ( "[connection]\n" @@ -759,26 +760,37 @@ test_bridge_vlans (void) "interface-name=br4\n" "\n" "[bridge]\n" - "vlan.9=untagged\n" - "vlan.1=pvid untagged\n" + "vlans=900 , 1 pvid untagged, 100-123 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); + g_assert_cmpuint (nm_setting_bridge_get_num_vlans (s_bridge), ==, 3); vlan = nm_setting_bridge_get_vlan (s_bridge, 0); g_assert (vlan); - g_assert_cmpuint (nm_bridge_vlan_get_vid (vlan), ==, 1); + nm_bridge_vlan_get_vid_range (vlan, &vid, &vid_end); + g_assert_cmpuint (vid, ==, 1); + g_assert_cmpuint (vid_end, ==, 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); + nm_bridge_vlan_get_vid_range (vlan, &vid, &vid_end); + g_assert_cmpuint (vid, ==, 100); + g_assert_cmpuint (vid_end, ==, 123); g_assert_cmpint (nm_bridge_vlan_is_pvid (vlan), ==, FALSE); g_assert_cmpint (nm_bridge_vlan_is_untagged (vlan), ==, TRUE); + vlan = nm_setting_bridge_get_vlan (s_bridge, 2); + g_assert (vlan); + nm_bridge_vlan_get_vid_range (vlan, &vid, &vid_end); + g_assert_cmpuint (vid, ==, 900); + g_assert_cmpuint (vid_end, ==, 900); + g_assert_cmpint (nm_bridge_vlan_is_pvid (vlan), ==, FALSE); + g_assert_cmpint (nm_bridge_vlan_is_untagged (vlan), ==, FALSE); + CLEAR (&con, &keyfile); } @@ -789,6 +801,7 @@ test_bridge_port_vlans (void) gs_unref_object NMConnection *con = NULL; NMSettingBridgePort *s_port; NMBridgeVlan *vlan; + guint16 vid_start, vid_end; con = nmtst_create_connection_from_keyfile ( "[connection]\n" @@ -799,31 +812,27 @@ test_bridge_port_vlans (void) "slave-type=bridge\n" "\n" "[bridge-port]\n" - "vlan.4000=\n" - "vlan.10=untagged\n" - "vlan.20=pvid untagged" + "vlans=4094 pvid , 10-20 untagged\n" "", "/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); + g_assert_cmpuint (nm_setting_bridge_port_get_num_vlans (s_port), ==, 2); vlan = nm_setting_bridge_port_get_vlan (s_port, 0); g_assert (vlan); - g_assert_cmpuint (nm_bridge_vlan_get_vid (vlan), ==, 10); + nm_bridge_vlan_get_vid_range (vlan, &vid_start, &vid_end); + g_assert_cmpuint (vid_start, ==, 10); + g_assert_cmpuint (vid_end, ==, 20); 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); + nm_bridge_vlan_get_vid_range (vlan, &vid_start, &vid_end); + g_assert_cmpuint (vid_start, ==, 4094); + g_assert_cmpuint (vid_end, ==, 4094); 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); diff --git a/libnm-core/tests/test-setting.c b/libnm-core/tests/test-setting.c index 57af014021..03100a039b 100644 --- a/libnm-core/tests/test-setting.c +++ b/libnm-core/tests/test-setting.c @@ -1664,6 +1664,7 @@ test_bridge_vlans (void) { NMBridgeVlan *v1, *v2; GError *error = NULL; + guint16 vid_start, vid_end; char *str; v1 = nm_bridge_vlan_from_str ("1 foobar", &error); @@ -1674,11 +1675,23 @@ test_bridge_vlans (void) nmtst_assert_no_success (v1, error); g_clear_error (&error); + /* test ranges */ + v1 = nm_bridge_vlan_from_str ("2-1000 untagged", &error); + nmtst_assert_success (v1, error); + g_assert_cmpint (nm_bridge_vlan_get_vid_range (v1, &vid_start, &vid_end), ==, TRUE); + g_assert_cmpuint (vid_start, ==, 2); + g_assert_cmpuint (vid_end, ==, 1000); + g_assert_cmpint (nm_bridge_vlan_is_pvid (v1), ==, FALSE); + g_assert_cmpint (nm_bridge_vlan_is_untagged (v1), ==, TRUE); + nm_bridge_vlan_unref (v1); + /* 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_get_vid_range (v1, &vid_start, &vid_end), ==, FALSE); + g_assert_cmpuint (vid_start, ==, 10); + g_assert_cmpuint (vid_end, ==, 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); diff --git a/libnm/libnm.ver b/libnm/libnm.ver index 8763e11a6c..8100f3b631 100644 --- a/libnm/libnm.ver +++ b/libnm/libnm.ver @@ -1524,7 +1524,7 @@ global: nm_bridge_vlan_cmp; nm_bridge_vlan_from_str; nm_bridge_vlan_get_type; - nm_bridge_vlan_get_vid; + nm_bridge_vlan_get_vid_range; nm_bridge_vlan_is_pvid; nm_bridge_vlan_is_sealed; nm_bridge_vlan_is_untagged; diff --git a/src/devices/nm-device-bridge.c b/src/devices/nm-device-bridge.c index 1d2f61e6c2..4275af912a 100644 --- a/src/devices/nm-device-bridge.c +++ b/src/devices/nm-device-bridge.c @@ -268,31 +268,37 @@ 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 ** +static const NMPlatformBridgeVlan ** setting_vlans_to_platform (GPtrArray *array) { - GPtrArray *plat_vlans; + NMPlatformBridgeVlan **arr; + NMPlatformBridgeVlan *p_data; guint i; if (!array || !array->len) return NULL; - plat_vlans = g_ptr_array_sized_new (array->len + 1); + G_STATIC_ASSERT_EXPR (_nm_alignof (NMPlatformBridgeVlan *) >= _nm_alignof (NMPlatformBridgeVlan)); + arr = g_malloc ( (sizeof (NMPlatformBridgeVlan *) * (array->len + 1)) + + (sizeof (NMPlatformBridgeVlan ) * (array->len ))); + p_data = (NMPlatformBridgeVlan *) &arr[array->len + 1]; for (i = 0; i < array->len; i++) { NMBridgeVlan *vlan = array->pdata[i]; - NMPlatformBridgeVlan *plat_vlan; + guint16 vid_start, vid_end; - 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); + nm_bridge_vlan_get_vid_range (vlan, &vid_start, &vid_end); - g_ptr_array_add (plat_vlans, plat_vlan); + p_data[i] = (NMPlatformBridgeVlan) { + .vid_start = vid_start, + .vid_end = vid_end, + .pvid = nm_bridge_vlan_is_pvid (vlan), + .untagged = nm_bridge_vlan_is_untagged (vlan), + }; + arr[i] = &p_data[i]; } - g_ptr_array_add (plat_vlans, NULL); - - return (NMPlatformBridgeVlan **) g_ptr_array_free (plat_vlans, FALSE); + arr[i] = NULL; + return (const NMPlatformBridgeVlan **) arr; } static void @@ -424,7 +430,7 @@ bridge_set_vlan_options (NMDevice *device, NMSettingBridge *s_bridge) NMPlatform *plat; int ifindex; gs_unref_ptrarray GPtrArray *vlans = NULL; - nm_auto_freev NMPlatformBridgeVlan **plat_vlans = NULL; + gs_free const NMPlatformBridgeVlan **plat_vlans = NULL; if (self->vlan_configured) return TRUE; @@ -479,8 +485,7 @@ bridge_set_vlan_options (NMDevice *device, NMSettingBridge *s_bridge) 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)) + && !nm_platform_link_set_bridge_vlans (plat, ifindex, FALSE, plat_vlans)) return FALSE; if (!nm_platform_sysctl_master_set_option (plat, ifindex, "vlan_filtering", "1")) @@ -568,8 +573,6 @@ 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))) @@ -584,8 +587,12 @@ enslave_slave (NMDevice *device, bridge_set_vlan_options (device, s_bridge); if (nm_setting_bridge_get_vlan_filtering (s_bridge)) { + gs_free const NMPlatformBridgeVlan **plat_vlans = NULL; + gs_unref_ptrarray GPtrArray *vlans = NULL; + 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 @@ -595,7 +602,7 @@ enslave_slave (NMDevice *device, && !nm_platform_link_set_bridge_vlans (nm_device_get_platform (slave), nm_device_get_ifindex (slave), TRUE, - (const NMPlatformBridgeVlan *const *) plat_vlans)) + plat_vlans)) return FALSE; } diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 4abb45960f..460b321bf6 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -6838,9 +6838,10 @@ link_set_bridge_vlans (NMPlatform *platform, /* Add VLANs */ for (i = 0; vlans[i]; i++) { const NMPlatformBridgeVlan *vlan = vlans[i]; + gboolean is_range = vlan->vid_start != vlan->vid_end; - vinfo.vid = vlan->vid; - vinfo.flags = 0; + vinfo.vid = vlan->vid_start; + vinfo.flags = is_range ? BRIDGE_VLAN_INFO_RANGE_BEGIN : 0; if (vlan->untagged) vinfo.flags |= BRIDGE_VLAN_INFO_UNTAGGED; @@ -6848,6 +6849,12 @@ link_set_bridge_vlans (NMPlatform *platform, vinfo.flags |= BRIDGE_VLAN_INFO_PVID; NLA_PUT (nlmsg, IFLA_BRIDGE_VLAN_INFO, sizeof (vinfo), &vinfo); + + if (is_range) { + vinfo.vid = vlan->vid_end; + vinfo.flags = BRIDGE_VLAN_INFO_RANGE_END; + NLA_PUT (nlmsg, IFLA_BRIDGE_VLAN_INFO, sizeof (vinfo), &vinfo); + } } } else { /* Flush existing VLANs */ diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 4e6bf95882..53ad78caf0 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -6542,14 +6542,18 @@ 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) { + char str_vid_end[64]; + if (!nm_utils_to_string_buffer_init_null (vlan, &buf, &len)) return buf; g_snprintf (buf, len, "%u" "%s" + "%s" "%s", - vlan->vid, + vlan->vid_start, + vlan->vid_start != vlan->vid_end ? nm_sprintf_buf (str_vid_end, "-%u", vlan->vid_end) : "", vlan->pvid ? " PVID" : "", vlan->untagged ? " untagged" : ""); diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 42f6c96136..fa8200e68b 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -671,7 +671,8 @@ typedef struct { } NMPlatformVF; typedef struct { - guint16 vid; + guint16 vid_start; + guint16 vid_end; bool untagged:1; bool pvid:1; } NMPlatformBridgeVlan; 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 35ed11096c..a5f158a85e 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c @@ -5114,7 +5114,7 @@ read_bridge_vlans (shvarFile *ifcfg, array = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_bridge_vlan_unref); - strv = nm_utils_strsplit_set (value, ","); + strv = nm_utils_escaped_tokens_split (value, ","); if (strv) { for (iter = strv; *iter; iter++) { vlan = nm_bridge_vlan_from_str (*iter, &local); 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 a57e6c3a60..a8be7cdec2 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c @@ -1491,8 +1491,8 @@ write_bridge_vlans (NMSetting *setting, if (!vlan_str) return FALSE; if (string->len > 0) - g_string_append (string, ", "); - g_string_append (string, vlan_str); + g_string_append (string, ","); + nm_utils_escaped_tokens_escape_gstr_assert (vlan_str, ",", string); } svSetValueStr (ifcfg, key, string->str); 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 0b4db7c1ed..a8ff8df3e9 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,7 +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" +BRIDGE_PORT_VLANS="1 untagged,2 pvid,4-4094 untagged" 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 09e2c0e4b5..ca280fbfec 100644 --- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c +++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c @@ -7651,14 +7651,14 @@ test_write_bridge_main (void) 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); + vlan = nm_bridge_vlan_new (10, 16); nm_bridge_vlan_set_untagged (vlan, TRUE); g_ptr_array_add (vlans, vlan); - vlan = nm_bridge_vlan_new (22); + vlan = nm_bridge_vlan_new (22, 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); + vlan = nm_bridge_vlan_new (44, 0); g_ptr_array_add (vlans, vlan); g_object_set (s_bridge, @@ -7774,14 +7774,14 @@ test_write_bridge_component (void) /* Bridge port */ vlans = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_bridge_vlan_unref); - vlan = nm_bridge_vlan_new (1); + vlan = nm_bridge_vlan_new (1, 0); 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); + vlan = nm_bridge_vlan_new (4, 4094); nm_bridge_vlan_set_untagged (vlan, TRUE); g_ptr_array_add (vlans, vlan); - vlan = nm_bridge_vlan_new (4); + vlan = nm_bridge_vlan_new (2, 2); + nm_bridge_vlan_set_pvid (vlan, TRUE); g_ptr_array_add (vlans, vlan); s_port = nm_setting_bridge_port_new (); |