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
tcp/tcpsend.c
- Committer:
- andrewboyson
- Date:
- 2019-04-10
- Revision:
- 140:9000ea70b220
- Parent:
- 133:a37eb35a03f1
- Child:
- 144:6bd5c54efc7d
File content as of revision 140:9000ea70b220:
#include <stdint.h> #include <stdbool.h> #include <stdarg.h> #include "log.h" #include "net.h" #include "action.h" #include "tcp.h" #include "tcphdr.h" #include "tcb.h" #include "ip4.h" #include "dhcp.h" #include "http.h" #include "https.h" #include "led.h" #include "tcpsend.h" #include "mstimer.h" #define TIMEOUT_RETRANSMISSION_MS 700 #define MAX_RETRANSMISSIONS 5 #define TIMEOUT_BROKEN_LINK_MS 600000 static void log(uint16_t remPort, char* fmt, ...) { if (TcpTrace) { if (NetTraceNewLine) Log("\r\n"); LogTimeF("TCP port %hu - ", remPort); va_list argptr; va_start(argptr, fmt); LogV(fmt, argptr); va_end(argptr); Log("\r\n"); } } static bool doTrace(uint16_t port) { switch (port) { case 80: if (HttpTrace) return true; default: return false; } } static int addApplicationData(void* pPacket, uint16_t port, uint32_t start, int mss, int todo) { int dataLength = 0; char* pData = (char*)pPacket + TcpHdrSizeGet(); switch (port) { case 80: HttpSendReply(&dataLength, pData, start, mss, todo); break; case 443: HttpsSendReply(&dataLength, pData, start, mss, todo); break; default: break; } return dataLength; } static int preparePacket(void* pPacket, struct tcb* pTcb, int dataLength, int* pSize) { //Set the acknowledge flag TcpHdrACK = true; //Swap the ports for the reply TcpHdrSrcPort = pTcb->locPort; TcpHdrDstPort = pTcb->remPort; //Specify the receive window size to not throttle TcpHdrWindow = 4000; //Write the header TcpHdrWriteToPacket(pPacket); //Calculate the size of the reply *pSize = TcpHdrSizeGet() + dataLength; return ActionMakeFromDestAndTrace(UNICAST, doTrace(pTcb->locPort) && NetTraceStack); } int TcpSend(int* pSize, void* pPacket, struct tcb* pTcb) { int dataLength = 0; TcpHdrMakeEmpty(); int locMss = *pSize - TcpHdrSizeGet(); switch (pTcb->state) { case TCB_SYN_RECEIVED: if (pTcb->bytesSentToRem == 0) { TcpHdrMssSet(locMss); TcpHdrSYN = true; } break; case TCB_ESTABLISHED: if (!pTcb->sentFin) { if (pTcb->bytesSentToRem - pTcb->bytesAckdByRem < pTcb->window) { if (pTcb->todo && pTcb->postComplete && MsTimerAbsolute(pTcb->delayUntil)) //don't send response until any post has completed { dataLength = addApplicationData(pPacket, pTcb->locPort, pTcb->bytesSentToRem - 1, pTcb->remMss, pTcb->todo); if (dataLength < pTcb->remMss) { TcpHdrFIN = true; pTcb->sentFin = true; } } else { if (pTcb->rcvdFin) { TcpHdrFIN = true; pTcb->sentFin = true; } } } } break; } //Handle the acknowledgement of received bytes bool rcvdSeqHasAdvanced = pTcb->bytesRcvdFromRem > pTcb->bytesAckdToRem; 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 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; if (TcpHdrSYN) bytesToSend += 1; //Add one to acknowledge the SYN bytesToSend += dataLength; //Add the number of bytes received if (TcpHdrFIN) bytesToSend += 1; //Add one to acknowledge the FIN 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) { int dataLength = 0; TcpHdrMakeEmpty(); int locMss = *pSize - TcpHdrSizeGet(); uint32_t seqNum = pTcb->bytesAckdByRem; switch (pTcb->state) { case TCB_SYN_RECEIVED: TcpHdrMssSet(locMss); TcpHdrSYN = true; break; case TCB_ESTABLISHED: case TCB_CLOSE_FIN_WAIT: if (pTcb->todo) { dataLength = addApplicationData(pPacket, pTcb->locPort, seqNum - 1, pTcb->remMss, pTcb->todo); if (dataLength < pTcb->remMss) { TcpHdrFIN = true; pTcb->sentFin = true; } } break; } 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) { int dataLength = 0; TcpHdrMakeEmpty(); 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 TcpSendReset(int* pSize, void *pPacket, struct tcb* pTcb) { int dataLength = 0; TcpHdrMakeEmpty(); 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 TcpHdrRST = true; 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, int* pLocIpScope) { //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) pTcb = 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) { log(pTcb->remPort, "missing remote AR index -> reaping TCB"); pTcb->state = TCB_EMPTY; return DO_NOTHING; } *pRemArIndex = pTcb->remArIndex; //Return the local IP scope if (pLocIpScope) *pLocIpScope = pTcb->locIpScope; //Reap old ones if (MsTimerRelative(pTcb->timeLastRcvd, TIMEOUT_BROKEN_LINK_MS)) { log(pTcb->remPort, "broken link -> reaping TCB"); pTcb->state = TCB_EMPTY; return DO_NOTHING; } //Reset the RTO if all bytes are acknowledged if (pTcb->bytesSentToRem == pTcb->bytesAckdByRem) { pTcb->timeSendsBeingAcked = MsTimerCount; pTcb->countSendsNotAcked = 0; } //Check if have unacknowledged send bytes after the RTO if (MsTimerRelative(pTcb->timeSendsBeingAcked, TIMEOUT_RETRANSMISSION_MS)) { pTcb->countSendsNotAcked++; if (pTcb->countSendsNotAcked > MAX_RETRANSMISSIONS) { log(pTcb->remPort, "reached maximum retransmissions -> sending reset"); pTcb->state = TCB_EMPTY; return TcpSendReset(pSize, pPacket, pTcb); } else { log(pTcb->remPort, "only had ack of %lu while sent %lu -> resending", pTcb->bytesAckdByRem, pTcb->bytesSentToRem); pTcb->timeSendsBeingAcked = MsTimerCount; return TcpResendLastUnAcked(pSize, pPacket, pTcb); } } else { //If haven't had to do anything else then do a normal send return TcpSend(pSize, pPacket, pTcb); } }