Add neigh_carrier_down helper to flush non-permanent entries on carrier
down.

Signed-off-by: David Ahern <dsah...@gmail.com>
---
 include/net/neighbour.h |  1 +
 net/core/neighbour.c    | 26 ++++++++++++++++++++++----
 2 files changed, 23 insertions(+), 4 deletions(-)

diff --git a/include/net/neighbour.h b/include/net/neighbour.h
index 2492000e1035..b2eff6ffbfdc 100644
--- a/include/net/neighbour.h
+++ b/include/net/neighbour.h
@@ -320,6 +320,7 @@ int neigh_update(struct neighbour *neigh, const u8 *lladdr, 
u8 new, u32 flags,
 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_carrier_down(struct neigh_table *tbl, struct net_device *dev);
 int neigh_ifdown(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);
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index 6ea3a1a7f36a..a7012bca1ce9 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -229,7 +229,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;
@@ -247,6 +248,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)));
@@ -282,20 +287,33 @@ 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 void __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(tbl, dev);
        write_unlock_bh(&tbl->lock);
 
        del_timer_sync(&tbl->proxy_timer);
        pneigh_queue_purge(&tbl->proxy_queue);
+}
+
+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);
-- 
2.1.4

Reply via email to