summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Makefile.am4
-rw-r--r--src/nm-types.h3
-rw-r--r--src/platform/nm-linux-platform.c135
-rw-r--r--src/platform/nm-platform.c52
-rw-r--r--src/platform/nm-platform.h5
-rw-r--r--src/platform/nmp-netns-utils.c484
-rw-r--r--src/platform/nmp-netns-utils.h71
-rw-r--r--src/platform/nmp-object.c4
-rw-r--r--src/platform/nmp-object.h2
-rw-r--r--src/platform/tests/test-general.c4
-rw-r--r--src/platform/tests/test-link.c44
-rw-r--r--src/platform/tests/test-nmp-object.c4
12 files changed, 745 insertions, 67 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 1711b925c0..a180c1bc20 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -279,6 +279,8 @@ libNetworkManager_la_SOURCES = \
dnsmasq-manager/nm-dnsmasq-utils.c \
dnsmasq-manager/nm-dnsmasq-utils.h \
\
+ platform/nmp-netns-utils.c \
+ platform/nmp-netns-utils.h \
platform/nm-fake-platform.c \
platform/nm-fake-platform.h \
platform/nm-linux-platform.c \
@@ -504,6 +506,8 @@ libnm_iface_helper_la_SOURCES = \
platform/nm-platform.h \
platform/nm-platform-utils.c \
platform/nm-platform-utils.h \
+ platform/nmp-netns-utils.c \
+ platform/nmp-netns-utils.h \
platform/nmp-object.c \
platform/nmp-object.h \
platform/wifi/wifi-utils-nl80211.c \
diff --git a/src/nm-types.h b/src/nm-types.h
index 0fda9216a7..8f8e17908e 100644
--- a/src/nm-types.h
+++ b/src/nm-types.h
@@ -82,6 +82,9 @@ typedef struct _NMPlatformLink NMPlatformLink;
typedef struct _NMPObject NMPObject;
+typedef struct _NMPNetns NMPNetns;
+typedef struct _NMPNetnsManager NMPNetnsManager;
+
typedef enum {
/* Please don't interpret type numbers outside nm-platform and use functions
* like nm_platform_link_is_software() and nm_platform_supports_slaves().
diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c
index 1ebffd0007..696dd139a3 100644
--- a/src/platform/nm-linux-platform.c
+++ b/src/platform/nm-linux-platform.c
@@ -45,6 +45,7 @@
#include "nm-core-internal.h"
#include "NetworkManagerUtils.h"
#include "nm-linux-platform.h"
+#include "nmp-netns-utils.h"
#include "nm-platform-utils.h"
#include "NetworkManagerUtils.h"
#include "nm-utils.h"
@@ -222,6 +223,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);
/*****************************************************************************/
@@ -648,6 +650,8 @@ _linktype_get_type (NMPlatform *platform,
{
guint i;
+ _assert_netns_current (platform);
+
if (completed_from_cache) {
const NMPObject *obj;
@@ -2420,6 +2424,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 (nmp_netns_get_current () == nm_platform_netns_get (platform));
+#endif
+}
+
+static void
_log_dbg_sysctl_set_impl (NMPlatform *platform, const char *path, const char *value)
{
GError *error = NULL;
@@ -2452,6 +2466,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, len, nwrote, tries;
char *actual;
@@ -2464,6 +2479,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))
+ g_return_val_if_reached (FALSE);
+
fd = open (path, O_WRONLY | O_TRUNC);
if (fd == -1) {
if (errno == ENOENT) {
@@ -2572,6 +2590,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;
@@ -2581,6 +2600,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))
+ g_return_val_if_reached (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)
@@ -4129,11 +4151,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))
+ g_return_val_if_reached (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.
@@ -4144,6 +4170,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);
@@ -4152,6 +4179,9 @@ link_supports_vlans (NMPlatform *platform, int ifindex)
if (!obj || obj->link.arptype != ARPHRD_ETHER)
return FALSE;
+ if (!nm_platform_netns_push (platform, &netns))
+ g_return_val_if_reached (FALSE);
+
return nmp_utils_ethtool_supports_vlans (obj->link.name);
}
@@ -4190,6 +4220,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))
+ g_return_val_if_reached (FALSE);
+
return nmp_utils_ethtool_get_permanent_address (nm_platform_link_get_name (platform, ifindex), buf, length);
}
@@ -4971,15 +5006,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;
@@ -4988,90 +5027,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);
}
/******************************************************************/
@@ -5079,33 +5092,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);
}
@@ -5114,8 +5115,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))
+ g_return_val_if_reached (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) {
@@ -5136,6 +5141,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))
+ g_return_val_if_reached (FALSE);
+
return nmp_utils_ethtool_get_driver_info (nm_platform_link_get_name (platform, ifindex),
out_driver_name,
out_driver_version,
@@ -5716,6 +5726,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;
@@ -5728,6 +5739,9 @@ event_handler_read_netlink (NMPlatform *platform, gboolean wait_for_acks)
gint64 timeout_abs_ns;
} data_next;
+ if (!nm_platform_netns_push (platform, &netns))
+ g_return_val_if_reached (FALSE);
+
while (TRUE) {
while (TRUE) {
@@ -5932,11 +5946,14 @@ 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_get_initial () == nmp_netns_get_current ()) && nmp_cache_use_udev_detect ();
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));
diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c
index d82b096a2c..633b968126 100644
--- a/src/platform/nm-platform.c
+++ b/src/platform/nm-platform.c
@@ -37,6 +37,7 @@
#include "nm-platform.h"
#include "nm-platform-utils.h"
#include "nmp-object.h"
+#include "nmp-netns-utils.h"
#include "NetworkManagerUtils.h"
#include "nm-enum-types.h"
#include "nm-core-internal.h"
@@ -2127,6 +2128,10 @@ nm_platform_link_veth_get_properties (NMPlatform *self, int ifindex, int *out_pe
/* Pre-4.1 kernel did not expose the peer_ifindex as IFA_LINK. Lookup via ethtool. */
if (out_peer_ifindex) {
+ nm_auto_pop_netns NMPNetns *netns = NULL;
+
+ if (!nm_platform_netns_push (self, &netns))
+ return FALSE;
peer_ifindex = nmp_utils_ethtool_get_peer_ifindex (plink->name);
if (peer_ifindex <= 0)
return FALSE;
@@ -2372,16 +2377,24 @@ _to_string_dev (NMPlatform *self, int ifindex, char *buf, size_t size)
gboolean
nm_platform_ethtool_set_wake_on_lan (NMPlatform *self, const char *ifname, NMSettingWiredWakeOnLan wol, const char *wol_password)
{
+ nm_auto_pop_netns NMPNetns *netns = NULL;
_CHECK_SELF (self, klass, FALSE);
+ if (!nm_platform_netns_push (self, &netns))
+ return FALSE;
+
return nmp_utils_ethtool_set_wake_on_lan (ifname, wol, wol_password);
}
gboolean
nm_platform_ethtool_get_link_speed (NMPlatform *self, const char *ifname, guint32 *out_speed)
{
+ nm_auto_pop_netns NMPNetns *netns = NULL;
_CHECK_SELF (self, klass, FALSE);
+ if (!nm_platform_netns_push (self, &netns))
+ return FALSE;
+
return nmp_utils_ethtool_get_link_speed (ifname, out_speed);
}
@@ -3997,6 +4010,30 @@ log_ip6_route (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatform
/******************************************************************/
+NMPNetns *
+nm_platform_netns_get (NMPlatform *self)
+{
+ _CHECK_SELF (self, klass, NULL);
+
+ return self->_netns;
+}
+
+gboolean
+nm_platform_netns_push (NMPlatform *platform, NMPNetns **netns)
+{
+ g_return_val_if_fail (NM_IS_PLATFORM (platform), FALSE);
+ g_return_val_if_fail (!netns || !*netns, FALSE);
+
+ if ( platform->_netns
+ && !nmp_netns_push (platform->_netns))
+ return FALSE;
+
+ NM_SET_OUT (netns, platform->_netns);
+ return TRUE;
+}
+
+/******************************************************************/
+
static gboolean
_vtr_v4_route_add (NMPlatform *self, int ifindex, const NMPlatformIPXRoute *route, gint64 metric)
{
@@ -4124,6 +4161,20 @@ constructed (GObject *object)
static void
nm_platform_init (NMPlatform *object)
{
+ NMPlatform *self = NM_PLATFORM (object);
+ NMPNetns *netns;
+
+ netns = nmp_netns_get_current ();
+ if (netns)
+ self->_netns = nmp_netns_ref (netns);
+}
+
+static void
+finalize (GObject *object)
+{
+ NMPlatform *self = NM_PLATFORM (object);
+
+ nmp_netns_unref (self->_netns);
}
static void
@@ -4135,6 +4186,7 @@ nm_platform_class_init (NMPlatformClass *platform_class)
object_class->set_property = set_property;
object_class->constructed = constructed;
+ object_class->finalize = finalize;
platform_class->wifi_set_powersave = wifi_set_powersave;
diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h
index 151a33794c..3d1ab823c0 100644
--- a/src/platform/nm-platform.h
+++ b/src/platform/nm-platform.h
@@ -460,6 +460,8 @@ typedef struct {
struct _NMPlatform {
GObject parent;
+
+ NMPNetns *_netns;
};
typedef struct {
@@ -666,6 +668,9 @@ _nm_platform_uint8_inv (guint8 scope)
return (guint8) ~scope;
}
+NMPNetns *nm_platform_netns_get (NMPlatform *self);
+gboolean nm_platform_netns_push (NMPlatform *platform, NMPNetns **netns);
+
const char *nm_link_type_to_string (NMLinkType link_type);
const char *_nm_platform_error_to_string (NMPlatformError error);
diff --git a/src/platform/nmp-netns-utils.c b/src/platform/nmp-netns-utils.c
new file mode 100644
index 0000000000..1a2243bb2c
--- /dev/null
+++ b/src/platform/nmp-netns-utils.c
@@ -0,0 +1,484 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* nm-platform.c - Handle runtime kernel networking configuration
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2016 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+#include "nmp-netns-utils.h"
+
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/mount.h>
+
+#include "NetworkManagerUtils.h"
+
+/*********************************************************************************************/
+
+#define _NMLOG_DOMAIN LOGD_PLATFORM
+#define _NMLOG_PREFIX_NAME "netns"
+#define _NMLOG(level, netns, ...) \
+ G_STMT_START { \
+ NMLogLevel _level = (level); \
+ \
+ if (nm_logging_enabled (_level, _NMLOG_DOMAIN)) { \
+ NMPNetns *_netns = (netns); \
+ char _sbuf[20]; \
+ \
+ _nm_log (_level, _NMLOG_DOMAIN, 0, \
+ "%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
+ _NMLOG_PREFIX_NAME, \
+ (_netns ? nm_sprintf_buf (_sbuf, "[%p]", _netns) : "") \
+ _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
+ } \
+ } G_STMT_END
+
+/*********************************************************************************************/
+
+NM_GOBJECT_PROPERTIES_DEFINE_BASE (
+ PROP_FD_NET,
+ PROP_FD_MNT,
+);
+
+typedef struct _NMPNetnsPrivate NMPNetnsPrivate;
+
+struct _NMPNetnsPrivate {
+ int fd_net;
+ int fd_mnt;
+};
+
+typedef struct {
+ NMPNetns *netns;
+ int count;
+} NetnsInfo;
+
+static void _stack_push (NMPNetns *netns);
+static NMPNetns *_netns_new (GError **error);
+
+/*********************************************************************************************/
+
+static GArray *netns_stack = NULL;
+
+static void
+_stack_ensure_init (void)
+{
+ if (G_UNLIKELY (!netns_stack)) {
+ NMPNetns *netns;
+ GError *error = NULL;
+
+ netns_stack = g_array_new (FALSE, FALSE, sizeof (NetnsInfo));
+
+ /* at the bottom of the stack we must try to create a netns instance
+ * that we never pop. It's the base to which we need to return. */
+
+ netns = _netns_new (&error);
+
+ if (!netns) {
+ /* don't know how to recover from this error. Netns are not supported. */
+ _LOGE (NULL, "failed to create initial netns: %s", error->message);
+ g_clear_error (&error);
+ return;
+ }
+
+ _stack_push (netns);
+
+ /* we leak this instance. */
+ nmp_netns_unref (netns);
+ }
+}
+
+static NetnsInfo *
+_stack_peek (void)
+{
+ nm_assert (netns_stack);
+
+ if (netns_stack->len > 0)
+ return &g_array_index (netns_stack, NetnsInfo, (netns_stack->len - 1));
+ return NULL;
+}
+
+static NetnsInfo *
+_stack_peek2 (void)
+{
+ nm_assert (netns_stack);
+
+ if (netns_stack->len > 1)
+ return &g_array_index (netns_stack, NetnsInfo, (netns_stack->len - 2));
+ return NULL;
+}
+
+static NetnsInfo *
+_stack_bottom (void)
+{
+ nm_assert (netns_stack);
+
+ if (netns_stack->len > 0)
+ return &g_array_index (netns_stack, NetnsInfo, 0);
+ return NULL;
+}
+
+static void
+_stack_push (NMPNetns *netns)
+{
+ NetnsInfo *info;
+
+ nm_assert (netns_stack);
+ nm_assert (NMP_IS_NETNS (netns));
+
+ g_array_set_size (netns_stack, netns_stack->len + 1);
+
+ info = &g_array_index (netns_stack, NetnsInfo, (netns_stack->len - 1));
+ info->netns = nmp_netns_ref (netns);
+ info->count = 1;
+}
+
+static void
+_stack_pop (void)
+{
+ NetnsInfo *info;
+
+ nm_assert (netns_stack);
+ nm_assert (netns_stack->len > 1);
+
+ info = &g_array_index (netns_stack, NetnsInfo, (netns_stack->len - 1));
+
+ nm_assert (NMP_IS_NETNS (info->netns));
+ nm_assert (info->count == 1);
+
+ nmp_netns_unref (info->netns);
+
+ g_array_set_size (netns_stack, netns_stack->len - 1);
+}
+
+static guint
+_stack_size (void)
+{
+ nm_assert (netns_stack);
+
+ return netns_stack->len;
+}
+
+/*********************************************************************************************/
+
+G_DEFINE_TYPE (NMPNetns, nmp_netns, G_TYPE_OBJECT);
+
+#define NMP_NETNS_GET_PRIVATE(o) ((o)->priv)
+
+/*********************************************************************************************/
+
+NMPNetns *
+nmp_netns_ref (NMPNetns *self)
+{
+ g_return_val_if_fail (NMP_IS_NETNS (self), NULL);
+
+ g_object_ref (self);
+ return self;
+}
+
+NMPNetns *
+nmp_netns_unref (NMPNetns *self)
+{
+ if (self) {
+ g_return_val_if_fail (NMP_IS_NETNS (self), NULL);
+
+ g_object_unref (self);
+ }
+ return NULL;
+}
+
+/*********************************************************************************************/
+
+static NMPNetns *
+_netns_new (GError **error)
+{
+ NMPNetns *self;
+ int fd_net, fd_mnt;
+ int errsv;
+
+ fd_net = open ("/proc/self/ns/net", O_RDONLY);
+ if (fd_net == -1) {
+ errsv = errno;
+ g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
+ "Failed opening netns: %s",
+ g_strerror (errsv));
+ return NULL;
+ }
+
+ fd_mnt = open ("/proc/self/ns/mnt", O_RDONLY);
+ if (fd_mnt == -1) {
+ errsv = errno;
+ g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
+ "Failed opening mntns: %s",
+ g_strerror (errsv));
+ close (fd_net);
+ return NULL;
+ }
+
+ self = g_object_new (NMP_TYPE_NETNS,
+ NMP_NETNS_FD_NET, fd_net,
+ NMP_NETNS_FD_MNT, fd_mnt,
+ NULL);
+
+ _LOGD (self, "new netns (net:%d, mnt:%d)", fd_net, fd_mnt);
+
+ return self;
+}
+
+static gboolean
+_netns_switch (NMPNetns *self, NMPNetns *netns_fail)
+{
+ int errsv;
+
+ if (setns (self->priv->fd_net, CLONE_NEWNET) != 0) {
+ errsv = errno;
+ _LOGE (self, "failed to switch netns: %s", g_strerror (errsv));
+ return FALSE;
+ }
+ /* try to fix the mess by returning to the previous netns. */
+ if (setns (self->priv->fd_mnt, CLONE_NEWNS) != 0) {
+ errsv = errno;
+ _LOGE (self, "failed to switch mntns: %s", g_strerror (errsv));
+
+ if (netns_fail) {
+ if (setns (netns_fail->priv->fd_net, CLONE_NEWNET) != 0) {
+ errsv = errno;
+ _LOGE (netns_fail, "failed to restore netns: %s", g_strerror (errsv));
+ }
+ }
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*********************************************************************************************/
+
+gboolean
+nmp_netns_push (NMPNetns *self)
+{
+ NetnsInfo *info;
+
+ g_return_val_if_fail (NMP_IS_NETNS (self), FALSE);
+
+ _stack_ensure_init ();
+
+ info = _stack_peek ();
+ g_return_val_if_fail (info, FALSE);
+
+ if (info->netns == self) {
+ info->count++;
+ _LOGt (self, "push (increase count to %d)", info->count);
+ return TRUE;
+ }
+
+ _LOGD (self, "push (was %p)", info->netns);
+
+ if (!_netns_switch (self, info->netns))
+ return FALSE;
+
+ _stack_push (self);
+ return TRUE;
+}
+
+NMPNetns *
+nmp_netns_new (void)
+{
+ NetnsInfo *info;
+ NMPNetns *self;
+ int errsv;
+ GError *error = NULL;
+
+ _stack_ensure_init ();
+
+ if (!_stack_peek ()) {
+ /* there are no netns instances. We cannot create a new one
+ * (because after unshare we couldn't return to the original one). */
+ return NULL;
+ }
+
+ if (unshare (CLONE_NEWNET) != 0) {
+ errsv = errno;
+ _LOGE (NULL, "failed to create netns: %s", g_strerror (errsv));
+ return NULL;
+ }
+
+ if (unshare (CLONE_NEWNS) != 0) {
+ errsv = errno;
+ _LOGE (NULL, "failed to create ns: %s", g_strerror (errsv));
+ goto err_out;
+ }
+
+ if (mount ("", "/", "none", MS_SLAVE | MS_REC, NULL)) {
+ _LOGE (NULL, "failed mount --make-rslave: %s", error->message);
+ goto err_out;
+ }
+
+ if (umount2 ("/sys", MNT_DETACH) < 0) {
+ _LOGE (NULL, "failed umount /sys: %s", error->message);
+ goto err_out;
+ }
+
+ if (mount (NULL, "/sys", "sysfs", 0, NULL) < 0) {
+ _LOGE (NULL, "failed mount /sys: %s", error->message);
+ goto err_out;
+ }
+
+ self = _netns_new (&error);
+ if (!self) {
+ _LOGE (NULL, "failed to create netns after unshare: %s", error->message);
+ g_clear_error (&error);
+ goto err_out;
+ }
+
+ _stack_push (self);
+
+ return self;
+err_out:
+ info = _stack_peek ();
+ _netns_switch (info->netns, NULL);
+ return NULL;
+}
+
+gboolean
+nmp_netns_pop (NMPNetns *self)
+{
+ NetnsInfo *info;
+
+ g_return_val_if_fail (NMP_IS_NETNS (self), FALSE);
+
+ _stack_ensure_init ();
+
+ info = _stack_peek ();
+
+ g_return_val_if_fail (info, FALSE);
+ g_return_val_if_fail (info->netns == self, FALSE);
+
+ if (info->count > 1) {
+ info->count--;
+ _LOGt (self, "pop (decrease count to %d)", info->count);
+ return TRUE;
+ }
+ g_return_val_if_fail (info->count == 1, FALSE);
+
+ /* cannot pop the original netns. */
+ g_return_val_if_fail (_stack_size () > 1, FALSE);
+
+ _LOGD (self, "pop (restore %p)", _stack_peek2 ());
+
+ _stack_pop ();
+ info = _stack_peek ();
+
+ nm_assert (info);
+
+ return _netns_switch (info->netns, NULL);
+}
+
+NMPNetns *
+nmp_netns_get_current (void)
+{
+ NetnsInfo *info;
+
+ _stack_ensure_init ();
+
+ info = _stack_peek ();
+ return info ? info->netns : NULL;
+}
+
+NMPNetns *
+nmp_netns_get_initial (void)
+{
+ NetnsInfo *info;
+
+ _stack_ensure_init ();
+
+ info = _stack_bottom ();
+ return info ? info->netns : NULL;
+}
+
+/*********************************************************************************************/
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NMPNetns *self = NMP_NETNS (object);
+
+ switch (prop_id) {
+ case PROP_FD_NET:
+ /* construct only */
+ self->priv->fd_net = g_value_get_int (value);
+ g_return_if_fail (self->priv->fd_net > 0);
+ break;
+ case PROP_FD_MNT:
+ /* construct only */
+ self->priv->fd_mnt = g_value_get_int (value);
+ g_return_if_fail (self->priv->fd_mnt > 0);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmp_netns_init (NMPNetns *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NMP_TYPE_NETNS, NMPNetnsPrivate);
+}
+
+static void
+dispose (GObject *object)
+{
+ NMPNetns *self = NMP_NETNS (object);
+
+ if (self->priv->fd_net > 0) {
+ close (self->priv->fd_net);
+ self->priv->fd_net = 0;
+ }
+
+ if (self->priv->fd_mnt > 0) {
+ close (self->priv->fd_mnt);
+ self->priv->fd_mnt = 0;
+ }
+
+ G_OBJECT_CLASS (nmp_netns_parent_class)->dispose (object);
+}
+
+static void
+nmp_netns_class_init (NMPNetnsClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (NMPNetnsPrivate));
+
+ object_class->set_property = set_property;
+ object_class->dispose = dispose;
+
+ obj_properties[PROP_FD_NET]
+ = g_param_spec_int (NMP_NETNS_FD_NET, "", "",
+ 0, G_MAXINT, 0,
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+ obj_properties[PROP_FD_MNT]
+ = g_param_spec_int (NMP_NETNS_FD_MNT, "", "",
+ 0, G_MAXINT, 0,
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+ g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+}
diff --git a/src/platform/nmp-netns-utils.h b/src/platform/nmp-netns-utils.h
new file mode 100644
index 0000000000..b1c5fefded
--- /dev/null
+++ b/src/platform/nmp-netns-utils.h
@@ -0,0 +1,71 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* nm-platform.c - Handle runtime kernel networking configuration
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2016 Red Hat, Inc.
+ */
+
+#ifndef __NMP_NETNS_UTILS_H__
+#define __NMP_NETNS_UTILS_H__
+
+/*****************************************************************************/
+
+#define NMP_TYPE_NETNS (nmp_netns_get_type ())
+#define NMP_NETNS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMP_TYPE_NETNS, NMPNetns))
+#define NMP_NETNS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMP_TYPE_NETNS, NMPNetnsClass))
+#define NMP_IS_NETNS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMP_TYPE_NETNS))
+#define NMP_IS_NETNS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMP_TYPE_NETNS))
+#define NMP_NETNS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMP_TYPE_NETNS, NMPNetnsClass))
+
+#define NMP_NETNS_FD_NET "fd-net"
+#define NMP_NETNS_FD_MNT "fd-mnt"
+
+struct _NMPNetnsPrivate;
+
+struct _NMPNetns {
+ GObject parent;
+ struct _NMPNetnsPrivate *priv;
+};
+
+typedef struct {
+ GObjectClass parent;
+} NMPNetnsClass;
+
+GType nmp_netns_get_type (void);
+
+NMPNetns *nmp_netns_new (void);
+
+NMPNetns *nmp_netns_ref (NMPNetns *netns);
+NMPNetns *nmp_netns_unref (NMPNetns *netns);
+
+gboolean nmp_netns_push (NMPNetns *netns);
+gboolean nmp_netns_pop (NMPNetns *netns);
+
+NMPNetns *nmp_netns_get_current (void);
+NMPNetns *nmp_netns_get_initial (void);
+
+static inline void
+_nm_auto_pop_netns (void *p)
+{
+ NMPNetns *netns = *((NMPNetns **) p);
+
+ if (netns)
+ nmp_netns_pop (netns);
+}
+
+#define nm_auto_pop_netns __attribute__((cleanup(_nm_auto_pop_netns)))
+
+#endif /* __NMP_NETNS_UTILS_H__ */
diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c
index aa5d918c98..7c5b680d26 100644
--- a/src/platform/nmp-object.c
+++ b/src/platform/nmp-object.c
@@ -1856,7 +1856,7 @@ nmp_cache_update_link_master_connected (NMPCache *cache, int ifindex, NMPObject
/******************************************************************/
NMPCache *
-nmp_cache_new ()
+nmp_cache_new (gboolean use_udev)
{
NMPCache *cache = g_new (NMPCache, 1);
@@ -1868,7 +1868,7 @@ nmp_cache_new ()
(NMMultiIndexFuncEqual) nmp_cache_id_equal,
(NMMultiIndexFuncClone) nmp_cache_id_clone,
(NMMultiIndexFuncDestroy) nmp_cache_id_destroy);
- cache->use_udev = nmp_cache_use_udev_detect ();
+ cache->use_udev = !!use_udev;
return cache;
}
diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h
index 7758798f33..09ecd74729 100644
--- a/src/platform/nmp-object.h
+++ b/src/platform/nmp-object.h
@@ -411,7 +411,7 @@ NMPCacheOpsType nmp_cache_update_netlink (NMPCache *cache, NMPObject *obj, NMPOb
NMPCacheOpsType nmp_cache_update_link_udev (NMPCache *cache, int ifindex, GUdevDevice *udev_device, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data);
NMPCacheOpsType nmp_cache_update_link_master_connected (NMPCache *cache, int ifindex, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data);
-NMPCache *nmp_cache_new (void);
+NMPCache *nmp_cache_new (gboolean use_udev);
void nmp_cache_free (NMPCache *cache);
#endif /* __NMP_OBJECT_H__ */
diff --git a/src/platform/tests/test-general.c b/src/platform/tests/test-general.c
index f342e5653e..66f72fa570 100644
--- a/src/platform/tests/test-general.c
+++ b/src/platform/tests/test-general.c
@@ -18,12 +18,12 @@
* Copyright (C) 2015 Red Hat, Inc.
*/
-#include "nm-platform-utils.h"
+#include "nm-default.h"
#include <linux/rtnetlink.h>
+#include "nm-platform-utils.h"
#include "nm-linux-platform.h"
-#include "nm-default.h"
#include "nm-test-utils.h"
diff --git a/src/platform/tests/test-link.c b/src/platform/tests/test-link.c
index 3fa5ad5001..cc9a832a61 100644
--- a/src/platform/tests/test-link.c
+++ b/src/platform/tests/test-link.c
@@ -23,6 +23,8 @@
#include <sched.h>
#include "nmp-object.h"
+#include "nmp-netns-utils.h"
+#include "nm-platform-utils.h"
#include "test-common.h"
#include "nm-test-utils.h"
@@ -1846,6 +1848,46 @@ again:
nmtstp_link_del (-1, ifindex_dummy0, IFACE_DUMMY0);
}
+/******************************************************************/
+
+static void
+test_netns_new (void)
+{
+ gs_unref_object NMPlatform *platform_1 = NULL;
+ gs_unref_object NMPlatform *platform_2 = NULL;
+ gs_unref_object NMPNetns *netns_2 = NULL;
+ char sbuf[100];
+
+ platform_1 = g_object_new (NM_TYPE_LINUX_PLATFORM, NULL);
+
+ netns_2 = nmp_netns_new ();
+ platform_2 = g_object_new (NM_TYPE_LINUX_PLATFORM, NULL);
+ nmp_netns_pop (netns_2);
+
+ g_assert_cmpint (nm_platform_link_dummy_add (platform_1, "dummy1_", NULL), ==, NM_PLATFORM_ERROR_SUCCESS);
+ g_assert_cmpint (nm_platform_link_dummy_add (platform_1, "dummy2a", NULL), ==, NM_PLATFORM_ERROR_SUCCESS);
+ g_assert_cmpint (nm_platform_link_dummy_add (platform_2, "dummy1_", NULL), ==, NM_PLATFORM_ERROR_SUCCESS);
+ g_assert_cmpint (nm_platform_link_dummy_add (platform_2, "dummy2b", NULL), ==, NM_PLATFORM_ERROR_SUCCESS);
+
+ g_assert_cmpstr (nm_platform_sysctl_get (platform_1, "/sys/devices/virtual/net/dummy1_/ifindex"), ==, nm_sprintf_buf (sbuf, "%d", nm_platform_link_get_by_ifname (platform_1, "dummy1_")->ifindex));
+ g_assert_cmpstr (nm_platform_sysctl_get (platform_1, "/sys/devices/virtual/net/dummy2a/ifindex"), ==, nm_sprintf_buf (sbuf, "%d", nm_platform_link_get_by_ifname (platform_1, "dummy2a")->ifindex));
+ g_assert_cmpstr (nm_platform_sysctl_get (platform_1, "/sys/devices/virtual/net/dummy2b/ifindex"), ==, NULL);
+
+ g_assert_cmpstr (nm_platform_sysctl_get (platform_2, "/sys/devices/virtual/net/dummy1_/ifindex"), ==, nm_sprintf_buf (sbuf, "%d", nm_platform_link_get_by_ifname (platform_2, "dummy1_")->ifindex));
+ g_assert_cmpstr (nm_platform_sysctl_get (platform_2, "/sys/devices/virtual/net/dummy2a/ifindex"), ==, NULL);
+ g_assert_cmpstr (nm_platform_sysctl_get (platform_2, "/sys/devices/virtual/net/dummy2b/ifindex"), ==, nm_sprintf_buf (sbuf, "%d", nm_platform_link_get_by_ifname (platform_2, "dummy2b")->ifindex));
+
+ g_assert ( nmp_utils_ethtool_get_driver_info ("dummy1_", NULL, NULL, NULL));
+ g_assert ( nmp_utils_ethtool_get_driver_info ("dummy2a", NULL, NULL, NULL));
+ g_assert (!nmp_utils_ethtool_get_driver_info ("dummy2b", NULL, NULL, NULL));
+
+ g_assert (nm_platform_netns_push (platform_2, NULL));
+
+ g_assert ( nmp_utils_ethtool_get_driver_info ("dummy1_", NULL, NULL, NULL));
+ g_assert (!nmp_utils_ethtool_get_driver_info ("dummy2a", NULL, NULL, NULL));
+ g_assert ( nmp_utils_ethtool_get_driver_info ("dummy2b", NULL, NULL, NULL));
+}
+
/*****************************************************************************/
void
@@ -1894,5 +1936,7 @@ setup_tests (void)
g_test_add_func ("/link/nl-bugs/veth", test_nl_bugs_veth);
g_test_add_func ("/link/nl-bugs/spurious-newlink", test_nl_bugs_spuroius_newlink);
g_test_add_func ("/link/nl-bugs/spurious-dellink", test_nl_bugs_spuroius_dellink);
+
+ g_test_add_func ("/general/netns/new", test_netns_new);
}
}
diff --git a/src/platform/tests/test-nmp-object.c b/src/platform/tests/test-nmp-object.c
index 3b44abb8d6..d77170b364 100644
--- a/src/platform/tests/test-nmp-object.c
+++ b/src/platform/tests/test-nmp-object.c
@@ -223,9 +223,7 @@ test_cache_link (void)
GUdevDevice *udev_device_3 = g_list_nth_data (global.udev_devices, 0);
NMPCacheOpsType ops_type;
- cache = nmp_cache_new ();
-
- nmp_cache_use_udev_set (cache, g_rand_int_range (nmtst_get_rand (), 0, 2));
+ cache = nmp_cache_new (nmtst_get_rand_int () % 2);
/* if we have a link, and don't set is_in_netlink, adding it has no effect. */
obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2);