xref: /openbmc/linux/samples/bpf/test_lwt_bpf.c (revision 7419995a)
1 /* Copyright (c) 2016 Thomas Graf <tgraf@tgraf.ch>
2  *
3  * This program is free software; you can redistribute it and/or
4  * modify it under the terms of version 2 of the GNU General Public
5  * License as published by the Free Software Foundation.
6  *
7  * This program is distributed in the hope that it will be useful, but
8  * WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10  * General Public License for more details.
11  */
12 
13 #include <stdint.h>
14 #include <stddef.h>
15 #include <linux/bpf.h>
16 #include <linux/ip.h>
17 #include <linux/in.h>
18 #include <linux/in6.h>
19 #include <linux/tcp.h>
20 #include <linux/udp.h>
21 #include <linux/icmpv6.h>
22 #include <linux/if_ether.h>
23 #include <bpf/bpf_helpers.h>
24 #include <string.h>
25 
26 # define printk(fmt, ...)						\
27 		({							\
28 			char ____fmt[] = fmt;				\
29 			bpf_trace_printk(____fmt, sizeof(____fmt),	\
30 				     ##__VA_ARGS__);			\
31 		})
32 
33 #define CB_MAGIC 1234
34 
35 /* Test: Pass all packets through */
36 SEC("nop")
37 int do_nop(struct __sk_buff *skb)
38 {
39 	return BPF_OK;
40 }
41 
42 /* Test: Verify context information can be accessed */
43 SEC("test_ctx")
44 int do_test_ctx(struct __sk_buff *skb)
45 {
46 	skb->cb[0] = CB_MAGIC;
47 	printk("len %d hash %d protocol %d\n", skb->len, skb->hash,
48 	       skb->protocol);
49 	printk("cb %d ingress_ifindex %d ifindex %d\n", skb->cb[0],
50 	       skb->ingress_ifindex, skb->ifindex);
51 
52 	return BPF_OK;
53 }
54 
55 /* Test: Ensure skb->cb[] buffer is cleared */
56 SEC("test_cb")
57 int do_test_cb(struct __sk_buff *skb)
58 {
59 	printk("cb0: %x cb1: %x cb2: %x\n", skb->cb[0], skb->cb[1],
60 	       skb->cb[2]);
61 	printk("cb3: %x cb4: %x\n", skb->cb[3], skb->cb[4]);
62 
63 	return BPF_OK;
64 }
65 
66 /* Test: Verify skb data can be read */
67 SEC("test_data")
68 int do_test_data(struct __sk_buff *skb)
69 {
70 	void *data = (void *)(long)skb->data;
71 	void *data_end = (void *)(long)skb->data_end;
72 	struct iphdr *iph = data;
73 
74 	if (data + sizeof(*iph) > data_end) {
75 		printk("packet truncated\n");
76 		return BPF_DROP;
77 	}
78 
79 	printk("src: %x dst: %x\n", iph->saddr, iph->daddr);
80 
81 	return BPF_OK;
82 }
83 
84 #define IP_CSUM_OFF offsetof(struct iphdr, check)
85 #define IP_DST_OFF offsetof(struct iphdr, daddr)
86 #define IP_SRC_OFF offsetof(struct iphdr, saddr)
87 #define IP_PROTO_OFF offsetof(struct iphdr, protocol)
88 #define TCP_CSUM_OFF offsetof(struct tcphdr, check)
89 #define UDP_CSUM_OFF offsetof(struct udphdr, check)
90 #define IS_PSEUDO 0x10
91 
92 static inline int rewrite(struct __sk_buff *skb, uint32_t old_ip,
93 			  uint32_t new_ip, int rw_daddr)
94 {
95 	int ret, off = 0, flags = IS_PSEUDO;
96 	uint8_t proto;
97 
98 	ret = bpf_skb_load_bytes(skb, IP_PROTO_OFF, &proto, 1);
99 	if (ret < 0) {
100 		printk("bpf_l4_csum_replace failed: %d\n", ret);
101 		return BPF_DROP;
102 	}
103 
104 	switch (proto) {
105 	case IPPROTO_TCP:
106 		off = TCP_CSUM_OFF;
107 		break;
108 
109 	case IPPROTO_UDP:
110 		off = UDP_CSUM_OFF;
111 		flags |= BPF_F_MARK_MANGLED_0;
112 		break;
113 
114 	case IPPROTO_ICMPV6:
115 		off = offsetof(struct icmp6hdr, icmp6_cksum);
116 		break;
117 	}
118 
119 	if (off) {
120 		ret = bpf_l4_csum_replace(skb, off, old_ip, new_ip,
121 					  flags | sizeof(new_ip));
122 		if (ret < 0) {
123 			printk("bpf_l4_csum_replace failed: %d\n");
124 			return BPF_DROP;
125 		}
126 	}
127 
128 	ret = bpf_l3_csum_replace(skb, IP_CSUM_OFF, old_ip, new_ip, sizeof(new_ip));
129 	if (ret < 0) {
130 		printk("bpf_l3_csum_replace failed: %d\n", ret);
131 		return BPF_DROP;
132 	}
133 
134 	if (rw_daddr)
135 		ret = bpf_skb_store_bytes(skb, IP_DST_OFF, &new_ip, sizeof(new_ip), 0);
136 	else
137 		ret = bpf_skb_store_bytes(skb, IP_SRC_OFF, &new_ip, sizeof(new_ip), 0);
138 
139 	if (ret < 0) {
140 		printk("bpf_skb_store_bytes() failed: %d\n", ret);
141 		return BPF_DROP;
142 	}
143 
144 	return BPF_OK;
145 }
146 
147 /* Test: Verify skb data can be modified */
148 SEC("test_rewrite")
149 int do_test_rewrite(struct __sk_buff *skb)
150 {
151 	uint32_t old_ip, new_ip = 0x3fea8c0;
152 	int ret;
153 
154 	ret = bpf_skb_load_bytes(skb, IP_DST_OFF, &old_ip, 4);
155 	if (ret < 0) {
156 		printk("bpf_skb_load_bytes failed: %d\n", ret);
157 		return BPF_DROP;
158 	}
159 
160 	if (old_ip == 0x2fea8c0) {
161 		printk("out: rewriting from %x to %x\n", old_ip, new_ip);
162 		return rewrite(skb, old_ip, new_ip, 1);
163 	}
164 
165 	return BPF_OK;
166 }
167 
168 static inline int __do_push_ll_and_redirect(struct __sk_buff *skb)
169 {
170 	uint64_t smac = SRC_MAC, dmac = DST_MAC;
171 	int ret, ifindex = DST_IFINDEX;
172 	struct ethhdr ehdr;
173 
174 	ret = bpf_skb_change_head(skb, 14, 0);
175 	if (ret < 0) {
176 		printk("skb_change_head() failed: %d\n", ret);
177 	}
178 
179 	ehdr.h_proto = __constant_htons(ETH_P_IP);
180 	memcpy(&ehdr.h_source, &smac, 6);
181 	memcpy(&ehdr.h_dest, &dmac, 6);
182 
183 	ret = bpf_skb_store_bytes(skb, 0, &ehdr, sizeof(ehdr), 0);
184 	if (ret < 0) {
185 		printk("skb_store_bytes() failed: %d\n", ret);
186 		return BPF_DROP;
187 	}
188 
189 	return bpf_redirect(ifindex, 0);
190 }
191 
192 SEC("push_ll_and_redirect_silent")
193 int do_push_ll_and_redirect_silent(struct __sk_buff *skb)
194 {
195 	return __do_push_ll_and_redirect(skb);
196 }
197 
198 SEC("push_ll_and_redirect")
199 int do_push_ll_and_redirect(struct __sk_buff *skb)
200 {
201 	int ret, ifindex = DST_IFINDEX;
202 
203 	ret = __do_push_ll_and_redirect(skb);
204 	if (ret >= 0)
205 		printk("redirected to %d\n", ifindex);
206 
207 	return ret;
208 }
209 
210 static inline void __fill_garbage(struct __sk_buff *skb)
211 {
212 	uint64_t f = 0xFFFFFFFFFFFFFFFF;
213 
214 	bpf_skb_store_bytes(skb, 0, &f, sizeof(f), 0);
215 	bpf_skb_store_bytes(skb, 8, &f, sizeof(f), 0);
216 	bpf_skb_store_bytes(skb, 16, &f, sizeof(f), 0);
217 	bpf_skb_store_bytes(skb, 24, &f, sizeof(f), 0);
218 	bpf_skb_store_bytes(skb, 32, &f, sizeof(f), 0);
219 	bpf_skb_store_bytes(skb, 40, &f, sizeof(f), 0);
220 	bpf_skb_store_bytes(skb, 48, &f, sizeof(f), 0);
221 	bpf_skb_store_bytes(skb, 56, &f, sizeof(f), 0);
222 	bpf_skb_store_bytes(skb, 64, &f, sizeof(f), 0);
223 	bpf_skb_store_bytes(skb, 72, &f, sizeof(f), 0);
224 	bpf_skb_store_bytes(skb, 80, &f, sizeof(f), 0);
225 	bpf_skb_store_bytes(skb, 88, &f, sizeof(f), 0);
226 }
227 
228 SEC("fill_garbage")
229 int do_fill_garbage(struct __sk_buff *skb)
230 {
231 	__fill_garbage(skb);
232 	printk("Set initial 96 bytes of header to FF\n");
233 	return BPF_OK;
234 }
235 
236 SEC("fill_garbage_and_redirect")
237 int do_fill_garbage_and_redirect(struct __sk_buff *skb)
238 {
239 	int ifindex = DST_IFINDEX;
240 	__fill_garbage(skb);
241 	printk("redirected to %d\n", ifindex);
242 	return bpf_redirect(ifindex, 0);
243 }
244 
245 /* Drop all packets */
246 SEC("drop_all")
247 int do_drop_all(struct __sk_buff *skb)
248 {
249 	printk("dropping with: %d\n", BPF_DROP);
250 	return BPF_DROP;
251 }
252 
253 char _license[] SEC("license") = "GPL";
254