167117Sdfr/* Copyright (c) 2016 Thomas Graf <tgraf@tgraf.ch>
267117Sdfr *
367117Sdfr * This program is free software; you can redistribute it and/or
467117Sdfr * modify it under the terms of version 2 of the GNU General Public
567117Sdfr * License as published by the Free Software Foundation.
667117Sdfr *
767117Sdfr * This program is distributed in the hope that it will be useful, but
867117Sdfr * WITHOUT ANY WARRANTY; without even the implied warranty of
967117Sdfr * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1067117Sdfr * General Public License for more details.
1167117Sdfr */
1267117Sdfr
1367117Sdfr#include "vmlinux.h"
1467117Sdfr#include "net_shared.h"
1567117Sdfr#include <bpf/bpf_helpers.h>
1667117Sdfr#include <string.h>
1767117Sdfr
1867117Sdfr# define printk(fmt, ...)						\
1967117Sdfr		({							\
2067117Sdfr			char ____fmt[] = fmt;				\
2167117Sdfr			bpf_trace_printk(____fmt, sizeof(____fmt),	\
2267117Sdfr				     ##__VA_ARGS__);			\
2367117Sdfr		})
2467117Sdfr
2567117Sdfr#define CB_MAGIC 1234
2667117Sdfr
2767117Sdfr/* Test: Pass all packets through */
2867117SdfrSEC("nop")
2967117Sdfrint do_nop(struct __sk_buff *skb)
30111777Sobrien{
31111777Sobrien	return BPF_OK;
32111777Sobrien}
3367117Sdfr
3467117Sdfr/* Test: Verify context information can be accessed */
3567117SdfrSEC("test_ctx")
3667117Sdfrint do_test_ctx(struct __sk_buff *skb)
3767117Sdfr{
3867117Sdfr	skb->cb[0] = CB_MAGIC;
3967117Sdfr	printk("len %d hash %d protocol %d", skb->len, skb->hash,
4067117Sdfr	       skb->protocol);
4167117Sdfr	printk("cb %d ingress_ifindex %d ifindex %d", skb->cb[0],
4267117Sdfr	       skb->ingress_ifindex, skb->ifindex);
4367117Sdfr
4484755Sdfr	return BPF_OK;
4567117Sdfr}
4684755Sdfr
47115084Smarcel/* Test: Ensure skb->cb[] buffer is cleared */
48115084SmarcelSEC("test_cb")
49115084Smarcelint do_test_cb(struct __sk_buff *skb)
5067117Sdfr{
51115084Smarcel	printk("cb0: %x cb1: %x cb2: %x", skb->cb[0], skb->cb[1],
5284755Sdfr	       skb->cb[2]);
5384755Sdfr	printk("cb3: %x cb4: %x", skb->cb[3], skb->cb[4]);
54115084Smarcel
5567117Sdfr	return BPF_OK;
5667117Sdfr}
5767117Sdfr
58/* Test: Verify skb data can be read */
59SEC("test_data")
60int 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
84static 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 */
140SEC("test_rewrite")
141int 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
160static 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
184SEC("push_ll_and_redirect_silent")
185int do_push_ll_and_redirect_silent(struct __sk_buff *skb)
186{
187	return __do_push_ll_and_redirect(skb);
188}
189
190SEC("push_ll_and_redirect")
191int 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
202static 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
220SEC("fill_garbage")
221int 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
228SEC("fill_garbage_and_redirect")
229int 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 */
238SEC("drop_all")
239int do_drop_all(struct __sk_buff *skb)
240{
241	printk("dropping with: %d", BPF_DROP);
242	return BPF_DROP;
243}
244
245char _license[] SEC("license") = "GPL";
246