On Tue, Jan 29, 2019 at 4:01 PM Matteo Croce <mcr...@redhat.com> 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 <mcr...@redhat.com> > --- > ip/ipnetns.c | 62 ++++++++++++++++++++++++++++++++++----------- > man/man8/ip-netns.8 | 10 ++++++++ > 2 files changed, 57 insertions(+), 15 deletions(-) > > diff --git a/ip/ipnetns.c b/ip/ipnetns.c > index 03879b49..430d8844 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"); > @@ -632,24 +633,40 @@ static int create_netns_dir(void) > return 0; > } > > -static int netns_add(int argc, char **argv) > +static int netns_add(int argc, char **argv, bool create) > { > /* This function creates a new network namespace and > * a new mount namespace and bind them into a well known > * location in the filesystem based on the name provided. > * > + * If create is true, a new namespace will be created, > + * otherwise an existing one will be attached to the file. > + * > * 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 uers. > + * a new /etc/resolv.conf can be shared between users. > */ > - char netns_path[PATH_MAX]; > + char netns_path[PATH_MAX], proc_path[PATH_MAX]; > const char *name; > + pid_t pid; > int fd; > int made_netns_run_dir_mount = 0; > > - if (argc < 1) { > - fprintf(stderr, "No netns name specified\n"); > - return -1; > + if (create) { > + if (argc < 1) { > + fprintf(stderr, "No netns name specified\n"); > + return -1; > + } > + } else { > + if (argc < 2) { > + fprintf(stderr, "No netns name and PID specified\n"); > + return -1; > + } > + > + if (get_s32(&pid, argv[1], 0) || !pid) { > + fprintf(stderr, "Invalid PID: %s\n", argv[1]); > + return -1; > + } > } > name = argv[0]; > > @@ -689,21 +706,33 @@ static int netns_add(int argc, char **argv) > return -1; > } > close(fd); > - if (unshare(CLONE_NEWNET) < 0) { > - fprintf(stderr, "Failed to create a new network namespace > \"%s\": %s\n", > - name, strerror(errno)); > - goto out_delete; > + > + if (create) { > + if (unshare(CLONE_NEWNET) < 0) { > + fprintf(stderr, "Failed to create a new network > namespace \"%s\": %s\n", > + name, strerror(errno)); > + goto out_delete; > + } > + > + strcpy(proc_path, "/proc/self/ns/net"); > + } else { > + snprintf(proc_path, sizeof(proc_path), "/proc/%d/ns/net", > pid); > } > > /* Bind the netns last so I can watch for it */ > - if (mount("/proc/self/ns/net", netns_path, "none", MS_BIND, NULL) < > 0) { > - fprintf(stderr, "Bind /proc/self/ns/net -> %s failed: %s\n", > - netns_path, strerror(errno)); > + 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: > - netns_delete(argc, argv); > + if (create) { > + netns_delete(argc, argv); > + } else if (unlink(netns_path) < 0) { > + fprintf(stderr, "Cannot remove namespace file \"%s\": %s\n", > + netns_path, strerror(errno)); > + } > return -1; > } > > @@ -846,7 +875,7 @@ int do_netns(int argc, char **argv) > return usage(); > > if (matches(*argv, "add") == 0) > - return netns_add(argc-1, argv+1); > + return netns_add(argc-1, argv+1, true); > > if (matches(*argv, "set") == 0) > return netns_set(argc-1, argv+1); > @@ -866,6 +895,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_add(argc-1, argv+1, false); > + > 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 > -- > 2.20.1 >
Reviewed-by: Andrea Claudi <acla...@redhat.com> Tested-by: Andrea Claudi <acla...@redhat.com>