linma / rpms / iproute

Forked from rpms/iproute 4 years ago
Clone

Blame SOURCES/0071-ip-reset-netns-after-each-command-in-batch-mode.patch

8def76
From e94d7e4519668e840f1c768a569486eebdc3825d Mon Sep 17 00:00:00 2001
8def76
From: Andrea Claudi <aclaudi@redhat.com>
8def76
Date: Tue, 25 Jun 2019 19:03:18 +0200
8def76
Subject: [PATCH] ip: reset netns after each command in batch mode
8def76
8def76
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1671016
8def76
Upstream Status: iproute2.git commit 80a931d41c058
8def76
Conflicts: on ip/ipnetns.c due to missing commit e3dbcb2a12ab1
8def76
           ("netns: add subcommand to attach an existing network namespace")
8def76
8def76
commit 80a931d41c0587a4f5bfc1ece7eddac3442b5d9a
8def76
Author: Matteo Croce <mcroce@redhat.com>
8def76
Date:   Fri Jun 7 22:41:22 2019 +0200
8def76
8def76
    ip: reset netns after each command in batch mode
8def76
8def76
    When creating a new netns or executing a program into an existing one,
8def76
    the unshare() or setns() calls will change the current netns.
8def76
    In batch mode, this can run commands on the wrong interfaces, as the
8def76
    ifindex value is meaningful only in the current netns. For example, this
8def76
    command fails because veth-c doesn't exists in the init netns:
8def76
8def76
        # ip -b - <<-'EOF'
8def76
            netns add client
8def76
            link add name veth-c type veth peer veth-s netns client
8def76
            addr add 192.168.2.1/24 dev veth-c
8def76
        EOF
8def76
        Cannot find device "veth-c"
8def76
        Command failed -:7
8def76
8def76
    But if there are two devices with the same name in the init and new netns,
8def76
    ip will build a wrong ll_map with indexes belonging to the new netns,
8def76
    and will execute actions in the init netns using this wrong mapping.
8def76
    This script will flush all eth0 addresses and bring it down, as it has
8def76
    the same ifindex of veth0 in the new netns:
8def76
8def76
        # ip addr
8def76
        1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
8def76
            link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
8def76
            inet 127.0.0.1/8 scope host lo
8def76
               valid_lft forever preferred_lft forever
8def76
        2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
8def76
            link/ether 52:54:00:12:34:56 brd ff:ff:ff:ff:ff:ff
8def76
            inet 192.168.122.76/24 brd 192.168.122.255 scope global dynamic eth0
8def76
               valid_lft 3598sec preferred_lft 3598sec
8def76
8def76
        # ip -b - <<-'EOF'
8def76
            netns add client
8def76
            link add name veth0 type veth peer name veth1
8def76
            link add name veth-ns type veth peer name veth0 netns client
8def76
            link set veth0 down
8def76
            address flush veth0
8def76
        EOF
8def76
8def76
        # ip addr
8def76
        1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
8def76
            link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
8def76
            inet 127.0.0.1/8 scope host lo
8def76
               valid_lft forever preferred_lft forever
8def76
        2: eth0: <BROADCAST,MULTICAST> mtu 1500 qdisc mq state DOWN group default qlen 1000
8def76
            link/ether 52:54:00:12:34:56 brd ff:ff:ff:ff:ff:ff
8def76
        3: veth1@veth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN group default qlen 1000
8def76
            link/ether c2:db:d0:34:13:4a brd ff:ff:ff:ff:ff:ff
8def76
        4: veth0@veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN group default qlen 1000
8def76
            link/ether ca:9d:6b:5f:5f:8f brd ff:ff:ff:ff:ff:ff
8def76
        5: veth-ns@if2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
8def76
            link/ether 32:ef:22:df:51:0a brd ff:ff:ff:ff:ff:ff link-netns client
8def76
8def76
    The same issue can be triggered by the netns exec subcommand with a
8def76
    sligthy different script:
8def76
8def76
        # ip netns add client
8def76
        # ip -b - <<-'EOF'
8def76
            netns exec client true
8def76
            link add name veth0 type veth peer name veth1
8def76
            link add name veth-ns type veth peer name veth0 netns client
8def76
            link set veth0 down
8def76
            address flush veth0
8def76
        EOF
8def76
8def76
    Fix this by adding two netns_{save,reset} functions, which are used
8def76
    to get a file descriptor for the init netns, and restore it after
8def76
    each batch command.
8def76
    netns_save() is called before the unshare() or setns(),
8def76
    while netns_restore() is called after each command.
8def76
8def76
    Fixes: 0dc34c7713bb ("iproute2: Add processless network namespace support")
8def76
    Reviewed-and-tested-by: Andrea Claudi <aclaudi@redhat.com>
8def76
    Signed-off-by: Matteo Croce <mcroce@redhat.com>
8def76
    Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
8def76
---
8def76
 include/namespace.h |  2 ++
8def76
 ip/ip.c             |  1 +
8def76
 ip/ipnetns.c        |  1 +
8def76
 lib/namespace.c     | 31 +++++++++++++++++++++++++++++++
8def76
 4 files changed, 35 insertions(+)
8def76
8def76
diff --git a/include/namespace.h b/include/namespace.h
8def76
index e47f9b5d49d12..89cdda11782e8 100644
8def76
--- a/include/namespace.h
8def76
+++ b/include/namespace.h
8def76
@@ -49,6 +49,8 @@ static inline int setns(int fd, int nstype)
8def76
 }
8def76
 #endif /* HAVE_SETNS */
8def76
 
8def76
+void netns_save(void);
8def76
+void netns_restore(void);
8def76
 int netns_switch(char *netns);
8def76
 int netns_get_fd(const char *netns);
8def76
 int netns_foreach(int (*func)(char *nsname, void *arg), void *arg);
8def76
diff --git a/ip/ip.c b/ip/ip.c
8def76
index 2ca55e37a4c62..6e8230b3ee584 100644
8def76
--- a/ip/ip.c
8def76
+++ b/ip/ip.c
8def76
@@ -158,6 +158,7 @@ static int batch(const char *name)
8def76
 			if (!force)
8def76
 				break;
8def76
 		}
8def76
+		netns_restore();
8def76
 	}
8def76
 	if (line)
8def76
 		free(line);
8def76
diff --git a/ip/ipnetns.c b/ip/ipnetns.c
8def76
index 368be0cbc0a48..a6e3ea575c363 100644
8def76
--- a/ip/ipnetns.c
8def76
+++ b/ip/ipnetns.c
8def76
@@ -689,6 +689,7 @@ static int netns_add(int argc, char **argv)
8def76
 		return -1;
8def76
 	}
8def76
 	close(fd);
8def76
+	netns_save();
8def76
 	if (unshare(CLONE_NEWNET) < 0) {
8def76
 		fprintf(stderr, "Failed to create a new network namespace \"%s\": %s\n",
8def76
 			name, strerror(errno));
8def76
diff --git a/lib/namespace.c b/lib/namespace.c
8def76
index 06ae0a48c2243..a2aea57ad4109 100644
8def76
--- a/lib/namespace.c
8def76
+++ b/lib/namespace.c
8def76
@@ -15,6 +15,35 @@
8def76
 #include "utils.h"
8def76
 #include "namespace.h"
8def76
 
8def76
+static int saved_netns = -1;
8def76
+
8def76
+/* Obtain a FD for the current namespace, so we can reenter it later */
8def76
+void netns_save(void)
8def76
+{
8def76
+	if (saved_netns != -1)
8def76
+		return;
8def76
+
8def76
+	saved_netns = open("/proc/self/ns/net", O_RDONLY | O_CLOEXEC);
8def76
+	if (saved_netns == -1) {
8def76
+		perror("Cannot open init namespace");
8def76
+		exit(1);
8def76
+	}
8def76
+}
8def76
+
8def76
+void netns_restore(void)
8def76
+{
8def76
+	if (saved_netns == -1)
8def76
+		return;
8def76
+
8def76
+	if (setns(saved_netns, CLONE_NEWNET)) {
8def76
+		perror("setns");
8def76
+		exit(1);
8def76
+	}
8def76
+
8def76
+	close(saved_netns);
8def76
+	saved_netns = -1;
8def76
+}
8def76
+
8def76
 static void bind_etc(const char *name)
8def76
 {
8def76
 	char etc_netns_path[sizeof(NETNS_ETC_DIR) + NAME_MAX];
8def76
@@ -61,6 +90,8 @@ int netns_switch(char *name)
8def76
 		return -1;
8def76
 	}
8def76
 
8def76
+	netns_save();
8def76
+
8def76
 	if (setns(netns, CLONE_NEWNET) < 0) {
8def76
 		fprintf(stderr, "setting the network namespace \"%s\" failed: %s\n",
8def76
 			name, strerror(errno));
8def76
-- 
8def76
2.20.1
8def76