summary refs log tree commit diff
path: root/src/platform
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2015-08-28 01:13:48 +0200
committerMichael Biebl <biebl@debian.org>2015-08-28 01:13:48 +0200
commit81836c2d44802b4cca833d7775dd627e0797a7e2 (patch)
tree91154e6cefc0465306603f51ec0010d9963bbea4 /src/platform
parent50a58f0fabd8a34c1b6108a107e08abe3c1ccd24 (diff)
Imported Upstream version 1.0.6 upstream/1.0.6
Diffstat (limited to 'src/platform')
-rw-r--r--src/platform/nm-linux-platform.c106
-rw-r--r--src/platform/nm-platform-utils.c62
-rw-r--r--src/platform/nm-platform-utils.h6
-rw-r--r--src/platform/nm-platform.c54
-rw-r--r--src/platform/nm-platform.h7
-rw-r--r--src/platform/nmp-object.c28
-rw-r--r--src/platform/tests/test-link.c5
-rw-r--r--src/platform/wifi/wifi-utils-wext.c4
8 files changed, 176 insertions, 96 deletions
diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c
index 80757f4e..cd0a4e07 100644
--- a/src/platform/nm-linux-platform.c
+++ b/src/platform/nm-linux-platform.c
@@ -67,8 +67,9 @@
 
 /*********************************************************************************************/
 
-#define _LOG_DOMAIN LOGD_PLATFORM
-#define _LOG_PREFIX_NAME "platform-linux"
+#define _NMLOG_DOMAIN                     LOGD_PLATFORM
+#define _NMLOG_PREFIX_NAME                "platform-linux"
+#define _NMLOG(level, ...)                _LOG(level, _NMLOG_DOMAIN, platform, __VA_ARGS__)
 
 #define _LOG(level, domain, self, ...) \
     G_STMT_START { \
@@ -77,11 +78,11 @@
         \
         if (nm_logging_enabled (__level, __domain)) { \
             char __prefix[32]; \
-            const char *__p_prefix = _LOG_PREFIX_NAME; \
+            const char *__p_prefix = _NMLOG_PREFIX_NAME; \
             const void *const __self = (self); \
             \
             if (__self && __self != nm_platform_try_get ()) { \
-                g_snprintf (__prefix, sizeof (__prefix), "%s[%p]", _LOG_PREFIX_NAME, __self); \
+                g_snprintf (__prefix, sizeof (__prefix), "%s[%p]", _NMLOG_PREFIX_NAME, __self); \
                 __p_prefix = __prefix; \
             } \
             _nm_log (__level, __domain, 0, \
@@ -89,25 +90,12 @@
                      __p_prefix _NM_UTILS_MACRO_REST (__VA_ARGS__)); \
         } \
     } G_STMT_END
-#define _LOG_LEVEL_ENABLED(level, domain) \
-    ( nm_logging_enabled ((level), (domain)) )
 
-#ifdef NM_MORE_LOGGING
-#define _LOGT_ENABLED()     _LOG_LEVEL_ENABLED (LOGL_TRACE, _LOG_DOMAIN)
-#define _LOGT(...)          _LOG (LOGL_TRACE, _LOG_DOMAIN, platform, __VA_ARGS__)
-#else
-#define _LOGT_ENABLED()     FALSE
-#define _LOGT(...)          G_STMT_START { if (FALSE) { _LOG (LOGL_TRACE, _LOG_DOMAIN, platform, __VA_ARGS__); } } G_STMT_END
-#endif
-
-#define _LOGD(...)      _LOG (LOGL_DEBUG, _LOG_DOMAIN, platform, __VA_ARGS__)
-#define _LOGI(...)      _LOG (LOGL_INFO , _LOG_DOMAIN, platform, __VA_ARGS__)
-#define _LOGW(...)      _LOG (LOGL_WARN , _LOG_DOMAIN, platform, __VA_ARGS__)
-#define _LOGE(...)      _LOG (LOGL_ERR  , _LOG_DOMAIN, platform, __VA_ARGS__)
-
-#define debug(...)      _LOG (LOGL_DEBUG, _LOG_DOMAIN, NULL, __VA_ARGS__)
-#define warning(...)    _LOG (LOGL_WARN , _LOG_DOMAIN, NULL, __VA_ARGS__)
-#define error(...)      _LOG (LOGL_ERR  , _LOG_DOMAIN, NULL, __VA_ARGS__)
+#define trace(...)      _LOG (LOGL_TRACE, _NMLOG_DOMAIN, NULL, __VA_ARGS__)
+#define debug(...)      _LOG (LOGL_DEBUG, _NMLOG_DOMAIN, NULL, __VA_ARGS__)
+#define info(...)       _LOG (LOGL_INFO,  _NMLOG_DOMAIN, NULL, __VA_ARGS__)
+#define warning(...)    _LOG (LOGL_WARN , _NMLOG_DOMAIN, NULL, __VA_ARGS__)
+#define error(...)      _LOG (LOGL_ERR  , _NMLOG_DOMAIN, NULL, __VA_ARGS__)
 
 /******************************************************************
  * Forward declarations and enums
@@ -150,8 +138,10 @@ static NMPCacheOpsType cache_remove_netlink (NMPlatform *platform, const NMPObje
 struct libnl_vtable
 {
 	void *handle;
+	void *handle_route;
 
 	int (*f_nl_has_capability) (int capability);
+	int (*f_rtnl_link_get_link_netnsid) (const struct rtnl_link *link, gint32 *out_link_netnsid);
 };
 
 static int
@@ -160,24 +150,25 @@ _nl_f_nl_has_capability (int capability)
 	return FALSE;
 }
 
-static struct libnl_vtable *
+static const struct libnl_vtable *
 _nl_get_vtable (void)
 {
 	static struct libnl_vtable vtable;
 
 	if (G_UNLIKELY (!vtable.f_nl_has_capability)) {
-		void *handle;
-
-		handle = dlopen ("libnl-3.so.200", RTLD_LAZY | RTLD_NOLOAD);
-		if (handle) {
-			vtable.handle = handle;
-			vtable.f_nl_has_capability = dlsym (handle, "nl_has_capability");
+		vtable.handle = dlopen ("libnl-3.so.200", RTLD_LAZY | RTLD_NOLOAD);
+		if (vtable.handle) {
+			vtable.f_nl_has_capability = dlsym (vtable.handle, "nl_has_capability");
+		}
+		vtable.handle_route = dlopen ("libnl-route-3.so.200", RTLD_LAZY | RTLD_NOLOAD);
+		if (vtable.handle_route) {
+			vtable.f_rtnl_link_get_link_netnsid = dlsym (vtable.handle_route, "rtnl_link_get_link_netnsid");
 		}
 
 		if (!vtable.f_nl_has_capability)
 			vtable.f_nl_has_capability = &_nl_f_nl_has_capability;
 
-		g_return_val_if_fail (vtable.handle, &vtable);
+		trace ("libnl: rtnl_link_get_link_netnsid() %s", vtable.f_rtnl_link_get_link_netnsid ? "supported" : "not supported");
 	}
 
 	return &vtable;
@@ -189,6 +180,26 @@ _nl_has_capability (int capability)
 	return (_nl_get_vtable ()->f_nl_has_capability) (capability);
 }
 
+static int
+_rtnl_link_get_link_netnsid (const struct rtnl_link *link, gint32 *out_link_netnsid)
+{
+	const struct libnl_vtable *vtable;
+
+	g_return_val_if_fail (link, -NLE_INVAL);
+	g_return_val_if_fail (out_link_netnsid, -NLE_INVAL);
+
+	vtable = _nl_get_vtable ();
+	return vtable->f_rtnl_link_get_link_netnsid
+	    ? vtable->f_rtnl_link_get_link_netnsid (link, out_link_netnsid)
+	    : -NLE_OPNOTSUPP;
+}
+
+gboolean
+nm_platform_check_support_libnl_link_netnsid (void)
+{
+	return !!(_nl_get_vtable ()->f_rtnl_link_get_link_netnsid);
+}
+
 /* Automatic deallocation of local variables */
 #define auto_nl_object __attribute__((cleanup(_nl_auto_nl_object)))
 static void
@@ -994,6 +1005,7 @@ _nmp_vt_cmd_plobj_init_from_nl_link (NMPlatform *platform, NMPlatformObject *_ob
 	gboolean completed_from_cache_val = FALSE;
 	gboolean *completed_from_cache = complete_from_cache ? &completed_from_cache_val : NULL;
 	const NMPObject *link_cached = NULL;
+	int parent;
 
 	nm_assert (memcmp (obj, ((char [sizeof (NMPObjectLink)]) { 0 }), sizeof (NMPObjectLink)) == 0);
 
@@ -1013,7 +1025,15 @@ _nmp_vt_cmd_plobj_init_from_nl_link (NMPlatform *platform, NMPlatformObject *_ob
 	obj->flags = rtnl_link_get_flags (nlo);
 	obj->connected = NM_FLAGS_HAS (obj->flags, IFF_LOWER_UP);
 	obj->master = rtnl_link_get_master (nlo);
-	obj->parent = rtnl_link_get_link (nlo);
+	parent = rtnl_link_get_link (nlo);
+	if (parent > 0) {
+		gint32 link_netnsid;
+
+		if (_rtnl_link_get_link_netnsid (nlo, &link_netnsid) == 0)
+			obj->parent = NM_PLATFORM_LINK_OTHER_NETNS;
+		else
+			obj->parent = parent;
+	}
 	obj->mtu = rtnl_link_get_mtu (nlo);
 	obj->arptype = rtnl_link_get_arptype (nlo);
 
@@ -2306,11 +2326,25 @@ event_notification (struct nl_msg *msg, gpointer user_data)
 	if (_support_user_ipv6ll_still_undecided() && msghdr->nlmsg_type == RTM_NEWLINK)
 		_support_user_ipv6ll_detect ((struct rtnl_link *) nlo);
 
-	obj = nmp_object_from_nl (platform, nlo, FALSE, TRUE);
+	switch (msghdr->nlmsg_type) {
+	case RTM_DELADDR:
+	case RTM_DELLINK:
+	case RTM_DELROUTE:
+		/* The event notifies about a deleted object. We don't need to initialize all the
+		 * fields of the nmp-object. Shortcut nmp_object_from_nl(). */
+		obj = nmp_object_from_nl (platform, nlo, TRUE, TRUE);
+		_LOGD ("event-notification: %s, seq %u: %s",
+		       _nl_nlmsg_type_to_str (msghdr->nlmsg_type, buf_nlmsg_type, sizeof (buf_nlmsg_type)),
+		       msghdr->nlmsg_seq, nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ID, NULL, 0));
+		break;
+	default:
+		obj = nmp_object_from_nl (platform, nlo, FALSE, TRUE);
+		_LOGD ("event-notification: %s, seq %u: %s",
+		       _nl_nlmsg_type_to_str (msghdr->nlmsg_type, buf_nlmsg_type, sizeof (buf_nlmsg_type)),
+		       msghdr->nlmsg_seq, nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0));
+		break;
+	}
 
-	_LOGD ("event-notification: %s, seq %u: %s",
-	       _nl_nlmsg_type_to_str (msghdr->nlmsg_type, buf_nlmsg_type, sizeof (buf_nlmsg_type)),
-	       msghdr->nlmsg_seq, nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0));
 	if (obj) {
 		auto_nmp_obj NMPObject *obj_cache = NULL;
 
@@ -4549,7 +4583,7 @@ event_handler_read_netlink_one (NMPlatform *platform)
 			debug ("Uncritical failure to retrieve incoming events: %s (%d)", nl_geterror (nle), nle);
 			break;
 		case -NLE_NOMEM:
-			warning ("Too many netlink events. Need to resynchronize platform cache");
+			info ("Too many netlink events. Need to resynchronize platform cache");
 			/* Drain the event queue, we've lost events and are out of sync anyway and we'd
 			 * like to free up some space. We'll read in the status synchronously. */
 			_nl_sock_flush_data (priv->nlh_event);
diff --git a/src/platform/nm-platform-utils.c b/src/platform/nm-platform-utils.c
index d3c62bdd..b7f0947e 100644
--- a/src/platform/nm-platform-utils.c
+++ b/src/platform/nm-platform-utils.c
@@ -33,6 +33,7 @@
 #include "nm-utils.h"
 #include "NetworkManagerUtils.h"
 #include "nm-logging.h"
+#include "nm-setting-wired.h"
 
 
 /******************************************************************
@@ -48,8 +49,14 @@ ethtool_get (const char *name, gpointer edata)
 	if (!name || !*name)
 		return FALSE;
 
+	if (!nmp_utils_device_exists (name))
+		return FALSE;
+
+	/* nmp_utils_device_exists() already errors out if @name is invalid. */
+	nm_assert (strlen (name) < IFNAMSIZ);
+
 	memset (&ifr, 0, sizeof (ifr));
-	strncpy (ifr.ifr_name, name, IFNAMSIZ);
+	strcpy (ifr.ifr_name, name);
 	ifr.ifr_data = edata;
 
 	fd = socket (PF_INET, SOCK_DGRAM, 0);
@@ -255,6 +262,43 @@ nmp_utils_ethtool_get_link_speed (const char *ifname, guint32 *out_speed)
 	return TRUE;
 }
 
+gboolean
+nmp_utils_ethtool_set_wake_on_lan (const char *ifname,
+                                   NMSettingWiredWakeOnLan wol,
+                                   const char *wol_password)
+{
+	struct ethtool_wolinfo wol_info = { };
+
+	nm_log_dbg (LOGD_PLATFORM, "setting Wake-on-LAN options 0x%x, password '%s'",
+	            (unsigned int) wol, wol_password);
+
+	wol_info.cmd = ETHTOOL_SWOL;
+	wol_info.wolopts = 0;
+
+	if (NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_PHY))
+		wol_info.wolopts |= WAKE_PHY;
+	if (NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_UNICAST))
+		wol_info.wolopts |= WAKE_UCAST;
+	if (NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_MULTICAST))
+		wol_info.wolopts |= WAKE_MCAST;
+	if (NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_BROADCAST))
+		wol_info.wolopts |= WAKE_BCAST;
+	if (NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_ARP))
+		wol_info.wolopts |= WAKE_ARP;
+	if (NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_MAGIC))
+		wol_info.wolopts |= WAKE_MAGIC;
+
+	if (wol_password) {
+		if (!nm_utils_hwaddr_aton (wol_password, wol_info.sopass, ETH_ALEN)) {
+			nm_log_dbg (LOGD_PLATFORM, "couldn't parse Wake-on-LAN password '%s'", wol_password);
+			return FALSE;
+		}
+		wol_info.wolopts |= WAKE_MAGICSECURE;
+	}
+
+	return ethtool_get (ifname, &wol_info);
+}
+
 /******************************************************************
  * mii
  ******************************************************************/
@@ -270,6 +314,9 @@ nmp_utils_mii_supports_carrier_detect (const char *ifname)
 	if (!ifname)
 		return FALSE;
 
+	if (!nmp_utils_device_exists (ifname))
+		return FALSE;
+
 	fd = socket (PF_INET, SOCK_DGRAM, 0);
 	if (fd < 0) {
 		nm_log_err (LOGD_PLATFORM, "mii: couldn't open control socket (%s)", ifname);
@@ -433,4 +480,17 @@ nmp_utils_lifetime_get (guint32 timestamp,
 	return TRUE;
 }
 
+gboolean
+nmp_utils_device_exists (const char *name)
+{
+#define SYS_CLASS_NET "/sys/class/net/"
+	char sysdir[STRLEN (SYS_CLASS_NET) + IFNAMSIZ] = SYS_CLASS_NET;
 
+	if (   !name
+	    || strlen (name) >= IFNAMSIZ
+	    || !nm_utils_is_valid_path_component (name))
+		g_return_val_if_reached (FALSE);
+
+	strcpy (&sysdir[STRLEN (SYS_CLASS_NET)], name);
+	return g_file_test (sysdir, G_FILE_TEST_EXISTS);
+}
diff --git a/src/platform/nm-platform-utils.h b/src/platform/nm-platform-utils.h
index 557de844..3769a8e1 100644
--- a/src/platform/nm-platform-utils.h
+++ b/src/platform/nm-platform-utils.h
@@ -26,6 +26,7 @@
 #include <gudev/gudev.h>
 
 #include "nm-platform.h"
+#include "nm-setting-wired.h"
 
 
 const char *nmp_utils_ethtool_get_driver (const char *ifname);
@@ -33,6 +34,9 @@ gboolean nmp_utils_ethtool_supports_carrier_detect (const char *ifname);
 gboolean nmp_utils_ethtool_supports_vlans (const char *ifname);
 int nmp_utils_ethtool_get_peer_ifindex (const char *ifname);
 gboolean nmp_utils_ethtool_get_wake_on_lan (const char *ifname);
+gboolean nmp_utils_ethtool_set_wake_on_lan (const char *ifname, NMSettingWiredWakeOnLan wol,
+                                            const char *wol_password);
+
 gboolean nmp_utils_ethtool_get_link_speed (const char *ifname, guint32 *out_speed);
 
 gboolean nmp_utils_ethtool_get_driver_info (const char *ifname,
@@ -63,4 +67,6 @@ gboolean nmp_utils_lifetime_get (guint32 timestamp,
                                  guint32 *out_lifetime,
                                  guint32 *out_preferred);
 
+gboolean nmp_utils_device_exists (const char *name);
+
 #endif /* __NM_PLATFORM_UTILS_H__ */
diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c
index b6d87ff9..fcb5b061 100644
--- a/src/platform/nm-platform.c
+++ b/src/platform/nm-platform.c
@@ -43,43 +43,26 @@
 
 G_STATIC_ASSERT (sizeof ( ((NMPlatformLink *) NULL)->addr.data ) == NM_UTILS_HWADDR_LEN_MAX);
 
-#define _LOG_DOMAIN LOGD_PLATFORM
-#define _LOG_PREFIX_NAME "platform"
-
-#define _LOG(level, domain, self, ...) \
+#define _NMLOG_DOMAIN           LOGD_PLATFORM
+#define _NMLOG_PREFIX_NAME      "platform"
+#define _NMLOG(level, ...) \
     G_STMT_START { \
         const NMLogLevel __level = (level); \
-        const NMLogDomain __domain = (domain); \
         \
-        if (nm_logging_enabled (__level, __domain)) { \
+        if (nm_logging_enabled (__level, _NMLOG_DOMAIN)) { \
             char __prefix[32]; \
-            const char *__p_prefix = _LOG_PREFIX_NAME; \
+            const char *__p_prefix = _NMLOG_PREFIX_NAME; \
             const void *const __self = (self); \
             \
             if (__self && __self != nm_platform_try_get ()) { \
-                g_snprintf (__prefix, sizeof (__prefix), "%s[%p]", _LOG_PREFIX_NAME, __self); \
+                g_snprintf (__prefix, sizeof (__prefix), "%s[%p]", _NMLOG_PREFIX_NAME, __self); \
                 __p_prefix = __prefix; \
             } \
-            _nm_log (__level, __domain, 0, \
+            _nm_log (__level, _NMLOG_DOMAIN, 0, \
                      "%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
                      __p_prefix _NM_UTILS_MACRO_REST (__VA_ARGS__)); \
         } \
     } G_STMT_END
-#define _LOG_LEVEL_ENABLED(level, domain) \
-    ( nm_logging_enabled ((level), (domain)) )
-
-#ifdef NM_MORE_LOGGING
-#define _LOGT_ENABLED()     _LOG_LEVEL_ENABLED (LOGL_TRACE, _LOG_DOMAIN)
-#define _LOGT(...)          _LOG (LOGL_TRACE, _LOG_DOMAIN, self, __VA_ARGS__)
-#else
-#define _LOGT_ENABLED()     FALSE
-#define _LOGT(...)          G_STMT_START { if (FALSE) { _LOG (LOGL_TRACE, _LOG_DOMAIN, self, __VA_ARGS__); } } G_STMT_END
-#endif
-
-#define _LOGD(...)      _LOG (LOGL_DEBUG, _LOG_DOMAIN, self, __VA_ARGS__)
-#define _LOGI(...)      _LOG (LOGL_INFO , _LOG_DOMAIN, self, __VA_ARGS__)
-#define _LOGW(...)      _LOG (LOGL_WARN , _LOG_DOMAIN, self, __VA_ARGS__)
-#define _LOGE(...)      _LOG (LOGL_ERR  , _LOG_DOMAIN, self, __VA_ARGS__)
 
 #define NM_PLATFORM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_PLATFORM, NMPlatformPrivate))
 
@@ -464,9 +447,12 @@ nm_platform_link_get_all (NMPlatform *self)
 			g_warn_if_fail (g_hash_table_contains (unseen, GINT_TO_POINTER (item->master)));
 		}
 		if (item->parent != 0) {
-			g_warn_if_fail (item->parent > 0);
-			g_warn_if_fail (item->parent != item->ifindex);
-			g_warn_if_fail (g_hash_table_contains (unseen, GINT_TO_POINTER (item->parent)));
+			if (item->parent != NM_PLATFORM_LINK_OTHER_NETNS) {
+				g_warn_if_fail (item->parent > 0);
+				g_warn_if_fail (item->parent != item->ifindex);
+				g_warn_if_fail (   !nm_platform_check_support_libnl_link_netnsid ()
+				                || g_hash_table_contains (unseen, GINT_TO_POINTER (item->parent)));
+			}
 		}
 	}
 #endif
@@ -1873,7 +1859,7 @@ nm_platform_ip4_address_add (NMPlatform *self,
 	g_return_val_if_fail (klass->ip4_address_add, FALSE);
 	g_return_val_if_fail (!label || strlen (label) < sizeof (((NMPlatformIP4Address *) NULL)->label), FALSE);
 
-	if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) {
+	if (_LOGD_ENABLED ()) {
 		NMPlatformIP4Address addr = { 0 };
 
 		addr.ifindex = ifindex;
@@ -1909,7 +1895,7 @@ nm_platform_ip6_address_add (NMPlatform *self,
 	g_return_val_if_fail (preferred <= lifetime, FALSE);
 	g_return_val_if_fail (klass->ip6_address_add, FALSE);
 
-	if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) {
+	if (_LOGD_ENABLED ()) {
 		NMPlatformIP6Address addr = { 0 };
 
 		addr.ifindex = ifindex;
@@ -2190,7 +2176,7 @@ nm_platform_ip4_route_add (NMPlatform *self,
 	g_return_val_if_fail (0 <= plen && plen <= 32, FALSE);
 	g_return_val_if_fail (klass->ip4_route_add, FALSE);
 
-	if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) {
+	if (_LOGD_ENABLED ()) {
 		NMPlatformIP4Route route = { 0 };
 
 		route.ifindex = ifindex;
@@ -2218,7 +2204,7 @@ nm_platform_ip6_route_add (NMPlatform *self,
 	g_return_val_if_fail (0 <= plen && plen <= 128, FALSE);
 	g_return_val_if_fail (klass->ip6_route_add, FALSE);
 
-	if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) {
+	if (_LOGD_ENABLED ()) {
 		NMPlatformIP6Route route = { 0 };
 
 		route.ifindex = ifindex;
@@ -2372,8 +2358,10 @@ nm_platform_link_to_string (const NMPlatformLink *link)
 	else
 		master[0] = 0;
 
-	if (link->parent)
-		g_snprintf (parent, sizeof (master), "@%d", link->parent);
+	if (link->parent > 0)
+		g_snprintf (parent, sizeof (parent), "@%d", link->parent);
+	else if (link->parent == NM_PLATFORM_LINK_OTHER_NETNS)
+		g_strlcpy (parent, "@other-netns", sizeof (parent));
 	else
 		parent[0] = 0;
 
diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h
index 4d254195..3219dd5a 100644
--- a/src/platform/nm-platform.h
+++ b/src/platform/nm-platform.h
@@ -87,6 +87,8 @@ typedef enum {
 	_NM_PLATFORM_REASON_CACHE_CHECK_INTERNAL,
 } NMPlatformReason;
 
+#define NM_PLATFORM_LINK_OTHER_NETNS    (-1)
+
 #define __NMPlatformObject_COMMON \
 	int ifindex; \
 	;
@@ -105,6 +107,10 @@ struct _NMPlatformLink {
 
 	gboolean initialized;
 	int master;
+
+	/* rtnl_link_get_link(), IFLA_LINK.
+	 * If IFLA_LINK_NETNSID indicates that the parent is in another namespace,
+	 * this field be set to (negative) NM_PLATFORM_LINK_OTHER_NETNS. */
 	int parent;
 
 	/* rtnl_link_get_arptype(), ifinfomsg.ifi_type. */
@@ -734,6 +740,7 @@ int nm_platform_ip4_route_cmp (const NMPlatformIP4Route *a, const NMPlatformIP4R
 int nm_platform_ip6_route_cmp (const NMPlatformIP6Route *a, const NMPlatformIP6Route *b);
 
 gboolean nm_platform_check_support_libnl_extended_ifa_flags (void);
+gboolean nm_platform_check_support_libnl_link_netnsid (void);
 gboolean nm_platform_check_support_kernel_extended_ifa_flags (NMPlatform *self);
 gboolean nm_platform_check_support_user_ipv6ll (NMPlatform *self);
 
diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c
index 51684b93..7fe2d7d3 100644
--- a/src/platform/nmp-object.c
+++ b/src/platform/nmp-object.c
@@ -18,10 +18,11 @@
  * Copyright (C) 2015 Red Hat, Inc.
  */
 
-#include "nmp-object.h"
+#include "config.h"
 
 #include <unistd.h>
 
+#include "nmp-object.h"
 #include "nm-platform-utils.h"
 #include "NetworkManagerUtils.h"
 #include "nm-utils.h"
@@ -29,38 +30,21 @@
 
 /*********************************************************************************************/
 
-#define _LOG_DOMAIN LOGD_PLATFORM
-
-#define _LOG(level, domain, obj, ...) \
+#define _NMLOG_DOMAIN LOGD_PLATFORM
+#define _NMLOG(level, obj, ...) \
     G_STMT_START { \
         const NMLogLevel __level = (level); \
-        const NMLogDomain __domain = (domain); \
         \
-        if (nm_logging_enabled (__level, __domain)) { \
+        if (nm_logging_enabled (__level, _NMLOG_DOMAIN)) { \
             const NMPObject *const __obj = (obj); \
             \
-            _nm_log (__level, __domain, 0, \
+            _nm_log (__level, _NMLOG_DOMAIN, 0, \
                      "nmp-object[%p/%s]: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
                      __obj, \
                      (__obj ? NMP_OBJECT_GET_CLASS (__obj)->obj_type_name : "???") \
                      _NM_UTILS_MACRO_REST (__VA_ARGS__)); \
         } \
     } G_STMT_END
-#define _LOG_LEVEL_ENABLED(level, domain) \
-    ( nm_logging_enabled ((level), (domain)) )
-
-#ifdef NM_MORE_LOGGING
-#define _LOGT_ENABLED()     _LOG_LEVEL_ENABLED (LOGL_TRACE, _LOG_DOMAIN)
-#define _LOGT(obj, ...)          _LOG (LOGL_TRACE, _LOG_DOMAIN, obj, __VA_ARGS__)
-#else
-#define _LOGT_ENABLED()     FALSE
-#define _LOGT(obj, ...)          G_STMT_START { if (FALSE) { _LOG (LOGL_TRACE, _LOG_DOMAIN, obj, __VA_ARGS__); } } G_STMT_END
-#endif
-
-#define _LOGD(obj, ...)      _LOG (LOGL_DEBUG, _LOG_DOMAIN, obj, __VA_ARGS__)
-#define _LOGI(obj, ...)      _LOG (LOGL_INFO , _LOG_DOMAIN, obj, __VA_ARGS__)
-#define _LOGW(obj, ...)      _LOG (LOGL_WARN , _LOG_DOMAIN, obj, __VA_ARGS__)
-#define _LOGE(obj, ...)      _LOG (LOGL_ERR  , _LOG_DOMAIN, obj, __VA_ARGS__)
 
 /*********************************************************************************************/
 
diff --git a/src/platform/tests/test-link.c b/src/platform/tests/test-link.c
index dc7b8397..ce371b75 100644
--- a/src/platform/tests/test-link.c
+++ b/src/platform/tests/test-link.c
@@ -231,10 +231,7 @@ test_slave (int master, int type, SignalData *master_changed)
 	g_assert (nm_platform_link_release (NM_PLATFORM_GET, master, ifindex));
 	g_assert_cmpint (nm_platform_link_get_master (NM_PLATFORM_GET, ifindex), ==, 0);
 	accept_signals (link_changed, 1, 3);
-	if (link_type != NM_LINK_TYPE_TEAM)
-		accept_signals (master_changed, 1, 2);
-	else
-		accept_signals (master_changed, 1, 1);
+	accept_signals (master_changed, 1, 2);
 
 	ensure_no_signal (master_changed);
 
diff --git a/src/platform/wifi/wifi-utils-wext.c b/src/platform/wifi/wifi-utils-wext.c
index 8428010a..470b73d1 100644
--- a/src/platform/wifi/wifi-utils-wext.c
+++ b/src/platform/wifi/wifi-utils-wext.c
@@ -34,6 +34,7 @@
 #include "wifi-utils-wext.h"
 #include "nm-logging.h"
 #include "nm-utils.h"
+#include "nm-platform-utils.h"
 
 /* Hacks necessary to #include wireless.h; yay for WEXT */
 #ifndef __user
@@ -670,6 +671,9 @@ wifi_wext_is_wifi (const char *iface)
 	struct iwreq iwr;
 	gboolean is_wifi = FALSE;
 
+	if (!nmp_utils_device_exists (iface))
+		return FALSE;
+
 	fd = socket (PF_INET, SOCK_DGRAM, 0);
 	if (fd >= 0) {
 		strncpy (iwr.ifr_ifrn.ifrn_name, iface, IFNAMSIZ);