blob: 1b568575ad11aae31b44f908e596bf3bffe97491 [file] [log] [blame]
Thomas Graff74599f2016-11-30 17:10:11 +01001/* 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>
Toke Høiland-Jørgensen7cf245a2020-01-20 14:06:49 +010023#include <bpf/bpf_helpers.h>
Thomas Graff74599f2016-11-30 17:10:11 +010024#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 */
36SEC("nop")
37int do_nop(struct __sk_buff *skb)
38{
39 return BPF_OK;
40}
41
42/* Test: Verify context information can be accessed */
43SEC("test_ctx")
44int 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 */
56SEC("test_cb")
57int 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 */
67SEC("test_data")
68int 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
92static 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 */
148SEC("test_rewrite")
149int 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
168static 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
192SEC("push_ll_and_redirect_silent")
193int do_push_ll_and_redirect_silent(struct __sk_buff *skb)
194{
195 return __do_push_ll_and_redirect(skb);
196}
197
198SEC("push_ll_and_redirect")
199int 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
210static 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
228SEC("fill_garbage")
229int 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
236SEC("fill_garbage_and_redirect")
237int 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 */
246SEC("drop_all")
247int do_drop_all(struct __sk_buff *skb)
248{
249 printk("dropping with: %d\n", BPF_DROP);
250 return BPF_DROP;
251}
252
253char _license[] SEC("license") = "GPL";