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)

Reply via email to