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 "bpfilter/cgen/tc.h"
7 :
8 : #include <linux/bpf.h>
9 : #include <linux/bpf_common.h>
10 : #include <linux/pkt_cls.h>
11 :
12 : #include <stddef.h>
13 :
14 : #include "bpfilter/cgen/cgen.h"
15 : #include "bpfilter/cgen/prog/link.h"
16 : #include "bpfilter/cgen/program.h"
17 : #include "bpfilter/cgen/stub.h"
18 : #include "core/btf.h"
19 : #include "core/flavor.h"
20 : #include "core/helper.h"
21 : #include "core/hook.h"
22 : #include "core/list.h"
23 : #include "core/logger.h"
24 : #include "core/verdict.h"
25 :
26 : #include "external/filter.h"
27 :
28 : static int _bf_tc_gen_inline_prologue(struct bf_program *program);
29 : static int _bf_tc_gen_inline_epilogue(struct bf_program *program);
30 : static int _bf_tc_get_verdict(enum bf_verdict verdict);
31 : static int _bf_tc_attach_prog(
32 : struct bf_program *new_prog, struct bf_program *old_prog,
33 : int (*get_new_link_cb)(struct bf_program *prog, struct bf_link *old_link,
34 : struct bf_link **new_link));
35 : static int _bf_tc_detach_prog(struct bf_program *program);
36 :
37 : const struct bf_flavor_ops bf_flavor_ops_tc = {
38 : .gen_inline_prologue = _bf_tc_gen_inline_prologue,
39 : .gen_inline_epilogue = _bf_tc_gen_inline_epilogue,
40 : .get_verdict = _bf_tc_get_verdict,
41 : .attach_prog = _bf_tc_attach_prog,
42 : .detach_prog = _bf_tc_detach_prog,
43 : };
44 :
45 0 : static int _bf_tc_gen_inline_prologue(struct bf_program *program)
46 : {
47 : int r;
48 :
49 0 : bf_assert(program);
50 :
51 : // Calculate the packet size and store it into the runtime context
52 0 : EMIT(program, BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1,
53 : offsetof(struct __sk_buff, data)));
54 0 : EMIT(program, BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1,
55 : offsetof(struct __sk_buff, data_end)));
56 0 : EMIT(program, BPF_ALU64_REG(BPF_SUB, BPF_REG_3, BPF_REG_2));
57 0 : EMIT(program,
58 : BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_3, BF_PROG_CTX_OFF(pkt_size)));
59 :
60 : /** The @c __sk_buff structure contains two fields related to the interface
61 : * index: @c ingress_ifindex and @c ifindex . @c ingress_ifindex is the
62 : * interface index the packet has been received on. However, we use
63 : * @c ifindex which is the interface index the packet is processed by: if
64 : * a packet is redirected locally from interface #1 to interface #2, then
65 : * @c ingress_ifindex will contain @c 1 but @c ifindex will contains @c 2 .
66 : * For egress, only @c ifindex is used.
67 : */
68 0 : if ((r = bf_btf_get_field_off("__sk_buff", "ifindex")) < 0)
69 : return r;
70 0 : EMIT(program, BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, r));
71 0 : EMIT(program,
72 : BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_2, BF_PROG_CTX_OFF(ifindex)));
73 :
74 0 : r = bf_stub_make_ctx_skb_dynptr(program, BPF_REG_1);
75 0 : if (r)
76 : return r;
77 :
78 0 : r = bf_stub_parse_l2_ethhdr(program);
79 0 : if (r)
80 : return r;
81 :
82 0 : r = bf_stub_parse_l3_hdr(program);
83 0 : if (r)
84 : return r;
85 :
86 0 : r = bf_stub_parse_l4_hdr(program);
87 : if (r)
88 : return r;
89 :
90 : return 0;
91 : }
92 :
93 0 : static int _bf_tc_gen_inline_epilogue(struct bf_program *program)
94 : {
95 : UNUSED(program);
96 :
97 0 : return 0;
98 : }
99 :
100 : /**
101 : * Convert a standard verdict into a return value.
102 : *
103 : * @param verdict Verdict to convert. Must be valid.
104 : * @return TC return code corresponding to the verdict, as an integer.
105 : */
106 0 : static int _bf_tc_get_verdict(enum bf_verdict verdict)
107 : {
108 0 : bf_assert(0 <= verdict && verdict < _BF_TERMINAL_VERDICT_MAX);
109 :
110 : static const int verdicts[] = {
111 : [BF_VERDICT_ACCEPT] = TC_ACT_OK,
112 : [BF_VERDICT_DROP] = TC_ACT_SHOT,
113 : };
114 :
115 : static_assert(ARRAY_SIZE(verdicts) == _BF_TERMINAL_VERDICT_MAX);
116 :
117 0 : return verdicts[verdict];
118 : }
119 :
120 0 : static int _bf_tc_attach_prog(struct bf_program *new_prog,
121 : struct bf_program *old_prog,
122 : int (*get_new_link_cb)(struct bf_program *prog,
123 : struct bf_link *old_link,
124 : struct bf_link **new_link))
125 : {
126 : struct bf_link *new_link;
127 : struct bf_link *old_link;
128 : int new_fd;
129 : unsigned int ifindex;
130 : int r;
131 :
132 0 : bf_assert(new_prog && get_new_link_cb);
133 :
134 0 : old_link = old_prog ? bf_list_get_at(&old_prog->links, 0) : NULL;
135 0 : new_fd = new_prog->runtime.prog_fd;
136 0 : ifindex = new_prog->runtime.chain->hook_opts.ifindex;
137 :
138 0 : r = get_new_link_cb(new_prog, old_link, &new_link);
139 0 : if (r)
140 0 : return bf_err_r(r, "failed to create new TC link");
141 :
142 0 : if (old_link) {
143 0 : r = bf_link_update(new_link, new_fd);
144 0 : if (r) {
145 0 : return bf_err_r(r,
146 : "failed to update existing link for TC bf_program");
147 : }
148 : } else {
149 0 : r = bf_link_attach_tc(new_link, new_fd, ifindex);
150 0 : if (r)
151 0 : return bf_err_r(r, "failed to attach TC program");
152 : }
153 :
154 : return 0;
155 : }
156 :
157 : /**
158 : * Detach the TC BPF program.
159 : *
160 : * @param program Attached TC BPF program. Can't be NULL.
161 : * @return 0 on success, negative errno value on failure.
162 : */
163 0 : static int _bf_tc_detach_prog(struct bf_program *program)
164 : {
165 0 : bf_assert(program);
166 :
167 0 : return bf_link_detach(bf_list_get_at(&program->links, 0));
168 : }
|