2018-05-10 06:34:27 +03:00
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2017-18 David Ahern <dsahern@gmail.com>
*
* 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 .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*/
# 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"
# define IPV6_FLOWINFO_MASK cpu_to_be32(0x0FFFFFFF)
struct bpf_map_def SEC ( " maps " ) tx_port = {
. type = BPF_MAP_TYPE_DEVMAP ,
. key_size = sizeof ( int ) ,
. value_size = sizeof ( int ) ,
. max_entries = 64 ,
} ;
2018-05-16 02:20:52 +03:00
/* from include/net/ip.h */
static __always_inline int ip_decrease_ttl ( struct iphdr * iph )
{
u32 check = ( __force u32 ) iph - > check ;
check + = ( __force u32 ) htons ( 0x0100 ) ;
iph - > check = ( __force __sum16 ) ( check + ( check > = 0xFFFF ) ) ;
return - - iph - > ttl ;
}
2018-05-10 06:34:27 +03:00
static __always_inline int xdp_fwd_flags ( struct xdp_md * ctx , u32 flags )
{
void * data_end = ( void * ) ( long ) ctx - > data_end ;
void * data = ( void * ) ( long ) ctx - > data ;
struct bpf_fib_lookup fib_params ;
struct ethhdr * eth = data ;
2018-05-16 02:20:52 +03:00
struct ipv6hdr * ip6h ;
struct iphdr * iph ;
2018-05-10 06:34:27 +03:00
u16 h_proto ;
u64 nh_off ;
2018-06-27 02:21:18 +03:00
int rc ;
2018-05-10 06:34:27 +03:00
nh_off = sizeof ( * eth ) ;
if ( data + nh_off > data_end )
return XDP_DROP ;
__builtin_memset ( & fib_params , 0 , sizeof ( fib_params ) ) ;
h_proto = eth - > h_proto ;
if ( h_proto = = htons ( ETH_P_IP ) ) {
2018-05-16 02:20:52 +03:00
iph = data + nh_off ;
2018-05-10 06:34:27 +03:00
if ( iph + 1 > data_end )
return XDP_DROP ;
2018-05-16 02:20:52 +03:00
if ( iph - > ttl < = 1 )
return XDP_PASS ;
2018-05-10 06:34:27 +03:00
fib_params . family = AF_INET ;
fib_params . tos = iph - > tos ;
fib_params . l4_protocol = iph - > protocol ;
fib_params . sport = 0 ;
fib_params . dport = 0 ;
fib_params . tot_len = ntohs ( iph - > tot_len ) ;
fib_params . ipv4_src = iph - > saddr ;
fib_params . ipv4_dst = iph - > daddr ;
} else if ( h_proto = = htons ( ETH_P_IPV6 ) ) {
struct in6_addr * src = ( struct in6_addr * ) fib_params . ipv6_src ;
struct in6_addr * dst = ( struct in6_addr * ) fib_params . ipv6_dst ;
2018-05-16 02:20:52 +03:00
ip6h = data + nh_off ;
if ( ip6h + 1 > data_end )
2018-05-10 06:34:27 +03:00
return XDP_DROP ;
2018-05-16 02:20:52 +03:00
if ( ip6h - > hop_limit < = 1 )
return XDP_PASS ;
2018-05-10 06:34:27 +03:00
fib_params . family = AF_INET6 ;
2018-06-03 18:15:19 +03:00
fib_params . flowinfo = * ( __be32 * ) ip6h & IPV6_FLOWINFO_MASK ;
2018-05-16 02:20:52 +03:00
fib_params . l4_protocol = ip6h - > nexthdr ;
2018-05-10 06:34:27 +03:00
fib_params . sport = 0 ;
fib_params . dport = 0 ;
2018-05-16 02:20:52 +03:00
fib_params . tot_len = ntohs ( ip6h - > payload_len ) ;
* src = ip6h - > saddr ;
* dst = ip6h - > daddr ;
2018-05-10 06:34:27 +03:00
} else {
return XDP_PASS ;
}
fib_params . ifindex = ctx - > ingress_ifindex ;
2018-06-27 02:21:18 +03:00
rc = bpf_fib_lookup ( ctx , & fib_params , sizeof ( fib_params ) , flags ) ;
2018-05-10 06:34:27 +03:00
/* verify egress index has xdp support
* TO - DO bpf_map_lookup_elem ( & tx_port , & key ) fails with
* cannot pass map_type 14 into func bpf_map_lookup_elem # 1 :
* NOTE : without verification that egress index supports XDP
* forwarding packets are dropped .
*/
2018-06-27 02:21:18 +03:00
if ( rc = = 0 ) {
2018-05-16 02:20:52 +03:00
if ( h_proto = = htons ( ETH_P_IP ) )
ip_decrease_ttl ( iph ) ;
else if ( h_proto = = htons ( ETH_P_IPV6 ) )
ip6h - > hop_limit - - ;
2018-05-10 06:34:27 +03:00
memcpy ( eth - > h_dest , fib_params . dmac , ETH_ALEN ) ;
memcpy ( eth - > h_source , fib_params . smac , ETH_ALEN ) ;
2018-06-27 02:21:18 +03:00
return bpf_redirect_map ( & tx_port , fib_params . ifindex , 0 ) ;
2018-05-10 06:34:27 +03:00
}
return XDP_PASS ;
}
SEC ( " xdp_fwd " )
int xdp_fwd_prog ( struct xdp_md * ctx )
{
return xdp_fwd_flags ( ctx , 0 ) ;
}
SEC ( " xdp_fwd_direct " )
int xdp_fwd_direct_prog ( struct xdp_md * ctx )
{
return xdp_fwd_flags ( ctx , BPF_FIB_LOOKUP_DIRECT ) ;
}
char _license [ ] SEC ( " license " ) = " GPL " ;