Web 51 - TCP, part 1

ICMP  software.html  TCP part 2

This description is valid for versions <1.13; version 1.13 uses multiple transmit buffers and the code is therefore a little different.

Contents

Processing of a received TCP packet
Processing the RST flag
Processing the ACK flag (without other flags)
Processing the SYN flag
Processing the FIN flag
Processing the ACK and PSH flags cast 2
Example of a communication between two Web51's cast 2

TCP - Transmission Control Protocol

TCP - Transmission Control Protocol - is described in RFC793 (Transmission Control Protocol). TCP is an integral part of IP. TCP is a connection-oriented service; that is, the receiving side acknowleges received data and asks for retransmission if something gets lost. TCP creates a virtual, full-duplex circuit between two applications for the duration of the connection. It uses the IP protocol for its operation. Transmitted packets are numbered and contain checksums. Numbering allows for the detection of lost packets and restoring of proper ordering (IP packets may arrive in a different order than sent).

Besides the IP addresses, both ends of a connection (source and destination) are further identified by a port number. The port number is basically the address of an application within a device identified by its IP address. When establishing a connection, the source usually uses a randomly generated port number from an allowed range (usually >1024). Destination port number is usually fixed and depends on the particular application. Usual port numbers are in RFC1700 (Assigned Numbers). The example Web51 application listens on two ports:

Since TCP is a connection-oriented service, it has to take care of issues regarding connection establishing and terminating, and the acknowledging of received data. The connection must be established before any data transmission and closed afterwards. The header of each data packet contains a bit field with flags used for transmission control. More, the connection can find itself in several defined states (open/establishing/terminating/etc.) In Web51, the connection state is represented by the variable tcpState23 or tcpState80. Individual bits of this variable, together with the flags in the received packet, control the packet processing.

The TCP packet header contains the following important flags:

A typical WWW connection looks like this:

(Client)(Server)
 [tcpState80 = 0]
(SYN)[tcpState80.stateSyn] (SYN ACK)
(ACK...)[tcpState80.stateEstablished] (ACK...)
(ACK...) 

Termination of a WWW connection is extremely simplified in Web51. After all data are sent, a packet with the FIN flag set is sent, its acknowledge is not waited for.

Now, let's detail the processing of a TCP packet.

Processing of a received TCP paketu

void ProcessTCP (void) {
  switch (rx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader.DP) {

First, determine which application does the packet belong to by the port number.

    case 23:
      if(tcpState23.stateEstablished) {

If a connection is established, verify whether the packet came from the correct station.

      	if (TestSrcAddr(&IPsrcAddr23) != 0) goto TCPSendRst23; /* attack ?? */
      }
      wordtemp = rx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader.Flags;
Est23port:

Identify other station's request from the flags in the packet header.

      if( wordtemp & 0x0002 ) goto TCPSynRcvd23;		//SYN bit
      if( wordtemp & 0x0001 ) goto TCPFinRcvd23;		//FIN bit
      if( wordtemp & 0x0004 ) goto TCPRstRcvd23;		//RST bit
      if(( wordtemp & 0x0010 ) == 0 ) goto TCPRstRcvd23;	//ACK bit is NOT set
      if( tcpState23.stateEstablished != 0) {
        if(( wordtemp & 0x0008 ) == 0 ) goto TCPAckRcvd23;	//ACK bit & NOT PSH bit
        goto TCPPshAckRcvd23;					//PSH & ACK bit
      }

At this point it is certain that this is the first ACK from the other side, and therefore an acknowledge of the transmitted SYN flag as well. It should be checked whether the connection has been properly established. Web51 uses a constant magic number as the initial packet number. The transmitted SYN flag counts as one byte of data; the other side should therefore return this magic number increased by one if the connection is properly established.

Note. An implementation that uses a magic number is less secure against attacks; however, it is often used. Some network management tools can identify a TCP implementation by the magic number. It is more secure to use a random number; however, Web51 does not have enough memory nor computing power for that.

      if( rx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader.Ack != IP_MAGIC+1 )
        goto TCPSendRst23; //port 23 closed

Connection has been correctly established; store the "numbering plan" for the transmitted/received data and the IP/MAC of the other side, including the port number.

      tcpState23.stateEstablished = 1;
      lastAck23 = IP_MAGIC+1;
      lastRcv23 = rx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader.Seq;
      IPsrcAddr23 = rx_eth_pkt.pkt.ip.ipheader.SrcAddr;
      TCPsrcPort23 = rx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader.SP;
      MacSrcAddr23 = EthRcvHdr.pktSrc;

After establishing the connection, return to processing of the packet contents.

      goto Est23port; //"restart" TCP flag scanner
    case 80:

The same processing goes on for port 80 that is intended for the WWW server.

      if(tcpState80.stateEstablished) {
      	if (TestSrcAddr(&IPsrcAddr80) != 0) goto TCPSendRst80; /* attack ?? */
      }
      wordtemp = rx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader.Flags;
Est80port:
      if( wordtemp & 0x0002 ) goto TCPSynRcvd80;		//SYN bit
      if( wordtemp & 0x0001 ) goto TCPFinRcvd80;		//FIN bit
      if( wordtemp & 0x0004 ) goto TCPRstRcvd80;		//RST bit
      if(( wordtemp & 0x0010 ) == 0 ) goto TCPRstRcvd80;	//ACK bit is NOT set
      if( tcpState80.stateEstablished != 0) {
        if(( wordtemp & 0x0008 ) == 0 ) goto TCPAckRcvd80;	//ACK bit & NOT PSH bit
        goto TCPPshAckRcvd80;					//PSH & ACK bit
      }
      if( rx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader.Ack != IP_MAGIC+1 )
        goto TCPSendRst80; //port 80 closed
      tcpState80.stateEstablished = 1;
      sendSeq80 = IP_MAGIC+1;
      rcvSeq80 = rx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader.Seq;
      IPsrcAddr80 = rx_eth_pkt.pkt.ip.ipheader.SrcAddr;
      TCPsrcPort80 = rx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader.SP;
      MacSrcAddr80 = EthRcvHdr.pktSrc;
      goto Est80port; //"restart" TCP flag scanner
    default:

If the port is not "telnet" or "www", ignore the packet.

      return;
  }
  return;

Processing the RST flag

If a packet has the RST flag set, an immediate termination of the connection is requested. The termination is simple for Web51 - the state variable for the corresponding port is zeroed.
TCPRstRcvd23:
  tcpState23 = 0;
  return ;
TCPRstRcvd80:
  tcpState80 = 0;
  return ;

Processing the ACK flag (without other flags)

Packet contains no other data. It only confirms received data.
TCPAckRcvd23:

"Telnet" uses a full TCP implementation with retransmission support. When an acknowledge is received, the ack'ed data are removed from the buffer, counter of ack'ed data is stored, and the timeout counter is restarted.

  Ack23Byte ();
  return ;
TCPAckRcvd80:

WWW port uses a simplified TCP implementation without retransmission support. In case of a transmission error, the entire request needs to be repeated. However, at least the counter of ack'ed data has to be stored.

  rcvSeq80 = rx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader.Seq;
  return ;

Processing the SYN flag

The SYN flag identifies a packet containing the initial value for the "numbering plan". However, like many other implementations, Web51 does not determine the numbering from the SYN packet but rather from the first data packet.
TCPSynRcvd23:
//SYN rcvd

If a packet with SYN set and ACK unset is received, the other station wants to establish a connection. We reply with a SYN ACK packet.

  if(( wordtemp & 0x0010 ) != 0 ) goto TCPSynRcvd; //ACK is not set

If a packet with SYN and ACK set is received, it is the reply to our request for a connection. Transmission of connection requests (SYN packets) is stopped and the reply is checked. If valid, the "connection is open" flag is set and the initial packet numbering state is set.

//ACK SYN rcvd
  tcpState23.stateSynSent = 0;
  if( rx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader.Ack != IP_MAGIC+1 )
    goto TCPSendRst23; //port 23 closed
  if (TestSrcAddr(&IPsrcAddr23) != 0) goto TCPSendRst23; /* attack ?? */
  tcpState23.stateEstablished = 1;
  lastRcv23 = IP_MAGIC+1;
  lastAck23 = IP_MAGIC+1;
  return ;

Processing of received SYN packets on the WWW port is similar with one exception. Web51 does not establish outgoing www connections; reception of a SYN ACK packet signifies an error or an attack attempt.

TCPSynRcvd80:
//SYN rcvd
  if(( wordtemp & 0x0010 ) == 0 ) return; //SYN ACK is set

Unlike in "telnet" that responds with SYN ACK to every SYN packet until the first data packet is received, it is necessary to lower the probability of the other side opening multiple connections at the same time in case of WWW. Web browsers tend to open a separate connection for each embedded element in the WWW page (pictures, ...). Web51 needs to force them to use only one connection at a time. This can be achieved by limiting the number of replies to one in a given time. Web51 uses the state bit tcpState80.stateSyn that is reset in the timer handler after a certain time.

  if( tcpState80.stateEstablished != 0) return;
  if( tcpState80.stateSyn != 0) return;
  tcpState80.stateSyn = 1;		//port80 - max 1 SYN ACK / 50 ms
TCPSynRcvd:				//SYN rcv, snd SYN ACK

A SYN ACK packet is sent using the standard Web51 procedure. The ready-made contents are copied to the packet.

  //MakeSYNheader ();
  tx_eth_pkt.hdr.pktSrc = flash_my_ether;
  tx_eth_pkt.hdr.pktType = 0x0800;	//IP
  tx_eth_pkt.pkt.ip.ipheader.VerLen = 0x45; //IP ver = 4., std. header length = 5
  tx_eth_pkt.pkt.ip.ipheader.TOS = 0;	//IP type of service
  tx_eth_pkt.pkt.ip.ipheader.Len = sizeof(rx_eth_pkt.pkt.ip.ipheader) +
    sizeof(rx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader) + 4;	//packet length
  tx_eth_pkt.pkt.ip.ipheader.FragOff = 0;	//fragment offset
  tx_eth_pkt.pkt.ip.ipheader.TTL = 64; 	//time to live (in gateway hops)
  tx_eth_pkt.pkt.ip.ipheader.Proto = 6;	//protocol (ICMP=1, TCP=6, EGP=8, UDP=17)
  tx_eth_pkt.pkt.ip.ipheader.CkSum = 0;
  tx_eth_pkt.pkt.ip.ipheader.SrcAddr = flash_my_ip; //IP address of source
  tx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader.Seq = IP_MAGIC;//seqnum
  tx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader.Flags = 0x6012;//flags
  tx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader.Win = win80;	//window
  tx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader.ChkSum = 0;	//checksum
  tx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader.Urg = 0;	//urgentPointer
  tx_eth_pkt.pkt.ip.ipdata.tcp.tcpdta[0] = 2;		//MSS cmd
  tx_eth_pkt.pkt.ip.ipdata.tcp.tcpdta[1] = 4;
  tx_eth_pkt.pkt.ip.ipdata.tcp.tcpdta[2] = (mss80 >> 8);//max data length
  tx_eth_pkt.pkt.ip.ipdata.tcp.tcpdta[3] = (mss80 & 0xFF);

Fill in a reply to the offered initial numbering, port numbers of source and destination.

  tx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader.Ack =
    rx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader.Seq + 0x00000001l;
  tx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader.DP = rx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader.SP;
  tx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader.SP = rx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader.DP;

Complete checksums and send.

  MakeTCPreplyAndChecksum ();
  xmit_frame (sizeof(tx_eth_pkt.hdr) +
	      sizeof(tx_eth_pkt.pkt.ip.ipheader) +
	      sizeof(tx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader) + 4);
  return;

Processing the FIN flag

A packet with the FIN flag set indicates a request to close the connection. After sending a reply acknowledging the request, we should wait for the acknowledge of the reply (also containing a FIN asking for acknowledge); however, no wait is implemented and the connection is closed immediately (reset-like).
TCPFinRcvd23:
  tcpState23 = 0;	//close port 23
  goto TCPFinRcvd;
TCPFinRcvd80:
  tcpState80 = 0;	//close port 80
TCPFinRcvd:	//FIN bit

The FIN ACK packet is again sent in the usual Web51 way. Prepared contents are copied to the packet.

  // MakeFINheader ();
  tx_eth_pkt.hdr.pktSrc = flash_my_ether;
  tx_eth_pkt.hdr.pktType = 0x0800;	//IP
  tx_eth_pkt.pkt.ip.ipheader.VerLen = 0x45; //IP ver = 4., std. header length = 5
  tx_eth_pkt.pkt.ip.ipheader.TOS = 0;	//IP type of service
  tx_eth_pkt.pkt.ip.ipheader.Len = sizeof(rx_eth_pkt.pkt.ip.ipheader) +
    sizeof(rx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader);	//packet length
  tx_eth_pkt.pkt.ip.ipheader.FragOff = 0;	//fragment offset
  tx_eth_pkt.pkt.ip.ipheader.TTL = 64; 	//time to live (in gateway hops)
  tx_eth_pkt.pkt.ip.ipheader.Proto = 6;	//protocol (ICMP=1, TCP=6, EGP=8, UDP=17)
  tx_eth_pkt.pkt.ip.ipheader.CkSum = 0;
  tx_eth_pkt.pkt.ip.ipheader.SrcAddr = flash_my_ip; //IP address of source
  tx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader.Flags = 0x5011;//flags ACK & FIN, Data Offset = 5
  tx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader.Win = win80;	//window
  tx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader.ChkSum = 0;	//checksum
  tx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader.Urg = 0;	//urgentPointer

Fill in numbering, acknowledge FIN (counts like 1 data byte, like SYN), source port and destination port.

  tx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader.Seq = rx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader.Ack;
  tx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader.Ack =
    rx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader.Seq + 0x00000001l;
  tx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader.DP = rx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader.SP;
  tx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader.SP = rx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader.DP;

Complete with checksums and send.

  MakeTCPreplyAndChecksum ();
  xmit_frame (sizeof(tx_eth_pkt.hdr) +
	      sizeof(tx_eth_pkt.pkt.ip.ipheader) +
	      sizeof(tx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader));
  return;
Dil 2TCP, part 2




Sponzored by LPhard Ltd. Graphics by GIMP Created by EasyPad

(c)Copyright 2000 - 2002, HW server & Radek Benedikt
Web51@HW.cz, Web51.HW.cz
ICMP  Obsah  TCP part 2