1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Copyright (c) 2019 Facebook */ |
3 | #include <linux/stddef.h> |
4 | #include <linux/if_ether.h> |
5 | #include <linux/ipv6.h> |
6 | #include <linux/bpf.h> |
7 | #include <linux/tcp.h> |
8 | #include <bpf/bpf_helpers.h> |
9 | #include <bpf/bpf_endian.h> |
10 | #include <bpf/bpf_tracing.h> |
11 | |
12 | struct sk_buff { |
13 | unsigned int len; |
14 | }; |
15 | |
16 | __u64 test_result = 0; |
17 | SEC("fexit/test_pkt_access" ) |
18 | int BPF_PROG(test_main, struct sk_buff *skb, int ret) |
19 | { |
20 | int len; |
21 | |
22 | __builtin_preserve_access_index(({ |
23 | len = skb->len; |
24 | })); |
25 | if (len != 74 || ret != 0) |
26 | return 0; |
27 | test_result = 1; |
28 | return 0; |
29 | } |
30 | |
31 | __u64 test_result_subprog1 = 0; |
32 | SEC("fexit/test_pkt_access_subprog1" ) |
33 | int BPF_PROG(test_subprog1, struct sk_buff *skb, int ret) |
34 | { |
35 | int len; |
36 | |
37 | __builtin_preserve_access_index(({ |
38 | len = skb->len; |
39 | })); |
40 | if (len != 74 || ret != 148) |
41 | return 0; |
42 | test_result_subprog1 = 1; |
43 | return 0; |
44 | } |
45 | |
46 | /* Though test_pkt_access_subprog2() is defined in C as: |
47 | * static __attribute__ ((noinline)) |
48 | * int test_pkt_access_subprog2(int val, volatile struct __sk_buff *skb) |
49 | * { |
50 | * return skb->len * val; |
51 | * } |
52 | * llvm optimizations remove 'int val' argument and generate BPF assembly: |
53 | * r0 = *(u32 *)(r1 + 0) |
54 | * w0 <<= 1 |
55 | * exit |
56 | * In such case the verifier falls back to conservative and |
57 | * tracing program can access arguments and return value as u64 |
58 | * instead of accurate types. |
59 | */ |
60 | struct args_subprog2 { |
61 | __u64 args[5]; |
62 | __u64 ret; |
63 | }; |
64 | __u64 test_result_subprog2 = 0; |
65 | SEC("fexit/test_pkt_access_subprog2" ) |
66 | int test_subprog2(struct args_subprog2 *ctx) |
67 | { |
68 | struct sk_buff *skb = (void *)ctx->args[0]; |
69 | __u64 ret; |
70 | int len; |
71 | |
72 | bpf_probe_read_kernel(&len, sizeof(len), |
73 | __builtin_preserve_access_index(&skb->len)); |
74 | |
75 | ret = ctx->ret; |
76 | /* bpf_prog_test_load() loads "test_pkt_access.bpf.o" with |
77 | * BPF_F_TEST_RND_HI32 which randomizes upper 32 bits after BPF_ALU32 |
78 | * insns. Hence after 'w0 <<= 1' upper bits of $rax are random. That is |
79 | * expected and correct. Trim them. |
80 | */ |
81 | ret = (__u32) ret; |
82 | if (len != 74 || ret != 148) |
83 | return 0; |
84 | test_result_subprog2 = 1; |
85 | return 0; |
86 | } |
87 | |
88 | __u64 test_result_subprog3 = 0; |
89 | SEC("fexit/test_pkt_access_subprog3" ) |
90 | int BPF_PROG(test_subprog3, int val, struct sk_buff *skb, int ret) |
91 | { |
92 | int len; |
93 | |
94 | __builtin_preserve_access_index(({ |
95 | len = skb->len; |
96 | })); |
97 | if (len != 74 || ret != 74 * val || val != 3) |
98 | return 0; |
99 | test_result_subprog3 = 1; |
100 | return 0; |
101 | } |
102 | |
103 | __u64 test_get_skb_len = 0; |
104 | SEC("freplace/get_skb_len" ) |
105 | int new_get_skb_len(struct __sk_buff *skb) |
106 | { |
107 | int len = skb->len; |
108 | |
109 | if (len != 74) |
110 | return 0; |
111 | test_get_skb_len = 1; |
112 | return 74; /* original get_skb_len() returns skb->len */ |
113 | } |
114 | |
115 | __u64 test_get_skb_ifindex = 0; |
116 | SEC("freplace/get_skb_ifindex" ) |
117 | int new_get_skb_ifindex(int val, struct __sk_buff *skb, int var) |
118 | { |
119 | void *data_end = (void *)(long)skb->data_end; |
120 | void *data = (void *)(long)skb->data; |
121 | struct ipv6hdr ip6, *ip6p; |
122 | int ifindex = skb->ifindex; |
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 | |
155 | __u64 test_pkt_write_access_subprog = 0; |
156 | SEC("freplace/test_pkt_write_access_subprog" ) |
157 | int new_test_pkt_write_access_subprog(struct __sk_buff *skb, __u32 off) |
158 | { |
159 | |
160 | void *data = (void *)(long)skb->data; |
161 | void *data_end = (void *)(long)skb->data_end; |
162 | struct tcphdr *tcp; |
163 | |
164 | if (off > sizeof(struct ethhdr) + sizeof(struct ipv6hdr)) |
165 | return -1; |
166 | |
167 | tcp = data + off; |
168 | if (tcp + 1 > data_end) |
169 | return -1; |
170 | |
171 | /* make modifications to the packet data */ |
172 | tcp->check++; |
173 | tcp->syn = 0; |
174 | |
175 | test_pkt_write_access_subprog = 1; |
176 | return 0; |
177 | } |
178 | |
179 | char _license[] SEC("license" ) = "GPL" ; |
180 | |