2016-11-30 17:10:10 +01:00
/* Copyright (c) 2016 Thomas Graf <tgraf@tgraf.ch>
*
* 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 .
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/skbuff.h>
# include <linux/types.h>
# include <linux/bpf.h>
# include <net/lwtunnel.h>
2019-02-13 11:53:37 -08:00
# include <net/gre.h>
2019-02-13 11:53:39 -08:00
# include <net/ip6_route.h>
2019-03-22 06:06:09 -07:00
# include <net/ipv6_stubs.h>
2016-11-30 17:10:10 +01:00
struct bpf_lwt_prog {
struct bpf_prog * prog ;
char * name ;
} ;
struct bpf_lwt {
struct bpf_lwt_prog in ;
struct bpf_lwt_prog out ;
struct bpf_lwt_prog xmit ;
int family ;
} ;
# define MAX_PROG_NAME 256
static inline struct bpf_lwt * bpf_lwt_lwtunnel ( struct lwtunnel_state * lwt )
{
return ( struct bpf_lwt * ) lwt - > data ;
}
# define NO_REDIRECT false
# define CAN_REDIRECT true
static int run_lwt_bpf ( struct sk_buff * skb , struct bpf_lwt_prog * lwt ,
struct dst_entry * dst , bool can_redirect )
{
int ret ;
/* Preempt disable is needed to protect per-cpu redirect_info between
* BPF prog and skb_do_redirect ( ) . The call_rcu in bpf_prog_put ( ) and
* access to maps strictly require a rcu_read_lock ( ) for protection ,
* mixing with BH RCU lock doesn ' t work .
*/
preempt_disable ( ) ;
2017-09-25 02:25:50 +02:00
bpf_compute_data_pointers ( skb ) ;
2016-11-30 17:10:10 +01:00
ret = bpf_prog_run_save_cb ( lwt - > prog , skb ) ;
switch ( ret ) {
case BPF_OK :
2019-02-13 11:53:39 -08:00
case BPF_LWT_REROUTE :
2016-11-30 17:10:10 +01:00
break ;
case BPF_REDIRECT :
if ( unlikely ( ! can_redirect ) ) {
pr_warn_once ( " Illegal redirect return code in prog %s \n " ,
lwt - > name ? : " <unknown> " ) ;
ret = BPF_OK ;
} else {
2019-01-15 20:19:22 -05:00
skb_reset_mac_header ( skb ) ;
2016-11-30 17:10:10 +01:00
ret = skb_do_redirect ( skb ) ;
if ( ret = = 0 )
ret = BPF_REDIRECT ;
}
break ;
case BPF_DROP :
kfree_skb ( skb ) ;
ret = - EPERM ;
break ;
default :
pr_warn_once ( " bpf-lwt: Illegal return value %u, expect packet loss \n " , ret ) ;
kfree_skb ( skb ) ;
ret = - EINVAL ;
break ;
}
preempt_enable ( ) ;
return ret ;
}
2019-02-13 11:53:39 -08:00
static int bpf_lwt_input_reroute ( struct sk_buff * skb )
{
int err = - EINVAL ;
if ( skb - > protocol = = htons ( ETH_P_IP ) ) {
struct iphdr * iph = ip_hdr ( skb ) ;
err = ip_route_input_noref ( skb , iph - > daddr , iph - > saddr ,
iph - > tos , skb_dst ( skb ) - > dev ) ;
} else if ( skb - > protocol = = htons ( ETH_P_IPV6 ) ) {
err = ipv6_stub - > ipv6_route_input ( skb ) ;
} else {
err = - EAFNOSUPPORT ;
}
if ( err )
goto err ;
return dst_input ( skb ) ;
err :
kfree_skb ( skb ) ;
return err ;
}
2016-11-30 17:10:10 +01:00
static int bpf_input ( struct sk_buff * skb )
{
struct dst_entry * dst = skb_dst ( skb ) ;
struct bpf_lwt * bpf ;
int ret ;
bpf = bpf_lwt_lwtunnel ( dst - > lwtstate ) ;
if ( bpf - > in . prog ) {
ret = run_lwt_bpf ( skb , & bpf - > in , dst , NO_REDIRECT ) ;
if ( ret < 0 )
return ret ;
2019-02-13 11:53:39 -08:00
if ( ret = = BPF_LWT_REROUTE )
return bpf_lwt_input_reroute ( skb ) ;
2016-11-30 17:10:10 +01:00
}
if ( unlikely ( ! dst - > lwtstate - > orig_input ) ) {
kfree_skb ( skb ) ;
return - EINVAL ;
}
return dst - > lwtstate - > orig_input ( skb ) ;
}
static int bpf_output ( struct net * net , struct sock * sk , struct sk_buff * skb )
{
struct dst_entry * dst = skb_dst ( skb ) ;
struct bpf_lwt * bpf ;
int ret ;
bpf = bpf_lwt_lwtunnel ( dst - > lwtstate ) ;
if ( bpf - > out . prog ) {
ret = run_lwt_bpf ( skb , & bpf - > out , dst , NO_REDIRECT ) ;
if ( ret < 0 )
return ret ;
}
if ( unlikely ( ! dst - > lwtstate - > orig_output ) ) {
pr_warn_once ( " orig_output not set on dst for prog %s \n " ,
bpf - > out . name ) ;
kfree_skb ( skb ) ;
return - EINVAL ;
}
return dst - > lwtstate - > orig_output ( net , sk , skb ) ;
}
static int xmit_check_hhlen ( struct sk_buff * skb )
{
int hh_len = skb_dst ( skb ) - > dev - > hard_header_len ;
if ( skb_headroom ( skb ) < hh_len ) {
int nhead = HH_DATA_ALIGN ( hh_len - skb_headroom ( skb ) ) ;
if ( pskb_expand_head ( skb , nhead , 0 , GFP_ATOMIC ) )
return - ENOMEM ;
}
return 0 ;
}
2019-02-13 11:53:39 -08:00
static int bpf_lwt_xmit_reroute ( struct sk_buff * skb )
{
struct net_device * l3mdev = l3mdev_master_dev_rcu ( skb_dst ( skb ) - > dev ) ;
int oif = l3mdev ? l3mdev - > ifindex : 0 ;
struct dst_entry * dst = NULL ;
2019-02-14 10:39:31 -08:00
int err = - EAFNOSUPPORT ;
2019-02-13 11:53:39 -08:00
struct sock * sk ;
struct net * net ;
bool ipv4 ;
if ( skb - > protocol = = htons ( ETH_P_IP ) )
ipv4 = true ;
else if ( skb - > protocol = = htons ( ETH_P_IPV6 ) )
ipv4 = false ;
else
2019-02-14 10:39:31 -08:00
goto err ;
2019-02-13 11:53:39 -08:00
sk = sk_to_full_sk ( skb - > sk ) ;
if ( sk ) {
if ( sk - > sk_bound_dev_if )
oif = sk - > sk_bound_dev_if ;
net = sock_net ( sk ) ;
} else {
net = dev_net ( skb_dst ( skb ) - > dev ) ;
}
if ( ipv4 ) {
struct iphdr * iph = ip_hdr ( skb ) ;
struct flowi4 fl4 = { } ;
struct rtable * rt ;
fl4 . flowi4_oif = oif ;
fl4 . flowi4_mark = skb - > mark ;
fl4 . flowi4_uid = sock_net_uid ( net , sk ) ;
fl4 . flowi4_tos = RT_TOS ( iph - > tos ) ;
fl4 . flowi4_flags = FLOWI_FLAG_ANYSRC ;
fl4 . flowi4_proto = iph - > protocol ;
fl4 . daddr = iph - > daddr ;
fl4 . saddr = iph - > saddr ;
rt = ip_route_output_key ( net , & fl4 ) ;
2019-02-14 10:39:31 -08:00
if ( IS_ERR ( rt ) ) {
err = PTR_ERR ( rt ) ;
goto err ;
}
2019-02-13 11:53:39 -08:00
dst = & rt - > dst ;
} else {
struct ipv6hdr * iph6 = ipv6_hdr ( skb ) ;
struct flowi6 fl6 = { } ;
fl6 . flowi6_oif = oif ;
fl6 . flowi6_mark = skb - > mark ;
fl6 . flowi6_uid = sock_net_uid ( net , sk ) ;
fl6 . flowlabel = ip6_flowinfo ( iph6 ) ;
fl6 . flowi6_proto = iph6 - > nexthdr ;
fl6 . daddr = iph6 - > daddr ;
fl6 . saddr = iph6 - > saddr ;
err = ipv6_stub - > ipv6_dst_lookup ( net , skb - > sk , & dst , & fl6 ) ;
2019-02-14 10:39:31 -08:00
if ( unlikely ( err ) )
goto err ;
if ( IS_ERR ( dst ) ) {
err = PTR_ERR ( dst ) ;
goto err ;
}
2019-02-13 11:53:39 -08:00
}
if ( unlikely ( dst - > error ) ) {
2019-02-14 10:39:31 -08:00
err = dst - > error ;
2019-02-13 11:53:39 -08:00
dst_release ( dst ) ;
2019-02-14 10:39:31 -08:00
goto err ;
2019-02-13 11:53:39 -08:00
}
/* Although skb header was reserved in bpf_lwt_push_ip_encap(), it
* was done for the previous dst , so we are doing it here again , in
* case the new dst needs much more space . The call below is a noop
* if there is enough header space in skb .
*/
err = skb_cow_head ( skb , LL_RESERVED_SPACE ( dst - > dev ) ) ;
if ( unlikely ( err ) )
2019-02-14 10:39:31 -08:00
goto err ;
2019-02-13 11:53:39 -08:00
skb_dst_drop ( skb ) ;
skb_dst_set ( skb , dst ) ;
err = dst_output ( dev_net ( skb_dst ( skb ) - > dev ) , skb - > sk , skb ) ;
if ( unlikely ( err ) )
2019-02-23 18:25:01 -08:00
return err ;
2019-02-13 11:53:39 -08:00
/* ip[6]_finish_output2 understand LWTUNNEL_XMIT_DONE */
return LWTUNNEL_XMIT_DONE ;
2019-02-14 10:39:31 -08:00
err :
kfree_skb ( skb ) ;
return err ;
2019-02-13 11:53:39 -08:00
}
2016-11-30 17:10:10 +01:00
static int bpf_xmit ( struct sk_buff * skb )
{
struct dst_entry * dst = skb_dst ( skb ) ;
struct bpf_lwt * bpf ;
bpf = bpf_lwt_lwtunnel ( dst - > lwtstate ) ;
if ( bpf - > xmit . prog ) {
2019-02-13 11:53:39 -08:00
__be16 proto = skb - > protocol ;
2016-11-30 17:10:10 +01:00
int ret ;
ret = run_lwt_bpf ( skb , & bpf - > xmit , dst , CAN_REDIRECT ) ;
switch ( ret ) {
case BPF_OK :
2019-02-13 11:53:39 -08:00
/* If the header changed, e.g. via bpf_lwt_push_encap,
* BPF_LWT_REROUTE below should have been used if the
* protocol was also changed .
*/
if ( skb - > protocol ! = proto ) {
kfree_skb ( skb ) ;
return - EINVAL ;
}
2016-11-30 17:10:10 +01:00
/* If the header was expanded, headroom might be too
* small for L2 header to come , expand as needed .
*/
ret = xmit_check_hhlen ( skb ) ;
if ( unlikely ( ret ) )
return ret ;
return LWTUNNEL_XMIT_CONTINUE ;
case BPF_REDIRECT :
return LWTUNNEL_XMIT_DONE ;
2019-02-13 11:53:39 -08:00
case BPF_LWT_REROUTE :
return bpf_lwt_xmit_reroute ( skb ) ;
2016-11-30 17:10:10 +01:00
default :
return ret ;
}
}
return LWTUNNEL_XMIT_CONTINUE ;
}
static void bpf_lwt_prog_destroy ( struct bpf_lwt_prog * prog )
{
if ( prog - > prog )
bpf_prog_put ( prog - > prog ) ;
kfree ( prog - > name ) ;
}
static void bpf_destroy_state ( struct lwtunnel_state * lwt )
{
struct bpf_lwt * bpf = bpf_lwt_lwtunnel ( lwt ) ;
bpf_lwt_prog_destroy ( & bpf - > in ) ;
bpf_lwt_prog_destroy ( & bpf - > out ) ;
bpf_lwt_prog_destroy ( & bpf - > xmit ) ;
}
static const struct nla_policy bpf_prog_policy [ LWT_BPF_PROG_MAX + 1 ] = {
[ LWT_BPF_PROG_FD ] = { . type = NLA_U32 , } ,
[ LWT_BPF_PROG_NAME ] = { . type = NLA_NUL_STRING ,
. len = MAX_PROG_NAME } ,
} ;
static int bpf_parse_prog ( struct nlattr * attr , struct bpf_lwt_prog * prog ,
enum bpf_prog_type type )
{
struct nlattr * tb [ LWT_BPF_PROG_MAX + 1 ] ;
struct bpf_prog * p ;
int ret ;
u32 fd ;
2017-04-12 14:34:07 +02:00
ret = nla_parse_nested ( tb , LWT_BPF_PROG_MAX , attr , bpf_prog_policy ,
NULL ) ;
2016-11-30 17:10:10 +01:00
if ( ret < 0 )
return ret ;
if ( ! tb [ LWT_BPF_PROG_FD ] | | ! tb [ LWT_BPF_PROG_NAME ] )
return - EINVAL ;
2018-07-29 00:28:31 +09:00
prog - > name = nla_memdup ( tb [ LWT_BPF_PROG_NAME ] , GFP_ATOMIC ) ;
2016-11-30 17:10:10 +01:00
if ( ! prog - > name )
return - ENOMEM ;
fd = nla_get_u32 ( tb [ LWT_BPF_PROG_FD ] ) ;
p = bpf_prog_get_type ( fd , type ) ;
if ( IS_ERR ( p ) )
return PTR_ERR ( p ) ;
prog - > prog = p ;
return 0 ;
}
static const struct nla_policy bpf_nl_policy [ LWT_BPF_MAX + 1 ] = {
[ LWT_BPF_IN ] = { . type = NLA_NESTED , } ,
[ LWT_BPF_OUT ] = { . type = NLA_NESTED , } ,
[ LWT_BPF_XMIT ] = { . type = NLA_NESTED , } ,
[ LWT_BPF_XMIT_HEADROOM ] = { . type = NLA_U32 } ,
} ;
2017-01-30 12:07:37 -08:00
static int bpf_build_state ( struct nlattr * nla ,
2016-11-30 17:10:10 +01:00
unsigned int family , const void * cfg ,
2017-05-27 16:19:28 -06:00
struct lwtunnel_state * * ts ,
struct netlink_ext_ack * extack )
2016-11-30 17:10:10 +01:00
{
struct nlattr * tb [ LWT_BPF_MAX + 1 ] ;
struct lwtunnel_state * newts ;
struct bpf_lwt * bpf ;
int ret ;
if ( family ! = AF_INET & & family ! = AF_INET6 )
return - EAFNOSUPPORT ;
2017-05-27 16:19:28 -06:00
ret = nla_parse_nested ( tb , LWT_BPF_MAX , nla , bpf_nl_policy , extack ) ;
2016-11-30 17:10:10 +01:00
if ( ret < 0 )
return ret ;
if ( ! tb [ LWT_BPF_IN ] & & ! tb [ LWT_BPF_OUT ] & & ! tb [ LWT_BPF_XMIT ] )
return - EINVAL ;
newts = lwtunnel_state_alloc ( sizeof ( * bpf ) ) ;
if ( ! newts )
return - ENOMEM ;
newts - > type = LWTUNNEL_ENCAP_BPF ;
bpf = bpf_lwt_lwtunnel ( newts ) ;
if ( tb [ LWT_BPF_IN ] ) {
newts - > flags | = LWTUNNEL_STATE_INPUT_REDIRECT ;
ret = bpf_parse_prog ( tb [ LWT_BPF_IN ] , & bpf - > in ,
BPF_PROG_TYPE_LWT_IN ) ;
if ( ret < 0 )
goto errout ;
}
if ( tb [ LWT_BPF_OUT ] ) {
newts - > flags | = LWTUNNEL_STATE_OUTPUT_REDIRECT ;
ret = bpf_parse_prog ( tb [ LWT_BPF_OUT ] , & bpf - > out ,
BPF_PROG_TYPE_LWT_OUT ) ;
if ( ret < 0 )
goto errout ;
}
if ( tb [ LWT_BPF_XMIT ] ) {
newts - > flags | = LWTUNNEL_STATE_XMIT_REDIRECT ;
ret = bpf_parse_prog ( tb [ LWT_BPF_XMIT ] , & bpf - > xmit ,
BPF_PROG_TYPE_LWT_XMIT ) ;
if ( ret < 0 )
goto errout ;
}
if ( tb [ LWT_BPF_XMIT_HEADROOM ] ) {
u32 headroom = nla_get_u32 ( tb [ LWT_BPF_XMIT_HEADROOM ] ) ;
if ( headroom > LWT_BPF_MAX_HEADROOM ) {
ret = - ERANGE ;
goto errout ;
}
newts - > headroom = headroom ;
}
bpf - > family = family ;
* ts = newts ;
return 0 ;
errout :
bpf_destroy_state ( newts ) ;
kfree ( newts ) ;
return ret ;
}
static int bpf_fill_lwt_prog ( struct sk_buff * skb , int attr ,
struct bpf_lwt_prog * prog )
{
struct nlattr * nest ;
if ( ! prog - > prog )
return 0 ;
2019-04-26 11:13:06 +02:00
nest = nla_nest_start_noflag ( skb , attr ) ;
2016-11-30 17:10:10 +01:00
if ( ! nest )
return - EMSGSIZE ;
if ( prog - > name & &
nla_put_string ( skb , LWT_BPF_PROG_NAME , prog - > name ) )
return - EMSGSIZE ;
return nla_nest_end ( skb , nest ) ;
}
static int bpf_fill_encap_info ( struct sk_buff * skb , struct lwtunnel_state * lwt )
{
struct bpf_lwt * bpf = bpf_lwt_lwtunnel ( lwt ) ;
if ( bpf_fill_lwt_prog ( skb , LWT_BPF_IN , & bpf - > in ) < 0 | |
bpf_fill_lwt_prog ( skb , LWT_BPF_OUT , & bpf - > out ) < 0 | |
bpf_fill_lwt_prog ( skb , LWT_BPF_XMIT , & bpf - > xmit ) < 0 )
return - EMSGSIZE ;
return 0 ;
}
static int bpf_encap_nlsize ( struct lwtunnel_state * lwtstate )
{
int nest_len = nla_total_size ( sizeof ( struct nlattr ) ) +
nla_total_size ( MAX_PROG_NAME ) + /* LWT_BPF_PROG_NAME */
0 ;
return nest_len + /* LWT_BPF_IN */
nest_len + /* LWT_BPF_OUT */
nest_len + /* LWT_BPF_XMIT */
0 ;
}
2017-01-12 14:39:28 +00:00
static int bpf_lwt_prog_cmp ( struct bpf_lwt_prog * a , struct bpf_lwt_prog * b )
2016-11-30 17:10:10 +01:00
{
/* FIXME:
* The LWT state is currently rebuilt for delete requests which
* results in a new bpf_prog instance . Comparing names for now .
*/
if ( ! a - > name & & ! b - > name )
return 0 ;
if ( ! a - > name | | ! b - > name )
return 1 ;
return strcmp ( a - > name , b - > name ) ;
}
static int bpf_encap_cmp ( struct lwtunnel_state * a , struct lwtunnel_state * b )
{
struct bpf_lwt * a_bpf = bpf_lwt_lwtunnel ( a ) ;
struct bpf_lwt * b_bpf = bpf_lwt_lwtunnel ( b ) ;
return bpf_lwt_prog_cmp ( & a_bpf - > in , & b_bpf - > in ) | |
bpf_lwt_prog_cmp ( & a_bpf - > out , & b_bpf - > out ) | |
bpf_lwt_prog_cmp ( & a_bpf - > xmit , & b_bpf - > xmit ) ;
}
static const struct lwtunnel_encap_ops bpf_encap_ops = {
. build_state = bpf_build_state ,
. destroy_state = bpf_destroy_state ,
. input = bpf_input ,
. output = bpf_output ,
. xmit = bpf_xmit ,
. fill_encap = bpf_fill_encap_info ,
. get_encap_size = bpf_encap_nlsize ,
. cmp_encap = bpf_encap_cmp ,
2017-01-24 16:26:47 +00:00
. owner = THIS_MODULE ,
2016-11-30 17:10:10 +01:00
} ;
2019-02-13 11:53:37 -08:00
static int handle_gso_type ( struct sk_buff * skb , unsigned int gso_type ,
int encap_len )
{
struct skb_shared_info * shinfo = skb_shinfo ( skb ) ;
gso_type | = SKB_GSO_DODGY ;
shinfo - > gso_type | = gso_type ;
skb_decrease_gso_size ( shinfo , encap_len ) ;
shinfo - > gso_segs = 0 ;
return 0 ;
}
2019-02-13 11:53:36 -08:00
static int handle_gso_encap ( struct sk_buff * skb , bool ipv4 , int encap_len )
{
2019-02-13 11:53:37 -08:00
int next_hdr_offset ;
void * next_hdr ;
__u8 protocol ;
/* SCTP and UDP_L4 gso need more nuanced handling than what
* handle_gso_type ( ) does above : skb_decrease_gso_size ( ) is not enough .
* So at the moment only TCP GSO packets are let through .
*/
if ( ! ( skb_shinfo ( skb ) - > gso_type & ( SKB_GSO_TCPV4 | SKB_GSO_TCPV6 ) ) )
return - ENOTSUPP ;
if ( ipv4 ) {
protocol = ip_hdr ( skb ) - > protocol ;
next_hdr_offset = sizeof ( struct iphdr ) ;
next_hdr = skb_network_header ( skb ) + next_hdr_offset ;
} else {
protocol = ipv6_hdr ( skb ) - > nexthdr ;
next_hdr_offset = sizeof ( struct ipv6hdr ) ;
next_hdr = skb_network_header ( skb ) + next_hdr_offset ;
}
switch ( protocol ) {
case IPPROTO_GRE :
next_hdr_offset + = sizeof ( struct gre_base_hdr ) ;
if ( next_hdr_offset > encap_len )
return - EINVAL ;
if ( ( ( struct gre_base_hdr * ) next_hdr ) - > flags & GRE_CSUM )
return handle_gso_type ( skb , SKB_GSO_GRE_CSUM ,
encap_len ) ;
return handle_gso_type ( skb , SKB_GSO_GRE , encap_len ) ;
case IPPROTO_UDP :
next_hdr_offset + = sizeof ( struct udphdr ) ;
if ( next_hdr_offset > encap_len )
return - EINVAL ;
if ( ( ( struct udphdr * ) next_hdr ) - > check )
return handle_gso_type ( skb , SKB_GSO_UDP_TUNNEL_CSUM ,
encap_len ) ;
return handle_gso_type ( skb , SKB_GSO_UDP_TUNNEL , encap_len ) ;
case IPPROTO_IP :
case IPPROTO_IPV6 :
if ( ipv4 )
return handle_gso_type ( skb , SKB_GSO_IPXIP4 , encap_len ) ;
else
return handle_gso_type ( skb , SKB_GSO_IPXIP6 , encap_len ) ;
default :
return - EPROTONOSUPPORT ;
}
2019-02-13 11:53:36 -08:00
}
int bpf_lwt_push_ip_encap ( struct sk_buff * skb , void * hdr , u32 len , bool ingress )
{
struct iphdr * iph ;
bool ipv4 ;
int err ;
if ( unlikely ( len < sizeof ( struct iphdr ) | | len > LWT_BPF_MAX_HEADROOM ) )
return - EINVAL ;
/* validate protocol and length */
iph = ( struct iphdr * ) hdr ;
if ( iph - > version = = 4 ) {
ipv4 = true ;
if ( unlikely ( len < iph - > ihl * 4 ) )
return - EINVAL ;
} else if ( iph - > version = = 6 ) {
ipv4 = false ;
if ( unlikely ( len < sizeof ( struct ipv6hdr ) ) )
return - EINVAL ;
} else {
return - EINVAL ;
}
if ( ingress )
err = skb_cow_head ( skb , len + skb - > mac_len ) ;
else
err = skb_cow_head ( skb ,
len + LL_RESERVED_SPACE ( skb_dst ( skb ) - > dev ) ) ;
if ( unlikely ( err ) )
return err ;
/* push the encap headers and fix pointers */
skb_reset_inner_headers ( skb ) ;
2019-03-04 16:27:08 -08:00
skb_reset_inner_mac_header ( skb ) ; /* mac header is not yet set */
skb_set_inner_protocol ( skb , skb - > protocol ) ;
2019-02-13 11:53:36 -08:00
skb - > encapsulation = 1 ;
skb_push ( skb , len ) ;
if ( ingress )
skb_postpush_rcsum ( skb , iph , len ) ;
skb_reset_network_header ( skb ) ;
memcpy ( skb_network_header ( skb ) , hdr , len ) ;
bpf_compute_data_pointers ( skb ) ;
skb_clear_hash ( skb ) ;
if ( ipv4 ) {
skb - > protocol = htons ( ETH_P_IP ) ;
iph = ip_hdr ( skb ) ;
if ( ! iph - > check )
iph - > check = ip_fast_csum ( ( unsigned char * ) iph ,
iph - > ihl ) ;
} else {
skb - > protocol = htons ( ETH_P_IPV6 ) ;
}
if ( skb_is_gso ( skb ) )
return handle_gso_encap ( skb , ipv4 , len ) ;
return 0 ;
}
2016-11-30 17:10:10 +01:00
static int __init bpf_lwt_init ( void )
{
return lwtunnel_encap_add_ops ( & bpf_encap_ops , LWTUNNEL_ENCAP_BPF ) ;
}
subsys_initcall ( bpf_lwt_init )