xdp_router_ipv4.bpf.c (3886B)
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 16struct 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 */ 25union key_4 { 26 u32 b32[2]; 27 u8 b8[8]; 28}; 29 30struct arp_entry { 31 __be64 mac; 32 __be32 dst; 33}; 34 35struct direct_map { 36 struct arp_entry arp; 37 int ifindex; 38 __be64 mac; 39}; 40 41/* Map for trie implementation */ 42struct { 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 */ 51struct { 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 */ 59struct { 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 66struct { 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 73SEC("xdp") 74int 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 } 154 } 155 156 if (src_mac && dest_mac) { 157 int ret; 158 159 __builtin_memcpy(eth->h_dest, dest_mac, ETH_ALEN); 160 __builtin_memcpy(eth->h_source, src_mac, ETH_ALEN); 161 162 ret = bpf_redirect_map(&tx_port, forward_to, 0); 163 if (ret == XDP_REDIRECT) { 164 if (rec) 165 NO_TEAR_INC(rec->xdp_redirect); 166 return ret; 167 } 168 } 169 } 170 default: 171 break; 172 } 173drop: 174 if (rec) 175 NO_TEAR_INC(rec->xdp_drop); 176 177 return XDP_DROP; 178} 179 180char _license[] SEC("license") = "GPL";