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 "cgen/prog/link.h"
7 :
8 : #include <linux/bpf.h>
9 : #include <linux/if_link.h>
10 :
11 : #include <errno.h>
12 : #include <fcntl.h>
13 : #include <stdio.h>
14 : #include <stdlib.h>
15 : #include <string.h>
16 : #include <unistd.h>
17 :
18 : #include <bpfilter/bpf.h>
19 : #include <bpfilter/dump.h>
20 : #include <bpfilter/flavor.h>
21 : #include <bpfilter/helper.h>
22 : #include <bpfilter/hook.h>
23 : #include <bpfilter/logger.h>
24 : #include <bpfilter/pack.h>
25 :
26 3 : int bf_link_new(struct bf_link **link, const char *name)
27 : {
28 3 : _free_bf_link_ struct bf_link *_link = NULL;
29 :
30 3 : bf_assert(link && name);
31 3 : bf_assert(name[0] != '\0');
32 :
33 3 : _link = malloc(sizeof(*_link));
34 3 : if (!_link)
35 : return -ENOMEM;
36 :
37 3 : bf_strncpy(_link->name, BPF_OBJ_NAME_LEN, name);
38 :
39 3 : _link->hookopts = NULL;
40 3 : _link->fd = -1;
41 :
42 3 : *link = TAKE_PTR(_link);
43 :
44 3 : return 0;
45 : }
46 :
47 0 : int bf_link_new_from_pack(struct bf_link **link, int dir_fd,
48 : bf_rpack_node_t node)
49 : {
50 0 : _free_bf_link_ struct bf_link *_link = NULL;
51 0 : _cleanup_free_ char *name = NULL;
52 : bf_rpack_node_t child;
53 : int r;
54 :
55 0 : bf_assert(link);
56 :
57 0 : r = bf_rpack_kv_str(node, "name", &name);
58 0 : if (r)
59 0 : return bf_rpack_key_err(r, "bf_link.name");
60 :
61 0 : r = bf_link_new(&_link, name);
62 0 : if (r)
63 0 : return bf_err_r(r, "failed to create bf_link from pack");
64 :
65 0 : r = bf_rpack_kv_node(node, "hookopts", &child);
66 0 : if (r)
67 0 : return bf_rpack_key_err(r, "bf_link.hookopts");
68 0 : if (!bf_rpack_is_nil(child)) {
69 0 : r = bf_hookopts_new_from_pack(&_link->hookopts, child);
70 0 : if (r)
71 : return r;
72 : }
73 :
74 0 : if (dir_fd != -1) {
75 0 : r = bf_bpf_obj_get(_link->name, dir_fd, &_link->fd);
76 0 : if (r) {
77 0 : return bf_err_r(r, "failed to open pinned BPF link '%s'",
78 : _link->name);
79 : }
80 : }
81 :
82 0 : *link = TAKE_PTR(_link);
83 :
84 0 : return 0;
85 : }
86 :
87 6 : void bf_link_free(struct bf_link **link)
88 : {
89 6 : bf_assert(link);
90 :
91 6 : if (!*link)
92 : return;
93 :
94 3 : bf_hookopts_free(&(*link)->hookopts);
95 3 : closep(&(*link)->fd);
96 : freep((void *)link);
97 : }
98 :
99 0 : int bf_link_pack(const struct bf_link *link, bf_wpack_t *pack)
100 : {
101 0 : bf_assert(link);
102 0 : bf_assert(pack);
103 :
104 0 : bf_wpack_kv_str(pack, "name", link->name);
105 :
106 0 : if (link->hookopts) {
107 0 : bf_wpack_open_object(pack, "hookopts");
108 0 : bf_hookopts_pack(link->hookopts, pack);
109 0 : bf_wpack_close_object(pack);
110 : } else {
111 : bf_wpack_kv_nil(pack, "hookopts");
112 : }
113 :
114 0 : return bf_wpack_is_valid(pack) ? 0 : -EINVAL;
115 : }
116 :
117 0 : void bf_link_dump(const struct bf_link *link, prefix_t *prefix)
118 : {
119 0 : bf_assert(link && prefix);
120 :
121 0 : DUMP(prefix, "struct bf_link at %p", link);
122 :
123 0 : bf_dump_prefix_push(prefix);
124 0 : DUMP(prefix, "name: %s", link->name);
125 :
126 0 : if (link->hookopts) {
127 0 : DUMP(prefix, "hookopts: struct bf_hookopts *");
128 0 : bf_dump_prefix_push(prefix);
129 0 : bf_hookopts_dump(link->hookopts, prefix);
130 0 : bf_dump_prefix_pop(prefix);
131 : } else {
132 0 : DUMP(prefix, "hookopts: struct bf_hookopts * (NULL)");
133 : }
134 :
135 0 : DUMP(bf_dump_prefix_last(prefix), "fd: %d", link->fd);
136 0 : bf_dump_prefix_pop(prefix);
137 0 : }
138 :
139 0 : int bf_link_attach(struct bf_link *link, enum bf_hook hook,
140 : struct bf_hookopts **hookopts, int prog_fd)
141 : {
142 0 : bf_assert(link && hookopts);
143 :
144 0 : _cleanup_close_ int cgroup_fd = -1;
145 0 : struct bf_hookopts *_hookopts = *hookopts;
146 : int r;
147 :
148 0 : switch (bf_hook_to_flavor(hook)) {
149 0 : case BF_FLAVOR_XDP:
150 0 : r = bf_bpf_link_create(prog_fd, _hookopts->ifindex, hook, _hookopts,
151 : XDP_FLAGS_SKB_MODE);
152 0 : break;
153 0 : case BF_FLAVOR_TC:
154 0 : r = bf_bpf_link_create(prog_fd, _hookopts->ifindex, hook, _hookopts, 0);
155 0 : break;
156 0 : case BF_FLAVOR_CGROUP:
157 0 : cgroup_fd = open(_hookopts->cgpath, O_DIRECTORY | O_RDONLY);
158 0 : if (cgroup_fd < 0) {
159 0 : return bf_err_r(errno, "failed to open cgroup '%s'",
160 : _hookopts->cgpath);
161 : }
162 :
163 0 : r = bf_bpf_link_create(prog_fd, cgroup_fd, hook, _hookopts, 0);
164 0 : break;
165 0 : case BF_FLAVOR_NF:
166 0 : r = bf_bpf_link_create(prog_fd, 0, hook, _hookopts, 0);
167 0 : break;
168 : default:
169 : return -ENOTSUP;
170 : }
171 :
172 0 : if (r < 0)
173 : return r;
174 :
175 0 : link->fd = r;
176 0 : link->hookopts = TAKE_PTR(*hookopts);
177 :
178 0 : return 0;
179 : }
180 :
181 0 : static int _bf_link_update_nf(struct bf_link *link, enum bf_hook hook,
182 : int prog_fd)
183 : {
184 0 : bf_assert(link);
185 :
186 0 : _cleanup_close_ int new_link_fd = -1;
187 0 : struct bf_hookopts opts = *link->hookopts;
188 :
189 0 : opts.priorities[0] = link->hookopts->priorities[1];
190 0 : opts.priorities[1] = link->hookopts->priorities[0];
191 :
192 0 : new_link_fd = bf_bpf_link_create(prog_fd, 0, hook, &opts, 0);
193 0 : if (new_link_fd < 0)
194 : return new_link_fd;
195 :
196 : // Swap priorities, so priorities[0] is the one currently used
197 0 : link->hookopts->priorities[0] = opts.priorities[0];
198 0 : link->hookopts->priorities[1] = opts.priorities[1];
199 :
200 0 : closep(&link->fd);
201 0 : link->fd = TAKE_FD(new_link_fd);
202 :
203 0 : return 0;
204 : }
205 :
206 0 : int bf_link_update(struct bf_link *link, enum bf_hook hook, int prog_fd)
207 : {
208 0 : bf_assert(link);
209 :
210 : int r;
211 :
212 0 : switch (bf_hook_to_flavor(hook)) {
213 0 : case BF_FLAVOR_XDP:
214 : case BF_FLAVOR_TC:
215 : case BF_FLAVOR_CGROUP:
216 0 : r = bf_bpf_link_update(link->fd, prog_fd);
217 0 : break;
218 0 : case BF_FLAVOR_NF:
219 0 : r = _bf_link_update_nf(link, hook, prog_fd);
220 0 : break;
221 : default:
222 : return -ENOTSUP;
223 : }
224 :
225 : return r;
226 : }
227 :
228 0 : void bf_link_detach(struct bf_link *link)
229 : {
230 0 : bf_assert(link);
231 :
232 : int r;
233 :
234 0 : r = bf_bpf_link_detach(link->fd);
235 0 : if (r) {
236 0 : bf_warn_r(
237 : r,
238 : "call to BPF_LINK_DETACH failed, closing the file descriptor and assuming the link is destroyed");
239 : }
240 :
241 0 : bf_hookopts_free(&link->hookopts);
242 0 : closep(&link->fd);
243 0 : }
244 :
245 0 : int bf_link_pin(struct bf_link *link, int dir_fd)
246 : {
247 : int r;
248 :
249 0 : bf_assert(link);
250 0 : bf_assert(dir_fd > 0);
251 :
252 0 : r = bf_bpf_obj_pin(link->name, link->fd, dir_fd);
253 0 : if (r)
254 0 : return bf_err_r(r, "failed to pin BPF link '%s'", link->name);
255 :
256 : return 0;
257 : }
258 :
259 0 : void bf_link_unpin(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 = unlinkat(dir_fd, link->name, 0);
267 0 : if (r < 0 && errno != ENOENT) {
268 : // Do not warn on ENOENT, we want the file to be gone!
269 0 : bf_warn_r(
270 : errno,
271 : "failed to unlink BPF link '%s', assuming the link is not pinned",
272 : link->name);
273 : }
274 0 : }
|