19a856caeSMartin KaFai Lau // SPDX-License-Identifier: GPL-2.0
29a856caeSMartin KaFai Lau /* Copyright (c) 2020 Facebook */
39a856caeSMartin KaFai Lau 
49a856caeSMartin KaFai Lau #include <string.h>
59a856caeSMartin KaFai Lau #include <errno.h>
69a856caeSMartin KaFai Lau #include <netinet/in.h>
79a856caeSMartin KaFai Lau #include <linux/stddef.h>
89a856caeSMartin KaFai Lau #include <linux/bpf.h>
99a856caeSMartin KaFai Lau #include <linux/ipv6.h>
109a856caeSMartin KaFai Lau #include <linux/tcp.h>
119a856caeSMartin KaFai Lau #include <linux/if_ether.h>
129a856caeSMartin KaFai Lau #include <linux/pkt_cls.h>
139a856caeSMartin KaFai Lau 
149a856caeSMartin KaFai Lau #include <bpf/bpf_helpers.h>
159a856caeSMartin KaFai Lau #include <bpf/bpf_endian.h>
169a856caeSMartin KaFai Lau #include "bpf_tcp_helpers.h"
179a856caeSMartin KaFai Lau 
189a856caeSMartin KaFai Lau struct sockaddr_in6 srv_sa6 = {};
199a856caeSMartin KaFai Lau __u16 listen_tp_sport = 0;
209a856caeSMartin KaFai Lau __u16 req_sk_sport = 0;
219a856caeSMartin KaFai Lau __u32 recv_cookie = 0;
229a856caeSMartin KaFai Lau __u32 gen_cookie = 0;
239a856caeSMartin KaFai Lau __u32 linum = 0;
249a856caeSMartin KaFai Lau 
259a856caeSMartin KaFai Lau #define LOG() ({ if (!linum) linum = __LINE__; })
269a856caeSMartin KaFai Lau 
test_syncookie_helper(struct ipv6hdr * ip6h,struct tcphdr * th,struct tcp_sock * tp,struct __sk_buff * skb)279a856caeSMartin KaFai Lau static void test_syncookie_helper(struct ipv6hdr *ip6h, struct tcphdr *th,
289a856caeSMartin KaFai Lau 				  struct tcp_sock *tp,
299a856caeSMartin KaFai Lau 				  struct __sk_buff *skb)
309a856caeSMartin KaFai Lau {
319a856caeSMartin KaFai Lau 	if (th->syn) {
329a856caeSMartin KaFai Lau 		__s64 mss_cookie;
339a856caeSMartin KaFai Lau 		void *data_end;
349a856caeSMartin KaFai Lau 
359a856caeSMartin KaFai Lau 		data_end = (void *)(long)(skb->data_end);
369a856caeSMartin KaFai Lau 
379a856caeSMartin KaFai Lau 		if (th->doff * 4 != 40) {
389a856caeSMartin KaFai Lau 			LOG();
399a856caeSMartin KaFai Lau 			return;
409a856caeSMartin KaFai Lau 		}
419a856caeSMartin KaFai Lau 
429a856caeSMartin KaFai Lau 		if ((void *)th + 40 > data_end) {
439a856caeSMartin KaFai Lau 			LOG();
449a856caeSMartin KaFai Lau 			return;
459a856caeSMartin KaFai Lau 		}
469a856caeSMartin KaFai Lau 
479a856caeSMartin KaFai Lau 		mss_cookie = bpf_tcp_gen_syncookie(tp, ip6h, sizeof(*ip6h),
489a856caeSMartin KaFai Lau 						   th, 40);
499a856caeSMartin KaFai Lau 		if (mss_cookie < 0) {
509a856caeSMartin KaFai Lau 			if (mss_cookie != -ENOENT)
519a856caeSMartin KaFai Lau 				LOG();
529a856caeSMartin KaFai Lau 		} else {
539a856caeSMartin KaFai Lau 			gen_cookie = (__u32)mss_cookie;
549a856caeSMartin KaFai Lau 		}
559a856caeSMartin KaFai Lau 	} else if (gen_cookie) {
569a856caeSMartin KaFai Lau 		/* It was in cookie mode */
579a856caeSMartin KaFai Lau 		int ret = bpf_tcp_check_syncookie(tp, ip6h, sizeof(*ip6h),
589a856caeSMartin KaFai Lau 						  th, sizeof(*th));
599a856caeSMartin KaFai Lau 
609a856caeSMartin KaFai Lau 		if (ret < 0) {
619a856caeSMartin KaFai Lau 			if (ret != -ENOENT)
629a856caeSMartin KaFai Lau 				LOG();
639a856caeSMartin KaFai Lau 		} else {
649a856caeSMartin KaFai Lau 			recv_cookie = bpf_ntohl(th->ack_seq) - 1;
659a856caeSMartin KaFai Lau 		}
669a856caeSMartin KaFai Lau 	}
679a856caeSMartin KaFai Lau }
689a856caeSMartin KaFai Lau 
handle_ip6_tcp(struct ipv6hdr * ip6h,struct __sk_buff * skb)699a856caeSMartin KaFai Lau static int handle_ip6_tcp(struct ipv6hdr *ip6h, struct __sk_buff *skb)
709a856caeSMartin KaFai Lau {
719a856caeSMartin KaFai Lau 	struct bpf_sock_tuple *tuple;
729a856caeSMartin KaFai Lau 	struct bpf_sock *bpf_skc;
739a856caeSMartin KaFai Lau 	unsigned int tuple_len;
749a856caeSMartin KaFai Lau 	struct tcphdr *th;
759a856caeSMartin KaFai Lau 	void *data_end;
769a856caeSMartin KaFai Lau 
779a856caeSMartin KaFai Lau 	data_end = (void *)(long)(skb->data_end);
789a856caeSMartin KaFai Lau 
799a856caeSMartin KaFai Lau 	th = (struct tcphdr *)(ip6h + 1);
809a856caeSMartin KaFai Lau 	if (th + 1 > data_end)
819a856caeSMartin KaFai Lau 		return TC_ACT_OK;
829a856caeSMartin KaFai Lau 
839a856caeSMartin KaFai Lau 	/* Is it the testing traffic? */
849a856caeSMartin KaFai Lau 	if (th->dest != srv_sa6.sin6_port)
859a856caeSMartin KaFai Lau 		return TC_ACT_OK;
869a856caeSMartin KaFai Lau 
879a856caeSMartin KaFai Lau 	tuple_len = sizeof(tuple->ipv6);
889a856caeSMartin KaFai Lau 	tuple = (struct bpf_sock_tuple *)&ip6h->saddr;
899a856caeSMartin KaFai Lau 	if ((void *)tuple + tuple_len > data_end) {
909a856caeSMartin KaFai Lau 		LOG();
919a856caeSMartin KaFai Lau 		return TC_ACT_OK;
929a856caeSMartin KaFai Lau 	}
939a856caeSMartin KaFai Lau 
949a856caeSMartin KaFai Lau 	bpf_skc = bpf_skc_lookup_tcp(skb, tuple, tuple_len,
959a856caeSMartin KaFai Lau 				     BPF_F_CURRENT_NETNS, 0);
969a856caeSMartin KaFai Lau 	if (!bpf_skc) {
979a856caeSMartin KaFai Lau 		LOG();
989a856caeSMartin KaFai Lau 		return TC_ACT_OK;
999a856caeSMartin KaFai Lau 	}
1009a856caeSMartin KaFai Lau 
1019a856caeSMartin KaFai Lau 	if (bpf_skc->state == BPF_TCP_NEW_SYN_RECV) {
1029a856caeSMartin KaFai Lau 		struct request_sock *req_sk;
1039a856caeSMartin KaFai Lau 
1049a856caeSMartin KaFai Lau 		req_sk = (struct request_sock *)bpf_skc_to_tcp_request_sock(bpf_skc);
1059a856caeSMartin KaFai Lau 		if (!req_sk) {
1069a856caeSMartin KaFai Lau 			LOG();
1079a856caeSMartin KaFai Lau 			goto release;
1089a856caeSMartin KaFai Lau 		}
1099a856caeSMartin KaFai Lau 
1109a856caeSMartin KaFai Lau 		if (bpf_sk_assign(skb, req_sk, 0)) {
1119a856caeSMartin KaFai Lau 			LOG();
1129a856caeSMartin KaFai Lau 			goto release;
1139a856caeSMartin KaFai Lau 		}
1149a856caeSMartin KaFai Lau 
1159a856caeSMartin KaFai Lau 		req_sk_sport = req_sk->__req_common.skc_num;
1169a856caeSMartin KaFai Lau 
1179a856caeSMartin KaFai Lau 		bpf_sk_release(req_sk);
1189a856caeSMartin KaFai Lau 		return TC_ACT_OK;
1199a856caeSMartin KaFai Lau 	} else if (bpf_skc->state == BPF_TCP_LISTEN) {
1209a856caeSMartin KaFai Lau 		struct tcp_sock *tp;
1219a856caeSMartin KaFai Lau 
1229a856caeSMartin KaFai Lau 		tp = bpf_skc_to_tcp_sock(bpf_skc);
1239a856caeSMartin KaFai Lau 		if (!tp) {
1249a856caeSMartin KaFai Lau 			LOG();
1259a856caeSMartin KaFai Lau 			goto release;
1269a856caeSMartin KaFai Lau 		}
1279a856caeSMartin KaFai Lau 
1289a856caeSMartin KaFai Lau 		if (bpf_sk_assign(skb, tp, 0)) {
1299a856caeSMartin KaFai Lau 			LOG();
1309a856caeSMartin KaFai Lau 			goto release;
1319a856caeSMartin KaFai Lau 		}
1329a856caeSMartin KaFai Lau 
1339a856caeSMartin KaFai Lau 		listen_tp_sport = tp->inet_conn.icsk_inet.sk.__sk_common.skc_num;
1349a856caeSMartin KaFai Lau 
1359a856caeSMartin KaFai Lau 		test_syncookie_helper(ip6h, th, tp, skb);
1369a856caeSMartin KaFai Lau 		bpf_sk_release(tp);
1379a856caeSMartin KaFai Lau 		return TC_ACT_OK;
1389a856caeSMartin KaFai Lau 	}
1399a856caeSMartin KaFai Lau 
1409a856caeSMartin KaFai Lau 	if (bpf_sk_assign(skb, bpf_skc, 0))
1419a856caeSMartin KaFai Lau 		LOG();
1429a856caeSMartin KaFai Lau 
1439a856caeSMartin KaFai Lau release:
1449a856caeSMartin KaFai Lau 	bpf_sk_release(bpf_skc);
1459a856caeSMartin KaFai Lau 	return TC_ACT_OK;
1469a856caeSMartin KaFai Lau }
1479a856caeSMartin KaFai Lau 
148*c22bdd28SAndrii Nakryiko SEC("tc")
cls_ingress(struct __sk_buff * skb)1499a856caeSMartin KaFai Lau int cls_ingress(struct __sk_buff *skb)
1509a856caeSMartin KaFai Lau {
1519a856caeSMartin KaFai Lau 	struct ipv6hdr *ip6h;
1529a856caeSMartin KaFai Lau 	struct ethhdr *eth;
1539a856caeSMartin KaFai Lau 	void *data_end;
1549a856caeSMartin KaFai Lau 
1559a856caeSMartin KaFai Lau 	data_end = (void *)(long)(skb->data_end);
1569a856caeSMartin KaFai Lau 
1579a856caeSMartin KaFai Lau 	eth = (struct ethhdr *)(long)(skb->data);
1589a856caeSMartin KaFai Lau 	if (eth + 1 > data_end)
1599a856caeSMartin KaFai Lau 		return TC_ACT_OK;
1609a856caeSMartin KaFai Lau 
1619a856caeSMartin KaFai Lau 	if (eth->h_proto != bpf_htons(ETH_P_IPV6))
1629a856caeSMartin KaFai Lau 		return TC_ACT_OK;
1639a856caeSMartin KaFai Lau 
1649a856caeSMartin KaFai Lau 	ip6h = (struct ipv6hdr *)(eth + 1);
1659a856caeSMartin KaFai Lau 	if (ip6h + 1 > data_end)
1669a856caeSMartin KaFai Lau 		return TC_ACT_OK;
1679a856caeSMartin KaFai Lau 
1689a856caeSMartin KaFai Lau 	if (ip6h->nexthdr == IPPROTO_TCP)
1699a856caeSMartin KaFai Lau 		return handle_ip6_tcp(ip6h, skb);
1709a856caeSMartin KaFai Lau 
1719a856caeSMartin KaFai Lau 	return TC_ACT_OK;
1729a856caeSMartin KaFai Lau }
1739a856caeSMartin KaFai Lau 
1749a856caeSMartin KaFai Lau char _license[] SEC("license") = "GPL";
175