Package: slirp4netns Version: 1.2.0-1 Severity: wishlist Tags: patch upstream
Hi, thanks for maintaining slirp4netns. I'd like to use it in a more flexible way than it currently provides. The usual mode of operation is that one creates a network namespace and then launches slirp4netns outside that namespace while telling it which namespace one is interested in via a pid or path. Then slirp4netns creates a child that joins this namespace and creates the interface inside. This results in the need for readiness information and the ability for slirp4netns to join namespaces. It would be nice if slirp4netns was able to consume an existing tap file descriptor. The program that creates the network namespace can open /dev/net/tun and pass the file descriptor to its supervisor in the initial namespace. If passing this file descriptor could be passed to slirp4netns, it could skip creating a child and just use the given one. This approach has a few distinct advantages and disadvantages compared to the usual way of doing things. For instance, --configure cannot work. Also choosing the name of the interface becomes a responsibility of the caller. On the flip side, slirp4netns no longer needs to fork nor use any namespace syscalls. Also readiness becomes trivial, because creation of the network interface is entirely performed by the caller. Since patches speak louder than words, I am attaching a demo patch of how this is supposed to work. What do you think about it? Helmut
--- slirp4netns-1.2.1.orig/main.c +++ slirp4netns-1.2.1/main.c @@ -333,11 +333,11 @@ return fd; } -static int parent(int sock, int ready_fd, int exit_fd, const char *api_socket, +static int parent(int tapfd, int ready_fd, int exit_fd, const char *api_socket, struct slirp4netns_config *cfg, pid_t target_pid) { char str[INET6_ADDRSTRLEN]; - int rc, tapfd; + int rc; struct in_addr vdhcp_end = { #define NB_BOOTP_CLIENTS 16 /* NB_BOOTP_CLIENTS is hard-coded to 16 in libslirp: @@ -345,11 +345,6 @@ .s_addr = htonl(ntohl(cfg->vdhcp_start.s_addr) + NB_BOOTP_CLIENTS - 1), #undef NB_BOOTP_CLIENTS }; - if ((tapfd = recvfd(sock)) < 0) { - return tapfd; - } - fprintf(stderr, "received tapfd=%d\n", tapfd); - close(sock); printf("Starting slirp\n"); printf("* MTU: %d\n", cfg->mtu); printf("* Network: %s\n", @@ -420,7 +415,7 @@ static void usage(const char *argv0) { - printf("Usage: %s [OPTION]... PID|PATH [TAPNAME]\n", argv0); + printf("Usage: %s [OPTION]... PID|PATH|FD [TAPNAME]\n", argv0); printf("User-mode networking for unprivileged network namespaces.\n\n"); printf("-c, --configure bring up the interface\n"); printf("-e, --exit-fd=FD specify the FD for terminating " @@ -439,8 +434,8 @@ printf("--disable-host-loopback prohibit connecting to 127.0.0.1:* on the " "host namespace\n"); /* v0.4.0 */ - printf("--netns-type=TYPE specify network namespace type ([path|pid], " - "default=%s)\n", + printf("--netns-type=TYPE specify network namespace type " + "([path|pid|tapfd], default=%s)\n", DEFAULT_NETNS_TYPE); printf("--userns-path=PATH specify user namespace path\n"); printf( @@ -510,6 +505,7 @@ char *macaddress; // --macaddress char *target_type; // --target-type char *bess_socket; // argv[1] (When --target-type="bess") + int tapfd; // argv[1] (When --netns-type="tapfd") }; static void options_init(struct options *options) @@ -517,6 +513,7 @@ memset(options, 0, sizeof(*options)); options->exit_fd = options->ready_fd = -1; options->mtu = DEFAULT_MTU; + options->tapfd = -1; } static void options_destroy(struct options *options) @@ -806,13 +803,9 @@ fprintf(stderr, "--target-type must be either \"netns\" or \"bess\"\n"); goto error; } - if (argc - optind < 2) { + if (argc - optind < 1) { goto error; } - if (argc - optind > 2) { - // not an error, for preventing potential compatibility issue - printf("WARNING: too many arguments\n"); - } if (!options->netns_type || strcmp(options->netns_type, DEFAULT_NETNS_TYPE) == 0) { errno = 0; @@ -821,6 +814,14 @@ fprintf(stderr, "PID must be a positive integer\n"); goto error; } + } else if (options->netns_type && + strcmp(options->netns_type, "tapfd") == 0) { + errno = 0; + options->tapfd = strtol(argv[optind], &strtol_e, 10); + if (errno || *strtol_e != '\0' || options->tapfd < 0) { + fprintf(stderr, "TAPFD must a file descriptor\n"); + goto error; + } } else { options->netns_path = strdup(argv[optind]); if (access(options->netns_path, F_OK) == -1) { @@ -828,7 +829,21 @@ goto error; } } - options->tapname = strdup(argv[optind + 1]); + if (options->tapfd >= 0) { + if (argc - optind > 1) { + // not an error, for preventing potential compatibility issue + printf("WARNING: too many arguments\n"); + } + } else { + if (argc - optind < 2) { + goto error; + } + if (argc - optind > 2) { + // not an error, for preventing potential compatibility issue + printf("WARNING: too many arguments\n"); + } + options->tapname = strdup(argv[optind + 1]); + } return; error: usage(argv[0]); @@ -1092,7 +1107,7 @@ int main(int argc, char *const argv[]) { int sv[2]; - pid_t child_pid; + pid_t child_pid = -1; struct options options; struct slirp4netns_config slirp4netns_config; int exit_status = 0; @@ -1107,11 +1122,12 @@ exit_status = EXIT_FAILURE; goto finish; } - if ((child_pid = fork()) < 0) { - perror("fork"); - exit_status = EXIT_FAILURE; - goto finish; - } + if (options.tapfd < 0) + if ((child_pid = fork()) < 0) { + perror("fork"); + exit_status = EXIT_FAILURE; + goto finish; + } if (child_pid == 0) { int ret; if (options.target_type != NULL && @@ -1127,28 +1143,40 @@ goto finish; } } else { - int ret, child_wstatus, child_status; - do - ret = waitpid(child_pid, &child_wstatus, 0); - while (ret < 0 && errno == EINTR); - if (ret < 0) { - perror("waitpid"); - exit_status = EXIT_FAILURE; - goto finish; - } - if (!WIFEXITED(child_wstatus)) { - fprintf(stderr, "child failed(wstatus=%d, !WIFEXITED)\n", - child_wstatus); - exit_status = EXIT_FAILURE; - goto finish; - } - child_status = WEXITSTATUS(child_wstatus); - if (child_status != 0) { - fprintf(stderr, "child failed(%d)\n", child_status); - exit_status = child_status; - goto finish; + int tapfd; + if (options.tapfd >= 0) { + tapfd = options.tapfd; + } else { + int ret, child_wstatus, child_status; + do + ret = waitpid(child_pid, &child_wstatus, 0); + while (ret < 0 && errno == EINTR); + if (ret < 0) { + perror("waitpid"); + exit_status = EXIT_FAILURE; + goto finish; + } + if (!WIFEXITED(child_wstatus)) { + fprintf(stderr, "child failed(wstatus=%d, !WIFEXITED)\n", + child_wstatus); + exit_status = EXIT_FAILURE; + goto finish; + } + child_status = WEXITSTATUS(child_wstatus); + if (child_status != 0) { + fprintf(stderr, "child failed(%d)\n", child_status); + exit_status = child_status; + goto finish; + } + if ((tapfd = recvfd(sv[0])) < 0) { + fprintf(stderr, "failed to receive tapfd from child\n"); + exit_status = EXIT_FAILURE; + goto finish; + } + fprintf(stderr, "received tapfd=%d\n", tapfd); + close(sv[0]); } - if (parent(sv[0], options.ready_fd, options.exit_fd, options.api_socket, + if (parent(tapfd, options.ready_fd, options.exit_fd, options.api_socket, &slirp4netns_config, options.target_pid) < 0) { fprintf(stderr, "parent failed\n"); exit_status = EXIT_FAILURE; --- slirp4netns-1.2.1.orig/slirp4netns.1 +++ slirp4netns-1.2.1/slirp4netns.1 @@ -8,7 +8,7 @@ .SH SYNOPSIS .PP -slirp4netns [OPTION]... PID|PATH [TAPNAME] +slirp4netns [OPTION]... PID|PATH|FD [TAPNAME] .SH DESCRIPTION @@ -84,7 +84,10 @@ .PP \fB\fC\-\-netns\-type=TYPE\fR (since v0.4.0) -specify network namespace type ([path|pid], default=pid) +specify network namespace type ([path|pid|tapfd], default=pid) + +If the namespace type is tapfd, the first positional argument is expected to be an inherited file descriptor that corresponds to a \fB\fC/dev/net/tun\fR connection. +This conflicts with \fB\fC\-\-configure\fR and a \fB\fcTAPNAME\fR is ignored. .PP \fB\fC\-\-userns\-path=PATH\fR (since v0.4.0)