Web 51 - TCP, part 2

TCP part 1  software.html  Simple http server

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

Contents

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

Processing the ACK and PSH flags

A packet with ACK and PSH flags contains data and may acknowledge received data.
TCPPshAckRcvd23:

"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 ();

Prepare the TCP/IP packet header using the "standard" method.

  //MakeTCPheader ();
  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 = 0x10;//IP type of service = Low DELAY
  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; //26 IP address of source
  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

Set default reply flags

  tcpStateFlg  = 0x5010;	//ACK, Data Offset = 5

Calculate beginning of data in the packet and their length.

  wordtemp &= 0xF000;		//Prepare Data Offset
  wordtemp >>= 10;		//Prepare Data Offset
				//shift word right (4>>32bit_data word,
				// 2<<convert_to_8bit_data, 8>>swapMSB/LSB)
  wordtemp += sizeof(tx_eth_pkt.hdr) + sizeof(tx_eth_pkt.pkt.ip.ipheader);
  data_addr = wordtemp + &rx_eth_pkt;	//start of data
  data_len = rx_eth_pkt.pkt.ip.ipheader.Len;
  data_len += sizeof(tx_eth_pkt.hdr);
  data_len -= wordtemp;		//sub TCP header length
  workreg = rx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader.Seq;
  workreg += data_len;
  workreg -= lastRcv23;

Check if the packet contains valid data (detects "wandering" or lost packets).

  if( workreg < 0) {workreg = 0; goto oldData;}
  if( workreg > 0xFFFF ) return;  //packetOutOfOrder - ignore packet
  if( (data_len  - workreg) < 0 ) return; //packetOutOfOrder - ignore packet
  data_addr += (data_len  - workreg);
  data_len = workreg;

Try to transmit valid data over the serial line.

  copy_r2s (ebuf, data_addr, sizeof(ebuf));
  data_addr += sizeof(ebuf);
  regR0 = (BYTE)(ebuf - 1);
  for ( ; data_len != 0 ; data_len-- ) {
    for ( regR0++; regR0 != (BYTE)(ebuf + sizeof(ebuf)); ) {

If the transmit buffer is full and is not emptied within a reasonable time, only a portion of the received packt is sent.

      if( xputchar (ebuf[regR0]) == 0) goto t23timeout;
    }
  }
  goto t23allsend;
t23timeout:

When only a portion of the packet is sent, change the number of acknowledged data.

  workreg -= data_len;
t23allsend:;
oldData:
  lastRcv23 += workreg;

Acknowledge data that actually made it into the serial line buffer.

  tx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader.Ack = lastRcv23;

Send data received from the serial line, if any.

  sendTelnetData ();
  return ;

TCPPshAckRcvd80:

As already mentioned, the WWW port uses a simpler implementation. It can't retransmit corrupted data; in such a case the connection is reset and the server waits for another request.

  rcvSeq80 = rx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader.Seq;
  if (rx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader.Ack != sendSeq80)
    goto noResendAvailable80; //reset connection

If the numbering is OK, prepare the TCP/IP header (similar to port 23 processing).

  //MakeTCPheader ();
  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.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; //26 IP address of source
  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

Set default reply flags. In this case: acknowledging request, sending reply, finished.

  tcpStateFlg = 0x5019;		//ACK & PSH & FIN, Data Offset = 5

Calculate beginning of the data in the packet and their length.

  wordtemp &= 0xF000;		//Prepare Data Offset
  wordtemp >>= 10;		//Prepare Data Offset
				//shift word right (4>>32bit_data word,
				// 2<<convert_to_8bit_data, 8>>swapMSB/LSB)
  wordtemp += sizeof(tx_eth_pkt.hdr) + sizeof(tx_eth_pkt.pkt.ip.ipheader);
  data_addr = wordtemp + &rx_eth_pkt;	//start of data
  data_len = rx_eth_pkt.pkt.ip.ipheader.Len;
  data_len += sizeof(tx_eth_pkt.hdr);
  data_len -= wordtemp;		//sub TCP header length

Fill in port numbers and acknowledge reception of data.

  tx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader.DP = TCPsrcPort80;
  tx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader.SP = 80;
  rcvSeq80 += data_len;
  tx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader.Ack = rcvSeq80;

Zero the unwritten data counter and set write pointer.

  unwrited = 0;
  tcpWritePointer = &tx_eth_pkt.pkt.ip.ipdata.tcp.tcpdta;

Process the WWW request.

  process_url ();

Empty the write buffer, set flags, and close the connection after transmission.

  bflush ();
  tx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader.Flags = tcpStateFlg;
  tcpState80 = 0;		//after send break connection, don't wait

Send the packet.

  goto flushPckt;

Note. Entry point flushPacket serves for sequential transmission of a WWW reply, if it exceeds one Ethernet block.

flushPacket:
  tx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader.Flags = 0x5018;//ACK & PSH, Data Offset = 5
flushPckt:
  dwordtemp.wrd.low = tcpWritePointer - &tx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader;

Number the transmitted packet

  tx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader.Seq = sendSeq80;
  dwordtemp.wrd.high = 0;

Prepare the numbering for the next packet

  sendSeq80 += dwordtemp.dwrd;

Calculate the length of the transmitted packet

  dwordtemp.wrd.low += sizeof(tx_eth_pkt.pkt.ip.ipheader) +
	      sizeof(tx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader);
  tx_eth_pkt.pkt.ip.ipheader.Len = dwordtemp.wrd.low;
  dwordtemp.wrd.low += sizeof(tx_eth_pkt.hdr);		//Ethernet length

Fill in checksums etc. and send.

  MakeTCPreplyAndChecksum ();
  xmit_frame (dwordtemp.wrd.low);

After transmission, set initial values for another packet.

  tcpWritePointer = &tx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader;
  tx_eth_pkt.pkt.ip.ipheader.CkSum = 0;		//header checksum
  return;

Example of communication between two Web51's

The example details a situation where the 192.168.0.78 unit is enabled for automatic establishing of connection. The first unit (.78) has a terminal connected to its serial line. A message for the other unit is entered manually on this terminal. The second unit (.77) has a loopback (connector with bridged pins 2-3 and 7-8) attached to its serial line. The resulting communication recorded by TCPDUMP on a Linux box looks like this:
Besides the IP address, we need to know the other unit's MAC address. So, we'll send an ARP request.
14:55:11.342768 arp who-has 192.168.0.77 tell 192.168.0.78

The other unit replies with its MAC address.

14:55:11.347234 arp reply 192.168.0.77 is-at 0:0:e8:ee:9:f5

After determining the MAC, let's start the "numbering plan" negotiation using SYN packets.

14:55:11.400890 192.168.0.78.telnet > 192.168.0.77.telnet:
  S 1080189804:1080189804(0) win 8192 <mss 1460>
14:55:11.411816 411816 192.168.0.77.telnet > 192.168.0.78.telnet:
  S 1080189804:1080189804(0) ack 1080189805 win 8192 <mss 1460>

'A' key pressed, send it.

14:55:11.460434 192.168.0.78.telnet > 192.168.0.77.telnet:
  P 1:2(1) ack 1 win 8192 [tos 0x10]

38400 Bd loopback can clearly transfer the char fast enough, it is returned in the acknowledging packet.

14:55:11.476284 192.168.0.77.telnet > 192.168.0.78.telnet:
  P 1:2(1) ack 2 win 8192 [tos 0x10]

Next keypress is not that fast, nothing is to be transmitted, therefore just an acknowledgement is sent.

14:55:11.491283 192.168.0.78.telnet > 192.168.0.77.telnet:
  . ack 2 win 8192 [tos 0x10]

'h' pressed, similar sequence results

14:55:11.833186 192.168.0.78.telnet > 192.168.0.77.telnet:
  P 2:3(1) ack 2 win 8192 [tos 0x10]
14:55:11.848471 192.168.0.77.telnet > 192.168.0.78.telnet:
  P 2:3(1) ack 3 win 8192 [tos 0x10]
14:55:11.863352 192.168.0.78.telnet > 192.168.0.77.telnet:
  . ack 3 win 8192 [tos 0x10]

'o'

14:55:12.106023 192.168.0.78.telnet > 192.168.0.77.telnet:
  P 3:4(1) ack 3 win 8192 [tos 0x10]
14:55:12.121338 192.168.0.77.telnet > 192.168.0.78.telnet:
  P 3:4(1) ack 4 win 8192 [tos 0x10]
14:55:12.136226 192.168.0.78.telnet > 192.168.0.77.telnet:
  . ack 4 win 8192 [tos 0x10]

'j'

14:55:12.378848 192.168.0.78.telnet > 192.168.0.77.telnet:
  P 4:5(1) ack 4 win 8192 [tos 0x10]
14:55:12.394170 192.168.0.77.telnet > 192.168.0.78.telnet:
  P 4:5(1) ack 5 win 8192 [tos 0x10]
14:55:12.409056 192.168.0.78.telnet > 192.168.0.77.telnet:
  . ack 5 win 8192 [tos 0x10]

'Enter' pressed, generating a 'CR' 'LF' pair

14:55:13.151318 192.168.0.78.telnet > 192.168.0.77.telnet:
  P 5:7(2) ack 5 win 8192 [tos 0x10]
14:55:13.166735 192.168.0.77.telnet > 192.168.0.78.telnet:
  P 5:7(2) ack 7 win 8192 [tos 0x10]
14:55:13.181737 192.168.0.78.telnet > 192.168.0.77.telnet:
  . ack 8 win 8192 [tos 0x10]

If nothing happens for about 50 seconds, nothing is to be sent and all data are ack'ed, the connection is reset.

14:56:03.160335 192.168.0.77.telnet > 192.168.0.78.telnet:
  R 1080189812:1080189812(0) win 8192
14:56:03.176021 192.168.0.78.telnet > 192.168.0.77.telnet:
  R 1080189811:1080189811(0) win 8192




Sponzored by LPhard Ltd. Graphics by GIMP Created by EasyPad

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