2005-04-16 15:20:36 -07:00
# ifndef __NET_ACT_API_H
# define __NET_ACT_API_H
/*
* Public police action API for classifiers / qdiscs
*/
# include <net/sch_generic.h>
# include <net/pkt_sched.h>
2016-02-22 15:57:53 -08:00
# include <net/net_namespace.h>
# include <net/netns/generic.h>
2005-04-16 15:20:36 -07:00
2006-08-21 23:54:55 -07:00
struct tcf_common {
2013-12-15 20:15:09 -08:00
struct hlist_node tcfc_head ;
2006-08-21 23:54:55 -07:00
u32 tcfc_index ;
int tcfc_refcnt ;
int tcfc_bindcnt ;
u32 tcfc_capab ;
int tcfc_action ;
struct tcf_t tcfc_tm ;
2009-08-16 09:36:49 +00:00
struct gnet_stats_basic_packed tcfc_bstats ;
2006-08-21 23:54:55 -07:00
struct gnet_stats_queue tcfc_qstats ;
2013-06-06 08:43:22 -07:00
struct gnet_stats_rate_est64 tcfc_rate_est ;
2006-08-21 23:54:55 -07:00
spinlock_t tcfc_lock ;
2010-06-09 02:09:23 +00:00
struct rcu_head tcfc_rcu ;
2015-07-06 05:18:04 -07:00
struct gnet_stats_basic_cpu __percpu * cpu_bstats ;
struct gnet_stats_queue __percpu * cpu_qstats ;
2006-08-21 23:54:55 -07:00
} ;
2013-12-15 20:15:09 -08:00
# define tcf_head common.tcfc_head
2006-08-21 23:54:55 -07:00
# define tcf_index common.tcfc_index
# define tcf_refcnt common.tcfc_refcnt
# define tcf_bindcnt common.tcfc_bindcnt
# define tcf_capab common.tcfc_capab
# define tcf_action common.tcfc_action
# define tcf_tm common.tcfc_tm
# define tcf_bstats common.tcfc_bstats
# define tcf_qstats common.tcfc_qstats
# define tcf_rate_est common.tcfc_rate_est
# define tcf_lock common.tcfc_lock
2010-06-09 02:09:23 +00:00
# define tcf_rcu common.tcfc_rcu
2006-08-21 23:54:55 -07:00
struct tcf_hashinfo {
2013-12-15 20:15:09 -08:00
struct hlist_head * htab ;
2006-08-21 23:54:55 -07:00
unsigned int hmask ;
2013-12-15 20:15:09 -08:00
spinlock_t lock ;
2014-01-09 16:13:59 -08:00
u32 index ;
2006-08-21 23:54:55 -07:00
} ;
static inline unsigned int tcf_hash ( u32 index , unsigned int hmask )
{
return index & hmask ;
}
2005-04-16 15:20:36 -07:00
2013-12-15 20:15:08 -08:00
static inline int tcf_hashinfo_init ( struct tcf_hashinfo * hf , unsigned int mask )
{
2013-12-15 20:15:09 -08:00
int i ;
spin_lock_init ( & hf - > lock ) ;
2014-01-09 16:13:59 -08:00
hf - > index = 0 ;
2013-12-15 20:15:08 -08:00
hf - > hmask = mask ;
2013-12-15 20:15:09 -08:00
hf - > htab = kzalloc ( ( mask + 1 ) * sizeof ( struct hlist_head ) ,
2013-12-15 20:15:08 -08:00
GFP_KERNEL ) ;
if ( ! hf - > htab )
return - ENOMEM ;
2013-12-15 20:15:09 -08:00
for ( i = 0 ; i < mask + 1 ; i + + )
INIT_HLIST_HEAD ( & hf - > htab [ i ] ) ;
2013-12-15 20:15:08 -08:00
return 0 ;
}
2015-07-06 05:18:08 -07:00
/* Update lastuse only if needed, to avoid dirtying a cache line.
* We use a temp variable to avoid fetching jiffies twice .
*/
static inline void tcf_lastuse_update ( struct tcf_t * tm )
{
unsigned long now = jiffies ;
if ( tm - > lastuse ! = now )
tm - > lastuse = now ;
}
2006-08-21 23:54:55 -07:00
struct tc_action {
void * priv ;
2011-07-05 23:25:42 +00:00
const struct tc_action_ops * ops ;
2006-08-21 23:54:55 -07:00
__u32 type ; /* for backward compat(TCA_OLD_COMPAT) */
__u32 order ;
2013-12-15 20:15:05 -08:00
struct list_head list ;
2016-02-22 15:57:53 -08:00
struct tcf_hashinfo * hinfo ;
2005-04-16 15:20:36 -07:00
} ;
2016-03-08 12:42:31 +02:00
# ifdef CONFIG_NET_CLS_ACT
# define ACT_P_CREATED 1
# define ACT_P_DELETED 1
2006-08-21 23:54:55 -07:00
struct tc_action_ops {
2013-12-15 20:15:10 -08:00
struct list_head head ;
2005-04-16 15:20:36 -07:00
char kind [ IFNAMSIZ ] ;
__u32 type ; /* TBD to match kind */
struct module * owner ;
2011-07-05 23:25:42 +00:00
int ( * act ) ( struct sk_buff * , const struct tc_action * , struct tcf_result * ) ;
2006-08-21 23:54:55 -07:00
int ( * dump ) ( struct sk_buff * , struct tc_action * , int , int ) ;
2014-02-11 17:07:32 -08:00
void ( * cleanup ) ( struct tc_action * , int bind ) ;
2016-02-22 15:57:53 -08:00
int ( * lookup ) ( struct net * , struct tc_action * , u32 ) ;
2013-01-14 05:15:39 +00:00
int ( * init ) ( struct net * net , struct nlattr * nla ,
struct nlattr * est , struct tc_action * act , int ovr ,
int bind ) ;
2016-02-22 15:57:53 -08:00
int ( * walk ) ( struct net * , struct sk_buff * ,
struct netlink_callback * , int , struct tc_action * ) ;
} ;
struct tc_action_net {
struct tcf_hashinfo * hinfo ;
const struct tc_action_ops * ops ;
2005-04-16 15:20:36 -07:00
} ;
2016-02-22 15:57:53 -08:00
static inline
int tc_action_net_init ( struct tc_action_net * tn , const struct tc_action_ops * ops ,
unsigned int mask )
{
int err = 0 ;
tn - > hinfo = kmalloc ( sizeof ( * tn - > hinfo ) , GFP_KERNEL ) ;
if ( ! tn - > hinfo )
return - ENOMEM ;
tn - > ops = ops ;
err = tcf_hashinfo_init ( tn - > hinfo , mask ) ;
if ( err )
kfree ( tn - > hinfo ) ;
return err ;
}
void tcf_hashinfo_destroy ( const struct tc_action_ops * ops ,
struct tcf_hashinfo * hinfo ) ;
static inline void tc_action_net_exit ( struct tc_action_net * tn )
{
tcf_hashinfo_destroy ( tn - > ops , tn - > hinfo ) ;
2016-04-04 10:32:48 -07:00
kfree ( tn - > hinfo ) ;
2016-02-22 15:57:53 -08:00
}
int tcf_generic_walker ( struct tc_action_net * tn , struct sk_buff * skb ,
struct netlink_callback * cb , int type ,
struct tc_action * a ) ;
int tcf_hash_search ( struct tc_action_net * tn , struct tc_action * a , u32 index ) ;
u32 tcf_hash_new_index ( struct tc_action_net * tn ) ;
int tcf_hash_check ( struct tc_action_net * tn , u32 index , struct tc_action * a ,
int bind ) ;
int tcf_hash_create ( struct tc_action_net * tn , u32 index , struct nlattr * est ,
struct tc_action * a , int size , int bind , bool cpustats ) ;
2014-02-11 17:07:31 -08:00
void tcf_hash_cleanup ( struct tc_action * a , struct nlattr * est ) ;
2016-02-22 15:57:53 -08:00
void tcf_hash_insert ( struct tc_action_net * tn , struct tc_action * a ) ;
2006-08-21 23:54:55 -07:00
net: sched: fix refcount imbalance in actions
Since commit 55334a5db5cd ("net_sched: act: refuse to remove bound action
outside"), we end up with a wrong reference count for a tc action.
Test case 1:
FOO="1,6 0 0 4294967295,"
BAR="1,6 0 0 4294967294,"
tc filter add dev foo parent 1: bpf bytecode "$FOO" flowid 1:1 \
action bpf bytecode "$FOO"
tc actions show action bpf
action order 0: bpf bytecode '1,6 0 0 4294967295' default-action pipe
index 1 ref 1 bind 1
tc actions replace action bpf bytecode "$BAR" index 1
tc actions show action bpf
action order 0: bpf bytecode '1,6 0 0 4294967294' default-action pipe
index 1 ref 2 bind 1
tc actions replace action bpf bytecode "$FOO" index 1
tc actions show action bpf
action order 0: bpf bytecode '1,6 0 0 4294967295' default-action pipe
index 1 ref 3 bind 1
Test case 2:
FOO="1,6 0 0 4294967295,"
tc filter add dev foo parent 1: bpf bytecode "$FOO" flowid 1:1 action ok
tc actions show action gact
action order 0: gact action pass
random type none pass val 0
index 1 ref 1 bind 1
tc actions add action drop index 1
RTNETLINK answers: File exists [...]
tc actions show action gact
action order 0: gact action pass
random type none pass val 0
index 1 ref 2 bind 1
tc actions add action drop index 1
RTNETLINK answers: File exists [...]
tc actions show action gact
action order 0: gact action pass
random type none pass val 0
index 1 ref 3 bind 1
What happens is that in tcf_hash_check(), we check tcf_common for a given
index and increase tcfc_refcnt and conditionally tcfc_bindcnt when we've
found an existing action. Now there are the following cases:
1) We do a late binding of an action. In that case, we leave the
tcfc_refcnt/tcfc_bindcnt increased and are done with the ->init()
handler. This is correctly handeled.
2) We replace the given action, or we try to add one without replacing
and find out that the action at a specific index already exists
(thus, we go out with error in that case).
In case of 2), we have to undo the reference count increase from
tcf_hash_check() in the tcf_hash_check() function. Currently, we fail to
do so because of the 'tcfc_bindcnt > 0' check which bails out early with
an -EPERM error.
Now, while commit 55334a5db5cd prevents 'tc actions del action ...' on an
already classifier-bound action to drop the reference count (which could
then become negative, wrap around etc), this restriction only accounts for
invocations outside a specific action's ->init() handler.
One possible solution would be to add a flag thus we possibly trigger
the -EPERM ony in situations where it is indeed relevant.
After the patch, above test cases have correct reference count again.
Fixes: 55334a5db5cd ("net_sched: act: refuse to remove bound action outside")
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Reviewed-by: Cong Wang <cwang@twopensource.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2015-07-29 23:35:25 +02:00
int __tcf_hash_release ( struct tc_action * a , bool bind , bool strict ) ;
static inline int tcf_hash_release ( struct tc_action * a , bool bind )
{
return __tcf_hash_release ( a , bind , false ) ;
}
2016-02-22 15:57:53 -08:00
int tcf_register_action ( struct tc_action_ops * a , struct pernet_operations * ops ) ;
int tcf_unregister_action ( struct tc_action_ops * a , struct pernet_operations * ops ) ;
2014-02-11 17:07:34 -08:00
int tcf_action_destroy ( struct list_head * actions , int bind ) ;
2013-12-15 20:15:05 -08:00
int tcf_action_exec ( struct sk_buff * skb , const struct list_head * actions ,
2013-07-30 22:47:13 -07:00
struct tcf_result * res ) ;
2013-12-15 20:15:05 -08:00
int tcf_action_init ( struct net * net , struct nlattr * nla ,
2013-07-30 22:47:13 -07:00
struct nlattr * est , char * n , int ovr ,
2013-12-15 20:15:05 -08:00
int bind , struct list_head * ) ;
2013-07-30 22:47:13 -07:00
struct tc_action * tcf_action_init_1 ( struct net * net , struct nlattr * nla ,
struct nlattr * est , char * n , int ovr ,
int bind ) ;
2013-12-15 20:15:05 -08:00
int tcf_action_dump ( struct sk_buff * skb , struct list_head * , int , int ) ;
2013-07-30 22:47:13 -07:00
int tcf_action_dump_old ( struct sk_buff * skb , struct tc_action * a , int , int ) ;
int tcf_action_dump_1 ( struct sk_buff * skb , struct tc_action * a , int , int ) ;
int tcf_action_copy_stats ( struct sk_buff * , struct tc_action * , int ) ;
2016-03-08 12:42:31 +02:00
# define tc_no_actions(_exts) \
( list_empty ( & ( _exts ) - > actions ) )
# define tc_for_each_action(_a, _exts) \
list_for_each_entry ( a , & ( _exts ) - > actions , list )
# else /* CONFIG_NET_CLS_ACT */
# define tc_no_actions(_exts) true
# define tc_for_each_action(_a, _exts) while (0)
2005-04-16 15:20:36 -07:00
# endif /* CONFIG_NET_CLS_ACT */
# endif