Coverage Report

Created: 2025-07-09 06:39

/src/systemd/src/core/bpf-socket-bind.c
Line
Count
Source (jump to first uncovered line)
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3
#include "alloc-util.h"
4
#include "bpf-socket-bind.h"
5
#include "cgroup.h"
6
#include "fd-util.h"
7
#include "fdset.h"
8
#include "unit.h"
9
10
#if BPF_FRAMEWORK
11
/* libbpf, clang, llvm and bpftool compile time dependencies are satisfied */
12
#include "bpf-dlopen.h"
13
#include "bpf-link.h"
14
#include "bpf/socket_bind/socket-bind-api.bpf.h"
15
#include "bpf/socket_bind/socket-bind-skel.h"
16
17
static struct socket_bind_bpf *socket_bind_bpf_free(struct socket_bind_bpf *obj) {
18
        /* socket_bind_bpf__destroy handles object == NULL case */
19
        (void) socket_bind_bpf__destroy(obj);
20
21
        return NULL;
22
}
23
24
DEFINE_TRIVIAL_CLEANUP_FUNC(struct socket_bind_bpf *, socket_bind_bpf_free);
25
26
static int update_rules_map(
27
                int map_fd,
28
                CGroupSocketBindItem *head) {
29
30
        uint32_t i = 0;
31
32
        assert(map_fd >= 0);
33
34
        if (!head) {
35
                static const struct socket_bind_rule val = {
36
                        .address_family = SOCKET_BIND_RULE_AF_MATCH_NOTHING,
37
                };
38
39
                if (sym_bpf_map_update_elem(map_fd, &i, &val, BPF_ANY) != 0)
40
                        return -errno;
41
        }
42
43
        LIST_FOREACH(socket_bind_items, item, head) {
44
                struct socket_bind_rule val = {
45
                        .address_family = (uint32_t) item->address_family,
46
                        .protocol = item->ip_protocol,
47
                        .nr_ports = item->nr_ports,
48
                        .port_min = item->port_min,
49
                };
50
51
                uint32_t key = i++;
52
53
                if (sym_bpf_map_update_elem(map_fd, &key, &val, BPF_ANY) != 0)
54
                        return -errno;
55
        }
56
57
        return 0;
58
}
59
60
static int prepare_socket_bind_bpf(
61
                Unit *unit,
62
                CGroupSocketBindItem *allow,
63
                CGroupSocketBindItem *deny,
64
                struct socket_bind_bpf **ret_obj) {
65
66
        _cleanup_(socket_bind_bpf_freep) struct socket_bind_bpf *obj = NULL;
67
        size_t allow_count = 0, deny_count = 0;
68
        int allow_map_fd, deny_map_fd, r;
69
70
        assert(ret_obj);
71
72
        LIST_FOREACH(socket_bind_items, item, allow)
73
                allow_count++;
74
75
        LIST_FOREACH(socket_bind_items, item, deny)
76
                deny_count++;
77
78
        if (allow_count > SOCKET_BIND_MAX_RULES)
79
                return log_unit_full_errno(unit, unit ? LOG_ERR : LOG_WARNING, SYNTHETIC_ERRNO(EINVAL),
80
                                           "bpf-socket-bind: Maximum number of socket bind rules=%i is exceeded", SOCKET_BIND_MAX_RULES);
81
82
        if (deny_count > SOCKET_BIND_MAX_RULES)
83
                return log_unit_full_errno(unit, unit ? LOG_ERR : LOG_WARNING, SYNTHETIC_ERRNO(EINVAL),
84
                                           "bpf-socket-bind: Maximum number of socket bind rules=%i is exceeded", SOCKET_BIND_MAX_RULES);
85
86
        obj = socket_bind_bpf__open();
87
        if (!obj)
88
                return log_unit_full_errno(unit, unit ? LOG_ERR : LOG_DEBUG, errno, "bpf-socket-bind: Failed to open BPF object: %m");
89
90
        if (sym_bpf_map__set_max_entries(obj->maps.sd_bind_allow, MAX(allow_count, 1u)) != 0)
91
                return log_unit_full_errno(unit, unit ? LOG_ERR : LOG_WARNING, errno,
92
                                           "bpf-socket-bind: Failed to resize BPF map '%s': %m", sym_bpf_map__name(obj->maps.sd_bind_allow));
93
94
        if (sym_bpf_map__set_max_entries(obj->maps.sd_bind_deny, MAX(deny_count, 1u)) != 0)
95
                return log_unit_full_errno(unit, unit ? LOG_ERR : LOG_WARNING, errno,
96
                                           "bpf-socket-bind: Failed to resize BPF map '%s': %m", sym_bpf_map__name(obj->maps.sd_bind_deny));
97
98
        if (socket_bind_bpf__load(obj) != 0)
99
                return log_unit_full_errno(unit, unit ? LOG_ERR : LOG_DEBUG, errno,
100
                                           "bpf-socket-bind: Failed to load BPF object: %m");
101
102
        allow_map_fd = sym_bpf_map__fd(obj->maps.sd_bind_allow);
103
        assert(allow_map_fd >= 0);
104
105
        r = update_rules_map(allow_map_fd, allow);
106
        if (r < 0)
107
                return log_unit_full_errno(unit, unit ? LOG_ERR : LOG_WARNING, r,
108
                                           "bpf-socket-bind: Failed to put socket bind allow rules into BPF map '%s'",
109
                                           sym_bpf_map__name(obj->maps.sd_bind_allow));
110
111
        deny_map_fd = sym_bpf_map__fd(obj->maps.sd_bind_deny);
112
        assert(deny_map_fd >= 0);
113
114
        r = update_rules_map(deny_map_fd, deny);
115
        if (r < 0)
116
                return log_unit_full_errno(unit, unit ? LOG_ERR : LOG_WARNING, r,
117
                                           "bpf-socket-bind: Failed to put socket bind deny rules into BPF map '%s'",
118
                                           sym_bpf_map__name(obj->maps.sd_bind_deny));
119
120
        *ret_obj = TAKE_PTR(obj);
121
        return 0;
122
}
123
124
int bpf_socket_bind_supported(void) {
125
        _cleanup_(socket_bind_bpf_freep) struct socket_bind_bpf *obj = NULL;
126
        int r;
127
128
        if (dlopen_bpf_full(LOG_WARNING) < 0)
129
                return false;
130
131
        r = prepare_socket_bind_bpf(/*unit=*/NULL, /*allow_rules=*/NULL, /*deny_rules=*/NULL, &obj);
132
        if (r < 0) {
133
                log_debug_errno(r, "bpf-socket-bind: socket bind filtering is not supported: %m");
134
                return false;
135
        }
136
137
        return bpf_can_link_program(obj->progs.sd_bind4);
138
}
139
140
int bpf_socket_bind_add_initial_link_fd(Unit *u, int fd) {
141
        int r;
142
143
        assert(u);
144
145
        CGroupRuntime *crt = unit_get_cgroup_runtime(u);
146
        if (!crt)
147
                return log_unit_error_errno(u, SYNTHETIC_ERRNO(EINVAL),
148
                                            "Failed to get control group runtime object.");
149
150
        if (!crt->initial_socket_bind_link_fds) {
151
                crt->initial_socket_bind_link_fds = fdset_new();
152
                if (!crt->initial_socket_bind_link_fds)
153
                        return log_oom();
154
        }
155
156
        r = fdset_put(crt->initial_socket_bind_link_fds, fd);
157
        if (r < 0)
158
                return log_unit_error_errno(u, r, "bpf-socket-bind: Failed to put BPF fd %d to initial fdset", fd);
159
160
        return 0;
161
}
162
163
static int socket_bind_install_impl(Unit *u) {
164
        _cleanup_(bpf_link_freep) struct bpf_link *ipv4 = NULL, *ipv6 = NULL;
165
        _cleanup_(socket_bind_bpf_freep) struct socket_bind_bpf *obj = NULL;
166
        _cleanup_free_ char *cgroup_path = NULL;
167
        _cleanup_close_ int cgroup_fd = -EBADF;
168
        CGroupContext *cc;
169
        CGroupRuntime *crt;
170
        int r;
171
172
        assert(u);
173
174
        cc = unit_get_cgroup_context(u);
175
        if (!cc)
176
                return 0;
177
178
        crt = unit_get_cgroup_runtime(u);
179
        if (!crt)
180
                return 0;
181
182
        r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, crt->cgroup_path, NULL, &cgroup_path);
183
        if (r < 0)
184
                return log_unit_error_errno(u, r, "bpf-socket-bind: Failed to get cgroup path: %m");
185
186
        if (!cc->socket_bind_allow && !cc->socket_bind_deny)
187
                return 0;
188
189
        r = prepare_socket_bind_bpf(u, cc->socket_bind_allow, cc->socket_bind_deny, &obj);
190
        if (r < 0)
191
                return log_unit_error_errno(u, r, "bpf-socket-bind: Failed to load BPF object: %m");
192
193
        cgroup_fd = open(cgroup_path, O_RDONLY | O_CLOEXEC, 0);
194
        if (cgroup_fd < 0)
195
                return log_unit_error_errno(u, errno, "bpf-socket-bind: Failed to open cgroup %s for reading: %m", cgroup_path);
196
197
        ipv4 = sym_bpf_program__attach_cgroup(obj->progs.sd_bind4, cgroup_fd);
198
        r = bpf_get_error_translated(ipv4);
199
        if (r != 0)
200
                return log_unit_error_errno(u, r, "bpf-socket-bind: Failed to link '%s' cgroup-bpf program: %m",
201
                                            sym_bpf_program__name(obj->progs.sd_bind4));
202
203
        ipv6 = sym_bpf_program__attach_cgroup(obj->progs.sd_bind6, cgroup_fd);
204
        r = bpf_get_error_translated(ipv6);
205
        if (r != 0)
206
                return log_unit_error_errno(u, r, "bpf-socket-bind: Failed to link '%s' cgroup-bpf program: %m",
207
                                            sym_bpf_program__name(obj->progs.sd_bind6));
208
209
        crt->ipv4_socket_bind_link = TAKE_PTR(ipv4);
210
        crt->ipv6_socket_bind_link = TAKE_PTR(ipv6);
211
212
        return 0;
213
}
214
215
int bpf_socket_bind_install(Unit *u) {
216
        CGroupRuntime *crt;
217
        int r;
218
219
        assert(u);
220
221
        crt = unit_get_cgroup_runtime(u);
222
        if (!crt)
223
                return 0;
224
225
        r = socket_bind_install_impl(u);
226
        fdset_close(crt->initial_socket_bind_link_fds, /* async= */ false);
227
        return r;
228
}
229
230
int bpf_socket_bind_serialize(Unit *u, FILE *f, FDSet *fds) {
231
        CGroupRuntime *crt;
232
        int r;
233
234
        assert(u);
235
236
        crt = unit_get_cgroup_runtime(u);
237
        if (!crt)
238
                return 0;
239
240
        r = bpf_serialize_link(f, fds, "ipv4-socket-bind-bpf-link", crt->ipv4_socket_bind_link);
241
        if (r < 0)
242
                return r;
243
244
        return bpf_serialize_link(f, fds, "ipv6-socket-bind-bpf-link", crt->ipv6_socket_bind_link);
245
}
246
247
#else /* ! BPF_FRAMEWORK */
248
0
int bpf_socket_bind_supported(void) {
249
0
        return false;
250
0
}
251
252
0
int bpf_socket_bind_add_initial_link_fd(Unit *u, int fd) {
253
0
        return 0;
254
0
}
255
256
0
int bpf_socket_bind_install(Unit *u) {
257
0
        return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EOPNOTSUPP),
258
0
                                    "bpf-socket-bind: Failed to install; BPF framework is not supported");
259
0
}
260
261
6.16k
int bpf_socket_bind_serialize(Unit *u, FILE *f, FDSet *fds) {
262
6.16k
        return 0;
263
6.16k
}
264
#endif