Commit 8a1631d5 authored by James Chapman's avatar James Chapman Committed by David S. Miller

l2tp: make datapath sequence number support RFC-compliant

The L2TP datapath is not currently RFC-compliant when sequence numbers
are used in L2TP data packets. According to the L2TP RFC, any received
sequence number NR greater than or equal to the next expected NR is
acceptable, where the "greater than or equal to" test is determined by
the NR wrap point. This differs for L2TPv2 and L2TPv3, so add state in
the session context to hold the max NR value and the NR window size in
order to do the acceptable sequence number value check. These might be
configurable later, but for now we derive it from the tunnel L2TP
version, which determines the sequence number field size.
Signed-off-by: default avatarJames Chapman <jchapman@katalix.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent b6dc01a4
...@@ -414,10 +414,7 @@ static void l2tp_recv_dequeue_skb(struct l2tp_session *session, struct sk_buff * ...@@ -414,10 +414,7 @@ static void l2tp_recv_dequeue_skb(struct l2tp_session *session, struct sk_buff *
if (L2TP_SKB_CB(skb)->has_seq) { if (L2TP_SKB_CB(skb)->has_seq) {
/* Bump our Nr */ /* Bump our Nr */
session->nr++; session->nr++;
if (tunnel->version == L2TP_HDR_VER_2) session->nr &= session->nr_max;
session->nr &= 0xffff;
else
session->nr &= 0xffffff;
l2tp_dbg(session, L2TP_MSG_SEQ, "%s: updated nr to %hu\n", l2tp_dbg(session, L2TP_MSG_SEQ, "%s: updated nr to %hu\n",
session->name, session->nr); session->name, session->nr);
...@@ -542,11 +539,34 @@ static inline int l2tp_verify_udp_checksum(struct sock *sk, ...@@ -542,11 +539,34 @@ static inline int l2tp_verify_udp_checksum(struct sock *sk,
return __skb_checksum_complete(skb); return __skb_checksum_complete(skb);
} }
static int l2tp_seq_check_rx_window(struct l2tp_session *session, u32 nr)
{
u32 nws;
if (nr >= session->nr)
nws = nr - session->nr;
else
nws = (session->nr_max + 1) - (session->nr - nr);
return nws < session->nr_window_size;
}
/* If packet has sequence numbers, queue it if acceptable. Returns 0 if /* If packet has sequence numbers, queue it if acceptable. Returns 0 if
* acceptable, else non-zero. * acceptable, else non-zero.
*/ */
static int l2tp_recv_data_seq(struct l2tp_session *session, struct sk_buff *skb) static int l2tp_recv_data_seq(struct l2tp_session *session, struct sk_buff *skb)
{ {
if (!l2tp_seq_check_rx_window(session, L2TP_SKB_CB(skb)->ns)) {
/* Packet sequence number is outside allowed window.
* Discard it.
*/
l2tp_dbg(session, L2TP_MSG_SEQ,
"%s: pkt %u len %d discarded, outside window, nr=%u\n",
session->name, L2TP_SKB_CB(skb)->ns,
L2TP_SKB_CB(skb)->length, session->nr);
goto discard;
}
if (session->reorder_timeout != 0) { if (session->reorder_timeout != 0) {
/* Packet reordering enabled. Add skb to session's /* Packet reordering enabled. Add skb to session's
* reorder queue, in order of ns. * reorder queue, in order of ns.
...@@ -556,7 +576,8 @@ static int l2tp_recv_data_seq(struct l2tp_session *session, struct sk_buff *skb) ...@@ -556,7 +576,8 @@ static int l2tp_recv_data_seq(struct l2tp_session *session, struct sk_buff *skb)
/* Packet reordering disabled. Discard out-of-sequence /* Packet reordering disabled. Discard out-of-sequence
* packets * packets
*/ */
if (L2TP_SKB_CB(skb)->ns != session->nr) { if ((L2TP_SKB_CB(skb)->ns != session->nr) &&
(!session->reorder_skip)) {
atomic_long_inc(&session->stats.rx_seq_discards); atomic_long_inc(&session->stats.rx_seq_discards);
l2tp_dbg(session, L2TP_MSG_SEQ, l2tp_dbg(session, L2TP_MSG_SEQ,
"%s: oos pkt %u len %d discarded, waiting for %u, reorder_q_len=%d\n", "%s: oos pkt %u len %d discarded, waiting for %u, reorder_q_len=%d\n",
...@@ -1826,6 +1847,11 @@ struct l2tp_session *l2tp_session_create(int priv_size, struct l2tp_tunnel *tunn ...@@ -1826,6 +1847,11 @@ struct l2tp_session *l2tp_session_create(int priv_size, struct l2tp_tunnel *tunn
session->session_id = session_id; session->session_id = session_id;
session->peer_session_id = peer_session_id; session->peer_session_id = peer_session_id;
session->nr = 0; session->nr = 0;
if (tunnel->version == L2TP_HDR_VER_2)
session->nr_max = 0xffff;
else
session->nr_max = 0xffffff;
session->nr_window_size = session->nr_max / 2;
sprintf(&session->name[0], "sess %u/%u", sprintf(&session->name[0], "sess %u/%u",
tunnel->tunnel_id, session->session_id); tunnel->tunnel_id, session->session_id);
......
...@@ -102,6 +102,8 @@ struct l2tp_session { ...@@ -102,6 +102,8 @@ struct l2tp_session {
u32 nr; /* session NR state (receive) */ u32 nr; /* session NR state (receive) */
u32 ns; /* session NR state (send) */ u32 ns; /* session NR state (send) */
struct sk_buff_head reorder_q; /* receive reorder queue */ struct sk_buff_head reorder_q; /* receive reorder queue */
u32 nr_max; /* max NR. Depends on tunnel */
u32 nr_window_size; /* NR window size */
struct hlist_node hlist; /* Hash list node */ struct hlist_node hlist; /* Hash list node */
atomic_t ref_count; atomic_t ref_count;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment