2005-04-17 02:20:36 +04:00
# ifndef __NET_ACT_API_H
# define __NET_ACT_API_H
/*
2016-06-05 17:41:32 +03:00
* Public action API for classifiers / qdiscs
*/
2005-04-17 02:20:36 +04:00
# include <net/sch_generic.h>
# include <net/pkt_sched.h>
2016-02-23 02:57:53 +03:00
# include <net/net_namespace.h>
# include <net/netns/generic.h>
2005-04-17 02:20:36 +04:00
2016-07-26 02:09:41 +03:00
struct tcf_hashinfo {
struct hlist_head * htab ;
unsigned int hmask ;
spinlock_t lock ;
u32 index ;
} ;
struct tc_action_ops ;
struct tc_action {
const struct tc_action_ops * ops ;
2016-07-26 02:09:42 +03:00
__u32 type ; /* for backward compat(TCA_OLD_COMPAT) */
__u32 order ;
struct list_head list ;
struct tcf_hashinfo * hinfo ;
2016-07-26 02:09:41 +03:00
2016-07-26 02:09:42 +03:00
struct hlist_node tcfa_head ;
u32 tcfa_index ;
int tcfa_refcnt ;
int tcfa_bindcnt ;
u32 tcfa_capab ;
int tcfa_action ;
struct tcf_t tcfa_tm ;
struct gnet_stats_basic_packed tcfa_bstats ;
struct gnet_stats_queue tcfa_qstats ;
2016-12-04 20:48:16 +03:00
struct net_rate_estimator __rcu * tcfa_rate_est ;
2016-07-26 02:09:42 +03:00
spinlock_t tcfa_lock ;
struct rcu_head tcfa_rcu ;
2015-07-06 15:18:04 +03:00
struct gnet_stats_basic_cpu __percpu * cpu_bstats ;
struct gnet_stats_queue __percpu * cpu_qstats ;
2017-01-24 15:02:41 +03:00
struct tc_cookie * act_cookie ;
2006-08-22 10:54:55 +04:00
} ;
2016-07-26 02:09:42 +03:00
# define tcf_head common.tcfa_head
# define tcf_index common.tcfa_index
# define tcf_refcnt common.tcfa_refcnt
# define tcf_bindcnt common.tcfa_bindcnt
# define tcf_capab common.tcfa_capab
# define tcf_action common.tcfa_action
# define tcf_tm common.tcfa_tm
# define tcf_bstats common.tcfa_bstats
# define tcf_qstats common.tcfa_qstats
# define tcf_rate_est common.tcfa_rate_est
# define tcf_lock common.tcfa_lock
# define tcf_rcu common.tcfa_rcu
2006-08-22 10:54:55 +04:00
static inline unsigned int tcf_hash ( u32 index , unsigned int hmask )
{
return index & hmask ;
}
2005-04-17 02:20:36 +04:00
2013-12-16 08:15:08 +04:00
static inline int tcf_hashinfo_init ( struct tcf_hashinfo * hf , unsigned int mask )
{
2013-12-16 08:15:09 +04:00
int i ;
spin_lock_init ( & hf - > lock ) ;
2014-01-10 04:13:59 +04:00
hf - > index = 0 ;
2013-12-16 08:15:08 +04:00
hf - > hmask = mask ;
2013-12-16 08:15:09 +04:00
hf - > htab = kzalloc ( ( mask + 1 ) * sizeof ( struct hlist_head ) ,
2013-12-16 08:15:08 +04:00
GFP_KERNEL ) ;
if ( ! hf - > htab )
return - ENOMEM ;
2013-12-16 08:15:09 +04:00
for ( i = 0 ; i < mask + 1 ; i + + )
INIT_HLIST_HEAD ( & hf - > htab [ i ] ) ;
2013-12-16 08:15:08 +04:00
return 0 ;
}
2015-07-06 15:18:08 +03: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 ;
2016-06-06 13:32:54 +03:00
if ( unlikely ( ! tm - > firstuse ) )
tm - > firstuse = now ;
2015-07-06 15:18:08 +03:00
}
2016-06-06 13:32:55 +03:00
static inline void tcf_tm_dump ( struct tcf_t * dtm , const struct tcf_t * stm )
{
dtm - > install = jiffies_to_clock_t ( jiffies - stm - > install ) ;
dtm - > lastuse = jiffies_to_clock_t ( jiffies - stm - > lastuse ) ;
dtm - > firstuse = jiffies_to_clock_t ( jiffies - stm - > firstuse ) ;
dtm - > expires = jiffies_to_clock_t ( stm - > expires ) ;
}
2016-03-08 13:42:31 +03:00
# ifdef CONFIG_NET_CLS_ACT
# define ACT_P_CREATED 1
# define ACT_P_DELETED 1
2006-08-22 10:54:55 +04:00
struct tc_action_ops {
2013-12-16 08:15:10 +04:00
struct list_head head ;
2005-04-17 02:20:36 +04:00
char kind [ IFNAMSIZ ] ;
__u32 type ; /* TBD to match kind */
2016-07-26 02:09:41 +03:00
size_t size ;
2005-04-17 02:20:36 +04:00
struct module * owner ;
2016-06-05 17:41:32 +03:00
int ( * act ) ( struct sk_buff * , const struct tc_action * ,
struct tcf_result * ) ;
2006-08-22 10:54:55 +04:00
int ( * dump ) ( struct sk_buff * , struct tc_action * , int , int ) ;
2014-02-12 05:07:32 +04:00
void ( * cleanup ) ( struct tc_action * , int bind ) ;
2016-07-26 02:09:41 +03:00
int ( * lookup ) ( struct net * , struct tc_action * * , u32 ) ;
2013-01-14 09:15:39 +04:00
int ( * init ) ( struct net * net , struct nlattr * nla ,
2016-07-26 02:09:41 +03:00
struct nlattr * est , struct tc_action * * act , int ovr ,
2013-01-14 09:15:39 +04:00
int bind ) ;
2016-02-23 02:57:53 +03:00
int ( * walk ) ( struct net * , struct sk_buff * ,
2016-07-26 02:09:41 +03:00
struct netlink_callback * , int , const struct tc_action_ops * ) ;
2016-05-13 15:55:35 +03:00
void ( * stats_update ) ( struct tc_action * , u64 , u32 , u64 ) ;
2016-12-01 15:06:36 +03:00
int ( * get_dev ) ( const struct tc_action * a , struct net * net ,
struct net_device * * mirred_dev ) ;
2016-02-23 02:57:53 +03:00
} ;
struct tc_action_net {
struct tcf_hashinfo * hinfo ;
const struct tc_action_ops * ops ;
2005-04-17 02:20:36 +04:00
} ;
2016-02-23 02:57:53 +03:00
static inline
2016-06-05 17:41:32 +03:00
int tc_action_net_init ( struct tc_action_net * tn ,
const struct tc_action_ops * ops , unsigned int mask )
2016-02-23 02:57:53 +03:00
{
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 20:32:48 +03:00
kfree ( tn - > hinfo ) ;
2016-02-23 02:57:53 +03:00
}
int tcf_generic_walker ( struct tc_action_net * tn , struct sk_buff * skb ,
struct netlink_callback * cb , int type ,
2016-07-26 02:09:41 +03:00
const struct tc_action_ops * ops ) ;
int tcf_hash_search ( struct tc_action_net * tn , struct tc_action * * a , u32 index ) ;
2016-02-23 02:57:53 +03:00
u32 tcf_hash_new_index ( struct tc_action_net * tn ) ;
2016-07-26 02:09:41 +03:00
bool tcf_hash_check ( struct tc_action_net * tn , u32 index , struct tc_action * * a ,
2016-06-13 23:46:28 +03:00
int bind ) ;
2016-02-23 02:57:53 +03:00
int tcf_hash_create ( struct tc_action_net * tn , u32 index , struct nlattr * est ,
2016-07-26 02:09:41 +03:00
struct tc_action * * a , const struct tc_action_ops * ops , int bind ,
bool cpustats ) ;
2014-02-12 05:07:31 +04:00
void tcf_hash_cleanup ( struct tc_action * a , struct nlattr * est ) ;
2016-02-23 02:57:53 +03:00
void tcf_hash_insert ( struct tc_action_net * tn , struct tc_action * a ) ;
2006-08-22 10:54:55 +04: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-30 00:35:25 +03: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-23 02:57:53 +03:00
int tcf_register_action ( struct tc_action_ops * a , struct pernet_operations * ops ) ;
2016-06-05 17:41:32 +03:00
int tcf_unregister_action ( struct tc_action_ops * a ,
struct pernet_operations * ops ) ;
2014-02-12 05:07:34 +04:00
int tcf_action_destroy ( struct list_head * actions , int bind ) ;
2016-08-14 08:35:00 +03:00
int tcf_action_exec ( struct sk_buff * skb , struct tc_action * * actions ,
int nr_actions , struct tcf_result * res ) ;
2013-12-16 08:15:05 +04:00
int tcf_action_init ( struct net * net , struct nlattr * nla ,
2013-07-31 09:47:13 +04:00
struct nlattr * est , char * n , int ovr ,
2013-12-16 08:15:05 +04:00
int bind , struct list_head * ) ;
2013-07-31 09:47:13 +04: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-16 08:15:05 +04:00
int tcf_action_dump ( struct sk_buff * skb , struct list_head * , int , int ) ;
2013-07-31 09:47:13 +04: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 13:42:31 +03:00
2016-08-14 08:34:59 +03:00
# endif /* CONFIG_NET_CLS_ACT */
2016-07-25 13:12:33 +03:00
2016-05-13 15:55:35 +03:00
static inline void tcf_action_stats_update ( struct tc_action * a , u64 bytes ,
u64 packets , u64 lastuse )
{
2016-08-14 08:34:59 +03:00
# ifdef CONFIG_NET_CLS_ACT
2016-05-13 15:55:35 +03:00
if ( ! a - > ops - > stats_update )
return ;
a - > ops - > stats_update ( a , bytes , packets , lastuse ) ;
2016-08-14 08:34:59 +03:00
# endif
2016-05-13 15:55:35 +03:00
}
2005-04-17 02:20:36 +04:00
# endif