[pptp-server] Packet reordering patch

Philip Van Baren phil at vibrationresearch.com
Wed Sep 27 09:47:57 CDT 2000


I made a patch to pptpd-1.1.1 which implements a simple packet reordering
scheme.  This made a huge difference on my network which has many
out-of-order packets.

Note that if in addition to your packet order problems you are getting
dropped packets and you have encryption enabled, you will still probably see
the message:
  Sep 27 00:03:15 gateway pppd[10544]: rcvd [Compressed data] 10 32 ae 68 c0
8e e1 92 ...
in your log file after a packet gets dropped, after which the link seems to
lock up.  The only way I have been able to solve this problem so far is to
disable encryption because pppd doesn't seem to recover from lost packets
when encryption is enabled.

Has anyone found a way to get pppd to recover nicely from lost packets when
using encryption?

(I am using this with pptpd-1.1.1 and pppd-2.3.11 and kernel 2.2.17)

Phil Van Baren
phil at vibrationresearch.com

Here is the patch to add packet reordering:

diff -u pptpd-1.1.1/pptpgre.c pptpd-1.1.1-reorder/pptpgre.c
--- pptpd-1.1.1/pptpgre.c	Thu Dec 23 20:03:44 1999
+++ pptpd-1.1.1-reorder/pptpgre.c	Wed Sep 27 09:36:20 2000
@@ -73,6 +73,7 @@

   memset (gre, 0, sizeof (*gre));
   reset_pty_to_gre (gre);
+  gre->seq_recv = -1;

   /* Open IP protocol socket */
   gre->gre_fd = socket(AF_INET, SOCK_RAW, PPTP_PROTO);
@@ -437,7 +438,8 @@
 static int
 read_gre (struct gre_state *gre)
 {
-  int status, offset=0;
+  int status, offset=0,i,j;
+  int recv_time;
   unsigned char buffer[GRE_PACKET_SIZE + 64 /* ip header */];
   struct gre_rcv_packet *recv_packet;
   unsigned char *data;
@@ -449,10 +451,6 @@
   memset (buffer, 0x66, sizeof(buffer));
   status = read (gre->gre_fd, buffer, sizeof(buffer));

-  recv_packet = &gre->window[(gre->gre_current_packet
-                              + gre->gre_num_packets) %
-                            (PCKT_RECV_WINDOW_SIZE + 1)];
-
   if (status < 0) {
     if (errno == EAGAIN || errno == EINTR) {
       /*syslog (LOG_INFO, "GRE read() wants retry");*/
@@ -480,11 +478,9 @@
     return 0;
   }

-  memset (recv_packet, 0x11, sizeof(*recv_packet));
-  /* FIXME: Too much copying.  Should just buffer the IP header. */
-  memcpy (recv_packet, &buffer[offset], status);
-
+  recv_packet = (struct gre_rcv_packet *) (buffer+offset);
   header = recv_packet->header;
+  recv_time = time(NULL);

   /* Validate the packet. */
   if ((ntoh8 (header.ver) & ~PPTP_GRE_FLAG_A) != PPTP_GRE_VER
@@ -532,7 +528,8 @@
   if (has_payload) {
     u_int32_t seq;

-    if (gre->gre_num_packets >= PCKT_RECV_WINDOW_SIZE) {
+    /* Keep 8 packets of buffer room for packet reordering */
+    if (gre->gre_num_packets >= (PCKT_RECV_WINDOW_SIZE-PCKT_REORDER_SIZE))
{
       syslog (LOG_ERR, "GRE: window overflowed.  Dropping packet...");
       return 1;
     }
@@ -546,15 +543,10 @@
       data = recv_packet->body.one.data;
     }

-    if (pptpctrl_debug && seq != gre->seq_recv + 1) {
-      syslog (LOG_INFO, "Unexpected sequence number; got %u after %u",
-              seq, gre->seq_recv);
-    }
-
     /* Check sequence number; discard if out of order */
     if (!seq_less_than (gre->seq_recv, seq)) {
       syslog (LOG_WARNING,
-              "Discarding out-of-order packet %x, already have %x",
+              "Discarding out-of-order packet %u, already have %u",
               seq, gre->seq_recv);
       return 0;
     }
@@ -568,8 +560,108 @@
       return 0;
     }

-    gre->seq_recv = seq;
-    gre->gre_num_packets++;
+    i=seq - gre->seq_recv - 1;
+
+    /* If this packet is beyond the reorder buffer,
+     * or if it has been 3 seconds size we last accepted a packet,
+     * stop waiting for the missing packet */
+    if ((i >= PCKT_REORDER_SIZE) || (recv_time >
(gre->gre_last_recv_time+PCKT_REORDER_WAIT_TIME)) ) {
+      if(gre->gre_num_packets > 0) {
+	/* There are still packets in the queue, so we can't skip packets yet */
+	if(pptpctrl_debug) {
+	  syslog (LOG_INFO, "Dropping out-of-order packet; got %u after %u",
+		  PCKT_REORDER_SIZE,seq, gre->seq_recv);
+	}
+	return 0;
+      }
+        /* Stop waiting for the oldest packet */
+        for(j=0 ; (j<PCKT_REORDER_SIZE) &&
(gre->window[(gre->gre_current_packet+j)%(PCKT_RECV_WINDOW_SIZE+1)].header.p
ayload_len == 0) ; j++);
+
+        /* Missing more than PCKT_REORDER_SIZE consecutive packets - just
ignore them and process this new packet */
+	if(pptpctrl_debug) {
+	  if(recv_time > (gre->gre_last_recv_time+PCKT_REORDER_WAIT_TIME)) {
+	    /* Hide this message on the first packet because it is meaningless */
+	    if(gre->gre_last_recv_time != 0)
+	      syslog (LOG_INFO, "Packet reorder timeout.");
+	  } else if(j==PCKT_REORDER_SIZE) {
+	    syslog (LOG_INFO, "Missing %d consecutive packets; got %u after %u",
+		    PCKT_REORDER_SIZE,seq, gre->seq_recv);
+	  } else {
+	    syslog (LOG_INFO, "Exceeded packet reorder buffer size; got %u after
%u; skipping %d packets",
+		    seq, gre->seq_recv,j);
+	  }
+	}
+        if(j==PCKT_REORDER_SIZE) {
+
memcpy(&gre->window[gre->gre_current_packet],buffer+offset,status);
+	    /*
+	    if(pptpctrl_debug) {
+	      syslog(LOG_INFO,"Filling packet buffer slot %d with packet
%u",gre->gre_current_packet,seq);
+	    }
+	    */
+            gre->seq_recv = seq;
+            gre->gre_num_packets++;
+	    gre->gre_last_recv_time=recv_time;
+            return 1;
+        }
+
+        /* Else skip forward to the oldest previously received packet */
+        gre->gre_current_packet=(gre->gre_current_packet+j) %
(PCKT_RECV_WINDOW_SIZE+1);
+        gre->seq_recv += j;
+
+        i-=j;
+     }
+     /* Buffer the latest packet if it isn't too far ahead, else just drop
it */
+     if(i < PCKT_REORDER_SIZE) {
+        if(pptpctrl_debug && (i>0)) {
+	      syslog (LOG_INFO, "Buffering out-of-order packet; got %u after %u",
+        	      seq, gre->seq_recv);
+        }
+
memcpy(&gre->window[(gre->gre_current_packet+gre->gre_num_packets+i) %
(PCKT_RECV_WINDOW_SIZE+1)],buffer+offset,status);
+	/*
+	    if(pptpctrl_debug) {
+	      syslog(LOG_INFO,"Filling packet buffer slot %d with packet %u
(curr=%d,n=%d,i=%d)",(gre->gre_current_packet+gre->gre_num_packets+i) %
(PCKT_RECV_WINDOW_SIZE+1),seq,gre->gre_current_packet,gre->gre_num_packets,i
);
+	    }
+	*/
+     } else if(pptpctrl_debug) {
+          syslog (LOG_INFO, "Dropping out-of-order packet %u",seq);
+     }
+
+     /* Skip lost packets if we get PCKT_REORDER_SIZE consecutive following
packets */
+
+     if((gre->gre_num_packets==0) &&
(gre->window[gre->gre_current_packet].header.payload_len == 0)) {
+       for(i=0,j=1;(i < (PCKT_REORDER_RESUME_SIZE)) &&
(j<PCKT_REORDER_SIZE);j++) {
+	 if(gre->window[(gre->gre_current_packet+j) %
(PCKT_RECV_WINDOW_SIZE+1)].header.payload_len != 0) {
+	   i++;
+	 } else {
+	   i=0;
+	 }
+       }
+       if(i >= (PCKT_REORDER_RESUME_SIZE)) {
+	 /* Advance counters over the lost packet(s) */
+	 for(j=1 ; (j<PCKT_REORDER_SIZE) &&
(gre->window[(gre->gre_current_packet+j)%(PCKT_RECV_WINDOW_SIZE+1)].header.p
ayload_len == 0) ; j++);
+	 if(pptpctrl_debug) {
+	   syslog (LOG_INFO, "Gave up waiting for %d lost packets",j);
+	 }
+
+	 gre->gre_current_packet=(gre->gre_current_packet+j) %
(PCKT_RECV_WINDOW_SIZE+1);
+	 gre->seq_recv += j;
+       }
+     }
+
+     /* Add all consecutive available packets to the queue */
+     for(j=0 ; (j<PCKT_REORDER_SIZE) &&
(gre->window[(gre->gre_current_packet+gre->gre_num_packets+j)%(PCKT_RECV_WIN
DOW_SIZE+1)].header.payload_len != 0) ; j++);
+     /*
+     if(pptpctrl_debug && (j>1)) {
+       syslog(LOG_INFO,"Adding %d packets to the queue",j);
+     }
+     */
+     gre->seq_recv += j;
+     gre->gre_num_packets += j;
+     if(j>0) {
+       gre->gre_last_recv_time = recv_time;
+       return 1;
+     }
+     return 0;

 #if 0
     /* Dump start of packet. */
@@ -578,7 +670,6 @@
             status, data[0], data[1], data[2], data[3], data[4],
             data[5], data[6]);
 #endif
-    return 1;
   }

   if (!has_payload && !has_ack)
@@ -627,8 +718,8 @@
   packet = &gre->window[gre->gre_current_packet];
   header = packet->header;
   if (header.protocol != ntoh16(PPTP_GRE_PROTO)) {
-    syslog (LOG_ERR, "INTERNAL ERROR: Bad protocol %x in gre_to_hdlc",
-            ntoh16(header.protocol));
+    syslog (LOG_ERR, "INTERNAL ERROR: Bad protocol %x in gre_to_hdlc,
buffer slot %d",
+            ntoh16(header.protocol),gre->gre_current_packet);
   }

   data = (PPTP_GRE_IS_A (ntoh8 (header.ver))
@@ -656,8 +747,8 @@
   gre->gre_current_packet = ((gre->gre_current_packet + 1)
                              % (PCKT_RECV_WINDOW_SIZE + 1));

-  /* Fill packet with garbage */
-  memset (packet, 0x33, sizeof (*packet));
+  /* Zero out the packet so the payload_len parameter is zero which
indicates this slot is empty */
+  memset (packet, 0x00, sizeof (*packet));
 }

 /* Send some stuff to the PTY.  Return 1 if we wrote something, 0 if
diff -u pptpd-1.1.1/pptpgre.h pptpd-1.1.1-reorder/pptpgre.h
--- pptpd-1.1.1/pptpgre.h	Thu Dec 23 16:43:33 1999
+++ pptpd-1.1.1-reorder/pptpgre.h	Wed Sep 27 09:36:16 2000
@@ -14,6 +14,26 @@
 #define GRE_PACKET_SIZE 2048
 #define HDLC_PACKET_SIZE (2*GRE_PACKET_SIZE + 6)

+/* Variables to control the packet reordering scheme:
+ *  PCKT_REORDER_SIZE
+ *     this is the number of packets ahead of the current packet that
+ *     will get buffered while waiting for a packet.  If a new packet
+ *     comes in more than this far ahead, we stop waiting for the missing
packet
+ *
+ *  PCKT_REORDER_RESUME_SIZE
+ *     if this many consecutive packets are available in the buffer
+ *     while we are waiting for a lost packet, then we will stop waiting
+ *     for the missing packet
+ *
+ *  PCKT_REORDER_WAIT_TIME
+ *     if a new packet comes in and this many seconds have expired since
+ *     we last passed on a packet to pppd, then we will stop waiting
+ *     for the missing packet
+ */
+#define PCKT_REORDER_SIZE 8
+#define PCKT_REORDER_RESUME_SIZE 4
+#define PCKT_REORDER_WAIT_TIME 3
+
 enum gre_header_type { GRE_HEAD_ONE, GRE_HEAD_BOTH };

 struct gre_xmit_packet {
@@ -72,6 +92,7 @@
   struct gre_rcv_packet window[PCKT_RECV_WINDOW_SIZE+1];
   int gre_current_packet;
   int gre_num_packets;
+  int gre_last_recv_time;

   /* PTY output state */
   int pty_fd;





More information about the pptp-server mailing list