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/tcprecv.c

Committer:
andrewboyson
Date:
2018-11-20
Revision:
88:1ba13e6062a3
Parent:
86:55bc5ddac16c
Child:
89:9b765a67699b

File content as of revision 88:1ba13e6062a3:

#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 "tcpsend.h"
#include     "tcb.h"
#include     "ip4.h"
#include    "dhcp.h"
#include    "http.h"
#include     "led.h"

static void logReset(char* fmt, ...)
{
    if (TcpTrace)
    {
        if (NetTraceNewLine) Log("\r\n");
        LogTime("TCP sent RST - ");
        va_list argptr;
        va_start(argptr, fmt);
        LogV(fmt, argptr);
        Log("\r\n");
        va_end(argptr);
    }
}

static void logTraceBack(void (*traceback)(void), char* fmt, ...)
{
    if (TcpTrace)
    {
        if (NetTraceNewLine) Log("\r\n");
        va_list argptr;
        va_start(argptr, fmt);
        LogV(fmt, argptr);
        va_end(argptr);
        Log("\r\n");
        if (NetTraceStack) traceback();
    }
}

static void handleSyn(void *pPacket, int ipType, int remArIndex, int locMss, struct tcb* pTcb)
{
    //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->timeSendsBeingAcked = TcbElapsed;
    pTcb->countSendsNotAcked  = 0;
    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, struct tcb* pTcb)
{
    pTcb->window = TcpHdrWindow;
    char* pData = (char*)pPacket + TcpHdrSizeGet();
    switch (pTcb->locPort)
    {
        case 80:
            HttpHandleRequest(dataLength, pData, position, &pTcb->todo);
            break;
        default:
            break;
    }
}

int TcpHandleReceivedPacket(void (*traceback)(void), int sizeRx, void* pPacketRx, int* pSizeTx, void* pPacketTx, int ipType, int remArIndex, int locIpScope)
{
    int action = DO_NOTHING;
    bool doTrace = false;
    
    Led1Set(true);
    if (remArIndex < 0)
    {
        LogTimeF("Invalid remote AR index %d", remArIndex);
        Led1Set(false);
        return DO_NOTHING;
    }
    
    TcpHdrReadFromPacket(pPacketRx);
    
    int dataLength =   sizeRx - TcpHdrSizeGet();
    int locMss     = *pSizeTx - TcpHdrSizeGet();
    
    //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:
            logTraceBack(traceback, "TCP - unknown port %d", TcpHdrDstPort);
            Led1Set(false);
            return DO_NOTHING; //Ignore unknown ports
    }
    
    //Get the Transmission Control Block
    struct tcb* pTcb = TcbGetExisting(ipType, remArIndex, locIpScope, TcpHdrSrcPort, TcpHdrDstPort);
    if (!pTcb) pTcb = TcbGetEmpty();
    if (!pTcb) //Bomb out if no more tcbs are available
    {
        logTraceBack(traceback, "TCP - no more tcbs are available");
        Led1Set(false);
        return DO_NOTHING;
    }
    pTcb->timeLastRcvd     = TcbElapsed;
    pTcb->remArIndex       = remArIndex;
    pTcb->ipType           = ipType;
    pTcb->locIpScope       = locIpScope;
    pTcb->remPort          = TcpHdrSrcPort;
    pTcb->locPort          = TcpHdrDstPort;
    pTcb->window           = TcpHdrWindow;
    
    //Handle request to reset
    if (TcpHdrRST)
    {
        if (pTcb->state)
        {
            logTraceBack(traceback, "TCP - received reset - resetting TCB");
            pTcb->state = TCB_EMPTY;
        }
        Led1Set(false);
        return DO_NOTHING;        //Don't reply
    }
    
    //Handle request to synchronise
    if (TcpHdrSYN)
    {
        if (pTcb->state)
        {
            logReset("received a SYN on port %d when connection open", TcpHdrSrcPort);
            pTcb->state = TCB_EMPTY;
            action = TcpSendReset(pSizeTx, pPacketTx, pTcb);
            Led1Set(false);
            return action;
        }
        else
        {
            handleSyn(pPacketRx, ipType, remArIndex, locMss, pTcb);
        }
    }
    
    //Calculate the sequence length of the received packet
    int seqLengthRcvd = 0;
    if (TcpHdrSYN) seqLengthRcvd += 1;          //Add one to acknowledge the SYN
                   seqLengthRcvd += dataLength; //Add the number of bytes received
    if (TcpHdrFIN) seqLengthRcvd += 1;          //Add one to acknowledge the FIN
    
    /*RFC793 p36 If the connection does not exist (CLOSED) then a reset is sent
    in response to any incoming segment except another reset.
    If the incoming segment has an ACK field, the reset takes its sequence number from the ACK field of the segment,
    otherwise                                 the reset has sequence number zero
    and
    the ACK field is set to the sum of the sequence number and segment length of the incoming segment.
    The connection remains in the CLOSED state.
    In TcpSendReset 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
    */
    if (!TcpHdrSYN && !pTcb->state)
    {
        pTcb->remIsn           = 0;
        pTcb->locIsn           = 0;
        pTcb->bytesRcvdFromRem = 0;
        pTcb->bytesAckdByRem   = 0;
        
        pTcb->bytesSentToRem   = TcpHdrACK ? TcpHdrAckNum : 0; //Seq number
        pTcb->bytesAckdToRem   = TcpHdrSeqNum + seqLengthRcvd; //Ack number
        logReset("non SYN packet received on a closed connection");
        pTcb->state = TCB_EMPTY;
        action = TcpSendReset(pSizeTx, pPacketTx, pTcb);
        Led1Set(false);
        return action;
    }
    
    //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;
        pTcb->countSendsNotAcked  = 0;
    }

    //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
    acknowledgment segment containing the current send-sequence number
    and an acknowledgment indicating the next sequence number expected
    to be received, and the connection remains in the same state.*/
    uint32_t seqRcvdFromRem = TcpHdrSeqNum - pTcb->remIsn;
    if (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);
        }
        action = TcpResendLastAck(pSizeTx, pPacketTx, pTcb);
        Led1Set(false);
        return action;
    }
    Led2Set(true);
    //Handle FIN
    if (TcpHdrFIN) pTcb->rcvdFin = true; //When reply is all sent only a passive close is needed
        
    if (doTrace && NetTraceStack) traceback(); //This will already include the TCP header
    
    //Record the number of bytes received from the remote host
    pTcb->bytesRcvdFromRem += seqLengthRcvd;

    switch (pTcb->state) //This is the state of the connection BEFORE this packet arrived
    {
        case TCB_EMPTY:
            pTcb->state = TCB_SYN_RECEIVED;
            break;
            
        case TCB_SYN_RECEIVED:
            if (dataLength)
            {
                logReset("data received before connection established");
                pTcb->state = TCB_EMPTY;
                action = TcpSendReset(pSizeTx, pPacketTx, pTcb);
                Led1Set(false);
                Led2Set(false);
                return action;
            }
            pTcb->state = TCB_ESTABLISHED;
            break;
            
        case TCB_ESTABLISHED:
            if (dataLength) handleReceivedData (pPacketRx, dataLength, seqRcvdFromRem - 1, pTcb);
            if (pTcb->sentFin)
            {
                pTcb->state = pTcb->rcvdFin ? TCB_EMPTY : TCB_CLOSE_FIN_WAIT;
            }
            break;
            
        case TCB_CLOSE_FIN_WAIT: //End of active close
            if (TcpHdrFIN)
            {
                pTcb->state = TCB_EMPTY;//Ignore ACK to our FIN. Wait for FIN then close.
            }
            break;
            
    }
    
    action = TcpSend(pSizeTx, pPacketTx, pTcb);
    Led1Set(false);
    Led2Set(false);

    return action;
}