]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
net: Evict neighbor entries on carrier down
authorDavid Ahern <dsahern@gmail.com>
Fri, 12 Oct 2018 03:33:49 +0000 (20:33 -0700)
committerDavid S. Miller <davem@davemloft.net>
Fri, 12 Oct 2018 16:47:39 +0000 (09:47 -0700)
When a link's carrier goes down it could be a sign of the port changing
networks. If the new network has overlapping addresses with the old one,
then the kernel will continue trying to use neighbor entries established
based on the old network until the entries finally age out - meaning a
potentially long delay with communications not working.

This patch evicts neighbor entries on carrier down with the exception of
those marked permanent. Permanent entries are managed by userspace (either
an admin or a routing daemon such as FRR).

Signed-off-by: David Ahern <dsahern@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/neighbour.h
net/core/neighbour.c
net/ipv4/arp.c
net/ipv6/ndisc.c

index 0874f7fcd8598bb2eefa04a692672c4b416a553c..f58b384aa6c9e0fd67b05f59bd921ab38bb67923 100644 (file)
@@ -323,6 +323,7 @@ void __neigh_set_probe_once(struct neighbour *neigh);
 bool neigh_remove_one(struct neighbour *ndel, struct neigh_table *tbl);
 void neigh_changeaddr(struct neigh_table *tbl, struct net_device *dev);
 int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev);
+int neigh_carrier_down(struct neigh_table *tbl, struct net_device *dev);
 int neigh_resolve_output(struct neighbour *neigh, struct sk_buff *skb);
 int neigh_connected_output(struct neighbour *neigh, struct sk_buff *skb);
 int neigh_direct_output(struct neighbour *neigh, struct sk_buff *skb);
index dc1389b8beb120a90ba65c5294c051da3e15b909..69c41cb3966deb3e08f69cb63499fe07958e7fdb 100644 (file)
@@ -232,7 +232,8 @@ static void pneigh_queue_purge(struct sk_buff_head *list)
        }
 }
 
-static void neigh_flush_dev(struct neigh_table *tbl, struct net_device *dev)
+static void neigh_flush_dev(struct neigh_table *tbl, struct net_device *dev,
+                           bool skip_perm)
 {
        int i;
        struct neigh_hash_table *nht;
@@ -250,6 +251,10 @@ static void neigh_flush_dev(struct neigh_table *tbl, struct net_device *dev)
                                np = &n->next;
                                continue;
                        }
+                       if (skip_perm && n->nud_state & NUD_PERMANENT) {
+                               np = &n->next;
+                               continue;
+                       }
                        rcu_assign_pointer(*np,
                                   rcu_dereference_protected(n->next,
                                                lockdep_is_held(&tbl->lock)));
@@ -285,21 +290,35 @@ static void neigh_flush_dev(struct neigh_table *tbl, struct net_device *dev)
 void neigh_changeaddr(struct neigh_table *tbl, struct net_device *dev)
 {
        write_lock_bh(&tbl->lock);
-       neigh_flush_dev(tbl, dev);
+       neigh_flush_dev(tbl, dev, false);
        write_unlock_bh(&tbl->lock);
 }
 EXPORT_SYMBOL(neigh_changeaddr);
 
-int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev)
+static int __neigh_ifdown(struct neigh_table *tbl, struct net_device *dev,
+                         bool skip_perm)
 {
        write_lock_bh(&tbl->lock);
-       neigh_flush_dev(tbl, dev);
+       neigh_flush_dev(tbl, dev, skip_perm);
        pneigh_ifdown_and_unlock(tbl, dev);
 
        del_timer_sync(&tbl->proxy_timer);
        pneigh_queue_purge(&tbl->proxy_queue);
        return 0;
 }
+
+int neigh_carrier_down(struct neigh_table *tbl, struct net_device *dev)
+{
+       __neigh_ifdown(tbl, dev, true);
+       return 0;
+}
+EXPORT_SYMBOL(neigh_carrier_down);
+
+int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev)
+{
+       __neigh_ifdown(tbl, dev, false);
+       return 0;
+}
 EXPORT_SYMBOL(neigh_ifdown);
 
 static struct neighbour *neigh_alloc(struct neigh_table *tbl, struct net_device *dev)
index e90c89ef8c08abc2d2f5ca58ccb06d68ba1d4444..850a6f13a082c9dd43edfa7f95a7d44d08f4384d 100644 (file)
@@ -1255,6 +1255,8 @@ static int arp_netdev_event(struct notifier_block *this, unsigned long event,
                change_info = ptr;
                if (change_info->flags_changed & IFF_NOARP)
                        neigh_changeaddr(&arp_tbl, dev);
+               if (!netif_carrier_ok(dev))
+                       neigh_carrier_down(&arp_tbl, dev);
                break;
        default:
                break;
index 51863ada15a4fd73a1ffd99862730420a9f77e7c..a25cfdd47c89a0376a6a7ec5ae296ebfa2b0e682 100644 (file)
@@ -1784,6 +1784,8 @@ static int ndisc_netdev_event(struct notifier_block *this, unsigned long event,
                change_info = ptr;
                if (change_info->flags_changed & IFF_NOARP)
                        neigh_changeaddr(&nd_tbl, dev);
+               if (!netif_carrier_ok(dev))
+                       neigh_carrier_down(&nd_tbl, dev);
                break;
        case NETDEV_DOWN:
                neigh_ifdown(&nd_tbl, dev);