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