2005-04-25 07:10:16 +04:00
/*
* net / sched / simp . c Simple example of an action
*
* 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 ( 2005 )
*
*/
2005-04-25 23:15:01 +04:00
# include <linux/module.h>
# include <linux/init.h>
2005-04-25 07:10:16 +04:00
# include <linux/kernel.h>
# include <linux/skbuff.h>
# include <linux/rtnetlink.h>
2007-03-26 10:06:12 +04:00
# include <net/netlink.h>
2005-04-25 07:10:16 +04:00
# include <net/pkt_sched.h>
# define TCA_ACT_SIMP 22
# include <linux/tc_act/tc_defact.h>
# include <net/tc_act/tc_defact.h>
2006-08-22 10:54:55 +04:00
# define SIMP_TAB_MASK 7
static struct tcf_common * tcf_simp_ht [ SIMP_TAB_MASK + 1 ] ;
static u32 simp_idx_gen ;
2005-04-25 07:10:16 +04:00
static DEFINE_RWLOCK ( simp_lock ) ;
2006-08-31 02:03:07 +04:00
static struct tcf_hashinfo simp_hash_info = {
2006-08-22 10:54:55 +04:00
. htab = tcf_simp_ht ,
. hmask = SIMP_TAB_MASK ,
. lock = & simp_lock ,
} ;
2005-04-25 07:10:16 +04:00
2006-01-09 09:15:34 +03:00
static int tcf_simp ( struct sk_buff * skb , struct tc_action * a , struct tcf_result * res )
2005-04-25 07:10:16 +04:00
{
2006-08-22 10:54:55 +04:00
struct tcf_defact * d = a - > priv ;
2005-04-25 07:10:16 +04:00
2006-08-22 10:54:55 +04:00
spin_lock ( & d - > tcf_lock ) ;
d - > tcf_tm . lastuse = jiffies ;
d - > tcf_bstats . bytes + = skb - > len ;
d - > tcf_bstats . packets + + ;
2005-04-25 07:10:16 +04:00
2007-02-09 17:25:16 +03:00
/* print policy string followed by _ then packet count
* Example if this was the 3 rd packet and the string was " hello "
* then it would look like " hello_3 " ( without quotes )
2005-04-25 07:10:16 +04:00
* */
2006-08-22 10:54:55 +04:00
printk ( " simple: %s_%d \n " ,
( char * ) d - > tcfd_defdata , d - > tcf_bstats . packets ) ;
spin_unlock ( & d - > tcf_lock ) ;
return d - > tcf_action ;
}
static int tcf_simp_release ( struct tcf_defact * d , int bind )
{
int ret = 0 ;
if ( d ) {
if ( bind )
d - > tcf_bindcnt - - ;
d - > tcf_refcnt - - ;
if ( d - > tcf_bindcnt < = 0 & & d - > tcf_refcnt < = 0 ) {
kfree ( d - > tcfd_defdata ) ;
tcf_hash_destroy ( & d - > common , & simp_hash_info ) ;
ret = 1 ;
}
}
return ret ;
}
static int alloc_defdata ( struct tcf_defact * d , u32 datalen , void * defdata )
{
2006-11-21 06:19:40 +03:00
d - > tcfd_defdata = kmemdup ( defdata , datalen , GFP_KERNEL ) ;
2006-08-22 10:54:55 +04:00
if ( unlikely ( ! d - > tcfd_defdata ) )
return - ENOMEM ;
d - > tcfd_datalen = datalen ;
return 0 ;
}
static int realloc_defdata ( struct tcf_defact * d , u32 datalen , void * defdata )
{
kfree ( d - > tcfd_defdata ) ;
return alloc_defdata ( d , datalen , defdata ) ;
}
static int tcf_simp_init ( struct rtattr * rta , struct rtattr * est ,
struct tc_action * a , int ovr , int bind )
{
struct rtattr * tb [ TCA_DEF_MAX ] ;
struct tc_defact * parm ;
struct tcf_defact * d ;
struct tcf_common * pc ;
void * defdata ;
u32 datalen = 0 ;
int ret = 0 ;
if ( rta = = NULL | | rtattr_parse_nested ( tb , TCA_DEF_MAX , rta ) < 0 )
return - EINVAL ;
if ( tb [ TCA_DEF_PARMS - 1 ] = = NULL | |
RTA_PAYLOAD ( tb [ TCA_DEF_PARMS - 1 ] ) < sizeof ( * parm ) )
return - EINVAL ;
parm = RTA_DATA ( tb [ TCA_DEF_PARMS - 1 ] ) ;
defdata = RTA_DATA ( tb [ TCA_DEF_DATA - 1 ] ) ;
if ( defdata = = NULL )
return - EINVAL ;
datalen = RTA_PAYLOAD ( tb [ TCA_DEF_DATA - 1 ] ) ;
if ( datalen < = 0 )
return - EINVAL ;
pc = tcf_hash_check ( parm - > index , a , bind , & simp_hash_info ) ;
if ( ! pc ) {
pc = tcf_hash_create ( parm - > index , est , a , sizeof ( * d ) , bind ,
& simp_idx_gen , & simp_hash_info ) ;
if ( unlikely ( ! pc ) )
return - ENOMEM ;
d = to_defact ( pc ) ;
ret = alloc_defdata ( d , datalen , defdata ) ;
if ( ret < 0 ) {
kfree ( pc ) ;
return ret ;
}
ret = ACT_P_CREATED ;
} else {
d = to_defact ( pc ) ;
if ( ! ovr ) {
tcf_simp_release ( d , bind ) ;
return - EEXIST ;
}
realloc_defdata ( d , datalen , defdata ) ;
}
spin_lock_bh ( & d - > tcf_lock ) ;
d - > tcf_action = parm - > action ;
spin_unlock_bh ( & d - > tcf_lock ) ;
if ( ret = = ACT_P_CREATED )
tcf_hash_insert ( pc , & simp_hash_info ) ;
return ret ;
}
static inline int tcf_simp_cleanup ( struct tc_action * a , int bind )
{
struct tcf_defact * d = a - > priv ;
if ( d )
return tcf_simp_release ( d , bind ) ;
return 0 ;
}
static inline int tcf_simp_dump ( struct sk_buff * skb , struct tc_action * a ,
int bind , int ref )
{
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_defact * d = a - > priv ;
struct tc_defact opt ;
struct tcf_t t ;
opt . index = d - > tcf_index ;
opt . refcnt = d - > tcf_refcnt - ref ;
opt . bindcnt = d - > tcf_bindcnt - bind ;
opt . action = d - > tcf_action ;
RTA_PUT ( skb , TCA_DEF_PARMS , sizeof ( opt ) , & opt ) ;
RTA_PUT ( skb , TCA_DEF_DATA , d - > tcfd_datalen , d - > tcfd_defdata ) ;
t . install = jiffies_to_clock_t ( jiffies - d - > tcf_tm . install ) ;
t . lastuse = jiffies_to_clock_t ( jiffies - d - > tcf_tm . lastuse ) ;
t . expires = jiffies_to_clock_t ( d - > tcf_tm . expires ) ;
RTA_PUT ( skb , TCA_DEF_TM , sizeof ( t ) , & t ) ;
return skb - > len ;
rtattr_failure :
2007-03-26 10:06:12 +04:00
nlmsg_trim ( skb , b ) ;
2006-08-22 10:54:55 +04:00
return - 1 ;
2005-04-25 07:10:16 +04:00
}
static struct tc_action_ops act_simp_ops = {
2006-08-22 10:54:55 +04:00
. kind = " simple " ,
. hinfo = & simp_hash_info ,
. type = TCA_ACT_SIMP ,
. capab = TCA_CAP_NONE ,
. owner = THIS_MODULE ,
. act = tcf_simp ,
. dump = tcf_simp_dump ,
. cleanup = tcf_simp_cleanup ,
. init = tcf_simp_init ,
. walk = tcf_generic_walker ,
2005-04-25 07:10:16 +04:00
} ;
MODULE_AUTHOR ( " Jamal Hadi Salim(2005) " ) ;
MODULE_DESCRIPTION ( " Simple example action " ) ;
MODULE_LICENSE ( " GPL " ) ;
static int __init simp_init_module ( void )
{
int ret = tcf_register_action ( & act_simp_ops ) ;
if ( ! ret )
printk ( " Simple TC action Loaded \n " ) ;
return ret ;
}
static void __exit simp_cleanup_module ( void )
{
tcf_unregister_action ( & act_simp_ops ) ;
}
module_init ( simp_init_module ) ;
module_exit ( simp_cleanup_module ) ;