From: Lennart Poettering Date: Tue, 8 Jun 2021 16:20:02 +0000 (+0200) Subject: bpf-program: serialize attached BPF programs across daemon reexec/reload X-Git-Tag: v249-rc1~57^2~2 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=b57d75232615f98aefcf41cb145ec2ea3262857d;p=thirdparty%2Fsystemd.git bpf-program: serialize attached BPF programs across daemon reexec/reload Alternative to #17495 --- diff --git a/src/core/unit-serialize.c b/src/core/unit-serialize.c index f8a1ca7b75c..daf7c59cc15 100644 --- a/src/core/unit-serialize.c +++ b/src/core/unit-serialize.c @@ -166,6 +166,11 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool switching_root) { (void) bpf_serialize_socket_bind(u, f, fds); + (void) bpf_program_serialize_attachment(f, fds, "ip-bpf-ingress-installed", u->ip_bpf_ingress_installed); + (void) bpf_program_serialize_attachment(f, fds, "ip-bpf-egress-installed", u->ip_bpf_egress_installed); + (void) bpf_program_serialize_attachment_set(f, fds, "ip-bpf-custom-ingress-installed", u->ip_bpf_custom_ingress_installed); + (void) bpf_program_serialize_attachment_set(f, fds, "ip-bpf-custom-egress-installed", u->ip_bpf_custom_egress_installed); + if (uid_is_valid(u->ref_uid)) (void) serialize_item_format(f, "ref-uid", UID_FMT, u->ref_uid); if (gid_is_valid(u->ref_gid)) @@ -385,16 +390,28 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) { else { if (fdset_remove(fds, fd) < 0) { log_unit_debug(u, "Failed to remove %s value=%d from fdset", l, fd); - continue; } (void) bpf_socket_bind_add_initial_link_fd(u, fd); } continue; - } - else if (streq(l, "ref-uid")) { + } else if (streq(l, "ip-bpf-ingress-installed")) { + (void) bpf_program_deserialize_attachment(v, fds, &u->ip_bpf_ingress_installed); + continue; + } else if (streq(l, "ip-bpf-egress-installed")) { + (void) bpf_program_deserialize_attachment(v, fds, &u->ip_bpf_egress_installed); + continue; + + } else if (streq(l, "ip-bpf-custom-ingress-installed")) { + (void) bpf_program_deserialize_attachment_set(v, fds, &u->ip_bpf_custom_ingress_installed); + continue; + } else if (streq(l, "ip-bpf-custom-egress-installed")) { + (void) bpf_program_deserialize_attachment_set(v, fds, &u->ip_bpf_custom_egress_installed); + continue; + + } else if (streq(l, "ref-uid")) { uid_t uid; r = parse_uid(v, &uid); diff --git a/src/shared/bpf-program.c b/src/shared/bpf-program.c index 647dd6e2ba6..0f865a71689 100644 --- a/src/shared/bpf-program.c +++ b/src/shared/bpf-program.c @@ -7,10 +7,12 @@ #include "alloc-util.h" #include "bpf-program.h" +#include "escape.h" #include "fd-util.h" #include "memory-util.h" #include "missing_syscall.h" #include "path-util.h" +#include "serialize.h" #include "string-table.h" static const char *const bpf_cgroup_attach_type_table[__MAX_BPF_ATTACH_TYPE] = { @@ -362,3 +364,139 @@ int bpf_program_get_id_by_fd(int prog_fd, uint32_t *ret_id) { return 0; }; + +int bpf_program_serialize_attachment( + FILE *f, + FDSet *fds, + const char *key, + BPFProgram *p) { + + _cleanup_free_ char *escaped = NULL; + int copy, r; + + if (!p || !p->attached_path) + return 0; + + assert(p->kernel_fd >= 0); + + escaped = cescape(p->attached_path); + if (!escaped) + return -ENOMEM; + + copy = fdset_put_dup(fds, p->kernel_fd); + if (copy < 0) + return log_error_errno(copy, "Failed to add BPF kernel fd to serialize: %m"); + + r = serialize_item_format( + f, + key, + "%i %s %s", + copy, + bpf_cgroup_attach_type_to_string(p->attached_type), + escaped); + if (r < 0) + return r; + + /* After serialization, let's forget the fact that this program is attached. The attachment — if you + * so will — is now 'owned' by the serialization, and not us anymore. Why does that matter? Because + * of BPF's less-than-ideal lifecycle handling: to detach a program from a cgroup we have to + * explicitly do so, it's not done implicitly on close(). Now, since we are serializing here we don't + * want the program to be detached while freeing things, so that the attachment can be retained after + * deserializing again. bpf_program_free() implicitly detaches things, if attached_path is non-NULL, + * hence we set it to NULL here. */ + + p->attached_path = mfree(p->attached_path); + return 0; +} + +int bpf_program_serialize_attachment_set(FILE *f, FDSet *fds, const char *key, Set *set) { + BPFProgram *p; + int r; + + SET_FOREACH(p, set) { + r = bpf_program_serialize_attachment(f, fds, key, p); + if (r < 0) + return r; + } + + return 0; +} + +int bpf_program_deserialize_attachment(const char *v, FDSet *fds, BPFProgram **bpfp) { + _cleanup_free_ char *sfd = NULL, *sat = NULL, *unescaped = NULL; + _cleanup_(bpf_program_unrefp) BPFProgram *p = NULL; + _cleanup_close_ int fd = -1; + int ifd, at, r; + + assert(v); + assert(bpfp); + + /* Extract first word: the fd number */ + r = extract_first_word(&v, &sfd, NULL, 0); + if (r < 0) + return r; + if (r == 0) + return -EINVAL; + + r = safe_atoi(sfd, &ifd); + if (r < 0) + return r; + if (ifd < 0) + return -EBADF; + + /* Extract second word: the attach type */ + r = extract_first_word(&v, &sat, NULL, 0); + if (r < 0) + return r; + if (r == 0) + return -EINVAL; + + at = bpf_cgroup_attach_type_from_string(sat); + if (at < 0) + return at; + + /* The rest is the path */ + r = cunescape(v, 0, &unescaped); + if (r < 0) + return r; + + fd = fdset_remove(fds, ifd); + if (fd < 0) + return fd; + + p = new(BPFProgram, 1); + if (!p) + return -ENOMEM; + + *p = (BPFProgram) { + .n_ref = 1, + .kernel_fd = TAKE_FD(fd), + .prog_type = BPF_PROG_TYPE_UNSPEC, + .attached_path = TAKE_PTR(unescaped), + .attached_type = at, + }; + + if (*bpfp) + bpf_program_unref(*bpfp); + + *bpfp = TAKE_PTR(p); + return 0; +} + +int bpf_program_deserialize_attachment_set(const char *v, FDSet *fds, Set **bpfsetp) { + BPFProgram *p = NULL; + int r; + + assert(v); + assert(bpfsetp); + + r = bpf_program_deserialize_attachment(v, fds, &p); + if (r < 0) + return r; + + r = set_ensure_consume(bpfsetp, &bpf_program_hash_ops, p); + if (r < 0) + return r; + + return 0; +} diff --git a/src/shared/bpf-program.h b/src/shared/bpf-program.h index b8b9f792610..245e2b395dd 100644 --- a/src/shared/bpf-program.h +++ b/src/shared/bpf-program.h @@ -3,8 +3,10 @@ #include #include +#include #include +#include "fdset.h" #include "list.h" #include "macro.h" @@ -38,6 +40,11 @@ int bpf_program_cgroup_detach(BPFProgram *p); int bpf_program_pin(int prog_fd, const char *bpffs_path); int bpf_program_get_id_by_fd(int prog_fd, uint32_t *ret_id); +int bpf_program_serialize_attachment(FILE *f, FDSet *fds, const char *key, BPFProgram *p); +int bpf_program_serialize_attachment_set(FILE *f, FDSet *fds, const char *key, Set *set); +int bpf_program_deserialize_attachment(const char *v, FDSet *fds, BPFProgram **bpfp); +int bpf_program_deserialize_attachment_set(const char *v, FDSet *fds, Set **bpfsetp); + extern const struct hash_ops bpf_program_hash_ops; int bpf_map_new(enum bpf_map_type type, size_t key_size, size_t value_size, size_t max_entries, uint32_t flags);