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) */
25 #include <sys/inotify.h>
27 #include <sys/types.h>
31 #include <linux/limits.h>
33 #include <libubox/uloop.h>
34 #include <libubox/utils.h>
36 #include <libubox/blobmsg.h>
37 #include <libubox/blobmsg_json.h>
44 #define INOTIFY_SZ (sizeof(struct inotify_event) + PATH_MAX + 1)
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";
51 static char *jail_name
, *ubus_sock_path
, *ubus_sock_dir
, *uci_config_network
= NULL
;
53 static char *inotify_buffer
;
54 static struct uloop_fd fd_inotify_read
;
55 static struct passwd
*ubus_pw
;
58 static struct ubus_context
*host_ubus_ctx
= NULL
;
59 static struct ubus_context
*jail_ubus_ctx
= NULL
;
61 static struct ubus_subscriber config_watch_subscribe
;
63 /* generate /etc/config/network for jail'ed netifd */
64 static int gen_jail_uci_network(void)
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;
73 /* if no network configuration is active just return */
74 if (!uci_config_network
)
77 /* open output uci network config file */
78 ucinetf
= fopen(uci_config_network
, "w");
84 /* load network uci package */
85 if (uci_load(uci_ctx
, uci_net
, &pkg
) != UCI_OK
) {
87 uci_get_errorstr(uci_ctx
, &err
, uci_net
);
88 fprintf(stderr
, "unable to load configuration (%s)\n", err
);
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
};
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
)) {
103 struct uci_option
*jio
= uci_lookup_option(uci_ctx
, s
, "jail_device");
105 jio
= uci_lookup_option(uci_ctx
, s
, "jail_ifname");
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
);
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
);
122 uci_delete(uci_ctx
, &ptr
);
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"))
131 const char *devname
= uci_lookup_option_string(uci_ctx
, s
, "device");
132 if (devname
&& !strcmp(devname
, "lo")) {
138 /* create loopback interface section if not defined */
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
);
153 ret
= uci_export(uci_ctx
, ucinetf
, pkg
, false);
159 uci_free_context(uci_ctx
);
164 static void run_ubusd(struct uloop_timeout
*t
)
166 static struct blob_buf req
;
167 void *ins
, *in
, *cmd
;
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
);
181 blobmsg_add_string(&req
, "user", "ubus");
182 blobmsg_add_string(&req
, "group", "ubus");
185 blobmsg_close_table(&req
, in
);
186 blobmsg_close_table(&req
, ins
);
188 if (!ubus_lookup_id(host_ubus_ctx
, "container", &id
))
189 ubus_invoke(host_ubus_ctx
, id
, "add", req
.head
, NULL
, NULL
, 3000);
194 static void run_netifd(struct uloop_timeout
*t
)
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";
202 bool running
= false;
204 uloop_fd_delete(&fd_inotify_read
);
205 close(fd_inotify_read
.fd
);
207 jail_ubus_ctx
= ubus_connect(ubus_sock_path
);
211 if (asprintf(&resolvconf_dir
, "/tmp/resolv.conf-%s.d", jail_name
) == -1)
214 if (asprintf(&resolvconf
, "%s/resolv.conf.auto", resolvconf_dir
) == -1)
215 goto netifd_out_resolvconf_dir
;
217 if (!mkdtemp(uci_dir
))
218 goto netifd_out_resolvconf
;
220 if (asprintf(&uci_config_network
, "%s/network", uci_dir
) == -1)
221 goto netifd_out_ucidir
;
223 if (asprintf(&ucimount
, "%s:/etc/config", uci_dir
) == -1)
224 goto netifd_out_ucinetconf
;
226 if (asprintf(&ubusmount
, "%s:/var/run/ubus", ubus_sock_dir
) == -1)
227 goto netifd_out_ucimount
;
229 if (gen_jail_uci_network())
230 goto netifd_out_ubusmount
;
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");
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
);
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
);
247 jail
= blobmsg_open_table(&req
, "jail");
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
);
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
, "0");
264 blobmsg_add_string(&req
, "/bin/cat", "0");
265 blobmsg_add_string(&req
, "/bin/ipcalc.sh", "0");
266 blobmsg_add_string(&req
, "/bin/kill", "0");
267 blobmsg_add_string(&req
, "/bin/ubus", "0");
268 blobmsg_add_string(&req
, "/etc/hotplug.d", "0");
269 blobmsg_add_string(&req
, "/lib/functions", "0");
270 blobmsg_add_string(&req
, "/lib/functions.sh", "0");
271 blobmsg_add_string(&req
, "/lib/netifd", "0");
272 blobmsg_add_string(&req
, "/lib/network", "0");
273 blobmsg_add_string(&req
, "/usr/bin/awk", "0");
274 blobmsg_add_string(&req
, "/usr/bin/killall", "0");
275 blobmsg_add_string(&req
, "/usr/bin/logger", "0");
276 blobmsg_add_string(&req
, "/usr/bin/jshn", "0");
277 blobmsg_add_string(&req
, "/usr/share/libubox/jshn.sh", "0");
278 blobmsg_add_string(&req
, "/sbin/hotplug-call", "0");
279 blobmsg_add_string(&req
, "/sbin/udhcpc", "0");
280 blobmsg_close_table(&req
, mount
);
282 blobmsg_add_u8(&req
, "log", 1);
283 blobmsg_add_u8(&req
, "procfs", 1);
284 blobmsg_add_u8(&req
, "sysfs", 1);
286 blobmsg_add_u8(&req
, "requirejail", 1);
288 blobmsg_close_table(&req
, jail
);
290 blobmsg_add_u8(&req
, "stdout", 1);
291 blobmsg_add_u8(&req
, "stderr", 1);
293 blobmsg_close_table(&req
, in
);
294 blobmsg_close_table(&req
, ins
);
296 if (!ubus_lookup_id(host_ubus_ctx
, "container", &id
))
297 running
= !ubus_invoke(host_ubus_ctx
, id
, "add", req
.head
, NULL
, NULL
, 3000);
301 netifd_out_ubusmount
:
305 netifd_out_ucinetconf
:
307 unlink(uci_config_network
);
308 free(uci_config_network
);
313 netifd_out_resolvconf
:
315 netifd_out_resolvconf_dir
:
316 free(resolvconf_dir
);
321 static struct uloop_timeout netifd_start_timeout
= { .cb
= run_netifd
, };
323 static void inotify_read_handler(struct uloop_fd
*u
, unsigned int events
)
327 struct inotify_event
*in
;
329 /* read inotify events */
330 while ((rc
= read(u
->fd
, inotify_buffer
, INOTIFY_SZ
)) == -1 && errno
== EINTR
);
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
;
344 if (!strncmp(ubus_sock_name
, in
->name
, in
->len
))
345 uloop_timeout_add(&netifd_start_timeout
);
349 static void netns_updown(struct ubus_context
*ubus
, const char *name
, bool start
, int netns_fd
)
351 static struct blob_buf req
;
357 blob_buf_init(&req
, 0);
359 blobmsg_add_string(&req
, "jail", name
);
361 blobmsg_add_u8(&req
, "start", start
);
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");
371 static void jail_network_reload(struct uloop_timeout
*t
)
378 if (gen_jail_uci_network())
381 if (ubus_lookup_id(jail_ubus_ctx
, "network", &id
))
384 ubus_invoke(jail_ubus_ctx
, id
, "reload", NULL
, NULL
, NULL
, 3000);
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
, };
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
)
394 struct blob_attr
*attr
;
397 if (strcmp(method
, "config.change"))
400 blobmsg_parse(&service_watch_policy
, 1, &attr
, blob_data(msg
), blob_len(msg
));
404 config
= blobmsg_get_string(attr
);
405 if (strcmp(config
, "network"))
408 uloop_timeout_add(&jail_network_reload_timeout
);
413 static void watch_ubus_service(void)
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");
423 if (ubus_lookup_id(host_ubus_ctx
, "service", &id
))
426 if (!ubus_subscribe(host_ubus_ctx
, &config_watch_subscribe
, id
))
429 ERROR("failed to subscribe %d\n", id
);
432 static struct uloop_timeout ubus_start_timeout
= { .cb
= run_ubusd
, };
434 int jail_network_start(struct ubus_context
*new_ctx
, char *new_jail_name
, pid_t new_ns_pid
)
436 ubus_pw
= getpwnam("ubus");
440 host_ubus_ctx
= new_ctx
;
442 jail_name
= new_jail_name
;
444 if (asprintf(&ubus_sock_dir
, "/var/containers/ubus-%s", jail_name
) == -1) {
449 if (asprintf(&ubus_sock_path
, "%s/%s", ubus_sock_dir
, ubus_sock_name
) == -1) {
454 mkdir_p(ubus_sock_dir
, 0755);
456 ret
= chown(ubus_sock_dir
, ubus_pw
->pw_uid
, ubus_pw
->pw_gid
);
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");
470 uloop_fd_add(&fd_inotify_read
, ULOOP_READ
);
472 inotify_buffer
= calloc(1, INOTIFY_SZ
);
473 if (!inotify_buffer
) {
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
);
485 watch_ubus_service();
487 netns_fd
= ns_open_pid("net", ns_pid
);
493 netns_updown(host_ubus_ctx
, jail_name
, true, netns_fd
);
496 uloop_timeout_add(&ubus_start_timeout
);
502 close(fd_inotify_read
.fd
);
504 free(ubus_sock_path
);
511 static int jail_delete_instance(const char *instance
)
513 static struct blob_buf req
;
516 if (ubus_lookup_id(host_ubus_ctx
, "container", &id
))
519 blob_buf_init(&req
, 0);
520 blobmsg_add_string(&req
, "name", jail_name
);
521 blobmsg_add_string(&req
, "instance", instance
);
523 return ubus_invoke(host_ubus_ctx
, id
, "delete", req
.head
, NULL
, NULL
, 3000);
526 int jail_network_stop(void)
528 int host_netns
= open("/proc/self/ns/net", O_RDONLY
);
533 netns_updown(jail_ubus_ctx
, NULL
, false, host_netns
);
536 ubus_free(jail_ubus_ctx
);
538 jail_delete_instance("netifd");
539 jail_delete_instance("ubus");
541 if (uci_config_network
) {
542 unlink(uci_config_network
);
543 rmdir(dirname(uci_config_network
));
544 free(uci_config_network
);
547 free(ubus_sock_path
);
548 rmdir(ubus_sock_dir
);