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/nf.h"
7 : :
8 : : #include <linux/bpf.h>
9 : : #include <linux/bpf_common.h>
10 : : #include <linux/if_ether.h>
11 : : #include <linux/netfilter.h>
12 : :
13 : : #include <stdbool.h>
14 : : #include <stddef.h>
15 : : #include <stdint.h>
16 : : #include <sys/socket.h>
17 : :
18 : : #include <bpfilter/btf.h>
19 : : #include <bpfilter/flavor.h>
20 : : #include <bpfilter/helper.h>
21 : : #include <bpfilter/hook.h>
22 : : #include <bpfilter/matcher.h>
23 : : #include <bpfilter/verdict.h>
24 : :
25 : : #include "cgen/jmp.h"
26 : : #include "cgen/matcher/cmp.h"
27 : : #include "cgen/packet.h"
28 : : #include "cgen/program.h"
29 : : #include "cgen/stub.h"
30 : : #include "cgen/swich.h"
31 : : #include "filter.h"
32 : :
33 : : #define BF_NF_PRIO_EVEN 2
34 : : #define BF_NF_PRIO_ODD 1
35 : :
36 : : // Forward definition to avoid headers clusterfuck.
37 : : uint16_t htons(uint16_t hostshort);
38 : :
39 : : static inline bool _bf_nf_hook_is_ingress(enum bf_hook hook)
40 : : {
41 : 618 : return hook == BF_HOOK_NF_PRE_ROUTING || hook == BF_HOOK_NF_LOCAL_IN ||
42 : : hook == BF_HOOK_NF_FORWARD;
43 : : }
44 : :
45 : 618 : static int _bf_nf_gen_inline_prologue(struct bf_program *program)
46 : : {
47 : : int r;
48 : : int offset;
49 : :
50 : : assert(program);
51 : :
52 : : // Copy the ifindex from to bpf_nf_ctx.state.{in,out}.ifindex the runtime context
53 [ + - ]: 618 : if ((offset = bf_btf_get_field_off("bpf_nf_ctx", "state")) < 0)
54 : : return offset;
55 [ + - ]: 618 : EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, offset));
56 [ + + ]: 618 : if (_bf_nf_hook_is_ingress(program->runtime.chain->hook)) {
57 [ + - ]: 376 : if ((offset = bf_btf_get_field_off("nf_hook_state", "in")) < 0)
58 : : return offset;
59 [ + - ]: 376 : EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_2, offset));
60 : : } else {
61 [ + - ]: 242 : if ((offset = bf_btf_get_field_off("nf_hook_state", "out")) < 0)
62 : : return offset;
63 [ + - ]: 242 : EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_2, offset));
64 : : }
65 : :
66 [ + - ]: 618 : if ((offset = bf_btf_get_field_off("net_device", "ifindex")) < 0)
67 : : return offset;
68 [ + - ]: 618 : EMIT(program, BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_3, offset));
69 [ + - ]: 618 : EMIT(program,
70 : : BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_4, BF_PROG_CTX_OFF(ifindex)));
71 : :
72 : : /* BPF_PROG_TYPE_NETFILTER doesn't provide access to the Ethernet header,
73 : : * so we can't parse it and discover the L3 protocol ID.
74 : : * Instead, we use the nf_hook_state.pf value and convert it to the
75 : : * corresponding ethertype. */
76 [ + - ]: 618 : if ((offset = bf_btf_get_field_off("nf_hook_state", "pf")) < 0)
77 : : return offset;
78 [ + - ]: 618 : EMIT(program, BPF_LDX_MEM(BPF_B, BPF_REG_3, BPF_REG_2, offset));
79 : :
80 : : {
81 : 618 : _clean_bf_swich_ struct bf_swich swich =
82 [ - + ]: 618 : bf_swich_get(program, BPF_REG_3);
83 : :
84 [ - + ]: 618 : EMIT_SWICH_OPTION(&swich, AF_INET,
85 : : BPF_MOV64_IMM(BPF_REG_7, htons(ETH_P_IP)));
86 [ - + ]: 618 : EMIT_SWICH_OPTION(&swich, AF_INET6,
87 : : BPF_MOV64_IMM(BPF_REG_7, htons(ETH_P_IPV6)));
88 [ - + ]: 618 : EMIT_SWICH_DEFAULT(&swich, BPF_MOV64_IMM(BPF_REG_7, 0));
89 : :
90 : 618 : r = bf_swich_generate(&swich);
91 [ + - ]: 618 : if (r)
92 : : return r;
93 : : }
94 : :
95 [ + - ]: 618 : EMIT(program, BPF_ST_MEM(BPF_DW, BPF_REG_10, BF_PROG_CTX_OFF(l2_hdr), 0));
96 [ + - ]: 618 : EMIT(program, BPF_ST_MEM(BPF_W, BPF_REG_10, BF_PROG_CTX_OFF(l3_offset), 0));
97 : :
98 : : // Calculate the packet size (+ETH_HLEN) and store it into the runtime context
99 [ + - ]: 618 : if ((offset = bf_btf_get_field_off("bpf_nf_ctx", "skb")) < 0)
100 : : return offset;
101 [ + - ]: 618 : EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, offset));
102 [ + - ]: 618 : if ((offset = bf_btf_get_field_off("sk_buff", "len")) < 0)
103 : : return offset;
104 [ + - ]: 618 : EMIT(program, BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, offset));
105 [ + - ]: 618 : EMIT(program, BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, ETH_HLEN));
106 [ + - ]: 618 : EMIT(program,
107 : : BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_2, BF_PROG_CTX_OFF(pkt_size)));
108 : :
109 : 618 : r = bf_stub_make_ctx_skb_dynptr(program, BPF_REG_1);
110 [ + - ]: 618 : if (r)
111 : : return r;
112 : :
113 : 618 : r = bf_stub_parse_l3_hdr(program);
114 [ + - ]: 618 : if (r)
115 : : return r;
116 : :
117 : 618 : r = bf_stub_parse_l4_hdr(program);
118 : : if (r)
119 : : return r;
120 : :
121 : : return 0;
122 : : }
123 : :
124 : 617 : static int _bf_nf_gen_inline_epilogue(struct bf_program *program)
125 : : {
126 : : (void)program;
127 : :
128 : 617 : return 0;
129 : : }
130 : :
131 : 1285 : static int _bf_nf_gen_inline_matcher(struct bf_program *program,
132 : : const struct bf_matcher *matcher)
133 : : {
134 : : int offset;
135 : :
136 : : assert(program);
137 : : assert(matcher);
138 : :
139 [ + + ]: 1285 : switch (bf_matcher_get_type(matcher)) {
140 : 30 : case BF_MATCHER_META_MARK:
141 [ + - ]: 30 : EMIT(program,
142 : : BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, BF_PROG_CTX_OFF(arg)));
143 [ + - ]: 30 : if ((offset = bf_btf_get_field_off("bpf_nf_ctx", "skb")) < 0)
144 : : return offset;
145 [ + - ]: 30 : EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, offset));
146 [ + - ]: 30 : if ((offset = bf_btf_get_field_off("sk_buff", "mark")) < 0)
147 : : return offset;
148 [ + - ]: 30 : EMIT(program, BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_2, offset));
149 : :
150 : 30 : return bf_cmp_value(program, matcher, bf_matcher_payload(matcher), 4,
151 : : BPF_REG_1);
152 : 1255 : default:
153 : 1255 : return bf_packet_gen_inline_matcher(program, matcher);
154 : : }
155 : : }
156 : :
157 : : /**
158 : : * Convert a standard verdict into a return value.
159 : : *
160 : : * @param verdict Verdict to convert. Must be valid.
161 : : * @param ret_code Netfilter return code. Can't be NULL.
162 : : * @return 0 on success, or a negative errno value on failure.
163 : : */
164 : 2572 : static int _bf_nf_get_verdict(enum bf_verdict verdict, int *ret_code)
165 : : {
166 : : assert(ret_code);
167 : :
168 [ + + - ]: 2572 : switch (verdict) {
169 : 1968 : case BF_VERDICT_ACCEPT:
170 : : case BF_VERDICT_NEXT:
171 : 1968 : *ret_code = NF_ACCEPT;
172 : 1968 : return 0;
173 : 604 : case BF_VERDICT_DROP:
174 : 604 : *ret_code = NF_DROP;
175 : 604 : return 0;
176 : : default:
177 : : return -ENOTSUP;
178 : : }
179 : : }
180 : :
181 : : const struct bf_flavor_ops bf_flavor_ops_nf = {
182 : : .gen_inline_prologue = _bf_nf_gen_inline_prologue,
183 : : .gen_inline_epilogue = _bf_nf_gen_inline_epilogue,
184 : : .get_verdict = _bf_nf_get_verdict,
185 : : .gen_inline_matcher = _bf_nf_gen_inline_matcher,
186 : : .gen_inline_log = bf_packet_gen_inline_log,
187 : : };
|