summaryrefslogtreecommitdiff
path: root/src/core/bpf-foreign.c
blob: 83c3bac87fda5346aac88897f6098e91ac42de35 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
/* SPDX-License-Identifier: LGPL-2.1-or-later */

#include "bpf-foreign.h"
#include "bpf-program.h"
#include "cgroup.h"
#include "memory-util.h"
#include "missing_magic.h"
#include "mountpoint-util.h"
#include "set.h"
#include "stat-util.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_free);

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, "bpf-foreign: 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_freep) BPFProgram *prog = NULL;
        _cleanup_free_ BPFForeignKey *key = NULL;
        uint32_t prog_id;
        int r;

        assert(u);
        assert(bpffs_path);

        r = path_is_fs_type(bpffs_path, BPF_FS_MAGIC);
        if (r < 0)
                return log_unit_error_errno(u, r,
                                "bpf-foreign: Failed to determine filesystem type of %s: %m", bpffs_path);
        if (r == 0)
                return log_unit_error_errno(u, SYNTHETIC_ERRNO(EINVAL),
                                "bpf-foreign: Path in BPF filesystem is expected.");

        r = bpf_program_new_from_bpffs_path(bpffs_path, &prog);
        if (r < 0)
                return log_unit_error_errno(u, r, "bpf-foreign: Failed to create foreign BPF program: %m");

        r = bpf_program_get_id_by_fd(prog->kernel_fd, &prog_id);
        if (r < 0)
                return log_unit_error_errno(u, r, "bpf-foreign: Failed to get BPF program id from fd: %m");

        r = bpf_foreign_key_new(prog_id, attach_type, &key);
        if (r < 0)
                return log_unit_error_errno(u, r,
                                "bpf-foreign: 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, "bpf-foreign: Foreign BPF program already exists, ignoring: %m");
                return 0;
        }
        if (r < 0)
                return log_unit_error_errno(u, r, "bpf-foreign: Failed to put foreign BPF program into map: %m");

        TAKE_PTR(key);
        TAKE_PTR(prog);

        return 0;
}

int bpf_foreign_install(Unit *u) {
        _cleanup_free_ char *cgroup_path = NULL;
        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, "bpf-foreign: 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, "bpf-foreign: 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, "bpf-foreign: Failed to install foreign BPF programs: %m");

        return 0;
}