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 <david@gibson.dropbear.id.au>
[sbrivio: Fix typo in man page]
Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
This commit is contained in:
David Gibson 2022-08-26 14:58:38 +10:00 committed by Stefano Brivio
parent 9e0dbc8948
commit c188736cd8
2 changed files with 75 additions and 30 deletions

89
conf.c
View file

@ -490,34 +490,16 @@ out:
} }
/** /**
* conf_ns_opt() - Parse non-option argument to namespace paths * conf_netns() - Parse --netns option
* @userns: buffer of size PATH_MAX, initially contains --userns
* argument (may be empty), updated with userns path
* @netns: buffer of size PATH_MAX, updated with netns path * @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 * 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; 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, '/')) { if (!strchr(arg, '/')) {
/* looks like a netns name */ /* looks like a netns name */
ret = snprintf(netns, PATH_MAX, "%s/%s", NETNS_RUN_DIR, arg); 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; 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 * conf_ns_check() - Check if we can enter configured namespaces
* @arg: Execution context * @arg: Execution context
@ -708,10 +727,13 @@ static unsigned int conf_ip6(unsigned int ifi,
static void usage(const char *name) static void usage(const char *name)
{ {
if (strstr(name, "pasta")) { 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("");
info("Without PID|PATH|NAME, run the default shell in a new"); info("Without PID or --netns, run the given command or a");
info("network and user namespace, and connect it via pasta."); info("default shell in a new network and user namespace, and");
info("connect it via pasta.");
} else { } else {
info("Usage: %s [OPTION]...", name); info("Usage: %s [OPTION]...", name);
} }
@ -858,6 +880,7 @@ pasta_opts:
info( " SPEC is as described above"); info( " SPEC is as described above");
info( " default: auto"); info( " default: auto");
info( " --userns NSPATH Target user namespace to join"); 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( " --netns-only Don't join existing user namespace");
info( " implied if PATH or NAME are given without --userns"); info( " implied if PATH or NAME are given without --userns");
info( " --config-net Configure tap interface in namespace"); 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' }, {"tcp-ns", required_argument, NULL, 'T' },
{"udp-ns", required_argument, NULL, 'U' }, {"udp-ns", required_argument, NULL, 'U' },
{"userns", required_argument, NULL, 2 }, {"userns", required_argument, NULL, 2 },
{"netns", required_argument, NULL, 3 },
{"netns-only", no_argument, &c->netns_only, 1 }, {"netns-only", no_argument, &c->netns_only, 1 },
{"config-net", no_argument, &c->pasta_conf_ns, 1 }, {"config-net", no_argument, &c->pasta_conf_ns, 1 },
{"ns-mac-addr", required_argument, NULL, 4 }, {"ns-mac-addr", required_argument, NULL, 4 },
@ -1091,6 +1115,16 @@ void conf(struct ctx *c, int argc, char **argv)
usage(argv[0]); usage(argv[0]);
} }
break; 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: case 4:
if (c->mode != MODE_PASTA) { if (c->mode != MODE_PASTA) {
err("--ns-mac-addr is for pasta mode only"); 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); check_root(c);
if (c->mode == MODE_PASTA && optind + 1 == argc) { 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) if (ret < 0)
usage(argv[0]); usage(argv[0]);
} else if (c->mode == MODE_PASTA && *userns && optind == argc) { } else if (c->mode == MODE_PASTA && *userns
err("--userns requires PID, PATH or NAME"); && !*netns && optind == argc) {
err("--userns requires --netns or PID");
usage(argv[0]); usage(argv[0]);
} else if (optind != argc) { } else if (optind != argc) {
usage(argv[0]); usage(argv[0]);

16
passt.1
View file

@ -15,7 +15,10 @@
[\fIOPTION\fR]... [\fIOPTION\fR]...
.br .br
.B pasta .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 .SH DESCRIPTION
@ -59,7 +62,7 @@ or with the \fBqrap\fR(1) wrapper.
equivalent functionality to network namespaces, as the one offered by equivalent functionality to network namespaces, as the one offered by
\fBpasst\fR for virtual machines. \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 network namespace. Otherwise, \fBpasta\fR creates a new user and network
namespace, and spawns an interactive shell within this context. A \fItap\fR namespace, and spawns an interactive shell within this context. A \fItap\fR
device within the network namespace is created to provide network connectivity. 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, 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. 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 .TP
.BR \-\-netns-only .BR \-\-netns-only