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 
12 struct sockaddr_in6 srv_sa6 = {};
13 struct sockaddr_in srv_sa4 = {};
14 __u16 listen_tp_sport = 0;
15 __u16 req_sk_sport = 0;
16 __u32 recv_cookie = 0;
17 __u32 gen_cookie = 0;
18 __u32 mss = 0;
19 __u32 linum = 0;
20 
21 #define LOG() ({ if (!linum) linum = __LINE__; })
22 
test_syncookie_helper(void * iphdr,int iphdr_size,struct tcphdr * th,struct tcp_sock * tp,struct __sk_buff * skb)23 static void test_syncookie_helper(void *iphdr, int iphdr_size,
24 				  struct tcphdr *th, struct tcp_sock *tp,
25 				  struct __sk_buff *skb)
26 {
27 	if (th->syn) {
28 		__s64 mss_cookie;
29 		void *data_end;
30 
31 		data_end = (void *)(long)(skb->data_end);
32 
33 		if (th->doff * 4 != 40) {
34 			LOG();
35 			return;
36 		}
37 
38 		if ((void *)th + 40 > data_end) {
39 			LOG();
40 			return;
41 		}
42 
43 		mss_cookie = bpf_tcp_gen_syncookie(tp, iphdr, iphdr_size,
44 						   th, 40);
45 		if (mss_cookie < 0) {
46 			if (mss_cookie != -ENOENT)
47 				LOG();
48 		} else {
49 			gen_cookie = (__u32)mss_cookie;
50 			mss = mss_cookie >> 32;
51 		}
52 	} else if (gen_cookie) {
53 		/* It was in cookie mode */
54 		int ret = bpf_tcp_check_syncookie(tp, iphdr, iphdr_size,
55 						  th, sizeof(*th));
56 
57 		if (ret < 0) {
58 			if (ret != -ENOENT)
59 				LOG();
60 		} else {
61 			recv_cookie = bpf_ntohl(th->ack_seq) - 1;
62 		}
63 	}
64 }
65 
handle_ip_tcp(struct ethhdr * eth,struct __sk_buff * skb)66 static int handle_ip_tcp(struct ethhdr *eth, struct __sk_buff *skb)
67 {
68 	struct bpf_sock_tuple *tuple = NULL;
69 	unsigned int tuple_len = 0;
70 	struct bpf_sock *bpf_skc;
71 	void *data_end, *iphdr;
72 	struct ipv6hdr *ip6h;
73 	struct iphdr *ip4h;
74 	struct tcphdr *th;
75 	int iphdr_size;
76 
77 	data_end = (void *)(long)(skb->data_end);
78 
79 	switch (eth->h_proto) {
80 	case bpf_htons(ETH_P_IP):
81 		ip4h = (struct iphdr *)(eth + 1);
82 		if (ip4h + 1 > data_end)
83 			return TC_ACT_OK;
84 		if (ip4h->protocol != IPPROTO_TCP)
85 			return TC_ACT_OK;
86 		th = (struct tcphdr *)(ip4h + 1);
87 		if (th + 1 > data_end)
88 			return TC_ACT_OK;
89 		/* Is it the testing traffic? */
90 		if (th->dest != srv_sa4.sin_port)
91 			return TC_ACT_OK;
92 		tuple_len = sizeof(tuple->ipv4);
93 		tuple = (struct bpf_sock_tuple *)&ip4h->saddr;
94 		iphdr = ip4h;
95 		iphdr_size = sizeof(*ip4h);
96 		break;
97 	case bpf_htons(ETH_P_IPV6):
98 		ip6h = (struct ipv6hdr *)(eth + 1);
99 		if (ip6h + 1 > data_end)
100 			return TC_ACT_OK;
101 		if (ip6h->nexthdr != IPPROTO_TCP)
102 			return TC_ACT_OK;
103 		th = (struct tcphdr *)(ip6h + 1);
104 		if (th + 1 > data_end)
105 			return TC_ACT_OK;
106 		/* Is it the testing traffic? */
107 		if (th->dest != srv_sa6.sin6_port)
108 			return TC_ACT_OK;
109 		tuple_len = sizeof(tuple->ipv6);
110 		tuple = (struct bpf_sock_tuple *)&ip6h->saddr;
111 		iphdr = ip6h;
112 		iphdr_size = sizeof(*ip6h);
113 		break;
114 	default:
115 		return TC_ACT_OK;
116 	}
117 
118 	if ((void *)tuple + tuple_len > data_end) {
119 		LOG();
120 		return TC_ACT_OK;
121 	}
122 
123 	bpf_skc = bpf_skc_lookup_tcp(skb, tuple, tuple_len,
124 				     BPF_F_CURRENT_NETNS, 0);
125 	if (!bpf_skc) {
126 		LOG();
127 		return TC_ACT_OK;
128 	}
129 
130 	if (bpf_skc->state == BPF_TCP_NEW_SYN_RECV) {
131 		struct request_sock *req_sk;
132 
133 		req_sk = (struct request_sock *)bpf_skc_to_tcp_request_sock(bpf_skc);
134 		if (!req_sk) {
135 			LOG();
136 			goto release;
137 		}
138 
139 		if (bpf_sk_assign(skb, req_sk, 0)) {
140 			LOG();
141 			goto release;
142 		}
143 
144 		req_sk_sport = req_sk->__req_common.skc_num;
145 
146 		bpf_sk_release(req_sk);
147 		return TC_ACT_OK;
148 	} else if (bpf_skc->state == BPF_TCP_LISTEN) {
149 		struct tcp_sock *tp;
150 
151 		tp = bpf_skc_to_tcp_sock(bpf_skc);
152 		if (!tp) {
153 			LOG();
154 			goto release;
155 		}
156 
157 		if (bpf_sk_assign(skb, tp, 0)) {
158 			LOG();
159 			goto release;
160 		}
161 
162 		listen_tp_sport = tp->inet_conn.icsk_inet.sk.__sk_common.skc_num;
163 
164 		test_syncookie_helper(iphdr, iphdr_size, th, tp, skb);
165 		bpf_sk_release(tp);
166 		return TC_ACT_OK;
167 	}
168 
169 	if (bpf_sk_assign(skb, bpf_skc, 0))
170 		LOG();
171 
172 release:
173 	bpf_sk_release(bpf_skc);
174 	return TC_ACT_OK;
175 }
176 
177 SEC("tc")
cls_ingress(struct __sk_buff * skb)178 int cls_ingress(struct __sk_buff *skb)
179 {
180 	struct ethhdr *eth;
181 	void *data_end;
182 
183 	data_end = (void *)(long)(skb->data_end);
184 
185 	eth = (struct ethhdr *)(long)(skb->data);
186 	if (eth + 1 > data_end)
187 		return TC_ACT_OK;
188 
189 	if (eth->h_proto != bpf_htons(ETH_P_IP) &&
190 	    eth->h_proto != bpf_htons(ETH_P_IPV6))
191 		return TC_ACT_OK;
192 
193 	return handle_ip_tcp(eth, skb);
194 }
195 
196 char _license[] SEC("license") = "GPL";
197