Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependents: oldheating gps motorhome heating
tcp/tcpsend.c
- Committer:
- andrewboyson
- Date:
- 2019-05-14
- Revision:
- 145:206bf0d073c7
- Parent:
- 144:6bd5c54efc7d
- Child:
- 146:0fc66d610fd6
File content as of revision 145:206bf0d073c7:
#include <stdint.h>
#include <stdbool.h>
#include <stdarg.h>
#include "log.h"
#include "net.h"
#include "action.h"
#include "tcp.h"
#include "tcpbuf.h"
#include "tcphdr.h"
#include "tcb.h"
#include "ip4.h"
#include "dhcp.h"
#include "http.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 bool addApplicationData(int *pDataLength, void* pPacket, uint16_t port, uint32_t start, int mss, char* pAppState, bool clientFinished)
{
char* pData = (char*)pPacket + TcpHdrSizeGet();
TcpBufStart(start, mss, pData);
bool finished = false;
switch (port)
{
case 80: finished = HttpReplyPoll (pAppState, clientFinished); break;
case 443: finished = HttpsReplyPoll(pAppState, clientFinished); break;
}
*pDataLength = TcpBufLength();
return finished;
}
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)
{
bool finished = addApplicationData(&dataLength, pPacket, pTcb->locPort, pTcb->bytesSentToRem - 1, pTcb->remMss, pTcb->appData, pTcb->rcvdFin);
if (finished)
{
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:
{
bool finished = addApplicationData(&dataLength, pPacket, pTcb->locPort, seqNum - 1, pTcb->remMss, pTcb->appData, pTcb->rcvdFin);
if (finished)
{
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);
}
}