Branch data Line data Source code
1 : : /* SPDX-License-Identifier: LGPL-2.1+ */
2 : :
3 : : #include <fcntl.h>
4 : : #include <sys/stat.h>
5 : : #include <sys/types.h>
6 : : #include <unistd.h>
7 : :
8 : : #include "alloc-util.h"
9 : : #include "bpf-program.h"
10 : : #include "fd-util.h"
11 : : #include "log.h"
12 : : #include "memory-util.h"
13 : : #include "missing.h"
14 : : #include "path-util.h"
15 : :
16 : 20 : int bpf_program_new(uint32_t prog_type, BPFProgram **ret) {
17 : 20 : _cleanup_(bpf_program_unrefp) BPFProgram *p = NULL;
18 : :
19 : 20 : p = new0(BPFProgram, 1);
20 [ - + ]: 20 : if (!p)
21 : 0 : return log_oom();
22 : :
23 : 20 : p->n_ref = 1;
24 : 20 : p->prog_type = prog_type;
25 : 20 : p->kernel_fd = -1;
26 : :
27 : 20 : *ret = TAKE_PTR(p);
28 : :
29 : 20 : return 0;
30 : : }
31 : :
32 : 20 : static BPFProgram *bpf_program_free(BPFProgram *p) {
33 [ - + ]: 20 : assert(p);
34 : :
35 : : /* Unfortunately, the kernel currently doesn't implicitly detach BPF programs from their cgroups when the last
36 : : * fd to the BPF program is closed. This has nasty side-effects since this means that abnormally terminated
37 : : * programs that attached one of their BPF programs to a cgroup will leave this programs pinned for good with
38 : : * zero chance of recovery, until the cgroup is removed. This is particularly problematic if the cgroup in
39 : : * question is the root cgroup (or any other cgroup belonging to a service that cannot be restarted during
40 : : * operation, such as dbus), as the memory for the BPF program can only be reclaimed through a reboot. To
41 : : * counter this, we track closely to which cgroup a program was attached to and will detach it on our own
42 : : * whenever we close the BPF fd. */
43 : 20 : (void) bpf_program_cgroup_detach(p);
44 : :
45 : 20 : safe_close(p->kernel_fd);
46 : 20 : free(p->instructions);
47 : 20 : free(p->attached_path);
48 : :
49 : 20 : return mfree(p);
50 : : }
51 : :
52 [ + + - + : 43660 : DEFINE_TRIVIAL_REF_UNREF_FUNC(BPFProgram, bpf_program, bpf_program_free);
- + ]
53 : :
54 : 20 : int bpf_program_add_instructions(BPFProgram *p, const struct bpf_insn *instructions, size_t count) {
55 : :
56 [ - + ]: 20 : assert(p);
57 : :
58 [ - + ]: 20 : if (p->kernel_fd >= 0) /* don't allow modification after we uploaded things to the kernel */
59 : 0 : return -EBUSY;
60 : :
61 [ - + ]: 20 : if (!GREEDY_REALLOC(p->instructions, p->allocated, p->n_instructions + count))
62 : 0 : return -ENOMEM;
63 : :
64 : 20 : memcpy(p->instructions + p->n_instructions, instructions, sizeof(struct bpf_insn) * count);
65 : 20 : p->n_instructions += count;
66 : :
67 : 20 : return 0;
68 : : }
69 : :
70 : 20 : int bpf_program_load_kernel(BPFProgram *p, char *log_buf, size_t log_size) {
71 : : union bpf_attr attr;
72 : :
73 [ - + ]: 20 : assert(p);
74 : :
75 [ - + ]: 20 : if (p->kernel_fd >= 0) { /* make this idempotent */
76 [ # # ]: 0 : memzero(log_buf, log_size);
77 : 0 : return 0;
78 : : }
79 : :
80 : 80 : attr = (union bpf_attr) {
81 : 20 : .prog_type = p->prog_type,
82 : 20 : .insns = PTR_TO_UINT64(p->instructions),
83 : 20 : .insn_cnt = p->n_instructions,
84 : 20 : .license = PTR_TO_UINT64("GPL"),
85 : 20 : .log_buf = PTR_TO_UINT64(log_buf),
86 : 20 : .log_level = !!log_buf,
87 : : .log_size = log_size,
88 : : };
89 : :
90 : 20 : p->kernel_fd = bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
91 [ - + ]: 20 : if (p->kernel_fd < 0)
92 : 0 : return -errno;
93 : :
94 : 20 : return 0;
95 : : }
96 : :
97 : 0 : int bpf_program_load_from_bpf_fs(BPFProgram *p, const char *path) {
98 : : union bpf_attr attr;
99 : :
100 [ # # ]: 0 : assert(p);
101 : :
102 [ # # ]: 0 : if (p->kernel_fd >= 0) /* don't overwrite an assembled or loaded program */
103 : 0 : return -EBUSY;
104 : :
105 : 0 : attr = (union bpf_attr) {
106 : 0 : .pathname = PTR_TO_UINT64(path),
107 : : };
108 : :
109 : 0 : p->kernel_fd = bpf(BPF_OBJ_GET, &attr, sizeof(attr));
110 [ # # ]: 0 : if (p->kernel_fd < 0)
111 : 0 : return -errno;
112 : :
113 : 0 : return 0;
114 : : }
115 : :
116 : 0 : int bpf_program_cgroup_attach(BPFProgram *p, int type, const char *path, uint32_t flags) {
117 : 0 : _cleanup_free_ char *copy = NULL;
118 : 0 : _cleanup_close_ int fd = -1;
119 : : union bpf_attr attr;
120 : : int r;
121 : :
122 [ # # ]: 0 : assert(p);
123 [ # # ]: 0 : assert(type >= 0);
124 [ # # ]: 0 : assert(path);
125 : :
126 [ # # # # ]: 0 : if (!IN_SET(flags, 0, BPF_F_ALLOW_OVERRIDE, BPF_F_ALLOW_MULTI))
127 : 0 : return -EINVAL;
128 : :
129 : : /* We need to track which cgroup the program is attached to, and we can only track one attachment, hence let's
130 : : * refuse this early. */
131 [ # # ]: 0 : if (p->attached_path) {
132 [ # # ]: 0 : if (!path_equal(p->attached_path, path))
133 : 0 : return -EBUSY;
134 [ # # ]: 0 : if (p->attached_type != type)
135 : 0 : return -EBUSY;
136 [ # # ]: 0 : if (p->attached_flags != flags)
137 : 0 : return -EBUSY;
138 : :
139 : : /* Here's a shortcut: if we previously attached this program already, then we don't have to do so
140 : : * again. Well, with one exception: if we are in BPF_F_ALLOW_OVERRIDE mode then someone else might have
141 : : * replaced our program since the last time, hence let's reattach it again, just to be safe. In flags
142 : : * == 0 mode this is not an issue since nobody else can replace our program in that case, and in flags
143 : : * == BPF_F_ALLOW_MULTI mode any other's program would be installed in addition to ours hence ours
144 : : * would remain in effect. */
145 [ # # ]: 0 : if (flags != BPF_F_ALLOW_OVERRIDE)
146 : 0 : return 0;
147 : : }
148 : :
149 : : /* Ensure we have a kernel object for this. */
150 : 0 : r = bpf_program_load_kernel(p, NULL, 0);
151 [ # # ]: 0 : if (r < 0)
152 : 0 : return r;
153 : :
154 : 0 : copy = strdup(path);
155 [ # # ]: 0 : if (!copy)
156 : 0 : return -ENOMEM;
157 : :
158 : 0 : fd = open(path, O_DIRECTORY|O_RDONLY|O_CLOEXEC);
159 [ # # ]: 0 : if (fd < 0)
160 : 0 : return -errno;
161 : :
162 : 0 : attr = (union bpf_attr) {
163 : : .attach_type = type,
164 : : .target_fd = fd,
165 : 0 : .attach_bpf_fd = p->kernel_fd,
166 : : .attach_flags = flags,
167 : : };
168 : :
169 [ # # ]: 0 : if (bpf(BPF_PROG_ATTACH, &attr, sizeof(attr)) < 0)
170 : 0 : return -errno;
171 : :
172 : 0 : free_and_replace(p->attached_path, copy);
173 : 0 : p->attached_type = type;
174 : 0 : p->attached_flags = flags;
175 : :
176 : 0 : return 0;
177 : : }
178 : :
179 : 20 : int bpf_program_cgroup_detach(BPFProgram *p) {
180 : 20 : _cleanup_close_ int fd = -1;
181 : :
182 [ - + ]: 20 : assert(p);
183 : :
184 [ + - ]: 20 : if (!p->attached_path)
185 : 20 : return -EUNATCH;
186 : :
187 : 0 : fd = open(p->attached_path, O_DIRECTORY|O_RDONLY|O_CLOEXEC);
188 [ # # ]: 0 : if (fd < 0) {
189 [ # # ]: 0 : if (errno != ENOENT)
190 : 0 : return -errno;
191 : :
192 : : /* If the cgroup does not exist anymore, then we don't have to explicitly detach, it got detached
193 : : * implicitly by the removal, hence don't complain */
194 : :
195 : : } else {
196 : : union bpf_attr attr;
197 : :
198 : 0 : attr = (union bpf_attr) {
199 : 0 : .attach_type = p->attached_type,
200 : : .target_fd = fd,
201 : 0 : .attach_bpf_fd = p->kernel_fd,
202 : : };
203 : :
204 [ # # ]: 0 : if (bpf(BPF_PROG_DETACH, &attr, sizeof(attr)) < 0)
205 : 0 : return -errno;
206 : : }
207 : :
208 : 0 : p->attached_path = mfree(p->attached_path);
209 : :
210 : 0 : return 0;
211 : : }
212 : :
213 : 0 : int bpf_map_new(enum bpf_map_type type, size_t key_size, size_t value_size, size_t max_entries, uint32_t flags) {
214 : 0 : union bpf_attr attr = {
215 : : .map_type = type,
216 : : .key_size = key_size,
217 : : .value_size = value_size,
218 : : .max_entries = max_entries,
219 : : .map_flags = flags,
220 : : };
221 : : int fd;
222 : :
223 : 0 : fd = bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
224 [ # # ]: 0 : if (fd < 0)
225 : 0 : return -errno;
226 : :
227 : 0 : return fd;
228 : : }
229 : :
230 : 0 : int bpf_map_update_element(int fd, const void *key, void *value) {
231 : :
232 : 0 : union bpf_attr attr = {
233 : : .map_fd = fd,
234 : 0 : .key = PTR_TO_UINT64(key),
235 : 0 : .value = PTR_TO_UINT64(value),
236 : : };
237 : :
238 [ # # ]: 0 : if (bpf(BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr)) < 0)
239 : 0 : return -errno;
240 : :
241 : 0 : return 0;
242 : : }
243 : :
244 : 0 : int bpf_map_lookup_element(int fd, const void *key, void *value) {
245 : :
246 : 0 : union bpf_attr attr = {
247 : : .map_fd = fd,
248 : 0 : .key = PTR_TO_UINT64(key),
249 : 0 : .value = PTR_TO_UINT64(value),
250 : : };
251 : :
252 [ # # ]: 0 : if (bpf(BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr)) < 0)
253 : 0 : return -errno;
254 : :
255 : 0 : return 0;
256 : : }
|