diff options
author | Ido Schimmel <idosch@mellanox.com> | 2019-12-23 15:28:18 +0200 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2019-12-24 22:37:30 -0800 |
commit | 0284696b97b2fad1b220871559dff410cc3187e0 (patch) | |
tree | fa2e455e3b73d5a5f7f265f2e384f00ea92e5cad | |
parent | d2f0c9b11410f9c6a07c126f8a215b0b81cdcf6c (diff) | |
download | linux-stable-0284696b97b2fad1b220871559dff410cc3187e0.tar.gz linux-stable-0284696b97b2fad1b220871559dff410cc3187e0.tar.bz2 linux-stable-0284696b97b2fad1b220871559dff410cc3187e0.zip |
ipv6: Handle multipath route deletion notification
When an entire multipath route is deleted, only emit a notification if
it is the first route in the node. Emit a replace notification in case
the last sibling is followed by another route. Otherwise, emit a delete
notification.
Signed-off-by: Ido Schimmel <idosch@mellanox.com>
Reviewed-by: Jiri Pirko <jiri@mellanox.com>
Reviewed-by: David Ahern <dsahern@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | net/ipv6/route.c | 26 |
1 files changed, 26 insertions, 0 deletions
diff --git a/net/ipv6/route.c b/net/ipv6/route.c index c0809f52f9ef..646716a47cc9 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -3749,6 +3749,7 @@ static int __ip6_del_rt_siblings(struct fib6_info *rt, struct fib6_config *cfg) if (rt->fib6_nsiblings && cfg->fc_delete_all_nh) { struct fib6_info *sibling, *next_sibling; + struct fib6_node *fn; /* prefer to send a single notification with all hops */ skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any()); @@ -3764,7 +3765,32 @@ static int __ip6_del_rt_siblings(struct fib6_info *rt, struct fib6_config *cfg) info->skip_notify = 1; } + /* 'rt' points to the first sibling route. If it is not the + * leaf, then we do not need to send a notification. Otherwise, + * we need to check if the last sibling has a next route or not + * and emit a replace or delete notification, respectively. + */ info->skip_notify_kernel = 1; + fn = rcu_dereference_protected(rt->fib6_node, + lockdep_is_held(&table->tb6_lock)); + if (rcu_access_pointer(fn->leaf) == rt) { + struct fib6_info *last_sibling, *replace_rt; + + last_sibling = list_last_entry(&rt->fib6_siblings, + struct fib6_info, + fib6_siblings); + replace_rt = rcu_dereference_protected( + last_sibling->fib6_next, + lockdep_is_held(&table->tb6_lock)); + if (replace_rt) + call_fib6_entry_notifiers_replace(net, + replace_rt); + else + call_fib6_multipath_entry_notifiers(net, + FIB_EVENT_ENTRY_DEL_TMP, + rt, rt->fib6_nsiblings, + NULL); + } call_fib6_multipath_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, rt, |