about summary refs log tree commit diff
path: root/src/devices/nm-device-bridge.c
diff options
context:
space:
mode:
authorSebastien Bacher <seb128@ubuntu.com>2019-05-10 15:07:08 +0200
committerSebastien Bacher <seb128@ubuntu.com>2019-05-22 13:41:30 +0200
commitd57a1dd26f8e9859252b0983c2d2ec3b95eb5714 (patch)
tree46f1146e87af8cc7b58b751f7e575bd0abb2f59d /src/devices/nm-device-bridge.c
parentad9ed8bfb963266b4eea524131845f406cfc55d7 (diff)
parent85563b7fc7ec2cd21e38debb9b28db342e2e8e7c (diff)
Import Debian changes 1.18.0-1ubuntu1
network-manager (1.18.0-1ubuntu1) eoan; urgency=medium

  * Update to 1.18, merge on Debian, new version includes nwe support for
    policy routing rules and for VLAN filtering for Linux bridge.
  * Remaining Ubuntu changes
    - Use systemd-resolved instead of dnsmasq
    - debian/control:
      + Depend on isc-dhcp-client instead of recommends
      + Recommend network-manager-pptp
      + Suggest avahi-autoipd for IPv4LL support
    - debian/rules, debian/network-manager.postinst:
      + Don't restart NetworkManager on upgrade but recommend restarting
        the computer
    - debian/rules, debian/network-manager.postinst:
      + Don't install sysvinit scripts or migrate from sysvinit
    - debian/network-manager.postinst:
      + Don't add the netdev group.
      + drop in an empty override file for NetworkManager to manage all
        devices for upgrade from any version, as long as there is no
        netplan configuration yet.
    - debian/default-wifi-powersave-on.conf, debian/rules:
      + Install a config file to enable WiFi powersave
    - Enable build tests
    - Add autopkgtests
    - debian/source_network-manager.py, debian/network-manager.install,
      debian/network-manager.links: Add apport hook
    - Add network-manager-config-connectivity-ubuntu package
    - NetworkManager.conf: disable MAC randomization feature. There is no
      easy way for desktop users to disable this feature yet. And there are
      reports that it doesn't work well with some systems.
    - Update Vcs links to point to Ubuntu branch
    - Add patches. See patch descriptions for more details:
      + Provide-access-to-some-of-NM-s-interfaces-to-whoopsie.patch
      + Update-dnsmasq-parameters.patch
      + Disable-general-with-expect.patch
      + libnm-Check-self-still-NMManager-or-not.patch
      + dns-manager-don-t-merge-split-DNS-search-domains.patch (but disabled)
      + Read-system-connections-from-run.patch
    - debian/tests/urfkill-integration - don't stop/start network manager
    - Revert "Add Conflicts to network-manager-dev against deprecated libraries"
      This reverts commit b4acc5e03e2b821e1cccc69529bb70826c741942.  We're still
      building libnm-glib for now, so these packages have a use in Ubuntu.
  * Removed delta, not needed anymore
    - debian/network-manager.maintscript
      + Remove /etc/dbus-1/system.d/nm-ofono.conf
Diffstat (limited to 'src/devices/nm-device-bridge.c')
-rw-r--r--src/devices/nm-device-bridge.c174
1 files changed, 159 insertions, 15 deletions
diff --git a/src/devices/nm-device-bridge.c b/src/devices/nm-device-bridge.c
index 4c8921c0..4275af91 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,21 +268,45 @@ 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 const NMPlatformBridgeVlan **
+setting_vlans_to_platform (GPtrArray *array)
 {
-	const Option *option;
-	NMSetting *s = NM_SETTING (setting);
-
-	for (option = master_options; option->name; option++)
-		commit_option (device, s, option, FALSE);
+	NMPlatformBridgeVlan **arr;
+	NMPlatformBridgeVlan *p_data;
+	guint i;
+
+	if (!array || !array->len)
+		return NULL;
+
+	G_STATIC_ASSERT_EXPR (_nm_alignof (NMPlatformBridgeVlan *) >= _nm_alignof (NMPlatformBridgeVlan));
+	arr = g_malloc (  (sizeof (NMPlatformBridgeVlan *) * (array->len + 1))
+	                + (sizeof (NMPlatformBridgeVlan  ) * (array->len    )));
+	p_data = (NMPlatformBridgeVlan *) &arr[array->len + 1];
+
+	for (i = 0; i < array->len; i++) {
+		NMBridgeVlan *vlan = array->pdata[i];
+		guint16 vid_start, vid_end;
+
+		nm_bridge_vlan_get_vid_range (vlan, &vid_start, &vid_end);
+
+		p_data[i] = (NMPlatformBridgeVlan) {
+			.vid_start = vid_start,
+			.vid_end   = vid_end,
+			.pvid      = nm_bridge_vlan_is_pvid (vlan),
+			.untagged  = nm_bridge_vlan_is_untagged (vlan),
+		};
+		arr[i] = &p_data[i];
+	}
+	arr[i] = NULL;
+	return (const NMPlatformBridgeVlan **) arr;
 }
 
 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 +315,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 +419,112 @@ 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;
+	gs_free const 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, 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))
+	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);
+
+	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;
+	}
 
-	commit_master_options (device, nm_connection_get_setting_bridge (connection));
+	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 +570,43 @@ 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);
+
+		if (nm_setting_bridge_get_vlan_filtering (s_bridge)) {
+			gs_free const NMPlatformBridgeVlan **plat_vlans = NULL;
+			gs_unref_ptrarray GPtrArray *vlans = NULL;
+
+			if (s_port)
+				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,
+			                                           plat_vlans))
+				return FALSE;
+		}
+
+		commit_slave_options (slave, s_port);
 
 		_LOGI (LOGD_BRIDGE, "attached bridge port %s",
 		       nm_device_get_ip_iface (slave));