Branch data 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 : 64 : int bf_link_new(struct bf_link **link, const char *name)
27 : : {
28 : 64 : _free_bf_link_ struct bf_link *_link = NULL;
29 : :
30 : : bf_assert(link && name);
31 : : bf_assert(name[0] != '\0');
32 : :
33 : 64 : _link = malloc(sizeof(*_link));
34 [ + - ]: 64 : if (!_link)
35 : : return -ENOMEM;
36 : :
37 : 64 : bf_strncpy(_link->name, BPF_OBJ_NAME_LEN, name);
38 : :
39 : 64 : _link->hookopts = NULL;
40 : 64 : _link->fd = -1;
41 : :
42 : 64 : *link = TAKE_PTR(_link);
43 : :
44 : 64 : return 0;
45 : : }
46 : :
47 : 2 : int bf_link_new_from_pack(struct bf_link **link, int dir_fd,
48 : : bf_rpack_node_t node)
49 : : {
50 : 2 : _free_bf_link_ struct bf_link *_link = NULL;
51 : 2 : _cleanup_free_ char *name = NULL;
52 : : bf_rpack_node_t child;
53 : : int r;
54 : :
55 : : bf_assert(link);
56 : :
57 : 2 : r = bf_rpack_kv_str(node, "name", &name);
58 [ - + ]: 2 : if (r)
59 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_link.name");
60 : :
61 : 2 : r = bf_link_new(&_link, name);
62 [ - + ]: 2 : if (r)
63 [ # # ]: 0 : return bf_err_r(r, "failed to create bf_link from pack");
64 : :
65 : 2 : r = bf_rpack_kv_node(node, "hookopts", &child);
66 [ - + ]: 2 : if (r)
67 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_link.hookopts");
68 [ + + ]: 2 : if (!bf_rpack_is_nil(child)) {
69 : 1 : r = bf_hookopts_new_from_pack(&_link->hookopts, child);
70 [ + - ]: 1 : if (r)
71 : : return r;
72 : : }
73 : :
74 [ + - ]: 2 : if (dir_fd != -1) {
75 : 2 : r = bf_bpf_obj_get(_link->name, dir_fd, &_link->fd);
76 [ + + ]: 2 : if (r) {
77 [ + - ]: 1 : return bf_err_r(r, "failed to open pinned BPF link '%s'",
78 : : _link->name);
79 : : }
80 : : }
81 : :
82 : 1 : *link = TAKE_PTR(_link);
83 : :
84 : 1 : return 0;
85 : : }
86 : :
87 : 130 : void bf_link_free(struct bf_link **link)
88 : : {
89 : : bf_assert(link);
90 : :
91 [ + + ]: 130 : if (!*link)
92 : : return;
93 : :
94 : 64 : bf_hookopts_free(&(*link)->hookopts);
95 : 64 : closep(&(*link)->fd);
96 : : freep((void *)link);
97 : : }
98 : :
99 : 164 : int bf_link_pack(const struct bf_link *link, bf_wpack_t *pack)
100 : : {
101 : : bf_assert(link);
102 : : bf_assert(pack);
103 : :
104 : 164 : bf_wpack_kv_str(pack, "name", link->name);
105 : :
106 [ + + ]: 164 : if (link->hookopts) {
107 : 46 : bf_wpack_open_object(pack, "hookopts");
108 : 46 : bf_hookopts_pack(link->hookopts, pack);
109 : 46 : bf_wpack_close_object(pack);
110 : : } else {
111 : : bf_wpack_kv_nil(pack, "hookopts");
112 : : }
113 : :
114 [ - + ]: 164 : return bf_wpack_is_valid(pack) ? 0 : -EINVAL;
115 : : }
116 : :
117 : 2 : void bf_link_dump(const struct bf_link *link, prefix_t *prefix)
118 : : {
119 : : bf_assert(link && prefix);
120 : :
121 [ + - ]: 2 : DUMP(prefix, "struct bf_link at %p", link);
122 : :
123 : 2 : bf_dump_prefix_push(prefix);
124 [ + - ]: 2 : DUMP(prefix, "name: %s", link->name);
125 : :
126 [ + + ]: 2 : if (link->hookopts) {
127 [ + - ]: 1 : DUMP(prefix, "hookopts: struct bf_hookopts *");
128 : 1 : bf_dump_prefix_push(prefix);
129 : 1 : bf_hookopts_dump(link->hookopts, prefix);
130 : 1 : bf_dump_prefix_pop(prefix);
131 : : } else {
132 [ + - ]: 1 : DUMP(prefix, "hookopts: struct bf_hookopts * (NULL)");
133 : : }
134 : :
135 [ + - ]: 2 : DUMP(bf_dump_prefix_last(prefix), "fd: %d", link->fd);
136 : 2 : bf_dump_prefix_pop(prefix);
137 : 2 : }
138 : :
139 : 25 : int bf_link_attach(struct bf_link *link, enum bf_hook hook,
140 : : struct bf_hookopts **hookopts, int prog_fd)
141 : : {
142 : : bf_assert(link && hookopts);
143 : :
144 : 25 : _cleanup_close_ int cgroup_fd = -1;
145 : 25 : struct bf_hookopts *_hookopts = *hookopts;
146 : : int r;
147 : :
148 [ + + + + : 25 : switch (bf_hook_to_flavor(hook)) {
- ]
149 : 12 : case BF_FLAVOR_XDP:
150 : 12 : r = bf_bpf_link_create(prog_fd, _hookopts->ifindex, hook, _hookopts,
151 : : XDP_FLAGS_SKB_MODE);
152 : 12 : break;
153 : 5 : case BF_FLAVOR_TC:
154 : 5 : r = bf_bpf_link_create(prog_fd, _hookopts->ifindex, hook, _hookopts, 0);
155 : 5 : break;
156 : 2 : case BF_FLAVOR_CGROUP:
157 : 2 : cgroup_fd = open(_hookopts->cgpath, O_DIRECTORY | O_RDONLY);
158 [ - + ]: 2 : if (cgroup_fd < 0) {
159 [ # # ]: 0 : return bf_err_r(errno, "failed to open cgroup '%s'",
160 : : _hookopts->cgpath);
161 : : }
162 : :
163 : 2 : r = bf_bpf_link_create(prog_fd, cgroup_fd, hook, _hookopts, 0);
164 : 2 : break;
165 : 6 : case BF_FLAVOR_NF:
166 : 6 : r = bf_bpf_link_create(prog_fd, 0, hook, _hookopts, 0);
167 : 6 : break;
168 : : default:
169 : : return -ENOTSUP;
170 : : }
171 : :
172 [ + + ]: 25 : if (r < 0)
173 : : return r;
174 : :
175 : 21 : link->fd = r;
176 : 21 : link->hookopts = TAKE_PTR(*hookopts);
177 : :
178 : 21 : 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 : : 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 : 3 : int bf_link_update(struct bf_link *link, enum bf_hook hook, int prog_fd)
207 : : {
208 : : bf_assert(link);
209 : :
210 : : int r;
211 : :
212 [ + - - ]: 3 : switch (bf_hook_to_flavor(hook)) {
213 : 3 : case BF_FLAVOR_XDP:
214 : : case BF_FLAVOR_TC:
215 : : case BF_FLAVOR_CGROUP:
216 : 3 : r = bf_bpf_link_update(link->fd, prog_fd);
217 : 3 : 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 : 36 : void bf_link_detach(struct bf_link *link)
229 : : {
230 : : bf_assert(link);
231 : :
232 : : int r;
233 : :
234 : 36 : r = bf_bpf_link_detach(link->fd);
235 [ + + ]: 36 : if (r) {
236 [ + - ]: 20 : 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 : 36 : bf_hookopts_free(&link->hookopts);
242 : 36 : closep(&link->fd);
243 : 36 : }
244 : :
245 : 24 : int bf_link_pin(struct bf_link *link, int dir_fd)
246 : : {
247 : : int r;
248 : :
249 : : bf_assert(link);
250 : : bf_assert(dir_fd > 0);
251 : :
252 : 24 : r = bf_bpf_obj_pin(link->name, link->fd, dir_fd);
253 [ - + ]: 24 : if (r)
254 [ # # ]: 0 : return bf_err_r(r, "failed to pin BPF link '%s'", link->name);
255 : :
256 : : return 0;
257 : : }
258 : :
259 : 39 : void bf_link_unpin(struct bf_link *link, int dir_fd)
260 : : {
261 : : int r;
262 : :
263 : : bf_assert(link);
264 : : bf_assert(dir_fd > 0);
265 : :
266 : 39 : r = unlinkat(dir_fd, link->name, 0);
267 [ + + - + ]: 39 : 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 : 39 : }
|