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