1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2019 Facebook */
3 #include <linux/stddef.h>
4 #include <linux/ipv6.h>
5 #include <linux/bpf.h>
6 #include <bpf/bpf_helpers.h>
7 #include <bpf/bpf_endian.h>
8 #include <bpf/bpf_tracing.h>
9 
10 struct sk_buff {
11 	unsigned int len;
12 };
13 
14 __u64 test_result = 0;
15 SEC("fexit/test_pkt_access")
16 int BPF_PROG(test_main, struct sk_buff *skb, int ret)
17 {
18 	int len;
19 
20 	__builtin_preserve_access_index(({
21 		len = skb->len;
22 	}));
23 	if (len != 74 || ret != 0)
24 		return 0;
25 	test_result = 1;
26 	return 0;
27 }
28 
29 __u64 test_result_subprog1 = 0;
30 SEC("fexit/test_pkt_access_subprog1")
31 int BPF_PROG(test_subprog1, struct sk_buff *skb, int ret)
32 {
33 	int len;
34 
35 	__builtin_preserve_access_index(({
36 		len = skb->len;
37 	}));
38 	if (len != 74 || ret != 148)
39 		return 0;
40 	test_result_subprog1 = 1;
41 	return 0;
42 }
43 
44 /* Though test_pkt_access_subprog2() is defined in C as:
45  * static __attribute__ ((noinline))
46  * int test_pkt_access_subprog2(int val, volatile struct __sk_buff *skb)
47  * {
48  *     return skb->len * val;
49  * }
50  * llvm optimizations remove 'int val' argument and generate BPF assembly:
51  *   r0 = *(u32 *)(r1 + 0)
52  *   w0 <<= 1
53  *   exit
54  * In such case the verifier falls back to conservative and
55  * tracing program can access arguments and return value as u64
56  * instead of accurate types.
57  */
58 struct args_subprog2 {
59 	__u64 args[5];
60 	__u64 ret;
61 };
62 __u64 test_result_subprog2 = 0;
63 SEC("fexit/test_pkt_access_subprog2")
64 int test_subprog2(struct args_subprog2 *ctx)
65 {
66 	struct sk_buff *skb = (void *)ctx->args[0];
67 	__u64 ret;
68 	int len;
69 
70 	bpf_probe_read_kernel(&len, sizeof(len),
71 			      __builtin_preserve_access_index(&skb->len));
72 
73 	ret = ctx->ret;
74 	/* bpf_prog_load() loads "test_pkt_access.o" with BPF_F_TEST_RND_HI32
75 	 * which randomizes upper 32 bits after BPF_ALU32 insns.
76 	 * Hence after 'w0 <<= 1' upper bits of $rax are random.
77 	 * That is expected and correct. Trim them.
78 	 */
79 	ret = (__u32) ret;
80 	if (len != 74 || ret != 148)
81 		return 0;
82 	test_result_subprog2 = 1;
83 	return 0;
84 }
85 
86 __u64 test_result_subprog3 = 0;
87 SEC("fexit/test_pkt_access_subprog3")
88 int BPF_PROG(test_subprog3, int val, struct sk_buff *skb, int ret)
89 {
90 	int len;
91 
92 	__builtin_preserve_access_index(({
93 		len = skb->len;
94 	}));
95 	if (len != 74 || ret != 74 * val || val != 3)
96 		return 0;
97 	test_result_subprog3 = 1;
98 	return 0;
99 }
100 
101 __u64 test_get_skb_len = 0;
102 SEC("freplace/get_skb_len")
103 int new_get_skb_len(struct __sk_buff *skb)
104 {
105 	int len = skb->len;
106 
107 	if (len != 74)
108 		return 0;
109 	test_get_skb_len = 1;
110 	return 74; /* original get_skb_len() returns skb->len */
111 }
112 
113 __u64 test_get_skb_ifindex = 0;
114 SEC("freplace/get_skb_ifindex")
115 int new_get_skb_ifindex(int val, struct __sk_buff *skb, int var)
116 {
117 	void *data_end = (void *)(long)skb->data_end;
118 	void *data = (void *)(long)skb->data;
119 	struct ipv6hdr ip6, *ip6p;
120 	int ifindex = skb->ifindex;
121 	__u32 eth_proto;
122 	__u32 nh_off;
123 
124 	/* check that BPF extension can read packet via direct packet access */
125 	if (data + 14 + sizeof(ip6) > data_end)
126 		return 0;
127 	ip6p = data + 14;
128 
129 	if (ip6p->nexthdr != 6 || ip6p->payload_len != __bpf_constant_htons(123))
130 		return 0;
131 
132 	/* check that legacy packet access helper works too */
133 	if (bpf_skb_load_bytes(skb, 14, &ip6, sizeof(ip6)) < 0)
134 		return 0;
135 	ip6p = &ip6;
136 	if (ip6p->nexthdr != 6 || ip6p->payload_len != __bpf_constant_htons(123))
137 		return 0;
138 
139 	if (ifindex != 1 || val != 3 || var != 1)
140 		return 0;
141 	test_get_skb_ifindex = 1;
142 	return 3; /* original get_skb_ifindex() returns val * ifindex * var */
143 }
144 
145 volatile __u64 test_get_constant = 0;
146 SEC("freplace/get_constant")
147 int new_get_constant(long val)
148 {
149 	if (val != 123)
150 		return 0;
151 	test_get_constant = 1;
152 	return test_get_constant; /* original get_constant() returns val - 122 */
153 }
154 char _license[] SEC("license") = "GPL";
155