Commit d2f0c9b1 authored by Ido Schimmel's avatar Ido Schimmel Committed by David S. Miller
Browse files

ipv6: Handle route deletion notification



For the purpose of route offload, when a single route is deleted, it is
only of interest if it is the first route in the node or if it is
sibling to such a route.

In the first case, distinguish between several possibilities:

1. Route is the last route in the node. Emit a delete notification

2. Route is followed by a non-multipath route. Emit a replace
notification for the non-multipath route.

3. Route is followed by a multipath route. Emit a replace notification
for the multipath route.

In the second case, only emit a delete notification to ensure the route
is no longer used as a valid nexthop.

Signed-off-by: default avatarIdo Schimmel <idosch@mellanox.com>
Reviewed-by: default avatarJiri Pirko <jiri@mellanox.com>
Reviewed-by: default avatarDavid Ahern <dsahern@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 9c6ecd3c
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -487,6 +487,7 @@ int call_fib6_multipath_entry_notifiers(struct net *net,
					struct fib6_info *rt,
					unsigned int nsiblings,
					struct netlink_ext_ack *extack);
int call_fib6_entry_notifiers_replace(struct net *net, struct fib6_info *rt);
void fib6_rt_update(struct net *net, struct fib6_info *rt,
		    struct nl_info *info);
void inet6_rt_notify(int event, struct fib6_info *rt, struct nl_info *info,
+43 −1
Original line number Diff line number Diff line
@@ -415,6 +415,18 @@ int call_fib6_multipath_entry_notifiers(struct net *net,
	return call_fib6_notifiers(net, event_type, &info.info);
}

int call_fib6_entry_notifiers_replace(struct net *net, struct fib6_info *rt)
{
	struct fib6_entry_notifier_info info = {
		.rt = rt,
		.nsiblings = rt->fib6_nsiblings,
	};

	rt->fib6_table->fib_seq++;
	return call_fib6_notifiers(net, FIB_EVENT_ENTRY_REPLACE_TMP,
				   &info.info);
}

struct fib6_dump_arg {
	struct net *net;
	struct notifier_block *nb;
@@ -1910,13 +1922,29 @@ static struct fib6_node *fib6_repair_tree(struct net *net,
static void fib6_del_route(struct fib6_table *table, struct fib6_node *fn,
			   struct fib6_info __rcu **rtp, struct nl_info *info)
{
	struct fib6_info *leaf, *replace_rt = NULL;
	struct fib6_walker *w;
	struct fib6_info *rt = rcu_dereference_protected(*rtp,
				    lockdep_is_held(&table->tb6_lock));
	struct net *net = info->nl_net;
	bool notify_del = false;

	RT6_TRACE("fib6_del_route\n");

	/* If the deleted route is the first in the node and it is not part of
	 * a multipath route, then we need to replace it with the next route
	 * in the node, if exists.
	 */
	leaf = rcu_dereference_protected(fn->leaf,
					 lockdep_is_held(&table->tb6_lock));
	if (leaf == rt && !rt->fib6_nsiblings) {
		if (rcu_access_pointer(rt->fib6_next))
			replace_rt = rcu_dereference_protected(rt->fib6_next,
					    lockdep_is_held(&table->tb6_lock));
		else
			notify_del = true;
	}

	/* Unlink it */
	*rtp = rt->fib6_next;
	rt->fib6_node = NULL;
@@ -1934,6 +1962,14 @@ static void fib6_del_route(struct fib6_table *table, struct fib6_node *fn,
	if (rt->fib6_nsiblings) {
		struct fib6_info *sibling, *next_sibling;

		/* The route is deleted from a multipath route. If this
		 * multipath route is the first route in the node, then we need
		 * to emit a delete notification. Otherwise, we need to skip
		 * the notification.
		 */
		if (rt->fib6_metric == leaf->fib6_metric &&
		    rt6_qualify_for_ecmp(leaf))
			notify_del = true;
		list_for_each_entry_safe(sibling, next_sibling,
					 &rt->fib6_siblings, fib6_siblings)
			sibling->fib6_nsiblings--;
@@ -1969,8 +2005,14 @@ static void fib6_del_route(struct fib6_table *table, struct fib6_node *fn,

	fib6_purge_rt(rt, fn, net);

	if (!info->skip_notify_kernel)
	if (!info->skip_notify_kernel) {
		if (notify_del)
			call_fib6_entry_notifiers(net, FIB_EVENT_ENTRY_DEL_TMP,
						  rt, NULL);
		else if (replace_rt)
			call_fib6_entry_notifiers_replace(net, replace_rt);
		call_fib6_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, rt, NULL);
	}
	if (!info->skip_notify)
		inet6_rt_notify(RTM_DELROUTE, rt, info, 0);