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 :
31 0 : static int _bf_matcher_generate_ip6_addr(struct bf_program *program,
32 : const struct bf_matcher *matcher)
33 : {
34 : struct bf_jmpctx j0, j1;
35 : struct bf_matcher_ip6_addr *addr = (void *)&matcher->payload;
36 0 : size_t offset = matcher->type == BF_MATCHER_IP6_SADDR ?
37 0 : offsetof(struct ipv6hdr, saddr) :
38 : offsetof(struct ipv6hdr, daddr);
39 :
40 0 : EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_6, offset));
41 0 : EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_6, offset + 8));
42 :
43 0 : if (addr->mask[_BF_MASK_LAST_BYTE] != (uint8_t)~0) {
44 0 : EMIT(program, BPF_MOV32_IMM(BPF_REG_3,
45 : _bf_make32(addr->mask[7], addr->mask[6],
46 : addr->mask[5], addr->mask[4])));
47 0 : EMIT(program, BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 32));
48 0 : EMIT(program, BPF_MOV32_IMM(BPF_REG_4,
49 : _bf_make32(addr->mask[3], addr->mask[2],
50 : addr->mask[1], addr->mask[0])));
51 0 : EMIT(program, BPF_ALU64_REG(BPF_OR, BPF_REG_3, BPF_REG_4));
52 0 : EMIT(program, BPF_ALU64_REG(BPF_AND, BPF_REG_1, BPF_REG_3));
53 :
54 0 : EMIT(program,
55 : BPF_MOV32_IMM(BPF_REG_3,
56 : _bf_make32(addr->mask[15], addr->mask[14],
57 : addr->mask[13], addr->mask[12])));
58 0 : EMIT(program, BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 32));
59 0 : EMIT(program, BPF_MOV32_IMM(BPF_REG_4,
60 : _bf_make32(addr->mask[11], addr->mask[10],
61 : addr->mask[9], addr->mask[8])));
62 0 : EMIT(program, BPF_ALU64_REG(BPF_OR, BPF_REG_3, BPF_REG_4));
63 0 : EMIT(program, BPF_ALU64_REG(BPF_AND, BPF_REG_2, BPF_REG_3));
64 : }
65 :
66 0 : if (matcher->op == BF_MATCHER_EQ) {
67 : /* If we want to match an IP, both addr->addr[0] and addr->addr[1]
68 : * must match the packet, otherwise we jump to the next rule. */
69 0 : EMIT(program, BPF_MOV32_IMM(BPF_REG_3,
70 : _bf_make32(addr->addr[7] & addr->mask[7],
71 : addr->addr[6] & addr->mask[6],
72 : addr->addr[5] & addr->mask[5],
73 : addr->addr[4] & addr->mask[4])));
74 0 : EMIT(program, BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 32));
75 0 : EMIT(program, BPF_MOV32_IMM(BPF_REG_4,
76 : _bf_make32(addr->addr[3] & addr->mask[3],
77 : addr->addr[2] & addr->mask[2],
78 : addr->addr[1] & addr->mask[1],
79 : addr->addr[0] & addr->mask[0])));
80 0 : EMIT(program, BPF_ALU64_REG(BPF_OR, BPF_REG_3, BPF_REG_4));
81 0 : EMIT_FIXUP_JMP_NEXT_RULE(program,
82 : BPF_JMP_REG(BPF_JNE, BPF_REG_1, BPF_REG_3, 0));
83 :
84 0 : EMIT(program,
85 : BPF_MOV32_IMM(BPF_REG_3,
86 : _bf_make32(addr->addr[15] & addr->mask[15],
87 : addr->addr[14] & addr->mask[14],
88 : addr->addr[13] & addr->mask[13],
89 : addr->addr[12] & addr->mask[12])));
90 0 : EMIT(program, BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 32));
91 0 : EMIT(program, BPF_MOV32_IMM(BPF_REG_4,
92 : _bf_make32(addr->addr[11] & addr->mask[11],
93 : addr->addr[10] & addr->mask[10],
94 : addr->addr[9] & addr->mask[9],
95 : addr->addr[8] & addr->mask[8])));
96 0 : EMIT(program, BPF_ALU64_REG(BPF_OR, BPF_REG_3, BPF_REG_4));
97 0 : EMIT_FIXUP_JMP_NEXT_RULE(program,
98 : BPF_JMP_REG(BPF_JNE, BPF_REG_2, BPF_REG_3, 0));
99 : } else {
100 : /* If we want to *not* match an IP, none of addr->addr[0] and
101 : * addr->addr[1] should match the packet, otherwise we jump to the
102 : * next rule. */
103 0 : EMIT(program, BPF_MOV32_IMM(BPF_REG_3,
104 : _bf_make32(addr->addr[7] & addr->mask[7],
105 : addr->addr[6] & addr->mask[6],
106 : addr->addr[5] & addr->mask[5],
107 : addr->addr[4] & addr->mask[4])));
108 0 : EMIT(program, BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 32));
109 0 : EMIT(program, BPF_MOV32_IMM(BPF_REG_4,
110 : _bf_make32(addr->addr[3] & addr->mask[3],
111 : addr->addr[2] & addr->mask[2],
112 : addr->addr[1] & addr->mask[1],
113 : addr->addr[0] & addr->mask[0])));
114 0 : EMIT(program, BPF_ALU64_REG(BPF_OR, BPF_REG_3, BPF_REG_4));
115 :
116 : /* Branching:
117 : * - addr->addr[0] matches the address' 64 MSB: continue to compare
118 : * the address' 64 LSB.
119 : * - addr->addr[0] doesn't matches the address' 64 MSB: jump to the
120 : * end of the matcher to continue the processing of the current rule.
121 : * This matcher matched. */
122 0 : j0 = bf_jmpctx_get(program,
123 : BPF_JMP_REG(BPF_JNE, BPF_REG_1, BPF_REG_3, 0));
124 :
125 0 : EMIT(program,
126 : BPF_MOV32_IMM(BPF_REG_3,
127 : _bf_make32(addr->addr[15] & addr->mask[15],
128 : addr->addr[14] & addr->mask[14],
129 : addr->addr[13] & addr->mask[13],
130 : addr->addr[12] & addr->mask[12])));
131 0 : EMIT(program, BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 32));
132 0 : EMIT(program, BPF_MOV32_IMM(BPF_REG_4,
133 : _bf_make32(addr->addr[11] & addr->mask[11],
134 : addr->addr[10] & addr->mask[10],
135 : addr->addr[9] & addr->mask[9],
136 : addr->addr[8] & addr->mask[8])));
137 0 : EMIT(program, BPF_ALU64_REG(BPF_OR, BPF_REG_3, BPF_REG_4));
138 :
139 : /* Branching:
140 : * - addr->addr[1] matches the address' 64 LSB: addr->addr matches the
141 : * packet's address, meaning the matcher doesn't match. Jump to the
142 : * next rule.
143 : * - addr->addr[1] doesn't matches the address' 64 LSB: the matcher
144 : * matched: addr->addr is not equal to the packet's address. Continue
145 : * processing with the next matcher. */
146 0 : j1 = bf_jmpctx_get(program,
147 : BPF_JMP_REG(BPF_JNE, BPF_REG_2, BPF_REG_3, 0));
148 :
149 0 : EMIT_FIXUP_JMP_NEXT_RULE(program, BPF_JMP_A(0));
150 :
151 : // j0 and j1 should jump here if they can't match the packet's IP.
152 0 : bf_jmpctx_cleanup(&j0);
153 0 : bf_jmpctx_cleanup(&j1);
154 : }
155 :
156 : return 0;
157 : }
158 :
159 0 : static int _bf_matcher_generate_ip6_net(struct bf_program *program,
160 : const struct bf_matcher *matcher)
161 : {
162 : uint32_t set_id;
163 : struct bf_set *set;
164 : int16_t offset;
165 :
166 0 : bf_assert(program && matcher);
167 :
168 0 : set_id = *(uint32_t *)matcher->payload;
169 0 : set = bf_list_get_at(&program->runtime.chain->sets, set_id);
170 0 : if (!set)
171 0 : return bf_err_r(-ENOENT, "set #%d not found", set_id);
172 :
173 0 : switch (set->type) {
174 0 : case BF_SET_IP6_SUBNET:
175 : // Copy bf_ip6_lpm_key entries starting at scratch offset 4, so the
176 : // 64-bits copies for the address will be aligned
177 0 : offset = matcher->type == BF_MATCHER_IP6_SNET ?
178 : offsetof(struct ipv6hdr, saddr) :
179 : offsetof(struct ipv6hdr, daddr);
180 0 : EMIT(program, BPF_MOV64_IMM(BPF_REG_3, 128));
181 0 : EMIT(program,
182 : BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_3, BF_PROG_SCR_OFF(4)));
183 :
184 0 : EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_6, offset));
185 0 : EMIT(program,
186 : BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_2, BF_PROG_SCR_OFF(8)));
187 0 : EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_6, offset + 8));
188 0 : EMIT(program,
189 : BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_2, BF_PROG_SCR_OFF(16)));
190 : break;
191 0 : default:
192 0 : return bf_err_r(-EINVAL, "unsupported set type: %s",
193 : bf_set_type_to_str(set->type));
194 : }
195 :
196 0 : EMIT_LOAD_SET_FD_FIXUP(program, BPF_REG_1, set_id);
197 0 : EMIT(program, BPF_MOV64_REG(BPF_REG_2, BPF_REG_10));
198 0 : EMIT(program, BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, BF_PROG_SCR_OFF(4)));
199 :
200 0 : EMIT(program, BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem));
201 :
202 : // Jump to the next rule if map_lookup_elem returned 0
203 0 : EMIT_FIXUP_JMP_NEXT_RULE(program, BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 0));
204 :
205 0 : return 0;
206 : }
207 :
208 0 : int bf_matcher_generate_ip6(struct bf_program *program,
209 : const struct bf_matcher *matcher)
210 : {
211 : int r;
212 :
213 0 : EMIT_FIXUP_JMP_NEXT_RULE(
214 : program, BPF_JMP_IMM(BPF_JNE, BPF_REG_7, htobe16(ETH_P_IPV6), 0));
215 :
216 0 : EMIT(program,
217 : BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_10, BF_PROG_CTX_OFF(l3_hdr)));
218 :
219 0 : switch (matcher->type) {
220 0 : case BF_MATCHER_IP6_SADDR:
221 : case BF_MATCHER_IP6_DADDR:
222 0 : r = _bf_matcher_generate_ip6_addr(program, matcher);
223 0 : break;
224 0 : case BF_MATCHER_IP6_SNET:
225 : case BF_MATCHER_IP6_DNET:
226 0 : r = _bf_matcher_generate_ip6_net(program, matcher);
227 0 : break;
228 0 : default:
229 0 : return bf_err_r(-EINVAL, "unknown matcher type %d", matcher->type);
230 : };
231 :
232 : return r;
233 : }
|