root/samples/bpf/xdp_tx_iptunnel_kern.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. count_tx
  2. get_dport
  3. set_ethhdr
  4. handle_ipv4
  5. handle_ipv6
  6. SEC

   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  * This program shows how to use bpf_xdp_adjust_head() by
   8  * encapsulating the incoming packet in an IPv4/v6 header
   9  * and then XDP_TX it out.
  10  */
  11 #define KBUILD_MODNAME "foo"
  12 #include <uapi/linux/bpf.h>
  13 #include <linux/in.h>
  14 #include <linux/if_ether.h>
  15 #include <linux/if_packet.h>
  16 #include <linux/if_vlan.h>
  17 #include <linux/ip.h>
  18 #include <linux/ipv6.h>
  19 #include "bpf_helpers.h"
  20 #include "xdp_tx_iptunnel_common.h"
  21 
  22 struct {
  23         __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
  24         __type(key, __u32);
  25         __type(value, __u64);
  26         __uint(max_entries, 256);
  27 } rxcnt SEC(".maps");
  28 
  29 struct {
  30         __uint(type, BPF_MAP_TYPE_HASH);
  31         __type(key, struct vip);
  32         __type(value, struct iptnl_info);
  33         __uint(max_entries, MAX_IPTNL_ENTRIES);
  34 } vip2tnl SEC(".maps");
  35 
  36 static __always_inline void count_tx(u32 protocol)
  37 {
  38         u64 *rxcnt_count;
  39 
  40         rxcnt_count = bpf_map_lookup_elem(&rxcnt, &protocol);
  41         if (rxcnt_count)
  42                 *rxcnt_count += 1;
  43 }
  44 
  45 static __always_inline int get_dport(void *trans_data, void *data_end,
  46                                      u8 protocol)
  47 {
  48         struct tcphdr *th;
  49         struct udphdr *uh;
  50 
  51         switch (protocol) {
  52         case IPPROTO_TCP:
  53                 th = (struct tcphdr *)trans_data;
  54                 if (th + 1 > data_end)
  55                         return -1;
  56                 return th->dest;
  57         case IPPROTO_UDP:
  58                 uh = (struct udphdr *)trans_data;
  59                 if (uh + 1 > data_end)
  60                         return -1;
  61                 return uh->dest;
  62         default:
  63                 return 0;
  64         }
  65 }
  66 
  67 static __always_inline void set_ethhdr(struct ethhdr *new_eth,
  68                                        const struct ethhdr *old_eth,
  69                                        const struct iptnl_info *tnl,
  70                                        __be16 h_proto)
  71 {
  72         memcpy(new_eth->h_source, old_eth->h_dest, sizeof(new_eth->h_source));
  73         memcpy(new_eth->h_dest, tnl->dmac, sizeof(new_eth->h_dest));
  74         new_eth->h_proto = h_proto;
  75 }
  76 
  77 static __always_inline int handle_ipv4(struct xdp_md *xdp)
  78 {
  79         void *data_end = (void *)(long)xdp->data_end;
  80         void *data = (void *)(long)xdp->data;
  81         struct iptnl_info *tnl;
  82         struct ethhdr *new_eth;
  83         struct ethhdr *old_eth;
  84         struct iphdr *iph = data + sizeof(struct ethhdr);
  85         u16 *next_iph_u16;
  86         u16 payload_len;
  87         struct vip vip = {};
  88         int dport;
  89         u32 csum = 0;
  90         int i;
  91 
  92         if (iph + 1 > data_end)
  93                 return XDP_DROP;
  94 
  95         dport = get_dport(iph + 1, data_end, iph->protocol);
  96         if (dport == -1)
  97                 return XDP_DROP;
  98 
  99         vip.protocol = iph->protocol;
 100         vip.family = AF_INET;
 101         vip.daddr.v4 = iph->daddr;
 102         vip.dport = dport;
 103         payload_len = ntohs(iph->tot_len);
 104 
 105         tnl = bpf_map_lookup_elem(&vip2tnl, &vip);
 106         /* It only does v4-in-v4 */
 107         if (!tnl || tnl->family != AF_INET)
 108                 return XDP_PASS;
 109 
 110         /* The vip key is found.  Add an IP header and send it out */
 111 
 112         if (bpf_xdp_adjust_head(xdp, 0 - (int)sizeof(struct iphdr)))
 113                 return XDP_DROP;
 114 
 115         data = (void *)(long)xdp->data;
 116         data_end = (void *)(long)xdp->data_end;
 117 
 118         new_eth = data;
 119         iph = data + sizeof(*new_eth);
 120         old_eth = data + sizeof(*iph);
 121 
 122         if (new_eth + 1 > data_end ||
 123             old_eth + 1 > data_end ||
 124             iph + 1 > data_end)
 125                 return XDP_DROP;
 126 
 127         set_ethhdr(new_eth, old_eth, tnl, htons(ETH_P_IP));
 128 
 129         iph->version = 4;
 130         iph->ihl = sizeof(*iph) >> 2;
 131         iph->frag_off = 0;
 132         iph->protocol = IPPROTO_IPIP;
 133         iph->check = 0;
 134         iph->tos = 0;
 135         iph->tot_len = htons(payload_len + sizeof(*iph));
 136         iph->daddr = tnl->daddr.v4;
 137         iph->saddr = tnl->saddr.v4;
 138         iph->ttl = 8;
 139 
 140         next_iph_u16 = (u16 *)iph;
 141 #pragma clang loop unroll(full)
 142         for (i = 0; i < sizeof(*iph) >> 1; i++)
 143                 csum += *next_iph_u16++;
 144 
 145         iph->check = ~((csum & 0xffff) + (csum >> 16));
 146 
 147         count_tx(vip.protocol);
 148 
 149         return XDP_TX;
 150 }
 151 
 152 static __always_inline int handle_ipv6(struct xdp_md *xdp)
 153 {
 154         void *data_end = (void *)(long)xdp->data_end;
 155         void *data = (void *)(long)xdp->data;
 156         struct iptnl_info *tnl;
 157         struct ethhdr *new_eth;
 158         struct ethhdr *old_eth;
 159         struct ipv6hdr *ip6h = data + sizeof(struct ethhdr);
 160         __u16 payload_len;
 161         struct vip vip = {};
 162         int dport;
 163 
 164         if (ip6h + 1 > data_end)
 165                 return XDP_DROP;
 166 
 167         dport = get_dport(ip6h + 1, data_end, ip6h->nexthdr);
 168         if (dport == -1)
 169                 return XDP_DROP;
 170 
 171         vip.protocol = ip6h->nexthdr;
 172         vip.family = AF_INET6;
 173         memcpy(vip.daddr.v6, ip6h->daddr.s6_addr32, sizeof(vip.daddr));
 174         vip.dport = dport;
 175         payload_len = ip6h->payload_len;
 176 
 177         tnl = bpf_map_lookup_elem(&vip2tnl, &vip);
 178         /* It only does v6-in-v6 */
 179         if (!tnl || tnl->family != AF_INET6)
 180                 return XDP_PASS;
 181 
 182         /* The vip key is found.  Add an IP header and send it out */
 183 
 184         if (bpf_xdp_adjust_head(xdp, 0 - (int)sizeof(struct ipv6hdr)))
 185                 return XDP_DROP;
 186 
 187         data = (void *)(long)xdp->data;
 188         data_end = (void *)(long)xdp->data_end;
 189 
 190         new_eth = data;
 191         ip6h = data + sizeof(*new_eth);
 192         old_eth = data + sizeof(*ip6h);
 193 
 194         if (new_eth + 1 > data_end ||
 195             old_eth + 1 > data_end ||
 196             ip6h + 1 > data_end)
 197                 return XDP_DROP;
 198 
 199         set_ethhdr(new_eth, old_eth, tnl, htons(ETH_P_IPV6));
 200 
 201         ip6h->version = 6;
 202         ip6h->priority = 0;
 203         memset(ip6h->flow_lbl, 0, sizeof(ip6h->flow_lbl));
 204         ip6h->payload_len = htons(ntohs(payload_len) + sizeof(*ip6h));
 205         ip6h->nexthdr = IPPROTO_IPV6;
 206         ip6h->hop_limit = 8;
 207         memcpy(ip6h->saddr.s6_addr32, tnl->saddr.v6, sizeof(tnl->saddr.v6));
 208         memcpy(ip6h->daddr.s6_addr32, tnl->daddr.v6, sizeof(tnl->daddr.v6));
 209 
 210         count_tx(vip.protocol);
 211 
 212         return XDP_TX;
 213 }
 214 
 215 SEC("xdp_tx_iptunnel")
 216 int _xdp_tx_iptunnel(struct xdp_md *xdp)
 217 {
 218         void *data_end = (void *)(long)xdp->data_end;
 219         void *data = (void *)(long)xdp->data;
 220         struct ethhdr *eth = data;
 221         __u16 h_proto;
 222 
 223         if (eth + 1 > data_end)
 224                 return XDP_DROP;
 225 
 226         h_proto = eth->h_proto;
 227 
 228         if (h_proto == htons(ETH_P_IP))
 229                 return handle_ipv4(xdp);
 230         else if (h_proto == htons(ETH_P_IPV6))
 231 
 232                 return handle_ipv6(xdp);
 233         else
 234                 return XDP_PASS;
 235 }
 236 
 237 char _license[] SEC("license") = "GPL";

/* [<][>][^][v][top][bottom][index][help] */