| /* 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_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"; |