summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2016-02-19 01:06:28 +0100
committerThomas Haller <thaller@redhat.com>2016-02-20 00:19:09 +0100
commit6635e54d613304c3b319d822d6ae1a93d016e968 (patch)
tree7d58a573cf408dff8a2a9a3c60ebc55fab940a4d
parent94d6aaac70ecec4baca6a072d32825287f8ee109 (diff)
downloadNetworkManager-th/platform-netns.tar.gz
platform: add network namespace support to platformth/platform-netns
Platform not only uses the netlink socket, but also sysfs, udev, ethtool, mii. To properly support network namespaces, we must switch the namespace as necessary. In case of udev, it is only supported on the main namespace.
-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);