17df5e3dbSPeter Oskolkov // SPDX-License-Identifier: GPL-2.0
27df5e3dbSPeter Oskolkov #include <stdint.h>
37df5e3dbSPeter Oskolkov #include <linux/bpf.h>
47df5e3dbSPeter Oskolkov #include <linux/if_ether.h>
54ce150b6SIlya Leoshkevich #include <linux/stddef.h>
67df5e3dbSPeter Oskolkov #include <linux/in.h>
77df5e3dbSPeter Oskolkov #include <linux/ip.h>
87df5e3dbSPeter Oskolkov #include <linux/pkt_cls.h>
97df5e3dbSPeter Oskolkov #include <linux/tcp.h>
103e689141SToke Høiland-Jørgensen #include <bpf/bpf_helpers.h>
113e689141SToke Høiland-Jørgensen #include <bpf/bpf_endian.h>
127df5e3dbSPeter Oskolkov 
137df5e3dbSPeter Oskolkov /* the maximum delay we are willing to add (drop packets beyond that) */
147df5e3dbSPeter Oskolkov #define TIME_HORIZON_NS (2000 * 1000 * 1000)
157df5e3dbSPeter Oskolkov #define NS_PER_SEC 1000000000
167df5e3dbSPeter Oskolkov #define ECN_HORIZON_NS 5000000
177df5e3dbSPeter Oskolkov #define THROTTLE_RATE_BPS (5 * 1000 * 1000)
187df5e3dbSPeter Oskolkov 
197df5e3dbSPeter Oskolkov /* flow_key => last_tstamp timestamp used */
20*ccc3f569SAndrii Nakryiko struct {
21*ccc3f569SAndrii Nakryiko 	__uint(type, BPF_MAP_TYPE_HASH);
22*ccc3f569SAndrii Nakryiko 	__type(key, uint32_t);
23*ccc3f569SAndrii Nakryiko 	__type(value, uint64_t);
24*ccc3f569SAndrii Nakryiko 	__uint(max_entries, 1);
25*ccc3f569SAndrii Nakryiko } flow_map SEC(".maps");
267df5e3dbSPeter Oskolkov 
throttle_flow(struct __sk_buff * skb)277df5e3dbSPeter Oskolkov static inline int throttle_flow(struct __sk_buff *skb)
287df5e3dbSPeter Oskolkov {
297df5e3dbSPeter Oskolkov 	int key = 0;
307df5e3dbSPeter Oskolkov 	uint64_t *last_tstamp = bpf_map_lookup_elem(&flow_map, &key);
317df5e3dbSPeter Oskolkov 	uint64_t delay_ns = ((uint64_t)skb->len) * NS_PER_SEC /
327df5e3dbSPeter Oskolkov 			THROTTLE_RATE_BPS;
337df5e3dbSPeter Oskolkov 	uint64_t now = bpf_ktime_get_ns();
347df5e3dbSPeter Oskolkov 	uint64_t tstamp, next_tstamp = 0;
357df5e3dbSPeter Oskolkov 
367df5e3dbSPeter Oskolkov 	if (last_tstamp)
377df5e3dbSPeter Oskolkov 		next_tstamp = *last_tstamp + delay_ns;
387df5e3dbSPeter Oskolkov 
397df5e3dbSPeter Oskolkov 	tstamp = skb->tstamp;
407df5e3dbSPeter Oskolkov 	if (tstamp < now)
417df5e3dbSPeter Oskolkov 		tstamp = now;
427df5e3dbSPeter Oskolkov 
437df5e3dbSPeter Oskolkov 	/* should we throttle? */
447df5e3dbSPeter Oskolkov 	if (next_tstamp <= tstamp) {
457df5e3dbSPeter Oskolkov 		if (bpf_map_update_elem(&flow_map, &key, &tstamp, BPF_ANY))
467df5e3dbSPeter Oskolkov 			return TC_ACT_SHOT;
477df5e3dbSPeter Oskolkov 		return TC_ACT_OK;
487df5e3dbSPeter Oskolkov 	}
497df5e3dbSPeter Oskolkov 
507df5e3dbSPeter Oskolkov 	/* do not queue past the time horizon */
517df5e3dbSPeter Oskolkov 	if (next_tstamp - now >= TIME_HORIZON_NS)
527df5e3dbSPeter Oskolkov 		return TC_ACT_SHOT;
537df5e3dbSPeter Oskolkov 
547df5e3dbSPeter Oskolkov 	/* set ecn bit, if needed */
557df5e3dbSPeter Oskolkov 	if (next_tstamp - now >= ECN_HORIZON_NS)
567df5e3dbSPeter Oskolkov 		bpf_skb_ecn_set_ce(skb);
577df5e3dbSPeter Oskolkov 
587df5e3dbSPeter Oskolkov 	if (bpf_map_update_elem(&flow_map, &key, &next_tstamp, BPF_EXIST))
597df5e3dbSPeter Oskolkov 		return TC_ACT_SHOT;
607df5e3dbSPeter Oskolkov 	skb->tstamp = next_tstamp;
617df5e3dbSPeter Oskolkov 
627df5e3dbSPeter Oskolkov 	return TC_ACT_OK;
637df5e3dbSPeter Oskolkov }
647df5e3dbSPeter Oskolkov 
handle_tcp(struct __sk_buff * skb,struct tcphdr * tcp)657df5e3dbSPeter Oskolkov static inline int handle_tcp(struct __sk_buff *skb, struct tcphdr *tcp)
667df5e3dbSPeter Oskolkov {
677df5e3dbSPeter Oskolkov 	void *data_end = (void *)(long)skb->data_end;
687df5e3dbSPeter Oskolkov 
697df5e3dbSPeter Oskolkov 	/* drop malformed packets */
707df5e3dbSPeter Oskolkov 	if ((void *)(tcp + 1) > data_end)
717df5e3dbSPeter Oskolkov 		return TC_ACT_SHOT;
727df5e3dbSPeter Oskolkov 
737df5e3dbSPeter Oskolkov 	if (tcp->dest == bpf_htons(9000))
747df5e3dbSPeter Oskolkov 		return throttle_flow(skb);
757df5e3dbSPeter Oskolkov 
767df5e3dbSPeter Oskolkov 	return TC_ACT_OK;
777df5e3dbSPeter Oskolkov }
787df5e3dbSPeter Oskolkov 
handle_ipv4(struct __sk_buff * skb)797df5e3dbSPeter Oskolkov static inline int handle_ipv4(struct __sk_buff *skb)
807df5e3dbSPeter Oskolkov {
817df5e3dbSPeter Oskolkov 	void *data_end = (void *)(long)skb->data_end;
827df5e3dbSPeter Oskolkov 	void *data = (void *)(long)skb->data;
837df5e3dbSPeter Oskolkov 	struct iphdr *iph;
847df5e3dbSPeter Oskolkov 	uint32_t ihl;
857df5e3dbSPeter Oskolkov 
867df5e3dbSPeter Oskolkov 	/* drop malformed packets */
877df5e3dbSPeter Oskolkov 	if (data + sizeof(struct ethhdr) > data_end)
887df5e3dbSPeter Oskolkov 		return TC_ACT_SHOT;
897df5e3dbSPeter Oskolkov 	iph = (struct iphdr *)(data + sizeof(struct ethhdr));
907df5e3dbSPeter Oskolkov 	if ((void *)(iph + 1) > data_end)
917df5e3dbSPeter Oskolkov 		return TC_ACT_SHOT;
927df5e3dbSPeter Oskolkov 	ihl = iph->ihl * 4;
937df5e3dbSPeter Oskolkov 	if (((void *)iph) + ihl > data_end)
947df5e3dbSPeter Oskolkov 		return TC_ACT_SHOT;
957df5e3dbSPeter Oskolkov 
967df5e3dbSPeter Oskolkov 	if (iph->protocol == IPPROTO_TCP)
977df5e3dbSPeter Oskolkov 		return handle_tcp(skb, (struct tcphdr *)(((void *)iph) + ihl));
987df5e3dbSPeter Oskolkov 
997df5e3dbSPeter Oskolkov 	return TC_ACT_OK;
1007df5e3dbSPeter Oskolkov }
1017df5e3dbSPeter Oskolkov 
tc_prog(struct __sk_buff * skb)1027df5e3dbSPeter Oskolkov SEC("cls_test") int tc_prog(struct __sk_buff *skb)
1037df5e3dbSPeter Oskolkov {
1047df5e3dbSPeter Oskolkov 	if (skb->protocol == bpf_htons(ETH_P_IP))
1057df5e3dbSPeter Oskolkov 		return handle_ipv4(skb);
1067df5e3dbSPeter Oskolkov 
1077df5e3dbSPeter Oskolkov 	return TC_ACT_OK;
1087df5e3dbSPeter Oskolkov }
1097df5e3dbSPeter Oskolkov 
1107df5e3dbSPeter Oskolkov char __license[] SEC("license") = "GPL";
111