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