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