linux/tools/testing/selftests/bpf/progs/test_tc_neigh_fib.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2#include <stdint.h>
   3#include <stdbool.h>
   4#include <stddef.h>
   5
   6#include <linux/bpf.h>
   7#include <linux/stddef.h>
   8#include <linux/pkt_cls.h>
   9#include <linux/if_ether.h>
  10#include <linux/in.h>
  11#include <linux/ip.h>
  12#include <linux/ipv6.h>
  13
  14#include <bpf/bpf_helpers.h>
  15#include <bpf/bpf_endian.h>
  16
  17#ifndef ctx_ptr
  18# define ctx_ptr(field)         (void *)(long)(field)
  19#endif
  20
  21#define AF_INET 2
  22#define AF_INET6 10
  23
  24static __always_inline int fill_fib_params_v4(struct __sk_buff *skb,
  25                                              struct bpf_fib_lookup *fib_params)
  26{
  27        void *data_end = ctx_ptr(skb->data_end);
  28        void *data = ctx_ptr(skb->data);
  29        struct iphdr *ip4h;
  30
  31        if (data + sizeof(struct ethhdr) > data_end)
  32                return -1;
  33
  34        ip4h = (struct iphdr *)(data + sizeof(struct ethhdr));
  35        if ((void *)(ip4h + 1) > data_end)
  36                return -1;
  37
  38        fib_params->family = AF_INET;
  39        fib_params->tos = ip4h->tos;
  40        fib_params->l4_protocol = ip4h->protocol;
  41        fib_params->sport = 0;
  42        fib_params->dport = 0;
  43        fib_params->tot_len = bpf_ntohs(ip4h->tot_len);
  44        fib_params->ipv4_src = ip4h->saddr;
  45        fib_params->ipv4_dst = ip4h->daddr;
  46
  47        return 0;
  48}
  49
  50static __always_inline int fill_fib_params_v6(struct __sk_buff *skb,
  51                                              struct bpf_fib_lookup *fib_params)
  52{
  53        struct in6_addr *src = (struct in6_addr *)fib_params->ipv6_src;
  54        struct in6_addr *dst = (struct in6_addr *)fib_params->ipv6_dst;
  55        void *data_end = ctx_ptr(skb->data_end);
  56        void *data = ctx_ptr(skb->data);
  57        struct ipv6hdr *ip6h;
  58
  59        if (data + sizeof(struct ethhdr) > data_end)
  60                return -1;
  61
  62        ip6h = (struct ipv6hdr *)(data + sizeof(struct ethhdr));
  63        if ((void *)(ip6h + 1) > data_end)
  64                return -1;
  65
  66        fib_params->family = AF_INET6;
  67        fib_params->flowinfo = 0;
  68        fib_params->l4_protocol = ip6h->nexthdr;
  69        fib_params->sport = 0;
  70        fib_params->dport = 0;
  71        fib_params->tot_len = bpf_ntohs(ip6h->payload_len);
  72        *src = ip6h->saddr;
  73        *dst = ip6h->daddr;
  74
  75        return 0;
  76}
  77
  78SEC("chk_egress") int tc_chk(struct __sk_buff *skb)
  79{
  80        void *data_end = ctx_ptr(skb->data_end);
  81        void *data = ctx_ptr(skb->data);
  82        __u32 *raw = data;
  83
  84        if (data + sizeof(struct ethhdr) > data_end)
  85                return TC_ACT_SHOT;
  86
  87        return !raw[0] && !raw[1] && !raw[2] ? TC_ACT_SHOT : TC_ACT_OK;
  88}
  89
  90static __always_inline int tc_redir(struct __sk_buff *skb)
  91{
  92        struct bpf_fib_lookup fib_params = { .ifindex = skb->ingress_ifindex };
  93        __u8 zero[ETH_ALEN * 2];
  94        int ret = -1;
  95
  96        switch (skb->protocol) {
  97        case __bpf_constant_htons(ETH_P_IP):
  98                ret = fill_fib_params_v4(skb, &fib_params);
  99                break;
 100        case __bpf_constant_htons(ETH_P_IPV6):
 101                ret = fill_fib_params_v6(skb, &fib_params);
 102                break;
 103        }
 104
 105        if (ret)
 106                return TC_ACT_OK;
 107
 108        ret = bpf_fib_lookup(skb, &fib_params, sizeof(fib_params), 0);
 109        if (ret == BPF_FIB_LKUP_RET_NOT_FWDED || ret < 0)
 110                return TC_ACT_OK;
 111
 112        __builtin_memset(&zero, 0, sizeof(zero));
 113        if (bpf_skb_store_bytes(skb, 0, &zero, sizeof(zero), 0) < 0)
 114                return TC_ACT_SHOT;
 115
 116        if (ret == BPF_FIB_LKUP_RET_NO_NEIGH) {
 117                struct bpf_redir_neigh nh_params = {};
 118
 119                nh_params.nh_family = fib_params.family;
 120                __builtin_memcpy(&nh_params.ipv6_nh, &fib_params.ipv6_dst,
 121                                 sizeof(nh_params.ipv6_nh));
 122
 123                return bpf_redirect_neigh(fib_params.ifindex, &nh_params,
 124                                          sizeof(nh_params), 0);
 125
 126        } else if (ret == BPF_FIB_LKUP_RET_SUCCESS) {
 127                void *data_end = ctx_ptr(skb->data_end);
 128                struct ethhdr *eth = ctx_ptr(skb->data);
 129
 130                if (eth + 1 > data_end)
 131                        return TC_ACT_SHOT;
 132
 133                __builtin_memcpy(eth->h_dest, fib_params.dmac, ETH_ALEN);
 134                __builtin_memcpy(eth->h_source, fib_params.smac, ETH_ALEN);
 135
 136                return bpf_redirect(fib_params.ifindex, 0);
 137        }
 138
 139        return TC_ACT_SHOT;
 140}
 141
 142/* these are identical, but keep them separate for compatibility with the
 143 * section names expected by test_tc_redirect.sh
 144 */
 145SEC("dst_ingress") int tc_dst(struct __sk_buff *skb)
 146{
 147        return tc_redir(skb);
 148}
 149
 150SEC("src_ingress") int tc_src(struct __sk_buff *skb)
 151{
 152        return tc_redir(skb);
 153}
 154
 155char __license[] SEC("license") = "GPL";
 156