From 1e5b0788bcc91e7998dfffda3a116735f90ed239 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Sat, 16 Mar 2019 17:21:35 +0100 Subject: [PATCH] libnm-core: add vlans property to bridge-port setting --- clients/common/settings-docs.h.in | 1 + libnm-core/nm-connection.c | 13 ++ libnm-core/nm-core-internal.h | 1 + libnm-core/nm-setting-bridge-port.c | 298 +++++++++++++++++++++++++++- libnm-core/nm-setting-bridge-port.h | 16 ++ libnm-core/nm-setting-bridge.c | 287 ++++++++++++++++++++++++++- libnm-core/nm-setting-bridge.h | 32 +++ libnm-core/nm-setting-private.h | 4 + libnm-core/nm-utils-private.h | 13 ++ libnm-core/nm-utils.c | 140 +++++++++++++ libnm/libnm.ver | 19 ++ shared/nm-libnm-core-utils.h | 4 + 12 files changed, 825 insertions(+), 3 deletions(-) diff --git a/clients/common/settings-docs.h.in b/clients/common/settings-docs.h.in index 4a0da22fa..3a66ae73a 100644 --- a/clients/common/settings-docs.h.in +++ b/clients/common/settings-docs.h.in @@ -123,6 +123,7 @@ #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.") diff --git a/libnm-core/nm-connection.c b/libnm-core/nm-connection.c index ea7730bf9..312898368 100644 --- a/libnm-core/nm-connection.c +++ b/libnm-core/nm-connection.c @@ -1297,6 +1297,18 @@ _normalize_sriov_vf_order (NMConnection *self, GHashTable *parameters) return _nm_setting_sriov_sort_vfs (s_sriov); } +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 +1657,7 @@ 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_port_vlan_order (connection, parameters); /* Verify anew. */ success = _nm_connection_verify (connection, error); diff --git a/libnm-core/nm-core-internal.h b/libnm-core/nm-core-internal.h index 394ecb567..3859c2bb4 100644 --- a/libnm-core/nm-core-internal.h +++ b/libnm-core/nm-core-internal.h @@ -578,6 +578,7 @@ 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); /*****************************************************************************/ diff --git a/libnm-core/nm-setting-bridge-port.c b/libnm-core/nm-setting-bridge-port.c index 614c883cb..404a48625 100644 --- a/libnm-core/nm-setting-bridge-port.c +++ b/libnm-core/nm-setting-bridge-port.c @@ -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,38 @@ 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 + **/ + 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); + } diff --git a/libnm-core/nm-setting-bridge-port.h b/libnm-core/nm-setting-bridge-port.h index 0ff6d9f13..b1b1c8f40 100644 --- a/libnm-core/nm-setting-bridge-port.h +++ b/libnm-core/nm-setting-bridge-port.h @@ -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__ */ diff --git a/libnm-core/nm-setting-bridge.c b/libnm-core/nm-setting-bridge.c index 93c1f102d..26f2fe305 100644 --- a/libnm-core/nm-setting-bridge.c +++ b/libnm-core/nm-setting-bridge.c @@ -74,6 +74,291 @@ 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; +} + +/*****************************************************************************/ + + /** * nm_setting_bridge_get_mac_address: * @setting: the #NMSettingBridge @@ -716,7 +1001,7 @@ nm_setting_bridge_class_init (NMSettingBridgeClass *klass) */ obj_properties[PROP_VLAN_DEFAULT_PVID] = g_param_spec_uint (NM_SETTING_BRIDGE_VLAN_DEFAULT_PVID, "", "", - 0, 4094, 1, + 0, NM_BRIDGE_VLAN_VID_MAX, 1, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_INFERRABLE | diff --git a/libnm-core/nm-setting-bridge.h b/libnm-core/nm-setting-bridge.h index 53bb5ad82..d277675f1 100644 --- a/libnm-core/nm-setting-bridge.h +++ b/libnm-core/nm-setting-bridge.h @@ -51,6 +51,9 @@ G_BEGIN_DECLS #define NM_SETTING_BRIDGE_VLAN_FILTERING "vlan-filtering" #define NM_SETTING_BRIDGE_VLAN_DEFAULT_PVID "vlan-default-pvid" +#define NM_BRIDGE_VLAN_VID_MIN 1 +#define NM_BRIDGE_VLAN_VID_MAX 4094 + /** * NMSettingBridge: * @@ -93,6 +96,35 @@ gboolean nm_setting_bridge_get_vlan_filtering (NMSettingBridge *setting); NM_AVAILABLE_IN_1_18 guint16 nm_setting_bridge_get_vlan_default_pvid (NMSettingBridge *setting); +typedef struct _NMBridgeVlan NMBridgeVlan; + +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); + G_END_DECLS #endif /* __NM_SETTING_BRIDGE_H__ */ diff --git a/libnm-core/nm-setting-private.h b/libnm-core/nm-setting-private.h index 2af74ec4c..b423eb5c6 100644 --- a/libnm-core/nm-setting-private.h +++ b/libnm-core/nm-setting-private.h @@ -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 */ diff --git a/libnm-core/nm-utils-private.h b/libnm-core/nm-utils-private.h index f730d3d36..a1a1369a3 100644 --- a/libnm-core/nm-utils-private.h +++ b/libnm-core/nm-utils-private.h @@ -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 diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index d662ce755..882dfd838 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -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" @@ -6710,3 +6711,142 @@ 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; +} + diff --git a/libnm/libnm.ver b/libnm/libnm.ver index e3a146c21..444b10459 100644 --- a/libnm/libnm.ver +++ b/libnm/libnm.ver @@ -1521,6 +1521,25 @@ global: libnm_1_18_0 { global: + nm_bridge_vlan_cmp; + 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_unref; 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; } libnm_1_16_0; diff --git a/shared/nm-libnm-core-utils.h b/shared/nm-libnm-core-utils.h index 3d06de00a..85dd3b9b2 100644 --- a/shared/nm-libnm-core-utils.h +++ b/shared/nm-libnm-core-utils.h @@ -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)