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/prog/link.h"
17 : #include "bpfilter/cgen/program.h"
18 : #include "bpfilter/cgen/stub.h"
19 : #include "bpfilter/cgen/swich.h"
20 : #include "core/btf.h"
21 : #include "core/flavor.h"
22 : #include "core/helper.h"
23 : #include "core/hook.h"
24 : #include "core/list.h"
25 : #include "core/logger.h"
26 : #include "core/verdict.h"
27 : #include "linux/bpf.h"
28 :
29 : #include "external/filter.h"
30 :
31 : static int _bf_cgroup_gen_inline_prologue(struct bf_program *program);
32 : static int _bf_cgroup_gen_inline_epilogue(struct bf_program *program);
33 : static int _bf_cgroup_get_verdict(enum bf_verdict verdict);
34 : static int _bf_cgroup_attach_prog(
35 : struct bf_program *new_prog, struct bf_program *old_prog,
36 : int (*get_new_link_cb)(struct bf_program *prog, struct bf_link *old_link,
37 : struct bf_link **new_link));
38 : static int _bf_cgroup_detach_prog(struct bf_program *program);
39 :
40 : const struct bf_flavor_ops bf_flavor_ops_cgroup = {
41 : .gen_inline_prologue = _bf_cgroup_gen_inline_prologue,
42 : .gen_inline_epilogue = _bf_cgroup_gen_inline_epilogue,
43 : .get_verdict = _bf_cgroup_get_verdict,
44 : .attach_prog = _bf_cgroup_attach_prog,
45 : .detach_prog = _bf_cgroup_detach_prog,
46 : };
47 :
48 : // Forward definition to avoid headers clusterfuck.
49 : uint16_t htons(uint16_t hostshort);
50 :
51 0 : static int _bf_cgroup_gen_inline_prologue(struct bf_program *program)
52 : {
53 : int offset;
54 : int r;
55 :
56 0 : bf_assert(program);
57 :
58 : // Calculate the packet size (+ETH_HLEN) and store it into the runtime context
59 0 : EMIT(program, BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1,
60 : offsetof(struct __sk_buff, data)));
61 0 : EMIT(program, BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1,
62 : offsetof(struct __sk_buff, data_end)));
63 0 : EMIT(program, BPF_ALU64_REG(BPF_SUB, BPF_REG_3, BPF_REG_2));
64 0 : EMIT(program, BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, ETH_HLEN));
65 0 : EMIT(program,
66 : BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_3, BF_PROG_CTX_OFF(pkt_size)));
67 :
68 : /** The @c __sk_buff structure contains two fields related to the interface
69 : * index: @c ingress_ifindex and @c ifindex . @c ingress_ifindex is the
70 : * interface index the packet has been received on. However, we use
71 : * @c ifindex which is the interface index the packet is processed by: if
72 : * a packet is redirected locally from interface #1 to interface #2, then
73 : * @c ingress_ifindex will contain @c 1 but @c ifindex will contains @c 2 .
74 : * For egress, only @c ifindex is used.
75 : */
76 0 : if ((r = bf_btf_get_field_off("__sk_buff", "ifindex")) < 0)
77 : return r;
78 0 : EMIT(program, BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, r));
79 0 : EMIT(program,
80 : BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_2, BF_PROG_CTX_OFF(ifindex)));
81 :
82 : /* BPF_PROG_TYPE_CGROUP_SKB doesn't provide access the the Ethernet header,
83 : * so we can't parse it and discover the L3 protocol ID.
84 : * Instead, we use the __sk_buff.family value and convert it to the
85 : * corresponding ethertype. */
86 0 : if ((offset = bf_btf_get_field_off("__sk_buff", "family")) < 0)
87 : return offset;
88 0 : EMIT(program, BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, offset));
89 :
90 : {
91 0 : _cleanup_bf_swich_ struct bf_swich swich =
92 0 : bf_swich_get(program, BPF_REG_2);
93 :
94 0 : EMIT_SWICH_OPTION(&swich, AF_INET,
95 : BPF_MOV64_IMM(BPF_REG_7, htons(ETH_P_IP)));
96 0 : EMIT_SWICH_OPTION(&swich, AF_INET6,
97 : BPF_MOV64_IMM(BPF_REG_7, htons(ETH_P_IPV6)));
98 0 : EMIT_SWICH_DEFAULT(&swich, BPF_MOV64_IMM(BPF_REG_7, 0));
99 :
100 0 : r = bf_swich_generate(&swich);
101 0 : if (r)
102 : return r;
103 : }
104 :
105 0 : EMIT(program, BPF_ST_MEM(BPF_W, BPF_REG_10, BF_PROG_CTX_OFF(l3_offset), 0));
106 :
107 0 : r = bf_stub_make_ctx_skb_dynptr(program, BPF_REG_1);
108 0 : if (r)
109 : return r;
110 :
111 0 : r = bf_stub_parse_l3_hdr(program);
112 0 : if (r)
113 : return r;
114 :
115 0 : r = bf_stub_parse_l4_hdr(program);
116 : if (r)
117 : return r;
118 :
119 : return 0;
120 : }
121 :
122 0 : static int _bf_cgroup_gen_inline_epilogue(struct bf_program *program)
123 : {
124 : UNUSED(program);
125 :
126 0 : return 0;
127 : }
128 :
129 : /**
130 : * Convert a standard verdict into a return value.
131 : *
132 : * @param verdict Verdict to convert. Must be valid.
133 : * @return TC return code corresponding to the verdict, as an integer.
134 : */
135 0 : static int _bf_cgroup_get_verdict(enum bf_verdict verdict)
136 : {
137 0 : bf_assert(0 <= verdict && verdict < _BF_TERMINAL_VERDICT_MAX);
138 :
139 : static const int verdicts[] = {
140 : [BF_VERDICT_ACCEPT] = 1,
141 : [BF_VERDICT_DROP] = 0,
142 : };
143 :
144 : static_assert(ARRAY_SIZE(verdicts) == _BF_TERMINAL_VERDICT_MAX);
145 :
146 0 : return verdicts[verdict];
147 : }
148 :
149 0 : static int _bf_cgroup_attach_prog(
150 : struct bf_program *new_prog, struct bf_program *old_prog,
151 : int (*get_new_link_cb)(struct bf_program *prog, struct bf_link *old_link,
152 : struct bf_link **new_link))
153 : {
154 : struct bf_link *new_link;
155 : struct bf_link *old_link;
156 : int new_fd;
157 : const char *cgroup_path;
158 : int r;
159 :
160 0 : bf_assert(new_prog && get_new_link_cb);
161 :
162 0 : old_link = old_prog ? bf_list_get_at(&old_prog->links, 0) : NULL;
163 0 : new_fd = new_prog->runtime.prog_fd;
164 0 : cgroup_path = new_prog->runtime.chain->hook_opts.cgroup;
165 :
166 0 : r = get_new_link_cb(new_prog, old_link, &new_link);
167 0 : if (r)
168 0 : return bf_err_r(r, "failed to create new cgroup link");
169 :
170 0 : if (old_link) {
171 0 : r = bf_link_update(new_link, new_fd);
172 0 : if (r) {
173 0 : return bf_err_r(
174 : r, "failed to update existing link for cgroup bf_program");
175 : }
176 : } else {
177 0 : r = bf_link_attach_cgroup(new_link, new_fd, cgroup_path);
178 0 : if (r)
179 0 : return bf_err_r(r, "failed to attach cgroup program");
180 : }
181 :
182 : return 0;
183 : }
184 :
185 : /**
186 : * Detach the cgroup BPF program.
187 : *
188 : * @param program Attached cgroup BPF program. Can't be NULL.
189 : * @return 0 on success, negative errno value on failure.
190 : */
191 0 : static int _bf_cgroup_detach_prog(struct bf_program *program)
192 : {
193 0 : bf_assert(program);
194 :
195 0 : return bf_link_detach(bf_list_get_at(&program->links, 0));
196 : }
|