Web 51 - TCP, part 1 |
|
This description is valid for versions <1.13; version 1.13 uses multiple transmit buffers and the code is therefore a little different.
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
Example of a communication between two Web51's
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:
- port 23 - used for "telnet" communication with the serial line
- port 80 - a simple WWW server listens here, allowing e.g. device configuration
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:
- ACK - TCP packet has a valid acknowledge number of received data.
- PSH - TCP packet carries application data.
- RST - Reset/refuse connection.
- SYN - TCP packet contains the initial value for data numbering.
- FIN - Terminate data transfer.
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.
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 closedConnection 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;
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 ;
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 ;
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 rcvdIf 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 setIf 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 setUnlike 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 ACKA 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;
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 bitThe 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; //urgentPointerFill 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;TCP, part 2
| Web51 description | News | FAQ | ORDER FORM | DOWNLOAD | Links |
| (c)Copyright 2000 - 2002, HW server & Radek Benedikt
Web51@HW.cz, Web51.HW.cz |
|