Beniamino Galvani 2019-03-26 17:32:03 +01:00
commit fc4aedc6f3
26 changed files with 2211 additions and 124 deletions

View file

@ -3434,6 +3434,25 @@ _objlist_obj_to_str_fcn_tc_config_qdiscs (NMMetaAccessorGetType get_type,
g_string_append (str, s);
}
static void
_objlist_obj_to_str_fcn_bridge_vlans (NMMetaAccessorGetType get_type,
NMSetting *setting,
guint idx,
GString *str)
{
gs_free char *s = NULL;
NMBridgeVlan *vlan;
if (NM_IS_SETTING_BRIDGE (setting))
vlan = nm_setting_bridge_get_vlan (NM_SETTING_BRIDGE (setting), idx);
else
vlan = nm_setting_bridge_port_get_vlan (NM_SETTING_BRIDGE_PORT (setting), idx);
s = nm_bridge_vlan_to_str (vlan, NULL);
if (s)
g_string_append (str, s);
}
static gboolean
_objlist_set_fcn_sriov_vfs (NMSetting *setting,
gboolean do_add,
@ -3482,6 +3501,43 @@ _objlist_set_fcn_tc_config_qdiscs (NMSetting *setting,
return TRUE;
}
static gboolean
_objlist_set_fcn_bridge_vlans (NMSetting *setting,
gboolean do_add,
const char *value,
GError **error)
{
nm_auto_unref_bridge_vlan NMBridgeVlan *vlan = NULL;
gs_free_error GError *local = NULL;
vlan = nm_bridge_vlan_from_str (value, &local);
if (!vlan) {
nm_utils_error_set (error, NM_UTILS_ERROR_INVALID_ARGUMENT,
"%s %s",
local->message,
_("The valid syntax is: '<vid> [pvid] [untagged]"));
return FALSE;
}
if (NM_IS_SETTING_BRIDGE (setting)) {
if (do_add)
nm_setting_bridge_add_vlan (NM_SETTING_BRIDGE (setting), vlan);
else {
nm_setting_bridge_remove_vlan_by_vid (NM_SETTING_BRIDGE (setting),
nm_bridge_vlan_get_vid (vlan));
}
} else {
if (do_add)
nm_setting_bridge_port_add_vlan (NM_SETTING_BRIDGE_PORT (setting), vlan);
else {
nm_setting_bridge_port_remove_vlan_by_vid (NM_SETTING_BRIDGE_PORT (setting),
nm_bridge_vlan_get_vid (vlan));
}
}
return TRUE;
}
static void
_objlist_obj_to_str_fcn_tc_config_tfilters (NMMetaAccessorGetType get_type,
NMSetting *setting,
@ -4808,6 +4864,23 @@ static const NMMetaPropertyInfo *const property_infos_BRIDGE[] = {
.prompt = N_("Enable IGMP snooping [no]"),
.property_type = &_pt_gobject_bool,
),
PROPERTY_INFO_WITH_DESC (NM_SETTING_BRIDGE_VLAN_FILTERING,
.property_type = &_pt_gobject_bool,
),
PROPERTY_INFO_WITH_DESC (NM_SETTING_BRIDGE_VLAN_DEFAULT_PVID,
.property_type = &_pt_gobject_int,
),
PROPERTY_INFO_WITH_DESC (NM_SETTING_BRIDGE_VLANS,
.property_type = &_pt_objlist,
.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
PROPERTY_TYP_DATA_SUBTYPE (objlist,
.get_num_fcn = OBJLIST_GET_NUM_FCN (NMSettingBridge, nm_setting_bridge_get_num_vlans),
.clear_all_fcn = OBJLIST_CLEAR_ALL_FCN (NMSettingBridge, nm_setting_bridge_clear_vlans),
.obj_to_str_fcn = _objlist_obj_to_str_fcn_bridge_vlans,
.set_fcn = _objlist_set_fcn_bridge_vlans,
),
),
),
NULL
};
@ -4832,6 +4905,17 @@ static const NMMetaPropertyInfo *const property_infos_BRIDGE_PORT[] = {
.prompt = N_("Hairpin [no]"),
.property_type = &_pt_gobject_bool,
),
PROPERTY_INFO_WITH_DESC (NM_SETTING_BRIDGE_PORT_VLANS,
.property_type = &_pt_objlist,
.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
PROPERTY_TYP_DATA_SUBTYPE (objlist,
.get_num_fcn = OBJLIST_GET_NUM_FCN (NMSettingBridgePort, nm_setting_bridge_port_get_num_vlans),
.clear_all_fcn = OBJLIST_CLEAR_ALL_FCN (NMSettingBridgePort, nm_setting_bridge_port_clear_vlans),
.obj_to_str_fcn = _objlist_obj_to_str_fcn_bridge_vlans,
.set_fcn = _objlist_set_fcn_bridge_vlans,
),
),
),
NULL
};

View file

@ -118,9 +118,13 @@
#define DESCRIBE_DOC_NM_SETTING_BRIDGE_MULTICAST_SNOOPING N_("Controls whether IGMP snooping is enabled for this bridge. Note that if snooping was automatically disabled due to hash collisions, the system may refuse to enable the feature until the collisions are resolved.")
#define DESCRIBE_DOC_NM_SETTING_BRIDGE_PRIORITY N_("Sets the Spanning Tree Protocol (STP) priority for this bridge. Lower values are \"better\"; the lowest priority bridge will be elected the root bridge.")
#define DESCRIBE_DOC_NM_SETTING_BRIDGE_STP N_("Controls whether Spanning Tree Protocol (STP) is enabled for this bridge.")
#define DESCRIBE_DOC_NM_SETTING_BRIDGE_VLAN_DEFAULT_PVID N_("The default PVID for the ports of the bridge, that is the VLAN id assigned to incoming untagged frames.")
#define DESCRIBE_DOC_NM_SETTING_BRIDGE_VLAN_FILTERING N_("Control whether VLAN filtering is enabled on the bridge.")
#define DESCRIBE_DOC_NM_SETTING_BRIDGE_VLANS N_("Array of bridge VLAN objects. In addition to the VLANs specified here, the bridge will also have the default-pvid VLAN configured by the bridge.vlan-default-pvid property. In nmcli the VLAN list can be specified with the following syntax: $vid [pvid] [untagged] [, $vid [pvid] [untagged]]...")
#define DESCRIBE_DOC_NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE N_("Enables or disables \"hairpin mode\" for the port, which allows frames to be sent back out through the port the frame was received on.")
#define DESCRIBE_DOC_NM_SETTING_BRIDGE_PORT_PATH_COST N_("The Spanning Tree Protocol (STP) port cost for destinations via this port.")
#define DESCRIBE_DOC_NM_SETTING_BRIDGE_PORT_PRIORITY N_("The Spanning Tree Protocol (STP) priority of this bridge port.")
#define DESCRIBE_DOC_NM_SETTING_BRIDGE_PORT_VLANS N_("Array of bridge VLAN objects. In addition to the VLANs specified here, the port will also have the default-pvid VLAN configured on the bridge by the bridge.vlan-default-pvid property. In nmcli the VLAN list can be specified with the following syntax: $vid [pvid] [untagged] [, $vid [pvid] [untagged]]...")
#define DESCRIBE_DOC_NM_SETTING_CDMA_MTU N_("If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple frames.")
#define DESCRIBE_DOC_NM_SETTING_CDMA_NUMBER N_("The number to dial to establish the connection to the CDMA-based mobile broadband network, if any. If not specified, the default number (#777) is used when required.")
#define DESCRIBE_DOC_NM_SETTING_CDMA_PASSWORD N_("The password used to authenticate with the network, if required. Many providers do not require a password, or accept any password. But if a password is required, it is specified here.")

View file

@ -1297,6 +1297,30 @@ _normalize_sriov_vf_order (NMConnection *self, GHashTable *parameters)
return _nm_setting_sriov_sort_vfs (s_sriov);
}
static gboolean
_normalize_bridge_vlan_order (NMConnection *self, GHashTable *parameters)
{
NMSettingBridge *s_bridge;
s_bridge = nm_connection_get_setting_bridge (self);
if (!s_bridge)
return FALSE;
return _nm_setting_bridge_sort_vlans (s_bridge);
}
static gboolean
_normalize_bridge_port_vlan_order (NMConnection *self, GHashTable *parameters)
{
NMSettingBridgePort *s_port;
s_port = nm_connection_get_setting_bridge_port (self);
if (!s_port)
return FALSE;
return _nm_setting_bridge_port_sort_vlans (s_port);
}
static gboolean
_normalize_required_settings (NMConnection *self, GHashTable *parameters)
{
@ -1645,6 +1669,8 @@ nm_connection_normalize (NMConnection *connection,
was_modified |= _normalize_ovs_interface_type (connection, parameters);
was_modified |= _normalize_ip_tunnel_wired_setting (connection, parameters);
was_modified |= _normalize_sriov_vf_order (connection, parameters);
was_modified |= _normalize_bridge_vlan_order (connection, parameters);
was_modified |= _normalize_bridge_port_vlan_order (connection, parameters);
/* Verify anew. */
success = _nm_connection_verify (connection, error);

View file

@ -578,6 +578,8 @@ gboolean _nm_utils_dhcp_duid_valid (const char *duid, GBytes **out_duid_bin);
/*****************************************************************************/
gboolean _nm_setting_sriov_sort_vfs (NMSettingSriov *setting);
gboolean _nm_setting_bridge_port_sort_vlans (NMSettingBridgePort *setting);
gboolean _nm_setting_bridge_sort_vlans (NMSettingBridge *setting);
/*****************************************************************************/
@ -750,4 +752,8 @@ gboolean nm_utils_base64secret_normalize (const char *base64_key,
/*****************************************************************************/
void _nm_bridge_vlan_str_append_rest (const NMBridgeVlan *vlan,
GString *string,
gboolean leading_space);
#endif

View file

@ -1519,20 +1519,71 @@ team_config_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key
}
static void
qdisc_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);
GPtrArray *qdiscs;
gs_unref_ptrarray GPtrArray *vlans = NULL;
gs_strfreev char **keys = NULL;
gsize n_keys = 0;
int i;
qdiscs = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_tc_qdisc_unref);
keys = nm_keyfile_plugin_kf_get_keys (info->keyfile, setting_name, &n_keys, NULL);
if (n_keys == 0)
return;
vlans = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_bridge_vlan_unref);
for (i = 0; i < n_keys; i++) {
NMBridgeVlan *vlan;
const char *index;
gs_free char *vlan_rest = NULL;
gs_free char *vlan_str = NULL;
gs_free_error GError *err = NULL;
if (!g_str_has_prefix (keys[i], "vlan."))
continue;
index = keys[i] + NM_STRLEN("vlan.");
if (index[0] == '\0')
continue;
if (index[0] == '0' && index[1] != '\0')
continue;
if (!NM_STRCHAR_ALL (index, ch, g_ascii_isdigit (ch)))
continue;
vlan_rest = nm_keyfile_plugin_kf_get_string (info->keyfile, setting_name, keys[i], NULL);
vlan_str = g_strdup_printf ("%s %s", index, vlan_rest);
vlan = nm_bridge_vlan_from_str (vlan_str, &err);
if (!vlan) {
handle_warn (info, keys[i], NM_KEYFILE_WARN_SEVERITY_WARN,
_("invalid bridge vlan: %s"),
err->message);
continue;
}
g_ptr_array_add (vlans, vlan);
}
if (vlans->len >= 1)
g_object_set (setting, key, vlans, NULL);
}
static void
qdisc_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key)
{
const char *setting_name = nm_setting_get_name (setting);
gs_unref_ptrarray GPtrArray *qdiscs = NULL;
gs_strfreev char **keys = NULL;
gsize n_keys = 0;
int i;
keys = nm_keyfile_plugin_kf_get_keys (info->keyfile, setting_name, &n_keys, NULL);
if (n_keys == 0)
return;
qdiscs = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_tc_qdisc_unref);
for (i = 0; i < n_keys; i++) {
NMTCQdisc *qdisc;
const char *qdisc_parent;
@ -1562,25 +1613,23 @@ qdisc_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key)
if (qdiscs->len >= 1)
g_object_set (setting, key, qdiscs, NULL);
g_ptr_array_unref (qdiscs);
}
static void
tfilter_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key)
{
const char *setting_name = nm_setting_get_name (setting);
GPtrArray *tfilters;
gs_unref_ptrarray GPtrArray *tfilters = NULL;
gs_strfreev char **keys = NULL;
gsize n_keys = 0;
int i;
tfilters = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_tc_tfilter_unref);
keys = nm_keyfile_plugin_kf_get_keys (info->keyfile, setting_name, &n_keys, NULL);
if (n_keys == 0)
return;
tfilters = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_tc_tfilter_unref);
for (i = 0; i < n_keys; i++) {
NMTCTfilter *tfilter;
const char *tfilter_parent;
@ -1610,8 +1659,6 @@ tfilter_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key)
if (tfilters->len >= 1)
g_object_set (setting, key, tfilters, NULL);
g_ptr_array_unref (tfilters);
}
/*****************************************************************************/
@ -1846,6 +1893,34 @@ route_writer (KeyfileWriterInfo *info,
write_ip_values (info->keyfile, setting_name, array, NULL, TRUE);
}
static void
bridge_vlan_writer (KeyfileWriterInfo *info,
NMSetting *setting,
const char *key,
const GValue *value)
{
gsize i;
GPtrArray *array;
nm_auto_free_gstring GString *value_str = NULL;
array = (GPtrArray *) g_value_get_boxed (value);
if (!array || !array->len)
return;
for (i = 0; i < array->len; i++) {
NMBridgeVlan *vlan = array->pdata[i];
char key_name[32];
nm_sprintf_buf (key_name, "vlan.%u", nm_bridge_vlan_get_vid (vlan));
nm_gstring_prepare (&value_str);
_nm_bridge_vlan_str_append_rest (vlan, value_str, FALSE);
nm_keyfile_plugin_kf_set_string (info->keyfile,
nm_setting_get_name (setting),
key_name,
value_str->str);
}
}
static void
qdisc_writer (KeyfileWriterInfo *info,
NMSetting *setting,
@ -2276,6 +2351,20 @@ static const ParseInfoSetting *const parse_infos[_NM_META_SETTING_TYPE_NUM] = {
PARSE_INFO_PROPERTY (NM_SETTING_BRIDGE_MAC_ADDRESS,
.parser = mac_address_parser_ETHER,
),
PARSE_INFO_PROPERTY (NM_SETTING_BRIDGE_VLANS,
.parser_no_check_key = TRUE,
.parser = bridge_vlan_parser,
.writer = bridge_vlan_writer,
),
),
),
PARSE_INFO_SETTING (NM_META_SETTING_TYPE_BRIDGE_PORT,
PARSE_INFO_PROPERTIES (
PARSE_INFO_PROPERTY (NM_SETTING_BRIDGE_PORT_VLANS,
.parser_no_check_key = TRUE,
.parser = bridge_vlan_parser,
.writer = bridge_vlan_writer,
),
),
),
PARSE_INFO_SETTING (NM_META_SETTING_TYPE_CONNECTION,

View file

@ -42,22 +42,60 @@
/*****************************************************************************/
NM_GOBJECT_PROPERTIES_DEFINE_BASE (
NM_GOBJECT_PROPERTIES_DEFINE (NMSettingBridgePort,
PROP_PRIORITY,
PROP_PATH_COST,
PROP_HAIRPIN_MODE,
PROP_VLANS,
);
typedef struct {
guint16 priority;
guint16 path_cost;
gboolean hairpin_mode;
GPtrArray *vlans;
} NMSettingBridgePortPrivate;
G_DEFINE_TYPE (NMSettingBridgePort, nm_setting_bridge_port, NM_TYPE_SETTING)
#define NM_SETTING_BRIDGE_PORT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_BRIDGE_PORT, NMSettingBridgePortPrivate))
static int
vlan_ptr_cmp (gconstpointer a, gconstpointer b)
{
const NMBridgeVlan *vlan_a = *(const NMBridgeVlan **) a;
const NMBridgeVlan *vlan_b = *(const NMBridgeVlan **) b;
return nm_bridge_vlan_cmp (vlan_a, vlan_b);
}
gboolean
_nm_setting_bridge_port_sort_vlans (NMSettingBridgePort *setting)
{
NMSettingBridgePortPrivate *priv;
gboolean need_sort = FALSE;
guint i;
priv = NM_SETTING_BRIDGE_PORT_GET_PRIVATE (setting);
for (i = 1; i < priv->vlans->len; i++) {
NMBridgeVlan *vlan_prev = priv->vlans->pdata[i - 1];
NMBridgeVlan *vlan = priv->vlans->pdata[i];
if (nm_bridge_vlan_cmp (vlan_prev, vlan) > 0) {
need_sort = TRUE;
break;
}
}
if (need_sort) {
g_ptr_array_sort (priv->vlans, vlan_ptr_cmp);
_notify (setting, PROP_VLANS);
}
return need_sort;
}
/*****************************************************************************/
/**
@ -102,11 +140,160 @@ nm_setting_bridge_port_get_hairpin_mode (NMSettingBridgePort *setting)
return NM_SETTING_BRIDGE_PORT_GET_PRIVATE (setting)->hairpin_mode;
}
/**
* nm_setting_bridge_port_add_vlan:
* @setting: the #NMSettingBridgePort
* @vlan: the vlan to add
*
* Appends a new vlan and associated information to the setting. The
* given vlan gets sealed and a reference to it is added.
*
* Since: 1.18
**/
void
nm_setting_bridge_port_add_vlan (NMSettingBridgePort *setting,
NMBridgeVlan *vlan)
{
NMSettingBridgePortPrivate *priv;
g_return_if_fail (NM_IS_SETTING_BRIDGE_PORT (setting));
g_return_if_fail (vlan);
priv = NM_SETTING_BRIDGE_PORT_GET_PRIVATE (setting);
nm_bridge_vlan_seal (vlan);
nm_bridge_vlan_ref (vlan);
g_ptr_array_add (priv->vlans, vlan);
_notify (setting, PROP_VLANS);
}
/**
* nm_setting_bridge_port_get_num_vlans:
* @setting: the #NMSettingBridgePort
*
* Returns: the number of VLANs
*
* Since: 1.18
**/
guint
nm_setting_bridge_port_get_num_vlans (NMSettingBridgePort *setting)
{
NMSettingBridgePortPrivate *priv;
g_return_val_if_fail (NM_IS_SETTING_BRIDGE_PORT (setting), 0);
priv = NM_SETTING_BRIDGE_PORT_GET_PRIVATE (setting);
return priv->vlans->len;
}
/**
* nm_setting_bridge_port_get_vlan:
* @setting: the #NMSettingBridgePort
* @idx: index number of the VLAN to return
*
* Returns: (transfer none): the VLAN at index @idx
*
* Since: 1.18
**/
NMBridgeVlan *
nm_setting_bridge_port_get_vlan (NMSettingBridgePort *setting, guint idx)
{
NMSettingBridgePortPrivate *priv;
g_return_val_if_fail (NM_IS_SETTING_BRIDGE_PORT (setting), NULL);
priv = NM_SETTING_BRIDGE_PORT_GET_PRIVATE (setting);
g_return_val_if_fail (idx < priv->vlans->len, NULL);
return priv->vlans->pdata[idx];
}
/**
* nm_setting_bridge_port_remove_vlan:
* @setting: the #NMSettingBridgePort
* @idx: index number of the VLAN.
*
* Removes the vlan at index @idx.
*
* Since: 1.18
**/
void
nm_setting_bridge_port_remove_vlan (NMSettingBridgePort *setting, guint idx)
{
NMSettingBridgePortPrivate *priv;
g_return_if_fail (NM_IS_SETTING_BRIDGE_PORT (setting));
priv = NM_SETTING_BRIDGE_PORT_GET_PRIVATE (setting);
g_return_if_fail (idx < priv->vlans->len);
g_ptr_array_remove_index (priv->vlans, idx);
_notify (setting, PROP_VLANS);
}
/**
* nm_setting_bridge_port_remove_vlan_by_vid:
* @setting: the #NMSettingBridgePort
* @vid: the vlan index of the vlan to remove
*
* Removes the vlan vith id @vid.
*
* Returns: %TRUE if the vlan was found and removed; %FALSE otherwise
*
* Since: 1.18
**/
gboolean
nm_setting_bridge_port_remove_vlan_by_vid (NMSettingBridgePort *setting,
guint16 vid)
{
NMSettingBridgePortPrivate *priv;
guint i;
g_return_val_if_fail (NM_IS_SETTING_BRIDGE_PORT (setting), FALSE);
priv = NM_SETTING_BRIDGE_PORT_GET_PRIVATE (setting);
for (i = 0; i < priv->vlans->len; i++) {
if (nm_bridge_vlan_get_vid (priv->vlans->pdata[i]) == vid) {
g_ptr_array_remove_index (priv->vlans, i);
_notify (setting, PROP_VLANS);
return TRUE;
}
}
return FALSE;
}
/**
* nm_setting_bridge_port_clear_vlans:
* @setting: the #NMSettingBridgePort
*
* Removes all configured VLANs.
*
* Since: 1.18
**/
void
nm_setting_bridge_port_clear_vlans (NMSettingBridgePort *setting)
{
NMSettingBridgePortPrivate *priv;
g_return_if_fail (NM_IS_SETTING_BRIDGE_PORT (setting));
priv = NM_SETTING_BRIDGE_PORT_GET_PRIVATE (setting);
if (priv->vlans->len != 0) {
g_ptr_array_set_size (priv->vlans, 0);
_notify (setting, PROP_VLANS);
}
}
/*****************************************************************************/
static gboolean
verify (NMSetting *setting, NMConnection *connection, GError **error)
{
NMSettingBridgePortPrivate *priv;
priv = NM_SETTING_BRIDGE_PORT_GET_PRIVATE (setting);
if (connection) {
NMSettingConnection *s_con;
const char *slave_type;
@ -136,9 +323,58 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
}
}
if (!_nm_utils_bridge_vlan_verify_list (priv->vlans,
FALSE,
error,
NM_SETTING_BRIDGE_PORT_SETTING_NAME,
NM_SETTING_BRIDGE_PORT_VLANS))
return FALSE;
/* Failures from here on are NORMALIZABLE... */
if (!_nm_utils_bridge_vlan_verify_list (priv->vlans,
TRUE,
error,
NM_SETTING_BRIDGE_PORT_SETTING_NAME,
NM_SETTING_BRIDGE_PORT_VLANS))
return NM_SETTING_VERIFY_NORMALIZABLE;
return TRUE;
}
static NMTernary
compare_property (const NMSettInfoSetting *sett_info,
guint property_idx,
NMSetting *setting,
NMSetting *other,
NMSettingCompareFlags flags)
{
NMSettingBridgePortPrivate *priv_a;
NMSettingBridgePortPrivate *priv_b;
guint i;
if (nm_streq (sett_info->property_infos[property_idx].name, NM_SETTING_BRIDGE_PORT_VLANS)) {
if (other) {
priv_a = NM_SETTING_BRIDGE_PORT_GET_PRIVATE (setting);
priv_b = NM_SETTING_BRIDGE_PORT_GET_PRIVATE (other);
if (priv_a->vlans->len != priv_b->vlans->len)
return FALSE;
for (i = 0; i < priv_a->vlans->len; i++) {
if (nm_bridge_vlan_cmp (priv_a->vlans->pdata[i], priv_b->vlans->pdata[i]))
return FALSE;
}
}
return TRUE;
}
return NM_SETTING_CLASS (nm_setting_bridge_port_parent_class)->compare_property (sett_info,
property_idx,
setting,
other,
flags);
}
/*****************************************************************************/
static void
@ -157,6 +393,11 @@ get_property (GObject *object, guint prop_id,
case PROP_HAIRPIN_MODE:
g_value_set_boolean (value, priv->hairpin_mode);
break;
case PROP_VLANS:
g_value_take_boxed (value, _nm_utils_copy_array (priv->vlans,
(NMUtilsCopyFunc) nm_bridge_vlan_ref,
(GDestroyNotify) nm_bridge_vlan_unref));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -179,6 +420,12 @@ set_property (GObject *object, guint prop_id,
case PROP_HAIRPIN_MODE:
priv->hairpin_mode = g_value_get_boolean (value);
break;
case PROP_VLANS:
g_ptr_array_unref (priv->vlans);
priv->vlans = _nm_utils_copy_array (g_value_get_boxed (value),
(NMUtilsCopyFunc) _nm_bridge_vlan_dup_and_seal,
(GDestroyNotify) nm_bridge_vlan_unref);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -190,6 +437,9 @@ set_property (GObject *object, guint prop_id,
static void
nm_setting_bridge_port_init (NMSettingBridgePort *setting)
{
NMSettingBridgePortPrivate *priv = NM_SETTING_BRIDGE_PORT_GET_PRIVATE (setting);
priv->vlans = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_bridge_vlan_unref);
}
/**
@ -205,17 +455,30 @@ nm_setting_bridge_port_new (void)
return (NMSetting *) g_object_new (NM_TYPE_SETTING_BRIDGE_PORT, NULL);
}
static void
finalize (GObject *object)
{
NMSettingBridgePortPrivate *priv = NM_SETTING_BRIDGE_PORT_GET_PRIVATE (object);
g_ptr_array_unref (priv->vlans);
G_OBJECT_CLASS (nm_setting_bridge_port_parent_class)->finalize (object);
}
static void
nm_setting_bridge_port_class_init (NMSettingBridgePortClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
GArray *properties_override = _nm_sett_info_property_override_create_array ();
g_type_class_add_private (klass, sizeof (NMSettingBridgePortPrivate));
object_class->finalize = finalize;
object_class->get_property = get_property;
object_class->set_property = set_property;
setting_class->compare_property = compare_property;
setting_class->verify = verify;
/**
@ -280,7 +543,45 @@ nm_setting_bridge_port_class_init (NMSettingBridgePortClass *klass)
NM_SETTING_PARAM_INFERRABLE |
G_PARAM_STATIC_STRINGS);
/**
* NMSettingBridgePort:vlans: (type GPtrArray(NMBridgeVlan))
*
* Array of bridge VLAN objects. In addition to the VLANs
* specified here, the port will also have the default-pvid
* VLAN configured on the bridge by the bridge.vlan-default-pvid
* property.
*
* In nmcli the VLAN list can be specified with the following
* syntax:
*
* $vid [pvid] [untagged] [, $vid [pvid] [untagged]]...
*
* Since: 1.18
**/
/* ---ifcfg-rh---
* property: vlans
* variable: BRIDGE_PORT_VLANS
* description: List of VLANs on the bridge port
* example: BRIDGE_PORT_VLANS="1 pvid untagged,20,40 untagged"
* ---end---
*/
obj_properties[PROP_VLANS] =
g_param_spec_boxed (NM_SETTING_BRIDGE_PORT_VLANS, "", "",
G_TYPE_PTR_ARRAY,
G_PARAM_READWRITE |
NM_SETTING_PARAM_INFERRABLE |
G_PARAM_STATIC_STRINGS);
_properties_override_add_override (properties_override,
obj_properties[PROP_VLANS],
G_VARIANT_TYPE ("aa{sv}"),
_nm_utils_bridge_vlans_to_dbus,
_nm_utils_bridge_vlans_from_dbus,
NULL);
g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
_nm_setting_class_commit (setting_class, NM_META_SETTING_TYPE_BRIDGE_PORT);
_nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_BRIDGE_PORT,
NULL, properties_override);
}

View file

@ -27,6 +27,7 @@
#endif
#include "nm-setting.h"
#include "nm-setting-bridge.h"
G_BEGIN_DECLS
@ -42,6 +43,7 @@ G_BEGIN_DECLS
#define NM_SETTING_BRIDGE_PORT_PRIORITY "priority"
#define NM_SETTING_BRIDGE_PORT_PATH_COST "path-cost"
#define NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE "hairpin-mode"
#define NM_SETTING_BRIDGE_PORT_VLANS "vlans"
/**
* NMSettingBridgePort:
@ -69,6 +71,20 @@ guint16 nm_setting_bridge_port_get_path_cost (NMSettingBridgePort *settin
gboolean nm_setting_bridge_port_get_hairpin_mode (NMSettingBridgePort *setting);
NM_AVAILABLE_IN_1_18
void nm_setting_bridge_port_add_vlan (NMSettingBridgePort *setting,
NMBridgeVlan *vlan);
NM_AVAILABLE_IN_1_18
guint nm_setting_bridge_port_get_num_vlans (NMSettingBridgePort *setting);
NM_AVAILABLE_IN_1_18
NMBridgeVlan *nm_setting_bridge_port_get_vlan (NMSettingBridgePort *setting, guint idx);
NM_AVAILABLE_IN_1_18
void nm_setting_bridge_port_remove_vlan (NMSettingBridgePort *setting, guint idx);
NM_AVAILABLE_IN_1_18
gboolean nm_setting_bridge_port_remove_vlan_by_vid (NMSettingBridgePort *setting, guint16 vid);
NM_AVAILABLE_IN_1_18
void nm_setting_bridge_port_clear_vlans (NMSettingBridgePort *setting);
G_END_DECLS
#endif /* __NM_SETTING_BRIDGE_PORT_H__ */

View file

@ -40,7 +40,7 @@
/*****************************************************************************/
NM_GOBJECT_PROPERTIES_DEFINE_BASE (
NM_GOBJECT_PROPERTIES_DEFINE (NMSettingBridge,
PROP_MAC_ADDRESS,
PROP_STP,
PROP_PRIORITY,
@ -50,6 +50,9 @@ NM_GOBJECT_PROPERTIES_DEFINE_BASE (
PROP_AGEING_TIME,
PROP_GROUP_FORWARD_MASK,
PROP_MULTICAST_SNOOPING,
PROP_VLAN_FILTERING,
PROP_VLAN_DEFAULT_PVID,
PROP_VLANS,
);
typedef struct {
@ -62,6 +65,9 @@ typedef struct {
guint32 ageing_time;
guint16 group_forward_mask;
gboolean multicast_snooping;
gboolean vlan_filtering;
guint16 vlan_default_pvid;
GPtrArray *vlans;
} NMSettingBridgePrivate;
G_DEFINE_TYPE (NMSettingBridge, nm_setting_bridge, NM_TYPE_SETTING)
@ -70,6 +76,446 @@ G_DEFINE_TYPE (NMSettingBridge, nm_setting_bridge, NM_TYPE_SETTING)
/*****************************************************************************/
G_DEFINE_BOXED_TYPE (NMBridgeVlan, nm_bridge_vlan, _nm_bridge_vlan_dup, nm_bridge_vlan_unref)
struct _NMBridgeVlan {
guint refcount;
guint16 vid;
bool untagged:1;
bool pvid:1;
bool sealed:1;
};
static gboolean
NM_IS_BRIDGE_VLAN (const NMBridgeVlan *self, gboolean also_sealed)
{
return self
&& self->refcount > 0
&& (also_sealed || !self->sealed);
}
/**
* nm_bridge_vlan_new:
* @vid: the VLAN id, must be between 1 and 4094.
*
* Creates a new #NMBridgeVlan object.
*
* Returns: (transfer full): the new #NMBridgeVlan object.
*
* Since: 1.18
**/
NMBridgeVlan *
nm_bridge_vlan_new (guint16 vid)
{
NMBridgeVlan *vlan;
g_return_val_if_fail (vid >= NM_BRIDGE_VLAN_VID_MIN, NULL);
g_return_val_if_fail (vid <= NM_BRIDGE_VLAN_VID_MAX, NULL);
vlan = g_slice_new0 (NMBridgeVlan);
vlan->refcount = 1;
vlan->vid = vid;
return vlan;
}
/**
* nm_bridge_vlan_ref:
* @vlan: the #NMBridgeVlan
*
* Increases the reference count of the object.
*
* Returns: the input argument @vlan object.
*
* Since: 1.18
**/
NMBridgeVlan *
nm_bridge_vlan_ref (NMBridgeVlan *vlan)
{
g_return_val_if_fail (NM_IS_BRIDGE_VLAN (vlan, TRUE), NULL);
nm_assert (vlan->refcount < G_MAXUINT);
vlan->refcount++;
return vlan;
}
/**
* nm_bridge_vlan_unref:
* @vlan: the #NMBridgeVlan
*
* Decreases the reference count of the object. If the reference count
* reaches zero the object will be destroyed.
*
* Since: 1.18
**/
void
nm_bridge_vlan_unref (NMBridgeVlan *vlan)
{
g_return_if_fail (NM_IS_BRIDGE_VLAN (vlan, TRUE));
if (--vlan->refcount == 0)
g_slice_free (NMBridgeVlan, vlan);
}
/**
* nm_bridge_vlan_cmp:
* @a: a #NMBridgeVlan
* @b: another #NMBridgeVlan
*
* Compare two bridge VLAN objects.
*
* Returns: zero of the two instances are equivalent or
* a non-zero integer otherwise. This defines a total ordering
* over the VLANs. Whether a VLAN is sealed or not does not
* affect the comparison.
*
* Since: 1.18
**/
int
nm_bridge_vlan_cmp (const NMBridgeVlan *a, const NMBridgeVlan *b)
{
g_return_val_if_fail (NM_IS_BRIDGE_VLAN (a, TRUE), 0);
g_return_val_if_fail (NM_IS_BRIDGE_VLAN (b, TRUE), 0);
NM_CMP_SELF (a, b);
NM_CMP_FIELD (a, b, vid);
NM_CMP_FIELD_BOOL (a, b, untagged);
NM_CMP_FIELD_BOOL (a, b, pvid);
return 0;
}
NMBridgeVlan *
_nm_bridge_vlan_dup (const NMBridgeVlan *vlan)
{
g_return_val_if_fail (NM_IS_BRIDGE_VLAN (vlan, TRUE), NULL);
if (vlan->sealed) {
nm_bridge_vlan_ref ((NMBridgeVlan *) vlan);
return (NMBridgeVlan *) vlan;
}
return nm_bridge_vlan_new_clone (vlan);
}
NMBridgeVlan *
_nm_bridge_vlan_dup_and_seal (const NMBridgeVlan *vlan)
{
NMBridgeVlan *new;
g_return_val_if_fail (NM_IS_BRIDGE_VLAN (vlan, TRUE), NULL);
new = _nm_bridge_vlan_dup (vlan);
nm_bridge_vlan_seal (new);
return new;
}
/**
* nm_bridge_vlan_get_vid:
* @vlan: the #NMBridgeVlan
*
* Gets the VLAN id of the object.
*
* Returns: the VLAN id
*
* Since: 1.18
**/
guint16
nm_bridge_vlan_get_vid (const NMBridgeVlan *vlan)
{
g_return_val_if_fail (NM_IS_BRIDGE_VLAN (vlan, TRUE), 0);
return vlan->vid;
}
/**
* nm_bridge_vlan_is_untagged:
* @vlan: the #NMBridgeVlan
*
* Returns whether the VLAN is untagged.
*
* Returns: %TRUE if the VLAN is untagged, %FALSE otherwise
*
* Since: 1.18
**/
gboolean
nm_bridge_vlan_is_untagged (const NMBridgeVlan *vlan)
{
g_return_val_if_fail (NM_IS_BRIDGE_VLAN (vlan, TRUE), FALSE);
return vlan->untagged;
}
/**
* nm_bridge_vlan_is_pvid:
* @vlan: the #NMBridgeVlan
*
* Returns whether the VLAN is the PVID for the port.
*
* Returns: %TRUE if the VLAN is the PVID
*
* Since: 1.18
**/
gboolean
nm_bridge_vlan_is_pvid (const NMBridgeVlan *vlan)
{
g_return_val_if_fail (NM_IS_BRIDGE_VLAN (vlan, TRUE), FALSE);
return vlan->pvid;
}
/**
* nm_bridge_vlan_set_untagged:
* @vlan: the #NMBridgeVlan
* @value: the new value
*
* Change the value of the untagged property of the VLAN.
*
* Since: 1.18
**/
void
nm_bridge_vlan_set_untagged (NMBridgeVlan *vlan, gboolean value)
{
g_return_if_fail (NM_IS_BRIDGE_VLAN (vlan, FALSE));
vlan->untagged = value;
}
/**
* nm_bridge_vlan_set_pvid:
* @vlan: the #NMBridgeVlan
* @value: the new value
*
* Change the value of the PVID property of the VLAN.
*
* Since: 1.18
**/
void
nm_bridge_vlan_set_pvid (NMBridgeVlan *vlan, gboolean value)
{
g_return_if_fail (NM_IS_BRIDGE_VLAN (vlan, FALSE));
vlan->pvid = value;
}
/**
* nm_bridge_vlan_is_sealed:
* @vlan: the #NMBridgeVlan instance
*
* Returns: whether @self is sealed or not.
*
* Since: 1.18
*/
gboolean
nm_bridge_vlan_is_sealed (const NMBridgeVlan *vlan)
{
g_return_val_if_fail (NM_IS_BRIDGE_VLAN (vlan, TRUE), FALSE);
return vlan->sealed;
}
/**
* nm_bridge_vlan_seal:
* @vlan: the #NMBridgeVlan instance
*
* Seal the #NMBridgeVlan instance. Afterwards, it is a bug
* to call all functions that modify the instance (except ref/unref).
* A sealed instance cannot be unsealed again, but you can create
* an unsealed copy with nm_bridge_vlan_new_clone().
*
* Since: 1.18
*/
void
nm_bridge_vlan_seal (NMBridgeVlan *vlan)
{
g_return_if_fail (NM_IS_BRIDGE_VLAN (vlan, TRUE));
vlan->sealed = TRUE;
}
/**
* nm_bridge_vlan_new_clone:
* @vlan: the #NMBridgeVlan instance to copy
*
* Returns: (transfer full): a clone of @vlan. This instance
* is always unsealed.
*
* Since: 1.18
*/
NMBridgeVlan *
nm_bridge_vlan_new_clone (const NMBridgeVlan *vlan)
{
NMBridgeVlan *copy;
g_return_val_if_fail (NM_IS_BRIDGE_VLAN (vlan, TRUE), NULL);
copy = nm_bridge_vlan_new (vlan->vid);
copy->untagged = vlan->untagged;
copy->pvid = vlan->pvid;
return copy;
}
void
_nm_bridge_vlan_str_append_rest (const NMBridgeVlan *vlan,
GString *string,
gboolean leading_space)
{
if (nm_bridge_vlan_is_pvid (vlan)) {
if (leading_space)
g_string_append_c (string, ' ');
g_string_append (string, "pvid");
leading_space = TRUE;
}
if (nm_bridge_vlan_is_untagged (vlan)) {
if (leading_space)
g_string_append_c (string, ' ');
g_string_append (string, "untagged");
leading_space = TRUE;
}
}
/**
* nm_bridge_vlan_to_str:
* @vlan: the %NMBridgeVlan
* @error: location of the error
*
* Convert a %NMBridgeVlan to a string.
*
* Returns: formatted string or %NULL
*
* Since: 1.18
*/
char *
nm_bridge_vlan_to_str (const NMBridgeVlan *vlan, GError **error)
{
GString *string;
g_return_val_if_fail (vlan, NULL);
g_return_val_if_fail (!error || !*error, NULL);
/* The function never fails at the moment, but it might in the
* future if more parameters are added to the object that could
* make it invalid. */
string = g_string_sized_new (20);
g_string_append_printf (string, "%u", nm_bridge_vlan_get_vid (vlan));
_nm_bridge_vlan_str_append_rest (vlan, string, TRUE);
return g_string_free (string, FALSE);
}
/**
* nm_bridge_vlan_from_str:
* @str: the string representation of a bridge VLAN
* @error: location of the error
*
* Parses the string representation of the queueing
* discipline to a %NMBridgeVlan instance.
*
* Returns: the %NMBridgeVlan or %NULL
*
* Since: 1.18
*/
NMBridgeVlan *
nm_bridge_vlan_from_str (const char *str, GError **error)
{
NMBridgeVlan *vlan = NULL;
gs_unref_hashtable GHashTable *ht = NULL;
gs_free const char **tokens = NULL;
guint i, vid;
gboolean pvid = FALSE;
gboolean untagged = FALSE;
g_return_val_if_fail (str, NULL);
g_return_val_if_fail (!error || !*error, NULL);
tokens = nm_utils_strsplit_set (str, " ", FALSE);
if (!tokens || !tokens[0]) {
g_set_error_literal (error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_FAILED,
"missing VLAN id");
return NULL;
}
vid = _nm_utils_ascii_str_to_uint64 (tokens[0],
10,
NM_BRIDGE_VLAN_VID_MIN,
NM_BRIDGE_VLAN_VID_MAX,
G_MAXUINT);
if (vid == G_MAXUINT) {
g_set_error (error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_FAILED,
"invalid VLAN id '%s', must be in [1,4094]", tokens[0]);
return NULL;
}
for (i = 1; tokens[i]; i++) {
if (nm_streq (tokens[i], "pvid"))
pvid = TRUE;
else if (nm_streq (tokens[i], "untagged"))
untagged = TRUE;
else {
g_set_error (error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_FAILED,
"invalid option '%s'", tokens[i]);
return NULL;
}
}
vlan = nm_bridge_vlan_new (vid);
nm_bridge_vlan_set_pvid (vlan, pvid);
nm_bridge_vlan_set_untagged (vlan, untagged);
return vlan;
}
/*****************************************************************************/
static int
vlan_ptr_cmp (gconstpointer a, gconstpointer b)
{
const NMBridgeVlan *vlan_a = *(const NMBridgeVlan **) a;
const NMBridgeVlan *vlan_b = *(const NMBridgeVlan **) b;
return nm_bridge_vlan_cmp (vlan_a, vlan_b);
}
gboolean
_nm_setting_bridge_sort_vlans (NMSettingBridge *setting)
{
NMSettingBridgePrivate *priv;
gboolean need_sort = FALSE;
guint i;
priv = NM_SETTING_BRIDGE_GET_PRIVATE (setting);
for (i = 1; i < priv->vlans->len; i++) {
NMBridgeVlan *vlan_prev = priv->vlans->pdata[i - 1];
NMBridgeVlan *vlan = priv->vlans->pdata[i];
if (nm_bridge_vlan_cmp (vlan_prev, vlan) > 0) {
need_sort = TRUE;
break;
}
}
if (need_sort) {
g_ptr_array_sort (priv->vlans, vlan_ptr_cmp);
_notify (setting, PROP_VLANS);
}
return need_sort;
}
/*****************************************************************************/
/**
* nm_setting_bridge_get_mac_address:
* @setting: the #NMSettingBridge
@ -200,6 +646,185 @@ nm_setting_bridge_get_multicast_snooping (NMSettingBridge *setting)
return NM_SETTING_BRIDGE_GET_PRIVATE (setting)->multicast_snooping;
}
/**
* nm_setting_bridge_get_vlan_filtering:
* @setting: the #NMSettingBridge
*
* Returns: the #NMSettingBridge:vlan-filtering property of the setting
*
* Since: 1.18
**/
gboolean
nm_setting_bridge_get_vlan_filtering (NMSettingBridge *setting)
{
g_return_val_if_fail (NM_IS_SETTING_BRIDGE (setting), FALSE);
return NM_SETTING_BRIDGE_GET_PRIVATE (setting)->vlan_filtering;
}
/**
* nm_setting_bridge_get_vlan_default_pvid:
* @setting: the #NMSettingBridge
*
* Returns: the #NMSettingBridge:vlan-default-pvid property of the setting
*
* Since: 1.18
**/
guint16
nm_setting_bridge_get_vlan_default_pvid (NMSettingBridge *setting)
{
g_return_val_if_fail (NM_IS_SETTING_BRIDGE (setting), 1);
return NM_SETTING_BRIDGE_GET_PRIVATE (setting)->vlan_default_pvid;
}
/**
* nm_setting_bridge_add_vlan:
* @setting: the #NMSettingBridge
* @vlan: the vlan to add
*
* Appends a new vlan and associated information to the setting. The
* given vlan gets sealed and a reference to it is added.
*
* Since: 1.18
**/
void
nm_setting_bridge_add_vlan (NMSettingBridge *setting,
NMBridgeVlan *vlan)
{
NMSettingBridgePrivate *priv;
g_return_if_fail (NM_IS_SETTING_BRIDGE (setting));
g_return_if_fail (vlan);
priv = NM_SETTING_BRIDGE_GET_PRIVATE (setting);
nm_bridge_vlan_seal (vlan);
nm_bridge_vlan_ref (vlan);
g_ptr_array_add (priv->vlans, vlan);
_notify (setting, PROP_VLANS);
}
/**
* nm_setting_bridge_get_num_vlans:
* @setting: the #NMSettingBridge
*
* Returns: the number of VLANs
*
* Since: 1.18
**/
guint
nm_setting_bridge_get_num_vlans (NMSettingBridge *setting)
{
NMSettingBridgePrivate *priv;
g_return_val_if_fail (NM_IS_SETTING_BRIDGE (setting), 0);
priv = NM_SETTING_BRIDGE_GET_PRIVATE (setting);
return priv->vlans->len;
}
/**
* nm_setting_bridge_get_vlan:
* @setting: the #NMSettingBridge
* @idx: index number of the VLAN to return
*
* Returns: (transfer none): the VLAN at index @idx
*
* Since: 1.18
**/
NMBridgeVlan *
nm_setting_bridge_get_vlan (NMSettingBridge *setting, guint idx)
{
NMSettingBridgePrivate *priv;
g_return_val_if_fail (NM_IS_SETTING_BRIDGE (setting), NULL);
priv = NM_SETTING_BRIDGE_GET_PRIVATE (setting);
g_return_val_if_fail (idx < priv->vlans->len, NULL);
return priv->vlans->pdata[idx];
}
/**
* nm_setting_bridge_remove_vlan:
* @setting: the #NMSettingBridge
* @idx: index number of the VLAN.
*
* Removes the vlan at index @idx.
*
* Since: 1.18
**/
void
nm_setting_bridge_remove_vlan (NMSettingBridge *setting, guint idx)
{
NMSettingBridgePrivate *priv;
g_return_if_fail (NM_IS_SETTING_BRIDGE (setting));
priv = NM_SETTING_BRIDGE_GET_PRIVATE (setting);
g_return_if_fail (idx < priv->vlans->len);
g_ptr_array_remove_index (priv->vlans, idx);
_notify (setting, PROP_VLANS);
}
/**
* nm_setting_bridge_remove_vlan_by_vid:
* @setting: the #NMSettingBridge
* @vid: the vlan index of the vlan to remove
*
* Removes the vlan vith id @vid.
*
* Returns: %TRUE if the vlan was found and removed; %FALSE otherwise
*
* Since: 1.18
**/
gboolean
nm_setting_bridge_remove_vlan_by_vid (NMSettingBridge *setting,
guint16 vid)
{
NMSettingBridgePrivate *priv;
guint i;
g_return_val_if_fail (NM_IS_SETTING_BRIDGE (setting), FALSE);
priv = NM_SETTING_BRIDGE_GET_PRIVATE (setting);
for (i = 0; i < priv->vlans->len; i++) {
if (nm_bridge_vlan_get_vid (priv->vlans->pdata[i]) == vid) {
g_ptr_array_remove_index (priv->vlans, i);
_notify (setting, PROP_VLANS);
return TRUE;
}
}
return FALSE;
}
/**
* nm_setting_bridge_clear_vlans:
* @setting: the #NMSettingBridge
*
* Removes all configured VLANs.
*
* Since: 1.18
**/
void
nm_setting_bridge_clear_vlans (NMSettingBridge *setting)
{
NMSettingBridgePrivate *priv;
g_return_if_fail (NM_IS_SETTING_BRIDGE (setting));
priv = NM_SETTING_BRIDGE_GET_PRIVATE (setting);
if (priv->vlans->len != 0) {
g_ptr_array_set_size (priv->vlans, 0);
_notify (setting, PROP_VLANS);
}
}
/*****************************************************************************/
static gboolean
check_range (guint32 val,
guint32 min,
@ -278,7 +903,59 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
return FALSE;
}
return _nm_connection_verify_required_interface_name (connection, error);
if (!_nm_connection_verify_required_interface_name (connection, error))
return FALSE;
if (!_nm_utils_bridge_vlan_verify_list (priv->vlans,
FALSE,
error,
NM_SETTING_BRIDGE_SETTING_NAME,
NM_SETTING_BRIDGE_VLANS))
return FALSE;
/* Failures from here on are NORMALIZABLE... */
if (!_nm_utils_bridge_vlan_verify_list (priv->vlans,
TRUE,
error,
NM_SETTING_BRIDGE_SETTING_NAME,
NM_SETTING_BRIDGE_VLANS))
return NM_SETTING_VERIFY_NORMALIZABLE;
return TRUE;
}
static NMTernary
compare_property (const NMSettInfoSetting *sett_info,
guint property_idx,
NMSetting *setting,
NMSetting *other,
NMSettingCompareFlags flags)
{
NMSettingBridgePrivate *priv_a;
NMSettingBridgePrivate *priv_b;
guint i;
if (nm_streq (sett_info->property_infos[property_idx].name, NM_SETTING_BRIDGE_VLANS)) {
if (other) {
priv_a = NM_SETTING_BRIDGE_GET_PRIVATE (setting);
priv_b = NM_SETTING_BRIDGE_GET_PRIVATE (other);
if (priv_a->vlans->len != priv_b->vlans->len)
return FALSE;
for (i = 0; i < priv_a->vlans->len; i++) {
if (nm_bridge_vlan_cmp (priv_a->vlans->pdata[i], priv_b->vlans->pdata[i]))
return FALSE;
}
}
return TRUE;
}
return NM_SETTING_CLASS (nm_setting_bridge_parent_class)->compare_property (sett_info,
property_idx,
setting,
other,
flags);
}
/*****************************************************************************/
@ -318,6 +995,17 @@ get_property (GObject *object, guint prop_id,
case PROP_MULTICAST_SNOOPING:
g_value_set_boolean (value, priv->multicast_snooping);
break;
case PROP_VLAN_FILTERING:
g_value_set_boolean (value, priv->vlan_filtering);
break;
case PROP_VLAN_DEFAULT_PVID:
g_value_set_uint (value, priv->vlan_default_pvid);
break;
case PROP_VLANS:
g_value_take_boxed (value, _nm_utils_copy_array (priv->vlans,
(NMUtilsCopyFunc) nm_bridge_vlan_ref,
(GDestroyNotify) nm_bridge_vlan_unref));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -360,6 +1048,18 @@ set_property (GObject *object, guint prop_id,
case PROP_MULTICAST_SNOOPING:
priv->multicast_snooping = g_value_get_boolean (value);
break;
case PROP_VLAN_FILTERING:
priv->vlan_filtering = g_value_get_boolean (value);
break;
case PROP_VLAN_DEFAULT_PVID:
priv->vlan_default_pvid = g_value_get_uint (value);
break;
case PROP_VLANS:
g_ptr_array_unref (priv->vlans);
priv->vlans = _nm_utils_copy_array (g_value_get_boxed (value),
(NMUtilsCopyFunc) _nm_bridge_vlan_dup_and_seal,
(GDestroyNotify) nm_bridge_vlan_unref);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -371,6 +1071,9 @@ set_property (GObject *object, guint prop_id,
static void
nm_setting_bridge_init (NMSettingBridge *setting)
{
NMSettingBridgePrivate *priv = NM_SETTING_BRIDGE_GET_PRIVATE (setting);
priv->vlans = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_bridge_vlan_unref);
}
/**
@ -392,6 +1095,7 @@ finalize (GObject *object)
NMSettingBridgePrivate *priv = NM_SETTING_BRIDGE_GET_PRIVATE (object);
g_free (priv->mac_address);
g_ptr_array_unref (priv->vlans);
G_OBJECT_CLASS (nm_setting_bridge_parent_class)->finalize (object);
}
@ -409,6 +1113,7 @@ nm_setting_bridge_class_init (NMSettingBridgeClass *klass)
object_class->set_property = set_property;
object_class->finalize = finalize;
setting_class->compare_property = compare_property;
setting_class->verify = verify;
/**
@ -627,6 +1332,88 @@ nm_setting_bridge_class_init (NMSettingBridgeClass *klass)
NM_SETTING_PARAM_INFERRABLE |
G_PARAM_STATIC_STRINGS);
/**
* NMSettingBridge:vlan-filtering:
*
* Control whether VLAN filtering is enabled on the bridge.
*
* Since: 1.18
**/
/* ---ifcfg-rh---
* property: vlan-filtering
* variable: BRIDGING_OPTS: vlan_filtering=
* values: 0 or 1
* default: 0
* description: VLAN filtering support.
* ---end---
*/
obj_properties[PROP_VLAN_FILTERING] =
g_param_spec_boolean (NM_SETTING_BRIDGE_VLAN_FILTERING, "", "",
FALSE,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT |
NM_SETTING_PARAM_INFERRABLE |
G_PARAM_STATIC_STRINGS);
/**
* NMSettingBridge:vlan-default-pvid:
*
* The default PVID for the ports of the bridge, that is the VLAN id
* assigned to incoming untagged frames.
*
* Since: 1.18
**/
/* ---ifcfg-rh---
* property: vlan-default-pvid
* variable: BRIDGING_OPTS: default_pvid=
* values: 0 - 4094
* default: 1
* description: default VLAN PVID.
* ---end---
*/
obj_properties[PROP_VLAN_DEFAULT_PVID] =
g_param_spec_uint (NM_SETTING_BRIDGE_VLAN_DEFAULT_PVID, "", "",
0, NM_BRIDGE_VLAN_VID_MAX, 1,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT |
NM_SETTING_PARAM_INFERRABLE |
G_PARAM_STATIC_STRINGS);
/**
* NMSettingBridge:vlans: (type GPtrArray(NMBridgeVlan))
*
* Array of bridge VLAN objects. In addition to the VLANs
* specified here, the bridge will also have the default-pvid
* VLAN configured by the bridge.vlan-default-pvid property.
*
* In nmcli the VLAN list can be specified with the following
* syntax:
*
* $vid [pvid] [untagged] [, $vid [pvid] [untagged]]...
*
* Since: 1.18
**/
/* ---ifcfg-rh---
* property: vlans
* variable: BRIDGE_VLANS
* description: List of VLANs on the bridge
* example: BRIDGE_VLANS="1 pvid untagged,20,40 untagged"
* ---end---
*/
obj_properties[PROP_VLANS] =
g_param_spec_boxed (NM_SETTING_BRIDGE_VLANS, "", "",
G_TYPE_PTR_ARRAY,
G_PARAM_READWRITE |
NM_SETTING_PARAM_INFERRABLE |
G_PARAM_STATIC_STRINGS);
_properties_override_add_override (properties_override,
obj_properties[PROP_VLANS],
G_VARIANT_TYPE ("aa{sv}"),
_nm_utils_bridge_vlans_to_dbus,
_nm_utils_bridge_vlans_from_dbus,
NULL);
/* ---dbus---
* property: interface-name
* format: string

View file

@ -48,6 +48,12 @@ G_BEGIN_DECLS
#define NM_SETTING_BRIDGE_AGEING_TIME "ageing-time"
#define NM_SETTING_BRIDGE_GROUP_FORWARD_MASK "group-forward-mask"
#define NM_SETTING_BRIDGE_MULTICAST_SNOOPING "multicast-snooping"
#define NM_SETTING_BRIDGE_VLAN_FILTERING "vlan-filtering"
#define NM_SETTING_BRIDGE_VLAN_DEFAULT_PVID "vlan-default-pvid"
#define NM_SETTING_BRIDGE_VLANS "vlans"
#define NM_BRIDGE_VLAN_VID_MIN 1
#define NM_BRIDGE_VLAN_VID_MAX 4094
/**
* NMSettingBridge:
@ -65,6 +71,8 @@ typedef struct {
gpointer padding[4];
} NMSettingBridgeClass;
typedef struct _NMBridgeVlan NMBridgeVlan;
GType nm_setting_bridge_get_type (void);
NMSetting * nm_setting_bridge_new (void);
@ -86,6 +94,55 @@ NM_AVAILABLE_IN_1_10
guint16 nm_setting_bridge_get_group_forward_mask (NMSettingBridge *setting);
gboolean nm_setting_bridge_get_multicast_snooping (NMSettingBridge *setting);
NM_AVAILABLE_IN_1_18
gboolean nm_setting_bridge_get_vlan_filtering (NMSettingBridge *setting);
NM_AVAILABLE_IN_1_18
guint16 nm_setting_bridge_get_vlan_default_pvid (NMSettingBridge *setting);
NM_AVAILABLE_IN_1_18
void nm_setting_bridge_add_vlan (NMSettingBridge *setting,
NMBridgeVlan *vlan);
NM_AVAILABLE_IN_1_18
guint nm_setting_bridge_get_num_vlans (NMSettingBridge *setting);
NM_AVAILABLE_IN_1_18
NMBridgeVlan *nm_setting_bridge_get_vlan (NMSettingBridge *setting, guint idx);
NM_AVAILABLE_IN_1_18
void nm_setting_bridge_remove_vlan (NMSettingBridge *setting, guint idx);
NM_AVAILABLE_IN_1_18
gboolean nm_setting_bridge_remove_vlan_by_vid (NMSettingBridge *setting, guint16 vid);
NM_AVAILABLE_IN_1_18
void nm_setting_bridge_clear_vlans (NMSettingBridge *setting);
NM_AVAILABLE_IN_1_18
GType nm_bridge_vlan_get_type (void);
NM_AVAILABLE_IN_1_18
NMBridgeVlan * nm_bridge_vlan_new (guint16 vid);
NM_AVAILABLE_IN_1_18
NMBridgeVlan * nm_bridge_vlan_ref (NMBridgeVlan *vlan);
NM_AVAILABLE_IN_1_18
void nm_bridge_vlan_unref (NMBridgeVlan *vlan);
NM_AVAILABLE_IN_1_18
NMBridgeVlan * nm_bridge_vlan_new_clone (const NMBridgeVlan *vlan);
NM_AVAILABLE_IN_1_18
int nm_bridge_vlan_cmp (const NMBridgeVlan *a, const NMBridgeVlan *b);
NM_AVAILABLE_IN_1_18
void nm_bridge_vlan_seal (NMBridgeVlan *vlan);
NM_AVAILABLE_IN_1_18
gboolean nm_bridge_vlan_is_sealed (const NMBridgeVlan *vlan);
NM_AVAILABLE_IN_1_18
void nm_bridge_vlan_set_untagged (NMBridgeVlan *vlan, gboolean value);
NM_AVAILABLE_IN_1_18
void nm_bridge_vlan_set_pvid (NMBridgeVlan *vlan, gboolean value);
NM_AVAILABLE_IN_1_18
guint16 nm_bridge_vlan_get_vid (const NMBridgeVlan *vlan);
NM_AVAILABLE_IN_1_18
gboolean nm_bridge_vlan_is_untagged (const NMBridgeVlan *vlan);
NM_AVAILABLE_IN_1_18
gboolean nm_bridge_vlan_is_pvid (const NMBridgeVlan *vlan);
NM_AVAILABLE_IN_1_18
char * nm_bridge_vlan_to_str (const NMBridgeVlan *vlan, GError **error);
NM_AVAILABLE_IN_1_18
NMBridgeVlan * nm_bridge_vlan_from_str (const char *str, GError **error);
G_END_DECLS

View file

@ -26,6 +26,7 @@
#endif
#include "nm-setting.h"
#include "nm-setting-bridge.h"
#include "nm-connection.h"
#include "nm-core-enum-types.h"
@ -204,6 +205,9 @@ gboolean _nm_setting_should_compare_secret_property (NMSetting *setting,
const char *secret_name,
NMSettingCompareFlags flags);
NMBridgeVlan *_nm_bridge_vlan_dup (const NMBridgeVlan *vlan);
NMBridgeVlan *_nm_bridge_vlan_dup_and_seal (const NMBridgeVlan *vlan);
/*****************************************************************************/
#endif /* NM_SETTING_PRIVATE_H */

View file

@ -94,7 +94,7 @@ _vf_vlan_create_hash (void)
}
/**
* nm_srio_vf_new:
* nm_sriov_vf_new:
* @index: the VF index
*
* Creates a new #NMSriovVF object.
@ -894,8 +894,10 @@ _nm_setting_sriov_sort_vfs (NMSettingSriov *setting)
}
}
if (need_sort)
if (need_sort) {
g_ptr_array_sort (setting->vfs, vf_index_compare);
_notify (setting, PROP_VFS);
}
return need_sort;
}

View file

@ -103,6 +103,19 @@ void _nm_utils_format_variant_attributes_full (GString *str,
char key_value_separator);
gboolean _nm_sriov_vf_parse_vlans (NMSriovVF *vf, const char *str, GError **error);
GVariant * _nm_utils_bridge_vlans_to_dbus (NMSetting *setting, const char *property);
gboolean _nm_utils_bridge_vlans_from_dbus (NMSetting *setting,
GVariant *connection_dict,
const char *property,
GVariant *value,
NMSettingParseFlags parse_flags,
GError **error);
gboolean _nm_utils_bridge_vlan_verify_list (GPtrArray *vlans,
gboolean check_normalizable,
GError **error,
const char *setting,
const char *property);
/* JSON to GValue conversion macros */
static inline void

View file

@ -46,6 +46,7 @@
#include "nm-crypto.h"
#include "nm-setting-bond.h"
#include "nm-setting-bridge.h"
#include "nm-setting-bridge-port.h"
#include "nm-setting-infiniband.h"
#include "nm-setting-ip6-config.h"
#include "nm-setting-team.h"
@ -2439,7 +2440,7 @@ _tc_read_common_opts (const char *str,
* @str: the string representation of a qdisc
* @error: location of the error
*
* Parces the tc style string qdisc representation of the queueing
* Parses the tc style string qdisc representation of the queueing
* discipline to a %NMTCQdisc instance. Supports a subset of the tc language.
*
* Returns: the %NMTCQdisc or %NULL
@ -2554,7 +2555,7 @@ nm_utils_tc_action_to_str (NMTCAction *action, GError **error)
* @str: the string representation of a action
* @error: location of the error
*
* Parces the tc style string action representation of the queueing
* Parses the tc style string action representation of the queueing
* discipline to a %NMTCAction instance. Supports a subset of the tc language.
*
* Returns: the %NMTCAction or %NULL
@ -2707,7 +2708,7 @@ static const NMVariantAttributeSpec * const tc_tfilter_attribute_spec[] = {
* @str: the string representation of a tfilter
* @error: location of the error
*
* Parces the tc style string tfilter representation of the queueing
* Parses the tc style string tfilter representation of the queueing
* discipline to a %NMTCTfilter instance. Supports a subset of the tc language.
*
* Returns: the %NMTCTfilter or %NULL
@ -6710,3 +6711,141 @@ nm_utils_base64secret_normalize (const char *base64_key,
nm_explicit_bzero (buf, required_key_len);
return TRUE;
}
GVariant *
_nm_utils_bridge_vlans_to_dbus (NMSetting *setting, const char *property)
{
gs_unref_ptrarray GPtrArray *vlans = NULL;
GVariantBuilder builder;
guint i;
g_object_get (setting, property, &vlans, NULL);
g_variant_builder_init (&builder, G_VARIANT_TYPE ("aa{sv}"));
if (vlans) {
for (i = 0; i < vlans->len; i++) {
NMBridgeVlan *vlan = vlans->pdata[i];
GVariantBuilder vlan_builder;
g_variant_builder_init (&vlan_builder, G_VARIANT_TYPE_VARDICT);
g_variant_builder_add (&vlan_builder, "{sv}", "vid",
g_variant_new_uint16 (nm_bridge_vlan_get_vid (vlan)));
g_variant_builder_add (&vlan_builder, "{sv}", "pvid",
g_variant_new_boolean (nm_bridge_vlan_is_pvid (vlan)));
g_variant_builder_add (&vlan_builder, "{sv}", "untagged",
g_variant_new_boolean (nm_bridge_vlan_is_untagged (vlan)));
g_variant_builder_add (&builder, "a{sv}", &vlan_builder);
}
}
return g_variant_builder_end (&builder);
}
gboolean
_nm_utils_bridge_vlans_from_dbus (NMSetting *setting,
GVariant *connection_dict,
const char *property,
GVariant *value,
NMSettingParseFlags parse_flags,
GError **error)
{
gs_unref_ptrarray GPtrArray *vlans = NULL;
GVariantIter vlan_iter;
GVariant *vlan_var;
g_return_val_if_fail (g_variant_is_of_type (value, G_VARIANT_TYPE ("aa{sv}")), FALSE);
vlans = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_bridge_vlan_unref);
g_variant_iter_init (&vlan_iter, value);
while (g_variant_iter_next (&vlan_iter, "@a{sv}", &vlan_var)) {
gs_unref_variant GVariant *var_unref = vlan_var;
NMBridgeVlan *vlan;
guint16 vid;
gboolean pvid = FALSE, untagged = FALSE;
if (!g_variant_lookup (vlan_var, "vid", "q", &vid))
continue;
if ( vid < NM_BRIDGE_VLAN_VID_MIN
|| vid > NM_BRIDGE_VLAN_VID_MAX)
continue;
g_variant_lookup (vlan_var, "pvid", "b", &pvid);
g_variant_lookup (vlan_var, "untagged", "b", &untagged);
vlan = nm_bridge_vlan_new (vid);
nm_bridge_vlan_set_untagged (vlan, untagged);
nm_bridge_vlan_set_pvid (vlan, pvid);
g_ptr_array_add (vlans, vlan);
}
g_object_set (setting, property, vlans, NULL);
return TRUE;
}
gboolean
_nm_utils_bridge_vlan_verify_list (GPtrArray *vlans,
gboolean check_normalizable,
GError **error,
const char *setting,
const char *property)
{
guint i;
gs_unref_hashtable GHashTable *h = NULL;
gboolean pvid_found = FALSE;
if (!vlans || !vlans->len)
return TRUE;
if (check_normalizable) {
for (i = 1; i < vlans->len; i++) {
NMBridgeVlan *vlan_prev = vlans->pdata[i - 1];
NMBridgeVlan *vlan = vlans->pdata[i];
if (nm_bridge_vlan_get_vid (vlan_prev) > nm_bridge_vlan_get_vid (vlan)) {
g_set_error (error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("Bridge VLANs %d and %d are not sorted by ascending vid"),
nm_bridge_vlan_get_vid (vlan_prev),
nm_bridge_vlan_get_vid (vlan));
g_prefix_error (error, "%s.%s: ", setting, property);
return FALSE;
}
}
return TRUE;
}
h = g_hash_table_new (nm_direct_hash, NULL);
for (i = 0; i < vlans->len; i++) {
NMBridgeVlan *vlan = vlans->pdata[i];
guint vid;
vid = nm_bridge_vlan_get_vid (vlan);
if (g_hash_table_contains (h, GUINT_TO_POINTER (vid))) {
g_set_error (error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("duplicate bridge VLAN vid %u"), vid);
g_prefix_error (error, "%s.%s: ", setting, property);
return FALSE;
}
if (nm_bridge_vlan_is_pvid (vlan)) {
if (pvid_found) {
g_set_error_literal (error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("only one VLAN can be the PVID"));
g_prefix_error (error, "%s.%s: ", setting, property);
return FALSE;
}
pvid_found = TRUE;
}
g_hash_table_add (h, GUINT_TO_POINTER (vid));
}
return TRUE;
}

View file

@ -744,6 +744,93 @@ test_vpn_1 (void)
/*****************************************************************************/
static void
test_bridge_vlans (void)
{
gs_unref_keyfile GKeyFile *keyfile = NULL;
gs_unref_object NMConnection *con = NULL;
NMSettingBridge *s_bridge;
NMBridgeVlan *vlan;
con = nmtst_create_connection_from_keyfile (
"[connection]\n"
"id=t\n"
"type=bridge\n"
"interface-name=br4\n"
"\n"
"[bridge]\n"
"vlan.9=untagged\n"
"vlan.1=pvid untagged\n"
"",
"/test_bridge_port/vlans");
s_bridge = NM_SETTING_BRIDGE (nm_connection_get_setting (con, NM_TYPE_SETTING_BRIDGE));
g_assert (s_bridge);
g_assert_cmpuint (nm_setting_bridge_get_num_vlans (s_bridge), ==, 2);
vlan = nm_setting_bridge_get_vlan (s_bridge, 0);
g_assert (vlan);
g_assert_cmpuint (nm_bridge_vlan_get_vid (vlan), ==, 1);
g_assert_cmpint (nm_bridge_vlan_is_pvid (vlan), ==, TRUE);
g_assert_cmpint (nm_bridge_vlan_is_untagged (vlan), ==, TRUE);
vlan = nm_setting_bridge_get_vlan (s_bridge, 1);
g_assert (vlan);
g_assert_cmpuint (nm_bridge_vlan_get_vid (vlan), ==, 9);
g_assert_cmpint (nm_bridge_vlan_is_pvid (vlan), ==, FALSE);
g_assert_cmpint (nm_bridge_vlan_is_untagged (vlan), ==, TRUE);
CLEAR (&con, &keyfile);
}
static void
test_bridge_port_vlans (void)
{
gs_unref_keyfile GKeyFile *keyfile = NULL;
gs_unref_object NMConnection *con = NULL;
NMSettingBridgePort *s_port;
NMBridgeVlan *vlan;
con = nmtst_create_connection_from_keyfile (
"[connection]\n"
"id=t\n"
"type=dummy\n"
"interface-name=dummy1\n"
"master=br0\n"
"slave-type=bridge\n"
"\n"
"[bridge-port]\n"
"vlan.4000=\n"
"vlan.10=untagged\n"
"vlan.20=pvid untagged"
"",
"/test_bridge_port/vlans");
s_port = NM_SETTING_BRIDGE_PORT (nm_connection_get_setting (con, NM_TYPE_SETTING_BRIDGE_PORT));
g_assert (s_port);
g_assert_cmpuint (nm_setting_bridge_port_get_num_vlans (s_port), ==, 3);
vlan = nm_setting_bridge_port_get_vlan (s_port, 0);
g_assert (vlan);
g_assert_cmpuint (nm_bridge_vlan_get_vid (vlan), ==, 10);
g_assert_cmpint (nm_bridge_vlan_is_pvid (vlan), ==, FALSE);
g_assert_cmpint (nm_bridge_vlan_is_untagged (vlan), ==, TRUE);
vlan = nm_setting_bridge_port_get_vlan (s_port, 1);
g_assert (vlan);
g_assert_cmpuint (nm_bridge_vlan_get_vid (vlan), ==, 20);
g_assert_cmpint (nm_bridge_vlan_is_pvid (vlan), ==, TRUE);
g_assert_cmpint (nm_bridge_vlan_is_untagged (vlan), ==, TRUE);
vlan = nm_setting_bridge_port_get_vlan (s_port, 2);
g_assert (vlan);
g_assert_cmpuint (nm_bridge_vlan_get_vid (vlan), ==, 4000);
g_assert_cmpint (nm_bridge_vlan_is_pvid (vlan), ==, FALSE);
g_assert_cmpint (nm_bridge_vlan_is_untagged (vlan), ==, FALSE);
CLEAR (&con, &keyfile);
}
/*****************************************************************************/
NMTST_DEFINE ();
int main (int argc, char **argv)
@ -757,6 +844,8 @@ int main (int argc, char **argv)
g_test_add_func ("/core/keyfile/test_team_conf_read/invalid", test_team_conf_read_invalid);
g_test_add_func ("/core/keyfile/test_user/1", test_user_1);
g_test_add_func ("/core/keyfile/test_vpn/1", test_vpn_1);
g_test_add_func ("/core/keyfile/bridge/vlans", test_bridge_vlans);
g_test_add_func ("/core/keyfile/bridge-port/vlans", test_bridge_port_vlans);
return g_test_run ();
}

View file

@ -1658,6 +1658,63 @@ test_sriov_parse_vlans (void)
test_sriov_parse_vlan_one ("1.2.ad;2.0.q;5;3", TRUE, {1, 2, 1}, {2, 0, 0}, {3, 0, 0}, {5, 0, 0});
}
static void
test_bridge_vlans (void)
{
NMBridgeVlan *v1, *v2;
GError *error = NULL;
char *str;
v1 = nm_bridge_vlan_from_str ("1 foobar", &error);
nmtst_assert_no_success (v1, error);
g_clear_error (&error);
v1 = nm_bridge_vlan_from_str ("4095", &error);
nmtst_assert_no_success (v1, error);
g_clear_error (&error);
/* test comparison (1) */
v1 = nm_bridge_vlan_from_str ("10 untagged", &error);
nmtst_assert_success (v1, error);
g_assert_cmpuint (nm_bridge_vlan_get_vid (v1), ==, 10);
g_assert_cmpint (nm_bridge_vlan_is_sealed (v1), ==, FALSE);
g_assert_cmpint (nm_bridge_vlan_is_pvid (v1), ==, FALSE);
g_assert_cmpint (nm_bridge_vlan_is_untagged (v1), ==, TRUE);
nm_bridge_vlan_set_pvid (v1, TRUE);
nm_bridge_vlan_set_untagged (v1, FALSE);
nm_bridge_vlan_seal (v1);
g_assert_cmpint (nm_bridge_vlan_is_sealed (v1), ==, TRUE);
g_assert_cmpint (nm_bridge_vlan_is_pvid (v1), ==, TRUE);
g_assert_cmpint (nm_bridge_vlan_is_untagged (v1), ==, FALSE);
str = nm_bridge_vlan_to_str (v1, &error);
nmtst_assert_success (str, error);
g_assert_cmpstr (str, ==, "10 pvid");
g_clear_pointer (&str, g_free);
v2 = nm_bridge_vlan_from_str (" 10 pvid ", &error);
nmtst_assert_success (v2, error);
g_assert_cmpint (nm_bridge_vlan_cmp (v1, v2), ==, 0);
nm_bridge_vlan_unref (v1);
nm_bridge_vlan_unref (v2);
/* test comparison (2) */
v1 = nm_bridge_vlan_from_str ("10", &error);
nmtst_assert_success (v1, error);
v2 = nm_bridge_vlan_from_str ("20", &error);
nmtst_assert_success (v2, error);
g_assert_cmpint (nm_bridge_vlan_cmp (v1, v2), <, 0);
nm_bridge_vlan_unref (v1);
nm_bridge_vlan_unref (v2);
}
/*****************************************************************************/
static void
@ -2663,6 +2720,8 @@ main (int argc, char **argv)
g_test_add_func ("/libnm/settings/tc_config/setting/duplicates", test_tc_config_setting_duplicates);
g_test_add_func ("/libnm/settings/tc_config/dbus", test_tc_config_dbus);
g_test_add_func ("/libnm/settings/bridge/vlans", test_bridge_vlans);
#if WITH_JSON_VALIDATION
g_test_add_func ("/libnm/settings/team/sync_runner_from_config_roundrobin",
test_runner_roundrobin_sync_from_config);

View file

@ -1518,3 +1518,36 @@ global:
nm_wireguard_peer_set_public_key;
nm_wireguard_peer_unref;
} libnm_1_14_0;
libnm_1_18_0 {
global:
nm_bridge_vlan_cmp;
nm_bridge_vlan_from_str;
nm_bridge_vlan_get_type;
nm_bridge_vlan_get_vid;
nm_bridge_vlan_is_pvid;
nm_bridge_vlan_is_sealed;
nm_bridge_vlan_is_untagged;
nm_bridge_vlan_new;
nm_bridge_vlan_new_clone;
nm_bridge_vlan_ref;
nm_bridge_vlan_seal;
nm_bridge_vlan_set_pvid;
nm_bridge_vlan_set_untagged;
nm_bridge_vlan_to_str;
nm_bridge_vlan_unref;
nm_setting_bridge_add_vlan;
nm_setting_bridge_clear_vlans;
nm_setting_bridge_get_num_vlans;
nm_setting_bridge_get_vlan;
nm_setting_bridge_get_vlan_filtering;
nm_setting_bridge_get_vlan_default_pvid;
nm_setting_bridge_port_add_vlan;
nm_setting_bridge_port_clear_vlans;
nm_setting_bridge_port_get_num_vlans;
nm_setting_bridge_port_get_vlan;
nm_setting_bridge_port_remove_vlan;
nm_setting_bridge_port_remove_vlan_by_vid;
nm_setting_bridge_remove_vlan;
nm_setting_bridge_remove_vlan_by_vid;
} libnm_1_16_0;

View file

@ -20,6 +20,7 @@
/****************************************************************************/
#include "nm-setting-bridge.h"
#include "nm-setting-connection.h"
#include "nm-setting-ip-config.h"
#include "nm-setting-ip4-config.h"
@ -46,6 +47,9 @@ NM_AUTO_DEFINE_FCN0 (NMTCQdisc *, _nm_auto_unref_tc_qdisc, nm_tc_qdisc_unref)
#define nm_auto_unref_tc_tfilter nm_auto (_nm_auto_unref_tc_tfilter)
NM_AUTO_DEFINE_FCN0 (NMTCTfilter *, _nm_auto_unref_tc_tfilter, nm_tc_tfilter_unref)
#define nm_auto_unref_bridge_vlan nm_auto (_nm_auto_unref_bridge_vlan)
NM_AUTO_DEFINE_FCN0 (NMBridgeVlan *, _nm_auto_unref_bridge_vlan, nm_bridge_vlan_unref)
#define nm_auto_unref_team_link_watcher nm_auto (_nm_auto_unref_team_link_watcher)
NM_AUTO_DEFINE_FCN0 (NMTeamLinkWatcher *, _nm_auto_unref_team_link_watcher, nm_team_link_watcher_unref)

View file

@ -37,6 +37,7 @@ _LOG_DECLARE_SELF(NMDeviceBridge);
struct _NMDeviceBridge {
NMDevice parent;
bool vlan_configured:1;
};
struct _NMDeviceBridgeClass {
@ -267,21 +268,39 @@ commit_option (NMDevice *device, NMSetting *setting, const Option *option, gbool
nm_platform_sysctl_master_set_option (nm_device_get_platform (device), ifindex, option->sysname, value);
}
static void
commit_master_options (NMDevice *device, NMSettingBridge *setting)
static NMPlatformBridgeVlan **
setting_vlans_to_platform (GPtrArray *array)
{
const Option *option;
NMSetting *s = NM_SETTING (setting);
GPtrArray *plat_vlans;
guint i;
for (option = master_options; option->name; option++)
commit_option (device, s, option, FALSE);
if (!array || !array->len)
return NULL;
plat_vlans = g_ptr_array_sized_new (array->len + 1);
for (i = 0; i < array->len; i++) {
NMBridgeVlan *vlan = array->pdata[i];
NMPlatformBridgeVlan *plat_vlan;
plat_vlan = g_new0 (NMPlatformBridgeVlan, 1);
plat_vlan->vid = nm_bridge_vlan_get_vid (vlan);
plat_vlan->pvid = nm_bridge_vlan_is_pvid (vlan);
plat_vlan->untagged = nm_bridge_vlan_is_untagged (vlan);
g_ptr_array_add (plat_vlans, plat_vlan);
}
g_ptr_array_add (plat_vlans, NULL);
return (NMPlatformBridgeVlan **) g_ptr_array_free (plat_vlans, FALSE);
}
static void
commit_slave_options (NMDevice *device, NMSettingBridgePort *setting)
{
const Option *option;
NMSetting *s, *s_clear = NULL;
NMSetting *s;
gs_unref_object NMSetting *s_clear = NULL;
if (setting)
s = NM_SETTING (setting);
@ -290,8 +309,6 @@ commit_slave_options (NMDevice *device, NMSettingBridgePort *setting)
for (option = slave_options; option->name; option++)
commit_option (device, s, option, TRUE);
g_clear_object (&s_clear);
}
static void
@ -396,22 +413,113 @@ master_update_slave_connection (NMDevice *device,
return TRUE;
}
static gboolean
bridge_set_vlan_options (NMDevice *device, NMSettingBridge *s_bridge)
{
NMDeviceBridge *self = NM_DEVICE_BRIDGE (device);
gconstpointer hwaddr;
size_t length;
gboolean enabled;
guint16 pvid;
NMPlatform *plat;
int ifindex;
gs_unref_ptrarray GPtrArray *vlans = NULL;
nm_auto_freev NMPlatformBridgeVlan **plat_vlans = NULL;
if (self->vlan_configured)
return TRUE;
plat = nm_device_get_platform (device);
ifindex = nm_device_get_ifindex (device);
enabled = nm_setting_bridge_get_vlan_filtering (s_bridge);
if (!enabled) {
nm_platform_sysctl_master_set_option (plat, ifindex, "vlan_filtering", "0");
nm_platform_sysctl_master_set_option (plat, ifindex, "default_pvid", "1");
nm_platform_link_set_bridge_vlans (plat, ifindex, FALSE, NULL);
return TRUE;
}
hwaddr = nm_platform_link_get_address (plat, ifindex, &length);
g_return_val_if_fail (length == ETH_ALEN, FALSE);
if (nm_utils_hwaddr_matches (hwaddr, ETH_ALEN, nm_ip_addr_zero.addr_eth, ETH_ALEN)) {
/* We need a non-zero MAC address to set the default pvid.
* Retry later. */
return TRUE;
}
self->vlan_configured = TRUE;
/* Filtering must be disabled to change the default PVID */
if (!nm_platform_sysctl_master_set_option (plat, ifindex, "vlan_filtering", "0"))
return FALSE;
/* Clear the default PVID so that we later can force the re-creation of
* default PVID VLANs by writing the option again. */
if (!nm_platform_sysctl_master_set_option (plat, ifindex, "default_pvid", "0"))
return FALSE;
/* Clear all existing VLANs */
if (!nm_platform_link_set_bridge_vlans (plat, ifindex, FALSE, NULL))
return FALSE;
/* Now set the default PVID. After this point the kernel creates
* a PVID VLAN on each port, including the bridge itself. */
pvid = nm_setting_bridge_get_vlan_default_pvid (s_bridge);
if (pvid) {
char value[32];
nm_sprintf_buf (value, "%u", pvid);
if (!nm_platform_sysctl_master_set_option (plat, ifindex, "default_pvid", value))
return FALSE;
}
/* Create VLANs only after setting the default PVID, so that
* any PVID VLAN overrides the bridge's default PVID. */
g_object_get (s_bridge, NM_SETTING_BRIDGE_VLANS, &vlans, NULL);
plat_vlans = setting_vlans_to_platform (vlans);
if ( plat_vlans
&& !nm_platform_link_set_bridge_vlans (plat, ifindex, FALSE,
(const NMPlatformBridgeVlan *const *) plat_vlans))
return FALSE;
if (!nm_platform_sysctl_master_set_option (plat, ifindex, "vlan_filtering", "1"))
return FALSE;
return TRUE;
}
static NMActStageReturn
act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
{
NMActStageReturn ret;
NMConnection *connection = nm_device_get_applied_connection (device);
NMConnection *connection;
NMSetting *s_bridge;
const Option *option;
g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE);
NM_DEVICE_BRIDGE (device)->vlan_configured = FALSE;
ret = NM_DEVICE_CLASS (nm_device_bridge_parent_class)->act_stage1_prepare (device, out_failure_reason);
if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
return ret;
if (!nm_device_hw_addr_set_cloned (device, nm_device_get_applied_connection (device), FALSE))
return NM_ACT_STAGE_RETURN_FAILURE;
connection = nm_device_get_applied_connection (device);
g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE);
s_bridge = (NMSetting *) nm_connection_get_setting_bridge (connection);
g_return_val_if_fail (s_bridge, NM_ACT_STAGE_RETURN_FAILURE);
commit_master_options (device, nm_connection_get_setting_bridge (connection));
if (!nm_device_hw_addr_set_cloned (device, connection, FALSE)) {
NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
return NM_ACT_STAGE_RETURN_FAILURE;
}
for (option = master_options; option->name; option++)
commit_option (device, s_bridge, option, FALSE);
if (!bridge_set_vlan_options (device, (NMSettingBridge *) s_bridge)) {
NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
return NM_ACT_STAGE_RETURN_FAILURE;
}
return NM_ACT_STAGE_RETURN_SUCCESS;
}
@ -457,12 +565,41 @@ enslave_slave (NMDevice *device,
gboolean configure)
{
NMDeviceBridge *self = NM_DEVICE_BRIDGE (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)))
return FALSE;
commit_slave_options (slave, nm_connection_get_setting_bridge_port (connection));
master_connection = nm_device_get_applied_connection (device);
nm_assert (master_connection);
s_bridge = nm_connection_get_setting_bridge (master_connection);
nm_assert (s_bridge);
s_port = nm_connection_get_setting_bridge_port (connection);
bridge_set_vlan_options (device, s_bridge);
if (nm_setting_bridge_get_vlan_filtering (s_bridge)) {
if (s_port)
g_object_get (s_port, NM_SETTING_BRIDGE_PORT_VLANS, &vlans, NULL);
plat_vlans = setting_vlans_to_platform (vlans);
/* Since the link was just enslaved, there are no existing VLANs
* (except for the default one) and so there's no need to flush. */
if ( plat_vlans
&& !nm_platform_link_set_bridge_vlans (nm_device_get_platform (slave),
nm_device_get_ifindex (slave),
TRUE,
(const NMPlatformBridgeVlan *const *) plat_vlans))
return FALSE;
}
commit_slave_options (slave, s_port);
_LOGI (LOGD_BRIDGE, "attached bridge port %s",
nm_device_get_ip_iface (slave));

View file

@ -29,6 +29,7 @@
#include <linux/fib_rules.h>
#include <linux/ip.h>
#include <linux/if_arp.h>
#include <linux/if_bridge.h>
#include <linux/if_link.h>
#include <linux/if_tun.h>
#include <linux/if_tunnel.h>
@ -277,6 +278,14 @@ struct _ifla_vf_vlan_info {
/*****************************************************************************/
/* Appeared in in kernel 4.0 dated April 12, 2015 */
#ifndef BRIDGE_VLAN_INFO_RANGE_BEGIN
#define BRIDGE_VLAN_INFO_RANGE_BEGIN (1 << 3) /* VLAN is start of vlan range */
#define BRIDGE_VLAN_INFO_RANGE_END (1 << 4) /* VLAN is end of vlan range */
#endif
/*****************************************************************************/
typedef enum {
INFINIBAND_ACTION_CREATE_CHILD,
INFINIBAND_ACTION_DELETE_CHILD,
@ -3842,21 +3851,23 @@ nla_put_failure:
}
static struct nl_msg *
_nl_msg_new_link (int nlmsg_type,
int nlmsg_flags,
int ifindex,
const char *ifname,
unsigned flags_mask,
unsigned flags_set)
_nl_msg_new_link_full (int nlmsg_type,
int nlmsg_flags,
int ifindex,
const char *ifname,
guint8 family,
unsigned flags_mask,
unsigned flags_set)
{
nm_auto_nlmsg struct nl_msg *msg = NULL;
const struct ifinfomsg ifi = {
.ifi_family = family,
.ifi_change = flags_mask,
.ifi_flags = flags_set,
.ifi_index = ifindex,
};
nm_assert (NM_IN_SET (nlmsg_type, RTM_DELLINK, RTM_NEWLINK, RTM_GETLINK));
nm_assert (NM_IN_SET (nlmsg_type, RTM_DELLINK, RTM_NEWLINK, RTM_GETLINK, RTM_SETLINK));
msg = nlmsg_alloc_simple (nlmsg_type, nlmsg_flags);
@ -3872,6 +3883,15 @@ nla_put_failure:
g_return_val_if_reached (NULL);
}
static struct nl_msg *
_nl_msg_new_link (int nlmsg_type,
int nlmsg_flags,
int ifindex,
const char *ifname)
{
return _nl_msg_new_link_full (nlmsg_type, nlmsg_flags, ifindex, ifname, AF_UNSPEC, 0, 0);
}
/* Copied and modified from libnl3's build_addr_msg(). */
static struct nl_msg *
_nl_msg_new_address (int nlmsg_type,
@ -5574,9 +5594,7 @@ do_request_link_no_delayed_actions (NMPlatform *platform, int ifindex, const cha
nlmsg = _nl_msg_new_link (RTM_GETLINK,
0,
ifindex,
name,
0,
0);
name);
if (nlmsg) {
nle = _nl_send_nlmsg (platform, nlmsg, NULL, NULL, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL);
if (nle < 0) {
@ -6274,9 +6292,7 @@ link_add (NMPlatform *platform,
nlmsg = _nl_msg_new_link (RTM_NEWLINK,
NLM_F_CREATE | NLM_F_EXCL,
0,
name,
0,
0);
name);
if (!nlmsg)
return -NME_UNSPEC;
@ -6305,9 +6321,7 @@ link_delete (NMPlatform *platform, int ifindex)
nlmsg = _nl_msg_new_link (RTM_DELLINK,
0,
ifindex,
NULL,
0,
0);
NULL);
nmp_object_stackinit_id_link (&obj_id, ifindex);
return do_delete_object (platform, &obj_id, nlmsg);
@ -6330,9 +6344,7 @@ link_set_netns (NMPlatform *platform,
nlmsg = _nl_msg_new_link (RTM_NEWLINK,
0,
ifindex,
NULL,
0,
0);
NULL);
if (!nlmsg)
return FALSE;
@ -6359,12 +6371,13 @@ link_change_flags (NMPlatform *platform,
nm_platform_link_flags2str (flags_set, s_flags, sizeof (s_flags)),
nm_platform_link_flags2str (flags_mask, NULL, 0));
nlmsg = _nl_msg_new_link (RTM_NEWLINK,
0,
ifindex,
NULL,
flags_mask,
flags_set);
nlmsg = _nl_msg_new_link_full (RTM_NEWLINK,
0,
ifindex,
NULL,
AF_UNSPEC,
flags_mask,
flags_set);
if (!nlmsg)
return -NME_UNSPEC;
return do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL);
@ -6428,9 +6441,7 @@ link_set_user_ipv6ll_enabled (NMPlatform *platform, int ifindex, gboolean enable
nlmsg = _nl_msg_new_link (RTM_NEWLINK,
0,
ifindex,
NULL,
0,
0);
NULL);
if ( !nlmsg
|| !_nl_msg_new_link_set_afspec (nlmsg, mode, NULL))
g_return_val_if_reached (-NME_BUG);
@ -6447,7 +6458,7 @@ link_set_token (NMPlatform *platform, int ifindex, NMUtilsIPv6IfaceId iid)
_LOGD ("link: change %d: token: set IPv6 address generation token to %s",
ifindex, nm_utils_inet6_interface_identifier_to_token (iid, sbuf));
nlmsg = _nl_msg_new_link (RTM_NEWLINK, 0, ifindex, NULL, 0, 0);
nlmsg = _nl_msg_new_link (RTM_NEWLINK, 0, ifindex, NULL);
if (!nlmsg || !_nl_msg_new_link_set_afspec (nlmsg, -1, &iid))
g_return_val_if_reached (FALSE);
@ -6528,9 +6539,7 @@ link_set_address (NMPlatform *platform, int ifindex, gconstpointer address, size
nlmsg = _nl_msg_new_link (RTM_NEWLINK,
0,
ifindex,
NULL,
0,
0);
NULL);
if (!nlmsg)
g_return_val_if_reached (-NME_BUG);
@ -6549,9 +6558,7 @@ link_set_name (NMPlatform *platform, int ifindex, const char *name)
nlmsg = _nl_msg_new_link (RTM_NEWLINK,
0,
ifindex,
NULL,
0,
0);
NULL);
if (!nlmsg)
g_return_val_if_reached (-NME_BUG);
@ -6584,9 +6591,7 @@ link_set_mtu (NMPlatform *platform, int ifindex, guint32 mtu)
nlmsg = _nl_msg_new_link (RTM_NEWLINK,
0,
ifindex,
NULL,
0,
0);
NULL);
if (!nlmsg)
return FALSE;
@ -6702,9 +6707,7 @@ link_set_sriov_vfs (NMPlatform *platform, int ifindex, const NMPlatformVF *const
nlmsg = _nl_msg_new_link (RTM_NEWLINK,
0,
ifindex,
NULL,
0,
0);
NULL);
if (!nlmsg)
g_return_val_if_reached (-NME_BUG);
@ -6787,6 +6790,67 @@ nla_put_failure:
g_return_val_if_reached (FALSE);
}
static gboolean
link_set_bridge_vlans (NMPlatform *platform,
int ifindex,
gboolean on_master,
const NMPlatformBridgeVlan *const *vlans)
{
nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
struct nlattr *list;
struct bridge_vlan_info vinfo = { };
guint i;
nlmsg = _nl_msg_new_link_full (vlans ? RTM_SETLINK : RTM_DELLINK,
0,
ifindex,
NULL,
AF_BRIDGE,
0,
0);
if (!nlmsg)
g_return_val_if_reached (-NME_BUG);
if (!(list = nla_nest_start (nlmsg, IFLA_AF_SPEC)))
goto nla_put_failure;
NLA_PUT_U16 (nlmsg,
IFLA_BRIDGE_FLAGS,
on_master ? BRIDGE_FLAGS_MASTER : BRIDGE_FLAGS_SELF);
if (vlans) {
/* Add VLANs */
for (i = 0; vlans[i]; i++) {
const NMPlatformBridgeVlan *vlan = vlans[i];
vinfo.vid = vlan->vid;
vinfo.flags = 0;
if (vlan->untagged)
vinfo.flags |= BRIDGE_VLAN_INFO_UNTAGGED;
if (vlan->pvid)
vinfo.flags |= BRIDGE_VLAN_INFO_PVID;
NLA_PUT (nlmsg, IFLA_BRIDGE_VLAN_INFO, sizeof (vinfo), &vinfo);
}
} else {
/* Flush existing VLANs */
vinfo.vid = 1;
vinfo.flags = BRIDGE_VLAN_INFO_RANGE_BEGIN;
NLA_PUT (nlmsg, IFLA_BRIDGE_VLAN_INFO, sizeof (vinfo), &vinfo);
vinfo.vid = 4094;
vinfo.flags = BRIDGE_VLAN_INFO_RANGE_END;
NLA_PUT (nlmsg, IFLA_BRIDGE_VLAN_INFO, sizeof (vinfo), &vinfo);
}
nla_nest_end (nlmsg, list);
return (do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) >= 0);
nla_put_failure:
g_return_val_if_reached (FALSE);
}
static char *
link_get_physical_port_id (NMPlatform *platform, int ifindex)
{
@ -6832,9 +6896,7 @@ vlan_add (NMPlatform *platform,
nlmsg = _nl_msg_new_link (RTM_NEWLINK,
NLM_F_CREATE | NLM_F_EXCL,
0,
name,
0,
0);
name);
if (!nlmsg)
return FALSE;
@ -6868,9 +6930,7 @@ link_gre_add (NMPlatform *platform,
nlmsg = _nl_msg_new_link (RTM_NEWLINK,
NLM_F_CREATE | NLM_F_EXCL,
0,
name,
0,
0);
name);
if (!nlmsg)
return FALSE;
@ -6920,9 +6980,7 @@ link_ip6tnl_add (NMPlatform *platform,
nlmsg = _nl_msg_new_link (RTM_NEWLINK,
NLM_F_CREATE | NLM_F_EXCL,
0,
name,
0,
0);
name);
if (!nlmsg)
return FALSE;
@ -6976,9 +7034,7 @@ link_ip6gre_add (NMPlatform *platform,
nlmsg = _nl_msg_new_link (RTM_NEWLINK,
NLM_F_CREATE | NLM_F_EXCL,
0,
name,
0,
0);
name);
if (!nlmsg)
return FALSE;
@ -7035,9 +7091,7 @@ link_ipip_add (NMPlatform *platform,
nlmsg = _nl_msg_new_link (RTM_NEWLINK,
NLM_F_CREATE | NLM_F_EXCL,
0,
name,
0,
0);
name);
if (!nlmsg)
return FALSE;
@ -7079,9 +7133,7 @@ link_macsec_add (NMPlatform *platform,
nlmsg = _nl_msg_new_link (RTM_NEWLINK,
NLM_F_CREATE | NLM_F_EXCL,
0,
name,
0,
0);
name);
if (!nlmsg)
return FALSE;
@ -7136,9 +7188,7 @@ link_macvlan_add (NMPlatform *platform,
nlmsg = _nl_msg_new_link (RTM_NEWLINK,
NLM_F_CREATE | NLM_F_EXCL,
0,
name,
0,
0);
name);
if (!nlmsg)
return FALSE;
@ -7178,9 +7228,7 @@ link_sit_add (NMPlatform *platform,
nlmsg = _nl_msg_new_link (RTM_NEWLINK,
NLM_F_CREATE | NLM_F_EXCL,
0,
name,
0,
0);
name);
if (!nlmsg)
return FALSE;
@ -7280,9 +7328,7 @@ link_vxlan_add (NMPlatform *platform,
nlmsg = _nl_msg_new_link (RTM_NEWLINK,
NLM_F_CREATE | NLM_F_EXCL,
0,
name,
0,
0);
name);
if (!nlmsg)
return FALSE;
@ -7346,9 +7392,7 @@ link_6lowpan_add (NMPlatform *platform,
nlmsg = _nl_msg_new_link (RTM_NEWLINK,
NLM_F_CREATE | NLM_F_EXCL,
0,
name,
0,
0);
name);
if (!nlmsg)
return FALSE;
@ -7496,9 +7540,7 @@ link_vlan_change (NMPlatform *platform,
nlmsg = _nl_msg_new_link (RTM_NEWLINK,
0,
ifindex,
NULL,
0,
0);
NULL);
if ( !nlmsg
|| !_nl_msg_new_link_set_linkinfo_vlan (nlmsg,
-1,
@ -7522,9 +7564,7 @@ link_enslave (NMPlatform *platform, int master, int slave)
nlmsg = _nl_msg_new_link (RTM_NEWLINK,
0,
ifindex,
NULL,
0,
0);
NULL);
if (!nlmsg)
return FALSE;
@ -8951,6 +8991,7 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass)
platform_class->link_set_name = link_set_name;
platform_class->link_set_sriov_params = link_set_sriov_params;
platform_class->link_set_sriov_vfs = link_set_sriov_vfs;
platform_class->link_set_bridge_vlans = link_set_bridge_vlans;
platform_class->link_get_physical_port_id = link_get_physical_port_id;
platform_class->link_get_dev_id = link_get_dev_id;

View file

@ -1518,6 +1518,28 @@ nm_platform_link_set_sriov_vfs (NMPlatform *self, int ifindex, const NMPlatformV
return klass->link_set_sriov_vfs (self, ifindex, vfs);
}
gboolean
nm_platform_link_set_bridge_vlans (NMPlatform *self, int ifindex, gboolean on_master, const NMPlatformBridgeVlan *const *vlans)
{
guint i;
_CHECK_SELF (self, klass, FALSE);
g_return_val_if_fail (ifindex > 0, FALSE);
_LOG3D ("link: %s bridge VLANs on %s",
vlans ? "setting" : "clearing",
on_master ? "master" : "self");
if (vlans) {
for (i = 0; vlans[i]; i++) {
const NMPlatformBridgeVlan *vlan = vlans[i];
_LOG3D ("link: bridge VLAN %s", nm_platform_bridge_vlan_to_string (vlan, NULL, 0));
}
}
return klass->link_set_bridge_vlans (self, ifindex, on_master, vlans);
}
/**
* nm_platform_link_set_up:
* @self: platform instance
@ -6506,6 +6528,24 @@ nm_platform_vf_to_string (const NMPlatformVF *vf, char *buf, gsize len)
return buf;
}
const char *
nm_platform_bridge_vlan_to_string (const NMPlatformBridgeVlan *vlan, char *buf, gsize len)
{
if (!nm_utils_to_string_buffer_init_null (vlan, &buf, &len))
return buf;
g_snprintf (buf, len,
"%u"
"%s"
"%s",
vlan->vid,
vlan->pvid ? " PVID" : "",
vlan->untagged ? " untagged" : "");
return buf;
}
void
nm_platform_link_hash_update (const NMPlatformLink *obj, NMHashState *h)
{

View file

@ -670,6 +670,12 @@ typedef struct {
gint8 trust;
} NMPlatformVF;
typedef struct {
guint16 vid;
bool untagged:1;
bool pvid:1;
} NMPlatformBridgeVlan;
typedef struct {
in_addr_t local;
in_addr_t remote;
@ -893,6 +899,7 @@ typedef struct {
gboolean (*link_set_name) (NMPlatform *, int ifindex, const char *name);
gboolean (*link_set_sriov_params) (NMPlatform *, int ifindex, guint num_vfs, int autoprobe);
gboolean (*link_set_sriov_vfs) (NMPlatform *self, int ifindex, const NMPlatformVF *const *vfs);
gboolean (*link_set_bridge_vlans) (NMPlatform *self, int ifindex, gboolean on_master, const NMPlatformBridgeVlan *const *vlans);
char * (*link_get_physical_port_id) (NMPlatform *, int ifindex);
guint (*link_get_dev_id) (NMPlatform *, int ifindex);
@ -1326,6 +1333,7 @@ int nm_platform_link_set_mtu (NMPlatform *self, int ifindex, guint32 mtu);
gboolean nm_platform_link_set_name (NMPlatform *self, int ifindex, const char *name);
gboolean nm_platform_link_set_sriov_params (NMPlatform *self, int ifindex, guint num_vfs, int autoprobe);
gboolean nm_platform_link_set_sriov_vfs (NMPlatform *self, int ifindex, const NMPlatformVF *const *vfs);
gboolean nm_platform_link_set_bridge_vlans (NMPlatform *self, int ifindex, gboolean on_master, const NMPlatformBridgeVlan *const *vlans);
char *nm_platform_link_get_physical_port_id (NMPlatform *self, int ifindex);
guint nm_platform_link_get_dev_id (NMPlatform *self, int ifindex);
@ -1585,6 +1593,7 @@ const char *nm_platform_routing_rule_to_string (const NMPlatformRoutingRule *rou
const char *nm_platform_qdisc_to_string (const NMPlatformQdisc *qdisc, char *buf, gsize len);
const char *nm_platform_tfilter_to_string (const NMPlatformTfilter *tfilter, char *buf, gsize len);
const char *nm_platform_vf_to_string (const NMPlatformVF *vf, char *buf, gsize len);
const char *nm_platform_bridge_vlan_to_string (const NMPlatformBridgeVlan *vlan, char *buf, gsize len);
const char *nm_platform_vlan_qos_mapping_to_string (const char *name,
const NMVlanQosMapping *map,

View file

@ -4958,6 +4958,8 @@ handle_bridge_option (NMSetting *setting,
{ "max_age", NM_SETTING_BRIDGE_MAX_AGE, BRIDGE_OPT_TYPE_OPTION, .only_with_stp = TRUE },
{ "ageing_time", NM_SETTING_BRIDGE_AGEING_TIME, BRIDGE_OPT_TYPE_OPTION },
{ "multicast_snooping", NM_SETTING_BRIDGE_MULTICAST_SNOOPING, BRIDGE_OPT_TYPE_OPTION },
{ "vlan_filtering", NM_SETTING_BRIDGE_VLAN_FILTERING, BRIDGE_OPT_TYPE_OPTION },
{ "default_pvid", NM_SETTING_BRIDGE_VLAN_DEFAULT_PVID, BRIDGE_OPT_TYPE_OPTION },
{ "group_fwd_mask", NM_SETTING_BRIDGE_GROUP_FORWARD_MASK, BRIDGE_OPT_TYPE_OPTION },
{ "priority", NM_SETTING_BRIDGE_PORT_PRIORITY, BRIDGE_OPT_TYPE_PORT_OPTION },
{ "path_cost", NM_SETTING_BRIDGE_PORT_PATH_COST, BRIDGE_OPT_TYPE_PORT_OPTION },
@ -5052,6 +5054,43 @@ handle_bridging_opts (NMSetting *setting,
}
}
static void
read_bridge_vlans (shvarFile *ifcfg,
const char *key,
NMSetting *setting,
const char *property)
{
gs_unref_ptrarray GPtrArray *array = NULL;
gs_free char *value_to_free = NULL;
const char *value;
value = svGetValueStr (ifcfg, key, &value_to_free);
if (value) {
gs_free const char **strv = NULL;
const char *const *iter;
GError *local = NULL;
NMBridgeVlan *vlan;
array = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_bridge_vlan_unref);
strv = nm_utils_strsplit_set (value, ",", FALSE);
if (strv) {
for (iter = strv; *iter; iter++) {
vlan = nm_bridge_vlan_from_str (*iter, &local);
if (!vlan) {
PARSE_WARNING ("invalid bridge VLAN: %s", local->message);
g_clear_error (&local);
continue;
}
g_ptr_array_add (array, vlan);
}
}
nm_clear_g_free (&value_to_free);
}
g_object_set (setting, property, array, NULL);
}
static NMSetting *
make_bridge_setting (shvarFile *ifcfg,
const char *file,
@ -5110,6 +5149,11 @@ make_bridge_setting (shvarFile *ifcfg,
nm_clear_g_free (&value_to_free);
}
read_bridge_vlans (ifcfg,
"BRIDGE_VLANS",
NM_SETTING (s_bridge),
NM_SETTING_BRIDGE_VLANS);
return (NMSetting *) g_steal_pointer (&s_bridge);
}
@ -5179,6 +5223,11 @@ make_bridge_port_setting (shvarFile *ifcfg)
handle_bridging_opts (s_port, FALSE, value, handle_bridge_option, BRIDGE_OPT_TYPE_PORT_OPTION);
nm_clear_g_free (&value_to_free);
}
read_bridge_vlans (ifcfg,
"BRIDGE_PORT_VLANS",
s_port,
NM_SETTING_BRIDGE_PORT_VLANS);
}
return s_port;

View file

@ -1463,6 +1463,43 @@ get_setting_default_boolean (NMSetting *setting, const char *prop)
return ret;
}
static gboolean
write_bridge_vlans (NMSetting *setting,
const char *property_name,
shvarFile *ifcfg,
const char *key,
GError **error)
{
gs_unref_ptrarray GPtrArray *vlans = NULL;
NMBridgeVlan *vlan;
GString *string;
guint i;
g_object_get (setting, property_name, &vlans, NULL);
if (!vlans || !vlans->len) {
svUnsetValue (ifcfg, key);
return TRUE;
}
string = g_string_new ("");
for (i = 0; i < vlans->len; i++) {
gs_free char *vlan_str = NULL;
vlan = vlans->pdata[i];
vlan_str = nm_bridge_vlan_to_str (vlan, error);
if (!vlan_str)
return FALSE;
if (string->len > 0)
g_string_append (string, ", ");
g_string_append (string, vlan_str);
}
svSetValueStr (ifcfg, key, string->str);
g_string_free (string, TRUE);
return TRUE;
}
static gboolean
write_bridge_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wired, GError **error)
{
@ -1534,10 +1571,31 @@ write_bridge_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wire
g_string_append_printf (opts, "multicast_snooping=%u", (guint32) b);
}
b = nm_setting_bridge_get_vlan_filtering (s_bridge);
if (b != get_setting_default_boolean (NM_SETTING (s_bridge), NM_SETTING_BRIDGE_VLAN_FILTERING)) {
if (opts->len)
g_string_append_c (opts, ' ');
g_string_append_printf (opts, "vlan_filtering=%u", (guint32) b);
}
i = nm_setting_bridge_get_vlan_default_pvid (s_bridge);
if (i != get_setting_default_uint (NM_SETTING (s_bridge), NM_SETTING_BRIDGE_VLAN_DEFAULT_PVID)) {
if (opts->len)
g_string_append_c (opts, ' ');
g_string_append_printf (opts, "default_pvid=%u", i);
}
if (opts->len)
svSetValueStr (ifcfg, "BRIDGING_OPTS", opts->str);
g_string_free (opts, TRUE);
if (!write_bridge_vlans ((NMSetting *) s_bridge,
NM_SETTING_BRIDGE_VLANS,
ifcfg,
"BRIDGE_VLANS",
error))
return FALSE;
svSetValueStr (ifcfg, "TYPE", TYPE_BRIDGE);
*wired = write_wired_for_virtual (connection, ifcfg);
@ -1550,7 +1608,7 @@ write_bridge_port_setting (NMConnection *connection, shvarFile *ifcfg, GError **
{
NMSettingBridgePort *s_port;
guint32 i;
GString *opts;
GString *string;
s_port = nm_connection_get_setting_bridge_port (connection);
if (!s_port)
@ -1559,28 +1617,35 @@ write_bridge_port_setting (NMConnection *connection, shvarFile *ifcfg, GError **
svUnsetValue (ifcfg, "BRIDGING_OPTS");
/* Bridge options */
opts = g_string_sized_new (32);
string = g_string_sized_new (32);
i = nm_setting_bridge_port_get_priority (s_port);
if (i != get_setting_default_uint (NM_SETTING (s_port), NM_SETTING_BRIDGE_PORT_PRIORITY))
g_string_append_printf (opts, "priority=%u", i);
g_string_append_printf (string, "priority=%u", i);
i = nm_setting_bridge_port_get_path_cost (s_port);
if (i != get_setting_default_uint (NM_SETTING (s_port), NM_SETTING_BRIDGE_PORT_PATH_COST)) {
if (opts->len)
g_string_append_c (opts, ' ');
g_string_append_printf (opts, "path_cost=%u", i);
if (string->len)
g_string_append_c (string, ' ');
g_string_append_printf (string, "path_cost=%u", i);
}
if (nm_setting_bridge_port_get_hairpin_mode (s_port)) {
if (opts->len)
g_string_append_c (opts, ' ');
g_string_append_printf (opts, "hairpin_mode=1");
if (string->len)
g_string_append_c (string, ' ');
g_string_append_printf (string, "hairpin_mode=1");
}
if (opts->len)
svSetValueStr (ifcfg, "BRIDGING_OPTS", opts->str);
g_string_free (opts, TRUE);
if (string->len)
svSetValueStr (ifcfg, "BRIDGING_OPTS", string->str);
g_string_free (string, TRUE);
if (!write_bridge_vlans ((NMSetting *) s_port,
NM_SETTING_BRIDGE_PORT_VLANS,
ifcfg,
"BRIDGE_PORT_VLANS",
error))
return FALSE;
return TRUE;
}

View file

@ -2,6 +2,7 @@ HWADDR=31:33:33:37:BE:CD
MTU=1492
TYPE=Ethernet
BRIDGING_OPTS="priority=50 path_cost=33"
BRIDGE_PORT_VLANS="1 untagged, 2 pvid untagged, 4"
NAME="Test Write Bridge Component"
UUID=${UUID}
ONBOOT=yes

View file

@ -4,5 +4,5 @@ TYPE=Bridge
BOOTPROTO=dhcp
STP=on
DELAY=2
BRIDGING_OPTS="priority=32744 hello_time=7 max_age=39 ageing_time=235352 multicast_snooping=0 group_fwd_mask=24"
BRIDGING_OPTS="priority=32744 hello_time=7 max_age=39 ageing_time=235352 multicast_snooping=0 group_fwd_mask=24 vlan_filtering=1 default_pvid=99"
MACADDR=00:16:41:11:22:33

View file

@ -7507,6 +7507,8 @@ test_read_bridge_main (void)
g_assert_cmpuint (nm_setting_bridge_get_ageing_time (s_bridge), ==, 235352);
g_assert_cmpuint (nm_setting_bridge_get_group_forward_mask (s_bridge), ==, 24);
g_assert (!nm_setting_bridge_get_multicast_snooping (s_bridge));
g_assert_cmpint (nm_setting_bridge_get_vlan_filtering (s_bridge), ==, TRUE);
g_assert_cmpint (nm_setting_bridge_get_vlan_default_pvid (s_bridge), ==, 99);
/* MAC address */
s_wired = nm_connection_get_setting_wired (connection);
@ -7531,6 +7533,8 @@ test_write_bridge_main (void)
NMIPAddress *addr;
static const char *mac = "31:33:33:37:be:cd";
GError *error = NULL;
gs_unref_ptrarray GPtrArray *vlans = NULL;
NMBridgeVlan *vlan;
connection = nm_simple_connection_new ();
g_assert (connection);
@ -7551,9 +7555,23 @@ test_write_bridge_main (void)
s_bridge = (NMSettingBridge *) nm_setting_bridge_new ();
nm_connection_add_setting (connection, NM_SETTING (s_bridge));
vlans = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_bridge_vlan_unref);
vlan = nm_bridge_vlan_new (11);
nm_bridge_vlan_set_untagged (vlan, TRUE);
g_ptr_array_add (vlans, vlan);
vlan = nm_bridge_vlan_new (22);
nm_bridge_vlan_set_pvid (vlan, TRUE);
nm_bridge_vlan_set_untagged (vlan, TRUE);
g_ptr_array_add (vlans, vlan);
vlan = nm_bridge_vlan_new (44);
g_ptr_array_add (vlans, vlan);
g_object_set (s_bridge,
NM_SETTING_BRIDGE_MAC_ADDRESS, mac,
NM_SETTING_BRIDGE_GROUP_FORWARD_MASK, 19008,
NM_SETTING_BRIDGE_VLAN_FILTERING, TRUE,
NM_SETTING_BRIDGE_VLAN_DEFAULT_PVID, 4000,
NM_SETTING_BRIDGE_VLANS, vlans,
NULL);
/* IP4 setting */
@ -7631,6 +7649,8 @@ test_write_bridge_component (void)
NMSetting *s_port;
static const char *mac = "31:33:33:37:be:cd";
guint32 mtu = 1492;
gs_unref_ptrarray GPtrArray *vlans = NULL;
NMBridgeVlan *vlan;
connection = nm_simple_connection_new ();
g_assert (connection);
@ -7658,11 +7678,23 @@ test_write_bridge_component (void)
NULL);
/* Bridge port */
vlans = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_bridge_vlan_unref);
vlan = nm_bridge_vlan_new (1);
nm_bridge_vlan_set_untagged (vlan, TRUE);
g_ptr_array_add (vlans, vlan);
vlan = nm_bridge_vlan_new (2);
nm_bridge_vlan_set_pvid (vlan, TRUE);
nm_bridge_vlan_set_untagged (vlan, TRUE);
g_ptr_array_add (vlans, vlan);
vlan = nm_bridge_vlan_new (4);
g_ptr_array_add (vlans, vlan);
s_port = nm_setting_bridge_port_new ();
nm_connection_add_setting (connection, s_port);
g_object_set (s_port,
NM_SETTING_BRIDGE_PORT_PRIORITY, 50,
NM_SETTING_BRIDGE_PORT_PATH_COST, 33,
NM_SETTING_BRIDGE_PORT_VLANS, vlans,
NULL);
nmtst_assert_connection_verifies (connection);