2015-01-15 11:52:39 +03:00
/*
* Copyright ( c ) 2015 Jiri Pirko < jiri @ resnulli . us >
*
* 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 .
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/skbuff.h>
# include <linux/rtnetlink.h>
# include <linux/filter.h>
2015-03-20 17:11:12 +03:00
# include <linux/bpf.h>
2015-01-15 11:52:39 +03:00
# include <net/netlink.h>
# include <net/pkt_sched.h>
# include <linux/tc_act/tc_bpf.h>
# include <net/tc_act/tc_bpf.h>
2015-03-20 17:11:12 +03:00
# define BPF_TAB_MASK 15
# define ACT_BPF_NAME_LEN 256
struct tcf_bpf_cfg {
struct bpf_prog * filter ;
struct sock_filter * bpf_ops ;
char * bpf_name ;
u32 bpf_fd ;
u16 bpf_num_ops ;
} ;
2015-01-15 11:52:39 +03:00
2015-03-20 17:11:12 +03:00
static int tcf_bpf ( struct sk_buff * skb , const struct tc_action * act ,
2015-01-15 11:52:39 +03:00
struct tcf_result * res )
{
2015-03-20 17:11:12 +03:00
struct tcf_bpf * prog = act - > priv ;
act_bpf: allow non-default TC_ACT opcodes as BPF exec outcome
Revisiting commit d23b8ad8ab23 ("tc: add BPF based action") with regards
to eBPF support, I was thinking that it might be better to improve
return semantics from a BPF program invoked through BPF_PROG_RUN().
Currently, in case filter_res is 0, we overwrite the default action
opcode with TC_ACT_SHOT. A default action opcode configured through tc's
m_bpf can be: TC_ACT_RECLASSIFY, TC_ACT_PIPE, TC_ACT_SHOT, TC_ACT_UNSPEC,
TC_ACT_OK.
In cls_bpf, we have the possibility to overwrite the default class
associated with the classifier in case filter_res is _not_ 0xffffffff
(-1).
That allows us to fold multiple [e]BPF programs into a single one, where
they would otherwise need to be defined as a separate classifier with
its own classid, needlessly redoing parsing work, etc.
Similarly, we could do better in act_bpf: Since above TC_ACT* opcodes
are exported to UAPI anyway, we reuse them for return-code-to-tc-opcode
mapping, where we would allow above possibilities. Thus, like in cls_bpf,
a filter_res of 0xffffffff (-1) means that the configured _default_ action
is used. Any unkown return code from the BPF program would fail in
tcf_bpf() with TC_ACT_UNSPEC.
Should we one day want to make use of TC_ACT_STOLEN or TC_ACT_QUEUED,
which both have the same semantics, we have the option to either use
that as a default action (filter_res of 0xffffffff) or non-default BPF
return code.
All that will allow us to transparently use tcf_bpf() for both BPF
flavours.
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Cc: Jiri Pirko <jiri@resnulli.us>
Cc: Alexei Starovoitov <ast@plumgrid.com>
Cc: Jamal Hadi Salim <jhs@mojatatu.com>
Acked-by: Jiri Pirko <jiri@resnulli.us>
Signed-off-by: David S. Miller <davem@davemloft.net>
2015-03-17 22:25:57 +03:00
int action , filter_res ;
2015-06-04 20:11:53 +03:00
bool at_ingress = G_TC_AT ( skb - > tc_verd ) & AT_INGRESS ;
2015-01-15 11:52:39 +03:00
2015-04-15 22:55:45 +03:00
if ( unlikely ( ! skb_mac_header_was_set ( skb ) ) )
return TC_ACT_UNSPEC ;
2015-03-20 17:11:12 +03:00
spin_lock ( & prog - > tcf_lock ) ;
act_bpf: allow non-default TC_ACT opcodes as BPF exec outcome
Revisiting commit d23b8ad8ab23 ("tc: add BPF based action") with regards
to eBPF support, I was thinking that it might be better to improve
return semantics from a BPF program invoked through BPF_PROG_RUN().
Currently, in case filter_res is 0, we overwrite the default action
opcode with TC_ACT_SHOT. A default action opcode configured through tc's
m_bpf can be: TC_ACT_RECLASSIFY, TC_ACT_PIPE, TC_ACT_SHOT, TC_ACT_UNSPEC,
TC_ACT_OK.
In cls_bpf, we have the possibility to overwrite the default class
associated with the classifier in case filter_res is _not_ 0xffffffff
(-1).
That allows us to fold multiple [e]BPF programs into a single one, where
they would otherwise need to be defined as a separate classifier with
its own classid, needlessly redoing parsing work, etc.
Similarly, we could do better in act_bpf: Since above TC_ACT* opcodes
are exported to UAPI anyway, we reuse them for return-code-to-tc-opcode
mapping, where we would allow above possibilities. Thus, like in cls_bpf,
a filter_res of 0xffffffff (-1) means that the configured _default_ action
is used. Any unkown return code from the BPF program would fail in
tcf_bpf() with TC_ACT_UNSPEC.
Should we one day want to make use of TC_ACT_STOLEN or TC_ACT_QUEUED,
which both have the same semantics, we have the option to either use
that as a default action (filter_res of 0xffffffff) or non-default BPF
return code.
All that will allow us to transparently use tcf_bpf() for both BPF
flavours.
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Cc: Jiri Pirko <jiri@resnulli.us>
Cc: Alexei Starovoitov <ast@plumgrid.com>
Cc: Jamal Hadi Salim <jhs@mojatatu.com>
Acked-by: Jiri Pirko <jiri@resnulli.us>
Signed-off-by: David S. Miller <davem@davemloft.net>
2015-03-17 22:25:57 +03:00
2015-03-20 17:11:12 +03:00
prog - > tcf_tm . lastuse = jiffies ;
bstats_update ( & prog - > tcf_bstats , skb ) ;
2015-01-15 11:52:39 +03:00
2015-03-20 17:11:12 +03:00
/* Needed here for accessing maps. */
rcu_read_lock ( ) ;
2015-06-04 20:11:53 +03:00
if ( at_ingress ) {
__skb_push ( skb , skb - > mac_len ) ;
filter_res = BPF_PROG_RUN ( prog - > filter , skb ) ;
__skb_pull ( skb , skb - > mac_len ) ;
} else {
filter_res = BPF_PROG_RUN ( prog - > filter , skb ) ;
}
2015-03-20 17:11:12 +03:00
rcu_read_unlock ( ) ;
act_bpf: allow non-default TC_ACT opcodes as BPF exec outcome
Revisiting commit d23b8ad8ab23 ("tc: add BPF based action") with regards
to eBPF support, I was thinking that it might be better to improve
return semantics from a BPF program invoked through BPF_PROG_RUN().
Currently, in case filter_res is 0, we overwrite the default action
opcode with TC_ACT_SHOT. A default action opcode configured through tc's
m_bpf can be: TC_ACT_RECLASSIFY, TC_ACT_PIPE, TC_ACT_SHOT, TC_ACT_UNSPEC,
TC_ACT_OK.
In cls_bpf, we have the possibility to overwrite the default class
associated with the classifier in case filter_res is _not_ 0xffffffff
(-1).
That allows us to fold multiple [e]BPF programs into a single one, where
they would otherwise need to be defined as a separate classifier with
its own classid, needlessly redoing parsing work, etc.
Similarly, we could do better in act_bpf: Since above TC_ACT* opcodes
are exported to UAPI anyway, we reuse them for return-code-to-tc-opcode
mapping, where we would allow above possibilities. Thus, like in cls_bpf,
a filter_res of 0xffffffff (-1) means that the configured _default_ action
is used. Any unkown return code from the BPF program would fail in
tcf_bpf() with TC_ACT_UNSPEC.
Should we one day want to make use of TC_ACT_STOLEN or TC_ACT_QUEUED,
which both have the same semantics, we have the option to either use
that as a default action (filter_res of 0xffffffff) or non-default BPF
return code.
All that will allow us to transparently use tcf_bpf() for both BPF
flavours.
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Cc: Jiri Pirko <jiri@resnulli.us>
Cc: Alexei Starovoitov <ast@plumgrid.com>
Cc: Jamal Hadi Salim <jhs@mojatatu.com>
Acked-by: Jiri Pirko <jiri@resnulli.us>
Signed-off-by: David S. Miller <davem@davemloft.net>
2015-03-17 22:25:57 +03:00
/* A BPF program may overwrite the default action opcode.
* Similarly as in cls_bpf , if filter_res = = - 1 we use the
* default action specified from tc .
*
* In case a different well - known TC_ACT opcode has been
* returned , it will overwrite the default one .
*
* For everything else that is unkown , TC_ACT_UNSPEC is
* returned .
*/
switch ( filter_res ) {
case TC_ACT_PIPE :
case TC_ACT_RECLASSIFY :
case TC_ACT_OK :
action = filter_res ;
break ;
case TC_ACT_SHOT :
action = filter_res ;
2015-03-20 17:11:12 +03:00
prog - > tcf_qstats . drops + + ;
act_bpf: allow non-default TC_ACT opcodes as BPF exec outcome
Revisiting commit d23b8ad8ab23 ("tc: add BPF based action") with regards
to eBPF support, I was thinking that it might be better to improve
return semantics from a BPF program invoked through BPF_PROG_RUN().
Currently, in case filter_res is 0, we overwrite the default action
opcode with TC_ACT_SHOT. A default action opcode configured through tc's
m_bpf can be: TC_ACT_RECLASSIFY, TC_ACT_PIPE, TC_ACT_SHOT, TC_ACT_UNSPEC,
TC_ACT_OK.
In cls_bpf, we have the possibility to overwrite the default class
associated with the classifier in case filter_res is _not_ 0xffffffff
(-1).
That allows us to fold multiple [e]BPF programs into a single one, where
they would otherwise need to be defined as a separate classifier with
its own classid, needlessly redoing parsing work, etc.
Similarly, we could do better in act_bpf: Since above TC_ACT* opcodes
are exported to UAPI anyway, we reuse them for return-code-to-tc-opcode
mapping, where we would allow above possibilities. Thus, like in cls_bpf,
a filter_res of 0xffffffff (-1) means that the configured _default_ action
is used. Any unkown return code from the BPF program would fail in
tcf_bpf() with TC_ACT_UNSPEC.
Should we one day want to make use of TC_ACT_STOLEN or TC_ACT_QUEUED,
which both have the same semantics, we have the option to either use
that as a default action (filter_res of 0xffffffff) or non-default BPF
return code.
All that will allow us to transparently use tcf_bpf() for both BPF
flavours.
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Cc: Jiri Pirko <jiri@resnulli.us>
Cc: Alexei Starovoitov <ast@plumgrid.com>
Cc: Jamal Hadi Salim <jhs@mojatatu.com>
Acked-by: Jiri Pirko <jiri@resnulli.us>
Signed-off-by: David S. Miller <davem@davemloft.net>
2015-03-17 22:25:57 +03:00
break ;
case TC_ACT_UNSPEC :
2015-03-20 17:11:12 +03:00
action = prog - > tcf_action ;
act_bpf: allow non-default TC_ACT opcodes as BPF exec outcome
Revisiting commit d23b8ad8ab23 ("tc: add BPF based action") with regards
to eBPF support, I was thinking that it might be better to improve
return semantics from a BPF program invoked through BPF_PROG_RUN().
Currently, in case filter_res is 0, we overwrite the default action
opcode with TC_ACT_SHOT. A default action opcode configured through tc's
m_bpf can be: TC_ACT_RECLASSIFY, TC_ACT_PIPE, TC_ACT_SHOT, TC_ACT_UNSPEC,
TC_ACT_OK.
In cls_bpf, we have the possibility to overwrite the default class
associated with the classifier in case filter_res is _not_ 0xffffffff
(-1).
That allows us to fold multiple [e]BPF programs into a single one, where
they would otherwise need to be defined as a separate classifier with
its own classid, needlessly redoing parsing work, etc.
Similarly, we could do better in act_bpf: Since above TC_ACT* opcodes
are exported to UAPI anyway, we reuse them for return-code-to-tc-opcode
mapping, where we would allow above possibilities. Thus, like in cls_bpf,
a filter_res of 0xffffffff (-1) means that the configured _default_ action
is used. Any unkown return code from the BPF program would fail in
tcf_bpf() with TC_ACT_UNSPEC.
Should we one day want to make use of TC_ACT_STOLEN or TC_ACT_QUEUED,
which both have the same semantics, we have the option to either use
that as a default action (filter_res of 0xffffffff) or non-default BPF
return code.
All that will allow us to transparently use tcf_bpf() for both BPF
flavours.
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Cc: Jiri Pirko <jiri@resnulli.us>
Cc: Alexei Starovoitov <ast@plumgrid.com>
Cc: Jamal Hadi Salim <jhs@mojatatu.com>
Acked-by: Jiri Pirko <jiri@resnulli.us>
Signed-off-by: David S. Miller <davem@davemloft.net>
2015-03-17 22:25:57 +03:00
break ;
default :
action = TC_ACT_UNSPEC ;
break ;
2015-01-15 11:52:39 +03:00
}
2015-03-20 17:11:12 +03:00
spin_unlock ( & prog - > tcf_lock ) ;
2015-01-15 11:52:39 +03:00
return action ;
}
2015-03-20 17:11:12 +03:00
static bool tcf_bpf_is_ebpf ( const struct tcf_bpf * prog )
{
return ! prog - > bpf_ops ;
}
static int tcf_bpf_dump_bpf_info ( const struct tcf_bpf * prog ,
struct sk_buff * skb )
{
struct nlattr * nla ;
if ( nla_put_u16 ( skb , TCA_ACT_BPF_OPS_LEN , prog - > bpf_num_ops ) )
return - EMSGSIZE ;
nla = nla_reserve ( skb , TCA_ACT_BPF_OPS , prog - > bpf_num_ops *
sizeof ( struct sock_filter ) ) ;
if ( nla = = NULL )
return - EMSGSIZE ;
memcpy ( nla_data ( nla ) , prog - > bpf_ops , nla_len ( nla ) ) ;
return 0 ;
}
static int tcf_bpf_dump_ebpf_info ( const struct tcf_bpf * prog ,
struct sk_buff * skb )
{
if ( nla_put_u32 ( skb , TCA_ACT_BPF_FD , prog - > bpf_fd ) )
return - EMSGSIZE ;
if ( prog - > bpf_name & &
nla_put_string ( skb , TCA_ACT_BPF_NAME , prog - > bpf_name ) )
return - EMSGSIZE ;
return 0 ;
}
static int tcf_bpf_dump ( struct sk_buff * skb , struct tc_action * act ,
2015-01-15 11:52:39 +03:00
int bind , int ref )
{
unsigned char * tp = skb_tail_pointer ( skb ) ;
2015-03-20 17:11:12 +03:00
struct tcf_bpf * prog = act - > priv ;
2015-01-15 11:52:39 +03:00
struct tc_act_bpf opt = {
2015-03-20 17:11:12 +03:00
. index = prog - > tcf_index ,
. refcnt = prog - > tcf_refcnt - ref ,
. bindcnt = prog - > tcf_bindcnt - bind ,
. action = prog - > tcf_action ,
2015-01-15 11:52:39 +03:00
} ;
2015-03-20 17:11:12 +03:00
struct tcf_t tm ;
int ret ;
2015-01-15 11:52:39 +03:00
if ( nla_put ( skb , TCA_ACT_BPF_PARMS , sizeof ( opt ) , & opt ) )
goto nla_put_failure ;
2015-03-20 17:11:12 +03:00
if ( tcf_bpf_is_ebpf ( prog ) )
ret = tcf_bpf_dump_ebpf_info ( prog , skb ) ;
else
ret = tcf_bpf_dump_bpf_info ( prog , skb ) ;
if ( ret )
2015-01-15 11:52:39 +03:00
goto nla_put_failure ;
2015-03-20 17:11:12 +03:00
tm . install = jiffies_to_clock_t ( jiffies - prog - > tcf_tm . install ) ;
tm . lastuse = jiffies_to_clock_t ( jiffies - prog - > tcf_tm . lastuse ) ;
tm . expires = jiffies_to_clock_t ( prog - > tcf_tm . expires ) ;
2015-01-15 11:52:39 +03:00
2015-03-20 17:11:12 +03:00
if ( nla_put ( skb , TCA_ACT_BPF_TM , sizeof ( tm ) , & tm ) )
2015-01-15 11:52:39 +03:00
goto nla_put_failure ;
2015-03-20 17:11:12 +03:00
2015-01-15 11:52:39 +03:00
return skb - > len ;
nla_put_failure :
nlmsg_trim ( skb , tp ) ;
return - 1 ;
}
static const struct nla_policy act_bpf_policy [ TCA_ACT_BPF_MAX + 1 ] = {
[ TCA_ACT_BPF_PARMS ] = { . len = sizeof ( struct tc_act_bpf ) } ,
2015-03-20 17:11:12 +03:00
[ TCA_ACT_BPF_FD ] = { . type = NLA_U32 } ,
[ TCA_ACT_BPF_NAME ] = { . type = NLA_NUL_STRING , . len = ACT_BPF_NAME_LEN } ,
2015-01-15 11:52:39 +03:00
[ TCA_ACT_BPF_OPS_LEN ] = { . type = NLA_U16 } ,
[ TCA_ACT_BPF_OPS ] = { . type = NLA_BINARY ,
. len = sizeof ( struct sock_filter ) * BPF_MAXINSNS } ,
} ;
2015-03-20 17:11:12 +03:00
static int tcf_bpf_init_from_ops ( struct nlattr * * tb , struct tcf_bpf_cfg * cfg )
2015-01-15 11:52:39 +03:00
{
struct sock_filter * bpf_ops ;
2015-03-20 17:11:12 +03:00
struct sock_fprog_kern fprog_tmp ;
2015-01-15 11:52:39 +03:00
struct bpf_prog * fp ;
2015-03-20 17:11:12 +03:00
u16 bpf_size , bpf_num_ops ;
2015-01-15 11:52:39 +03:00
int ret ;
bpf_num_ops = nla_get_u16 ( tb [ TCA_ACT_BPF_OPS_LEN ] ) ;
if ( bpf_num_ops > BPF_MAXINSNS | | bpf_num_ops = = 0 )
return - EINVAL ;
bpf_size = bpf_num_ops * sizeof ( * bpf_ops ) ;
2015-01-22 12:58:19 +03:00
if ( bpf_size ! = nla_len ( tb [ TCA_ACT_BPF_OPS ] ) )
return - EINVAL ;
2015-01-15 11:52:39 +03:00
bpf_ops = kzalloc ( bpf_size , GFP_KERNEL ) ;
2015-03-20 17:11:12 +03:00
if ( bpf_ops = = NULL )
2015-01-15 11:52:39 +03:00
return - ENOMEM ;
memcpy ( bpf_ops , nla_data ( tb [ TCA_ACT_BPF_OPS ] ) , bpf_size ) ;
2015-03-20 17:11:12 +03:00
fprog_tmp . len = bpf_num_ops ;
fprog_tmp . filter = bpf_ops ;
2015-01-15 11:52:39 +03:00
2015-03-20 17:11:12 +03:00
ret = bpf_prog_create ( & fp , & fprog_tmp ) ;
if ( ret < 0 ) {
kfree ( bpf_ops ) ;
return ret ;
}
2015-01-15 11:52:39 +03:00
2015-03-20 17:11:12 +03:00
cfg - > bpf_ops = bpf_ops ;
cfg - > bpf_num_ops = bpf_num_ops ;
cfg - > filter = fp ;
return 0 ;
}
static int tcf_bpf_init_from_efd ( struct nlattr * * tb , struct tcf_bpf_cfg * cfg )
{
struct bpf_prog * fp ;
char * name = NULL ;
u32 bpf_fd ;
bpf_fd = nla_get_u32 ( tb [ TCA_ACT_BPF_FD ] ) ;
fp = bpf_prog_get ( bpf_fd ) ;
if ( IS_ERR ( fp ) )
return PTR_ERR ( fp ) ;
if ( fp - > type ! = BPF_PROG_TYPE_SCHED_ACT ) {
bpf_prog_put ( fp ) ;
return - EINVAL ;
}
if ( tb [ TCA_ACT_BPF_NAME ] ) {
name = kmemdup ( nla_data ( tb [ TCA_ACT_BPF_NAME ] ) ,
nla_len ( tb [ TCA_ACT_BPF_NAME ] ) ,
GFP_KERNEL ) ;
if ( ! name ) {
bpf_prog_put ( fp ) ;
return - ENOMEM ;
}
}
cfg - > bpf_fd = bpf_fd ;
cfg - > bpf_name = name ;
cfg - > filter = fp ;
return 0 ;
}
static int tcf_bpf_init ( struct net * net , struct nlattr * nla ,
struct nlattr * est , struct tc_action * act ,
int replace , int bind )
{
struct nlattr * tb [ TCA_ACT_BPF_MAX + 1 ] ;
struct tc_act_bpf * parm ;
struct tcf_bpf * prog ;
struct tcf_bpf_cfg cfg ;
bool is_bpf , is_ebpf ;
int ret ;
if ( ! nla )
return - EINVAL ;
ret = nla_parse_nested ( tb , TCA_ACT_BPF_MAX , nla , act_bpf_policy ) ;
if ( ret < 0 )
return ret ;
is_bpf = tb [ TCA_ACT_BPF_OPS_LEN ] & & tb [ TCA_ACT_BPF_OPS ] ;
is_ebpf = tb [ TCA_ACT_BPF_FD ] ;
if ( ( ! is_bpf & & ! is_ebpf ) | | ( is_bpf & & is_ebpf ) | |
! tb [ TCA_ACT_BPF_PARMS ] )
return - EINVAL ;
parm = nla_data ( tb [ TCA_ACT_BPF_PARMS ] ) ;
memset ( & cfg , 0 , sizeof ( cfg ) ) ;
ret = is_bpf ? tcf_bpf_init_from_ops ( tb , & cfg ) :
tcf_bpf_init_from_efd ( tb , & cfg ) ;
if ( ret < 0 )
return ret ;
if ( ! tcf_hash_check ( parm - > index , act , bind ) ) {
ret = tcf_hash_create ( parm - > index , est , act ,
sizeof ( * prog ) , bind ) ;
if ( ret < 0 )
2015-01-15 11:52:39 +03:00
goto destroy_fp ;
ret = ACT_P_CREATED ;
} else {
2015-03-20 17:11:12 +03:00
/* Don't override defaults. */
2015-01-15 11:52:39 +03:00
if ( bind )
goto destroy_fp ;
2015-03-20 17:11:12 +03:00
tcf_hash_release ( act , bind ) ;
if ( ! replace ) {
2015-01-15 11:52:39 +03:00
ret = - EEXIST ;
goto destroy_fp ;
}
}
2015-03-20 17:11:12 +03:00
prog = to_bpf ( act ) ;
spin_lock_bh ( & prog - > tcf_lock ) ;
prog - > bpf_ops = cfg . bpf_ops ;
prog - > bpf_name = cfg . bpf_name ;
if ( cfg . bpf_num_ops )
prog - > bpf_num_ops = cfg . bpf_num_ops ;
if ( cfg . bpf_fd )
prog - > bpf_fd = cfg . bpf_fd ;
prog - > tcf_action = parm - > action ;
prog - > filter = cfg . filter ;
spin_unlock_bh ( & prog - > tcf_lock ) ;
2015-01-15 11:52:39 +03:00
if ( ret = = ACT_P_CREATED )
2015-03-20 17:11:12 +03:00
tcf_hash_insert ( act ) ;
2015-01-15 11:52:39 +03:00
return ret ;
destroy_fp :
2015-03-20 17:11:12 +03:00
if ( is_ebpf )
bpf_prog_put ( cfg . filter ) ;
else
bpf_prog_destroy ( cfg . filter ) ;
kfree ( cfg . bpf_ops ) ;
kfree ( cfg . bpf_name ) ;
2015-01-15 11:52:39 +03:00
return ret ;
}
2015-03-20 17:11:12 +03:00
static void tcf_bpf_cleanup ( struct tc_action * act , int bind )
2015-01-15 11:52:39 +03:00
{
2015-03-20 17:11:12 +03:00
const struct tcf_bpf * prog = act - > priv ;
2015-01-15 11:52:39 +03:00
2015-03-20 17:11:12 +03:00
if ( tcf_bpf_is_ebpf ( prog ) )
bpf_prog_put ( prog - > filter ) ;
else
bpf_prog_destroy ( prog - > filter ) ;
2015-01-15 11:52:39 +03:00
}
2015-03-20 17:11:12 +03:00
static struct tc_action_ops act_bpf_ops __read_mostly = {
. kind = " bpf " ,
. type = TCA_ACT_BPF ,
. owner = THIS_MODULE ,
. act = tcf_bpf ,
. dump = tcf_bpf_dump ,
. cleanup = tcf_bpf_cleanup ,
. init = tcf_bpf_init ,
2015-01-15 11:52:39 +03:00
} ;
static int __init bpf_init_module ( void )
{
return tcf_register_action ( & act_bpf_ops , BPF_TAB_MASK ) ;
}
static void __exit bpf_cleanup_module ( void )
{
tcf_unregister_action ( & act_bpf_ops ) ;
}
module_init ( bpf_init_module ) ;
module_exit ( bpf_cleanup_module ) ;
MODULE_AUTHOR ( " Jiri Pirko <jiri@resnulli.us> " ) ;
MODULE_DESCRIPTION ( " TC BPF based action " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;