friendev EtherDune TCP/IP library
/home/jander/temp/etherdune/TCPSocket.cpp
Go to the documentation of this file.
1 // EtherDune TCP implementation as a NetworkService
2 // Author: Javier Peletier <jm@friendev.com>
3 // Summary: Implements the TCP protocol
4 //
5 // Copyright (c) 2015 All Rights Reserved, http://friendev.com
6 //
7 // This source is subject to the GPLv2 license.
8 // Please see the License.txt file for more information.
9 // All other rights reserved.
10 //
11 // THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
12 // KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
13 // IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
14 // PARTICULAR PURPOSE.
15 
16 #include "TCPSocket.h"
17 #include "Checksum.h"
18 
19 #define AC_LOGLEVEL 6
20 #include <ACLog.h>
21 ACROSS_MODULE("TCPSocket");
22 
50 void TCPSocket::onReceive(uint16_t len, const byte* data) {}
55 
57 {
58 
60  sequenceNumber = 0;
61  stateTimer = 0;
62  nextFlags.clear();
63 
64 
65 }
66 
69 // Set these properties prior to calling connect()
72 {
73 
74 #if _DEBUG
75  randomSeed((uint16_t)millis() + analogRead(A1) + analogRead(A5));
76  localPort.l = random(255);
77 #else
79 #endif
81  ackNumber = 0;
82 
83  setState(SCK_STATE_SYN_SENT, SCK_TIMEOUT_SYN_SENT);
84  sendSYN(false);
85 }
86 
92 {
93  remoteIP.u = 0;
95 }
96 
102 {
105 
106  ackNumber = packet.tcp.sequenceNumber + 1;
107 
108  sendSYN(true);
109  setState(SCK_STATE_SYN_RECEIVED, SCK_TIMEOUT_SYN_RECEIVED);
110 
111 }
112 
121 {
122  sequenceNumber = listener.sequenceNumber;
124  accept();
125 
126 }
127 
128 void TCPSocket::prepareTCPPacket(bool options, uint16_t dataLength)
129 {
130 
131  packet.ip.totalLength = dataLength + (options ?
132  sizeof(IPHeader) + sizeof(TCPOptions) + sizeof(TCPHeader) :
133  sizeof(IPHeader) + sizeof(TCPHeader));
134 
136 
137  prepareIPPacket();
138 
141  packet.tcp.sequenceNumber = sequenceNumber;
142  packet.tcp.flags.clear();
143 
144  packet.tcp.windowSize = 512;
145  packet.tcp.acknowledgementNumber = ackNumber;
148 
149  packet.tcp.headerLength = options ? (sizeof(TCPHeader) + sizeof(TCPOptions)) / 4 : sizeof(TCPHeader) / 4;
150 
151 
152 }
153 
154 
155 void TCPSocket::sendSYN(bool ack)
156 {
157  ACTRACE("sendSYN ACK=%d",ack);
158 
159  prepareTCPPacket(true, 0);
160 
161  packet.tcp.flags.SYN = 1;
162  packet.tcp.flags.ACK = ack;
163 
164  packet.tcpOptions.option1 = 0x02;
167 
168 
170 
171  sendIPPacket(sizeof(IPHeader) + sizeof(TCPHeader) + sizeof(TCPOptions));
172 }
173 
174 
184 {
186  ACASSERT(buffer.isEmpty(), "tx buffer not empty");
187  buffer.flush();
188  onTerminate();
189 }
190 
195 {
196 
197  switch (state)
198  {
200  {
201  setState(SCK_STATE_LAST_ACK, SCK_TIMEOUT_LAST_ACK);
202  }break;
204  {
205  setState(SCK_STATE_FIN_WAIT_1, SCK_TIMEOUT_FIN_WAIT_1);
206  //nextFlags.FIN = 1;
207  }break;
208 
209  }
210 }
211 
212 
213 void TCPSocket::tick()
214 {
215 
216 
217  ACDEBUG("tick/state=%S",getStateString());
218 
219  if (stateTimer == 1) //handle timeouts
220  {
221  ACTRACE("%S state timeout", getStateString());
222 
223  switch (state)
224  {
226  case SCK_STATE_SYN_SENT:
227  case SCK_STATE_TIME_WAIT:
228  case SCK_STATE_CLOSING:
231  case SCK_STATE_LAST_ACK:
232  {
233  terminate();
234  }return;
235 
236 
237  }
238  }
239 
240  if (stateTimer>0)
241  stateTimer--;
242 
243  switch (state)
244  {
245  case SCK_STATE_SYN_SENT:
246  {
247  sendSYN(false);
248  }break;
249 
251  {
252  sendSYN(true);
253  }break;
254 
255  case SCK_STATE_CLOSING:
257  {
258  if (!buffer.isEmpty())
259  goto sck_state_established;
260  }
261 
262  case SCK_STATE_LAST_ACK:
263  {
264  nextFlags.FIN = 1;
265  }
267  {
268  nextFlags.ACK = 1;
269  }
270  case SCK_STATE_TIME_WAIT:
272  {
273  sck_state_established:
274  processOutgoingBuffer();
275  }
276 
277  }
278 
279 
280 }
281 
282 bool TCPSocket::onPacketReceived()
283 {
284  if (!(
287  state != SCK_STATE_CLOSED &&
290  ))
291  {
292  return false;
293  }
294 
295  loadAll();
296 
297 #if ENABLE_UDPTCP_RX_CHECKSUM
298 
299  if (!verifyUDPTCPChecksum())
300  {
301  ACWARN("TCP checksum error");
302  return true;// drop packet, TCP checksum error
303  }
304 
305 #endif
306 
307  ACTRACE("Header SYN=%d ACK=%d FIN=%d RST=%d", packet.tcp.flags.SYN, packet.tcp.flags.ACK, packet.tcp.flags.FIN, packet.tcp.flags.RST)
308 
309  uint32_t incomingAckNum = packet.tcp.acknowledgementNumber;
310  uint32_t incomingSeqNum = packet.tcp.sequenceNumber;
311 
312  int32_t bytesAck = (int32_t)(incomingAckNum - sequenceNumber);
313  int32_t bytesReceived;
314 
315  ACTRACE("incomingAck=%lu localSeqNum=%lu bytesAck=%ld incomingSeqNum=%lu localAckNum=%lu",
316  incomingAckNum, sequenceNumber, bytesAck, incomingSeqNum, ackNumber);
317 
318  if (packet.tcp.flags.RST)
319  {
320  if(state != SCK_STATE_LISTEN)
321  terminate();
322 
323  return true;
324  }
325 
326  if (bytesAck > 0)
327  sequenceNumber += bytesAck;
328 
329 
331  {
332  ackNumber = incomingSeqNum + 1;
334  nextFlags.ACK = 1;
335  onConnect();
336  return true;
337  }
338 
340  {
342  return true;
343  }
344 
346  {
347  if (!packet.tcp.flags.ACK)
348  return true;
349 
351  bytesAck--; // count one less byte (SYN counts as 1 "fake" byte)
352  onConnect();
353  }
354 
355  releaseWindow(bytesAck);
356 
357  if (ackNumber != incomingSeqNum)
358  {
359  ACDEBUG("dropped packet out of sequence.");
360  nextFlags.ACK = 1;
361  return true;
362  }
363 
364  int16_t headerLength = sizeof(IPHeader) + packet.tcp.headerLength * 4;
365  bytesReceived = packet.ip.totalLength - headerLength;
366  ackNumber += bytesReceived;
367  ACTRACE("bytesReceived=%ld", bytesReceived);
368 
369 
370 
371  if (bytesReceived > 0) // do not send an ACK if this was a packet with no data
372  {
373  nextFlags.ACK = 1;
374  int16_t slen = min(bytesReceived, (int16_t)(sizeof(EthBuffer) - sizeof(EthernetHeader)) - headerLength);
375 
376  if (slen > 0)
377  onReceive((uint16_t)slen, packet.raw + sizeof(EthernetHeader) + headerLength);
378  }
379 
380 
381  if (packet.tcp.flags.FIN)
382  {
383  ackNumber++;
384  }
385 
386 
387  switch (state)
388  {
390  {
391 
392  if (packet.tcp.flags.FIN)
393  {
395  onClose();
396  }
397 
398 
399  }break;
400 
401 
403  {
404  if (bytesAck == 1 && buffer.isEmpty())
405  {
406  if (packet.tcp.flags.FIN)
407  {
408  setState(SCK_STATE_TIME_WAIT, SCK_TIMEOUT_TIME_WAIT);
409  onClose();
410  }
411  else
412  setState(SCK_STATE_FIN_WAIT_2, SCK_TIMEOUT_FIN_WAIT_2);
413  }
414  else
415  {
416  if (packet.tcp.flags.FIN)
417  {
418  setState(SCK_STATE_CLOSING, SCK_TIMEOUT_CLOSING);
419  nextFlags.ACK = 1;
420  onClose();
421  }
422  }
423 
424  }break;
425  case SCK_STATE_CLOSING:
426  {
427  if (bytesAck == 1 && buffer.isEmpty())
428  setState(SCK_STATE_TIME_WAIT, SCK_TIMEOUT_TIME_WAIT);
429  else
430  {
431  nextFlags.ACK = 1;
432  }
433 
434  }break;
435 
436  //case SCK_STATE_TIME_WAIT:
438  {
439  if (packet.tcp.flags.FIN)
440  {
441  setState(SCK_STATE_TIME_WAIT, SCK_TIMEOUT_TIME_WAIT);
442  nextFlags.ACK = 1;
443  onClose();
444  }
445 
446  }break;
447 
448  case SCK_STATE_LAST_ACK:
449  {
450  terminate();
451  }
452 
453 
454  default:
455  break;
456  }
457 
458 
459 
460  return true;
461 }
462 
463 void TCPSocket::processOutgoingBuffer()
464 {
465  ACTRACE("processOutgoingBuffer");
466  uint16_t dataLength = 0;
467  uint16_t dataChecksum = 0;
468 
469  if (!buffer.isEmpty())
470  {
471  dataLength = buffer.fillTxBuffer(sizeof(EthernetHeader) + sizeof(IPHeader) + sizeof(TCPHeader), dataChecksum);
472  nextFlags.ACK = 1;
473  }
474 
475  if (nextFlags.raw !=0)
476  {
477  prepareTCPPacket(false, dataLength);
478  packet.tcp.sequenceNumber = sequenceNumber;
479  packet.tcp.flags = nextFlags;
480  nextFlags.clear();
481 
482  ACTRACE("dataLength=%d dataChecksum=%d", dataLength, dataChecksum);
483 
484  packet.tcp.checksum.rawValue = calcTCPChecksum(false, dataLength, dataChecksum);
485 
486  sendIPPacket(sizeof(IPHeader) + sizeof(TCPHeader));
487 
488  }
489 
490 }
491 
499 {
500  nextFlags.PSH = true;
501  processOutgoingBuffer();
502 }
503 
504 void TCPSocket::releaseWindow(int32_t& bytesAck)
505 {
506 
507  while (bytesAck > 0 && !buffer.isEmpty())
508  bytesAck -= buffer.release();
509 
510  ACASSERT(bytesAck >= 0, "released too much. bytesAck=%d", bytesAck);
511 
512 }
513 
514 __FlashStringHelper* TCPSocket::getStateString()
515 {
516  const char* s;
517 
518  switch (state)
519  {
520  case SCK_STATE_CLOSED: s = PSTR("CLOSED"); break;
521  case SCK_STATE_LISTEN: s = PSTR("LISTEN"); break;
522  case SCK_STATE_SYN_SENT: s = PSTR("SYN_SENT"); break;
523  case SCK_STATE_SYN_RECEIVED: s = PSTR("SYN_RECEIVED"); break;
524  case SCK_STATE_ESTABLISHED: s = PSTR("ESTABLISHED"); break;
525  case SCK_STATE_FIN_WAIT_1: s = PSTR("FIN_WAIT_1"); break;
526  case SCK_STATE_FIN_WAIT_2: s = PSTR("FIN_WAIT_2"); break;
527  case SCK_STATE_CLOSE_WAIT: s = PSTR("CLOSE_WAIT"); break;
528  case SCK_STATE_CLOSING: s = PSTR("CLOSING"); break;
529  case SCK_STATE_LAST_ACK: s = PSTR("LAST_ACK"); break;
530  case SCK_STATE_TIME_WAIT: s = PSTR("TIME_WAIT"); break;
531  case SCK_STATE_RESOLVING: s = PSTR("RESOLVING NAME"); break;
532 
533  default:
534  s = PSTR("UNKNOWN");
535  }
536 
537  return (__FlashStringHelper*)s;
538 
539 }
uint16_t release()
Releases one fragment of data
static const uint8_t TCP_SRC_PORT_H
Indicates the most significant byte of the source port number that EtherDune will use by default when...
Definition: config.h:143
static const uint8_t SCK_STATE_FIN_WAIT_1
(both server and client) represents waiting for a connection termination request from the remote TCP...
Definition: inet.h:555
Union of all the different protocol headers and layers to help EtherDune interpret or build packet...
Definition: inet.h:496
nint16_t localPort
local TCP or UDP port
Definition: Socket.h:54
virtual void onConnectRequest()
Called when a listening socket receives a connection request.
Definition: TCPSocket.cpp:44
uint8_t RST
Reset the connection.
Definition: inet.h:334
static const uint8_t SCK_STATE_LAST_ACK
(both server and client) represents waiting for an acknowledgment of the connection termination reque...
Definition: inet.h:559
nint16_t etherType
Protocol type, e.g.
Definition: inet.h:461
static const uint16_t ETHTYPE_IP
Ethernet header protocol type for IP.
Definition: inet.h:578
nint16_t option1_value
option value
Definition: inet.h:383
IPAddress sourceIP
Source address.
Definition: inet.h:320
uint32_t u
access the IP as a uint32_t for convenience
Definition: inet.h:184
ACROSS_MODULE("TCPSocket")
uint8_t raw[566]
Definition: inet.h:537
nint32_t acknowledgementNumber
if the ACK flag is set then the value of this field is the next sequence number that the receiver is ...
Definition: inet.h:358
virtual void onConnect()
Fires when the socket connection is established.
Definition: TCPSocket.cpp:39
void prepareIPPacket()
Definition: Socket.cpp:27
static EthBuffer packet
in-memory packet buffer currently being processed.
void push()
Sets the PSH TCP flag and also sends data in the outgoing buffer immediately.
Definition: TCPSocket.cpp:498
static const uint8_t SCK_STATE_FIN_WAIT_2
(both server and client) represents waiting for a connection termination request from the remote TCP...
Definition: inet.h:556
void setState(uint8_t newState, uint8_t timeout)
Changes to a new state
Definition: Stateful.cpp:28
nint16_t checksum
The 16-bit checksum field is used for error-checking of the header and data.
Definition: inet.h:373
nint16_t remotePort
remote TCP or UDP port
Definition: Socket.h:53
uint16_t rawValue
provides low level access to the memory containing the network-order integer
Definition: inet.h:61
nint16_t destinationPort
identifies the receiving port
Definition: inet.h:356
uint8_t headerLength
Definition: inet.h:366
void listen()
Starts listening on the local port indicated by the localPort property.
Definition: TCPSocket.cpp:91
static bool verifyUDPTCPChecksum()
Verifies if the UDP or TCP checksum of the current packet is correct.
Definition: Socket.cpp:205
nint16_t urgentPointer
if the URG flag is set, then this 16-bit field is an offset from the sequence number indicating the l...
Definition: inet.h:374
Represents the header of an Ethernet frame.
Definition: inet.h:457
static const uint8_t SCK_STATE_SYN_RECEIVED
(server) represents waiting for a confirming connection request acknowledgment after having both rece...
Definition: inet.h:553
static const uint8_t SCK_STATE_CLOSED
(both server and client) represents no connection state at all.
Definition: inet.h:550
uint8_t stateTimer
state timer, in ticks
Definition: Stateful.h:34
uint8_t raw
byte-wise access to all flags
Definition: inet.h:341
SharedBuffer buffer
output buffer for this socket
Definition: Socket.h:42
static const uint8_t SCK_STATE_SYN_SENT
(client) represents waiting for a matching connection request after having sent a connection request...
Definition: inet.h:552
static const uint8_t SCK_STATE_RESOLVING
IP Address is being resolved.
Definition: inet.h:561
uint8_t protocol
Protocol.
Definition: inet.h:318
void flush()
Releases all data in this buffer, freeing up space.
IP header.
Definition: inet.h:296
static const uint8_t SCK_STATE_TIME_WAIT
(either server or client) represents waiting for enough time to pass to be sure the remote TCP receiv...
Definition: inet.h:560
void connect()
Initiates a TCP connection to remoteIP and remotePort.
Definition: TCPSocket.cpp:71
nint16_t windowSize
The size of the receive window, which specifies the number of window size units (by default...
Definition: inet.h:372
TCP Header data structure.
Definition: inet.h:353
static const uint16_t TCP_MAXIMUM_SEGMENT_SIZE
Maximum size of payload accepted by the library.
Definition: config.h:150
uint8_t state
state code
Definition: Stateful.h:35
uint8_t option1_length
option length
Definition: inet.h:382
Implements the TCP protocol.
Definition: TCPSocket.h:45
void clear()
Sets all flags to zero.
Definition: inet.h:346
uint8_t PSH
Push function.
Definition: inet.h:335
bool isEmpty()
Determines whether this buffer is empty
static bool sendIPPacket(uint16_t length)
Puts the current in-memory packet in the network
static uint16_t calcTCPChecksum(bool options, uint16_t dataLength, uint16_t dataChecksum)
Calculates the TCP checksum.
Definition: Socket.cpp:177
Structure to encode one 2-byte long TCP option.
Definition: inet.h:378
static const uint8_t IP_PROTO_TCP
IP header protocol type for TCP.
Definition: inet.h:571
static const uint8_t SCK_STATE_LISTEN
(server) represents waiting for a connection request from any remote TCP and port.
Definition: inet.h:551
uint8_t FIN
No more data from sender.
Definition: inet.h:332
static const uint8_t SCK_STATE_ESTABLISHED
(both server and client) represents an open connection, data received can be delivered to the user...
Definition: inet.h:554
static const uint8_t SCK_STATE_CLOSING
(both server and client) represents waiting for a connection termination request acknowledgment from ...
Definition: inet.h:558
TCPFlags flags
TCP Flags.
Definition: inet.h:367
static uint8_t srcPort_L_count
self-incrementing counter for local ports.
Definition: Socket.h:43
static void loadAll()
Definition: ENC28J60.cpp:400
uint8_t l
least significant byte
Definition: inet.h:65
nint32_t sequenceNumber
has a dual role: If the SYN flag is set(1), then this is the initial sequence number.The sequence number of the actual first data byte and the acknowledged number in the corresponding ACK are then this sequence number plus 1.
Definition: inet.h:357
void terminate()
Immediately shuts down the socket and makes it available for a new task.
Definition: TCPSocket.cpp:183
virtual void onTerminate()
Called when the socket is ready to be reused.
Definition: TCPSocket.cpp:54
TCPOptions tcpOptions
Definition: inet.h:528
IPHeader ip
Definition: inet.h:506
IPAddress remoteIP
remote IP address to connect to (TCP) or send the next packet to (UDP)
Definition: Socket.h:55
nint16_t totalLength
This 16-bit field defines the entire packet (fragment) size, including header and data...
Definition: inet.h:310
uint8_t h
most significant byte
Definition: inet.h:64
uint8_t option1
Option kind.
Definition: inet.h:381
TCPHeader tcp
Definition: inet.h:527
void close()
Attempts to gracefully close a connection.
Definition: TCPSocket.cpp:194
void accept()
Accepts a connection request that has been received on the port this instance was listening on ...
Definition: TCPSocket.cpp:101
nint16_t sourcePort
identifies the sending port
Definition: inet.h:355
virtual void onReceive(uint16_t len, const byte *data)
Called for each data packet received
Definition: TCPSocket.cpp:50
EthernetHeader eth
Definition: inet.h:500
void zero()
sets the variable to 0.
Definition: inet.h:97
uint16_t fillTxBuffer(uint16_t dstOffset, uint16_t &checksum, uint16_t count=0xFFFF)
Copies up to count fragments to the transmit buffer via DMA, concatenating them starting at given off...
virtual void onClose()
Fires when the other party closed the incoming end of the connection because it has no more data to s...
Definition: TCPSocket.cpp:31
uint8_t ACK
Indicates that the Acknowledgment field is significant.
Definition: inet.h:336
uint8_t SYN
Synchronize sequence numbers.
Definition: inet.h:333
static const uint8_t SCK_STATE_CLOSE_WAIT
(both server and client) represents waiting for a connection termination request from the local user...
Definition: inet.h:557