• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/procd/jail/netifd.c

  1 /*
  2  * Copyright (C) 2021 Daniel Golle <daniel@makrotopia.org>
  3  *
  4  * This program is free software; you can redistribute it and/or modify
  5  * it under the terms of the GNU Lesser General Public License version 2.1
  6  * as published by the Free Software Foundation
  7  *
  8  * This program is distributed in the hope that it will be useful,
  9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 11  * GNU General Public License for more details.
 12  *
 13  * launch private ubus and netifd instances for containers with managed
 14  * network namespace.
 15  */
 16 
 17 #define _GNU_SOURCE         /* See feature_test_macros(7) */
 18 #include <stdio.h>
 19 #include <stdlib.h>
 20 #include <string.h>
 21 #include <errno.h>
 22 #include <libgen.h>
 23 #include <fcntl.h>
 24 
 25 #include <sys/inotify.h>
 26 #include <sys/stat.h>
 27 #include <sys/types.h>
 28 
 29 #include <pwd.h>
 30 
 31 #include <linux/limits.h>
 32 
 33 #include <libubox/uloop.h>
 34 #include <libubox/utils.h>
 35 #include <libubus.h>
 36 #include <libubox/blobmsg.h>
 37 #include <libubox/blobmsg_json.h>
 38 #include <uci.h>
 39 
 40 #include "netifd.h"
 41 #include "log.h"
 42 #include "jail.h"
 43 
 44 #define INOTIFY_SZ (sizeof(struct inotify_event) + PATH_MAX + 1)
 45 
 46 static const char ubusd_path[] = "/sbin/ubusd";
 47 static const char netifd_path[] = "/sbin/netifd";
 48 static const char uci_net[] = "network";
 49 static const char ubus_sock_name[] = "ubus.sock";
 50 
 51 static char *jail_name, *ubus_sock_path, *ubus_sock_dir, *uci_config_network = NULL;
 52 
 53 static char *inotify_buffer;
 54 static struct uloop_fd fd_inotify_read;
 55 static struct passwd *ubus_pw;
 56 static pid_t ns_pid;
 57 
 58 static struct ubus_context *host_ubus_ctx = NULL;
 59 static struct ubus_context *jail_ubus_ctx = NULL;
 60 
 61 static struct ubus_subscriber config_watch_subscribe;
 62 
 63 /* generate /etc/config/network for jail'ed netifd */
 64 static int gen_jail_uci_network(void)
 65 {
 66         struct uci_context *uci_ctx = uci_alloc_context();
 67         struct uci_package *pkg = NULL;
 68         struct uci_element *e, *t;
 69         bool has_loopback = false;
 70         int ret = 0;
 71         FILE *ucinetf;
 72 
 73         /* if no network configuration is active just return */
 74         if (!uci_config_network)
 75                 goto uci_out;
 76 
 77         /* open output uci network config file */
 78         ucinetf = fopen(uci_config_network, "w");
 79         if (!ucinetf) {
 80                 ret = errno;
 81                 goto uci_out;
 82         }
 83 
 84         /* load network uci package */
 85         if (uci_load(uci_ctx, uci_net, &pkg) != UCI_OK) {
 86                 char *err;
 87                 uci_get_errorstr(uci_ctx, &err, uci_net);
 88                 fprintf(stderr, "unable to load configuration (%s)\n", err);
 89                 free(err);
 90                 ret = EIO;
 91                 goto ucinetf_out;
 92         }
 93 
 94         /* remove all sections which don't match jail */
 95         uci_foreach_element_safe(&pkg->sections, t, e) {
 96                 struct uci_section *s = uci_to_section(e);
 97                 struct uci_option *o = uci_lookup_option(uci_ctx, s, "jail");
 98                 struct uci_ptr ptr = { .p = pkg, .s = s };
 99 
100                 /* keep match, but remove 'jail' option and rename 'jail_ifname' */
101                 if (o && o->type == UCI_TYPE_STRING && !strcmp(o->v.string, jail_name)) {
102                         ptr.o = o;
103                         struct uci_option *jio = uci_lookup_option(uci_ctx, s, "jail_device");
104                         if (!jio)
105                                 jio = uci_lookup_option(uci_ctx, s, "jail_ifname");
106 
107                         if (jio) {
108                                 struct uci_ptr ren_ptr = { .p = pkg, .s = s, .o = jio, .value = "device" };
109                                 struct uci_option *host_device = uci_lookup_option(uci_ctx, s, "device");
110                                 struct uci_option *legacy_ifname = uci_lookup_option(uci_ctx, s, "ifname");
111                                 if (host_device && legacy_ifname) {
112                                         struct uci_ptr delif_ptr = { .p = pkg, .s = s, .o = legacy_ifname };
113                                         uci_delete(uci_ctx, &delif_ptr);
114                                 }
115 
116                                 struct uci_ptr renif_ptr = { .p = pkg, .s = s, .o = host_device?:legacy_ifname, .value = "host_device" };
117                                 uci_rename(uci_ctx, &renif_ptr);
118                                 uci_rename(uci_ctx, &ren_ptr);
119                         }
120                 }
121 
122                 uci_delete(uci_ctx, &ptr);
123         }
124 
125         /* check if device 'lo' is defined by any remaining interfaces */
126         uci_foreach_element(&pkg->sections, e) {
127                 struct uci_section *s = uci_to_section(e);
128                 if (strcmp(s->type, "interface"))
129                         continue;
130 
131                 const char *devname = uci_lookup_option_string(uci_ctx, s, "device");
132                 if (devname && !strcmp(devname, "lo")) {
133                         has_loopback = true;
134                         break;
135                 }
136         }
137 
138         /* create loopback interface section if not defined */
139         if (!has_loopback) {
140                 struct uci_ptr ptr = { .p = pkg, .section = "loopback", .value = "interface" };
141                 uci_set(uci_ctx, &ptr);
142                 uci_reorder_section(uci_ctx, ptr.s, 0);
143                 struct uci_ptr ptr1 = { .p = pkg, .s = ptr.s, .option = "device", .value = "lo" };
144                 struct uci_ptr ptr2 = { .p = pkg, .s = ptr.s, .option = "proto", .value = "static" };
145                 struct uci_ptr ptr3 = { .p = pkg, .s = ptr.s, .option = "ipaddr", .value = "127.0.0.1" };
146                 struct uci_ptr ptr4 = { .p = pkg, .s = ptr.s, .option = "netmask", .value = "255.0.0.0" };
147                 uci_set(uci_ctx, &ptr1);
148                 uci_set(uci_ctx, &ptr2);
149                 uci_set(uci_ctx, &ptr3);
150                 uci_set(uci_ctx, &ptr4);
151         }
152 
153         ret = uci_export(uci_ctx, ucinetf, pkg, false);
154 
155 ucinetf_out:
156         fclose(ucinetf);
157 
158 uci_out:
159         uci_free_context(uci_ctx);
160 
161         return ret;
162 }
163 
164 static void run_ubusd(struct uloop_timeout *t)
165 {
166         static struct blob_buf req;
167         void *ins, *in, *cmd;
168         uint32_t id;
169 
170         blob_buf_init(&req, 0);
171         blobmsg_add_string(&req, "name", jail_name);
172         ins = blobmsg_open_table(&req, "instances");
173         in = blobmsg_open_table(&req, "ubus");
174         cmd = blobmsg_open_array(&req, "command");
175         blobmsg_add_string(&req, "", ubusd_path);
176         blobmsg_add_string(&req, "", "-s");
177         blobmsg_add_string(&req, "", ubus_sock_path);
178         blobmsg_close_array(&req, cmd);
179 
180         if (ubus_pw) {
181                 blobmsg_add_string(&req, "user", "ubus");
182                 blobmsg_add_string(&req, "group", "ubus");
183         }
184 
185         blobmsg_close_table(&req, in);
186         blobmsg_close_table(&req, ins);
187 
188         if (!ubus_lookup_id(host_ubus_ctx, "container", &id))
189                 ubus_invoke(host_ubus_ctx, id, "add", req.head, NULL, NULL, 3000);
190 
191         blob_buf_free(&req);
192 }
193 
194 static void run_netifd(struct uloop_timeout *t)
195 {
196         static struct blob_buf req;
197         void *ins, *in, *cmd, *jail, *setns, *setnso, *namespaces, *mount, *pathenv;
198         char *resolvconf_dir, *resolvconf, *ucimount, *ubusmount;
199         char uci_dir[] = "/var/containers/ujail-uci-XXXXXX";
200 
201         uint32_t id;
202         bool running = false;
203 
204         uloop_fd_delete(&fd_inotify_read);
205         close(fd_inotify_read.fd);
206 
207         jail_ubus_ctx = ubus_connect(ubus_sock_path);
208         if (!jail_ubus_ctx)
209                 return;
210 
211         if (asprintf(&resolvconf_dir, "/tmp/resolv.conf-%s.d", jail_name) == -1)
212                 return;
213 
214         if (asprintf(&resolvconf, "%s/resolv.conf.auto", resolvconf_dir) == -1)
215                 goto netifd_out_resolvconf_dir;
216 
217         if (!mkdtemp(uci_dir))
218                 goto netifd_out_resolvconf;
219 
220         if (asprintf(&uci_config_network, "%s/network", uci_dir) == -1)
221                 goto netifd_out_ucidir;
222 
223         if (asprintf(&ucimount, "%s:/etc/config", uci_dir) == -1)
224                 goto netifd_out_ucinetconf;
225 
226         if (asprintf(&ubusmount, "%s:/var/run/ubus", ubus_sock_dir) == -1)
227                 goto netifd_out_ucimount;
228 
229         if (gen_jail_uci_network())
230                 goto netifd_out_ubusmount;
231 
232         blob_buf_init(&req, 0);
233         blobmsg_add_string(&req, "name", jail_name);
234         ins = blobmsg_open_table(&req, "instances");
235         in = blobmsg_open_table(&req, "netifd");
236 
237         cmd = blobmsg_open_array(&req, "command");
238         blobmsg_add_string(&req, "", netifd_path);
239         blobmsg_add_string(&req, "", "-r");
240         blobmsg_add_string(&req, "", resolvconf);
241         blobmsg_close_array(&req, cmd);
242 
243         pathenv = blobmsg_open_table(&req, "env");
244         blobmsg_add_string(&req, "PATH", "/usr/sbin:/usr/bin:/sbin:/bin");
245         blobmsg_close_table(&req, pathenv);
246 
247         jail = blobmsg_open_table(&req, "jail");
248 
249         setns = blobmsg_open_array(&req, "setns");
250         setnso = blobmsg_open_table(&req, "");
251         blobmsg_add_u32(&req, "pid", ns_pid);
252         namespaces = blobmsg_open_array(&req, "namespaces");
253         blobmsg_add_string(&req, "", "net");
254         blobmsg_add_string(&req, "", "ipc");
255         blobmsg_add_string(&req, "", "uts");
256         blobmsg_close_array(&req, namespaces);
257         blobmsg_close_table(&req, setnso);
258         blobmsg_close_array(&req, setns);
259 
260         mount = blobmsg_open_table(&req, "mount");
261         blobmsg_add_string(&req, ubusmount, "1");
262         blobmsg_add_string(&req, resolvconf_dir, "1");
263         blobmsg_add_string(&req, ucimount, "");
264         blobmsg_add_string(&req, "/bin/cat", "");
265         blobmsg_add_string(&req, "/bin/ipcalc.sh", "");
266         blobmsg_add_string(&req, "/bin/kill", "");
267         blobmsg_add_string(&req, "/bin/ubus", "");
268         blobmsg_add_string(&req, "/etc/hotplug.d", "");
269         blobmsg_add_string(&req, "/lib/functions", "");
270         blobmsg_add_string(&req, "/lib/functions.sh", "");
271         blobmsg_add_string(&req, "/lib/netifd", "");
272         blobmsg_add_string(&req, "/lib/network", "");
273         blobmsg_add_string(&req, "/usr/bin/awk", "");
274         blobmsg_add_string(&req, "/usr/bin/killall", "");
275         blobmsg_add_string(&req, "/usr/bin/logger", "");
276         blobmsg_add_string(&req, "/usr/bin/jshn", "");
277         blobmsg_add_string(&req, "/usr/share/libubox/jshn.sh", "");
278         blobmsg_add_string(&req, "/sbin/hotplug-call", "");
279         blobmsg_add_string(&req, "/sbin/udhcpc", "");
280         blobmsg_close_table(&req, mount);
281 
282         blobmsg_add_u8(&req, "log", 1);
283         blobmsg_add_u8(&req, "procfs", 1);
284         blobmsg_add_u8(&req, "sysfs", 1);
285 
286         blobmsg_add_u8(&req, "requirejail", 1);
287 
288         blobmsg_close_table(&req, jail);
289 
290         blobmsg_add_u8(&req, "stdout", 1);
291         blobmsg_add_u8(&req, "stderr", 1);
292 
293         blobmsg_close_table(&req, in);
294         blobmsg_close_table(&req, ins);
295 
296         if (!ubus_lookup_id(host_ubus_ctx, "container", &id))
297                 running = !ubus_invoke(host_ubus_ctx, id, "add", req.head, NULL, NULL, 3000);
298 
299         if (!running)
300                 blob_buf_free(&req);
301 netifd_out_ubusmount:
302         free(ubusmount);
303 netifd_out_ucimount:
304         free(ucimount);
305 netifd_out_ucinetconf:
306         if (!running) {
307                 unlink(uci_config_network);
308                 free(uci_config_network);
309         }
310 netifd_out_ucidir:
311         if (!running)
312                 rmdir(uci_dir);
313 netifd_out_resolvconf:
314         free(resolvconf);
315 netifd_out_resolvconf_dir:
316         free(resolvconf_dir);
317 
318         uloop_end();
319 }
320 
321 static struct uloop_timeout netifd_start_timeout = { .cb = run_netifd, };
322 
323 static void inotify_read_handler(struct uloop_fd *u, unsigned int events)
324 {
325         int rc;
326         char *p;
327         struct inotify_event *in;
328 
329         /* read inotify events */
330         while ((rc = read(u->fd, inotify_buffer, INOTIFY_SZ)) == -1 && errno == EINTR);
331 
332         if (rc <= 0)
333                 return;
334 
335         /* process events from buffer */
336         for (p = inotify_buffer;
337             rc - (p - inotify_buffer) >= (int)sizeof(struct inotify_event);
338             p += sizeof(struct inotify_event) + in->len) {
339                 in = (struct inotify_event*)p;
340 
341                 if (in->len < 4)
342                         continue;
343 
344                 if (!strncmp(ubus_sock_name, in->name, in->len))
345                         uloop_timeout_add(&netifd_start_timeout);
346         }
347 }
348 
349 static void netns_updown(struct ubus_context *ubus, const char *name, bool start, int netns_fd)
350 {
351         static struct blob_buf req;
352         uint32_t id;
353 
354         if (!ubus)
355                 return;
356 
357         blob_buf_init(&req, 0);
358         if (name)
359                 blobmsg_add_string(&req, "jail", name);
360 
361         blobmsg_add_u8(&req, "start", start);
362 
363         if (ubus_lookup_id(ubus, "network", &id) ||
364             ubus_invoke_fd(ubus, id, "netns_updown", req.head, NULL, NULL, 3000, netns_fd)) {
365                 INFO("ubus request failed\n");
366         }
367 
368         blob_buf_free(&req);
369 }
370 
371 static void jail_network_reload(struct uloop_timeout *t)
372 {
373         uint32_t id;
374 
375         if (!jail_ubus_ctx)
376                 return;
377 
378         if (gen_jail_uci_network())
379                 return;
380 
381         if (ubus_lookup_id(jail_ubus_ctx, "network", &id))
382                 return;
383 
384         ubus_invoke(jail_ubus_ctx, id, "reload", NULL, NULL, NULL, 3000);
385 }
386 
387 static const struct blobmsg_policy service_watch_policy = { "config", BLOBMSG_TYPE_STRING };
388 static struct uloop_timeout jail_network_reload_timeout = { .cb = jail_network_reload, };
389 
390 static int config_watch_notify_cb(struct ubus_context *ctx, struct ubus_object *obj,
391                            struct ubus_request_data *req, const char *method,
392                            struct blob_attr *msg)
393 {
394         struct blob_attr *attr;
395         const char *config;
396 
397         if (strcmp(method, "config.change"))
398                 return 0;
399 
400         blobmsg_parse(&service_watch_policy, 1, &attr, blob_data(msg), blob_len(msg));
401         if (!attr)
402                 return 1;
403 
404         config = blobmsg_get_string(attr);
405         if (strcmp(config, "network"))
406                 return 0;
407 
408         uloop_timeout_add(&jail_network_reload_timeout);
409 
410         return 0;
411 }
412 
413 static void watch_ubus_service(void)
414 {
415         uint32_t id;
416 
417         config_watch_subscribe.cb = config_watch_notify_cb;
418         if (ubus_register_subscriber(host_ubus_ctx, &config_watch_subscribe)) {
419                 ERROR("failed to register ubus subscriber\n");
420                 return;
421         }
422 
423         if (ubus_lookup_id(host_ubus_ctx, "service", &id))
424                 return;
425 
426         if (!ubus_subscribe(host_ubus_ctx, &config_watch_subscribe, id))
427                 return;
428 
429         ERROR("failed to subscribe %d\n", id);
430 }
431 
432 static struct uloop_timeout ubus_start_timeout = { .cb = run_ubusd, };
433 
434 int jail_network_start(struct ubus_context *new_ctx, char *new_jail_name, pid_t new_ns_pid)
435 {
436         ubus_pw = getpwnam("ubus");
437         int ret = 0;
438         int netns_fd;
439 
440         host_ubus_ctx = new_ctx;
441         ns_pid = new_ns_pid;
442         jail_name = new_jail_name;
443 
444         if (asprintf(&ubus_sock_dir, "/var/containers/ubus-%s", jail_name) == -1) {
445                 ret = ENOMEM;
446                 goto errout_dir;
447         }
448 
449         if (asprintf(&ubus_sock_path, "%s/%s", ubus_sock_dir, ubus_sock_name) == -1) {
450                 ret = ENOMEM;
451                 goto errout_path;
452         }
453 
454         mkdir_p(ubus_sock_dir, 0755);
455         if (ubus_pw) {
456                 ret = chown(ubus_sock_dir, ubus_pw->pw_uid, ubus_pw->pw_gid);
457                 if (ret) {
458                         ret = errno;
459                         goto errout;
460                 }
461         }
462 
463         fd_inotify_read.fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
464         fd_inotify_read.cb = inotify_read_handler;
465         if (fd_inotify_read.fd == -1) {
466                 ERROR("failed to initialize inotify handler\n");
467                 ret = EIO;
468                 goto errout;
469         }
470         uloop_fd_add(&fd_inotify_read, ULOOP_READ);
471 
472         inotify_buffer = calloc(1, INOTIFY_SZ);
473         if (!inotify_buffer) {
474                 ret = ENOMEM;
475                 goto errout_inotify;
476         }
477 
478         if (inotify_add_watch(fd_inotify_read.fd, ubus_sock_dir, IN_CREATE) == -1) {
479                 ERROR("failed to add inotify watch on %s\n", ubus_sock_dir);
480                 free(inotify_buffer);
481                 ret = EIO;
482                 goto errout_inotify;
483         }
484 
485         watch_ubus_service();
486 
487         netns_fd = ns_open_pid("net", ns_pid);
488         if (netns_fd < 0) {
489                 ret = ESRCH;
490                 goto errout_inotify;
491         }
492 
493         netns_updown(host_ubus_ctx, jail_name, true, netns_fd);
494 
495         close(netns_fd);
496         uloop_timeout_add(&ubus_start_timeout);
497         uloop_run();
498 
499         return 0;
500 
501 errout_inotify:
502         close(fd_inotify_read.fd);
503 errout:
504         free(ubus_sock_path);
505 errout_path:
506         free(ubus_sock_dir);
507 errout_dir:
508         return ret;
509 }
510 
511 static int jail_delete_instance(const char *instance)
512 {
513         static struct blob_buf req;
514         uint32_t id;
515 
516         if (ubus_lookup_id(host_ubus_ctx, "container", &id))
517                 return -1;
518 
519         blob_buf_init(&req, 0);
520         blobmsg_add_string(&req, "name", jail_name);
521         blobmsg_add_string(&req, "instance", instance);
522 
523         return ubus_invoke(host_ubus_ctx, id, "delete", req.head, NULL, NULL, 3000);
524 }
525 
526 int jail_network_stop(void)
527 {
528         int host_netns = open("/proc/self/ns/net", O_RDONLY);
529 
530         if (host_netns < 0)
531                 return errno;
532 
533         netns_updown(jail_ubus_ctx, NULL, false, host_netns);
534 
535         close(host_netns);
536         ubus_free(jail_ubus_ctx);
537 
538         jail_delete_instance("netifd");
539         jail_delete_instance("ubus");
540 
541         if (uci_config_network) {
542                 unlink(uci_config_network);
543                 rmdir(dirname(uci_config_network));
544                 free(uci_config_network);
545         }
546 
547         free(ubus_sock_path);
548         rmdir(ubus_sock_dir);
549         free(ubus_sock_dir);
550 
551         return 0;
552 }
553 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt