1 /* Copyright (C) 2017 Cavium, Inc. 2 * 3 * This program is free software; you can redistribute it and/or modify it 4 * under the terms of version 2 of the GNU General Public License 5 * as published by the Free Software Foundation. 6 */ 7 8 #include "vmlinux.h" 9 #include "xdp_sample.bpf.h" 10 #include "xdp_sample_shared.h" 11 12 #define ETH_ALEN 6 13 #define ETH_P_8021Q 0x8100 14 #define ETH_P_8021AD 0x88A8 15 16 struct trie_value { 17 __u8 prefix[4]; 18 __be64 value; 19 int ifindex; 20 int metric; 21 __be32 gw; 22 }; 23 24 /* Key for lpm_trie */ 25 union key_4 { 26 u32 b32[2]; 27 u8 b8[8]; 28 }; 29 30 struct arp_entry { 31 __be64 mac; 32 __be32 dst; 33 }; 34 35 struct direct_map { 36 struct arp_entry arp; 37 int ifindex; 38 __be64 mac; 39 }; 40 41 /* Map for trie implementation */ 42 struct { 43 __uint(type, BPF_MAP_TYPE_LPM_TRIE); 44 __uint(key_size, 8); 45 __uint(value_size, sizeof(struct trie_value)); 46 __uint(max_entries, 50); 47 __uint(map_flags, BPF_F_NO_PREALLOC); 48 } lpm_map SEC(".maps"); 49 50 /* Map for ARP table */ 51 struct { 52 __uint(type, BPF_MAP_TYPE_HASH); 53 __type(key, __be32); 54 __type(value, __be64); 55 __uint(max_entries, 50); 56 } arp_table SEC(".maps"); 57 58 /* Map to keep the exact match entries in the route table */ 59 struct { 60 __uint(type, BPF_MAP_TYPE_HASH); 61 __type(key, __be32); 62 __type(value, struct direct_map); 63 __uint(max_entries, 50); 64 } exact_match SEC(".maps"); 65 66 struct { 67 __uint(type, BPF_MAP_TYPE_DEVMAP); 68 __uint(key_size, sizeof(int)); 69 __uint(value_size, sizeof(int)); 70 __uint(max_entries, 100); 71 } tx_port SEC(".maps"); 72 73 SEC("xdp") 74 int xdp_router_ipv4_prog(struct xdp_md *ctx) 75 { 76 void *data_end = (void *)(long)ctx->data_end; 77 void *data = (void *)(long)ctx->data; 78 struct ethhdr *eth = data; 79 u64 nh_off = sizeof(*eth); 80 struct datarec *rec; 81 __be16 h_proto; 82 u32 key = 0; 83 84 rec = bpf_map_lookup_elem(&rx_cnt, &key); 85 if (rec) 86 NO_TEAR_INC(rec->processed); 87 88 if (data + nh_off > data_end) 89 goto drop; 90 91 h_proto = eth->h_proto; 92 if (h_proto == bpf_htons(ETH_P_8021Q) || 93 h_proto == bpf_htons(ETH_P_8021AD)) { 94 struct vlan_hdr *vhdr; 95 96 vhdr = data + nh_off; 97 nh_off += sizeof(struct vlan_hdr); 98 if (data + nh_off > data_end) 99 goto drop; 100 101 h_proto = vhdr->h_vlan_encapsulated_proto; 102 } 103 104 switch (bpf_ntohs(h_proto)) { 105 case ETH_P_ARP: 106 if (rec) 107 NO_TEAR_INC(rec->xdp_pass); 108 return XDP_PASS; 109 case ETH_P_IP: { 110 struct iphdr *iph = data + nh_off; 111 struct direct_map *direct_entry; 112 __be64 *dest_mac, *src_mac; 113 int forward_to; 114 115 if (iph + 1 > data_end) 116 goto drop; 117 118 direct_entry = bpf_map_lookup_elem(&exact_match, &iph->daddr); 119 120 /* Check for exact match, this would give a faster lookup */ 121 if (direct_entry && direct_entry->mac && 122 direct_entry->arp.mac) { 123 src_mac = &direct_entry->mac; 124 dest_mac = &direct_entry->arp.mac; 125 forward_to = direct_entry->ifindex; 126 } else { 127 struct trie_value *prefix_value; 128 union key_4 key4; 129 130 /* Look up in the trie for lpm */ 131 key4.b32[0] = 32; 132 key4.b8[4] = iph->daddr & 0xff; 133 key4.b8[5] = (iph->daddr >> 8) & 0xff; 134 key4.b8[6] = (iph->daddr >> 16) & 0xff; 135 key4.b8[7] = (iph->daddr >> 24) & 0xff; 136 137 prefix_value = bpf_map_lookup_elem(&lpm_map, &key4); 138 if (!prefix_value) 139 goto drop; 140 141 forward_to = prefix_value->ifindex; 142 src_mac = &prefix_value->value; 143 if (!src_mac) 144 goto drop; 145 146 dest_mac = bpf_map_lookup_elem(&arp_table, &iph->daddr); 147 if (!dest_mac) { 148 if (!prefix_value->gw) 149 goto drop; 150 151 dest_mac = bpf_map_lookup_elem(&arp_table, 152 &prefix_value->gw); 153 if (!dest_mac) { 154 /* Forward the packet to the kernel in 155 * order to trigger ARP discovery for 156 * the default gw. 157 */ 158 if (rec) 159 NO_TEAR_INC(rec->xdp_pass); 160 return XDP_PASS; 161 } 162 } 163 } 164 165 if (src_mac && dest_mac) { 166 int ret; 167 168 __builtin_memcpy(eth->h_dest, dest_mac, ETH_ALEN); 169 __builtin_memcpy(eth->h_source, src_mac, ETH_ALEN); 170 171 ret = bpf_redirect_map(&tx_port, forward_to, 0); 172 if (ret == XDP_REDIRECT) { 173 if (rec) 174 NO_TEAR_INC(rec->xdp_redirect); 175 return ret; 176 } 177 } 178 } 179 default: 180 break; 181 } 182 drop: 183 if (rec) 184 NO_TEAR_INC(rec->xdp_drop); 185 186 return XDP_DROP; 187 } 188 189 char _license[] SEC("license") = "GPL"; 190