From c188736cd81aab5924073118f11d1b9dc7696382 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Fri, 26 Aug 2022 14:58:38 +1000 Subject: [PATCH] Use explicit --netns option rather than multiplexing with PID When attaching to an existing namespace, pasta can take a PID or the name or path of a network namespace as a non-option parameter. We disambiguate based on what the parameter looks like. Make this more explicit by using a --netns option for explicitly giving the path or name, and treating a non-option argument always as a PID. Signed-off-by: David Gibson [sbrivio: Fix typo in man page] Signed-off-by: Stefano Brivio --- conf.c | 89 ++++++++++++++++++++++++++++++++++++++++----------------- passt.1 | 16 +++++++++-- 2 files changed, 75 insertions(+), 30 deletions(-) diff --git a/conf.c b/conf.c index fdca582..1d049d6 100644 --- a/conf.c +++ b/conf.c @@ -490,34 +490,16 @@ out: } /** - * conf_ns_opt() - Parse non-option argument to namespace paths - * @userns: buffer of size PATH_MAX, initially contains --userns - * argument (may be empty), updated with userns path + * conf_netns() - Parse --netns option * @netns: buffer of size PATH_MAX, updated with netns path - * @arg: PID, path or name of network namespace + * @arg: --netns argument * * Return: 0 on success, negative error code otherwise */ -static int conf_ns_opt(char *userns, char *netns, const char *arg) +static int conf_netns(char *netns, const char *arg) { - char *endptr; - long pidval; int ret; - pidval = strtol(arg, &endptr, 10); - if (!*endptr) { - /* Looks like a pid */ - if (pidval < 0 || pidval > INT_MAX) { - err("Invalid PID %s", arg); - return -EINVAL; - } - - snprintf(netns, PATH_MAX, "/proc/%ld/ns/net", pidval); - if (!*userns) - snprintf(userns, PATH_MAX, "/proc/%ld/ns/user", pidval); - return 0; - } - if (!strchr(arg, '/')) { /* looks like a netns name */ ret = snprintf(netns, PATH_MAX, "%s/%s", NETNS_RUN_DIR, arg); @@ -534,6 +516,43 @@ static int conf_ns_opt(char *userns, char *netns, const char *arg) return 0; } +/** + * conf_ns_pid() - Parse non-option argument as a PID + * @userns: buffer of size PATH_MAX, initially contains --userns + * argument (may be empty), updated with userns path + * @netns: buffer of size PATH_MAX, initial contains --netns + * argument (may be empty), updated with netns path + * @arg: PID of network namespace + * + * Return: 0 on success, negative error code otherwise + */ +static int conf_ns_pid(char *userns, char *netns, const char *arg) +{ + char *endptr; + long pidval; + + if (*netns) { + err("Both --netns and PID given"); + return -EINVAL; + } + + pidval = strtol(arg, &endptr, 10); + if (!*endptr) { + /* Looks like a pid */ + if (pidval < 0 || pidval > INT_MAX) { + err("Invalid PID %s", arg); + return -EINVAL; + } + + snprintf(netns, PATH_MAX, "/proc/%ld/ns/net", pidval); + if (!*userns) + snprintf(userns, PATH_MAX, "/proc/%ld/ns/user", pidval); + return 0; + } + + return -EINVAL; +} + /** * conf_ns_check() - Check if we can enter configured namespaces * @arg: Execution context @@ -708,10 +727,13 @@ static unsigned int conf_ip6(unsigned int ifi, static void usage(const char *name) { if (strstr(name, "pasta")) { - info("Usage: %s [OPTION]... [PID|PATH|NAME]", name); + info("Usage: %s [OPTION]... [COMMAND] [ARGS]...", name); + info(" %s [OPTION]... PID", name); + info(" %s [OPTION]... --netns [PATH|NAME]", name); info(""); - info("Without PID|PATH|NAME, run the default shell in a new"); - info("network and user namespace, and connect it via pasta."); + info("Without PID or --netns, run the given command or a"); + info("default shell in a new network and user namespace, and"); + info("connect it via pasta."); } else { info("Usage: %s [OPTION]...", name); } @@ -858,6 +880,7 @@ pasta_opts: info( " SPEC is as described above"); info( " default: auto"); info( " --userns NSPATH Target user namespace to join"); + info( " --netns PATH|NAME Target network namespace to join"); info( " --netns-only Don't join existing user namespace"); info( " implied if PATH or NAME are given without --userns"); info( " --config-net Configure tap interface in namespace"); @@ -1038,6 +1061,7 @@ void conf(struct ctx *c, int argc, char **argv) {"tcp-ns", required_argument, NULL, 'T' }, {"udp-ns", required_argument, NULL, 'U' }, {"userns", required_argument, NULL, 2 }, + {"netns", required_argument, NULL, 3 }, {"netns-only", no_argument, &c->netns_only, 1 }, {"config-net", no_argument, &c->pasta_conf_ns, 1 }, {"ns-mac-addr", required_argument, NULL, 4 }, @@ -1091,6 +1115,16 @@ void conf(struct ctx *c, int argc, char **argv) usage(argv[0]); } break; + case 3: + if (c->mode != MODE_PASTA) { + err("--netns is for pasta mode only"); + usage(argv[0]); + } + + ret = conf_netns(netns, optarg); + if (ret < 0) + usage(argv[0]); + break; case 4: if (c->mode != MODE_PASTA) { err("--ns-mac-addr is for pasta mode only"); @@ -1465,11 +1499,12 @@ void conf(struct ctx *c, int argc, char **argv) check_root(c); if (c->mode == MODE_PASTA && optind + 1 == argc) { - ret = conf_ns_opt(userns, netns, argv[optind]); + ret = conf_ns_pid(userns, netns, argv[optind]); if (ret < 0) usage(argv[0]); - } else if (c->mode == MODE_PASTA && *userns && optind == argc) { - err("--userns requires PID, PATH or NAME"); + } else if (c->mode == MODE_PASTA && *userns + && !*netns && optind == argc) { + err("--userns requires --netns or PID"); usage(argv[0]); } else if (optind != argc) { usage(argv[0]); diff --git a/passt.1 b/passt.1 index bbdadc1..1f0cd47 100644 --- a/passt.1 +++ b/passt.1 @@ -15,7 +15,10 @@ [\fIOPTION\fR]... .br .B pasta -[\fIOPTION\fR]... [\fIPID\fR|\fIPATH\fR|\fINAME\fR] +[\fIOPTION\fR]... [\fIPID\fR] +.br +.B pasta +[\fIOPTION\fR]... \fB--netns\fR [\fIPATH\fR|\fINAME\fR] .SH DESCRIPTION @@ -59,7 +62,7 @@ or with the \fBqrap\fR(1) wrapper. equivalent functionality to network namespaces, as the one offered by \fBpasst\fR for virtual machines. -If PID, PATH or NAME are given, \fBpasta\fR associates to an existing user and +If PID or --netns are given, \fBpasta\fR associates to an existing user and network namespace. Otherwise, \fBpasta\fR creates a new user and network namespace, and spawns an interactive shell within this context. A \fItap\fR device within the network namespace is created to provide network connectivity. @@ -445,7 +448,14 @@ Default is \fBauto\fR. Target user namespace to join, as a path. If PID is given, without this option, the user namespace will be the one of the corresponding process. -This option requires PID, PATH or NAME to be specified. +This option requires --netns or a PID to be specified. + +.TP +.BR \-\-netns " " \fIspec +Target network namespace to join, as a path or a name. A name is treated as +with \fBip-netns(8)\fR as equivalent to a path in \fI/run/netns\fR. + +This option can't be specified with a PID. .TP .BR \-\-netns-only