0001
0002
0003
0004
0005
0006
0007
0008 #include "vmlinux.h"
0009 #include "xdp_sample.bpf.h"
0010 #include "xdp_sample_shared.h"
0011
0012 #define ETH_ALEN 6
0013 #define ETH_P_8021Q 0x8100
0014 #define ETH_P_8021AD 0x88A8
0015
0016 struct trie_value {
0017 __u8 prefix[4];
0018 __be64 value;
0019 int ifindex;
0020 int metric;
0021 __be32 gw;
0022 };
0023
0024
0025 union key_4 {
0026 u32 b32[2];
0027 u8 b8[8];
0028 };
0029
0030 struct arp_entry {
0031 __be64 mac;
0032 __be32 dst;
0033 };
0034
0035 struct direct_map {
0036 struct arp_entry arp;
0037 int ifindex;
0038 __be64 mac;
0039 };
0040
0041
0042 struct {
0043 __uint(type, BPF_MAP_TYPE_LPM_TRIE);
0044 __uint(key_size, 8);
0045 __uint(value_size, sizeof(struct trie_value));
0046 __uint(max_entries, 50);
0047 __uint(map_flags, BPF_F_NO_PREALLOC);
0048 } lpm_map SEC(".maps");
0049
0050
0051 struct {
0052 __uint(type, BPF_MAP_TYPE_HASH);
0053 __type(key, __be32);
0054 __type(value, __be64);
0055 __uint(max_entries, 50);
0056 } arp_table SEC(".maps");
0057
0058
0059 struct {
0060 __uint(type, BPF_MAP_TYPE_HASH);
0061 __type(key, __be32);
0062 __type(value, struct direct_map);
0063 __uint(max_entries, 50);
0064 } exact_match SEC(".maps");
0065
0066 struct {
0067 __uint(type, BPF_MAP_TYPE_DEVMAP);
0068 __uint(key_size, sizeof(int));
0069 __uint(value_size, sizeof(int));
0070 __uint(max_entries, 100);
0071 } tx_port SEC(".maps");
0072
0073 SEC("xdp")
0074 int xdp_router_ipv4_prog(struct xdp_md *ctx)
0075 {
0076 void *data_end = (void *)(long)ctx->data_end;
0077 void *data = (void *)(long)ctx->data;
0078 struct ethhdr *eth = data;
0079 u64 nh_off = sizeof(*eth);
0080 struct datarec *rec;
0081 __be16 h_proto;
0082 u32 key = 0;
0083
0084 rec = bpf_map_lookup_elem(&rx_cnt, &key);
0085 if (rec)
0086 NO_TEAR_INC(rec->processed);
0087
0088 if (data + nh_off > data_end)
0089 goto drop;
0090
0091 h_proto = eth->h_proto;
0092 if (h_proto == bpf_htons(ETH_P_8021Q) ||
0093 h_proto == bpf_htons(ETH_P_8021AD)) {
0094 struct vlan_hdr *vhdr;
0095
0096 vhdr = data + nh_off;
0097 nh_off += sizeof(struct vlan_hdr);
0098 if (data + nh_off > data_end)
0099 goto drop;
0100
0101 h_proto = vhdr->h_vlan_encapsulated_proto;
0102 }
0103
0104 switch (bpf_ntohs(h_proto)) {
0105 case ETH_P_ARP:
0106 if (rec)
0107 NO_TEAR_INC(rec->xdp_pass);
0108 return XDP_PASS;
0109 case ETH_P_IP: {
0110 struct iphdr *iph = data + nh_off;
0111 struct direct_map *direct_entry;
0112 __be64 *dest_mac, *src_mac;
0113 int forward_to;
0114
0115 if (iph + 1 > data_end)
0116 goto drop;
0117
0118 direct_entry = bpf_map_lookup_elem(&exact_match, &iph->daddr);
0119
0120
0121 if (direct_entry && direct_entry->mac &&
0122 direct_entry->arp.mac) {
0123 src_mac = &direct_entry->mac;
0124 dest_mac = &direct_entry->arp.mac;
0125 forward_to = direct_entry->ifindex;
0126 } else {
0127 struct trie_value *prefix_value;
0128 union key_4 key4;
0129
0130
0131 key4.b32[0] = 32;
0132 key4.b8[4] = iph->daddr & 0xff;
0133 key4.b8[5] = (iph->daddr >> 8) & 0xff;
0134 key4.b8[6] = (iph->daddr >> 16) & 0xff;
0135 key4.b8[7] = (iph->daddr >> 24) & 0xff;
0136
0137 prefix_value = bpf_map_lookup_elem(&lpm_map, &key4);
0138 if (!prefix_value)
0139 goto drop;
0140
0141 forward_to = prefix_value->ifindex;
0142 src_mac = &prefix_value->value;
0143 if (!src_mac)
0144 goto drop;
0145
0146 dest_mac = bpf_map_lookup_elem(&arp_table, &iph->daddr);
0147 if (!dest_mac) {
0148 if (!prefix_value->gw)
0149 goto drop;
0150
0151 dest_mac = bpf_map_lookup_elem(&arp_table,
0152 &prefix_value->gw);
0153 if (!dest_mac) {
0154
0155
0156
0157
0158 if (rec)
0159 NO_TEAR_INC(rec->xdp_pass);
0160 return XDP_PASS;
0161 }
0162 }
0163 }
0164
0165 if (src_mac && dest_mac) {
0166 int ret;
0167
0168 __builtin_memcpy(eth->h_dest, dest_mac, ETH_ALEN);
0169 __builtin_memcpy(eth->h_source, src_mac, ETH_ALEN);
0170
0171 ret = bpf_redirect_map(&tx_port, forward_to, 0);
0172 if (ret == XDP_REDIRECT) {
0173 if (rec)
0174 NO_TEAR_INC(rec->xdp_redirect);
0175 return ret;
0176 }
0177 }
0178 }
0179 default:
0180 break;
0181 }
0182 drop:
0183 if (rec)
0184 NO_TEAR_INC(rec->xdp_drop);
0185
0186 return XDP_DROP;
0187 }
0188
0189 char _license[] SEC("license") = "GPL";