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