125763b3cSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2bd4aed0eSJiong Wang /* Copyright (c) 2017 Facebook
3bd4aed0eSJiong Wang  */
4bd4aed0eSJiong Wang #include <stddef.h>
5bd4aed0eSJiong Wang #include <string.h>
6bd4aed0eSJiong Wang #include <linux/bpf.h>
7bd4aed0eSJiong Wang #include <linux/if_ether.h>
8bd4aed0eSJiong Wang #include <linux/if_packet.h>
9bd4aed0eSJiong Wang #include <linux/ip.h>
10bd4aed0eSJiong Wang #include <linux/ipv6.h>
11bd4aed0eSJiong Wang #include <linux/in.h>
12bd4aed0eSJiong Wang #include <linux/tcp.h>
13bd4aed0eSJiong Wang #include <linux/pkt_cls.h>
143e689141SToke Høiland-Jørgensen #include <bpf/bpf_helpers.h>
153e689141SToke Høiland-Jørgensen #include <bpf/bpf_endian.h>
16*c8ed6685SAndrii Nakryiko #include "bpf_misc.h"
17bd4aed0eSJiong Wang 
184c096324SAlexei Starovoitov /* llvm will optimize both subprograms into exactly the same BPF assembly
194c096324SAlexei Starovoitov  *
204c096324SAlexei Starovoitov  * Disassembly of section .text:
214c096324SAlexei Starovoitov  *
224c096324SAlexei Starovoitov  * 0000000000000000 test_pkt_access_subprog1:
234c096324SAlexei Starovoitov  * ; 	return skb->len * 2;
244c096324SAlexei Starovoitov  *        0:	61 10 00 00 00 00 00 00	r0 = *(u32 *)(r1 + 0)
254c096324SAlexei Starovoitov  *        1:	64 00 00 00 01 00 00 00	w0 <<= 1
264c096324SAlexei Starovoitov  *        2:	95 00 00 00 00 00 00 00	exit
274c096324SAlexei Starovoitov  *
284c096324SAlexei Starovoitov  * 0000000000000018 test_pkt_access_subprog2:
294c096324SAlexei Starovoitov  * ; 	return skb->len * val;
304c096324SAlexei Starovoitov  *        3:	61 10 00 00 00 00 00 00	r0 = *(u32 *)(r1 + 0)
314c096324SAlexei Starovoitov  *        4:	64 00 00 00 01 00 00 00	w0 <<= 1
324c096324SAlexei Starovoitov  *        5:	95 00 00 00 00 00 00 00	exit
334c096324SAlexei Starovoitov  *
344c096324SAlexei Starovoitov  * Which makes it an interesting test for BTF-enabled verifier.
354c096324SAlexei Starovoitov  */
364c096324SAlexei Starovoitov static __attribute__ ((noinline))
test_pkt_access_subprog1(volatile struct __sk_buff * skb)374c096324SAlexei Starovoitov int test_pkt_access_subprog1(volatile struct __sk_buff *skb)
384c096324SAlexei Starovoitov {
394c096324SAlexei Starovoitov 	return skb->len * 2;
404c096324SAlexei Starovoitov }
414c096324SAlexei Starovoitov 
424c096324SAlexei Starovoitov static __attribute__ ((noinline))
test_pkt_access_subprog2(int val,volatile struct __sk_buff * skb)434c096324SAlexei Starovoitov int test_pkt_access_subprog2(int val, volatile struct __sk_buff *skb)
444c096324SAlexei Starovoitov {
454c096324SAlexei Starovoitov 	return skb->len * val;
464c096324SAlexei Starovoitov }
474c096324SAlexei Starovoitov 
487608e4dbSAlexei Starovoitov #define MAX_STACK (512 - 2 * 32)
497608e4dbSAlexei Starovoitov 
507608e4dbSAlexei Starovoitov __attribute__ ((noinline))
get_skb_len(struct __sk_buff * skb)517608e4dbSAlexei Starovoitov int get_skb_len(struct __sk_buff *skb)
527608e4dbSAlexei Starovoitov {
537608e4dbSAlexei Starovoitov 	volatile char buf[MAX_STACK] = {};
547608e4dbSAlexei Starovoitov 
55*c8ed6685SAndrii Nakryiko 	__sink(buf[MAX_STACK - 1]);
56*c8ed6685SAndrii Nakryiko 
577608e4dbSAlexei Starovoitov 	return skb->len;
587608e4dbSAlexei Starovoitov }
597608e4dbSAlexei Starovoitov 
607805fe84SAlexei Starovoitov __attribute__ ((noinline))
get_constant(long val)617805fe84SAlexei Starovoitov int get_constant(long val)
627805fe84SAlexei Starovoitov {
637805fe84SAlexei Starovoitov 	return val - 122;
647805fe84SAlexei Starovoitov }
657805fe84SAlexei Starovoitov 
667608e4dbSAlexei Starovoitov int get_skb_ifindex(int, struct __sk_buff *skb, int);
677608e4dbSAlexei Starovoitov 
687608e4dbSAlexei Starovoitov __attribute__ ((noinline))
test_pkt_access_subprog3(int val,struct __sk_buff * skb)697608e4dbSAlexei Starovoitov int test_pkt_access_subprog3(int val, struct __sk_buff *skb)
707608e4dbSAlexei Starovoitov {
717805fe84SAlexei Starovoitov 	return get_skb_len(skb) * get_skb_ifindex(val, skb, get_constant(123));
727608e4dbSAlexei Starovoitov }
737608e4dbSAlexei Starovoitov 
747608e4dbSAlexei Starovoitov __attribute__ ((noinline))
get_skb_ifindex(int val,struct __sk_buff * skb,int var)757608e4dbSAlexei Starovoitov int get_skb_ifindex(int val, struct __sk_buff *skb, int var)
767608e4dbSAlexei Starovoitov {
777608e4dbSAlexei Starovoitov 	volatile char buf[MAX_STACK] = {};
787608e4dbSAlexei Starovoitov 
79*c8ed6685SAndrii Nakryiko 	__sink(buf[MAX_STACK - 1]);
80*c8ed6685SAndrii Nakryiko 
817608e4dbSAlexei Starovoitov 	return skb->ifindex * val * var;
827608e4dbSAlexei Starovoitov }
837608e4dbSAlexei Starovoitov 
846dc03dc7SUdip Pant __attribute__ ((noinline))
test_pkt_write_access_subprog(struct __sk_buff * skb,__u32 off)856dc03dc7SUdip Pant int test_pkt_write_access_subprog(struct __sk_buff *skb, __u32 off)
866dc03dc7SUdip Pant {
876dc03dc7SUdip Pant 	void *data = (void *)(long)skb->data;
886dc03dc7SUdip Pant 	void *data_end = (void *)(long)skb->data_end;
896dc03dc7SUdip Pant 	struct tcphdr *tcp = NULL;
906dc03dc7SUdip Pant 
916dc03dc7SUdip Pant 	if (off > sizeof(struct ethhdr) + sizeof(struct ipv6hdr))
926dc03dc7SUdip Pant 		return -1;
936dc03dc7SUdip Pant 
946dc03dc7SUdip Pant 	tcp = data + off;
956dc03dc7SUdip Pant 	if (tcp + 1 > data_end)
966dc03dc7SUdip Pant 		return -1;
976dc03dc7SUdip Pant 	/* make modification to the packet data */
986dc03dc7SUdip Pant 	tcp->check++;
996dc03dc7SUdip Pant 	return 0;
1006dc03dc7SUdip Pant }
1016dc03dc7SUdip Pant 
102c22bdd28SAndrii Nakryiko SEC("tc")
test_pkt_access(struct __sk_buff * skb)1034c096324SAlexei Starovoitov int test_pkt_access(struct __sk_buff *skb)
104bd4aed0eSJiong Wang {
105bd4aed0eSJiong Wang 	void *data_end = (void *)(long)skb->data_end;
106bd4aed0eSJiong Wang 	void *data = (void *)(long)skb->data;
107bd4aed0eSJiong Wang 	struct ethhdr *eth = (struct ethhdr *)(data);
108bd4aed0eSJiong Wang 	struct tcphdr *tcp = NULL;
109bd4aed0eSJiong Wang 	__u8 proto = 255;
110bd4aed0eSJiong Wang 	__u64 ihl_len;
111bd4aed0eSJiong Wang 
112bd4aed0eSJiong Wang 	if (eth + 1 > data_end)
113bd4aed0eSJiong Wang 		return TC_ACT_SHOT;
114bd4aed0eSJiong Wang 
115bd4aed0eSJiong Wang 	if (eth->h_proto == bpf_htons(ETH_P_IP)) {
116bd4aed0eSJiong Wang 		struct iphdr *iph = (struct iphdr *)(eth + 1);
117bd4aed0eSJiong Wang 
118bd4aed0eSJiong Wang 		if (iph + 1 > data_end)
119bd4aed0eSJiong Wang 			return TC_ACT_SHOT;
120bd4aed0eSJiong Wang 		ihl_len = iph->ihl * 4;
121bd4aed0eSJiong Wang 		proto = iph->protocol;
122bd4aed0eSJiong Wang 		tcp = (struct tcphdr *)((void *)(iph) + ihl_len);
123bd4aed0eSJiong Wang 	} else if (eth->h_proto == bpf_htons(ETH_P_IPV6)) {
124bd4aed0eSJiong Wang 		struct ipv6hdr *ip6h = (struct ipv6hdr *)(eth + 1);
125bd4aed0eSJiong Wang 
126bd4aed0eSJiong Wang 		if (ip6h + 1 > data_end)
127bd4aed0eSJiong Wang 			return TC_ACT_SHOT;
128bd4aed0eSJiong Wang 		ihl_len = sizeof(*ip6h);
129bd4aed0eSJiong Wang 		proto = ip6h->nexthdr;
130bd4aed0eSJiong Wang 		tcp = (struct tcphdr *)((void *)(ip6h) + ihl_len);
131bd4aed0eSJiong Wang 	}
132bd4aed0eSJiong Wang 
1334c096324SAlexei Starovoitov 	if (test_pkt_access_subprog1(skb) != skb->len * 2)
1344c096324SAlexei Starovoitov 		return TC_ACT_SHOT;
1354c096324SAlexei Starovoitov 	if (test_pkt_access_subprog2(2, skb) != skb->len * 2)
1364c096324SAlexei Starovoitov 		return TC_ACT_SHOT;
1377608e4dbSAlexei Starovoitov 	if (test_pkt_access_subprog3(3, skb) != skb->len * 3 * skb->ifindex)
1387608e4dbSAlexei Starovoitov 		return TC_ACT_SHOT;
139bd4aed0eSJiong Wang 	if (tcp) {
1406dc03dc7SUdip Pant 		if (test_pkt_write_access_subprog(skb, (void *)tcp - data))
1416dc03dc7SUdip Pant 			return TC_ACT_SHOT;
142bd4aed0eSJiong Wang 		if (((void *)(tcp) + 20) > data_end || proto != 6)
143bd4aed0eSJiong Wang 			return TC_ACT_SHOT;
144bd4aed0eSJiong Wang 		barrier(); /* to force ordering of checks */
145bd4aed0eSJiong Wang 		if (((void *)(tcp) + 18) > data_end)
146bd4aed0eSJiong Wang 			return TC_ACT_SHOT;
147bd4aed0eSJiong Wang 		if (tcp->urg_ptr == 123)
148bd4aed0eSJiong Wang 			return TC_ACT_OK;
149bd4aed0eSJiong Wang 	}
150bd4aed0eSJiong Wang 
151bd4aed0eSJiong Wang 	return TC_ACT_UNSPEC;
152bd4aed0eSJiong Wang }
153