Branch data 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 "cgen/cgroup_skb.h"
7 : :
8 : : #include <linux/bpf_common.h>
9 : : #include <linux/if_ether.h>
10 : :
11 : : #include <stddef.h>
12 : : #include <stdint.h>
13 : :
14 : : #include <bpfilter/btf.h>
15 : : #include <bpfilter/flavor.h>
16 : : #include <bpfilter/helper.h>
17 : : #include <bpfilter/matcher.h>
18 : : #include <bpfilter/verdict.h>
19 : :
20 : : #include "cgen/matcher/cmp.h"
21 : : #include "cgen/packet.h"
22 : : #include "cgen/program.h"
23 : : #include "cgen/stub.h"
24 : : #include "filter.h"
25 : : #include "linux/bpf.h"
26 : :
27 : 257 : static int _bf_cgroup_skb_gen_inline_prologue(struct bf_program *program)
28 : : {
29 : : int offset;
30 : : int r;
31 : :
32 : : assert(program);
33 : :
34 : : // Copy the packet size (+ETH_HLEN) into the runtime context
35 [ + - ]: 257 : EMIT(program, BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1,
36 : : offsetof(struct __sk_buff, len)));
37 [ + - ]: 257 : EMIT(program, BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, ETH_HLEN));
38 [ + - ]: 257 : EMIT(program,
39 : : BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_3, BF_PROG_CTX_OFF(pkt_size)));
40 : :
41 : : /** The @c __sk_buff structure contains two fields related to the interface
42 : : * index: @c ingress_ifindex and @c ifindex . @c ingress_ifindex is the
43 : : * interface index the packet has been received on. However, we use
44 : : * @c ifindex which is the interface index the packet is processed by: if
45 : : * a packet is redirected locally from interface #1 to interface #2, then
46 : : * @c ingress_ifindex will contain @c 1 but @c ifindex will contains @c 2 .
47 : : * For egress, only @c ifindex is used.
48 : : */
49 [ + - ]: 257 : if ((r = bf_btf_get_field_off("__sk_buff", "ifindex")) < 0)
50 : : return r;
51 [ + - ]: 257 : EMIT(program, BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, r));
52 [ + - ]: 257 : EMIT(program,
53 : : BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_2, BF_PROG_CTX_OFF(ifindex)));
54 : :
55 : : /* BPF_PROG_TYPE_CGROUP_SKB doesn't provide access to the Ethernet header,
56 : : * so we can't parse it and discover the L3 protocol ID.
57 : : * Instead, we use __sk_buff.protocol: the packet's ethertype, in network
58 : : * byte order, as in the Ethernet header. Don't use __sk_buff.family: it is
59 : : * the socket's family, and an AF_INET6 socket can send and receive IPv4
60 : : * packets. */
61 [ + - ]: 257 : if ((offset = bf_btf_get_field_off("__sk_buff", "protocol")) < 0)
62 : : return offset;
63 [ + - ]: 257 : EMIT(program, BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_1, offset));
64 : :
65 [ + - ]: 257 : EMIT(program, BPF_ST_MEM(BPF_W, BPF_REG_10, BF_PROG_CTX_OFF(l3_offset), 0));
66 : :
67 : 257 : r = bf_stub_make_ctx_skb_dynptr(program, BPF_REG_1);
68 [ + - ]: 257 : if (r)
69 : : return r;
70 : :
71 : 257 : r = bf_stub_parse_l3_hdr(program);
72 [ + - ]: 257 : if (r)
73 : : return r;
74 : :
75 : 257 : r = bf_stub_parse_l4_hdr(program);
76 : : if (r)
77 : : return r;
78 : :
79 : : return 0;
80 : : }
81 : :
82 : 256 : static int _bf_cgroup_skb_gen_inline_epilogue(struct bf_program *program)
83 : : {
84 : : (void)program;
85 : :
86 : 256 : return 0;
87 : : }
88 : :
89 : 4 : static int _bf_cgroup_skb_gen_inline_set_mark(struct bf_program *program,
90 : : uint32_t mark)
91 : : {
92 [ + - ]: 4 : EMIT(program,
93 : : BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, BF_PROG_CTX_OFF(arg)));
94 [ + - ]: 4 : EMIT(program, BPF_MOV64_IMM(BPF_REG_2, mark));
95 : 4 : EMIT(program, BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_2,
96 : : offsetof(struct __sk_buff, mark)));
97 : :
98 : : return 0;
99 : : }
100 : :
101 : 527 : static int _bf_cgroup_skb_gen_inline_matcher(struct bf_program *program,
102 : : const struct bf_matcher *matcher)
103 : : {
104 : : assert(program);
105 : : assert(matcher);
106 : :
107 [ + + ]: 527 : switch (bf_matcher_get_type(matcher)) {
108 : 12 : case BF_MATCHER_META_MARK:
109 [ + - ]: 12 : EMIT(program,
110 : : BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, BF_PROG_CTX_OFF(arg)));
111 [ + - ]: 12 : EMIT(program, BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_1,
112 : : offsetof(struct __sk_buff, mark)));
113 : :
114 : 12 : return bf_cmp_value(program, matcher, bf_matcher_payload(matcher), 4,
115 : : BPF_REG_1);
116 : 515 : default:
117 : 515 : return bf_packet_gen_inline_matcher(program, matcher);
118 : : }
119 : : }
120 : :
121 : : /**
122 : : * Convert a standard verdict into a return value.
123 : : *
124 : : * @param verdict Verdict to convert. Must be valid.
125 : : * @param ret_code Cgroup return code. Can't be NULL.
126 : : * @return 0 on success, or a negative errno value on failure.
127 : : */
128 : 1070 : static int _bf_cgroup_skb_get_verdict(enum bf_verdict verdict, int *ret_code)
129 : : {
130 : : assert(ret_code);
131 : :
132 [ + + - ]: 1070 : switch (verdict) {
133 : 816 : case BF_VERDICT_ACCEPT:
134 : : case BF_VERDICT_NEXT:
135 : 816 : *ret_code = 1;
136 : 816 : return 0;
137 : 254 : case BF_VERDICT_DROP:
138 : 254 : *ret_code = 0;
139 : 254 : return 0;
140 : : default:
141 : : return -ENOTSUP;
142 : : }
143 : : }
144 : :
145 : : const struct bf_flavor_ops bf_flavor_ops_cgroup_skb = {
146 : : .gen_inline_prologue = _bf_cgroup_skb_gen_inline_prologue,
147 : : .gen_inline_epilogue = _bf_cgroup_skb_gen_inline_epilogue,
148 : : .gen_inline_set_mark = _bf_cgroup_skb_gen_inline_set_mark,
149 : : .get_verdict = _bf_cgroup_skb_get_verdict,
150 : : .gen_inline_matcher = _bf_cgroup_skb_gen_inline_matcher,
151 : : .gen_inline_log = bf_packet_gen_inline_log,
152 : : };
|