diff mbox series

[iproute2-next] netns: add subcommand to attach an existing network namespace

Message ID 20190124155053.7795-1-mcroce@redhat.com
State Changes Requested
Delegated to: David Ahern
Headers show
Series [iproute2-next] netns: add subcommand to attach an existing network namespace | expand

Commit Message

Matteo Croce Jan. 24, 2019, 3:50 p.m. UTC
ip tracks namespaces with dummy files in /var/run/netns/, but can't see
namespaces created with other tools.
Creating the dummy file and bind mounting the correct procfs entry will
make ip aware of that namespace.
Add an ip netns subcommand to automate this task.

Signed-off-by: Matteo Croce <mcroce@redhat.com>
---
 ip/ipnetns.c        | 84 +++++++++++++++++++++++++++++++++++++++++++++
 man/man8/ip-netns.8 | 10 ++++++
 2 files changed, 94 insertions(+)

Comments

David Ahern Jan. 28, 2019, 4:38 p.m. UTC | #1
On 1/24/19 8:50 AM, Matteo Croce wrote:
> ip tracks namespaces with dummy files in /var/run/netns/, but can't see
> namespaces created with other tools.
> Creating the dummy file and bind mounting the correct procfs entry will
> make ip aware of that namespace.
> Add an ip netns subcommand to automate this task.
> 
> Signed-off-by: Matteo Croce <mcroce@redhat.com>
> ---
>  ip/ipnetns.c        | 84 +++++++++++++++++++++++++++++++++++++++++++++
>  man/man8/ip-netns.8 | 10 ++++++
>  2 files changed, 94 insertions(+)
> 
> diff --git a/ip/ipnetns.c b/ip/ipnetns.c
> index 03879b49..86b1a36b 100644
> --- a/ip/ipnetns.c
> +++ b/ip/ipnetns.c
> @@ -28,6 +28,7 @@ static int usage(void)
>  {
>  	fprintf(stderr, "Usage: ip netns list\n");
>  	fprintf(stderr, "       ip netns add NAME\n");
> +	fprintf(stderr, "       ip netns attach NAME PID\n");
>  	fprintf(stderr, "       ip netns set NAME NETNSID\n");
>  	fprintf(stderr, "       ip [-all] netns delete [NAME]\n");
>  	fprintf(stderr, "       ip netns identify [PID]\n");
> @@ -811,6 +812,86 @@ static int netns_monitor(int argc, char **argv)
>  	return 0;
>  }
>  
> +static int netns_attach(int argc, char **argv)
> +{
> +	/* This function bind mounts an existing network namespace to a
> +	 * well known location in the filesystem based on the name provided.
> +	 * If everything succeeds, the result is the same as netns_add.
> +	 *
> +	 * The mount namespace is created so that any necessary
> +	 * userspace tweaks like remounting /sys, or bind mounting
> +	 * a new /etc/resolv.conf can be shared between users.
> +	 */
> +	char netns_path[PATH_MAX], proc_path[PATH_MAX];
> +	const char *name;
> +	int fd;
> +	pid_t pid;
> +	int made_netns_run_dir_mount = 0;
> +
> +	if (argc < 2) {
> +		fprintf(stderr, "No netns name and PID specified\n");
> +		return -1;
> +	}
> +	name = argv[0];
> +
> +	if (get_s32(&pid, argv[1], 0) || !pid) {
> +		fprintf(stderr, "Invalid PID: %s\n", argv[1]);
> +		return -1;
> +	}
> +
> +	snprintf(netns_path, sizeof(netns_path), "%s/%s", NETNS_RUN_DIR, name);
> +
> +	if (create_netns_dir())
> +		return -1;
> +
> +	/* Make it possible for network namespace mounts to propagate between
> +	 * mount namespaces.  This makes it likely that a unmounting a network
> +	 * namespace file in one namespace will unmount the network namespace
> +	 * file in all namespaces allowing the network namespace to be freed
> +	 * sooner.
> +	 */
> +	while (mount("", NETNS_RUN_DIR, "none", MS_SHARED | MS_REC, NULL)) {
> +		/* Fail unless we need to make the mount point */
> +		if (errno != EINVAL || made_netns_run_dir_mount) {
> +			fprintf(stderr, "mount --make-shared %s failed: %s\n",
> +				NETNS_RUN_DIR, strerror(errno));
> +			return -1;
> +		}
> +
> +		/* Upgrade NETNS_RUN_DIR to a mount point */
> +		if (mount(NETNS_RUN_DIR, NETNS_RUN_DIR, "none", MS_BIND | MS_REC, NULL)) {
> +			fprintf(stderr, "mount --bind %s %s failed: %s\n",
> +				NETNS_RUN_DIR, NETNS_RUN_DIR, strerror(errno));
> +			return -1;
> +		}
> +		made_netns_run_dir_mount = 1;
> +	}
> +
> +	/* Create the filesystem state */
> +	fd = open(netns_path, O_RDONLY|O_CREAT|O_EXCL, 0);
> +	if (fd < 0) {
> +		fprintf(stderr, "Cannot create namespace file \"%s\": %s\n",
> +			netns_path, strerror(errno));
> +		return -1;
> +	}
> +	close(fd);
> +
> +	snprintf(proc_path, sizeof(proc_path), "/proc/%d/ns/net", pid);
> +
> +	/* Bind the netns last so I can watch for it */
> +	if (mount(proc_path, netns_path, "none", MS_BIND, NULL) < 0) {
> +		fprintf(stderr, "Bind %s -> %s failed: %s\n",
> +			proc_path, netns_path, strerror(errno));
> +		goto out_delete;
> +	}
> +	return 0;
> +out_delete:
> +	if (unlink(netns_path) < 0)
> +		fprintf(stderr, "Cannot remove namespace file \"%s\": %s\n",
> +			netns_path, strerror(errno));
> +	return -1;
> +}
> +

Rather than duplicate netns_add, refactor it for use by both attach and add.
diff mbox series

Patch

diff --git a/ip/ipnetns.c b/ip/ipnetns.c
index 03879b49..86b1a36b 100644
--- a/ip/ipnetns.c
+++ b/ip/ipnetns.c
@@ -28,6 +28,7 @@  static int usage(void)
 {
 	fprintf(stderr, "Usage: ip netns list\n");
 	fprintf(stderr, "       ip netns add NAME\n");
+	fprintf(stderr, "       ip netns attach NAME PID\n");
 	fprintf(stderr, "       ip netns set NAME NETNSID\n");
 	fprintf(stderr, "       ip [-all] netns delete [NAME]\n");
 	fprintf(stderr, "       ip netns identify [PID]\n");
@@ -811,6 +812,86 @@  static int netns_monitor(int argc, char **argv)
 	return 0;
 }
 
+static int netns_attach(int argc, char **argv)
+{
+	/* This function bind mounts an existing network namespace to a
+	 * well known location in the filesystem based on the name provided.
+	 * If everything succeeds, the result is the same as netns_add.
+	 *
+	 * The mount namespace is created so that any necessary
+	 * userspace tweaks like remounting /sys, or bind mounting
+	 * a new /etc/resolv.conf can be shared between users.
+	 */
+	char netns_path[PATH_MAX], proc_path[PATH_MAX];
+	const char *name;
+	int fd;
+	pid_t pid;
+	int made_netns_run_dir_mount = 0;
+
+	if (argc < 2) {
+		fprintf(stderr, "No netns name and PID specified\n");
+		return -1;
+	}
+	name = argv[0];
+
+	if (get_s32(&pid, argv[1], 0) || !pid) {
+		fprintf(stderr, "Invalid PID: %s\n", argv[1]);
+		return -1;
+	}
+
+	snprintf(netns_path, sizeof(netns_path), "%s/%s", NETNS_RUN_DIR, name);
+
+	if (create_netns_dir())
+		return -1;
+
+	/* Make it possible for network namespace mounts to propagate between
+	 * mount namespaces.  This makes it likely that a unmounting a network
+	 * namespace file in one namespace will unmount the network namespace
+	 * file in all namespaces allowing the network namespace to be freed
+	 * sooner.
+	 */
+	while (mount("", NETNS_RUN_DIR, "none", MS_SHARED | MS_REC, NULL)) {
+		/* Fail unless we need to make the mount point */
+		if (errno != EINVAL || made_netns_run_dir_mount) {
+			fprintf(stderr, "mount --make-shared %s failed: %s\n",
+				NETNS_RUN_DIR, strerror(errno));
+			return -1;
+		}
+
+		/* Upgrade NETNS_RUN_DIR to a mount point */
+		if (mount(NETNS_RUN_DIR, NETNS_RUN_DIR, "none", MS_BIND | MS_REC, NULL)) {
+			fprintf(stderr, "mount --bind %s %s failed: %s\n",
+				NETNS_RUN_DIR, NETNS_RUN_DIR, strerror(errno));
+			return -1;
+		}
+		made_netns_run_dir_mount = 1;
+	}
+
+	/* Create the filesystem state */
+	fd = open(netns_path, O_RDONLY|O_CREAT|O_EXCL, 0);
+	if (fd < 0) {
+		fprintf(stderr, "Cannot create namespace file \"%s\": %s\n",
+			netns_path, strerror(errno));
+		return -1;
+	}
+	close(fd);
+
+	snprintf(proc_path, sizeof(proc_path), "/proc/%d/ns/net", pid);
+
+	/* Bind the netns last so I can watch for it */
+	if (mount(proc_path, netns_path, "none", MS_BIND, NULL) < 0) {
+		fprintf(stderr, "Bind %s -> %s failed: %s\n",
+			proc_path, netns_path, strerror(errno));
+		goto out_delete;
+	}
+	return 0;
+out_delete:
+	if (unlink(netns_path) < 0)
+		fprintf(stderr, "Cannot remove namespace file \"%s\": %s\n",
+			netns_path, strerror(errno));
+	return -1;
+}
+
 static int invalid_name(const char *name)
 {
 	return !*name || strlen(name) > NAME_MAX ||
@@ -866,6 +947,9 @@  int do_netns(int argc, char **argv)
 	if (matches(*argv, "monitor") == 0)
 		return netns_monitor(argc-1, argv+1);
 
+	if (matches(*argv, "attach") == 0)
+		return netns_attach(argc-1, argv+1);
+
 	fprintf(stderr, "Command \"%s\" is unknown, try \"ip netns help\".\n", *argv);
 	exit(-1);
 }
diff --git a/man/man8/ip-netns.8 b/man/man8/ip-netns.8
index d539f18b..39a10e76 100644
--- a/man/man8/ip-netns.8
+++ b/man/man8/ip-netns.8
@@ -19,6 +19,10 @@  ip-netns \- process network namespace management
 .B ip netns add
 .I NETNSNAME
 
+.ti -8
+.B ip netns attach
+.I NETNSNAME PID
+
 .ti -8
 .B ip [-all] netns del
 .RI "[ " NETNSNAME " ]"
@@ -89,6 +93,12 @@  This command displays all of the network namespaces in /var/run/netns
 If NAME is available in /var/run/netns/ this command creates a new
 network namespace and assigns NAME.
 
+.TP
+.B ip netns attach NAME PID - create a new named network namespace
+.sp
+If NAME is available in /var/run/netns/ this command attaches the network
+namespace of the process PID to NAME as if it were created with ip netns.
+
 .TP
 .B ip [-all] netns delete [ NAME ] - delete the name of a network namespace(s)
 .sp