]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/bpf-socket-bind.c
Merge pull request #20058 from keszybz/status-format
[thirdparty/systemd.git] / src / core / bpf-socket-bind.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #if BPF_FRAMEWORK
4 #include <bpf/bpf.h>
5 #endif
6
7 #include "fd-util.h"
8 #include "bpf-socket-bind.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.skel.h"
15 #include "bpf/socket_bind/socket-bind-api.bpf.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 CGroupSocketBindItem *item;
31 uint32_t i = 0;
32
33 assert(map_fd >= 0);
34
35 LIST_FOREACH(socket_bind_items, item, head) {
36 struct socket_bind_rule val = {
37 .address_family = (uint32_t) item->address_family,
38 .protocol = item->ip_protocol,
39 .nr_ports = item->nr_ports,
40 .port_min = item->port_min,
41 };
42
43 uint32_t key = i++;
44
45 if (sym_bpf_map_update_elem(map_fd, &key, &val, BPF_ANY) != 0)
46 return -errno;
47 }
48
49 return 0;
50 }
51
52 static int prepare_socket_bind_bpf(
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;
60 int allow_map_fd, deny_map_fd, r;
61 CGroupSocketBindItem *item;
62
63 assert(ret_obj);
64
65 LIST_FOREACH(socket_bind_items, item, allow)
66 allow_count++;
67
68 LIST_FOREACH(socket_bind_items, item, deny)
69 deny_count++;
70
71 if (allow_count > SOCKET_BIND_MAX_RULES)
72 return log_unit_full_errno(u, u ? LOG_ERR : LOG_WARNING, SYNTHETIC_ERRNO(EINVAL),
73 "Maximum number of socket bind rules=%u is exceeded", SOCKET_BIND_MAX_RULES);
74
75 if (deny_count > SOCKET_BIND_MAX_RULES)
76 return log_unit_full_errno(u, u ? LOG_ERR : LOG_WARNING, SYNTHETIC_ERRNO(EINVAL),
77 "Maximum number of socket bind rules=%u is exceeded", SOCKET_BIND_MAX_RULES);
78
79 obj = socket_bind_bpf__open();
80 if (!obj)
81 return log_unit_full_errno(u, u ? LOG_ERR : LOG_DEBUG, SYNTHETIC_ERRNO(ENOMEM),
82 "Failed to open BPF object");
83
84 if (sym_bpf_map__resize(obj->maps.sd_bind_allow, MAX(allow_count, 1u)) != 0)
85 return log_unit_full_errno(u, u ? LOG_ERR : LOG_WARNING, errno,
86 "Failed to resize BPF map '%s': %m", sym_bpf_map__name(obj->maps.sd_bind_allow));
87
88 if (sym_bpf_map__resize(obj->maps.sd_bind_deny, MAX(deny_count, 1u)) != 0)
89 return log_unit_full_errno(u, u ? LOG_ERR : LOG_WARNING, errno,
90 "Failed to resize BPF map '%s': %m", sym_bpf_map__name(obj->maps.sd_bind_deny));
91
92 if (socket_bind_bpf__load(obj) != 0)
93 return log_unit_full_errno(u, u ? LOG_ERR : LOG_DEBUG, errno,
94 "Failed to load BPF object: %m");
95
96 allow_map_fd = sym_bpf_map__fd(obj->maps.sd_bind_allow);
97 assert(allow_map_fd >= 0);
98
99 r = update_rules_map(allow_map_fd, allow);
100 if (r < 0)
101 return log_unit_full_errno(u, u ? LOG_ERR : LOG_WARNING, r,
102 "Failed to put socket bind allow rules into BPF map '%s'",
103 sym_bpf_map__name(obj->maps.sd_bind_allow));
104
105 deny_map_fd = sym_bpf_map__fd(obj->maps.sd_bind_deny);
106 assert(deny_map_fd >= 0);
107
108 r = update_rules_map(deny_map_fd, deny);
109 if (r < 0)
110 return log_unit_full_errno(u, u ? LOG_ERR : LOG_WARNING, r,
111 "Failed to put socket bind deny rules into BPF map '%s'",
112 sym_bpf_map__name(obj->maps.sd_bind_deny));
113
114 *ret_obj = TAKE_PTR(obj);
115 return 0;
116 }
117
118 int bpf_socket_bind_supported(void) {
119 _cleanup_(socket_bind_bpf_freep) struct socket_bind_bpf *obj = NULL;
120 int r;
121
122 r = cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER);
123 if (r < 0)
124 return log_debug_errno(r, "Can't determine whether the unified hierarchy is used: %m");
125 if (r == 0) {
126 log_debug("Not running with unified cgroup hierarchy, BPF is not supported");
127 return false;
128 }
129
130 if (dlopen_bpf() < 0)
131 return false;
132
133 if (!sym_bpf_probe_prog_type(BPF_PROG_TYPE_CGROUP_SOCK_ADDR, /*ifindex=*/0)) {
134 log_debug("BPF program type cgroup_sock_addr is not supported");
135 return false;
136 }
137
138 r = prepare_socket_bind_bpf(/*unit=*/NULL, /*allow_rules=*/NULL, /*deny_rules=*/NULL, &obj);
139 if (r < 0) {
140 log_debug_errno(r, "BPF based socket_bind is not supported: %m");
141 return false;
142 }
143
144 return bpf_can_link_program(obj->progs.sd_bind4);
145 }
146
147 int bpf_socket_bind_add_initial_link_fd(Unit *u, int fd) {
148 int r;
149
150 assert(u);
151
152 if (!u->initial_socket_bind_link_fds) {
153 u->initial_socket_bind_link_fds = fdset_new();
154 if (!u->initial_socket_bind_link_fds)
155 return log_oom();
156 }
157
158 r = fdset_put(u->initial_socket_bind_link_fds, fd);
159 if (r < 0)
160 return log_unit_error_errno(u, r, "Failed to put socket-bind BPF link fd %d to initial fdset", fd);
161
162 return 0;
163 }
164
165 static int socket_bind_install_impl(Unit *u) {
166 _cleanup_(bpf_link_freep) struct bpf_link *ipv4 = NULL, *ipv6 = NULL;
167 _cleanup_(socket_bind_bpf_freep) struct socket_bind_bpf *obj = NULL;
168 _cleanup_free_ char *cgroup_path = NULL;
169 _cleanup_close_ int cgroup_fd = -1;
170 CGroupContext *cc;
171 int r;
172
173 assert(u);
174
175 cc = unit_get_cgroup_context(u);
176 if (!cc)
177 return 0;
178
179 r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, NULL, &cgroup_path);
180 if (r < 0)
181 return log_unit_error_errno(u, r, "Failed to get cgroup path: %m");
182
183 if (!cc->socket_bind_allow && !cc->socket_bind_deny)
184 return 0;
185
186 r = prepare_socket_bind_bpf(u, cc->socket_bind_allow, cc->socket_bind_deny, &obj);
187 if (r < 0)
188 return log_unit_error_errno(u, r, "Failed to load BPF object: %m");
189
190 cgroup_fd = open(cgroup_path, O_RDONLY | O_CLOEXEC, 0);
191 if (cgroup_fd < 0)
192 return log_unit_error_errno(u, errno, "Failed to open cgroup=%s for reading: %m", cgroup_path);
193
194 ipv4 = sym_bpf_program__attach_cgroup(obj->progs.sd_bind4, cgroup_fd);
195 r = sym_libbpf_get_error(ipv4);
196 if (r != 0)
197 return log_unit_error_errno(u, r, "Failed to link '%s' cgroup-bpf program: %m",
198 sym_bpf_program__name(obj->progs.sd_bind4));
199
200 ipv6 = sym_bpf_program__attach_cgroup(obj->progs.sd_bind6, cgroup_fd);
201 r = sym_libbpf_get_error(ipv6);
202 if (r != 0)
203 return log_unit_error_errno(u, r, "Failed to link '%s' cgroup-bpf program: %m",
204 sym_bpf_program__name(obj->progs.sd_bind6));
205
206 u->ipv4_socket_bind_link = TAKE_PTR(ipv4);
207 u->ipv6_socket_bind_link = TAKE_PTR(ipv6);
208
209 return 0;
210 }
211
212 int bpf_socket_bind_install(Unit *u) {
213 int r;
214
215 assert(u);
216
217 r = socket_bind_install_impl(u);
218 if (r == -ENOMEM)
219 return r;
220
221 fdset_close(u->initial_socket_bind_link_fds);
222 return r;
223 }
224
225 int bpf_serialize_socket_bind(Unit *u, FILE *f, FDSet *fds) {
226 int r;
227
228 assert(u);
229
230 r = bpf_serialize_link(f, fds, "ipv4-socket-bind-bpf-link", u->ipv4_socket_bind_link);
231 if (r < 0)
232 return r;
233
234 return bpf_serialize_link(f, fds, "ipv6-socket-bind-bpf-link", u->ipv6_socket_bind_link);
235 }
236
237 #else /* ! BPF_FRAMEWORK */
238 int bpf_socket_bind_supported(void) {
239 return false;
240 }
241
242 int bpf_socket_bind_add_initial_link_fd(Unit *u, int fd) {
243 return 0;
244 }
245
246 int bpf_socket_bind_install(Unit *u) {
247 return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EOPNOTSUPP), "Failed to install socket bind: BPF framework is not supported");
248 }
249
250 int bpf_serialize_socket_bind(Unit *u, FILE *f, FDSet *fds) {
251 return 0;
252 }
253 #endif