2015-07-21 10:43:46 +02:00
/*
* lwtunnel Infrastructure for light weight tunnels like mpls
*
* Authors : Roopa Prabhu , < roopa @ cumulusnetworks . com >
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*
*/
# include <linux/capability.h>
# include <linux/module.h>
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/uaccess.h>
# include <linux/skbuff.h>
# include <linux/netdevice.h>
# include <linux/lwtunnel.h>
# include <linux/in.h>
# include <linux/init.h>
# include <linux/err.h>
# include <net/lwtunnel.h>
# include <net/rtnetlink.h>
2015-07-21 10:43:49 +02:00
# include <net/ip6_fib.h>
2015-07-21 10:43:46 +02:00
struct lwtunnel_state * lwtunnel_state_alloc ( int encap_len )
{
struct lwtunnel_state * lws ;
lws = kzalloc ( sizeof ( * lws ) + encap_len , GFP_ATOMIC ) ;
return lws ;
}
EXPORT_SYMBOL ( lwtunnel_state_alloc ) ;
2015-07-29 09:45:40 +02:00
static const struct lwtunnel_encap_ops __rcu *
2015-07-21 10:43:46 +02:00
lwtun_encaps [ LWTUNNEL_ENCAP_MAX + 1 ] __read_mostly ;
int lwtunnel_encap_add_ops ( const struct lwtunnel_encap_ops * ops ,
unsigned int num )
{
if ( num > LWTUNNEL_ENCAP_MAX )
return - ERANGE ;
return ! cmpxchg ( ( const struct lwtunnel_encap_ops * * )
& lwtun_encaps [ num ] ,
NULL , ops ) ? 0 : - 1 ;
}
EXPORT_SYMBOL ( lwtunnel_encap_add_ops ) ;
int lwtunnel_encap_del_ops ( const struct lwtunnel_encap_ops * ops ,
unsigned int encap_type )
{
int ret ;
if ( encap_type = = LWTUNNEL_ENCAP_NONE | |
encap_type > LWTUNNEL_ENCAP_MAX )
return - ERANGE ;
ret = ( cmpxchg ( ( const struct lwtunnel_encap_ops * * )
& lwtun_encaps [ encap_type ] ,
ops , NULL ) = = ops ) ? 0 : - 1 ;
synchronize_net ( ) ;
return ret ;
}
EXPORT_SYMBOL ( lwtunnel_encap_del_ops ) ;
int lwtunnel_build_state ( struct net_device * dev , u16 encap_type ,
2015-08-24 09:45:41 -07:00
struct nlattr * encap , unsigned int family ,
const void * cfg , struct lwtunnel_state * * lws )
2015-07-21 10:43:46 +02:00
{
const struct lwtunnel_encap_ops * ops ;
int ret = - EINVAL ;
if ( encap_type = = LWTUNNEL_ENCAP_NONE | |
encap_type > LWTUNNEL_ENCAP_MAX )
return ret ;
ret = - EOPNOTSUPP ;
rcu_read_lock ( ) ;
ops = rcu_dereference ( lwtun_encaps [ encap_type ] ) ;
if ( likely ( ops & & ops - > build_state ) )
2015-08-24 09:45:41 -07:00
ret = ops - > build_state ( dev , encap , family , cfg , lws ) ;
2015-07-21 10:43:46 +02:00
rcu_read_unlock ( ) ;
return ret ;
}
EXPORT_SYMBOL ( lwtunnel_build_state ) ;
int lwtunnel_fill_encap ( struct sk_buff * skb , struct lwtunnel_state * lwtstate )
{
const struct lwtunnel_encap_ops * ops ;
struct nlattr * nest ;
int ret = - EINVAL ;
if ( ! lwtstate )
return 0 ;
if ( lwtstate - > type = = LWTUNNEL_ENCAP_NONE | |
lwtstate - > type > LWTUNNEL_ENCAP_MAX )
return 0 ;
ret = - EOPNOTSUPP ;
nest = nla_nest_start ( skb , RTA_ENCAP ) ;
rcu_read_lock ( ) ;
ops = rcu_dereference ( lwtun_encaps [ lwtstate - > type ] ) ;
if ( likely ( ops & & ops - > fill_encap ) )
ret = ops - > fill_encap ( skb , lwtstate ) ;
rcu_read_unlock ( ) ;
if ( ret )
goto nla_put_failure ;
nla_nest_end ( skb , nest ) ;
ret = nla_put_u16 ( skb , RTA_ENCAP_TYPE , lwtstate - > type ) ;
if ( ret )
goto nla_put_failure ;
return 0 ;
nla_put_failure :
nla_nest_cancel ( skb , nest ) ;
return ( ret = = - EOPNOTSUPP ? 0 : ret ) ;
}
EXPORT_SYMBOL ( lwtunnel_fill_encap ) ;
int lwtunnel_get_encap_size ( struct lwtunnel_state * lwtstate )
{
const struct lwtunnel_encap_ops * ops ;
int ret = 0 ;
if ( ! lwtstate )
return 0 ;
if ( lwtstate - > type = = LWTUNNEL_ENCAP_NONE | |
lwtstate - > type > LWTUNNEL_ENCAP_MAX )
return 0 ;
rcu_read_lock ( ) ;
ops = rcu_dereference ( lwtun_encaps [ lwtstate - > type ] ) ;
if ( likely ( ops & & ops - > get_encap_size ) )
ret = nla_total_size ( ops - > get_encap_size ( lwtstate ) ) ;
rcu_read_unlock ( ) ;
return ret ;
}
EXPORT_SYMBOL ( lwtunnel_get_encap_size ) ;
int lwtunnel_cmp_encap ( struct lwtunnel_state * a , struct lwtunnel_state * b )
{
const struct lwtunnel_encap_ops * ops ;
int ret = 0 ;
if ( ! a & & ! b )
return 0 ;
if ( ! a | | ! b )
return 1 ;
if ( a - > type ! = b - > type )
return 1 ;
if ( a - > type = = LWTUNNEL_ENCAP_NONE | |
a - > type > LWTUNNEL_ENCAP_MAX )
return 0 ;
rcu_read_lock ( ) ;
ops = rcu_dereference ( lwtun_encaps [ a - > type ] ) ;
if ( likely ( ops & & ops - > cmp_encap ) )
ret = ops - > cmp_encap ( a , b ) ;
rcu_read_unlock ( ) ;
return ret ;
}
EXPORT_SYMBOL ( lwtunnel_cmp_encap ) ;
2015-07-21 10:43:49 +02:00
2015-10-07 16:48:47 -05:00
int lwtunnel_output ( struct net * net , struct sock * sk , struct sk_buff * skb )
2015-07-21 10:43:49 +02:00
{
2015-08-20 13:56:25 +02:00
struct dst_entry * dst = skb_dst ( skb ) ;
2015-07-21 10:43:49 +02:00
const struct lwtunnel_encap_ops * ops ;
2015-08-20 13:56:25 +02:00
struct lwtunnel_state * lwtstate ;
2015-07-21 10:43:49 +02:00
int ret = - EINVAL ;
2015-08-20 13:56:25 +02:00
if ( ! dst )
2015-07-21 10:43:49 +02:00
goto drop ;
2015-08-20 13:56:25 +02:00
lwtstate = dst - > lwtstate ;
2015-07-21 10:43:49 +02:00
if ( lwtstate - > type = = LWTUNNEL_ENCAP_NONE | |
lwtstate - > type > LWTUNNEL_ENCAP_MAX )
return 0 ;
ret = - EOPNOTSUPP ;
rcu_read_lock ( ) ;
ops = rcu_dereference ( lwtun_encaps [ lwtstate - > type ] ) ;
if ( likely ( ops & & ops - > output ) )
2015-10-07 16:48:47 -05:00
ret = ops - > output ( net , sk , skb ) ;
2015-07-21 10:43:49 +02:00
rcu_read_unlock ( ) ;
if ( ret = = - EOPNOTSUPP )
goto drop ;
return ret ;
drop :
2015-07-27 11:07:47 +03:00
kfree_skb ( skb ) ;
2015-07-21 10:43:49 +02:00
return ret ;
}
EXPORT_SYMBOL ( lwtunnel_output ) ;
2015-08-17 13:42:24 -07:00
2015-08-20 13:56:25 +02:00
int lwtunnel_input ( struct sk_buff * skb )
2015-08-17 13:42:24 -07:00
{
2015-08-20 13:56:25 +02:00
struct dst_entry * dst = skb_dst ( skb ) ;
2015-08-17 13:42:24 -07:00
const struct lwtunnel_encap_ops * ops ;
2015-08-20 13:56:25 +02:00
struct lwtunnel_state * lwtstate ;
2015-08-17 13:42:24 -07:00
int ret = - EINVAL ;
2015-08-20 13:56:25 +02:00
if ( ! dst )
2015-08-17 13:42:24 -07:00
goto drop ;
2015-08-20 13:56:25 +02:00
lwtstate = dst - > lwtstate ;
2015-08-17 13:42:24 -07:00
if ( lwtstate - > type = = LWTUNNEL_ENCAP_NONE | |
lwtstate - > type > LWTUNNEL_ENCAP_MAX )
return 0 ;
ret = - EOPNOTSUPP ;
rcu_read_lock ( ) ;
ops = rcu_dereference ( lwtun_encaps [ lwtstate - > type ] ) ;
if ( likely ( ops & & ops - > input ) )
ret = ops - > input ( skb ) ;
rcu_read_unlock ( ) ;
if ( ret = = - EOPNOTSUPP )
goto drop ;
return ret ;
drop :
kfree_skb ( skb ) ;
return ret ;
}
EXPORT_SYMBOL ( lwtunnel_input ) ;