Commit da204257 authored by Beniamino Galvani's avatar Beniamino Galvani

all: support bridge vlan ranges

In some cases it is convenient to specify ranges of bridge vlans, as
already supported by iproute2 and natively by kernel. With this commit
it becomes possible to add a range in this way:

 nmcli connection modify eth0-slave +bridge-port.vlans "100-200 untagged"

vlan ranges can't be PVIDs because only one PVID vlan can exist.

https://bugzilla.redhat.com/show_bug.cgi?id=1652910
(cherry picked from commit 70935157)
parent ea8ed6ce
......@@ -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);
}
}
......
......@@ -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.")
......
......@@ -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_strsplit_set (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, ",");
g_string_append (string, vlan_str);
}
nm_keyfile_plugin_kf_set_string (info->keyfile,
nm_setting_get_name (setting),
"vlans",
string->str);
g_string_free (string, TRUE);
}
static void
......
......@@ -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] =
......
......@@ -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);
......
......@@ -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,9 +450,10 @@ 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);
......@@ -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] =
......
......@@ -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
......
......@@ -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);
......@@ -6875,14 +6890,18 @@ _nm_utils_bridge_vlan_verify_list (GPtrArray *vlans,
for (i = 1; i < vlans->len; i++) {
NMBridgeVlan *vlan_prev = vlans->pdata[i - 1];
NMBridgeVlan *vlan = vlans->pdata[i];
guint16 vid_prev, vid;
if (nm_bridge_vlan_get_vid (vlan_prev) > nm_bridge_vlan_get_vid (vlan)) {
nm_bridge_vlan_get_vid_range (vlan_prev, &vid_prev, NULL);
nm_bridge_vlan_get_vid_range (vlan, &vid, NULL);
if (vid_prev > vid) {
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,
vid);
g_prefix_error (error, "%s.%s: ", setting, property);
return FALSE;
}
......@@ -6893,32 +6912,32 @@ _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;
vid = nm_bridge_vlan_get_vid (vlan);
guint16 v, 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;
}
if (nm_bridge_vlan_is_pvid (vlan)) {
if (pvid_found) {
g_set_error_literal (error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("only one VLAN can be the PVID"));
nm_bridge_vlan_get_vid_range (vlan, &vid_start, &vid_end);
for (v = vid_start; v <= vid_end; v++) {
if (g_hash_table_contains (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;
}
pvid_found = TRUE;
}
g_hash_table_add (h, GUINT_TO_POINTER (vid));
if (nm_bridge_vlan_is_pvid (vlan)) {
if (pvid_found) {
g_set_error_literal (error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("only one VLAN can be the PVID"));
g_prefix_error (error, "%s.%s: ", setting, property);
return FALSE;
}
pvid_found = TRUE;
}
g_hash_table_add (h, GUINT_TO_POINTER (v));
}
}
return TRUE;
......
......@@ -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), ==