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 : #include "core/set.h"
23 :
24 : #include "external/filter.h"
25 :
26 : #define _bf_make32(a, b, c, d) \
27 : (((uint32_t)(a) << 24) | ((uint32_t)(b) << 16) | ((uint32_t)(c) << 8) | \
28 : (uint32_t)(d))
29 : #define _BF_MASK_LAST_BYTE 15
30 : #define BF_IPV6_EH_HOPOPTS(x) ((x) << 0)
31 : #define BF_IPV6_EH_ROUTING(x) ((x) << 1)
32 : #define BF_IPV6_EH_FRAGMENT(x) ((x) << 2)
33 : #define BF_IPV6_EH_AH(x) ((x) << 3)
34 : #define BF_IPV6_EH_DSTOPTS(x) ((x) << 4)
35 : #define BF_IPV6_EH_MH(x) ((x) << 5)
36 :
37 0 : static int _bf_matcher_generate_ip6_addr(struct bf_program *program,
38 : const struct bf_matcher *matcher)
39 : {
40 : struct bf_jmpctx j0, j1;
41 : uint8_t *addr = (uint8_t *)&matcher->payload;
42 0 : size_t offset = matcher->type == BF_MATCHER_IP6_SADDR ?
43 0 : offsetof(struct ipv6hdr, saddr) :
44 : offsetof(struct ipv6hdr, daddr);
45 :
46 0 : EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_6, offset));
47 0 : EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_6, offset + 8));
48 :
49 0 : if (matcher->op == BF_MATCHER_EQ) {
50 : /* If we want to match an IP, both addr[0] and addr[1]
51 : * must match the packet, otherwise we jump to the next rule. */
52 0 : EMIT(program, BPF_MOV32_IMM(BPF_REG_3, _bf_make32(addr[7], addr[6],
53 : addr[5], addr[4])));
54 0 : EMIT(program, BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 32));
55 0 : EMIT(program, BPF_MOV32_IMM(BPF_REG_4, _bf_make32(addr[3], addr[2],
56 : addr[1], addr[0])));
57 0 : EMIT(program, BPF_ALU64_REG(BPF_OR, BPF_REG_3, BPF_REG_4));
58 0 : EMIT_FIXUP_JMP_NEXT_RULE(program,
59 : BPF_JMP_REG(BPF_JNE, BPF_REG_1, BPF_REG_3, 0));
60 :
61 0 : EMIT(program, BPF_MOV32_IMM(BPF_REG_3, _bf_make32(addr[15], addr[14],
62 : addr[13], addr[12])));
63 0 : EMIT(program, BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 32));
64 0 : EMIT(program, BPF_MOV32_IMM(BPF_REG_4, _bf_make32(addr[11], addr[10],
65 : addr[9], addr[8])));
66 0 : EMIT(program, BPF_ALU64_REG(BPF_OR, BPF_REG_3, BPF_REG_4));
67 0 : EMIT_FIXUP_JMP_NEXT_RULE(program,
68 : BPF_JMP_REG(BPF_JNE, BPF_REG_2, BPF_REG_3, 0));
69 : } else {
70 : /* If we want to *not* match an IP, none of addr[0] and
71 : * addr[1] should match the packet, otherwise we jump to the
72 : * next rule. */
73 0 : EMIT(program, BPF_MOV32_IMM(BPF_REG_3, _bf_make32(addr[7], addr[6],
74 : addr[5], addr[4])));
75 0 : EMIT(program, BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 32));
76 0 : EMIT(program, BPF_MOV32_IMM(BPF_REG_4, _bf_make32(addr[3], addr[2],
77 : addr[1], addr[0])));
78 0 : EMIT(program, BPF_ALU64_REG(BPF_OR, BPF_REG_3, BPF_REG_4));
79 :
80 : /* Branching:
81 : * - addr[0] matches the address' 64 MSB: continue to compare
82 : * the address' 64 LSB.
83 : * - addr[0] doesn't matches the address' 64 MSB: jump to the
84 : * end of the matcher to continue the processing of the current rule.
85 : * This matcher matched. */
86 0 : j0 = bf_jmpctx_get(program,
87 : BPF_JMP_REG(BPF_JNE, BPF_REG_1, BPF_REG_3, 0));
88 :
89 0 : EMIT(program, BPF_MOV32_IMM(BPF_REG_3, _bf_make32(addr[15], addr[14],
90 : addr[13], addr[12])));
91 0 : EMIT(program, BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 32));
92 0 : EMIT(program, BPF_MOV32_IMM(BPF_REG_4, _bf_make32(addr[11], addr[10],
93 : addr[9], addr[8])));
94 0 : EMIT(program, BPF_ALU64_REG(BPF_OR, BPF_REG_3, BPF_REG_4));
95 :
96 : /* Branching:
97 : * - addr[1] matches the address' 64 LSB: addr matches the
98 : * packet's address, meaning the matcher doesn't match. Jump to the
99 : * next rule.
100 : * - addr[1] doesn't matches the address' 64 LSB: the matcher
101 : * matched: addr is not equal to the packet's address. Continue
102 : * processing with the next matcher. */
103 0 : j1 = bf_jmpctx_get(program,
104 : BPF_JMP_REG(BPF_JNE, BPF_REG_2, BPF_REG_3, 0));
105 :
106 0 : EMIT_FIXUP_JMP_NEXT_RULE(program, BPF_JMP_A(0));
107 :
108 : // j0 and j1 should jump here if they can't match the packet's IP.
109 0 : bf_jmpctx_cleanup(&j0);
110 0 : bf_jmpctx_cleanup(&j1);
111 : }
112 :
113 : return 0;
114 : }
115 :
116 0 : static int _bf_matcher_generate_ip6_net_single(struct bf_program *program,
117 : const struct bf_matcher *matcher)
118 : {
119 : struct bf_jmpctx j0, j1;
120 : struct bf_matcher_ip6_addr *addr = (void *)&matcher->payload;
121 0 : size_t offset = matcher->type == BF_MATCHER_IP6_SNET ?
122 0 : offsetof(struct ipv6hdr, saddr) :
123 : offsetof(struct ipv6hdr, daddr);
124 :
125 0 : EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_6, offset));
126 0 : EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_6, offset + 8));
127 :
128 0 : if (addr->mask[_BF_MASK_LAST_BYTE] != (uint8_t)~0) {
129 0 : EMIT(program, BPF_MOV32_IMM(BPF_REG_3,
130 : _bf_make32(addr->mask[7], addr->mask[6],
131 : addr->mask[5], addr->mask[4])));
132 0 : EMIT(program, BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 32));
133 0 : EMIT(program, BPF_MOV32_IMM(BPF_REG_4,
134 : _bf_make32(addr->mask[3], addr->mask[2],
135 : addr->mask[1], addr->mask[0])));
136 0 : EMIT(program, BPF_ALU64_REG(BPF_OR, BPF_REG_3, BPF_REG_4));
137 0 : EMIT(program, BPF_ALU64_REG(BPF_AND, BPF_REG_1, BPF_REG_3));
138 :
139 0 : EMIT(program,
140 : BPF_MOV32_IMM(BPF_REG_3,
141 : _bf_make32(addr->mask[15], addr->mask[14],
142 : addr->mask[13], addr->mask[12])));
143 0 : EMIT(program, BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 32));
144 0 : EMIT(program, BPF_MOV32_IMM(BPF_REG_4,
145 : _bf_make32(addr->mask[11], addr->mask[10],
146 : addr->mask[9], addr->mask[8])));
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_2, BPF_REG_3));
149 : }
150 :
151 0 : if (matcher->op == BF_MATCHER_EQ) {
152 : /* If we want to match an IP, both addr->addr[0] and addr->addr[1]
153 : * must match the packet, otherwise we jump to the next rule. */
154 0 : EMIT(program, BPF_MOV32_IMM(BPF_REG_3,
155 : _bf_make32(addr->addr[7] & addr->mask[7],
156 : addr->addr[6] & addr->mask[6],
157 : addr->addr[5] & addr->mask[5],
158 : addr->addr[4] & addr->mask[4])));
159 0 : EMIT(program, BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 32));
160 0 : EMIT(program, BPF_MOV32_IMM(BPF_REG_4,
161 : _bf_make32(addr->addr[3] & addr->mask[3],
162 : addr->addr[2] & addr->mask[2],
163 : addr->addr[1] & addr->mask[1],
164 : addr->addr[0] & addr->mask[0])));
165 0 : EMIT(program, BPF_ALU64_REG(BPF_OR, BPF_REG_3, BPF_REG_4));
166 0 : EMIT_FIXUP_JMP_NEXT_RULE(program,
167 : BPF_JMP_REG(BPF_JNE, BPF_REG_1, BPF_REG_3, 0));
168 :
169 0 : EMIT(program,
170 : BPF_MOV32_IMM(BPF_REG_3,
171 : _bf_make32(addr->addr[15] & addr->mask[15],
172 : addr->addr[14] & addr->mask[14],
173 : addr->addr[13] & addr->mask[13],
174 : addr->addr[12] & addr->mask[12])));
175 0 : EMIT(program, BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 32));
176 0 : EMIT(program, BPF_MOV32_IMM(BPF_REG_4,
177 : _bf_make32(addr->addr[11] & addr->mask[11],
178 : addr->addr[10] & addr->mask[10],
179 : addr->addr[9] & addr->mask[9],
180 : addr->addr[8] & addr->mask[8])));
181 0 : EMIT(program, BPF_ALU64_REG(BPF_OR, BPF_REG_3, BPF_REG_4));
182 0 : EMIT_FIXUP_JMP_NEXT_RULE(program,
183 : BPF_JMP_REG(BPF_JNE, BPF_REG_2, BPF_REG_3, 0));
184 : } else {
185 : /* If we want to *not* match an IP, none of addr->addr[0] and
186 : * addr->addr[1] should match the packet, otherwise we jump to the
187 : * next rule. */
188 0 : EMIT(program, BPF_MOV32_IMM(BPF_REG_3,
189 : _bf_make32(addr->addr[7] & addr->mask[7],
190 : addr->addr[6] & addr->mask[6],
191 : addr->addr[5] & addr->mask[5],
192 : addr->addr[4] & addr->mask[4])));
193 0 : EMIT(program, BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 32));
194 0 : EMIT(program, BPF_MOV32_IMM(BPF_REG_4,
195 : _bf_make32(addr->addr[3] & addr->mask[3],
196 : addr->addr[2] & addr->mask[2],
197 : addr->addr[1] & addr->mask[1],
198 : addr->addr[0] & addr->mask[0])));
199 0 : EMIT(program, BPF_ALU64_REG(BPF_OR, BPF_REG_3, BPF_REG_4));
200 :
201 : /* Branching:
202 : * - addr->addr[0] matches the address' 64 MSB: continue to compare
203 : * the address' 64 LSB.
204 : * - addr->addr[0] doesn't matches the address' 64 MSB: jump to the
205 : * end of the matcher to continue the processing of the current rule.
206 : * This matcher matched. */
207 0 : j0 = bf_jmpctx_get(program,
208 : BPF_JMP_REG(BPF_JNE, BPF_REG_1, BPF_REG_3, 0));
209 :
210 0 : EMIT(program,
211 : BPF_MOV32_IMM(BPF_REG_3,
212 : _bf_make32(addr->addr[15] & addr->mask[15],
213 : addr->addr[14] & addr->mask[14],
214 : addr->addr[13] & addr->mask[13],
215 : addr->addr[12] & addr->mask[12])));
216 0 : EMIT(program, BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 32));
217 0 : EMIT(program, BPF_MOV32_IMM(BPF_REG_4,
218 : _bf_make32(addr->addr[11] & addr->mask[11],
219 : addr->addr[10] & addr->mask[10],
220 : addr->addr[9] & addr->mask[9],
221 : addr->addr[8] & addr->mask[8])));
222 0 : EMIT(program, BPF_ALU64_REG(BPF_OR, BPF_REG_3, BPF_REG_4));
223 :
224 : /* Branching:
225 : * - addr->addr[1] matches the address' 64 LSB: addr->addr matches the
226 : * packet's address, meaning the matcher doesn't match. Jump to the
227 : * next rule.
228 : * - addr->addr[1] doesn't matches the address' 64 LSB: the matcher
229 : * matched: addr->addr is not equal to the packet's address. Continue
230 : * processing with the next matcher. */
231 0 : j1 = bf_jmpctx_get(program,
232 : BPF_JMP_REG(BPF_JNE, BPF_REG_2, BPF_REG_3, 0));
233 :
234 0 : EMIT_FIXUP_JMP_NEXT_RULE(program, BPF_JMP_A(0));
235 :
236 : // j0 and j1 should jump here if they can't match the packet's IP.
237 0 : bf_jmpctx_cleanup(&j0);
238 0 : bf_jmpctx_cleanup(&j1);
239 : }
240 :
241 : return 0;
242 : }
243 :
244 0 : static int _bf_matcher_generate_ip6_net_in(struct bf_program *program,
245 : const struct bf_matcher *matcher)
246 : {
247 : uint32_t set_id;
248 : struct bf_set *set;
249 0 : int16_t offset = matcher->type == BF_MATCHER_IP6_SNET ?
250 : offsetof(struct ipv6hdr, saddr) :
251 : offsetof(struct ipv6hdr, daddr);
252 :
253 0 : bf_assert(program && matcher);
254 :
255 0 : set_id = *(uint32_t *)matcher->payload;
256 0 : set = bf_list_get_at(&program->runtime.chain->sets, set_id);
257 0 : if (!set)
258 0 : return bf_err_r(-ENOENT, "set #%d not found", set_id);
259 :
260 : // Copy bf_ip6_lpm_key entries starting at scratch offset 4, so the
261 : // 64-bits copies for the address will be aligned
262 0 : EMIT(program, BPF_MOV64_IMM(BPF_REG_3, 128));
263 0 : EMIT(program,
264 : BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_3, BF_PROG_SCR_OFF(4)));
265 :
266 0 : EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_6, offset));
267 0 : EMIT(program,
268 : BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_2, BF_PROG_SCR_OFF(8)));
269 0 : EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_6, offset + 8));
270 0 : EMIT(program,
271 : BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_2, BF_PROG_SCR_OFF(16)));
272 :
273 0 : EMIT_LOAD_SET_FD_FIXUP(program, BPF_REG_1, set_id);
274 0 : EMIT(program, BPF_MOV64_REG(BPF_REG_2, BPF_REG_10));
275 0 : EMIT(program, BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, BF_PROG_SCR_OFF(4)));
276 :
277 0 : EMIT(program, BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem));
278 :
279 : // Jump to the next rule if map_lookup_elem returned 0
280 0 : EMIT_FIXUP_JMP_NEXT_RULE(program, BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 0));
281 :
282 0 : return 0;
283 : }
284 :
285 0 : static int _bf_matcher_generate_ip6_nexthdr(struct bf_program *program,
286 : const struct bf_matcher *matcher)
287 : {
288 0 : const uint8_t ehdr = matcher->payload[0];
289 : uint8_t eh_mask;
290 :
291 0 : if ((matcher->op != BF_MATCHER_EQ) && (matcher->op != BF_MATCHER_NE))
292 : return -EINVAL;
293 :
294 0 : switch (ehdr) {
295 0 : case IPPROTO_HOPOPTS:
296 : case IPPROTO_ROUTING:
297 : case IPPROTO_DSTOPTS:
298 : case IPPROTO_FRAGMENT:
299 : case IPPROTO_AH:
300 : case IPPROTO_MH:
301 0 : eh_mask = (BF_IPV6_EH_HOPOPTS(ehdr == IPPROTO_HOPOPTS) |
302 0 : BF_IPV6_EH_ROUTING(ehdr == IPPROTO_ROUTING) |
303 0 : BF_IPV6_EH_FRAGMENT(ehdr == IPPROTO_FRAGMENT) |
304 0 : BF_IPV6_EH_AH(ehdr == IPPROTO_AH) |
305 0 : BF_IPV6_EH_DSTOPTS(ehdr == IPPROTO_DSTOPTS) |
306 : BF_IPV6_EH_MH(ehdr == IPPROTO_MH));
307 0 : EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10,
308 : BF_PROG_CTX_OFF(ipv6_eh)));
309 0 : EMIT(program, BPF_ALU64_IMM(BPF_AND, BPF_REG_1, eh_mask));
310 0 : EMIT_FIXUP_JMP_NEXT_RULE(
311 : program,
312 : BPF_JMP_IMM((matcher->op == BF_MATCHER_EQ) ? BPF_JEQ : BPF_JNE,
313 : BPF_REG_1, 0, 0));
314 0 : break;
315 0 : default:
316 : /* check l4 protocols using BPF_REG_8 */
317 0 : EMIT_FIXUP_JMP_NEXT_RULE(
318 : program,
319 : BPF_JMP_IMM((matcher->op == BF_MATCHER_EQ) ? BPF_JNE : BPF_JEQ,
320 : BPF_REG_8, ehdr, 0));
321 0 : break;
322 : }
323 :
324 : return 0;
325 : }
326 :
327 0 : static int _bf_matcher_generate_ip6_net(struct bf_program *program,
328 : const struct bf_matcher *matcher)
329 : {
330 0 : bf_assert(program && matcher);
331 :
332 : int r;
333 :
334 0 : switch (matcher->op) {
335 0 : case BF_MATCHER_EQ:
336 : case BF_MATCHER_NE:
337 0 : r = _bf_matcher_generate_ip6_net_single(program, matcher);
338 0 : break;
339 0 : case BF_MATCHER_IN:
340 0 : r = _bf_matcher_generate_ip6_net_in(program, matcher);
341 0 : break;
342 0 : default:
343 0 : return bf_err_r(-ENOTSUP, "unsupported operator %s for matcher %s",
344 : bf_matcher_type_to_str(matcher->type),
345 : bf_matcher_op_to_str(matcher->op));
346 : }
347 :
348 : return r;
349 : }
350 :
351 0 : int bf_matcher_generate_ip6(struct bf_program *program,
352 : const struct bf_matcher *matcher)
353 : {
354 : int r;
355 :
356 0 : EMIT_FIXUP_JMP_NEXT_RULE(
357 : program, BPF_JMP_IMM(BPF_JNE, BPF_REG_7, htobe16(ETH_P_IPV6), 0));
358 :
359 0 : EMIT(program,
360 : BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_10, BF_PROG_CTX_OFF(l3_hdr)));
361 :
362 0 : switch (matcher->type) {
363 0 : case BF_MATCHER_IP6_SADDR:
364 : case BF_MATCHER_IP6_DADDR:
365 0 : r = _bf_matcher_generate_ip6_addr(program, matcher);
366 0 : break;
367 0 : case BF_MATCHER_IP6_SNET:
368 : case BF_MATCHER_IP6_DNET:
369 0 : r = _bf_matcher_generate_ip6_net(program, matcher);
370 0 : break;
371 0 : case BF_MATCHER_IP6_NEXTHDR:
372 0 : r = _bf_matcher_generate_ip6_nexthdr(program, matcher);
373 0 : break;
374 0 : default:
375 0 : return bf_err_r(-EINVAL, "unknown matcher type %d", matcher->type);
376 : };
377 :
378 : return r;
379 : }
|