]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core: add bpf-foreign unit helpers
authorJulia Kartseva <hex@fb.com>
Wed, 16 Sep 2020 22:58:04 +0000 (15:58 -0700)
committerJulia Kartseva <hex@fb.com>
Sat, 10 Apr 2021 03:28:47 +0000 (20:28 -0700)
- Introduce support of cgroup-bpf programs managed (i.e. compiled,
loaded to and unloaded from kernel) externally. Systemd is only
responsible for attaching programs to unit cgroup hence the name
'foreign'.

Foreign BPF programs are identified by bpf program ID and attach type.

systemd:
- Gets kernel FD of BPF program;
- Makes a unique identifier of BPF program from BPF attach type and
program ID. Same program IDs mean the same program, i.e the same
chunk of kernel memory. Even if the same program is passed multiple
times, identical (program_id, attach_type) instances are collapsed
into one;
- Attaches programs to unit cgroup.

src/core/bpf-foreign.c [new file with mode: 0644]
src/core/bpf-foreign.h [new file with mode: 0644]
src/core/meson.build
src/core/unit.c
src/core/unit.h

diff --git a/src/core/bpf-foreign.c b/src/core/bpf-foreign.c
new file mode 100644 (file)
index 0000000..98655bd
--- /dev/null
@@ -0,0 +1,151 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "bpf-foreign.h"
+#include "bpf-program.h"
+#include "cgroup.h"
+#include "memory-util.h"
+#include "mountpoint-util.h"
+#include "set.h"
+
+typedef struct BPFForeignKey BPFForeignKey;
+struct BPFForeignKey {
+        uint32_t prog_id;
+        uint32_t attach_type;
+};
+
+static int bpf_foreign_key_new(uint32_t prog_id,
+                enum bpf_attach_type attach_type,
+                BPFForeignKey **ret) {
+        _cleanup_free_ BPFForeignKey *p = NULL;
+
+        assert(ret);
+
+        p = new(BPFForeignKey, 1);
+        if (!p)
+                return log_oom();
+
+        *p = (BPFForeignKey) {
+                .prog_id = prog_id,
+                .attach_type = attach_type,
+        };
+
+        *ret = TAKE_PTR(p);
+
+        return 0;
+}
+
+static int bpf_foreign_key_compare_func(const BPFForeignKey *a, const BPFForeignKey *b) {
+        int r = CMP(a->prog_id, b->prog_id);
+        if (r != 0)
+                return r;
+
+        return CMP(a->attach_type, b->attach_type);
+}
+
+static void bpf_foreign_key_hash_func(const BPFForeignKey *p, struct siphash *h) {
+        siphash24_compress(&p->prog_id, sizeof(p->prog_id), h);
+        siphash24_compress(&p->attach_type, sizeof(p->attach_type), h);
+}
+
+DEFINE_PRIVATE_HASH_OPS_FULL(bpf_foreign_by_key_hash_ops,
+                BPFForeignKey, bpf_foreign_key_hash_func, bpf_foreign_key_compare_func, free,
+                BPFProgram, bpf_program_unref);
+
+static int attach_programs(Unit *u, const char *path, Hashmap* foreign_by_key, uint32_t attach_flags) {
+        const BPFForeignKey *key;
+        BPFProgram *prog;
+        int r;
+
+        assert(u);
+
+        HASHMAP_FOREACH_KEY(prog, key, foreign_by_key) {
+                r = bpf_program_cgroup_attach(prog, key->attach_type, path, attach_flags);
+                if (r < 0)
+                        return log_unit_error_errno(u, r, "Attaching foreign BPF program to cgroup %s failed: %m", path);
+        }
+
+        return 0;
+}
+
+/*
+ * Prepare foreign BPF program for installation:
+ * - Load the program from BPF filesystem to the kernel;
+ * - Store program FD identified by program ID and attach type in the unit.
+ */
+static int bpf_foreign_prepare(
+                Unit *u,
+                enum bpf_attach_type attach_type,
+                const char *bpffs_path) {
+        _cleanup_(bpf_program_unrefp) BPFProgram *prog = NULL;
+        _cleanup_free_ BPFForeignKey *key = NULL;
+        uint32_t prog_id;
+        int r;
+
+        assert(u);
+        assert(bpffs_path);
+
+        r = bpf_program_new_from_bpffs_path(bpffs_path, &prog);
+        if (r < 0)
+                return log_unit_error_errno(u, r, "Failed to create foreign BPFProgram: %m");
+
+        r = bpf_program_get_id_by_fd(prog->kernel_fd, &prog_id);
+        if (r < 0)
+                return log_unit_error_errno(u, r, "Failed to get BPF program id by fd: %m");
+
+        r = bpf_foreign_key_new(prog_id, attach_type, &key);
+        if (r < 0)
+                return log_unit_error_errno(u, r,
+                                "Failed to create foreign BPF program key from path '%s': %m", bpffs_path);
+
+        r = hashmap_ensure_put(&u->bpf_foreign_by_key, &bpf_foreign_by_key_hash_ops, key, prog);
+        if (r == -EEXIST) {
+                log_unit_warning_errno(u, r, "Foreign BPF program already exists, ignoring: %m");
+                return 0;
+        }
+        if (r < 0)
+                return log_unit_error_errno(u, r, "Failed to put foreign BPFProgram into map: %m");
+
+        TAKE_PTR(key);
+        TAKE_PTR(prog);
+
+        return 0;
+}
+
+int bpf_foreign_supported(void) {
+        int r;
+
+        r = cg_all_unified();
+        if (r <= 0)
+                return r;
+
+        return path_is_mount_point("/sys/fs/bpf", NULL, 0);
+}
+
+int bpf_foreign_install(Unit *u) {
+        _cleanup_free_ char *cgroup_path = NULL;
+        CGroupBPFForeignProgram *p;
+        CGroupContext *cc;
+        int r;
+
+        assert(u);
+
+        cc = unit_get_cgroup_context(u);
+        if (!cc)
+                return 0;
+
+        r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, NULL, &cgroup_path);
+        if (r < 0)
+                return log_unit_error_errno(u, r, "Failed to get cgroup path: %m");
+
+        LIST_FOREACH(programs, p, cc->bpf_foreign_programs) {
+                r = bpf_foreign_prepare(u, p->attach_type, p->bpffs_path);
+                if (r < 0)
+                        return log_unit_error_errno(u, r, "Failed to prepare foreign BPF hashmap: %m");
+        }
+
+        r = attach_programs(u, cgroup_path, u->bpf_foreign_by_key, BPF_F_ALLOW_MULTI);
+        if (r < 0)
+                  return log_unit_error_errno(u, r, "Failed to install foreign BPF programs: %m");
+
+        return 0;
+}
diff --git a/src/core/bpf-foreign.h b/src/core/bpf-foreign.h
new file mode 100644 (file)
index 0000000..7704986
--- /dev/null
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#pragma once
+
+#include "unit.h"
+
+int bpf_foreign_supported(void);
+/*
+ * Attach cgroup-bpf programs foreign to systemd, i.e. loaded to the kernel by an entity
+ * external to systemd.
+ */
+int bpf_foreign_install(Unit *u);
index a389c906b368609084a171fd8cd9235a56ef8110..a1294f3a7251cc3ebc80cc34ee81b6d7bffad4f0 100644 (file)
@@ -11,6 +11,8 @@ libcore_sources = '''
         bpf-devices.h
         bpf-firewall.c
         bpf-firewall.h
+        bpf-foreign.c
+        bpf-foreign.h
         cgroup.c
         cgroup.h
         core-varlink.c
index 2c5dc54379a77e0e86f2816ba79c58a91ee6e6d3..cf83272dcbed6b01abfe3034206c9fa0ff377413 100644 (file)
@@ -11,6 +11,7 @@
 #include "all-units.h"
 #include "alloc-util.h"
 #include "bpf-firewall.h"
+#include "bpf-foreign.h"
 #include "bus-common-errors.h"
 #include "bus-util.h"
 #include "cgroup-setup.h"
@@ -723,6 +724,8 @@ Unit* unit_free(Unit *u) {
         set_free(u->ip_bpf_custom_ingress_installed);
         set_free(u->ip_bpf_custom_egress_installed);
 
+        hashmap_free(u->bpf_foreign_by_key);
+
         bpf_program_unref(u->bpf_device_control_installed);
 
         condition_free_list(u->conditions);
index 6d38e6668031112855e81e66f88d18549f982758..128122b8df7bc82081d553824e05d8166675747c 100644 (file)
@@ -305,6 +305,10 @@ typedef struct Unit {
         Set *ip_bpf_custom_egress;
         Set *ip_bpf_custom_egress_installed;
 
+        /* BPF programs managed (e.g. loaded to kernel) by an entity external to systemd,
+         * attached to unit cgroup by provided program fd and attach type. */
+        Hashmap *bpf_foreign_by_key;
+
         uint64_t ip_accounting_extra[_CGROUP_IP_ACCOUNTING_METRIC_MAX];
 
         /* Low-priority event source which is used to remove watched PIDs that have gone away, and subscribe to any new