about summary refs log tree commit diff
path: root/src/platform/nm-linux-platform.c
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2016-03-30 00:56:30 +0200
committerMichael Biebl <biebl@debian.org>2016-03-30 00:56:30 +0200
commitd9c99a29a0d3384c9c3d2adce430f5cb1134ab6a (patch)
treefa41baf72753961e71dd8d5bdbe2b89c9109e4f1 /src/platform/nm-linux-platform.c
parentc2de0d98ba39e0a1a970d066fd19be786092f376 (diff)
Imported Upstream version 1.1.92 upstream/1.1.92
Diffstat (limited to 'src/platform/nm-linux-platform.c')
-rw-r--r--src/platform/nm-linux-platform.c282
1 files changed, 182 insertions, 100 deletions
diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c
index 196e75cb..e2ff1f61 100644
--- a/src/platform/nm-linux-platform.c
+++ b/src/platform/nm-linux-platform.c
@@ -50,6 +50,7 @@
 
 #include "nm-core-utils.h"
 #include "nmp-object.h"
+#include "nmp-netns.h"
 #include "nm-platform-utils.h"
 #include "wifi/wifi-utils.h"
 #include "wifi/wifi-utils-wext.h"
@@ -221,6 +222,7 @@ static void do_request_all_no_delayed_actions (NMPlatform *platform, DelayedActi
 static void cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMPCacheOpsType ops_type, gpointer user_data);
 static void cache_prune_candidates_prune (NMPlatform *platform);
 static gboolean event_handler_read_netlink (NMPlatform *platform, gboolean wait_for_acks);
+static void _assert_netns_current (NMPlatform *platform);
 
 /*****************************************************************************/
 
@@ -647,6 +649,8 @@ _linktype_get_type (NMPlatform *platform,
 {
 	guint i;
 
+	_assert_netns_current (platform);
+
 	if (completed_from_cache) {
 		const NMPObject *obj;
 
@@ -1673,11 +1677,9 @@ _new_from_nl_addr (struct nlmsghdr *nlh, gboolean id_only)
 
 	obj->ip_address.source = NM_IP_CONFIG_SOURCE_KERNEL;
 
-	if (!is_v4) {
-		obj->ip6_address.n_ifa_flags = tb[IFA_FLAGS]
-		                               ? nla_get_u32 (tb[IFA_FLAGS])
-		                               : ifa->ifa_flags;
-	}
+	obj->ip_address.n_ifa_flags = tb[IFA_FLAGS]
+	                              ? nla_get_u32 (tb[IFA_FLAGS])
+	                              : ifa->ifa_flags;
 
 	if (is_v4) {
 		if (tb[IFA_LABEL]) {
@@ -2225,7 +2227,7 @@ _nl_msg_new_address (int nlmsg_type,
 		NLA_PUT (msg, IFA_CACHEINFO, sizeof(ca), &ca);
 	}
 
-	if (flags & ~0xFF) {
+	if (flags & ~((guint32) 0xFF)) {
 		/* only set the IFA_FLAGS attribute, if they actually contain additional
 		 * flags that are not already set to am.ifa_flags.
 		 *
@@ -2412,6 +2414,7 @@ void
 nm_linux_platform_setup (void)
 {
 	g_object_new (NM_TYPE_LINUX_PLATFORM,
+	              NM_PLATFORM_NETNS_SUPPORT, FALSE,
 	              NM_PLATFORM_REGISTER_SINGLETON, TRUE,
 	              NULL);
 }
@@ -2419,6 +2422,16 @@ nm_linux_platform_setup (void)
 /******************************************************************/
 
 static void
+_assert_netns_current (NMPlatform *platform)
+{
+#if NM_MORE_ASSERTS
+	nm_assert (NM_IS_LINUX_PLATFORM (platform));
+
+	nm_assert (NM_IN_SET (nm_platform_netns_get (platform), NULL, nmp_netns_get_current ()));
+#endif
+}
+
+static void
 _log_dbg_sysctl_set_impl (NMPlatform *platform, const char *path, const char *value)
 {
 	GError *error = NULL;
@@ -2451,6 +2464,7 @@ _log_dbg_sysctl_set_impl (NMPlatform *platform, const char *path, const char *va
 static gboolean
 sysctl_set (NMPlatform *platform, const char *path, const char *value)
 {
+	nm_auto_pop_netns NMPNetns *netns = NULL;
 	int fd, tries;
 	gssize nwrote;
 	gsize len;
@@ -2466,6 +2480,9 @@ sysctl_set (NMPlatform *platform, const char *path, const char *value)
 	/* Don't write to suspicious locations */
 	g_assert (!strstr (path, "/../"));
 
+	if (!nm_platform_netns_push (platform, &netns))
+		return FALSE;
+
 	fd = open (path, O_WRONLY | O_TRUNC);
 	if (fd == -1) {
 		if (errno == ENOENT) {
@@ -2580,6 +2597,7 @@ _log_dbg_sysctl_get_impl (NMPlatform *platform, const char *path, const char *co
 static char *
 sysctl_get (NMPlatform *platform, const char *path)
 {
+	nm_auto_pop_netns NMPNetns *netns = NULL;
 	GError *error = NULL;
 	char *contents;
 
@@ -2589,6 +2607,9 @@ sysctl_get (NMPlatform *platform, const char *path)
 	/* Don't write to suspicious locations */
 	g_assert (!strstr (path, "/../"));
 
+	if (!nm_platform_netns_push (platform, &netns))
+		return NULL;
+
 	if (!g_file_get_contents (path, &contents, NULL, &error)) {
 		/* We assume FAILED means EOPNOTSUP */
 		if (   g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)
@@ -2653,6 +2674,9 @@ do_emit_signal (NMPlatform *platform, const NMPObject *obj, NMPCacheOpsType cach
 	nm_assert (!obj || cache_op == NMP_CACHE_OPS_REMOVED || obj == nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, obj));
 	nm_assert (!obj || cache_op != NMP_CACHE_OPS_REMOVED || obj != nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, obj));
 
+	/* we raise the signals inside the namespace of the NMPlatform instance. */
+	_assert_netns_current (platform);
+
 	switch (cache_op) {
 	case NMP_CACHE_OPS_ADDED:
 		if (!nmp_object_is_visible (obj))
@@ -3857,6 +3881,7 @@ do_change_link (NMPlatform *platform,
                 int ifindex,
                 struct nl_msg *nlmsg)
 {
+	nm_auto_pop_netns NMPNetns *netns = NULL;
 	WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN;
 	int nle;
 	char s_buf[256];
@@ -3864,6 +3889,9 @@ do_change_link (NMPlatform *platform,
 	NMLogLevel log_level = LOGL_DEBUG;
 	const char *log_result = "failure", *log_detail = "";
 
+	if (!nm_platform_netns_push (platform, &netns))
+		return NM_PLATFORM_ERROR_UNSPECIFIED;
+
 retry:
 	nle = _nl_send_auto_with_seq (platform, nlmsg, &seq_result);
 	if (nle < 0) {
@@ -3927,7 +3955,7 @@ link_add (NMPlatform *platform,
 		 * bond0 automatically.
 		 */
 		if (!g_file_test ("/sys/class/net/bonding_masters", G_FILE_TEST_EXISTS))
-			nm_utils_modprobe (NULL, TRUE, "bonding", "max_bonds=0", NULL);
+			(void) nm_utils_modprobe (NULL, TRUE, "bonding", "max_bonds=0", NULL);
 	}
 
 	_LOGD ("link: add link '%s' of type '%s' (%d)",
@@ -3994,7 +4022,7 @@ link_get_type_name (NMPlatform *platform, int ifindex)
 		return nm_link_type_to_string (obj->link.type);
 	}
 	/* Link type not detected. Fallback to rtnl_link_get_type()/IFLA_INFO_KIND. */
-	return str_if_set (obj->link.kind, "unknown");
+	return obj->link.kind ?: "unknown";
 }
 
 static gboolean
@@ -4023,6 +4051,31 @@ link_refresh (NMPlatform *platform, int ifindex)
 	return !!cache_lookup_link (platform, ifindex);
 }
 
+static gboolean
+link_set_netns (NMPlatform *platform,
+                int ifindex,
+                int netns_fd)
+{
+	nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
+
+	_LOGD ("link: move link %d to network namespace with fd %d", ifindex, netns_fd);
+
+	nlmsg = _nl_msg_new_link (RTM_NEWLINK,
+	                          0,
+	                          ifindex,
+	                          NULL,
+	                          0,
+	                          0);
+	if (!nlmsg)
+		return FALSE;
+
+	NLA_PUT (nlmsg, IFLA_NET_NS_FD, 4, &netns_fd);
+	return do_change_link (platform, ifindex, nlmsg) == NM_PLATFORM_ERROR_SUCCESS;
+
+nla_put_failure:
+	g_return_val_if_reached (FALSE);
+}
+
 static NMPlatformError
 link_change_flags (NMPlatform *platform,
                    int ifindex,
@@ -4137,11 +4190,15 @@ link_set_user_ipv6ll_enabled (NMPlatform *platform, int ifindex, gboolean enable
 static gboolean
 link_supports_carrier_detect (NMPlatform *platform, int ifindex)
 {
+	nm_auto_pop_netns NMPNetns *netns = NULL;
 	const char *name = nm_platform_link_get_name (platform, ifindex);
 
 	if (!name)
 		return FALSE;
 
+	if (!nm_platform_netns_push (platform, &netns))
+		return FALSE;
+
 	/* We use netlink for the actual carrier detection, but netlink can't tell
 	 * us whether the device actually supports carrier detection in the first
 	 * place. We assume any device that does implements one of these two APIs.
@@ -4152,6 +4209,7 @@ link_supports_carrier_detect (NMPlatform *platform, int ifindex)
 static gboolean
 link_supports_vlans (NMPlatform *platform, int ifindex)
 {
+	nm_auto_pop_netns NMPNetns *netns = NULL;
 	const NMPObject *obj;
 
 	obj = cache_lookup_link (platform, ifindex);
@@ -4160,6 +4218,9 @@ link_supports_vlans (NMPlatform *platform, int ifindex)
 	if (!obj || obj->link.arptype != ARPHRD_ETHER)
 		return FALSE;
 
+	if (!nm_platform_netns_push (platform, &netns))
+		return FALSE;
+
 	return nmp_utils_ethtool_supports_vlans (obj->link.name);
 }
 
@@ -4198,6 +4259,11 @@ link_get_permanent_address (NMPlatform *platform,
                             guint8 *buf,
                             size_t *length)
 {
+	nm_auto_pop_netns NMPNetns *netns = NULL;
+
+	if (!nm_platform_netns_push (platform, &netns))
+		return FALSE;
+
 	return nmp_utils_ethtool_get_permanent_address (nm_platform_link_get_name (platform, ifindex), buf, length);
 }
 
@@ -4234,7 +4300,7 @@ link_get_physical_port_id (NMPlatform *platform, int ifindex)
 	if (!ifname)
 		return NULL;
 
-	ifname = ASSERT_VALID_PATH_COMPONENT (ifname);
+	ifname = NM_ASSERT_VALID_PATH_COMPONENT (ifname);
 
 	path = g_strdup_printf ("/sys/class/net/%s/phys_port_id", ifname);
 	id = sysctl_get (platform, path);
@@ -4254,7 +4320,7 @@ link_get_dev_id (NMPlatform *platform, int ifindex)
 	if (!ifname)
 		return 0;
 
-	ifname = ASSERT_VALID_PATH_COMPONENT (ifname);
+	ifname = NM_ASSERT_VALID_PATH_COMPONENT (ifname);
 
 	path = g_strdup_printf ("/sys/class/net/%s/dev_id", ifname);
 	id = sysctl_get (platform, path);
@@ -4837,7 +4903,7 @@ tun_add (NMPlatform *platform, const char *name, gboolean tap,
 	if (fd < 0)
 		return FALSE;
 
-	strncpy (ifr.ifr_name, name, IFNAMSIZ);
+	nm_utils_ifname_cpy (ifr.ifr_name, name);
 	ifr.ifr_flags = tap ? IFF_TAP : IFF_TUN;
 
 	if (!pi)
@@ -4930,7 +4996,7 @@ infiniband_partition_add (NMPlatform *platform, int parent, int p_key, const NMP
 
 	ifname = g_strdup_printf ("%s.%04x", obj_parent->link.name, p_key);
 
-	path = g_strdup_printf ("/sys/class/net/%s/create_child", ASSERT_VALID_PATH_COMPONENT (obj_parent->link.name));
+	path = g_strdup_printf ("/sys/class/net/%s/create_child", NM_ASSERT_VALID_PATH_COMPONENT (obj_parent->link.name));
 	id = g_strdup_printf ("0x%04x", p_key);
 	if (!nm_platform_sysctl_set (platform, path, id))
 		return FALSE;
@@ -4977,15 +5043,19 @@ wifi_get_wifi_data (NMPlatform *platform, int ifindex)
 
 	return wifi_data;
 }
+#define WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, retval) \
+	nm_auto_pop_netns NMPNetns *netns = NULL; \
+	WifiData *wifi_data; \
+	if (!nm_platform_netns_push (platform, &netns)) \
+		return retval; \
+	wifi_data = wifi_get_wifi_data (platform, ifindex); \
+	if (!wifi_data) \
+		return retval;
 
 static gboolean
 wifi_get_capabilities (NMPlatform *platform, int ifindex, NMDeviceWifiCapabilities *caps)
 {
-	WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
-
-	if (!wifi_data)
-		return FALSE;
-
+	WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, FALSE);
 	if (caps)
 		*caps = wifi_utils_get_caps (wifi_data);
 	return TRUE;
@@ -4994,90 +5064,64 @@ wifi_get_capabilities (NMPlatform *platform, int ifindex, NMDeviceWifiCapabiliti
 static gboolean
 wifi_get_bssid (NMPlatform *platform, int ifindex, guint8 *bssid)
 {
-	WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
-
-	if (!wifi_data)
-		return FALSE;
+	WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, FALSE);
 	return wifi_utils_get_bssid (wifi_data, bssid);
 }
 
 static guint32
 wifi_get_frequency (NMPlatform *platform, int ifindex)
 {
-	WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
-
-	if (!wifi_data)
-		return 0;
+	WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, 0);
 	return wifi_utils_get_freq (wifi_data);
 }
 
 static gboolean
 wifi_get_quality (NMPlatform *platform, int ifindex)
 {
-	WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
-
-	if (!wifi_data)
-		return FALSE;
+	WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, FALSE);
 	return wifi_utils_get_qual (wifi_data);
 }
 
 static guint32
 wifi_get_rate (NMPlatform *platform, int ifindex)
 {
-	WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
-
-	if (!wifi_data)
-		return FALSE;
+	WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, FALSE);
 	return wifi_utils_get_rate (wifi_data);
 }
 
 static NM80211Mode
 wifi_get_mode (NMPlatform *platform, int ifindex)
 {
-	WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
-
-	if (!wifi_data)
-		return NM_802_11_MODE_UNKNOWN;
-
+	WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, NM_802_11_MODE_UNKNOWN);
 	return wifi_utils_get_mode (wifi_data);
 }
 
 static void
 wifi_set_mode (NMPlatform *platform, int ifindex, NM80211Mode mode)
 {
-	WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
-
-	if (wifi_data)
-		wifi_utils_set_mode (wifi_data, mode);
+	WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, );
+	wifi_utils_set_mode (wifi_data, mode);
 }
 
 static void
 wifi_set_powersave (NMPlatform *platform, int ifindex, guint32 powersave)
 {
-	WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
-
-	if (wifi_data)
-		wifi_utils_set_powersave (wifi_data, powersave);
+	WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, );
+	wifi_utils_set_powersave (wifi_data, powersave);
 }
 
 static guint32
 wifi_find_frequency (NMPlatform *platform, int ifindex, const guint32 *freqs)
 {
-	WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
-
-	if (!wifi_data)
-		return 0;
-
+	WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, 0);
 	return wifi_utils_find_freq (wifi_data, freqs);
 }
 
 static void
 wifi_indicate_addressing_running (NMPlatform *platform, int ifindex, gboolean running)
 {
-	WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
-
-	if (wifi_data)
-		wifi_utils_indicate_addressing_running (wifi_data, running);
+	WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, );
+	wifi_utils_indicate_addressing_running (wifi_data, running);
 }
 
 /******************************************************************/
@@ -5085,33 +5129,21 @@ wifi_indicate_addressing_running (NMPlatform *platform, int ifindex, gboolean ru
 static guint32
 mesh_get_channel (NMPlatform *platform, int ifindex)
 {
-	WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
-
-	if (!wifi_data)
-		return 0;
-
+	WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, 0);
 	return wifi_utils_get_mesh_channel (wifi_data);
 }
 
 static gboolean
 mesh_set_channel (NMPlatform *platform, int ifindex, guint32 channel)
 {
-	WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
-
-	if (!wifi_data)
-		return FALSE;
-
+	WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, FALSE);
 	return wifi_utils_set_mesh_channel (wifi_data, channel);
 }
 
 static gboolean
 mesh_set_ssid (NMPlatform *platform, int ifindex, const guint8 *ssid, gsize len)
 {
-	WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
-
-	if (!wifi_data)
-		return FALSE;
-
+	WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, FALSE);
 	return wifi_utils_set_mesh_ssid (wifi_data, ssid, len);
 }
 
@@ -5120,8 +5152,12 @@ mesh_set_ssid (NMPlatform *platform, int ifindex, const guint8 *ssid, gsize len)
 static gboolean
 link_get_wake_on_lan (NMPlatform *platform, int ifindex)
 {
+	nm_auto_pop_netns NMPNetns *netns = NULL;
 	NMLinkType type = nm_platform_link_get_type (platform, ifindex);
 
+	if (!nm_platform_netns_push (platform, &netns))
+		return FALSE;
+
 	if (type == NM_LINK_TYPE_ETHERNET)
 		return nmp_utils_ethtool_get_wake_on_lan (nm_platform_link_get_name (platform, ifindex));
 	else if (type == NM_LINK_TYPE_WIFI) {
@@ -5142,6 +5178,11 @@ link_get_driver_info (NMPlatform *platform,
                       char **out_driver_version,
                       char **out_fw_version)
 {
+	nm_auto_pop_netns NMPNetns *netns = NULL;
+
+	if (!nm_platform_netns_push (platform, &netns))
+		return FALSE;
+
 	return nmp_utils_ethtool_get_driver_info (nm_platform_link_get_name (platform, ifindex),
 	                                          out_driver_name,
 	                                          out_driver_version,
@@ -5184,6 +5225,7 @@ ip4_address_add (NMPlatform *platform,
                  in_addr_t peer_addr,
                  guint32 lifetime,
                  guint32 preferred,
+                 guint32 flags,
                  const char *label)
 {
 	NMPObject obj_id;
@@ -5196,8 +5238,8 @@ ip4_address_add (NMPlatform *platform,
 	                             &addr,
 	                             plen,
 	                             &peer_addr,
-	                             0,
-	                             nmp_utils_ip4_address_is_link_local (addr) ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE,
+	                             flags,
+	                             nm_utils_ip4_address_is_link_local (addr) ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE,
 	                             lifetime,
 	                             preferred,
 	                             label);
@@ -5722,6 +5764,7 @@ out:
 static gboolean
 event_handler_read_netlink (NMPlatform *platform, gboolean wait_for_acks)
 {
+	nm_auto_pop_netns NMPNetns *netns = NULL;
 	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
 	int r, nle;
 	struct pollfd pfd;
@@ -5734,6 +5777,9 @@ event_handler_read_netlink (NMPlatform *platform, gboolean wait_for_acks)
 		gint64 timeout_abs_ns;
 	} data_next;
 
+	if (!nm_platform_netns_push (platform, &netns))
+		return FALSE;
+
 	while (TRUE) {
 
 		while (TRUE) {
@@ -5838,7 +5884,14 @@ cache_update_link_udev (NMPlatform *platform, int ifindex, GUdevDevice *udev_dev
 	NMPCacheOpsType cache_op;
 
 	cache_op = nmp_cache_update_link_udev (priv->cache, ifindex, udev_device, &obj_cache, &was_visible, cache_pre_hook, platform);
-	do_emit_signal (platform, obj_cache, cache_op, was_visible);
+
+	if (cache_op != NMP_CACHE_OPS_UNCHANGED) {
+		nm_auto_pop_netns NMPNetns *netns = NULL;
+
+		if (!nm_platform_netns_push (platform, &netns))
+			return;
+		do_emit_signal (platform, obj_cache, cache_op, was_visible);
+	}
 }
 
 static void
@@ -5854,22 +5907,22 @@ udev_device_added (NMPlatform *platform,
 		return;
 	}
 
-	if (g_udev_device_get_property (udev_device, "IFINDEX"))
-		ifindex = g_udev_device_get_property_as_int (udev_device, "IFINDEX");
-	else {
-		_LOGW ("(%s): udev-add: failed to get device's ifindex", ifname);
+	if (!g_udev_device_get_property (udev_device, "IFINDEX")) {
+		_LOGW ("udev-add[%s]failed to get device's ifindex", ifname);
 		return;
 	}
+	ifindex = g_udev_device_get_property_as_int (udev_device, "IFINDEX");
 	if (ifindex <= 0) {
-		_LOGW ("(%s): udev-add: retrieved invalid IFINDEX=%d", ifname, ifindex);
+		_LOGW ("udev-add[%s]: retrieved invalid IFINDEX=%d", ifname, ifindex);
 		return;
 	}
 
 	if (!g_udev_device_get_sysfs_path (udev_device)) {
-		_LOGD ("(%s): udev-add: couldn't determine device path; ignoring...", ifname);
+		_LOGD ("udev-add[%s,%d]: couldn't determine device path; ignoring...", ifname, ifindex);
 		return;
 	}
 
+	_LOGT ("udev-add[%s,%d]: device added", ifname, ifindex);
 	cache_update_link_udev (platform, ifindex, udev_device);
 }
 
@@ -5909,6 +5962,7 @@ handle_udev_event (GUdevClient *client,
                    GUdevDevice *udev_device,
                    gpointer user_data)
 {
+	nm_auto_pop_netns NMPNetns *netns = NULL;
 	NMPlatform *platform = NM_PLATFORM (user_data);
 	const char *subsys;
 	const char *ifindex;
@@ -5916,6 +5970,9 @@ handle_udev_event (GUdevClient *client,
 
 	g_return_if_fail (action != NULL);
 
+	if (!nm_platform_netns_push (platform, &netns))
+		return;
+
 	/* A bit paranoid */
 	subsys = g_udev_device_get_subsystem (udev_device);
 	g_return_if_fail (!g_strcmp0 (subsys, "net"));
@@ -5938,15 +5995,22 @@ static void
 nm_linux_platform_init (NMLinuxPlatform *self)
 {
 	NMLinuxPlatformPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NM_TYPE_LINUX_PLATFORM, NMLinuxPlatformPrivate);
+	gboolean use_udev;
+
+	use_udev =    nmp_netns_is_initial ()
+	           && access ("/sys", W_OK) == 0;
 
 	self->priv = priv;
 
 	priv->nlh_seq_next = 1;
-	priv->cache = nmp_cache_new ();
+	priv->cache = nmp_cache_new (use_udev);
 	priv->delayed_action.list_master_connected = g_ptr_array_new ();
 	priv->delayed_action.list_refresh_link = g_ptr_array_new ();
 	priv->delayed_action.list_wait_for_nl_response = g_array_new (FALSE, TRUE, sizeof (DelayedActionWaitForNlResponseData));
 	priv->wifi_data = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) wifi_utils_deinit);
+
+	if (use_udev)
+		priv->udev_client = g_udev_client_new ((const char *[]) { "net", NULL });
 }
 
 static void
@@ -5954,14 +6018,22 @@ constructed (GObject *_object)
 {
 	NMPlatform *platform = NM_PLATFORM (_object);
 	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
-	const char *udev_subsys[] = { "net", NULL };
 	int channel_flags;
 	gboolean status;
 	int nle;
-	GUdevEnumerator *enumerator;
-	GList *devices, *iter;
 
-	_LOGD ("create");
+	nm_assert (!platform->_netns || platform->_netns == nmp_netns_get_current ());
+
+	_LOGD ("create (%s netns, %s, %s udev)",
+	       !platform->_netns ? "ignore" : "use",
+	       !platform->_netns && nmp_netns_is_initial ()
+	           ? "initial netns"
+	           : (!nmp_netns_get_current ()
+	                ? "no netns support"
+	                : nm_sprintf_bufa (100, "in netns[%p]%s",
+	                                   nmp_netns_get_current (),
+	                                   nmp_netns_get_current () == nmp_netns_get_initial () ? "/main" : "")),
+	       nmp_cache_use_udev_get (priv->cache) ? "use" : "no");
 
 	priv->nlh = nl_socket_alloc ();
 	g_assert (priv->nlh);
@@ -5993,16 +6065,12 @@ constructed (GObject *_object)
 
 	channel_flags = g_io_channel_get_flags (priv->event_channel);
 	status = g_io_channel_set_flags (priv->event_channel,
-		channel_flags | G_IO_FLAG_NONBLOCK, NULL);
+	                                 channel_flags | G_IO_FLAG_NONBLOCK, NULL);
 	g_assert (status);
 	priv->event_id = g_io_add_watch (priv->event_channel,
 	                                (EVENT_CONDITIONS | ERROR_CONDITIONS | DISCONNECT_CONDITIONS),
 	                                 event_handler, platform);
 
-	/* Set up udev monitoring */
-	priv->udev_client = g_udev_client_new (udev_subsys);
-	g_signal_connect (priv->udev_client, "uevent", G_CALLBACK (handle_udev_event), platform);
-
 	/* complete construction of the GObject instance before populating the cache. */
 	G_OBJECT_CLASS (nm_linux_platform_parent_class)->constructed (_object);
 
@@ -6017,19 +6085,27 @@ constructed (GObject *_object)
 
 	delayed_action_handle_all (platform, FALSE);
 
-	/* And read initial device list */
-	enumerator = g_udev_enumerator_new (priv->udev_client);
-	g_udev_enumerator_add_match_subsystem (enumerator, "net");
+	/* Set up udev monitoring */
+	if (priv->udev_client) {
+		GUdevEnumerator *enumerator;
+		GList *devices, *iter;
+
+		g_signal_connect (priv->udev_client, "uevent", G_CALLBACK (handle_udev_event), platform);
+
+		/* And read initial device list */
+		enumerator = g_udev_enumerator_new (priv->udev_client);
+		g_udev_enumerator_add_match_subsystem (enumerator, "net");
 
-	g_udev_enumerator_add_match_is_initialized (enumerator);
+		g_udev_enumerator_add_match_is_initialized (enumerator);
 
-	devices = g_udev_enumerator_execute (enumerator);
-	for (iter = devices; iter; iter = g_list_next (iter)) {
-		udev_device_added (platform, G_UDEV_DEVICE (iter->data));
-		g_object_unref (G_UDEV_DEVICE (iter->data));
+		devices = g_udev_enumerator_execute (enumerator);
+		for (iter = devices; iter; iter = g_list_next (iter)) {
+			udev_device_added (platform, G_UDEV_DEVICE (iter->data));
+			g_object_unref (G_UDEV_DEVICE (iter->data));
+		}
+		g_list_free (devices);
+		g_object_unref (enumerator);
 	}
-	g_list_free (devices);
-	g_object_unref (enumerator);
 }
 
 static void
@@ -6048,6 +6124,11 @@ dispose (GObject *object)
 
 	g_clear_pointer (&priv->prune_candidates, g_hash_table_unref);
 
+	if (priv->udev_client) {
+		g_signal_handlers_disconnect_by_func (priv->udev_client, G_CALLBACK (handle_udev_event), platform);
+		g_clear_object (&priv->udev_client);
+	}
+
 	G_OBJECT_CLASS (nm_linux_platform_parent_class)->dispose (object);
 }
 
@@ -6067,7 +6148,6 @@ nm_linux_platform_finalize (GObject *object)
 	g_io_channel_unref (priv->event_channel);
 	nl_socket_free (priv->nlh);
 
-	g_object_unref (priv->udev_client);
 	g_hash_table_unref (priv->wifi_data);
 
 	if (priv->sysctl_get_prev_values) {
@@ -6109,6 +6189,8 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass)
 
 	platform_class->link_refresh = link_refresh;
 
+	platform_class->link_set_netns = link_set_netns;
+
 	platform_class->link_set_up = link_set_up;
 	platform_class->link_set_down = link_set_down;
 	platform_class->link_set_arp = link_set_arp;