aboutgitcodebugslistschat
path: root/pasta.c
diff options
context:
space:
mode:
Diffstat (limited to 'pasta.c')
-rw-r--r--pasta.c313
1 files changed, 179 insertions, 134 deletions
diff --git a/pasta.c b/pasta.c
index f15084d..4cd58fd 100644
--- a/pasta.c
+++ b/pasta.c
@@ -27,7 +27,6 @@
#include <stdint.h>
#include <unistd.h>
#include <syslog.h>
-#include <sys/epoll.h>
#include <sys/inotify.h>
#include <sys/mount.h>
#include <sys/timerfd.h>
@@ -41,14 +40,16 @@
#include <arpa/inet.h>
#include <netinet/in.h>
#include <net/ethernet.h>
+#include <sys/prctl.h>
#include <sys/syscall.h>
#include <linux/magic.h>
#include "util.h"
#include "passt.h"
-#include "isolation.h"
+#include "pasta.h"
#include "netlink.h"
#include "log.h"
+#include "epoll_ctl.h"
#define HOSTNAME_PREFIX "pasta-"
@@ -57,15 +58,13 @@ int pasta_child_pid;
/**
* pasta_child_handler() - Exit once shell exits (if we started it), reap clones
- * @signal: Unused, handler deals with SIGCHLD only
+ * @signal: Signal number; this handler deals with SIGCHLD only
*/
void pasta_child_handler(int signal)
{
int errno_save = errno;
siginfo_t infop;
- (void)signal;
-
if (signal != SIGCHLD)
return;
@@ -73,12 +72,12 @@ void pasta_child_handler(int signal)
!waitid(P_PID, pasta_child_pid, &infop, WEXITED | WNOHANG)) {
if (infop.si_pid == pasta_child_pid) {
if (infop.si_code == CLD_EXITED)
- _exit(infop.si_status);
+ passt_exit(infop.si_status);
/* If killed by a signal, si_status is the number.
* Follow common shell convention of returning it + 128.
*/
- _exit(infop.si_status + 128);
+ passt_exit(infop.si_status + 128);
/* Nothing to do, detached PID namespace going away */
}
@@ -169,10 +168,12 @@ void pasta_open_ns(struct ctx *c, const char *netns)
* struct pasta_spawn_cmd_arg - Argument for pasta_spawn_cmd()
* @exe: Executable to run
* @argv: Command and arguments to run
+ * @ctx: Context to read config from
*/
struct pasta_spawn_cmd_arg {
const char *exe;
char *const *argv;
+ struct ctx *c;
};
/**
@@ -186,18 +187,31 @@ static int pasta_spawn_cmd(void *arg)
{
char hostname[HOST_NAME_MAX + 1] = HOSTNAME_PREFIX;
const struct pasta_spawn_cmd_arg *a;
+ size_t conf_hostname_len;
sigset_t set;
- /* We run in a detached PID and mount namespace: mount /proc over */
- if (mount("", "/proc", "proc", 0, NULL))
+ /* If the parent dies with an error, so should we */
+ if (prctl(PR_SET_PDEATHSIG, SIGKILL))
+ die_perror("Couldn't set PR_SET_PDEATHSIG");
+
+ a = (const struct pasta_spawn_cmd_arg *)arg;
+
+ /* We run in a detached mount namespace, and, unless --no-pidns was
+ * given, in a detached PID namespace: mount /proc over
+ */
+ if (!a->c->no_pidns && mount("", "/proc", "proc", 0, NULL))
warn_perror("Couldn't mount /proc");
if (write_file("/proc/sys/net/ipv4/ping_group_range", "0 0"))
warn("Cannot set ping_group_range, ICMP requests might fail");
- if (!gethostname(hostname + sizeof(HOSTNAME_PREFIX) - 1,
- HOST_NAME_MAX + 1 - sizeof(HOSTNAME_PREFIX)) ||
- errno == ENAMETOOLONG) {
+ conf_hostname_len = strlen(a->c->hostname);
+ if (conf_hostname_len > 0) {
+ if (sethostname(a->c->hostname, conf_hostname_len))
+ warn("Unable to set configured hostname");
+ } else if (!gethostname(hostname + sizeof(HOSTNAME_PREFIX) - 1,
+ HOST_NAME_MAX + 1 - sizeof(HOSTNAME_PREFIX)) ||
+ errno == ENAMETOOLONG) {
hostname[HOST_NAME_MAX] = '\0';
if (sethostname(hostname, strlen(hostname)))
warn("Unable to set pasta-prefixed hostname");
@@ -208,7 +222,12 @@ static int pasta_spawn_cmd(void *arg)
sigaddset(&set, SIGUSR1);
sigwaitinfo(&set, NULL);
- a = (const struct pasta_spawn_cmd_arg *)arg;
+ /* Once exec()ed this process is more valuable, and easier to see and
+ * clean up. Let us outlive our parent now.
+ */
+ if (prctl(PR_SET_PDEATHSIG, 0))
+ die_perror("Couldn't clear PR_SET_PDEATHSIG");
+
execvp(a->exe, a->argv);
die_perror("Failed to start command or shell");
@@ -217,21 +236,19 @@ static int pasta_spawn_cmd(void *arg)
/**
* pasta_start_ns() - Fork command in new namespace if target ns is not given
* @c: Execution context
- * @uid: UID we're running as in the init namespace
- * @gid: GID we're running as in the init namespace
* @argc: Number of arguments for spawned command
* @argv: Command to spawn and arguments
*/
-void pasta_start_ns(struct ctx *c, uid_t uid, gid_t gid,
- int argc, char *argv[])
+void pasta_start_ns(struct ctx *c, int argc, char *argv[])
{
+ int flags = CLONE_NEWIPC | CLONE_NEWNET | CLONE_NEWUTS | CLONE_NEWNS;
char ns_fn_stack[NS_FN_STACK_SIZE]
__attribute__ ((aligned(__alignof__(max_align_t))));
struct pasta_spawn_cmd_arg arg = {
.exe = argv[0],
.argv = argv,
+ .c = c,
};
- char uidmap[BUFSIZ], gidmap[BUFSIZ];
char *sh_argv[] = { NULL, NULL };
char sh_arg0[PATH_MAX + 1];
sigset_t set;
@@ -240,19 +257,6 @@ void pasta_start_ns(struct ctx *c, uid_t uid, gid_t gid,
if (!c->debug)
c->quiet = 1;
- /* Configure user and group mappings */
- if (snprintf_check(uidmap, BUFSIZ, "0 %u 1", uid))
- die_perror("Can't build uidmap");
-
- if (snprintf_check(gidmap, BUFSIZ, "0 %u 1", gid))
- die_perror("Can't build gidmap");
-
- if (write_file("/proc/self/uid_map", uidmap) ||
- write_file("/proc/self/setgroups", "deny") ||
- write_file("/proc/self/gid_map", gidmap)) {
- warn("Couldn't configure user mappings");
- }
-
if (argc == 0) {
arg.exe = getenv("SHELL");
if (!arg.exe)
@@ -271,10 +275,11 @@ void pasta_start_ns(struct ctx *c, uid_t uid, gid_t gid,
sigaddset(&set, SIGUSR1);
sigprocmask(SIG_BLOCK, &set, NULL);
+ if (!c->no_pidns)
+ flags |= CLONE_NEWPID;
+
pasta_child_pid = do_clone(pasta_spawn_cmd, ns_fn_stack,
- sizeof(ns_fn_stack),
- CLONE_NEWIPC | CLONE_NEWPID | CLONE_NEWNET |
- CLONE_NEWUTS | CLONE_NEWNS | SIGCHLD,
+ sizeof(ns_fn_stack), flags | SIGCHLD,
(void *)&arg);
if (pasta_child_pid == -1)
@@ -286,18 +291,87 @@ void pasta_start_ns(struct ctx *c, uid_t uid, gid_t gid,
}
/**
+ * pasta_conf_addrs() - Configure addresses for one address family in namespace
+ * @c: Execution context
+ * @af: Address family (AF_INET or AF_INET6)
+ * @ifi: Host interface index for this address family
+ * @no_copy: If true, set configured address; if false, copy from host
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int pasta_conf_addrs(const struct ctx *c, sa_family_t af, int ifi,
+ bool no_copy)
+{
+ const struct guest_addr *a;
+
+ if (!ifi)
+ return 0;
+
+ if (!no_copy)
+ return nl_addr_dup(nl_sock, ifi, nl_sock_ns, c->pasta_ifi, af);
+
+ a = fwd_get_addr(c, af, true, CONF_ADDR_ANY);
+ if (!a)
+ return 0;
+
+ if (af == AF_INET) {
+ const struct in_addr *a4;
+ int plen = 0;
+
+ a4 = inany_prefix_v4(&a->addr, a->prefix_len, &plen);
+ return nl_addr_set(nl_sock_ns, c->pasta_ifi, AF_INET,
+ a4, plen);
+ }
+
+ return nl_addr_set(nl_sock_ns, c->pasta_ifi, AF_INET6,
+ &a->addr.a6, a->prefix_len);
+}
+
+/**
+ * pasta_conf_routes() - Configure routes for one address family in namespace
+ * @c: Execution context
+ * @af: Address family (AF_INET or AF_INET6)
+ * @ifi: Host interface index for this address family
+ * @no_copy: If true, set default route; if false, copy routes from host
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int pasta_conf_routes(struct ctx *c, sa_family_t af, int ifi,
+ bool no_copy)
+{
+ const void *gw;
+
+ if (af == AF_INET)
+ gw = &c->ip4.guest_gw;
+ else
+ gw = &c->ip6.guest_gw;
+
+ if (!ifi)
+ return 0;
+
+ if (!no_copy)
+ return nl_route_dup(nl_sock, ifi, nl_sock_ns, c->pasta_ifi, af);
+
+ return nl_route_set_def(nl_sock_ns, c->pasta_ifi, af, gw);
+}
+
+/**
* pasta_ns_conf() - Set up loopback and tap interfaces in namespace as needed
* @c: Execution context
*/
void pasta_ns_conf(struct ctx *c)
{
- int rc = 0;
+ unsigned int flags = IFF_UP;
+ int rc;
rc = nl_link_set_flags(nl_sock_ns, 1 /* lo */, IFF_UP, IFF_UP);
if (rc < 0)
die("Couldn't bring up loopback interface in namespace: %s",
strerror_(-rc));
+ if (c->splice_only)
+ return;
+
/* Get or set MAC in target namespace */
if (MAC_IS_ZERO(c->guest_mac))
nl_link_get_mac(nl_sock_ns, c->pasta_ifi, c->guest_mac);
@@ -307,102 +381,68 @@ void pasta_ns_conf(struct ctx *c)
die("Couldn't set MAC address in namespace: %s",
strerror_(-rc));
- if (c->pasta_conf_ns) {
- unsigned int flags = IFF_UP;
-
- if (c->mtu != -1)
- nl_link_set_mtu(nl_sock_ns, c->pasta_ifi, c->mtu);
-
- if (c->ifi6) /* Avoid duplicate address detection on link up */
- flags |= IFF_NOARP;
-
- nl_link_set_flags(nl_sock_ns, c->pasta_ifi, flags, flags);
+ proto_update_l2_buf(c->guest_mac);
- if (c->ifi4) {
- if (c->ip4.no_copy_addrs) {
- rc = nl_addr_set(nl_sock_ns, c->pasta_ifi,
- AF_INET,
- &c->ip4.addr,
- c->ip4.prefix_len);
- } else {
- rc = nl_addr_dup(nl_sock, c->ifi4,
- nl_sock_ns, c->pasta_ifi,
- AF_INET);
- }
+ if (!c->pasta_conf_ns)
+ return;
- if (rc < 0) {
- die("Couldn't set IPv4 address(es) in namespace: %s",
- strerror_(-rc));
- }
-
- if (c->ip4.no_copy_routes) {
- rc = nl_route_set_def(nl_sock_ns, c->pasta_ifi,
- AF_INET,
- &c->ip4.guest_gw);
- } else {
- rc = nl_route_dup(nl_sock, c->ifi4, nl_sock_ns,
- c->pasta_ifi, AF_INET);
- }
-
- if (rc < 0) {
+ if (c->mtu)
+ nl_link_set_mtu(nl_sock_ns, c->pasta_ifi, c->mtu);
+
+ if (c->ifi6) /* Avoid duplicate address detection on link up */
+ flags |= IFF_NOARP;
+
+ nl_link_set_flags(nl_sock_ns, c->pasta_ifi, flags, flags);
+
+ if (c->ifi4) {
+ rc = pasta_conf_addrs(c, AF_INET, c->ifi4,
+ c->ip4.no_copy_addrs);
+ if (c->ifi4 == -1 && rc == -ENOTSUP) {
+ warn("IPv4 not supported, disabling");
+ c->ifi4 = 0;
+ } else if (rc < 0) {
+ die("Couldn't set IPv4 address(es) in namespace: %s",
+ strerror_(-rc));
+ } else {
+ rc = pasta_conf_routes(c, AF_INET, c->ifi4,
+ c->ip4.no_copy_routes);
+ if (rc < 0)
die("Couldn't set IPv4 route(s) in guest: %s",
strerror_(-rc));
- }
}
+ }
- if (c->ifi6) {
- rc = nl_addr_get_ll(nl_sock_ns, c->pasta_ifi,
- &c->ip6.addr_ll_seen);
- if (rc < 0) {
- warn("Can't get LL address from namespace: %s",
- strerror_(-rc));
- }
-
- rc = nl_addr_set_ll_nodad(nl_sock_ns, c->pasta_ifi);
- if (rc < 0) {
- warn("Can't set nodad for LL in namespace: %s",
- strerror_(-rc));
- }
-
- /* We dodged DAD: re-enable neighbour solicitations */
- nl_link_set_flags(nl_sock_ns, c->pasta_ifi,
- 0, IFF_NOARP);
-
- if (c->ip6.no_copy_addrs) {
- if (!IN6_IS_ADDR_UNSPECIFIED(&c->ip6.addr)) {
- rc = nl_addr_set(nl_sock_ns,
- c->pasta_ifi, AF_INET6,
- &c->ip6.addr, 64);
- }
- } else {
- rc = nl_addr_dup(nl_sock, c->ifi6,
- nl_sock_ns, c->pasta_ifi,
- AF_INET6);
- }
-
- if (rc < 0) {
- die("Couldn't set IPv6 address(es) in namespace: %s",
- strerror_(-rc));
- }
-
- if (c->ip6.no_copy_routes) {
- rc = nl_route_set_def(nl_sock_ns, c->pasta_ifi,
- AF_INET6,
- &c->ip6.guest_gw);
- } else {
- rc = nl_route_dup(nl_sock, c->ifi6,
- nl_sock_ns, c->pasta_ifi,
- AF_INET6);
- }
-
- if (rc < 0) {
+ if (c->ifi6) {
+ rc = nl_addr_get_ll(nl_sock_ns, c->pasta_ifi,
+ &c->ip6.addr_ll_seen);
+ if (rc < 0)
+ warn("Can't get LL address from namespace: %s",
+ strerror_(-rc));
+
+ rc = nl_addr_set_ll_nodad(nl_sock_ns, c->pasta_ifi);
+ if (rc < 0)
+ warn("Can't set nodad for LL in namespace: %s",
+ strerror_(-rc));
+
+ /* We dodged DAD: re-enable neighbour solicitations */
+ nl_link_set_flags(nl_sock_ns, c->pasta_ifi, 0, IFF_NOARP);
+
+ rc = pasta_conf_addrs(c, AF_INET6, c->ifi6,
+ c->ip6.no_copy_addrs);
+ if (c->ifi6 == -1 && rc == -ENOTSUP) {
+ warn("IPv6 not supported, disabling");
+ c->ifi6 = 0;
+ } else if (rc < 0) {
+ die("Couldn't set IPv6 address(es) in namespace: %s",
+ strerror_(-rc));
+ } else {
+ rc = pasta_conf_routes(c, AF_INET6, c->ifi6,
+ c->ip6.no_copy_routes);
+ if (rc < 0)
die("Couldn't set IPv6 route(s) in guest: %s",
strerror_(-rc));
- }
}
}
-
- proto_update_l2_buf(c->guest_mac, NULL);
}
/**
@@ -435,7 +475,6 @@ static int pasta_netns_quit_timer(void)
*/
void pasta_netns_quit_init(const struct ctx *c)
{
- struct epoll_event ev = { .events = EPOLLIN };
int flags = O_NONBLOCK | O_CLOEXEC;
struct statfs s = { 0 };
bool try_inotify = true;
@@ -478,8 +517,8 @@ void pasta_netns_quit_init(const struct ctx *c)
die("netns monitor file number %i too big, exiting", fd);
ref.fd = fd;
- ev.data.u64 = ref.u64;
- epoll_ctl(c->epollfd, EPOLL_CTL_ADD, fd, &ev);
+
+ epoll_add(c->epollfd, EPOLLIN, ref);
}
/**
@@ -487,19 +526,25 @@ void pasta_netns_quit_init(const struct ctx *c)
* @c: Execution context
* @inotify_fd: inotify file descriptor with watch on namespace directory
*/
-void pasta_netns_quit_inotify_handler(struct ctx *c, int inotify_fd)
+void pasta_netns_quit_inotify_handler(const struct ctx *c, int inotify_fd)
{
- char buf[sizeof(struct inotify_event) + NAME_MAX + 1];
- const struct inotify_event *in_ev = (struct inotify_event *)buf;
+ char buf[sizeof(struct inotify_event) + NAME_MAX + 1]
+ __attribute__ ((aligned(__alignof__(struct inotify_event))));
+ const struct inotify_event *ev;
+ ssize_t n;
+ char *p;
- if (read(inotify_fd, buf, sizeof(buf)) < (ssize_t)sizeof(*in_ev))
+ if ((n = read(inotify_fd, buf, sizeof(buf))) < (ssize_t)sizeof(*ev))
return;
- if (strncmp(in_ev->name, c->netns_base, sizeof(c->netns_base)))
- return;
+ for (p = buf; p < buf + n; p += sizeof(*ev) + ev->len) {
+ ev = (const struct inotify_event *)p;
- info("Namespace %s is gone, exiting", c->netns_base);
- _exit(EXIT_SUCCESS);
+ if (!strncmp(ev->name, c->netns_base, sizeof(c->netns_base))) {
+ info("Namespace %s is gone, exiting", c->netns_base);
+ passt_exit(EXIT_SUCCESS);
+ }
+ }
}
/**
@@ -507,7 +552,7 @@ void pasta_netns_quit_inotify_handler(struct ctx *c, int inotify_fd)
* @c: Execution context
* @ref: epoll reference for timer descriptor
*/
-void pasta_netns_quit_timer_handler(struct ctx *c, union epoll_ref ref)
+void pasta_netns_quit_timer_handler(const struct ctx *c, union epoll_ref ref)
{
uint64_t expirations;
ssize_t n;
@@ -525,7 +570,7 @@ void pasta_netns_quit_timer_handler(struct ctx *c, union epoll_ref ref)
return;
info("Namespace %s is gone, exiting", c->netns_base);
- _exit(EXIT_SUCCESS);
+ passt_exit(EXIT_SUCCESS);
}
close(fd);