/******************************************************************************************
tcp.c (v1.0)
-------------------------------------------------------------------------------------
This code is from the book:
"Embedded Internet: TCP/IP Basics, Implementation and Applications" by Sergio Scaglia
[Pearson Education, 2006 - ISBN: 0-32-130638-4]
This code is copyright (c) 2006 by Sergio Scaglia, and it may only be used for educational
purposes. For commercial use, please contact me at sscaglia@intramarket.com.ar
For more information and updates, please visit www.embeddedinternet.org
******************************************************************************************/
#include "type.h"
#include "stack.h"
#include "ip.h"
#include "timer.h"
#ifdef ETHERNET
#include "Ethernet.h"
#endif
#ifdef PPP_LINK
#include "PPP.h"
#endif
#include "icmp.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define TCPr ((struct tcp_hdr *)&rx_buf[sizeof(struct ip_hdr) + DATALINK_HDR_SIZE])
#define TCPt ((struct tcp_hdr *)&tx_buf[sizeof(struct ip_hdr) + DATALINK_HDR_SIZE])
#define TCP_DEF_rTOUT 30 // Re-Transmission default TimeOut (3 sec)
#define TCP_DEF_TIME_WAIT 5 // Time-Out for the TCP_SOCKET_TIME_WAIT state (0,5 sec)
#define TCP_DEF_RETRIES 2 // default retries = 2 (original segment and 2 re-transmissions
#define TCP_DEF_cTOUT 1200 // Connection default TimeOut (120 sec)
#pragma pack(2)
struct tcp_hdr
{
unsigned short srcport; // Source Port
unsigned short destport; // Destination Port
unsigned int seq; // Sequence Number
unsigned int ack; // Acknowledgement Number
char data_offset; // Data Offset
char flags; // Flags (Control bits)
unsigned short window; // Window Size
unsigned short tcpchksum; // TCP Checksum
unsigned short urgent_ptr; // Urgent Pointer
};
#pragma pack()
struct tcb
{
char state;
unsigned short local_port;
char remote_ip[4];
unsigned short remote_port;
unsigned short remote_mss;
char flags;
unsigned int snduna;
unsigned int sndnxt;
unsigned short sndwnd;
unsigned int rcvnxt;
unsigned short rcvwnd;
unsigned short rTimer;
unsigned short rTimeout;
char retries;
unsigned short cTimer;
void (*event_handler)(int,char,char*,unsigned short,unsigned short,unsigned short);
unsigned short more_data;
unsigned short position;
int (*fill_buffer) (int,unsigned short *,unsigned short *);
};
#define TCP_SOCKET_FREE 0
#define TCP_SOCKET_CLOSED 1
#define TCP_SOCKET_LISTEN 2
#define TCP_SOCKET_SYN_SENT 3
#define TCP_SOCKET_SYN_RECEIVED 4
#define TCP_SOCKET_ESTABLISHED 5
#define TCP_SOCKET_FIN_WAIT_1 6
#define TCP_SOCKET_FIN_WAIT_2 7
#define TCP_SOCKET_CLOSING 8
#define TCP_SOCKET_TIME_WAIT 9
#define TCP_SOCKET_CLOSE_WAIT 10
#define TCP_SOCKET_LAST_ACK 11
#define FIN 0x01
#define SYN 0x02
#define RST 0x04
#define PSH 0x08
#define ACK 0x10
#define URG 0x20
#define TCP_MSS TCP_DATA_MAX
unsigned short len;
unsigned short max_len;
struct tcb tcp_socket[TCP_MAX_SOCKETS];
static unsigned short last_port = 1024;
static unsigned short opt_len = 0;
static unsigned short data_len = 0;
static char tcp_options = 0;
static char data_to_ack = 0;
extern char test; // test Re-Transmissions
/***************************************/
/* TCP Private Functions */
/***************************************/
//#if debug_tcp
void tcp_display(void)
{
int i;
printf("TCP Message:\n");
printf("Source Port: %d\n", HTONS(TCPr->srcport));
printf("Destination Port: %d\n", HTONS(TCPr->destport));
printf("Sequence Number: %u\n", SWAP(TCPr->seq));
printf("Acknowledg.Number: %u\n", TCPr->ack);
printf("Data Offset: %d bytes (Options Length: %d bytes / Data Length: %d bytes)\n",
((TCPr->data_offset)>>4)*4, opt_len, data_len);
printf("Flags: ");
if (TCPr->flags & FIN)
printf("FIN ");
if (TCPr->flags & SYN)
printf("SYN ");
if (TCPr->flags & RST)
printf("RESET ");
if (TCPr->flags & PSH)
printf("PUSH ");
if (TCPr->flags & ACK)
printf("ACK ");
if (TCPr->flags & URG)
printf("URG ");
printf("\nWindow: %d\n", HTONS(TCPr->window));
printf("Checksum: %04x\n", HTONS(TCPr->tcpchksum));
printf("Urgent Pointer: %d\n", HTONS(TCPr->urgent_ptr));
printf("Data:\n");
for (i=0; i<data_len; i++)
{
printf("%c", rx_buf[TCP_DATA_START+i]);
}
printf("\n-------------------------------------------\n");
}
//#endif //debug_tcp
int tcp_validate_socket(int socket)
{
if ((socket < 1) || (socket > TCP_MAX_SOCKETS))
{
printf("TCP: Invalid Socket Error!\r\n");
return TCP_INVALID_SOCKET;
}
return 0;
}
short get_max_len(int socket)
{
int iSocket = socket-1;
if (tcp_validate_socket(socket) == TCP_INVALID_SOCKET)
return TCP_SOCKET_ERROR;
max_len = tcp_socket[iSocket].remote_mss;
if (max_len > tcp_socket[iSocket].sndwnd)
max_len = tcp_socket[iSocket].sndwnd;
if (max_len > TCP_DATA_MAX)
max_len = TCP_DATA_MAX;
return max_len;
}
void tcp_send(int socket, unsigned short len)
{
struct pseudo_hdr p_hdr;
int chksum2;
int iSocket = socket-1;
if (tcp_validate_socket(socket) == TCP_INVALID_SOCKET)
return;
if (tcp_socket[iSocket].state == TCP_SOCKET_FREE)
{
printf("TCP Send: the socket is free: %d\n", socket);
return;
}
switch (tcp_socket[iSocket].state)
{
case TCP_SOCKET_SYN_RECEIVED:
case TCP_SOCKET_SYN_SENT:
tcp_socket[iSocket].flags = SYN;
//send a MSS=1460 TCP Option
tx_buf[TCP_DATA_START] = 0x02;
tx_buf[TCP_DATA_START+1]= 0x04;
tx_buf[TCP_DATA_START+2]= TCP_MSS>>8;
tx_buf[TCP_DATA_START+3]= TCP_MSS&0xFF;
tcp_options = 4;
break;
case TCP_SOCKET_FIN_WAIT_1:
tcp_socket[iSocket].flags = FIN;
break;
}
if (len>0)
tcp_socket[iSocket].flags |= PSH;
if (tcp_socket[iSocket].state != TCP_SOCKET_SYN_SENT)
tcp_socket[iSocket].flags |= ACK;
TCPt->srcport = HTONS(tcp_socket[iSocket].local_port);
TCPt->destport = HTONS(tcp_socket[iSocket].remote_port);
TCPt->seq = SWAP(tcp_socket[iSocket].sndnxt);
TCPt->ack = SWAP(tcp_socket[iSocket].rcvnxt);
TCPt->data_offset = ((sizeof(struct tcp_hdr) + tcp_options)/4)<<4;
TCPt->flags = tcp_socket[iSocket].flags;
TCPt->window = tcp_socket[iSocket].rcvwnd;
TCPt->tcpchksum = 0;
TCPt->urgent_ptr = 0;
// Calculate checksum
memcpy(&p_hdr.srcipaddr[0], &MyIP[0], 4);
memcpy(&p_hdr.destipaddr[0], tcp_socket[iSocket].remote_ip, 4);
p_hdr.zero = 0;
p_hdr.protocol = TCP_PROTOCOL;
p_hdr.len = HTONS(len+tcp_options+20);
chksum2 = chksum16(&p_hdr.srcipaddr[0], sizeof(p_hdr), 0, 0);
chksum2 = chksum16(&TCPt->srcport,len+tcp_options+20, chksum2, 1);
TCPt->tcpchksum = HTONS(chksum2);
ip_send(TCP_PROTOCOL, tcp_socket[iSocket].remote_ip, len+tcp_options+sizeof(struct tcp_hdr));
if (tcp_socket[iSocket].flags != ACK)
tcp_socket[iSocket].rTimer = tcp_socket[iSocket].rTimeout; // Init timer
if (len>0)
{
tcp_socket[iSocket].sndnxt += len;
}
else
{
if ((TCPt->flags & SYN) || (TCPt->flags & FIN))
tcp_socket[iSocket].sndnxt++;
}
data_to_ack = 0;
tcp_socket[iSocket].flags = 0;
tcp_options = 0;
}
void tcp_resend(int socket)
{
unsigned short len=0;
int iSocket = socket-1;
if (tcp_validate_socket(socket) == TCP_INVALID_SOCKET)
return;
if (tcp_socket[iSocket].state == TCP_SOCKET_ESTABLISHED)
{
len = tcp_socket[iSocket].sndnxt - tcp_socket[iSocket].snduna;
tcp_socket[iSocket].position -= len;
tcp_socket[iSocket].more_data = tcp_socket[iSocket].fill_buffer(socke