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