Blob Blame History Raw
From: Ido Schimmel <idosch@mellanox.com>
Date: Mon, 23 Dec 2019 15:28:18 +0200
Subject: ipv6: Handle multipath route deletion notification
Patch-mainline: v5.6-rc1
Git-commit: 0284696b97b2fad1b220871559dff410cc3187e0
References: bsc#1176774

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>
Acked-by: Thomas Bogendoerfer <tbogendoerfer@suse.de>
---
 net/ipv6/route.c |   26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -3759,6 +3759,7 @@ static int __ip6_del_rt_siblings(struct
 
 	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());
@@ -3774,7 +3775,32 @@ static int __ip6_del_rt_siblings(struct
 				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,