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