]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
bpf-program: serialize attached BPF programs across daemon reexec/reload
authorLennart Poettering <lennart@poettering.net>
Tue, 8 Jun 2021 16:20:02 +0000 (18:20 +0200)
committerLennart Poettering <lennart@poettering.net>
Tue, 8 Jun 2021 20:02:35 +0000 (22:02 +0200)
Alternative to #17495

src/core/unit-serialize.c
src/shared/bpf-program.c
src/shared/bpf-program.h

index f8a1ca7b75c6da3e71b6ce85a37458105aa91d52..daf7c59cc15965b25bdfe5756f91a43499a3b17f 100644 (file)
@@ -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);
index 647dd6e2ba6e1a30a9ca52c05e1c98d16ec21ecf..0f865a71689a38f89134a3e406847975c544b80f 100644 (file)
@@ -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;
+}
index b8b9f792610f66879907e40f56788df8448e72b2..245e2b395ddd4a274bec868a38be41f65baa9c63 100644 (file)
@@ -3,8 +3,10 @@
 
 #include <linux/bpf.h>
 #include <stdint.h>
+#include <stdio.h>
 #include <sys/syscall.h>
 
+#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);