Branch data 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 "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 : : #include <stdint.h>
14 : :
15 : : #include <bpfilter/btf.h>
16 : : #include <bpfilter/flavor.h>
17 : : #include <bpfilter/helper.h>
18 : : #include <bpfilter/matcher.h>
19 : : #include <bpfilter/verdict.h>
20 : :
21 : : #include "cgen/matcher/cmp.h"
22 : : #include "cgen/packet.h"
23 : : #include "cgen/program.h"
24 : : #include "cgen/stub.h"
25 : : #include "filter.h"
26 : :
27 : 244 : static int _bf_tc_gen_inline_prologue(struct bf_program *program)
28 : : {
29 : : int r;
30 : :
31 : : assert(program);
32 : :
33 : : // Copy the packet size into the runtime context
34 [ + - ]: 244 : EMIT(program, BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1,
35 : : offsetof(struct __sk_buff, len)));
36 [ + - ]: 244 : EMIT(program,
37 : : BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_3, BF_PROG_CTX_OFF(pkt_size)));
38 : :
39 : : /** The @c __sk_buff structure contains two fields related to the interface
40 : : * index: @c ingress_ifindex and @c ifindex . @c ingress_ifindex is the
41 : : * interface index the packet has been received on. However, we use
42 : : * @c ifindex which is the interface index the packet is processed by: if
43 : : * a packet is redirected locally from interface #1 to interface #2, then
44 : : * @c ingress_ifindex will contain @c 1 but @c ifindex will contains @c 2 .
45 : : * For egress, only @c ifindex is used.
46 : : */
47 [ + - ]: 244 : if ((r = bf_btf_get_field_off("__sk_buff", "ifindex")) < 0)
48 : : return r;
49 [ + - ]: 244 : EMIT(program, BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, r));
50 [ + - ]: 244 : EMIT(program,
51 : : BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_2, BF_PROG_CTX_OFF(ifindex)));
52 : :
53 : 244 : r = bf_stub_make_ctx_skb_dynptr(program, BPF_REG_1);
54 [ + - ]: 244 : if (r)
55 : : return r;
56 : :
57 : 244 : r = bf_stub_parse_l2_ethhdr(program);
58 [ + - ]: 244 : if (r)
59 : : return r;
60 : :
61 : 244 : r = bf_stub_parse_l3_hdr(program);
62 [ + - ]: 244 : if (r)
63 : : return r;
64 : :
65 : 244 : r = bf_stub_parse_l4_hdr(program);
66 : : if (r)
67 : : return r;
68 : :
69 : : return 0;
70 : : }
71 : :
72 : 244 : static int _bf_tc_gen_inline_epilogue(struct bf_program *program)
73 : : {
74 : : (void)program;
75 : :
76 : 244 : return 0;
77 : : }
78 : :
79 : 6 : static int _bf_tc_gen_inline_set_mark(struct bf_program *program, uint32_t mark)
80 : : {
81 [ + - ]: 6 : EMIT(program,
82 : : BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, BF_PROG_CTX_OFF(arg)));
83 [ + - ]: 6 : EMIT(program, BPF_MOV64_IMM(BPF_REG_2, mark));
84 : 6 : EMIT(program, BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_2,
85 : : offsetof(struct __sk_buff, mark)));
86 : :
87 : : return 0;
88 : : }
89 : :
90 : 551 : static int _bf_tc_gen_inline_matcher(struct bf_program *program,
91 : : const struct bf_matcher *matcher)
92 : : {
93 : : assert(program);
94 : : assert(matcher);
95 : :
96 [ + + + ]: 551 : switch (bf_matcher_get_type(matcher)) {
97 : 12 : case BF_MATCHER_META_MARK:
98 [ + - ]: 12 : EMIT(program,
99 : : BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, BF_PROG_CTX_OFF(arg)));
100 [ + - ]: 12 : EMIT(program, BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_1,
101 : : offsetof(struct __sk_buff, mark)));
102 : :
103 : 12 : return bf_cmp_value(program, bf_matcher_get_op(matcher),
104 : : bf_matcher_payload(matcher), 4, BPF_REG_1);
105 : 36 : case BF_MATCHER_META_FLOW_HASH:
106 [ + - ]: 36 : EMIT(program,
107 : : BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, BF_PROG_CTX_OFF(arg)));
108 [ + - ]: 36 : EMIT(program, BPF_EMIT_CALL(BPF_FUNC_get_hash_recalc));
109 : :
110 [ + + ]: 36 : if (bf_matcher_get_op(matcher) == BF_MATCHER_RANGE) {
111 : 16 : uint32_t *hash = (uint32_t *)bf_matcher_payload(matcher);
112 : 16 : return bf_cmp_range(program, hash[0], hash[1], BPF_REG_0);
113 : : }
114 : :
115 : 20 : return bf_cmp_value(program, bf_matcher_get_op(matcher),
116 : : bf_matcher_payload(matcher), 4, BPF_REG_0);
117 : 503 : default:
118 : 503 : return bf_packet_gen_inline_matcher(program, matcher);
119 : : }
120 : : }
121 : :
122 : : /**
123 : : * @brief Generate bytecode to redirect a packet using TC.
124 : : *
125 : : * TC redirect supports both ingress and egress directions via the
126 : : * BPF_F_INGRESS flag passed to bpf_redirect().
127 : : *
128 : : * @param program Program to generate bytecode for. Can't be NULL.
129 : : * @param ifindex Target interface index.
130 : : * @param dir Direction: ingress or egress.
131 : : * @return 0 on success, or a negative errno value on failure.
132 : : */
133 : 6 : static int _bf_tc_gen_inline_redirect(struct bf_program *program,
134 : : uint32_t ifindex,
135 : : enum bf_redirect_dir dir)
136 : : {
137 : 6 : uint64_t flags = dir == BF_REDIRECT_INGRESS ? BPF_F_INGRESS : 0;
138 : :
139 : : assert(program);
140 : :
141 : : // bpf_redirect(ifindex, flags)
142 [ + - ]: 6 : EMIT(program, BPF_MOV64_IMM(BPF_REG_1, ifindex));
143 [ + - ]: 6 : EMIT(program, BPF_MOV64_IMM(BPF_REG_2, flags));
144 [ + - ]: 6 : EMIT(program, BPF_EMIT_CALL(BPF_FUNC_redirect));
145 : :
146 : : // Return value from bpf_redirect() is TC_ACT_REDIRECT on success
147 : 6 : EMIT(program, BPF_EXIT_INSN());
148 : :
149 : : return 0;
150 : : }
151 : :
152 : : /**
153 : : * Convert a standard verdict into a return value.
154 : : *
155 : : * @param verdict Verdict to convert. Must be valid.
156 : : * @param ret_code TC return code. Can't be NULL.
157 : : * @return 0 on success, or a negative errno value on failure.
158 : : */
159 : 1504 : static int _bf_tc_get_verdict(enum bf_verdict verdict, int *ret_code)
160 : : {
161 : : assert(ret_code);
162 : :
163 [ + + + - ]: 1504 : switch (verdict) {
164 : 1267 : case BF_VERDICT_ACCEPT:
165 : 1267 : *ret_code = TCX_PASS;
166 : 1267 : return 0;
167 : 227 : case BF_VERDICT_DROP:
168 : 227 : *ret_code = TCX_DROP;
169 : 227 : return 0;
170 : 10 : case BF_VERDICT_NEXT:
171 : 10 : *ret_code = TCX_NEXT;
172 : 10 : return 0;
173 : : default:
174 : : return -ENOTSUP;
175 : : }
176 : : }
177 : :
178 : : const struct bf_flavor_ops bf_flavor_ops_tc = {
179 : : .gen_inline_prologue = _bf_tc_gen_inline_prologue,
180 : : .gen_inline_epilogue = _bf_tc_gen_inline_epilogue,
181 : : .gen_inline_set_mark = _bf_tc_gen_inline_set_mark,
182 : : .gen_inline_redirect = _bf_tc_gen_inline_redirect,
183 : : .get_verdict = _bf_tc_get_verdict,
184 : : .gen_inline_matcher = _bf_tc_gen_inline_matcher,
185 : : .gen_inline_log = bf_packet_gen_inline_log,
186 : : };
|