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