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/nf.h"
7 : :
8 : : #include <linux/bpf.h>
9 : : #include <linux/bpf_common.h>
10 : : #include <linux/if_ether.h>
11 : : #include <linux/netfilter.h>
12 : :
13 : : #include <stdbool.h>
14 : : #include <stddef.h>
15 : : #include <stdint.h>
16 : : #include <sys/socket.h>
17 : :
18 : : #include <bpfilter/btf.h>
19 : : #include <bpfilter/flavor.h>
20 : : #include <bpfilter/helper.h>
21 : : #include <bpfilter/hook.h>
22 : : #include <bpfilter/verdict.h>
23 : :
24 : : #include "cgen/jmp.h"
25 : : #include "cgen/program.h"
26 : : #include "cgen/stub.h"
27 : : #include "cgen/swich.h"
28 : : #include "filter.h"
29 : :
30 : : #define BF_NF_PRIO_EVEN 2
31 : : #define BF_NF_PRIO_ODD 1
32 : :
33 : : // Forward definition to avoid headers clusterfuck.
34 : : uint16_t htons(uint16_t hostshort);
35 : :
36 : : static inline bool _bf_nf_hook_is_ingress(enum bf_hook hook)
37 : : {
38 : 13 : return hook == BF_HOOK_NF_PRE_ROUTING || hook == BF_HOOK_NF_LOCAL_IN ||
39 : : hook == BF_HOOK_NF_FORWARD;
40 : : }
41 : :
42 : 13 : static int _bf_nf_gen_inline_prologue(struct bf_program *program)
43 : : {
44 : : int r;
45 : : int offset;
46 : :
47 : : bf_assert(program);
48 : :
49 : : // Copy the ifindex from to bpf_nf_ctx.state.{in,out}.ifindex the runtime context
50 [ + - ]: 13 : if ((offset = bf_btf_get_field_off("bpf_nf_ctx", "state")) < 0)
51 : : return offset;
52 [ - + ]: 13 : EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, offset));
53 [ + + ]: 13 : if (_bf_nf_hook_is_ingress(program->runtime.chain->hook)) {
54 [ + - ]: 11 : if ((offset = bf_btf_get_field_off("nf_hook_state", "in")) < 0)
55 : : return offset;
56 [ - + ]: 11 : EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_2, offset));
57 : : } else {
58 [ + - ]: 2 : if ((offset = bf_btf_get_field_off("nf_hook_state", "out")) < 0)
59 : : return offset;
60 [ - + ]: 2 : EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_2, offset));
61 : : }
62 : :
63 [ + - ]: 13 : if ((offset = bf_btf_get_field_off("net_device", "ifindex")) < 0)
64 : : return offset;
65 [ - + ]: 13 : EMIT(program, BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_3, offset));
66 [ - + ]: 13 : EMIT(program,
67 : : BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_4, BF_PROG_CTX_OFF(ifindex)));
68 : :
69 : : /* BPF_PROG_TYPE_CGROUP_SKB doesn't provide access the the Ethernet header,
70 : : * so we can't parse it and discover the L3 protocol ID.
71 : : * Instead, we use the __sk_buff.family value and convert it to the
72 : : * corresponding ethertype. */
73 [ + - ]: 13 : if ((offset = bf_btf_get_field_off("nf_hook_state", "pf")) < 0)
74 : : return offset;
75 [ - + ]: 13 : EMIT(program, BPF_LDX_MEM(BPF_B, BPF_REG_3, BPF_REG_2, offset));
76 : :
77 : : {
78 : 13 : _clean_bf_swich_ struct bf_swich swich =
79 [ - + ]: 13 : bf_swich_get(program, BPF_REG_3);
80 : :
81 [ - + ]: 13 : EMIT_SWICH_OPTION(&swich, AF_INET,
82 : : BPF_MOV64_IMM(BPF_REG_7, htons(ETH_P_IP)));
83 [ - + ]: 13 : EMIT_SWICH_OPTION(&swich, AF_INET6,
84 : : BPF_MOV64_IMM(BPF_REG_7, htons(ETH_P_IPV6)));
85 [ - + ]: 13 : EMIT_SWICH_DEFAULT(&swich, BPF_MOV64_IMM(BPF_REG_7, 0));
86 : :
87 : 13 : r = bf_swich_generate(&swich);
88 [ + - ]: 13 : if (r)
89 : : return r;
90 : : }
91 : :
92 [ - + ]: 13 : EMIT(program, BPF_ST_MEM(BPF_W, BPF_REG_10, BF_PROG_CTX_OFF(l3_offset), 0));
93 : :
94 : : // Calculate the packet size (+ETH_HLEN) and store it into the runtime context
95 [ + - ]: 13 : if ((offset = bf_btf_get_field_off("bpf_nf_ctx", "skb")) < 0)
96 : : return offset;
97 [ - + ]: 13 : EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, offset));
98 [ + - ]: 13 : if ((offset = bf_btf_get_field_off("sk_buff", "len")) < 0)
99 : : return offset;
100 [ - + ]: 13 : EMIT(program, BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, offset));
101 [ - + ]: 13 : EMIT(program, BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, ETH_HLEN));
102 [ - + ]: 13 : EMIT(program,
103 : : BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_2, BF_PROG_CTX_OFF(pkt_size)));
104 : :
105 : 13 : r = bf_stub_make_ctx_skb_dynptr(program, BPF_REG_1);
106 [ + - ]: 13 : if (r)
107 : : return r;
108 : :
109 : 13 : r = bf_stub_parse_l3_hdr(program);
110 [ + - ]: 13 : if (r)
111 : : return r;
112 : :
113 : 13 : r = bf_stub_parse_l4_hdr(program);
114 : : if (r)
115 : : return r;
116 : :
117 : : return 0;
118 : : }
119 : :
120 : 13 : static int _bf_nf_gen_inline_epilogue(struct bf_program *program)
121 : : {
122 : : UNUSED(program);
123 : :
124 : 13 : return 0;
125 : : }
126 : :
127 : 10 : static int _bf_nf_gen_inline_get_mark(struct bf_program *program, int reg)
128 : : {
129 : : int offset;
130 : :
131 [ - + ]: 10 : EMIT(program,
132 : : BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, BF_PROG_CTX_OFF(arg)));
133 [ + - ]: 10 : if ((offset = bf_btf_get_field_off("bpf_nf_ctx", "skb")) < 0)
134 : : return offset;
135 [ - + ]: 10 : EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, offset));
136 [ + - ]: 10 : if ((offset = bf_btf_get_field_off("sk_buff", "mark")) < 0)
137 : : return offset;
138 [ - + ]: 10 : EMIT(program, BPF_LDX_MEM(BPF_W, reg, BPF_REG_2, offset));
139 : :
140 : 10 : return 0;
141 : : }
142 : :
143 : 0 : static int _bf_nf_gen_inline_get_skb(struct bf_program *program, int reg)
144 : : {
145 : : int offset;
146 : :
147 [ # # ]: 0 : EMIT(program, BPF_LDX_MEM(BPF_DW, reg, BPF_REG_10, BF_PROG_CTX_OFF(arg)));
148 : 0 : if ((offset = bf_btf_get_field_off("bpf_nf_ctx", "skb")) < 0)
149 : : return offset;
150 : :
151 : : return 0;
152 : : }
153 : :
154 : : /**
155 : : * Convert a standard verdict into a return value.
156 : : *
157 : : * @param verdict Verdict to convert. Must be valid.
158 : : * @return TC return code corresponding to the verdict, as an integer.
159 : : */
160 : 173 : static int _bf_nf_get_verdict(enum bf_verdict verdict)
161 : : {
162 : : bf_assert(0 <= verdict && verdict < _BF_TERMINAL_VERDICT_MAX);
163 : :
164 : : static const int verdicts[] = {
165 : : [BF_VERDICT_ACCEPT] = NF_ACCEPT,
166 : : [BF_VERDICT_DROP] = NF_DROP,
167 : : };
168 : :
169 : : static_assert(ARRAY_SIZE(verdicts) == _BF_TERMINAL_VERDICT_MAX);
170 : :
171 : 173 : return verdicts[verdict];
172 : : }
173 : :
174 : : const struct bf_flavor_ops bf_flavor_ops_nf = {
175 : : .gen_inline_prologue = _bf_nf_gen_inline_prologue,
176 : : .gen_inline_epilogue = _bf_nf_gen_inline_epilogue,
177 : : .gen_inline_get_mark = _bf_nf_gen_inline_get_mark,
178 : : .gen_inline_get_skb = _bf_nf_gen_inline_get_skb,
179 : : .get_verdict = _bf_nf_get_verdict,
180 : : };
|