0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-12-22 09:41:54 +00:00

Flock: telnet to machines

This commit is contained in:
Maria Matejka 2024-09-27 21:38:56 +02:00
parent d36ad6af6a
commit 56aff8ef39
5 changed files with 210 additions and 132 deletions

View File

@ -8,6 +8,7 @@
#include <poll.h>
#include <sched.h>
#include <stdlib.h>
#include <sys/mount.h>
#include <sys/wait.h>
#include <unistd.h>
@ -73,6 +74,39 @@ container_poweroff(int fd, int sig)
exit(0);
}
static void
container_zombie(void)
{
zombie = 0;
log(L_INFO "Zombie elimination routine invoked.");
while (1) {
int status;
pid_t p = waitpid(-1, &status, WNOHANG);
if (p < 0)
{
if (errno != ECHILD)
log(L_ERR "Zombie elimination failed: %m");
return;
}
if (p == 0)
return;
const char *coreinfo = WCOREDUMP(status) ? " (core dumped)" : "";
if (WIFEXITED(status))
log(L_INFO "Process %d ended with status %d%s", p, WEXITSTATUS(status), coreinfo);
else if (WIFSIGNALED(status))
log(L_INFO "Process %d exited by signal %d (%s)%s", p, WTERMSIG(status), strsignal(WTERMSIG(status)), coreinfo);
else
log(L_ERR "Process %d exited with a strange status %d", p, status);
}
}
#define SYSCALL(x, ...) ({ int e = x(__VA_ARGS__); if (e < 0) die("Failed to run %s at %s:%d: %m", #x, __FILE__, __LINE__); e; })
static void
container_mainloop(int fd)
{
@ -82,6 +116,10 @@ container_mainloop(int fd)
signal(SIGINT, container_poweroff_sighandler);
signal(SIGCHLD, container_child_sighandler);
/* Remounting proc to reflect the new PID namespace */
SYSCALL(mount, "none", "/", NULL, MS_REC | MS_PRIVATE, NULL);
SYSCALL(mount, "proc", "/proc", "proc", MS_NOSUID | MS_NODEV | MS_NOEXEC, NULL);
/* TODO: mount overlayfs and chroot */
while (1)
{
@ -95,13 +133,31 @@ container_mainloop(int fd)
int res = ppoll(&pfd, 1, NULL, &newmask);
if ((res < 0) && (errno != EINTR))
log(L_INFO "ppoll returned -1: %m");
if (poweroff)
container_poweroff(fd, poweroff);
if (zombie)
container_zombie();
if (pfd.revents & POLLIN)
{
byte buf[128];
ssize_t sz = read(fd, buf, sizeof buf);
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 sz = recvmsg(fd, &m, 0);
if (sz < 0)
{
log(L_ERR "error reading data from control socket: %m");
@ -116,11 +172,47 @@ container_mainloop(int fd)
container_poweroff(fd, 0);
break;
case 0x21:
ASSERT_DIE(buf[2] == 0xf6);
struct cmsghdr *c = CMSG_FIRSTHDR(&m);
memcpy(&sfd, CMSG_DATA(c), sizeof sfd);
int e = fork();
if (e < 0) bug("Cannot fork: %m");
if (e == 0) {
int fd = accept(sfd, NULL, 0);
if (fd < 0)
{
log(L_ERR "failed to accept telnet connection: %m");
exit(1);
}
log(L_INFO "telnet connected");
close(0);
close(1);
close(2);
dup2(fd, 0);
dup2(fd, 1);
/* Unblock signals */
sigset_t newmask;
sigemptyset(&newmask);
sigprocmask(SIG_SETMASK, &newmask, NULL);
/* Exec the telnet */
e = execl("/usr/sbin/telnetd", "telnetd", "-E", "/bin/bash", NULL);
log(L_ERR "failed to execl: %m");
exit(42);
}
close(sfd);
break;
default:
log(L_ERR "unknown command on control socket: %d", buf[1]);
break;
}
}
/* TODO: check for telnet socket */
log(L_INFO "woken up, res %d (%m)!", res);
}
}
@ -356,6 +448,14 @@ crt_err(sock *s, int err UNUSED)
crt->ccc = NULL;
}
int
container_ctl_fd(const char *name)
{
uint h = mem_hash(name, strlen(name));
struct container_runtime *crt = HASH_FIND(hcf.hash, CRT, name, h);
return (crt && crt->s) ? crt->s->fd : -1;
}
static void
container_created(callback *cb)
{

View File

@ -186,15 +186,20 @@ hcs_parse(struct cbor_parser_context *ctx, const byte *buf, s64 size)
break;
case 3: /* telnet listener open */
if ((ctx->type != 7) || (ctx->value != 22))
CBOR_PARSER_ERROR("Expected null, got %u-%u", ctx->type, ctx->value);
/* TODO: allow this also for machines */
if ((ctx->type == 7) && (ctx->value == 22))
{
hexp_get_telnet(ctx->sock, NULL);
ctx->major_state = 1;
break;
}
log(L_INFO "Requested telnet open");
else if (ctx->type != 3)
CBOR_PARSER_ERROR("Expected null or string, got %u-%u", ctx->type, ctx->value);
hexp_get_telnet(ctx->sock, NULL);
ASSERT_DIE(!ctx->target_buf);
ctx->cfg.cf.name = ctx->target_buf = lp_alloc(ctx->lp, ctx->value + 1);
ctx->target_len = ctx->value;
ctx->major_state = 1;
break;
case 4: /* telnet listener close */
@ -364,6 +369,11 @@ hcs_parse(struct cbor_parser_context *ctx, const byte *buf, s64 size)
/* Read completely! */
switch (ctx->major_state)
{
case 3:
hexp_get_telnet(ctx->sock, ctx->cfg.cf.name);
ctx->major_state = 1;
break;
case 5:
/* Actually not this one */
CBOR_PARSER_ERROR("NOT IMPLEMENTED YET");

View File

@ -105,6 +105,12 @@ def container_stop(hypervisor: str, name: str):
for k,v in msg(hypervisor, { 4: { 0: name, }}).items():
print(k,v)
@handler
def container_telnet(hypervisor: str, name: str):
for k,v in msg(hypervisor, { 1: name}).items():
assert(k == -2)
os.execlp("telnet", "telnet", "localhost", str(v))
try:
binname = sys.argv.pop(0)
except Exception as e:

View File

@ -48,6 +48,7 @@ union flock_machine_config {
void hypervisor_container_request(sock *s, const char *name, const char *basedir, const char *workdir);
void hypervisor_container_shutdown(sock *s, const char *name);
int container_ctl_fd(const char *name);
extern event reboot_event, poweroff_event;
extern event_list shutdown_event_list;

View File

@ -127,6 +127,8 @@ static struct hypervisor_exposed {
pool *p;
sock *s;
struct birdloop *loop;
const char *port_name;
sock *port_sreq;
} he;
/**
@ -136,7 +138,6 @@ static struct hypervisor_exposed {
static void hexp_received_telnet(void *);
struct hexp_received_telnet {
event e;
struct hexp_telnet_port *p;
int fd;
u16 port;
};
@ -165,6 +166,7 @@ hypervisor_telnet_connected(sock *sk, uint size UNUSED)
{
log(L_INFO "telnet connected");
close(fd);
sk_close(sk);
return 1;
}
@ -214,20 +216,12 @@ hypervisor_exposed_parent_rx(sock *sk, uint size UNUSED)
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");
struct hexp_received_telnet *hrt = mb_allocz(he.p, sizeof *hrt);
*hrt = (struct hexp_received_telnet) {
.e = {
.hook = hexp_received_telnet,
.data = hrt,
},
.p = sk->data,
.port = port,
.fd = sfd,
};
@ -413,141 +407,108 @@ hypervisor_exposed_fork(void)
* 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
#define TLIST_PREFIX hexp_telnet_requestor
#define TLIST_TYPE struct hexp_telnet_requestor
#define TLIST_ITEM n
struct hexp_telnet_requestor {
TLIST_DEFAULT_NODE;
sock *s;
struct cbor_parser_context *ctx;
};
#define TLIST_WANT_ADD_TAIL
#include "lib/tlists.h"
static void
hexp_sock_err(sock *s, int err UNUSED)
{
struct hexp_telnet_requestor *req = s->data;
s->data = req->ctx;
hexp_telnet_requestor_rem_node(hexp_telnet_requestor_enlisted(req), req);
}
struct hexp_telnet_port {
struct hexp_telnet_port *next;
const char *name;
uint hash;
uint port;
TLIST_LIST(hexp_telnet_requestor) requestors;
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);
ASSERT_DIE(s == he.port_sreq);
he.port_name = NULL;
he.port_sreq = NULL;
}
void
hexp_get_telnet(sock *s, const char *name)
{
if (!hexp_telnet.pool)
hexp_init_telnet();
ASSERT_DIE(!he.port_name);
he.port_name = name ?: "";
he.port_sreq = s;
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 && p->port)
return hexp_have_telnet(s, p);
else if (!p)
{
he.s->data = p = mb_alloc(hcs_pool, sizeof *p);
*p = (struct hexp_telnet_port) {
.name = name,
.hash = h,
.fd = -1,
};
HASH_INSERT(hexp_telnet.port_hash, HEXP_TELNET, p);
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;
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;
int e = write(he.s->fd, buf, cw->pt);
if (e != cw->pt)
bug("write error handling not implemented, got %d (%m)", e);
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);
}
rfree(lp);
s->err_paused = hexp_sock_err;
sk_pause_rx(s->loop, s);
struct hexp_telnet_requestor *req = mb_allocz(hcs_pool, sizeof *req);
req->s = s;
req->ctx = s->data;
s->data = req;
hexp_telnet_requestor_add_tail(&p->requestors, req);
}
static void hexp_received_telnet(void *_data)
{
struct hexp_received_telnet *hrt = _data;
ASSERT_DIE(!hrt->p->port);
hrt->p->port = hrt->port;
hrt->p->fd = hrt->fd;
ASSERT_DIE(he.port_name);
const char *name = he.port_name;
he.port_name = NULL;
byte outbuf[128];
linpool *lp = lp_new(hcs_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, hrt->port);
sock *s = he.port_sreq;
he.port_sreq = NULL;
WALK_TLIST_DELSAFE(hexp_telnet_requestor, r, &hrt->p->requestors)
if (name[0])
{
sk_resume_rx(r->s->loop, r->s);
memcpy(r->s->tbuf, outbuf, cw->pt);
sk_send(r->s, cw->pt);
hexp_telnet_requestor_rem_node(&hrt->p->requestors, r);
/* Transferring the received listening socket to the container */
int fd = container_ctl_fd(name);
/* TODO: unduplicate this code */
byte outbuf[128];
linpool *lp = lp_new(hcs_pool);
struct cbor_writer *cw = cbor_init(outbuf, sizeof outbuf, lp);
cbor_open_block_with_length(cw, 1);
cbor_add_int(cw, -2);
write_item(cw, 7, 22);
struct iovec v = {
.iov_base = outbuf,
.iov_len = cw->pt,
};
byte cbuf[CMSG_SPACE(sizeof hrt->fd)];
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 hrt->fd);
memcpy(CMSG_DATA(c), &hrt->fd, sizeof hrt->fd);
int e = sendmsg(fd, &m, 0);
if (e < 0)
log(L_ERR "Failed to send socket: %m");
close(hrt->fd);
}
else
{
/* Opening listener here */
sock *skl = sk_new(hcs_pool);
skl->type = SK_MAGIC;
skl->rx_hook = hypervisor_telnet_connected;
skl->data = skl;
skl->fd = hrt->fd;
if (sk_open(skl, hcs_loop) < 0)
bug("Telnet listener: sk_open failed");
}
if (s)
{
linpool *lp = lp_new(hcs_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, hrt->port);
sk_send(s, cw->pt);
sk_resume_rx(hcs_loop, s);
rfree(lp);
}
birdloop_enter(he.loop);