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