Line data Source code
1 : /* SPDX-License-Identifier: GPL-2.0-only */
2 : /*
3 : * Copyright (c) 2022 Meta Platforms, Inc. and affiliates.
4 : */
5 :
6 : #include "bpfilter/cgen/matcher/ip6.h"
7 :
8 : #include <linux/bpf.h>
9 : #include <linux/bpf_common.h>
10 : #include <linux/if_ether.h>
11 : #include <linux/ipv6.h>
12 :
13 : #include <endian.h>
14 : #include <errno.h>
15 : #include <stddef.h>
16 : #include <stdint.h>
17 :
18 : #include "bpfilter/cgen/jmp.h"
19 : #include "bpfilter/cgen/program.h"
20 : #include "core/logger.h"
21 : #include "core/matcher.h"
22 :
23 : #include "external/filter.h"
24 :
25 : #define _bf_make32(a, b, c, d) \
26 : (((uint32_t)(a) << 24) | ((uint32_t)(b) << 16) | ((uint32_t)(c) << 8) | \
27 : (uint32_t)(d))
28 : #define _BF_MASK_LAST_BYTE 15
29 : #define BF_IPV6_EH_HOPOPTS(x) ((x) << 0)
30 : #define BF_IPV6_EH_ROUTING(x) ((x) << 1)
31 : #define BF_IPV6_EH_FRAGMENT(x) ((x) << 2)
32 : #define BF_IPV6_EH_AH(x) ((x) << 3)
33 : #define BF_IPV6_EH_DSTOPTS(x) ((x) << 4)
34 : #define BF_IPV6_EH_MH(x) ((x) << 5)
35 :
36 0 : static int _bf_matcher_generate_ip6_addr(struct bf_program *program,
37 : const struct bf_matcher *matcher)
38 : {
39 : struct bf_jmpctx j0, j1;
40 : uint8_t *addr = (uint8_t *)&matcher->payload;
41 0 : size_t offset = matcher->type == BF_MATCHER_IP6_SADDR ?
42 0 : offsetof(struct ipv6hdr, saddr) :
43 : offsetof(struct ipv6hdr, daddr);
44 :
45 0 : EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_6, offset));
46 0 : EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_6, offset + 8));
47 :
48 0 : if (matcher->op == BF_MATCHER_EQ) {
49 : /* If we want to match an IP, both addr[0] and addr[1]
50 : * must match the packet, otherwise we jump to the next rule. */
51 0 : EMIT(program, BPF_MOV32_IMM(BPF_REG_3, _bf_make32(addr[7], addr[6],
52 : addr[5], addr[4])));
53 0 : EMIT(program, BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 32));
54 0 : EMIT(program, BPF_MOV32_IMM(BPF_REG_4, _bf_make32(addr[3], addr[2],
55 : addr[1], addr[0])));
56 0 : EMIT(program, BPF_ALU64_REG(BPF_OR, BPF_REG_3, BPF_REG_4));
57 0 : EMIT_FIXUP_JMP_NEXT_RULE(program,
58 : BPF_JMP_REG(BPF_JNE, BPF_REG_1, BPF_REG_3, 0));
59 :
60 0 : EMIT(program, BPF_MOV32_IMM(BPF_REG_3, _bf_make32(addr[15], addr[14],
61 : addr[13], addr[12])));
62 0 : EMIT(program, BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 32));
63 0 : EMIT(program, BPF_MOV32_IMM(BPF_REG_4, _bf_make32(addr[11], addr[10],
64 : addr[9], addr[8])));
65 0 : EMIT(program, BPF_ALU64_REG(BPF_OR, BPF_REG_3, BPF_REG_4));
66 0 : EMIT_FIXUP_JMP_NEXT_RULE(program,
67 : BPF_JMP_REG(BPF_JNE, BPF_REG_2, BPF_REG_3, 0));
68 : } else {
69 : /* If we want to *not* match an IP, none of addr[0] and
70 : * addr[1] should match the packet, otherwise we jump to the
71 : * next rule. */
72 0 : EMIT(program, BPF_MOV32_IMM(BPF_REG_3, _bf_make32(addr[7], addr[6],
73 : addr[5], addr[4])));
74 0 : EMIT(program, BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 32));
75 0 : EMIT(program, BPF_MOV32_IMM(BPF_REG_4, _bf_make32(addr[3], addr[2],
76 : addr[1], addr[0])));
77 0 : EMIT(program, BPF_ALU64_REG(BPF_OR, BPF_REG_3, BPF_REG_4));
78 :
79 : /* Branching:
80 : * - addr[0] matches the address' 64 MSB: continue to compare
81 : * the address' 64 LSB.
82 : * - addr[0] doesn't matches the address' 64 MSB: jump to the
83 : * end of the matcher to continue the processing of the current rule.
84 : * This matcher matched. */
85 0 : j0 = bf_jmpctx_get(program,
86 : BPF_JMP_REG(BPF_JNE, BPF_REG_1, BPF_REG_3, 0));
87 :
88 0 : EMIT(program, BPF_MOV32_IMM(BPF_REG_3, _bf_make32(addr[15], addr[14],
89 : addr[13], addr[12])));
90 0 : EMIT(program, BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 32));
91 0 : EMIT(program, BPF_MOV32_IMM(BPF_REG_4, _bf_make32(addr[11], addr[10],
92 : addr[9], addr[8])));
93 0 : EMIT(program, BPF_ALU64_REG(BPF_OR, BPF_REG_3, BPF_REG_4));
94 :
95 : /* Branching:
96 : * - addr[1] matches the address' 64 LSB: addr matches the
97 : * packet's address, meaning the matcher doesn't match. Jump to the
98 : * next rule.
99 : * - addr[1] doesn't matches the address' 64 LSB: the matcher
100 : * matched: addr is not equal to the packet's address. Continue
101 : * processing with the next matcher. */
102 0 : j1 = bf_jmpctx_get(program,
103 : BPF_JMP_REG(BPF_JNE, BPF_REG_2, BPF_REG_3, 0));
104 :
105 0 : EMIT_FIXUP_JMP_NEXT_RULE(program, BPF_JMP_A(0));
106 :
107 : // j0 and j1 should jump here if they can't match the packet's IP.
108 0 : bf_jmpctx_cleanup(&j0);
109 0 : bf_jmpctx_cleanup(&j1);
110 : }
111 :
112 : return 0;
113 : }
114 :
115 0 : static void _bf_ip6_prefix_to_mask(uint32_t prefixlen, uint8_t *mask)
116 : {
117 0 : bf_assert(mask);
118 :
119 0 : memset(mask, 0x00, 16);
120 :
121 0 : memset(mask, 0xff, prefixlen / 8);
122 0 : if (prefixlen % 8)
123 0 : mask[prefixlen / 8] = 0xff << (8 - prefixlen % 8) & 0xff;
124 0 : }
125 :
126 0 : static int _bf_matcher_generate_ip6_net(struct bf_program *program,
127 : const struct bf_matcher *matcher)
128 : {
129 : uint8_t mask[16];
130 : struct bf_jmpctx j0, j1;
131 : struct bf_ip6_lpm_key *addr = (void *)&matcher->payload;
132 0 : size_t offset = matcher->type == BF_MATCHER_IP6_SNET ?
133 0 : offsetof(struct ipv6hdr, saddr) :
134 : offsetof(struct ipv6hdr, daddr);
135 :
136 0 : EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_6, offset));
137 0 : EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_6, offset + 8));
138 :
139 0 : _bf_ip6_prefix_to_mask(addr->prefixlen, mask);
140 :
141 0 : if (mask[_BF_MASK_LAST_BYTE] != (uint8_t)~0) {
142 0 : EMIT(program, BPF_MOV32_IMM(BPF_REG_3, _bf_make32(mask[7], mask[6],
143 : mask[5], mask[4])));
144 0 : EMIT(program, BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 32));
145 0 : EMIT(program, BPF_MOV32_IMM(BPF_REG_4, _bf_make32(mask[3], mask[2],
146 : mask[1], mask[0])));
147 0 : EMIT(program, BPF_ALU64_REG(BPF_OR, BPF_REG_3, BPF_REG_4));
148 0 : EMIT(program, BPF_ALU64_REG(BPF_AND, BPF_REG_1, BPF_REG_3));
149 :
150 0 : EMIT(program, BPF_MOV32_IMM(BPF_REG_3, _bf_make32(mask[15], mask[14],
151 : mask[13], mask[12])));
152 0 : EMIT(program, BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 32));
153 0 : EMIT(program, BPF_MOV32_IMM(BPF_REG_4, _bf_make32(mask[11], mask[10],
154 : mask[9], mask[8])));
155 0 : EMIT(program, BPF_ALU64_REG(BPF_OR, BPF_REG_3, BPF_REG_4));
156 0 : EMIT(program, BPF_ALU64_REG(BPF_AND, BPF_REG_2, BPF_REG_3));
157 : }
158 :
159 0 : if (matcher->op == BF_MATCHER_EQ) {
160 : /* If we want to match an IP, both addr->data[0] and addr->data[1]
161 : * must match the packet, otherwise we jump to the next rule. */
162 0 : EMIT(program,
163 : BPF_MOV32_IMM(BPF_REG_3, _bf_make32(addr->data[7] & mask[7],
164 : addr->data[6] & mask[6],
165 : addr->data[5] & mask[5],
166 : addr->data[4] & mask[4])));
167 0 : EMIT(program, BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 32));
168 0 : EMIT(program,
169 : BPF_MOV32_IMM(BPF_REG_4, _bf_make32(addr->data[3] & mask[3],
170 : addr->data[2] & mask[2],
171 : addr->data[1] & mask[1],
172 : addr->data[0] & mask[0])));
173 0 : EMIT(program, BPF_ALU64_REG(BPF_OR, BPF_REG_3, BPF_REG_4));
174 0 : EMIT_FIXUP_JMP_NEXT_RULE(program,
175 : BPF_JMP_REG(BPF_JNE, BPF_REG_1, BPF_REG_3, 0));
176 :
177 0 : EMIT(program,
178 : BPF_MOV32_IMM(BPF_REG_3, _bf_make32(addr->data[15] & mask[15],
179 : addr->data[14] & mask[14],
180 : addr->data[13] & mask[13],
181 : addr->data[12] & mask[12])));
182 0 : EMIT(program, BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 32));
183 0 : EMIT(program,
184 : BPF_MOV32_IMM(BPF_REG_4, _bf_make32(addr->data[11] & mask[11],
185 : addr->data[10] & mask[10],
186 : addr->data[9] & mask[9],
187 : addr->data[8] & mask[8])));
188 0 : EMIT(program, BPF_ALU64_REG(BPF_OR, BPF_REG_3, BPF_REG_4));
189 0 : EMIT_FIXUP_JMP_NEXT_RULE(program,
190 : BPF_JMP_REG(BPF_JNE, BPF_REG_2, BPF_REG_3, 0));
191 : } else {
192 : /* If we want to *not* match an IP, none of addr->data[0] and
193 : * addr->data[1] should match the packet, otherwise we jump to the
194 : * next rule. */
195 0 : EMIT(program,
196 : BPF_MOV32_IMM(BPF_REG_3, _bf_make32(addr->data[7] & mask[7],
197 : addr->data[6] & mask[6],
198 : addr->data[5] & mask[5],
199 : addr->data[4] & mask[4])));
200 0 : EMIT(program, BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 32));
201 0 : EMIT(program,
202 : BPF_MOV32_IMM(BPF_REG_4, _bf_make32(addr->data[3] & mask[3],
203 : addr->data[2] & mask[2],
204 : addr->data[1] & mask[1],
205 : addr->data[0] & mask[0])));
206 0 : EMIT(program, BPF_ALU64_REG(BPF_OR, BPF_REG_3, BPF_REG_4));
207 :
208 : /* Branching:
209 : * - addr->data[0] matches the address' 64 MSB: continue to compare
210 : * the address' 64 LSB.
211 : * - addr->data[0] doesn't matches the address' 64 MSB: jump to the
212 : * end of the matcher to continue the processing of the current rule.
213 : * This matcher matched. */
214 0 : j0 = bf_jmpctx_get(program,
215 : BPF_JMP_REG(BPF_JNE, BPF_REG_1, BPF_REG_3, 0));
216 :
217 0 : EMIT(program,
218 : BPF_MOV32_IMM(BPF_REG_3, _bf_make32(addr->data[15] & mask[15],
219 : addr->data[14] & mask[14],
220 : addr->data[13] & mask[13],
221 : addr->data[12] & mask[12])));
222 0 : EMIT(program, BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 32));
223 0 : EMIT(program,
224 : BPF_MOV32_IMM(BPF_REG_4, _bf_make32(addr->data[11] & mask[11],
225 : addr->data[10] & mask[10],
226 : addr->data[9] & mask[9],
227 : addr->data[8] & mask[8])));
228 0 : EMIT(program, BPF_ALU64_REG(BPF_OR, BPF_REG_3, BPF_REG_4));
229 :
230 : /* Branching:
231 : * - addr->data[1] matches the address' 64 LSB: addr->data matches the
232 : * packet's address, meaning the matcher doesn't match. Jump to the
233 : * next rule.
234 : * - addr->data[1] doesn't matches the address' 64 LSB: the matcher
235 : * matched: addr->data is not equal to the packet's address. Continue
236 : * processing with the next matcher. */
237 0 : j1 = bf_jmpctx_get(program,
238 : BPF_JMP_REG(BPF_JNE, BPF_REG_2, BPF_REG_3, 0));
239 :
240 0 : EMIT_FIXUP_JMP_NEXT_RULE(program, BPF_JMP_A(0));
241 :
242 : // j0 and j1 should jump here if they can't match the packet's IP.
243 0 : bf_jmpctx_cleanup(&j0);
244 0 : bf_jmpctx_cleanup(&j1);
245 : }
246 :
247 : return 0;
248 : }
249 :
250 0 : static int _bf_matcher_generate_ip6_nexthdr(struct bf_program *program,
251 : const struct bf_matcher *matcher)
252 : {
253 0 : const uint8_t ehdr = matcher->payload[0];
254 : uint8_t eh_mask;
255 :
256 0 : if ((matcher->op != BF_MATCHER_EQ) && (matcher->op != BF_MATCHER_NE))
257 : return -EINVAL;
258 :
259 0 : switch (ehdr) {
260 0 : case IPPROTO_HOPOPTS:
261 : case IPPROTO_ROUTING:
262 : case IPPROTO_DSTOPTS:
263 : case IPPROTO_FRAGMENT:
264 : case IPPROTO_AH:
265 : case IPPROTO_MH:
266 0 : eh_mask = (BF_IPV6_EH_HOPOPTS(ehdr == IPPROTO_HOPOPTS) |
267 0 : BF_IPV6_EH_ROUTING(ehdr == IPPROTO_ROUTING) |
268 0 : BF_IPV6_EH_FRAGMENT(ehdr == IPPROTO_FRAGMENT) |
269 0 : BF_IPV6_EH_AH(ehdr == IPPROTO_AH) |
270 0 : BF_IPV6_EH_DSTOPTS(ehdr == IPPROTO_DSTOPTS) |
271 : BF_IPV6_EH_MH(ehdr == IPPROTO_MH));
272 0 : EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10,
273 : BF_PROG_CTX_OFF(ipv6_eh)));
274 0 : EMIT(program, BPF_ALU64_IMM(BPF_AND, BPF_REG_1, eh_mask));
275 0 : EMIT_FIXUP_JMP_NEXT_RULE(
276 : program,
277 : BPF_JMP_IMM((matcher->op == BF_MATCHER_EQ) ? BPF_JEQ : BPF_JNE,
278 : BPF_REG_1, 0, 0));
279 0 : break;
280 0 : default:
281 : /* check l4 protocols using BPF_REG_8 */
282 0 : EMIT_FIXUP_JMP_NEXT_RULE(
283 : program,
284 : BPF_JMP_IMM((matcher->op == BF_MATCHER_EQ) ? BPF_JNE : BPF_JEQ,
285 : BPF_REG_8, ehdr, 0));
286 0 : break;
287 : }
288 :
289 : return 0;
290 : }
291 :
292 0 : int bf_matcher_generate_ip6(struct bf_program *program,
293 : const struct bf_matcher *matcher)
294 : {
295 : int r;
296 :
297 0 : EMIT_FIXUP_JMP_NEXT_RULE(
298 : program, BPF_JMP_IMM(BPF_JNE, BPF_REG_7, htobe16(ETH_P_IPV6), 0));
299 :
300 0 : EMIT(program,
301 : BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_10, BF_PROG_CTX_OFF(l3_hdr)));
302 :
303 0 : switch (matcher->type) {
304 0 : case BF_MATCHER_IP6_SADDR:
305 : case BF_MATCHER_IP6_DADDR:
306 0 : r = _bf_matcher_generate_ip6_addr(program, matcher);
307 0 : break;
308 0 : case BF_MATCHER_IP6_SNET:
309 : case BF_MATCHER_IP6_DNET:
310 0 : r = _bf_matcher_generate_ip6_net(program, matcher);
311 0 : break;
312 0 : case BF_MATCHER_IP6_NEXTHDR:
313 0 : r = _bf_matcher_generate_ip6_nexthdr(program, matcher);
314 0 : break;
315 0 : default:
316 0 : return bf_err_r(-EINVAL, "unknown matcher type %d", matcher->type);
317 : };
318 :
319 : return r;
320 : }
|