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