2005-04-17 02:20:36 +04:00
/*
* net / sched / pedit . c Generic packet editor
*
* 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 .
*
* Authors : Jamal Hadi Salim ( 2002 - 4 )
*/
# 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>
2007-03-26 10:06:12 +04:00
# include <net/netlink.h>
2005-04-17 02:20:36 +04:00
# include <net/pkt_sched.h>
# include <linux/tc_act/tc_pedit.h>
# include <net/tc_act/tc_pedit.h>
2006-08-22 10:54:55 +04:00
# define PEDIT_TAB_MASK 15
static struct tcf_common * tcf_pedit_ht [ PEDIT_TAB_MASK + 1 ] ;
static u32 pedit_idx_gen ;
2005-04-17 02:20:36 +04:00
static DEFINE_RWLOCK ( pedit_lock ) ;
2006-08-22 10:54:55 +04:00
static struct tcf_hashinfo pedit_hash_info = {
. htab = tcf_pedit_ht ,
. hmask = PEDIT_TAB_MASK ,
. lock = & pedit_lock ,
} ;
2005-04-17 02:20:36 +04:00
2006-08-22 10:54:55 +04:00
static int tcf_pedit_init ( struct rtattr * rta , struct rtattr * est ,
struct tc_action * a , int ovr , int bind )
2005-04-17 02:20:36 +04:00
{
struct rtattr * tb [ TCA_PEDIT_MAX ] ;
struct tc_pedit * parm ;
int ret = 0 ;
struct tcf_pedit * p ;
2006-08-22 10:54:55 +04:00
struct tcf_common * pc ;
2005-04-17 02:20:36 +04:00
struct tc_pedit_key * keys = NULL ;
int ksize ;
if ( rta = = NULL | | rtattr_parse_nested ( tb , TCA_PEDIT_MAX , rta ) < 0 )
return - EINVAL ;
if ( tb [ TCA_PEDIT_PARMS - 1 ] = = NULL | |
RTA_PAYLOAD ( tb [ TCA_PEDIT_PARMS - 1 ] ) < sizeof ( * parm ) )
return - EINVAL ;
parm = RTA_DATA ( tb [ TCA_PEDIT_PARMS - 1 ] ) ;
ksize = parm - > nkeys * sizeof ( struct tc_pedit_key ) ;
if ( RTA_PAYLOAD ( tb [ TCA_PEDIT_PARMS - 1 ] ) < sizeof ( * parm ) + ksize )
return - EINVAL ;
2006-08-22 10:54:55 +04:00
pc = tcf_hash_check ( parm - > index , a , bind , & pedit_hash_info ) ;
if ( ! pc ) {
2005-04-17 02:20:36 +04:00
if ( ! parm - > nkeys )
return - EINVAL ;
2006-08-22 10:54:55 +04:00
pc = tcf_hash_create ( parm - > index , est , a , sizeof ( * p ) , bind ,
& pedit_idx_gen , & pedit_hash_info ) ;
if ( unlikely ( ! pc ) )
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
2006-08-22 10:54:55 +04:00
p = to_pedit ( pc ) ;
2005-04-17 02:20:36 +04:00
keys = kmalloc ( ksize , GFP_KERNEL ) ;
if ( keys = = NULL ) {
2006-08-22 10:54:55 +04:00
kfree ( pc ) ;
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
}
ret = ACT_P_CREATED ;
} else {
2006-08-22 10:54:55 +04:00
p = to_pedit ( pc ) ;
2005-04-17 02:20:36 +04:00
if ( ! ovr ) {
2006-08-22 10:54:55 +04:00
tcf_hash_release ( pc , bind , & pedit_hash_info ) ;
2005-04-17 02:20:36 +04:00
return - EEXIST ;
}
2006-08-22 10:54:55 +04:00
if ( p - > tcfp_nkeys & & p - > tcfp_nkeys ! = parm - > nkeys ) {
2005-04-17 02:20:36 +04:00
keys = kmalloc ( ksize , GFP_KERNEL ) ;
if ( keys = = NULL )
return - ENOMEM ;
}
}
2006-08-22 10:54:55 +04:00
spin_lock_bh ( & p - > tcf_lock ) ;
p - > tcfp_flags = parm - > flags ;
p - > tcf_action = parm - > action ;
2005-04-17 02:20:36 +04:00
if ( keys ) {
2006-08-22 10:54:55 +04:00
kfree ( p - > tcfp_keys ) ;
p - > tcfp_keys = keys ;
p - > tcfp_nkeys = parm - > nkeys ;
2005-04-17 02:20:36 +04:00
}
2006-08-22 10:54:55 +04:00
memcpy ( p - > tcfp_keys , parm - > keys , ksize ) ;
spin_unlock_bh ( & p - > tcf_lock ) ;
2005-04-17 02:20:36 +04:00
if ( ret = = ACT_P_CREATED )
2006-08-22 10:54:55 +04:00
tcf_hash_insert ( pc , & pedit_hash_info ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
2006-08-22 10:54:55 +04:00
static int tcf_pedit_cleanup ( struct tc_action * a , int bind )
2005-04-17 02:20:36 +04:00
{
2006-08-22 10:54:55 +04:00
struct tcf_pedit * p = a - > priv ;
2005-04-17 02:20:36 +04:00
2006-08-22 10:54:55 +04:00
if ( p ) {
struct tc_pedit_key * keys = p - > tcfp_keys ;
if ( tcf_hash_release ( & p - > common , bind , & pedit_hash_info ) ) {
2005-04-17 02:20:36 +04:00
kfree ( keys ) ;
return 1 ;
}
}
return 0 ;
}
2006-08-22 10:54:55 +04:00
static int tcf_pedit ( struct sk_buff * skb , struct tc_action * a ,
struct tcf_result * res )
2005-04-17 02:20:36 +04:00
{
2006-08-22 10:54:55 +04:00
struct tcf_pedit * p = a - > priv ;
2005-04-17 02:20:36 +04:00
int i , munged = 0 ;
u8 * pptr ;
if ( ! ( skb - > tc_verd & TC_OK2MUNGE ) ) {
/* should we set skb->cloned? */
if ( pskb_expand_head ( skb , 0 , 0 , GFP_ATOMIC ) ) {
2006-08-22 10:54:55 +04:00
return p - > tcf_action ;
2005-04-17 02:20:36 +04:00
}
}
2007-04-11 07:50:43 +04:00
pptr = skb_network_header ( skb ) ;
2005-04-17 02:20:36 +04:00
2006-08-22 10:54:55 +04:00
spin_lock ( & p - > tcf_lock ) ;
2005-04-17 02:20:36 +04:00
2006-08-22 10:54:55 +04:00
p - > tcf_tm . lastuse = jiffies ;
2005-04-17 02:20:36 +04:00
2006-08-22 10:54:55 +04:00
if ( p - > tcfp_nkeys > 0 ) {
struct tc_pedit_key * tkey = p - > tcfp_keys ;
2005-04-17 02:20:36 +04:00
2006-08-22 10:54:55 +04:00
for ( i = p - > tcfp_nkeys ; i > 0 ; i - - , tkey + + ) {
2005-04-17 02:20:36 +04:00
u32 * ptr ;
int offset = tkey - > off ;
if ( tkey - > offmask ) {
if ( skb - > len > tkey - > at ) {
char * j = pptr + tkey - > at ;
2007-02-09 17:25:16 +03:00
offset + = ( ( * j & tkey - > offmask ) > >
tkey - > shift ) ;
2005-04-17 02:20:36 +04:00
} else {
goto bad ;
}
}
if ( offset % 4 ) {
printk ( " offset must be on 32 bit boundaries \n " ) ;
goto bad ;
}
2007-06-01 08:33:35 +04:00
if ( offset > 0 & & offset > skb - > len ) {
2005-04-17 02:20:36 +04:00
printk ( " offset %d cant exceed pkt length %d \n " ,
offset , skb - > len ) ;
goto bad ;
}
ptr = ( u32 * ) ( pptr + offset ) ;
/* just do it, baby */
* ptr = ( ( * ptr & tkey - > mask ) ^ tkey - > val ) ;
munged + + ;
}
2007-02-09 17:25:16 +03:00
2005-04-17 02:20:36 +04:00
if ( munged )
skb - > tc_verd = SET_TC_MUNGED ( skb - > tc_verd ) ;
goto done ;
} else {
2006-08-22 10:54:55 +04:00
printk ( " pedit BUG: index %d \n " , p - > tcf_index ) ;
2005-04-17 02:20:36 +04:00
}
bad :
2006-08-22 10:54:55 +04:00
p - > tcf_qstats . overlimits + + ;
2005-04-17 02:20:36 +04:00
done :
2006-08-22 10:54:55 +04:00
p - > tcf_bstats . bytes + = skb - > len ;
p - > tcf_bstats . packets + + ;
spin_unlock ( & p - > tcf_lock ) ;
return p - > tcf_action ;
2005-04-17 02:20:36 +04:00
}
2006-08-22 10:54:55 +04:00
static int tcf_pedit_dump ( struct sk_buff * skb , struct tc_action * a ,
int bind , int ref )
2005-04-17 02:20:36 +04:00
{
2007-04-20 07:29:13 +04:00
unsigned char * b = skb_tail_pointer ( skb ) ;
2006-08-22 10:54:55 +04:00
struct tcf_pedit * p = a - > priv ;
2005-04-17 02:20:36 +04:00
struct tc_pedit * opt ;
struct tcf_t t ;
2007-02-09 17:25:16 +03:00
int s ;
2006-08-22 10:54:55 +04:00
s = sizeof ( * opt ) + p - > tcfp_nkeys * sizeof ( struct tc_pedit_key ) ;
2005-04-17 02:20:36 +04:00
/* netlink spinlocks held above us - must use ATOMIC */
2006-07-22 01:51:30 +04:00
opt = kzalloc ( s , GFP_ATOMIC ) ;
2006-08-22 10:54:55 +04:00
if ( unlikely ( ! opt ) )
2005-04-17 02:20:36 +04:00
return - ENOBUFS ;
2006-08-22 10:54:55 +04:00
memcpy ( opt - > keys , p - > tcfp_keys ,
p - > tcfp_nkeys * sizeof ( struct tc_pedit_key ) ) ;
opt - > index = p - > tcf_index ;
opt - > nkeys = p - > tcfp_nkeys ;
opt - > flags = p - > tcfp_flags ;
opt - > action = p - > tcf_action ;
opt - > refcnt = p - > tcf_refcnt - ref ;
opt - > bindcnt = p - > tcf_bindcnt - bind ;
2005-04-17 02:20:36 +04:00
RTA_PUT ( skb , TCA_PEDIT_PARMS , s , opt ) ;
2006-08-22 10:54:55 +04:00
t . install = jiffies_to_clock_t ( jiffies - p - > tcf_tm . install ) ;
t . lastuse = jiffies_to_clock_t ( jiffies - p - > tcf_tm . lastuse ) ;
t . expires = jiffies_to_clock_t ( p - > tcf_tm . expires ) ;
2005-04-17 02:20:36 +04:00
RTA_PUT ( skb , TCA_PEDIT_TM , sizeof ( t ) , & t ) ;
2006-01-09 09:17:27 +03:00
kfree ( opt ) ;
2005-04-17 02:20:36 +04:00
return skb - > len ;
rtattr_failure :
2007-03-26 10:06:12 +04:00
nlmsg_trim ( skb , b ) ;
2006-01-09 09:17:27 +03:00
kfree ( opt ) ;
2005-04-17 02:20:36 +04:00
return - 1 ;
}
2006-08-22 10:54:55 +04:00
static struct tc_action_ops act_pedit_ops = {
2005-04-17 02:20:36 +04:00
. kind = " pedit " ,
2006-08-22 10:54:55 +04:00
. hinfo = & pedit_hash_info ,
2005-04-17 02:20:36 +04:00
. type = TCA_ACT_PEDIT ,
. capab = TCA_CAP_NONE ,
. owner = THIS_MODULE ,
. act = tcf_pedit ,
. dump = tcf_pedit_dump ,
. cleanup = tcf_pedit_cleanup ,
. lookup = tcf_hash_search ,
. init = tcf_pedit_init ,
. walk = tcf_generic_walker
} ;
MODULE_AUTHOR ( " Jamal Hadi Salim(2002-4) " ) ;
MODULE_DESCRIPTION ( " Generic Packet Editor actions " ) ;
MODULE_LICENSE ( " GPL " ) ;
2006-08-22 10:54:55 +04:00
static int __init pedit_init_module ( void )
2005-04-17 02:20:36 +04:00
{
return tcf_register_action ( & act_pedit_ops ) ;
}
2006-08-22 10:54:55 +04:00
static void __exit pedit_cleanup_module ( void )
2005-04-17 02:20:36 +04:00
{
tcf_unregister_action ( & act_pedit_ops ) ;
}
module_init ( pedit_init_module ) ;
module_exit ( pedit_cleanup_module ) ;