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 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 0 : uint8_t *addr = (uint8_t *)bf_matcher_payload(matcher);
42 0 : size_t offset = bf_matcher_get_type(matcher) == 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 (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 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 void _bf_ip6_prefix_to_mask(uint32_t prefixlen, uint8_t *mask)
117 : {
118 0 : bf_assert(mask);
119 :
120 0 : memset(mask, 0x00, 16);
121 :
122 0 : memset(mask, 0xff, prefixlen / 8);
123 0 : if (prefixlen % 8)
124 0 : mask[prefixlen / 8] = 0xff << (8 - prefixlen % 8) & 0xff;
125 0 : }
126 :
127 0 : 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 0 : const struct bf_ip6_lpm_key *addr = bf_matcher_payload(matcher);
133 0 : size_t offset = bf_matcher_get_type(matcher) == BF_MATCHER_IP6_SNET ?
134 0 : offsetof(struct ipv6hdr, saddr) :
135 : offsetof(struct ipv6hdr, daddr);
136 :
137 0 : EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_6, offset));
138 0 : EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_6, offset + 8));
139 :
140 0 : _bf_ip6_prefix_to_mask(addr->prefixlen, mask);
141 :
142 0 : if (mask[_BF_MASK_LAST_BYTE] != (uint8_t)~0) {
143 0 : EMIT(program, BPF_MOV32_IMM(BPF_REG_3, _bf_make32(mask[7], mask[6],
144 : mask[5], mask[4])));
145 0 : EMIT(program, BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 32));
146 0 : EMIT(program, BPF_MOV32_IMM(BPF_REG_4, _bf_make32(mask[3], mask[2],
147 : mask[1], mask[0])));
148 0 : EMIT(program, BPF_ALU64_REG(BPF_OR, BPF_REG_3, BPF_REG_4));
149 0 : EMIT(program, BPF_ALU64_REG(BPF_AND, BPF_REG_1, BPF_REG_3));
150 :
151 0 : EMIT(program, BPF_MOV32_IMM(BPF_REG_3, _bf_make32(mask[15], mask[14],
152 : mask[13], mask[12])));
153 0 : EMIT(program, BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 32));
154 0 : EMIT(program, BPF_MOV32_IMM(BPF_REG_4, _bf_make32(mask[11], mask[10],
155 : mask[9], mask[8])));
156 0 : EMIT(program, BPF_ALU64_REG(BPF_OR, BPF_REG_3, BPF_REG_4));
157 0 : EMIT(program, BPF_ALU64_REG(BPF_AND, BPF_REG_2, BPF_REG_3));
158 : }
159 :
160 0 : 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 0 : 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 0 : EMIT(program, BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 32));
169 0 : 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 0 : EMIT(program, BPF_ALU64_REG(BPF_OR, BPF_REG_3, BPF_REG_4));
175 0 : EMIT_FIXUP_JMP_NEXT_RULE(program,
176 : BPF_JMP_REG(BPF_JNE, BPF_REG_1, BPF_REG_3, 0));
177 :
178 0 : 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 0 : EMIT(program, BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 32));
184 0 : 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 0 : EMIT(program, BPF_ALU64_REG(BPF_OR, BPF_REG_3, BPF_REG_4));
190 0 : 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 0 : 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 0 : EMIT(program, BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 32));
202 0 : 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 0 : 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 0 : j0 = bf_jmpctx_get(program,
216 : BPF_JMP_REG(BPF_JNE, BPF_REG_1, BPF_REG_3, 0));
217 :
218 0 : 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 0 : EMIT(program, BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 32));
224 0 : 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 0 : 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 0 : j1 = bf_jmpctx_get(program,
239 : BPF_JMP_REG(BPF_JNE, BPF_REG_2, BPF_REG_3, 0));
240 :
241 0 : 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 0 : bf_jmpctx_cleanup(&j0);
245 0 : bf_jmpctx_cleanup(&j1);
246 : }
247 :
248 : return 0;
249 : }
250 :
251 0 : static int _bf_matcher_generate_ip6_nexthdr(struct bf_program *program,
252 : const struct bf_matcher *matcher)
253 : {
254 0 : const uint8_t ehdr = *(uint8_t *)bf_matcher_payload(matcher);
255 : uint8_t eh_mask;
256 :
257 0 : if ((bf_matcher_get_op(matcher) != BF_MATCHER_EQ) &&
258 0 : (bf_matcher_get_op(matcher) != BF_MATCHER_NE))
259 : return -EINVAL;
260 :
261 0 : switch (ehdr) {
262 0 : case IPPROTO_HOPOPTS:
263 : case IPPROTO_ROUTING:
264 : case IPPROTO_DSTOPTS:
265 : case IPPROTO_FRAGMENT:
266 : case IPPROTO_AH:
267 : case IPPROTO_MH:
268 0 : eh_mask = (BF_IPV6_EH_HOPOPTS(ehdr == IPPROTO_HOPOPTS) |
269 0 : BF_IPV6_EH_ROUTING(ehdr == IPPROTO_ROUTING) |
270 0 : BF_IPV6_EH_FRAGMENT(ehdr == IPPROTO_FRAGMENT) |
271 0 : BF_IPV6_EH_AH(ehdr == IPPROTO_AH) |
272 0 : BF_IPV6_EH_DSTOPTS(ehdr == IPPROTO_DSTOPTS) |
273 : BF_IPV6_EH_MH(ehdr == IPPROTO_MH));
274 0 : EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10,
275 : BF_PROG_CTX_OFF(ipv6_eh)));
276 0 : EMIT(program, BPF_ALU64_IMM(BPF_AND, BPF_REG_1, eh_mask));
277 0 : 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 0 : break;
283 0 : default:
284 : /* check l4 protocols using BPF_REG_8 */
285 0 : 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 0 : break;
291 : }
292 :
293 : return 0;
294 : }
295 :
296 0 : int bf_matcher_generate_ip6(struct bf_program *program,
297 : const struct bf_matcher *matcher)
298 : {
299 : int r;
300 :
301 0 : EMIT_FIXUP_JMP_NEXT_RULE(
302 : program, BPF_JMP_IMM(BPF_JNE, BPF_REG_7, htobe16(ETH_P_IPV6), 0));
303 :
304 0 : EMIT(program,
305 : BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_10, BF_PROG_CTX_OFF(l3_hdr)));
306 :
307 0 : switch (bf_matcher_get_type(matcher)) {
308 0 : case BF_MATCHER_IP6_SADDR:
309 : case BF_MATCHER_IP6_DADDR:
310 0 : r = _bf_matcher_generate_ip6_addr(program, matcher);
311 0 : break;
312 0 : case BF_MATCHER_IP6_SNET:
313 : case BF_MATCHER_IP6_DNET:
314 0 : r = _bf_matcher_generate_ip6_net(program, matcher);
315 0 : break;
316 0 : case BF_MATCHER_IP6_NEXTHDR:
317 0 : r = _bf_matcher_generate_ip6_nexthdr(program, matcher);
318 0 : 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 : }
|