|
|
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 |
|