|
@@ -0,0 +1,293 @@
|
|
|
+/*
|
|
|
+ * Copyright (C) 2021 Daniel Golle <daniel@makrotopia.org>
|
|
|
+ *
|
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
|
+ * it under the terms of the GNU Lesser General Public License version 2.1
|
|
|
+ * as published by the Free Software Foundation
|
|
|
+ *
|
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
+ * GNU General Public License for more details.
|
|
|
+ *
|
|
|
+ * launch private ubus and netifd instances for containers with managed
|
|
|
+ * network namespace.
|
|
|
+ */
|
|
|
+
|
|
|
+#define _GNU_SOURCE /* See feature_test_macros(7) */
|
|
|
+#include <stdio.h>
|
|
|
+
|
|
|
+#include <sys/inotify.h>
|
|
|
+#include <sys/stat.h>
|
|
|
+#include <sys/types.h>
|
|
|
+
|
|
|
+#include <pwd.h>
|
|
|
+
|
|
|
+#include <linux/limits.h>
|
|
|
+
|
|
|
+#include <libubox/uloop.h>
|
|
|
+#include <libubox/utils.h>
|
|
|
+#include <libubus.h>
|
|
|
+#include <libubox/blobmsg.h>
|
|
|
+#include <libubox/blobmsg_json.h>
|
|
|
+
|
|
|
+#include "netifd.h"
|
|
|
+#include "log.h"
|
|
|
+
|
|
|
+#define INOTIFY_SZ (sizeof(struct inotify_event) + PATH_MAX + 1)
|
|
|
+
|
|
|
+static char ubusd_path[] = "/sbin/ubusd";
|
|
|
+static char netifd_path[] = "/sbin/netifd";
|
|
|
+
|
|
|
+static char *jail_name, *ubus_sock_path, *ubus_sock_dir;
|
|
|
+
|
|
|
+static char *inotify_buffer;
|
|
|
+static struct uloop_fd fd_inotify_read;
|
|
|
+struct ubus_context *ctx;
|
|
|
+static struct passwd *ubus_pw;
|
|
|
+static pid_t ns_pid;
|
|
|
+
|
|
|
+static void run_ubusd(struct uloop_timeout *t)
|
|
|
+{
|
|
|
+ static struct blob_buf req;
|
|
|
+ void *ins, *in, *cmd;
|
|
|
+ uint32_t id;
|
|
|
+
|
|
|
+ blob_buf_init(&req, 0);
|
|
|
+ blobmsg_add_string(&req, "name", jail_name);
|
|
|
+ ins = blobmsg_open_table(&req, "instances");
|
|
|
+ in = blobmsg_open_table(&req, "ubus");
|
|
|
+ cmd = blobmsg_open_array(&req, "command");
|
|
|
+ blobmsg_add_string(&req, "", ubusd_path);
|
|
|
+ blobmsg_add_string(&req, "", "-s");
|
|
|
+ blobmsg_add_string(&req, "", ubus_sock_path);
|
|
|
+ blobmsg_close_array(&req, cmd);
|
|
|
+
|
|
|
+ if (ubus_pw) {
|
|
|
+ blobmsg_add_string(&req, "user", "ubus");
|
|
|
+ blobmsg_add_string(&req, "group", "ubus");
|
|
|
+ }
|
|
|
+
|
|
|
+ blobmsg_close_table(&req, in);
|
|
|
+ blobmsg_close_table(&req, ins);
|
|
|
+
|
|
|
+ if (!ubus_lookup_id(ctx, "container", &id))
|
|
|
+ ubus_invoke(ctx, id, "add", req.head, NULL, NULL, 3000);
|
|
|
+
|
|
|
+ blob_buf_free(&req);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static void run_netifd(struct uloop_timeout *t)
|
|
|
+{
|
|
|
+ static struct blob_buf req;
|
|
|
+ void *ins, *in, *cmd, *jail, *setns, *setnso, *namespaces, *mount;
|
|
|
+ char *resolvconf_dir, *resolvconf;
|
|
|
+ uint32_t id;
|
|
|
+
|
|
|
+ uloop_fd_delete(&fd_inotify_read);
|
|
|
+ close(fd_inotify_read.fd);
|
|
|
+
|
|
|
+ if (asprintf(&resolvconf_dir, "/tmp/resolv.conf-%s.d", jail_name) == -1)
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (asprintf(&resolvconf, "%s/resolv.conf.auto", resolvconf_dir) == -1) {
|
|
|
+ free(resolvconf_dir);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ blob_buf_init(&req, 0);
|
|
|
+ blobmsg_add_string(&req, "name", jail_name);
|
|
|
+ ins = blobmsg_open_table(&req, "instances");
|
|
|
+ in = blobmsg_open_table(&req, "netifd");
|
|
|
+
|
|
|
+ cmd = blobmsg_open_array(&req, "command");
|
|
|
+ blobmsg_add_string(&req, "", netifd_path);
|
|
|
+ blobmsg_add_string(&req, "", "-r");
|
|
|
+ blobmsg_add_string(&req, "", resolvconf);
|
|
|
+ blobmsg_add_string(&req, "", "-s");
|
|
|
+ blobmsg_add_string(&req, "", ubus_sock_path);
|
|
|
+ blobmsg_close_array(&req, cmd);
|
|
|
+
|
|
|
+ jail = blobmsg_open_table(&req, "jail");
|
|
|
+
|
|
|
+ setns = blobmsg_open_array(&req, "setns");
|
|
|
+ setnso = blobmsg_open_table(&req, "");
|
|
|
+ blobmsg_add_u32(&req, "pid", ns_pid);
|
|
|
+ namespaces = blobmsg_open_array(&req, "namespaces");
|
|
|
+ blobmsg_add_string(&req, "", "net");
|
|
|
+ blobmsg_add_string(&req, "", "ipc");
|
|
|
+ blobmsg_add_string(&req, "", "uts");
|
|
|
+ blobmsg_close_array(&req, namespaces);
|
|
|
+ blobmsg_close_table(&req, setnso);
|
|
|
+ blobmsg_close_array(&req, setns);
|
|
|
+
|
|
|
+ mount = blobmsg_open_table(&req, "mount");
|
|
|
+ blobmsg_add_string(&req, ubus_sock_dir, "1");
|
|
|
+ blobmsg_add_string(&req, resolvconf_dir, "1");
|
|
|
+ blobmsg_add_string(&req, "/etc/hotplug.d", "0");
|
|
|
+ blobmsg_add_string(&req, "/lib/functions.sh", "0");
|
|
|
+ blobmsg_add_string(&req, "/lib/netifd", "0");
|
|
|
+ blobmsg_add_string(&req, "/lib/network", "0");
|
|
|
+ blobmsg_add_string(&req, "/usr/bin/logger", "0");
|
|
|
+ blobmsg_add_string(&req, "/usr/bin/jshn", "0");
|
|
|
+ blobmsg_add_string(&req, "/usr/share/libubox/jshn.sh", "0");
|
|
|
+ blobmsg_add_string(&req, "/sbin/hotplug-call", "0");
|
|
|
+ blobmsg_add_string(&req, "/sbin/udhcpc", "0");
|
|
|
+ blobmsg_close_table(&req, mount);
|
|
|
+
|
|
|
+ blobmsg_add_u8(&req, "log", 1);
|
|
|
+ blobmsg_add_u8(&req, "procfs", 1);
|
|
|
+ blobmsg_add_u8(&req, "sysfs", 1);
|
|
|
+
|
|
|
+ blobmsg_add_u8(&req, "requirejail", 1);
|
|
|
+
|
|
|
+ blobmsg_close_table(&req, jail);
|
|
|
+
|
|
|
+ blobmsg_add_u8(&req, "stdout", 1);
|
|
|
+ blobmsg_add_u8(&req, "stderr", 1);
|
|
|
+
|
|
|
+ blobmsg_close_table(&req, in);
|
|
|
+ blobmsg_close_table(&req, ins);
|
|
|
+
|
|
|
+ if (!ubus_lookup_id(ctx, "container", &id))
|
|
|
+ ubus_invoke(ctx, id, "add", req.head, NULL, NULL, 3000);
|
|
|
+
|
|
|
+ blob_buf_free(&req);
|
|
|
+ free(resolvconf_dir);
|
|
|
+ free(resolvconf);
|
|
|
+
|
|
|
+ uloop_end();
|
|
|
+}
|
|
|
+
|
|
|
+static int kill_jail_instance(char *instance)
|
|
|
+{
|
|
|
+ static struct blob_buf req;
|
|
|
+ uint32_t id;
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ blob_buf_init(&req, 0);
|
|
|
+ blobmsg_add_string(&req, "name", jail_name);
|
|
|
+ blobmsg_add_string(&req, "instance", instance);
|
|
|
+
|
|
|
+ if (ubus_lookup_id(ctx, "container", &id) ||
|
|
|
+ ubus_invoke(ctx, id, "delete", req.head, NULL, NULL, 3000)) {
|
|
|
+ ret = EIO;
|
|
|
+ }
|
|
|
+
|
|
|
+ blob_buf_free(&req);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static struct uloop_timeout netifd_start_timeout = { .cb = run_netifd, };
|
|
|
+
|
|
|
+static void inotify_read_handler(struct uloop_fd *u, unsigned int events)
|
|
|
+{
|
|
|
+ int rc;
|
|
|
+ char *p;
|
|
|
+ struct inotify_event *in;
|
|
|
+
|
|
|
+ /* read inotify events */
|
|
|
+ while ((rc = read(u->fd, inotify_buffer, INOTIFY_SZ)) == -1 && errno == EINTR);
|
|
|
+
|
|
|
+ if (rc <= 0)
|
|
|
+ return;
|
|
|
+
|
|
|
+ /* process events from buffer */
|
|
|
+ for (p = inotify_buffer;
|
|
|
+ rc - (p - inotify_buffer) >= (int)sizeof(struct inotify_event);
|
|
|
+ p += sizeof(struct inotify_event) + in->len) {
|
|
|
+ in = (struct inotify_event*)p;
|
|
|
+
|
|
|
+ if (in->len < 4)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (!strncmp("ubus", in->name, in->len))
|
|
|
+ uloop_timeout_add(&netifd_start_timeout);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static struct uloop_timeout ubus_start_timeout = { .cb = run_ubusd, };
|
|
|
+
|
|
|
+int jail_network_start(struct ubus_context *new_ctx, char *new_jail_name, pid_t new_ns_pid)
|
|
|
+{
|
|
|
+ ubus_pw = getpwnam("ubus");
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ ctx = new_ctx;
|
|
|
+ ns_pid = new_ns_pid;
|
|
|
+ jail_name = new_jail_name;
|
|
|
+
|
|
|
+ asprintf(&ubus_sock_dir, "/var/containers/ubus-%s", jail_name);
|
|
|
+ if (!ubus_sock_dir) {
|
|
|
+ ret = ENOMEM;
|
|
|
+ goto errout_dir;
|
|
|
+ }
|
|
|
+
|
|
|
+ asprintf(&ubus_sock_path, "%s/ubus", ubus_sock_dir);
|
|
|
+ if (!ubus_sock_path) {
|
|
|
+ ret = ENOMEM;
|
|
|
+ goto errout_path;
|
|
|
+ }
|
|
|
+
|
|
|
+ mkdir_p(ubus_sock_dir, 0755);
|
|
|
+ if (ubus_pw) {
|
|
|
+ ret = chown(ubus_sock_dir, ubus_pw->pw_uid, ubus_pw->pw_gid);
|
|
|
+ if (ret) {
|
|
|
+ ret = errno;
|
|
|
+ goto errout;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fd_inotify_read.fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
|
|
|
+ fd_inotify_read.cb = inotify_read_handler;
|
|
|
+ if (fd_inotify_read.fd == -1) {
|
|
|
+ ERROR("failed to initialize inotify handler\n");
|
|
|
+ ret = EIO;
|
|
|
+ goto errout;
|
|
|
+ }
|
|
|
+ uloop_fd_add(&fd_inotify_read, ULOOP_READ);
|
|
|
+
|
|
|
+ inotify_buffer = calloc(1, INOTIFY_SZ);
|
|
|
+ if (!inotify_buffer) {
|
|
|
+ ret = ENOMEM;
|
|
|
+ goto errout_inotify;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (inotify_add_watch(fd_inotify_read.fd, ubus_sock_dir, IN_CREATE) == -1) {
|
|
|
+ ERROR("failed to add inotify watch on %s\n", ubus_sock_dir);
|
|
|
+ free(inotify_buffer);
|
|
|
+ ret = EIO;
|
|
|
+ goto errout_inotify;
|
|
|
+ }
|
|
|
+
|
|
|
+ uloop_timeout_add(&ubus_start_timeout);
|
|
|
+ uloop_run();
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+errout_inotify:
|
|
|
+ close(fd_inotify_read.fd);
|
|
|
+errout:
|
|
|
+ free(ubus_sock_path);
|
|
|
+errout_path:
|
|
|
+ free(ubus_sock_dir);
|
|
|
+errout_dir:
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+int jail_network_stop(void)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = kill_jail_instance("netifd");
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ ret = kill_jail_instance("ubus");
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|