2017-11-05 06:22:30 +03:00
/* 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>
2020-01-20 16:06:49 +03:00
# include <bpf/bpf_helpers.h>
2017-11-05 06:22:30 +03:00
# 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*/
2019-11-07 03:51:53 +03:00
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 " ) ;
2017-11-05 06:22:30 +03:00
/* Map for counter*/
2019-11-07 03:51:53 +03:00
struct {
__uint ( type , BPF_MAP_TYPE_PERCPU_ARRAY ) ;
__type ( key , u32 ) ;
__type ( value , u64 ) ;
__uint ( max_entries , 256 ) ;
} rxcnt SEC ( " .maps " ) ;
2017-11-05 06:22:30 +03:00
/* Map for ARP table*/
2019-11-07 03:51:53 +03:00
struct {
__uint ( type , BPF_MAP_TYPE_HASH ) ;
__type ( key , __be32 ) ;
__type ( value , __be64 ) ;
__uint ( max_entries , 50 ) ;
} arp_table SEC ( " .maps " ) ;
2017-11-05 06:22:30 +03:00
/* Map to keep the exact match entries in the route table*/
2019-11-07 03:51:53 +03:00
struct {
__uint ( type , BPF_MAP_TYPE_HASH ) ;
__type ( key , __be32 ) ;
__type ( value , struct direct_map ) ;
__uint ( max_entries , 50 ) ;
} exact_match SEC ( " .maps " ) ;
2017-11-05 06:22:30 +03:00
2019-11-07 03:51:53 +03:00
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 " ) ;
2017-11-05 06:22:30 +03:00
/* 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 " ;