diff options
Diffstat (limited to 'jail/netifd.c')
-rw-r--r-- | jail/netifd.c | 216 |
1 files changed, 163 insertions, 53 deletions
diff --git a/jail/netifd.c b/jail/netifd.c index bedf2f4..f108993 100644 --- a/jail/netifd.c +++ b/jail/netifd.c @@ -38,6 +38,7 @@ #include "netifd.h" #include "log.h" +#include "jail.h" #define INOTIFY_SZ (sizeof(struct inotify_event) + PATH_MAX + 1) @@ -49,18 +50,34 @@ static char *jail_name, *ubus_sock_path, *ubus_sock_dir, *uci_config_network = N 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 struct ubus_context *ctx = NULL; +static struct ubus_context *netifd_ubus_ctx = NULL; + +static struct ubus_subscriber config_watch_subscribe; + /* generate /etc/config/network for jail'ed netifd */ -static int gen_jail_uci_network(const char *jailname, FILE *f) +static int gen_jail_uci_network(void) { struct uci_context *ctx = uci_alloc_context(); struct uci_package *pkg = NULL; struct uci_element *e, *t; bool has_loopback = false; int ret = 0; + FILE *ucinetf; + + /* if no network configuration is active just return */ + if (!uci_config_network) + goto uci_out; + + /* open output uci network config file */ + ucinetf = fopen(uci_config_network, "w"); + if (!ucinetf) { + ret = errno; + goto uci_out; + } /* load network uci package */ if (uci_load(ctx, uci_net, &pkg) != UCI_OK) { @@ -69,7 +86,7 @@ static int gen_jail_uci_network(const char *jailname, FILE *f) fprintf(stderr, "unable to load configuration (%s)\n", err); free(err); ret = EIO; - goto uci_out; + goto ucinetf_out; } /* remove all sections which don't match jail */ @@ -79,7 +96,7 @@ static int gen_jail_uci_network(const char *jailname, FILE *f) struct uci_ptr ptr = { .p = pkg, .s = s }; /* keep match, but remove 'jail' option and rename 'jail_ifname' */ - if (o && o->type == UCI_TYPE_STRING && !strcmp(o->v.string, jailname)) { + if (o && o->type == UCI_TYPE_STRING && !strcmp(o->v.string, jail_name)) { ptr.o = o; struct uci_option *jio = uci_lookup_option(ctx, s, "jail_device"); if (!jio) @@ -131,7 +148,10 @@ static int gen_jail_uci_network(const char *jailname, FILE *f) uci_set(ctx, &ptr4); } - ret = uci_export(ctx, f, pkg, false); + ret = uci_export(ctx, ucinetf, pkg, false); + +ucinetf_out: + fclose(ucinetf); uci_out: uci_free_context(ctx); @@ -169,21 +189,23 @@ static void run_ubusd(struct uloop_timeout *t) 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, *ucinetmount; - char uci_dir[] = "/tmp/ujail-uci-XXXXXX"; + char *resolvconf_dir, *resolvconf, *ucimount; + char uci_dir[] = "/var/containers/ujail-uci-XXXXXX"; - FILE *ucinetf; uint32_t id; bool running = false; uloop_fd_delete(&fd_inotify_read); close(fd_inotify_read.fd); + netifd_ubus_ctx = ubus_connect(ubus_sock_path); + if (!netifd_ubus_ctx) + return; + if (asprintf(&resolvconf_dir, "/tmp/resolv.conf-%s.d", jail_name) == -1) return; @@ -196,18 +218,11 @@ static void run_netifd(struct uloop_timeout *t) if (asprintf(&uci_config_network, "%s/network", uci_dir) == -1) goto netifd_out_ucidir; - if (asprintf(&ucinetmount, "%s:/etc/config/network", uci_config_network) == -1) + if (asprintf(&ucimount, "%s:/etc/config", uci_dir) == -1) goto netifd_out_ucinetconf; - ucinetf = fopen(uci_config_network, "w"); - if (!ucinetf) - goto netifd_out_ucinetmount; - - if (gen_jail_uci_network(jail_name, ucinetf)) { - fclose(ucinetf); - goto netifd_out_ucinetmount; - } - fclose(ucinetf); + if (gen_jail_uci_network()) + goto netifd_out_ucimount; blob_buf_init(&req, 0); blobmsg_add_string(&req, "name", jail_name); @@ -238,7 +253,7 @@ static void run_netifd(struct uloop_timeout *t) 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, ucinetmount, "0"); + blobmsg_add_string(&req, ucimount, "0"); blobmsg_add_string(&req, "/etc/hotplug.d", "0"); blobmsg_add_string(&req, "/lib/functions.sh", "0"); blobmsg_add_string(&req, "/lib/netifd", "0"); @@ -267,13 +282,15 @@ static void run_netifd(struct uloop_timeout *t) if (!ubus_lookup_id(ctx, "container", &id)) running = !ubus_invoke(ctx, id, "add", req.head, NULL, NULL, 3000); - blob_buf_free(&req); -netifd_out_ucinetmount: - free(ucinetmount); -netifd_out_ucinetconf: if (!running) + blob_buf_free(&req); +netifd_out_ucimount: + free(ucimount); +netifd_out_ucinetconf: + if (!running) { unlink(uci_config_network); - free(uci_config_network); + free(uci_config_network); + } netifd_out_ucidir: if (!running) rmdir(uci_dir); @@ -285,26 +302,6 @@ netifd_out_resolvconf_dir: 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) @@ -333,12 +330,96 @@ static void inotify_read_handler(struct uloop_fd *u, unsigned int events) } } +static void netns_updown(struct ubus_context *ctx, const char *name, bool start, int netns_fd) +{ + static struct blob_buf req; + uint32_t id; + + if (!ctx) + return; + + blob_buf_init(&req, 0); + if (name) + blobmsg_add_string(&req, "jail", name); + + blobmsg_add_u8(&req, "start", start); + + if (ubus_lookup_id(ctx, "network", &id) || + ubus_invoke_fd(ctx, id, "netns_updown", req.head, NULL, NULL, 3000, netns_fd)) { + INFO("ubus request failed\n"); + } + + blob_buf_free(&req); +} + +static void jail_network_reload(struct uloop_timeout *t) +{ + uint32_t id; + + if (!netifd_ubus_ctx) + return; + + if (gen_jail_uci_network()) + return; + + if (ubus_lookup_id(netifd_ubus_ctx, "network", &id)) + return; + + ubus_invoke(netifd_ubus_ctx, id, "reload", NULL, NULL, NULL, 3000); +} + +static const struct blobmsg_policy service_watch_policy = { "config", BLOBMSG_TYPE_STRING }; +static struct uloop_timeout jail_network_reload_timeout = { .cb = jail_network_reload, }; + +static int config_watch_notify_cb(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + struct blob_attr *attr; + const char *config; + + if (strcmp(method, "config.change")) + return 0; + + blobmsg_parse(&service_watch_policy, 1, &attr, blob_data(msg), blob_len(msg)); + if (!attr) + return 1; + + config = blobmsg_get_string(attr); + if (strcmp(config, "network")) + return 0; + + uloop_timeout_add(&jail_network_reload_timeout); + + return 0; +} + +static void watch_ubus_service(void) +{ + uint32_t id; + + config_watch_subscribe.cb = config_watch_notify_cb; + if (ubus_register_subscriber(ctx, &config_watch_subscribe)) { + ERROR("failed to register ubus subscriber\n"); + return; + } + + if (ubus_lookup_id(ctx, "service", &id)) + return; + + if (!ubus_subscribe(ctx, &config_watch_subscribe, id)) + return; + + ERROR("failed to subscribe %d\n", id); +} + 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; + int netns_fd; ctx = new_ctx; ns_pid = new_ns_pid; @@ -387,6 +468,13 @@ int jail_network_start(struct ubus_context *new_ctx, char *new_jail_name, pid_t goto errout_inotify; } + watch_ubus_service(); + + netns_fd = ns_open_pid("net", ns_pid); + + netns_updown(ctx, jail_name, true, netns_fd); + + close(netns_fd); uloop_timeout_add(&ubus_start_timeout); uloop_run(); @@ -402,23 +490,45 @@ errout_dir: return ret; } +static int jail_delete_instance(const char *instance) +{ + static struct blob_buf req; + uint32_t id; + + if (ubus_lookup_id(ctx, "container", &id)) + return -1; + + blob_buf_init(&req, 0); + blobmsg_add_string(&req, "name", jail_name); + blobmsg_add_string(&req, "instance", instance); + + return ubus_invoke(ctx, id, "delete", req.head, NULL, NULL, 3000); +} + int jail_network_stop(void) { - int ret; + int host_netns = open("/proc/self/ns/net", O_RDONLY); - ret = kill_jail_instance("netifd"); - if (ret) - return ret; + if (host_netns < 0) + return errno; + + netns_updown(netifd_ubus_ctx, NULL, false, host_netns); + + close(host_netns); + ubus_free(netifd_ubus_ctx); + + jail_delete_instance("netifd"); + jail_delete_instance("ubus"); if (uci_config_network) { unlink(uci_config_network); rmdir(dirname(uci_config_network)); free(uci_config_network); - }; + } - ret = kill_jail_instance("ubus"); - if (ret) - return ret; + free(ubus_sock_path); + rmdir(ubus_sock_dir); + free(ubus_sock_dir); return 0; } |