Thomas Graf | f74599f | 2016-11-30 17:10:11 +0100 | [diff] [blame] | 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> |
Toke Høiland-Jørgensen | 7cf245a | 2020-01-20 14:06:49 +0100 | [diff] [blame] | 23 | #include <bpf/bpf_helpers.h> |
Thomas Graf | f74599f | 2016-11-30 17:10:11 +0100 | [diff] [blame] | 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"; |