A stack which works with or without an Mbed os library. Provides IPv4 or IPv6 with a full 1500 byte buffer.

Dependents:   oldheating gps motorhome heating

Files at this revision

API Documentation at this revision

Comitter:
andrewboyson
Date:
Sun Nov 11 15:44:23 2018 +0000
Parent:
78:9d8fc88df405
Child:
80:4ef1500fca1d
Commit message:
Added RTO support to TCP and fixed a problem with polling of different IP versions.

Changed in this revision

net.txt Show annotated file Show diff for this revision Revisions of this file
tcp/http/http.c Show annotated file Show diff for this revision Revisions of this file
tcp/http/http.h Show annotated file Show diff for this revision Revisions of this file
tcp/tcb.c Show annotated file Show diff for this revision Revisions of this file
tcp/tcb.h Show annotated file Show diff for this revision Revisions of this file
tcp/tcp.txt Show annotated file Show diff for this revision Revisions of this file
tcp/tcpbuf.c Show annotated file Show diff for this revision Revisions of this file
tcp/tcpbuf.h Show annotated file Show diff for this revision Revisions of this file
tcp/tcprecv.c Show annotated file Show diff for this revision Revisions of this file
tcp/tcpsend.c Show annotated file Show diff for this revision Revisions of this file
--- a/net.txt	Mon Nov 05 19:27:19 2018 +0000
+++ b/net.txt	Sun Nov 11 15:44:23 2018 +0000
@@ -3,3 +3,10 @@
 NIC      - Hardware, Phy etc
 Link     - Raw data
 Ethernet - Contains the source and destination MACs and the Ethertype: ARP; IP4; IP6
+
+To use you need to:
+
+call NetInit once with the ipv4 name and the ipv6 name 
+call NetMain continuously.
+
+See the "ch" (Central Heating) or "gps" programs for examples. 
\ No newline at end of file
--- a/tcp/http/http.c	Mon Nov 05 19:27:19 2018 +0000
+++ b/tcp/http/http.c	Sun Nov 11 15:44:23 2018 +0000
@@ -12,11 +12,11 @@
 void (*HttpReplyFunction  )(int todo);                                       //Plumb into this from your html server
 int  (*HttpRequestFunction)(char *pPath, char *pLastModified, char *pQuery); //Plumb into this from your html server
 
-void HttpHandleRequest(int size, char* pRequestStream, int positionInRequestStream, int* pToDo)
+void HttpHandleRequest(int size, char* pRequestStream, uint32_t positionInRequestStream, int* pToDo)
 {
     if (HttpTrace)
     {
-        LogF("HTTP  <<< %d (%d)\r\n", size, positionInRequestStream);
+        LogF("HTTP  <<< %d (%u)\r\n", size, positionInRequestStream);
     }
     //Handle request for the first packet of data received but leave todo the same after that.
     if (size && positionInRequestStream == 0)
@@ -30,7 +30,7 @@
     }
     
 }
-void HttpSendReply(int* pSize, char* pReplyStream, int positionInReplyStream, uint16_t mss, int todo)
+void HttpSendReply(int* pSize, char* pReplyStream, uint32_t positionInReplyStream, uint16_t mss, int todo)
 {
     TcpBufStart(positionInReplyStream, mss, pReplyStream);
     HttpReplyFunction(todo);
--- a/tcp/http/http.h	Mon Nov 05 19:27:19 2018 +0000
+++ b/tcp/http/http.h	Sun Nov 11 15:44:23 2018 +0000
@@ -21,8 +21,8 @@
 extern void (*HttpReplyFunction)(int todo);
 
 extern bool   HttpTrace;
-extern void   HttpHandleRequest(int   size, char* pRequestStream, int positionInRequestStream, int* pToDo);
-extern void   HttpSendReply    (int* pSize, char* pReplyStream,   int positionInReplyStream, uint16_t mss, int todo);
+extern void   HttpHandleRequest(int   size, char* pRequestStream, uint32_t positionInRequestStream, int* pToDo);
+extern void   HttpSendReply    (int* pSize, char* pReplyStream,   uint32_t positionInReplyStream, uint16_t mss, int todo);
 
 extern void   HttpDateFromDateTime(const char* date, const char *ptime, char* ptext);
 extern void   HttpDateFromNow(char* pText);
--- a/tcp/tcb.c	Mon Nov 05 19:27:19 2018 +0000
+++ b/tcp/tcb.c	Sun Nov 11 15:44:23 2018 +0000
@@ -7,10 +7,6 @@
 
 #define TCB_COUNT 10
 
-#define TIMEOUT_SYN_RECEIVED 2
-#define TIMEOUT_ESTABLISHED 20
-#define TIMEOUT_CLOSING      2
-
 struct tcb tcbs[TCB_COUNT];
 
 uint32_t TcbGetIsn()
@@ -52,35 +48,10 @@
 }
 
 uint32_t TcbElapsed = 0;
-static void reap()
-{
-    static struct tcb* pTcb = tcbs;
-    
-    if (pTcb->state == TCB_EMPTY) return;
-    
-    uint32_t limit;
-    switch (pTcb->state)
-    {
-        case TCB_SYN_RECEIVED:   limit = TIMEOUT_SYN_RECEIVED; break;
-        case TCB_ESTABLISHED:    limit = TIMEOUT_ESTABLISHED;  break;
-        case TCB_CLOSE_FIN_WAIT: limit = TIMEOUT_CLOSING;      break;
-        case TCB_CLOSE_ACK_WAIT: limit = TIMEOUT_CLOSING;      break;
-    }
-    
-    if (TcbElapsed - pTcb->lastSendTime > limit)
-    {
-        if (TcpTrace) LogTimeF("Reaping TCB %d port %d\r\n", pTcb - tcbs, pTcb->remPort);
-        pTcb->state = TCB_EMPTY;
-    }
-    
-    pTcb++;
-    if (pTcb >= tcbs + TCB_COUNT) pTcb = tcbs;
-}
 
 void TcbMain()
 {
     if (ClockTicked) TcbElapsed++;
-    reap();
 }
 void TcbInit()
 {
--- a/tcp/tcb.h	Mon Nov 05 19:27:19 2018 +0000
+++ b/tcp/tcb.h	Sun Nov 11 15:44:23 2018 +0000
@@ -8,12 +8,12 @@
 #define TCB_SYN_RECEIVED   1
 #define TCB_ESTABLISHED    2
 #define TCB_CLOSE_FIN_WAIT 3
-#define TCB_CLOSE_ACK_WAIT 4
 
 struct tcb
 {
     int      state;
-    uint32_t lastSendTime;
+    uint32_t timeSendsBeingAcked; //Used for RTO
+    uint32_t timeLastRcvd;        //Used for detect idle links
     int      remArIndex;
     int      ipType;
     uint16_t remPort;
--- a/tcp/tcp.txt	Mon Nov 05 19:27:19 2018 +0000
+++ b/tcp/tcp.txt	Sun Nov 11 15:44:23 2018 +0000
@@ -23,4 +23,12 @@
   then the receiver returns to the LISTEN state, otherwise the receiver
   aborts the connection and goes to the CLOSED state.  If the receiver
   was in any other state, it aborts the connection and advises the user
-  and goes to the CLOSED state.
\ No newline at end of file
+  and goes to the CLOSED state.
+  
+Retransmission Timer – To retransmit lost segments, TCP uses retransmission timeout (RTO).
+When TCP sends a segment the timer starts and stops when the acknowledgment is received.
+If the timer expires timeout occurs and the segment is retransmitted.
+The timer 'SendsBeingAcked' is reset whenever:
+    tcprecv - a packet is received where the ack counter has advanced;
+    tcpsend - the ack count received is the same as the seq count sent
+    tcpsend - resent the last unacked packet
\ No newline at end of file
--- a/tcp/tcpbuf.c	Mon Nov 05 19:27:19 2018 +0000
+++ b/tcp/tcpbuf.c	Sun Nov 11 15:44:23 2018 +0000
@@ -3,8 +3,8 @@
 
 #include "http.h"
 
-static int currentPositionInMessage;
-static int bufferPositionInMessage;
+static uint32_t currentPositionInMessage;
+static uint32_t bufferPositionInMessage;
 static int bufferLength;
 static char* pBuffer;
 static char* p;
@@ -14,7 +14,7 @@
     return currentPositionInMessage >= bufferPositionInMessage && currentPositionInMessage < bufferPositionInMessage + bufferLength;
 }
 
-void TcpBufStart(int position, int mss, char *pData)
+void TcpBufStart(uint32_t position, int mss, char *pData)
 {
     currentPositionInMessage = 0;
     bufferPositionInMessage = position;
--- a/tcp/tcpbuf.h	Mon Nov 05 19:27:19 2018 +0000
+++ b/tcp/tcpbuf.h	Sun Nov 11 15:44:23 2018 +0000
@@ -1,4 +1,5 @@
 #include <stdarg.h>
+#include <stdint.h>
 
 extern void TcpBufAddChar  (char c);
 extern void TcpBufFillChar (char c, int length);
@@ -8,5 +9,5 @@
 extern void TcpBufAddData  (const char* data, int length);
 extern void TcpBufAddStream(void (*startFunction)(void), int (*enumerateFunction)(void));
 
-extern void TcpBufStart    (int position, int mss, char *pData);
+extern void TcpBufStart    (uint32_t position, int mss, char *pData);
 extern int  TcpBufLength   (void);
--- a/tcp/tcprecv.c	Mon Nov 05 19:27:19 2018 +0000
+++ b/tcp/tcprecv.c	Sun Nov 11 15:44:23 2018 +0000
@@ -22,9 +22,9 @@
     if (TcpTrace)
     {
         if (NetTraceNewLine) Log("\r\n");
+        LogTime("TCP sent RST - ");
         va_list argptr;
         va_start(argptr, fmt);
-        LogTime("TCP sent RST - ");
         LogV(fmt, argptr);
         Log("\r\n");
         va_end(argptr);
@@ -45,23 +45,23 @@
     }
 }
 
-static void startConnection(void *pPacket, int ipType, int remArIndex, int locMss)
+static void handleSyn(void *pPacket, int ipType, int remArIndex, int locMss)
 {
     //Get the MSS to use for sends - it is the lower of the MSS advertised by the remote host and our local MSS
     int remMss = TcpHdrMssGet();
     pTcb->remMss = remMss ? remMss : 536; //default MSS for IPv4 [576 - 20(TCP) - 20(IP)];
     if (pTcb->remMss > locMss) pTcb->remMss = locMss;
     
-    pTcb->lastSendTime     = TcbElapsed;
-    pTcb->rcvdFin          = false;
-    pTcb->sentFin          = false;
-    pTcb->todo             = 0;
-    pTcb->remIsn           = TcpHdrSeqNum;
-    pTcb->locIsn           = TcbGetIsn();
-    pTcb->bytesRcvdFromRem = 0;
-    pTcb->bytesAckdByRem   = 0;
-    pTcb->bytesAckdToRem   = 0;
-    pTcb->bytesSentToRem   = 0;
+    pTcb->timeSendsBeingAcked = TcbElapsed;
+    pTcb->rcvdFin             = false;
+    pTcb->sentFin             = false;
+    pTcb->todo                = 0;
+    pTcb->remIsn              = TcpHdrSeqNum;
+    pTcb->locIsn              = TcbGetIsn();
+    pTcb->bytesRcvdFromRem    = 0;
+    pTcb->bytesAckdByRem      = 0;
+    pTcb->bytesAckdToRem      = 0;
+    pTcb->bytesSentToRem      = 0;
 }
 static void handleReceivedData(void* pPacket, int dataLength, uint32_t position)
 {
@@ -105,7 +105,7 @@
             break;
             
         default:
-            logTraceBack(traceback, "TCP - unknown port %d\r\n", TcpHdrDstPort);
+            logTraceBack(traceback, "TCP - unknown port %d", TcpHdrDstPort);
             return DO_NOTHING; //Ignore unknown ports
     }
     
@@ -114,9 +114,10 @@
     if (!pTcb) pTcb = TcbGetEmpty();
     if (!pTcb) //Bomb out if no more tcbs are available
     {
-        logTraceBack(traceback, "TCP - no more tcbs are available\r\n");
+        logTraceBack(traceback, "TCP - no more tcbs are available");
         return DO_NOTHING;
     }
+    pTcb->timeLastRcvd     = TcbElapsed;
     pTcb->remArIndex       = remArIndex;
     pTcb->ipType           = ipType;
     pTcb->remPort          = TcpHdrSrcPort;
@@ -128,7 +129,7 @@
     {
         if (pTcb->state)
         {
-            logTraceBack(traceback, "TCP - received reset - resetting TCB\r\n");
+            logTraceBack(traceback, "TCP - received reset - resetting TCB");
             pTcb->state = TCB_EMPTY;
         }
         return DO_NOTHING;        //Don't reply
@@ -145,7 +146,7 @@
         }
         else
         {
-            startConnection(pPacketRx, ipType, remArIndex, locMss);
+            handleSyn(pPacketRx, ipType, remArIndex, locMss);
         }
     }
     
@@ -179,6 +180,13 @@
         return TcpSendReset(pSizeTx, pPacketTx, pTcb);
     }
     
+    //Check if the acks of bytes sent has progressed and reset the timer
+    uint32_t ackRcvdFromRem = TcpHdrAckNum - pTcb->locIsn;
+    if (ackRcvdFromRem > pTcb->bytesAckdByRem) pTcb->timeSendsBeingAcked = TcbElapsed;
+
+    //Record the number of bytes acked by the remote host
+    pTcb->bytesAckdByRem = ackRcvdFromRem;
+
     /* If the connection is in a synchronized state
     any unacceptable segment (out of window sequence number or
     unacceptible acknowledgment number) must elicit only an empty
@@ -188,13 +196,14 @@
     uint32_t seqRcvdFromRem = TcpHdrSeqNum - pTcb->remIsn;
     if (seqRcvdFromRem != pTcb->bytesAckdToRem)
     {
-        logTraceBack(traceback, "TCP - resending last ACK on port %d as seq rcvd is %d and seq last was %d\r\n", TcpHdrSrcPort, seqRcvdFromRem, pTcb->bytesAckdToRem);
+        //Only warn non keep-alives
+        if (seqRcvdFromRem != 0 || pTcb->bytesAckdToRem != 1)
+        {
+            logTraceBack(traceback, "TCP - resending last ACK on port %d as seq rcvd is %d and last seq ackd was %d", TcpHdrSrcPort, seqRcvdFromRem, pTcb->bytesAckdToRem);
+        }
         return TcpResendLastAck(pSizeTx, pPacketTx, pTcb);
     }
     
-    //Record the number of bytes acked by the remote host
-    pTcb->bytesAckdByRem = TcpHdrAckNum - pTcb->locIsn;
-    
     //Handle FIN
     if (TcpHdrFIN) pTcb->rcvdFin = true; //When reply is all sent only a passive close is needed
         
@@ -223,7 +232,6 @@
             if (dataLength) handleReceivedData (pPacketRx, dataLength, seqRcvdFromRem - 1);
             if (pTcb->sentFin)
             {
-                //pTcb->state = pTcb->rcvdFin ? TCB_CLOSE_ACK_WAIT : TCB_CLOSE_FIN_WAIT;
                 pTcb->state = pTcb->rcvdFin ? TCB_EMPTY : TCB_CLOSE_FIN_WAIT;
             }
             break;
@@ -235,9 +243,6 @@
             }
             break;
             
-        case TCB_CLOSE_ACK_WAIT: //End of passive close
-            pTcb->state = TCB_EMPTY;
-            break;
     }
     
     return TcpSend(pSizeTx, pPacketTx, pTcb);
--- a/tcp/tcpsend.c	Mon Nov 05 19:27:19 2018 +0000
+++ b/tcp/tcpsend.c	Sun Nov 11 15:44:23 2018 +0000
@@ -13,9 +13,11 @@
 #include    "led.h"
 #include "tcpsend.h"
 
-#define RTO_TIME 2
+#define TIMEOUT_RETRANSMISSION 2
+#define TIMEOUT_KEEP_ALIVE    60
+#define TIMEOUT_BROKEN_LINK  600
 
-static int addApplicationData(void* pPacket, uint16_t port, int start, int mss, int todo)
+static int addApplicationData(void* pPacket, uint16_t port, uint32_t start, int mss, int todo)
 {
     int dataLength = 0;
     char* pData = (char*)pPacket + TcpHdrSizeGet();
@@ -48,9 +50,6 @@
     //Calculate the size of the reply
     *pSize = TcpHdrSizeGet() + dataLength;
     
-    //Update the last send time
-    pTcb->lastSendTime = TcbElapsed;
-    
     return ActionMakeFromDestAndTrace(UNICAST, TcpDoTrace && NetTraceStack);
 }
 int TcpSend(int* pSize, void* pPacket, struct tcb* pTcb)
@@ -95,15 +94,14 @@
             break;
     }
 
-    //See if have need to acknowledge received bytes
+    //Handle the acknowledgement of received bytes
     bool rcvdSeqHasAdvanced = pTcb->bytesRcvdFromRem > pTcb->bytesAckdToRem;
-    
-    //Record the number of bytes acknowledged to the remote
     pTcb->bytesAckdToRem = pTcb->bytesRcvdFromRem;
+    TcpHdrAckNum  = pTcb->bytesAckdToRem + pTcb->remIsn;  //Set up the acknowledgement field ready to send
     
-    //Specify the start of the data being sent and acknowledge the data received
-    TcpHdrAckNum = pTcb->bytesAckdToRem + pTcb->remIsn;  //Set up the acknowledgement field ready to send
-    TcpHdrSeqNum = pTcb->bytesSentToRem + pTcb->locIsn;  //Set up the start of the message before adding the bytes sent
+    //Specify the start of the data being sent
+    uint32_t seqToSend = pTcb->bytesSentToRem;
+    TcpHdrSeqNum  = seqToSend            + pTcb->locIsn;  //Set up the start of the message before adding the bytes sent
 
     //Record the number of bytes sent
     uint32_t bytesToSend = 0;
@@ -113,8 +111,9 @@
 
     pTcb->bytesSentToRem += bytesToSend;
     
+    //Only send a packet if have bytes or an acknowledgement to send
     if (!rcvdSeqHasAdvanced && !bytesToSend) return DO_NOTHING;
-    
+        
     return preparePacket(pPacket, pTcb, dataLength, pSize);
 }
 int TcpResendLastUnAcked(int* pSize, void *pPacket, struct tcb* pTcb)
@@ -122,7 +121,7 @@
     int dataLength = 0;
     TcpHdrMakeEmpty();
     int locMss = *pSize - TcpHdrSizeGet();
-    int seqNum = pTcb->bytesAckdByRem;
+    uint32_t seqNum = pTcb->bytesAckdByRem;
     switch (pTcb->state)
     {
         case TCB_SYN_RECEIVED:
@@ -131,9 +130,10 @@
             break;
             
         case TCB_ESTABLISHED:
+        case TCB_CLOSE_FIN_WAIT:
             if (pTcb->todo)
             {
-                dataLength = addApplicationData(pPacket, pTcb->locPort, seqNum, pTcb->remMss, pTcb->todo);
+                dataLength = addApplicationData(pPacket, pTcb->locPort, seqNum - 1, pTcb->remMss, pTcb->todo);
                 if (dataLength < pTcb->remMss)
                 {
                     TcpHdrFIN     = true;
@@ -145,7 +145,7 @@
 
     TcpHdrAckNum = pTcb->bytesAckdToRem + pTcb->remIsn;  //Set up the acknowledgement field ready to send
     TcpHdrSeqNum = seqNum               + pTcb->locIsn;  //Set up the start of the message before adding the bytes sent
-
+    
     return preparePacket(pPacket, pTcb, dataLength, pSize);
 }
 int TcpResendLastAck(int* pSize, void *pPacket, struct tcb* pTcb)
@@ -168,31 +168,76 @@
     
     return preparePacket(pPacket, pTcb, dataLength, pSize);
 }
+int TcpSendClose(int* pSize, void *pPacket, struct tcb* pTcb)
+{
+    int dataLength = 0;
+    TcpHdrMakeEmpty();
+    TcpHdrFIN     = true;
+    pTcb->sentFin = true;
+    pTcb->bytesSentToRem += 1; //For the FIN
+    TcpHdrAckNum = pTcb->bytesAckdToRem + pTcb->remIsn;  //Set up the acknowledgement field ready to send
+    TcpHdrSeqNum = pTcb->bytesSentToRem + pTcb->locIsn;  //Set up the start of the message before adding the bytes sent
+    
+    return preparePacket(pPacket, pTcb, dataLength, pSize);
+}
+
 int TcpPollForPacketToSend(int* pSize, void* pPacket, int ipType, int* pRemArIndex)
 {
-     //This loops around the TCBs
-     //struct tcb* pTcb = TcbGetOld();
-     static struct tcb* pTcb = NULL; //Passing a pointer containing NULL to TcbGetNext causes it to return the first TCB
-     TcbGetNext(&pTcb);
-    
-    //Ignore empty TCBs
+    //This loops around the TCBs, moving on if empty but staying if not the right type
+    static struct tcb* pTcb = NULL; //Passing a pointer containing NULL to TcbGetNext causes it to return the first TCB
+    static bool stay = false;
+    if (!stay) TcbGetNext(&pTcb);
+    stay = false;
     if (!pTcb->state) return DO_NOTHING;
+    if (pTcb->ipType != ipType)
+    {
+        stay = true;
+        return DO_NOTHING;
+    }
+
+    //Check and make available the remote AR index
+    if (pTcb->remArIndex < 0)
+    {
+        if (TcpTrace) LogTimeF("TCP - Reaping TCB port %hu - missing remote AR index\r\n", pTcb->remPort);
+        pTcb->state = TCB_EMPTY;
+        return DO_NOTHING;
+    }
+    *pRemArIndex = pTcb->remArIndex;
+        
+    //Close old ones
+    if (TcbElapsed > pTcb->timeLastRcvd + TIMEOUT_KEEP_ALIVE)
+    {
+        if (TcpTrace) LogTimeF("TCP - Closing TCB port %hu - after keep alive\r\n", pTcb->remPort);
+        return TcpSendClose(pSize, pPacket, pTcb);
+    }
     
-    //Ignore TCBs of a different IP type
-    if (pTcb->ipType != ipType) return DO_NOTHING;
+    //Reap old ones
+    if (TcbElapsed > pTcb->timeLastRcvd + TIMEOUT_BROKEN_LINK)
+    {
+        if (TcpTrace) LogTimeF("TCP - Reaping TCB port %hu - broken link\r\n", pTcb->remPort);
+        pTcb->state = TCB_EMPTY;
+        return DO_NOTHING;
+    }
     
-    //Return the remote AR index
-    *pRemArIndex = pTcb->remArIndex;
+    //Reset the RTO if all bytes are acknowledged
+    if (pTcb->bytesSentToRem == pTcb->bytesAckdByRem)
+    {
+        pTcb->timeSendsBeingAcked = TcbElapsed;
+    }
     
-    //Check if have unacknowledged bytes longer than a time out
-    if (TcbElapsed - pTcb->lastSendTime > RTO_TIME && pTcb->bytesSentToRem > pTcb->bytesAckdByRem)
+    //Check if have unacknowledged send bytes after the RTO
+    if (TcbElapsed > pTcb->timeSendsBeingAcked + TIMEOUT_RETRANSMISSION)
     {
+        if (TcpTrace)
+        {
+            if (NetTraceNewLine) Log("\r\n");
+            LogTimeF("TCP - Resending seq %lu on port %hu waiting for ack of %lu bytes\r\n", pTcb->bytesAckdByRem, pTcb->remPort, pTcb->bytesSentToRem);
+        }
+        pTcb->timeSendsBeingAcked = TcbElapsed;
         return TcpResendLastUnAcked(pSize, pPacket, pTcb);
     }
-    //Otherwise do a normal send
-    else
-    {
-        return TcpSend(pSize, pPacket, pTcb);
-    }
+    
+    //If haven't had to do anything else then do a normal send
+    return TcpSend(pSize, pPacket, pTcb);
 }