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 : 258 : 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 [ + - ]: 258 : EMIT(program, BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1,
36 : : offsetof(struct __sk_buff, len)));
37 [ + - ]: 258 : EMIT(program, BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, ETH_HLEN));
38 [ + - ]: 258 : 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 [ + - ]: 258 : if ((r = bf_btf_get_field_off("__sk_buff", "ifindex")) < 0)
50 : : return r;
51 [ + - ]: 258 : EMIT(program, BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, r));
52 [ + - ]: 258 : 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 [ + - ]: 258 : if ((offset = bf_btf_get_field_off("__sk_buff", "protocol")) < 0)
62 : : return offset;
63 [ + - ]: 258 : EMIT(program, BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_1, offset));
64 : :
65 [ + - ]: 258 : EMIT(program, BPF_ST_MEM(BPF_DW, BPF_REG_10, BF_PROG_CTX_OFF(l2_hdr), 0));
66 [ + - ]: 258 : EMIT(program, BPF_ST_MEM(BPF_W, BPF_REG_10, BF_PROG_CTX_OFF(l3_offset), 0));
67 : :
68 : 258 : r = bf_stub_make_ctx_skb_dynptr(program, BPF_REG_1);
69 [ + - ]: 258 : if (r)
70 : : return r;
71 : :
72 : 258 : r = bf_stub_parse_l3_hdr(program);
73 [ + - ]: 258 : if (r)
74 : : return r;
75 : :
76 : 258 : r = bf_stub_parse_l4_hdr(program);
77 : : if (r)
78 : : return r;
79 : :
80 : : return 0;
81 : : }
82 : :
83 : 257 : static int _bf_cgroup_skb_gen_inline_epilogue(struct bf_program *program)
84 : : {
85 : : (void)program;
86 : :
87 : 257 : return 0;
88 : : }
89 : :
90 : 4 : static int _bf_cgroup_skb_gen_inline_set_mark(struct bf_program *program,
91 : : uint32_t mark)
92 : : {
93 [ + - ]: 4 : EMIT(program,
94 : : BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, BF_PROG_CTX_OFF(arg)));
95 [ + - ]: 4 : EMIT(program, BPF_MOV64_IMM(BPF_REG_2, mark));
96 : 4 : EMIT(program, BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_2,
97 : : offsetof(struct __sk_buff, mark)));
98 : :
99 : : return 0;
100 : : }
101 : :
102 : 528 : static int _bf_cgroup_skb_gen_inline_matcher(struct bf_program *program,
103 : : const struct bf_matcher *matcher)
104 : : {
105 : : assert(program);
106 : : assert(matcher);
107 : :
108 [ + + ]: 528 : switch (bf_matcher_get_type(matcher)) {
109 : 12 : case BF_MATCHER_META_MARK:
110 [ + - ]: 12 : EMIT(program,
111 : : BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, BF_PROG_CTX_OFF(arg)));
112 [ + - ]: 12 : EMIT(program, BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_1,
113 : : offsetof(struct __sk_buff, mark)));
114 : :
115 : 12 : return bf_cmp_value(program, matcher, bf_matcher_payload(matcher), 4,
116 : : BPF_REG_1);
117 : 516 : default:
118 : 516 : return bf_packet_gen_inline_matcher(program, matcher);
119 : : }
120 : : }
121 : :
122 : : /**
123 : : * Convert a standard verdict into a return value.
124 : : *
125 : : * @param verdict Verdict to convert. Must be valid.
126 : : * @param ret_code Cgroup return code. Can't be NULL.
127 : : * @return 0 on success, or a negative errno value on failure.
128 : : */
129 : 1074 : static int _bf_cgroup_skb_get_verdict(enum bf_verdict verdict, int *ret_code)
130 : : {
131 : : assert(ret_code);
132 : :
133 [ + + - ]: 1074 : switch (verdict) {
134 : 819 : case BF_VERDICT_ACCEPT:
135 : : case BF_VERDICT_NEXT:
136 : 819 : *ret_code = 1;
137 : 819 : return 0;
138 : 255 : case BF_VERDICT_DROP:
139 : 255 : *ret_code = 0;
140 : 255 : return 0;
141 : : default:
142 : : return -ENOTSUP;
143 : : }
144 : : }
145 : :
146 : : const struct bf_flavor_ops bf_flavor_ops_cgroup_skb = {
147 : : .gen_inline_prologue = _bf_cgroup_skb_gen_inline_prologue,
148 : : .gen_inline_epilogue = _bf_cgroup_skb_gen_inline_epilogue,
149 : : .gen_inline_set_mark = _bf_cgroup_skb_gen_inline_set_mark,
150 : : .get_verdict = _bf_cgroup_skb_get_verdict,
151 : : .gen_inline_matcher = _bf_cgroup_skb_gen_inline_matcher,
152 : : .gen_inline_log = bf_packet_gen_inline_log,
153 : : };
|