l2tp: fix reorder timeout recovery
authorJames Chapman <jchapman@katalix.com>
Wed, 9 May 2012 23:43:08 +0000 (23:43 +0000)
committerDavid S. Miller <davem@davemloft.net>
Fri, 11 May 2012 03:27:34 +0000 (23:27 -0400)
When L2TP data packet reordering is enabled, packets are held in a
queue while waiting for out-of-sequence packets. If a packet gets
lost, packets will be held until the reorder timeout expires, when we
are supposed to then advance to the sequence number of the next packet
but we don't currently do so. As a result, the data channel is stuck
because we are waiting for a packet that will never arrive - all
packets age out and none are passed.

The fix is to add a flag to the session context, which is set when the
reorder timeout expires and tells the receive code to reset the next
expected sequence number to that of the next packet in the queue.

Tested in a production L2TP network with Starent and Nortel L2TP gear.

Signed-off-by: James Chapman <jchapman@katalix.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/l2tp/l2tp_core.c
net/l2tp/l2tp_core.h

index 456b52d8f6d893ecd0ef112d2024eeda43b2d3d5..d1ab3a236cca50cc249be5665441776b0323fd8d 100644 (file)
@@ -428,6 +428,7 @@ start:
                               session->name, L2TP_SKB_CB(skb)->ns,
                               L2TP_SKB_CB(skb)->length, session->nr,
                               skb_queue_len(&session->reorder_q));
+                       session->reorder_skip = 1;
                        __skb_unlink(skb, &session->reorder_q);
                        kfree_skb(skb);
                        if (session->deref)
@@ -436,6 +437,14 @@ start:
                }
 
                if (L2TP_SKB_CB(skb)->has_seq) {
+                       if (session->reorder_skip) {
+                               PRINTK(session->debug, L2TP_MSG_SEQ, KERN_DEBUG,
+                                      "%s: advancing nr to next pkt: %u -> %u",
+                                      session->name, session->nr,
+                                      L2TP_SKB_CB(skb)->ns);
+                               session->reorder_skip = 0;
+                               session->nr = L2TP_SKB_CB(skb)->ns;
+                       }
                        if (L2TP_SKB_CB(skb)->ns != session->nr) {
                                PRINTK(session->debug, L2TP_MSG_SEQ, KERN_DEBUG,
                                       "%s: holding oos pkt %u len %d, "
index 0bf60fc88bb71f40ca6110b56d44410854d91fec..90026341a1e52e22e5b0b674c384c9592c5aa0dd 100644 (file)
@@ -123,6 +123,7 @@ struct l2tp_session {
                                                 * categories */
        int                     reorder_timeout; /* configured reorder timeout
                                                  * (in jiffies) */
+       int                     reorder_skip;   /* set if skip to next nr */
        int                     mtu;
        int                     mru;
        enum l2tp_pwtype        pwtype;