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