2016-02-27 16:08:54 +03:00
/*
* net / sched / ife . c Inter - FE action based on ForCES WG InterFE LFB
*
* Refer to :
* draft - ietf - forces - interfelfb - 03
* and
* netdev01 paper :
* " Distributing Linux Traffic Control Classifier-Action
* Subsystem "
* Authors : Jamal Hadi Salim and Damascene M . Joachimpillai
*
* 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 .
*
* copyright Jamal Hadi Salim ( 2015 )
*
*/
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/string.h>
# include <linux/errno.h>
# include <linux/skbuff.h>
# include <linux/rtnetlink.h>
# include <linux/module.h>
# include <linux/init.h>
# include <net/net_namespace.h>
# include <net/netlink.h>
# include <net/pkt_sched.h>
# include <uapi/linux/tc_act/tc_ife.h>
# include <net/tc_act/tc_ife.h>
# include <linux/etherdevice.h>
2017-02-01 16:30:03 +03:00
# include <net/ife.h>
2016-02-27 16:08:54 +03:00
netns: make struct pernet_operations::id unsigned int
Make struct pernet_operations::id unsigned.
There are 2 reasons to do so:
1)
This field is really an index into an zero based array and
thus is unsigned entity. Using negative value is out-of-bound
access by definition.
2)
On x86_64 unsigned 32-bit data which are mixed with pointers
via array indexing or offsets added or subtracted to pointers
are preffered to signed 32-bit data.
"int" being used as an array index needs to be sign-extended
to 64-bit before being used.
void f(long *p, int i)
{
g(p[i]);
}
roughly translates to
movsx rsi, esi
mov rdi, [rsi+...]
call g
MOVSX is 3 byte instruction which isn't necessary if the variable is
unsigned because x86_64 is zero extending by default.
Now, there is net_generic() function which, you guessed it right, uses
"int" as an array index:
static inline void *net_generic(const struct net *net, int id)
{
...
ptr = ng->ptr[id - 1];
...
}
And this function is used a lot, so those sign extensions add up.
Patch snipes ~1730 bytes on allyesconfig kernel (without all junk
messing with code generation):
add/remove: 0/0 grow/shrink: 70/598 up/down: 396/-2126 (-1730)
Unfortunately some functions actually grow bigger.
This is a semmingly random artefact of code generation with register
allocator being used differently. gcc decides that some variable
needs to live in new r8+ registers and every access now requires REX
prefix. Or it is shifted into r12, so [r12+0] addressing mode has to be
used which is longer than [r8]
However, overall balance is in negative direction:
add/remove: 0/0 grow/shrink: 70/598 up/down: 396/-2126 (-1730)
function old new delta
nfsd4_lock 3886 3959 +73
tipc_link_build_proto_msg 1096 1140 +44
mac80211_hwsim_new_radio 2776 2808 +32
tipc_mon_rcv 1032 1058 +26
svcauth_gss_legacy_init 1413 1429 +16
tipc_bcbase_select_primary 379 392 +13
nfsd4_exchange_id 1247 1260 +13
nfsd4_setclientid_confirm 782 793 +11
...
put_client_renew_locked 494 480 -14
ip_set_sockfn_get 730 716 -14
geneve_sock_add 829 813 -16
nfsd4_sequence_done 721 703 -18
nlmclnt_lookup_host 708 686 -22
nfsd4_lockt 1085 1063 -22
nfs_get_client 1077 1050 -27
tcf_bpf_init 1106 1076 -30
nfsd4_encode_fattr 5997 5930 -67
Total: Before=154856051, After=154854321, chg -0.00%
Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-11-17 04:58:21 +03:00
static unsigned int ife_net_id ;
2016-02-27 16:08:54 +03:00
static int max_metacnt = IFE_META_MAX + 1 ;
2016-07-26 02:09:41 +03:00
static struct tc_action_ops act_ife_ops ;
2016-02-27 16:08:54 +03:00
static const struct nla_policy ife_policy [ TCA_IFE_MAX + 1 ] = {
[ TCA_IFE_PARMS ] = { . len = sizeof ( struct tc_ife ) } ,
[ TCA_IFE_DMAC ] = { . len = ETH_ALEN } ,
[ TCA_IFE_SMAC ] = { . len = ETH_ALEN } ,
[ TCA_IFE_TYPE ] = { . type = NLA_U16 } ,
} ;
2016-09-18 14:31:42 +03:00
int ife_encode_meta_u16 ( u16 metaval , void * skbdata , struct tcf_meta_info * mi )
{
u16 edata = 0 ;
if ( mi - > metaval )
edata = * ( u16 * ) mi - > metaval ;
else if ( metaval )
edata = metaval ;
if ( ! edata ) /* will not encode */
return 0 ;
edata = htons ( edata ) ;
return ife_tlv_meta_encode ( skbdata , mi - > metaid , 2 , & edata ) ;
}
EXPORT_SYMBOL_GPL ( ife_encode_meta_u16 ) ;
2016-02-27 16:08:54 +03:00
int ife_get_meta_u32 ( struct sk_buff * skb , struct tcf_meta_info * mi )
{
if ( mi - > metaval )
return nla_put_u32 ( skb , mi - > metaid , * ( u32 * ) mi - > metaval ) ;
else
return nla_put ( skb , mi - > metaid , 0 , NULL ) ;
}
EXPORT_SYMBOL_GPL ( ife_get_meta_u32 ) ;
int ife_check_meta_u32 ( u32 metaval , struct tcf_meta_info * mi )
{
if ( metaval | | mi - > metaval )
return 8 ; /* T+L+V == 2+2+4 */
return 0 ;
}
EXPORT_SYMBOL_GPL ( ife_check_meta_u32 ) ;
2016-09-18 14:31:42 +03:00
int ife_check_meta_u16 ( u16 metaval , struct tcf_meta_info * mi )
{
if ( metaval | | mi - > metaval )
return 8 ; /* T+L+(V) == 2+2+(2+2bytepad) */
return 0 ;
}
EXPORT_SYMBOL_GPL ( ife_check_meta_u16 ) ;
2016-02-27 16:08:54 +03:00
int ife_encode_meta_u32 ( u32 metaval , void * skbdata , struct tcf_meta_info * mi )
{
u32 edata = metaval ;
if ( mi - > metaval )
edata = * ( u32 * ) mi - > metaval ;
else if ( metaval )
edata = metaval ;
if ( ! edata ) /* will not encode */
return 0 ;
edata = htonl ( edata ) ;
return ife_tlv_meta_encode ( skbdata , mi - > metaid , 4 , & edata ) ;
}
EXPORT_SYMBOL_GPL ( ife_encode_meta_u32 ) ;
int ife_get_meta_u16 ( struct sk_buff * skb , struct tcf_meta_info * mi )
{
if ( mi - > metaval )
return nla_put_u16 ( skb , mi - > metaid , * ( u16 * ) mi - > metaval ) ;
else
return nla_put ( skb , mi - > metaid , 0 , NULL ) ;
}
EXPORT_SYMBOL_GPL ( ife_get_meta_u16 ) ;
2016-06-20 23:37:18 +03:00
int ife_alloc_meta_u32 ( struct tcf_meta_info * mi , void * metaval , gfp_t gfp )
2016-02-27 16:08:54 +03:00
{
2016-06-20 23:37:18 +03:00
mi - > metaval = kmemdup ( metaval , sizeof ( u32 ) , gfp ) ;
2016-02-27 16:08:54 +03:00
if ( ! mi - > metaval )
return - ENOMEM ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( ife_alloc_meta_u32 ) ;
2016-06-20 23:37:18 +03:00
int ife_alloc_meta_u16 ( struct tcf_meta_info * mi , void * metaval , gfp_t gfp )
2016-02-27 16:08:54 +03:00
{
2016-06-20 23:37:18 +03:00
mi - > metaval = kmemdup ( metaval , sizeof ( u16 ) , gfp ) ;
2016-02-27 16:08:54 +03:00
if ( ! mi - > metaval )
return - ENOMEM ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( ife_alloc_meta_u16 ) ;
void ife_release_meta_gen ( struct tcf_meta_info * mi )
{
kfree ( mi - > metaval ) ;
}
EXPORT_SYMBOL_GPL ( ife_release_meta_gen ) ;
int ife_validate_meta_u32 ( void * val , int len )
{
2016-08-22 14:10:20 +03:00
if ( len = = sizeof ( u32 ) )
2016-02-27 16:08:54 +03:00
return 0 ;
return - EINVAL ;
}
EXPORT_SYMBOL_GPL ( ife_validate_meta_u32 ) ;
int ife_validate_meta_u16 ( void * val , int len )
{
2016-08-22 14:10:20 +03:00
/* length will not include padding */
if ( len = = sizeof ( u16 ) )
2016-02-27 16:08:54 +03:00
return 0 ;
return - EINVAL ;
}
EXPORT_SYMBOL_GPL ( ife_validate_meta_u16 ) ;
static LIST_HEAD ( ifeoplist ) ;
static DEFINE_RWLOCK ( ife_mod_lock ) ;
static struct tcf_meta_ops * find_ife_oplist ( u16 metaid )
{
struct tcf_meta_ops * o ;
read_lock ( & ife_mod_lock ) ;
list_for_each_entry ( o , & ifeoplist , list ) {
if ( o - > metaid = = metaid ) {
if ( ! try_module_get ( o - > owner ) )
o = NULL ;
read_unlock ( & ife_mod_lock ) ;
return o ;
}
}
read_unlock ( & ife_mod_lock ) ;
return NULL ;
}
int register_ife_op ( struct tcf_meta_ops * mops )
{
struct tcf_meta_ops * m ;
if ( ! mops - > metaid | | ! mops - > metatype | | ! mops - > name | |
! mops - > check_presence | | ! mops - > encode | | ! mops - > decode | |
! mops - > get | | ! mops - > alloc )
return - EINVAL ;
write_lock ( & ife_mod_lock ) ;
list_for_each_entry ( m , & ifeoplist , list ) {
if ( m - > metaid = = mops - > metaid | |
( strcmp ( mops - > name , m - > name ) = = 0 ) ) {
write_unlock ( & ife_mod_lock ) ;
return - EEXIST ;
}
}
if ( ! mops - > release )
mops - > release = ife_release_meta_gen ;
list_add_tail ( & mops - > list , & ifeoplist ) ;
write_unlock ( & ife_mod_lock ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( unregister_ife_op ) ;
int unregister_ife_op ( struct tcf_meta_ops * mops )
{
struct tcf_meta_ops * m ;
int err = - ENOENT ;
write_lock ( & ife_mod_lock ) ;
list_for_each_entry ( m , & ifeoplist , list ) {
if ( m - > metaid = = mops - > metaid ) {
list_del ( & mops - > list ) ;
err = 0 ;
break ;
}
}
write_unlock ( & ife_mod_lock ) ;
return err ;
}
EXPORT_SYMBOL_GPL ( register_ife_op ) ;
static int ife_validate_metatype ( struct tcf_meta_ops * ops , void * val , int len )
{
int ret = 0 ;
/* XXX: unfortunately cant use nla_policy at this point
* because a length of 0 is valid in the case of
* " allow " . " use " semantics do enforce for proper
* length and i couldve use nla_policy but it makes it hard
* to use it just for that . .
*/
if ( ops - > validate )
return ops - > validate ( val , len ) ;
if ( ops - > metatype = = NLA_U32 )
ret = ife_validate_meta_u32 ( val , len ) ;
else if ( ops - > metatype = = NLA_U16 )
ret = ife_validate_meta_u16 ( val , len ) ;
return ret ;
}
/* called when adding new meta information
2016-06-20 23:37:18 +03:00
* under ife - > tcf_lock for existing action
2016-02-27 16:08:54 +03:00
*/
static int load_metaops_and_vet ( struct tcf_ife_info * ife , u32 metaid ,
2016-06-20 23:37:18 +03:00
void * val , int len , bool exists )
2016-02-27 16:08:54 +03:00
{
struct tcf_meta_ops * ops = find_ife_oplist ( metaid ) ;
int ret = 0 ;
if ( ! ops ) {
ret = - ENOENT ;
# ifdef CONFIG_MODULES
2016-06-20 23:37:18 +03:00
if ( exists )
spin_unlock_bh ( & ife - > tcf_lock ) ;
2016-02-27 16:08:54 +03:00
rtnl_unlock ( ) ;
request_module ( " ifemeta%u " , metaid ) ;
rtnl_lock ( ) ;
2016-06-20 23:37:18 +03:00
if ( exists )
spin_lock_bh ( & ife - > tcf_lock ) ;
2016-02-27 16:08:54 +03:00
ops = find_ife_oplist ( metaid ) ;
# endif
}
if ( ops ) {
ret = 0 ;
if ( len )
ret = ife_validate_metatype ( ops , val , len ) ;
module_put ( ops - > owner ) ;
}
return ret ;
}
/* called when adding new meta information
2016-06-20 23:37:18 +03:00
* under ife - > tcf_lock for existing action
2016-02-27 16:08:54 +03:00
*/
static int add_metainfo ( struct tcf_ife_info * ife , u32 metaid , void * metaval ,
2016-06-20 23:37:19 +03:00
int len , bool atomic )
2016-02-27 16:08:54 +03:00
{
struct tcf_meta_info * mi = NULL ;
struct tcf_meta_ops * ops = find_ife_oplist ( metaid ) ;
int ret = 0 ;
if ( ! ops )
return - ENOENT ;
2016-06-20 23:37:19 +03:00
mi = kzalloc ( sizeof ( * mi ) , atomic ? GFP_ATOMIC : GFP_KERNEL ) ;
2016-02-27 16:08:54 +03:00
if ( ! mi ) {
/*put back what find_ife_oplist took */
module_put ( ops - > owner ) ;
return - ENOMEM ;
}
mi - > metaid = metaid ;
mi - > ops = ops ;
if ( len > 0 ) {
2016-06-20 23:37:19 +03:00
ret = ops - > alloc ( mi , metaval , atomic ? GFP_ATOMIC : GFP_KERNEL ) ;
2016-02-27 16:08:54 +03:00
if ( ret ! = 0 ) {
kfree ( mi ) ;
module_put ( ops - > owner ) ;
return ret ;
}
}
list_add_tail ( & mi - > metalist , & ife - > metalist ) ;
return ret ;
}
static int use_all_metadata ( struct tcf_ife_info * ife )
{
struct tcf_meta_ops * o ;
int rc = 0 ;
int installed = 0 ;
2016-06-20 23:37:19 +03:00
read_lock ( & ife_mod_lock ) ;
2016-02-27 16:08:54 +03:00
list_for_each_entry ( o , & ifeoplist , list ) {
2016-06-20 23:37:19 +03:00
rc = add_metainfo ( ife , o - > metaid , NULL , 0 , true ) ;
2016-02-27 16:08:54 +03:00
if ( rc = = 0 )
installed + = 1 ;
}
2016-06-20 23:37:19 +03:00
read_unlock ( & ife_mod_lock ) ;
2016-02-27 16:08:54 +03:00
if ( installed )
return 0 ;
else
return - EINVAL ;
}
static int dump_metalist ( struct sk_buff * skb , struct tcf_ife_info * ife )
{
struct tcf_meta_info * e ;
struct nlattr * nest ;
unsigned char * b = skb_tail_pointer ( skb ) ;
int total_encoded = 0 ;
/*can only happen on decode */
if ( list_empty ( & ife - > metalist ) )
return 0 ;
nest = nla_nest_start ( skb , TCA_IFE_METALST ) ;
if ( ! nest )
goto out_nlmsg_trim ;
list_for_each_entry ( e , & ife - > metalist , metalist ) {
if ( ! e - > ops - > get ( skb , e ) )
total_encoded + = 1 ;
}
if ( ! total_encoded )
goto out_nlmsg_trim ;
nla_nest_end ( skb , nest ) ;
return 0 ;
out_nlmsg_trim :
nlmsg_trim ( skb , b ) ;
return - 1 ;
}
/* under ife->tcf_lock */
static void _tcf_ife_cleanup ( struct tc_action * a , int bind )
{
2016-07-26 02:09:41 +03:00
struct tcf_ife_info * ife = to_ife ( a ) ;
2016-02-27 16:08:54 +03:00
struct tcf_meta_info * e , * n ;
list_for_each_entry_safe ( e , n , & ife - > metalist , metalist ) {
module_put ( e - > ops - > owner ) ;
list_del ( & e - > metalist ) ;
if ( e - > metaval ) {
if ( e - > ops - > release )
e - > ops - > release ( e ) ;
else
kfree ( e - > metaval ) ;
}
kfree ( e ) ;
}
}
static void tcf_ife_cleanup ( struct tc_action * a , int bind )
{
2016-07-26 02:09:41 +03:00
struct tcf_ife_info * ife = to_ife ( a ) ;
2016-02-27 16:08:54 +03:00
spin_lock_bh ( & ife - > tcf_lock ) ;
_tcf_ife_cleanup ( a , bind ) ;
spin_unlock_bh ( & ife - > tcf_lock ) ;
}
2016-06-20 23:37:18 +03:00
/* under ife->tcf_lock for existing action */
static int populate_metalist ( struct tcf_ife_info * ife , struct nlattr * * tb ,
bool exists )
2016-02-27 16:08:54 +03:00
{
int len = 0 ;
int rc = 0 ;
int i = 0 ;
void * val ;
for ( i = 1 ; i < max_metacnt ; i + + ) {
if ( tb [ i ] ) {
val = nla_data ( tb [ i ] ) ;
len = nla_len ( tb [ i ] ) ;
2016-06-20 23:37:18 +03:00
rc = load_metaops_and_vet ( ife , i , val , len , exists ) ;
2016-02-27 16:08:54 +03:00
if ( rc ! = 0 )
return rc ;
2016-06-20 23:37:18 +03:00
rc = add_metainfo ( ife , i , val , len , exists ) ;
2016-02-27 16:08:54 +03:00
if ( rc )
return rc ;
}
}
return rc ;
}
static int tcf_ife_init ( struct net * net , struct nlattr * nla ,
2016-07-26 02:09:41 +03:00
struct nlattr * est , struct tc_action * * a ,
2016-02-27 16:08:54 +03:00
int ovr , int bind )
{
struct tc_action_net * tn = net_generic ( net , ife_net_id ) ;
struct nlattr * tb [ TCA_IFE_MAX + 1 ] ;
struct nlattr * tb2 [ IFE_META_MAX + 1 ] ;
struct tcf_ife_info * ife ;
2017-08-28 22:03:14 +03:00
u16 ife_type = ETH_P_IFE ;
2016-02-27 16:08:54 +03:00
struct tc_ife * parm ;
u8 * daddr = NULL ;
u8 * saddr = NULL ;
2016-06-13 23:46:28 +03:00
bool exists = false ;
int ret = 0 ;
2016-02-27 16:08:54 +03:00
int err ;
2017-04-12 15:34:07 +03:00
err = nla_parse_nested ( tb , TCA_IFE_MAX , nla , ife_policy , NULL ) ;
2016-02-27 16:08:54 +03:00
if ( err < 0 )
return err ;
if ( ! tb [ TCA_IFE_PARMS ] )
return - EINVAL ;
parm = nla_data ( tb [ TCA_IFE_PARMS ] ) ;
2017-08-30 09:31:59 +03:00
exists = tcf_idr_check ( tn , parm - > index , a , bind ) ;
2016-05-10 23:49:31 +03:00
if ( exists & & bind )
return 0 ;
if ( ! exists ) {
2017-08-30 09:31:59 +03:00
ret = tcf_idr_create ( tn , parm - > index , est , a , & act_ife_ops ,
bind , false ) ;
2016-02-27 16:08:54 +03:00
if ( ret )
return ret ;
ret = ACT_P_CREATED ;
} else {
2017-08-30 09:31:59 +03:00
tcf_idr_release ( * a , bind ) ;
2016-02-27 16:08:54 +03:00
if ( ! ovr )
return - EEXIST ;
}
2016-07-26 02:09:41 +03:00
ife = to_ife ( * a ) ;
2016-02-27 16:08:54 +03:00
ife - > flags = parm - > flags ;
if ( parm - > flags & IFE_ENCODE ) {
2017-08-28 22:03:14 +03:00
if ( tb [ TCA_IFE_TYPE ] )
ife_type = nla_get_u16 ( tb [ TCA_IFE_TYPE ] ) ;
2016-02-27 16:08:54 +03:00
if ( tb [ TCA_IFE_DMAC ] )
daddr = nla_data ( tb [ TCA_IFE_DMAC ] ) ;
if ( tb [ TCA_IFE_SMAC ] )
saddr = nla_data ( tb [ TCA_IFE_SMAC ] ) ;
}
2016-06-20 23:37:18 +03:00
if ( exists )
spin_lock_bh ( & ife - > tcf_lock ) ;
2016-02-27 16:08:54 +03:00
ife - > tcf_action = parm - > action ;
if ( parm - > flags & IFE_ENCODE ) {
if ( daddr )
ether_addr_copy ( ife - > eth_dst , daddr ) ;
else
eth_zero_addr ( ife - > eth_dst ) ;
if ( saddr )
ether_addr_copy ( ife - > eth_src , saddr ) ;
else
eth_zero_addr ( ife - > eth_src ) ;
ife - > eth_type = ife_type ;
}
if ( ret = = ACT_P_CREATED )
INIT_LIST_HEAD ( & ife - > metalist ) ;
if ( tb [ TCA_IFE_METALST ] ) {
err = nla_parse_nested ( tb2 , IFE_META_MAX , tb [ TCA_IFE_METALST ] ,
2017-04-12 15:34:07 +03:00
NULL , NULL ) ;
2016-02-27 16:08:54 +03:00
if ( err ) {
metadata_parse_err :
2016-05-10 23:49:31 +03:00
if ( exists )
2017-08-30 09:31:59 +03:00
tcf_idr_release ( * a , bind ) ;
2016-02-27 16:08:54 +03:00
if ( ret = = ACT_P_CREATED )
2016-07-26 02:09:41 +03:00
_tcf_ife_cleanup ( * a , bind ) ;
2016-02-27 16:08:54 +03:00
2016-06-20 23:37:18 +03:00
if ( exists )
spin_unlock_bh ( & ife - > tcf_lock ) ;
2016-02-27 16:08:54 +03:00
return err ;
}
2016-06-20 23:37:18 +03:00
err = populate_metalist ( ife , tb2 , exists ) ;
2016-02-27 16:08:54 +03:00
if ( err )
goto metadata_parse_err ;
} else {
/* if no passed metadata allow list or passed allow-all
* then here we process by adding as many supported metadatum
* as we can . You better have at least one else we are
* going to bail out
*/
err = use_all_metadata ( ife ) ;
if ( err ) {
if ( ret = = ACT_P_CREATED )
2016-07-26 02:09:41 +03:00
_tcf_ife_cleanup ( * a , bind ) ;
2016-02-27 16:08:54 +03:00
2016-06-20 23:37:18 +03:00
if ( exists )
spin_unlock_bh ( & ife - > tcf_lock ) ;
2016-02-27 16:08:54 +03:00
return err ;
}
}
2016-06-20 23:37:18 +03:00
if ( exists )
spin_unlock_bh ( & ife - > tcf_lock ) ;
2016-02-27 16:08:54 +03:00
if ( ret = = ACT_P_CREATED )
2017-08-30 09:31:59 +03:00
tcf_idr_insert ( tn , * a ) ;
2016-02-27 16:08:54 +03:00
return ret ;
}
static int tcf_ife_dump ( struct sk_buff * skb , struct tc_action * a , int bind ,
int ref )
{
unsigned char * b = skb_tail_pointer ( skb ) ;
2016-07-26 02:09:41 +03:00
struct tcf_ife_info * ife = to_ife ( a ) ;
2016-02-27 16:08:54 +03:00
struct tc_ife opt = {
. index = ife - > tcf_index ,
. refcnt = ife - > tcf_refcnt - ref ,
. bindcnt = ife - > tcf_bindcnt - bind ,
. action = ife - > tcf_action ,
. flags = ife - > flags ,
} ;
struct tcf_t t ;
if ( nla_put ( skb , TCA_IFE_PARMS , sizeof ( opt ) , & opt ) )
goto nla_put_failure ;
2016-06-06 13:32:55 +03:00
tcf_tm_dump ( & t , & ife - > tcf_tm ) ;
2016-04-26 11:06:18 +03:00
if ( nla_put_64bit ( skb , TCA_IFE_TM , sizeof ( t ) , & t , TCA_IFE_PAD ) )
2016-02-27 16:08:54 +03:00
goto nla_put_failure ;
if ( ! is_zero_ether_addr ( ife - > eth_dst ) ) {
if ( nla_put ( skb , TCA_IFE_DMAC , ETH_ALEN , ife - > eth_dst ) )
goto nla_put_failure ;
}
if ( ! is_zero_ether_addr ( ife - > eth_src ) ) {
if ( nla_put ( skb , TCA_IFE_SMAC , ETH_ALEN , ife - > eth_src ) )
goto nla_put_failure ;
}
if ( nla_put ( skb , TCA_IFE_TYPE , 2 , & ife - > eth_type ) )
goto nla_put_failure ;
if ( dump_metalist ( skb , ife ) ) {
/*ignore failure to dump metalist */
pr_info ( " Failed to dump metalist \n " ) ;
}
return skb - > len ;
nla_put_failure :
nlmsg_trim ( skb , b ) ;
return - 1 ;
}
2017-03-16 13:53:41 +03:00
static int find_decode_metaid ( struct sk_buff * skb , struct tcf_ife_info * ife ,
u16 metaid , u16 mlen , void * mdata )
2016-02-27 16:08:54 +03:00
{
struct tcf_meta_info * e ;
/* XXX: use hash to speed up */
list_for_each_entry ( e , & ife - > metalist , metalist ) {
if ( metaid = = e - > metaid ) {
if ( e - > ops ) {
/* We check for decode presence already */
return e - > ops - > decode ( skb , mdata , mlen ) ;
}
}
}
return 0 ;
}
static int tcf_ife_decode ( struct sk_buff * skb , const struct tc_action * a ,
struct tcf_result * res )
{
2016-07-26 02:09:41 +03:00
struct tcf_ife_info * ife = to_ife ( a ) ;
2016-02-27 16:08:54 +03:00
int action = ife - > tcf_action ;
2017-02-01 16:30:03 +03:00
u8 * ifehdr_end ;
u8 * tlv_data ;
u16 metalen ;
2016-02-27 16:08:54 +03:00
spin_lock ( & ife - > tcf_lock ) ;
bstats_update ( & ife - > tcf_bstats , skb ) ;
2016-06-06 13:32:53 +03:00
tcf_lastuse_update ( & ife - > tcf_tm ) ;
2016-02-27 16:08:54 +03:00
spin_unlock ( & ife - > tcf_lock ) ;
2017-02-01 16:30:03 +03:00
if ( skb_at_tc_ingress ( skb ) )
skb_push ( skb , skb - > dev - > hard_header_len ) ;
tlv_data = ife_decode ( skb , & metalen ) ;
if ( unlikely ( ! tlv_data ) ) {
2016-02-27 16:08:54 +03:00
spin_lock ( & ife - > tcf_lock ) ;
ife - > tcf_qstats . drops + + ;
spin_unlock ( & ife - > tcf_lock ) ;
return TC_ACT_SHOT ;
}
2017-02-01 16:30:03 +03:00
ifehdr_end = tlv_data + metalen ;
for ( ; tlv_data < ifehdr_end ; tlv_data = ife_tlv_meta_next ( tlv_data ) ) {
u8 * curr_data ;
u16 mtype ;
u16 dlen ;
2016-02-27 16:08:54 +03:00
2017-02-01 16:30:03 +03:00
curr_data = ife_tlv_meta_decode ( tlv_data , & mtype , & dlen , NULL ) ;
2016-02-27 16:08:54 +03:00
2017-02-01 16:30:03 +03:00
if ( find_decode_metaid ( skb , ife , mtype , dlen , curr_data ) ) {
2016-02-27 16:08:54 +03:00
/* abuse overlimits to count when we receive metadata
* but dont have an ops for it
*/
2017-02-01 16:30:03 +03:00
pr_info_ratelimited ( " Unknown metaid %d dlen %d \n " ,
mtype , dlen ) ;
2016-02-27 16:08:54 +03:00
ife - > tcf_qstats . overlimits + + ;
}
2017-02-01 16:30:03 +03:00
}
2016-02-27 16:08:54 +03:00
2017-02-01 16:30:03 +03:00
if ( WARN_ON ( tlv_data ! = ifehdr_end ) ) {
spin_lock ( & ife - > tcf_lock ) ;
ife - > tcf_qstats . drops + + ;
spin_unlock ( & ife - > tcf_lock ) ;
return TC_ACT_SHOT ;
2016-02-27 16:08:54 +03:00
}
2017-02-01 16:30:03 +03:00
skb - > protocol = eth_type_trans ( skb , skb - > dev ) ;
2016-02-27 16:08:54 +03:00
skb_reset_network_header ( skb ) ;
2017-02-01 16:30:03 +03:00
2016-02-27 16:08:54 +03:00
return action ;
}
/*XXX: check if we can do this at install time instead of current
* send data path
* */
static int ife_get_sz ( struct sk_buff * skb , struct tcf_ife_info * ife )
{
struct tcf_meta_info * e , * n ;
int tot_run_sz = 0 , run_sz = 0 ;
list_for_each_entry_safe ( e , n , & ife - > metalist , metalist ) {
if ( e - > ops - > check_presence ) {
run_sz = e - > ops - > check_presence ( skb , e ) ;
tot_run_sz + = run_sz ;
}
}
return tot_run_sz ;
}
static int tcf_ife_encode ( struct sk_buff * skb , const struct tc_action * a ,
struct tcf_result * res )
{
2016-07-26 02:09:41 +03:00
struct tcf_ife_info * ife = to_ife ( a ) ;
2016-02-27 16:08:54 +03:00
int action = ife - > tcf_action ;
struct ethhdr * oethh ; /* outer ether header */
struct tcf_meta_info * e ;
/*
OUTERHDR : TOTMETALEN : { TLVHDR : Metadatum : TLVHDR . . } : ORIGDATA
where ORIGDATA = original ethernet header . . .
*/
u16 metalen = ife_get_sz ( skb , ife ) ;
int hdrm = metalen + skb - > dev - > hard_header_len + IFE_METAHDRLEN ;
2017-02-01 16:30:03 +03:00
unsigned int skboff = 0 ;
2016-02-27 16:08:54 +03:00
int new_len = skb - > len + hdrm ;
bool exceed_mtu = false ;
2017-02-01 16:30:03 +03:00
void * ife_meta ;
int err = 0 ;
2016-02-27 16:08:54 +03:00
2017-01-08 01:06:36 +03:00
if ( ! skb_at_tc_ingress ( skb ) ) {
2016-02-27 16:08:54 +03:00
if ( new_len > skb - > dev - > mtu )
exceed_mtu = true ;
}
spin_lock ( & ife - > tcf_lock ) ;
bstats_update ( & ife - > tcf_bstats , skb ) ;
2016-06-06 13:32:53 +03:00
tcf_lastuse_update ( & ife - > tcf_tm ) ;
2016-02-27 16:08:54 +03:00
if ( ! metalen ) { /* no metadata to send */
/* abuse overlimits to count when we allow packet
* with no metadata
*/
ife - > tcf_qstats . overlimits + + ;
spin_unlock ( & ife - > tcf_lock ) ;
return action ;
}
/* could be stupid policy setup or mtu config
* so lets be conservative . . */
if ( ( action = = TC_ACT_SHOT ) | | exceed_mtu ) {
ife - > tcf_qstats . drops + + ;
spin_unlock ( & ife - > tcf_lock ) ;
return TC_ACT_SHOT ;
}
2017-01-08 01:06:36 +03:00
if ( skb_at_tc_ingress ( skb ) )
2016-02-27 16:08:54 +03:00
skb_push ( skb , skb - > dev - > hard_header_len ) ;
2017-02-01 16:30:03 +03:00
ife_meta = ife_encode ( skb , metalen ) ;
2016-02-27 16:08:54 +03:00
/* XXX: we dont have a clever way of telling encode to
* not repeat some of the computations that are done by
* ops - > presence_check . . .
*/
list_for_each_entry ( e , & ife - > metalist , metalist ) {
if ( e - > ops - > encode ) {
2017-02-01 16:30:03 +03:00
err = e - > ops - > encode ( skb , ( void * ) ( ife_meta + skboff ) ,
2016-02-27 16:08:54 +03:00
e ) ;
}
if ( err < 0 ) {
/* too corrupt to keep around if overwritten */
ife - > tcf_qstats . drops + + ;
spin_unlock ( & ife - > tcf_lock ) ;
return TC_ACT_SHOT ;
}
skboff + = err ;
}
2017-02-01 16:30:03 +03:00
oethh = ( struct ethhdr * ) skb - > data ;
2016-02-27 16:08:54 +03:00
if ( ! is_zero_ether_addr ( ife - > eth_src ) )
ether_addr_copy ( oethh - > h_source , ife - > eth_src ) ;
if ( ! is_zero_ether_addr ( ife - > eth_dst ) )
ether_addr_copy ( oethh - > h_dest , ife - > eth_dst ) ;
oethh - > h_proto = htons ( ife - > eth_type ) ;
2017-01-08 01:06:36 +03:00
if ( skb_at_tc_ingress ( skb ) )
2016-02-27 16:08:54 +03:00
skb_pull ( skb , skb - > dev - > hard_header_len ) ;
spin_unlock ( & ife - > tcf_lock ) ;
return action ;
}
static int tcf_ife_act ( struct sk_buff * skb , const struct tc_action * a ,
struct tcf_result * res )
{
2016-07-26 02:09:41 +03:00
struct tcf_ife_info * ife = to_ife ( a ) ;
2016-02-27 16:08:54 +03:00
if ( ife - > flags & IFE_ENCODE )
return tcf_ife_encode ( skb , a , res ) ;
if ( ! ( ife - > flags & IFE_ENCODE ) )
return tcf_ife_decode ( skb , a , res ) ;
pr_info_ratelimited ( " unknown failure(policy neither de/encode \n " ) ;
spin_lock ( & ife - > tcf_lock ) ;
bstats_update ( & ife - > tcf_bstats , skb ) ;
2016-06-06 13:32:53 +03:00
tcf_lastuse_update ( & ife - > tcf_tm ) ;
2016-02-27 16:08:54 +03:00
ife - > tcf_qstats . drops + + ;
spin_unlock ( & ife - > tcf_lock ) ;
return TC_ACT_SHOT ;
}
static int tcf_ife_walker ( struct net * net , struct sk_buff * skb ,
struct netlink_callback * cb , int type ,
2016-07-26 02:09:41 +03:00
const struct tc_action_ops * ops )
2016-02-27 16:08:54 +03:00
{
struct tc_action_net * tn = net_generic ( net , ife_net_id ) ;
2016-07-26 02:09:41 +03:00
return tcf_generic_walker ( tn , skb , cb , type , ops ) ;
2016-02-27 16:08:54 +03:00
}
2016-07-26 02:09:41 +03:00
static int tcf_ife_search ( struct net * net , struct tc_action * * a , u32 index )
2016-02-27 16:08:54 +03:00
{
struct tc_action_net * tn = net_generic ( net , ife_net_id ) ;
2017-08-30 09:31:59 +03:00
return tcf_idr_search ( tn , a , index ) ;
2016-02-27 16:08:54 +03:00
}
static struct tc_action_ops act_ife_ops = {
. kind = " ife " ,
. type = TCA_ACT_IFE ,
. owner = THIS_MODULE ,
. act = tcf_ife_act ,
. dump = tcf_ife_dump ,
. cleanup = tcf_ife_cleanup ,
. init = tcf_ife_init ,
. walk = tcf_ife_walker ,
. lookup = tcf_ife_search ,
2016-07-26 02:09:41 +03:00
. size = sizeof ( struct tcf_ife_info ) ,
2016-02-27 16:08:54 +03:00
} ;
static __net_init int ife_init_net ( struct net * net )
{
struct tc_action_net * tn = net_generic ( net , ife_net_id ) ;
2017-08-30 09:31:59 +03:00
return tc_action_net_init ( tn , & act_ife_ops ) ;
2016-02-27 16:08:54 +03:00
}
static void __net_exit ife_exit_net ( struct net * net )
{
struct tc_action_net * tn = net_generic ( net , ife_net_id ) ;
tc_action_net_exit ( tn ) ;
}
static struct pernet_operations ife_net_ops = {
. init = ife_init_net ,
. exit = ife_exit_net ,
. id = & ife_net_id ,
. size = sizeof ( struct tc_action_net ) ,
} ;
static int __init ife_init_module ( void )
{
return tcf_register_action ( & act_ife_ops , & ife_net_ops ) ;
}
static void __exit ife_cleanup_module ( void )
{
tcf_unregister_action ( & act_ife_ops , & ife_net_ops ) ;
}
module_init ( ife_init_module ) ;
module_exit ( ife_cleanup_module ) ;
MODULE_AUTHOR ( " Jamal Hadi Salim(2015) " ) ;
MODULE_DESCRIPTION ( " Inter-FE LFB action " ) ;
MODULE_LICENSE ( " GPL " ) ;