mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager
synced 2024-10-15 20:45:32 +00:00
platform: add network namespace support to platform
Add a new NMPNetns class. This allows creation, deletion and switching of network namespaces. The API only offers push/pop operations to switch the namespace. This way the API enforces the user to always restore the previous namespace. A NMPlatform instance not only uses the netlink socket, but also sysfs, udev, ethtool, mii. Still, a NMPlatform instance lives entirely inside one namespace and is not spanning multiple namespaces. To properly support network namespaces, the platform instance must switch the namespace as necessary, transparent to the caller. Udev is only supported in the main namespace. For now, network namespaces are not actually used and are disabled via the NM_PLATFORM_NETNS_SUPPORT argument. https://bugzilla.gnome.org/show_bug.cgi?id=762408
This commit is contained in:
parent
1a1c5fb717
commit
c7b3862503
|
@ -311,6 +311,8 @@ libNetworkManager_la_SOURCES = \
|
|||
dnsmasq-manager/nm-dnsmasq-utils.c \
|
||||
dnsmasq-manager/nm-dnsmasq-utils.h \
|
||||
\
|
||||
platform/nmp-netns.c \
|
||||
platform/nmp-netns.h \
|
||||
platform/nm-fake-platform.c \
|
||||
platform/nm-fake-platform.h \
|
||||
platform/nm-linux-platform.c \
|
||||
|
@ -541,6 +543,8 @@ libnm_iface_helper_la_SOURCES = \
|
|||
platform/nm-platform.h \
|
||||
platform/nm-platform-utils.c \
|
||||
platform/nm-platform-utils.h \
|
||||
platform/nmp-netns.c \
|
||||
platform/nmp-netns.h \
|
||||
platform/nmp-object.c \
|
||||
platform/nmp-object.h \
|
||||
platform/wifi/wifi-utils-nl80211.c \
|
||||
|
|
|
@ -79,7 +79,7 @@ typedef struct _NMPlatformIP4Route NMPlatformIP4Route;
|
|||
typedef struct _NMPlatformIP6Address NMPlatformIP6Address;
|
||||
typedef struct _NMPlatformIP6Route NMPlatformIP6Route;
|
||||
typedef struct _NMPlatformLink NMPlatformLink;
|
||||
|
||||
typedef struct _NMPNetns NMPNetns;
|
||||
typedef struct _NMPObject NMPObject;
|
||||
|
||||
typedef enum {
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
|
||||
#include "nm-core-utils.h"
|
||||
#include "nmp-object.h"
|
||||
#include "nmp-netns.h"
|
||||
#include "nm-platform-utils.h"
|
||||
#include "wifi/wifi-utils.h"
|
||||
#include "wifi/wifi-utils-wext.h"
|
||||
|
@ -221,6 +222,7 @@ static void do_request_all_no_delayed_actions (NMPlatform *platform, DelayedActi
|
|||
static void cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMPCacheOpsType ops_type, gpointer user_data);
|
||||
static void cache_prune_candidates_prune (NMPlatform *platform);
|
||||
static gboolean event_handler_read_netlink (NMPlatform *platform, gboolean wait_for_acks);
|
||||
static void _assert_netns_current (NMPlatform *platform);
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
|
@ -647,6 +649,8 @@ _linktype_get_type (NMPlatform *platform,
|
|||
{
|
||||
guint i;
|
||||
|
||||
_assert_netns_current (platform);
|
||||
|
||||
if (completed_from_cache) {
|
||||
const NMPObject *obj;
|
||||
|
||||
|
@ -2410,12 +2414,23 @@ void
|
|||
nm_linux_platform_setup (void)
|
||||
{
|
||||
g_object_new (NM_TYPE_LINUX_PLATFORM,
|
||||
NM_PLATFORM_NETNS_SUPPORT, FALSE,
|
||||
NM_PLATFORM_REGISTER_SINGLETON, TRUE,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/******************************************************************/
|
||||
|
||||
static void
|
||||
_assert_netns_current (NMPlatform *platform)
|
||||
{
|
||||
#if NM_MORE_ASSERTS
|
||||
nm_assert (NM_IS_LINUX_PLATFORM (platform));
|
||||
|
||||
nm_assert (NM_IN_SET (nm_platform_netns_get (platform), NULL, nmp_netns_get_current ()));
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
_log_dbg_sysctl_set_impl (NMPlatform *platform, const char *path, const char *value)
|
||||
{
|
||||
|
@ -2449,6 +2464,7 @@ _log_dbg_sysctl_set_impl (NMPlatform *platform, const char *path, const char *va
|
|||
static gboolean
|
||||
sysctl_set (NMPlatform *platform, const char *path, const char *value)
|
||||
{
|
||||
nm_auto_pop_netns NMPNetns *netns = NULL;
|
||||
int fd, tries;
|
||||
gssize nwrote;
|
||||
gsize len;
|
||||
|
@ -2464,6 +2480,9 @@ sysctl_set (NMPlatform *platform, const char *path, const char *value)
|
|||
/* Don't write to suspicious locations */
|
||||
g_assert (!strstr (path, "/../"));
|
||||
|
||||
if (!nm_platform_netns_push (platform, &netns))
|
||||
return FALSE;
|
||||
|
||||
fd = open (path, O_WRONLY | O_TRUNC);
|
||||
if (fd == -1) {
|
||||
if (errno == ENOENT) {
|
||||
|
@ -2578,6 +2597,7 @@ _log_dbg_sysctl_get_impl (NMPlatform *platform, const char *path, const char *co
|
|||
static char *
|
||||
sysctl_get (NMPlatform *platform, const char *path)
|
||||
{
|
||||
nm_auto_pop_netns NMPNetns *netns = NULL;
|
||||
GError *error = NULL;
|
||||
char *contents;
|
||||
|
||||
|
@ -2587,6 +2607,9 @@ sysctl_get (NMPlatform *platform, const char *path)
|
|||
/* Don't write to suspicious locations */
|
||||
g_assert (!strstr (path, "/../"));
|
||||
|
||||
if (!nm_platform_netns_push (platform, &netns))
|
||||
return NULL;
|
||||
|
||||
if (!g_file_get_contents (path, &contents, NULL, &error)) {
|
||||
/* We assume FAILED means EOPNOTSUP */
|
||||
if ( g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)
|
||||
|
@ -2651,6 +2674,9 @@ do_emit_signal (NMPlatform *platform, const NMPObject *obj, NMPCacheOpsType cach
|
|||
nm_assert (!obj || cache_op == NMP_CACHE_OPS_REMOVED || obj == nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, obj));
|
||||
nm_assert (!obj || cache_op != NMP_CACHE_OPS_REMOVED || obj != nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, obj));
|
||||
|
||||
/* we raise the signals inside the namespace of the NMPlatform instance. */
|
||||
_assert_netns_current (platform);
|
||||
|
||||
switch (cache_op) {
|
||||
case NMP_CACHE_OPS_ADDED:
|
||||
if (!nmp_object_is_visible (obj))
|
||||
|
@ -4135,11 +4161,15 @@ link_set_user_ipv6ll_enabled (NMPlatform *platform, int ifindex, gboolean enable
|
|||
static gboolean
|
||||
link_supports_carrier_detect (NMPlatform *platform, int ifindex)
|
||||
{
|
||||
nm_auto_pop_netns NMPNetns *netns = NULL;
|
||||
const char *name = nm_platform_link_get_name (platform, ifindex);
|
||||
|
||||
if (!name)
|
||||
return FALSE;
|
||||
|
||||
if (!nm_platform_netns_push (platform, &netns))
|
||||
return FALSE;
|
||||
|
||||
/* We use netlink for the actual carrier detection, but netlink can't tell
|
||||
* us whether the device actually supports carrier detection in the first
|
||||
* place. We assume any device that does implements one of these two APIs.
|
||||
|
@ -4150,6 +4180,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);
|
||||
|
@ -4158,6 +4189,9 @@ link_supports_vlans (NMPlatform *platform, int ifindex)
|
|||
if (!obj || obj->link.arptype != ARPHRD_ETHER)
|
||||
return FALSE;
|
||||
|
||||
if (!nm_platform_netns_push (platform, &netns))
|
||||
return FALSE;
|
||||
|
||||
return nmp_utils_ethtool_supports_vlans (obj->link.name);
|
||||
}
|
||||
|
||||
|
@ -4196,6 +4230,11 @@ link_get_permanent_address (NMPlatform *platform,
|
|||
guint8 *buf,
|
||||
size_t *length)
|
||||
{
|
||||
nm_auto_pop_netns NMPNetns *netns = NULL;
|
||||
|
||||
if (!nm_platform_netns_push (platform, &netns))
|
||||
return FALSE;
|
||||
|
||||
return nmp_utils_ethtool_get_permanent_address (nm_platform_link_get_name (platform, ifindex), buf, length);
|
||||
}
|
||||
|
||||
|
@ -4975,15 +5014,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;
|
||||
|
@ -4992,90 +5035,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);
|
||||
}
|
||||
|
||||
/******************************************************************/
|
||||
|
@ -5083,33 +5100,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);
|
||||
}
|
||||
|
||||
|
@ -5118,8 +5123,12 @@ mesh_set_ssid (NMPlatform *platform, int ifindex, const guint8 *ssid, gsize len)
|
|||
static gboolean
|
||||
link_get_wake_on_lan (NMPlatform *platform, int ifindex)
|
||||
{
|
||||
nm_auto_pop_netns NMPNetns *netns = NULL;
|
||||
NMLinkType type = nm_platform_link_get_type (platform, ifindex);
|
||||
|
||||
if (!nm_platform_netns_push (platform, &netns))
|
||||
return FALSE;
|
||||
|
||||
if (type == NM_LINK_TYPE_ETHERNET)
|
||||
return nmp_utils_ethtool_get_wake_on_lan (nm_platform_link_get_name (platform, ifindex));
|
||||
else if (type == NM_LINK_TYPE_WIFI) {
|
||||
|
@ -5140,6 +5149,11 @@ link_get_driver_info (NMPlatform *platform,
|
|||
char **out_driver_version,
|
||||
char **out_fw_version)
|
||||
{
|
||||
nm_auto_pop_netns NMPNetns *netns = NULL;
|
||||
|
||||
if (!nm_platform_netns_push (platform, &netns))
|
||||
return FALSE;
|
||||
|
||||
return nmp_utils_ethtool_get_driver_info (nm_platform_link_get_name (platform, ifindex),
|
||||
out_driver_name,
|
||||
out_driver_version,
|
||||
|
@ -5721,6 +5735,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;
|
||||
|
@ -5733,6 +5748,9 @@ event_handler_read_netlink (NMPlatform *platform, gboolean wait_for_acks)
|
|||
gint64 timeout_abs_ns;
|
||||
} data_next;
|
||||
|
||||
if (!nm_platform_netns_push (platform, &netns))
|
||||
return FALSE;
|
||||
|
||||
while (TRUE) {
|
||||
|
||||
while (TRUE) {
|
||||
|
@ -5837,7 +5855,14 @@ cache_update_link_udev (NMPlatform *platform, int ifindex, GUdevDevice *udev_dev
|
|||
NMPCacheOpsType cache_op;
|
||||
|
||||
cache_op = nmp_cache_update_link_udev (priv->cache, ifindex, udev_device, &obj_cache, &was_visible, cache_pre_hook, platform);
|
||||
do_emit_signal (platform, obj_cache, cache_op, was_visible);
|
||||
|
||||
if (cache_op != NMP_CACHE_OPS_UNCHANGED) {
|
||||
nm_auto_pop_netns NMPNetns *netns = NULL;
|
||||
|
||||
if (!nm_platform_netns_push (platform, &netns))
|
||||
return;
|
||||
do_emit_signal (platform, obj_cache, cache_op, was_visible);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -5908,6 +5933,7 @@ handle_udev_event (GUdevClient *client,
|
|||
GUdevDevice *udev_device,
|
||||
gpointer user_data)
|
||||
{
|
||||
nm_auto_pop_netns NMPNetns *netns = NULL;
|
||||
NMPlatform *platform = NM_PLATFORM (user_data);
|
||||
const char *subsys;
|
||||
const char *ifindex;
|
||||
|
@ -5915,6 +5941,9 @@ handle_udev_event (GUdevClient *client,
|
|||
|
||||
g_return_if_fail (action != NULL);
|
||||
|
||||
if (!nm_platform_netns_push (platform, &netns))
|
||||
return;
|
||||
|
||||
/* A bit paranoid */
|
||||
subsys = g_udev_device_get_subsystem (udev_device);
|
||||
g_return_if_fail (!g_strcmp0 (subsys, "net"));
|
||||
|
@ -5939,7 +5968,8 @@ nm_linux_platform_init (NMLinuxPlatform *self)
|
|||
NMLinuxPlatformPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NM_TYPE_LINUX_PLATFORM, NMLinuxPlatformPrivate);
|
||||
gboolean use_udev;
|
||||
|
||||
use_udev = access ("/sys", W_OK) == 0;
|
||||
use_udev = nmp_netns_is_initial ()
|
||||
&& access ("/sys", W_OK) == 0;
|
||||
|
||||
self->priv = priv;
|
||||
|
||||
|
@ -5963,7 +5993,17 @@ constructed (GObject *_object)
|
|||
gboolean status;
|
||||
int nle;
|
||||
|
||||
_LOGD ("create (%s udev)",
|
||||
nm_assert (!platform->_netns || platform->_netns == nmp_netns_get_current ());
|
||||
|
||||
_LOGD ("create (%s netns, %s, %s udev)",
|
||||
!platform->_netns ? "ignore" : "use",
|
||||
!platform->_netns && nmp_netns_is_initial ()
|
||||
? "initial netns"
|
||||
: (!nmp_netns_get_current ()
|
||||
? "no netns support"
|
||||
: nm_sprintf_bufa (100, "in netns[%p]%s",
|
||||
nmp_netns_get_current (),
|
||||
nmp_netns_get_current () == nmp_netns_get_initial () ? "/main" : "")),
|
||||
nmp_cache_use_udev_get (priv->cache) ? "use" : "no");
|
||||
|
||||
priv->nlh = nl_socket_alloc ();
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include "nm-enum-types.h"
|
||||
#include "nm-platform-utils.h"
|
||||
#include "nmp-object.h"
|
||||
#include "nmp-netns.h"
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
|
@ -89,6 +90,7 @@ static guint signals[_NM_PLATFORM_SIGNAL_ID_LAST] = { 0 };
|
|||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_NETNS_SUPPORT,
|
||||
PROP_REGISTER_SINGLETON,
|
||||
LAST_PROP,
|
||||
};
|
||||
|
@ -2128,6 +2130,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;
|
||||
|
@ -2393,16 +2399,24 @@ _to_string_ifa_flags (guint32 ifa_flags, char *buf, gsize 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);
|
||||
}
|
||||
|
||||
|
@ -4018,6 +4032,31 @@ 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);
|
||||
|
||||
if ( platform->_netns
|
||||
&& !nmp_netns_push (platform->_netns)) {
|
||||
NM_SET_OUT (netns, NULL);
|
||||
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)
|
||||
{
|
||||
|
@ -4117,9 +4156,20 @@ static void
|
|||
set_property (GObject *object, guint prop_id,
|
||||
const GValue *value, GParamSpec *pspec)
|
||||
{
|
||||
NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE (object);
|
||||
NMPlatform *self = NM_PLATFORM (object);
|
||||
NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE (self);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_NETNS_SUPPORT:
|
||||
/* construct-only */
|
||||
if (g_value_get_boolean (value)) {
|
||||
NMPNetns *netns;
|
||||
|
||||
netns = nmp_netns_get_current ();
|
||||
if (netns)
|
||||
self->_netns = g_object_ref (netns);
|
||||
}
|
||||
break;
|
||||
case PROP_REGISTER_SINGLETON:
|
||||
/* construct-only */
|
||||
priv->register_singleton = g_value_get_boolean (value);
|
||||
|
@ -4147,6 +4197,14 @@ nm_platform_init (NMPlatform *object)
|
|||
{
|
||||
}
|
||||
|
||||
static void
|
||||
finalize (GObject *object)
|
||||
{
|
||||
NMPlatform *self = NM_PLATFORM (object);
|
||||
|
||||
g_clear_object (&self->_netns);
|
||||
}
|
||||
|
||||
static void
|
||||
nm_platform_class_init (NMPlatformClass *platform_class)
|
||||
{
|
||||
|
@ -4156,9 +4214,18 @@ 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;
|
||||
|
||||
g_object_class_install_property
|
||||
(object_class, PROP_NETNS_SUPPORT,
|
||||
g_param_spec_boolean (NM_PLATFORM_NETNS_SUPPORT, "", "",
|
||||
FALSE,
|
||||
G_PARAM_WRITABLE |
|
||||
G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property
|
||||
(object_class, PROP_REGISTER_SINGLETON,
|
||||
g_param_spec_boolean (NM_PLATFORM_REGISTER_SINGLETON, "", "",
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
|
||||
/******************************************************************/
|
||||
|
||||
#define NM_PLATFORM_NETNS_SUPPORT "netns-support"
|
||||
#define NM_PLATFORM_REGISTER_SINGLETON "register-singleton"
|
||||
|
||||
/******************************************************************/
|
||||
|
@ -462,6 +463,8 @@ typedef struct {
|
|||
|
||||
struct _NMPlatform {
|
||||
GObject parent;
|
||||
|
||||
NMPNetns *_netns;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
|
@ -669,6 +672,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);
|
||||
|
|
489
src/platform/nmp-netns.c
Normal file
489
src/platform/nmp-netns.c
Normal file
|
@ -0,0 +1,489 @@
|
|||
/* -*- 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.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_impl (void)
|
||||
{
|
||||
NMPNetns *netns;
|
||||
GError *error = NULL;
|
||||
|
||||
nm_assert (!netns_stack);
|
||||
|
||||
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 inside netns_stack. It cannot be popped. */
|
||||
g_object_unref (netns);
|
||||
}
|
||||
#define _stack_ensure_init() \
|
||||
G_STMT_START { \
|
||||
if (G_UNLIKELY (!netns_stack)) { \
|
||||
_stack_ensure_init_impl (); \
|
||||
} \
|
||||
} G_STMT_END
|
||||
|
||||
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 = g_object_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);
|
||||
|
||||
g_object_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)
|
||||
|
||||
/*********************************************************************************************/
|
||||
|
||||
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;
|
||||
}
|
||||
if (setns (self->priv->fd_mnt, CLONE_NEWNS) != 0) {
|
||||
errsv = errno;
|
||||
_LOGE (self, "failed to switch mntns: %s", g_strerror (errsv));
|
||||
|
||||
/* try to fix the mess by returning to the previous netns. */
|
||||
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;
|
||||
}
|
||||
|
||||
/*********************************************************************************************/
|
||||
|
||||
int
|
||||
nmp_netns_get_fd_net (NMPNetns *self)
|
||||
{
|
||||
g_return_val_if_fail (NMP_IS_NETNS (self), 0);
|
||||
|
||||
return self->priv->fd_net;
|
||||
}
|
||||
|
||||
int
|
||||
nmp_netns_get_fd_mnt (NMPNetns *self)
|
||||
{
|
||||
g_return_val_if_fail (NMP_IS_NETNS (self), 0);
|
||||
|
||||
return self->priv->fd_mnt;
|
||||
}
|
||||
|
||||
/*********************************************************************************************/
|
||||
|
||||
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 | CLONE_NEWNS) != 0) {
|
||||
errsv = errno;
|
||||
_LOGE (NULL, "failed to create new net and mnt namespace: %s", g_strerror (errsv));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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 ("sysfs", "/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;
|
||||
}
|
||||
|
||||
gboolean
|
||||
nmp_netns_is_initial (void)
|
||||
{
|
||||
if (G_UNLIKELY (!netns_stack))
|
||||
return TRUE;
|
||||
|
||||
return nmp_netns_get_current () == nmp_netns_get_initial ();
|
||||
}
|
||||
|
||||
/*********************************************************************************************/
|
||||
|
||||
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);
|
||||
}
|
70
src/platform/nmp-netns.h
Normal file
70
src/platform/nmp-netns.h
Normal file
|
@ -0,0 +1,70 @@
|
|||
/* -*- 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);
|
||||
|
||||
gboolean nmp_netns_push (NMPNetns *self);
|
||||
gboolean nmp_netns_pop (NMPNetns *self);
|
||||
|
||||
NMPNetns *nmp_netns_get_current (void);
|
||||
NMPNetns *nmp_netns_get_initial (void);
|
||||
gboolean nmp_netns_is_initial (void);
|
||||
|
||||
int nmp_netns_get_fd_net (NMPNetns *self);
|
||||
int nmp_netns_get_fd_mnt (NMPNetns *self);
|
||||
|
||||
static inline void
|
||||
_nm_auto_pop_netns (NMPNetns **p)
|
||||
{
|
||||
if (*p)
|
||||
nmp_netns_pop (*p);
|
||||
}
|
||||
|
||||
#define nm_auto_pop_netns __attribute__((cleanup(_nm_auto_pop_netns)))
|
||||
|
||||
#endif /* __NMP_NETNS_UTILS_H__ */
|
|
@ -23,6 +23,8 @@
|
|||
#include <sched.h>
|
||||
|
||||
#include "nmp-object.h"
|
||||
#include "nmp-netns.h"
|
||||
#include "nm-platform-utils.h"
|
||||
|
||||
#include "test-common.h"
|
||||
#include "nm-test-utils.h"
|
||||
|
@ -1846,6 +1848,138 @@ again:
|
|||
nmtstp_link_del (-1, ifindex_dummy0, IFACE_DUMMY0);
|
||||
}
|
||||
|
||||
/******************************************************************/
|
||||
|
||||
static void
|
||||
test_netns_general_setup (gpointer fixture, gconstpointer test_data)
|
||||
{
|
||||
/* the singleton platform instance has netns support disabled.
|
||||
* Destroy the instance before the test and re-create it afterwards. */
|
||||
g_object_unref (nm_platform_get ());
|
||||
}
|
||||
|
||||
static void
|
||||
test_netns_general_teardown (gpointer fixture, gconstpointer test_data)
|
||||
{
|
||||
/* re-create platform instance */
|
||||
SETUP ();
|
||||
}
|
||||
|
||||
static void
|
||||
test_netns_general (gpointer fixture, gconstpointer test_data)
|
||||
{
|
||||
gs_unref_object NMPlatform *platform_1 = NULL;
|
||||
gs_unref_object NMPlatform *platform_2 = NULL;
|
||||
gs_unref_object NMPNetns *netns_2 = NULL;
|
||||
NMPNetns *netns_tmp;
|
||||
char sbuf[100];
|
||||
int i, j, k, errsv;
|
||||
gboolean ethtool_support;
|
||||
|
||||
netns_tmp = nmp_netns_get_current ();
|
||||
if (!netns_tmp) {
|
||||
g_test_skip ("No netns support");
|
||||
return;
|
||||
}
|
||||
|
||||
g_assert (nmp_netns_get_fd_net (netns_tmp) > 0);
|
||||
if (setns (nmp_netns_get_fd_net (netns_tmp), CLONE_NEWNET) != 0) {
|
||||
errsv = errno;
|
||||
_LOGD ("setns() failed with \"%s\". This indicates missing support (valgrind?)", g_strerror (errsv));
|
||||
g_test_skip ("No netns support (setns failed)");
|
||||
return;
|
||||
}
|
||||
|
||||
platform_1 = g_object_new (NM_TYPE_LINUX_PLATFORM, NM_PLATFORM_NETNS_SUPPORT, TRUE, NULL);
|
||||
|
||||
netns_2 = nmp_netns_new ();
|
||||
platform_2 = g_object_new (NM_TYPE_LINUX_PLATFORM, NM_PLATFORM_NETNS_SUPPORT, TRUE, NULL);
|
||||
nmp_netns_pop (netns_2);
|
||||
|
||||
/* add some dummy devices. The "other-*" devices are there to bump the ifindex */
|
||||
for (k = 0; k < 2; k++) {
|
||||
NMPlatform *p = (k == 0 ? platform_1 : platform_2);
|
||||
const char *id = (k == 0 ? "a" : "b");
|
||||
|
||||
#define _ADD_DUMMY(platform, name) \
|
||||
g_assert_cmpint (nm_platform_link_dummy_add ((platform), (name), NULL), ==, NM_PLATFORM_ERROR_SUCCESS)
|
||||
|
||||
for (i = 0, j = nmtst_get_rand_int () % 5; i < j; i++)
|
||||
_ADD_DUMMY (p, nm_sprintf_buf (sbuf, "other-a-%s-%02d", id, i));
|
||||
|
||||
_ADD_DUMMY (p, "dummy1_");
|
||||
|
||||
for (i = 0, j = nmtst_get_rand_int () % 5; i < j; i++)
|
||||
_ADD_DUMMY (p, nm_sprintf_buf (sbuf, "other-b-%s-%02d", id, i));
|
||||
|
||||
_ADD_DUMMY (p, nm_sprintf_buf (sbuf, "dummy2%s", id));
|
||||
|
||||
for (i = 0, j = nmtst_get_rand_int () % 5; i < j; i++)
|
||||
_ADD_DUMMY (p, nm_sprintf_buf (sbuf, "other-c-%s-%02d", id, i));
|
||||
|
||||
#undef _ADD_DUMMY
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
NMPlatform *pl;
|
||||
const char *path;
|
||||
|
||||
j = nmtst_get_rand_int () % 2;
|
||||
|
||||
if (nmtst_get_rand_int () % 2) {
|
||||
pl = platform_1;
|
||||
if (nmtst_get_rand_int () % 2)
|
||||
path = "/proc/sys/net/ipv6/conf/dummy1_/disable_ipv6";
|
||||
else
|
||||
path = "/proc/sys/net/ipv6/conf/dummy2a/disable_ipv6";
|
||||
} else {
|
||||
pl = platform_2;
|
||||
if (nmtst_get_rand_int () % 2)
|
||||
path = "/proc/sys/net/ipv6/conf/dummy1_/disable_ipv6";
|
||||
else
|
||||
path = "/proc/sys/net/ipv6/conf/dummy2b/disable_ipv6";
|
||||
}
|
||||
g_assert (nm_platform_sysctl_set (pl, path, nm_sprintf_buf (sbuf, "%d", j)));
|
||||
g_assert_cmpstr (nm_platform_sysctl_get (pl, path), ==, nm_sprintf_buf (sbuf, "%d", j));
|
||||
}
|
||||
g_assert_cmpstr (nm_platform_sysctl_get (platform_1, "/proc/sys/net/ipv6/conf/dummy2b/disable_ipv6"), ==, NULL);
|
||||
g_assert_cmpstr (nm_platform_sysctl_get (platform_2, "/proc/sys/net/ipv6/conf/dummy2a/disable_ipv6"), ==, NULL);
|
||||
|
||||
/* older kernels (Ubuntu 12.04) don't support ethtool -i for dummy devices. Work around that and
|
||||
* skip asserts that are known to fail. */
|
||||
ethtool_support = nmtstp_run_command ("ethtool -i dummy1_ > /dev/null") == 0;
|
||||
if (ethtool_support) {
|
||||
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_cmpint (nmtstp_run_command ("ethtool -i dummy1_ > /dev/null"), ==, 0);
|
||||
g_assert_cmpint (nmtstp_run_command ("ethtool -i dummy2a > /dev/null"), ==, 0);
|
||||
g_assert_cmpint (nmtstp_run_command ("ethtool -i dummy2b 2> /dev/null"), !=, 0);
|
||||
}
|
||||
|
||||
g_assert (nm_platform_netns_push (platform_2, &netns_tmp));
|
||||
g_assert (netns_tmp == netns_2);
|
||||
|
||||
if (ethtool_support) {
|
||||
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_cmpint (nmtstp_run_command ("ethtool -i dummy1_ > /dev/null"), ==, 0);
|
||||
g_assert_cmpint (nmtstp_run_command ("ethtool -i dummy2a 2> /dev/null"), !=, 0);
|
||||
g_assert_cmpint (nmtstp_run_command ("ethtool -i dummy2b > /dev/null"), ==, 0);
|
||||
}
|
||||
|
||||
nmp_netns_pop (netns_tmp);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
void
|
||||
|
@ -1894,5 +2028,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_vtable ("/general/netns/general", 0, NULL, test_netns_general_setup, test_netns_general, test_netns_general_teardown);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -139,6 +139,14 @@ if [ $RESULT -ne 0 -a $RESULT -ne 77 ]; then
|
|||
exit $RESULT
|
||||
fi
|
||||
|
||||
if [ $HAS_ERRORS -eq 0 ]; then
|
||||
# valgrind doesn't support setns syscall and spams the logfile.
|
||||
# hack around it...
|
||||
if [ "$(basename "$TEST")" = 'test-link-linux' -a -z "$(sed -e '/^--[0-9]\+-- WARNING: unhandled .* syscall: /,/^--[0-9]\+-- it at http.*\.$/d' "$LOGFILE")" ]; then
|
||||
HAS_ERRORS=1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ $HAS_ERRORS -eq 0 ]; then
|
||||
# shouldn't actually happen...
|
||||
echo "valgrind succeeded, but log is not empty: '`realpath "$LOGFILE"`'" >&2
|
||||
|
|
Loading…
Reference in a new issue