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