netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/2] neighbour: allow referenced neighbours to be removed
@ 2021-03-17 18:53 Thadeu Lima de Souza Cascardo
  2021-03-17 18:53 ` [PATCH 2/2] neighbour: allow NUD_NOARP entries to be forced GCed Thadeu Lima de Souza Cascardo
  2021-03-17 23:42 ` [PATCH 1/2] neighbour: allow referenced neighbours to be removed David Ahern
  0 siblings, 2 replies; 8+ messages in thread
From: Thadeu Lima de Souza Cascardo @ 2021-03-17 18:53 UTC (permalink / raw)
  To: netdev; +Cc: davem, kuba, dsahern, Thadeu Lima de Souza Cascardo

During forced garbage collection, neighbours with more than a reference are
not removed. It's possible to DoS the neighbour table by using ARP spoofing
in such a way that there is always a timer pending for all neighbours,
preventing any of them from being removed. That will cause any new
neighbour creation to fail.

Use the same code as used by neigh_flush_dev, which deletes the timer and
cleans the queue when there are still references left.

With the same ARP spoofing technique, it was still possible to reach a valid
destination when this fix was applied, with no more table overflows.

Signed-off-by: Thadeu Lima de Souza Cascardo <cascardo@canonical.com>
---
 net/core/neighbour.c | 117 +++++++++++++++++++------------------------
 1 file changed, 51 insertions(+), 66 deletions(-)

diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index e2982b3970b8..bbc89c7ffdfd 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -173,25 +173,48 @@ static bool neigh_update_ext_learned(struct neighbour *neigh, u32 flags,
 	return rc;
 }
 
+static int neigh_del_timer(struct neighbour *n)
+{
+	if ((n->nud_state & NUD_IN_TIMER) &&
+	    del_timer(&n->timer)) {
+		neigh_release(n);
+		return 1;
+	}
+	return 0;
+}
+
 static bool neigh_del(struct neighbour *n, struct neighbour __rcu **np,
 		      struct neigh_table *tbl)
 {
-	bool retval = false;
-
+	rcu_assign_pointer(*np,
+		   rcu_dereference_protected(n->next,
+				lockdep_is_held(&tbl->lock)));
 	write_lock(&n->lock);
-	if (refcount_read(&n->refcnt) == 1) {
-		struct neighbour *neigh;
-
-		neigh = rcu_dereference_protected(n->next,
-						  lockdep_is_held(&tbl->lock));
-		rcu_assign_pointer(*np, neigh);
-		neigh_mark_dead(n);
-		retval = true;
+	neigh_del_timer(n);
+	neigh_mark_dead(n);
+	if (refcount_read(&n->refcnt) != 1) {
+		/* The most unpleasant situation.
+		   We must destroy neighbour entry,
+		   but someone still uses it.
+
+		   The destroy will be delayed until
+		   the last user releases us, but
+		   we must kill timers etc. and move
+		   it to safe state.
+		 */
+		__skb_queue_purge(&n->arp_queue);
+		n->arp_queue_len_bytes = 0;
+		n->output = neigh_blackhole;
+		if (n->nud_state & NUD_VALID)
+			n->nud_state = NUD_NOARP;
+		else
+			n->nud_state = NUD_NONE;
+		neigh_dbg(2, "neigh %p is stray\n", n);
 	}
 	write_unlock(&n->lock);
-	if (retval)
-		neigh_cleanup_and_release(n);
-	return retval;
+	neigh_cleanup_and_release(n);
+
+	return true;
 }
 
 bool neigh_remove_one(struct neighbour *ndel, struct neigh_table *tbl)
@@ -229,22 +252,20 @@ static int neigh_forced_gc(struct neigh_table *tbl)
 	write_lock_bh(&tbl->lock);
 
 	list_for_each_entry_safe(n, tmp, &tbl->gc_list, gc_list) {
-		if (refcount_read(&n->refcnt) == 1) {
-			bool remove = false;
-
-			write_lock(&n->lock);
-			if ((n->nud_state == NUD_FAILED) ||
-			    (tbl->is_multicast &&
-			     tbl->is_multicast(n->primary_key)) ||
-			    time_after(tref, n->updated))
-				remove = true;
-			write_unlock(&n->lock);
-
-			if (remove && neigh_remove_one(n, tbl))
-				shrunk++;
-			if (shrunk >= max_clean)
-				break;
-		}
+		bool remove = false;
+
+		write_lock(&n->lock);
+		if ((n->nud_state == NUD_FAILED) ||
+		    (tbl->is_multicast &&
+		     tbl->is_multicast(n->primary_key)) ||
+		    time_after(tref, n->updated))
+			remove = true;
+		write_unlock(&n->lock);
+
+		if (remove && neigh_remove_one(n, tbl))
+			shrunk++;
+		if (shrunk >= max_clean)
+			break;
 	}
 
 	tbl->last_flush = jiffies;
@@ -264,16 +285,6 @@ static void neigh_add_timer(struct neighbour *n, unsigned long when)
 	}
 }
 
-static int neigh_del_timer(struct neighbour *n)
-{
-	if ((n->nud_state & NUD_IN_TIMER) &&
-	    del_timer(&n->timer)) {
-		neigh_release(n);
-		return 1;
-	}
-	return 0;
-}
-
 static void pneigh_queue_purge(struct sk_buff_head *list)
 {
 	struct sk_buff *skb;
@@ -307,33 +318,7 @@ static void neigh_flush_dev(struct neigh_table *tbl, struct net_device *dev,
 				np = &n->next;
 				continue;
 			}
-			rcu_assign_pointer(*np,
-				   rcu_dereference_protected(n->next,
-						lockdep_is_held(&tbl->lock)));
-			write_lock(&n->lock);
-			neigh_del_timer(n);
-			neigh_mark_dead(n);
-			if (refcount_read(&n->refcnt) != 1) {
-				/* The most unpleasant situation.
-				   We must destroy neighbour entry,
-				   but someone still uses it.
-
-				   The destroy will be delayed until
-				   the last user releases us, but
-				   we must kill timers etc. and move
-				   it to safe state.
-				 */
-				__skb_queue_purge(&n->arp_queue);
-				n->arp_queue_len_bytes = 0;
-				n->output = neigh_blackhole;
-				if (n->nud_state & NUD_VALID)
-					n->nud_state = NUD_NOARP;
-				else
-					n->nud_state = NUD_NONE;
-				neigh_dbg(2, "neigh %p is stray\n", n);
-			}
-			write_unlock(&n->lock);
-			neigh_cleanup_and_release(n);
+			neigh_del(n, np, tbl);
 		}
 	}
 }
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2021-04-20  4:27 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2021-03-17 18:53 [PATCH 1/2] neighbour: allow referenced neighbours to be removed Thadeu Lima de Souza Cascardo
2021-03-17 18:53 ` [PATCH 2/2] neighbour: allow NUD_NOARP entries to be forced GCed Thadeu Lima de Souza Cascardo
2021-04-19 16:44   ` Kasper Dupont
2021-04-19 17:10     ` David Ahern
2021-04-19 17:52       ` Kasper Dupont
2021-04-20  4:27         ` David Ahern
2021-03-17 23:42 ` [PATCH 1/2] neighbour: allow referenced neighbours to be removed David Ahern
2021-03-22 21:33   ` Thadeu Lima de Souza Cascardo

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).