2005-04-17 02:20:36 +04:00
/*
* net / sched / act_api . c Packet action API .
*
* 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 .
*
* Author : Jamal Hadi Salim
*
*
*/
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/string.h>
# include <linux/errno.h>
# include <linux/skbuff.h>
# include <linux/init.h>
# include <linux/kmod.h>
2008-01-24 07:33:13 +03:00
# include <linux/err.h>
2007-11-30 16:21:31 +03:00
# include <net/net_namespace.h>
# include <net/sock.h>
2005-04-17 02:20:36 +04:00
# include <net/sch_generic.h>
# include <net/act_api.h>
2007-03-26 10:06:12 +04:00
# include <net/netlink.h>
2005-04-17 02:20:36 +04:00
2006-08-22 10:54:55 +04:00
void tcf_hash_destroy ( struct tcf_common * p , struct tcf_hashinfo * hinfo )
{
unsigned int h = tcf_hash ( p - > tcfc_index , hinfo - > hmask ) ;
struct tcf_common * * p1p ;
for ( p1p = & hinfo - > htab [ h ] ; * p1p ; p1p = & ( * p1p ) - > tcfc_next ) {
if ( * p1p = = p ) {
write_lock_bh ( hinfo - > lock ) ;
* p1p = p - > tcfc_next ;
write_unlock_bh ( hinfo - > lock ) ;
gen_kill_estimator ( & p - > tcfc_bstats ,
& p - > tcfc_rate_est ) ;
kfree ( p ) ;
return ;
}
}
BUG_TRAP ( 0 ) ;
}
EXPORT_SYMBOL ( tcf_hash_destroy ) ;
int tcf_hash_release ( struct tcf_common * p , int bind ,
struct tcf_hashinfo * hinfo )
{
int ret = 0 ;
if ( p ) {
if ( bind )
p - > tcfc_bindcnt - - ;
p - > tcfc_refcnt - - ;
2007-02-09 17:25:16 +03:00
if ( p - > tcfc_bindcnt < = 0 & & p - > tcfc_refcnt < = 0 ) {
2006-08-22 10:54:55 +04:00
tcf_hash_destroy ( p , hinfo ) ;
ret = 1 ;
}
}
return ret ;
}
EXPORT_SYMBOL ( tcf_hash_release ) ;
static int tcf_dump_walker ( struct sk_buff * skb , struct netlink_callback * cb ,
struct tc_action * a , struct tcf_hashinfo * hinfo )
{
struct tcf_common * p ;
int err = 0 , index = - 1 , i = 0 , s_i = 0 , n_i = 0 ;
2008-01-24 07:34:11 +03:00
struct nlattr * nest ;
2006-08-22 10:54:55 +04:00
[NET_SCHED] protect action config/dump from irqs
(with no apologies to C Heston)
On Mon, 2007-10-09 at 21:00 +0800, Herbert Xu wrote:
On Sun, Sep 02, 2007 at 01:11:29PM +0000, Christian Kujau wrote:
> >
> > after upgrading to 2.6.23-rc5 (and applying davem's fix [0]), lockdep
> > was quite noisy when I tried to shape my external (wireless) interface:
> >
> > [ 6400.534545] FahCore_78.exe/3552 just changed the state of lock:
> > [ 6400.534713] (&dev->ingress_lock){-+..}, at: [<c038d595>]
> > netif_receive_skb+0x2d5/0x3c0
> > [ 6400.534941] but this lock took another, soft-read-irq-unsafe lock in the
> > past:
> > [ 6400.535145] (police_lock){-.--}
>
> This is a genuine dead-lock. The police lock can be taken
> for reading with softirqs on. If a second CPU tries to take
> the police lock for writing, while holding the ingress lock,
> then a softirq on the first CPU can dead-lock when it tries
> to get the ingress lock.
Signed-off-by: Jamal Hadi Salim <hadi@cyberus.ca>
Acked-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2007-09-12 18:32:59 +04:00
read_lock_bh ( hinfo - > lock ) ;
2006-08-22 10:54:55 +04:00
s_i = cb - > args [ 0 ] ;
for ( i = 0 ; i < ( hinfo - > hmask + 1 ) ; i + + ) {
p = hinfo - > htab [ tcf_hash ( i , hinfo - > hmask ) ] ;
for ( ; p ; p = p - > tcfc_next ) {
index + + ;
if ( index < s_i )
continue ;
a - > priv = p ;
a - > order = n_i ;
2008-01-24 07:34:11 +03:00
nest = nla_nest_start ( skb , a - > order ) ;
if ( nest = = NULL )
goto nla_put_failure ;
2006-08-22 10:54:55 +04:00
err = tcf_action_dump_1 ( skb , a , 0 , 0 ) ;
if ( err < 0 ) {
index - - ;
2008-01-24 07:34:11 +03:00
nlmsg_trim ( skb , nest ) ;
2006-08-22 10:54:55 +04:00
goto done ;
}
2008-01-24 07:34:11 +03:00
nla_nest_end ( skb , nest ) ;
2006-08-22 10:54:55 +04:00
n_i + + ;
if ( n_i > = TCA_ACT_MAX_PRIO )
goto done ;
}
}
done :
[NET_SCHED] protect action config/dump from irqs
(with no apologies to C Heston)
On Mon, 2007-10-09 at 21:00 +0800, Herbert Xu wrote:
On Sun, Sep 02, 2007 at 01:11:29PM +0000, Christian Kujau wrote:
> >
> > after upgrading to 2.6.23-rc5 (and applying davem's fix [0]), lockdep
> > was quite noisy when I tried to shape my external (wireless) interface:
> >
> > [ 6400.534545] FahCore_78.exe/3552 just changed the state of lock:
> > [ 6400.534713] (&dev->ingress_lock){-+..}, at: [<c038d595>]
> > netif_receive_skb+0x2d5/0x3c0
> > [ 6400.534941] but this lock took another, soft-read-irq-unsafe lock in the
> > past:
> > [ 6400.535145] (police_lock){-.--}
>
> This is a genuine dead-lock. The police lock can be taken
> for reading with softirqs on. If a second CPU tries to take
> the police lock for writing, while holding the ingress lock,
> then a softirq on the first CPU can dead-lock when it tries
> to get the ingress lock.
Signed-off-by: Jamal Hadi Salim <hadi@cyberus.ca>
Acked-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2007-09-12 18:32:59 +04:00
read_unlock_bh ( hinfo - > lock ) ;
2006-08-22 10:54:55 +04:00
if ( n_i )
cb - > args [ 0 ] + = n_i ;
return n_i ;
2008-01-23 09:11:50 +03:00
nla_put_failure :
2008-01-24 07:34:11 +03:00
nla_nest_cancel ( skb , nest ) ;
2006-08-22 10:54:55 +04:00
goto done ;
}
static int tcf_del_walker ( struct sk_buff * skb , struct tc_action * a ,
struct tcf_hashinfo * hinfo )
{
struct tcf_common * p , * s_p ;
2008-01-24 07:34:11 +03:00
struct nlattr * nest ;
2006-08-22 10:54:55 +04:00
int i = 0 , n_i = 0 ;
2008-01-24 07:34:11 +03:00
nest = nla_nest_start ( skb , a - > order ) ;
if ( nest = = NULL )
goto nla_put_failure ;
2008-01-24 07:34:28 +03:00
NLA_PUT_STRING ( skb , TCA_KIND , a - > ops - > kind ) ;
2006-08-22 10:54:55 +04:00
for ( i = 0 ; i < ( hinfo - > hmask + 1 ) ; i + + ) {
p = hinfo - > htab [ tcf_hash ( i , hinfo - > hmask ) ] ;
while ( p ! = NULL ) {
s_p = p - > tcfc_next ;
if ( ACT_P_DELETED = = tcf_hash_release ( p , 0 , hinfo ) )
module_put ( a - > ops - > owner ) ;
n_i + + ;
p = s_p ;
}
}
2008-01-24 07:34:48 +03:00
NLA_PUT_U32 ( skb , TCA_FCNT , n_i ) ;
2008-01-24 07:34:11 +03:00
nla_nest_end ( skb , nest ) ;
2006-08-22 10:54:55 +04:00
return n_i ;
2008-01-23 09:11:50 +03:00
nla_put_failure :
2008-01-24 07:34:11 +03:00
nla_nest_cancel ( skb , nest ) ;
2006-08-22 10:54:55 +04:00
return - EINVAL ;
}
int tcf_generic_walker ( struct sk_buff * skb , struct netlink_callback * cb ,
int type , struct tc_action * a )
{
struct tcf_hashinfo * hinfo = a - > ops - > hinfo ;
if ( type = = RTM_DELACTION ) {
return tcf_del_walker ( skb , a , hinfo ) ;
} else if ( type = = RTM_GETACTION ) {
return tcf_dump_walker ( skb , cb , a , hinfo ) ;
} else {
printk ( " tcf_generic_walker: unknown action %d \n " , type ) ;
return - EINVAL ;
}
}
EXPORT_SYMBOL ( tcf_generic_walker ) ;
struct tcf_common * tcf_hash_lookup ( u32 index , struct tcf_hashinfo * hinfo )
{
struct tcf_common * p ;
[NET_SCHED] protect action config/dump from irqs
(with no apologies to C Heston)
On Mon, 2007-10-09 at 21:00 +0800, Herbert Xu wrote:
On Sun, Sep 02, 2007 at 01:11:29PM +0000, Christian Kujau wrote:
> >
> > after upgrading to 2.6.23-rc5 (and applying davem's fix [0]), lockdep
> > was quite noisy when I tried to shape my external (wireless) interface:
> >
> > [ 6400.534545] FahCore_78.exe/3552 just changed the state of lock:
> > [ 6400.534713] (&dev->ingress_lock){-+..}, at: [<c038d595>]
> > netif_receive_skb+0x2d5/0x3c0
> > [ 6400.534941] but this lock took another, soft-read-irq-unsafe lock in the
> > past:
> > [ 6400.535145] (police_lock){-.--}
>
> This is a genuine dead-lock. The police lock can be taken
> for reading with softirqs on. If a second CPU tries to take
> the police lock for writing, while holding the ingress lock,
> then a softirq on the first CPU can dead-lock when it tries
> to get the ingress lock.
Signed-off-by: Jamal Hadi Salim <hadi@cyberus.ca>
Acked-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2007-09-12 18:32:59 +04:00
read_lock_bh ( hinfo - > lock ) ;
2006-08-22 10:54:55 +04:00
for ( p = hinfo - > htab [ tcf_hash ( index , hinfo - > hmask ) ] ; p ;
p = p - > tcfc_next ) {
if ( p - > tcfc_index = = index )
break ;
}
[NET_SCHED] protect action config/dump from irqs
(with no apologies to C Heston)
On Mon, 2007-10-09 at 21:00 +0800, Herbert Xu wrote:
On Sun, Sep 02, 2007 at 01:11:29PM +0000, Christian Kujau wrote:
> >
> > after upgrading to 2.6.23-rc5 (and applying davem's fix [0]), lockdep
> > was quite noisy when I tried to shape my external (wireless) interface:
> >
> > [ 6400.534545] FahCore_78.exe/3552 just changed the state of lock:
> > [ 6400.534713] (&dev->ingress_lock){-+..}, at: [<c038d595>]
> > netif_receive_skb+0x2d5/0x3c0
> > [ 6400.534941] but this lock took another, soft-read-irq-unsafe lock in the
> > past:
> > [ 6400.535145] (police_lock){-.--}
>
> This is a genuine dead-lock. The police lock can be taken
> for reading with softirqs on. If a second CPU tries to take
> the police lock for writing, while holding the ingress lock,
> then a softirq on the first CPU can dead-lock when it tries
> to get the ingress lock.
Signed-off-by: Jamal Hadi Salim <hadi@cyberus.ca>
Acked-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2007-09-12 18:32:59 +04:00
read_unlock_bh ( hinfo - > lock ) ;
2006-08-22 10:54:55 +04:00
return p ;
}
EXPORT_SYMBOL ( tcf_hash_lookup ) ;
u32 tcf_hash_new_index ( u32 * idx_gen , struct tcf_hashinfo * hinfo )
{
u32 val = * idx_gen ;
do {
if ( + + val = = 0 )
val = 1 ;
} while ( tcf_hash_lookup ( val , hinfo ) ) ;
return ( * idx_gen = val ) ;
}
EXPORT_SYMBOL ( tcf_hash_new_index ) ;
int tcf_hash_search ( struct tc_action * a , u32 index )
{
struct tcf_hashinfo * hinfo = a - > ops - > hinfo ;
struct tcf_common * p = tcf_hash_lookup ( index , hinfo ) ;
if ( p ) {
a - > priv = p ;
return 1 ;
}
return 0 ;
}
EXPORT_SYMBOL ( tcf_hash_search ) ;
struct tcf_common * tcf_hash_check ( u32 index , struct tc_action * a , int bind ,
struct tcf_hashinfo * hinfo )
{
struct tcf_common * p = NULL ;
if ( index & & ( p = tcf_hash_lookup ( index , hinfo ) ) ! = NULL ) {
if ( bind ) {
p - > tcfc_bindcnt + + ;
p - > tcfc_refcnt + + ;
}
a - > priv = p ;
}
return p ;
}
EXPORT_SYMBOL ( tcf_hash_check ) ;
2008-01-23 09:11:50 +03:00
struct tcf_common * tcf_hash_create ( u32 index , struct nlattr * est , struct tc_action * a , int size , int bind , u32 * idx_gen , struct tcf_hashinfo * hinfo )
2006-08-22 10:54:55 +04:00
{
struct tcf_common * p = kzalloc ( size , GFP_KERNEL ) ;
if ( unlikely ( ! p ) )
return p ;
p - > tcfc_refcnt = 1 ;
if ( bind )
p - > tcfc_bindcnt = 1 ;
spin_lock_init ( & p - > tcfc_lock ) ;
p - > tcfc_index = index ? index : tcf_hash_new_index ( idx_gen , hinfo ) ;
p - > tcfc_tm . install = jiffies ;
p - > tcfc_tm . lastuse = jiffies ;
if ( est )
gen_new_estimator ( & p - > tcfc_bstats , & p - > tcfc_rate_est ,
2008-01-23 09:11:50 +03:00
& p - > tcfc_lock , est ) ;
2006-08-22 10:54:55 +04:00
a - > priv = ( void * ) p ;
return p ;
}
EXPORT_SYMBOL ( tcf_hash_create ) ;
void tcf_hash_insert ( struct tcf_common * p , struct tcf_hashinfo * hinfo )
{
unsigned int h = tcf_hash ( p - > tcfc_index , hinfo - > hmask ) ;
write_lock_bh ( hinfo - > lock ) ;
p - > tcfc_next = hinfo - > htab [ h ] ;
hinfo - > htab [ h ] = p ;
write_unlock_bh ( hinfo - > lock ) ;
}
EXPORT_SYMBOL ( tcf_hash_insert ) ;
2005-04-17 02:20:36 +04:00
static struct tc_action_ops * act_base = NULL ;
static DEFINE_RWLOCK ( act_mod_lock ) ;
int tcf_register_action ( struct tc_action_ops * act )
{
struct tc_action_ops * a , * * ap ;
write_lock ( & act_mod_lock ) ;
for ( ap = & act_base ; ( a = * ap ) ! = NULL ; ap = & a - > next ) {
if ( act - > type = = a - > type | | ( strcmp ( act - > kind , a - > kind ) = = 0 ) ) {
write_unlock ( & act_mod_lock ) ;
return - EEXIST ;
}
}
act - > next = NULL ;
* ap = act ;
write_unlock ( & act_mod_lock ) ;
return 0 ;
}
2008-01-23 09:10:23 +03:00
EXPORT_SYMBOL ( tcf_register_action ) ;
2005-04-17 02:20:36 +04:00
int tcf_unregister_action ( struct tc_action_ops * act )
{
struct tc_action_ops * a , * * ap ;
int err = - ENOENT ;
write_lock ( & act_mod_lock ) ;
for ( ap = & act_base ; ( a = * ap ) ! = NULL ; ap = & a - > next )
if ( a = = act )
break ;
if ( a ) {
* ap = a - > next ;
a - > next = NULL ;
err = 0 ;
}
write_unlock ( & act_mod_lock ) ;
return err ;
}
2008-01-23 09:10:23 +03:00
EXPORT_SYMBOL ( tcf_unregister_action ) ;
2005-04-17 02:20:36 +04:00
/* lookup by name */
static struct tc_action_ops * tc_lookup_action_n ( char * kind )
{
struct tc_action_ops * a = NULL ;
if ( kind ) {
read_lock ( & act_mod_lock ) ;
for ( a = act_base ; a ; a = a - > next ) {
if ( strcmp ( kind , a - > kind ) = = 0 ) {
if ( ! try_module_get ( a - > owner ) ) {
read_unlock ( & act_mod_lock ) ;
return NULL ;
}
break ;
}
}
read_unlock ( & act_mod_lock ) ;
}
return a ;
}
2008-01-23 09:11:50 +03:00
/* lookup by nlattr */
static struct tc_action_ops * tc_lookup_action ( struct nlattr * kind )
2005-04-17 02:20:36 +04:00
{
struct tc_action_ops * a = NULL ;
if ( kind ) {
read_lock ( & act_mod_lock ) ;
for ( a = act_base ; a ; a = a - > next ) {
2008-01-23 09:11:50 +03:00
if ( nla_strcmp ( kind , a - > kind ) = = 0 ) {
2005-04-17 02:20:36 +04:00
if ( ! try_module_get ( a - > owner ) ) {
read_unlock ( & act_mod_lock ) ;
return NULL ;
}
break ;
}
}
read_unlock ( & act_mod_lock ) ;
}
return a ;
}
#if 0
/* lookup by id */
static struct tc_action_ops * tc_lookup_action_id ( u32 type )
{
struct tc_action_ops * a = NULL ;
if ( type ) {
read_lock ( & act_mod_lock ) ;
for ( a = act_base ; a ; a = a - > next ) {
if ( a - > type = = type ) {
if ( ! try_module_get ( a - > owner ) ) {
read_unlock ( & act_mod_lock ) ;
return NULL ;
}
break ;
}
}
read_unlock ( & act_mod_lock ) ;
}
return a ;
}
# endif
int tcf_action_exec ( struct sk_buff * skb , struct tc_action * act ,
2007-02-09 17:25:16 +03:00
struct tcf_result * res )
2005-04-17 02:20:36 +04:00
{
struct tc_action * a ;
int ret = - 1 ;
if ( skb - > tc_verd & TC_NCLS ) {
skb - > tc_verd = CLR_TC_NCLS ( skb - > tc_verd ) ;
ret = TC_ACT_OK ;
goto exec_done ;
}
while ( ( a = act ) ! = NULL ) {
repeat :
if ( a - > ops & & a - > ops - > act ) {
2006-01-09 09:15:34 +03:00
ret = a - > ops - > act ( skb , a , res ) ;
2005-04-17 02:20:36 +04:00
if ( TC_MUNGED & skb - > tc_verd ) {
/* copied already, allow trampling */
skb - > tc_verd = SET_TC_OK2MUNGE ( skb - > tc_verd ) ;
skb - > tc_verd = CLR_TC_MUNGED ( skb - > tc_verd ) ;
}
if ( ret = = TC_ACT_REPEAT )
goto repeat ; /* we need a ttl - JHS */
2005-05-04 03:29:13 +04:00
if ( ret ! = TC_ACT_PIPE )
goto exec_done ;
2005-04-17 02:20:36 +04:00
}
act = a - > next ;
}
exec_done :
return ret ;
}
2008-01-23 09:10:23 +03:00
EXPORT_SYMBOL ( tcf_action_exec ) ;
2005-04-17 02:20:36 +04:00
void tcf_action_destroy ( struct tc_action * act , int bind )
{
struct tc_action * a ;
for ( a = act ; a ; a = act ) {
if ( a - > ops & & a - > ops - > cleanup ) {
if ( a - > ops - > cleanup ( a , bind ) = = ACT_P_DELETED )
module_put ( a - > ops - > owner ) ;
act = act - > next ;
kfree ( a ) ;
} else { /*FIXME: Remove later - catch insertion bugs*/
printk ( " tcf_action_destroy: BUG? destroying NULL ops \n " ) ;
act = act - > next ;
kfree ( a ) ;
}
}
}
int
tcf_action_dump_old ( struct sk_buff * skb , struct tc_action * a , int bind , int ref )
{
int err = - EINVAL ;
if ( a - > ops = = NULL | | a - > ops - > dump = = NULL )
return err ;
return a - > ops - > dump ( skb , a , bind , ref ) ;
}
int
tcf_action_dump_1 ( struct sk_buff * skb , struct tc_action * a , int bind , int ref )
{
int err = - EINVAL ;
2007-04-20 07:29:13 +04:00
unsigned char * b = skb_tail_pointer ( skb ) ;
2008-01-24 07:34:11 +03:00
struct nlattr * nest ;
2005-04-17 02:20:36 +04:00
if ( a - > ops = = NULL | | a - > ops - > dump = = NULL )
return err ;
2008-01-24 07:34:28 +03:00
NLA_PUT_STRING ( skb , TCA_KIND , a - > ops - > kind ) ;
2005-04-17 02:20:36 +04:00
if ( tcf_action_copy_stats ( skb , a , 0 ) )
2008-01-23 09:11:50 +03:00
goto nla_put_failure ;
2008-01-24 07:34:11 +03:00
nest = nla_nest_start ( skb , TCA_OPTIONS ) ;
if ( nest = = NULL )
goto nla_put_failure ;
2005-04-17 02:20:36 +04:00
if ( ( err = tcf_action_dump_old ( skb , a , bind , ref ) ) > 0 ) {
2008-01-24 07:34:11 +03:00
nla_nest_end ( skb , nest ) ;
2005-04-17 02:20:36 +04:00
return err ;
}
2008-01-23 09:11:50 +03:00
nla_put_failure :
2007-03-26 10:06:12 +04:00
nlmsg_trim ( skb , b ) ;
2005-04-17 02:20:36 +04:00
return - 1 ;
}
2008-01-23 09:10:23 +03:00
EXPORT_SYMBOL ( tcf_action_dump_1 ) ;
2005-04-17 02:20:36 +04:00
int
tcf_action_dump ( struct sk_buff * skb , struct tc_action * act , int bind , int ref )
{
struct tc_action * a ;
int err = - EINVAL ;
2008-01-24 07:34:11 +03:00
struct nlattr * nest ;
2005-04-17 02:20:36 +04:00
while ( ( a = act ) ! = NULL ) {
act = a - > next ;
2008-01-24 07:34:11 +03:00
nest = nla_nest_start ( skb , a - > order ) ;
if ( nest = = NULL )
goto nla_put_failure ;
2005-04-17 02:20:36 +04:00
err = tcf_action_dump_1 ( skb , a , bind , ref ) ;
if ( err < 0 )
2006-07-06 07:47:28 +04:00
goto errout ;
2008-01-24 07:34:11 +03:00
nla_nest_end ( skb , nest ) ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
2008-01-23 09:11:50 +03:00
nla_put_failure :
2006-07-06 07:47:28 +04:00
err = - EINVAL ;
errout :
2008-01-24 07:34:11 +03:00
nla_nest_cancel ( skb , nest ) ;
2006-07-06 07:47:28 +04:00
return err ;
2005-04-17 02:20:36 +04:00
}
2008-01-23 09:11:50 +03:00
struct tc_action * tcf_action_init_1 ( struct nlattr * nla , struct nlattr * est ,
2008-01-24 07:33:13 +03:00
char * name , int ovr , int bind )
2005-04-17 02:20:36 +04:00
{
struct tc_action * a ;
struct tc_action_ops * a_o ;
char act_name [ IFNAMSIZ ] ;
2008-01-23 09:11:50 +03:00
struct nlattr * tb [ TCA_ACT_MAX + 1 ] ;
struct nlattr * kind ;
2008-01-24 07:33:13 +03:00
int err ;
2005-04-17 02:20:36 +04:00
if ( name = = NULL ) {
2008-01-24 07:33:32 +03:00
err = nla_parse_nested ( tb , TCA_ACT_MAX , nla , NULL ) ;
if ( err < 0 )
2005-04-17 02:20:36 +04:00
goto err_out ;
2008-01-24 07:33:32 +03:00
err = - EINVAL ;
2008-01-23 09:11:50 +03:00
kind = tb [ TCA_ACT_KIND ] ;
2005-04-17 02:20:36 +04:00
if ( kind = = NULL )
goto err_out ;
2008-01-23 09:11:50 +03:00
if ( nla_strlcpy ( act_name , kind , IFNAMSIZ ) > = IFNAMSIZ )
2005-04-17 02:20:36 +04:00
goto err_out ;
} else {
2008-01-24 07:33:32 +03:00
err = - EINVAL ;
2005-04-17 02:20:36 +04:00
if ( strlcpy ( act_name , name , IFNAMSIZ ) > = IFNAMSIZ )
goto err_out ;
}
a_o = tc_lookup_action_n ( act_name ) ;
if ( a_o = = NULL ) {
# ifdef CONFIG_KMOD
rtnl_unlock ( ) ;
2006-01-09 09:22:14 +03:00
request_module ( " act_%s " , act_name ) ;
2005-04-17 02:20:36 +04:00
rtnl_lock ( ) ;
a_o = tc_lookup_action_n ( act_name ) ;
/* We dropped the RTNL semaphore in order to
* perform the module load . So , even if we
* succeeded in loading the module we have to
* tell the caller to replay the request . We
* indicate this using - EAGAIN .
*/
if ( a_o ! = NULL ) {
2008-01-24 07:33:13 +03:00
err = - EAGAIN ;
2005-04-17 02:20:36 +04:00
goto err_mod ;
}
# endif
2008-01-24 07:33:13 +03:00
err = - ENOENT ;
2005-04-17 02:20:36 +04:00
goto err_out ;
}
2008-01-24 07:33:13 +03:00
err = - ENOMEM ;
2006-07-22 01:51:30 +04:00
a = kzalloc ( sizeof ( * a ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( a = = NULL )
goto err_mod ;
/* backward compatibility for policer */
if ( name = = NULL )
2008-01-24 07:33:13 +03:00
err = a_o - > init ( tb [ TCA_ACT_OPTIONS ] , est , a , ovr , bind ) ;
2005-04-17 02:20:36 +04:00
else
2008-01-24 07:33:13 +03:00
err = a_o - > init ( nla , est , a , ovr , bind ) ;
if ( err < 0 )
2005-04-17 02:20:36 +04:00
goto err_free ;
/* module count goes up only when brand new policy is created
if it exists and is only bound to in a_o - > init ( ) then
ACT_P_CREATED is not returned ( a zero is ) .
*/
2008-01-24 07:33:13 +03:00
if ( err ! = ACT_P_CREATED )
2005-04-17 02:20:36 +04:00
module_put ( a_o - > owner ) ;
a - > ops = a_o ;
return a ;
err_free :
kfree ( a ) ;
err_mod :
module_put ( a_o - > owner ) ;
err_out :
2008-01-24 07:33:13 +03:00
return ERR_PTR ( err ) ;
2005-04-17 02:20:36 +04:00
}
2008-01-23 09:11:50 +03:00
struct tc_action * tcf_action_init ( struct nlattr * nla , struct nlattr * est ,
2008-01-24 07:33:13 +03:00
char * name , int ovr , int bind )
2005-04-17 02:20:36 +04:00
{
2008-01-23 09:11:50 +03:00
struct nlattr * tb [ TCA_ACT_MAX_PRIO + 1 ] ;
2005-04-17 02:20:36 +04:00
struct tc_action * head = NULL , * act , * act_prev = NULL ;
2008-01-24 07:33:32 +03:00
int err ;
2005-04-17 02:20:36 +04:00
int i ;
2008-01-24 07:33:32 +03:00
err = nla_parse_nested ( tb , TCA_ACT_MAX_PRIO , nla , NULL ) ;
if ( err < 0 )
return ERR_PTR ( err ) ;
2005-04-17 02:20:36 +04:00
2008-01-23 09:11:50 +03:00
for ( i = 1 ; i < = TCA_ACT_MAX_PRIO & & tb [ i ] ; i + + ) {
2008-01-24 07:33:13 +03:00
act = tcf_action_init_1 ( tb [ i ] , est , name , ovr , bind ) ;
if ( IS_ERR ( act ) )
2005-04-17 02:20:36 +04:00
goto err ;
2008-01-23 09:11:50 +03:00
act - > order = i ;
2005-04-17 02:20:36 +04:00
if ( head = = NULL )
head = act ;
else
act_prev - > next = act ;
act_prev = act ;
}
return head ;
err :
if ( head ! = NULL )
tcf_action_destroy ( head , bind ) ;
2008-01-24 07:33:13 +03:00
return act ;
2005-04-17 02:20:36 +04:00
}
int tcf_action_copy_stats ( struct sk_buff * skb , struct tc_action * a ,
int compat_mode )
{
int err = 0 ;
struct gnet_dump d ;
struct tcf_act_hdr * h = a - > priv ;
2007-02-09 17:25:16 +03:00
2005-04-17 02:20:36 +04:00
if ( h = = NULL )
goto errout ;
/* compat_mode being true specifies a call that is supposed
* to add additional backward compatiblity statistic TLVs .
*/
if ( compat_mode ) {
if ( a - > type = = TCA_OLD_COMPAT )
err = gnet_stats_start_copy_compat ( skb , 0 ,
2007-07-03 09:47:37 +04:00
TCA_STATS , TCA_XSTATS , & h - > tcf_lock , & d ) ;
2005-04-17 02:20:36 +04:00
else
return 0 ;
} else
err = gnet_stats_start_copy ( skb , TCA_ACT_STATS ,
2007-07-03 09:47:37 +04:00
& h - > tcf_lock , & d ) ;
2005-04-17 02:20:36 +04:00
if ( err < 0 )
goto errout ;
if ( a - > ops ! = NULL & & a - > ops - > get_stats ! = NULL )
if ( a - > ops - > get_stats ( skb , a ) < 0 )
goto errout ;
2006-08-22 10:54:55 +04:00
if ( gnet_stats_copy_basic ( & d , & h - > tcf_bstats ) < 0 | |
gnet_stats_copy_rate_est ( & d , & h - > tcf_rate_est ) < 0 | |
gnet_stats_copy_queue ( & d , & h - > tcf_qstats ) < 0 )
2005-04-17 02:20:36 +04:00
goto errout ;
if ( gnet_stats_finish_copy ( & d ) < 0 )
goto errout ;
return 0 ;
errout :
return - 1 ;
}
static int
tca_get_fill ( struct sk_buff * skb , struct tc_action * a , u32 pid , u32 seq ,
2007-02-09 17:25:16 +03:00
u16 flags , int event , int bind , int ref )
2005-04-17 02:20:36 +04:00
{
struct tcamsg * t ;
struct nlmsghdr * nlh ;
2007-04-20 07:29:13 +04:00
unsigned char * b = skb_tail_pointer ( skb ) ;
2008-01-24 07:34:11 +03:00
struct nlattr * nest ;
2005-04-17 02:20:36 +04:00
2005-06-19 09:55:31 +04:00
nlh = NLMSG_NEW ( skb , pid , seq , event , sizeof ( * t ) , flags ) ;
2005-04-17 02:20:36 +04:00
t = NLMSG_DATA ( nlh ) ;
t - > tca_family = AF_UNSPEC ;
2005-06-28 23:55:30 +04:00
t - > tca__pad1 = 0 ;
t - > tca__pad2 = 0 ;
2007-02-09 17:25:16 +03:00
2008-01-24 07:34:11 +03:00
nest = nla_nest_start ( skb , TCA_ACT_TAB ) ;
if ( nest = = NULL )
goto nla_put_failure ;
2005-04-17 02:20:36 +04:00
if ( tcf_action_dump ( skb , a , bind , ref ) < 0 )
2008-01-23 09:11:50 +03:00
goto nla_put_failure ;
2005-04-17 02:20:36 +04:00
2008-01-24 07:34:11 +03:00
nla_nest_end ( skb , nest ) ;
2007-02-09 17:25:16 +03:00
2007-04-20 07:29:13 +04:00
nlh - > nlmsg_len = skb_tail_pointer ( skb ) - b ;
2005-04-17 02:20:36 +04:00
return skb - > len ;
2008-01-23 09:11:50 +03:00
nla_put_failure :
2005-04-17 02:20:36 +04:00
nlmsg_failure :
2007-03-26 10:06:12 +04:00
nlmsg_trim ( skb , b ) ;
2005-04-17 02:20:36 +04:00
return - 1 ;
}
static int
act_get_notify ( u32 pid , struct nlmsghdr * n , struct tc_action * a , int event )
{
struct sk_buff * skb ;
skb = alloc_skb ( NLMSG_GOODSIZE , GFP_KERNEL ) ;
if ( ! skb )
return - ENOBUFS ;
if ( tca_get_fill ( skb , a , pid , n - > nlmsg_seq , 0 , event , 0 , 0 ) < = 0 ) {
kfree_skb ( skb ) ;
return - EINVAL ;
}
2006-08-15 11:30:25 +04:00
2007-11-20 09:26:51 +03:00
return rtnl_unicast ( skb , & init_net , pid ) ;
2005-04-17 02:20:36 +04:00
}
static struct tc_action *
2008-01-24 07:33:13 +03:00
tcf_action_get_1 ( struct nlattr * nla , struct nlmsghdr * n , u32 pid )
2005-04-17 02:20:36 +04:00
{
2008-01-23 09:11:50 +03:00
struct nlattr * tb [ TCA_ACT_MAX + 1 ] ;
2005-04-17 02:20:36 +04:00
struct tc_action * a ;
int index ;
2008-01-24 07:33:13 +03:00
int err ;
2005-04-17 02:20:36 +04:00
2008-01-24 07:33:32 +03:00
err = nla_parse_nested ( tb , TCA_ACT_MAX , nla , NULL ) ;
if ( err < 0 )
2008-01-24 07:33:13 +03:00
goto err_out ;
2005-04-17 02:20:36 +04:00
2008-01-24 07:33:32 +03:00
err = - EINVAL ;
2008-01-23 09:11:50 +03:00
if ( tb [ TCA_ACT_INDEX ] = = NULL | |
nla_len ( tb [ TCA_ACT_INDEX ] ) < sizeof ( index ) )
2008-01-24 07:33:13 +03:00
goto err_out ;
2008-01-24 07:35:03 +03:00
index = nla_get_u32 ( tb [ TCA_ACT_INDEX ] ) ;
2005-04-17 02:20:36 +04:00
2008-01-24 07:33:13 +03:00
err = - ENOMEM ;
2006-07-22 01:51:30 +04:00
a = kzalloc ( sizeof ( struct tc_action ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( a = = NULL )
2008-01-24 07:33:13 +03:00
goto err_out ;
2005-04-17 02:20:36 +04:00
2008-01-24 07:33:13 +03:00
err = - EINVAL ;
2008-01-23 09:11:50 +03:00
a - > ops = tc_lookup_action ( tb [ TCA_ACT_KIND ] ) ;
2005-04-17 02:20:36 +04:00
if ( a - > ops = = NULL )
goto err_free ;
if ( a - > ops - > lookup = = NULL )
goto err_mod ;
2008-01-24 07:33:13 +03:00
err = - ENOENT ;
2005-04-17 02:20:36 +04:00
if ( a - > ops - > lookup ( a , index ) = = 0 )
goto err_mod ;
module_put ( a - > ops - > owner ) ;
return a ;
2008-01-24 07:33:13 +03:00
2005-04-17 02:20:36 +04:00
err_mod :
module_put ( a - > ops - > owner ) ;
err_free :
kfree ( a ) ;
2008-01-24 07:33:13 +03:00
err_out :
return ERR_PTR ( err ) ;
2005-04-17 02:20:36 +04:00
}
static void cleanup_a ( struct tc_action * act )
{
struct tc_action * a ;
for ( a = act ; a ; a = act ) {
act = a - > next ;
kfree ( a ) ;
}
}
static struct tc_action * create_a ( int i )
{
struct tc_action * act ;
2006-07-22 01:51:30 +04:00
act = kzalloc ( sizeof ( * act ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( act = = NULL ) {
printk ( " create_a: failed to alloc! \n " ) ;
return NULL ;
}
act - > order = i ;
return act ;
}
2008-01-23 09:11:50 +03:00
static int tca_action_flush ( struct nlattr * nla , struct nlmsghdr * n , u32 pid )
2005-04-17 02:20:36 +04:00
{
struct sk_buff * skb ;
unsigned char * b ;
struct nlmsghdr * nlh ;
struct tcamsg * t ;
struct netlink_callback dcb ;
2008-01-24 07:34:11 +03:00
struct nlattr * nest ;
2008-01-23 09:11:50 +03:00
struct nlattr * tb [ TCA_ACT_MAX + 1 ] ;
struct nlattr * kind ;
2005-04-17 02:20:36 +04:00
struct tc_action * a = create_a ( 0 ) ;
int err = - EINVAL ;
if ( a = = NULL ) {
printk ( " tca_action_flush: couldnt create tc_action \n " ) ;
return err ;
}
skb = alloc_skb ( NLMSG_GOODSIZE , GFP_KERNEL ) ;
if ( ! skb ) {
printk ( " tca_action_flush: failed skb alloc \n " ) ;
kfree ( a ) ;
return - ENOBUFS ;
}
2007-04-20 07:29:13 +04:00
b = skb_tail_pointer ( skb ) ;
2005-04-17 02:20:36 +04:00
2008-01-24 07:33:32 +03:00
err = nla_parse_nested ( tb , TCA_ACT_MAX , nla , NULL ) ;
if ( err < 0 )
2005-04-17 02:20:36 +04:00
goto err_out ;
2008-01-24 07:33:32 +03:00
err = - EINVAL ;
2008-01-23 09:11:50 +03:00
kind = tb [ TCA_ACT_KIND ] ;
2005-04-17 02:20:36 +04:00
a - > ops = tc_lookup_action ( kind ) ;
if ( a - > ops = = NULL )
goto err_out ;
nlh = NLMSG_PUT ( skb , pid , n - > nlmsg_seq , RTM_DELACTION , sizeof ( * t ) ) ;
t = NLMSG_DATA ( nlh ) ;
t - > tca_family = AF_UNSPEC ;
2005-06-28 23:55:30 +04:00
t - > tca__pad1 = 0 ;
t - > tca__pad2 = 0 ;
2005-04-17 02:20:36 +04:00
2008-01-24 07:34:11 +03:00
nest = nla_nest_start ( skb , TCA_ACT_TAB ) ;
if ( nest = = NULL )
goto nla_put_failure ;
2005-04-17 02:20:36 +04:00
err = a - > ops - > walk ( skb , & dcb , RTM_DELACTION , a ) ;
if ( err < 0 )
2008-01-23 09:11:50 +03:00
goto nla_put_failure ;
2005-04-17 02:20:36 +04:00
2008-01-24 07:34:11 +03:00
nla_nest_end ( skb , nest ) ;
2005-04-17 02:20:36 +04:00
2007-04-20 07:29:13 +04:00
nlh - > nlmsg_len = skb_tail_pointer ( skb ) - b ;
2005-04-17 02:20:36 +04:00
nlh - > nlmsg_flags | = NLM_F_ROOT ;
module_put ( a - > ops - > owner ) ;
kfree ( a ) ;
2007-11-20 09:26:51 +03:00
err = rtnetlink_send ( skb , & init_net , pid , RTNLGRP_TC , n - > nlmsg_flags & NLM_F_ECHO ) ;
2005-04-17 02:20:36 +04:00
if ( err > 0 )
return 0 ;
return err ;
2008-01-23 09:11:50 +03:00
nla_put_failure :
2005-04-17 02:20:36 +04:00
nlmsg_failure :
2006-07-09 22:36:23 +04:00
module_put ( a - > ops - > owner ) ;
2005-04-17 02:20:36 +04:00
err_out :
kfree_skb ( skb ) ;
kfree ( a ) ;
return err ;
}
static int
2008-01-23 09:11:50 +03:00
tca_action_gd ( struct nlattr * nla , struct nlmsghdr * n , u32 pid , int event )
2005-04-17 02:20:36 +04:00
{
2008-01-24 07:33:32 +03:00
int i , ret ;
2008-01-23 09:11:50 +03:00
struct nlattr * tb [ TCA_ACT_MAX_PRIO + 1 ] ;
2005-04-17 02:20:36 +04:00
struct tc_action * head = NULL , * act , * act_prev = NULL ;
2008-01-24 07:33:32 +03:00
ret = nla_parse_nested ( tb , TCA_ACT_MAX_PRIO , nla , NULL ) ;
if ( ret < 0 )
return ret ;
2005-04-17 02:20:36 +04:00
if ( event = = RTM_DELACTION & & n - > nlmsg_flags & NLM_F_ROOT ) {
if ( tb [ 0 ] ! = NULL & & tb [ 1 ] = = NULL )
return tca_action_flush ( tb [ 0 ] , n , pid ) ;
}
2008-01-23 09:11:50 +03:00
for ( i = 1 ; i < = TCA_ACT_MAX_PRIO & & tb [ i ] ; i + + ) {
2008-01-24 07:33:13 +03:00
act = tcf_action_get_1 ( tb [ i ] , n , pid ) ;
if ( IS_ERR ( act ) ) {
ret = PTR_ERR ( act ) ;
2005-04-17 02:20:36 +04:00
goto err ;
2008-01-24 07:33:13 +03:00
}
2008-01-23 09:11:50 +03:00
act - > order = i ;
2005-04-17 02:20:36 +04:00
if ( head = = NULL )
head = act ;
else
act_prev - > next = act ;
act_prev = act ;
}
if ( event = = RTM_GETACTION )
ret = act_get_notify ( pid , n , head , event ) ;
else { /* delete */
struct sk_buff * skb ;
skb = alloc_skb ( NLMSG_GOODSIZE , GFP_KERNEL ) ;
if ( ! skb ) {
ret = - ENOBUFS ;
goto err ;
}
if ( tca_get_fill ( skb , head , pid , n - > nlmsg_seq , 0 , event ,
2007-02-09 17:25:16 +03:00
0 , 1 ) < = 0 ) {
2005-04-17 02:20:36 +04:00
kfree_skb ( skb ) ;
ret = - EINVAL ;
goto err ;
}
/* now do the delete */
tcf_action_destroy ( head , 0 ) ;
2007-11-20 09:26:51 +03:00
ret = rtnetlink_send ( skb , & init_net , pid , RTNLGRP_TC ,
2007-02-09 17:25:16 +03:00
n - > nlmsg_flags & NLM_F_ECHO ) ;
2005-04-17 02:20:36 +04:00
if ( ret > 0 )
return 0 ;
return ret ;
}
err :
cleanup_a ( head ) ;
return ret ;
}
static int tcf_add_notify ( struct tc_action * a , u32 pid , u32 seq , int event ,
2007-02-09 17:25:16 +03:00
u16 flags )
2005-04-17 02:20:36 +04:00
{
struct tcamsg * t ;
struct nlmsghdr * nlh ;
struct sk_buff * skb ;
2008-01-24 07:34:11 +03:00
struct nlattr * nest ;
2005-04-17 02:20:36 +04:00
unsigned char * b ;
int err = 0 ;
skb = alloc_skb ( NLMSG_GOODSIZE , GFP_KERNEL ) ;
if ( ! skb )
return - ENOBUFS ;
2007-04-20 07:29:13 +04:00
b = skb_tail_pointer ( skb ) ;
2005-04-17 02:20:36 +04:00
2005-06-19 09:55:31 +04:00
nlh = NLMSG_NEW ( skb , pid , seq , event , sizeof ( * t ) , flags ) ;
2005-04-17 02:20:36 +04:00
t = NLMSG_DATA ( nlh ) ;
t - > tca_family = AF_UNSPEC ;
2005-06-28 23:55:30 +04:00
t - > tca__pad1 = 0 ;
t - > tca__pad2 = 0 ;
2008-01-24 07:34:11 +03:00
nest = nla_nest_start ( skb , TCA_ACT_TAB ) ;
if ( nest = = NULL )
goto nla_put_failure ;
2005-04-17 02:20:36 +04:00
if ( tcf_action_dump ( skb , a , 0 , 0 ) < 0 )
2008-01-23 09:11:50 +03:00
goto nla_put_failure ;
2005-04-17 02:20:36 +04:00
2008-01-24 07:34:11 +03:00
nla_nest_end ( skb , nest ) ;
2007-02-09 17:25:16 +03:00
2007-04-20 07:29:13 +04:00
nlh - > nlmsg_len = skb_tail_pointer ( skb ) - b ;
2005-08-15 06:29:52 +04:00
NETLINK_CB ( skb ) . dst_group = RTNLGRP_TC ;
2007-02-09 17:25:16 +03:00
2007-11-20 09:26:51 +03:00
err = rtnetlink_send ( skb , & init_net , pid , RTNLGRP_TC , flags & NLM_F_ECHO ) ;
2005-04-17 02:20:36 +04:00
if ( err > 0 )
err = 0 ;
return err ;
2008-01-23 09:11:50 +03:00
nla_put_failure :
2005-04-17 02:20:36 +04:00
nlmsg_failure :
2006-03-13 07:33:22 +03:00
kfree_skb ( skb ) ;
2005-04-17 02:20:36 +04:00
return - 1 ;
}
2007-02-09 17:25:16 +03:00
2005-04-17 02:20:36 +04:00
static int
2008-01-23 09:11:50 +03:00
tcf_action_add ( struct nlattr * nla , struct nlmsghdr * n , u32 pid , int ovr )
2005-04-17 02:20:36 +04:00
{
int ret = 0 ;
struct tc_action * act ;
struct tc_action * a ;
u32 seq = n - > nlmsg_seq ;
2008-01-24 07:33:13 +03:00
act = tcf_action_init ( nla , NULL , NULL , ovr , 0 ) ;
2005-04-17 02:20:36 +04:00
if ( act = = NULL )
goto done ;
2008-01-24 07:33:13 +03:00
if ( IS_ERR ( act ) ) {
ret = PTR_ERR ( act ) ;
goto done ;
}
2005-04-17 02:20:36 +04:00
/* dump then free all the actions after update; inserted policy
* stays intact
* */
ret = tcf_add_notify ( act , pid , seq , RTM_NEWACTION , n - > nlmsg_flags ) ;
for ( a = act ; a ; a = act ) {
act = a - > next ;
kfree ( a ) ;
}
done :
return ret ;
}
static int tc_ctl_action ( struct sk_buff * skb , struct nlmsghdr * n , void * arg )
{
2008-03-25 20:26:21 +03:00
struct net * net = sock_net ( skb - > sk ) ;
2008-01-23 09:11:50 +03:00
struct nlattr * tca [ TCA_ACT_MAX + 1 ] ;
2005-04-17 02:20:36 +04:00
u32 pid = skb ? NETLINK_CB ( skb ) . pid : 0 ;
int ret = 0 , ovr = 0 ;
2007-11-30 16:21:31 +03:00
if ( net ! = & init_net )
return - EINVAL ;
2008-01-23 09:11:50 +03:00
ret = nlmsg_parse ( n , sizeof ( struct tcamsg ) , tca , TCA_ACT_MAX , NULL ) ;
if ( ret < 0 )
return ret ;
if ( tca [ TCA_ACT_TAB ] = = NULL ) {
2005-04-17 02:20:36 +04:00
printk ( " tc_ctl_action: received NO action attribs \n " ) ;
return - EINVAL ;
}
/* n->nlmsg_flags&NLM_F_CREATE
* */
switch ( n - > nlmsg_type ) {
case RTM_NEWACTION :
/* we are going to assume all other flags
* imply create only if it doesnt exist
* Note that CREATE | EXCL implies that
* but since we want avoid ambiguity ( eg when flags
* is zero ) then just set this
*/
if ( n - > nlmsg_flags & NLM_F_REPLACE )
ovr = 1 ;
replay :
2008-01-23 09:11:50 +03:00
ret = tcf_action_add ( tca [ TCA_ACT_TAB ] , n , pid , ovr ) ;
2005-04-17 02:20:36 +04:00
if ( ret = = - EAGAIN )
goto replay ;
break ;
case RTM_DELACTION :
2008-01-23 09:11:50 +03:00
ret = tca_action_gd ( tca [ TCA_ACT_TAB ] , n , pid , RTM_DELACTION ) ;
2005-04-17 02:20:36 +04:00
break ;
case RTM_GETACTION :
2008-01-23 09:11:50 +03:00
ret = tca_action_gd ( tca [ TCA_ACT_TAB ] , n , pid , RTM_GETACTION ) ;
2005-04-17 02:20:36 +04:00
break ;
default :
BUG ( ) ;
}
return ret ;
}
2008-01-23 09:11:50 +03:00
static struct nlattr *
2005-04-17 02:20:36 +04:00
find_dump_kind ( struct nlmsghdr * n )
{
2008-01-23 09:11:50 +03:00
struct nlattr * tb1 , * tb2 [ TCA_ACT_MAX + 1 ] ;
struct nlattr * tb [ TCA_ACT_MAX_PRIO + 1 ] ;
struct nlattr * nla [ TCAA_MAX + 1 ] ;
struct nlattr * kind ;
2005-04-17 02:20:36 +04:00
2008-01-24 07:32:58 +03:00
if ( nlmsg_parse ( n , sizeof ( struct tcamsg ) , nla , TCAA_MAX , NULL ) < 0 )
2005-04-17 02:20:36 +04:00
return NULL ;
2008-01-23 09:11:50 +03:00
tb1 = nla [ TCA_ACT_TAB ] ;
2005-04-17 02:20:36 +04:00
if ( tb1 = = NULL )
return NULL ;
2008-01-23 09:11:50 +03:00
if ( nla_parse ( tb , TCA_ACT_MAX_PRIO , nla_data ( tb1 ) ,
NLMSG_ALIGN ( nla_len ( tb1 ) ) , NULL ) < 0 )
2005-04-17 02:20:36 +04:00
return NULL ;
2008-01-24 07:32:42 +03:00
if ( tb [ 1 ] = = NULL )
return NULL ;
if ( nla_parse ( tb2 , TCA_ACT_MAX , nla_data ( tb [ 1 ] ) ,
nla_len ( tb [ 1 ] ) , NULL ) < 0 )
2005-04-17 02:20:36 +04:00
return NULL ;
2008-01-23 09:11:50 +03:00
kind = tb2 [ TCA_ACT_KIND ] ;
2005-04-17 02:20:36 +04:00
2006-07-06 07:45:06 +04:00
return kind ;
2005-04-17 02:20:36 +04:00
}
static int
tc_dump_action ( struct sk_buff * skb , struct netlink_callback * cb )
{
2008-03-25 20:26:21 +03:00
struct net * net = sock_net ( skb - > sk ) ;
2005-04-17 02:20:36 +04:00
struct nlmsghdr * nlh ;
2007-04-20 07:29:13 +04:00
unsigned char * b = skb_tail_pointer ( skb ) ;
2008-01-24 07:34:11 +03:00
struct nlattr * nest ;
2005-04-17 02:20:36 +04:00
struct tc_action_ops * a_o ;
struct tc_action a ;
int ret = 0 ;
struct tcamsg * t = ( struct tcamsg * ) NLMSG_DATA ( cb - > nlh ) ;
2008-01-23 09:11:50 +03:00
struct nlattr * kind = find_dump_kind ( cb - > nlh ) ;
2005-04-17 02:20:36 +04:00
2007-11-30 16:21:31 +03:00
if ( net ! = & init_net )
return 0 ;
2005-04-17 02:20:36 +04:00
if ( kind = = NULL ) {
printk ( " tc_dump_action: action bad kind \n " ) ;
return 0 ;
}
2006-07-06 07:45:06 +04:00
a_o = tc_lookup_action ( kind ) ;
2005-04-17 02:20:36 +04:00
if ( a_o = = NULL ) {
return 0 ;
}
memset ( & a , 0 , sizeof ( struct tc_action ) ) ;
a . ops = a_o ;
if ( a_o - > walk = = NULL ) {
2006-07-06 07:45:06 +04:00
printk ( " tc_dump_action: %s !capable of dumping table \n " , a_o - > kind ) ;
2008-01-23 09:11:50 +03:00
goto nla_put_failure ;
2005-04-17 02:20:36 +04:00
}
nlh = NLMSG_PUT ( skb , NETLINK_CB ( cb - > skb ) . pid , cb - > nlh - > nlmsg_seq ,
2007-02-09 17:25:16 +03:00
cb - > nlh - > nlmsg_type , sizeof ( * t ) ) ;
2005-04-17 02:20:36 +04:00
t = NLMSG_DATA ( nlh ) ;
t - > tca_family = AF_UNSPEC ;
2005-06-28 23:55:30 +04:00
t - > tca__pad1 = 0 ;
t - > tca__pad2 = 0 ;
2005-04-17 02:20:36 +04:00
2008-01-24 07:34:11 +03:00
nest = nla_nest_start ( skb , TCA_ACT_TAB ) ;
if ( nest = = NULL )
goto nla_put_failure ;
2005-04-17 02:20:36 +04:00
ret = a_o - > walk ( skb , cb , RTM_GETACTION , & a ) ;
if ( ret < 0 )
2008-01-23 09:11:50 +03:00
goto nla_put_failure ;
2005-04-17 02:20:36 +04:00
if ( ret > 0 ) {
2008-01-24 07:34:11 +03:00
nla_nest_end ( skb , nest ) ;
2005-04-17 02:20:36 +04:00
ret = skb - > len ;
} else
2008-01-24 07:34:11 +03:00
nla_nest_cancel ( skb , nest ) ;
2005-04-17 02:20:36 +04:00
2007-04-20 07:29:13 +04:00
nlh - > nlmsg_len = skb_tail_pointer ( skb ) - b ;
2005-04-17 02:20:36 +04:00
if ( NETLINK_CB ( cb - > skb ) . pid & & ret )
nlh - > nlmsg_flags | = NLM_F_MULTI ;
module_put ( a_o - > owner ) ;
return skb - > len ;
2008-01-23 09:11:50 +03:00
nla_put_failure :
2005-04-17 02:20:36 +04:00
nlmsg_failure :
module_put ( a_o - > owner ) ;
2007-03-26 10:06:12 +04:00
nlmsg_trim ( skb , b ) ;
2005-04-17 02:20:36 +04:00
return skb - > len ;
}
static int __init tc_action_init ( void )
{
2007-03-22 21:56:59 +03:00
rtnl_register ( PF_UNSPEC , RTM_NEWACTION , tc_ctl_action , NULL ) ;
rtnl_register ( PF_UNSPEC , RTM_DELACTION , tc_ctl_action , NULL ) ;
rtnl_register ( PF_UNSPEC , RTM_GETACTION , tc_ctl_action , tc_dump_action ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
subsys_initcall ( tc_action_init ) ;