From 96fab7b46258e2c292deae15cfbd380a0a255208 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Fri, 15 Mar 2019 08:53:37 +0100 Subject: [PATCH] all: add vlan-filtering and vlan-default-pvid bridge properties --- clients/common/nm-meta-setting-desc.c | 6 + clients/common/settings-docs.h.in | 2 + libnm-core/nm-setting-bridge.c | 95 +++++++++++++++ libnm-core/nm-setting-bridge.h | 6 + libnm/libnm.ver | 6 + src/devices/nm-device-bridge.c | 108 +++++++++++++++--- .../plugins/ifcfg-rh/nms-ifcfg-rh-reader.c | 2 + .../plugins/ifcfg-rh/nms-ifcfg-rh-writer.c | 14 +++ .../network-scripts/ifcfg-test-bridge-main | 2 +- .../plugins/ifcfg-rh/tests/test-ifcfg-rh.c | 4 + 10 files changed, 228 insertions(+), 17 deletions(-) diff --git a/clients/common/nm-meta-setting-desc.c b/clients/common/nm-meta-setting-desc.c index 052942011..bc2cdafc3 100644 --- a/clients/common/nm-meta-setting-desc.c +++ b/clients/common/nm-meta-setting-desc.c @@ -4808,6 +4808,12 @@ 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, + ), NULL }; diff --git a/clients/common/settings-docs.h.in b/clients/common/settings-docs.h.in index 7b958faae..4a0da22fa 100644 --- a/clients/common/settings-docs.h.in +++ b/clients/common/settings-docs.h.in @@ -118,6 +118,8 @@ #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_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.") diff --git a/libnm-core/nm-setting-bridge.c b/libnm-core/nm-setting-bridge.c index e418013cb..93c1f102d 100644 --- a/libnm-core/nm-setting-bridge.c +++ b/libnm-core/nm-setting-bridge.c @@ -50,6 +50,8 @@ NM_GOBJECT_PROPERTIES_DEFINE_BASE ( PROP_AGEING_TIME, PROP_GROUP_FORWARD_MASK, PROP_MULTICAST_SNOOPING, + PROP_VLAN_FILTERING, + PROP_VLAN_DEFAULT_PVID, ); typedef struct { @@ -62,6 +64,8 @@ typedef struct { guint32 ageing_time; guint16 group_forward_mask; gboolean multicast_snooping; + gboolean vlan_filtering; + guint16 vlan_default_pvid; } NMSettingBridgePrivate; G_DEFINE_TYPE (NMSettingBridge, nm_setting_bridge, NM_TYPE_SETTING) @@ -200,6 +204,38 @@ 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; +} + static gboolean check_range (guint32 val, guint32 min, @@ -318,6 +354,12 @@ 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; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -360,6 +402,12 @@ 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; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -627,6 +675,53 @@ 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, 4094, 1, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS); + /* ---dbus--- * property: interface-name * format: string diff --git a/libnm-core/nm-setting-bridge.h b/libnm-core/nm-setting-bridge.h index d640dde3d..53bb5ad82 100644 --- a/libnm-core/nm-setting-bridge.h +++ b/libnm-core/nm-setting-bridge.h @@ -48,6 +48,8 @@ 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" /** * NMSettingBridge: @@ -86,6 +88,10 @@ 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); G_END_DECLS diff --git a/libnm/libnm.ver b/libnm/libnm.ver index ece9686ef..e3a146c21 100644 --- a/libnm/libnm.ver +++ b/libnm/libnm.ver @@ -1518,3 +1518,9 @@ global: nm_wireguard_peer_set_public_key; nm_wireguard_peer_unref; } libnm_1_14_0; + +libnm_1_18_0 { +global: + nm_setting_bridge_get_vlan_filtering; + nm_setting_bridge_get_vlan_default_pvid; +} libnm_1_16_0; diff --git a/src/devices/nm-device-bridge.c b/src/devices/nm-device-bridge.c index 4c8921c07..9528a3af9 100644 --- a/src/devices/nm-device-bridge.c +++ b/src/devices/nm-device-bridge.c @@ -37,6 +37,7 @@ _LOG_DECLARE_SELF(NMDeviceBridge); struct _NMDeviceBridge { NMDevice parent; + bool vlan_configured:1; }; struct _NMDeviceBridgeClass { @@ -267,16 +268,6 @@ 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) -{ - const Option *option; - NMSetting *s = NM_SETTING (setting); - - for (option = master_options; option->name; option++) - commit_option (device, s, option, FALSE); -} - static void commit_slave_options (NMDevice *device, NMSettingBridgePort *setting) { @@ -396,22 +387,97 @@ 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; + + 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"); + 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; + + /* 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; + } + + 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 +523,22 @@ enslave_slave (NMDevice *device, gboolean configure) { NMDeviceBridge *self = NM_DEVICE_BRIDGE (device); + NMConnection *master_connection; + NMSettingBridge *s_bridge; + NMSettingBridgePort *s_port; 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); + commit_slave_options (slave, s_port); _LOGI (LOGD_BRIDGE, "attached bridge port %s", nm_device_get_ip_iface (slave)); diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c index 7779319b7..d0f37ea95 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c @@ -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 }, diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c index ee7fd1615..f7fc36bfd 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c @@ -1534,6 +1534,20 @@ 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); diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bridge-main b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bridge-main index 2bc987c24..1788efe26 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bridge-main +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bridge-main @@ -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 diff --git a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c index b352fbfc7..e504c7bbd 100644 --- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c +++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c @@ -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); @@ -7554,6 +7556,8 @@ test_write_bridge_main (void) 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, NULL); /* IP4 setting */