1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 /* nm-platform.c - Handle runtime kernel networking configuration
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2, or (at your option)
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Copyright (C) 2016 Red Hat, Inc.
21 #include "nm-default.h"
22 #include "nmp-netns.h"
26 #include <sys/mount.h>
28 #include <sys/types.h>
30 #include "NetworkManagerUtils.h"
32 #define PROC_SELF_NS_MNT "/proc/self/ns/mnt"
33 #define PROC_SELF_NS_NET "/proc/self/ns/net"
35 #define _CLONE_NS_ALL ((int) (CLONE_NEWNS | CLONE_NEWNET))
36 #define _CLONE_NS_ALL_V CLONE_NEWNS , CLONE_NEWNET
38 NM_UTILS_FLAGS2STR_DEFINE_STATIC (_clone_ns_to_str, int,
39 NM_UTILS_FLAGS2STR (CLONE_NEWNS, "mnt"),
40 NM_UTILS_FLAGS2STR (CLONE_NEWNET, "net"),
44 __ns_types_to_str (int ns_types, int ns_types_already_set, char *buf, gsize len)
49 nm_utils_strbuf_append_c (&buf, &len, '[');
50 if (ns_types & ~ns_types_already_set) {
51 nm_utils_strbuf_append_str (&buf, &len,
52 _clone_ns_to_str (ns_types & ~ns_types_already_set, bb, sizeof (bb)));
54 if (ns_types & ns_types_already_set) {
55 if (ns_types & ~ns_types_already_set)
56 nm_utils_strbuf_append_c (&buf, &len, '/');
57 nm_utils_strbuf_append_str (&buf, &len,
58 _clone_ns_to_str (ns_types & ns_types_already_set, bb, sizeof (bb)));
60 nm_utils_strbuf_append_c (&buf, &len, ']');
63 #define _ns_types_to_str(ns_types, ns_types_already_set, buf) \
64 __ns_types_to_str (ns_types, ns_types_already_set, buf, sizeof (buf))
66 /*********************************************************************************************/
68 #define _NMLOG_DOMAIN LOGD_PLATFORM
69 #define _NMLOG_PREFIX_NAME "netns"
70 #define _NMLOG(level, netns, ...) \
72 NMLogLevel _level = (level); \
74 if (nm_logging_enabled (_level, _NMLOG_DOMAIN)) { \
75 NMPNetns *_netns = (netns); \
78 _nm_log (_level, _NMLOG_DOMAIN, 0, \
79 "%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
81 (_netns ? nm_sprintf_buf (_sbuf, "[%p]", _netns) : "") \
82 _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
86 /*********************************************************************************************/
88 NM_GOBJECT_PROPERTIES_DEFINE_BASE (
93 typedef struct _NMPNetnsPrivate NMPNetnsPrivate;
95 struct _NMPNetnsPrivate {
106 static void _stack_push (NMPNetns *netns, int ns_types);
107 static NMPNetns *_netns_new (GError **error);
109 /*********************************************************************************************/
111 static GArray *netns_stack = NULL;
114 _stack_ensure_init_impl (void)
117 GError *error = NULL;
119 nm_assert (!netns_stack);
121 netns_stack = g_array_new (FALSE, FALSE, sizeof (NetnsInfo));
123 /* at the bottom of the stack we must try to create a netns instance
124 * that we never pop. It's the base to which we need to return. */
126 netns = _netns_new (&error);
129 /* don't know how to recover from this error. Netns are not supported. */
130 _LOGE (NULL, "failed to create initial netns: %s", error->message);
131 g_clear_error (&error);
135 _stack_push (netns, _CLONE_NS_ALL);
137 /* we leak this instance inside netns_stack. It cannot be popped. */
138 g_object_unref (netns);
140 #define _stack_ensure_init() \
142 if (G_UNLIKELY (!netns_stack)) { \
143 _stack_ensure_init_impl (); \
148 _stack_current_netns (int ns_types)
152 nm_assert (netns_stack && netns_stack->len > 0);
154 /* we search the stack top-down to find the netns that has
155 * all @ns_types set. */
156 for (j = netns_stack->len; ns_types && j >= 1; ) {
159 info = &g_array_index (netns_stack, NetnsInfo, --j);
161 if (NM_FLAGS_ALL (info->ns_types, ns_types))
165 g_return_val_if_reached (NULL);
169 _stack_current_ns_types (NMPNetns *netns, int ns_types)
171 const int ns_types_check[] = { _CLONE_NS_ALL_V };
176 nm_assert (netns_stack && netns_stack->len > 0);
178 /* we search the stack top-down to check which of @ns_types
179 * are already set to @netns. */
180 for (j = netns_stack->len; ns_types && j >= 1; ) {
183 info = &g_array_index (netns_stack, NetnsInfo, --j);
184 if (info->netns != netns) {
185 ns_types = NM_FLAGS_UNSET (ns_types, info->ns_types);
189 for (i = 0; i < G_N_ELEMENTS (ns_types_check); i++) {
190 if ( NM_FLAGS_HAS (ns_types, ns_types_check[i])
191 && NM_FLAGS_HAS (info->ns_types, ns_types_check[i])) {
192 res = NM_FLAGS_SET (res, ns_types_check[i]);
193 ns_types = NM_FLAGS_UNSET (ns_types, ns_types_check[i]);
204 nm_assert (netns_stack);
206 if (netns_stack->len > 0)
207 return &g_array_index (netns_stack, NetnsInfo, (netns_stack->len - 1));
214 nm_assert (netns_stack);
216 if (netns_stack->len > 0)
217 return &g_array_index (netns_stack, NetnsInfo, 0);
222 _stack_push (NMPNetns *netns, int ns_types)
226 nm_assert (netns_stack);
227 nm_assert (NMP_IS_NETNS (netns));
228 nm_assert (NM_FLAGS_ANY (ns_types, _CLONE_NS_ALL));
229 nm_assert (!NM_FLAGS_ANY (ns_types, ~_CLONE_NS_ALL));
231 g_array_set_size (netns_stack, netns_stack->len + 1);
233 info = &g_array_index (netns_stack, NetnsInfo, (netns_stack->len - 1));
234 info->netns = g_object_ref (netns);
235 info->ns_types = ns_types;
244 nm_assert (netns_stack);
245 nm_assert (netns_stack->len > 1);
247 info = &g_array_index (netns_stack, NetnsInfo, (netns_stack->len - 1));
249 nm_assert (NMP_IS_NETNS (info->netns));
250 nm_assert (info->count == 1);
252 g_object_unref (info->netns);
254 g_array_set_size (netns_stack, netns_stack->len - 1);
260 nm_assert (netns_stack);
262 return netns_stack->len;
265 /*********************************************************************************************/
267 G_DEFINE_TYPE (NMPNetns, nmp_netns, G_TYPE_OBJECT);
269 #define NMP_NETNS_GET_PRIVATE(o) ((o)->priv)
271 /*********************************************************************************************/
274 _netns_new (GError **error)
280 fd_net = open (PROC_SELF_NS_NET, O_RDONLY);
283 g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
284 "Failed opening netns: %s",
289 fd_mnt = open (PROC_SELF_NS_MNT, O_RDONLY);
292 g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
293 "Failed opening mntns: %s",
299 self = g_object_new (NMP_TYPE_NETNS,
300 NMP_NETNS_FD_NET, fd_net,
301 NMP_NETNS_FD_MNT, fd_mnt,
304 _LOGD (self, "new netns (net:%d, mnt:%d)", fd_net, fd_mnt);
310 _setns (NMPNetns *self, int type)
315 nm_assert (NM_IN_SET (type, _CLONE_NS_ALL_V));
317 fd = (type == CLONE_NEWNET) ? self->priv->fd_net : self->priv->fd_mnt;
319 _LOGt (self, "set netns(%s, %d)", _ns_types_to_str (type, 0, buf), fd);
321 return setns (fd, type);
325 _netns_switch_push (NMPNetns *self, int ns_types)
329 if ( NM_FLAGS_HAS (ns_types, CLONE_NEWNET)
330 && !_stack_current_ns_types (self, CLONE_NEWNET)
331 && _setns (self, CLONE_NEWNET) != 0) {
333 _LOGE (self, "failed to switch netns: %s", g_strerror (errsv));
336 if ( NM_FLAGS_HAS (ns_types, CLONE_NEWNS)
337 && !_stack_current_ns_types (self, CLONE_NEWNS)
338 && _setns (self, CLONE_NEWNS) != 0) {
340 _LOGE (self, "failed to switch mntns: %s", g_strerror (errsv));
342 /* try to fix the mess by returning to the previous netns. */
343 if ( NM_FLAGS_HAS (ns_types, CLONE_NEWNET)
344 && !_stack_current_ns_types (self, CLONE_NEWNET)) {
345 self = _stack_current_netns (CLONE_NEWNET);
347 && _setns (self, CLONE_NEWNET) != 0) {
349 _LOGE (self, "failed to restore netns: %s", g_strerror (errsv));
359 _netns_switch_pop (NMPNetns *self, int ns_types)
365 if ( NM_FLAGS_HAS (ns_types, CLONE_NEWNET)
366 && (!self || !_stack_current_ns_types (self, CLONE_NEWNET))) {
367 current = _stack_current_netns (CLONE_NEWNET);
369 g_warn_if_reached ();
371 } else if (_setns (current, CLONE_NEWNET) != 0) {
373 _LOGE (self, "failed to switch netns: %s", g_strerror (errsv));
377 if ( NM_FLAGS_HAS (ns_types, CLONE_NEWNS)
378 && (!self || !_stack_current_ns_types (self, CLONE_NEWNS))) {
379 current = _stack_current_netns (CLONE_NEWNS);
381 g_warn_if_reached ();
383 } else if (_setns (current, CLONE_NEWNS) != 0) {
385 _LOGE (self, "failed to switch mntns: %s", g_strerror (errsv));
393 /*********************************************************************************************/
396 nmp_netns_get_fd_net (NMPNetns *self)
398 g_return_val_if_fail (NMP_IS_NETNS (self), 0);
400 return self->priv->fd_net;
404 nmp_netns_get_fd_mnt (NMPNetns *self)
406 g_return_val_if_fail (NMP_IS_NETNS (self), 0);
408 return self->priv->fd_mnt;
411 /*********************************************************************************************/
414 _nmp_netns_push_type (NMPNetns *self, int ns_types)
419 _stack_ensure_init ();
421 info = _stack_peek ();
422 g_return_val_if_fail (info, FALSE);
424 if (info->netns == self && info->ns_types == ns_types) {
426 _LOGt (self, "push#%u* %s (increase count to %d)",
428 _ns_types_to_str (ns_types, ns_types, sbuf), info->count);
432 _LOGD (self, "push#%u %s",
434 _ns_types_to_str (ns_types,
435 _stack_current_ns_types (self, ns_types),
438 if (!_netns_switch_push (self, ns_types))
441 _stack_push (self, ns_types);
446 nmp_netns_push (NMPNetns *self)
448 g_return_val_if_fail (NMP_IS_NETNS (self), FALSE);
450 return _nmp_netns_push_type (self, _CLONE_NS_ALL);
454 nmp_netns_push_type (NMPNetns *self, int ns_types)
456 g_return_val_if_fail (NMP_IS_NETNS (self), FALSE);
457 g_return_val_if_fail (!NM_FLAGS_ANY (ns_types, ~_CLONE_NS_ALL), FALSE);
459 return _nmp_netns_push_type (self, ns_types == 0 ? _CLONE_NS_ALL : ns_types);
467 GError *error = NULL;
469 _stack_ensure_init ();
471 if (!_stack_peek ()) {
472 /* there are no netns instances. We cannot create a new one
473 * (because after unshare we couldn't return to the original one). */
477 if (unshare (_CLONE_NS_ALL) != 0) {
479 _LOGE (NULL, "failed to create new net and mnt namespace: %s", g_strerror (errsv));
483 if (mount ("", "/", "none", MS_SLAVE | MS_REC, NULL) != 0) {
485 _LOGE (NULL, "failed mount --make-rslave: %s", g_strerror (errsv));
489 if (umount2 ("/sys", MNT_DETACH) != 0) {
491 _LOGE (NULL, "failed umount /sys: %s", g_strerror (errsv));
495 if (mount ("sysfs", "/sys", "sysfs", 0, NULL) != 0) {
497 _LOGE (NULL, "failed mount /sys: %s", g_strerror (errsv));
501 self = _netns_new (&error);
503 _LOGE (NULL, "failed to create netns after unshare: %s", error->message);
504 g_clear_error (&error);
508 _stack_push (self, _CLONE_NS_ALL);
512 _netns_switch_pop (NULL, _CLONE_NS_ALL);
517 nmp_netns_pop (NMPNetns *self)
522 g_return_val_if_fail (NMP_IS_NETNS (self), FALSE);
524 _stack_ensure_init ();
526 info = _stack_peek ();
528 g_return_val_if_fail (info, FALSE);
529 g_return_val_if_fail (info->netns == self, FALSE);
531 if (info->count > 1) {
533 _LOGt (self, "pop#%u* (decrease count to %d)",
534 _stack_size () - 1, info->count);
537 g_return_val_if_fail (info->count == 1, FALSE);
539 /* cannot pop the original netns. */
540 g_return_val_if_fail (_stack_size () > 1, FALSE);
542 _LOGD (self, "pop#%u", _stack_size () - 1);
544 ns_types = info->ns_types;
548 return _netns_switch_pop (self, ns_types);
552 nmp_netns_get_current (void)
556 _stack_ensure_init ();
558 info = _stack_peek ();
559 return info ? info->netns : NULL;
563 nmp_netns_get_initial (void)
567 _stack_ensure_init ();
569 info = _stack_bottom ();
570 return info ? info->netns : NULL;
574 nmp_netns_is_initial (void)
576 if (G_UNLIKELY (!netns_stack))
579 return nmp_netns_get_current () == nmp_netns_get_initial ();
582 /*********************************************************************************************/
585 nmp_netns_bind_to_path (NMPNetns *self, const char *filename, int *out_fd)
587 gs_free char *dirname = NULL;
590 nm_auto_pop_netns NMPNetns *netns_pop = NULL;
592 g_return_val_if_fail (NMP_IS_NETNS (self), FALSE);
593 g_return_val_if_fail (filename && filename[0] == '/', FALSE);
595 if (!nmp_netns_push_type (self, CLONE_NEWNET))
599 dirname = g_path_get_dirname (filename);
600 if (mkdir (dirname, 0) != 0) {
602 if (errsv != EEXIST) {
603 _LOGE (self, "bind: failed to create directory %s: %s",
604 dirname, g_strerror (errsv));
609 if ((fd = creat (filename, S_IRUSR | S_IRGRP | S_IROTH)) == -1) {
611 _LOGE (self, "bind: failed to create %s: %s",
612 filename, g_strerror (errsv));
617 if (mount (PROC_SELF_NS_NET, filename, "none", MS_BIND, NULL) != 0) {
619 _LOGE (self, "bind: failed to mount %s to %s: %s",
620 PROC_SELF_NS_NET, filename, g_strerror (errsv));
626 if ((fd = open (filename, O_RDONLY)) == -1) {
628 _LOGE (self, "bind: failed to open %s: %s", filename, g_strerror (errsv));
629 umount2 (filename, MNT_DETACH);
640 nmp_netns_bind_to_path_destroy (NMPNetns *self, const char *filename)
644 g_return_val_if_fail (NMP_IS_NETNS (self), FALSE);
645 g_return_val_if_fail (filename && filename[0] == '/', FALSE);
647 if (umount2 (filename, MNT_DETACH) != 0) {
649 _LOGE (self, "bind: failed to unmount2 %s: %s", filename, g_strerror (errsv));
652 if (unlink (filename) != 0) {
654 _LOGE (self, "bind: failed to unlink %s: %s", filename, g_strerror (errsv));
660 /******************************************************************************/
663 set_property (GObject *object, guint prop_id,
664 const GValue *value, GParamSpec *pspec)
666 NMPNetns *self = NMP_NETNS (object);
671 self->priv->fd_net = g_value_get_int (value);
672 g_return_if_fail (self->priv->fd_net > 0);
676 self->priv->fd_mnt = g_value_get_int (value);
677 g_return_if_fail (self->priv->fd_mnt > 0);
680 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
686 nmp_netns_init (NMPNetns *self)
688 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NMP_TYPE_NETNS, NMPNetnsPrivate);
692 dispose (GObject *object)
694 NMPNetns *self = NMP_NETNS (object);
696 if (self->priv->fd_net > 0) {
697 close (self->priv->fd_net);
698 self->priv->fd_net = 0;
701 if (self->priv->fd_mnt > 0) {
702 close (self->priv->fd_mnt);
703 self->priv->fd_mnt = 0;
706 G_OBJECT_CLASS (nmp_netns_parent_class)->dispose (object);
710 nmp_netns_class_init (NMPNetnsClass *klass)
712 GObjectClass *object_class = G_OBJECT_CLASS (klass);
714 g_type_class_add_private (klass, sizeof (NMPNetnsPrivate));
716 object_class->set_property = set_property;
717 object_class->dispose = dispose;
719 obj_properties[PROP_FD_NET]
720 = g_param_spec_int (NMP_NETNS_FD_NET, "", "",
723 G_PARAM_CONSTRUCT_ONLY |
724 G_PARAM_STATIC_STRINGS);
725 obj_properties[PROP_FD_MNT]
726 = g_param_spec_int (NMP_NETNS_FD_MNT, "", "",
729 G_PARAM_CONSTRUCT_ONLY |
730 G_PARAM_STATIC_STRINGS);
731 g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);