• 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/config/uci.sh", "");
270         blobmsg_add_string(&req, "/lib/functions", "");
271         blobmsg_add_string(&req, "/lib/functions.sh", "");
272         blobmsg_add_string(&req, "/lib/netifd", "");
273         blobmsg_add_string(&req, "/lib/network", "");
274         blobmsg_add_string(&req, "/usr/bin/awk", "");
275         blobmsg_add_string(&req, "/usr/bin/cut", "");
276         blobmsg_add_string(&req, "/usr/bin/jshn", "");
277         blobmsg_add_string(&req, "/usr/bin/killall", "");
278         blobmsg_add_string(&req, "/usr/bin/logger", "");
279         blobmsg_add_string(&req, "/usr/bin/md5sum", "");
280         blobmsg_add_string(&req, "/usr/bin/ucode", "");
281         blobmsg_add_string(&req, "/usr/lib/ucode", "");
282         blobmsg_add_string(&req, "/usr/share/libubox/jshn.sh", "");
283         blobmsg_add_string(&req, "/usr/share/schema", "");
284         blobmsg_add_string(&req, "/usr/share/ucode/wifi", "");
285         blobmsg_add_string(&req, "/sbin/hotplug-call", "");
286         blobmsg_add_string(&req, "/sbin/uci", "");
287         blobmsg_add_string(&req, "/sbin/udhcpc", "");
288         blobmsg_close_table(&req, mount);
289 
290         blobmsg_add_u8(&req, "log", 1);
291         blobmsg_add_u8(&req, "procfs", 1);
292         blobmsg_add_u8(&req, "sysfs", 1);
293 
294         blobmsg_add_u8(&req, "requirejail", 1);
295 
296         blobmsg_close_table(&req, jail);
297 
298         blobmsg_add_u8(&req, "stdout", 1);
299         blobmsg_add_u8(&req, "stderr", 1);
300 
301         blobmsg_close_table(&req, in);
302         blobmsg_close_table(&req, ins);
303 
304         if (!ubus_lookup_id(host_ubus_ctx, "container", &id))
305                 running = !ubus_invoke(host_ubus_ctx, id, "add", req.head, NULL, NULL, 3000);
306 
307         if (!running)
308                 blob_buf_free(&req);
309 netifd_out_ubusmount:
310         free(ubusmount);
311 netifd_out_ucimount:
312         free(ucimount);
313 netifd_out_ucinetconf:
314         if (!running) {
315                 unlink(uci_config_network);
316                 free(uci_config_network);
317         }
318 netifd_out_ucidir:
319         if (!running)
320                 rmdir(uci_dir);
321 netifd_out_resolvconf:
322         free(resolvconf);
323 netifd_out_resolvconf_dir:
324         free(resolvconf_dir);
325 
326         uloop_end();
327 }
328 
329 static struct uloop_timeout netifd_start_timeout = { .cb = run_netifd, };
330 
331 static void inotify_read_handler(struct uloop_fd *u, unsigned int events)
332 {
333         int rc;
334         char *p;
335         struct inotify_event *in;
336 
337         /* read inotify events */
338         while ((rc = read(u->fd, inotify_buffer, INOTIFY_SZ)) == -1 && errno == EINTR);
339 
340         if (rc <= 0)
341                 return;
342 
343         /* process events from buffer */
344         for (p = inotify_buffer;
345             rc - (p - inotify_buffer) >= (int)sizeof(struct inotify_event);
346             p += sizeof(struct inotify_event) + in->len) {
347                 in = (struct inotify_event*)p;
348 
349                 if (in->len < 4)
350                         continue;
351 
352                 if (!strncmp(ubus_sock_name, in->name, in->len))
353                         uloop_timeout_add(&netifd_start_timeout);
354         }
355 }
356 
357 static void netns_updown(struct ubus_context *ubus, const char *name, bool start, int netns_fd)
358 {
359         static struct blob_buf req;
360         uint32_t id;
361 
362         if (!ubus)
363                 return;
364 
365         blob_buf_init(&req, 0);
366         if (name)
367                 blobmsg_add_string(&req, "jail", name);
368 
369         blobmsg_add_u8(&req, "start", start);
370 
371         if (ubus_lookup_id(ubus, "network", &id) ||
372             ubus_invoke_fd(ubus, id, "netns_updown", req.head, NULL, NULL, 3000, netns_fd)) {
373                 INFO("ubus request failed\n");
374         }
375 
376         blob_buf_free(&req);
377 }
378 
379 static void jail_network_reload(struct uloop_timeout *t)
380 {
381         uint32_t id;
382 
383         if (!jail_ubus_ctx)
384                 return;
385 
386         if (gen_jail_uci_network())
387                 return;
388 
389         if (ubus_lookup_id(jail_ubus_ctx, "network", &id))
390                 return;
391 
392         ubus_invoke(jail_ubus_ctx, id, "reload", NULL, NULL, NULL, 3000);
393 }
394 
395 static const struct blobmsg_policy service_watch_policy = { "config", BLOBMSG_TYPE_STRING };
396 static struct uloop_timeout jail_network_reload_timeout = { .cb = jail_network_reload, };
397 
398 static int config_watch_notify_cb(struct ubus_context *ctx, struct ubus_object *obj,
399                            struct ubus_request_data *req, const char *method,
400                            struct blob_attr *msg)
401 {
402         struct blob_attr *attr;
403         const char *config;
404 
405         if (strcmp(method, "config.change"))
406                 return 0;
407 
408         blobmsg_parse(&service_watch_policy, 1, &attr, blob_data(msg), blob_len(msg));
409         if (!attr)
410                 return 1;
411 
412         config = blobmsg_get_string(attr);
413         if (strcmp(config, "network"))
414                 return 0;
415 
416         uloop_timeout_add(&jail_network_reload_timeout);
417 
418         return 0;
419 }
420 
421 static void watch_ubus_service(void)
422 {
423         uint32_t id;
424 
425         config_watch_subscribe.cb = config_watch_notify_cb;
426         if (ubus_register_subscriber(host_ubus_ctx, &config_watch_subscribe)) {
427                 ERROR("failed to register ubus subscriber\n");
428                 return;
429         }
430 
431         if (ubus_lookup_id(host_ubus_ctx, "service", &id))
432                 return;
433 
434         if (!ubus_subscribe(host_ubus_ctx, &config_watch_subscribe, id))
435                 return;
436 
437         ERROR("failed to subscribe %d\n", id);
438 }
439 
440 static struct uloop_timeout ubus_start_timeout = { .cb = run_ubusd, };
441 
442 int jail_network_start(struct ubus_context *new_ctx, char *new_jail_name, pid_t new_ns_pid)
443 {
444         ubus_pw = getpwnam("ubus");
445         int ret = 0;
446         int netns_fd;
447 
448         host_ubus_ctx = new_ctx;
449         ns_pid = new_ns_pid;
450         jail_name = new_jail_name;
451 
452         if (asprintf(&ubus_sock_dir, "/var/containers/ubus-%s", jail_name) == -1) {
453                 ret = ENOMEM;
454                 goto errout_dir;
455         }
456 
457         if (asprintf(&ubus_sock_path, "%s/%s", ubus_sock_dir, ubus_sock_name) == -1) {
458                 ret = ENOMEM;
459                 goto errout_path;
460         }
461 
462         mkdir_p(ubus_sock_dir, 0755);
463         if (ubus_pw) {
464                 ret = chown(ubus_sock_dir, ubus_pw->pw_uid, ubus_pw->pw_gid);
465                 if (ret) {
466                         ret = errno;
467                         goto errout;
468                 }
469         }
470 
471         fd_inotify_read.fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
472         fd_inotify_read.cb = inotify_read_handler;
473         if (fd_inotify_read.fd == -1) {
474                 ERROR("failed to initialize inotify handler\n");
475                 ret = EIO;
476                 goto errout;
477         }
478         uloop_fd_add(&fd_inotify_read, ULOOP_READ);
479 
480         inotify_buffer = calloc(1, INOTIFY_SZ);
481         if (!inotify_buffer) {
482                 ret = ENOMEM;
483                 goto errout_inotify;
484         }
485 
486         if (inotify_add_watch(fd_inotify_read.fd, ubus_sock_dir, IN_CREATE) == -1) {
487                 ERROR("failed to add inotify watch on %s\n", ubus_sock_dir);
488                 free(inotify_buffer);
489                 ret = EIO;
490                 goto errout_inotify;
491         }
492 
493         watch_ubus_service();
494 
495         netns_fd = ns_open_pid("net", ns_pid);
496         if (netns_fd < 0) {
497                 ret = ESRCH;
498                 goto errout_inotify;
499         }
500 
501         netns_updown(host_ubus_ctx, jail_name, true, netns_fd);
502 
503         close(netns_fd);
504         uloop_timeout_add(&ubus_start_timeout);
505         uloop_run();
506 
507         return 0;
508 
509 errout_inotify:
510         close(fd_inotify_read.fd);
511 errout:
512         free(ubus_sock_path);
513 errout_path:
514         free(ubus_sock_dir);
515 errout_dir:
516         return ret;
517 }
518 
519 static int jail_delete_instance(const char *instance)
520 {
521         static struct blob_buf req;
522         uint32_t id;
523 
524         if (ubus_lookup_id(host_ubus_ctx, "container", &id))
525                 return -1;
526 
527         blob_buf_init(&req, 0);
528         blobmsg_add_string(&req, "name", jail_name);
529         blobmsg_add_string(&req, "instance", instance);
530 
531         return ubus_invoke(host_ubus_ctx, id, "delete", req.head, NULL, NULL, 3000);
532 }
533 
534 int jail_network_stop(void)
535 {
536         int host_netns = open("/proc/self/ns/net", O_RDONLY);
537 
538         if (host_netns < 0)
539                 return errno;
540 
541         netns_updown(jail_ubus_ctx, NULL, false, host_netns);
542 
543         close(host_netns);
544         ubus_free(jail_ubus_ctx);
545 
546         jail_delete_instance("netifd");
547         jail_delete_instance("ubus");
548 
549         if (uci_config_network) {
550                 unlink(uci_config_network);
551                 rmdir(dirname(uci_config_network));
552                 free(uci_config_network);
553         }
554 
555         free(ubus_sock_path);
556         rmdir(ubus_sock_dir);
557         free(ubus_sock_dir);
558 
559         return 0;
560 }
561 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt