|  | /* Copyright (C) 2017 Cavium, Inc. | 
|  | * | 
|  | * This program is free software; you can redistribute it and/or modify it | 
|  | * under the terms of version 2 of the GNU General Public License | 
|  | * as published by the Free Software Foundation. | 
|  | */ | 
|  | #define KBUILD_MODNAME "foo" | 
|  | #include <uapi/linux/bpf.h> | 
|  | #include <linux/in.h> | 
|  | #include <linux/if_ether.h> | 
|  | #include <linux/if_packet.h> | 
|  | #include <linux/if_vlan.h> | 
|  | #include <linux/ip.h> | 
|  | #include <linux/ipv6.h> | 
|  | #include <bpf/bpf_helpers.h> | 
|  | #include <linux/slab.h> | 
|  | #include <net/ip_fib.h> | 
|  |  | 
|  | struct trie_value { | 
|  | __u8 prefix[4]; | 
|  | __be64 value; | 
|  | int ifindex; | 
|  | int metric; | 
|  | __be32 gw; | 
|  | }; | 
|  |  | 
|  | /* Key for lpm_trie*/ | 
|  | union key_4 { | 
|  | u32 b32[2]; | 
|  | u8 b8[8]; | 
|  | }; | 
|  |  | 
|  | struct arp_entry { | 
|  | __be64 mac; | 
|  | __be32 dst; | 
|  | }; | 
|  |  | 
|  | struct direct_map { | 
|  | struct arp_entry arp; | 
|  | int ifindex; | 
|  | __be64 mac; | 
|  | }; | 
|  |  | 
|  | /* Map for trie implementation*/ | 
|  | struct { | 
|  | __uint(type, BPF_MAP_TYPE_LPM_TRIE); | 
|  | __uint(key_size, 8); | 
|  | __uint(value_size, sizeof(struct trie_value)); | 
|  | __uint(max_entries, 50); | 
|  | __uint(map_flags, BPF_F_NO_PREALLOC); | 
|  | } lpm_map SEC(".maps"); | 
|  |  | 
|  | /* Map for counter*/ | 
|  | struct { | 
|  | __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); | 
|  | __type(key, u32); | 
|  | __type(value, u64); | 
|  | __uint(max_entries, 256); | 
|  | } rxcnt SEC(".maps"); | 
|  |  | 
|  | /* Map for ARP table*/ | 
|  | struct { | 
|  | __uint(type, BPF_MAP_TYPE_HASH); | 
|  | __type(key, __be32); | 
|  | __type(value, __be64); | 
|  | __uint(max_entries, 50); | 
|  | } arp_table SEC(".maps"); | 
|  |  | 
|  | /* Map to keep the exact match entries in the route table*/ | 
|  | struct { | 
|  | __uint(type, BPF_MAP_TYPE_HASH); | 
|  | __type(key, __be32); | 
|  | __type(value, struct direct_map); | 
|  | __uint(max_entries, 50); | 
|  | } exact_match SEC(".maps"); | 
|  |  | 
|  | struct { | 
|  | __uint(type, BPF_MAP_TYPE_DEVMAP); | 
|  | __uint(key_size, sizeof(int)); | 
|  | __uint(value_size, sizeof(int)); | 
|  | __uint(max_entries, 100); | 
|  | } tx_port SEC(".maps"); | 
|  |  | 
|  | /* Function to set source and destination mac of the packet */ | 
|  | static inline void set_src_dst_mac(void *data, void *src, void *dst) | 
|  | { | 
|  | unsigned short *source = src; | 
|  | unsigned short *dest  = dst; | 
|  | unsigned short *p = data; | 
|  |  | 
|  | __builtin_memcpy(p, dest, 6); | 
|  | __builtin_memcpy(p + 3, source, 6); | 
|  | } | 
|  |  | 
|  | /* Parse IPV4 packet to get SRC, DST IP and protocol */ | 
|  | static inline int parse_ipv4(void *data, u64 nh_off, void *data_end, | 
|  | __be32 *src, __be32 *dest) | 
|  | { | 
|  | struct iphdr *iph = data + nh_off; | 
|  |  | 
|  | if (iph + 1 > data_end) | 
|  | return 0; | 
|  | *src = iph->saddr; | 
|  | *dest = iph->daddr; | 
|  | return iph->protocol; | 
|  | } | 
|  |  | 
|  | SEC("xdp_router_ipv4") | 
|  | int xdp_router_ipv4_prog(struct xdp_md *ctx) | 
|  | { | 
|  | void *data_end = (void *)(long)ctx->data_end; | 
|  | __be64 *dest_mac = NULL, *src_mac = NULL; | 
|  | void *data = (void *)(long)ctx->data; | 
|  | struct trie_value *prefix_value; | 
|  | int rc = XDP_DROP, forward_to; | 
|  | struct ethhdr *eth = data; | 
|  | union key_4 key4; | 
|  | long *value; | 
|  | u16 h_proto; | 
|  | u32 ipproto; | 
|  | u64 nh_off; | 
|  |  | 
|  | nh_off = sizeof(*eth); | 
|  | if (data + nh_off > data_end) | 
|  | return rc; | 
|  |  | 
|  | h_proto = eth->h_proto; | 
|  |  | 
|  | if (h_proto == htons(ETH_P_8021Q) || h_proto == htons(ETH_P_8021AD)) { | 
|  | struct vlan_hdr *vhdr; | 
|  |  | 
|  | vhdr = data + nh_off; | 
|  | nh_off += sizeof(struct vlan_hdr); | 
|  | if (data + nh_off > data_end) | 
|  | return rc; | 
|  | h_proto = vhdr->h_vlan_encapsulated_proto; | 
|  | } | 
|  | if (h_proto == htons(ETH_P_ARP)) { | 
|  | return XDP_PASS; | 
|  | } else if (h_proto == htons(ETH_P_IP)) { | 
|  | struct direct_map *direct_entry; | 
|  | __be32 src_ip = 0, dest_ip = 0; | 
|  |  | 
|  | ipproto = parse_ipv4(data, nh_off, data_end, &src_ip, &dest_ip); | 
|  | direct_entry = bpf_map_lookup_elem(&exact_match, &dest_ip); | 
|  | /* Check for exact match, this would give a faster lookup*/ | 
|  | if (direct_entry && direct_entry->mac && direct_entry->arp.mac) { | 
|  | src_mac = &direct_entry->mac; | 
|  | dest_mac = &direct_entry->arp.mac; | 
|  | forward_to = direct_entry->ifindex; | 
|  | } else { | 
|  | /* Look up in the trie for lpm*/ | 
|  | key4.b32[0] = 32; | 
|  | key4.b8[4] = dest_ip & 0xff; | 
|  | key4.b8[5] = (dest_ip >> 8) & 0xff; | 
|  | key4.b8[6] = (dest_ip >> 16) & 0xff; | 
|  | key4.b8[7] = (dest_ip >> 24) & 0xff; | 
|  | prefix_value = bpf_map_lookup_elem(&lpm_map, &key4); | 
|  | if (!prefix_value) | 
|  | return XDP_DROP; | 
|  | src_mac = &prefix_value->value; | 
|  | if (!src_mac) | 
|  | return XDP_DROP; | 
|  | dest_mac = bpf_map_lookup_elem(&arp_table, &dest_ip); | 
|  | if (!dest_mac) { | 
|  | if (!prefix_value->gw) | 
|  | return XDP_DROP; | 
|  | dest_ip = prefix_value->gw; | 
|  | dest_mac = bpf_map_lookup_elem(&arp_table, &dest_ip); | 
|  | } | 
|  | forward_to = prefix_value->ifindex; | 
|  | } | 
|  | } else { | 
|  | ipproto = 0; | 
|  | } | 
|  | if (src_mac && dest_mac) { | 
|  | set_src_dst_mac(data, src_mac, dest_mac); | 
|  | value = bpf_map_lookup_elem(&rxcnt, &ipproto); | 
|  | if (value) | 
|  | *value += 1; | 
|  | return  bpf_redirect_map(&tx_port, forward_to, 0); | 
|  | } | 
|  | return rc; | 
|  | } | 
|  |  | 
|  | char _license[] SEC("license") = "GPL"; |