Line data Source code
1 : /* SPDX-License-Identifier: GPL-2.0-only */
2 : /*
3 : * Copyright (c) 2023 Meta Platforms, Inc. and affiliates.
4 : */
5 :
6 : #include <linux/bpf.h>
7 : #include <linux/bpf_common.h>
8 :
9 : #include <stddef.h>
10 :
11 : #include "bpfilter/cgen/prog/link.h"
12 : #include "bpfilter/cgen/program.h"
13 : #include "bpfilter/cgen/stub.h"
14 : #include "core/bpf.h"
15 : #include "core/flavor.h"
16 : #include "core/helper.h"
17 : #include "core/hook.h"
18 : #include "core/list.h"
19 : #include "core/logger.h"
20 : #include "core/verdict.h"
21 :
22 : #include "external/filter.h"
23 :
24 : static int _bf_xdp_gen_inline_prologue(struct bf_program *program);
25 : static int _bf_xdp_gen_inline_epilogue(struct bf_program *program);
26 : static int _bf_xdp_get_verdict(enum bf_verdict verdict);
27 : static int _bf_xdp_attach_prog(
28 : struct bf_program *new_prog, struct bf_program *old_prog,
29 : int (*get_new_link_cb)(struct bf_program *prog, struct bf_link *old_link,
30 : struct bf_link **new_link));
31 : static int _bf_xdp_detach_prog(struct bf_program *program);
32 :
33 : const struct bf_flavor_ops bf_flavor_ops_xdp = {
34 : .gen_inline_prologue = _bf_xdp_gen_inline_prologue,
35 : .gen_inline_epilogue = _bf_xdp_gen_inline_epilogue,
36 : .get_verdict = _bf_xdp_get_verdict,
37 : .attach_prog = _bf_xdp_attach_prog,
38 : .detach_prog = _bf_xdp_detach_prog,
39 : };
40 :
41 : /**
42 : * Generate XDP program prologue.
43 : *
44 : * @warning @ref bf_stub_parse_l2_ethhdr will check for L3 protocol. If L3 is
45 : * not IPv4, the program will be terminated.
46 : *
47 : * @param program Program to generate the prologue for. Must not be NULL.
48 : * @return 0 on success, or negative errno value on error.
49 : */
50 0 : static int _bf_xdp_gen_inline_prologue(struct bf_program *program)
51 : {
52 : int r;
53 :
54 0 : bf_assert(program);
55 :
56 : // Calculate the packet size and store it into the runtime context
57 0 : EMIT(program, BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1,
58 : offsetof(struct xdp_md, data)));
59 0 : EMIT(program, BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1,
60 : offsetof(struct xdp_md, data_end)));
61 0 : EMIT(program, BPF_ALU64_REG(BPF_SUB, BPF_REG_3, BPF_REG_2));
62 0 : EMIT(program,
63 : BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_3, BF_PROG_CTX_OFF(pkt_size)));
64 :
65 : // Store the ingress ifindex into the runtime context
66 0 : EMIT(program, BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1,
67 : offsetof(struct xdp_md, ingress_ifindex)));
68 0 : EMIT(program,
69 : BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_2, BF_PROG_CTX_OFF(ifindex)));
70 :
71 0 : r = bf_stub_make_ctx_xdp_dynptr(program, BPF_REG_1);
72 0 : if (r)
73 : return r;
74 :
75 0 : r = bf_stub_parse_l2_ethhdr(program);
76 0 : if (r)
77 : return r;
78 :
79 0 : r = bf_stub_parse_l3_hdr(program);
80 0 : if (r)
81 : return r;
82 :
83 0 : r = bf_stub_parse_l4_hdr(program);
84 : if (r)
85 : return r;
86 :
87 : return 0;
88 : }
89 :
90 0 : static int _bf_xdp_gen_inline_epilogue(struct bf_program *program)
91 : {
92 : UNUSED(program);
93 :
94 0 : return 0;
95 : }
96 :
97 0 : static int _bf_xdp_get_verdict(enum bf_verdict verdict)
98 : {
99 0 : bf_assert(0 <= verdict && verdict < _BF_TERMINAL_VERDICT_MAX);
100 :
101 : static const int verdicts[] = {
102 : [BF_VERDICT_ACCEPT] = XDP_PASS,
103 : [BF_VERDICT_DROP] = XDP_DROP,
104 : };
105 :
106 : static_assert(ARRAY_SIZE(verdicts) == _BF_TERMINAL_VERDICT_MAX);
107 :
108 0 : return verdicts[verdict];
109 : }
110 :
111 0 : static int _bf_xdp_attach_prog(
112 : struct bf_program *new_prog, struct bf_program *old_prog,
113 : int (*get_new_link_cb)(struct bf_program *prog, struct bf_link *old_link,
114 : struct bf_link **new_link))
115 : {
116 : struct bf_link *new_link;
117 : struct bf_link *old_link;
118 : int new_fd;
119 : unsigned int ifindex;
120 : int r;
121 :
122 0 : bf_assert(new_prog && get_new_link_cb);
123 :
124 0 : old_link = old_prog ? bf_list_get_at(&old_prog->links, 0) : NULL;
125 0 : new_fd = new_prog->runtime.prog_fd;
126 0 : ifindex = new_prog->runtime.chain->hook_opts.ifindex;
127 :
128 0 : r = get_new_link_cb(new_prog, old_link, &new_link);
129 0 : if (r)
130 0 : return bf_err_r(r, "failed to create new XDP link");
131 :
132 0 : if (old_link) {
133 0 : r = bf_link_update(new_link, new_fd);
134 0 : if (r) {
135 0 : return bf_err_r(
136 : r, "failed to update existing link for XDP bf_program");
137 : }
138 : } else {
139 0 : r = bf_link_attach_xdp(new_link, new_fd, ifindex, BF_XDP_MODE_SKB);
140 0 : if (r)
141 0 : return bf_err_r(r, "failed to attach XDP program");
142 : }
143 :
144 : return 0;
145 : }
146 :
147 0 : static int _bf_xdp_detach_prog(struct bf_program *program)
148 : {
149 0 : bf_assert(program);
150 :
151 0 : return bf_link_detach(bf_list_get_at(&program->links, 0));
152 : }
|