Line data Source code
1 : // SPDX-License-Identifier: GPL-2.0-only
2 : /*
3 : * Copyright (c) 2022 Meta Platforms, Inc. and affiliates.
4 : */
5 :
6 : #include "bpfilter/cgen/prog/link.h"
7 :
8 : #include <linux/bpf.h>
9 :
10 : #include <errno.h>
11 : #include <fcntl.h>
12 : #include <stdio.h>
13 : #include <stdlib.h>
14 : #include <string.h>
15 : #include <unistd.h>
16 :
17 : #include "core/bpf.h"
18 : #include "core/dump.h"
19 : #include "core/helper.h"
20 : #include "core/hook.h"
21 : #include "core/logger.h"
22 : #include "core/marsh.h"
23 : #include "core/nf.h"
24 :
25 0 : int bf_link_new(struct bf_link **link, const char *name, enum bf_hook hook)
26 : {
27 0 : _cleanup_bf_link_ struct bf_link *_link = NULL;
28 :
29 0 : bf_assert(link && name);
30 0 : bf_assert(name[0] != '\0');
31 :
32 0 : _link = malloc(sizeof(*_link));
33 0 : if (!_link)
34 : return -ENOMEM;
35 :
36 : // snprintf() will write the \0 as part of the BPF_OBJ_NAME_LEN bytes
37 0 : (void)snprintf(_link->name, BPF_OBJ_NAME_LEN, name);
38 :
39 0 : _link->fd = -1;
40 0 : _link->hook = hook;
41 :
42 0 : *link = TAKE_PTR(_link);
43 :
44 0 : return 0;
45 : }
46 :
47 0 : int bf_link_new_from_marsh(struct bf_link **link, int dir_fd,
48 : const struct bf_marsh *marsh)
49 : {
50 0 : _cleanup_bf_link_ struct bf_link *_link = NULL;
51 : struct bf_marsh *elem = NULL;
52 : int r;
53 :
54 0 : bf_assert(link && marsh);
55 :
56 0 : _link = malloc(sizeof(*_link));
57 0 : if (!_link)
58 : return -ENOMEM;
59 :
60 0 : _link->fd = -1;
61 :
62 0 : if (!(elem = bf_marsh_next_child(marsh, elem)))
63 : return -EINVAL;
64 0 : memcpy(&_link->name, elem->data, BPF_OBJ_NAME_LEN);
65 :
66 0 : if (!(elem = bf_marsh_next_child(marsh, elem)))
67 : return -EINVAL;
68 0 : memcpy(&_link->hook, elem->data, sizeof(_link->hook));
69 :
70 0 : if (bf_marsh_next_child(marsh, elem))
71 0 : return bf_err_r(-E2BIG, "too many elements in bf_link marsh");
72 :
73 0 : if (dir_fd != -1) {
74 0 : r = bf_bpf_obj_get(_link->name, dir_fd, &_link->fd);
75 0 : if (r) {
76 0 : return bf_err_r(r, "failed to open pinned BPF link '%s'",
77 : _link->name);
78 : }
79 : }
80 :
81 0 : *link = TAKE_PTR(_link);
82 :
83 0 : return 0;
84 : }
85 :
86 0 : void bf_link_free(struct bf_link **link)
87 : {
88 0 : bf_assert(link);
89 :
90 0 : if (!*link)
91 : return;
92 :
93 : closep(&(*link)->fd);
94 : freep((void *)link);
95 : }
96 :
97 0 : int bf_link_marsh(const struct bf_link *link, struct bf_marsh **marsh)
98 : {
99 0 : _cleanup_bf_marsh_ struct bf_marsh *_marsh = NULL;
100 : int r;
101 :
102 0 : bf_assert(link && marsh);
103 :
104 0 : r = bf_marsh_new(&_marsh, NULL, 0);
105 0 : if (r)
106 : return r;
107 :
108 0 : r = bf_marsh_add_child_raw(&_marsh, &link->name, BPF_OBJ_NAME_LEN);
109 0 : if (r)
110 : return r;
111 :
112 0 : r = bf_marsh_add_child_raw(&_marsh, &link->hook, sizeof(link->hook));
113 0 : if (r)
114 : return r;
115 :
116 0 : *marsh = TAKE_PTR(_marsh);
117 :
118 0 : return 0;
119 : }
120 :
121 0 : void bf_link_dump(const struct bf_link *link, prefix_t *prefix)
122 : {
123 0 : bf_assert(link && prefix);
124 :
125 0 : DUMP(prefix, "struct bf_link at %p", link);
126 :
127 0 : bf_dump_prefix_push(prefix);
128 0 : DUMP(prefix, "name: %s", link->name);
129 0 : DUMP(prefix, "fd: %d", link->fd);
130 0 : DUMP(bf_dump_prefix_last(prefix), "hook: %s", bf_hook_to_str(link->hook));
131 0 : bf_dump_prefix_pop(prefix);
132 0 : }
133 :
134 0 : int bf_link_attach_xdp(struct bf_link *link, int prog_fd, unsigned int ifindex,
135 : enum bf_xdp_attach_mode mode)
136 : {
137 0 : union bpf_attr attr = {
138 : .link_create =
139 : {
140 : .prog_fd = prog_fd,
141 : .target_fd = ifindex,
142 : .attach_type = BPF_XDP,
143 : .flags = mode,
144 : },
145 : };
146 : int r;
147 :
148 0 : r = bf_bpf(BPF_LINK_CREATE, &attr);
149 0 : if (r < 0)
150 : return r;
151 :
152 0 : link->fd = r;
153 :
154 0 : return 0;
155 : }
156 :
157 0 : int bf_link_attach_tc(struct bf_link *link, int prog_fd, unsigned int ifindex)
158 : {
159 0 : union bpf_attr attr = {
160 : .link_create =
161 : {
162 : .prog_fd = prog_fd,
163 : .target_fd = ifindex,
164 0 : .attach_type = bf_hook_to_attach_type(link->hook),
165 : },
166 : };
167 : int r;
168 :
169 0 : r = bf_bpf(BPF_LINK_CREATE, &attr);
170 0 : if (r < 0)
171 : return r;
172 :
173 0 : link->fd = r;
174 :
175 0 : return 0;
176 : }
177 :
178 0 : int bf_link_attach_nf(struct bf_link *link, int prog_fd, unsigned int family,
179 : int priority)
180 : {
181 0 : union bpf_attr attr = {
182 : .link_create =
183 : {
184 : .prog_fd = prog_fd,
185 : .attach_type = BPF_NETFILTER,
186 : .netfilter =
187 : {
188 : .pf = family,
189 0 : .hooknum = bf_hook_to_nf_hook(link->hook),
190 : .priority = priority,
191 : },
192 : },
193 : };
194 : int r;
195 :
196 0 : r = bf_bpf(BPF_LINK_CREATE, &attr);
197 0 : if (r < 0)
198 : return r;
199 :
200 0 : link->fd = r;
201 :
202 0 : return 0;
203 : }
204 :
205 0 : int bf_link_attach_cgroup(struct bf_link *link, int prog_fd,
206 : const char *cgroup_path)
207 : {
208 : _cleanup_close_ int cgroup_fd = -1;
209 0 : union bpf_attr attr = {
210 : .link_create =
211 : {
212 : .prog_fd = prog_fd,
213 0 : .attach_type = bf_hook_to_attach_type(link->hook),
214 : },
215 : };
216 : int r;
217 :
218 0 : cgroup_fd = open(cgroup_path, O_DIRECTORY | O_RDONLY);
219 0 : if (cgroup_fd < 0)
220 0 : return bf_err_r(errno, "failed to open cgroup '%s'", cgroup_path);
221 :
222 0 : attr.link_create.target_fd = cgroup_fd;
223 :
224 0 : r = bf_bpf(BPF_LINK_CREATE, &attr);
225 0 : if (r < 0)
226 : return r;
227 :
228 0 : link->fd = r;
229 :
230 0 : return 0;
231 : }
232 :
233 0 : int bf_link_update(struct bf_link *link, int new_prog_fd)
234 : {
235 0 : union bpf_attr attr = {
236 0 : .link_update.link_fd = link->fd,
237 : .link_update.new_prog_fd = new_prog_fd,
238 : };
239 :
240 0 : return bf_bpf(BPF_LINK_UPDATE, &attr);
241 : }
242 :
243 0 : int bf_link_detach(struct bf_link *link)
244 : {
245 0 : union bpf_attr attr = {
246 0 : .link_detach.link_fd = link->fd,
247 : };
248 : int r;
249 :
250 0 : r = bf_bpf(BPF_LINK_DETACH, &attr);
251 0 : if (r)
252 : return r;
253 :
254 : closep(&link->fd);
255 :
256 0 : return 0;
257 : }
258 :
259 0 : int bf_link_pin(struct bf_link *link, int dir_fd)
260 : {
261 : int r;
262 :
263 0 : bf_assert(link);
264 0 : bf_assert(dir_fd > 0);
265 :
266 0 : r = bf_bpf_obj_pin(link->name, link->fd, dir_fd);
267 0 : if (r)
268 0 : return bf_err_r(r, "failed to pin BPF link '%s'", link->name);
269 :
270 : return 0;
271 : }
272 :
273 0 : void bf_link_unpin(struct bf_link *link, int dir_fd)
274 : {
275 : int r;
276 :
277 0 : bf_assert(link);
278 0 : bf_assert(dir_fd > 0);
279 :
280 0 : r = unlinkat(dir_fd, link->name, 0);
281 0 : if (r < 0 && errno != ENOENT) {
282 : // Do not warn on ENOENT, we want the file to be gone!
283 0 : bf_warn_r(
284 : errno,
285 : "failed to unlink BPF link '%s', assuming the link is not pinned",
286 : link->name);
287 : }
288 0 : }
289 :
290 0 : int bf_link_get_info(struct bf_link *link, struct bpf_link_info *info)
291 : {
292 0 : union bpf_attr attr = {};
293 :
294 0 : bf_assert(link && info);
295 :
296 0 : if (link->fd == -1)
297 : return -ENOENT;
298 :
299 0 : attr.info.bpf_fd = link->fd;
300 0 : attr.info.info_len = sizeof(*info);
301 0 : attr.info.info = bf_ptr_to_u64(info);
302 :
303 0 : return bf_bpf(BPF_OBJ_GET_INFO_BY_FD, &attr);
304 : }
|