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: 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>
This commit is contained in:

committed by
David S. Miller

parent
9c6ecd3cf6
commit
d2f0c9b114
@@ -487,6 +487,7 @@ int call_fib6_multipath_entry_notifiers(struct net *net,
|
|||||||
struct fib6_info *rt,
|
struct fib6_info *rt,
|
||||||
unsigned int nsiblings,
|
unsigned int nsiblings,
|
||||||
struct netlink_ext_ack *extack);
|
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,
|
void fib6_rt_update(struct net *net, struct fib6_info *rt,
|
||||||
struct nl_info *info);
|
struct nl_info *info);
|
||||||
void inet6_rt_notify(int event, struct fib6_info *rt, struct nl_info *info,
|
void inet6_rt_notify(int event, struct fib6_info *rt, struct nl_info *info,
|
||||||
|
@@ -415,6 +415,18 @@ int call_fib6_multipath_entry_notifiers(struct net *net,
|
|||||||
return call_fib6_notifiers(net, event_type, &info.info);
|
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 fib6_dump_arg {
|
||||||
struct net *net;
|
struct net *net;
|
||||||
struct notifier_block *nb;
|
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,
|
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 __rcu **rtp, struct nl_info *info)
|
||||||
{
|
{
|
||||||
|
struct fib6_info *leaf, *replace_rt = NULL;
|
||||||
struct fib6_walker *w;
|
struct fib6_walker *w;
|
||||||
struct fib6_info *rt = rcu_dereference_protected(*rtp,
|
struct fib6_info *rt = rcu_dereference_protected(*rtp,
|
||||||
lockdep_is_held(&table->tb6_lock));
|
lockdep_is_held(&table->tb6_lock));
|
||||||
struct net *net = info->nl_net;
|
struct net *net = info->nl_net;
|
||||||
|
bool notify_del = false;
|
||||||
|
|
||||||
RT6_TRACE("fib6_del_route\n");
|
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 */
|
/* Unlink it */
|
||||||
*rtp = rt->fib6_next;
|
*rtp = rt->fib6_next;
|
||||||
rt->fib6_node = NULL;
|
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) {
|
if (rt->fib6_nsiblings) {
|
||||||
struct fib6_info *sibling, *next_sibling;
|
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,
|
list_for_each_entry_safe(sibling, next_sibling,
|
||||||
&rt->fib6_siblings, fib6_siblings)
|
&rt->fib6_siblings, fib6_siblings)
|
||||||
sibling->fib6_nsiblings--;
|
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);
|
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);
|
call_fib6_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, rt, NULL);
|
||||||
|
}
|
||||||
if (!info->skip_notify)
|
if (!info->skip_notify)
|
||||||
inet6_rt_notify(RTM_DELROUTE, rt, info, 0);
|
inet6_rt_notify(RTM_DELROUTE, rt, info, 0);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user