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 .
*
2008-05-05 11:22:35 +04:00
* Authors : Jamal Hadi Salim ( 2005 - 8 )
2005-04-25 07:10:16 +04:00
*
*/
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
2008-05-05 11:22:35 +04:00
# define SIMP_MAX_DATA 32
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 ;
2008-07-20 11:08:27 +04:00
d - > tcf_bstats . bytes + = qdisc_pkt_len ( skb ) ;
2006-08-22 10:54:55 +04:00
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 ;
}
2008-05-05 11:22:35 +04:00
static int alloc_defdata ( struct tcf_defact * d , char * defdata )
2006-08-22 10:54:55 +04:00
{
2008-05-05 11:22:35 +04:00
d - > tcfd_defdata = kstrndup ( defdata , SIMP_MAX_DATA , GFP_KERNEL ) ;
2006-08-22 10:54:55 +04:00
if ( unlikely ( ! d - > tcfd_defdata ) )
return - ENOMEM ;
2008-05-05 11:22:35 +04:00
2006-08-22 10:54:55 +04:00
return 0 ;
}
2008-05-06 11:10:24 +04:00
static void reset_policy ( struct tcf_defact * d , char * defdata ,
struct tc_defact * p )
2006-08-22 10:54:55 +04:00
{
2008-05-06 11:10:24 +04:00
spin_lock_bh ( & d - > tcf_lock ) ;
d - > tcf_action = p - > action ;
memset ( d - > tcfd_defdata , 0 , SIMP_MAX_DATA ) ;
strlcpy ( d - > tcfd_defdata , defdata , SIMP_MAX_DATA ) ;
spin_unlock_bh ( & d - > tcf_lock ) ;
2006-08-22 10:54:55 +04:00
}
2008-01-24 07:36:30 +03:00
static const struct nla_policy simple_policy [ TCA_DEF_MAX + 1 ] = {
[ TCA_DEF_PARMS ] = { . len = sizeof ( struct tc_defact ) } ,
2008-05-05 11:22:35 +04:00
[ TCA_DEF_DATA ] = { . type = NLA_STRING , . len = SIMP_MAX_DATA } ,
2008-01-24 07:36:30 +03:00
} ;
2008-01-23 09:11:50 +03:00
static int tcf_simp_init ( struct nlattr * nla , struct nlattr * est ,
2006-08-22 10:54:55 +04:00
struct tc_action * a , int ovr , int bind )
{
2008-01-23 09:11:50 +03:00
struct nlattr * tb [ TCA_DEF_MAX + 1 ] ;
2006-08-22 10:54:55 +04:00
struct tc_defact * parm ;
struct tcf_defact * d ;
struct tcf_common * pc ;
2008-05-05 11:22:35 +04:00
char * defdata ;
2008-01-24 07:33:32 +03:00
int ret = 0 , err ;
2006-08-22 10:54:55 +04:00
2008-01-24 07:33:32 +03:00
if ( nla = = NULL )
2006-08-22 10:54:55 +04:00
return - EINVAL ;
2008-05-05 11:22:35 +04:00
err = nla_parse_nested ( tb , TCA_DEF_MAX , nla , simple_policy ) ;
2008-01-24 07:33:32 +03:00
if ( err < 0 )
return err ;
2008-01-24 07:36:30 +03:00
if ( tb [ TCA_DEF_PARMS ] = = NULL )
2006-08-22 10:54:55 +04:00
return - EINVAL ;
2008-05-05 11:22:35 +04:00
if ( tb [ TCA_DEF_DATA ] = = NULL )
2006-08-22 10:54:55 +04:00
return - EINVAL ;
2008-05-05 11:22:35 +04:00
parm = nla_data ( tb [ TCA_DEF_PARMS ] ) ;
defdata = nla_data ( tb [ TCA_DEF_DATA ] ) ;
2006-08-22 10:54:55 +04:00
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 ) ;
2008-11-26 08:12:32 +03:00
if ( IS_ERR ( pc ) )
return PTR_ERR ( pc ) ;
2006-08-22 10:54:55 +04:00
d = to_defact ( pc ) ;
2008-05-05 11:22:35 +04:00
ret = alloc_defdata ( d , defdata ) ;
2006-08-22 10:54:55 +04:00
if ( ret < 0 ) {
kfree ( pc ) ;
return ret ;
}
2008-05-06 11:10:24 +04:00
d - > tcf_action = parm - > action ;
2006-08-22 10:54:55 +04:00
ret = ACT_P_CREATED ;
} else {
d = to_defact ( pc ) ;
if ( ! ovr ) {
tcf_simp_release ( d , bind ) ;
return - EEXIST ;
}
2008-05-06 11:10:24 +04:00
reset_policy ( d , defdata , parm ) ;
2006-08-22 10:54:55 +04:00
}
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 ;
2008-01-23 09:11:50 +03:00
NLA_PUT ( skb , TCA_DEF_PARMS , sizeof ( opt ) , & opt ) ;
2008-05-05 11:22:35 +04:00
NLA_PUT_STRING ( skb , TCA_DEF_DATA , d - > tcfd_defdata ) ;
2006-08-22 10:54:55 +04:00
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 ) ;
2008-01-23 09:11:50 +03:00
NLA_PUT ( skb , TCA_DEF_TM , sizeof ( t ) , & t ) ;
2006-08-22 10:54:55 +04:00
return skb - > len ;
2008-01-23 09:11:50 +03:00
nla_put_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 ) ;