2 * Copyright (C) 2021 Daniel Golle <daniel@makrotopia.org>
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
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.
13 * launch private ubus and netifd instances for containers with managed
17 #define _GNU_SOURCE /* See feature_test_macros(7) */
24 #include <sys/inotify.h>
26 #include <sys/types.h>
30 #include <linux/limits.h>
32 #include <libubox/uloop.h>
33 #include <libubox/utils.h>
35 #include <libubox/blobmsg.h>
36 #include <libubox/blobmsg_json.h>
43 #define INOTIFY_SZ (sizeof(struct inotify_event) + PATH_MAX + 1)
45 static const char ubusd_path
[] = "/sbin/ubusd";
46 static const char netifd_path
[] = "/sbin/netifd";
47 static const char uci_net
[] = "network";
49 static char *jail_name
, *ubus_sock_path
, *ubus_sock_dir
, *uci_config_network
= NULL
;
51 static char *inotify_buffer
;
52 static struct uloop_fd fd_inotify_read
;
53 static struct passwd
*ubus_pw
;
56 static struct ubus_context
*ctx
= NULL
;
57 static struct ubus_context
*netifd_ubus_ctx
= NULL
;
59 static struct ubus_subscriber config_watch_subscribe
;
61 /* generate /etc/config/network for jail'ed netifd */
62 static int gen_jail_uci_network(void)
64 struct uci_context
*ctx
= uci_alloc_context();
65 struct uci_package
*pkg
= NULL
;
66 struct uci_element
*e
, *t
;
67 bool has_loopback
= false;
71 /* if no network configuration is active just return */
72 if (!uci_config_network
)
75 /* open output uci network config file */
76 ucinetf
= fopen(uci_config_network
, "w");
82 /* load network uci package */
83 if (uci_load(ctx
, uci_net
, &pkg
) != UCI_OK
) {
85 uci_get_errorstr(ctx
, &err
, uci_net
);
86 fprintf(stderr
, "unable to load configuration (%s)\n", err
);
92 /* remove all sections which don't match jail */
93 uci_foreach_element_safe(&pkg
->sections
, t
, e
) {
94 struct uci_section
*s
= uci_to_section(e
);
95 struct uci_option
*o
= uci_lookup_option(ctx
, s
, "jail");
96 struct uci_ptr ptr
= { .p
= pkg
, .s
= s
};
98 /* keep match, but remove 'jail' option and rename 'jail_ifname' */
99 if (o
&& o
->type
== UCI_TYPE_STRING
&& !strcmp(o
->v
.string
, jail_name
)) {
101 struct uci_option
*jio
= uci_lookup_option(ctx
, s
, "jail_device");
103 jio
= uci_lookup_option(ctx
, s
, "jail_ifname");
106 struct uci_ptr ren_ptr
= { .p
= pkg
, .s
= s
, .o
= jio
, .value
= "device" };
107 struct uci_option
*host_device
= uci_lookup_option(ctx
, s
, "device");
108 struct uci_option
*legacy_ifname
= uci_lookup_option(ctx
, s
, "ifname");
109 if (host_device
&& legacy_ifname
) {
110 struct uci_ptr delif_ptr
= { .p
= pkg
, .s
= s
, .o
= legacy_ifname
};
111 uci_delete(ctx
, &delif_ptr
);
114 struct uci_ptr renif_ptr
= { .p
= pkg
, .s
= s
, .o
= host_device
?:legacy_ifname
, .value
= "host_device" };
115 uci_rename(ctx
, &renif_ptr
);
116 uci_rename(ctx
, &ren_ptr
);
120 uci_delete(ctx
, &ptr
);
123 /* check if device 'lo' is defined by any remaining interfaces */
124 uci_foreach_element(&pkg
->sections
, e
) {
125 struct uci_section
*s
= uci_to_section(e
);
126 if (strcmp(s
->type
, "interface"))
129 const char *devname
= uci_lookup_option_string(ctx
, s
, "device");
130 if (devname
&& !strcmp(devname
, "lo")) {
136 /* create loopback interface section if not defined */
138 struct uci_ptr ptr
= { .p
= pkg
, .section
= "loopback", .value
= "interface" };
140 uci_reorder_section(ctx
, ptr
.s
, 0);
141 struct uci_ptr ptr1
= { .p
= pkg
, .s
= ptr
.s
, .option
= "device", .value
= "lo" };
142 struct uci_ptr ptr2
= { .p
= pkg
, .s
= ptr
.s
, .option
= "proto", .value
= "static" };
143 struct uci_ptr ptr3
= { .p
= pkg
, .s
= ptr
.s
, .option
= "ipaddr", .value
= "127.0.0.1" };
144 struct uci_ptr ptr4
= { .p
= pkg
, .s
= ptr
.s
, .option
= "netmask", .value
= "255.0.0.0" };
151 ret
= uci_export(ctx
, ucinetf
, pkg
, false);
157 uci_free_context(ctx
);
162 static void run_ubusd(struct uloop_timeout
*t
)
164 static struct blob_buf req
;
165 void *ins
, *in
, *cmd
;
168 blob_buf_init(&req
, 0);
169 blobmsg_add_string(&req
, "name", jail_name
);
170 ins
= blobmsg_open_table(&req
, "instances");
171 in
= blobmsg_open_table(&req
, "ubus");
172 cmd
= blobmsg_open_array(&req
, "command");
173 blobmsg_add_string(&req
, "", ubusd_path
);
174 blobmsg_add_string(&req
, "", "-s");
175 blobmsg_add_string(&req
, "", ubus_sock_path
);
176 blobmsg_close_array(&req
, cmd
);
179 blobmsg_add_string(&req
, "user", "ubus");
180 blobmsg_add_string(&req
, "group", "ubus");
183 blobmsg_close_table(&req
, in
);
184 blobmsg_close_table(&req
, ins
);
186 if (!ubus_lookup_id(ctx
, "container", &id
))
187 ubus_invoke(ctx
, id
, "add", req
.head
, NULL
, NULL
, 3000);
192 static void run_netifd(struct uloop_timeout
*t
)
194 static struct blob_buf req
;
195 void *ins
, *in
, *cmd
, *jail
, *setns
, *setnso
, *namespaces
, *mount
;
196 char *resolvconf_dir
, *resolvconf
, *ucimount
;
197 char uci_dir
[] = "/var/containers/ujail-uci-XXXXXX";
200 bool running
= false;
202 uloop_fd_delete(&fd_inotify_read
);
203 close(fd_inotify_read
.fd
);
205 netifd_ubus_ctx
= ubus_connect(ubus_sock_path
);
206 if (!netifd_ubus_ctx
)
209 if (asprintf(&resolvconf_dir
, "/tmp/resolv.conf-%s.d", jail_name
) == -1)
212 if (asprintf(&resolvconf
, "%s/resolv.conf.auto", resolvconf_dir
) == -1)
213 goto netifd_out_resolvconf_dir
;
215 if (!mkdtemp(uci_dir
))
216 goto netifd_out_resolvconf
;
218 if (asprintf(&uci_config_network
, "%s/network", uci_dir
) == -1)
219 goto netifd_out_ucidir
;
221 if (asprintf(&ucimount
, "%s:/etc/config", uci_dir
) == -1)
222 goto netifd_out_ucinetconf
;
224 if (gen_jail_uci_network())
225 goto netifd_out_ucimount
;
227 blob_buf_init(&req
, 0);
228 blobmsg_add_string(&req
, "name", jail_name
);
229 ins
= blobmsg_open_table(&req
, "instances");
230 in
= blobmsg_open_table(&req
, "netifd");
232 cmd
= blobmsg_open_array(&req
, "command");
233 blobmsg_add_string(&req
, "", netifd_path
);
234 blobmsg_add_string(&req
, "", "-r");
235 blobmsg_add_string(&req
, "", resolvconf
);
236 blobmsg_add_string(&req
, "", "-s");
237 blobmsg_add_string(&req
, "", ubus_sock_path
);
238 blobmsg_close_array(&req
, cmd
);
240 jail
= blobmsg_open_table(&req
, "jail");
242 setns
= blobmsg_open_array(&req
, "setns");
243 setnso
= blobmsg_open_table(&req
, "");
244 blobmsg_add_u32(&req
, "pid", ns_pid
);
245 namespaces
= blobmsg_open_array(&req
, "namespaces");
246 blobmsg_add_string(&req
, "", "net");
247 blobmsg_add_string(&req
, "", "ipc");
248 blobmsg_add_string(&req
, "", "uts");
249 blobmsg_close_array(&req
, namespaces
);
250 blobmsg_close_table(&req
, setnso
);
251 blobmsg_close_array(&req
, setns
);
253 mount
= blobmsg_open_table(&req
, "mount");
254 blobmsg_add_string(&req
, ubus_sock_dir
, "1");
255 blobmsg_add_string(&req
, resolvconf_dir
, "1");
256 blobmsg_add_string(&req
, ucimount
, "0");
257 blobmsg_add_string(&req
, "/etc/hotplug.d", "0");
258 blobmsg_add_string(&req
, "/lib/functions.sh", "0");
259 blobmsg_add_string(&req
, "/lib/netifd", "0");
260 blobmsg_add_string(&req
, "/lib/network", "0");
261 blobmsg_add_string(&req
, "/usr/bin/logger", "0");
262 blobmsg_add_string(&req
, "/usr/bin/jshn", "0");
263 blobmsg_add_string(&req
, "/usr/share/libubox/jshn.sh", "0");
264 blobmsg_add_string(&req
, "/sbin/hotplug-call", "0");
265 blobmsg_add_string(&req
, "/sbin/udhcpc", "0");
266 blobmsg_close_table(&req
, mount
);
268 blobmsg_add_u8(&req
, "log", 1);
269 blobmsg_add_u8(&req
, "procfs", 1);
270 blobmsg_add_u8(&req
, "sysfs", 1);
272 blobmsg_add_u8(&req
, "requirejail", 1);
274 blobmsg_close_table(&req
, jail
);
276 blobmsg_add_u8(&req
, "stdout", 1);
277 blobmsg_add_u8(&req
, "stderr", 1);
279 blobmsg_close_table(&req
, in
);
280 blobmsg_close_table(&req
, ins
);
282 if (!ubus_lookup_id(ctx
, "container", &id
))
283 running
= !ubus_invoke(ctx
, id
, "add", req
.head
, NULL
, NULL
, 3000);
289 netifd_out_ucinetconf
:
291 unlink(uci_config_network
);
292 free(uci_config_network
);
297 netifd_out_resolvconf
:
299 netifd_out_resolvconf_dir
:
300 free(resolvconf_dir
);
305 static struct uloop_timeout netifd_start_timeout
= { .cb
= run_netifd
, };
307 static void inotify_read_handler(struct uloop_fd
*u
, unsigned int events
)
311 struct inotify_event
*in
;
313 /* read inotify events */
314 while ((rc
= read(u
->fd
, inotify_buffer
, INOTIFY_SZ
)) == -1 && errno
== EINTR
);
319 /* process events from buffer */
320 for (p
= inotify_buffer
;
321 rc
- (p
- inotify_buffer
) >= (int)sizeof(struct inotify_event
);
322 p
+= sizeof(struct inotify_event
) + in
->len
) {
323 in
= (struct inotify_event
*)p
;
328 if (!strncmp("ubus", in
->name
, in
->len
))
329 uloop_timeout_add(&netifd_start_timeout
);
333 static void netns_updown(struct ubus_context
*ctx
, const char *name
, bool start
, int netns_fd
)
335 static struct blob_buf req
;
341 blob_buf_init(&req
, 0);
343 blobmsg_add_string(&req
, "jail", name
);
345 blobmsg_add_u8(&req
, "start", start
);
347 if (ubus_lookup_id(ctx
, "network", &id
) ||
348 ubus_invoke_fd(ctx
, id
, "netns_updown", req
.head
, NULL
, NULL
, 3000, netns_fd
)) {
349 INFO("ubus request failed\n");
355 static void jail_network_reload(struct uloop_timeout
*t
)
359 if (!netifd_ubus_ctx
)
362 if (gen_jail_uci_network())
365 if (ubus_lookup_id(netifd_ubus_ctx
, "network", &id
))
368 ubus_invoke(netifd_ubus_ctx
, id
, "reload", NULL
, NULL
, NULL
, 3000);
371 static const struct blobmsg_policy service_watch_policy
= { "config", BLOBMSG_TYPE_STRING
};
372 static struct uloop_timeout jail_network_reload_timeout
= { .cb
= jail_network_reload
, };
374 static int config_watch_notify_cb(struct ubus_context
*ctx
, struct ubus_object
*obj
,
375 struct ubus_request_data
*req
, const char *method
,
376 struct blob_attr
*msg
)
378 struct blob_attr
*attr
;
381 if (strcmp(method
, "config.change"))
384 blobmsg_parse(&service_watch_policy
, 1, &attr
, blob_data(msg
), blob_len(msg
));
388 config
= blobmsg_get_string(attr
);
389 if (strcmp(config
, "network"))
392 uloop_timeout_add(&jail_network_reload_timeout
);
397 static void watch_ubus_service(void)
401 config_watch_subscribe
.cb
= config_watch_notify_cb
;
402 if (ubus_register_subscriber(ctx
, &config_watch_subscribe
)) {
403 ERROR("failed to register ubus subscriber\n");
407 if (ubus_lookup_id(ctx
, "service", &id
))
410 if (!ubus_subscribe(ctx
, &config_watch_subscribe
, id
))
413 ERROR("failed to subscribe %d\n", id
);
416 static struct uloop_timeout ubus_start_timeout
= { .cb
= run_ubusd
, };
418 int jail_network_start(struct ubus_context
*new_ctx
, char *new_jail_name
, pid_t new_ns_pid
)
420 ubus_pw
= getpwnam("ubus");
426 jail_name
= new_jail_name
;
428 asprintf(&ubus_sock_dir
, "/var/containers/ubus-%s", jail_name
);
429 if (!ubus_sock_dir
) {
434 asprintf(&ubus_sock_path
, "%s/ubus", ubus_sock_dir
);
435 if (!ubus_sock_path
) {
440 mkdir_p(ubus_sock_dir
, 0755);
442 ret
= chown(ubus_sock_dir
, ubus_pw
->pw_uid
, ubus_pw
->pw_gid
);
449 fd_inotify_read
.fd
= inotify_init1(IN_NONBLOCK
| IN_CLOEXEC
);
450 fd_inotify_read
.cb
= inotify_read_handler
;
451 if (fd_inotify_read
.fd
== -1) {
452 ERROR("failed to initialize inotify handler\n");
456 uloop_fd_add(&fd_inotify_read
, ULOOP_READ
);
458 inotify_buffer
= calloc(1, INOTIFY_SZ
);
459 if (!inotify_buffer
) {
464 if (inotify_add_watch(fd_inotify_read
.fd
, ubus_sock_dir
, IN_CREATE
) == -1) {
465 ERROR("failed to add inotify watch on %s\n", ubus_sock_dir
);
466 free(inotify_buffer
);
471 watch_ubus_service();
473 netns_fd
= ns_open_pid("net", ns_pid
);
475 netns_updown(ctx
, jail_name
, true, netns_fd
);
478 uloop_timeout_add(&ubus_start_timeout
);
484 close(fd_inotify_read
.fd
);
486 free(ubus_sock_path
);
493 static int jail_delete_instance(const char *instance
)
495 static struct blob_buf req
;
498 if (ubus_lookup_id(ctx
, "container", &id
))
501 blob_buf_init(&req
, 0);
502 blobmsg_add_string(&req
, "name", jail_name
);
503 blobmsg_add_string(&req
, "instance", instance
);
505 return ubus_invoke(ctx
, id
, "delete", req
.head
, NULL
, NULL
, 3000);
508 int jail_network_stop(void)
510 int host_netns
= open("/proc/self/ns/net", O_RDONLY
);
515 netns_updown(netifd_ubus_ctx
, NULL
, false, host_netns
);
518 ubus_free(netifd_ubus_ctx
);
520 jail_delete_instance("netifd");
521 jail_delete_instance("ubus");
523 if (uci_config_network
) {
524 unlink(uci_config_network
);
525 rmdir(dirname(uci_config_network
));
526 free(uci_config_network
);
529 free(ubus_sock_path
);
530 rmdir(ubus_sock_dir
);