| 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 "vmlinux.h" |
| 14 | #include "net_shared.h" |
| 15 | #include <bpf/bpf_helpers.h> |
| 16 | #include <string.h> |
| 17 | |
| 18 | # define printk(fmt, ...) \ |
| 19 | ({ \ |
| 20 | char ____fmt[] = fmt; \ |
| 21 | bpf_trace_printk(____fmt, sizeof(____fmt), \ |
| 22 | ##__VA_ARGS__); \ |
| 23 | }) |
| 24 | |
| 25 | #define CB_MAGIC 1234 |
| 26 | |
| 27 | /* Test: Pass all packets through */ |
| 28 | SEC("nop" ) |
| 29 | int do_nop(struct __sk_buff *skb) |
| 30 | { |
| 31 | return BPF_OK; |
| 32 | } |
| 33 | |
| 34 | /* Test: Verify context information can be accessed */ |
| 35 | SEC("test_ctx" ) |
| 36 | int do_test_ctx(struct __sk_buff *skb) |
| 37 | { |
| 38 | skb->cb[0] = CB_MAGIC; |
| 39 | printk("len %d hash %d protocol %d" , skb->len, skb->hash, |
| 40 | skb->protocol); |
| 41 | printk("cb %d ingress_ifindex %d ifindex %d" , skb->cb[0], |
| 42 | skb->ingress_ifindex, skb->ifindex); |
| 43 | |
| 44 | return BPF_OK; |
| 45 | } |
| 46 | |
| 47 | /* Test: Ensure skb->cb[] buffer is cleared */ |
| 48 | SEC("test_cb" ) |
| 49 | int do_test_cb(struct __sk_buff *skb) |
| 50 | { |
| 51 | printk("cb0: %x cb1: %x cb2: %x" , skb->cb[0], skb->cb[1], |
| 52 | skb->cb[2]); |
| 53 | printk("cb3: %x cb4: %x" , skb->cb[3], skb->cb[4]); |
| 54 | |
| 55 | return BPF_OK; |
| 56 | } |
| 57 | |
| 58 | /* Test: Verify skb data can be read */ |
| 59 | SEC("test_data" ) |
| 60 | int 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 | |
| 84 | static 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 */ |
| 140 | SEC("test_rewrite" ) |
| 141 | int 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 | |
| 160 | static 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 | |
| 184 | SEC("push_ll_and_redirect_silent" ) |
| 185 | int do_push_ll_and_redirect_silent(struct __sk_buff *skb) |
| 186 | { |
| 187 | return __do_push_ll_and_redirect(skb); |
| 188 | } |
| 189 | |
| 190 | SEC("push_ll_and_redirect" ) |
| 191 | int 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 | |
| 202 | static 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 | |
| 220 | SEC("fill_garbage" ) |
| 221 | int 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 | |
| 228 | SEC("fill_garbage_and_redirect" ) |
| 229 | int 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 */ |
| 238 | SEC("drop_all" ) |
| 239 | int do_drop_all(struct __sk_buff *skb) |
| 240 | { |
| 241 | printk("dropping with: %d" , BPF_DROP); |
| 242 | return BPF_DROP; |
| 243 | } |
| 244 | |
| 245 | char _license[] SEC("license" ) = "GPL" ; |
| 246 | |