linux/samples/bpf/test_lwt_bpf.c
<<
>>
Prefs
   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 */
  36SEC("nop")
  37int do_nop(struct __sk_buff *skb)
  38{
  39        return BPF_OK;
  40}
  41
  42/* Test: Verify context information can be accessed */
  43SEC("test_ctx")
  44int 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 */
  56SEC("test_cb")
  57int 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 */
  67SEC("test_data")
  68int 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
  92static 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 */
 148SEC("test_rewrite")
 149int 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
 168static 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
 192SEC("push_ll_and_redirect_silent")
 193int do_push_ll_and_redirect_silent(struct __sk_buff *skb)
 194{
 195        return __do_push_ll_and_redirect(skb);
 196}
 197
 198SEC("push_ll_and_redirect")
 199int 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
 210static 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
 228SEC("fill_garbage")
 229int 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
 236SEC("fill_garbage_and_redirect")
 237int 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 */
 246SEC("drop_all")
 247int do_drop_all(struct __sk_buff *skb)
 248{
 249        printk("dropping with: %d\n", BPF_DROP);
 250        return BPF_DROP;
 251}
 252
 253char _license[] SEC("license") = "GPL";
 254