summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Ahern <dsahern@gmail.com>2018-10-11 23:33:49 -0400
committerDavid S. Miller <davem@davemloft.net>2018-10-12 12:47:39 -0400
commit859bd2ef1fc1110a8031b967ee656c53a6260a76 (patch)
tree96f07d4fb9cc3ec82e5b60dd1aae6976aa87ead3
parent7c6bb7d2faaf1ed7d78bafd712476e4cf2cf0d7d (diff)
net: Evict neighbor entries on carrier down
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>
-rw-r--r--include/net/neighbour.h1
-rw-r--r--net/core/neighbour.c27
-rw-r--r--net/ipv4/arp.c2
-rw-r--r--net/ipv6/ndisc.c2
4 files changed, 28 insertions, 4 deletions
diff --git a/include/net/neighbour.h b/include/net/neighbour.h
index 0874f7fcd859..f58b384aa6c9 100644
--- a/include/net/neighbour.h
+++ b/include/net/neighbour.h
@@ -323,6 +323,7 @@ void __neigh_set_probe_once(struct neighbour *neigh);
323bool neigh_remove_one(struct neighbour *ndel, struct neigh_table *tbl); 323bool neigh_remove_one(struct neighbour *ndel, struct neigh_table *tbl);
324void neigh_changeaddr(struct neigh_table *tbl, struct net_device *dev); 324void neigh_changeaddr(struct neigh_table *tbl, struct net_device *dev);
325int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev); 325int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev);
326int neigh_carrier_down(struct neigh_table *tbl, struct net_device *dev);
326int neigh_resolve_output(struct neighbour *neigh, struct sk_buff *skb); 327int neigh_resolve_output(struct neighbour *neigh, struct sk_buff *skb);
327int neigh_connected_output(struct neighbour *neigh, struct sk_buff *skb); 328int neigh_connected_output(struct neighbour *neigh, struct sk_buff *skb);
328int neigh_direct_output(struct neighbour *neigh, struct sk_buff *skb); 329int neigh_direct_output(struct neighbour *neigh, struct sk_buff *skb);
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index dc1389b8beb1..69c41cb3966d 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -232,7 +232,8 @@ static void pneigh_queue_purge(struct sk_buff_head *list)
232 } 232 }
233} 233}
234 234
235static void neigh_flush_dev(struct neigh_table *tbl, struct net_device *dev) 235static void neigh_flush_dev(struct neigh_table *tbl, struct net_device *dev,
236 bool skip_perm)
236{ 237{
237 int i; 238 int i;
238 struct neigh_hash_table *nht; 239 struct neigh_hash_table *nht;
@@ -250,6 +251,10 @@ static void neigh_flush_dev(struct neigh_table *tbl, struct net_device *dev)
250 np = &n->next; 251 np = &n->next;
251 continue; 252 continue;
252 } 253 }
254 if (skip_perm && n->nud_state & NUD_PERMANENT) {
255 np = &n->next;
256 continue;
257 }
253 rcu_assign_pointer(*np, 258 rcu_assign_pointer(*np,
254 rcu_dereference_protected(n->next, 259 rcu_dereference_protected(n->next,
255 lockdep_is_held(&tbl->lock))); 260 lockdep_is_held(&tbl->lock)));
@@ -285,21 +290,35 @@ static void neigh_flush_dev(struct neigh_table *tbl, struct net_device *dev)
285void neigh_changeaddr(struct neigh_table *tbl, struct net_device *dev) 290void neigh_changeaddr(struct neigh_table *tbl, struct net_device *dev)
286{ 291{
287 write_lock_bh(&tbl->lock); 292 write_lock_bh(&tbl->lock);
288 neigh_flush_dev(tbl, dev); 293 neigh_flush_dev(tbl, dev, false);
289 write_unlock_bh(&tbl->lock); 294 write_unlock_bh(&tbl->lock);
290} 295}
291EXPORT_SYMBOL(neigh_changeaddr); 296EXPORT_SYMBOL(neigh_changeaddr);
292 297
293int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev) 298static int __neigh_ifdown(struct neigh_table *tbl, struct net_device *dev,
299 bool skip_perm)
294{ 300{
295 write_lock_bh(&tbl->lock); 301 write_lock_bh(&tbl->lock);
296 neigh_flush_dev(tbl, dev); 302 neigh_flush_dev(tbl, dev, skip_perm);
297 pneigh_ifdown_and_unlock(tbl, dev); 303 pneigh_ifdown_and_unlock(tbl, dev);
298 304
299 del_timer_sync(&tbl->proxy_timer); 305 del_timer_sync(&tbl->proxy_timer);
300 pneigh_queue_purge(&tbl->proxy_queue); 306 pneigh_queue_purge(&tbl->proxy_queue);
301 return 0; 307 return 0;
302} 308}
309
310int neigh_carrier_down(struct neigh_table *tbl, struct net_device *dev)
311{
312 __neigh_ifdown(tbl, dev, true);
313 return 0;
314}
315EXPORT_SYMBOL(neigh_carrier_down);
316
317int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev)
318{
319 __neigh_ifdown(tbl, dev, false);
320 return 0;
321}
303EXPORT_SYMBOL(neigh_ifdown); 322EXPORT_SYMBOL(neigh_ifdown);
304 323
305static struct neighbour *neigh_alloc(struct neigh_table *tbl, struct net_device *dev) 324static struct neighbour *neigh_alloc(struct neigh_table *tbl, struct net_device *dev)
diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c
index e90c89ef8c08..850a6f13a082 100644
--- a/net/ipv4/arp.c
+++ b/net/ipv4/arp.c
@@ -1255,6 +1255,8 @@ static int arp_netdev_event(struct notifier_block *this, unsigned long event,
1255 change_info = ptr; 1255 change_info = ptr;
1256 if (change_info->flags_changed & IFF_NOARP) 1256 if (change_info->flags_changed & IFF_NOARP)
1257 neigh_changeaddr(&arp_tbl, dev); 1257 neigh_changeaddr(&arp_tbl, dev);
1258 if (!netif_carrier_ok(dev))
1259 neigh_carrier_down(&arp_tbl, dev);
1258 break; 1260 break;
1259 default: 1261 default:
1260 break; 1262 break;
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 51863ada15a4..a25cfdd47c89 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -1784,6 +1784,8 @@ static int ndisc_netdev_event(struct notifier_block *this, unsigned long event,
1784 change_info = ptr; 1784 change_info = ptr;
1785 if (change_info->flags_changed & IFF_NOARP) 1785 if (change_info->flags_changed & IFF_NOARP)
1786 neigh_changeaddr(&nd_tbl, dev); 1786 neigh_changeaddr(&nd_tbl, dev);
1787 if (!netif_carrier_ok(dev))
1788 neigh_carrier_down(&nd_tbl, dev);
1787 break; 1789 break;
1788 case NETDEV_DOWN: 1790 case NETDEV_DOWN:
1789 neigh_ifdown(&nd_tbl, dev); 1791 neigh_ifdown(&nd_tbl, dev);