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
This commit is contained in:
Beniamino Galvani 2019-04-18 09:39:51 +02:00
commit 693252d049
19 changed files with 342 additions and 185 deletions

View file

@ -167,8 +167,8 @@ _value_str_as_index_list (const char *value, gsize *out_len)
#define MULTILIST_WITH_ESCAPE_CHARS NM_ASCII_SPACES"," #define MULTILIST_WITH_ESCAPE_CHARS NM_ASCII_SPACES","
#define ESCAPED_TOKENS_DELIMTER ',' #define ESCAPED_TOKENS_DELIMITER ','
#define ESCAPED_TOKENS_DELIMTERS "," #define ESCAPED_TOKENS_DELIMITERS ","
typedef enum { typedef enum {
VALUE_STRSPLIT_MODE_STRIPPED, 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); strv = nm_utils_strsplit_set_full (value, MULTILIST_WITH_ESCAPE_CHARS, NM_UTILS_STRSPLIT_SET_FLAGS_ALLOW_ESCAPING);
break; break;
case VALUE_STRSPLIT_MODE_ESCAPED_TOKENS: 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)); NM_SET_OUT (out_len, NM_PTRARRAY_LEN (strv));
return g_steal_pointer (&strv); return g_steal_pointer (&strv);
default: default:
@ -3072,7 +3072,7 @@ _get_fcn_objlist (ARGS_GET_FCN)
&& property_info->property_typ_data->subtype.objlist.delimit_pretty_with_semicolon) && property_info->property_typ_data->subtype.objlist.delimit_pretty_with_semicolon)
g_string_append (str, "; "); g_string_append (str, "; ");
else { else {
G_STATIC_ASSERT_EXPR (ESCAPED_TOKENS_DELIMTER == ','); G_STATIC_ASSERT_EXPR (ESCAPED_TOKENS_DELIMITER == ',');
g_string_append (str, ", "); g_string_append (str, ", ");
} }
} }
@ -3400,7 +3400,7 @@ _objlist_obj_to_str_fcn_ip_config_routing_rules (NMMetaAccessorGetType get_type,
NULL, NULL,
NULL); NULL);
if (s) 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 static gboolean
@ -3559,7 +3559,7 @@ _objlist_obj_to_str_fcn_bridge_vlans (NMMetaAccessorGetType get_type,
s = nm_bridge_vlan_to_str (vlan, NULL); s = nm_bridge_vlan_to_str (vlan, NULL);
if (s) if (s)
g_string_append (str, s); nm_utils_escaped_tokens_escape_gstr_assert (s, ESCAPED_TOKENS_DELIMITERS, str);
} }
static gboolean static gboolean
@ -3618,13 +3618,14 @@ _objlist_set_fcn_bridge_vlans (NMSetting *setting,
{ {
nm_auto_unref_bridge_vlan NMBridgeVlan *vlan = NULL; nm_auto_unref_bridge_vlan NMBridgeVlan *vlan = NULL;
gs_free_error GError *local = NULL; gs_free_error GError *local = NULL;
guint16 vid_start, vid_end;
vlan = nm_bridge_vlan_from_str (value, &local); vlan = nm_bridge_vlan_from_str (value, &local);
if (!vlan) { if (!vlan) {
nm_utils_error_set (error, NM_UTILS_ERROR_INVALID_ARGUMENT, nm_utils_error_set (error, NM_UTILS_ERROR_INVALID_ARGUMENT,
"%s. %s", "%s. %s",
local->message, local->message,
_("The valid syntax is: '<vid> [pvid] [untagged]")); _("The valid syntax is: '<vid>[-<vid>] [pvid] [untagged]'"));
return FALSE; return FALSE;
} }
@ -3632,15 +3633,18 @@ _objlist_set_fcn_bridge_vlans (NMSetting *setting,
if (do_add) if (do_add)
nm_setting_bridge_add_vlan (NM_SETTING_BRIDGE (setting), vlan); nm_setting_bridge_add_vlan (NM_SETTING_BRIDGE (setting), vlan);
else { else {
nm_bridge_vlan_get_vid_range (vlan, &vid_start, &vid_end);
nm_setting_bridge_remove_vlan_by_vid (NM_SETTING_BRIDGE (setting), nm_setting_bridge_remove_vlan_by_vid (NM_SETTING_BRIDGE (setting),
nm_bridge_vlan_get_vid (vlan)); vid_start, vid_end);
} }
} else { } else {
if (do_add) if (do_add)
nm_setting_bridge_port_add_vlan (NM_SETTING_BRIDGE_PORT (setting), vlan); nm_setting_bridge_port_add_vlan (NM_SETTING_BRIDGE_PORT (setting), vlan);
else { 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_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), .clear_all_fcn = OBJLIST_CLEAR_ALL_FCN (NMSettingBridge, nm_setting_bridge_clear_vlans),
.obj_to_str_fcn = _objlist_obj_to_str_fcn_bridge_vlans, .obj_to_str_fcn = _objlist_obj_to_str_fcn_bridge_vlans,
.set_fcn = _objlist_set_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), .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, .obj_to_str_fcn = _objlist_obj_to_str_fcn_bridge_vlans,
.set_fcn = _objlist_set_fcn_bridge_vlans, .set_fcn = _objlist_set_fcn_bridge_vlans,
.strsplit_escaped_tokens = TRUE,
), ),
), ),
), ),

View file

@ -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_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_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_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_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_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_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_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_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.") #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.")

View file

@ -1626,51 +1626,37 @@ team_config_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key
static void static void
bridge_vlan_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key) 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_unref_ptrarray GPtrArray *vlans = NULL;
gs_strfreev char **keys = NULL; gs_free char *value = NULL;
gsize n_keys = 0; gs_free const char **strv = NULL;
int i; const char *const *iter;
GError *local = NULL;
NMBridgeVlan *vlan;
keys = nm_keyfile_plugin_kf_get_keys (info->keyfile, setting_name, &n_keys, NULL); value = nm_keyfile_plugin_kf_get_string (info->keyfile,
if (n_keys == 0) nm_setting_get_name (setting),
key,
NULL);
if (!value || !value[0])
return; return;
vlans = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_bridge_vlan_unref); vlans = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_bridge_vlan_unref);
for (i = 0; i < n_keys; i++) { strv = nm_utils_escaped_tokens_split (value, ",");
NMBridgeVlan *vlan; if (strv) {
const char *index; for (iter = strv; *iter; iter++) {
gs_free char *vlan_rest = NULL; vlan = nm_bridge_vlan_from_str (*iter, &local);
gs_free char *vlan_str = NULL; if (!vlan) {
gs_free_error GError *err = NULL; handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
"invalid bridge VLAN: %s", local->message);
if (!g_str_has_prefix (keys[i], "vlan.")) g_clear_error (&local);
continue; continue;
}
index = keys[i] + NM_STRLEN("vlan."); g_ptr_array_add (vlans, 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) if (vlans->len > 0)
g_object_set (setting, key, vlans, NULL); g_object_set (setting, key, vlans, NULL);
} }
@ -2004,26 +1990,34 @@ bridge_vlan_writer (KeyfileWriterInfo *info,
const char *key, const char *key,
const GValue *value) const GValue *value)
{ {
gsize i; NMBridgeVlan *vlan;
GPtrArray *array; GPtrArray *vlans;
nm_auto_free_gstring GString *value_str = NULL; GString *string;
guint i;
array = (GPtrArray *) g_value_get_boxed (value); vlans = (GPtrArray *) g_value_get_boxed (value);
if (!array || !array->len) if (!vlans || !vlans->len)
return; return;
for (i = 0; i < array->len; i++) { string = g_string_new ("");
NMBridgeVlan *vlan = array->pdata[i]; for (i = 0; i < vlans->len; i++) {
char key_name[32]; gs_free char *vlan_str = NULL;
nm_sprintf_buf (key_name, "vlan.%u", nm_bridge_vlan_get_vid (vlan)); vlan = vlans->pdata[i];
nm_gstring_prepare (&value_str); vlan_str = nm_bridge_vlan_to_str (vlan, NULL);
_nm_bridge_vlan_str_append_rest (vlan, value_str, FALSE); if (!vlan_str)
nm_keyfile_plugin_kf_set_string (info->keyfile, continue;
nm_setting_get_name (setting), if (string->len > 0)
key_name, g_string_append (string, ",");
value_str->str); 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 static void

View file

@ -235,9 +235,12 @@ nm_setting_bridge_port_remove_vlan (NMSettingBridgePort *setting, guint idx)
/** /**
* nm_setting_bridge_port_remove_vlan_by_vid: * nm_setting_bridge_port_remove_vlan_by_vid:
* @setting: the #NMSettingBridgePort * @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 * 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 gboolean
nm_setting_bridge_port_remove_vlan_by_vid (NMSettingBridgePort *setting, nm_setting_bridge_port_remove_vlan_by_vid (NMSettingBridgePort *setting,
guint16 vid) guint16 vid_start,
guint16 vid_end)
{ {
NMSettingBridgePortPrivate *priv; NMSettingBridgePortPrivate *priv;
guint16 v_start, v_end;
NMBridgeVlan *vlan;
guint i; guint i;
if (vid_end == 0)
vid_end = vid_start;
g_return_val_if_fail (NM_IS_SETTING_BRIDGE_PORT (setting), FALSE); g_return_val_if_fail (NM_IS_SETTING_BRIDGE_PORT (setting), FALSE);
priv = NM_SETTING_BRIDGE_PORT_GET_PRIVATE (setting); priv = NM_SETTING_BRIDGE_PORT_GET_PRIVATE (setting);
for (i = 0; i < priv->vlans->len; i++) { 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); g_ptr_array_remove_index (priv->vlans, i);
_notify (setting, PROP_VLANS); _notify (setting, PROP_VLANS);
return TRUE; return TRUE;
@ -556,13 +567,16 @@ nm_setting_bridge_port_class_init (NMSettingBridgePortClass *klass)
* *
* $vid [pvid] [untagged] [, $vid [pvid] [untagged]]... * $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 * Since: 1.18
**/ **/
/* ---ifcfg-rh--- /* ---ifcfg-rh---
* property: vlans * property: vlans
* variable: BRIDGE_PORT_VLANS * variable: BRIDGE_PORT_VLANS
* description: List of VLANs on the bridge port * 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--- * ---end---
*/ */
obj_properties[PROP_VLANS] = obj_properties[PROP_VLANS] =

View file

@ -81,7 +81,9 @@ NMBridgeVlan *nm_setting_bridge_port_get_vlan (NMSettingBridgePort *setting, gui
NM_AVAILABLE_IN_1_18 NM_AVAILABLE_IN_1_18
void nm_setting_bridge_port_remove_vlan (NMSettingBridgePort *setting, guint idx); void nm_setting_bridge_port_remove_vlan (NMSettingBridgePort *setting, guint idx);
NM_AVAILABLE_IN_1_18 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 NM_AVAILABLE_IN_1_18
void nm_setting_bridge_port_clear_vlans (NMSettingBridgePort *setting); void nm_setting_bridge_port_clear_vlans (NMSettingBridgePort *setting);

View file

@ -80,7 +80,8 @@ G_DEFINE_BOXED_TYPE (NMBridgeVlan, nm_bridge_vlan, _nm_bridge_vlan_dup, nm_bridg
struct _NMBridgeVlan { struct _NMBridgeVlan {
guint refcount; guint refcount;
guint16 vid; guint16 vid_start;
guint16 vid_end;
bool untagged:1; bool untagged:1;
bool pvid:1; bool pvid:1;
bool sealed:1; bool sealed:1;
@ -96,25 +97,33 @@ NM_IS_BRIDGE_VLAN (const NMBridgeVlan *self, gboolean also_sealed)
/** /**
* nm_bridge_vlan_new: * 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. * Returns: (transfer full): the new #NMBridgeVlan object.
* *
* Since: 1.18 * Since: 1.18
**/ **/
NMBridgeVlan * NMBridgeVlan *
nm_bridge_vlan_new (guint16 vid) nm_bridge_vlan_new (guint16 vid_start, guint16 vid_end)
{ {
NMBridgeVlan *vlan; NMBridgeVlan *vlan;
g_return_val_if_fail (vid >= NM_BRIDGE_VLAN_VID_MIN, NULL); if (vid_end == 0)
g_return_val_if_fail (vid <= NM_BRIDGE_VLAN_VID_MAX, NULL); 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 = g_slice_new0 (NMBridgeVlan);
vlan->refcount = 1; vlan->refcount = 1;
vlan->vid = vid; vlan->vid_start = vid_start;
vlan->vid_end = vid_end;
return vlan; 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); g_return_val_if_fail (NM_IS_BRIDGE_VLAN (b, TRUE), 0);
NM_CMP_SELF (a, b); 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, untagged);
NM_CMP_FIELD_BOOL (a, b, pvid); 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 * @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 * Since: 1.18
**/ **/
guint16 gboolean
nm_bridge_vlan_get_vid (const NMBridgeVlan *vlan) 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); 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 * @vlan: the #NMBridgeVlan
* @value: the new value * @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 * Since: 1.18
**/ **/
@ -296,6 +316,7 @@ void
nm_bridge_vlan_set_pvid (NMBridgeVlan *vlan, gboolean value) nm_bridge_vlan_set_pvid (NMBridgeVlan *vlan, gboolean value)
{ {
g_return_if_fail (NM_IS_BRIDGE_VLAN (vlan, FALSE)); g_return_if_fail (NM_IS_BRIDGE_VLAN (vlan, FALSE));
g_return_if_fail (!value || vlan->vid_start == vlan->vid_end);
vlan->pvid = value; 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); 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->untagged = vlan->untagged;
copy->pvid = vlan->pvid; 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 * future if more parameters are added to the object that could
* make it invalid. */ * 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); _nm_bridge_vlan_str_append_rest (vlan, string, TRUE);
return g_string_free (string, FALSE); return g_string_free (string, FALSE);
@ -425,14 +450,15 @@ nm_bridge_vlan_from_str (const char *str, GError **error)
{ {
NMBridgeVlan *vlan = NULL; NMBridgeVlan *vlan = NULL;
gs_free const char **tokens = NULL; gs_free const char **tokens = NULL;
guint i, vid; guint i, vid_start, vid_end = 0;
gboolean pvid = FALSE; gboolean pvid = FALSE;
gboolean untagged = FALSE; gboolean untagged = FALSE;
char *c;
g_return_val_if_fail (str, NULL); g_return_val_if_fail (str, NULL);
g_return_val_if_fail (!error || !*error, 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]) { if (!tokens || !tokens[0]) {
g_set_error_literal (error, g_set_error_literal (error,
NM_CONNECTION_ERROR, NM_CONNECTION_ERROR,
@ -441,23 +467,58 @@ nm_bridge_vlan_from_str (const char *str, GError **error)
return NULL; return NULL;
} }
vid = _nm_utils_ascii_str_to_uint64 (tokens[0], c = strchr (tokens[0], '-');
10, if (c)
NM_BRIDGE_VLAN_VID_MIN, *c = '\0';
NM_BRIDGE_VLAN_VID_MAX,
G_MAXUINT); vid_start = _nm_utils_ascii_str_to_uint64 (tokens[0],
if (vid == G_MAXUINT) { 10,
NM_BRIDGE_VLAN_VID_MIN,
NM_BRIDGE_VLAN_VID_MAX,
G_MAXUINT);
if (vid_start == G_MAXUINT) {
g_set_error (error, g_set_error (error,
NM_CONNECTION_ERROR, NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_FAILED, 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; 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++) { 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; pvid = TRUE;
else if (nm_streq (tokens[i], "untagged")) } else if (nm_streq (tokens[i], "untagged"))
untagged = TRUE; untagged = TRUE;
else { else {
g_set_error (error, 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_pvid (vlan, pvid);
nm_bridge_vlan_set_untagged (vlan, untagged); 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: * nm_setting_bridge_remove_vlan_by_vid:
* @setting: the #NMSettingBridge * @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 * 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 gboolean
nm_setting_bridge_remove_vlan_by_vid (NMSettingBridge *setting, nm_setting_bridge_remove_vlan_by_vid (NMSettingBridge *setting,
guint16 vid) guint16 vid_start,
guint16 vid_end)
{ {
NMSettingBridgePrivate *priv; NMSettingBridgePrivate *priv;
NMBridgeVlan *vlan;
guint i; guint i;
g_return_val_if_fail (NM_IS_SETTING_BRIDGE (setting), FALSE); g_return_val_if_fail (NM_IS_SETTING_BRIDGE (setting), FALSE);
priv = NM_SETTING_BRIDGE_GET_PRIVATE (setting); priv = NM_SETTING_BRIDGE_GET_PRIVATE (setting);
if (vid_end == 0)
vid_end = vid_start;
for (i = 0; i < priv->vlans->len; i++) { 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); g_ptr_array_remove_index (priv->vlans, i);
_notify (setting, PROP_VLANS); _notify (setting, PROP_VLANS);
return TRUE; return TRUE;
@ -1390,13 +1460,16 @@ nm_setting_bridge_class_init (NMSettingBridgeClass *klass)
* *
* $vid [pvid] [untagged] [, $vid [pvid] [untagged]]... * $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 * Since: 1.18
**/ **/
/* ---ifcfg-rh--- /* ---ifcfg-rh---
* property: vlans * property: vlans
* variable: BRIDGE_VLANS * variable: BRIDGE_VLANS
* description: List of VLANs on the bridge * 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--- * ---end---
*/ */
obj_properties[PROP_VLANS] = obj_properties[PROP_VLANS] =

View file

@ -108,14 +108,16 @@ NMBridgeVlan *nm_setting_bridge_get_vlan (NMSettingBridge *setting, guint idx);
NM_AVAILABLE_IN_1_18 NM_AVAILABLE_IN_1_18
void nm_setting_bridge_remove_vlan (NMSettingBridge *setting, guint idx); void nm_setting_bridge_remove_vlan (NMSettingBridge *setting, guint idx);
NM_AVAILABLE_IN_1_18 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 NM_AVAILABLE_IN_1_18
void nm_setting_bridge_clear_vlans (NMSettingBridge *setting); void nm_setting_bridge_clear_vlans (NMSettingBridge *setting);
NM_AVAILABLE_IN_1_18 NM_AVAILABLE_IN_1_18
GType nm_bridge_vlan_get_type (void); GType nm_bridge_vlan_get_type (void);
NM_AVAILABLE_IN_1_18 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 NM_AVAILABLE_IN_1_18
NMBridgeVlan * nm_bridge_vlan_ref (NMBridgeVlan *vlan); NMBridgeVlan * nm_bridge_vlan_ref (NMBridgeVlan *vlan);
NM_AVAILABLE_IN_1_18 NM_AVAILABLE_IN_1_18
@ -133,7 +135,7 @@ void nm_bridge_vlan_set_untagged (NMBridgeVlan *vlan, gboolean value);
NM_AVAILABLE_IN_1_18 NM_AVAILABLE_IN_1_18
void nm_bridge_vlan_set_pvid (NMBridgeVlan *vlan, gboolean value); void nm_bridge_vlan_set_pvid (NMBridgeVlan *vlan, gboolean value);
NM_AVAILABLE_IN_1_18 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 NM_AVAILABLE_IN_1_18
gboolean nm_bridge_vlan_is_untagged (const NMBridgeVlan *vlan); gboolean nm_bridge_vlan_is_untagged (const NMBridgeVlan *vlan);
NM_AVAILABLE_IN_1_18 NM_AVAILABLE_IN_1_18

View file

@ -6800,10 +6800,15 @@ _nm_utils_bridge_vlans_to_dbus (NMSetting *setting, const char *property)
for (i = 0; i < vlans->len; i++) { for (i = 0; i < vlans->len; i++) {
NMBridgeVlan *vlan = vlans->pdata[i]; NMBridgeVlan *vlan = vlans->pdata[i];
GVariantBuilder vlan_builder; 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_init (&vlan_builder, G_VARIANT_TYPE_VARDICT);
g_variant_builder_add (&vlan_builder, "{sv}", "vid", g_variant_builder_add (&vlan_builder, "{sv}", "vid-start",
g_variant_new_uint16 (nm_bridge_vlan_get_vid (vlan))); 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_builder_add (&vlan_builder, "{sv}", "pvid",
g_variant_new_boolean (nm_bridge_vlan_is_pvid (vlan))); g_variant_new_boolean (nm_bridge_vlan_is_pvid (vlan)));
g_variant_builder_add (&vlan_builder, "{sv}", "untagged", 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)) { while (g_variant_iter_next (&vlan_iter, "@a{sv}", &vlan_var)) {
_nm_unused gs_unref_variant GVariant *var_unref = vlan_var; _nm_unused gs_unref_variant GVariant *var_unref = vlan_var;
NMBridgeVlan *vlan; NMBridgeVlan *vlan;
guint16 vid; guint16 vid_start, vid_end;
gboolean pvid = FALSE, untagged = FALSE; 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; continue;
if ( vid < NM_BRIDGE_VLAN_VID_MIN if ( vid_start < NM_BRIDGE_VLAN_VID_MIN
|| vid > NM_BRIDGE_VLAN_VID_MAX) || 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_start > vid_end)
continue; continue;
g_variant_lookup (vlan_var, "pvid", "b", &pvid); g_variant_lookup (vlan_var, "pvid", "b", &pvid);
if (pvid && vid_start != vid_end)
continue;
g_variant_lookup (vlan_var, "untagged", "b", &untagged); 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_untagged (vlan, untagged);
nm_bridge_vlan_set_pvid (vlan, pvid); nm_bridge_vlan_set_pvid (vlan, pvid);
g_ptr_array_add (vlans, vlan); g_ptr_array_add (vlans, vlan);
@ -6868,24 +6883,33 @@ _nm_utils_bridge_vlan_verify_list (GPtrArray *vlans,
gs_unref_hashtable GHashTable *h = NULL; gs_unref_hashtable GHashTable *h = NULL;
gboolean pvid_found = FALSE; gboolean pvid_found = FALSE;
if (!vlans || !vlans->len) if ( !vlans
|| vlans->len <= 1)
return TRUE; return TRUE;
if (check_normalizable) { if (check_normalizable) {
for (i = 1; i < vlans->len; i++) { guint16 vid_prev_end, vid_start, vid_end;
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)) { 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++) {
const NMBridgeVlan *vlan = vlans->pdata[i];
nm_bridge_vlan_get_vid_range (vlan, &vid_start, &vid_end);
if (vid_prev_end > vid_start) {
g_set_error (error, g_set_error (error,
NM_CONNECTION_ERROR, NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY, NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("Bridge VLANs %d and %d are not sorted by ascending vid"), _("Bridge VLANs %d and %d are not sorted by ascending vid"),
nm_bridge_vlan_get_vid (vlan_prev), vid_prev_end,
nm_bridge_vlan_get_vid (vlan)); vid_start);
g_prefix_error (error, "%s.%s: ", setting, property); g_prefix_error (error, "%s.%s: ", setting, property);
return FALSE; return FALSE;
} }
vid_prev_end = vid_end;
} }
return TRUE; return TRUE;
} }
@ -6893,21 +6917,24 @@ _nm_utils_bridge_vlan_verify_list (GPtrArray *vlans,
h = g_hash_table_new (nm_direct_hash, NULL); h = g_hash_table_new (nm_direct_hash, NULL);
for (i = 0; i < vlans->len; i++) { for (i = 0; i < vlans->len; i++) {
NMBridgeVlan *vlan = vlans->pdata[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))) { for (v = vid_start; v <= vid_end; v++) {
g_set_error (error, if (!nm_g_hash_table_add (h, GUINT_TO_POINTER (v))) {
NM_CONNECTION_ERROR, g_set_error (error,
NM_CONNECTION_ERROR_INVALID_PROPERTY, NM_CONNECTION_ERROR,
_("duplicate bridge VLAN vid %u"), vid); NM_CONNECTION_ERROR_INVALID_PROPERTY,
g_prefix_error (error, "%s.%s: ", setting, property); _("duplicate bridge VLAN vid %u"), v);
return FALSE; g_prefix_error (error, "%s.%s: ", setting, property);
return FALSE;
}
} }
if (nm_bridge_vlan_is_pvid (vlan)) { if (nm_bridge_vlan_is_pvid (vlan)) {
if (pvid_found) { if ( vid_start != vid_end
|| pvid_found) {
g_set_error_literal (error, g_set_error_literal (error,
NM_CONNECTION_ERROR, NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY, NM_CONNECTION_ERROR_INVALID_PROPERTY,
@ -6917,8 +6944,6 @@ _nm_utils_bridge_vlan_verify_list (GPtrArray *vlans,
} }
pvid_found = TRUE; pvid_found = TRUE;
} }
g_hash_table_add (h, GUINT_TO_POINTER (vid));
} }
return TRUE; return TRUE;

View file

@ -751,6 +751,7 @@ test_bridge_vlans (void)
gs_unref_object NMConnection *con = NULL; gs_unref_object NMConnection *con = NULL;
NMSettingBridge *s_bridge; NMSettingBridge *s_bridge;
NMBridgeVlan *vlan; NMBridgeVlan *vlan;
guint16 vid, vid_end;
con = nmtst_create_connection_from_keyfile ( con = nmtst_create_connection_from_keyfile (
"[connection]\n" "[connection]\n"
@ -759,26 +760,37 @@ test_bridge_vlans (void)
"interface-name=br4\n" "interface-name=br4\n"
"\n" "\n"
"[bridge]\n" "[bridge]\n"
"vlan.9=untagged\n" "vlans=900 , 1 pvid untagged, 100-123 untagged\n"
"vlan.1=pvid untagged\n"
"", "",
"/test_bridge_port/vlans"); "/test_bridge_port/vlans");
s_bridge = NM_SETTING_BRIDGE (nm_connection_get_setting (con, NM_TYPE_SETTING_BRIDGE)); s_bridge = NM_SETTING_BRIDGE (nm_connection_get_setting (con, NM_TYPE_SETTING_BRIDGE));
g_assert (s_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); vlan = nm_setting_bridge_get_vlan (s_bridge, 0);
g_assert (vlan); 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_pvid (vlan), ==, TRUE);
g_assert_cmpint (nm_bridge_vlan_is_untagged (vlan), ==, TRUE); g_assert_cmpint (nm_bridge_vlan_is_untagged (vlan), ==, TRUE);
vlan = nm_setting_bridge_get_vlan (s_bridge, 1); vlan = nm_setting_bridge_get_vlan (s_bridge, 1);
g_assert (vlan); 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_pvid (vlan), ==, FALSE);
g_assert_cmpint (nm_bridge_vlan_is_untagged (vlan), ==, TRUE); 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); CLEAR (&con, &keyfile);
} }
@ -789,6 +801,7 @@ test_bridge_port_vlans (void)
gs_unref_object NMConnection *con = NULL; gs_unref_object NMConnection *con = NULL;
NMSettingBridgePort *s_port; NMSettingBridgePort *s_port;
NMBridgeVlan *vlan; NMBridgeVlan *vlan;
guint16 vid_start, vid_end;
con = nmtst_create_connection_from_keyfile ( con = nmtst_create_connection_from_keyfile (
"[connection]\n" "[connection]\n"
@ -799,31 +812,27 @@ test_bridge_port_vlans (void)
"slave-type=bridge\n" "slave-type=bridge\n"
"\n" "\n"
"[bridge-port]\n" "[bridge-port]\n"
"vlan.4000=\n" "vlans=4094 pvid , 10-20 untagged\n"
"vlan.10=untagged\n"
"vlan.20=pvid untagged"
"", "",
"/test_bridge_port/vlans"); "/test_bridge_port/vlans");
s_port = NM_SETTING_BRIDGE_PORT (nm_connection_get_setting (con, NM_TYPE_SETTING_BRIDGE_PORT)); s_port = NM_SETTING_BRIDGE_PORT (nm_connection_get_setting (con, NM_TYPE_SETTING_BRIDGE_PORT));
g_assert (s_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); vlan = nm_setting_bridge_port_get_vlan (s_port, 0);
g_assert (vlan); 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_pvid (vlan), ==, FALSE);
g_assert_cmpint (nm_bridge_vlan_is_untagged (vlan), ==, TRUE); g_assert_cmpint (nm_bridge_vlan_is_untagged (vlan), ==, TRUE);
vlan = nm_setting_bridge_port_get_vlan (s_port, 1); vlan = nm_setting_bridge_port_get_vlan (s_port, 1);
g_assert (vlan); 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_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); g_assert_cmpint (nm_bridge_vlan_is_untagged (vlan), ==, FALSE);
CLEAR (&con, &keyfile); CLEAR (&con, &keyfile);

View file

@ -1664,6 +1664,7 @@ test_bridge_vlans (void)
{ {
NMBridgeVlan *v1, *v2; NMBridgeVlan *v1, *v2;
GError *error = NULL; GError *error = NULL;
guint16 vid_start, vid_end;
char *str; char *str;
v1 = nm_bridge_vlan_from_str ("1 foobar", &error); v1 = nm_bridge_vlan_from_str ("1 foobar", &error);
@ -1674,11 +1675,23 @@ test_bridge_vlans (void)
nmtst_assert_no_success (v1, error); nmtst_assert_no_success (v1, error);
g_clear_error (&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) */ /* test comparison (1) */
v1 = nm_bridge_vlan_from_str ("10 untagged", &error); v1 = nm_bridge_vlan_from_str ("10 untagged", &error);
nmtst_assert_success (v1, 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_sealed (v1), ==, FALSE);
g_assert_cmpint (nm_bridge_vlan_is_pvid (v1), ==, FALSE); g_assert_cmpint (nm_bridge_vlan_is_pvid (v1), ==, FALSE);
g_assert_cmpint (nm_bridge_vlan_is_untagged (v1), ==, TRUE); g_assert_cmpint (nm_bridge_vlan_is_untagged (v1), ==, TRUE);

View file

@ -1524,7 +1524,7 @@ global:
nm_bridge_vlan_cmp; nm_bridge_vlan_cmp;
nm_bridge_vlan_from_str; nm_bridge_vlan_from_str;
nm_bridge_vlan_get_type; 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_pvid;
nm_bridge_vlan_is_sealed; nm_bridge_vlan_is_sealed;
nm_bridge_vlan_is_untagged; nm_bridge_vlan_is_untagged;

View file

@ -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); 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) setting_vlans_to_platform (GPtrArray *array)
{ {
GPtrArray *plat_vlans; NMPlatformBridgeVlan **arr;
NMPlatformBridgeVlan *p_data;
guint i; guint i;
if (!array || !array->len) if (!array || !array->len)
return NULL; 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++) { for (i = 0; i < array->len; i++) {
NMBridgeVlan *vlan = array->pdata[i]; NMBridgeVlan *vlan = array->pdata[i];
NMPlatformBridgeVlan *plat_vlan; guint16 vid_start, vid_end;
plat_vlan = g_new0 (NMPlatformBridgeVlan, 1); nm_bridge_vlan_get_vid_range (vlan, &vid_start, &vid_end);
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); 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); arr[i] = NULL;
return (const NMPlatformBridgeVlan **) arr;
return (NMPlatformBridgeVlan **) g_ptr_array_free (plat_vlans, FALSE);
} }
static void static void
@ -424,7 +430,7 @@ bridge_set_vlan_options (NMDevice *device, NMSettingBridge *s_bridge)
NMPlatform *plat; NMPlatform *plat;
int ifindex; int ifindex;
gs_unref_ptrarray GPtrArray *vlans = NULL; gs_unref_ptrarray GPtrArray *vlans = NULL;
nm_auto_freev NMPlatformBridgeVlan **plat_vlans = NULL; gs_free const NMPlatformBridgeVlan **plat_vlans = NULL;
if (self->vlan_configured) if (self->vlan_configured)
return TRUE; 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); g_object_get (s_bridge, NM_SETTING_BRIDGE_VLANS, &vlans, NULL);
plat_vlans = setting_vlans_to_platform (vlans); plat_vlans = setting_vlans_to_platform (vlans);
if ( plat_vlans if ( plat_vlans
&& !nm_platform_link_set_bridge_vlans (plat, ifindex, FALSE, && !nm_platform_link_set_bridge_vlans (plat, ifindex, FALSE, plat_vlans))
(const NMPlatformBridgeVlan *const *) plat_vlans))
return FALSE; return FALSE;
if (!nm_platform_sysctl_master_set_option (plat, ifindex, "vlan_filtering", "1")) if (!nm_platform_sysctl_master_set_option (plat, ifindex, "vlan_filtering", "1"))
@ -568,8 +573,6 @@ enslave_slave (NMDevice *device,
NMConnection *master_connection; NMConnection *master_connection;
NMSettingBridge *s_bridge; NMSettingBridge *s_bridge;
NMSettingBridgePort *s_port; NMSettingBridgePort *s_port;
gs_unref_ptrarray GPtrArray *vlans = NULL;
nm_auto_freev NMPlatformBridgeVlan **plat_vlans = NULL;
if (configure) { if (configure) {
if (!nm_platform_link_enslave (nm_device_get_platform (device), nm_device_get_ip_ifindex (device), nm_device_get_ip_ifindex (slave))) 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); bridge_set_vlan_options (device, s_bridge);
if (nm_setting_bridge_get_vlan_filtering (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) if (s_port)
g_object_get (s_port, NM_SETTING_BRIDGE_PORT_VLANS, &vlans, NULL); g_object_get (s_port, NM_SETTING_BRIDGE_PORT_VLANS, &vlans, NULL);
plat_vlans = setting_vlans_to_platform (vlans); plat_vlans = setting_vlans_to_platform (vlans);
/* Since the link was just enslaved, there are no existing 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_platform_link_set_bridge_vlans (nm_device_get_platform (slave),
nm_device_get_ifindex (slave), nm_device_get_ifindex (slave),
TRUE, TRUE,
(const NMPlatformBridgeVlan *const *) plat_vlans)) plat_vlans))
return FALSE; return FALSE;
} }

View file

@ -6838,9 +6838,10 @@ link_set_bridge_vlans (NMPlatform *platform,
/* Add VLANs */ /* Add VLANs */
for (i = 0; vlans[i]; i++) { for (i = 0; vlans[i]; i++) {
const NMPlatformBridgeVlan *vlan = vlans[i]; const NMPlatformBridgeVlan *vlan = vlans[i];
gboolean is_range = vlan->vid_start != vlan->vid_end;
vinfo.vid = vlan->vid; vinfo.vid = vlan->vid_start;
vinfo.flags = 0; vinfo.flags = is_range ? BRIDGE_VLAN_INFO_RANGE_BEGIN : 0;
if (vlan->untagged) if (vlan->untagged)
vinfo.flags |= BRIDGE_VLAN_INFO_UNTAGGED; vinfo.flags |= BRIDGE_VLAN_INFO_UNTAGGED;
@ -6848,6 +6849,12 @@ link_set_bridge_vlans (NMPlatform *platform,
vinfo.flags |= BRIDGE_VLAN_INFO_PVID; vinfo.flags |= BRIDGE_VLAN_INFO_PVID;
NLA_PUT (nlmsg, IFLA_BRIDGE_VLAN_INFO, sizeof (vinfo), &vinfo); 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 { } else {
/* Flush existing VLANs */ /* Flush existing VLANs */

View file

@ -6542,14 +6542,18 @@ nm_platform_vf_to_string (const NMPlatformVF *vf, char *buf, gsize len)
const char * const char *
nm_platform_bridge_vlan_to_string (const NMPlatformBridgeVlan *vlan, char *buf, gsize len) 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)) if (!nm_utils_to_string_buffer_init_null (vlan, &buf, &len))
return buf; return buf;
g_snprintf (buf, len, g_snprintf (buf, len,
"%u" "%u"
"%s" "%s"
"%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->pvid ? " PVID" : "",
vlan->untagged ? " untagged" : ""); vlan->untagged ? " untagged" : "");

View file

@ -671,7 +671,8 @@ typedef struct {
} NMPlatformVF; } NMPlatformVF;
typedef struct { typedef struct {
guint16 vid; guint16 vid_start;
guint16 vid_end;
bool untagged:1; bool untagged:1;
bool pvid:1; bool pvid:1;
} NMPlatformBridgeVlan; } NMPlatformBridgeVlan;

View file

@ -5114,7 +5114,7 @@ read_bridge_vlans (shvarFile *ifcfg,
array = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_bridge_vlan_unref); 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) { if (strv) {
for (iter = strv; *iter; iter++) { for (iter = strv; *iter; iter++) {
vlan = nm_bridge_vlan_from_str (*iter, &local); vlan = nm_bridge_vlan_from_str (*iter, &local);

View file

@ -1491,8 +1491,8 @@ write_bridge_vlans (NMSetting *setting,
if (!vlan_str) if (!vlan_str)
return FALSE; return FALSE;
if (string->len > 0) if (string->len > 0)
g_string_append (string, ", "); g_string_append (string, ",");
g_string_append (string, vlan_str); nm_utils_escaped_tokens_escape_gstr_assert (vlan_str, ",", string);
} }
svSetValueStr (ifcfg, key, string->str); svSetValueStr (ifcfg, key, string->str);

View file

@ -2,7 +2,7 @@ HWADDR=31:33:33:37:BE:CD
MTU=1492 MTU=1492
TYPE=Ethernet TYPE=Ethernet
BRIDGING_OPTS="priority=50 path_cost=33" 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" NAME="Test Write Bridge Component"
UUID=${UUID} UUID=${UUID}
ONBOOT=yes ONBOOT=yes

View file

@ -7651,14 +7651,14 @@ test_write_bridge_main (void)
nm_connection_add_setting (connection, NM_SETTING (s_bridge)); nm_connection_add_setting (connection, NM_SETTING (s_bridge));
vlans = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_bridge_vlan_unref); 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); nm_bridge_vlan_set_untagged (vlan, TRUE);
g_ptr_array_add (vlans, vlan); 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_pvid (vlan, TRUE);
nm_bridge_vlan_set_untagged (vlan, TRUE); nm_bridge_vlan_set_untagged (vlan, TRUE);
g_ptr_array_add (vlans, vlan); 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_ptr_array_add (vlans, vlan);
g_object_set (s_bridge, g_object_set (s_bridge,
@ -7774,14 +7774,14 @@ test_write_bridge_component (void)
/* Bridge port */ /* Bridge port */
vlans = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_bridge_vlan_unref); 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); nm_bridge_vlan_set_untagged (vlan, TRUE);
g_ptr_array_add (vlans, vlan); g_ptr_array_add (vlans, vlan);
vlan = nm_bridge_vlan_new (2); 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 (2, 2);
nm_bridge_vlan_set_pvid (vlan, TRUE); 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); g_ptr_array_add (vlans, vlan);
s_port = nm_setting_bridge_port_new (); s_port = nm_setting_bridge_port_new ();