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@80:4ef1500fca1d, 2018-11-15 (annotated)
- Committer:
- andrewboyson
- Date:
- Thu Nov 15 16:55:29 2018 +0000
- Revision:
- 80:4ef1500fca1d
- Parent:
- 79:f50e02fb5c94
- Child:
- 81:50bfdd512f23
Used local ip scope to determine the local address used in TCP.
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
andrewboyson | 74:c3756bfa960e | 1 | #include <stdint.h> |
andrewboyson | 74:c3756bfa960e | 2 | #include <stdbool.h> |
andrewboyson | 75:603b10404183 | 3 | #include <stdarg.h> |
andrewboyson | 74:c3756bfa960e | 4 | |
andrewboyson | 74:c3756bfa960e | 5 | #include "log.h" |
andrewboyson | 74:c3756bfa960e | 6 | #include "net.h" |
andrewboyson | 74:c3756bfa960e | 7 | #include "action.h" |
andrewboyson | 74:c3756bfa960e | 8 | #include "tcp.h" |
andrewboyson | 74:c3756bfa960e | 9 | #include "tcphdr.h" |
andrewboyson | 74:c3756bfa960e | 10 | #include "tcpsend.h" |
andrewboyson | 74:c3756bfa960e | 11 | #include "tcb.h" |
andrewboyson | 74:c3756bfa960e | 12 | #include "ip4.h" |
andrewboyson | 74:c3756bfa960e | 13 | #include "dhcp.h" |
andrewboyson | 74:c3756bfa960e | 14 | #include "http.h" |
andrewboyson | 74:c3756bfa960e | 15 | #include "led.h" |
andrewboyson | 74:c3756bfa960e | 16 | |
andrewboyson | 74:c3756bfa960e | 17 | //Payload variables |
andrewboyson | 74:c3756bfa960e | 18 | static struct tcb* pTcb; |
andrewboyson | 74:c3756bfa960e | 19 | |
andrewboyson | 78:9d8fc88df405 | 20 | static void logReset(char* fmt, ...) |
andrewboyson | 74:c3756bfa960e | 21 | { |
andrewboyson | 78:9d8fc88df405 | 22 | if (TcpTrace) |
andrewboyson | 78:9d8fc88df405 | 23 | { |
andrewboyson | 78:9d8fc88df405 | 24 | if (NetTraceNewLine) Log("\r\n"); |
andrewboyson | 79:f50e02fb5c94 | 25 | LogTime("TCP sent RST - "); |
andrewboyson | 78:9d8fc88df405 | 26 | va_list argptr; |
andrewboyson | 78:9d8fc88df405 | 27 | va_start(argptr, fmt); |
andrewboyson | 78:9d8fc88df405 | 28 | LogV(fmt, argptr); |
andrewboyson | 78:9d8fc88df405 | 29 | Log("\r\n"); |
andrewboyson | 78:9d8fc88df405 | 30 | va_end(argptr); |
andrewboyson | 78:9d8fc88df405 | 31 | } |
andrewboyson | 74:c3756bfa960e | 32 | } |
andrewboyson | 74:c3756bfa960e | 33 | |
andrewboyson | 75:603b10404183 | 34 | static void logTraceBack(void (*traceback)(void), char* fmt, ...) |
andrewboyson | 75:603b10404183 | 35 | { |
andrewboyson | 75:603b10404183 | 36 | if (TcpTrace) |
andrewboyson | 75:603b10404183 | 37 | { |
andrewboyson | 75:603b10404183 | 38 | if (NetTraceNewLine) Log("\r\n"); |
andrewboyson | 75:603b10404183 | 39 | va_list argptr; |
andrewboyson | 75:603b10404183 | 40 | va_start(argptr, fmt); |
andrewboyson | 75:603b10404183 | 41 | LogV(fmt, argptr); |
andrewboyson | 75:603b10404183 | 42 | va_end(argptr); |
andrewboyson | 75:603b10404183 | 43 | Log("\r\n"); |
andrewboyson | 75:603b10404183 | 44 | if (NetTraceStack) traceback(); |
andrewboyson | 75:603b10404183 | 45 | } |
andrewboyson | 75:603b10404183 | 46 | } |
andrewboyson | 75:603b10404183 | 47 | |
andrewboyson | 79:f50e02fb5c94 | 48 | static void handleSyn(void *pPacket, int ipType, int remArIndex, int locMss) |
andrewboyson | 74:c3756bfa960e | 49 | { |
andrewboyson | 74:c3756bfa960e | 50 | //Get the MSS to use for sends - it is the lower of the MSS advertised by the remote host and our local MSS |
andrewboyson | 74:c3756bfa960e | 51 | int remMss = TcpHdrMssGet(); |
andrewboyson | 74:c3756bfa960e | 52 | pTcb->remMss = remMss ? remMss : 536; //default MSS for IPv4 [576 - 20(TCP) - 20(IP)]; |
andrewboyson | 74:c3756bfa960e | 53 | if (pTcb->remMss > locMss) pTcb->remMss = locMss; |
andrewboyson | 74:c3756bfa960e | 54 | |
andrewboyson | 79:f50e02fb5c94 | 55 | pTcb->timeSendsBeingAcked = TcbElapsed; |
andrewboyson | 79:f50e02fb5c94 | 56 | pTcb->rcvdFin = false; |
andrewboyson | 79:f50e02fb5c94 | 57 | pTcb->sentFin = false; |
andrewboyson | 79:f50e02fb5c94 | 58 | pTcb->todo = 0; |
andrewboyson | 79:f50e02fb5c94 | 59 | pTcb->remIsn = TcpHdrSeqNum; |
andrewboyson | 79:f50e02fb5c94 | 60 | pTcb->locIsn = TcbGetIsn(); |
andrewboyson | 79:f50e02fb5c94 | 61 | pTcb->bytesRcvdFromRem = 0; |
andrewboyson | 79:f50e02fb5c94 | 62 | pTcb->bytesAckdByRem = 0; |
andrewboyson | 79:f50e02fb5c94 | 63 | pTcb->bytesAckdToRem = 0; |
andrewboyson | 79:f50e02fb5c94 | 64 | pTcb->bytesSentToRem = 0; |
andrewboyson | 74:c3756bfa960e | 65 | } |
andrewboyson | 75:603b10404183 | 66 | static void handleReceivedData(void* pPacket, int dataLength, uint32_t position) |
andrewboyson | 74:c3756bfa960e | 67 | { |
andrewboyson | 74:c3756bfa960e | 68 | pTcb->window = TcpHdrWindow; |
andrewboyson | 74:c3756bfa960e | 69 | char* pData = (char*)pPacket + TcpHdrSizeGet(); |
andrewboyson | 74:c3756bfa960e | 70 | switch (pTcb->locPort) |
andrewboyson | 74:c3756bfa960e | 71 | { |
andrewboyson | 74:c3756bfa960e | 72 | case 80: |
andrewboyson | 75:603b10404183 | 73 | HttpHandleRequest(dataLength, pData, position, &pTcb->todo); |
andrewboyson | 74:c3756bfa960e | 74 | break; |
andrewboyson | 74:c3756bfa960e | 75 | default: |
andrewboyson | 74:c3756bfa960e | 76 | break; |
andrewboyson | 74:c3756bfa960e | 77 | } |
andrewboyson | 74:c3756bfa960e | 78 | } |
andrewboyson | 74:c3756bfa960e | 79 | |
andrewboyson | 80:4ef1500fca1d | 80 | int TcpHandleReceivedPacket(void (*traceback)(void), int sizeRx, void* pPacketRx, int* pSizeTx, void* pPacketTx, int ipType, int remArIndex, int locIpScope) |
andrewboyson | 74:c3756bfa960e | 81 | { |
andrewboyson | 78:9d8fc88df405 | 82 | if (remArIndex < 0) |
andrewboyson | 78:9d8fc88df405 | 83 | { |
andrewboyson | 78:9d8fc88df405 | 84 | LogTimeF("Invalid remote AR index %d", remArIndex); |
andrewboyson | 78:9d8fc88df405 | 85 | return DO_NOTHING; |
andrewboyson | 78:9d8fc88df405 | 86 | } |
andrewboyson | 78:9d8fc88df405 | 87 | |
andrewboyson | 74:c3756bfa960e | 88 | TcpHdrReadFromPacket(pPacketRx); |
andrewboyson | 74:c3756bfa960e | 89 | |
andrewboyson | 74:c3756bfa960e | 90 | int dataLength = sizeRx - TcpHdrSizeGet(); |
andrewboyson | 74:c3756bfa960e | 91 | int locMss = *pSizeTx - TcpHdrSizeGet(); |
andrewboyson | 74:c3756bfa960e | 92 | |
andrewboyson | 74:c3756bfa960e | 93 | TcpDoTrace = false; |
andrewboyson | 74:c3756bfa960e | 94 | |
andrewboyson | 74:c3756bfa960e | 95 | //Filter out unwanted links |
andrewboyson | 74:c3756bfa960e | 96 | switch (TcpHdrDstPort) |
andrewboyson | 74:c3756bfa960e | 97 | { |
andrewboyson | 74:c3756bfa960e | 98 | case 80: |
andrewboyson | 74:c3756bfa960e | 99 | if (HttpTrace) |
andrewboyson | 74:c3756bfa960e | 100 | { |
andrewboyson | 74:c3756bfa960e | 101 | if (NetTraceNewLine) Log("\r\n"); |
andrewboyson | 74:c3756bfa960e | 102 | LogTime("HTTP server request\r\n"); |
andrewboyson | 74:c3756bfa960e | 103 | TcpDoTrace = true; |
andrewboyson | 74:c3756bfa960e | 104 | } |
andrewboyson | 74:c3756bfa960e | 105 | break; |
andrewboyson | 74:c3756bfa960e | 106 | |
andrewboyson | 74:c3756bfa960e | 107 | default: |
andrewboyson | 79:f50e02fb5c94 | 108 | logTraceBack(traceback, "TCP - unknown port %d", TcpHdrDstPort); |
andrewboyson | 74:c3756bfa960e | 109 | return DO_NOTHING; //Ignore unknown ports |
andrewboyson | 74:c3756bfa960e | 110 | } |
andrewboyson | 74:c3756bfa960e | 111 | |
andrewboyson | 76:17534bde28d3 | 112 | //Get the Transmission Control Block |
andrewboyson | 80:4ef1500fca1d | 113 | pTcb = TcbGetExisting(ipType, remArIndex, locIpScope, TcpHdrSrcPort, TcpHdrSrcPort); |
andrewboyson | 76:17534bde28d3 | 114 | if (!pTcb) pTcb = TcbGetEmpty(); |
andrewboyson | 76:17534bde28d3 | 115 | if (!pTcb) //Bomb out if no more tcbs are available |
andrewboyson | 76:17534bde28d3 | 116 | { |
andrewboyson | 79:f50e02fb5c94 | 117 | logTraceBack(traceback, "TCP - no more tcbs are available"); |
andrewboyson | 76:17534bde28d3 | 118 | return DO_NOTHING; |
andrewboyson | 76:17534bde28d3 | 119 | } |
andrewboyson | 79:f50e02fb5c94 | 120 | pTcb->timeLastRcvd = TcbElapsed; |
andrewboyson | 76:17534bde28d3 | 121 | pTcb->remArIndex = remArIndex; |
andrewboyson | 76:17534bde28d3 | 122 | pTcb->ipType = ipType; |
andrewboyson | 76:17534bde28d3 | 123 | pTcb->remPort = TcpHdrSrcPort; |
andrewboyson | 76:17534bde28d3 | 124 | pTcb->locPort = TcpHdrDstPort; |
andrewboyson | 76:17534bde28d3 | 125 | pTcb->window = TcpHdrWindow; |
andrewboyson | 75:603b10404183 | 126 | |
andrewboyson | 74:c3756bfa960e | 127 | //Handle request to reset |
andrewboyson | 74:c3756bfa960e | 128 | if (TcpHdrRST) |
andrewboyson | 74:c3756bfa960e | 129 | { |
andrewboyson | 76:17534bde28d3 | 130 | if (pTcb->state) |
andrewboyson | 74:c3756bfa960e | 131 | { |
andrewboyson | 79:f50e02fb5c94 | 132 | logTraceBack(traceback, "TCP - received reset - resetting TCB"); |
andrewboyson | 75:603b10404183 | 133 | pTcb->state = TCB_EMPTY; |
andrewboyson | 74:c3756bfa960e | 134 | } |
andrewboyson | 74:c3756bfa960e | 135 | return DO_NOTHING; //Don't reply |
andrewboyson | 74:c3756bfa960e | 136 | } |
andrewboyson | 74:c3756bfa960e | 137 | |
andrewboyson | 74:c3756bfa960e | 138 | //Handle request to synchronise |
andrewboyson | 74:c3756bfa960e | 139 | if (TcpHdrSYN) |
andrewboyson | 74:c3756bfa960e | 140 | { |
andrewboyson | 76:17534bde28d3 | 141 | if (pTcb->state) |
andrewboyson | 75:603b10404183 | 142 | { |
andrewboyson | 78:9d8fc88df405 | 143 | logReset("received a SYN on port %d when connection open", TcpHdrSrcPort); |
andrewboyson | 75:603b10404183 | 144 | pTcb->state = TCB_EMPTY; |
andrewboyson | 75:603b10404183 | 145 | return TcpSendReset(pSizeTx, pPacketTx, pTcb); |
andrewboyson | 75:603b10404183 | 146 | } |
andrewboyson | 76:17534bde28d3 | 147 | else |
andrewboyson | 75:603b10404183 | 148 | { |
andrewboyson | 79:f50e02fb5c94 | 149 | handleSyn(pPacketRx, ipType, remArIndex, locMss); |
andrewboyson | 75:603b10404183 | 150 | } |
andrewboyson | 74:c3756bfa960e | 151 | } |
andrewboyson | 74:c3756bfa960e | 152 | |
andrewboyson | 76:17534bde28d3 | 153 | //Calculate the sequence length of the received packet |
andrewboyson | 76:17534bde28d3 | 154 | int seqLengthRcvd = 0; |
andrewboyson | 76:17534bde28d3 | 155 | if (TcpHdrSYN) seqLengthRcvd += 1; //Add one to acknowledge the SYN |
andrewboyson | 76:17534bde28d3 | 156 | seqLengthRcvd += dataLength; //Add the number of bytes received |
andrewboyson | 76:17534bde28d3 | 157 | if (TcpHdrFIN) seqLengthRcvd += 1; //Add one to acknowledge the FIN |
andrewboyson | 76:17534bde28d3 | 158 | |
andrewboyson | 76:17534bde28d3 | 159 | /*RFC793 p36 If the connection does not exist (CLOSED) then a reset is sent |
andrewboyson | 76:17534bde28d3 | 160 | in response to any incoming segment except another reset. |
andrewboyson | 76:17534bde28d3 | 161 | If the incoming segment has an ACK field, the reset takes its sequence number from the ACK field of the segment, |
andrewboyson | 76:17534bde28d3 | 162 | otherwise the reset has sequence number zero |
andrewboyson | 76:17534bde28d3 | 163 | and |
andrewboyson | 76:17534bde28d3 | 164 | the ACK field is set to the sum of the sequence number and segment length of the incoming segment. |
andrewboyson | 76:17534bde28d3 | 165 | The connection remains in the CLOSED state. |
andrewboyson | 76:17534bde28d3 | 166 | In TcpSendReset TcpHdrAckNum = pTcb->bytesAckdToRem + pTcb->remIsn; //Set up the acknowledgement field ready to send |
andrewboyson | 76:17534bde28d3 | 167 | TcpHdrSeqNum = pTcb->bytesSentToRem + pTcb->locIsn; //Set up the start of the message before adding the bytes sent |
andrewboyson | 76:17534bde28d3 | 168 | */ |
andrewboyson | 76:17534bde28d3 | 169 | if (!TcpHdrSYN && !pTcb->state) |
andrewboyson | 76:17534bde28d3 | 170 | { |
andrewboyson | 76:17534bde28d3 | 171 | pTcb->remIsn = 0; |
andrewboyson | 76:17534bde28d3 | 172 | pTcb->locIsn = 0; |
andrewboyson | 76:17534bde28d3 | 173 | pTcb->bytesRcvdFromRem = 0; |
andrewboyson | 76:17534bde28d3 | 174 | pTcb->bytesAckdByRem = 0; |
andrewboyson | 76:17534bde28d3 | 175 | |
andrewboyson | 76:17534bde28d3 | 176 | pTcb->bytesSentToRem = TcpHdrACK ? TcpHdrAckNum : 0; //Seq number |
andrewboyson | 76:17534bde28d3 | 177 | pTcb->bytesAckdToRem = TcpHdrSeqNum + seqLengthRcvd; //Ack number |
andrewboyson | 78:9d8fc88df405 | 178 | logReset("non SYN packet received on a closed connection"); |
andrewboyson | 78:9d8fc88df405 | 179 | pTcb->state = TCB_EMPTY; |
andrewboyson | 76:17534bde28d3 | 180 | return TcpSendReset(pSizeTx, pPacketTx, pTcb); |
andrewboyson | 76:17534bde28d3 | 181 | } |
andrewboyson | 76:17534bde28d3 | 182 | |
andrewboyson | 79:f50e02fb5c94 | 183 | //Check if the acks of bytes sent has progressed and reset the timer |
andrewboyson | 79:f50e02fb5c94 | 184 | uint32_t ackRcvdFromRem = TcpHdrAckNum - pTcb->locIsn; |
andrewboyson | 79:f50e02fb5c94 | 185 | if (ackRcvdFromRem > pTcb->bytesAckdByRem) pTcb->timeSendsBeingAcked = TcbElapsed; |
andrewboyson | 79:f50e02fb5c94 | 186 | |
andrewboyson | 79:f50e02fb5c94 | 187 | //Record the number of bytes acked by the remote host |
andrewboyson | 79:f50e02fb5c94 | 188 | pTcb->bytesAckdByRem = ackRcvdFromRem; |
andrewboyson | 79:f50e02fb5c94 | 189 | |
andrewboyson | 76:17534bde28d3 | 190 | /* If the connection is in a synchronized state |
andrewboyson | 76:17534bde28d3 | 191 | any unacceptable segment (out of window sequence number or |
andrewboyson | 76:17534bde28d3 | 192 | unacceptible acknowledgment number) must elicit only an empty |
andrewboyson | 76:17534bde28d3 | 193 | acknowledgment segment containing the current send-sequence number |
andrewboyson | 76:17534bde28d3 | 194 | and an acknowledgment indicating the next sequence number expected |
andrewboyson | 76:17534bde28d3 | 195 | to be received, and the connection remains in the same state.*/ |
andrewboyson | 74:c3756bfa960e | 196 | uint32_t seqRcvdFromRem = TcpHdrSeqNum - pTcb->remIsn; |
andrewboyson | 78:9d8fc88df405 | 197 | if (seqRcvdFromRem != pTcb->bytesAckdToRem) |
andrewboyson | 78:9d8fc88df405 | 198 | { |
andrewboyson | 79:f50e02fb5c94 | 199 | //Only warn non keep-alives |
andrewboyson | 79:f50e02fb5c94 | 200 | if (seqRcvdFromRem != 0 || pTcb->bytesAckdToRem != 1) |
andrewboyson | 79:f50e02fb5c94 | 201 | { |
andrewboyson | 79:f50e02fb5c94 | 202 | logTraceBack(traceback, "TCP - resending last ACK on port %d as seq rcvd is %d and last seq ackd was %d", TcpHdrSrcPort, seqRcvdFromRem, pTcb->bytesAckdToRem); |
andrewboyson | 79:f50e02fb5c94 | 203 | } |
andrewboyson | 78:9d8fc88df405 | 204 | return TcpResendLastAck(pSizeTx, pPacketTx, pTcb); |
andrewboyson | 78:9d8fc88df405 | 205 | } |
andrewboyson | 74:c3756bfa960e | 206 | |
andrewboyson | 74:c3756bfa960e | 207 | //Handle FIN |
andrewboyson | 74:c3756bfa960e | 208 | if (TcpHdrFIN) pTcb->rcvdFin = true; //When reply is all sent only a passive close is needed |
andrewboyson | 74:c3756bfa960e | 209 | |
andrewboyson | 74:c3756bfa960e | 210 | if (TcpDoTrace && NetTraceStack) traceback(); //This will already include the TCP header |
andrewboyson | 74:c3756bfa960e | 211 | |
andrewboyson | 75:603b10404183 | 212 | //Record the number of bytes received from the remote host |
andrewboyson | 76:17534bde28d3 | 213 | pTcb->bytesRcvdFromRem += seqLengthRcvd; |
andrewboyson | 74:c3756bfa960e | 214 | |
andrewboyson | 76:17534bde28d3 | 215 | switch (pTcb->state) //This is the state of the connection BEFORE this packet arrived |
andrewboyson | 74:c3756bfa960e | 216 | { |
andrewboyson | 74:c3756bfa960e | 217 | case TCB_EMPTY: |
andrewboyson | 74:c3756bfa960e | 218 | pTcb->state = TCB_SYN_RECEIVED; |
andrewboyson | 74:c3756bfa960e | 219 | break; |
andrewboyson | 74:c3756bfa960e | 220 | |
andrewboyson | 74:c3756bfa960e | 221 | case TCB_SYN_RECEIVED: |
andrewboyson | 74:c3756bfa960e | 222 | if (dataLength) |
andrewboyson | 74:c3756bfa960e | 223 | { |
andrewboyson | 74:c3756bfa960e | 224 | logReset("data received before connection established"); |
andrewboyson | 74:c3756bfa960e | 225 | pTcb->state = TCB_EMPTY; |
andrewboyson | 75:603b10404183 | 226 | return TcpSendReset(pSizeTx, pPacketTx, pTcb); |
andrewboyson | 74:c3756bfa960e | 227 | } |
andrewboyson | 74:c3756bfa960e | 228 | pTcb->state = TCB_ESTABLISHED; |
andrewboyson | 74:c3756bfa960e | 229 | break; |
andrewboyson | 74:c3756bfa960e | 230 | |
andrewboyson | 74:c3756bfa960e | 231 | case TCB_ESTABLISHED: |
andrewboyson | 75:603b10404183 | 232 | if (dataLength) handleReceivedData (pPacketRx, dataLength, seqRcvdFromRem - 1); |
andrewboyson | 74:c3756bfa960e | 233 | if (pTcb->sentFin) |
andrewboyson | 74:c3756bfa960e | 234 | { |
andrewboyson | 77:6cb7d92c37f3 | 235 | pTcb->state = pTcb->rcvdFin ? TCB_EMPTY : TCB_CLOSE_FIN_WAIT; |
andrewboyson | 74:c3756bfa960e | 236 | } |
andrewboyson | 74:c3756bfa960e | 237 | break; |
andrewboyson | 74:c3756bfa960e | 238 | |
andrewboyson | 74:c3756bfa960e | 239 | case TCB_CLOSE_FIN_WAIT: //End of active close |
andrewboyson | 74:c3756bfa960e | 240 | if (TcpHdrFIN) |
andrewboyson | 74:c3756bfa960e | 241 | { |
andrewboyson | 74:c3756bfa960e | 242 | pTcb->state = TCB_EMPTY;//Ignore ACK to our FIN. Wait for FIN then close. |
andrewboyson | 74:c3756bfa960e | 243 | } |
andrewboyson | 74:c3756bfa960e | 244 | break; |
andrewboyson | 74:c3756bfa960e | 245 | |
andrewboyson | 74:c3756bfa960e | 246 | } |
andrewboyson | 74:c3756bfa960e | 247 | |
andrewboyson | 75:603b10404183 | 248 | return TcpSend(pSizeTx, pPacketTx, pTcb); |
andrewboyson | 74:c3756bfa960e | 249 | } |