0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013 #define KBUILD_MODNAME "foo"
0014 #include <uapi/linux/bpf.h>
0015 #include <linux/in.h>
0016 #include <linux/if_ether.h>
0017 #include <linux/if_packet.h>
0018 #include <linux/if_vlan.h>
0019 #include <linux/ip.h>
0020 #include <linux/ipv6.h>
0021
0022 #include <bpf/bpf_helpers.h>
0023
0024 #define IPV6_FLOWINFO_MASK cpu_to_be32(0x0FFFFFFF)
0025
0026 struct {
0027 __uint(type, BPF_MAP_TYPE_DEVMAP);
0028 __uint(key_size, sizeof(int));
0029 __uint(value_size, sizeof(int));
0030 __uint(max_entries, 64);
0031 } xdp_tx_ports SEC(".maps");
0032
0033
0034 static __always_inline int ip_decrease_ttl(struct iphdr *iph)
0035 {
0036 u32 check = (__force u32)iph->check;
0037
0038 check += (__force u32)htons(0x0100);
0039 iph->check = (__force __sum16)(check + (check >= 0xFFFF));
0040 return --iph->ttl;
0041 }
0042
0043 static __always_inline int xdp_fwd_flags(struct xdp_md *ctx, u32 flags)
0044 {
0045 void *data_end = (void *)(long)ctx->data_end;
0046 void *data = (void *)(long)ctx->data;
0047 struct bpf_fib_lookup fib_params;
0048 struct ethhdr *eth = data;
0049 struct ipv6hdr *ip6h;
0050 struct iphdr *iph;
0051 u16 h_proto;
0052 u64 nh_off;
0053 int rc;
0054
0055 nh_off = sizeof(*eth);
0056 if (data + nh_off > data_end)
0057 return XDP_DROP;
0058
0059 __builtin_memset(&fib_params, 0, sizeof(fib_params));
0060
0061 h_proto = eth->h_proto;
0062 if (h_proto == htons(ETH_P_IP)) {
0063 iph = data + nh_off;
0064
0065 if (iph + 1 > data_end)
0066 return XDP_DROP;
0067
0068 if (iph->ttl <= 1)
0069 return XDP_PASS;
0070
0071 fib_params.family = AF_INET;
0072 fib_params.tos = iph->tos;
0073 fib_params.l4_protocol = iph->protocol;
0074 fib_params.sport = 0;
0075 fib_params.dport = 0;
0076 fib_params.tot_len = ntohs(iph->tot_len);
0077 fib_params.ipv4_src = iph->saddr;
0078 fib_params.ipv4_dst = iph->daddr;
0079 } else if (h_proto == htons(ETH_P_IPV6)) {
0080 struct in6_addr *src = (struct in6_addr *) fib_params.ipv6_src;
0081 struct in6_addr *dst = (struct in6_addr *) fib_params.ipv6_dst;
0082
0083 ip6h = data + nh_off;
0084 if (ip6h + 1 > data_end)
0085 return XDP_DROP;
0086
0087 if (ip6h->hop_limit <= 1)
0088 return XDP_PASS;
0089
0090 fib_params.family = AF_INET6;
0091 fib_params.flowinfo = *(__be32 *)ip6h & IPV6_FLOWINFO_MASK;
0092 fib_params.l4_protocol = ip6h->nexthdr;
0093 fib_params.sport = 0;
0094 fib_params.dport = 0;
0095 fib_params.tot_len = ntohs(ip6h->payload_len);
0096 *src = ip6h->saddr;
0097 *dst = ip6h->daddr;
0098 } else {
0099 return XDP_PASS;
0100 }
0101
0102 fib_params.ifindex = ctx->ingress_ifindex;
0103
0104 rc = bpf_fib_lookup(ctx, &fib_params, sizeof(fib_params), flags);
0105
0106
0107
0108
0109
0110
0111
0112
0113
0114
0115
0116
0117
0118
0119
0120
0121 if (rc == BPF_FIB_LKUP_RET_SUCCESS) {
0122
0123
0124
0125
0126
0127
0128
0129
0130 if (!bpf_map_lookup_elem(&xdp_tx_ports, &fib_params.ifindex))
0131 return XDP_PASS;
0132
0133 if (h_proto == htons(ETH_P_IP))
0134 ip_decrease_ttl(iph);
0135 else if (h_proto == htons(ETH_P_IPV6))
0136 ip6h->hop_limit--;
0137
0138 memcpy(eth->h_dest, fib_params.dmac, ETH_ALEN);
0139 memcpy(eth->h_source, fib_params.smac, ETH_ALEN);
0140 return bpf_redirect_map(&xdp_tx_ports, fib_params.ifindex, 0);
0141 }
0142
0143 return XDP_PASS;
0144 }
0145
0146 SEC("xdp_fwd")
0147 int xdp_fwd_prog(struct xdp_md *ctx)
0148 {
0149 return xdp_fwd_flags(ctx, 0);
0150 }
0151
0152 SEC("xdp_fwd_direct")
0153 int xdp_fwd_direct_prog(struct xdp_md *ctx)
0154 {
0155 return xdp_fwd_flags(ctx, BPF_FIB_LOOKUP_DIRECT);
0156 }
0157
0158 char _license[] SEC("license") = "GPL";