1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <vmlinux.h> |
3 | #include <bpf/bpf_helpers.h> |
4 | #include <bpf/bpf_endian.h> |
5 | |
6 | #define EAFNOSUPPORT 97 |
7 | #define EPROTO 71 |
8 | #define ENONET 64 |
9 | #define EINVAL 22 |
10 | #define ENOENT 2 |
11 | |
12 | extern unsigned long CONFIG_HZ __kconfig; |
13 | |
14 | int test_einval_bpf_tuple = 0; |
15 | int test_einval_reserved = 0; |
16 | int test_einval_netns_id = 0; |
17 | int test_einval_len_opts = 0; |
18 | int test_eproto_l4proto = 0; |
19 | int test_enonet_netns_id = 0; |
20 | int test_enoent_lookup = 0; |
21 | int test_eafnosupport = 0; |
22 | int test_alloc_entry = -EINVAL; |
23 | int test_insert_entry = -EAFNOSUPPORT; |
24 | int test_succ_lookup = -ENOENT; |
25 | u32 test_delta_timeout = 0; |
26 | u32 test_status = 0; |
27 | u32 test_insert_lookup_mark = 0; |
28 | int test_snat_addr = -EINVAL; |
29 | int test_dnat_addr = -EINVAL; |
30 | __be32 saddr = 0; |
31 | __be16 sport = 0; |
32 | __be32 daddr = 0; |
33 | __be16 dport = 0; |
34 | int test_exist_lookup = -ENOENT; |
35 | u32 test_exist_lookup_mark = 0; |
36 | |
37 | enum nf_nat_manip_type___local { |
38 | NF_NAT_MANIP_SRC___local, |
39 | NF_NAT_MANIP_DST___local |
40 | }; |
41 | |
42 | struct nf_conn; |
43 | |
44 | struct bpf_ct_opts___local { |
45 | s32 netns_id; |
46 | s32 error; |
47 | u8 l4proto; |
48 | u8 reserved[3]; |
49 | } __attribute__((preserve_access_index)); |
50 | |
51 | struct nf_conn *bpf_xdp_ct_alloc(struct xdp_md *, struct bpf_sock_tuple *, u32, |
52 | struct bpf_ct_opts___local *, u32) __ksym; |
53 | struct nf_conn *bpf_xdp_ct_lookup(struct xdp_md *, struct bpf_sock_tuple *, u32, |
54 | struct bpf_ct_opts___local *, u32) __ksym; |
55 | struct nf_conn *bpf_skb_ct_alloc(struct __sk_buff *, struct bpf_sock_tuple *, u32, |
56 | struct bpf_ct_opts___local *, u32) __ksym; |
57 | struct nf_conn *bpf_skb_ct_lookup(struct __sk_buff *, struct bpf_sock_tuple *, u32, |
58 | struct bpf_ct_opts___local *, u32) __ksym; |
59 | struct nf_conn *bpf_ct_insert_entry(struct nf_conn *) __ksym; |
60 | void bpf_ct_release(struct nf_conn *) __ksym; |
61 | void bpf_ct_set_timeout(struct nf_conn *, u32) __ksym; |
62 | int bpf_ct_change_timeout(struct nf_conn *, u32) __ksym; |
63 | int bpf_ct_set_status(struct nf_conn *, u32) __ksym; |
64 | int bpf_ct_change_status(struct nf_conn *, u32) __ksym; |
65 | int bpf_ct_set_nat_info(struct nf_conn *, union nf_inet_addr *, |
66 | int port, enum nf_nat_manip_type___local) __ksym; |
67 | |
68 | static __always_inline void |
69 | nf_ct_test(struct nf_conn *(*lookup_fn)(void *, struct bpf_sock_tuple *, u32, |
70 | struct bpf_ct_opts___local *, u32), |
71 | struct nf_conn *(*alloc_fn)(void *, struct bpf_sock_tuple *, u32, |
72 | struct bpf_ct_opts___local *, u32), |
73 | void *ctx) |
74 | { |
75 | struct bpf_ct_opts___local opts_def = { .l4proto = IPPROTO_TCP, .netns_id = -1 }; |
76 | struct bpf_sock_tuple bpf_tuple; |
77 | struct nf_conn *ct; |
78 | |
79 | __builtin_memset(&bpf_tuple, 0, sizeof(bpf_tuple.ipv4)); |
80 | |
81 | ct = lookup_fn(ctx, NULL, 0, &opts_def, sizeof(opts_def)); |
82 | if (ct) |
83 | bpf_ct_release(ct); |
84 | else |
85 | test_einval_bpf_tuple = opts_def.error; |
86 | |
87 | opts_def.reserved[0] = 1; |
88 | ct = lookup_fn(ctx, &bpf_tuple, sizeof(bpf_tuple.ipv4), &opts_def, |
89 | sizeof(opts_def)); |
90 | opts_def.reserved[0] = 0; |
91 | opts_def.l4proto = IPPROTO_TCP; |
92 | if (ct) |
93 | bpf_ct_release(ct); |
94 | else |
95 | test_einval_reserved = opts_def.error; |
96 | |
97 | opts_def.netns_id = -2; |
98 | ct = lookup_fn(ctx, &bpf_tuple, sizeof(bpf_tuple.ipv4), &opts_def, |
99 | sizeof(opts_def)); |
100 | opts_def.netns_id = -1; |
101 | if (ct) |
102 | bpf_ct_release(ct); |
103 | else |
104 | test_einval_netns_id = opts_def.error; |
105 | |
106 | ct = lookup_fn(ctx, &bpf_tuple, sizeof(bpf_tuple.ipv4), &opts_def, |
107 | sizeof(opts_def) - 1); |
108 | if (ct) |
109 | bpf_ct_release(ct); |
110 | else |
111 | test_einval_len_opts = opts_def.error; |
112 | |
113 | opts_def.l4proto = IPPROTO_ICMP; |
114 | ct = lookup_fn(ctx, &bpf_tuple, sizeof(bpf_tuple.ipv4), &opts_def, |
115 | sizeof(opts_def)); |
116 | opts_def.l4proto = IPPROTO_TCP; |
117 | if (ct) |
118 | bpf_ct_release(ct); |
119 | else |
120 | test_eproto_l4proto = opts_def.error; |
121 | |
122 | opts_def.netns_id = 0xf00f; |
123 | ct = lookup_fn(ctx, &bpf_tuple, sizeof(bpf_tuple.ipv4), &opts_def, |
124 | sizeof(opts_def)); |
125 | opts_def.netns_id = -1; |
126 | if (ct) |
127 | bpf_ct_release(ct); |
128 | else |
129 | test_enonet_netns_id = opts_def.error; |
130 | |
131 | ct = lookup_fn(ctx, &bpf_tuple, sizeof(bpf_tuple.ipv4), &opts_def, |
132 | sizeof(opts_def)); |
133 | if (ct) |
134 | bpf_ct_release(ct); |
135 | else |
136 | test_enoent_lookup = opts_def.error; |
137 | |
138 | ct = lookup_fn(ctx, &bpf_tuple, sizeof(bpf_tuple.ipv4) - 1, &opts_def, |
139 | sizeof(opts_def)); |
140 | if (ct) |
141 | bpf_ct_release(ct); |
142 | else |
143 | test_eafnosupport = opts_def.error; |
144 | |
145 | bpf_tuple.ipv4.saddr = bpf_get_prandom_u32(); /* src IP */ |
146 | bpf_tuple.ipv4.daddr = bpf_get_prandom_u32(); /* dst IP */ |
147 | bpf_tuple.ipv4.sport = bpf_get_prandom_u32(); /* src port */ |
148 | bpf_tuple.ipv4.dport = bpf_get_prandom_u32(); /* dst port */ |
149 | |
150 | ct = alloc_fn(ctx, &bpf_tuple, sizeof(bpf_tuple.ipv4), &opts_def, |
151 | sizeof(opts_def)); |
152 | if (ct) { |
153 | __u16 sport = bpf_get_prandom_u32(); |
154 | __u16 dport = bpf_get_prandom_u32(); |
155 | union nf_inet_addr saddr = {}; |
156 | union nf_inet_addr daddr = {}; |
157 | struct nf_conn *ct_ins; |
158 | |
159 | bpf_ct_set_timeout(ct, 10000); |
160 | ct->mark = 77; |
161 | |
162 | /* snat */ |
163 | saddr.ip = bpf_get_prandom_u32(); |
164 | bpf_ct_set_nat_info(ct, &saddr, sport, NF_NAT_MANIP_SRC___local); |
165 | /* dnat */ |
166 | daddr.ip = bpf_get_prandom_u32(); |
167 | bpf_ct_set_nat_info(ct, &daddr, dport, NF_NAT_MANIP_DST___local); |
168 | |
169 | ct_ins = bpf_ct_insert_entry(ct); |
170 | if (ct_ins) { |
171 | struct nf_conn *ct_lk; |
172 | |
173 | ct_lk = lookup_fn(ctx, &bpf_tuple, sizeof(bpf_tuple.ipv4), |
174 | &opts_def, sizeof(opts_def)); |
175 | if (ct_lk) { |
176 | struct nf_conntrack_tuple *tuple; |
177 | |
178 | /* check snat and dnat addresses */ |
179 | tuple = &ct_lk->tuplehash[IP_CT_DIR_REPLY].tuple; |
180 | if (tuple->dst.u3.ip == saddr.ip && |
181 | tuple->dst.u.all == bpf_htons(sport)) |
182 | test_snat_addr = 0; |
183 | if (tuple->src.u3.ip == daddr.ip && |
184 | tuple->src.u.all == bpf_htons(dport)) |
185 | test_dnat_addr = 0; |
186 | |
187 | /* update ct entry timeout */ |
188 | bpf_ct_change_timeout(ct_lk, 10000); |
189 | test_delta_timeout = ct_lk->timeout - bpf_jiffies64(); |
190 | test_delta_timeout /= CONFIG_HZ; |
191 | test_insert_lookup_mark = ct_lk->mark; |
192 | bpf_ct_change_status(ct_lk, |
193 | IPS_CONFIRMED | IPS_SEEN_REPLY); |
194 | test_status = ct_lk->status; |
195 | |
196 | bpf_ct_release(ct_lk); |
197 | test_succ_lookup = 0; |
198 | } |
199 | bpf_ct_release(ct_ins); |
200 | test_insert_entry = 0; |
201 | } |
202 | test_alloc_entry = 0; |
203 | } |
204 | |
205 | bpf_tuple.ipv4.saddr = saddr; |
206 | bpf_tuple.ipv4.daddr = daddr; |
207 | bpf_tuple.ipv4.sport = sport; |
208 | bpf_tuple.ipv4.dport = dport; |
209 | ct = lookup_fn(ctx, &bpf_tuple, sizeof(bpf_tuple.ipv4), &opts_def, |
210 | sizeof(opts_def)); |
211 | if (ct) { |
212 | test_exist_lookup = 0; |
213 | if (ct->mark == 42) { |
214 | ct->mark++; |
215 | test_exist_lookup_mark = ct->mark; |
216 | } |
217 | bpf_ct_release(ct); |
218 | } else { |
219 | test_exist_lookup = opts_def.error; |
220 | } |
221 | } |
222 | |
223 | SEC("xdp" ) |
224 | int nf_xdp_ct_test(struct xdp_md *ctx) |
225 | { |
226 | nf_ct_test((void *)bpf_xdp_ct_lookup, (void *)bpf_xdp_ct_alloc, ctx); |
227 | return 0; |
228 | } |
229 | |
230 | SEC("tc" ) |
231 | int nf_skb_ct_test(struct __sk_buff *ctx) |
232 | { |
233 | nf_ct_test((void *)bpf_skb_ct_lookup, (void *)bpf_skb_ct_alloc, ctx); |
234 | return 0; |
235 | } |
236 | |
237 | char _license[] SEC("license" ) = "GPL" ; |
238 | |