1209513Simp/* Copyright (c) 2016 Facebook 2209513Simp * 3209552Simp * This program is free software; you can redistribute it and/or 4209513Simp * modify it under the terms of version 2 of the GNU General Public 5209513Simp * License as published by the Free Software Foundation. 6209513Simp * 7209513Simp * This program shows how to use bpf_xdp_adjust_head() by 8209513Simp * encapsulating the incoming packet in an IPv4/v6 header 9209513Simp * and then XDP_TX it out. 10209513Simp */ 11209513Simp#define KBUILD_MODNAME "foo" 12209513Simp#include <uapi/linux/bpf.h> 13209513Simp#include <linux/in.h> 14209513Simp#include <linux/if_ether.h> 15209513Simp#include <linux/if_packet.h> 16209513Simp#include <linux/if_vlan.h> 17209513Simp#include <linux/ip.h> 18209513Simp#include <linux/ipv6.h> 19209513Simp#include <bpf/bpf_helpers.h> 20209513Simp#include "xdp_tx_iptunnel_common.h" 21209513Simp 22209513Simpstruct { 23209513Simp __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); 24209513Simp __type(key, __u32); 25209513Simp __type(value, __u64); 26209513Simp __uint(max_entries, 256); 27209513Simp} rxcnt SEC(".maps"); 28209513Simp 29209513Simpstruct { 30209513Simp __uint(type, BPF_MAP_TYPE_HASH); 31211730Simp __type(key, struct vip); 32211730Simp __type(value, struct iptnl_info); 33211730Simp __uint(max_entries, MAX_IPTNL_ENTRIES); 34211730Simp} vip2tnl SEC(".maps"); 35220059Sjpaetzel 36247735Sjpaetzelstatic __always_inline void count_tx(u32 protocol) 37211730Simp{ 38209513Simp u64 *rxcnt_count; 39211730Simp 40209513Simp rxcnt_count = bpf_map_lookup_elem(&rxcnt, &protocol); 41209513Simp if (rxcnt_count) 42209513Simp *rxcnt_count += 1; 43209513Simp} 44209513Simp 45209513Simpstatic __always_inline int get_dport(void *trans_data, void *data_end, 46209513Simp u8 protocol) 47209513Simp{ 48209513Simp struct tcphdr *th; 49209513Simp struct udphdr *uh; 50209513Simp 51220059Sjpaetzel switch (protocol) { 52220059Sjpaetzel case IPPROTO_TCP: 53209513Simp th = (struct tcphdr *)trans_data; 54209513Simp if (th + 1 > data_end) 55209513Simp return -1; 56209513Simp return th->dest; 57209513Simp case IPPROTO_UDP: 58209513Simp uh = (struct udphdr *)trans_data; 59209513Simp if (uh + 1 > data_end) 60209513Simp return -1; 61209513Simp return uh->dest; 62220059Sjpaetzel default: 63220059Sjpaetzel return 0; 64209513Simp } 65209513Simp} 66209513Simp 67209513Simpstatic __always_inline void set_ethhdr(struct ethhdr *new_eth, 68209513Simp const struct ethhdr *old_eth, 69209513Simp const struct iptnl_info *tnl, 70209513Simp __be16 h_proto) 71209513Simp{ 72209513Simp memcpy(new_eth->h_source, old_eth->h_dest, sizeof(new_eth->h_source)); 73220059Sjpaetzel memcpy(new_eth->h_dest, tnl->dmac, sizeof(new_eth->h_dest)); 74209513Simp new_eth->h_proto = h_proto; 75209513Simp} 76209513Simp 77209513Simpstatic __always_inline int handle_ipv4(struct xdp_md *xdp) 78209513Simp{ 79209513Simp void *data_end = (void *)(long)xdp->data_end; 80209513Simp void *data = (void *)(long)xdp->data; 81209513Simp struct iptnl_info *tnl; 82209513Simp struct ethhdr *new_eth; 83209513Simp struct ethhdr *old_eth; 84209513Simp struct iphdr *iph = data + sizeof(struct ethhdr); 85220059Sjpaetzel u16 *next_iph_u16; 86220059Sjpaetzel u16 payload_len; 87209513Simp struct vip vip = {}; 88209513Simp int dport; 89209513Simp u32 csum = 0; 90209513Simp int i; 91209513Simp 92209513Simp if (iph + 1 > data_end) 93209513Simp return XDP_DROP; 94209513Simp 95209513Simp dport = get_dport(iph + 1, data_end, iph->protocol); 96220059Sjpaetzel if (dport == -1) 97220059Sjpaetzel return XDP_DROP; 98209513Simp 99209513Simp vip.protocol = iph->protocol; 100209513Simp vip.family = AF_INET; 101209513Simp vip.daddr.v4 = iph->daddr; 102209513Simp vip.dport = dport; 103209513Simp payload_len = ntohs(iph->tot_len); 104209513Simp 105209513Simp tnl = bpf_map_lookup_elem(&vip2tnl, &vip); 106209513Simp /* It only does v4-in-v4 */ 107220059Sjpaetzel if (!tnl || tnl->family != AF_INET) 108209513Simp return XDP_PASS; 109209513Simp 110209513Simp /* The vip key is found. Add an IP header and send it out */ 111209513Simp 112209513Simp if (bpf_xdp_adjust_head(xdp, 0 - (int)sizeof(struct iphdr))) 113209513Simp return XDP_DROP; 114209513Simp 115209513Simp data = (void *)(long)xdp->data; 116209513Simp data_end = (void *)(long)xdp->data_end; 117209513Simp 118209513Simp new_eth = data; 119209513Simp iph = data + sizeof(*new_eth); 120209513Simp old_eth = data + sizeof(*iph); 121209513Simp 122209513Simp if (new_eth + 1 > data_end || 123209513Simp old_eth + 1 > data_end || 124209513Simp iph + 1 > data_end) 125209513Simp return XDP_DROP; 126209513Simp 127220059Sjpaetzel set_ethhdr(new_eth, old_eth, tnl, htons(ETH_P_IP)); 128209513Simp 129209513Simp iph->version = 4; 130209513Simp iph->ihl = sizeof(*iph) >> 2; 131209513Simp iph->frag_off = 0; 132209513Simp iph->protocol = IPPROTO_IPIP; 133209513Simp iph->check = 0; 134209513Simp iph->tos = 0; 135209513Simp iph->tot_len = htons(payload_len + sizeof(*iph)); 136209513Simp iph->daddr = tnl->daddr.v4; 137209513Simp iph->saddr = tnl->saddr.v4; 138209513Simp iph->ttl = 8; 139209513Simp 140209513Simp next_iph_u16 = (u16 *)iph; 141209513Simp#pragma clang loop unroll(full) 142209513Simp for (i = 0; i < sizeof(*iph) >> 1; i++) 143209513Simp csum += *next_iph_u16++; 144209513Simp 145209513Simp iph->check = ~((csum & 0xffff) + (csum >> 16)); 146209513Simp 147220059Sjpaetzel count_tx(vip.protocol); 148209513Simp 149209513Simp return XDP_TX; 150209513Simp} 151209513Simp 152209513Simpstatic __always_inline int handle_ipv6(struct xdp_md *xdp) 153209513Simp{ 154209513Simp void *data_end = (void *)(long)xdp->data_end; 155209513Simp void *data = (void *)(long)xdp->data; 156209513Simp struct iptnl_info *tnl; 157209513Simp struct ethhdr *new_eth; 158209513Simp struct ethhdr *old_eth; 159209513Simp struct ipv6hdr *ip6h = data + sizeof(struct ethhdr); 160209513Simp __u16 payload_len; 161209513Simp struct vip vip = {}; 162209513Simp int dport; 163209513Simp 164209513Simp if (ip6h + 1 > data_end) 165209513Simp return XDP_DROP; 166209513Simp 167220059Sjpaetzel dport = get_dport(ip6h + 1, data_end, ip6h->nexthdr); 168209513Simp if (dport == -1) 169209513Simp return XDP_DROP; 170209513Simp 171209513Simp vip.protocol = ip6h->nexthdr; 172209513Simp vip.family = AF_INET6; 173209513Simp memcpy(vip.daddr.v6, ip6h->daddr.s6_addr32, sizeof(vip.daddr)); 174220059Sjpaetzel vip.dport = dport; 175220059Sjpaetzel payload_len = ip6h->payload_len; 176209513Simp 177209513Simp tnl = bpf_map_lookup_elem(&vip2tnl, &vip); 178209513Simp /* It only does v6-in-v6 */ 179217164Sjpaetzel if (!tnl || tnl->family != AF_INET6) 180209513Simp return XDP_PASS; 181209513Simp 182209513Simp /* The vip key is found. Add an IP header and send it out */ 183209513Simp 184209513Simp if (bpf_xdp_adjust_head(xdp, 0 - (int)sizeof(struct ipv6hdr))) 185211730Simp return XDP_DROP; 186211730Simp 187211730Simp data = (void *)(long)xdp->data; 188209513Simp data_end = (void *)(long)xdp->data_end; 189209513Simp 190209513Simp new_eth = data; 191209513Simp ip6h = data + sizeof(*new_eth); 192209513Simp old_eth = data + sizeof(*ip6h); 193209513Simp 194209513Simp if (new_eth + 1 > data_end || 195209513Simp old_eth + 1 > data_end || 196209513Simp ip6h + 1 > data_end) 197220059Sjpaetzel return XDP_DROP; 198209513Simp 199209513Simp set_ethhdr(new_eth, old_eth, tnl, htons(ETH_P_IPV6)); 200209513Simp 201209513Simp ip6h->version = 6; 202209513Simp ip6h->priority = 0; 203209513Simp memset(ip6h->flow_lbl, 0, sizeof(ip6h->flow_lbl)); 204220059Sjpaetzel ip6h->payload_len = htons(ntohs(payload_len) + sizeof(*ip6h)); 205209513Simp ip6h->nexthdr = IPPROTO_IPV6; 206209513Simp ip6h->hop_limit = 8; 207209513Simp memcpy(ip6h->saddr.s6_addr32, tnl->saddr.v6, sizeof(tnl->saddr.v6)); 208209513Simp memcpy(ip6h->daddr.s6_addr32, tnl->daddr.v6, sizeof(tnl->daddr.v6)); 209209513Simp 210209513Simp count_tx(vip.protocol); 211220059Sjpaetzel 212209513Simp return XDP_TX; 213209513Simp} 214209513Simp 215209513SimpSEC("xdp.frags") 216209513Simpint _xdp_tx_iptunnel(struct xdp_md *xdp) 217209513Simp{ 218220059Sjpaetzel void *data_end = (void *)(long)xdp->data_end; 219209513Simp void *data = (void *)(long)xdp->data; 220209513Simp struct ethhdr *eth = data; 221211730Simp __u16 h_proto; 222211730Simp 223211730Simp if (eth + 1 > data_end) 224211730Simp return XDP_DROP; 225220059Sjpaetzel 226211730Simp h_proto = eth->h_proto; 227211730Simp 228240165Sjpaetzel if (h_proto == htons(ETH_P_IP)) 229240165Sjpaetzel return handle_ipv4(xdp); 230240165Sjpaetzel else if (h_proto == htons(ETH_P_IPV6)) 231240165Sjpaetzel 232240165Sjpaetzel return handle_ipv6(xdp); 233240165Sjpaetzel else 234240165Sjpaetzel return XDP_PASS; 235240165Sjpaetzel} 236240165Sjpaetzel 237209513Simpchar _license[] SEC("license") = "GPL"; 238209513Simp