diff options
Diffstat (limited to 'pasta.c')
| -rw-r--r-- | pasta.c | 313 |
1 files changed, 179 insertions, 134 deletions
@@ -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); |
