148156Sjlemon// SPDX-License-Identifier: GPL-2.0
257828Sjlemon/* Copyright (c) 2020 Facebook */
348156Sjlemon
448156Sjlemon#include <stddef.h>
548156Sjlemon#include <errno.h>
648156Sjlemon#include <stdbool.h>
748156Sjlemon#include <sys/types.h>
848156Sjlemon#include <sys/socket.h>
948156Sjlemon#include <linux/tcp.h>
1048156Sjlemon#include <linux/socket.h>
1148156Sjlemon#include <linux/bpf.h>
1248156Sjlemon#include <linux/types.h>
1348156Sjlemon#include <bpf/bpf_helpers.h>
1448156Sjlemon#include <bpf/bpf_endian.h>
1548156Sjlemon#define BPF_PROG_TEST_TCP_HDR_OPTIONS
1648156Sjlemon#include "test_tcp_hdr_options.h"
1748156Sjlemon
1848156Sjlemon#ifndef sizeof_field
1948156Sjlemon#define sizeof_field(TYPE, MEMBER) sizeof((((TYPE *)0)->MEMBER))
2048156Sjlemon#endif
2148156Sjlemon
2248156Sjlemon__u8 test_kind = TCPOPT_EXP;
2348156Sjlemon__u16 test_magic = 0xeB9F;
2448156Sjlemon__u32 inherit_cb_flags = 0;
2548156Sjlemon
2650477Speterstruct bpf_test_option passive_synack_out = {};
2748156Sjlemonstruct bpf_test_option passive_fin_out	= {};
2848156Sjlemon
2948156Sjlemonstruct bpf_test_option passive_estab_in = {};
3048156Sjlemonstruct bpf_test_option passive_fin_in	= {};
3148156Sjlemon
3248156Sjlemonstruct bpf_test_option active_syn_out	= {};
3348156Sjlemonstruct bpf_test_option active_fin_out	= {};
3457828Sjlemon
3548156Sjlemonstruct bpf_test_option active_estab_in	= {};
36124471Smdoddstruct bpf_test_option active_fin_in	= {};
37124471Smdodd
38124471Smdoddstruct {
39124471Smdodd	__uint(type, BPF_MAP_TYPE_SK_STORAGE);
40124471Smdodd	__uint(map_flags, BPF_F_NO_PREALLOC);
41124471Smdodd	__type(key, int);
42124471Smdodd	__type(value, struct hdr_stg);
43124471Smdodd} hdr_stg_map SEC(".maps");
44124471Smdodd
45124471Smdoddstatic bool skops_want_cookie(const struct bpf_sock_ops *skops)
4657828Sjlemon{
47124471Smdodd	return skops->args[0] == BPF_WRITE_HDR_TCP_SYNACK_COOKIE;
48124471Smdodd}
4957828Sjlemon
5057828Sjlemonstatic bool skops_current_mss(const struct bpf_sock_ops *skops)
5157828Sjlemon{
5257828Sjlemon	return skops->args[0] == BPF_WRITE_HDR_TCP_CURRENT_MSS;
53124471Smdodd}
54124471Smdodd
55124471Smdoddstatic __u8 option_total_len(__u8 flags)
56124471Smdodd{
57124471Smdodd	__u8 i, len = 1; /* +1 for flags */
5848156Sjlemon
5948156Sjlemon	if (!flags)
6057828Sjlemon		return 0;
6148156Sjlemon
62124471Smdodd	/* RESEND bit does not use a byte */
63124471Smdodd	for (i = OPTION_RESEND + 1; i < __NR_OPTION_FLAGS; i++)
6448156Sjlemon		len += !!TEST_OPTION_FLAGS(flags, i);
6548156Sjlemon
6657828Sjlemon	if (test_kind == TCPOPT_EXP)
6757828Sjlemon		return len + TCP_BPF_EXPOPT_BASE_LEN;
68124471Smdodd	else
69124471Smdodd		return len + 2; /* +1 kind, +1 kind-len */
70124471Smdodd}
71124471Smdodd
7257828Sjlemonstatic void write_test_option(const struct bpf_test_option *test_opt,
7357828Sjlemon			      __u8 *data)
7457828Sjlemon{
7557828Sjlemon	__u8 offset = 0;
76124471Smdodd
77124471Smdodd	data[offset++] = test_opt->flags;
78124471Smdodd	if (TEST_OPTION_FLAGS(test_opt->flags, OPTION_MAX_DELACK_MS))
7957828Sjlemon		data[offset++] = test_opt->max_delack_ms;
8057828Sjlemon
8148156Sjlemon	if (TEST_OPTION_FLAGS(test_opt->flags, OPTION_RAND))
8248156Sjlemon		data[offset++] = test_opt->rand;
83124539Smdodd}
84124539Smdodd
85124539Smdoddstatic int store_option(struct bpf_sock_ops *skops,
86124471Smdodd			const struct bpf_test_option *test_opt)
8748156Sjlemon{
8848156Sjlemon	union {
89144991Smdodd		struct tcp_exprm_opt exprm;
9048156Sjlemon		struct tcp_opt regular;
91124539Smdodd	} write_opt;
92124539Smdodd	int err;
93124539Smdodd
94124539Smdodd	if (test_kind == TCPOPT_EXP) {
95124539Smdodd		write_opt.exprm.kind = TCPOPT_EXP;
96124539Smdodd		write_opt.exprm.len = option_total_len(test_opt->flags);
97124539Smdodd		write_opt.exprm.magic = __bpf_htons(test_magic);
98124539Smdodd		write_opt.exprm.data32 = 0;
99124539Smdodd		write_test_option(test_opt, write_opt.exprm.data);
100124539Smdodd		err = bpf_store_hdr_opt(skops, &write_opt.exprm,
101124539Smdodd					sizeof(write_opt.exprm), 0);
102124539Smdodd	} else {
103144992Smdodd		write_opt.regular.kind = test_kind;
104124539Smdodd		write_opt.regular.len = option_total_len(test_opt->flags);
105124539Smdodd		write_opt.regular.data32 = 0;
106144992Smdodd		write_test_option(test_opt, write_opt.regular.data);
107144992Smdodd		err = bpf_store_hdr_opt(skops, &write_opt.regular,
108144992Smdodd					sizeof(write_opt.regular), 0);
109144992Smdodd	}
110144992Smdodd
111144992Smdodd	if (err)
112144992Smdodd		RET_CG_ERR(err);
113124539Smdodd
114144992Smdodd	return CG_OK;
115124539Smdodd}
116144992Smdodd
117124539Smdoddstatic int parse_test_option(struct bpf_test_option *opt, const __u8 *start)
118124539Smdodd{
119124539Smdodd	opt->flags = *start++;
120124539Smdodd
121144992Smdodd	if (TEST_OPTION_FLAGS(opt->flags, OPTION_MAX_DELACK_MS))
122124539Smdodd		opt->max_delack_ms = *start++;
123124539Smdodd
124124539Smdodd	if (TEST_OPTION_FLAGS(opt->flags, OPTION_RAND))
125124539Smdodd		opt->rand = *start++;
126124539Smdodd
127124539Smdodd	return 0;
128124539Smdodd}
129124539Smdodd
130124539Smdoddstatic int load_option(struct bpf_sock_ops *skops,
131144992Smdodd		       struct bpf_test_option *test_opt, bool from_syn)
132124539Smdodd{
13348156Sjlemon	union {
134144992Smdodd		struct tcp_exprm_opt exprm;
135144992Smdodd		struct tcp_opt regular;
136144992Smdodd	} search_opt;
137124539Smdodd	int ret, load_flags = from_syn ? BPF_LOAD_HDR_OPT_TCP_SYN : 0;
138124539Smdodd
13963934Sjlemon	if (test_kind == TCPOPT_EXP) {
14063934Sjlemon		search_opt.exprm.kind = TCPOPT_EXP;
14163934Sjlemon		search_opt.exprm.len = 4;
14263934Sjlemon		search_opt.exprm.magic = __bpf_htons(test_magic);
14363934Sjlemon		search_opt.exprm.data32 = 0;
14463934Sjlemon		ret = bpf_load_hdr_opt(skops, &search_opt.exprm,
14563934Sjlemon				       sizeof(search_opt.exprm), load_flags);
14663934Sjlemon		if (ret < 0)
14763934Sjlemon			return ret;
14863934Sjlemon		return parse_test_option(test_opt, search_opt.exprm.data);
14963934Sjlemon	} else {
15063934Sjlemon		search_opt.regular.kind = test_kind;
151103870Salfred		search_opt.regular.len = 0;
15248156Sjlemon		search_opt.regular.data32 = 0;
153124539Smdodd		ret = bpf_load_hdr_opt(skops, &search_opt.regular,
154124539Smdodd				       sizeof(search_opt.regular), load_flags);
155124539Smdodd		if (ret < 0)
156124539Smdodd			return ret;
157124539Smdodd		return parse_test_option(test_opt, search_opt.regular.data);
158124539Smdodd	}
159124539Smdodd}
160124539Smdodd
161124539Smdoddstatic int synack_opt_len(struct bpf_sock_ops *skops)
162124539Smdodd{
163124539Smdodd	struct bpf_test_option test_opt = {};
164124539Smdodd	__u8 optlen;
165124539Smdodd	int err;
166124539Smdodd
167124539Smdodd	if (!passive_synack_out.flags)
168124539Smdodd		return CG_OK;
169124539Smdodd
170124539Smdodd	err = load_option(skops, &test_opt, true);
171124539Smdodd
172124539Smdodd	/* bpf_test_option is not found */
173124539Smdodd	if (err == -ENOMSG)
174124539Smdodd		return CG_OK;
175124539Smdodd
176124539Smdodd	if (err)
177124539Smdodd		RET_CG_ERR(err);
178124539Smdodd
179124539Smdodd	optlen = option_total_len(passive_synack_out.flags);
180124539Smdodd	if (optlen) {
181124539Smdodd		err = bpf_reserve_hdr_opt(skops, optlen, 0);
182124539Smdodd		if (err)
183124539Smdodd			RET_CG_ERR(err);
184124539Smdodd	}
185124539Smdodd
186124539Smdodd	return CG_OK;
187124539Smdodd}
188124539Smdodd
189124539Smdoddstatic int write_synack_opt(struct bpf_sock_ops *skops)
19048156Sjlemon{
191124539Smdodd	struct bpf_test_option opt;
192124539Smdodd
193124539Smdodd	if (!passive_synack_out.flags)
194124539Smdodd		/* We should not even be called since no header
195124539Smdodd		 * space has been reserved.
196124539Smdodd		 */
197124539Smdodd		RET_CG_ERR(0);
198124539Smdodd
199124539Smdodd	opt = passive_synack_out;
200124539Smdodd	if (skops_want_cookie(skops))
201124539Smdodd		SET_OPTION_FLAGS(opt.flags, OPTION_RESEND);
202124539Smdodd
203124539Smdodd	return store_option(skops, &opt);
204124539Smdodd}
205124539Smdodd
206124539Smdoddstatic int syn_opt_len(struct bpf_sock_ops *skops)
207124539Smdodd{
208124539Smdodd	__u8 optlen;
209124539Smdodd	int err;
210124539Smdodd
211124539Smdodd	if (!active_syn_out.flags)
212124539Smdodd		return CG_OK;
213124539Smdodd
214124539Smdodd	optlen = option_total_len(active_syn_out.flags);
215124539Smdodd	if (optlen) {
216124539Smdodd		err = bpf_reserve_hdr_opt(skops, optlen, 0);
217124539Smdodd		if (err)
218124539Smdodd			RET_CG_ERR(err);
219124539Smdodd	}
220124539Smdodd
221124539Smdodd	return CG_OK;
222124539Smdodd}
223124539Smdodd
224124539Smdoddstatic int write_syn_opt(struct bpf_sock_ops *skops)
225124539Smdodd{
226124539Smdodd	if (!active_syn_out.flags)
227103870Salfred		RET_CG_ERR(0);
22863934Sjlemon
229144991Smdodd	return store_option(skops, &active_syn_out);
230124539Smdodd}
231124539Smdodd
232124539Smdoddstatic int fin_opt_len(struct bpf_sock_ops *skops)
23363934Sjlemon{
23463934Sjlemon	struct bpf_test_option *opt;
23563934Sjlemon	struct hdr_stg *hdr_stg;
236124539Smdodd	__u8 optlen;
237124539Smdodd	int err;
238124539Smdodd
23963934Sjlemon	if (!skops->sk)
24063934Sjlemon		RET_CG_ERR(0);
241124539Smdodd
24263934Sjlemon	hdr_stg = bpf_sk_storage_get(&hdr_stg_map, skops->sk, NULL, 0);
24363934Sjlemon	if (!hdr_stg)
24463934Sjlemon		RET_CG_ERR(0);
24563934Sjlemon
24663934Sjlemon	if (hdr_stg->active)
24763934Sjlemon		opt = &active_fin_out;
24863934Sjlemon	else
24963934Sjlemon		opt = &passive_fin_out;
25063934Sjlemon
25163934Sjlemon	optlen = option_total_len(opt->flags);
25263934Sjlemon	if (optlen) {
25363934Sjlemon		err = bpf_reserve_hdr_opt(skops, optlen, 0);
25463934Sjlemon		if (err)
25563934Sjlemon			RET_CG_ERR(err);
25663934Sjlemon	}
25763934Sjlemon
258124539Smdodd	return CG_OK;
259103870Salfred}
260124539Smdodd
261124539Smdoddstatic int write_fin_opt(struct bpf_sock_ops *skops)
262124539Smdodd{
263124539Smdodd	struct bpf_test_option *opt;
264124539Smdodd	struct hdr_stg *hdr_stg;
265124539Smdodd
266124539Smdodd	if (!skops->sk)
267124539Smdodd		RET_CG_ERR(0);
268124539Smdodd
269124539Smdodd	hdr_stg = bpf_sk_storage_get(&hdr_stg_map, skops->sk, NULL, 0);
270124539Smdodd	if (!hdr_stg)
271124539Smdodd		RET_CG_ERR(0);
272124539Smdodd
273124539Smdodd	if (hdr_stg->active)
274124539Smdodd		opt = &active_fin_out;
275124539Smdodd	else
276124539Smdodd		opt = &passive_fin_out;
277124539Smdodd
278124539Smdodd	if (!opt->flags)
279124539Smdodd		RET_CG_ERR(0);
280124539Smdodd
281124539Smdodd	return store_option(skops, opt);
282124539Smdodd}
283124539Smdodd
284124539Smdoddstatic int resend_in_ack(struct bpf_sock_ops *skops)
285124539Smdodd{
286124539Smdodd	struct hdr_stg *hdr_stg;
287124539Smdodd
288124539Smdodd	if (!skops->sk)
289124539Smdodd		return -1;
290124539Smdodd
291124539Smdodd	hdr_stg = bpf_sk_storage_get(&hdr_stg_map, skops->sk, NULL, 0);
292124539Smdodd	if (!hdr_stg)
293124539Smdodd		return -1;
294124539Smdodd
295124539Smdodd	return !!hdr_stg->resend_syn;
296124539Smdodd}
297124539Smdodd
298124539Smdoddstatic int nodata_opt_len(struct bpf_sock_ops *skops)
299124539Smdodd{
300124539Smdodd	int resend;
301124539Smdodd
302124539Smdodd	resend = resend_in_ack(skops);
303124539Smdodd	if (resend < 0)
304124539Smdodd		RET_CG_ERR(0);
305124539Smdodd
306124539Smdodd	if (resend)
307124539Smdodd		return syn_opt_len(skops);
308124539Smdodd
309124539Smdodd	return CG_OK;
310124539Smdodd}
311124539Smdodd
312124539Smdoddstatic int write_nodata_opt(struct bpf_sock_ops *skops)
313124539Smdodd{
314124539Smdodd	int resend;
315124539Smdodd
316124539Smdodd	resend = resend_in_ack(skops);
317124539Smdodd	if (resend < 0)
318124539Smdodd		RET_CG_ERR(0);
319124539Smdodd
320124539Smdodd	if (resend)
321124539Smdodd		return write_syn_opt(skops);
322124539Smdodd
323124539Smdodd	return CG_OK;
324124539Smdodd}
325124539Smdodd
326124539Smdoddstatic int data_opt_len(struct bpf_sock_ops *skops)
327124539Smdodd{
328	/* Same as the nodata version.  Mostly to show
329	 * an example usage on skops->skb_len.
330	 */
331	return nodata_opt_len(skops);
332}
333
334static int write_data_opt(struct bpf_sock_ops *skops)
335{
336	return write_nodata_opt(skops);
337}
338
339static int current_mss_opt_len(struct bpf_sock_ops *skops)
340{
341	/* Reserve maximum that may be needed */
342	int err;
343
344	err = bpf_reserve_hdr_opt(skops, option_total_len(OPTION_MASK), 0);
345	if (err)
346		RET_CG_ERR(err);
347
348	return CG_OK;
349}
350
351static int handle_hdr_opt_len(struct bpf_sock_ops *skops)
352{
353	__u8 tcp_flags = skops_tcp_flags(skops);
354
355	if ((tcp_flags & TCPHDR_SYNACK) == TCPHDR_SYNACK)
356		return synack_opt_len(skops);
357
358	if (tcp_flags & TCPHDR_SYN)
359		return syn_opt_len(skops);
360
361	if (tcp_flags & TCPHDR_FIN)
362		return fin_opt_len(skops);
363
364	if (skops_current_mss(skops))
365		/* The kernel is calculating the MSS */
366		return current_mss_opt_len(skops);
367
368	if (skops->skb_len)
369		return data_opt_len(skops);
370
371	return nodata_opt_len(skops);
372}
373
374static int handle_write_hdr_opt(struct bpf_sock_ops *skops)
375{
376	__u8 tcp_flags = skops_tcp_flags(skops);
377	struct tcphdr *th;
378
379	if ((tcp_flags & TCPHDR_SYNACK) == TCPHDR_SYNACK)
380		return write_synack_opt(skops);
381
382	if (tcp_flags & TCPHDR_SYN)
383		return write_syn_opt(skops);
384
385	if (tcp_flags & TCPHDR_FIN)
386		return write_fin_opt(skops);
387
388	th = skops->skb_data;
389	if (th + 1 > skops->skb_data_end)
390		RET_CG_ERR(0);
391
392	if (skops->skb_len > tcp_hdrlen(th))
393		return write_data_opt(skops);
394
395	return write_nodata_opt(skops);
396}
397
398static int set_delack_max(struct bpf_sock_ops *skops, __u8 max_delack_ms)
399{
400	__u32 max_delack_us = max_delack_ms * 1000;
401
402	return bpf_setsockopt(skops, SOL_TCP, TCP_BPF_DELACK_MAX,
403			      &max_delack_us, sizeof(max_delack_us));
404}
405
406static int set_rto_min(struct bpf_sock_ops *skops, __u8 peer_max_delack_ms)
407{
408	__u32 min_rto_us = peer_max_delack_ms * 1000;
409
410	return bpf_setsockopt(skops, SOL_TCP, TCP_BPF_RTO_MIN, &min_rto_us,
411			      sizeof(min_rto_us));
412}
413
414static int handle_active_estab(struct bpf_sock_ops *skops)
415{
416	struct hdr_stg init_stg = {
417		.active = true,
418	};
419	int err;
420
421	err = load_option(skops, &active_estab_in, false);
422	if (err && err != -ENOMSG)
423		RET_CG_ERR(err);
424
425	init_stg.resend_syn = TEST_OPTION_FLAGS(active_estab_in.flags,
426						OPTION_RESEND);
427	if (!skops->sk || !bpf_sk_storage_get(&hdr_stg_map, skops->sk,
428					      &init_stg,
429					      BPF_SK_STORAGE_GET_F_CREATE))
430		RET_CG_ERR(0);
431
432	if (init_stg.resend_syn)
433		/* Don't clear the write_hdr cb now because
434		 * the ACK may get lost and retransmit may
435		 * be needed.
436		 *
437		 * PARSE_ALL_HDR cb flag is set to learn if this
438		 * resend_syn option has received by the peer.
439		 *
440		 * The header option will be resent until a valid
441		 * packet is received at handle_parse_hdr()
442		 * and all hdr cb flags will be cleared in
443		 * handle_parse_hdr().
444		 */
445		set_parse_all_hdr_cb_flags(skops);
446	else if (!active_fin_out.flags)
447		/* No options will be written from now */
448		clear_hdr_cb_flags(skops);
449
450	if (active_syn_out.max_delack_ms) {
451		err = set_delack_max(skops, active_syn_out.max_delack_ms);
452		if (err)
453			RET_CG_ERR(err);
454	}
455
456	if (active_estab_in.max_delack_ms) {
457		err = set_rto_min(skops, active_estab_in.max_delack_ms);
458		if (err)
459			RET_CG_ERR(err);
460	}
461
462	return CG_OK;
463}
464
465static int handle_passive_estab(struct bpf_sock_ops *skops)
466{
467	struct hdr_stg init_stg = {};
468	struct tcphdr *th;
469	int err;
470
471	inherit_cb_flags = skops->bpf_sock_ops_cb_flags;
472
473	err = load_option(skops, &passive_estab_in, true);
474	if (err == -ENOENT) {
475		/* saved_syn is not found. It was in syncookie mode.
476		 * We have asked the active side to resend the options
477		 * in ACK, so try to find the bpf_test_option from ACK now.
478		 */
479		err = load_option(skops, &passive_estab_in, false);
480		init_stg.syncookie = true;
481	}
482
483	/* ENOMSG: The bpf_test_option is not found which is fine.
484	 * Bail out now for all other errors.
485	 */
486	if (err && err != -ENOMSG)
487		RET_CG_ERR(err);
488
489	th = skops->skb_data;
490	if (th + 1 > skops->skb_data_end)
491		RET_CG_ERR(0);
492
493	if (th->syn) {
494		/* Fastopen */
495
496		/* Cannot clear cb_flags to stop write_hdr cb.
497		 * synack is not sent yet for fast open.
498		 * Even it was, the synack may need to be retransmitted.
499		 *
500		 * PARSE_ALL_HDR cb flag is set to learn
501		 * if synack has reached the peer.
502		 * All cb_flags will be cleared in handle_parse_hdr().
503		 */
504		set_parse_all_hdr_cb_flags(skops);
505		init_stg.fastopen = true;
506	} else if (!passive_fin_out.flags) {
507		/* No options will be written from now */
508		clear_hdr_cb_flags(skops);
509	}
510
511	if (!skops->sk ||
512	    !bpf_sk_storage_get(&hdr_stg_map, skops->sk, &init_stg,
513				BPF_SK_STORAGE_GET_F_CREATE))
514		RET_CG_ERR(0);
515
516	if (passive_synack_out.max_delack_ms) {
517		err = set_delack_max(skops, passive_synack_out.max_delack_ms);
518		if (err)
519			RET_CG_ERR(err);
520	}
521
522	if (passive_estab_in.max_delack_ms) {
523		err = set_rto_min(skops, passive_estab_in.max_delack_ms);
524		if (err)
525			RET_CG_ERR(err);
526	}
527
528	return CG_OK;
529}
530
531static int handle_parse_hdr(struct bpf_sock_ops *skops)
532{
533	struct hdr_stg *hdr_stg;
534	struct tcphdr *th;
535
536	if (!skops->sk)
537		RET_CG_ERR(0);
538
539	th = skops->skb_data;
540	if (th + 1 > skops->skb_data_end)
541		RET_CG_ERR(0);
542
543	hdr_stg = bpf_sk_storage_get(&hdr_stg_map, skops->sk, NULL, 0);
544	if (!hdr_stg)
545		RET_CG_ERR(0);
546
547	if (hdr_stg->resend_syn || hdr_stg->fastopen)
548		/* The PARSE_ALL_HDR cb flag was turned on
549		 * to ensure that the previously written
550		 * options have reached the peer.
551		 * Those previously written option includes:
552		 *     - Active side: resend_syn in ACK during syncookie
553		 *      or
554		 *     - Passive side: SYNACK during fastopen
555		 *
556		 * A valid packet has been received here after
557		 * the 3WHS, so the PARSE_ALL_HDR cb flag
558		 * can be cleared now.
559		 */
560		clear_parse_all_hdr_cb_flags(skops);
561
562	if (hdr_stg->resend_syn && !active_fin_out.flags)
563		/* Active side resent the syn option in ACK
564		 * because the server was in syncookie mode.
565		 * A valid packet has been received, so
566		 * clear header cb flags if there is no
567		 * more option to send.
568		 */
569		clear_hdr_cb_flags(skops);
570
571	if (hdr_stg->fastopen && !passive_fin_out.flags)
572		/* Passive side was in fastopen.
573		 * A valid packet has been received, so
574		 * the SYNACK has reached the peer.
575		 * Clear header cb flags if there is no more
576		 * option to send.
577		 */
578		clear_hdr_cb_flags(skops);
579
580	if (th->fin) {
581		struct bpf_test_option *fin_opt;
582		int err;
583
584		if (hdr_stg->active)
585			fin_opt = &active_fin_in;
586		else
587			fin_opt = &passive_fin_in;
588
589		err = load_option(skops, fin_opt, false);
590		if (err && err != -ENOMSG)
591			RET_CG_ERR(err);
592	}
593
594	return CG_OK;
595}
596
597SEC("sockops")
598int estab(struct bpf_sock_ops *skops)
599{
600	int true_val = 1;
601
602	switch (skops->op) {
603	case BPF_SOCK_OPS_TCP_LISTEN_CB:
604		bpf_setsockopt(skops, SOL_TCP, TCP_SAVE_SYN,
605			       &true_val, sizeof(true_val));
606		set_hdr_cb_flags(skops, BPF_SOCK_OPS_STATE_CB_FLAG);
607		break;
608	case BPF_SOCK_OPS_TCP_CONNECT_CB:
609		set_hdr_cb_flags(skops, 0);
610		break;
611	case BPF_SOCK_OPS_PARSE_HDR_OPT_CB:
612		return handle_parse_hdr(skops);
613	case BPF_SOCK_OPS_HDR_OPT_LEN_CB:
614		return handle_hdr_opt_len(skops);
615	case BPF_SOCK_OPS_WRITE_HDR_OPT_CB:
616		return handle_write_hdr_opt(skops);
617	case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB:
618		return handle_passive_estab(skops);
619	case BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB:
620		return handle_active_estab(skops);
621	}
622
623	return CG_OK;
624}
625
626char _license[] SEC("license") = "GPL";
627