1
2
3
4#include <stddef.h>
5#include <errno.h>
6#include <stdbool.h>
7#include <sys/types.h>
8#include <sys/socket.h>
9#include <linux/ipv6.h>
10#include <linux/tcp.h>
11#include <linux/socket.h>
12#include <linux/bpf.h>
13#include <linux/types.h>
14#include <bpf/bpf_helpers.h>
15#include <bpf/bpf_endian.h>
16#define BPF_PROG_TEST_TCP_HDR_OPTIONS
17#include "test_tcp_hdr_options.h"
18
19__u16 last_addr16_n = __bpf_htons(1);
20__u16 active_lport_n = 0;
21__u16 active_lport_h = 0;
22__u16 passive_lport_n = 0;
23__u16 passive_lport_h = 0;
24
25
26unsigned int nr_pure_ack = 0;
27unsigned int nr_data = 0;
28unsigned int nr_syn = 0;
29unsigned int nr_fin = 0;
30
31
32static int __check_active_hdr_in(struct bpf_sock_ops *skops, bool check_syn)
33{
34 union {
35 struct tcphdr th;
36 struct ipv6hdr ip6;
37 struct tcp_exprm_opt exprm_opt;
38 struct tcp_opt reg_opt;
39 __u8 data[100];
40 } hdr = {};
41 __u64 load_flags = check_syn ? BPF_LOAD_HDR_OPT_TCP_SYN : 0;
42 struct tcphdr *pth;
43 int ret;
44
45 hdr.reg_opt.kind = 0xB9;
46
47
48 ret = bpf_load_hdr_opt(skops, &hdr.reg_opt, 2, load_flags);
49 if (ret != -ENOSPC)
50 RET_CG_ERR(ret);
51
52
53 hdr.reg_opt.len = 4;
54 ret = bpf_load_hdr_opt(skops, &hdr.reg_opt, sizeof(hdr.reg_opt),
55 load_flags);
56 if (ret != -EINVAL)
57 RET_CG_ERR(ret);
58
59 hdr.reg_opt.len = 0;
60 ret = bpf_load_hdr_opt(skops, &hdr.reg_opt, sizeof(hdr.reg_opt),
61 load_flags);
62 if (ret != 4 || hdr.reg_opt.len != 4 || hdr.reg_opt.kind != 0xB9 ||
63 hdr.reg_opt.data[0] != 0xfa || hdr.reg_opt.data[1] != 0xce)
64 RET_CG_ERR(ret);
65
66
67 hdr.exprm_opt.kind = TCPOPT_EXP;
68 hdr.exprm_opt.len = 5;
69 hdr.exprm_opt.magic = 0;
70 ret = bpf_load_hdr_opt(skops, &hdr.exprm_opt, sizeof(hdr.exprm_opt),
71 load_flags);
72 if (ret != -EINVAL)
73 RET_CG_ERR(ret);
74
75
76 hdr.exprm_opt.len = 4;
77 ret = bpf_load_hdr_opt(skops, &hdr.exprm_opt, sizeof(hdr.exprm_opt),
78 load_flags);
79 if (ret != -ENOMSG)
80 RET_CG_ERR(ret);
81
82 hdr.exprm_opt.magic = __bpf_htons(0xeB9F);
83 ret = bpf_load_hdr_opt(skops, &hdr.exprm_opt, sizeof(hdr.exprm_opt),
84 load_flags);
85 if (ret != 4 || hdr.exprm_opt.len != 4 ||
86 hdr.exprm_opt.kind != TCPOPT_EXP ||
87 hdr.exprm_opt.magic != __bpf_htons(0xeB9F))
88 RET_CG_ERR(ret);
89
90 if (!check_syn)
91 return CG_OK;
92
93
94
95
96
97 ret = bpf_getsockopt(skops, SOL_TCP, TCP_BPF_SYN_IP, &hdr.ip6,
98 sizeof(hdr.ip6));
99 if (ret != -ENOSPC)
100 RET_CG_ERR(ret);
101
102 if (hdr.ip6.saddr.s6_addr16[7] != last_addr16_n ||
103 hdr.ip6.daddr.s6_addr16[7] != last_addr16_n)
104 RET_CG_ERR(0);
105
106 ret = bpf_getsockopt(skops, SOL_TCP, TCP_BPF_SYN_IP, &hdr, sizeof(hdr));
107 if (ret < 0)
108 RET_CG_ERR(ret);
109
110 pth = (struct tcphdr *)(&hdr.ip6 + 1);
111 if (pth->dest != passive_lport_n || pth->source != active_lport_n)
112 RET_CG_ERR(0);
113
114 ret = bpf_getsockopt(skops, SOL_TCP, TCP_BPF_SYN, &hdr, sizeof(hdr));
115 if (ret < 0)
116 RET_CG_ERR(ret);
117
118 if (hdr.th.dest != passive_lport_n || hdr.th.source != active_lport_n)
119 RET_CG_ERR(0);
120
121 return CG_OK;
122}
123
124static int check_active_syn_in(struct bpf_sock_ops *skops)
125{
126 return __check_active_hdr_in(skops, true);
127}
128
129static int check_active_hdr_in(struct bpf_sock_ops *skops)
130{
131 struct tcphdr *th;
132
133 if (__check_active_hdr_in(skops, false) == CG_ERR)
134 return CG_ERR;
135
136 th = skops->skb_data;
137 if (th + 1 > skops->skb_data_end)
138 RET_CG_ERR(0);
139
140 if (tcp_hdrlen(th) < skops->skb_len)
141 nr_data++;
142
143 if (th->fin)
144 nr_fin++;
145
146 if (th->ack && !th->fin && tcp_hdrlen(th) == skops->skb_len)
147 nr_pure_ack++;
148
149 return CG_OK;
150}
151
152static int active_opt_len(struct bpf_sock_ops *skops)
153{
154 int err;
155
156
157
158
159 err = bpf_reserve_hdr_opt(skops, 12, 0);
160 if (err)
161 RET_CG_ERR(err);
162
163 return CG_OK;
164}
165
166static int write_active_opt(struct bpf_sock_ops *skops)
167{
168 struct tcp_exprm_opt exprm_opt = {};
169 struct tcp_opt win_scale_opt = {};
170 struct tcp_opt reg_opt = {};
171 struct tcphdr *th;
172 int err, ret;
173
174 exprm_opt.kind = TCPOPT_EXP;
175 exprm_opt.len = 4;
176 exprm_opt.magic = __bpf_htons(0xeB9F);
177
178 reg_opt.kind = 0xB9;
179 reg_opt.len = 4;
180 reg_opt.data[0] = 0xfa;
181 reg_opt.data[1] = 0xce;
182
183 win_scale_opt.kind = TCPOPT_WINDOW;
184
185 err = bpf_store_hdr_opt(skops, &exprm_opt, sizeof(exprm_opt), 0);
186 if (err)
187 RET_CG_ERR(err);
188
189
190 err = bpf_store_hdr_opt(skops, &exprm_opt, sizeof(exprm_opt), 0);
191 if (err != -EEXIST)
192 RET_CG_ERR(err);
193
194 err = bpf_store_hdr_opt(skops, ®_opt, sizeof(reg_opt), 0);
195 if (err)
196 RET_CG_ERR(err);
197 err = bpf_store_hdr_opt(skops, ®_opt, sizeof(reg_opt), 0);
198 if (err != -EEXIST)
199 RET_CG_ERR(err);
200
201
202 ret = bpf_load_hdr_opt(skops, &exprm_opt, sizeof(exprm_opt), 0);
203 if (ret != 4 || exprm_opt.len != 4 || exprm_opt.kind != TCPOPT_EXP ||
204 exprm_opt.magic != __bpf_htons(0xeB9F))
205 RET_CG_ERR(ret);
206
207 reg_opt.len = 0;
208 ret = bpf_load_hdr_opt(skops, ®_opt, sizeof(reg_opt), 0);
209 if (ret != 4 || reg_opt.len != 4 || reg_opt.kind != 0xB9 ||
210 reg_opt.data[0] != 0xfa || reg_opt.data[1] != 0xce)
211 RET_CG_ERR(ret);
212
213 th = skops->skb_data;
214 if (th + 1 > skops->skb_data_end)
215 RET_CG_ERR(0);
216
217 if (th->syn) {
218 active_lport_h = skops->local_port;
219 active_lport_n = th->source;
220
221
222
223
224 ret = bpf_load_hdr_opt(skops, &win_scale_opt,
225 sizeof(win_scale_opt), 0);
226 if (ret != 3 || win_scale_opt.len != 3 ||
227 win_scale_opt.kind != TCPOPT_WINDOW)
228 RET_CG_ERR(ret);
229
230
231
232
233 err = bpf_store_hdr_opt(skops, &win_scale_opt,
234 sizeof(win_scale_opt), 0);
235 if (err != -EEXIST)
236 RET_CG_ERR(err);
237 }
238
239 return CG_OK;
240}
241
242static int handle_hdr_opt_len(struct bpf_sock_ops *skops)
243{
244 __u8 tcp_flags = skops_tcp_flags(skops);
245
246 if ((tcp_flags & TCPHDR_SYNACK) == TCPHDR_SYNACK)
247
248 return check_active_syn_in(skops);
249
250
251 if (skops->local_port == passive_lport_h)
252 RET_CG_ERR(0);
253
254 return active_opt_len(skops);
255}
256
257static int handle_write_hdr_opt(struct bpf_sock_ops *skops)
258{
259 if (skops->local_port == passive_lport_h)
260 RET_CG_ERR(0);
261
262 return write_active_opt(skops);
263}
264
265static int handle_parse_hdr(struct bpf_sock_ops *skops)
266{
267
268
269
270 if (skops->local_port == active_lport_h)
271 RET_CG_ERR(0);
272
273 return check_active_hdr_in(skops);
274}
275
276static int handle_passive_estab(struct bpf_sock_ops *skops)
277{
278 int err;
279
280
281 bpf_sock_ops_cb_flags_set(skops,
282 skops->bpf_sock_ops_cb_flags &
283 ~BPF_SOCK_OPS_WRITE_HDR_OPT_CB_FLAG);
284
285
286 err = check_active_syn_in(skops);
287 if (err == CG_ERR)
288 return err;
289
290 nr_syn++;
291
292
293 return check_active_hdr_in(skops);
294}
295
296SEC("sockops")
297int misc_estab(struct bpf_sock_ops *skops)
298{
299 int true_val = 1;
300
301 switch (skops->op) {
302 case BPF_SOCK_OPS_TCP_LISTEN_CB:
303 passive_lport_h = skops->local_port;
304 passive_lport_n = __bpf_htons(passive_lport_h);
305 bpf_setsockopt(skops, SOL_TCP, TCP_SAVE_SYN,
306 &true_val, sizeof(true_val));
307 set_hdr_cb_flags(skops, 0);
308 break;
309 case BPF_SOCK_OPS_TCP_CONNECT_CB:
310 set_hdr_cb_flags(skops, 0);
311 break;
312 case BPF_SOCK_OPS_PARSE_HDR_OPT_CB:
313 return handle_parse_hdr(skops);
314 case BPF_SOCK_OPS_HDR_OPT_LEN_CB:
315 return handle_hdr_opt_len(skops);
316 case BPF_SOCK_OPS_WRITE_HDR_OPT_CB:
317 return handle_write_hdr_opt(skops);
318 case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB:
319 return handle_passive_estab(skops);
320 }
321
322 return CG_OK;
323}
324
325char _license[] SEC("license") = "GPL";
326