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