aboutsummaryrefslogtreecommitdiffstats
path: root/net/l2tp
diff options
context:
space:
mode:
authorJames Chapman <jchapman@katalix.com>2013-07-02 15:28:59 -0400
committerDavid S. Miller <davem@davemloft.net>2013-07-02 19:33:24 -0400
commit8a1631d588a39e826f4248e60310498d5266c6fa (patch)
treed4bc90f912c4f54a3becf34c2d8a7f35d2c605c6 /net/l2tp
parentb6dc01a43aaca24e6e6928e24d9b37ba599f1e3c (diff)
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: James Chapman <jchapman@katalix.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/l2tp')
-rw-r--r--net/l2tp/l2tp_core.c36
-rw-r--r--net/l2tp/l2tp_core.h2
2 files changed, 33 insertions, 5 deletions
diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c
index 5ca29659171d..735cc06971ef 100644
--- a/net/l2tp/l2tp_core.c
+++ b/net/l2tp/l2tp_core.c
@@ -414,10 +414,7 @@ static void l2tp_recv_dequeue_skb(struct l2tp_session *session, struct sk_buff *
414 if (L2TP_SKB_CB(skb)->has_seq) { 414 if (L2TP_SKB_CB(skb)->has_seq) {
415 /* Bump our Nr */ 415 /* Bump our Nr */
416 session->nr++; 416 session->nr++;
417 if (tunnel->version == L2TP_HDR_VER_2) 417 session->nr &= session->nr_max;
418 session->nr &= 0xffff;
419 else
420 session->nr &= 0xffffff;
421 418
422 l2tp_dbg(session, L2TP_MSG_SEQ, "%s: updated nr to %hu\n", 419 l2tp_dbg(session, L2TP_MSG_SEQ, "%s: updated nr to %hu\n",
423 session->name, session->nr); 420 session->name, session->nr);
@@ -542,11 +539,34 @@ static inline int l2tp_verify_udp_checksum(struct sock *sk,
542 return __skb_checksum_complete(skb); 539 return __skb_checksum_complete(skb);
543} 540}
544 541
542static int l2tp_seq_check_rx_window(struct l2tp_session *session, u32 nr)
543{
544 u32 nws;
545
546 if (nr >= session->nr)
547 nws = nr - session->nr;
548 else
549 nws = (session->nr_max + 1) - (session->nr - nr);
550
551 return nws < session->nr_window_size;
552}
553
545/* If packet has sequence numbers, queue it if acceptable. Returns 0 if 554/* If packet has sequence numbers, queue it if acceptable. Returns 0 if
546 * acceptable, else non-zero. 555 * acceptable, else non-zero.
547 */ 556 */
548static int l2tp_recv_data_seq(struct l2tp_session *session, struct sk_buff *skb) 557static int l2tp_recv_data_seq(struct l2tp_session *session, struct sk_buff *skb)
549{ 558{
559 if (!l2tp_seq_check_rx_window(session, L2TP_SKB_CB(skb)->ns)) {
560 /* Packet sequence number is outside allowed window.
561 * Discard it.
562 */
563 l2tp_dbg(session, L2TP_MSG_SEQ,
564 "%s: pkt %u len %d discarded, outside window, nr=%u\n",
565 session->name, L2TP_SKB_CB(skb)->ns,
566 L2TP_SKB_CB(skb)->length, session->nr);
567 goto discard;
568 }
569
550 if (session->reorder_timeout != 0) { 570 if (session->reorder_timeout != 0) {
551 /* Packet reordering enabled. Add skb to session's 571 /* Packet reordering enabled. Add skb to session's
552 * reorder queue, in order of ns. 572 * 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 /* Packet reordering disabled. Discard out-of-sequence 576 /* Packet reordering disabled. Discard out-of-sequence
557 * packets 577 * packets
558 */ 578 */
559 if (L2TP_SKB_CB(skb)->ns != session->nr) { 579 if ((L2TP_SKB_CB(skb)->ns != session->nr) &&
580 (!session->reorder_skip)) {
560 atomic_long_inc(&session->stats.rx_seq_discards); 581 atomic_long_inc(&session->stats.rx_seq_discards);
561 l2tp_dbg(session, L2TP_MSG_SEQ, 582 l2tp_dbg(session, L2TP_MSG_SEQ,
562 "%s: oos pkt %u len %d discarded, waiting for %u, reorder_q_len=%d\n", 583 "%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 session->session_id = session_id; 1847 session->session_id = session_id;
1827 session->peer_session_id = peer_session_id; 1848 session->peer_session_id = peer_session_id;
1828 session->nr = 0; 1849 session->nr = 0;
1850 if (tunnel->version == L2TP_HDR_VER_2)
1851 session->nr_max = 0xffff;
1852 else
1853 session->nr_max = 0xffffff;
1854 session->nr_window_size = session->nr_max / 2;
1829 1855
1830 sprintf(&session->name[0], "sess %u/%u", 1856 sprintf(&session->name[0], "sess %u/%u",
1831 tunnel->tunnel_id, session->session_id); 1857 tunnel->tunnel_id, session->session_id);
diff --git a/net/l2tp/l2tp_core.h b/net/l2tp/l2tp_core.h
index 485a490fd990..4b9a3b724423 100644
--- a/net/l2tp/l2tp_core.h
+++ b/net/l2tp/l2tp_core.h
@@ -102,6 +102,8 @@ struct l2tp_session {
102 u32 nr; /* session NR state (receive) */ 102 u32 nr; /* session NR state (receive) */
103 u32 ns; /* session NR state (send) */ 103 u32 ns; /* session NR state (send) */
104 struct sk_buff_head reorder_q; /* receive reorder queue */ 104 struct sk_buff_head reorder_q; /* receive reorder queue */
105 u32 nr_max; /* max NR. Depends on tunnel */
106 u32 nr_window_size; /* NR window size */
105 struct hlist_node hlist; /* Hash list node */ 107 struct hlist_node hlist; /* Hash list node */
106 atomic_t ref_count; 108 atomic_t ref_count;
107 109