Commit 1ea186e3 authored by David S. Miller's avatar David S. Miller

Merge branch 'net-sched-validate-the-control-action-with-all-the-other-parameters'

Davide Caratti says:

====================
net/sched: validate the control action with all the other parameters

currently, the kernel checks for bad values of the control action in
tcf_action_init_1(), after a successful call to the action's init()
function. When the control action is 'goto chain', this causes two
undesired behaviors:

1. "misconfigured action after replace that causes kernel crash":
   if users replace a valid TC action with another one having invalid
   control action, all the new configuration data (including the bad
   control action) are applied successfully, even if the kernel returned
   an error. As a consequence, it's possible to trigger a NULL pointer
   dereference in the traffic path of every TC action (1), replacing the
   control action with 'goto chain x', when chain <x> doesn't exist.

2. "refcount leak that makes kmemleak complain"
   when a valid 'goto chain' action is overwritten with another action,
   the kernel forgets to decrease refcounts in the chain.

The above problems can be fixed if we validate the control action in each
action's init() function, the same way as we are already doing for all the
other configuration parameters.
Now that chains can be released after an action is replaced, we need to
care about concurrent access of 'goto_chain' pointer: ensure we access it
through RCU, like we did with most action-specific configuration parameters.

- Patch 1 removes the wrong checks and provides functions that can be
  used to properly validate control actions in  individual actions
- Patch 2 to 16 fix individual actions, and add TDC selftest code to
  verify the correct behavior (2)
- Patch 17 and 18 fix concurrent access issues on 'goto_chain', that can be
  observed after the chain refcount leak is fixed.

Changes since v1:
- reword the cover letter
- condense the extack message in case tc_action_check_ctrlact() is called
  with invalid parameters.
- add tcf_action_set_ctrlact() to avoid code duplication an make the
  RCU-ification of 'goto_chain' easier.
- fix errors in act_ife, act_simple, act_skbedit, and avoid useless 'goto
  end' in act_connmark, thanks a lot to Vlad Buslov.
- avoid dereferencing 'goto_chain' in tcf_gact_goto_chain_index(), so
  we don't have to care about the grace period there.
- let actions respect the grace period when they release chains, thanks
  to Cong Wang and Vlad Buslov.

Changes since RFC:
- include a fix for all TC actions
- add a selftest for each TC action
- squash fix for refcount leaks into a single patch, the first in the
  series, thanks to Cong Wang
- ensure that chain refcount is released without tcfa_lock held, thanks
  to Vlad Buslov

Notes:
(1) act_ipt didn't need any fix, as the control action is constantly equal
    to TC_ACT_OK.
(2) the selftest for act_simple fails because userspace tc backend for
    'simple' does not parse the control action correctly (and hardcodes it
    to TC_ACT_PIPE).
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents cd5afa91 ee3bbfe8
...@@ -39,7 +39,7 @@ struct tc_action { ...@@ -39,7 +39,7 @@ struct tc_action {
struct gnet_stats_basic_cpu __percpu *cpu_bstats_hw; struct gnet_stats_basic_cpu __percpu *cpu_bstats_hw;
struct gnet_stats_queue __percpu *cpu_qstats; struct gnet_stats_queue __percpu *cpu_qstats;
struct tc_cookie __rcu *act_cookie; struct tc_cookie __rcu *act_cookie;
struct tcf_chain *goto_chain; struct tcf_chain __rcu *goto_chain;
}; };
#define tcf_index common.tcfa_index #define tcf_index common.tcfa_index
#define tcf_refcnt common.tcfa_refcnt #define tcf_refcnt common.tcfa_refcnt
...@@ -90,7 +90,7 @@ struct tc_action_ops { ...@@ -90,7 +90,7 @@ struct tc_action_ops {
int (*lookup)(struct net *net, struct tc_action **a, u32 index); int (*lookup)(struct net *net, struct tc_action **a, u32 index);
int (*init)(struct net *net, struct nlattr *nla, int (*init)(struct net *net, struct nlattr *nla,
struct nlattr *est, struct tc_action **act, int ovr, struct nlattr *est, struct tc_action **act, int ovr,
int bind, bool rtnl_held, int bind, bool rtnl_held, struct tcf_proto *tp,
struct netlink_ext_ack *extack); struct netlink_ext_ack *extack);
int (*walk)(struct net *, struct sk_buff *, int (*walk)(struct net *, struct sk_buff *,
struct netlink_callback *, int, struct netlink_callback *, int,
...@@ -181,6 +181,11 @@ int tcf_action_dump_old(struct sk_buff *skb, struct tc_action *a, int, int); ...@@ -181,6 +181,11 @@ 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_dump_1(struct sk_buff *skb, struct tc_action *a, int, int);
int tcf_action_copy_stats(struct sk_buff *, struct tc_action *, int); int tcf_action_copy_stats(struct sk_buff *, struct tc_action *, int);
int tcf_action_check_ctrlact(int action, struct tcf_proto *tp,
struct tcf_chain **handle,
struct netlink_ext_ack *newchain);
struct tcf_chain *tcf_action_set_ctrlact(struct tc_action *a, int action,
struct tcf_chain *newchain);
#endif /* CONFIG_NET_CLS_ACT */ #endif /* CONFIG_NET_CLS_ACT */
static inline void tcf_action_stats_update(struct tc_action *a, u64 bytes, static inline void tcf_action_stats_update(struct tc_action *a, u64 bytes,
......
...@@ -378,6 +378,7 @@ struct tcf_chain { ...@@ -378,6 +378,7 @@ struct tcf_chain {
bool flushing; bool flushing;
const struct tcf_proto_ops *tmplt_ops; const struct tcf_proto_ops *tmplt_ops;
void *tmplt_priv; void *tmplt_priv;
struct rcu_head rcu;
}; };
struct tcf_block { struct tcf_block {
......
...@@ -56,7 +56,7 @@ static inline bool is_tcf_gact_goto_chain(const struct tc_action *a) ...@@ -56,7 +56,7 @@ static inline bool is_tcf_gact_goto_chain(const struct tc_action *a)
static inline u32 tcf_gact_goto_chain_index(const struct tc_action *a) static inline u32 tcf_gact_goto_chain_index(const struct tc_action *a)
{ {
return a->goto_chain->index; return READ_ONCE(a->tcfa_action) & TC_ACT_EXT_VAL_MASK;
} }
#endif /* __NET_TC_GACT_H */ #endif /* __NET_TC_GACT_H */
...@@ -28,27 +28,10 @@ ...@@ -28,27 +28,10 @@
#include <net/act_api.h> #include <net/act_api.h>
#include <net/netlink.h> #include <net/netlink.h>
static int tcf_action_goto_chain_init(struct tc_action *a, struct tcf_proto *tp)
{
u32 chain_index = a->tcfa_action & TC_ACT_EXT_VAL_MASK;
if (!tp)
return -EINVAL;
a->goto_chain = tcf_chain_get_by_act(tp->chain->block, chain_index);
if (!a->goto_chain)
return -ENOMEM;
return 0;
}
static void tcf_action_goto_chain_fini(struct tc_action *a)
{
tcf_chain_put_by_act(a->goto_chain);
}
static void tcf_action_goto_chain_exec(const struct tc_action *a, static void tcf_action_goto_chain_exec(const struct tc_action *a,
struct tcf_result *res) struct tcf_result *res)
{ {
const struct tcf_chain *chain = a->goto_chain; const struct tcf_chain *chain = rcu_dereference_bh(a->goto_chain);
res->goto_tp = rcu_dereference_bh(chain->filter_chain); res->goto_tp = rcu_dereference_bh(chain->filter_chain);
} }
...@@ -71,6 +54,51 @@ static void tcf_set_action_cookie(struct tc_cookie __rcu **old_cookie, ...@@ -71,6 +54,51 @@ static void tcf_set_action_cookie(struct tc_cookie __rcu **old_cookie,
call_rcu(&old->rcu, tcf_free_cookie_rcu); call_rcu(&old->rcu, tcf_free_cookie_rcu);
} }
int tcf_action_check_ctrlact(int action, struct tcf_proto *tp,
struct tcf_chain **newchain,
struct netlink_ext_ack *extack)
{
int opcode = TC_ACT_EXT_OPCODE(action), ret = -EINVAL;
u32 chain_index;
if (!opcode)
ret = action > TC_ACT_VALUE_MAX ? -EINVAL : 0;
else if (opcode <= TC_ACT_EXT_OPCODE_MAX || action == TC_ACT_UNSPEC)
ret = 0;
if (ret) {
NL_SET_ERR_MSG(extack, "invalid control action");
goto end;
}
if (TC_ACT_EXT_CMP(action, TC_ACT_GOTO_CHAIN)) {
chain_index = action & TC_ACT_EXT_VAL_MASK;
if (!tp || !newchain) {
ret = -EINVAL;
NL_SET_ERR_MSG(extack,
"can't goto NULL proto/chain");
goto end;
}
*newchain = tcf_chain_get_by_act(tp->chain->block, chain_index);
if (!*newchain) {
ret = -ENOMEM;
NL_SET_ERR_MSG(extack,
"can't allocate goto_chain");
}
}
end:
return ret;
}
EXPORT_SYMBOL(tcf_action_check_ctrlact);
struct tcf_chain *tcf_action_set_ctrlact(struct tc_action *a, int action,
struct tcf_chain *goto_chain)
{
a->tcfa_action = action;
rcu_swap_protected(a->goto_chain, goto_chain, 1);
return goto_chain;
}
EXPORT_SYMBOL(tcf_action_set_ctrlact);
/* XXX: For standalone actions, we don't need a RCU grace period either, because /* XXX: For standalone actions, we don't need a RCU grace period either, because
* actions are always connected to filters and filters are already destroyed in * actions are always connected to filters and filters are already destroyed in
* RCU callbacks, so after a RCU grace period actions are already disconnected * RCU callbacks, so after a RCU grace period actions are already disconnected
...@@ -78,13 +106,15 @@ static void tcf_set_action_cookie(struct tc_cookie __rcu **old_cookie, ...@@ -78,13 +106,15 @@ static void tcf_set_action_cookie(struct tc_cookie __rcu **old_cookie,
*/ */
static void free_tcf(struct tc_action *p) static void free_tcf(struct tc_action *p)
{ {
struct tcf_chain *chain = rcu_dereference_protected(p->goto_chain, 1);
free_percpu(p->cpu_bstats); free_percpu(p->cpu_bstats);
free_percpu(p->cpu_bstats_hw); free_percpu(p->cpu_bstats_hw);
free_percpu(p->cpu_qstats); free_percpu(p->cpu_qstats);
tcf_set_action_cookie(&p->act_cookie, NULL); tcf_set_action_cookie(&p->act_cookie, NULL);
if (p->goto_chain) if (chain)
tcf_action_goto_chain_fini(p); tcf_chain_put_by_act(chain);
kfree(p); kfree(p);
} }
...@@ -654,6 +684,10 @@ int tcf_action_exec(struct sk_buff *skb, struct tc_action **actions, ...@@ -654,6 +684,10 @@ int tcf_action_exec(struct sk_buff *skb, struct tc_action **actions,
return TC_ACT_OK; return TC_ACT_OK;
} }
} else if (TC_ACT_EXT_CMP(ret, TC_ACT_GOTO_CHAIN)) { } else if (TC_ACT_EXT_CMP(ret, TC_ACT_GOTO_CHAIN)) {
if (unlikely(!rcu_access_pointer(a->goto_chain))) {
net_warn_ratelimited("can't go to NULL chain!\n");
return TC_ACT_SHOT;
}
tcf_action_goto_chain_exec(a, res); tcf_action_goto_chain_exec(a, res);
} }
...@@ -800,15 +834,6 @@ static struct tc_cookie *nla_memdup_cookie(struct nlattr **tb) ...@@ -800,15 +834,6 @@ static struct tc_cookie *nla_memdup_cookie(struct nlattr **tb)
return c; return c;
} }
static bool tcf_action_valid(int action)
{
int opcode = TC_ACT_EXT_OPCODE(action);
if (!opcode)
return action <= TC_ACT_VALUE_MAX;
return opcode <= TC_ACT_EXT_OPCODE_MAX || action == TC_ACT_UNSPEC;
}
struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp, struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
struct nlattr *nla, struct nlattr *est, struct nlattr *nla, struct nlattr *est,
char *name, int ovr, int bind, char *name, int ovr, int bind,
...@@ -890,10 +915,10 @@ struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp, ...@@ -890,10 +915,10 @@ struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
/* backward compatibility for policer */ /* backward compatibility for policer */
if (name == NULL) if (name == NULL)
err = a_o->init(net, tb[TCA_ACT_OPTIONS], est, &a, ovr, bind, err = a_o->init(net, tb[TCA_ACT_OPTIONS], est, &a, ovr, bind,
rtnl_held, extack); rtnl_held, tp, extack);
else else
err = a_o->init(net, nla, est, &a, ovr, bind, rtnl_held, err = a_o->init(net, nla, est, &a, ovr, bind, rtnl_held,
extack); tp, extack);
if (err < 0) if (err < 0)
goto err_mod; goto err_mod;
...@@ -907,18 +932,10 @@ struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp, ...@@ -907,18 +932,10 @@ struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
if (err != ACT_P_CREATED) if (err != ACT_P_CREATED)
module_put(a_o->owner); module_put(a_o->owner);
if (TC_ACT_EXT_CMP(a->tcfa_action, TC_ACT_GOTO_CHAIN)) { if (TC_ACT_EXT_CMP(a->tcfa_action, TC_ACT_GOTO_CHAIN) &&
err = tcf_action_goto_chain_init(a, tp); !rcu_access_pointer(a->goto_chain)) {
if (err) {
tcf_action_destroy_1(a, bind);
NL_SET_ERR_MSG(extack, "Failed to init TC action chain");
return ERR_PTR(err);
}
}
if (!tcf_action_valid(a->tcfa_action)) {
tcf_action_destroy_1(a, bind); tcf_action_destroy_1(a, bind);
NL_SET_ERR_MSG(extack, "Invalid control action value"); NL_SET_ERR_MSG(extack, "can't use goto chain with NULL chain");
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
} }
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <net/netlink.h> #include <net/netlink.h>
#include <net/pkt_sched.h> #include <net/pkt_sched.h>
#include <net/pkt_cls.h>
#include <linux/tc_act/tc_bpf.h> #include <linux/tc_act/tc_bpf.h>
#include <net/tc_act/tc_bpf.h> #include <net/tc_act/tc_bpf.h>
...@@ -278,10 +279,11 @@ static void tcf_bpf_prog_fill_cfg(const struct tcf_bpf *prog, ...@@ -278,10 +279,11 @@ static void tcf_bpf_prog_fill_cfg(const struct tcf_bpf *prog,
static int tcf_bpf_init(struct net *net, struct nlattr *nla, static int tcf_bpf_init(struct net *net, struct nlattr *nla,
struct nlattr *est, struct tc_action **act, struct nlattr *est, struct tc_action **act,
int replace, int bind, bool rtnl_held, int replace, int bind, bool rtnl_held,
struct netlink_ext_ack *extack) struct tcf_proto *tp, struct netlink_ext_ack *extack)
{ {
struct tc_action_net *tn = net_generic(net, bpf_net_id); struct tc_action_net *tn = net_generic(net, bpf_net_id);
struct nlattr *tb[TCA_ACT_BPF_MAX + 1]; struct nlattr *tb[TCA_ACT_BPF_MAX + 1];
struct tcf_chain *goto_ch = NULL;
struct tcf_bpf_cfg cfg, old; struct tcf_bpf_cfg cfg, old;
struct tc_act_bpf *parm; struct tc_act_bpf *parm;
struct tcf_bpf *prog; struct tcf_bpf *prog;
...@@ -323,12 +325,16 @@ static int tcf_bpf_init(struct net *net, struct nlattr *nla, ...@@ -323,12 +325,16 @@ static int tcf_bpf_init(struct net *net, struct nlattr *nla,
return ret; return ret;
} }
ret = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
if (ret < 0)
goto release_idr;
is_bpf = tb[TCA_ACT_BPF_OPS_LEN] && tb[TCA_ACT_BPF_OPS]; is_bpf = tb[TCA_ACT_BPF_OPS_LEN] && tb[TCA_ACT_BPF_OPS];
is_ebpf = tb[TCA_ACT_BPF_FD]; is_ebpf = tb[TCA_ACT_BPF_FD];
if ((!is_bpf && !is_ebpf) || (is_bpf && is_ebpf)) { if ((!is_bpf && !is_ebpf) || (is_bpf && is_ebpf)) {
ret = -EINVAL; ret = -EINVAL;
goto out; goto put_chain;
} }
memset(&cfg, 0, sizeof(cfg)); memset(&cfg, 0, sizeof(cfg));
...@@ -336,7 +342,7 @@ static int tcf_bpf_init(struct net *net, struct nlattr *nla, ...@@ -336,7 +342,7 @@ static int tcf_bpf_init(struct net *net, struct nlattr *nla,
ret = is_bpf ? tcf_bpf_init_from_ops(tb, &cfg) : ret = is_bpf ? tcf_bpf_init_from_ops(tb, &cfg) :
tcf_bpf_init_from_efd(tb, &cfg); tcf_bpf_init_from_efd(tb, &cfg);
if (ret < 0) if (ret < 0)
goto out; goto put_chain;
prog = to_bpf(*act); prog = to_bpf(*act);
...@@ -350,10 +356,13 @@ static int tcf_bpf_init(struct net *net, struct nlattr *nla, ...@@ -350,10 +356,13 @@ static int tcf_bpf_init(struct net *net, struct nlattr *nla,
if (cfg.bpf_num_ops) if (cfg.bpf_num_ops)
prog->bpf_num_ops = cfg.bpf_num_ops; prog->bpf_num_ops = cfg.bpf_num_ops;
prog->tcf_action = parm->action; goto_ch = tcf_action_set_ctrlact(*act, parm->action, goto_ch);
rcu_assign_pointer(prog->filter, cfg.filter); rcu_assign_pointer(prog->filter, cfg.filter);
spin_unlock_bh(&prog->tcf_lock); spin_unlock_bh(&prog->tcf_lock);
if (goto_ch)
tcf_chain_put_by_act(goto_ch);
if (res == ACT_P_CREATED) { if (res == ACT_P_CREATED) {
tcf_idr_insert(tn, *act); tcf_idr_insert(tn, *act);
} else { } else {
...@@ -363,9 +372,13 @@ static int tcf_bpf_init(struct net *net, struct nlattr *nla, ...@@ -363,9 +372,13 @@ static int tcf_bpf_init(struct net *net, struct nlattr *nla,
} }
return res; return res;
out:
tcf_idr_release(*act, bind);
put_chain:
if (goto_ch)
tcf_chain_put_by_act(goto_ch);
release_idr:
tcf_idr_release(*act, bind);
return ret; return ret;
} }
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <net/netlink.h> #include <net/netlink.h>
#include <net/pkt_sched.h> #include <net/pkt_sched.h>
#include <net/act_api.h> #include <net/act_api.h>
#include <net/pkt_cls.h>
#include <uapi/linux/tc_act/tc_connmark.h> #include <uapi/linux/tc_act/tc_connmark.h>
#include <net/tc_act/tc_connmark.h> #include <net/tc_act/tc_connmark.h>
...@@ -97,13 +98,15 @@ static const struct nla_policy connmark_policy[TCA_CONNMARK_MAX + 1] = { ...@@ -97,13 +98,15 @@ static const struct nla_policy connmark_policy[TCA_CONNMARK_MAX + 1] = {
static int tcf_connmark_init(struct net *net, struct nlattr *nla, static int tcf_connmark_init(struct net *net, struct nlattr *nla,
struct nlattr *est, struct tc_action **a, struct nlattr *est, struct tc_action **a,
int ovr, int bind, bool rtnl_held, int ovr, int bind, bool rtnl_held,
struct tcf_proto *tp,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
{ {
struct tc_action_net *tn = net_generic(net, connmark_net_id); struct tc_action_net *tn = net_generic(net, connmark_net_id);
struct nlattr *tb[TCA_CONNMARK_MAX + 1]; struct nlattr *tb[TCA_CONNMARK_MAX + 1];
struct tcf_chain *goto_ch = NULL;
struct tcf_connmark_info *ci; struct tcf_connmark_info *ci;
struct tc_connmark *parm; struct tc_connmark *parm;
int ret = 0; int ret = 0, err;
if (!nla) if (!nla)
return -EINVAL; return -EINVAL;
...@@ -128,7 +131,11 @@ static int tcf_connmark_init(struct net *net, struct nlattr *nla, ...@@ -128,7 +131,11 @@ static int tcf_connmark_init(struct net *net, struct nlattr *nla,
} }
ci = to_connmark(*a); ci = to_connmark(*a);
ci->tcf_action = parm->action; err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch,
extack);
if (err < 0)
goto release_idr;
tcf_action_set_ctrlact(*a, parm->action, goto_ch);
ci->net = net; ci->net = net;
ci->zone = parm->zone; ci->zone = parm->zone;
...@@ -142,15 +149,24 @@ static int tcf_connmark_init(struct net *net, struct nlattr *nla, ...@@ -142,15 +149,24 @@ static int tcf_connmark_init(struct net *net, struct nlattr *nla,
tcf_idr_release(*a, bind); tcf_idr_release(*a, bind);
return -EEXIST; return -EEXIST;
} }
err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch,
extack);
if (err < 0)
goto release_idr;
/* replacing action and zone */ /* replacing action and zone */
spin_lock_bh(&ci->tcf_lock); spin_lock_bh(&ci->tcf_lock);
ci->tcf_action = parm->action; goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch);
ci->zone = parm->zone; ci->zone = parm->zone;
spin_unlock_bh(&ci->tcf_lock); spin_unlock_bh(&ci->tcf_lock);
if (goto_ch)
tcf_chain_put_by_act(goto_ch);
ret = 0; ret = 0;
} }
return ret; return ret;
release_idr:
tcf_idr_release(*a, bind);
return err;
} }
static inline int tcf_connmark_dump(struct sk_buff *skb, struct tc_action *a, static inline int tcf_connmark_dump(struct sk_buff *skb, struct tc_action *a,
......
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#include <net/sctp/checksum.h> #include <net/sctp/checksum.h>
#include <net/act_api.h> #include <net/act_api.h>
#include <net/pkt_cls.h>
#include <linux/tc_act/tc_csum.h> #include <linux/tc_act/tc_csum.h>
#include <net/tc_act/tc_csum.h> #include <net/tc_act/tc_csum.h>
...@@ -46,12 +47,13 @@ static struct tc_action_ops act_csum_ops; ...@@ -46,12 +47,13 @@ static struct tc_action_ops act_csum_ops;
static int tcf_csum_init(struct net *net, struct nlattr *nla, static int tcf_csum_init(struct net *net, struct nlattr *nla,
struct nlattr *est, struct tc_action **a, int ovr, struct nlattr *est, struct tc_action **a, int ovr,
int bind, bool rtnl_held, int bind, bool rtnl_held, struct tcf_proto *tp,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
{ {
struct tc_action_net *tn = net_generic(net, csum_net_id); struct tc_action_net *tn = net_generic(net, csum_net_id);
struct tcf_csum_params *params_new; struct tcf_csum_params *params_new;
struct nlattr *tb[TCA_CSUM_MAX + 1]; struct nlattr *tb[TCA_CSUM_MAX + 1];
struct tcf_chain *goto_ch = NULL;
struct tc_csum *parm; struct tc_csum *parm;
struct tcf_csum *p; struct tcf_csum *p;
int ret = 0, err; int ret = 0, err;
...@@ -87,21 +89,27 @@ static int tcf_csum_init(struct net *net, struct nlattr *nla, ...@@ -87,21 +89,27 @@ static int tcf_csum_init(struct net *net, struct nlattr *nla,
return err; return err;
} }
err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
if (err < 0)
goto release_idr;
p = to_tcf_csum(*a); p = to_tcf_csum(*a);
params_new = kzalloc(sizeof(*params_new), GFP_KERNEL); params_new = kzalloc(sizeof(*params_new), GFP_KERNEL);
if (unlikely(!params_new)) { if (unlikely(!params_new)) {
tcf_idr_release(*a, bind); err = -ENOMEM;
return -ENOMEM; goto put_chain;
} }
params_new->update_flags = parm->update_flags; params_new->update_flags = parm->update_flags;
spin_lock_bh(&p->tcf_lock); spin_lock_bh(&p->tcf_lock);
p->tcf_action = parm->action; goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch);
rcu_swap_protected(p->params, params_new, rcu_swap_protected(p->params, params_new,
lockdep_is_held(&p->tcf_lock)); lockdep_is_held(&p->tcf_lock));
spin_unlock_bh(&p->tcf_lock); spin_unlock_bh(&p->tcf_lock);
if (goto_ch)
tcf_chain_put_by_act(goto_ch);
if (params_new) if (params_new)
kfree_rcu(params_new, rcu); kfree_rcu(params_new, rcu);
...@@ -109,6 +117,12 @@ static int tcf_csum_init(struct net *net, struct nlattr *nla, ...@@ -109,6 +117,12 @@ static int tcf_csum_init(struct net *net, struct nlattr *nla,
tcf_idr_insert(tn, *a); tcf_idr_insert(tn, *a);
return ret; return ret;
put_chain:
if (goto_ch)
tcf_chain_put_by_act(goto_ch);
release_idr:
tcf_idr_release(*a, bind);
return err;
} }
/** /**
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <net/netlink.h> #include <net/netlink.h>
#include <net/pkt_sched.h> #include <net/pkt_sched.h>
#include <net/pkt_cls.h>
#include <linux/tc_act/tc_gact.h> #include <linux/tc_act/tc_gact.h>
#include <net/tc_act/tc_gact.h> #include <net/tc_act/tc_gact.h>
...@@ -57,10 +58,11 @@ static const struct nla_policy gact_policy[TCA_GACT_MAX + 1] = { ...@@ -57,10 +58,11 @@ static const struct nla_policy gact_policy[TCA_GACT_MAX + 1] = {
static int tcf_gact_init(struct net *net, struct nlattr *nla, static int tcf_gact_init(struct net *net, struct nlattr *nla,
struct nlattr *est, struct tc_action **a, struct nlattr *est, struct tc_action **a,
int ovr, int bind, bool rtnl_held, int ovr, int bind, bool rtnl_held,
struct netlink_ext_ack *extack) struct tcf_proto *tp, struct netlink_ext_ack *extack)
{ {
struct tc_action_net *tn = net_generic(net, gact_net_id); struct tc_action_net *tn = net_generic(net, gact_net_id);
struct nlattr *tb[TCA_GACT_MAX + 1]; struct nlattr *tb[TCA_GACT_MAX + 1];
struct tcf_chain *goto_ch = NULL;
struct tc_gact *parm; struct tc_gact *parm;
struct tcf_gact *gact; struct tcf_gact *gact;
int ret = 0; int ret = 0;
...@@ -116,10 +118,13 @@ static int tcf_gact_init(struct net *net, struct nlattr *nla, ...@@ -116,10 +118,13 @@ static int tcf_gact_init(struct net *net, struct nlattr *nla,
return err; return err;
} }
err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
if (err < 0)
goto release_idr;
gact = to_gact(*a); gact = to_gact(*a);
spin_lock_bh(&gact->tcf_lock); spin_lock_bh(&gact->tcf_lock);
gact->tcf_action = parm->action; goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch);
#ifdef CONFIG_GACT_PROB #ifdef CONFIG_GACT_PROB
if (p_parm) { if (p_parm) {
gact->tcfg_paction = p_parm->paction; gact->tcfg_paction = p_parm->paction;
...@@ -133,9 +138,15 @@ static int tcf_gact_init(struct net *net, struct nlattr *nla, ...@@ -133,9 +138,15 @@ static int tcf_gact_init(struct net *net, struct nlattr *nla,
#endif #endif
spin_unlock_bh(&gact->tcf_lock); spin_unlock_bh(&gact->tcf_lock);
if (goto_ch)
tcf_chain_put_by_act(goto_ch);
if (ret == ACT_P_CREATED) if (ret == ACT_P_CREATED)
tcf_idr_insert(tn, *a); tcf_idr_insert(tn, *a);
return ret; return ret;
release_idr:
tcf_idr_release(*a, bind);
return err;
} }
static int tcf_gact_act(struct sk_buff *skb, const struct tc_action *a, static int tcf_gact_act(struct sk_buff *skb, const struct tc_action *a,
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include <net/net_namespace.h> #include <net/net_namespace.h>
#include <net/netlink.h> #include <net/netlink.h>
#include <net/pkt_sched.h> #include <net/pkt_sched.h>
#include <net/pkt_cls.h>
#include <uapi/linux/tc_act/tc_ife.h> #include <uapi/linux/tc_act/tc_ife.h>
#include <net/tc_act/tc_ife.h> #include <net/tc_act/tc_ife.h>
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
...@@ -469,11 +470,12 @@ static int populate_metalist(struct tcf_ife_info *ife, struct nlattr **tb, ...@@ -469,11 +470,12 @@ static int populate_metalist(struct tcf_ife_info *ife, struct nlattr **tb,
static int tcf_ife_init(struct net *net, struct nlattr *nla, static int tcf_ife_init(struct net *net, struct nlattr *nla,
struct nlattr *est, struct tc_action **a, struct nlattr *est, struct tc_action **a,
int ovr, int bind, bool rtnl_held, int ovr, int bind, bool rtnl_held,
struct netlink_ext_ack *extack) struct tcf_proto *tp, struct netlink_ext_ack *extack)
{ {
struct tc_action_net *tn = net_generic(net, ife_net_id); struct tc_action_net *tn = net_generic(net, ife_net_id);
struct nlattr *tb[TCA_IFE_MAX + 1]; struct nlattr *tb[TCA_IFE_MAX + 1];
struct nlattr *tb2[IFE_META_MAX + 1]; struct nlattr *tb2[IFE_META_MAX + 1];
struct tcf_chain *goto_ch = NULL;
struct tcf_ife_params *p; struct tcf_ife_params *p;
struct tcf_ife_info *ife; struct tcf_ife_info *ife;
u16 ife_type = ETH_P_IFE; u16 ife_type = ETH_P_IFE;
...@@ -531,6 +533,10 @@ static int tcf_ife_init(struct net *net, struct nlattr *nla, ...@@ -531,6 +533,10 @@ static int tcf_ife_init(struct net *net, struct nlattr *nla,
} }
ife = to_ife(*a); ife = to_ife(*a);
err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
if (err < 0)
goto release_idr;
p->flags = parm->flags; p->flags = parm->flags;
if (parm->flags & IFE_ENCODE) { if (parm->flags & IFE_ENCODE) {
...@@ -563,13 +569,8 @@ static int tcf_ife_init(struct net *net, struct nlattr *nla, ...@@ -563,13 +569,8 @@ static int tcf_ife_init(struct net *net, struct nlattr *nla,
if (tb[TCA_IFE_METALST]) { if (tb[TCA_IFE_METALST]) {
err = nla_parse_nested(tb2, IFE_META_MAX, tb[TCA_IFE_METALST], err = nla_parse_nested(tb2, IFE_META_MAX, tb[TCA_IFE_METALST],
NULL, NULL); NULL, NULL);
if (err) { if (err)
metadata_parse_err: goto metadata_parse_err;
tcf_idr_release(*a, bind);
kfree(p);
return err;
}
err = populate_metalist(ife, tb2, exists, rtnl_held); err = populate_metalist(ife, tb2, exists, rtnl_held);
if (err) if (err)
goto metadata_parse_err; goto metadata_parse_err;
...@@ -581,21 +582,20 @@ static int tcf_ife_init(struct net *net, struct nlattr *nla, ...@@ -581,21 +582,20 @@ static int tcf_ife_init(struct net *net, struct nlattr *nla,
* going to bail out * going to bail out
*/ */
err = use_all_metadata(ife, exists); err = use_all_metadata(ife, exists);
if (err) { if (err)
tcf_idr_release(*a, bind); goto metadata_parse_err;
kfree(p);
return err;
}
} }
if (exists) if (exists)
spin_lock_bh(&ife->tcf_lock); spin_lock_bh(&ife->tcf_lock);
ife->tcf_action = parm->action;
/* protected by tcf_lock when modifying existing action */ /* protected by tcf_lock when modifying existing action */
goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch);
rcu_swap_protected(ife->params, p, 1); rcu_swap_protected(ife->params, p, 1);
if (exists) if (exists)
spin_unlock_bh(&ife->tcf_lock); spin_unlock_bh(&ife->tcf_lock);
if (goto_ch)
tcf_chain_put_by_act(goto_ch);
if (p) if (p)
kfree_rcu(p, rcu); kfree_rcu(p, rcu);
...@@ -603,6 +603,13 @@ static int tcf_ife_init(struct net *net, struct nlattr *nla, ...@@ -603,6 +603,13 @@ static int tcf_ife_init(struct net *net, struct nlattr *nla,
tcf_idr_insert(tn, *a); tcf_idr_insert(tn, *a);
return ret; return ret;
metadata_parse_err:
if (goto_ch)
tcf_chain_put_by_act(goto_ch);
release_idr:
kfree(p);
tcf_idr_release(*a, bind);
return err;
} }
static int tcf_ife_dump(struct sk_buff *skb, struct tc_action *a, int bind, static int tcf_ife_dump(struct sk_buff *skb, struct tc_action *a, int bind,
......
...@@ -97,7 +97,8 @@ static const struct nla_policy ipt_policy[TCA_IPT_MAX + 1] = { ...@@ -97,7 +97,8 @@ static const struct nla_policy ipt_policy[TCA_IPT_MAX + 1] = {
static int __tcf_ipt_init(struct net *net, unsigned int id, struct nlattr *nla, static int __tcf_ipt_init(struct net *net, unsigned int id, struct nlattr *nla,
struct nlattr *est, struct tc_action **a, struct nlattr *est, struct tc_action **a,
const struct tc_action_ops *ops, int ovr, int bind) const struct tc_action_ops *ops, int ovr, int bind,
struct tcf_proto *tp)
{ {
struct tc_action_net *tn = net_generic(net, id); struct tc_action_net *tn = net_generic(net, id);
struct nlattr *tb[TCA_IPT_MAX + 1]; struct nlattr *tb[TCA_IPT_MAX + 1];
...@@ -205,20 +206,20 @@ static int __tcf_ipt_init(struct net *net, unsigned int id, struct nlattr *nla, ...@@ -205,20 +206,20 @@ static int __tcf_ipt_init(struct net *net, unsigned int id, struct nlattr *nla,
static int tcf_ipt_init(struct net *net, struct nlattr *nla, static int tcf_ipt_init(struct net *net, struct nlattr *nla,
struct nlattr *est, struct tc_action **a, int ovr, struct nlattr *est, struct tc_action **a, int ovr,
int bind, bool rtnl_held, int bind, bool rtnl_held, struct tcf_proto *tp,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
{ {
return __tcf_ipt_init(net, ipt_net_id, nla, est, a, &act_ipt_ops, ovr, return __tcf_ipt_init(net, ipt_net_id, nla, est, a, &act_ipt_ops, ovr,
bind); bind, tp);
} }
static int tcf_xt_init(struct net *net, struct nlattr *nla, static int tcf_xt_init(struct net *net, struct nlattr *nla,
struct nlattr *est, struct tc_action **a, int ovr, struct nlattr *est, struct tc_action **a, int ovr,
int bind, bool unlocked, int bind, bool unlocked, struct tcf_proto *tp,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
{ {
return __tcf_ipt_init(net, xt_net_id, nla, est, a, &act_xt_ops, ovr, return __tcf_ipt_init(net, xt_net_id, nla, est, a, &act_xt_ops, ovr,
bind); bind, tp);
} }
static int tcf_ipt_act(struct sk_buff *skb, const struct tc_action *a, static int tcf_ipt_act(struct sk_buff *skb, const struct tc_action *a,
......
...@@ -94,10 +94,12 @@ static struct tc_action_ops act_mirred_ops; ...@@ -94,10 +94,12 @@ static struct tc_action_ops act_mirred_ops;
static int tcf_mirred_init(struct net *net, struct nlattr *nla, static int tcf_mirred_init(struct net *net, struct nlattr *nla,
struct nlattr *est, struct tc_action **a, struct nlattr *est, struct tc_action **a,
int ovr, int bind, bool rtnl_held, int ovr, int bind, bool rtnl_held,
struct tcf_proto *tp,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
{ {
struct tc_action_net *tn = net_generic(net, mirred_net_id); struct tc_action_net *tn = net_generic(net, mirred_net_id);
struct nlattr *tb[TCA_MIRRED_MAX + 1]; struct nlattr *tb[TCA_MIRRED_MAX + 1];
struct tcf_chain *goto_ch = NULL;
bool mac_header_xmit = false; bool mac_header_xmit = false;
struct tc_mirred *parm; struct tc_mirred *parm;
struct tcf_mirred *m; struct tcf_mirred *m;
...@@ -157,18 +159,20 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla, ...@@ -157,18 +159,20 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
tcf_idr_release(*a, bind); tcf_idr_release(*a, bind);
return -EEXIST; return -EEXIST;
} }
err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
if (err < 0)
goto release_idr;
m = to_mirred(*a); m = to_mirred(*a);
spin_lock_bh(&m->tcf_lock); spin_lock_bh(&m->tcf_lock);
m->tcf_action = parm->action;
m->tcfm_eaction = parm->eaction;
if (parm->ifindex) { if (parm->ifindex) {
dev = dev_get_by_index(net, parm->ifindex); dev = dev_get_by_index(net, parm->ifindex);
if (!dev) { if (!dev) {
spin_unlock_bh(&m->tcf_lock); spin_unlock_bh(&m->tcf_lock);
tcf_idr_release(*a, bind); err = -ENODEV;
return -ENODEV; goto put_chain;
} }
mac_header_xmit = dev_is_mac_header_xmit(dev); mac_header_xmit = dev_is_mac_header_xmit(dev);
rcu_swap_protected(m->tcfm_dev, dev, rcu_swap_protected(m->tcfm_dev, dev,
...@@ -177,7 +181,11 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla, ...@@ -177,7 +181,11 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
dev_put(dev); dev_put(dev);
m->tcfm_mac_header_xmit = mac_header_xmit; m->tcfm_mac_header_xmit = mac_header_xmit;
} }
goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch);
m->tcfm_eaction = parm->eaction;
spin_unlock_bh(&m->tcf_lock); spin_unlock_bh(&m->tcf_lock);
if (goto_ch)
tcf_chain_put_by_act(goto_ch);
if (ret == ACT_P_CREATED) { if (ret == ACT_P_CREATED) {
spin_lock(&mirred_list_lock); spin_lock(&mirred_list_lock);
...@@ -188,6 +196,12 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla, ...@@ -188,6 +196,12 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
} }
return ret; return ret;
put_chain:
if (goto_ch)
tcf_chain_put_by_act(goto_ch);
release_idr:
tcf_idr_release(*a, bind);
return err;
} }
static int tcf_mirred_act(struct sk_buff *skb, const struct tc_action *a, static int tcf_mirred_act(struct sk_buff *skb, const struct tc_action *a,
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <linux/string.h> #include <linux/string.h>
#include <linux/tc_act/tc_nat.h> #include <linux/tc_act/tc_nat.h>
#include <net/act_api.h> #include <net/act_api.h>
#include <net/pkt_cls.h>
#include <net/icmp.h> #include <net/icmp.h>
#include <net/ip.h> #include <net/ip.h>
#include <net/netlink.h> #include <net/netlink.h>
...@@ -38,10 +39,12 @@ static const struct nla_policy nat_policy[TCA_NAT_MAX + 1] = { ...@@ -38,10 +39,12 @@ static const struct nla_policy nat_policy[TCA_NAT_MAX + 1] = {
static int tcf_nat_init(struct net *net, struct nlattr *nla, struct nlattr *est, static int tcf_nat_init(struct net *net, struct nlattr *nla, struct nlattr *est,
struct tc_action **a, int ovr, int bind, struct tc_action **a, int ovr, int bind,
bool rtnl_held, struct netlink_ext_ack *extack) bool rtnl_held, struct tcf_proto *tp,
struct netlink_ext_ack *extack)
{ {
struct tc_action_net *tn = net_generic(net, nat_net_id); struct tc_action_net *tn = net_generic(net, nat_net_id);
struct nlattr *tb[TCA_NAT_MAX + 1]; struct nlattr *tb[TCA_NAT_MAX + 1];
struct tcf_chain *goto_ch = NULL;
struct tc_nat *parm; struct tc_nat *parm;
int ret = 0, err; int ret = 0, err;
struct tcf_nat *p; struct tcf_nat *p;
...@@ -76,6 +79,9 @@ static int tcf_nat_init(struct net *net, struct nlattr *nla, struct nlattr *est, ...@@ -76,6 +79,9 @@ static int tcf_nat_init(struct net *net, struct nlattr *nla, struct nlattr *est,
} else { } else {
return err; return err;
} }
err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
if (err < 0)
goto release_idr;
p = to_tcf_nat(*a); p = to_tcf_nat(*a);
spin_lock_bh(&p->tcf_lock); spin_lock_bh(&p->tcf_lock);
...@@ -84,13 +90,18 @@ static int tcf_nat_init(struct net *net, struct nlattr *nla, struct nlattr *est, ...@@ -84,13 +90,18 @@ static int tcf_nat_init(struct net *net, struct nlattr *nla, struct nlattr *est,
p->mask = parm->mask; p->mask = parm->mask;
p->flags = parm->flags; p->flags = parm->flags;
p->tcf_action = parm->action; goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch);
spin_unlock_bh(&p->tcf_lock); spin_unlock_bh(&p->tcf_lock);
if (goto_ch)
tcf_chain_put_by_act(goto_ch);
if (ret == ACT_P_CREATED) if (ret == ACT_P_CREATED)
tcf_idr_insert(tn, *a); tcf_idr_insert(tn, *a);
return ret; return ret;
release_idr:
tcf_idr_release(*a, bind);
return err;
} }
static int tcf_nat_act(struct sk_buff *skb, const struct tc_action *a, static int tcf_nat_act(struct sk_buff *skb, const struct tc_action *a,
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include <linux/tc_act/tc_pedit.h> #include <linux/tc_act/tc_pedit.h>
#include <net/tc_act/tc_pedit.h> #include <net/tc_act/tc_pedit.h>
#include <uapi/linux/tc_act/tc_pedit.h> #include <uapi/linux/tc_act/tc_pedit.h>
#include <net/pkt_cls.h>
static unsigned int pedit_net_id; static unsigned int pedit_net_id;
static struct tc_action_ops act_pedit_ops; static struct tc_action_ops act_pedit_ops;
...@@ -138,10 +139,11 @@ static int tcf_pedit_key_ex_dump(struct sk_buff *skb, ...@@ -138,10 +139,11 @@ static int tcf_pedit_key_ex_dump(struct sk_buff *skb,
static int tcf_pedit_init(struct net *net, struct nlattr *nla, static int tcf_pedit_init(struct net *net, struct nlattr *nla,
struct nlattr *est, struct tc_action **a, struct nlattr *est, struct tc_action **a,
int ovr, int bind, bool rtnl_held, int ovr, int bind, bool rtnl_held,
struct netlink_ext_ack *extack) struct tcf_proto *tp, struct netlink_ext_ack *extack)
{ {
struct tc_action_net *tn = net_generic(net, pedit_net_id); struct tc_action_net *tn = net_generic(net, pedit_net_id);
struct nlattr *tb[TCA_PEDIT_MAX + 1]; struct nlattr *tb[TCA_PEDIT_MAX + 1];
struct tcf_chain *goto_ch = NULL;
struct tc_pedit_key *keys = NULL; struct tc_pedit_key *keys = NULL;
struct tcf_pedit_key_ex *keys_ex; struct tcf_pedit_key_ex *keys_ex;
struct tc_pedit *parm; struct tc_pedit *parm;
...@@ -205,6 +207,11 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla, ...@@ -205,6 +207,11 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
goto out_free; goto out_free;
} }
err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
if (err < 0) {
ret = err;
goto out_release;
}
p = to_pedit(*a); p = to_pedit(*a);
spin_lock_bh(&p->tcf_lock); spin_lock_bh(&p->tcf_lock);
...@@ -214,7 +221,7 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla, ...@@ -214,7 +221,7 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
if (!keys) { if (!keys) {
spin_unlock_bh(&p->tcf_lock); spin_unlock_bh(&p->tcf_lock);
ret = -ENOMEM; ret = -ENOMEM;
goto out_release; goto put_chain;
} }
kfree(p->tcfp_keys); kfree(p->tcfp_keys);
p->tcfp_keys = keys; p->tcfp_keys = keys;
...@@ -223,16 +230,21 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla, ...@@ -223,16 +230,21 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
memcpy(p->tcfp_keys, parm->keys, ksize); memcpy(p->tcfp_keys, parm->keys, ksize);
p->tcfp_flags = parm->flags; p->tcfp_flags = parm->flags;
p->tcf_action = parm->action; goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch);
kfree(p->tcfp_keys_ex); kfree(p->tcfp_keys_ex);
p->tcfp_keys_ex = keys_ex; p->tcfp_keys_ex = keys_ex;
spin_unlock_bh(&p->tcf_lock); spin_unlock_bh(&p->tcf_lock);
if (goto_ch)
tcf_chain_put_by_act(goto_ch);
if (ret == ACT_P_CREATED) if (ret == ACT_P_CREATED)
tcf_idr_insert(tn, *a); tcf_idr_insert(tn, *a);
return ret; return ret;
put_chain:
if (goto_ch)
tcf_chain_put_by_act(goto_ch);
out_release: out_release:
tcf_idr_release(*a, bind); tcf_idr_release(*a, bind);
out_free: out_free:
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <net/act_api.h> #include <net/act_api.h>
#include <net/netlink.h> #include <net/netlink.h>
#include <net/pkt_cls.h>
struct tcf_police_params { struct tcf_police_params {
int tcfp_result; int tcfp_result;
...@@ -83,10 +84,12 @@ static const struct nla_policy police_policy[TCA_POLICE_MAX + 1] = { ...@@ -83,10 +84,12 @@ static const struct nla_policy police_policy[TCA_POLICE_MAX + 1] = {
static int tcf_police_init(struct net *net, struct nlattr *nla, static int tcf_police_init(struct net *net, struct nlattr *nla,
struct nlattr *est, struct tc_action **a, struct nlattr *est, struct tc_action **a,
int ovr, int bind, bool rtnl_held, int ovr, int bind, bool rtnl_held,
struct tcf_proto *tp,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
{ {
int ret = 0, tcfp_result = TC_ACT_OK, err, size; int ret = 0, tcfp_result = TC_ACT_OK, err, size;
struct nlattr *tb[TCA_POLICE_MAX + 1]; struct nlattr *tb[TCA_POLICE_MAX + 1];
struct tcf_chain *goto_ch = NULL;
struct tc_police *parm; struct tc_police *parm;
struct tcf_police *police; struct tcf_police *police;
struct qdisc_rate_table *R_tab = NULL, *P_tab = NULL; struct qdisc_rate_table *R_tab = NULL, *P_tab = NULL;
...@@ -128,6 +131,9 @@ static int tcf_police_init(struct net *net, struct nlattr *nla, ...@@ -128,6 +131,9 @@ static int tcf_police_init(struct net *net, struct nlattr *nla,
tcf_idr_release(*a, bind); tcf_idr_release(*a, bind);
return -EEXIST; return -EEXIST;
} }
err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
if (err < 0)
goto release_idr;
police = to_police(*a); police = to_police(*a);
if (parm->rate.rate) { if (parm->rate.rate) {
...@@ -213,12 +219,14 @@ static int tcf_police_init(struct net *net, struct nlattr *nla, ...@@ -213,12 +219,14 @@ static int tcf_police_init(struct net *net, struct nlattr *nla,
if (new->peak_present) if (new->peak_present)
police->tcfp_ptoks = new->tcfp_mtu_ptoks; police->tcfp_ptoks = new->tcfp_mtu_ptoks;
spin_unlock_bh(&police->tcfp_lock); spin_unlock_bh(&police->tcfp_lock);
police->tcf_action = parm->action; goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch);
rcu_swap_protected(police->params, rcu_swap_protected(police->params,
new, new,
lockdep_is_held(&police->tcf_lock)); lockdep_is_held(&police->tcf_lock));
spin_unlock_bh(&police->tcf_lock); spin_unlock_bh(&police->tcf_lock);
if (goto_ch)
tcf_chain_put_by_act(goto_ch);
if (new) if (new)
kfree_rcu(new, rcu); kfree_rcu(new, rcu);
...@@ -229,6 +237,9 @@ static int tcf_police_init(struct net *net, struct nlattr *nla, ...@@ -229,6 +237,9 @@ static int tcf_police_init(struct net *net, struct nlattr *nla,
failure: failure:
qdisc_put_rtab(P_tab); qdisc_put_rtab(P_tab);
qdisc_put_rtab(R_tab); qdisc_put_rtab(R_tab);
if (goto_ch)
tcf_chain_put_by_act(goto_ch);
release_idr:
tcf_idr_release(*a, bind); tcf_idr_release(*a, bind);
return err; return err;
} }
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <linux/tc_act/tc_sample.h> #include <linux/tc_act/tc_sample.h>
#include <net/tc_act/tc_sample.h> #include <net/tc_act/tc_sample.h>
#include <net/psample.h> #include <net/psample.h>
#include <net/pkt_cls.h>
#include <linux/if_arp.h> #include <linux/if_arp.h>
...@@ -37,12 +38,13 @@ static const struct nla_policy sample_policy[TCA_SAMPLE_MAX + 1] = { ...@@ -37,12 +38,13 @@ static const struct nla_policy sample_policy[TCA_SAMPLE_MAX + 1] = {
static int tcf_sample_init(struct net *net, struct nlattr *nla, static int tcf_sample_init(struct net *net, struct nlattr *nla,
struct nlattr *est, struct tc_action **a, int ovr, struct nlattr *est, struct tc_action **a, int ovr,
int bind, bool rtnl_held, int bind, bool rtnl_held, struct tcf_proto *tp,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
{ {
struct tc_action_net *tn = net_generic(net, sample_net_id); struct tc_action_net *tn = net_generic(net, sample_net_id);
struct nlattr *tb[TCA_SAMPLE_MAX + 1]; struct nlattr *tb[TCA_SAMPLE_MAX + 1];
struct psample_group *psample_group; struct psample_group *psample_group;
struct tcf_chain *goto_ch = NULL;
struct tc_sample *parm; struct tc_sample *parm;
u32 psample_group_num; u32 psample_group_num;
struct tcf_sample *s; struct tcf_sample *s;
...@@ -79,18 +81,21 @@ static int tcf_sample_init(struct net *net, struct nlattr *nla, ...@@ -79,18 +81,21 @@ static int tcf_sample_init(struct net *net, struct nlattr *nla,
tcf_idr_release(*a, bind); tcf_idr_release(*a, bind);
return -EEXIST; return -EEXIST;
} }
err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
if (err < 0)
goto release_idr;
psample_group_num = nla_get_u32(tb[TCA_SAMPLE_PSAMPLE_GROUP]); psample_group_num = nla_get_u32(tb[TCA_SAMPLE_PSAMPLE_GROUP]);
psample_group = psample_group_get(net, psample_group_num); psample_group = psample_group_get(net, psample_group_num);
if (!psample_group) { if (!psample_group) {
tcf_idr_release(*a, bind); err = -ENOMEM;
return -ENOMEM; goto put_chain;
} }
s = to_sample(*a); s = to_sample(*a);
spin_lock_bh(&s->tcf_lock); spin_lock_bh(&s->tcf_lock);
s->tcf_action = parm->action; goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch);
s->rate = nla_get_u32(tb[TCA_SAMPLE_RATE]); s->rate = nla_get_u32(tb[TCA_SAMPLE_RATE]);
s->psample_group_num = psample_group_num; s->psample_group_num = psample_group_num;
RCU_INIT_POINTER(s->psample_group, psample_group); RCU_INIT_POINTER(s->psample_group, psample_group);
...@@ -100,10 +105,18 @@ static int tcf_sample_init(struct net *net, struct nlattr *nla, ...@@ -100,10 +105,18 @@ static int tcf_sample_init(struct net *net, struct nlattr *nla,
s->trunc_size = nla_get_u32(tb[TCA_SAMPLE_TRUNC_SIZE]); s->trunc_size = nla_get_u32(tb[TCA_SAMPLE_TRUNC_SIZE]);
} }
spin_unlock_bh(&s->tcf_lock); spin_unlock_bh(&s->tcf_lock);
if (goto_ch)
tcf_chain_put_by_act(goto_ch);
if (ret == ACT_P_CREATED) if (ret == ACT_P_CREATED)
tcf_idr_insert(tn, *a); tcf_idr_insert(tn, *a);
return ret; return ret;
put_chain:
if (goto_ch)
tcf_chain_put_by_act(goto_ch);
release_idr:
tcf_idr_release(*a, bind);
return err;
} }
static void tcf_sample_cleanup(struct tc_action *a) static void tcf_sample_cleanup(struct tc_action *a)
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <linux/rtnetlink.h> #include <linux/rtnetlink.h>
#include <net/netlink.h> #include <net/netlink.h>
#include <net/pkt_sched.h> #include <net/pkt_sched.h>
#include <net/pkt_cls.h>
#include <linux/tc_act/tc_defact.h> #include <linux/tc_act/tc_defact.h>
#include <net/tc_act/tc_defact.h> #include <net/tc_act/tc_defact.h>
...@@ -60,14 +61,26 @@ static int alloc_defdata(struct tcf_defact *d, const struct nlattr *defdata) ...@@ -60,14 +61,26 @@ static int alloc_defdata(struct tcf_defact *d, const struct nlattr *defdata)
return 0; return 0;
} }
static void reset_policy(struct tcf_defact *d, const struct nlattr *defdata, static int reset_policy(struct tc_action *a, const struct nlattr *defdata,
struct tc_defact *p) struct tc_defact *p, struct tcf_proto *tp,
struct netlink_ext_ack *extack)
{ {
struct tcf_chain *goto_ch = NULL;
struct tcf_defact *d;
int err;
err = tcf_action_check_ctrlact(p->action, tp, &goto_ch, extack);
if (err < 0)
return err;
d = to_defact(a);
spin_lock_bh(&d->tcf_lock); spin_lock_bh(&d->tcf_lock);
d->tcf_action = p->action; goto_ch = tcf_action_set_ctrlact(a, p->action, goto_ch);
memset(d->tcfd_defdata, 0, SIMP_MAX_DATA); memset(d->tcfd_defdata, 0, SIMP_MAX_DATA);
nla_strlcpy(d->tcfd_defdata, defdata, SIMP_MAX_DATA); nla_strlcpy(d->tcfd_defdata, defdata, SIMP_MAX_DATA);
spin_unlock_bh(&d->tcf_lock); spin_unlock_bh(&d->tcf_lock);
if (goto_ch)
tcf_chain_put_by_act(goto_ch);
return 0;
} }
static const struct nla_policy simple_policy[TCA_DEF_MAX + 1] = { static const struct nla_policy simple_policy[TCA_DEF_MAX + 1] = {
...@@ -78,10 +91,11 @@ static const struct nla_policy simple_policy[TCA_DEF_MAX + 1] = { ...@@ -78,10 +91,11 @@ static const struct nla_policy simple_policy[TCA_DEF_MAX + 1] = {
static int tcf_simp_init(struct net *net, struct nlattr *nla, static int tcf_simp_init(struct net *net, struct nlattr *nla,
struct nlattr *est, struct tc_action **a, struct nlattr *est, struct tc_action **a,
int ovr, int bind, bool rtnl_held, int ovr, int bind, bool rtnl_held,
struct netlink_ext_ack *extack) struct tcf_proto *tp, struct netlink_ext_ack *extack)
{ {
struct tc_action_net *tn = net_generic(net, simp_net_id); struct tc_action_net *tn = net_generic(net, simp_net_id);
struct nlattr *tb[TCA_DEF_MAX + 1]; struct nlattr *tb[TCA_DEF_MAX + 1];
struct tcf_chain *goto_ch = NULL;
struct tc_defact *parm; struct tc_defact *parm;
struct tcf_defact *d; struct tcf_defact *d;
bool exists = false; bool exists = false;
...@@ -122,27 +136,37 @@ static int tcf_simp_init(struct net *net, struct nlattr *nla, ...@@ -122,27 +136,37 @@ static int tcf_simp_init(struct net *net, struct nlattr *nla,
} }
d = to_defact(*a); d = to_defact(*a);
ret = alloc_defdata(d, tb[TCA_DEF_DATA]); err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch,
if (ret < 0) { extack);
tcf_idr_release(*a, bind); if (err < 0)
return ret; goto release_idr;
}
d->tcf_action = parm->action; err = alloc_defdata(d, tb[TCA_DEF_DATA]);
if (err < 0)
goto put_chain;
tcf_action_set_ctrlact(*a, parm->action, goto_ch);
ret = ACT_P_CREATED; ret = ACT_P_CREATED;
} else { } else {
d = to_defact(*a);
if (!ovr) { if (!ovr) {
tcf_idr_release(*a, bind); err = -EEXIST;
return -EEXIST; goto release_idr;
} }
reset_policy(d, tb[TCA_DEF_DATA], parm); err = reset_policy(*a, tb[TCA_DEF_DATA], parm, tp, extack);
if (err)
goto release_idr;
} }
if (ret == ACT_P_CREATED) if (ret == ACT_P_CREATED)
tcf_idr_insert(tn, *a); tcf_idr_insert(tn, *a);
return ret; return ret;
put_chain:
if (goto_ch)
tcf_chain_put_by_act(goto_ch);
release_idr:
tcf_idr_release(*a, bind);
return err;
} }
static int tcf_simp_dump(struct sk_buff *skb, struct tc_action *a, static int tcf_simp_dump(struct sk_buff *skb, struct tc_action *a,
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include <net/ip.h> #include <net/ip.h>
#include <net/ipv6.h> #include <net/ipv6.h>
#include <net/dsfield.h> #include <net/dsfield.h>
#include <net/pkt_cls.h>
#include <linux/tc_act/tc_skbedit.h> #include <linux/tc_act/tc_skbedit.h>
#include <net/tc_act/tc_skbedit.h> #include <net/tc_act/tc_skbedit.h>
...@@ -96,11 +97,13 @@ static const struct nla_policy skbedit_policy[TCA_SKBEDIT_MAX + 1] = { ...@@ -96,11 +97,13 @@ static const struct nla_policy skbedit_policy[TCA_SKBEDIT_MAX + 1] = {
static int tcf_skbedit_init(struct net *net, struct nlattr *nla, static int tcf_skbedit_init(struct net *net, struct nlattr *nla,
struct nlattr *est, struct tc_action **a, struct nlattr *est, struct tc_action **a,
int ovr, int bind, bool rtnl_held, int ovr, int bind, bool rtnl_held,
struct tcf_proto *tp,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
{ {
struct tc_action_net *tn = net_generic(net, skbedit_net_id); struct tc_action_net *tn = net_generic(net, skbedit_net_id);
struct tcf_skbedit_params *params_new; struct tcf_skbedit_params *params_new;
struct nlattr *tb[TCA_SKBEDIT_MAX + 1]; struct nlattr *tb[TCA_SKBEDIT_MAX + 1];
struct tcf_chain *goto_ch = NULL;
struct tc_skbedit *parm; struct tc_skbedit *parm;
struct tcf_skbedit *d; struct tcf_skbedit *d;
u32 flags = 0, *priority = NULL, *mark = NULL, *mask = NULL; u32 flags = 0, *priority = NULL, *mark = NULL, *mask = NULL;
...@@ -186,11 +189,14 @@ static int tcf_skbedit_init(struct net *net, struct nlattr *nla, ...@@ -186,11 +189,14 @@ static int tcf_skbedit_init(struct net *net, struct nlattr *nla,
return -EEXIST; return -EEXIST;
} }
} }
err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
if (err < 0)
goto release_idr;
params_new = kzalloc(sizeof(*params_new), GFP_KERNEL); params_new = kzalloc(sizeof(*params_new), GFP_KERNEL);
if (unlikely(!params_new)) { if (unlikely(!params_new)) {
tcf_idr_release(*a, bind); err = -ENOMEM;
return -ENOMEM; goto put_chain;
} }
params_new->flags = flags; params_new->flags = flags;
...@@ -208,16 +214,24 @@ static int tcf_skbedit_init(struct net *net, struct nlattr *nla, ...@@ -208,16 +214,24 @@ static int tcf_skbedit_init(struct net *net, struct nlattr *nla,
params_new->mask = *mask; params_new->mask = *mask;
spin_lock_bh(&d->tcf_lock); spin_lock_bh(&d->tcf_lock);
d->tcf_action = parm->action; goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch);
rcu_swap_protected(d->params, params_new, rcu_swap_protected(d->params, params_new,
lockdep_is_held(&d->tcf_lock)); lockdep_is_held(&d->tcf_lock));
spin_unlock_bh(&d->tcf_lock); spin_unlock_bh(&d->tcf_lock);
if (params_new) if (params_new)
kfree_rcu(params_new, rcu); kfree_rcu(params_new, rcu);
if (goto_ch)
tcf_chain_put_by_act(goto_ch);
if (ret == ACT_P_CREATED) if (ret == ACT_P_CREATED)
tcf_idr_insert(tn, *a); tcf_idr_insert(tn, *a);
return ret; return ret;
put_chain:
if (goto_ch)
tcf_chain_put_by_act(goto_ch);
release_idr:
tcf_idr_release(*a, bind);
return err;
} }
static int tcf_skbedit_dump(struct sk_buff *skb, struct tc_action *a, static int tcf_skbedit_dump(struct sk_buff *skb, struct tc_action *a,
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <linux/rtnetlink.h> #include <linux/rtnetlink.h>
#include <net/netlink.h> #include <net/netlink.h>
#include <net/pkt_sched.h> #include <net/pkt_sched.h>
#include <net/pkt_cls.h>
#include <linux/tc_act/tc_skbmod.h> #include <linux/tc_act/tc_skbmod.h>
#include <net/tc_act/tc_skbmod.h> #include <net/tc_act/tc_skbmod.h>
...@@ -82,11 +83,13 @@ static const struct nla_policy skbmod_policy[TCA_SKBMOD_MAX + 1] = { ...@@ -82,11 +83,13 @@ static const struct nla_policy skbmod_policy[TCA_SKBMOD_MAX + 1] = {
static int tcf_skbmod_init(struct net *net, struct nlattr *nla, static int tcf_skbmod_init(struct net *net, struct nlattr *nla,
struct nlattr *est, struct tc_action **a, struct nlattr *est, struct tc_action **a,
int ovr, int bind, bool rtnl_held, int ovr, int bind, bool rtnl_held,
struct tcf_proto *tp,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
{ {
struct tc_action_net *tn = net_generic(net, skbmod_net_id); struct tc_action_net *tn = net_generic(net, skbmod_net_id);
struct nlattr *tb[TCA_SKBMOD_MAX + 1]; struct nlattr *tb[TCA_SKBMOD_MAX + 1];
struct tcf_skbmod_params *p, *p_old; struct tcf_skbmod_params *p, *p_old;
struct tcf_chain *goto_ch = NULL;
struct tc_skbmod *parm; struct tc_skbmod *parm;
struct tcf_skbmod *d; struct tcf_skbmod *d;
bool exists = false; bool exists = false;
...@@ -153,21 +156,24 @@ static int tcf_skbmod_init(struct net *net, struct nlattr *nla, ...@@ -153,21 +156,24 @@ static int tcf_skbmod_init(struct net *net, struct nlattr *nla,
tcf_idr_release(*a, bind); tcf_idr_release(*a, bind);
return -EEXIST; return -EEXIST;
} }
err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
if (err < 0)
goto release_idr;
d = to_skbmod(*a); d = to_skbmod(*a);
p = kzalloc(sizeof(struct tcf_skbmod_params), GFP_KERNEL); p = kzalloc(sizeof(struct tcf_skbmod_params), GFP_KERNEL);
if (unlikely(!p)) { if (unlikely(!p)) {
tcf_idr_release(*a, bind); err = -ENOMEM;
return -ENOMEM; goto put_chain;
} }
p->flags = lflags; p->flags = lflags;
d->tcf_action = parm->action;
if (ovr) if (ovr)
spin_lock_bh(&d->tcf_lock); spin_lock_bh(&d->tcf_lock);
/* Protected by tcf_lock if overwriting existing action. */ /* Protected by tcf_lock if overwriting existing action. */
goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch);
p_old = rcu_dereference_protected(d->skbmod_p, 1); p_old = rcu_dereference_protected(d->skbmod_p, 1);
if (lflags & SKBMOD_F_DMAC) if (lflags & SKBMOD_F_DMAC)
...@@ -183,10 +189,18 @@ static int tcf_skbmod_init(struct net *net, struct nlattr *nla, ...@@ -183,10 +189,18 @@ static int tcf_skbmod_init(struct net *net, struct nlattr *nla,
if (p_old) if (p_old)
kfree_rcu(p_old, rcu); kfree_rcu(p_old, rcu);
if (goto_ch)
tcf_chain_put_by_act(goto_ch);
if (ret == ACT_P_CREATED) if (ret == ACT_P_CREATED)
tcf_idr_insert(tn, *a); tcf_idr_insert(tn, *a);
return ret; return ret;
put_chain:
if (goto_ch)
tcf_chain_put_by_act(goto_ch);
release_idr:
tcf_idr_release(*a, bind);
return err;
} }
static void tcf_skbmod_cleanup(struct tc_action *a) static void tcf_skbmod_cleanup(struct tc_action *a)
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <net/netlink.h> #include <net/netlink.h>
#include <net/pkt_sched.h> #include <net/pkt_sched.h>
#include <net/dst.h> #include <net/dst.h>
#include <net/pkt_cls.h>
#include <linux/tc_act/tc_tunnel_key.h> #include <linux/tc_act/tc_tunnel_key.h>
#include <net/tc_act/tc_tunnel_key.h> #include <net/tc_act/tc_tunnel_key.h>
...@@ -210,12 +211,14 @@ static void tunnel_key_release_params(struct tcf_tunnel_key_params *p) ...@@ -210,12 +211,14 @@ static void tunnel_key_release_params(struct tcf_tunnel_key_params *p)
static int tunnel_key_init(struct net *net, struct nlattr *nla, static int tunnel_key_init(struct net *net, struct nlattr *nla,
struct nlattr *est, struct tc_action **a, struct nlattr *est, struct tc_action **a,
int ovr, int bind, bool rtnl_held, int ovr, int bind, bool rtnl_held,
struct tcf_proto *tp,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
{ {
struct tc_action_net *tn = net_generic(net, tunnel_key_net_id); struct tc_action_net *tn = net_generic(net, tunnel_key_net_id);
struct nlattr *tb[TCA_TUNNEL_KEY_MAX + 1]; struct nlattr *tb[TCA_TUNNEL_KEY_MAX + 1];
struct tcf_tunnel_key_params *params_new; struct tcf_tunnel_key_params *params_new;
struct metadata_dst *metadata = NULL; struct metadata_dst *metadata = NULL;
struct tcf_chain *goto_ch = NULL;
struct tc_tunnel_key *parm; struct tc_tunnel_key *parm;
struct tcf_tunnel_key *t; struct tcf_tunnel_key *t;
bool exists = false; bool exists = false;
...@@ -359,6 +362,12 @@ static int tunnel_key_init(struct net *net, struct nlattr *nla, ...@@ -359,6 +362,12 @@ static int tunnel_key_init(struct net *net, struct nlattr *nla,
goto release_tun_meta; goto release_tun_meta;
} }
err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
if (err < 0) {
ret = err;
exists = true;
goto release_tun_meta;
}
t = to_tunnel_key(*a); t = to_tunnel_key(*a);
params_new = kzalloc(sizeof(*params_new), GFP_KERNEL); params_new = kzalloc(sizeof(*params_new), GFP_KERNEL);
...@@ -366,23 +375,29 @@ static int tunnel_key_init(struct net *net, struct nlattr *nla, ...@@ -366,23 +375,29 @@ static int tunnel_key_init(struct net *net, struct nlattr *nla,
NL_SET_ERR_MSG(extack, "Cannot allocate tunnel key parameters"); NL_SET_ERR_MSG(extack, "Cannot allocate tunnel key parameters");
ret = -ENOMEM; ret = -ENOMEM;
exists = true; exists = true;
goto release_tun_meta; goto put_chain;
} }
params_new->tcft_action = parm->t_action; params_new->tcft_action = parm->t_action;
params_new->tcft_enc_metadata = metadata; params_new->tcft_enc_metadata = metadata;
spin_lock_bh(&t->tcf_lock); spin_lock_bh(&t->tcf_lock);
t->tcf_action = parm->action; goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch);
rcu_swap_protected(t->params, params_new, rcu_swap_protected(t->params, params_new,
lockdep_is_held(&t->tcf_lock)); lockdep_is_held(&t->tcf_lock));
spin_unlock_bh(&t->tcf_lock); spin_unlock_bh(&t->tcf_lock);
tunnel_key_release_params(params_new); tunnel_key_release_params(params_new);
if (goto_ch)
tcf_chain_put_by_act(goto_ch);
if (ret == ACT_P_CREATED) if (ret == ACT_P_CREATED)
tcf_idr_insert(tn, *a); tcf_idr_insert(tn, *a);
return ret; return ret;
put_chain:
if (goto_ch)
tcf_chain_put_by_act(goto_ch);
release_tun_meta: release_tun_meta:
if (metadata) if (metadata)
dst_release(&metadata->dst); dst_release(&metadata->dst);
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <linux/if_vlan.h> #include <linux/if_vlan.h>
#include <net/netlink.h> #include <net/netlink.h>
#include <net/pkt_sched.h> #include <net/pkt_sched.h>
#include <net/pkt_cls.h>
#include <linux/tc_act/tc_vlan.h> #include <linux/tc_act/tc_vlan.h>
#include <net/tc_act/tc_vlan.h> #include <net/tc_act/tc_vlan.h>
...@@ -105,10 +106,11 @@ static const struct nla_policy vlan_policy[TCA_VLAN_MAX + 1] = { ...@@ -105,10 +106,11 @@ static const struct nla_policy vlan_policy[TCA_VLAN_MAX + 1] = {
static int tcf_vlan_init(struct net *net, struct nlattr *nla, static int tcf_vlan_init(struct net *net, struct nlattr *nla,
struct nlattr *est, struct tc_action **a, struct nlattr *est, struct tc_action **a,
int ovr, int bind, bool rtnl_held, int ovr, int bind, bool rtnl_held,
struct netlink_ext_ack *extack) struct tcf_proto *tp, struct netlink_ext_ack *extack)
{ {
struct tc_action_net *tn = net_generic(net, vlan_net_id); struct tc_action_net *tn = net_generic(net, vlan_net_id);
struct nlattr *tb[TCA_VLAN_MAX + 1]; struct nlattr *tb[TCA_VLAN_MAX + 1];
struct tcf_chain *goto_ch = NULL;
struct tcf_vlan_params *p; struct tcf_vlan_params *p;
struct tc_vlan *parm; struct tc_vlan *parm;
struct tcf_vlan *v; struct tcf_vlan *v;
...@@ -200,12 +202,16 @@ static int tcf_vlan_init(struct net *net, struct nlattr *nla, ...@@ -200,12 +202,16 @@ static int tcf_vlan_init(struct net *net, struct nlattr *nla,
return -EEXIST; return -EEXIST;
} }
err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
if (err < 0)
goto release_idr;
v = to_vlan(*a); v = to_vlan(*a);
p = kzalloc(sizeof(*p), GFP_KERNEL); p = kzalloc(sizeof(*p), GFP_KERNEL);
if (!p) { if (!p) {
tcf_idr_release(*a, bind); err = -ENOMEM;
return -ENOMEM; goto put_chain;
} }
p->tcfv_action = action; p->tcfv_action = action;
...@@ -214,16 +220,24 @@ static int tcf_vlan_init(struct net *net, struct nlattr *nla, ...@@ -214,16 +220,24 @@ static int tcf_vlan_init(struct net *net, struct nlattr *nla,
p->tcfv_push_proto = push_proto; p->tcfv_push_proto = push_proto;
spin_lock_bh(&v->tcf_lock); spin_lock_bh(&v->tcf_lock);
v->tcf_action = parm->action; goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch);
rcu_swap_protected(v->vlan_p, p, lockdep_is_held(&v->tcf_lock)); rcu_swap_protected(v->vlan_p, p, lockdep_is_held(&v->tcf_lock));
spin_unlock_bh(&v->tcf_lock); spin_unlock_bh(&v->tcf_lock);
if (goto_ch)
tcf_chain_put_by_act(goto_ch);
if (p) if (p)
kfree_rcu(p, rcu); kfree_rcu(p, rcu);
if (ret == ACT_P_CREATED) if (ret == ACT_P_CREATED)
tcf_idr_insert(tn, *a); tcf_idr_insert(tn, *a);
return ret; return ret;
put_chain:
if (goto_ch)
tcf_chain_put_by_act(goto_ch);
release_idr:
tcf_idr_release(*a, bind);
return err;
} }
static void tcf_vlan_cleanup(struct tc_action *a) static void tcf_vlan_cleanup(struct tc_action *a)
......
...@@ -367,7 +367,7 @@ static void tcf_chain_destroy(struct tcf_chain *chain, bool free_block) ...@@ -367,7 +367,7 @@ static void tcf_chain_destroy(struct tcf_chain *chain, bool free_block)
struct tcf_block *block = chain->block; struct tcf_block *block = chain->block;
mutex_destroy(&chain->filter_chain_lock); mutex_destroy(&chain->filter_chain_lock);
kfree(chain); kfree_rcu(chain, rcu);
if (free_block) if (free_block)
tcf_block_destroy(block); tcf_block_destroy(block);
} }
......
...@@ -286,5 +286,30 @@ ...@@ -286,5 +286,30 @@
"teardown": [ "teardown": [
"$TC action flush action bpf" "$TC action flush action bpf"
] ]
},
{
"id": "b8a1",
"name": "Replace bpf action with invalid goto_chain control",
"category": [
"actions",
"bpf"
],
"setup": [
[
"$TC actions flush action bpf",
0,
1,
255
],
"$TC action add action bpf bytecode '1,6 0 0 4294967295' pass index 90"
],
"cmdUnderTest": "$TC action replace action bpf bytecode '1,6 0 0 4294967295' goto chain 42 index 90 cookie c1a0c1a0",
"expExitCode": "255",
"verifyCmd": "$TC action list action bpf",
"matchPattern": "action order [0-9]*: bpf.* default-action pass.*index 90",
"matchCount": "1",
"teardown": [
"$TC action flush action bpf"
]
} }
] ]
...@@ -287,5 +287,30 @@ ...@@ -287,5 +287,30 @@
"teardown": [ "teardown": [
"$TC actions flush action connmark" "$TC actions flush action connmark"
] ]
},
{
"id": "c506",
"name": "Replace connmark with invalid goto chain control",
"category": [
"actions",
"connmark"
],
"setup": [
[
"$TC actions flush action connmark",
0,
1,
255
],
"$TC actions add action connmark pass index 90"
],
"cmdUnderTest": "$TC actions replace action connmark goto chain 42 index 90 cookie c1a0c1a0",
"expExitCode": "255",
"verifyCmd": "$TC actions get action connmark index 90",
"matchPattern": "action order [0-9]+: connmark zone 0 pass.*index 90 ref",
"matchCount": "1",
"teardown": [
"$TC actions flush action connmark"
]
} }
] ]
...@@ -500,5 +500,30 @@ ...@@ -500,5 +500,30 @@
"matchPattern": "^[ \t]+index [0-9]+ ref", "matchPattern": "^[ \t]+index [0-9]+ ref",
"matchCount": "0", "matchCount": "0",
"teardown": [] "teardown": []
},
{
"id": "d128",
"name": "Replace csum action with invalid goto chain control",
"category": [
"actions",
"csum"
],
"setup": [
[
"$TC actions flush action csum",
0,
1,
255
],
"$TC actions add action csum iph index 90"
],
"cmdUnderTest": "$TC actions replace action csum iph goto chain 42 index 90 cookie c1a0c1a0",
"expExitCode": "255",
"verifyCmd": "$TC actions get action csum index 90",
"matchPattern": "action order [0-9]*: csum \\(iph\\) action pass.*index 90 ref",
"matchCount": "1",
"teardown": [
"$TC actions flush action csum"
]
} }
] ]
...@@ -560,5 +560,30 @@ ...@@ -560,5 +560,30 @@
"teardown": [ "teardown": [
"$TC actions flush action gact" "$TC actions flush action gact"
] ]
},
{
"id": "ca89",
"name": "Replace gact action with invalid goto chain control",
"category": [
"actions",
"gact"
],
"setup": [
[
"$TC actions flush action gact",
0,
1,
255
],
"$TC actions add action pass random determ drop 2 index 90"
],
"cmdUnderTest": "$TC actions replace action goto chain 42 random determ drop 5 index 90 cookie c1a0c1a0",
"expExitCode": "255",
"verifyCmd": "$TC actions list action gact",
"matchPattern": "action order [0-9]*: gact action pass.*random type determ drop val 2.*index 90 ref",
"matchCount": "1",
"teardown": [
"$TC actions flush action gact"
]
} }
] ]
...@@ -1060,5 +1060,30 @@ ...@@ -1060,5 +1060,30 @@
"matchPattern": "action order [0-9]*: ife encode action pipe.*allow prio.*index 4", "matchPattern": "action order [0-9]*: ife encode action pipe.*allow prio.*index 4",
"matchCount": "0", "matchCount": "0",
"teardown": [] "teardown": []
},
{
"id": "a0e2",
"name": "Replace ife encode action with invalid goto chain control",
"category": [
"actions",
"ife"
],
"setup": [
[
"$TC actions flush action ife",
0,
1,
255
],
"$TC actions add action ife encode allow mark pass index 90"
],
"cmdUnderTest": "$TC actions replace action ife encode allow mark goto chain 42 index 90 cookie c1a0c1a0",
"expExitCode": "255",
"verifyCmd": "$TC actions get action ife index 90",
"matchPattern": "action order [0-9]*: ife encode action pass.*type 0[xX]ED3E .*allow mark.*index 90 ref",
"matchCount": "1",
"teardown": [
"$TC actions flush action ife"
]
} }
] ]
...@@ -434,5 +434,30 @@ ...@@ -434,5 +434,30 @@
"teardown": [ "teardown": [
"$TC actions flush action mirred" "$TC actions flush action mirred"
] ]
},
{
"id": "2a9a",
"name": "Replace mirred action with invalid goto chain control",
"category": [
"actions",
"mirred"
],
"setup": [
[
"$TC actions flush action mirred",
0,
1,
255
],
"$TC actions add action mirred ingress mirror dev lo drop index 90"
],
"cmdUnderTest": "$TC actions replace action mirred ingress mirror dev lo goto chain 42 index 90 cookie c1a0c1a0",
"expExitCode": "255",
"verifyCmd": "$TC actions get action mirred index 90",
"matchPattern": "action order [0-9]*: mirred \\(Ingress Mirror to device lo\\) drop.*index 90 ref",
"matchCount": "1",
"teardown": [
"$TC actions flush action mirred"
]
} }
] ]
...@@ -589,5 +589,30 @@ ...@@ -589,5 +589,30 @@
"teardown": [ "teardown": [
"$TC actions flush action nat" "$TC actions flush action nat"
] ]
},
{
"id": "4b12",
"name": "Replace nat action with invalid goto chain control",
"category": [
"actions",
"nat"
],
"setup": [
[
"$TC actions flush action nat",
0,
1,
255
],
"$TC actions add action nat ingress 1.18.1.1 1.18.2.2 drop index 90"
],
"cmdUnderTest": "$TC actions replace action nat ingress 1.18.1.1 1.18.2.2 goto chain 42 index 90 cookie c1a0c1a0",
"expExitCode": "255",
"verifyCmd": "$TC actions get action nat index 90",
"matchPattern": "action order [0-9]+: nat ingress 1.18.1.1/32 1.18.2.2 drop.*index 90 ref",
"matchCount": "1",
"teardown": [
"$TC actions flush action nat"
]
} }
] ]
[
{
"id": "319a",
"name": "Add pedit action that mangles IP TTL",
"category": [
"actions",
"pedit"
],
"setup": [
[
"$TC actions flush action pedit",
0,
1,
255
]
],
"cmdUnderTest": "$TC actions add action pedit ex munge ip ttl set 10",
"expExitCode": "0",
"verifyCmd": "$TC actions ls action pedit",
"matchPattern": "action order [0-9]+: pedit action pass keys 1.*index 1 ref.*key #0 at ipv4\\+8: val 0a000000 mask 00ffffff",
"matchCount": "1",
"teardown": [
"$TC actions flush action pedit"
]
},
{
"id": "7e67",
"name": "Replace pedit action with invalid goto chain",
"category": [
"actions",
"pedit"
],
"setup": [
[
"$TC actions flush action pedit",
0,
1,
255
],
"$TC actions add action pedit ex munge ip ttl set 10 pass index 90"
],
"cmdUnderTest": "$TC actions replace action pedit ex munge ip ttl set 10 goto chain 42 index 90 cookie c1a0c1a0",
"expExitCode": "255",
"verifyCmd": "$TC actions ls action pedit",
"matchPattern": "action order [0-9]+: pedit action pass keys 1.*index 90 ref.*key #0 at ipv4\\+8: val 0a000000 mask 00ffffff",
"matchCount": "1",
"teardown": [
"$TC actions flush action pedit"
]
}
]
...@@ -739,5 +739,30 @@ ...@@ -739,5 +739,30 @@
"teardown": [ "teardown": [
"$TC actions flush action police" "$TC actions flush action police"
] ]
},
{
"id": "689e",
"name": "Replace police action with invalid goto chain control",
"category": [
"actions",
"police"
],
"setup": [
[
"$TC actions flush action police",
0,
1,
255
],
"$TC actions add action police rate 3mbit burst 250k drop index 90"
],
"cmdUnderTest": "$TC actions replace action police rate 3mbit burst 250k goto chain 42 index 90 cookie c1a0c1a0",
"expExitCode": "255",
"verifyCmd": "$TC actions get action police index 90",
"matchPattern": "action order [0-9]*: police 0x5a rate 3Mbit burst 250Kb mtu 2Kb action drop",
"matchCount": "1",
"teardown": [
"$TC actions flush action police"
]
} }
] ]
...@@ -584,5 +584,30 @@ ...@@ -584,5 +584,30 @@
"teardown": [ "teardown": [
"$TC actions flush action sample" "$TC actions flush action sample"
] ]
},
{
"id": "0a6e",
"name": "Replace sample action with invalid goto chain control",
"category": [
"actions",
"sample"
],
"setup": [
[
"$TC actions flush action sample",
0,
1,
255
],
"$TC actions add action sample rate 1024 group 4 pass index 90"
],
"cmdUnderTest": "$TC actions replace action sample rate 1024 group 7 goto chain 42 index 90 cookie c1a0c1a0",
"expExitCode": "255",
"verifyCmd": "$TC actions list action sample",
"matchPattern": "action order [0-9]+: sample rate 1/1024 group 4 pass.*index 90",
"matchCount": "1",
"teardown": [
"$TC actions flush action sample"
]
} }
] ]
...@@ -126,5 +126,30 @@ ...@@ -126,5 +126,30 @@
"teardown": [ "teardown": [
"" ""
] ]
},
{
"id": "b776",
"name": "Replace simple action with invalid goto chain control",
"category": [
"actions",
"simple"
],
"setup": [
[
"$TC actions flush action simple",
0,
1,
255
],
"$TC actions add action simple sdata \"hello\" pass index 90"
],
"cmdUnderTest": "$TC actions replace action simple sdata \"world\" goto chain 42 index 90 cookie c1a0c1a0",
"expExitCode": "255",
"verifyCmd": "$TC actions list action simple",
"matchPattern": "action order [0-9]*: Simple <hello>.*index 90 ref",
"matchCount": "1",
"teardown": [
"$TC actions flush action simple"
]
} }
] ]
...@@ -484,5 +484,30 @@ ...@@ -484,5 +484,30 @@
"teardown": [ "teardown": [
"$TC actions flush action skbedit" "$TC actions flush action skbedit"
] ]
},
{
"id": "1b2b",
"name": "Replace skbedit action with invalid goto_chain control",
"category": [
"actions",
"skbedit"
],
"setup": [
[
"$TC actions flush action skbedit",
0,
1,
255
],
"$TC actions add action skbedit ptype host pass index 90"
],
"cmdUnderTest": "$TC actions replace action skbedit ptype host goto chain 42 index 90 cookie c1a0c1a0",
"expExitCode": "255",
"verifyCmd": "$TC actions list action skbedit",
"matchPattern": "action order [0-9]*: skbedit ptype host pass.*index 90 ref",
"matchCount": "1",
"teardown": [
"$TC actions flush action skbedit"
]
} }
] ]
...@@ -392,5 +392,30 @@ ...@@ -392,5 +392,30 @@
"teardown": [ "teardown": [
"$TC actions flush action skbmod" "$TC actions flush action skbmod"
] ]
},
{
"id": "b651",
"name": "Replace skbmod action with invalid goto_chain control",
"category": [
"actions",
"skbmod"
],
"setup": [
[
"$TC actions flush action skbmod",
0,
1,
255
],
"$TC actions add action skbmod set etype 0x1111 pass index 90"
],
"cmdUnderTest": "$TC actions replace action skbmod set etype 0x1111 goto chain 42 index 90 cookie c1a0c1a0",
"expExitCode": "255",
"verifyCmd": "$TC actions ls action skbmod",
"matchPattern": "action order [0-9]*: skbmod pass set etype 0x1111\\s+index 90 ref",
"matchCount": "1",
"teardown": [
"$TC actions flush action skbmod"
]
} }
] ]
...@@ -884,5 +884,30 @@ ...@@ -884,5 +884,30 @@
"teardown": [ "teardown": [
"$TC actions flush action tunnel_key" "$TC actions flush action tunnel_key"
] ]
},
{
"id": "8242",
"name": "Replace tunnel_key set action with invalid goto chain",
"category": [
"actions",
"tunnel_key"
],
"setup": [
[
"$TC actions flush action tunnel_key",
0,
1,
255
],
"$TC actions add action tunnel_key set src_ip 10.10.10.1 dst_ip 20.20.20.2 dst_port 3128 nocsum id 1 pass index 90"
],
"cmdUnderTest": "$TC actions replace action tunnel_key set src_ip 10.10.10.2 dst_ip 20.20.20.1 dst_port 3129 id 2 csum goto chain 42 index 90 cookie c1a0c1a0",
"expExitCode": "255",
"verifyCmd": "$TC actions get action tunnel_key index 90",
"matchPattern": "action order [0-9]+: tunnel_key.*set.*src_ip 10.10.10.1.*dst_ip 20.20.20.2.*key_id 1.*dst_port 3128.*csum pass.*index 90 ref",
"matchCount": "1",
"teardown": [
"$TC actions flush action tunnel_key"
]
} }
] ]
...@@ -688,5 +688,30 @@ ...@@ -688,5 +688,30 @@
"teardown": [ "teardown": [
"$TC actions flush action vlan" "$TC actions flush action vlan"
] ]
},
{
"id": "e394",
"name": "Replace vlan push action with invalid goto chain control",
"category": [
"actions",
"vlan"
],
"setup": [
[
"$TC actions flush action vlan",
0,
1,
255
],
"$TC actions add action vlan push id 500 pass index 90"
],
"cmdUnderTest": "$TC actions replace action vlan push id 500 goto chain 42 index 90 cookie c1a0c1a0",
"expExitCode": "255",
"verifyCmd": "$TC actions get action vlan index 90",
"matchPattern": "action order [0-9]+: vlan.*push id 500 protocol 802.1Q priority 0 pass.*index 90 ref",
"matchCount": "1",
"teardown": [
"$TC actions flush action vlan"
]
} }
] ]
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment