summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBeniamino Galvani <bgalvani@redhat.com>2019-04-18 09:53:52 +0200
committerBeniamino Galvani <bgalvani@redhat.com>2019-04-18 09:53:52 +0200
commit73e32e43c8ca08adc3500d603eacb9133e40ed3c (patch)
tree26e28318f0eee78ed25ecce6a0120e0e8c699c6f
parentfd8b78dd6a75a6dedfd65bb5c3b523cdf64702b4 (diff)
parent05a547133b3e98c6c08f52df2923cfacc4ca8400 (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.c24
-rw-r--r--clients/common/settings-docs.h.in4
-rw-r--r--libnm-core/nm-keyfile.c98
-rw-r--r--libnm-core/nm-setting-bridge-port.c24
-rw-r--r--libnm-core/nm-setting-bridge-port.h4
-rw-r--r--libnm-core/nm-setting-bridge.c143
-rw-r--r--libnm-core/nm-setting-bridge.h8
-rw-r--r--libnm-core/nm-utils.c75
-rw-r--r--libnm-core/tests/test-keyfile.c43
-rw-r--r--libnm-core/tests/test-setting.c15
-rw-r--r--libnm/libnm.ver2
-rw-r--r--src/devices/nm-device-bridge.c43
-rw-r--r--src/platform/nm-linux-platform.c11
-rw-r--r--src/platform/nm-platform.c6
-rw-r--r--src/platform/nm-platform.h3
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c2
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c4
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bridge_Component.cexpected2
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c14
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 ();