2022-03-16 10:13:23 +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 .
*/
# include "vmlinux.h"
# include "xdp_sample.bpf.h"
# include "xdp_sample_shared.h"
# define ETH_ALEN 6
# define ETH_P_8021Q 0x8100
# define ETH_P_8021AD 0x88A8
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 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 " ) ;
SEC ( " xdp " )
int xdp_router_ipv4_prog ( struct xdp_md * ctx )
{
void * data_end = ( void * ) ( long ) ctx - > data_end ;
void * data = ( void * ) ( long ) ctx - > data ;
struct ethhdr * eth = data ;
u64 nh_off = sizeof ( * eth ) ;
struct datarec * rec ;
__be16 h_proto ;
u32 key = 0 ;
rec = bpf_map_lookup_elem ( & rx_cnt , & key ) ;
if ( rec )
NO_TEAR_INC ( rec - > processed ) ;
if ( data + nh_off > data_end )
goto drop ;
h_proto = eth - > h_proto ;
if ( h_proto = = bpf_htons ( ETH_P_8021Q ) | |
h_proto = = bpf_htons ( ETH_P_8021AD ) ) {
struct vlan_hdr * vhdr ;
vhdr = data + nh_off ;
nh_off + = sizeof ( struct vlan_hdr ) ;
if ( data + nh_off > data_end )
goto drop ;
h_proto = vhdr - > h_vlan_encapsulated_proto ;
}
switch ( bpf_ntohs ( h_proto ) ) {
case ETH_P_ARP :
if ( rec )
NO_TEAR_INC ( rec - > xdp_pass ) ;
return XDP_PASS ;
case ETH_P_IP : {
struct iphdr * iph = data + nh_off ;
struct direct_map * direct_entry ;
__be64 * dest_mac , * src_mac ;
int forward_to ;
if ( iph + 1 > data_end )
goto drop ;
direct_entry = bpf_map_lookup_elem ( & exact_match , & iph - > daddr ) ;
/* 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 {
struct trie_value * prefix_value ;
union key_4 key4 ;
/* Look up in the trie for lpm */
key4 . b32 [ 0 ] = 32 ;
key4 . b8 [ 4 ] = iph - > daddr & 0xff ;
key4 . b8 [ 5 ] = ( iph - > daddr > > 8 ) & 0xff ;
key4 . b8 [ 6 ] = ( iph - > daddr > > 16 ) & 0xff ;
key4 . b8 [ 7 ] = ( iph - > daddr > > 24 ) & 0xff ;
prefix_value = bpf_map_lookup_elem ( & lpm_map , & key4 ) ;
if ( ! prefix_value )
goto drop ;
forward_to = prefix_value - > ifindex ;
src_mac = & prefix_value - > value ;
if ( ! src_mac )
goto drop ;
dest_mac = bpf_map_lookup_elem ( & arp_table , & iph - > daddr ) ;
if ( ! dest_mac ) {
if ( ! prefix_value - > gw )
goto drop ;
dest_mac = bpf_map_lookup_elem ( & arp_table ,
& prefix_value - > gw ) ;
2022-05-25 12:44:27 +03:00
if ( ! dest_mac ) {
/* Forward the packet to the kernel in
* order to trigger ARP discovery for
* the default gw .
*/
if ( rec )
NO_TEAR_INC ( rec - > xdp_pass ) ;
return XDP_PASS ;
}
2022-03-16 10:13:23 +03:00
}
}
if ( src_mac & & dest_mac ) {
int ret ;
__builtin_memcpy ( eth - > h_dest , dest_mac , ETH_ALEN ) ;
__builtin_memcpy ( eth - > h_source , src_mac , ETH_ALEN ) ;
ret = bpf_redirect_map ( & tx_port , forward_to , 0 ) ;
if ( ret = = XDP_REDIRECT ) {
if ( rec )
NO_TEAR_INC ( rec - > xdp_redirect ) ;
return ret ;
}
}
}
default :
break ;
}
drop :
if ( rec )
NO_TEAR_INC ( rec - > xdp_drop ) ;
return XDP_DROP ;
}
char _license [ ] SEC ( " license " ) = " GPL " ;