Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 #include <stdint.h>
0003 #include <stdbool.h>
0004 #include <stddef.h>
0005 
0006 #include <linux/bpf.h>
0007 #include <linux/stddef.h>
0008 #include <linux/pkt_cls.h>
0009 #include <linux/if_ether.h>
0010 #include <linux/in.h>
0011 #include <linux/ip.h>
0012 #include <linux/ipv6.h>
0013 
0014 #include <bpf/bpf_helpers.h>
0015 #include <bpf/bpf_endian.h>
0016 
0017 #ifndef ctx_ptr
0018 # define ctx_ptr(field)     (void *)(long)(field)
0019 #endif
0020 
0021 #define AF_INET 2
0022 #define AF_INET6 10
0023 
0024 static __always_inline int fill_fib_params_v4(struct __sk_buff *skb,
0025                           struct bpf_fib_lookup *fib_params)
0026 {
0027     void *data_end = ctx_ptr(skb->data_end);
0028     void *data = ctx_ptr(skb->data);
0029     struct iphdr *ip4h;
0030 
0031     if (data + sizeof(struct ethhdr) > data_end)
0032         return -1;
0033 
0034     ip4h = (struct iphdr *)(data + sizeof(struct ethhdr));
0035     if ((void *)(ip4h + 1) > data_end)
0036         return -1;
0037 
0038     fib_params->family = AF_INET;
0039     fib_params->tos = ip4h->tos;
0040     fib_params->l4_protocol = ip4h->protocol;
0041     fib_params->sport = 0;
0042     fib_params->dport = 0;
0043     fib_params->tot_len = bpf_ntohs(ip4h->tot_len);
0044     fib_params->ipv4_src = ip4h->saddr;
0045     fib_params->ipv4_dst = ip4h->daddr;
0046 
0047     return 0;
0048 }
0049 
0050 static __always_inline int fill_fib_params_v6(struct __sk_buff *skb,
0051                           struct bpf_fib_lookup *fib_params)
0052 {
0053     struct in6_addr *src = (struct in6_addr *)fib_params->ipv6_src;
0054     struct in6_addr *dst = (struct in6_addr *)fib_params->ipv6_dst;
0055     void *data_end = ctx_ptr(skb->data_end);
0056     void *data = ctx_ptr(skb->data);
0057     struct ipv6hdr *ip6h;
0058 
0059     if (data + sizeof(struct ethhdr) > data_end)
0060         return -1;
0061 
0062     ip6h = (struct ipv6hdr *)(data + sizeof(struct ethhdr));
0063     if ((void *)(ip6h + 1) > data_end)
0064         return -1;
0065 
0066     fib_params->family = AF_INET6;
0067     fib_params->flowinfo = 0;
0068     fib_params->l4_protocol = ip6h->nexthdr;
0069     fib_params->sport = 0;
0070     fib_params->dport = 0;
0071     fib_params->tot_len = bpf_ntohs(ip6h->payload_len);
0072     *src = ip6h->saddr;
0073     *dst = ip6h->daddr;
0074 
0075     return 0;
0076 }
0077 
0078 SEC("tc")
0079 int tc_chk(struct __sk_buff *skb)
0080 {
0081     void *data_end = ctx_ptr(skb->data_end);
0082     void *data = ctx_ptr(skb->data);
0083     __u32 *raw = data;
0084 
0085     if (data + sizeof(struct ethhdr) > data_end)
0086         return TC_ACT_SHOT;
0087 
0088     return !raw[0] && !raw[1] && !raw[2] ? TC_ACT_SHOT : TC_ACT_OK;
0089 }
0090 
0091 static __always_inline int tc_redir(struct __sk_buff *skb)
0092 {
0093     struct bpf_fib_lookup fib_params = { .ifindex = skb->ingress_ifindex };
0094     __u8 zero[ETH_ALEN * 2];
0095     int ret = -1;
0096 
0097     switch (skb->protocol) {
0098     case __bpf_constant_htons(ETH_P_IP):
0099         ret = fill_fib_params_v4(skb, &fib_params);
0100         break;
0101     case __bpf_constant_htons(ETH_P_IPV6):
0102         ret = fill_fib_params_v6(skb, &fib_params);
0103         break;
0104     }
0105 
0106     if (ret)
0107         return TC_ACT_OK;
0108 
0109     ret = bpf_fib_lookup(skb, &fib_params, sizeof(fib_params), 0);
0110     if (ret == BPF_FIB_LKUP_RET_NOT_FWDED || ret < 0)
0111         return TC_ACT_OK;
0112 
0113     __builtin_memset(&zero, 0, sizeof(zero));
0114     if (bpf_skb_store_bytes(skb, 0, &zero, sizeof(zero), 0) < 0)
0115         return TC_ACT_SHOT;
0116 
0117     if (ret == BPF_FIB_LKUP_RET_NO_NEIGH) {
0118         struct bpf_redir_neigh nh_params = {};
0119 
0120         nh_params.nh_family = fib_params.family;
0121         __builtin_memcpy(&nh_params.ipv6_nh, &fib_params.ipv6_dst,
0122                  sizeof(nh_params.ipv6_nh));
0123 
0124         return bpf_redirect_neigh(fib_params.ifindex, &nh_params,
0125                       sizeof(nh_params), 0);
0126 
0127     } else if (ret == BPF_FIB_LKUP_RET_SUCCESS) {
0128         void *data_end = ctx_ptr(skb->data_end);
0129         struct ethhdr *eth = ctx_ptr(skb->data);
0130 
0131         if (eth + 1 > data_end)
0132             return TC_ACT_SHOT;
0133 
0134         __builtin_memcpy(eth->h_dest, fib_params.dmac, ETH_ALEN);
0135         __builtin_memcpy(eth->h_source, fib_params.smac, ETH_ALEN);
0136 
0137         return bpf_redirect(fib_params.ifindex, 0);
0138     }
0139 
0140     return TC_ACT_SHOT;
0141 }
0142 
0143 /* these are identical, but keep them separate for compatibility with the
0144  * section names expected by test_tc_redirect.sh
0145  */
0146 SEC("tc")
0147 int tc_dst(struct __sk_buff *skb)
0148 {
0149     return tc_redir(skb);
0150 }
0151 
0152 SEC("tc")
0153 int tc_src(struct __sk_buff *skb)
0154 {
0155     return tc_redir(skb);
0156 }
0157 
0158 char __license[] SEC("license") = "GPL";