fib_rules: Resolve goto rules target on delete
We should avoid marking goto rules unresolved when their target is actually reachable after rule deletion. Consolder following sample scenario: # ip -4 ru sh 0: from all lookup local 32000: from all goto 32100 32100: from all lookup main 32100: from all lookup default 32766: from all lookup main 32767: from all lookup default # ip -4 ru del pref 32100 table main # ip -4 ru sh 0: from all lookup local 32000: from all goto 32100 [unresolved] 32100: from all lookup default 32766: from all lookup main 32767: from all lookup default After removal of first rule with preference 32100 we mark all goto rules as unreachable, even when rule with same preference as removed one still present. Check if next rule with same preference is available and make all rules with goto action pointing to it. Signed-off-by: Serhey Popovych <serhe.popovych@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
86fdb3448c
commit
bdaf32c3ce
|
@ -568,7 +568,7 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh,
|
||||||
struct net *net = sock_net(skb->sk);
|
struct net *net = sock_net(skb->sk);
|
||||||
struct fib_rule_hdr *frh = nlmsg_data(nlh);
|
struct fib_rule_hdr *frh = nlmsg_data(nlh);
|
||||||
struct fib_rules_ops *ops = NULL;
|
struct fib_rules_ops *ops = NULL;
|
||||||
struct fib_rule *rule, *tmp;
|
struct fib_rule *rule, *r;
|
||||||
struct nlattr *tb[FRA_MAX+1];
|
struct nlattr *tb[FRA_MAX+1];
|
||||||
struct fib_kuid_range range;
|
struct fib_kuid_range range;
|
||||||
int err = -EINVAL;
|
int err = -EINVAL;
|
||||||
|
@ -668,18 +668,25 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check if this rule is a target to any of them. If so,
|
* Check if this rule is a target to any of them. If so,
|
||||||
|
* adjust to the next one with the same preference or
|
||||||
* disable them. As this operation is eventually very
|
* disable them. As this operation is eventually very
|
||||||
* expensive, it is only performed if goto rules have
|
* expensive, it is only performed if goto rules, except
|
||||||
* actually been added.
|
* current if it is goto rule, have actually been added.
|
||||||
*/
|
*/
|
||||||
if (ops->nr_goto_rules > 0) {
|
if (ops->nr_goto_rules > 0) {
|
||||||
list_for_each_entry(tmp, &ops->rules_list, list) {
|
struct fib_rule *n;
|
||||||
if (rtnl_dereference(tmp->ctarget) == rule) {
|
|
||||||
RCU_INIT_POINTER(tmp->ctarget, NULL);
|
n = list_next_entry(rule, list);
|
||||||
|
if (&n->list == &ops->rules_list || n->pref != rule->pref)
|
||||||
|
n = NULL;
|
||||||
|
list_for_each_entry(r, &ops->rules_list, list) {
|
||||||
|
if (rtnl_dereference(r->ctarget) != rule)
|
||||||
|
continue;
|
||||||
|
rcu_assign_pointer(r->ctarget, n);
|
||||||
|
if (!n)
|
||||||
ops->unresolved_rules++;
|
ops->unresolved_rules++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
notify_rule_change(RTM_DELRULE, rule, ops, nlh,
|
notify_rule_change(RTM_DELRULE, rule, ops, nlh,
|
||||||
NETLINK_CB(skb).portid);
|
NETLINK_CB(skb).portid);
|
||||||
|
|
Loading…
Reference in a new issue