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>
2020-01-20 16:06:49 +03:00
# include <bpf/bpf_helpers.h>
2018-05-10 06:34:27 +03:00
# define IPV6_FLOWINFO_MASK cpu_to_be32(0x0FFFFFFF)
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 , 64 ) ;
} xdp_tx_ports SEC ( " .maps " ) ;
2018-05-10 06:34:27 +03:00
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 ) ;
2019-08-08 19:17:47 +03:00
/*
* Some rc ( return codes ) from bpf_fib_lookup ( ) are important ,
* to understand how this XDP - prog interacts with network stack .
*
* BPF_FIB_LKUP_RET_NO_NEIGH :
* Even if route lookup was a success , then the MAC - addresses are also
* needed . This is obtained from arp / neighbour table , but if table is
* ( still ) empty then BPF_FIB_LKUP_RET_NO_NEIGH is returned . To avoid
* doing ARP lookup directly from XDP , then send packet to normal
* network stack via XDP_PASS and expect it will do ARP resolution .
*
* BPF_FIB_LKUP_RET_FWD_DISABLED :
* The bpf_fib_lookup respect sysctl net . ipv { 4 , 6 } . conf . all . forwarding
* setting , and will return BPF_FIB_LKUP_RET_FWD_DISABLED if not
* enabled this on ingress device .
*/
if ( rc = = BPF_FIB_LKUP_RET_SUCCESS ) {
2019-08-08 19:17:42 +03:00
/* Verify egress index has been configured as TX-port.
* ( Note : User can still have inserted an egress ifindex that
* doesn ' t support XDP xmit , which will result in packet drops ) .
*
* Note : lookup in devmap supported since 0 cdbb4b09a0 .
* If not supported will fail with :
* cannot pass map_type 14 into func bpf_map_lookup_elem # 1 :
*/
if ( ! bpf_map_lookup_elem ( & xdp_tx_ports , & fib_params . ifindex ) )
return XDP_PASS ;
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 ) ;
2019-08-08 19:17:37 +03:00
return bpf_redirect_map ( & xdp_tx_ports , 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 " ;