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/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/cgen/jmp.h"
19 : #include "bpfilter/cgen/program.h"
20 : #include "bpfilter/cgen/stub.h"
21 : #include "bpfilter/cgen/swich.h"
22 : #include "core/btf.h"
23 : #include "core/flavor.h"
24 : #include "core/helper.h"
25 : #include "core/hook.h"
26 : #include "core/verdict.h"
27 :
28 : #include "external/filter.h"
29 :
30 : #define BF_NF_PRIO_EVEN 2
31 : #define BF_NF_PRIO_ODD 1
32 :
33 : // Forward definition to avoid headers clusterfuck.
34 : uint16_t htons(uint16_t hostshort);
35 :
36 : static inline bool _bf_nf_hook_is_ingress(enum bf_hook hook)
37 : {
38 0 : return hook == BF_HOOK_NF_PRE_ROUTING || hook == BF_HOOK_NF_LOCAL_IN ||
39 : hook == BF_HOOK_NF_FORWARD;
40 : }
41 :
42 0 : static int _bf_nf_gen_inline_prologue(struct bf_program *program)
43 : {
44 : int r;
45 : int offset;
46 :
47 0 : bf_assert(program);
48 :
49 : // Copy the ifindex from to bpf_nf_ctx.state.{in,out}.ifindex the runtime context
50 0 : if ((offset = bf_btf_get_field_off("bpf_nf_ctx", "state")) < 0)
51 : return offset;
52 0 : EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, offset));
53 0 : if (_bf_nf_hook_is_ingress(program->runtime.chain->hook)) {
54 0 : if ((offset = bf_btf_get_field_off("nf_hook_state", "in")) < 0)
55 : return offset;
56 0 : EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_2, offset));
57 : } else {
58 0 : if ((offset = bf_btf_get_field_off("nf_hook_state", "out")) < 0)
59 : return offset;
60 0 : EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_2, offset));
61 : }
62 :
63 0 : if ((offset = bf_btf_get_field_off("net_device", "ifindex")) < 0)
64 : return offset;
65 0 : EMIT(program, BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_3, offset));
66 0 : EMIT(program,
67 : BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_4, BF_PROG_CTX_OFF(ifindex)));
68 :
69 : /* BPF_PROG_TYPE_CGROUP_SKB doesn't provide access the the Ethernet header,
70 : * so we can't parse it and discover the L3 protocol ID.
71 : * Instead, we use the __sk_buff.family value and convert it to the
72 : * corresponding ethertype. */
73 0 : if ((offset = bf_btf_get_field_off("nf_hook_state", "pf")) < 0)
74 : return offset;
75 0 : EMIT(program, BPF_LDX_MEM(BPF_B, BPF_REG_3, BPF_REG_2, offset));
76 :
77 : {
78 0 : _cleanup_bf_swich_ struct bf_swich swich =
79 0 : bf_swich_get(program, BPF_REG_3);
80 :
81 0 : EMIT_SWICH_OPTION(&swich, AF_INET,
82 : BPF_MOV64_IMM(BPF_REG_7, htons(ETH_P_IP)));
83 0 : EMIT_SWICH_OPTION(&swich, AF_INET6,
84 : BPF_MOV64_IMM(BPF_REG_7, htons(ETH_P_IPV6)));
85 0 : EMIT_SWICH_DEFAULT(&swich, BPF_MOV64_IMM(BPF_REG_7, 0));
86 :
87 0 : r = bf_swich_generate(&swich);
88 0 : if (r)
89 : return r;
90 : }
91 :
92 0 : EMIT(program, BPF_ST_MEM(BPF_W, BPF_REG_10, BF_PROG_CTX_OFF(l3_offset), 0));
93 :
94 : // Calculate the packet size (+ETH_HLEN) and store it into the runtime context
95 0 : if ((offset = bf_btf_get_field_off("bpf_nf_ctx", "skb")) < 0)
96 : return offset;
97 0 : EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, offset));
98 0 : if ((offset = bf_btf_get_field_off("sk_buff", "len")) < 0)
99 : return offset;
100 0 : EMIT(program, BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, offset));
101 0 : EMIT(program, BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, ETH_HLEN));
102 0 : EMIT(program,
103 : BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_2, BF_PROG_CTX_OFF(pkt_size)));
104 :
105 0 : r = bf_stub_make_ctx_skb_dynptr(program, BPF_REG_1);
106 0 : if (r)
107 : return r;
108 :
109 0 : r = bf_stub_parse_l3_hdr(program);
110 0 : if (r)
111 : return r;
112 :
113 0 : r = bf_stub_parse_l4_hdr(program);
114 : if (r)
115 : return r;
116 :
117 : return 0;
118 : }
119 :
120 0 : static int _bf_nf_gen_inline_epilogue(struct bf_program *program)
121 : {
122 : UNUSED(program);
123 :
124 0 : return 0;
125 : }
126 :
127 : /**
128 : * Convert a standard verdict into a return value.
129 : *
130 : * @param verdict Verdict to convert. Must be valid.
131 : * @return TC return code corresponding to the verdict, as an integer.
132 : */
133 0 : static int _bf_nf_get_verdict(enum bf_verdict verdict)
134 : {
135 0 : bf_assert(0 <= verdict && verdict < _BF_TERMINAL_VERDICT_MAX);
136 :
137 : static const int verdicts[] = {
138 : [BF_VERDICT_ACCEPT] = NF_ACCEPT,
139 : [BF_VERDICT_DROP] = NF_DROP,
140 : };
141 :
142 : static_assert(ARRAY_SIZE(verdicts) == _BF_TERMINAL_VERDICT_MAX);
143 :
144 0 : return verdicts[verdict];
145 : }
146 :
147 : const struct bf_flavor_ops bf_flavor_ops_nf = {
148 : .gen_inline_prologue = _bf_nf_gen_inline_prologue,
149 : .gen_inline_epilogue = _bf_nf_gen_inline_epilogue,
150 : .get_verdict = _bf_nf_get_verdict,
151 : };
|