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