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#define KBUILD_MODNAME "foo" 8#include <uapi/linux/bpf.h> 9#include <linux/in.h> 10#include <linux/if_ether.h> 11#include <linux/if_packet.h> 12#include <linux/if_vlan.h> 13#include <linux/ip.h> 14#include <linux/ipv6.h> 15#include <bpf/bpf_helpers.h> 16#include <linux/slab.h> 17#include <net/ip_fib.h> 18 19struct trie_value { 20 __u8 prefix[4]; 21 __be64 value; 22 int ifindex; 23 int metric; 24 __be32 gw; 25}; 26 27/* Key for lpm_trie*/ 28union key_4 { 29 u32 b32[2]; 30 u8 b8[8]; 31}; 32 33struct arp_entry { 34 __be64 mac; 35 __be32 dst; 36}; 37 38struct direct_map { 39 struct arp_entry arp; 40 int ifindex; 41 __be64 mac; 42}; 43 44/* Map for trie implementation*/ 45struct { 46 __uint(type, BPF_MAP_TYPE_LPM_TRIE); 47 __uint(key_size, 8); 48 __uint(value_size, sizeof(struct trie_value)); 49 __uint(max_entries, 50); 50 __uint(map_flags, BPF_F_NO_PREALLOC); 51} lpm_map SEC(".maps"); 52 53/* Map for counter*/ 54struct { 55 __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); 56 __type(key, u32); 57 __type(value, u64); 58 __uint(max_entries, 256); 59} rxcnt SEC(".maps"); 60 61/* Map for ARP table*/ 62struct { 63 __uint(type, BPF_MAP_TYPE_HASH); 64 __type(key, __be32); 65 __type(value, __be64); 66 __uint(max_entries, 50); 67} arp_table SEC(".maps"); 68 69/* Map to keep the exact match entries in the route table*/ 70struct { 71 __uint(type, BPF_MAP_TYPE_HASH); 72 __type(key, __be32); 73 __type(value, struct direct_map); 74 __uint(max_entries, 50); 75} exact_match SEC(".maps"); 76 77struct { 78 __uint(type, BPF_MAP_TYPE_DEVMAP); 79 __uint(key_size, sizeof(int)); 80 __uint(value_size, sizeof(int)); 81 __uint(max_entries, 100); 82} tx_port SEC(".maps"); 83 84/* Function to set source and destination mac of the packet */ 85static inline void set_src_dst_mac(void *data, void *src, void *dst) 86{ 87 unsigned short *source = src; 88 unsigned short *dest = dst; 89 unsigned short *p = data; 90 91 __builtin_memcpy(p, dest, 6); 92 __builtin_memcpy(p + 3, source, 6); 93} 94 95/* Parse IPV4 packet to get SRC, DST IP and protocol */ 96static inline int parse_ipv4(void *data, u64 nh_off, void *data_end, 97 __be32 *src, __be32 *dest) 98{ 99 struct iphdr *iph = data + nh_off; 100 101 if (iph + 1 > data_end) 102 return 0; 103 *src = iph->saddr; 104 *dest = iph->daddr; 105 return iph->protocol; 106} 107 108SEC("xdp_router_ipv4") 109int xdp_router_ipv4_prog(struct xdp_md *ctx) 110{ 111 void *data_end = (void *)(long)ctx->data_end; 112 __be64 *dest_mac = NULL, *src_mac = NULL; 113 void *data = (void *)(long)ctx->data; 114 struct trie_value *prefix_value; 115 int rc = XDP_DROP, forward_to; 116 struct ethhdr *eth = data; 117 union key_4 key4; 118 long *value; 119 u16 h_proto; 120 u32 ipproto; 121 u64 nh_off; 122 123 nh_off = sizeof(*eth); 124 if (data + nh_off > data_end) 125 return rc; 126 127 h_proto = eth->h_proto; 128 129 if (h_proto == htons(ETH_P_8021Q) || h_proto == htons(ETH_P_8021AD)) { 130 struct vlan_hdr *vhdr; 131 132 vhdr = data + nh_off; 133 nh_off += sizeof(struct vlan_hdr); 134 if (data + nh_off > data_end) 135 return rc; 136 h_proto = vhdr->h_vlan_encapsulated_proto; 137 } 138 if (h_proto == htons(ETH_P_ARP)) { 139 return XDP_PASS; 140 } else if (h_proto == htons(ETH_P_IP)) { 141 struct direct_map *direct_entry; 142 __be32 src_ip = 0, dest_ip = 0; 143 144 ipproto = parse_ipv4(data, nh_off, data_end, &src_ip, &dest_ip); 145 direct_entry = bpf_map_lookup_elem(&exact_match, &dest_ip); 146 /* Check for exact match, this would give a faster lookup*/ 147 if (direct_entry && direct_entry->mac && direct_entry->arp.mac) { 148 src_mac = &direct_entry->mac; 149 dest_mac = &direct_entry->arp.mac; 150 forward_to = direct_entry->ifindex; 151 } else { 152 /* Look up in the trie for lpm*/ 153 key4.b32[0] = 32; 154 key4.b8[4] = dest_ip & 0xff; 155 key4.b8[5] = (dest_ip >> 8) & 0xff; 156 key4.b8[6] = (dest_ip >> 16) & 0xff; 157 key4.b8[7] = (dest_ip >> 24) & 0xff; 158 prefix_value = bpf_map_lookup_elem(&lpm_map, &key4); 159 if (!prefix_value) 160 return XDP_DROP; 161 src_mac = &prefix_value->value; 162 if (!src_mac) 163 return XDP_DROP; 164 dest_mac = bpf_map_lookup_elem(&arp_table, &dest_ip); 165 if (!dest_mac) { 166 if (!prefix_value->gw) 167 return XDP_DROP; 168 dest_ip = prefix_value->gw; 169 dest_mac = bpf_map_lookup_elem(&arp_table, &dest_ip); 170 } 171 forward_to = prefix_value->ifindex; 172 } 173 } else { 174 ipproto = 0; 175 } 176 if (src_mac && dest_mac) { 177 set_src_dst_mac(data, src_mac, dest_mac); 178 value = bpf_map_lookup_elem(&rxcnt, &ipproto); 179 if (value) 180 *value += 1; 181 return bpf_redirect_map(&tx_port, forward_to, 0); 182 } 183 return rc; 184} 185 186char _license[] SEC("license") = "GPL"; 187