minijail: Support entering an existing net namespace.

When launching a full OS as the jailed process, it is useful to first be
able to configure a network namespace and start the new process in that
namespace.

This adds the "-e<net namespace file>" optional argument to -e.  It
allows, for example, passing "-e/var/run/netns/newns" to minijail0.

Change-Id: I0613162072a1d14f10c58444c514f6d052c3d1e5
Signed-off-by: Dylan Reid <dgreid@chromium.org>
diff --git a/libminijail.c b/libminijail.c
index a797823..4f2228a 100644
--- a/libminijail.c
+++ b/libminijail.c
@@ -87,6 +87,7 @@
 		int enter_vfs:1;
 		int pids:1;
 		int net:1;
+		int enter_net:1;
 		int userns:1;
 		int seccomp:1;
 		int remount_proc_ro:1;
@@ -108,6 +109,7 @@
 	uint64_t caps;
 	pid_t initpid;
 	int mountns_fd;
+	int netns_fd;
 	int filter_len;
 	int binding_count;
 	char *chrootdir;
@@ -297,6 +299,16 @@
 	j->flags.net = 1;
 }
 
+void API minijail_namespace_enter_net(struct minijail *j, const char *ns_path)
+{
+	int ns_fd = open(ns_path, O_RDONLY);
+	if (ns_fd < 0) {
+		pdie("failed to open namespace '%s'", ns_path);
+	}
+	j->netns_fd = ns_fd;
+	j->flags.enter_net = 1;
+}
+
 void API minijail_remount_proc_readonly(struct minijail *j)
 {
 	j->flags.vfs = 1;
@@ -1011,8 +1023,12 @@
             pdie("mount(/, private)");
         }
 
-	if (j->flags.net && unshare(CLONE_NEWNET))
+	if (j->flags.enter_net) {
+		if (setns(j->netns_fd, CLONE_NEWNET))
+			pdie("setns(CLONE_NEWNET)");
+	} else if (j->flags.net && unshare(CLONE_NEWNET)) {
 		pdie("unshare(net)");
+	}
 
 	if (j->flags.chroot && enter_chroot(j))
 		pdie("chroot");
diff --git a/libminijail.h b/libminijail.h
index 62e4007..bfce714 100644
--- a/libminijail.h
+++ b/libminijail.h
@@ -51,6 +51,7 @@
 void minijail_namespace_vfs(struct minijail *j);
 void minijail_namespace_enter_vfs(struct minijail *j, const char *ns_path);
 void minijail_namespace_net(struct minijail *j);
+void minijail_namespace_enter_net(struct minijail *j, const char *ns_path);
 /* Implies namespace_vfs and remount_proc_readonly.
  * WARNING: this is NOT THREAD SAFE. See the block comment in </libminijail.c>.
  */
diff --git a/minijail0.c b/minijail0.c
index 8329080..e272702 100644
--- a/minijail0.c
+++ b/minijail0.c
@@ -86,7 +86,7 @@
 	       "  -c <caps>:  restrict caps to <caps>\n"
 	       "  -C <dir>:   chroot to <dir>\n"
 	       "              Not compatible with -P\n"
-	       "  -e:         enter new network namespace\n"
+	       "  -e[file]:   enter new network namespace, or existing one if 'file' is provided\n"
 	       "  -f <file>:  write the pid of the jailed process to <file>\n"
 	       "  -G:         inherit secondary groups from uid\n"
 	       "  -g <group>: change gid to <group>\n"
@@ -145,7 +145,7 @@
 	if (argc > 1 && argv[1][0] != '-')
 		return 1;
 	while ((opt = getopt(argc, argv,
-			     "u:g:sS:c:C:P:b:V:f:m:M:vrGhHinpLetIU")) != -1) {
+			     "u:g:sS:c:C:P:b:V:f:m:M:e::vrGhHinpLtIU")) != -1) {
 		switch (opt) {
 		case 'u':
 			set_user(j, optarg);
@@ -233,7 +233,10 @@
 			minijail_namespace_pids(j);
 			break;
 		case 'e':
-			minijail_namespace_net(j);
+			if (optarg)
+				minijail_namespace_enter_net(j, optarg);
+			else
+				minijail_namespace_net(j);
 			break;
 		case 'i':
 			*exit_immediately = 1;