Andrew Boyson / net

Dependents:   oldheating gps motorhome heating

tcp/tcpsend.c

Committer:
andrewboyson
Date:
2018-11-05
Revision:
78:9d8fc88df405
Parent:
75:603b10404183
Child:
79:f50e02fb5c94

File content as of revision 78:9d8fc88df405:

#include <stdint.h>
#include <stdbool.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    "led.h"
#include "tcpsend.h"

#define RTO_TIME 2

static int addApplicationData(void* pPacket, uint16_t port, int start, int mss, int todo)
{
    int dataLength = 0;
    char* pData = (char*)pPacket + TcpHdrSizeGet();
    switch (port)
    {
        case 80:
            HttpSendReply(&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;
    
    //Update the last send time
    pTcb->lastSendTime = TcbElapsed;
    
    return ActionMakeFromDestAndTrace(UNICAST, TcpDoTrace && 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)
                    {
                        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;
    }

    //See if have need to acknowledge received bytes
    bool rcvdSeqHasAdvanced = pTcb->bytesRcvdFromRem > pTcb->bytesAckdToRem;
    
    //Record the number of bytes acknowledged to the remote
    pTcb->bytesAckdToRem = pTcb->bytesRcvdFromRem;
    
    //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

    //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;
    
    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();
    int seqNum = pTcb->bytesAckdByRem;
    switch (pTcb->state)
    {
        case TCB_SYN_RECEIVED:
            TcpHdrMssSet(locMss);
            TcpHdrSYN = true;
            break;
            
        case TCB_ESTABLISHED:
            if (pTcb->todo)
            {
                dataLength = addApplicationData(pPacket, pTcb->locPort, seqNum, 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 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
    if (!pTcb->state) return DO_NOTHING;
    
    //Ignore TCBs of a different IP type
    if (pTcb->ipType != ipType) return DO_NOTHING;
    
    //Return the remote AR index
    *pRemArIndex = pTcb->remArIndex;
    
    //Check if have unacknowledged bytes longer than a time out
    if (TcbElapsed - pTcb->lastSendTime > RTO_TIME && pTcb->bytesSentToRem > pTcb->bytesAckdByRem)
    {
        return TcpResendLastUnAcked(pSize, pPacket, pTcb);
    }
    //Otherwise do a normal send
    else
    {
        return TcpSend(pSize, pPacket, pTcb);
    }
}