diff options
author | WANG Cong <xiyou.wangcong@gmail.com> | 2014-02-11 20:07:34 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-02-12 19:23:32 -0500 |
commit | 55334a5db5cd32b207ac697cec3ec8e078f345d4 (patch) | |
tree | f4093d86b0d26f72f8bd68e1a7e1ea8fd1d476c3 /net/sched | |
parent | 4f1e9d8949b438c7791993515fc164312e9080e2 (diff) |
net_sched: act: refuse to remove bound action outside
When an action is bonnd to a filter, there is no point to
remove it outside. Currently we just silently decrease the refcnt,
we should reject this explicitly with EPERM.
Cc: Jamal Hadi Salim <jhs@mojatatu.com>
Cc: David S. Miller <davem@davemloft.net>
Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com>
Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/sched')
-rw-r--r-- | net/sched/act_api.c | 26 |
1 files changed, 20 insertions, 6 deletions
diff --git a/net/sched/act_api.c b/net/sched/act_api.c index c88d382d3b09..27e4c531ade1 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c | |||
@@ -53,6 +53,8 @@ int tcf_hash_release(struct tc_action *a, int bind) | |||
53 | if (p) { | 53 | if (p) { |
54 | if (bind) | 54 | if (bind) |
55 | p->tcfc_bindcnt--; | 55 | p->tcfc_bindcnt--; |
56 | else if (p->tcfc_bindcnt > 0) | ||
57 | return -EPERM; | ||
56 | 58 | ||
57 | p->tcfc_refcnt--; | 59 | p->tcfc_refcnt--; |
58 | if (p->tcfc_bindcnt <= 0 && p->tcfc_refcnt <= 0) { | 60 | if (p->tcfc_bindcnt <= 0 && p->tcfc_refcnt <= 0) { |
@@ -123,6 +125,7 @@ static int tcf_del_walker(struct sk_buff *skb, struct tc_action *a) | |||
123 | struct tcf_common *p; | 125 | struct tcf_common *p; |
124 | struct nlattr *nest; | 126 | struct nlattr *nest; |
125 | int i = 0, n_i = 0; | 127 | int i = 0, n_i = 0; |
128 | int ret = -EINVAL; | ||
126 | 129 | ||
127 | nest = nla_nest_start(skb, a->order); | 130 | nest = nla_nest_start(skb, a->order); |
128 | if (nest == NULL) | 131 | if (nest == NULL) |
@@ -133,10 +136,12 @@ static int tcf_del_walker(struct sk_buff *skb, struct tc_action *a) | |||
133 | head = &hinfo->htab[tcf_hash(i, hinfo->hmask)]; | 136 | head = &hinfo->htab[tcf_hash(i, hinfo->hmask)]; |
134 | hlist_for_each_entry_safe(p, n, head, tcfc_head) { | 137 | hlist_for_each_entry_safe(p, n, head, tcfc_head) { |
135 | a->priv = p; | 138 | a->priv = p; |
136 | if (ACT_P_DELETED == tcf_hash_release(a, 0)) { | 139 | ret = tcf_hash_release(a, 0); |
140 | if (ret == ACT_P_DELETED) { | ||
137 | module_put(a->ops->owner); | 141 | module_put(a->ops->owner); |
138 | n_i++; | 142 | n_i++; |
139 | } | 143 | } else if (ret < 0) |
144 | goto nla_put_failure; | ||
140 | } | 145 | } |
141 | } | 146 | } |
142 | if (nla_put_u32(skb, TCA_FCNT, n_i)) | 147 | if (nla_put_u32(skb, TCA_FCNT, n_i)) |
@@ -146,7 +151,7 @@ static int tcf_del_walker(struct sk_buff *skb, struct tc_action *a) | |||
146 | return n_i; | 151 | return n_i; |
147 | nla_put_failure: | 152 | nla_put_failure: |
148 | nla_nest_cancel(skb, nest); | 153 | nla_nest_cancel(skb, nest); |
149 | return -EINVAL; | 154 | return ret; |
150 | } | 155 | } |
151 | 156 | ||
152 | static int tcf_generic_walker(struct sk_buff *skb, struct netlink_callback *cb, | 157 | static int tcf_generic_walker(struct sk_buff *skb, struct netlink_callback *cb, |
@@ -401,16 +406,21 @@ exec_done: | |||
401 | } | 406 | } |
402 | EXPORT_SYMBOL(tcf_action_exec); | 407 | EXPORT_SYMBOL(tcf_action_exec); |
403 | 408 | ||
404 | void tcf_action_destroy(struct list_head *actions, int bind) | 409 | int tcf_action_destroy(struct list_head *actions, int bind) |
405 | { | 410 | { |
406 | struct tc_action *a, *tmp; | 411 | struct tc_action *a, *tmp; |
412 | int ret = 0; | ||
407 | 413 | ||
408 | list_for_each_entry_safe(a, tmp, actions, list) { | 414 | list_for_each_entry_safe(a, tmp, actions, list) { |
409 | if (tcf_hash_release(a, bind) == ACT_P_DELETED) | 415 | ret = tcf_hash_release(a, bind); |
416 | if (ret == ACT_P_DELETED) | ||
410 | module_put(a->ops->owner); | 417 | module_put(a->ops->owner); |
418 | else if (ret < 0) | ||
419 | return ret; | ||
411 | list_del(&a->list); | 420 | list_del(&a->list); |
412 | kfree(a); | 421 | kfree(a); |
413 | } | 422 | } |
423 | return ret; | ||
414 | } | 424 | } |
415 | 425 | ||
416 | int | 426 | int |
@@ -838,7 +848,11 @@ tcf_del_notify(struct net *net, struct nlmsghdr *n, struct list_head *actions, | |||
838 | } | 848 | } |
839 | 849 | ||
840 | /* now do the delete */ | 850 | /* now do the delete */ |
841 | tcf_action_destroy(actions, 0); | 851 | ret = tcf_action_destroy(actions, 0); |
852 | if (ret < 0) { | ||
853 | kfree_skb(skb); | ||
854 | return ret; | ||
855 | } | ||
842 | 856 | ||
843 | ret = rtnetlink_send(skb, net, portid, RTNLGRP_TC, | 857 | ret = rtnetlink_send(skb, net, portid, RTNLGRP_TC, |
844 | n->nlmsg_flags & NLM_F_ECHO); | 858 | n->nlmsg_flags & NLM_F_ECHO); |