]> rtime.felk.cvut.cz Git - pes-rpp/rpp-lwip.git/commitdiff
Merge branch 'master' of ssh://git.sv.gnu.org/srv/git/lwip
authorSimon Goldschmidt <goldsimon@gmx.de>
Fri, 23 Sep 2011 17:16:01 +0000 (19:16 +0200)
committerSimon Goldschmidt <goldsimon@gmx.de>
Fri, 23 Sep 2011 17:16:01 +0000 (19:16 +0200)
CHANGELOG
src/core/tcp.c
src/core/tcp_in.c
src/include/lwip/pbuf.h

index c829a7203d6b5dc51872c1d8f397f60ebddc601c..b5811da4dfad8bce63a16925b12a1fd5b4a53675 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -65,6 +65,10 @@ HISTORY
 
  ++ Bugfixes:
 
+  2011-09-23: Simon Goldschmidt
+  * pbuf.h, tcp.c, tcp_in.c: fixed bug #33871: rejecting TCP_EVENT_RECV() for
+    the last packet including FIN can lose data
+
   2011-09-22: Simon Goldschmidt
   * tcp.c: fixed bug #34360 tcp_shutdown: RST on unacked is not send when
     shutting down both rx AND tx
index 853151715b5962db882debeda92a50d151301a73..cd94e7486d7c4a10fcf5d383a0bc9c4f5c8ee7a5 100644 (file)
@@ -1055,13 +1055,32 @@ tcp_fasttmr(void)
     if (pcb->refused_data != NULL) {
       /* Notify again application with data previously received. */
       err_t err;
+      u8_t refused_flags = pcb->refused_data->flags;
+      /* set pcb->refused_data to NULL in case the callback frees it and then
+         closes the pcb */
+      struct pbuf *refused_data = pcb->refused_data;
+      pcb->refused_data = NULL;
       LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_fasttmr: notify kept packet\n"));
-      TCP_EVENT_RECV(pcb, pcb->refused_data, ERR_OK, err);
+      TCP_EVENT_RECV(pcb, refused_data, ERR_OK, err);
       if (err == ERR_OK) {
-        pcb->refused_data = NULL;
+        /* did refused_data include a FIN? If so, handle it now. */
+        if (refused_flags & PBUF_FLAG_TCP_FIN) {
+          /* correct rcv_wnd as the application won't call tcp_recved()
+             for the FIN's seqno */
+          if (pcb->rcv_wnd != TCP_WND) {
+            pcb->rcv_wnd++;
+          }
+          TCP_EVENT_CLOSED(pcb, err);
+          if (err == ERR_ABRT) {
+            pcb = NULL;
+          }
+        }
       } else if (err == ERR_ABRT) {
         /* if err == ERR_ABRT, 'pcb' is already deallocated */
         pcb = NULL;
+      } else {
+        /* data is still refused */
+        pcb->refused_data = refused_data;
       }
     }
 
index 0cfb437bb54e88310a38af8b23242b99abdea511..c659b494337e6999cdfe8e8b2f5d30549be69f42 100644 (file)
@@ -306,17 +306,36 @@ tcp_input(struct pbuf *p, struct netif *inp)
 
     /* If there is data which was previously "refused" by upper layer */
     if (pcb->refused_data != NULL) {
+      u8_t refused_flags = pcb->refused_data->flags;
+      /* set pcb->refused_data to NULL in case the callback frees it and then
+         closes the pcb */
+      struct pbuf *refused_data = pcb->refused_data;
+      pcb->refused_data = NULL;
       /* Notify again application with data previously received. */
       LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: notify kept packet\n"));
-      TCP_EVENT_RECV(pcb, pcb->refused_data, ERR_OK, err);
+      TCP_EVENT_RECV(pcb, refused_data, ERR_OK, err);
       if (err == ERR_OK) {
-        pcb->refused_data = NULL;
+        /* did refused_data include a FIN? */
+        if (refused_flags & PBUF_FLAG_TCP_FIN) {
+          /* correct rcv_wnd as the application won't call tcp_recved()
+             for the FIN's seqno */
+          if (pcb->rcv_wnd != TCP_WND) {
+            pcb->rcv_wnd++;
+          }
+          TCP_EVENT_CLOSED(pcb, err);
+          if (err == ERR_ABRT) {
+            goto dropped;
+          }
+        }
       } else if ((err == ERR_ABRT) || (tcplen > 0)) {
         /* if err == ERR_ABRT, 'pcb' is already deallocated */
         /* Drop incoming packets because pcb is "full" (only if the incoming
            segment contains data). */
         LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: drop incoming packets, because pcb is \"full\"\n"));
         goto dropped;
+      } else {
+        /* data is still refused, pbuf is still valid (go on for ACK-only packets) */
+        pcb->refused_data = refused_data;
       }
     }
     tcp_input_pcb = pcb;
@@ -375,14 +394,19 @@ tcp_input(struct pbuf *p, struct netif *inp)
         /* If a FIN segment was received, we call the callback
            function with a NULL buffer to indicate EOF. */
         if (recv_flags & TF_GOT_FIN) {
-          /* correct rcv_wnd as the application won't call tcp_recved()
-             for the FIN's seqno */
-          if (pcb->rcv_wnd != TCP_WND) {
-            pcb->rcv_wnd++;
-          }
-          TCP_EVENT_CLOSED(pcb, err);
-          if (err == ERR_ABRT) {
-            goto aborted;
+          if (pcb->refused_data != NULL) {
+            /* Delay this if we have refused data. */
+            pcb->refused_data->flags |= PBUF_FLAG_TCP_FIN;
+          } else {
+            /* correct rcv_wnd as the application won't call tcp_recved()
+               for the FIN's seqno */
+            if (pcb->rcv_wnd != TCP_WND) {
+              pcb->rcv_wnd++;
+            }
+            TCP_EVENT_CLOSED(pcb, err);
+            if (err == ERR_ABRT) {
+              goto aborted;
+            }
           }
         }
 
index 41c49099ff566eaa460ef5039f43f6d146fb1ea9..786b288b4da1ee2010af73efb7b69650ba8fa254 100644 (file)
@@ -80,6 +80,8 @@ typedef enum {
 #define PBUF_FLAG_LLBCAST   0x08U
 /** indicates this pbuf was received as link-level multicast */
 #define PBUF_FLAG_LLMCAST   0x10U
+/** indicates this pbuf includes a TCP FIN flag */
+#define PBUF_FLAG_TCP_FIN   0x20U
 
 struct pbuf {
   /** next pbuf in singly linked pbuf chain */