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/btf.h>
18 : : #include <bpfilter/chain.h>
19 : : #include <bpfilter/elfstub.h>
20 : : #include <bpfilter/flavor.h>
21 : : #include <bpfilter/hook.h>
22 : : #include <bpfilter/logger.h>
23 : : #include <bpfilter/matcher.h>
24 : : #include <bpfilter/rule.h>
25 : : #include <bpfilter/runtime.h>
26 : : #include <bpfilter/set.h>
27 : : #include <bpfilter/verdict.h>
28 : :
29 : : #include "cgen/jmp.h"
30 : : #include "cgen/matcher/cmp.h"
31 : : #include "cgen/matcher/meta.h"
32 : : #include "cgen/matcher/set.h"
33 : : #include "cgen/program.h"
34 : : #include "cgen/runtime.h"
35 : : #include "cgen/stub.h"
36 : : #include "cgen/swich.h"
37 : : #include "filter.h"
38 : :
39 : : // Forward definition to avoid header conflicts.
40 : : uint16_t htons(uint16_t hostshort);
41 : :
42 : 82 : static int _bf_cgroup_sock_addr_gen_inline_prologue(struct bf_program *program)
43 : : {
44 : : int r;
45 : :
46 : : assert(program);
47 : :
48 : : /* `R6` = `bpf_sock_addr` context pointer. Unlike packet-based flavors where
49 : : * `R6` changes per header, the socket context is fixed so we set it once. */
50 [ + - ]: 82 : EMIT(program, BPF_MOV64_REG(BPF_REG_6, BPF_REG_1));
51 : :
52 : : // The counters stub reads `pkt_size` unconditionally; zero it out.
53 [ + - ]: 82 : EMIT(program, BPF_ST_MEM(BPF_DW, BPF_REG_10, BF_PROG_CTX_OFF(pkt_size), 0));
54 : :
55 : : /* Convert `bpf_sock_addr.family` to L3 protocol ID in `R7`, using the same
56 : : * `bf_swich` pattern as cgroup_skb. */
57 [ + - ]: 82 : EMIT(program, BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_6,
58 : : offsetof(struct bpf_sock_addr, family)));
59 : :
60 : : {
61 : 82 : _clean_bf_swich_ struct bf_swich swich =
62 [ - + ]: 82 : bf_swich_get(program, BPF_REG_2);
63 : :
64 [ - + ]: 82 : EMIT_SWICH_OPTION(&swich, AF_INET,
65 : : BPF_MOV64_IMM(BPF_REG_7, htons(ETH_P_IP)));
66 [ - + ]: 82 : EMIT_SWICH_OPTION(&swich, AF_INET6,
67 : : BPF_MOV64_IMM(BPF_REG_7, htons(ETH_P_IPV6)));
68 [ - + ]: 82 : EMIT_SWICH_DEFAULT(&swich, BPF_MOV64_IMM(BPF_REG_7, 0));
69 : :
70 : 82 : r = bf_swich_generate(&swich);
71 [ + - ]: 82 : if (r)
72 : : return r;
73 : : }
74 : :
75 : 82 : EMIT(program, BPF_LDX_MEM(BPF_W, BPF_REG_8, BPF_REG_6,
76 : : offsetof(struct bpf_sock_addr, protocol)));
77 : :
78 : : return 0;
79 : : }
80 : :
81 : 82 : static int _bf_cgroup_sock_addr_gen_inline_epilogue(struct bf_program *program)
82 : : {
83 : : (void)program;
84 : :
85 : 82 : return 0;
86 : : }
87 : :
88 : : /**
89 : : * @brief Load a field from the `bpf_sock_addr` context into a register.
90 : : *
91 : : * `R6` must already point to the context. For 16-byte fields, the low
92 : : * 8 bytes go into `reg` and the high 8 bytes into `reg + 1`.
93 : : *
94 : : * When the field offset is not 8-byte aligned, 8- and 16-byte loads fall
95 : : * back to 4-byte reads packed via shift/or. This clobbers `reg + 1` for
96 : : * 8-byte loads and `reg + 2` for 16-byte loads.
97 : : *
98 : : * @param program Program to emit into. Can't be NULL.
99 : : * @param offset Byte offset into `struct bpf_sock_addr`.
100 : : * @param size Field size in bytes: 1, 2, 4, 8, or 16.
101 : : * @param reg BPF register to load the value into.
102 : : * @return 0 on success, negative errno on error.
103 : : */
104 : 92 : static int _bf_cgroup_sock_addr_load_field(struct bf_program *program,
105 : : size_t offset, size_t size, int reg)
106 : : {
107 : : assert(program);
108 : :
109 [ - - + - : 92 : switch (size) {
+ - ]
110 : 0 : case 1:
111 : 0 : EMIT(program, BPF_LDX_MEM(BPF_B, reg, BPF_REG_6, offset));
112 : : break;
113 : 0 : case 2:
114 : 0 : EMIT(program, BPF_LDX_MEM(BPF_H, reg, BPF_REG_6, offset));
115 : : break;
116 : 75 : case 4:
117 : 75 : EMIT(program, BPF_LDX_MEM(BPF_W, reg, BPF_REG_6, offset));
118 : : break;
119 : 0 : case 8:
120 [ # # ]: 0 : if (offset % 8 == 0) {
121 : 0 : EMIT(program, BPF_LDX_MEM(BPF_DW, reg, BPF_REG_6, offset));
122 : : } else {
123 [ # # ]: 0 : EMIT(program, BPF_LDX_MEM(BPF_W, reg, BPF_REG_6, offset));
124 [ # # ]: 0 : EMIT(program, BPF_LDX_MEM(BPF_W, reg + 1, BPF_REG_6, offset + 4));
125 [ # # ]: 0 : EMIT(program, BPF_ALU64_IMM(BPF_LSH, reg + 1, 32));
126 : 0 : EMIT(program, BPF_ALU64_REG(BPF_OR, reg, reg + 1));
127 : : }
128 : : break;
129 : 17 : case 16:
130 [ + + ]: 17 : if (offset % 8 == 0) {
131 [ + - ]: 14 : EMIT(program, BPF_LDX_MEM(BPF_DW, reg, BPF_REG_6, offset));
132 : 14 : EMIT(program, BPF_LDX_MEM(BPF_DW, reg + 1, BPF_REG_6, offset + 8));
133 : : } else {
134 [ + - ]: 3 : EMIT(program, BPF_LDX_MEM(BPF_W, reg, BPF_REG_6, offset));
135 [ + - ]: 3 : EMIT(program, BPF_LDX_MEM(BPF_W, reg + 2, BPF_REG_6, offset + 4));
136 [ + - ]: 3 : EMIT(program, BPF_ALU64_IMM(BPF_LSH, reg + 2, 32));
137 [ + - ]: 3 : EMIT(program, BPF_ALU64_REG(BPF_OR, reg, reg + 2));
138 [ + - ]: 3 : EMIT(program, BPF_LDX_MEM(BPF_W, reg + 1, BPF_REG_6, offset + 8));
139 [ + - ]: 3 : EMIT(program, BPF_LDX_MEM(BPF_W, reg + 2, BPF_REG_6, offset + 12));
140 [ + - ]: 3 : EMIT(program, BPF_ALU64_IMM(BPF_LSH, reg + 2, 32));
141 : 3 : EMIT(program, BPF_ALU64_REG(BPF_OR, reg + 1, reg + 2));
142 : : }
143 : : break;
144 : : default:
145 : : return -EINVAL;
146 : : }
147 : :
148 : : return 0;
149 : : }
150 : :
151 : : /**
152 : : * @brief Store a register value at an offset from `BPF_REG_10`.
153 : : *
154 : : * Counterpart to `_bf_cgroup_sock_addr_load_field`. For 16-byte stores,
155 : : * `reg` holds the low 8 bytes and `reg + 1` the high 8 bytes, matching
156 : : * the layout produced by `_bf_cgroup_sock_addr_load_field`.
157 : : *
158 : : * @param program Program to emit into. Can't be NULL.
159 : : * @param offset Byte offset from `BPF_REG_10`.
160 : : * @param size Field size in bytes: 1, 2, 4, 8, or 16.
161 : : * @param reg BPF register holding the value to store.
162 : : * @return 0 on success, negative errno on error.
163 : : */
164 : 10 : static int _bf_cgroup_sock_addr_store_field(struct bf_program *program,
165 : : int offset, size_t size, int reg)
166 : : {
167 : : assert(program);
168 : :
169 [ - + + - : 10 : switch (size) {
+ - ]
170 : 0 : case 1:
171 : 0 : EMIT(program, BPF_STX_MEM(BPF_B, BPF_REG_10, reg, offset));
172 : : break;
173 : 4 : case 2:
174 : 4 : EMIT(program, BPF_STX_MEM(BPF_H, BPF_REG_10, reg, offset));
175 : : break;
176 : 3 : case 4:
177 : 3 : EMIT(program, BPF_STX_MEM(BPF_W, BPF_REG_10, reg, offset));
178 : : break;
179 : 0 : case 8:
180 : 0 : EMIT(program, BPF_STX_MEM(BPF_DW, BPF_REG_10, reg, offset));
181 : : break;
182 : 3 : case 16:
183 [ + - ]: 3 : EMIT(program, BPF_STX_MEM(BPF_DW, BPF_REG_10, reg, offset));
184 : 3 : EMIT(program, BPF_STX_MEM(BPF_DW, BPF_REG_10, reg + 1, offset + 8));
185 : : break;
186 : : default:
187 : : return -EINVAL;
188 : : }
189 : :
190 : : return 0;
191 : : }
192 : :
193 : 13 : static int _bf_cgroup_sock_addr_load_and_cmp(struct bf_program *program,
194 : : const struct bf_matcher *matcher,
195 : : size_t offset, size_t size)
196 : : {
197 : : int r;
198 : :
199 : : assert(program);
200 : : assert(matcher);
201 : :
202 : 13 : r = _bf_cgroup_sock_addr_load_field(program, offset, size, BPF_REG_1);
203 [ + - ]: 13 : if (r)
204 : : return r;
205 : :
206 : 13 : return bf_cmp_value(program, matcher, bf_matcher_payload(matcher), size,
207 : : BPF_REG_1);
208 : : }
209 : :
210 : 13 : static int _bf_cgroup_sock_addr_generate_net(struct bf_program *program,
211 : : const struct bf_matcher *matcher,
212 : : size_t offset, size_t size)
213 : : {
214 : : uint32_t prefixlen;
215 : : const void *data;
216 : : int r;
217 : :
218 : : assert(program);
219 : : assert(matcher);
220 : :
221 : 13 : prefixlen = *(const uint32_t *)bf_matcher_payload(matcher);
222 : 13 : data = (const uint8_t *)bf_matcher_payload(matcher) + sizeof(uint32_t);
223 : :
224 : 13 : r = _bf_cgroup_sock_addr_load_field(program, offset, size, BPF_REG_1);
225 [ + - ]: 13 : if (r)
226 : : return r;
227 : :
228 : 13 : return bf_cmp_masked_value(program, matcher, data, prefixlen, size,
229 : : BPF_REG_1);
230 : : }
231 : :
232 : : /* `user_port` is a __u32 in network byte order with the upper 16 bits
233 : : * guaranteed zero by the kernel ABI. Loaded as `BPF_W` so EQ/NE compare
234 : : * the full 32-bit register (safe because upper bits are zero). For range
235 : : * comparisons, `BSWAP` converts to host order (and zeroes the upper bits). */
236 : 56 : static int _bf_cgroup_sock_addr_generate_port(struct bf_program *program,
237 : : const struct bf_matcher *matcher)
238 : : {
239 : : int r;
240 : :
241 : : assert(program);
242 : : assert(matcher);
243 : :
244 : 56 : r = _bf_cgroup_sock_addr_load_field(
245 : : program, offsetof(struct bpf_sock_addr, user_port), 4, BPF_REG_1);
246 [ + - ]: 56 : if (r)
247 : : return r;
248 : :
249 [ + + ]: 56 : if (bf_matcher_get_op(matcher) == BF_MATCHER_RANGE) {
250 : 16 : uint16_t *ports = (uint16_t *)bf_matcher_payload(matcher);
251 [ + - ]: 16 : EMIT(program, BPF_BSWAP(BPF_REG_1, 16));
252 : 16 : return bf_cmp_range(program, matcher, ports[0], ports[1], BPF_REG_1);
253 : : }
254 : :
255 : 40 : return bf_cmp_value(program, matcher, bf_matcher_payload(matcher), 2,
256 : : BPF_REG_1);
257 : : }
258 : :
259 : : static ssize_t _bf_cgroup_sock_addr_ctx_offset(enum bf_matcher_type type)
260 : : {
261 : : switch (type) {
262 : : case BF_MATCHER_IP4_SADDR:
263 : : case BF_MATCHER_IP4_SNET:
264 : : return offsetof(struct bpf_sock_addr, msg_src_ip4);
265 : : case BF_MATCHER_IP4_DADDR:
266 : : case BF_MATCHER_IP4_DNET:
267 : : return offsetof(struct bpf_sock_addr, user_ip4);
268 : : case BF_MATCHER_IP6_SADDR:
269 : : case BF_MATCHER_IP6_SNET:
270 : : return offsetof(struct bpf_sock_addr, msg_src_ip6);
271 : : case BF_MATCHER_IP6_DADDR:
272 : : case BF_MATCHER_IP6_DNET:
273 : : return offsetof(struct bpf_sock_addr, user_ip6);
274 : : case BF_MATCHER_IP4_PROTO:
275 : : return offsetof(struct bpf_sock_addr, protocol);
276 : : case BF_MATCHER_META_DPORT:
277 : : case BF_MATCHER_TCP_DPORT:
278 : : case BF_MATCHER_UDP_DPORT:
279 : : return offsetof(struct bpf_sock_addr, user_port);
280 : : default:
281 : : return -ENOTSUP;
282 : : }
283 : : }
284 : :
285 : 24 : static int _bf_cgroup_sock_addr_generate_set(struct bf_program *program,
286 : : const struct bf_matcher *matcher)
287 : : {
288 : : const struct bf_set *set;
289 : : size_t offset = 0;
290 : : int r;
291 : :
292 : : assert(program);
293 : : assert(matcher);
294 : :
295 : 24 : set = bf_chain_get_set_for_matcher(program->runtime.chain, matcher);
296 [ - + ]: 24 : if (!set) {
297 [ # # ]: 0 : return bf_err_r(-ENOENT, "set #%u not found in %s",
298 : : *(uint32_t *)bf_matcher_payload(matcher),
299 : : program->runtime.chain->name);
300 : : }
301 : :
302 [ + + ]: 24 : if (set->use_trie) {
303 : 4 : const struct bf_matcher_meta *meta = bf_matcher_get_meta(set->key[0]);
304 [ + - ]: 4 : ssize_t ctx_off = _bf_cgroup_sock_addr_ctx_offset(set->key[0]);
305 : :
306 [ - + ]: 4 : if (!meta) {
307 [ # # ]: 0 : return bf_err_r(-EINVAL, "missing meta for set component '%s'",
308 : : bf_matcher_type_to_str(set->key[0]));
309 : : }
310 : :
311 [ - + ]: 4 : if (ctx_off < 0) {
312 [ # # ]: 0 : return bf_err_r(
313 : : (int)ctx_off,
314 : : "set component '%s' not supported for cgroup_sock_addr",
315 : : bf_matcher_type_to_str(set->key[0]));
316 : : }
317 : :
318 : 4 : return bf_set_generate_trie_lookup(program, matcher, (size_t)ctx_off,
319 : 4 : meta->hdr_payload_size);
320 : : }
321 : :
322 [ + + ]: 54 : for (size_t i = 0; i < set->n_comps; ++i) {
323 : 34 : enum bf_matcher_type type = set->key[i];
324 : 34 : const struct bf_matcher_meta *meta = bf_matcher_get_meta(type);
325 : : ssize_t ctx_off = _bf_cgroup_sock_addr_ctx_offset(type);
326 : :
327 [ - + ]: 34 : if (!meta) {
328 [ # # ]: 0 : return bf_err_r(-EINVAL, "missing meta for set component '%s'",
329 : : bf_matcher_type_to_str(type));
330 : : }
331 : :
332 : : /* The PID is not part of the context, a helper returns it. The key
333 : : * components are packed, so the store offset might be misaligned:
334 : : * `bf_stub_store` splits the store accordingly. */
335 [ + + ]: 34 : if (type == BF_MATCHER_META_PID) {
336 [ + - ]: 6 : EMIT(program, BPF_EMIT_CALL(BPF_FUNC_get_current_pid_tgid));
337 [ + - ]: 6 : EMIT(program, BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32));
338 : :
339 : 6 : r = bf_stub_store(program, BPF_REG_0, meta->hdr_payload_size,
340 : 6 : BF_PROG_SCR_OFF(offset));
341 [ + - ]: 6 : if (r)
342 : : return r;
343 : :
344 : 6 : offset += meta->hdr_payload_size;
345 : 6 : continue;
346 : : }
347 : :
348 [ - + ]: 28 : if (ctx_off < 0) {
349 [ # # ]: 0 : return bf_err_r(
350 : : (int)ctx_off,
351 : : "set component '%s' not supported for cgroup_sock_addr",
352 : : bf_matcher_type_to_str(type));
353 : : }
354 : :
355 : : /* The BPF verifier enforces specific ctx access widths on
356 : : * `bpf_sock_addr` fields. `bf_stub_load()` reads
357 : : * `meta->hdr_payload_size` bytes from ctx:
358 : : * - Ports (`hdr_payload_size == 2`): `user_port` is a 4-byte
359 : : * `__u32`, but the 2-byte narrow read is accepted and rewritten
360 : : * to the NBO port value.
361 : : * - Protocol (`hdr_payload_size == 1`): only 4-byte reads are
362 : : * allowed, so a 1-byte `bf_stub_load()` would be rejected. Reuse
363 : : * `BPF_REG_8`, which the prologue loaded with a 4-byte read. */
364 [ + + ]: 28 : if (type == BF_MATCHER_IP4_PROTO) {
365 [ + - ]: 2 : EMIT(program, BPF_STX_MEM(BPF_B, BPF_REG_10, BPF_REG_8,
366 : : BF_PROG_SCR_OFF(offset)));
367 : : } else {
368 : 26 : r = bf_stub_load(program, (size_t)ctx_off, meta->hdr_payload_size,
369 : 26 : BF_PROG_SCR_OFF(offset));
370 [ + - ]: 26 : if (r)
371 : : return r;
372 : : }
373 : :
374 : 28 : offset += meta->hdr_payload_size;
375 : : }
376 : :
377 : 20 : return bf_set_generate_map_lookup(program, matcher, BF_PROG_SCR_OFF(0));
378 : : }
379 : :
380 : : static int
381 : 144 : _bf_cgroup_sock_addr_gen_inline_matcher(struct bf_program *program,
382 : : const struct bf_matcher *matcher)
383 : : {
384 : : assert(program);
385 : : assert(matcher);
386 : :
387 [ + + + + : 144 : switch (bf_matcher_get_type(matcher)) {
+ + + + +
+ + + +
- ]
388 : 25 : case BF_MATCHER_META_L3_PROTO:
389 : : case BF_MATCHER_META_L4_PROTO:
390 : : case BF_MATCHER_META_PROBABILITY:
391 : 25 : return bf_matcher_generate_meta(program, matcher);
392 : 8 : case BF_MATCHER_META_PID:
393 [ + - ]: 8 : EMIT(program, BPF_EMIT_CALL(BPF_FUNC_get_current_pid_tgid));
394 [ + - ]: 8 : EMIT(program, BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32));
395 : 8 : return bf_cmp_value(program, matcher, bf_matcher_payload(matcher), 4,
396 : : BPF_REG_0);
397 : 1 : case BF_MATCHER_IP4_SADDR:
398 : 1 : return _bf_cgroup_sock_addr_load_and_cmp(
399 : : program, matcher, offsetof(struct bpf_sock_addr, msg_src_ip4), 4);
400 : 1 : case BF_MATCHER_IP4_SNET:
401 : 1 : return _bf_cgroup_sock_addr_generate_net(
402 : : program, matcher, offsetof(struct bpf_sock_addr, msg_src_ip4), 4);
403 : 5 : case BF_MATCHER_IP4_DADDR:
404 : 5 : return _bf_cgroup_sock_addr_load_and_cmp(
405 : : program, matcher, offsetof(struct bpf_sock_addr, user_ip4), 4);
406 : 5 : case BF_MATCHER_IP4_DNET:
407 : 5 : return _bf_cgroup_sock_addr_generate_net(
408 : : program, matcher, offsetof(struct bpf_sock_addr, user_ip4), 4);
409 : 5 : case BF_MATCHER_IP4_PROTO:
410 [ + - ]: 5 : EMIT(program, BPF_MOV64_REG(BPF_REG_1, BPF_REG_8));
411 : 5 : return bf_cmp_value(program, matcher, bf_matcher_payload(matcher), 1,
412 : : BPF_REG_1);
413 : 1 : case BF_MATCHER_IP6_SADDR:
414 : 1 : return _bf_cgroup_sock_addr_load_and_cmp(
415 : : program, matcher, offsetof(struct bpf_sock_addr, msg_src_ip6), 16);
416 : 1 : case BF_MATCHER_IP6_SNET:
417 : 1 : return _bf_cgroup_sock_addr_generate_net(
418 : : program, matcher, offsetof(struct bpf_sock_addr, msg_src_ip6), 16);
419 : 6 : case BF_MATCHER_IP6_DADDR:
420 : 6 : return _bf_cgroup_sock_addr_load_and_cmp(
421 : : program, matcher, offsetof(struct bpf_sock_addr, user_ip6), 16);
422 : 6 : case BF_MATCHER_IP6_DNET:
423 : 6 : return _bf_cgroup_sock_addr_generate_net(
424 : : program, matcher, offsetof(struct bpf_sock_addr, user_ip6), 16);
425 : 56 : case BF_MATCHER_META_DPORT:
426 : : case BF_MATCHER_TCP_DPORT:
427 : : case BF_MATCHER_UDP_DPORT:
428 : 56 : return _bf_cgroup_sock_addr_generate_port(program, matcher);
429 : 24 : case BF_MATCHER_SET:
430 : 24 : return _bf_cgroup_sock_addr_generate_set(program, matcher);
431 : 0 : default:
432 [ # # ]: 0 : return bf_err_r(-ENOTSUP,
433 : : "matcher '%s' not supported for cgroup_sock_addr",
434 : : bf_matcher_type_to_str(bf_matcher_get_type(matcher)));
435 : : }
436 : : }
437 : :
438 : : /**
439 : : * @brief Convert a standard verdict into a return value.
440 : : *
441 : : * @param verdict Verdict to convert. Must be valid.
442 : : * @param ret_code Cgroup return code. Can't be NULL.
443 : : * @return 0 on success, or a negative errno value on failure.
444 : : */
445 : 177 : static int _bf_cgroup_sock_addr_get_verdict(enum bf_verdict verdict,
446 : : int *ret_code)
447 : : {
448 : : assert(ret_code);
449 : :
450 [ + + - ]: 177 : switch (verdict) {
451 : 99 : case BF_VERDICT_ACCEPT:
452 : : case BF_VERDICT_NEXT:
453 : 99 : *ret_code = 1;
454 : 99 : return 0;
455 : 78 : case BF_VERDICT_DROP:
456 : 78 : *ret_code = 0;
457 : 78 : return 0;
458 : : default:
459 : : return -ENOTSUP;
460 : : }
461 : : }
462 : :
463 : : #define _BF_SOCK_ADDR_NS_PID_OFF \
464 : : BF_PROG_SCR_OFF(offsetof(struct bf_runtime_sock_addr, ns_pid))
465 : :
466 : : /* The kernel pointers dereferenced to reach the namespace-local PID are staged
467 : : * in the scratch area, right after `bf_runtime_sock_addr`. */
468 : : #define _BF_SOCK_ADDR_NS_PID_WALK_OFF \
469 : : BF_PROG_SCR_OFF(sizeof(struct bf_runtime_sock_addr))
470 : :
471 : : static_assert(sizeof(struct bf_runtime_sock_addr) + sizeof(__u64) <= 64,
472 : : "no room left in the scratch area for the PID namespace walk");
473 : :
474 : : /**
475 : : * @brief Store the process' namespace-local PID into the staging area.
476 : : *
477 : : * `bpf_get_current_pid_tgid()` reports the PID as seen from the initial PID
478 : : * namespace. The namespace-local one is only reachable through the kernel
479 : : * structures: `current->group_leader->thread_pid->numbers[level].nr`, with
480 : : * `level` the depth of the process' own PID namespace. `level` is 0 in the
481 : : * initial namespace, where this yields the same value as
482 : : * `bpf_get_current_pid_tgid()`.
483 : : *
484 : : * `numbers` is indexed with a runtime value, and the verifier rejects variable
485 : : * offsets on BTF pointers, so the walk uses `bpf_probe_read_kernel()` and
486 : : * computes the addresses as scalars.
487 : : *
488 : : * `ns_pid` is left to 0 if `level` is 0: the process' only PID is the one
489 : : * reported by `bpf_get_current_pid_tgid()`. The PID allocator never hands out
490 : : * 0, so 0 unambiguously means "no namespace-local PID to report", including
491 : : * when the walk failed: `bpf_probe_read_kernel()` zeroes its destination on
492 : : * failure, so an unreadable address leaves the slot zeroed instead of stale.
493 : : *
494 : : * @todo `pid.level` and `upid.nr` are assumed to be 4 bytes: `level` is loaded
495 : : * back from the slot with `BPF_W`, and `ns_pid` is a `__u32`. Validate their
496 : : * size against the BTF data, so a kernel changing either of them fails the
497 : : * program generation instead of logging a wrong PID.
498 : : *
499 : : * @param program Program to emit the instructions into. Can't be NULL.
500 : : * @return 0 on success, or a negative errno value on failure.
501 : : */
502 : 4 : static int _bf_cgroup_sock_addr_store_ns_pid(struct bf_program *program)
503 : : {
504 : : int group_leader_off;
505 : : int thread_pid_off;
506 : : int level_off;
507 : :
508 : : assert(program);
509 : :
510 : : /* The pointers are only needed to reach the next one, they are read into
511 : : * the walk slot. `level` and the PID itself are read into the `ns_pid`
512 : : * slot, which then holds the 0 to report if the walk stops at the initial
513 : : * namespace. */
514 : :
515 : : // Walk: current->group_leader
516 : 4 : group_leader_off = bf_btf_get_field_off("task_struct", "group_leader");
517 [ + - ]: 4 : if (group_leader_off < 0)
518 : : return group_leader_off;
519 : :
520 [ + - ]: 4 : EMIT(program, BPF_EMIT_CALL(BPF_FUNC_get_current_task));
521 [ + - ]: 4 : EMIT(program, BPF_MOV64_REG(BPF_REG_1, BPF_REG_10));
522 [ + - ]: 4 : EMIT(program,
523 : : BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, _BF_SOCK_ADDR_NS_PID_WALK_OFF));
524 [ + - ]: 4 : EMIT(program, BPF_MOV64_IMM(BPF_REG_2, 8));
525 [ + - ]: 4 : EMIT(program, BPF_MOV64_REG(BPF_REG_3, BPF_REG_0));
526 [ + - ]: 4 : EMIT(program, BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, group_leader_off));
527 [ + - ]: 4 : EMIT(program, BPF_EMIT_CALL(BPF_FUNC_probe_read_kernel));
528 : :
529 : : // Walk: current->group_leader->thread_pid
530 : 4 : thread_pid_off = bf_btf_get_field_off("task_struct", "thread_pid");
531 [ + - ]: 4 : if (thread_pid_off < 0)
532 : : return thread_pid_off;
533 : :
534 [ + - ]: 4 : EMIT(program, BPF_MOV64_REG(BPF_REG_1, BPF_REG_10));
535 [ + - ]: 4 : EMIT(program,
536 : : BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, _BF_SOCK_ADDR_NS_PID_WALK_OFF));
537 [ + - ]: 4 : EMIT(program, BPF_MOV64_IMM(BPF_REG_2, 8));
538 [ + - ]: 4 : EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_10,
539 : : _BF_SOCK_ADDR_NS_PID_WALK_OFF));
540 [ + - ]: 4 : EMIT(program, BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, thread_pid_off));
541 [ + - ]: 4 : EMIT(program, BPF_EMIT_CALL(BPF_FUNC_probe_read_kernel));
542 : :
543 : : // Walk: current->group_leader->thread_pid->level
544 : 4 : level_off = bf_btf_get_field_off("pid", "level");
545 [ + - ]: 4 : if (level_off < 0)
546 : : return level_off;
547 : :
548 : : /* R9 is callee-saved and free at this point: the rate limiter in
549 : : * `_bf_program_generate_log()` is done with it. It keeps `thread_pid`
550 : : * across the `level` read, to index `numbers` with it. */
551 [ + - ]: 4 : EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_9, BPF_REG_10,
552 : : _BF_SOCK_ADDR_NS_PID_WALK_OFF));
553 [ + - ]: 4 : EMIT(program, BPF_MOV64_REG(BPF_REG_1, BPF_REG_10));
554 [ + - ]: 4 : EMIT(program, BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, _BF_SOCK_ADDR_NS_PID_OFF));
555 [ + - ]: 4 : EMIT(program, BPF_MOV64_IMM(BPF_REG_2, 4));
556 [ + - ]: 4 : EMIT(program, BPF_MOV64_REG(BPF_REG_3, BPF_REG_9));
557 [ + - ]: 4 : EMIT(program, BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, level_off));
558 [ + - ]: 4 : EMIT(program, BPF_EMIT_CALL(BPF_FUNC_probe_read_kernel));
559 : :
560 : : /* Walk: current->group_leader->thread_pid->numbers[level].nr, skipped
561 : : * for the initial namespace: the slot then keeps the level, which is the
562 : : * 0 to report. */
563 [ + - ]: 4 : EMIT(program,
564 : : BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_10, _BF_SOCK_ADDR_NS_PID_OFF));
565 : :
566 : : {
567 : : int numbers_off;
568 : : int nr_off;
569 : : int upid_size;
570 : 4 : _clean_bf_jmpctx_ struct bf_jmpctx _ =
571 [ + - ]: 4 : bf_jmpctx_get(program, BPF_JMP_IMM(BPF_JEQ, BPF_REG_3, 0, 0));
572 : :
573 : 4 : numbers_off = bf_btf_get_field_off("pid", "numbers");
574 [ + - ]: 4 : if (numbers_off < 0)
575 : : return numbers_off;
576 : :
577 : 4 : nr_off = bf_btf_get_field_off("upid", "nr");
578 [ + - ]: 4 : if (nr_off < 0)
579 : : return nr_off;
580 : :
581 : 4 : upid_size = bf_btf_get_type_size("upid");
582 [ + - ]: 4 : if (upid_size < 0)
583 : : return upid_size;
584 : :
585 [ + - ]: 4 : EMIT(program, BPF_MOV64_REG(BPF_REG_1, BPF_REG_10));
586 [ + - ]: 4 : EMIT(program,
587 : : BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, _BF_SOCK_ADDR_NS_PID_OFF));
588 [ + - ]: 4 : EMIT(program, BPF_MOV64_IMM(BPF_REG_2, 4));
589 [ + - ]: 4 : EMIT(program, BPF_ALU64_IMM(BPF_MUL, BPF_REG_3, upid_size));
590 [ + - ]: 4 : EMIT(program, BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, numbers_off + nr_off));
591 [ + - ]: 4 : EMIT(program, BPF_ALU64_REG(BPF_ADD, BPF_REG_3, BPF_REG_9));
592 [ + - ]: 4 : EMIT(program, BPF_EMIT_CALL(BPF_FUNC_probe_read_kernel));
593 : : }
594 : :
595 : 4 : return 0;
596 : : }
597 : :
598 : 4 : static int _bf_cgroup_sock_addr_gen_inline_log(struct bf_program *program,
599 : : const struct bf_rule *rule)
600 : : {
601 : : uint8_t captured_fields = 0;
602 : : bool has_saddr = false;
603 : : size_t addr_size = 0;
604 : : size_t saddr_off = 0;
605 : : size_t daddr_off = 0;
606 : : int r;
607 : :
608 : : assert(program);
609 : : assert(rule);
610 : :
611 : : // Zero the staging area: connect hooks have no source address,
612 : : // and IPv4 hooks only write 4 of the 16 address bytes.
613 [ + + ]: 28 : for (int i = 0; i < (int)sizeof(struct bf_runtime_sock_addr); i += 8)
614 [ + - ]: 24 : EMIT(program, BPF_ST_MEM(BPF_DW, BPF_REG_10, BF_PROG_SCR_OFF(i), 0));
615 : :
616 [ + + - + : 4 : switch (program->runtime.chain->hook) {
+ ]
617 : : case BF_HOOK_CGROUP_SOCK_ADDR_SENDMSG4:
618 : : has_saddr = true;
619 : : saddr_off = offsetof(struct bpf_sock_addr, msg_src_ip4);
620 : : __attribute__((fallthrough));
621 : : case BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4:
622 : : addr_size = 4;
623 : : daddr_off = offsetof(struct bpf_sock_addr, user_ip4);
624 : : break;
625 : : case BF_HOOK_CGROUP_SOCK_ADDR_SENDMSG6:
626 : : has_saddr = true;
627 : : saddr_off = offsetof(struct bpf_sock_addr, msg_src_ip6);
628 : : __attribute__((fallthrough));
629 : : case BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6:
630 : : addr_size = 16;
631 : : daddr_off = offsetof(struct bpf_sock_addr, user_ip6);
632 : : break;
633 : 0 : default:
634 [ # # ]: 0 : return bf_err_r(-ENOTSUP, "unexpected hook: %s",
635 : : bf_hook_to_str(program->runtime.chain->hook));
636 : : }
637 : :
638 : : if (has_saddr) {
639 : : captured_fields |= BF_LOG_SOCK_ADDR_SADDR;
640 : 2 : r = _bf_cgroup_sock_addr_load_field(program, saddr_off, addr_size,
641 : : BPF_REG_1);
642 [ + - ]: 2 : if (r)
643 : : return r;
644 : 2 : r = _bf_cgroup_sock_addr_store_field(
645 : : program,
646 : : BF_PROG_SCR_OFF(offsetof(struct bf_runtime_sock_addr, saddr)),
647 : : addr_size, BPF_REG_1);
648 [ + - ]: 2 : if (r)
649 : : return r;
650 : : }
651 : :
652 : 4 : r = _bf_cgroup_sock_addr_load_field(program, daddr_off, addr_size,
653 : : BPF_REG_1);
654 [ + - ]: 4 : if (r)
655 : : return r;
656 : 4 : r = _bf_cgroup_sock_addr_store_field(
657 : : program, BF_PROG_SCR_OFF(offsetof(struct bf_runtime_sock_addr, daddr)),
658 : : addr_size, BPF_REG_1);
659 [ + - ]: 4 : if (r)
660 : : return r;
661 : :
662 : : /* Destination port: valid for all cgroup_sock_addr hooks.
663 : : * user_port is __be32; BSWAP 16 converts to host order. */
664 : 4 : r = _bf_cgroup_sock_addr_load_field(
665 : : program, offsetof(struct bpf_sock_addr, user_port), 4, BPF_REG_1);
666 [ + - ]: 4 : if (r)
667 : : return r;
668 [ + - ]: 4 : EMIT(program, BPF_BSWAP(BPF_REG_1, 16));
669 : 4 : r = _bf_cgroup_sock_addr_store_field(
670 : : program, BF_PROG_SCR_OFF(offsetof(struct bf_runtime_sock_addr, dport)),
671 : : 2, BPF_REG_1);
672 [ + - ]: 4 : if (r)
673 : : return r;
674 : :
675 : 4 : r = _bf_cgroup_sock_addr_store_ns_pid(program);
676 [ + - ]: 4 : if (r)
677 : : return r;
678 : :
679 [ + - ]: 4 : EMIT(program, BPF_MOV64_REG(BPF_REG_1, BPF_REG_10));
680 [ + - ]: 4 : EMIT(program, BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, BF_PROG_CTX_OFF(arg)));
681 [ + - ]: 4 : EMIT(program, BPF_MOV64_IMM(BPF_REG_2, rule->index));
682 [ + - ]: 4 : EMIT(program, BPF_MOV64_IMM(BPF_REG_3, rule->verdict));
683 [ + - ]: 4 : EMIT(program, BPF_MOV64_REG(BPF_REG_4, BPF_REG_7));
684 [ + - ]: 4 : EMIT(program, BPF_ALU64_IMM(BPF_LSH, BPF_REG_4, 16));
685 [ + - ]: 4 : EMIT(program, BPF_ALU64_REG(BPF_OR, BPF_REG_4, BPF_REG_8));
686 [ + - ]: 4 : EMIT(program, BPF_MOV64_IMM(BPF_REG_5, captured_fields));
687 : 4 : EMIT_FIXUP_ELFSTUB(program, BF_ELFSTUB_SOCK_ADDR_LOG);
688 : :
689 : : return 0;
690 : : }
691 : :
692 : : const struct bf_flavor_ops bf_flavor_ops_cgroup_sock_addr = {
693 : : .gen_inline_prologue = _bf_cgroup_sock_addr_gen_inline_prologue,
694 : : .gen_inline_epilogue = _bf_cgroup_sock_addr_gen_inline_epilogue,
695 : : .get_verdict = _bf_cgroup_sock_addr_get_verdict,
696 : : .gen_inline_matcher = _bf_cgroup_sock_addr_gen_inline_matcher,
697 : : .gen_inline_log = _bf_cgroup_sock_addr_gen_inline_log,
698 : : };
|