1// SPDX-License-Identifier: GPL-2.0
2/* Copyright (c) 2020 Facebook */
3
4#include "bpf_tracing_net.h"
5#include <bpf/bpf_helpers.h>
6#include <bpf/bpf_endian.h>
7
8#ifndef ENOENT
9#define ENOENT 2
10#endif
11
12struct sockaddr_in6 srv_sa6 = {};
13__u16 listen_tp_sport = 0;
14__u16 req_sk_sport = 0;
15__u32 recv_cookie = 0;
16__u32 gen_cookie = 0;
17__u32 linum = 0;
18
19#define LOG() ({ if (!linum) linum = __LINE__; })
20
21static void test_syncookie_helper(struct ipv6hdr *ip6h, struct tcphdr *th,
22				  struct tcp_sock *tp,
23				  struct __sk_buff *skb)
24{
25	if (th->syn) {
26		__s64 mss_cookie;
27		void *data_end;
28
29		data_end = (void *)(long)(skb->data_end);
30
31		if (th->doff * 4 != 40) {
32			LOG();
33			return;
34		}
35
36		if ((void *)th + 40 > data_end) {
37			LOG();
38			return;
39		}
40
41		mss_cookie = bpf_tcp_gen_syncookie(tp, ip6h, sizeof(*ip6h),
42						   th, 40);
43		if (mss_cookie < 0) {
44			if (mss_cookie != -ENOENT)
45				LOG();
46		} else {
47			gen_cookie = (__u32)mss_cookie;
48		}
49	} else if (gen_cookie) {
50		/* It was in cookie mode */
51		int ret = bpf_tcp_check_syncookie(tp, ip6h, sizeof(*ip6h),
52						  th, sizeof(*th));
53
54		if (ret < 0) {
55			if (ret != -ENOENT)
56				LOG();
57		} else {
58			recv_cookie = bpf_ntohl(th->ack_seq) - 1;
59		}
60	}
61}
62
63static int handle_ip6_tcp(struct ipv6hdr *ip6h, struct __sk_buff *skb)
64{
65	struct bpf_sock_tuple *tuple;
66	struct bpf_sock *bpf_skc;
67	unsigned int tuple_len;
68	struct tcphdr *th;
69	void *data_end;
70
71	data_end = (void *)(long)(skb->data_end);
72
73	th = (struct tcphdr *)(ip6h + 1);
74	if (th + 1 > data_end)
75		return TC_ACT_OK;
76
77	/* Is it the testing traffic? */
78	if (th->dest != srv_sa6.sin6_port)
79		return TC_ACT_OK;
80
81	tuple_len = sizeof(tuple->ipv6);
82	tuple = (struct bpf_sock_tuple *)&ip6h->saddr;
83	if ((void *)tuple + tuple_len > data_end) {
84		LOG();
85		return TC_ACT_OK;
86	}
87
88	bpf_skc = bpf_skc_lookup_tcp(skb, tuple, tuple_len,
89				     BPF_F_CURRENT_NETNS, 0);
90	if (!bpf_skc) {
91		LOG();
92		return TC_ACT_OK;
93	}
94
95	if (bpf_skc->state == BPF_TCP_NEW_SYN_RECV) {
96		struct request_sock *req_sk;
97
98		req_sk = (struct request_sock *)bpf_skc_to_tcp_request_sock(bpf_skc);
99		if (!req_sk) {
100			LOG();
101			goto release;
102		}
103
104		if (bpf_sk_assign(skb, req_sk, 0)) {
105			LOG();
106			goto release;
107		}
108
109		req_sk_sport = req_sk->__req_common.skc_num;
110
111		bpf_sk_release(req_sk);
112		return TC_ACT_OK;
113	} else if (bpf_skc->state == BPF_TCP_LISTEN) {
114		struct tcp_sock *tp;
115
116		tp = bpf_skc_to_tcp_sock(bpf_skc);
117		if (!tp) {
118			LOG();
119			goto release;
120		}
121
122		if (bpf_sk_assign(skb, tp, 0)) {
123			LOG();
124			goto release;
125		}
126
127		listen_tp_sport = tp->inet_conn.icsk_inet.sk.__sk_common.skc_num;
128
129		test_syncookie_helper(ip6h, th, tp, skb);
130		bpf_sk_release(tp);
131		return TC_ACT_OK;
132	}
133
134	if (bpf_sk_assign(skb, bpf_skc, 0))
135		LOG();
136
137release:
138	bpf_sk_release(bpf_skc);
139	return TC_ACT_OK;
140}
141
142SEC("tc")
143int cls_ingress(struct __sk_buff *skb)
144{
145	struct ipv6hdr *ip6h;
146	struct ethhdr *eth;
147	void *data_end;
148
149	data_end = (void *)(long)(skb->data_end);
150
151	eth = (struct ethhdr *)(long)(skb->data);
152	if (eth + 1 > data_end)
153		return TC_ACT_OK;
154
155	if (eth->h_proto != bpf_htons(ETH_P_IPV6))
156		return TC_ACT_OK;
157
158	ip6h = (struct ipv6hdr *)(eth + 1);
159	if (ip6h + 1 > data_end)
160		return TC_ACT_OK;
161
162	if (ip6h->nexthdr == IPPROTO_TCP)
163		return handle_ip6_tcp(ip6h, skb);
164
165	return TC_ACT_OK;
166}
167
168char _license[] SEC("license") = "GPL";
169