linux/samples/bpf/tc_l2_redirect_kern.c
<<
>>
Prefs
   1/* Copyright (c) 2016 Facebook
   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#define KBUILD_MODNAME "foo"
   8#include <uapi/linux/bpf.h>
   9#include <uapi/linux/if_ether.h>
  10#include <uapi/linux/if_packet.h>
  11#include <uapi/linux/ip.h>
  12#include <uapi/linux/ipv6.h>
  13#include <uapi/linux/in.h>
  14#include <uapi/linux/tcp.h>
  15#include <uapi/linux/filter.h>
  16#include <uapi/linux/pkt_cls.h>
  17#include <net/ipv6.h>
  18#include <bpf/bpf_helpers.h>
  19
  20#define _htonl __builtin_bswap32
  21
  22#define PIN_GLOBAL_NS           2
  23struct bpf_elf_map {
  24        __u32 type;
  25        __u32 size_key;
  26        __u32 size_value;
  27        __u32 max_elem;
  28        __u32 flags;
  29        __u32 id;
  30        __u32 pinning;
  31};
  32
  33/* copy of 'struct ethhdr' without __packed */
  34struct eth_hdr {
  35        unsigned char   h_dest[ETH_ALEN];
  36        unsigned char   h_source[ETH_ALEN];
  37        unsigned short  h_proto;
  38};
  39
  40struct bpf_elf_map SEC("maps") tun_iface = {
  41        .type = BPF_MAP_TYPE_ARRAY,
  42        .size_key = sizeof(int),
  43        .size_value = sizeof(int),
  44        .pinning = PIN_GLOBAL_NS,
  45        .max_elem = 1,
  46};
  47
  48static __always_inline bool is_vip_addr(__be16 eth_proto, __be32 daddr)
  49{
  50        if (eth_proto == htons(ETH_P_IP))
  51                return (_htonl(0xffffff00) & daddr) == _htonl(0x0a0a0100);
  52        else if (eth_proto == htons(ETH_P_IPV6))
  53                return (daddr == _htonl(0x2401face));
  54
  55        return false;
  56}
  57
  58SEC("l2_to_iptun_ingress_forward")
  59int _l2_to_iptun_ingress_forward(struct __sk_buff *skb)
  60{
  61        struct bpf_tunnel_key tkey = {};
  62        void *data = (void *)(long)skb->data;
  63        struct eth_hdr *eth = data;
  64        void *data_end = (void *)(long)skb->data_end;
  65        int key = 0, *ifindex;
  66
  67        int ret;
  68
  69        if (data + sizeof(*eth) > data_end)
  70                return TC_ACT_OK;
  71
  72        ifindex = bpf_map_lookup_elem(&tun_iface, &key);
  73        if (!ifindex)
  74                return TC_ACT_OK;
  75
  76        if (eth->h_proto == htons(ETH_P_IP)) {
  77                char fmt4[] = "ingress forward to ifindex:%d daddr4:%x\n";
  78                struct iphdr *iph = data + sizeof(*eth);
  79
  80                if (data + sizeof(*eth) + sizeof(*iph) > data_end)
  81                        return TC_ACT_OK;
  82
  83                if (iph->protocol != IPPROTO_IPIP)
  84                        return TC_ACT_OK;
  85
  86                bpf_trace_printk(fmt4, sizeof(fmt4), *ifindex,
  87                                 _htonl(iph->daddr));
  88                return bpf_redirect(*ifindex, BPF_F_INGRESS);
  89        } else if (eth->h_proto == htons(ETH_P_IPV6)) {
  90                char fmt6[] = "ingress forward to ifindex:%d daddr6:%x::%x\n";
  91                struct ipv6hdr *ip6h = data + sizeof(*eth);
  92
  93                if (data + sizeof(*eth) + sizeof(*ip6h) > data_end)
  94                        return TC_ACT_OK;
  95
  96                if (ip6h->nexthdr != IPPROTO_IPIP &&
  97                    ip6h->nexthdr != IPPROTO_IPV6)
  98                        return TC_ACT_OK;
  99
 100                bpf_trace_printk(fmt6, sizeof(fmt6), *ifindex,
 101                                 _htonl(ip6h->daddr.s6_addr32[0]),
 102                                 _htonl(ip6h->daddr.s6_addr32[3]));
 103                return bpf_redirect(*ifindex, BPF_F_INGRESS);
 104        }
 105
 106        return TC_ACT_OK;
 107}
 108
 109SEC("l2_to_iptun_ingress_redirect")
 110int _l2_to_iptun_ingress_redirect(struct __sk_buff *skb)
 111{
 112        struct bpf_tunnel_key tkey = {};
 113        void *data = (void *)(long)skb->data;
 114        struct eth_hdr *eth = data;
 115        void *data_end = (void *)(long)skb->data_end;
 116        int key = 0, *ifindex;
 117
 118        int ret;
 119
 120        if (data + sizeof(*eth) > data_end)
 121                return TC_ACT_OK;
 122
 123        ifindex = bpf_map_lookup_elem(&tun_iface, &key);
 124        if (!ifindex)
 125                return TC_ACT_OK;
 126
 127        if (eth->h_proto == htons(ETH_P_IP)) {
 128                char fmt4[] = "e/ingress redirect daddr4:%x to ifindex:%d\n";
 129                struct iphdr *iph = data + sizeof(*eth);
 130                __be32 daddr = iph->daddr;
 131
 132                if (data + sizeof(*eth) + sizeof(*iph) > data_end)
 133                        return TC_ACT_OK;
 134
 135                if (!is_vip_addr(eth->h_proto, daddr))
 136                        return TC_ACT_OK;
 137
 138                bpf_trace_printk(fmt4, sizeof(fmt4), _htonl(daddr), *ifindex);
 139        } else {
 140                return TC_ACT_OK;
 141        }
 142
 143        tkey.tunnel_id = 10000;
 144        tkey.tunnel_ttl = 64;
 145        tkey.remote_ipv4 = 0x0a020166; /* 10.2.1.102 */
 146        bpf_skb_set_tunnel_key(skb, &tkey, sizeof(tkey), 0);
 147        return bpf_redirect(*ifindex, 0);
 148}
 149
 150SEC("l2_to_ip6tun_ingress_redirect")
 151int _l2_to_ip6tun_ingress_redirect(struct __sk_buff *skb)
 152{
 153        struct bpf_tunnel_key tkey = {};
 154        void *data = (void *)(long)skb->data;
 155        struct eth_hdr *eth = data;
 156        void *data_end = (void *)(long)skb->data_end;
 157        int key = 0, *ifindex;
 158
 159        if (data + sizeof(*eth) > data_end)
 160                return TC_ACT_OK;
 161
 162        ifindex = bpf_map_lookup_elem(&tun_iface, &key);
 163        if (!ifindex)
 164                return TC_ACT_OK;
 165
 166        if (eth->h_proto == htons(ETH_P_IP)) {
 167                char fmt4[] = "e/ingress redirect daddr4:%x to ifindex:%d\n";
 168                struct iphdr *iph = data + sizeof(*eth);
 169
 170                if (data + sizeof(*eth) + sizeof(*iph) > data_end)
 171                        return TC_ACT_OK;
 172
 173                if (!is_vip_addr(eth->h_proto, iph->daddr))
 174                        return TC_ACT_OK;
 175
 176                bpf_trace_printk(fmt4, sizeof(fmt4), _htonl(iph->daddr),
 177                                 *ifindex);
 178        } else if (eth->h_proto == htons(ETH_P_IPV6)) {
 179                char fmt6[] = "e/ingress redirect daddr6:%x to ifindex:%d\n";
 180                struct ipv6hdr *ip6h = data + sizeof(*eth);
 181
 182                if (data + sizeof(*eth) + sizeof(*ip6h) > data_end)
 183                        return TC_ACT_OK;
 184
 185                if (!is_vip_addr(eth->h_proto, ip6h->daddr.s6_addr32[0]))
 186                        return TC_ACT_OK;
 187
 188                bpf_trace_printk(fmt6, sizeof(fmt6),
 189                                 _htonl(ip6h->daddr.s6_addr32[0]), *ifindex);
 190        } else {
 191                return TC_ACT_OK;
 192        }
 193
 194        tkey.tunnel_id = 10000;
 195        tkey.tunnel_ttl = 64;
 196        /* 2401:db02:0:0:0:0:0:66 */
 197        tkey.remote_ipv6[0] = _htonl(0x2401db02);
 198        tkey.remote_ipv6[1] = 0;
 199        tkey.remote_ipv6[2] = 0;
 200        tkey.remote_ipv6[3] = _htonl(0x00000066);
 201        bpf_skb_set_tunnel_key(skb, &tkey, sizeof(tkey), BPF_F_TUNINFO_IPV6);
 202        return bpf_redirect(*ifindex, 0);
 203}
 204
 205SEC("drop_non_tun_vip")
 206int _drop_non_tun_vip(struct __sk_buff *skb)
 207{
 208        struct bpf_tunnel_key tkey = {};
 209        void *data = (void *)(long)skb->data;
 210        struct eth_hdr *eth = data;
 211        void *data_end = (void *)(long)skb->data_end;
 212
 213        if (data + sizeof(*eth) > data_end)
 214                return TC_ACT_OK;
 215
 216        if (eth->h_proto == htons(ETH_P_IP)) {
 217                struct iphdr *iph = data + sizeof(*eth);
 218
 219                if (data + sizeof(*eth) + sizeof(*iph) > data_end)
 220                        return TC_ACT_OK;
 221
 222                if (is_vip_addr(eth->h_proto, iph->daddr))
 223                        return TC_ACT_SHOT;
 224        } else if (eth->h_proto == htons(ETH_P_IPV6)) {
 225                struct ipv6hdr *ip6h = data + sizeof(*eth);
 226
 227                if (data + sizeof(*eth) + sizeof(*ip6h) > data_end)
 228                        return TC_ACT_OK;
 229
 230                if (is_vip_addr(eth->h_proto, ip6h->daddr.s6_addr32[0]))
 231                        return TC_ACT_SHOT;
 232        }
 233
 234        return TC_ACT_OK;
 235}
 236
 237char _license[] SEC("license") = "GPL";
 238