Andrew Boyson / net

Dependents:   oldheating gps motorhome heating

tcp/tcp.c

Committer:
andrewboyson
Date:
2018-10-23
Revision:
73:43e3d7fb3d60
Parent:
72:19457bba58d0
Child:
74:c3756bfa960e

File content as of revision 73:43e3d7fb3d60:

#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"


//Payload variables
static int dataLength;
static int maxMss = 0;

bool TcpTrace = false;
static bool doTrace = false;

static struct tcb* pTcb;

static void resetConnectionOut(char* message)
{
    if (TcpTrace)
    {
        LogTime("TCP ");
        Log(message);
        Log("\r\n");
    }
    dataLength = 0;
    TcpHdrClearOptions();
    TcpHdrACK = false;
    TcpHdrPSH = false;
    TcpHdrRST = true;
    TcpHdrSYN = false;
    TcpHdrFIN = false;
    
    pTcb->state = TCB_EMPTY;
}
static void startConnectionIn(void *pPacket, void* pCachedRemIp)
{
    TcpHdrReadOptions(pPacket);                 //Get the MSS
    if (TcpHdrMss > maxMss) TcpHdrMss = maxMss;
    
    pTcb->mss     = TcpHdrMss;
    pTcb->state   = TCB_SYN_RECEIVED;
    pTcb->elapsed = TcbElapsed;
    pTcb->pIp     = pCachedRemIp;
    pTcb->port    = TcpHdrSrcPort;
    pTcb->hadFin  = false;
}
static void startConnectionOut(void* pPacket)
{
    TcpHdrMss = maxMss;                 //Ethernet 1500 - 20 - 20; or, in our case 768 - 20 - 20
    TcpHdrWriteOptions(pPacket);
    dataLength = 0;
   
    TcpHdrACK = true;                    //Send ACK and SYN
    TcpHdrPSH = false;
    TcpHdrRST = false;
    TcpHdrSYN = true;
    TcpHdrFIN = false;
}
static void establishConnectionIn()
{
    pTcb->state   = TCB_ESTABLISHED;
    pTcb->elapsed = TcbElapsed;
    pTcb->todo    = 0;
}
static void handleEstablishedConnectionIn(void* pPacket)
{
    char* pData = (char*)pPacket + TcpHdrSize;
    HttpHandleRequest(dataLength, pData, pTcb->recdBytes - 1, &pTcb->todo);
}
static void handleEstablishedConnectionOut(void* pPacket)
{
    TcpHdrClearOptions();
    char* pData = (char*)pPacket + TcpHdrSize;
    HttpSendReply(&dataLength, pData, pTcb->sentBytes - 1, TcpHdrMss, pTcb->todo);
    
    TcpHdrACK = true;                 //Send ACK
    TcpHdrRST = false;
    TcpHdrSYN = false;
    TcpHdrPSH = false;
    
    if (dataLength < TcpHdrMss) //If a part packet then there can be no more to send
    {
        TcpHdrFIN = true;       //Inform the client that we have no more to send after this
        if (pTcb->hadFin) pTcb->state = TCB_ACK_WAIT; //Passive close
        else              pTcb->state = TCB_FIN_WAIT; //Active close
    }
    else
    {
        TcpHdrFIN = false;
    }

    pTcb->elapsed = TcbElapsed;
}
static void closeConnectionOut()
{        
    TcpHdrACK = true;               //Send ACK
    TcpHdrPSH = false;
    TcpHdrRST = false;
    TcpHdrSYN = false;
    TcpHdrFIN = false;
    
    TcpHdrClearOptions();
    dataLength = 0;
    
    pTcb->state  = TCB_EMPTY;
}

int TcpHandleReceivedPacket(void (*traceback)(void), int sizeRx, void* pPacketRx, int* pSizeTx, void* pPacketTx, int type, void* pCachedRemIp)
{
    TcpHdrRead(pPacketRx);
    
    dataLength =   sizeRx - TcpHdrSize;
    maxMss     = *pSizeTx - TcpHdrSize;
        
    doTrace = false;
    
    //Filter out unwanted links
    switch (TcpHdrDstPort)
    {
        case 80:
            if (HttpTrace)
            {
                if (NetTraceNewLine) Log("\r\n");
                LogTime("HTTP server request\r\n");
                doTrace = true;
            }
            break;
            
        default:
            if (TcpTrace)
            {
                if (NetTraceNewLine) Log("\r\n");
                LogTimeF("TCP unknown port %d\r\n", TcpHdrDstPort);
                if (NetTraceStack) traceback();
            }
            return DO_NOTHING; //Ignore unknown ports
    }
    
    //Get the Transmission Control Block
    pTcb = TcbGetExisting(TcpHdrSrcPort);
    if (!pTcb)  pTcb = TcbGetEmpty();
    if (!pTcb)
    {
        if (TcpTrace)
        {
            if (NetTraceNewLine) Log("\r\n");
            LogTime("TCP no more tcbs are available\r\n");
            if (NetTraceStack) traceback();
        }
        return DO_NOTHING; //Bomb out if no more tcbs are available
    }

    //Handle request to reset
    if (TcpHdrRST)
    {
        if (TcpTrace)
        {
            if (NetTraceNewLine) Log("\r\n");
            LogTime("TCP received reset - resetting TCB\r\n");
            if (NetTraceStack) traceback();
        }
        pTcb->state  = TCB_EMPTY; //Reset connection
        return DO_NOTHING;        //Don't reply
    }
    
    //Handle request to synchronise
    if (TcpHdrSYN)
    {
        pTcb->recvIsn   = TcpHdrSeqNum;
        pTcb->sendIsn   = TcbGetIsn();
        pTcb->recdBytes = 0;
        pTcb->sentBytes = 0;
    }
    else
    {
        pTcb->recdBytes = TcpHdrSeqNum - pTcb->recvIsn;
        pTcb->sentBytes = TcpHdrAckNum - pTcb->sendIsn;
    }
        
    if (doTrace && NetTraceStack) traceback(); //This will already include the TCP header
    
    if (TcpHdrSYN) pTcb->recdBytes += 1;            //Add one to acknowledge the SYN
                   pTcb->recdBytes += dataLength;   //Add the number of bytes received
    if (TcpHdrFIN) pTcb->recdBytes += 1;            //Add one to acknowledge the FIN

    switch (pTcb->state)
    {
        case TCB_EMPTY:
            if (!TcpHdrSYN) { resetConnectionOut("received other than a SYN when connection closed"); break; }
            startConnectionIn(pPacketRx, pCachedRemIp);
            startConnectionOut(pPacketTx);
            break;
            
        case TCB_SYN_RECEIVED:
            if (dataLength) { resetConnectionOut("data received before connection established"); break; }
            if (!TcpHdrACK)       { resetConnectionOut("received other than an ACK before connection established"); break; }
            establishConnectionIn();
            return DO_NOTHING;
            
        case TCB_ESTABLISHED:
            if (!TcpHdrACK) { resetConnectionOut("received other than an ACK during established conection"); break; }
            if (TcpHdrFIN) pTcb->hadFin = true; //When reply is all sent only a passive close is needed
            handleEstablishedConnectionIn (pPacketRx);
            handleEstablishedConnectionOut(pPacketTx);
            break;
            
        case TCB_FIN_WAIT: //End of active close
            if (!TcpHdrFIN) return DO_NOTHING; //Ignore ACK to our FIN. Wait for FIN then close.
            closeConnectionOut();
            break;
            
        case TCB_ACK_WAIT: //End of passive close
            if (!TcpHdrACK) { resetConnectionOut("received other than an ACK when closing half open connection"); break; } 
            pTcb->state = TCB_EMPTY;
            return DO_NOTHING;
    }
    
    //Specify the start of the data being sent and acknowledge the data received
    TcpHdrAckNum = pTcb->recdBytes + pTcb->recvIsn;       //Set up the acknowledgement field ready to send
    TcpHdrSeqNum = pTcb->sentBytes + pTcb->sendIsn;       //Set up the start of the message before adding the bytes sent
    
    //Keep a record of where we expect the next packet send to start
    if (TcpHdrSYN) pTcb->sentBytes += 1;           //Add one to acknowledge the SYN
                   pTcb->sentBytes += dataLength;  //Record the next sequence number
    if (TcpHdrFIN) pTcb->sentBytes += 1;           //Add one to acknowledge the FIN
    
    //Swap the ports for the reply
    TcpHdrSrcPort = TcpHdrDstPort;
    TcpHdrDstPort = pTcb->port;
    
    //Specify the receive window size to not throttle
    TcpHdrWindow = 4000;
    
    //Calculate the size of the reply
    *pSizeTx = TcpHdrSize + dataLength;
        
    return ActionMakeFromDestAndTrace(UNICAST, doTrace && NetTraceStack);
}

int TcpPollForPacketToSend(int* pSize, void* pPacket, int type, void* pCachedRemIp)
{    
    int dataLength =         *pSize - TcpHdrSize;
    
    int action = DO_NOTHING;
    
    TcpHdrSrcPort = TcpHdrDstPort;
    TcpHdrDstPort = pTcb->port;
    
    *pSize =  TcpHdrSize + dataLength;
    return action;
}