#include "lib/birdlib.h" #include "lib/cbor.h" #include "lib/hash.h" #include "lib/io-loop.h" #include "lib/resource.h" #include "lib/socket.h" #include "flock/flock.h" #include #include /** * Main control socket **/ static struct birdloop *hcs_loop; static pool *hcs_pool; OBSREF(struct shutdown_placeholder) hcs_shutdown_placeholder; static int hcs_rx(sock *s, uint size) { s64 sz = hcs_parse(s->data, s->rbuf, size); if (sz < 0) { log(L_INFO "CLI parser error at position %ld: %s", -sz-1, hcs_error(s->data)); sk_close(s); return 0; /* Must return 0 when closed */ } if (!hcs_complete(s->data)) { ASSERT_DIE(sz == size); return 1; } log(L_INFO "Parsed command."); /* TODO do something more */ hcs_parser_cleanup(s->data); s->data = hcs_parser_init(s); if (sz == size) return 1; memmove(s->rbuf, s->rbuf + sz, size - sz); return hcs_rx(s, size - sz); } static void hcs_err(sock *s, int err) { log(L_INFO "CLI dropped: %s", strerror(err)); hcs_parser_cleanup(s->data); sk_close(s); } static int hcs_connect(sock *s, uint size UNUSED) { log(L_INFO "CLI connected: %p", s); s->rx_hook = hcs_rx; s->err_hook = hcs_err; s->data = hcs_parser_init(s); return 1; } static void hcs_connect_err(sock *s UNUSED, int err) { ASSERT_DIE(err); log(L_INFO "Failed to accept CLI connection: %s", strerror(err)); } static void hcs_stopped(void *data) { ASSERT_DIE(data == hcs_loop); hcs_pool = NULL; hcs_loop = NULL; OBSREF_CLEAR(hcs_shutdown_placeholder); unlink(flock_config.control_socket_path); } static void hcs_shutdown(void *_data UNUSED) { birdloop_stop(hcs_loop, hcs_stopped, hcs_loop); } void hypervisor_control_socket(void) { struct birdloop *loop = hcs_loop = birdloop_new(&root_pool, DOMAIN_ORDER(control), 0, "Control socket"); birdloop_enter(loop); pool *p = hcs_pool = rp_new(birdloop_pool(loop), birdloop_domain(loop), "Control socket pool"); sock *s = sk_new(p); s->type = SK_UNIX_PASSIVE; s->rx_hook = hcs_connect; s->err_hook = hcs_connect_err; s->rbsize = 1024; s->tbsize = 1024; unlink(flock_config.control_socket_path); if (sk_open_unix(s, loop, flock_config.control_socket_path) < 0) die("Can't create control socket %s: %m", flock_config.control_socket_path); ev_send(&shutdown_event_list, ev_new_init(p, hcs_shutdown, NULL)); birdloop_leave(loop); OBSREF_SET(hcs_shutdown_placeholder, &shutdown_placeholder); } /** * Exposed process' communication structure **/ static struct hypervisor_exposed { pool *p; sock *s; struct birdloop *loop; } he; /** * Exposed process' parent side (requestor) **/ static int hypervisor_telnet_connected(sock *sk, uint size UNUSED) { int fd = accept(sk->fd, NULL, 0); if (fd < 0) { if (errno == EAGAIN) return 1; log(L_ERR "failed to accept telnet connection: %m"); return 0; } int e = fork(); if (e < 0) { log(L_ERR "failed to fork: %m"); return 0; } if (e) { log(L_INFO "telnet connected"); close(fd); return 1; } close(0); close(1); close(2); dup2(fd, 0); dup2(fd, 1); e = execl("/usr/sbin/telnetd", "telnetd", "-E", "/bin/bash", NULL); log(L_ERR "failed to execl: %m"); exit(42); } static int hypervisor_exposed_parent_rx(sock *sk, uint size UNUSED) { int sfd = -1; byte buf[128], cbuf[CMSG_SPACE(sizeof sfd)]; struct iovec v = { .iov_base = buf, .iov_len = sizeof buf, }; struct msghdr m = { .msg_iov = &v, .msg_iovlen = 1, .msg_control = &cbuf, .msg_controllen = sizeof cbuf, }; int e = recvmsg(sk->fd, &m, 0); struct cmsghdr *c = CMSG_FIRSTHDR(&m); memcpy(&sfd, CMSG_DATA(c), sizeof sfd); ASSERT_DIE(buf[0] == 0xa1); ASSERT_DIE(buf[1] == 0x21); ASSERT_DIE(buf[2] == 0x19); u16 port = ntohs(*((u16 *) &buf[3])); log(L_INFO "RX %d bytes, fd %d, port %u", e, sfd, port); sock *skl = sk_new(sk->pool); skl->type = SK_MAGIC; skl->rx_hook = hypervisor_telnet_connected; skl->fd = sfd; if (sk_open(skl, sk->loop) < 0) bug("Telnet listener: sk_open failed"); // hexp_received_telnet(port, sfd); return 0; } static void hypervisor_exposed_parent_err(sock *sk, int e UNUSED) { sk_close(sk); } /** * Exposed process' child side (executor) **/ static int hypervisor_exposed_child_rx(sock *sk, uint size UNUSED) { byte buf[128]; struct iovec v = { .iov_base = buf, .iov_len = sizeof buf, }; struct msghdr m = { .msg_iov = &v, .msg_iovlen = 1, }; int e = recvmsg(sk->fd, &m, 0); if (e != 3) { log(L_ERR "Got something strange: %d, %m", e); return 0; } /* Only one thing is actually supported for now: opening a listening socket */ int sfd = socket(AF_INET6, SOCK_STREAM, 0); if (sfd < 0) { log(L_ERR "Failed to socket(): %m"); return 0; } while (1) { u32 r = (random_u32() % (32768-1024) + 1024); union { struct sockaddr_in6 sin; struct sockaddr a; } sin = { .sin = { .sin6_family = AF_INET6, .sin6_port = htons(r), .sin6_addr.s6_addr[15] = 1, }, }; int e = bind(sfd, &sin.a, sizeof sin); if (e < 0) if (errno == EADDRINUSE) { log(L_INFO "Tried to bind to %u but already in use", r); continue; } else { log(L_ERR "Failed to bind to %u: %m", r); close(sfd); return 0; } e = listen(sfd, 10); if (e < 0) { log(L_ERR "Failed to listen(): %m", e); close(sfd); return 0; } log(L_INFO "SUCCESS"); byte outbuf[128]; linpool *lp = lp_new(sk->pool); struct cbor_writer *cw = cbor_init(outbuf, sizeof outbuf, lp); cbor_open_block_with_length(cw, 1); cbor_add_int(cw, -2); cbor_add_int(cw, r); struct iovec v = { .iov_base = outbuf, .iov_len = cw->pt, }; byte cbuf[CMSG_SPACE(sizeof sfd)]; struct msghdr m = { .msg_iov = &v, .msg_iovlen = 1, .msg_control = &cbuf, .msg_controllen = sizeof cbuf, }; struct cmsghdr *c = CMSG_FIRSTHDR(&m); c->cmsg_level = SOL_SOCKET; c->cmsg_type = SCM_RIGHTS; c->cmsg_len = CMSG_LEN(sizeof sfd); memcpy(CMSG_DATA(c), &sfd, sizeof sfd); e = sendmsg(sk->fd, &m, 0); if (e < 0) log(L_ERR "Failed to send socket: %m"); close(sfd); return 0; } } static void hypervisor_exposed_child_err(sock *sk UNUSED, int e UNUSED) { } /** * Common init code */ void hypervisor_exposed_fork(void) { int fds[2], e; /* create socketpair before forking to do communication */ e = socketpair(AF_UNIX, SOCK_STREAM, 0, fds); if (e < 0) die("Failed to create internal socketpair: %m"); e = fork(); if (e < 0) die("Failed to fork exposed: %m"); /* Create the communication channel (this runs twice!) */ he.loop = birdloop_new(&root_pool, DOMAIN_ORDER(proto), 0, "Exposed interlink"); birdloop_enter(he.loop); he.p = rp_new(birdloop_pool(he.loop), birdloop_domain(he.loop), "Exposed interlink pool"); he.s = sk_new(he.p); he.s->type = SK_MAGIC; /* Set the hooks and fds according to the side we are at */ he.s->rx_hook = e ? hypervisor_exposed_parent_rx : hypervisor_exposed_child_rx; he.s->err_hook = e ? hypervisor_exposed_parent_err : hypervisor_exposed_child_err; he.s->fd = fds[!!e]; close(fds[!e]); if (sk_open(he.s, he.loop) < 0) bug("Exposed parent: sk_open failed"); birdloop_leave(he.loop); /* Now there is a loop both in child and parent, prepared to read the socket. * There is only one difference. Whereas the parent has to continue its run * to do other duties, the child is stuck here forever. */ if (e) return; /** * Child only **/ /* Run worker threads */ struct thread_config tc = {}; bird_thread_commit(&tc); /* Wait for Godot */ birdloop_minimalist_main(); } /** * Hypervisor's mapping between external ports and names */ #define HEXP_TELNET_KEY(tp) tp->name, tp->hash #define HEXP_TELNET_NEXT(tp) tp->next #define HEXP_TELNET_EQ(a,h,b,i) ((h) == (i)) && (!(a) && !(b) || !strcmp(a,b)) #define HEXP_TELNET_FN(a,h) h struct hexp_telnet_port { struct hexp_telnet_port *next; const char *name; uint hash; uint port; int fd; }; static struct hexp_telnet { pool *pool; HASH(struct hexp_telnet_port) port_hash; } hexp_telnet; static void hexp_init_telnet(void) { pool *p = rp_new(hcs_pool, hcs_pool->domain, "Hypervisor exposed telnets"); hexp_telnet.pool = p; HASH_INIT(hexp_telnet.port_hash, p, 6); } static void hexp_have_telnet(sock *s, struct hexp_telnet_port *p) { struct linpool *lp = lp_new(s->pool); struct cbor_writer *cw = cbor_init(s->tbuf, s->tbsize, lp); cbor_open_block_with_length(cw, 1); cbor_add_int(cw, -2); cbor_add_int(cw, p->port); sk_send(s, cw->pt); rfree(lp); } void hexp_get_telnet(sock *s, const char *name) { if (!hexp_telnet.pool) hexp_init_telnet(); uint h = name ? mem_hash(name, strlen(name)) : 0; struct hexp_telnet_port *p = HASH_FIND(hexp_telnet.port_hash, HEXP_TELNET, name, h); if (p) if (p->port) return hexp_have_telnet(s, p); else return; /* Waiting for the exposed part */ uint8_t buf[64]; linpool *lp = lp_new(s->pool); struct cbor_writer *cw = cbor_init(buf, sizeof buf, lp); cbor_open_block_with_length(cw, 1); cbor_add_int(cw, 1); cw->cbor[cw->pt++] = 0xf6; struct iovec v = { .iov_base = buf, .iov_len = cw->pt, }; struct msghdr m = { .msg_iov = &v, .msg_iovlen = 1, }; int e = sendmsg(he.s->fd, &m, 0); if (e != cw->pt) bug("sendmsg error handling not implemented, got %d (%m)", e); rfree(lp); }