From: Ren Wei <weir@nebusec.ai>
To: netdev@vger.kernel.org
Cc: davem@davemloft.net, edumazet@google.com, kuba@kernel.org,
pabeni@redhat.com, horms@kernel.org, kees@kernel.org,
kuniyu@google.com, alice.kernel@fastmail.im,
michael.bommarito@gmail.com, mail@david-bauer.net,
jchapman@katalix.com, vega@nebusec.ai, caoruide123@gmail.com,
weir@nebusec.ai
Subject: [PATCH net 1/1] l2tp: bound the reorder queue
Date: Thu, 24 Sep 2026 08:32:57 +0800 [thread overview]
Message-ID: <f0d62dc44f984114abe09f9b1d0432c98de7dbbf.1790136447.git.caoruide123@gmail.com> (raw)
In-Reply-To: <cover.1790136447.git.caoruide123@gmail.com>
From: Ruide Cao <caoruide123@gmail.com>
The receive-window check accepts every sequence number in the large
window. If the peer omits the expected packet, distinct future packets
can therefore fill the ordered reorder queue. Since insertion walks the
queue and expiration was checked only while receiving another packet,
this allowed unbounded memory and CPU use.
Reject duplicate sequence numbers and cap the reorder queue at 64
packets. Packets beyond the cap are discarded, except that the expected
sequence number is admitted so an in-order packet can drain the queue.
The existing timeout recovery then skips a missing sequence number when
the queue limit is reached.
Use a session timer to run the existing dequeue path at the head
expiration and shut it down before purging a session. This bounds the
queue and services expiration even when the peer stops sending.
Fixes: 3557baabf280 ("[L2TP]: PPP over L2TP driver core")
Cc: stable@vger.kernel.org
Reported-by: Vega <vega@nebusec.ai>
Assisted-by: LLM
Signed-off-by: Ruide Cao <caoruide123@gmail.com>
Signed-off-by: Ren Wei <weir@nebusec.ai>
---
net/l2tp/l2tp_core.c | 57 +++++++++++++++++++++++++++++++++++++++++---
net/l2tp/l2tp_core.h | 2 ++
2 files changed, 56 insertions(+), 3 deletions(-)
diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c
index f940914959b1..c92d4392f9b0 100644
--- a/net/l2tp/l2tp_core.c
+++ b/net/l2tp/l2tp_core.c
@@ -82,6 +82,7 @@
#define L2TP_SL_SEQ_MASK 0x00ffffff
#define L2TP_HDR_SIZE_MAX 14
+#define L2TP_REORDER_MAX_QUEUE 64
/* Default trace flags */
#define L2TP_DEFAULT_DEBUG_FLAGS 0
@@ -637,7 +638,22 @@ static void l2tp_recv_queue_skb(struct l2tp_session *session, struct sk_buff *sk
u32 ns = L2TP_SKB_CB(skb)->ns;
spin_lock_bh(&session->reorder_q.lock);
+ if (skb_queue_len(&session->reorder_q) >= L2TP_REORDER_MAX_QUEUE &&
+ ns != session->nr) {
+ atomic_long_inc(&session->stats.rx_seq_discards);
+ atomic_long_inc(&session->stats.rx_errors);
+ kfree_skb(skb);
+ goto out;
+ }
+
skb_queue_walk_safe(&session->reorder_q, skbp, tmp) {
+ if (unlikely(L2TP_SKB_CB(skbp)->has_seq &&
+ L2TP_SKB_CB(skbp)->ns == ns)) {
+ atomic_long_inc(&session->stats.rx_seq_discards);
+ atomic_long_inc(&session->stats.rx_errors);
+ kfree_skb(skb);
+ goto out;
+ }
if (L2TP_SKB_CB(skbp)->ns > ns) {
__skb_queue_before(&session->reorder_q, skbp, skb);
atomic_long_inc(&session->stats.rx_oos_packets);
@@ -651,6 +667,20 @@ static void l2tp_recv_queue_skb(struct l2tp_session *session, struct sk_buff *sk
spin_unlock_bh(&session->reorder_q.lock);
}
+static bool l2tp_recv_queue_tail_skb(struct l2tp_session *session,
+ struct sk_buff *skb)
+{
+ spin_lock_bh(&session->reorder_q.lock);
+ if (skb_queue_len(&session->reorder_q) >= L2TP_REORDER_MAX_QUEUE) {
+ spin_unlock_bh(&session->reorder_q.lock);
+ return false;
+ }
+ __skb_queue_tail(&session->reorder_q, skb);
+ spin_unlock_bh(&session->reorder_q.lock);
+
+ return true;
+}
+
/* Dequeue a single skb.
*/
static void l2tp_recv_dequeue_skb(struct l2tp_session *session, struct sk_buff *skb)
@@ -687,6 +717,7 @@ static void l2tp_recv_dequeue_skb(struct l2tp_session *session, struct sk_buff *
*/
static void l2tp_recv_dequeue(struct l2tp_session *session)
{
+ bool dequeued = false;
struct sk_buff *skb;
struct sk_buff *tmp;
@@ -706,6 +737,7 @@ static void l2tp_recv_dequeue(struct l2tp_session *session)
trace_session_pkt_expired(session, cb->ns);
session->reorder_skip = 1;
__skb_unlink(skb, &session->reorder_q);
+ dequeued = true;
kfree_skb(skb);
continue;
}
@@ -720,6 +752,7 @@ static void l2tp_recv_dequeue(struct l2tp_session *session)
goto out;
}
__skb_unlink(skb, &session->reorder_q);
+ dequeued = true;
/* Process the skb. We release the queue lock while we
* do so to let other contexts process the queue.
@@ -730,9 +763,22 @@ static void l2tp_recv_dequeue(struct l2tp_session *session)
}
out:
+ if (skb_queue_empty(&session->reorder_q))
+ timer_delete(&session->reorder_timer);
+ else if (dequeued || !timer_pending(&session->reorder_timer))
+ timer_reduce(&session->reorder_timer,
+ L2TP_SKB_CB(skb_peek(&session->reorder_q))->expires);
spin_unlock_bh(&session->reorder_q.lock);
}
+static void l2tp_recv_dequeue_timer(struct timer_list *timer)
+{
+ struct l2tp_session *session = timer_container_of(session, timer,
+ reorder_timer);
+
+ l2tp_recv_dequeue(session);
+}
+
static int l2tp_seq_check_rx_window(struct l2tp_session *session, u32 nr)
{
u32 nws;
@@ -774,7 +820,8 @@ static int l2tp_recv_data_seq(struct l2tp_session *session, struct sk_buff *skb)
* sequence number to re-enable packet reception.
*/
if (cb->ns == session->nr) {
- skb_queue_tail(&session->reorder_q, skb);
+ if (!l2tp_recv_queue_tail_skb(session, skb))
+ goto discard;
} else {
u32 nr_oos = cb->ns;
u32 nr_next = (session->nr_oos + 1) & session->nr_max;
@@ -793,7 +840,8 @@ static int l2tp_recv_data_seq(struct l2tp_session *session, struct sk_buff *skb)
trace_session_pkt_oos(session, cb->ns);
goto discard;
}
- skb_queue_tail(&session->reorder_q, skb);
+ if (!l2tp_recv_queue_tail_skb(session, skb))
+ goto discard;
}
out:
@@ -986,7 +1034,8 @@ void l2tp_recv_common(struct l2tp_session *session, struct sk_buff *skb,
* reorder queue. This ensures that it will be
* delivered after all previous sequenced skbs.
*/
- skb_queue_tail(&session->reorder_q, skb);
+ if (!l2tp_recv_queue_tail_skb(session, skb))
+ goto discard;
}
/* Try to dequeue as many skbs from reorder_q as we can. */
@@ -1748,6 +1797,7 @@ static void l2tp_session_del_work(struct work_struct *work)
del_work);
l2tp_session_unhash(session);
+ timer_shutdown_sync(&session->reorder_timer);
l2tp_session_queue_purge(session);
if (session->session_close)
(*session->session_close)(session);
@@ -1804,6 +1854,7 @@ struct l2tp_session *l2tp_session_create(int priv_size, struct l2tp_tunnel *tunn
tunnel->tunnel_id, session->session_id);
skb_queue_head_init(&session->reorder_q);
+ timer_setup(&session->reorder_timer, l2tp_recv_dequeue_timer, 0);
session->hlist_key = l2tp_v3_session_hashkey(tunnel->sock, session->session_id);
INIT_HLIST_NODE(&session->hlist);
diff --git a/net/l2tp/l2tp_core.h b/net/l2tp/l2tp_core.h
index ffd8ced3a51f..6e4c1ef6a0a2 100644
--- a/net/l2tp/l2tp_core.h
+++ b/net/l2tp/l2tp_core.h
@@ -4,6 +4,7 @@
* Copyright (c) 2008,2009 Katalix Systems Ltd
*/
#include <linux/refcount.h>
+#include <linux/timer.h>
#ifndef _L2TP_CORE_H_
#define _L2TP_CORE_H_
@@ -80,6 +81,7 @@ struct l2tp_session {
u32 nr; /* session NR state (receive) */
u32 ns; /* session NR state (send) */
struct sk_buff_head reorder_q; /* receive reorder queue */
+ struct timer_list reorder_timer;
u32 nr_max; /* max NR. Depends on tunnel */
u32 nr_window_size; /* NR window size */
u32 nr_oos; /* NR of last OOS packet */
--
2.47.3
next prev parent reply other threads:[~2026-09-24 0:33 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-24 0:32 [PATCH net 0/1] l2tp: bound the reorder queue Ren Wei
2026-09-24 0:32 ` Ren Wei [this message]
2026-09-24 7:17 ` [PATCH net 1/1] " Eric Dumazet
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=f0d62dc44f984114abe09f9b1d0432c98de7dbbf.1790136447.git.caoruide123@gmail.com \
--to=weir@nebusec.ai \
--cc=alice.kernel@fastmail.im \
--cc=caoruide123@gmail.com \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=horms@kernel.org \
--cc=jchapman@katalix.com \
--cc=kees@kernel.org \
--cc=kuba@kernel.org \
--cc=kuniyu@google.com \
--cc=mail@david-bauer.net \
--cc=michael.bommarito@gmail.com \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=vega@nebusec.ai \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox