]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/bpf-program.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2016 Daniel Mack
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
23 #include <sys/types.h>
26 #include "alloc-util.h"
27 #include "bpf-program.h"
31 #include "path-util.h"
34 int bpf_program_new(uint32_t prog_type
, BPFProgram
**ret
) {
35 _cleanup_(bpf_program_unrefp
) BPFProgram
*p
= NULL
;
37 p
= new0(BPFProgram
, 1);
42 p
->prog_type
= prog_type
;
50 BPFProgram
*bpf_program_ref(BPFProgram
*p
) {
60 BPFProgram
*bpf_program_unref(BPFProgram
*p
) {
70 /* Unfortunately, the kernel currently doesn't implicitly detach BPF programs from their cgroups when the last
71 * fd to the BPF program is closed. This has nasty side-effects since this means that abnormally terminated
72 * programs that attached one of their BPF programs to a cgroup will leave this programs pinned for good with
73 * zero chance of recovery, until the cgroup is removed. This is particularly problematic if the cgroup in
74 * question is the root cgroup (or any other cgroup belonging to a service that cannot be restarted during
75 * operation, such as dbus), as the memory for the BPF program can only be reclaimed through a reboot. To
76 * counter this, we track closely to which cgroup a program was attached to and will detach it on our own
77 * whenever we close the BPF fd. */
78 (void) bpf_program_cgroup_detach(p
);
80 safe_close(p
->kernel_fd
);
81 free(p
->instructions
);
82 free(p
->attached_path
);
87 int bpf_program_add_instructions(BPFProgram
*p
, const struct bpf_insn
*instructions
, size_t count
) {
91 if (p
->kernel_fd
>= 0) /* don't allow modification after we uploaded things to the kernel */
94 if (!GREEDY_REALLOC(p
->instructions
, p
->allocated
, p
->n_instructions
+ count
))
97 memcpy(p
->instructions
+ p
->n_instructions
, instructions
, sizeof(struct bpf_insn
) * count
);
98 p
->n_instructions
+= count
;
103 int bpf_program_load_kernel(BPFProgram
*p
, char *log_buf
, size_t log_size
) {
108 if (p
->kernel_fd
>= 0) { /* make this idempotent */
109 memzero(log_buf
, log_size
);
113 attr
= (union bpf_attr
) {
114 .prog_type
= p
->prog_type
,
115 .insns
= PTR_TO_UINT64(p
->instructions
),
116 .insn_cnt
= p
->n_instructions
,
117 .license
= PTR_TO_UINT64("GPL"),
118 .log_buf
= PTR_TO_UINT64(log_buf
),
119 .log_level
= !!log_buf
,
120 .log_size
= log_size
,
123 p
->kernel_fd
= bpf(BPF_PROG_LOAD
, &attr
, sizeof(attr
));
124 if (p
->kernel_fd
< 0)
130 int bpf_program_cgroup_attach(BPFProgram
*p
, int type
, const char *path
, uint32_t flags
) {
131 _cleanup_free_
char *copy
= NULL
;
132 _cleanup_close_
int fd
= -1;
140 if (!IN_SET(flags
, 0, BPF_F_ALLOW_OVERRIDE
, BPF_F_ALLOW_MULTI
))
143 /* We need to track which cgroup the program is attached to, and we can only track one attachment, hence let's
144 * refuse this early. */
145 if (p
->attached_path
) {
146 if (!path_equal(p
->attached_path
, path
))
148 if (p
->attached_type
!= type
)
150 if (p
->attached_flags
!= flags
)
153 /* Here's a shortcut: if we previously attached this program already, then we don't have to do so
154 * again. Well, with one exception: if we are in BPF_F_ALLOW_OVERRIDE mode then someone else might have
155 * replaced our program since the last time, hence let's reattach it again, just to be safe. In flags
156 * == 0 mode this is not an issue since nobody else can replace our program in that case, and in flags
157 * == BPF_F_ALLOW_MULTI mode any other's program would be installed in addition to ours hence ours
158 * would remain in effect. */
159 if (flags
!= BPF_F_ALLOW_OVERRIDE
)
163 /* Ensure we have a kernel object for this. */
164 r
= bpf_program_load_kernel(p
, NULL
, 0);
172 fd
= open(path
, O_DIRECTORY
|O_RDONLY
|O_CLOEXEC
);
176 attr
= (union bpf_attr
) {
179 .attach_bpf_fd
= p
->kernel_fd
,
180 .attach_flags
= flags
,
183 if (bpf(BPF_PROG_ATTACH
, &attr
, sizeof(attr
)) < 0)
186 free_and_replace(p
->attached_path
, copy
);
187 p
->attached_type
= type
;
188 p
->attached_flags
= flags
;
193 int bpf_program_cgroup_detach(BPFProgram
*p
) {
194 _cleanup_close_
int fd
= -1;
198 if (!p
->attached_path
)
201 fd
= open(p
->attached_path
, O_DIRECTORY
|O_RDONLY
|O_CLOEXEC
);
206 /* If the cgroup does not exist anymore, then we don't have to explicitly detach, it got detached
207 * implicitly by the removal, hence don't complain */
212 attr
= (union bpf_attr
) {
213 .attach_type
= p
->attached_type
,
215 .attach_bpf_fd
= p
->kernel_fd
,
218 if (bpf(BPF_PROG_DETACH
, &attr
, sizeof(attr
)) < 0)
222 p
->attached_path
= mfree(p
->attached_path
);
227 int bpf_map_new(enum bpf_map_type type
, size_t key_size
, size_t value_size
, size_t max_entries
, uint32_t flags
) {
228 union bpf_attr attr
= {
230 .key_size
= key_size
,
231 .value_size
= value_size
,
232 .max_entries
= max_entries
,
237 fd
= bpf(BPF_MAP_CREATE
, &attr
, sizeof(attr
));
244 int bpf_map_update_element(int fd
, const void *key
, void *value
) {
246 union bpf_attr attr
= {
248 .key
= PTR_TO_UINT64(key
),
249 .value
= PTR_TO_UINT64(value
),
252 if (bpf(BPF_MAP_UPDATE_ELEM
, &attr
, sizeof(attr
)) < 0)
258 int bpf_map_lookup_element(int fd
, const void *key
, void *value
) {
260 union bpf_attr attr
= {
262 .key
= PTR_TO_UINT64(key
),
263 .value
= PTR_TO_UINT64(value
),
266 if (bpf(BPF_MAP_LOOKUP_ELEM
, &attr
, sizeof(attr
)) < 0)