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 
18 struct 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 
test_syncookie_helper(struct ipv6hdr * ip6h,struct tcphdr * th,struct tcp_sock * tp,struct __sk_buff * skb)27 static 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 
handle_ip6_tcp(struct ipv6hdr * ip6h,struct __sk_buff * skb)69 static 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 
143 release:
144 	bpf_sk_release(bpf_skc);
145 	return TC_ACT_OK;
146 }
147 
148 SEC("tc")
cls_ingress(struct __sk_buff * skb)149 int 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 
174 char _license[] SEC("license") = "GPL";
175