1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
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 
socket_bind_bpf_free(struct socket_bind_bpf * obj)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 
update_rules_map(int map_fd,CGroupSocketBindItem * head)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         LIST_FOREACH(socket_bind_items, item, head) {
35                 struct socket_bind_rule val = {
36                         .address_family = (uint32_t) item->address_family,
37                         .protocol = item->ip_protocol,
38                         .nr_ports = item->nr_ports,
39                         .port_min = item->port_min,
40                 };
41 
42                 uint32_t key = i++;
43 
44                 if (sym_bpf_map_update_elem(map_fd, &key, &val, BPF_ANY) != 0)
45                         return -errno;
46         }
47 
48         return 0;
49 }
50 
prepare_socket_bind_bpf(Unit * u,CGroupSocketBindItem * allow,CGroupSocketBindItem * deny,struct socket_bind_bpf ** ret_obj)51 static int prepare_socket_bind_bpf(
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;
59         int allow_map_fd, deny_map_fd, r;
60 
61         assert(ret_obj);
62 
63         LIST_FOREACH(socket_bind_items, item, allow)
64                 allow_count++;
65 
66         LIST_FOREACH(socket_bind_items, item, deny)
67                 deny_count++;
68 
69         if (allow_count > SOCKET_BIND_MAX_RULES)
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);
72 
73         if (deny_count > SOCKET_BIND_MAX_RULES)
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);
76 
77         obj = socket_bind_bpf__open();
78         if (!obj)
79                 return log_unit_full_errno(u, u ? LOG_ERR : LOG_DEBUG, errno, "Failed to open BPF object: %m");
80 
81         if (sym_bpf_map__resize(obj->maps.sd_bind_allow, MAX(allow_count, 1u)) != 0)
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));
84 
85         if (sym_bpf_map__resize(obj->maps.sd_bind_deny, MAX(deny_count, 1u)) != 0)
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));
88 
89         if (socket_bind_bpf__load(obj) != 0)
90                 return log_unit_full_errno(u, u ? LOG_ERR : LOG_DEBUG, errno,
91                                            "Failed to load BPF object: %m");
92 
93         allow_map_fd = sym_bpf_map__fd(obj->maps.sd_bind_allow);
94         assert(allow_map_fd >= 0);
95 
96         r = update_rules_map(allow_map_fd, allow);
97         if (r < 0)
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));
101 
102         deny_map_fd = sym_bpf_map__fd(obj->maps.sd_bind_deny);
103         assert(deny_map_fd >= 0);
104 
105         r = update_rules_map(deny_map_fd, deny);
106         if (r < 0)
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));
110 
111         *ret_obj = TAKE_PTR(obj);
112         return 0;
113 }
114 
bpf_socket_bind_supported(void)115 int bpf_socket_bind_supported(void) {
116         _cleanup_(socket_bind_bpf_freep) struct socket_bind_bpf *obj = NULL;
117         int r;
118 
119         r = cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER);
120         if (r < 0)
121                 return log_debug_errno(r, "Can't determine whether the unified hierarchy is used: %m");
122         if (r == 0) {
123                 log_debug("Not running with unified cgroup hierarchy, BPF is not supported");
124                 return false;
125         }
126 
127         if (dlopen_bpf() < 0)
128                 return false;
129 
130         if (!sym_bpf_probe_prog_type(BPF_PROG_TYPE_CGROUP_SOCK_ADDR, /*ifindex=*/0)) {
131                 log_debug("BPF program type cgroup_sock_addr is not supported");
132                 return false;
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");
138                 return false;
139         }
140 
141         return bpf_can_link_program(obj->progs.sd_bind4);
142 }
143 
bpf_socket_bind_add_initial_link_fd(Unit * u,int fd)144 int bpf_socket_bind_add_initial_link_fd(Unit *u, int fd) {
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 
socket_bind_install_impl(Unit * u)162 static int socket_bind_install_impl(Unit *u) {
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 
170         assert(u);
171 
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)
189                 return log_unit_error_errno(u, errno, "Failed to open cgroup=%s for reading: %m", cgroup_path);
190 
191         ipv4 = sym_bpf_program__attach_cgroup(obj->progs.sd_bind4, cgroup_fd);
192         r = sym_libbpf_get_error(ipv4);
193         if (r != 0)
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));
196 
197         ipv6 = sym_bpf_program__attach_cgroup(obj->progs.sd_bind6, cgroup_fd);
198         r = sym_libbpf_get_error(ipv6);
199         if (r != 0)
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));
202 
203         u->ipv4_socket_bind_link = TAKE_PTR(ipv4);
204         u->ipv6_socket_bind_link = TAKE_PTR(ipv6);
205 
206         return 0;
207 }
208 
bpf_socket_bind_install(Unit * u)209 int bpf_socket_bind_install(Unit *u) {
210         int r;
211 
212         assert(u);
213 
214         r = socket_bind_install_impl(u);
215         if (r == -ENOMEM)
216                 return r;
217 
218         fdset_close(u->initial_socket_bind_link_fds);
219         return r;
220 }
221 
bpf_serialize_socket_bind(Unit * u,FILE * f,FDSet * fds)222 int bpf_serialize_socket_bind(Unit *u, FILE *f, FDSet *fds) {
223         int r;
224 
225         assert(u);
226 
227         r = bpf_serialize_link(f, fds, "ipv4-socket-bind-bpf-link", u->ipv4_socket_bind_link);
228         if (r < 0)
229                 return r;
230 
231         return bpf_serialize_link(f, fds, "ipv6-socket-bind-bpf-link", u->ipv6_socket_bind_link);
232 }
233 
234 #else /* ! BPF_FRAMEWORK */
bpf_socket_bind_supported(void)235 int bpf_socket_bind_supported(void) {
236         return false;
237 }
238 
bpf_socket_bind_add_initial_link_fd(Unit * u,int fd)239 int bpf_socket_bind_add_initial_link_fd(Unit *u, int fd) {
240         return 0;
241 }
242 
bpf_socket_bind_install(Unit * u)243 int bpf_socket_bind_install(Unit *u) {
244         return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EOPNOTSUPP), "Failed to install socket bind: BPF framework is not supported");
245 }
246 
bpf_serialize_socket_bind(Unit * u,FILE * f,FDSet * fds)247 int bpf_serialize_socket_bind(Unit *u, FILE *f, FDSet *fds) {
248         return 0;
249 }
250 #endif
251