Web 51 - 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.
Processing a received TCP packet
Procesing the RST flag
Processing the ACK flag (without other flags)
Processing the SYN flag
Processing the FIN flag
Processing the ACK and PSH flags
Example of communication between two Web51's
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; //urgentPointerSet default reply flags
tcpStateFlg = 0x5010; //ACK, Data Offset = 5Calculate 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 connectionIf 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; //urgentPointerSet default reply flags. In this case: acknowledging request, sending reply, finished.
tcpStateFlg = 0x5019; //ACK & PSH & FIN, Data Offset = 5Calculate 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 lengthFill 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 waitSend 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 lengthFill 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;
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.78The other unit replies with its MAC address.
14:55:11.347234 arp reply 192.168.0.77 is-at 0:0:e8:ee:9:f5After 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
| Web51 description | News | FAQ | ORDER FORM | DOWNLOAD | Links |
| (c)Copyright 2000 - 2002, HW server & Radek Benedikt
Web51@HW.cz, Web51.HW.cz |
|