Branch data Line data Source code
1 : : /* SPDX-License-Identifier: GPL-2.0-only */
2 : : /*
3 : : * Copyright (c) Meta Platforms, Inc. and affiliates.
4 : : */
5 : :
6 : : #include "cgen/cgroup_sock_addr.h"
7 : :
8 : : #include <linux/bpf.h>
9 : : #include <linux/bpf_common.h>
10 : : #include <linux/if_ether.h>
11 : :
12 : : #include <assert.h>
13 : : #include <stddef.h>
14 : : #include <stdint.h>
15 : : #include <sys/socket.h>
16 : :
17 : : #include <bpfilter/flavor.h>
18 : : #include <bpfilter/logger.h>
19 : : #include <bpfilter/matcher.h>
20 : : #include <bpfilter/runtime.h>
21 : : #include <bpfilter/verdict.h>
22 : :
23 : : #include "cgen/matcher/cmp.h"
24 : : #include "cgen/matcher/meta.h"
25 : : #include "cgen/program.h"
26 : : #include "cgen/swich.h"
27 : : #include "filter.h"
28 : :
29 : : // Forward definition to avoid header conflicts.
30 : : uint16_t htons(uint16_t hostshort);
31 : :
32 : 50 : static int _bf_cgroup_sock_addr_gen_inline_prologue(struct bf_program *program)
33 : : {
34 : : int r;
35 : :
36 : : assert(program);
37 : :
38 : : /* `R6` = `bpf_sock_addr` context pointer. Unlike packet-based flavors where
39 : : * `R6` changes per header, the socket context is fixed so we set it once. */
40 [ - + ]: 50 : EMIT(program, BPF_MOV64_REG(BPF_REG_6, BPF_REG_1));
41 : :
42 : : // The counters stub reads `pkt_size` unconditionally; zero it out.
43 [ - + ]: 50 : EMIT(program, BPF_ST_MEM(BPF_DW, BPF_REG_10, BF_PROG_CTX_OFF(pkt_size), 0));
44 : :
45 : : /* Convert `bpf_sock_addr.family` to L3 protocol ID in `R7`, using the same
46 : : * `bf_swich` pattern as cgroup_skb. */
47 [ - + ]: 50 : EMIT(program, BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_6,
48 : : offsetof(struct bpf_sock_addr, family)));
49 : :
50 : : {
51 : 50 : _clean_bf_swich_ struct bf_swich swich =
52 [ - + ]: 50 : bf_swich_get(program, BPF_REG_2);
53 : :
54 [ - + ]: 50 : EMIT_SWICH_OPTION(&swich, AF_INET,
55 : : BPF_MOV64_IMM(BPF_REG_7, htons(ETH_P_IP)));
56 [ - + ]: 50 : EMIT_SWICH_OPTION(&swich, AF_INET6,
57 : : BPF_MOV64_IMM(BPF_REG_7, htons(ETH_P_IPV6)));
58 [ - + ]: 50 : EMIT_SWICH_DEFAULT(&swich, BPF_MOV64_IMM(BPF_REG_7, 0));
59 : :
60 : 50 : r = bf_swich_generate(&swich);
61 [ + - ]: 50 : if (r)
62 : : return r;
63 : : }
64 : :
65 [ - + ]: 50 : EMIT(program, BPF_LDX_MEM(BPF_W, BPF_REG_8, BPF_REG_6,
66 : : offsetof(struct bpf_sock_addr, protocol)));
67 : :
68 : 50 : return 0;
69 : : }
70 : :
71 : 50 : static int _bf_cgroup_sock_addr_gen_inline_epilogue(struct bf_program *program)
72 : : {
73 : : (void)program;
74 : :
75 : 50 : return 0;
76 : : }
77 : :
78 : : /**
79 : : * @brief Load a field from the `bpf_sock_addr` context into a register.
80 : : *
81 : : * `R6` must already point to the context. For 16-byte fields, the low
82 : : * 8 bytes go into `reg` and the high 8 bytes into `reg + 1`.
83 : : *
84 : : * When the field offset is not 8-byte aligned, 8- and 16-byte loads fall
85 : : * back to 4-byte reads packed via shift/or. This clobbers `reg + 1` for
86 : : * 8-byte loads and `reg + 2` for 16-byte loads.
87 : : *
88 : : * @param program Program to emit into. Can't be NULL.
89 : : * @param offset Byte offset into `struct bpf_sock_addr`.
90 : : * @param size Field size in bytes: 1, 2, 4, 8, or 16.
91 : : * @param reg BPF register to load the value into.
92 : : * @return 0 on success, negative errno on error.
93 : : */
94 : 82 : static int _bf_cgroup_sock_addr_load_field(struct bf_program *program,
95 : : size_t offset, size_t size, int reg)
96 : : {
97 : : assert(program);
98 : :
99 [ - - + - : 82 : switch (size) {
+ - ]
100 : 0 : case 1:
101 [ # # ]: 0 : EMIT(program, BPF_LDX_MEM(BPF_B, reg, BPF_REG_6, offset));
102 : 0 : break;
103 : 0 : case 2:
104 [ # # ]: 0 : EMIT(program, BPF_LDX_MEM(BPF_H, reg, BPF_REG_6, offset));
105 : 0 : break;
106 : 68 : case 4:
107 [ - + ]: 68 : EMIT(program, BPF_LDX_MEM(BPF_W, reg, BPF_REG_6, offset));
108 : 68 : break;
109 : 0 : case 8:
110 [ # # ]: 0 : if (offset % 8 == 0) {
111 [ # # ]: 0 : EMIT(program, BPF_LDX_MEM(BPF_DW, reg, BPF_REG_6, offset));
112 : : } else {
113 [ # # ]: 0 : EMIT(program, BPF_LDX_MEM(BPF_W, reg, BPF_REG_6, offset));
114 [ # # ]: 0 : EMIT(program, BPF_LDX_MEM(BPF_W, reg + 1, BPF_REG_6, offset + 4));
115 [ # # ]: 0 : EMIT(program, BPF_ALU64_IMM(BPF_LSH, reg + 1, 32));
116 [ # # ]: 0 : EMIT(program, BPF_ALU64_REG(BPF_OR, reg, reg + 1));
117 : : }
118 : : break;
119 : 14 : case 16:
120 [ + + ]: 14 : if (offset % 8 == 0) {
121 [ - + ]: 12 : EMIT(program, BPF_LDX_MEM(BPF_DW, reg, BPF_REG_6, offset));
122 [ - + ]: 12 : EMIT(program, BPF_LDX_MEM(BPF_DW, reg + 1, BPF_REG_6, offset + 8));
123 : : } else {
124 [ - + ]: 2 : EMIT(program, BPF_LDX_MEM(BPF_W, reg, BPF_REG_6, offset));
125 [ - + ]: 2 : EMIT(program, BPF_LDX_MEM(BPF_W, reg + 2, BPF_REG_6, offset + 4));
126 [ - + ]: 2 : EMIT(program, BPF_ALU64_IMM(BPF_LSH, reg + 2, 32));
127 [ - + ]: 2 : EMIT(program, BPF_ALU64_REG(BPF_OR, reg, reg + 2));
128 [ - + ]: 2 : EMIT(program, BPF_LDX_MEM(BPF_W, reg + 1, BPF_REG_6, offset + 8));
129 [ - + ]: 2 : EMIT(program, BPF_LDX_MEM(BPF_W, reg + 2, BPF_REG_6, offset + 12));
130 [ - + ]: 2 : EMIT(program, BPF_ALU64_IMM(BPF_LSH, reg + 2, 32));
131 [ - + ]: 2 : EMIT(program, BPF_ALU64_REG(BPF_OR, reg + 1, reg + 2));
132 : : }
133 : : break;
134 : : default:
135 : : return -EINVAL;
136 : : }
137 : :
138 : : return 0;
139 : : }
140 : :
141 : 13 : static int _bf_cgroup_sock_addr_load_and_cmp(struct bf_program *program,
142 : : const struct bf_matcher *matcher,
143 : : size_t offset, size_t size)
144 : : {
145 : : int r;
146 : :
147 : : assert(program);
148 : : assert(matcher);
149 : :
150 : 13 : r = _bf_cgroup_sock_addr_load_field(program, offset, size, BPF_REG_1);
151 [ + - ]: 13 : if (r)
152 : : return r;
153 : :
154 : 13 : return bf_cmp_value(program, bf_matcher_get_op(matcher),
155 : : bf_matcher_payload(matcher), size, BPF_REG_1);
156 : : }
157 : :
158 : 13 : static int _bf_cgroup_sock_addr_generate_net(struct bf_program *program,
159 : : const struct bf_matcher *matcher,
160 : : size_t offset, size_t size)
161 : : {
162 : : uint32_t prefixlen;
163 : : const void *data;
164 : : int r;
165 : :
166 : : assert(program);
167 : : assert(matcher);
168 : :
169 : 13 : prefixlen = *(const uint32_t *)bf_matcher_payload(matcher);
170 : 13 : data = (const uint8_t *)bf_matcher_payload(matcher) + sizeof(uint32_t);
171 : :
172 : 13 : r = _bf_cgroup_sock_addr_load_field(program, offset, size, BPF_REG_1);
173 [ + - ]: 13 : if (r)
174 : : return r;
175 : :
176 : 13 : return bf_cmp_masked_value(program, bf_matcher_get_op(matcher), data,
177 : : prefixlen, size, BPF_REG_1);
178 : : }
179 : :
180 : : /* `user_port` is a __u32 in network byte order with the upper 16 bits
181 : : * guaranteed zero by the kernel ABI. Loaded as `BPF_W` so EQ/NE compare
182 : : * the full 32-bit register (safe because upper bits are zero). For range
183 : : * comparisons, `BSWAP` converts to host order (and zeroes the upper bits). */
184 : 56 : static int _bf_cgroup_sock_addr_generate_port(struct bf_program *program,
185 : : const struct bf_matcher *matcher)
186 : : {
187 : : int r;
188 : :
189 : : assert(program);
190 : : assert(matcher);
191 : :
192 : 56 : r = _bf_cgroup_sock_addr_load_field(
193 : : program, offsetof(struct bpf_sock_addr, user_port), 4, BPF_REG_1);
194 [ + - ]: 56 : if (r)
195 : : return r;
196 : :
197 [ + + ]: 56 : if (bf_matcher_get_op(matcher) == BF_MATCHER_RANGE) {
198 : 16 : uint16_t *ports = (uint16_t *)bf_matcher_payload(matcher);
199 [ - + ]: 16 : EMIT(program, BPF_BSWAP(BPF_REG_1, 16));
200 : 16 : return bf_cmp_range(program, ports[0], ports[1], BPF_REG_1);
201 : : }
202 : :
203 : 40 : return bf_cmp_value(program, bf_matcher_get_op(matcher),
204 : : bf_matcher_payload(matcher), 2, BPF_REG_1);
205 : : }
206 : :
207 : : static int
208 : 111 : _bf_cgroup_sock_addr_gen_inline_matcher(struct bf_program *program,
209 : : const struct bf_matcher *matcher)
210 : : {
211 : : assert(program);
212 : : assert(matcher);
213 : :
214 [ + + + + : 111 : switch (bf_matcher_get_type(matcher)) {
+ + + + +
+ + - ]
215 : 24 : case BF_MATCHER_META_L3_PROTO:
216 : : case BF_MATCHER_META_L4_PROTO:
217 : : case BF_MATCHER_META_PROBABILITY:
218 : 24 : return bf_matcher_generate_meta(program, matcher);
219 : 1 : case BF_MATCHER_IP4_SADDR:
220 : 1 : return _bf_cgroup_sock_addr_load_and_cmp(
221 : : program, matcher, offsetof(struct bpf_sock_addr, msg_src_ip4), 4);
222 : 1 : case BF_MATCHER_IP4_SNET:
223 : 1 : return _bf_cgroup_sock_addr_generate_net(
224 : : program, matcher, offsetof(struct bpf_sock_addr, msg_src_ip4), 4);
225 : 5 : case BF_MATCHER_IP4_DADDR:
226 : 5 : return _bf_cgroup_sock_addr_load_and_cmp(
227 : : program, matcher, offsetof(struct bpf_sock_addr, user_ip4), 4);
228 : 5 : case BF_MATCHER_IP4_DNET:
229 : 5 : return _bf_cgroup_sock_addr_generate_net(
230 : : program, matcher, offsetof(struct bpf_sock_addr, user_ip4), 4);
231 : 5 : case BF_MATCHER_IP4_PROTO:
232 [ - + ]: 5 : EMIT(program, BPF_MOV64_REG(BPF_REG_1, BPF_REG_8));
233 : 5 : return bf_cmp_value(program, bf_matcher_get_op(matcher),
234 : : bf_matcher_payload(matcher), 1, BPF_REG_1);
235 : 1 : case BF_MATCHER_IP6_SADDR:
236 : 1 : return _bf_cgroup_sock_addr_load_and_cmp(
237 : : program, matcher, offsetof(struct bpf_sock_addr, msg_src_ip6), 16);
238 : 1 : case BF_MATCHER_IP6_SNET:
239 : 1 : return _bf_cgroup_sock_addr_generate_net(
240 : : program, matcher, offsetof(struct bpf_sock_addr, msg_src_ip6), 16);
241 : 6 : case BF_MATCHER_IP6_DADDR:
242 : 6 : return _bf_cgroup_sock_addr_load_and_cmp(
243 : : program, matcher, offsetof(struct bpf_sock_addr, user_ip6), 16);
244 : 6 : case BF_MATCHER_IP6_DNET:
245 : 6 : return _bf_cgroup_sock_addr_generate_net(
246 : : program, matcher, offsetof(struct bpf_sock_addr, user_ip6), 16);
247 : 56 : case BF_MATCHER_META_DPORT:
248 : : case BF_MATCHER_TCP_DPORT:
249 : : case BF_MATCHER_UDP_DPORT:
250 : 56 : return _bf_cgroup_sock_addr_generate_port(program, matcher);
251 : 0 : default:
252 [ # # ]: 0 : return bf_err_r(-ENOTSUP,
253 : : "matcher '%s' not supported for cgroup_sock_addr",
254 : : bf_matcher_type_to_str(bf_matcher_get_type(matcher)));
255 : : }
256 : : }
257 : :
258 : : /**
259 : : * @brief Convert a standard verdict into a return value.
260 : : *
261 : : * @param verdict Verdict to convert. Must be valid.
262 : : * @return Cgroup return code corresponding to the verdict, as an integer.
263 : : */
264 : 113 : static int _bf_cgroup_sock_addr_get_verdict(enum bf_verdict verdict)
265 : : {
266 [ + - + ]: 113 : switch (verdict) {
267 : : case BF_VERDICT_ACCEPT:
268 : : return 1;
269 : 46 : case BF_VERDICT_DROP:
270 : 46 : return 0;
271 : 0 : default:
272 : 0 : return -ENOTSUP;
273 : : }
274 : : }
275 : :
276 : : const struct bf_flavor_ops bf_flavor_ops_cgroup_sock_addr = {
277 : : .gen_inline_prologue = _bf_cgroup_sock_addr_gen_inline_prologue,
278 : : .gen_inline_epilogue = _bf_cgroup_sock_addr_gen_inline_epilogue,
279 : : .get_verdict = _bf_cgroup_sock_addr_get_verdict,
280 : : .gen_inline_matcher = _bf_cgroup_sock_addr_gen_inline_matcher,
281 : : };
|