diff options
author | Cong Wang <cwang@twopensource.com> | 2014-04-25 16:54:06 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-04-27 23:42:39 -0400 |
commit | 2f7ef2f8790f5bf53db4fc6b2310943139285827 (patch) | |
tree | f2deb92bec03f3af01b9d7c1bf4dfb7b97760ecf /net/sched/cls_u32.c | |
parent | 4940b8cd1bc6d452eaffa442d92a28534850ca78 (diff) |
sched, cls: check if we could overwrite actions when changing a filter
When actions are attached to a filter, they are a part of the filter
itself, so when changing a filter we should allow to overwrite the actions
inside as well.
In my specific case, when I tried to _append_ a new action to an existing
filter which already has an action, I got EEXIST since kernel refused
to overwrite the existing one in kernel.
This patch checks if we are changing the filter checking NLM_F_CREATE flag
(Sigh, filters don't use NLM_F_REPLACE...) and then passes the boolean down
to actions. This fixes the problem above.
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: Cong Wang <cwang@twopensource.com>
Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/sched/cls_u32.c')
-rw-r--r-- | net/sched/cls_u32.c | 10 |
1 files changed, 5 insertions, 5 deletions
diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index 84c28daff848..c39b583ace32 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c | |||
@@ -486,13 +486,13 @@ static const struct nla_policy u32_policy[TCA_U32_MAX + 1] = { | |||
486 | static int u32_set_parms(struct net *net, struct tcf_proto *tp, | 486 | static int u32_set_parms(struct net *net, struct tcf_proto *tp, |
487 | unsigned long base, struct tc_u_hnode *ht, | 487 | unsigned long base, struct tc_u_hnode *ht, |
488 | struct tc_u_knode *n, struct nlattr **tb, | 488 | struct tc_u_knode *n, struct nlattr **tb, |
489 | struct nlattr *est) | 489 | struct nlattr *est, bool ovr) |
490 | { | 490 | { |
491 | int err; | 491 | int err; |
492 | struct tcf_exts e; | 492 | struct tcf_exts e; |
493 | 493 | ||
494 | tcf_exts_init(&e, TCA_U32_ACT, TCA_U32_POLICE); | 494 | tcf_exts_init(&e, TCA_U32_ACT, TCA_U32_POLICE); |
495 | err = tcf_exts_validate(net, tp, tb, est, &e); | 495 | err = tcf_exts_validate(net, tp, tb, est, &e, ovr); |
496 | if (err < 0) | 496 | if (err < 0) |
497 | return err; | 497 | return err; |
498 | 498 | ||
@@ -545,7 +545,7 @@ errout: | |||
545 | static int u32_change(struct net *net, struct sk_buff *in_skb, | 545 | static int u32_change(struct net *net, struct sk_buff *in_skb, |
546 | struct tcf_proto *tp, unsigned long base, u32 handle, | 546 | struct tcf_proto *tp, unsigned long base, u32 handle, |
547 | struct nlattr **tca, | 547 | struct nlattr **tca, |
548 | unsigned long *arg) | 548 | unsigned long *arg, bool ovr) |
549 | { | 549 | { |
550 | struct tc_u_common *tp_c = tp->data; | 550 | struct tc_u_common *tp_c = tp->data; |
551 | struct tc_u_hnode *ht; | 551 | struct tc_u_hnode *ht; |
@@ -569,7 +569,7 @@ static int u32_change(struct net *net, struct sk_buff *in_skb, | |||
569 | return -EINVAL; | 569 | return -EINVAL; |
570 | 570 | ||
571 | return u32_set_parms(net, tp, base, n->ht_up, n, tb, | 571 | return u32_set_parms(net, tp, base, n->ht_up, n, tb, |
572 | tca[TCA_RATE]); | 572 | tca[TCA_RATE], ovr); |
573 | } | 573 | } |
574 | 574 | ||
575 | if (tb[TCA_U32_DIVISOR]) { | 575 | if (tb[TCA_U32_DIVISOR]) { |
@@ -656,7 +656,7 @@ static int u32_change(struct net *net, struct sk_buff *in_skb, | |||
656 | } | 656 | } |
657 | #endif | 657 | #endif |
658 | 658 | ||
659 | err = u32_set_parms(net, tp, base, ht, n, tb, tca[TCA_RATE]); | 659 | err = u32_set_parms(net, tp, base, ht, n, tb, tca[TCA_RATE], ovr); |
660 | if (err == 0) { | 660 | if (err == 0) { |
661 | struct tc_u_knode **ins; | 661 | struct tc_u_knode **ins; |
662 | for (ins = &ht->ht[TC_U32_HASH(handle)]; *ins; ins = &(*ins)->next) | 662 | for (ins = &ht->ht[TC_U32_HASH(handle)]; *ins; ins = &(*ins)->next) |