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 <linux/netfilter.h>
7 : #include <linux/netfilter/nf_tables.h>
8 :
9 : #include <endian.h>
10 : #include <errno.h>
11 : #include <stdbool.h>
12 : #include <stdint.h>
13 : #include <stdlib.h>
14 : #include <unistd.h>
15 :
16 : #include "bpfilter/cgen/cgen.h"
17 : #include "bpfilter/ctx.h"
18 : #include "bpfilter/xlate/front.h"
19 : #include "bpfilter/xlate/nft/nfgroup.h"
20 : #include "bpfilter/xlate/nft/nfmsg.h"
21 : #include "core/chain.h"
22 : #include "core/counter.h"
23 : #include "core/dump.h"
24 : #include "core/front.h"
25 : #include "core/helper.h"
26 : #include "core/hook.h"
27 : #include "core/list.h"
28 : #include "core/logger.h"
29 : #include "core/marsh.h"
30 : #include "core/matcher.h"
31 : #include "core/request.h"
32 : #include "core/response.h"
33 : #include "core/rule.h"
34 : #include "core/verdict.h"
35 :
36 : struct bf_marsh;
37 :
38 : enum
39 : {
40 : BF_IP4HDR_PROTO_OFFSET = 9,
41 : BF_IP4HDR_SADDR_OFFSET = 12,
42 : BF_IP4HDR_DADDR_OFFSET = 16,
43 : };
44 :
45 : static const char *_bf_table_name = "bpfilter";
46 : static const char *_bf_chain_name = "prerouting";
47 :
48 : static int _bf_nft_setup(void);
49 : static int _bf_nft_teardown(void);
50 : static int _bf_nft_request_handler(struct bf_request *request,
51 : struct bf_response **response);
52 : static int _bf_nft_marsh(struct bf_marsh **marsh);
53 : static int _bf_nft_unmarsh(struct bf_marsh *marsh);
54 :
55 : const struct bf_front_ops nft_front = {
56 : .setup = _bf_nft_setup,
57 : .teardown = _bf_nft_teardown,
58 : .request_handler = _bf_nft_request_handler,
59 : .marsh = _bf_nft_marsh,
60 : .unmarsh = _bf_nft_unmarsh,
61 : };
62 :
63 : static bf_list *_bf_nft_rules = NULL;
64 :
65 0 : static int _bf_nft_setup(void)
66 : {
67 : int r;
68 :
69 : // If the cache has been restored already, skip this.
70 0 : if (_bf_nft_rules)
71 : return 0;
72 :
73 0 : r = bf_list_new(
74 : &_bf_nft_rules,
75 0 : (bf_list_ops[]) {{.free = (bf_list_ops_free)bf_nfmsg_free}});
76 0 : if (r < 0)
77 0 : return bf_err_r(r, "failed to create bf_list");
78 :
79 : return 0;
80 : }
81 :
82 0 : static int _bf_nft_teardown(void)
83 : {
84 0 : bf_list_free(&_bf_nft_rules);
85 :
86 0 : return 0;
87 : }
88 :
89 0 : static int _bf_nft_marsh(struct bf_marsh **marsh)
90 : {
91 0 : bf_assert(marsh);
92 :
93 : int r = 0;
94 :
95 0 : bf_list_foreach (_bf_nft_rules, rule_node) {
96 0 : struct bf_nfmsg *msg = bf_list_node_get_data(rule_node);
97 :
98 0 : r = bf_marsh_add_child_raw(marsh, bf_nfmsg_hdr(msg), bf_nfmsg_len(msg));
99 0 : if (r < 0)
100 0 : return bf_err_r(r, "failed to add rule to marsh");
101 : }
102 :
103 : return 0;
104 : }
105 :
106 0 : static int _bf_nft_unmarsh(struct bf_marsh *marsh)
107 : {
108 0 : bf_assert(marsh);
109 :
110 0 : _cleanup_bf_list_ bf_list *list = NULL;
111 : struct bf_marsh *child = NULL;
112 : int r;
113 :
114 0 : r = bf_list_new(
115 0 : &list, (bf_list_ops[]) {{.free = (bf_list_ops_free)bf_nfmsg_free}});
116 0 : if (r < 0)
117 0 : return bf_err_r(r, "failed to create bf_list");
118 :
119 0 : while ((child = bf_marsh_next_child(marsh, child))) {
120 0 : _cleanup_bf_nfmsg_ struct bf_nfmsg *msg = NULL;
121 0 : struct nlmsghdr *nlh = (struct nlmsghdr *)(child->data);
122 :
123 0 : r = bf_nfmsg_new_from_nlmsghdr(&msg, nlh);
124 0 : if (r < 0)
125 0 : return bf_err_r(r, "failed to create bf_nfmsg from marsh");
126 :
127 0 : r = bf_list_add_tail(list, msg);
128 0 : if (r < 0)
129 0 : return bf_err_r(r, "failed to add bf_nfmsg to bf_list");
130 0 : TAKE_PTR(msg);
131 : }
132 :
133 0 : _bf_nft_rules = TAKE_PTR(list);
134 :
135 0 : return 0;
136 : }
137 :
138 0 : static int _bf_nft_getgen_cb(const struct bf_nfmsg *req, struct bf_nfgroup *res)
139 : {
140 0 : bf_assert(req);
141 0 : bf_assert(res);
142 :
143 0 : struct bf_nfmsg *msg = NULL;
144 : int r;
145 :
146 0 : r = bf_nfgroup_add_new_message(res, &msg, NFT_MSG_NEWGEN,
147 0 : bf_nfmsg_seqnr(req));
148 0 : if (r < 0)
149 0 : return bf_err_r(r, "failed to create bf_nfmsg");
150 :
151 0 : bf_nfmsg_push_u32_or_jmp(msg, NFTA_GEN_ID, 0);
152 0 : bf_nfmsg_push_u32_or_jmp(msg, NFTA_GEN_PROC_PID, getpid());
153 0 : bf_nfmsg_push_str_or_jmp(msg, NFTA_GEN_PROC_NAME, "nft");
154 :
155 : return 0;
156 :
157 : bf_nfmsg_push_failure:
158 : return -EINVAL;
159 : }
160 :
161 0 : static int _bf_nft_gettable_cb(const struct bf_nfmsg *req,
162 : struct bf_nfgroup *res)
163 : {
164 0 : bf_assert(req);
165 0 : bf_assert(res);
166 :
167 0 : struct bf_nfmsg *msg = NULL;
168 : int r;
169 :
170 0 : r = bf_nfgroup_add_new_message(res, &msg, NFT_MSG_NEWTABLE,
171 0 : bf_nfmsg_seqnr(req));
172 0 : if (r < 0)
173 0 : return bf_err_r(r, "failed to create bf_nfmsg");
174 :
175 0 : bf_nfmsg_push_str_or_jmp(msg, NFTA_TABLE_NAME, _bf_table_name);
176 0 : bf_nfmsg_push_u32_or_jmp(msg, NFTA_TABLE_FLAGS, 0);
177 0 : bf_nfmsg_push_u64_or_jmp(msg, NFTA_TABLE_HANDLE, 0);
178 0 : bf_nfmsg_push_u32_or_jmp(msg, NFTA_TABLE_USE, 1);
179 :
180 0 : return 0;
181 :
182 : bf_nfmsg_push_failure:
183 : return -EINVAL;
184 : }
185 :
186 0 : static int _bf_nft_newtable_cb(const struct bf_nfmsg *req)
187 : {
188 0 : bf_assert(req);
189 :
190 0 : bf_nfattr *attrs[__NFTA_TABLE_MAX] = {};
191 : int r;
192 :
193 0 : r = bf_nfmsg_parse(req, attrs, __NFTA_TABLE_MAX, bf_nf_table_policy);
194 0 : if (r < 0)
195 0 : return bf_err_r(r, "failed to parse NFT_MSG_GETTABLE attributes");
196 :
197 0 : if (!attrs[NFTA_TABLE_NAME])
198 0 : return bf_warn_r(-EINVAL, "missing NFTA_TABLE_NAME attribute");
199 :
200 0 : if (!bf_streq(bf_nfattr_get_str(attrs[NFTA_TABLE_NAME]), _bf_table_name)) {
201 0 : return bf_warn_r(-EINVAL, "invalid table name '%s'",
202 : bf_nfattr_get_str(attrs[NFTA_TABLE_NAME]));
203 : }
204 :
205 : return 0;
206 : }
207 :
208 0 : static int _bf_nft_newchain_cb(const struct bf_nfmsg *req)
209 : {
210 0 : bf_assert(req);
211 :
212 0 : _cleanup_bf_cgen_ struct bf_cgen *cgen = NULL;
213 0 : _cleanup_bf_chain_ struct bf_chain *chain = NULL;
214 0 : bf_nfattr *chain_attrs[__NFTA_CHAIN_MAX] = {};
215 0 : bf_nfattr *hook_attrs[__NFTA_HOOK_MAX] = {};
216 : enum bf_verdict verdict;
217 : int r;
218 :
219 0 : r = bf_nfmsg_parse(req, chain_attrs, __NFTA_CHAIN_MAX, bf_nf_chain_policy);
220 0 : if (r < 0)
221 0 : return bf_err_r(r, "failed to parse NFT_MSG_NEWCHAIN attributes");
222 :
223 0 : if (!chain_attrs[NFTA_CHAIN_TABLE] ||
224 0 : !bf_streq(bf_nfattr_get_str(chain_attrs[NFTA_CHAIN_TABLE]),
225 : _bf_table_name))
226 0 : return bf_err_r(-EINVAL, "invalid table name");
227 :
228 0 : if (!chain_attrs[NFTA_CHAIN_NAME] ||
229 0 : !bf_streq(bf_nfattr_get_str(chain_attrs[NFTA_CHAIN_NAME]),
230 : _bf_chain_name))
231 0 : return bf_err_r(-EINVAL, "invalid table name");
232 :
233 0 : if (!chain_attrs[NFTA_CHAIN_POLICY])
234 0 : return bf_err_r(-EINVAL, "missing NFTA_CHAIN_POLICY attribute");
235 :
236 0 : if (!chain_attrs[NFTA_CHAIN_HOOK])
237 0 : return bf_err_r(-EINVAL, "missing NFTA_CHAIN_HOOK attribute");
238 :
239 0 : r = bf_nfattr_parse(chain_attrs[NFTA_CHAIN_HOOK], hook_attrs,
240 : __NFTA_HOOK_MAX, bf_nf_hook_policy);
241 0 : if (r < 0)
242 0 : return bf_err_r(r, "failed to parse NFTA_CHAIN_HOOK attributes");
243 :
244 0 : if (!hook_attrs[NFTA_HOOK_HOOKNUM] ||
245 : NF_INET_PRE_ROUTING !=
246 0 : bf_nfattr_get_u32(hook_attrs[NFTA_HOOK_HOOKNUM])) {
247 0 : return bf_err_r(
248 : -EINVAL, "missing or invalid hook (NF_INET_PRE_ROUTING required)");
249 : }
250 :
251 0 : switch (be32toh(bf_nfattr_get_u32(chain_attrs[NFTA_CHAIN_POLICY]))) {
252 : case NF_ACCEPT:
253 : verdict = BF_VERDICT_ACCEPT;
254 : break;
255 0 : case NF_DROP:
256 : verdict = BF_VERDICT_DROP;
257 0 : break;
258 0 : default:
259 0 : return bf_err_r(
260 : -ENOTSUP, "unsupported policy %u",
261 : be32toh(bf_nfattr_get_u32(chain_attrs[NFTA_CHAIN_POLICY])));
262 : };
263 :
264 0 : r = bf_chain_new(&chain, BF_HOOK_NF_LOCAL_IN, verdict, NULL, NULL);
265 0 : if (r < 0)
266 0 : return bf_err_r(r, "failed to create new chain");
267 :
268 0 : cgen = bf_ctx_get_cgen(BF_HOOK_NF_LOCAL_IN, NULL);
269 0 : if (cgen && verdict != cgen->chain->policy) {
270 0 : r = bf_cgen_update(cgen, &chain);
271 0 : if (r < 0)
272 0 : return bf_err_r(r, "failed to update codegen");
273 :
274 0 : bf_info("existing codegen updated with new policy");
275 0 : } else if (!cgen) {
276 0 : r = bf_cgen_new(&cgen, BF_FRONT_NFT, &chain);
277 0 : if (r < 0)
278 0 : return bf_err_r(r, "failed to create bf_cgen");
279 :
280 0 : r = bf_cgen_up(cgen);
281 0 : if (r < 0)
282 0 : return bf_err_r(r, "failed to generate codegen");
283 :
284 0 : r = bf_ctx_set_cgen(cgen);
285 0 : if (r < 0) {
286 0 : bf_cgen_unload(cgen);
287 0 : return r;
288 : }
289 :
290 0 : bf_info("new codegen created and loaded");
291 : } else {
292 0 : bf_info("codegen already properly configured, skipping generation");
293 : }
294 :
295 0 : TAKE_PTR(cgen);
296 :
297 0 : return 0;
298 : }
299 :
300 0 : static int _bf_nft_getchain_cb(const struct bf_nfmsg *req,
301 : struct bf_nfgroup *res)
302 : {
303 0 : bf_assert(req);
304 0 : bf_assert(res);
305 :
306 : struct bf_nfmsg *msg;
307 : struct bf_cgen *cgen;
308 : uint32_t policy;
309 : int r;
310 :
311 : // Only BF_HOOK_NF_LOCAL_IN is supported.
312 0 : cgen = bf_ctx_get_cgen(BF_HOOK_NF_LOCAL_IN, NULL);
313 0 : if (!cgen) {
314 : /* If no codegen is found, do not fill the messages group and return
315 : * success. The response message will then contain only a DONE
316 : * message. */
317 : return 0;
318 : }
319 :
320 0 : r = bf_nfgroup_add_new_message(res, &msg, NFT_MSG_NEWCHAIN,
321 0 : bf_nfmsg_seqnr(req));
322 0 : if (r < 0)
323 0 : return bf_err_r(r, "failed to create bf_nfmsg");
324 :
325 0 : bf_cgen_dump(cgen, EMPTY_PREFIX);
326 :
327 0 : switch (cgen->chain->policy) {
328 : case BF_VERDICT_ACCEPT:
329 : policy = NF_ACCEPT;
330 : break;
331 0 : case BF_VERDICT_DROP:
332 : policy = NF_DROP;
333 0 : break;
334 0 : default:
335 0 : return bf_err_r(-ENOTSUP, "unsupported codegen policy %u",
336 : cgen->chain->policy);
337 : };
338 :
339 0 : bf_nfmsg_push_str_or_jmp(msg, NFTA_CHAIN_TABLE, _bf_table_name);
340 0 : bf_nfmsg_push_str_or_jmp(msg, NFTA_CHAIN_NAME, _bf_chain_name);
341 0 : bf_nfmsg_push_u64_or_jmp(msg, NFTA_CHAIN_HANDLE, BF_HOOK_NF_LOCAL_IN);
342 0 : bf_nfmsg_push_u32_or_jmp(msg, NFTA_CHAIN_POLICY, htobe32(policy));
343 0 : bf_nfmsg_push_str_or_jmp(msg, NFTA_CHAIN_TYPE, "filter");
344 0 : bf_nfmsg_push_u32_or_jmp(msg, NFTA_CHAIN_FLAGS, NFT_CHAIN_BASE);
345 0 : bf_nfmsg_push_u32_or_jmp(msg, NFTA_CHAIN_USE,
346 : htobe32(bf_list_size(&cgen->chain->rules)));
347 :
348 : {
349 0 : _cleanup_bf_nfnest_ struct bf_nfnest _ =
350 0 : bf_nfnest_or_jmp(msg, NFTA_CHAIN_HOOK);
351 :
352 0 : bf_nfmsg_push_u32_or_jmp(msg, NFTA_HOOK_HOOKNUM,
353 : htobe32(NF_INET_PRE_ROUTING));
354 0 : bf_nfmsg_push_u32_or_jmp(msg, NFTA_HOOK_PRIORITY, htobe32(0));
355 : }
356 :
357 0 : return 0;
358 :
359 0 : bf_nfmsg_push_failure:
360 0 : return bf_err_r(-EINVAL, "failed to add attribute to Netlink message");
361 : }
362 :
363 0 : static int _bf_nft_newrule_cb(const struct bf_nfmsg *req)
364 : {
365 0 : bf_assert(req);
366 :
367 0 : _cleanup_bf_rule_ struct bf_rule *rule = NULL;
368 0 : _cleanup_bf_nfmsg_ struct bf_nfmsg *req_copy;
369 : struct bf_chain *chain;
370 : struct bf_cgen *cgen;
371 0 : bf_nfattr *rule_attrs[__NFTA_RULE_MAX] = {};
372 0 : bf_nfattr *expr_attrs[__NFTA_EXPR_MAX] = {};
373 0 : bf_nfattr *payload_attrs[__NFTA_PAYLOAD_MAX] = {};
374 0 : bf_nfattr *cmp_attrs[__NFTA_CMP_MAX] = {};
375 0 : bf_nfattr *data_attrs[__NFTA_DATA_MAX] = {};
376 0 : bf_nfattr *immediate_attrs[__NFTA_IMMEDIATE_MAX] = {};
377 0 : bf_nfattr *verdict_attrs[__NFTA_VERDICT_MAX] = {};
378 : bf_nfattr *parent;
379 : bf_nfattr *attr;
380 : size_t rem;
381 : int r;
382 :
383 0 : r = bf_nfmsg_parse(req, rule_attrs, __NFTA_RULE_MAX, bf_nf_rule_policy);
384 0 : if (r < 0)
385 0 : return bf_err_r(r, "failed to parse NFT_MSG_NEWRULE attributes");
386 :
387 0 : if (!rule_attrs[NFTA_RULE_TABLE] ||
388 0 : !bf_streq(bf_nfattr_get_str(rule_attrs[NFTA_RULE_TABLE]),
389 : _bf_table_name))
390 0 : return bf_err_r(-EINVAL, "invalid table name");
391 :
392 0 : if (!rule_attrs[NFTA_RULE_CHAIN] ||
393 0 : !bf_streq(bf_nfattr_get_str(rule_attrs[NFTA_RULE_CHAIN]),
394 : _bf_chain_name))
395 0 : return bf_err_r(-EINVAL, "invalid chain name");
396 :
397 0 : parent = rule_attrs[NFTA_RULE_EXPRESSIONS];
398 0 : if (!parent)
399 0 : return bf_err_r(-EINVAL, "missing NFTA_RULE_EXPRESSIONS attribute");
400 0 : rem = bf_nfattr_data_len(parent);
401 :
402 0 : attr = (bf_nfattr *)bf_nfattr_data(parent);
403 0 : if (!bf_nfattr_is_ok(attr, rem))
404 0 : return bf_err_r(-EINVAL, "invalid NFTA_RULE_EXPRESSIONS attribute");
405 :
406 0 : r = bf_nfattr_parse(attr, expr_attrs, __NFTA_EXPR_MAX, bf_nf_expr_policy);
407 0 : if (r < 0) {
408 0 : return bf_err_r(r, "failed to parse NFTA_RULE_EXPRESSIONS attributes");
409 : }
410 :
411 0 : if (!expr_attrs[NFTA_EXPR_NAME] ||
412 0 : !bf_streq(bf_nfattr_get_str(expr_attrs[NFTA_EXPR_NAME]), "payload"))
413 0 : return bf_err_r(-EINVAL, "expecting rule expression 'payload'");
414 :
415 0 : r = bf_nfattr_parse(expr_attrs[NFTA_EXPR_DATA], payload_attrs,
416 : __NFTA_PAYLOAD_MAX, bf_nf_payload_policy);
417 0 : if (r < 0)
418 0 : return bf_err_r(r, "failed to parse NFTA_EXPR_DATA attributes");
419 :
420 0 : if (!payload_attrs[NFTA_PAYLOAD_BASE] ||
421 0 : be32toh(bf_nfattr_get_u32(payload_attrs[NFTA_PAYLOAD_BASE])) !=
422 : NFT_PAYLOAD_NETWORK_HEADER) {
423 0 : return bf_err_r(-EINVAL,
424 : "expecting payload base NFT_PAYLOAD_NETWORK_HEADER");
425 : }
426 :
427 0 : uint32_t len = be32toh(bf_nfattr_get_u32(payload_attrs[NFTA_PAYLOAD_LEN]));
428 : uint32_t off =
429 0 : be32toh(bf_nfattr_get_u32(payload_attrs[NFTA_PAYLOAD_OFFSET]));
430 :
431 0 : attr = bf_nfattr_next(attr, &rem);
432 0 : if (!bf_nfattr_is_ok(attr, rem))
433 0 : return bf_err_r(-EINVAL, "invalid NFTA_RULE_EXPRESSIONS attribute");
434 :
435 0 : r = bf_nfattr_parse(attr, expr_attrs, __NFTA_EXPR_MAX, bf_nf_expr_policy);
436 0 : if (r < 0) {
437 0 : return bf_err_r(r, "failed to parse NFTA_RULE_EXPRESSIONS attributes");
438 : }
439 :
440 0 : if (!expr_attrs[NFTA_EXPR_NAME] ||
441 0 : !bf_streq(bf_nfattr_get_str(expr_attrs[NFTA_EXPR_NAME]), "cmp"))
442 0 : return bf_err_r(-EINVAL, "expecting rule expression 'cmp'");
443 :
444 0 : r = bf_nfattr_parse(expr_attrs[NFTA_EXPR_DATA], cmp_attrs, __NFTA_CMP_MAX,
445 : bf_nf_cmp_policy);
446 0 : if (r < 0)
447 0 : return bf_err_r(r, "failed to parse NFTA_EXPR_DATA attributes");
448 :
449 0 : uint32_t op = be32toh(bf_nfattr_get_u32(cmp_attrs[NFTA_CMP_OP]));
450 0 : if (op != NFT_CMP_EQ)
451 0 : return bf_err_r(-EINVAL, "only NFTA_CMP_OP is supported");
452 :
453 0 : r = bf_nfattr_parse(cmp_attrs[NFTA_CMP_DATA], data_attrs, __NFTA_DATA_MAX,
454 : bf_nf_data_policy);
455 0 : if (r < 0)
456 0 : return bf_err_r(r, "failed to parse NFTA_CMP_DATA attributes");
457 :
458 : uint32_t cmp_value =
459 0 : be32toh(bf_nfattr_get_u32(data_attrs[NFTA_DATA_VALUE]));
460 :
461 0 : attr = bf_nfattr_next(attr, &rem);
462 0 : if (!bf_nfattr_is_ok(attr, rem))
463 0 : return bf_err_r(-EINVAL, "invalid NFTA_RULE_EXPRESSIONS attribute");
464 :
465 0 : r = bf_nfattr_parse(attr, expr_attrs, __NFTA_EXPR_MAX, bf_nf_expr_policy);
466 0 : if (r < 0) {
467 0 : return bf_err_r(r, "failed to parse NFTA_RULE_EXPRESSIONS attributes");
468 : }
469 :
470 : bool counter = false;
471 0 : if (bf_streq(bf_nfattr_data(expr_attrs[NFTA_EXPR_NAME]), "counter")) {
472 : counter = true;
473 :
474 0 : attr = bf_nfattr_next(attr, &rem);
475 0 : if (!bf_nfattr_is_ok(attr, rem))
476 0 : return bf_err_r(-EINVAL, "expecting cmp, got invalid attribute");
477 :
478 0 : r = bf_nfattr_parse(attr, expr_attrs, __NFTA_EXPR_MAX,
479 : bf_nf_expr_policy);
480 0 : if (r < 0) {
481 0 : return bf_err_r(r,
482 : "failed to parse NFTA_RULE_EXPRESSIONS attributes");
483 : }
484 : }
485 :
486 0 : if (!bf_streq(bf_nfattr_data(expr_attrs[NFTA_EXPR_NAME]), "immediate")) {
487 0 : return bf_err_r(r,
488 : "expected immediate attribute, but have '%s' instead",
489 : bf_nfattr_get_str(expr_attrs[NFTA_EXPR_NAME]));
490 : }
491 :
492 0 : r = bf_nfattr_parse(expr_attrs[NFTA_EXPR_DATA], immediate_attrs,
493 : __NFTA_IMMEDIATE_MAX, bf_nf_immediate_policy);
494 0 : if (r < 0)
495 0 : return bf_err_r(r, "failed to parse NFTA_EXPR_DATA attributes");
496 :
497 0 : r = bf_nfattr_parse(immediate_attrs[NFTA_IMMEDIATE_DATA], data_attrs,
498 : __NFTA_DATA_MAX, bf_nf_data_policy);
499 0 : if (r < 0)
500 0 : return bf_err_r(r, "failed to parse NFTA_IMMEDIATE_DATA attributes");
501 :
502 0 : if (!data_attrs[NFTA_DATA_VERDICT])
503 0 : return bf_err_r(-EINVAL, "missing NFTA_DATA_VERDICT attribute");
504 :
505 0 : r = bf_nfattr_parse(data_attrs[NFTA_DATA_VERDICT], verdict_attrs,
506 : __NFTA_VERDICT_MAX, bf_nf_verdict_policy);
507 0 : if (r < 0)
508 0 : return bf_err_r(r, "failed to parse NFTA_DATA_VERDICT attributes");
509 :
510 0 : if (!verdict_attrs[NFTA_VERDICT_CODE])
511 0 : return bf_err_r(-EINVAL, "missing NFTA_VERDICT_CODE attribute");
512 :
513 0 : int32_t nf_verdict =
514 0 : be32toh(bf_nfattr_get_s32(verdict_attrs[NFTA_VERDICT_CODE]));
515 :
516 : enum bf_verdict verdict;
517 0 : switch (nf_verdict) {
518 : case NF_ACCEPT:
519 : verdict = BF_VERDICT_ACCEPT;
520 : break;
521 : case NF_DROP:
522 : verdict = BF_VERDICT_DROP;
523 : break;
524 : case NFT_CONTINUE:
525 : verdict = BF_VERDICT_CONTINUE;
526 : break;
527 0 : default:
528 0 : return bf_err_r(
529 : -EINVAL, "only ACCEPT, DROP and CONTINUE verdicts are supported");
530 : }
531 :
532 : // Add the rule to the relevant codegen
533 0 : cgen = bf_ctx_get_cgen(BF_HOOK_NF_LOCAL_IN, NULL);
534 0 : if (!cgen)
535 0 : return bf_err_r(-EINVAL, "no codegen found for hook");
536 :
537 0 : r = bf_rule_new(&rule);
538 0 : if (r < 0)
539 0 : return bf_err_r(r, "failed to create bf_rule");
540 :
541 0 : rule->counters = counter;
542 0 : switch (off) {
543 : case BF_IP4HDR_PROTO_OFFSET:
544 0 : r = bf_rule_add_matcher(rule, BF_MATCHER_IP4_PROTO, BF_MATCHER_EQ,
545 0 : (uint16_t[]) {htobe32(cmp_value)},
546 : sizeof(uint16_t));
547 0 : if (r)
548 0 : return r;
549 0 : break;
550 : case BF_IP4HDR_SADDR_OFFSET:
551 0 : r = bf_rule_add_matcher(
552 : rule, BF_MATCHER_IP4_SRC_ADDR, BF_MATCHER_EQ,
553 0 : (struct bf_matcher_ip4_addr[]) {
554 0 : {.addr = htobe32(cmp_value), .mask = ~0ULL >> (32 - len * 8)}},
555 : sizeof(struct bf_matcher_ip4_addr));
556 0 : if (r)
557 : return r;
558 : break;
559 : case BF_IP4HDR_DADDR_OFFSET:
560 0 : r = bf_rule_add_matcher(
561 : rule, BF_MATCHER_IP4_DST_ADDR, BF_MATCHER_EQ,
562 0 : (struct bf_matcher_ip4_addr[]) {
563 0 : {.addr = htobe32(cmp_value), .mask = ~0ULL >> (32 - len * 8)}},
564 : sizeof(struct bf_matcher_ip4_addr));
565 0 : if (r)
566 : return r;
567 : break;
568 0 : default:
569 0 : return bf_err_r(-EINVAL, "unknown IP header offset %d", off);
570 : };
571 :
572 0 : chain = cgen->chain;
573 :
574 0 : rule->verdict = verdict;
575 0 : rule->index = bf_list_size(&cgen->chain->rules);
576 0 : r = bf_chain_add_rule(chain, rule);
577 0 : if (r < 0)
578 0 : return bf_err_r(r, "failed to add rule to chain");
579 0 : TAKE_PTR(rule);
580 :
581 0 : r = bf_cgen_update(cgen, &cgen->chain);
582 0 : if (r < 0)
583 0 : return bf_err_r(r, "failed to update codegen");
584 :
585 : // Backup the rule in the front-end context
586 0 : r = bf_nfmsg_new_from_nlmsghdr(&req_copy, bf_nfmsg_hdr(req));
587 0 : if (r < 0)
588 0 : return bf_err_r(r, "failed to create bf_nfmsg from nlmsghdr");
589 :
590 0 : r = bf_list_add_tail(_bf_nft_rules, req_copy);
591 0 : if (r < 0)
592 0 : return bf_err_r(r, "failed to add rule to bf_list");
593 0 : TAKE_PTR(req_copy);
594 :
595 0 : return 0;
596 : }
597 :
598 0 : static int _bf_nft_getrule_cb(const struct bf_nfmsg *req,
599 : struct bf_nfgroup *res)
600 : {
601 0 : bf_assert(req);
602 0 : bf_assert(res);
603 :
604 0 : bf_nfattr *rule_attrs[__NFTA_RULE_MAX] = {};
605 : int i = 0;
606 : int r;
607 :
608 0 : bf_list_foreach (_bf_nft_rules, rule_node) {
609 0 : _cleanup_bf_nfmsg_ struct bf_nfmsg *msg = NULL;
610 0 : struct bf_nfmsg *cached_msg = bf_list_node_get_data(rule_node);
611 :
612 0 : r = bf_nfgroup_add_new_message(res, &msg, NFT_MSG_NEWRULE,
613 0 : bf_nfmsg_seqnr(req));
614 0 : if (r < 0)
615 0 : return bf_err_r(r, "failed to create bf_nfmsg");
616 :
617 0 : bf_nfmsg_push_str_or_jmp(msg, NFTA_RULE_TABLE, _bf_table_name);
618 0 : bf_nfmsg_push_str_or_jmp(msg, NFTA_RULE_CHAIN, _bf_chain_name);
619 0 : bf_nfmsg_push_u64_or_jmp(msg, NFTA_RULE_HANDLE, i);
620 0 : bf_nfmsg_push_u64_or_jmp(msg, NFTA_RULE_POSITION, i);
621 :
622 0 : r = bf_nfmsg_parse(cached_msg, rule_attrs, __NFTA_RULE_MAX,
623 : bf_nf_rule_policy);
624 0 : if (r < 0)
625 0 : return bf_err_r(r, "failed to parse NFT_MSG_NEWRULE attributes");
626 :
627 : {
628 0 : _cleanup_bf_nfnest_ struct bf_nfnest _ =
629 0 : bf_nfnest_or_jmp(msg, NFTA_RULE_EXPRESSIONS);
630 0 : bf_nfattr *expr_attrs[__NFTA_EXPR_MAX] = {};
631 0 : bf_nfattr *expressions = rule_attrs[NFTA_RULE_EXPRESSIONS];
632 : bf_nfattr *expression;
633 0 : size_t remaining = bf_nfattr_data_len(expressions);
634 :
635 0 : expression = bf_nfattr_data(expressions);
636 0 : while (bf_nfattr_is_ok(expression, remaining)) {
637 0 : _cleanup_bf_nfnest_ struct bf_nfnest _ =
638 0 : bf_nfnest_or_jmp(msg, NFTA_LIST_ELEM);
639 :
640 0 : r = bf_nfattr_parse(expression, expr_attrs, __NFTA_EXPR_MAX,
641 : bf_nf_expr_policy);
642 0 : if (r < 0) {
643 0 : return bf_err_r(r,
644 : "failed to parse NFTA_EXPR_* attributes");
645 : }
646 :
647 0 : bf_nfmsg_push_str_or_jmp(
648 : msg, NFTA_EXPR_NAME,
649 : bf_nfattr_get_str(expr_attrs[NFTA_EXPR_NAME]));
650 0 : if (bf_streq(bf_nfattr_data(expr_attrs[NFTA_EXPR_NAME]),
651 : "counter")) {
652 0 : _cleanup_bf_nfnest_ struct bf_nfnest _ =
653 0 : bf_nfnest_or_jmp(msg, NFTA_EXPR_DATA);
654 : struct bf_counter counter;
655 :
656 0 : r = bf_cgen_get_counter(
657 0 : bf_ctx_get_cgen(BF_HOOK_NF_LOCAL_IN, NULL), i,
658 : &counter);
659 0 : if (r < 0)
660 0 : return bf_err_r(r, "failed to get counter");
661 :
662 0 : bf_nfmsg_push_u64_or_jmp(msg, NFTA_COUNTER_BYTES,
663 : be64toh(counter.bytes));
664 0 : bf_nfmsg_push_u64_or_jmp(msg, NFTA_COUNTER_PACKETS,
665 : be64toh(counter.packets));
666 : } else {
667 0 : bf_nfmsg_attr_push_or_jmp(
668 : msg, NFTA_EXPR_DATA,
669 : bf_nfattr_data(expr_attrs[NFTA_EXPR_DATA]),
670 : bf_nfattr_data_len(expr_attrs[NFTA_EXPR_DATA]));
671 : }
672 :
673 0 : expression = bf_nfattr_next(expression, &remaining);
674 : }
675 : }
676 :
677 0 : i++;
678 0 : TAKE_PTR(msg);
679 : }
680 :
681 : return 0;
682 :
683 : bf_nfmsg_push_failure:
684 0 : return bf_err_r(-EINVAL, "failed to add attribute to Netlink message");
685 : }
686 :
687 0 : static int _bf_nft_request_handle(const struct bf_nfmsg *req,
688 : struct bf_nfgroup *res)
689 : {
690 0 : bf_assert(req);
691 0 : bf_assert(res);
692 :
693 : int r = 0;
694 :
695 0 : switch (bf_nfmsg_command(req)) {
696 0 : case NFT_MSG_GETGEN:
697 0 : r = _bf_nft_getgen_cb(req, res);
698 0 : break;
699 0 : case NFT_MSG_GETTABLE:
700 0 : r = _bf_nft_gettable_cb(req, res);
701 0 : break;
702 0 : case NFT_MSG_NEWTABLE:
703 0 : r = _bf_nft_newtable_cb(req);
704 0 : break;
705 0 : case NFT_MSG_GETCHAIN:
706 0 : r = _bf_nft_getchain_cb(req, res);
707 0 : break;
708 0 : case NFT_MSG_NEWCHAIN:
709 0 : r = _bf_nft_newchain_cb(req);
710 0 : break;
711 0 : case NFT_MSG_GETRULE:
712 0 : r = _bf_nft_getrule_cb(req, res);
713 0 : break;
714 0 : case NFT_MSG_NEWRULE:
715 0 : r = _bf_nft_newrule_cb(req);
716 0 : break;
717 : case NFT_MSG_GETOBJ:
718 : case NFT_MSG_GETFLOWTABLE:
719 : case NFT_MSG_GETSET:
720 : break;
721 0 : default:
722 0 : r = bf_warn_r(-ENOTSUP, "unsupported nft command %hu",
723 : bf_nfmsg_command(req));
724 0 : break;
725 : }
726 :
727 0 : return r;
728 : }
729 :
730 0 : static int _bf_nft_request_handler(struct bf_request *request,
731 : struct bf_response **response)
732 : {
733 0 : bf_assert(request);
734 0 : bf_assert(response);
735 :
736 0 : _cleanup_bf_nfgroup_ struct bf_nfgroup *req = NULL;
737 0 : _cleanup_bf_nfgroup_ struct bf_nfgroup *res = NULL;
738 : int r;
739 :
740 0 : r = bf_nfgroup_new_from_stream(&req, (struct nlmsghdr *)request->data,
741 : request->data_len);
742 0 : if (r < 0)
743 0 : return bf_err_r(r, "failed to get bf_nfgroup from request");
744 :
745 0 : r = bf_nfgroup_new(&res);
746 0 : if (r < 0)
747 0 : return bf_err_r(r, "failed to create bf_nfgroup");
748 :
749 0 : bf_list_foreach (bf_nfgroup_messages(req), msg_node) {
750 0 : struct bf_nfmsg *msg = bf_list_node_get_data(msg_node);
751 0 : r = _bf_nft_request_handle(msg, res);
752 0 : if (r)
753 0 : return bf_err_r(r, "failed to handle nft request");
754 : }
755 :
756 0 : return bf_nfgroup_to_response(res, response);
757 : }
|