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