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