MPPP: Discard fragments even when reassembling failed
[l2tpns.git] / ppp.c
diff --git a/ppp.c b/ppp.c
index e21c103..ee299cb 100644 (file)
--- a/ppp.c
+++ b/ppp.c
@@ -1866,18 +1866,36 @@ void processmpin(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
        
        // update M
        sess_local[s].last_seq = seq_num;
-       if (seq_num < this_fragmentation->M)
-               this_fragmentation->M = seq_num;
-       else
        {
-               uint32_t i, min = sess_local[(this_bundle->members[0])].last_seq;;
-               for (i = 1; i < this_bundle->num_of_links; i++)
+               /* seq # can be spread over the wrapping limit, and we must
+                  choose the "lowest" one, taking into account the wrapping:
+
+                  |-#-##--#----------------------M--###-#|
+
+                  but we must also care when the last highest seq # wraps,
+                  so we keep two minimums: one for the higher limit, and
+                  one for the lower one.
+
+                  In the nominal case, low_min = high_min.
+               */
+               uint32_t i, low_min, high_min;
+               low_min = sess_local[(this_bundle->members[0])].last_seq;;
+               high_min = this_bundle->max_seq; // max_seq is impossible to reach
+
+               for (i = 0; i < this_bundle->num_of_links; i++)
                {
                        uint32_t s_seq = sess_local[(this_bundle->members[i])].last_seq; 
-                       if (s_seq < min)
-                               min = s_seq;
+                       if (s_seq < low_min)
+                               low_min = s_seq;
+                       if (s_seq >= this_fragmentation->M && s_seq < high_min)
+                               high_min = s_seq;
                }
-               this_fragmentation->M = min;
+
+               // if high_min was found, it's the "lowest" one
+               if (high_min < this_bundle->max_seq)
+                       this_fragmentation->M = high_min;
+               else
+                       this_fragmentation->M = low_min;
        }
 
        LOG(4, s, t, "MPPP: Setting M to %d\n", this_fragmentation->M); 
@@ -1928,7 +1946,7 @@ void processmpin(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
 
                // return if a lost fragment is found
                if (!(this_fragmentation->fragment[begin_index].length))
-                       return; // assembling frame failed
+                       goto discard_lost_frames; // assembling frame failed
                // get the end of his frame
                while (this_fragmentation->fragment[end_index].length)
                {
@@ -1939,7 +1957,7 @@ void processmpin(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
 
                // return if a lost fragment is found
                if (!(this_fragmentation->fragment[end_index].length))
-                       return; // assembling frame failed
+                       goto discard_lost_frames; // assembling frame failed
 
                // assemble the packet
                //assemble frame, process it, reset fragmentation
@@ -1981,16 +1999,57 @@ void processmpin(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
                                break;
                }
        }
-       //discard fragments received before the recently assembled frame
-       begin_index = this_fragmentation->start_index;
-       this_fragmentation->start_index = (end_index + 1) & MAXFRAGNUM_MASK;
-       this_fragmentation->start_seq = (this_fragmentation->fragment[end_index].seq + 1) & (this_bundle->max_seq-1);
-       //clear length and flags of the discarded fragments
-       while (begin_index != this_fragmentation->start_index)
+
+discard_lost_frames:
+       //discard fragments numbererd below M and part of an unassembled frame,
+       //but not the one from the frame beginning just before M
+
+       // if we have something to discard
+       if (M_index != this_fragmentation->start_index)
        {
-               this_fragmentation->fragment[begin_index].flags = 0;
-               this_fragmentation->fragment[begin_index].length = 0;
-               begin_index = (begin_index + 1) & MAXFRAGNUM_MASK;
+               // look for end of previous frame
+               // start at M-1, going backward
+               uint16_t index = M_index;
+               uint8_t end_or_hole_found = 0;
+
+               while (index != this_fragmentation->start_index)
+               {
+                       fragmentt *this_frag;
+
+                       if (!end_or_hole_found)
+                       {
+                               fragmentt *front_frag = &this_fragmentation->frag[index];
+
+                               // before a MP_BEGIN, there must be a MP_END
+                               if (front_frag->length && (front_frag->flags & MP_BEGIN))
+                               {
+                                       end_or_hole_found = 1;
+                                       end_index = index;
+                               }
+                       }
+
+                       index = (index + (MAXFRAGNUM-1)) & MAXFRAGNUM_MASK;
+                       this_frag = &this_fragmentation->frag[index];
+
+                       if (!end_or_hole_found)
+                       {
+                               // we are a hole or a MP_END
+                               if (!this_frag->length || (this_frag->flags & MP_END))
+                               {
+                                       end_or_hole_found = 1;
+                                       end_index = index;
+                               }
+                       }
+
+                       if (end_or_hole_found)
+                       {
+                               this_frag->flags = 0;
+                               this_frag->length = 0;
+                       }
+               }
+
+               this_fragmentation->start_index = (end_index + 1) & MAXFRAGNUM_MASK;
+               this_fragmentation->start_seq = (this_fragmentation->fragment[end_index].seq + 1) & (this_bundle->max_seq-1);
        }
 
        LOG(4, s, t, "MPPP after assembling: M index is =%d, start index is = %d, start seq=%d\n",M_index, this_fragmentation->start_index, this_fragmentation->start_seq);