Line data Source code
1 : /* SPDX-License-Identifier: GPL-2.0-only */
2 : /*
3 : * Copyright (c) 2023 Meta Platforms, Inc. and affiliates.
4 : */
5 :
6 : #include "bpfilter/cgen/cgroup.h"
7 :
8 : #include <linux/bpf_common.h>
9 : #include <linux/if_ether.h>
10 :
11 : #include <stddef.h>
12 : #include <stdint.h>
13 : #include <sys/socket.h>
14 :
15 : #include "bpfilter/cgen/cgen.h"
16 : #include "bpfilter/cgen/program.h"
17 : #include "bpfilter/cgen/stub.h"
18 : #include "bpfilter/cgen/swich.h"
19 : #include "core/btf.h"
20 : #include "core/flavor.h"
21 : #include "core/helper.h"
22 : #include "core/verdict.h"
23 : #include "linux/bpf.h"
24 :
25 : #include "external/filter.h"
26 :
27 : // Forward definition to avoid headers clusterfuck.
28 : uint16_t htons(uint16_t hostshort);
29 :
30 0 : static int _bf_cgroup_gen_inline_prologue(struct bf_program *program)
31 : {
32 : int offset;
33 : int r;
34 :
35 0 : bf_assert(program);
36 :
37 : // Calculate the packet size (+ETH_HLEN) and store it into the runtime context
38 0 : EMIT(program, BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1,
39 : offsetof(struct __sk_buff, data)));
40 0 : EMIT(program, BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1,
41 : offsetof(struct __sk_buff, data_end)));
42 0 : EMIT(program, BPF_ALU64_REG(BPF_SUB, BPF_REG_3, BPF_REG_2));
43 0 : EMIT(program, BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, ETH_HLEN));
44 0 : EMIT(program,
45 : BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_3, BF_PROG_CTX_OFF(pkt_size)));
46 :
47 : /** The @c __sk_buff structure contains two fields related to the interface
48 : * index: @c ingress_ifindex and @c ifindex . @c ingress_ifindex is the
49 : * interface index the packet has been received on. However, we use
50 : * @c ifindex which is the interface index the packet is processed by: if
51 : * a packet is redirected locally from interface #1 to interface #2, then
52 : * @c ingress_ifindex will contain @c 1 but @c ifindex will contains @c 2 .
53 : * For egress, only @c ifindex is used.
54 : */
55 0 : if ((r = bf_btf_get_field_off("__sk_buff", "ifindex")) < 0)
56 : return r;
57 0 : EMIT(program, BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, r));
58 0 : EMIT(program,
59 : BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_2, BF_PROG_CTX_OFF(ifindex)));
60 :
61 : /* BPF_PROG_TYPE_CGROUP_SKB doesn't provide access the the Ethernet header,
62 : * so we can't parse it and discover the L3 protocol ID.
63 : * Instead, we use the __sk_buff.family value and convert it to the
64 : * corresponding ethertype. */
65 0 : if ((offset = bf_btf_get_field_off("__sk_buff", "family")) < 0)
66 : return offset;
67 0 : EMIT(program, BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, offset));
68 :
69 : {
70 0 : _cleanup_bf_swich_ struct bf_swich swich =
71 0 : bf_swich_get(program, BPF_REG_2);
72 :
73 0 : EMIT_SWICH_OPTION(&swich, AF_INET,
74 : BPF_MOV64_IMM(BPF_REG_7, htons(ETH_P_IP)));
75 0 : EMIT_SWICH_OPTION(&swich, AF_INET6,
76 : BPF_MOV64_IMM(BPF_REG_7, htons(ETH_P_IPV6)));
77 0 : EMIT_SWICH_DEFAULT(&swich, BPF_MOV64_IMM(BPF_REG_7, 0));
78 :
79 0 : r = bf_swich_generate(&swich);
80 0 : if (r)
81 : return r;
82 : }
83 :
84 0 : EMIT(program, BPF_ST_MEM(BPF_W, BPF_REG_10, BF_PROG_CTX_OFF(l3_offset), 0));
85 :
86 0 : r = bf_stub_make_ctx_skb_dynptr(program, BPF_REG_1);
87 0 : if (r)
88 : return r;
89 :
90 0 : r = bf_stub_parse_l3_hdr(program);
91 0 : if (r)
92 : return r;
93 :
94 0 : r = bf_stub_parse_l4_hdr(program);
95 : if (r)
96 : return r;
97 :
98 : return 0;
99 : }
100 :
101 0 : static int _bf_cgroup_gen_inline_epilogue(struct bf_program *program)
102 : {
103 : UNUSED(program);
104 :
105 0 : return 0;
106 : }
107 :
108 : /**
109 : * Convert a standard verdict into a return value.
110 : *
111 : * @param verdict Verdict to convert. Must be valid.
112 : * @return TC return code corresponding to the verdict, as an integer.
113 : */
114 0 : static int _bf_cgroup_get_verdict(enum bf_verdict verdict)
115 : {
116 0 : bf_assert(0 <= verdict && verdict < _BF_TERMINAL_VERDICT_MAX);
117 :
118 : static const int verdicts[] = {
119 : [BF_VERDICT_ACCEPT] = 1,
120 : [BF_VERDICT_DROP] = 0,
121 : };
122 :
123 : static_assert(ARRAY_SIZE(verdicts) == _BF_TERMINAL_VERDICT_MAX);
124 :
125 0 : return verdicts[verdict];
126 : }
127 :
128 : const struct bf_flavor_ops bf_flavor_ops_cgroup = {
129 : .gen_inline_prologue = _bf_cgroup_gen_inline_prologue,
130 : .gen_inline_epilogue = _bf_cgroup_gen_inline_epilogue,
131 : .get_verdict = _bf_cgroup_get_verdict,
132 : };
|