www.pudn.com > TcpIpOn8051.rar > TCP.C
/*------------------------------------------------------------------------------
tcp.C
------------------------------------------------------------------------------*/
#include "public.h"
#include "ether.h"
#include "ip.h"
#include "tcp.h"
TCP_SOCK xdata tsock[nsocks];
extern mycomp xdata mypara;
extern netcard xdata sendbuffer[2];
extern ippack_senflag xdata ipsenflag[2];
void tcp_send_pack(TCP_SOCK *ts);
void tcp_newstate(TCP_SOCK *ts, TCP_STATE news);
void tcp_make_sock(TCP_SOCK *ts, BYTE flags, WORD dlen);
void tcp_reset_net(netcard *netbuf, WORD rdlen);
void tcp_reset_ts(TCP_SOCK *ts, WORD rdlen);
void tcp_sock_auto(TCP_SOCK *ts,WORD dlen,bit rxortx);
void tcp_remake_sock(TCP_SOCK *ts);
WORD tcp_tsestab_rx(TCP_SOCK *ts, tcppacket *tcp, WORD dlen);
void tcp_tsestab_tx(TCP_SOCK *ts,WORD force);
WORD tcp_upcall(TCP_SOCK *ts, CONN_STATE conn,BYTE server);
extern WORD mindata(WORD a, WORD b);
extern DWORD get_current();
extern WORD csum(void *dp, WORD count);
extern bit ip_send_pack(netcard *netbuf,BYTE bufnum,ip destip,BYTE pcol,WORD len);
extern void ip_send(netcard *netbuf,ip destip,WORD *macaddr,BYTE pcol,WORD dlen);
extern WORD buff_in(CBUFF *bp, BYTE *tdata, WORD len);
extern WORD buff_dlen(CBUFF *bp);
extern WORD buff_untriedlen(CBUFF *bp);
extern WORD buff_trylen(CBUFF *bp);
extern WORD buff_try(CBUFF *bp, BYTE *tdata, WORD maxlen);
extern void buff_setall(CBUFF *bp, DWORD start);
extern WORD buff_out(CBUFF *bp, BYTE *tdata, WORD maxlen);
extern WORD buff_instr(CBUFF *bp, char *str);
extern void print(char str[]);
extern WORD server_action(TCP_SOCK *ts, CONN_STATE conn);
extern WORD client_action(TCP_SOCK *ts, CONN_STATE conn);
#define TCP_RXWIN 1024
#define TCP_TIMEOUT 2000
#define TCP_RETRNUM 4
#define TCP_MAXDATA 512
/*---------------------------
tcp upcall,to match user application
----------------------------*/
void tcp_init(void)
{
register BYTE i;
for(i=0;i=msec)
{
*timep = tim;
tout = 1;
}
return(tout);
}
/*---------------------------
----------------------------*/
WORD tcp_check(tcppacket *tcp,DWORD srcip,DWORD destip,WORD tlen)
{
pheader tph;
DWORD sum;
sum = csum(&tcp->t,tlen); // Checksum TCP segment
tph.len = tlen; // Make pseudo-header
tph.srce = srcip;
tph.dest = destip;
tph.z = 0;
tph.pcol = tcp->i.type;
sum += csum(&tph, sizeof(pheader)); // Checksum pseudo-header
return(WORD)(sum + (sum>>16)); // Return total plus carry
}
/*---------------------------
----------------------------*/
WORD gettcp_opt(tcppacket *tcp, WORD *mssp)
{
WORD olen;
olen = ((tcp->t.hlen & 0xf0) >> 2) - sizeof(tcpheader);
if (mssp && olen>=4 && tcp->tcpdata[0]==2 && tcp->tcpdata[1]==4)
*mssp = *(WORD *)&tcp->tcpdata[2];
return(olen);
}
/*---------------------------
----------------------------*/
BYTE tcp_open(TCP_SOCK *ts,ip destip, WORD destport)
{
WORD ok=0;
if ((ok = ts->state==TCP_CLOSED)!=0)
{
ts->remport = destport;
ts->remip = destip;
tcp_newstate(ts,TCP_AOPEN);
}
tcp_sock_auto(ts,0,(bit)0);
return(ok);
}
/*---------------------------
----------------------------*/
BYTE tcp_close(TCP_SOCK *ts)
{
if (ts->state==TCP_EST || ts->state==TCP_SYNR)
tcp_newstate(ts, TCP_ACLOSE);
return(ts->state == TCP_CLOSED);
}
/*---------------------------
----------------------------*/
void tcp_reset(TCP_SOCK *ts)
{
if (ts->state)
tcp_make_sock(ts,TRST,0);
}
/*---------------------------
----------------------------*/
void tcp_newstate(TCP_SOCK *ts, TCP_STATE news)
{
if (news != ts->state)
ts->state = news;
//mstimeout(&ts->time, 0);
ts->time=get_current();
ts->retries = TCP_RETRNUM;
ts->timeout = TCP_TIMEOUT;
}
/*---------------------------
tcp upcall,to match user application
----------------------------*/
/*
BYTE tcp_upcall(TCP_SOCK *ts,CONN_STATE conn)
{
return 0;
}
*/
/*---------------------------
tcp upcall,to match user application
----------------------------*/
void tcp_make_sock(TCP_SOCK *ts, BYTE flags, WORD dlen)
{
BYTE st;
DWORD tseq, tack;
tseq = ts->txbuf.trial; /* Seq and ack values if connected */
tack = ts->rxbuf.in;
if ((st=ts->state)==TCP_SYNR || st==TCP_SYNS)
tseq--; /* Decrement SEQ if sending SYN */
else if (st==TCP_CLING || st==TCP_FINWT2 || st==TCP_TWAIT)
tseq++; /* ..or increment if sending FIN */
if (st==TCP_LASTACK || st==TCP_CLWAIT || st==TCP_CLING || st==TCP_TWAIT)
tack++; /* Increment ACK if FIN received */
ts->txbuf.seqnum=tseq;
ts->txbuf.acknum=tack;
ts->txbuf.flags=flags;
ts->txbuf.dlen=dlen;
ts->txbuf.yesornot=1;
}
/*----------------------------------------
tcp_poll
-----------------------------------------*/
void tcp_poll(void)
{
register BYTE i;
BYTE ok=0;
TCP_SOCK *ts;
for(i=0;i<2;i++)
{
ts=&tsock[i];
if(ts->rxbuf.yesornot==1)
tcp_sock_auto(ts,ts->rxbuf.dlen,(bit)1);
else
{
if (ts->state)
{
// if(ts->state==TCP_SYNS)
// if(mstimeout(&ts->time, ts->timeout))
if (ts->timeout && mstimeout(&ts->time, ts->timeout))
{
ts->timeout += ts->timeout;
ts->retries--;
if (ts->retries > 0)
tcp_remake_sock(ts);
else
{
tcp_newstate(ts, TCP_CLOSED);
tcp_make_sock(ts, TRST, 0);
}
}
else // Send Tx data
tcp_sock_auto(ts,0,(bit)0);
}
}
if(tsock[i].txbuf.yesornot==1)
tcp_send_pack(&tsock[i]);
}
}
/*---------------------------
查看有无要发送的数据,在tcp发送缓冲区中,
若有,检查ip发送缓冲区有无空闲
空闲则发送出数据包,由任务调度
保证发送出数据
----------------------------*/
void tcp_send_pack(TCP_SOCK *ts)
{
register j;
BYTE ok=0;
WORD hlen,tlen,dlen,olen=0;
tcppacket *tcp;
ip srcip,destip;
for(j=0;j<2 && !ok;j++)
{
if(ok=ipsenflag[j].flag==0)
{
tcp=(tcppacket *)sendbuffer[j].etherframe.packet;
tcp->t.seq=ts->txbuf.seqnum;
tcp->t.ack=ts->txbuf.acknum;
tcp->t.window=ts->rxwin;
if(ts->rxwin==0)
ts->rxwin=TCP_RXWIN;
tcp->t.sport=ts->locport;
tcp->t.dport=ts->remport;
hlen=sizeof(tcpheader);
dlen=ts->txbuf.dlen;
if (dlen > 0) // Get the Tx data
dlen = buff_try(&ts->txbuf, tcp->tcpdata, dlen); //copy data
if(ts->txbuf.flags & TSYN && dlen==0)
{
hlen+=(olen=4);
tcp->tcpdata[0]=2;
tcp->tcpdata[1]=4;
*(WORD *)&tcp->tcpdata[2]=TCP_MAXDATA;
}
tcp->t.hlen=(BYTE)(hlen<<2);
tcp->t.flags=ts->txbuf.flags;
tcp->t.urgent=tcp->t.check=0;
//degug
tlen=hlen+dlen;
srcip=mypara.myip;
destip=ts->remip;
tcp->t.check=~tcp_check(tcp,srcip,destip,tlen);
ts->txbuf.yesornot=0; //release txd buffer
ip_send_pack(&sendbuffer[j],j,destip,PTCP,tlen);
}
}
}
/*---------------------------
----------------------------*/
void tcp_deal_pack(netcard *netbuf)
{
register BYTE i;
tcppacket *tcp;
TCP_SOCK *ts;
WORD sum;
WORD len,olen,dlen,rdlen;
BYTE ok=0;
tcp=(tcppacket *)netbuf->etherframe.packet;
rdlen=len=tcp->i.totallen-sizeof(ipheader);
//check protocal and minimum length
if(tcp->i.type==PTCP && len>=sizeof(tcpheader) && tcp->t.hlen>=0x50)
{
sum=tcp_check(tcp,tcp->i.srcip,tcp->i.destip,len); //check tcp
//sum=0xffff;
if(sum==0xffff) //if correct
{
len=len-sizeof(tcpheader);
len -= gettcp_opt(tcp, 0); // Subtract options len
dlen = len; // Return 0 if data len=0
olen = ((tcp->t.hlen & 0xf0) >> 2) - sizeof(tcpheader);
len=len-olen;
for(i=0;ii.destip==mypara.myip && tcp->t.dport==ts->locport &&
tcp->i.srcip==ts->remip && tcp->t.sport==ts->remport;
}
for(i=0;istate==TCP_CLOSED)!=0)
{
ts->locport=tcp->t.dport;
ts->remip=tcp->i.srcip;
ts->remport=tcp->t.sport;
}
}
if(ok)
{
ts->rxbuf.flags=tcp->t.flags & (TFIN+TSYN+TRST+TACK);
ts->rxseq = tcp->t.seq;
ts->rxack = tcp->t.ack;
ts->txwin = tcp->t.window;
ts->rxbuf.yesornot=1;
ts->rxbuf.dlen=dlen;
if(dlen!=0)
if(ts->state==TCP_SYNR || ts->state==TCP_EST || ts->state==TCP_ACLOSE || ts->state==TCP_FINWT1 ||
ts->state==TCP_FINWT2 || ts->state==TCP_SYNR)
tcp_tsestab_rx(ts,tcp,dlen); //rxbuffer
//tcp_sock_auto(ts,dlen);
}
if(!ok)
{
//reset
tcp_reset_net(netbuf, rdlen);
}
}
}
}
/*---------------------------
deal sock data and update sock state
rxortx:if rx 1,else if tx 0
----------------------------*/
void tcp_sock_auto(TCP_SOCK *ts,WORD dlen,bit rxortx)
{
BYTE rflags=0;
BYTE upcall=1;
WORD txlen=0;
WORD tx=0;
static WORD eport=mineport;
// WORD (*upcall)(TCP_SOCK *ts, CONN_STATE conn);
//CONN_STATE conn;
// server_upcall=server_action;
// client_upcall=client_action;
if(dlen)
dlen=ts->rxbuf.dlen;
// upcall = ts->server ? server_upcall : client_upcall;
if (rxortx)
{
ts->rxbuf.yesornot=0;
rflags = ts->rxbuf.flags;
if (rflags & TRST)
tcp_newstate(ts, TCP_RSTR);
else if (rflags&TACK &&
ts->rxack>=ts->txbuf.out && ts->rxack<=ts->txbuf.trial)
ts->txbuf.out = ts->rxack;
}
// Passive (remote) open a connection
if(ts->state==TCP_CLOSED)
{
if (rflags == TSYN) // If closed & SYN received..
{
buff_setall(&ts->rxbuf, ++ts->rxseq); // Load my ACK value
buff_setall(&ts->txbuf, get_current()*0x100L); // ..and my SEQ
ts->rxwin = TCP_RXWIN; // Default Rx window
ts->connflags = 0; // No special flags
if (upcall && !tcp_upcall(ts, TCP_OPEN,1)) // Upcall err?
tcp_reset_ts(ts, dlen); // ..don't accept SYN
else
{
tcp_newstate(ts, TCP_SYNR); // If OK, send SYN+ACK
tcp_make_sock(ts, TSYN+TACK, 0);
ts->server = 1; // Identify as server socket
}
}
else // If not SYN..
tcp_reset_ts(ts, dlen); // ..send reset
}
else if(ts->state==TCP_SYNR) // SYN+ACK sent, if ACK Rx..
{
if (rflags==TACK && ts->rxseq==ts->rxbuf.in && ts->rxack==ts->txbuf.out)
{
if (upcall && !tcp_upcall(ts, TCP_CONN,ts->server))// If upcall not OK..
tcp_newstate(ts, TCP_ACLOSE); // ..close after sending data
else // If OK..
tcp_newstate(ts, TCP_EST); // ..go established
}
else if (dlen && ts->rxseq==ts->rxbuf.in-1) // If repeat SYN..
tcp_make_sock(ts, TSYN+TACK, 0);
}
// Connection established
else if(ts->state==TCP_EST)
{
if (rflags) // Refresh timer if Rx
tcp_newstate(ts, ts->state);
if (rflags & TFIN && ts->rxseq==ts->rxbuf.in) // If remote close..
{
if (!upcall || tcp_upcall(ts, TCP_CLOSE,ts->server))
{
tcp_newstate(ts, TCP_LASTACK); // ..FIN+ACK if OK
tcp_make_sock(ts, TFIN+TACK, 0);
}
else
{
tcp_newstate(ts, TCP_CLWAIT); // ..or send data if not
tcp_tsestab_tx(ts, 1);
}
}
else
{
if (upcall && !tcp_upcall(ts, dlen>0 ? TCP_DATA : TCP_NODATA,ts->server))
{
tcp_newstate(ts, TCP_ACLOSE); // If upcall 0, start close
tx = 1;
}
else if (dlen > 0) // If Rx data, send ack
tx = 1;
//tx=0;
tcp_tsestab_tx(ts,tx);// Send packet, maybe Tx data
}
}
// Passive (remote) close a connection
else if(ts->state==TCP_CLWAIT) // Do upcall to application
{
if (!upcall || tcp_upcall(ts, TCP_CLOSE,ts->server)) // If OK, send FIN
{
tcp_newstate(ts, TCP_LASTACK);
tcp_make_sock(ts, TFIN+TACK, 0);
}
else // If not, keep open
tcp_tsestab_tx(ts, 0);
}
else if(ts->state==TCP_LASTACK) // If ACK of my FIN..
{
if (rflags==TACK && ts->rxseq==ts->rxbuf.in+1)
tcp_newstate(ts, TCP_CLOSED); // ..connection closed
}
// Active (local) open a connection
else if(ts->state==TCP_AOPEN)
{
ts->server = 0; // Identify as client socket
buff_setall(&ts->txbuf, get_current()*100L); // ARPed: set my SEQ value
ts->rxwin = TCP_RXWIN; // Default window size
ts->connflags = 0; // No special flags
ts->locport = ++eport>=maxeport ? mineport : eport;// New port num
if (upcall) // Do upcall
tcp_upcall(ts, TCP_OPEN,ts->server);
tcp_newstate(ts, TCP_SYNS);
tcp_make_sock(ts, TSYN, 0);
}
else if(ts->state==TCP_SYNS) // Sent SYN, if SYN+ACK Rx
{
if (rflags==TSYN+TACK && ts->rxack==ts->txbuf.out)
{
buff_setall(&ts->rxbuf, ts->rxseq+1); // Set my ACK value
if (upcall) // Do upcall
tcp_upcall(ts, TCP_CONN,ts->server);
tcp_newstate(ts, TCP_EST); // ..send ACK, go established
tcp_make_sock(ts, TACK, 0);
}
else if (rflags) // If anything else..
tcp_reset_ts(ts, dlen); // ..send reset
}
// Active (local) close a connection
else if(ts->state==TCP_ACLOSE)
{
if (buff_dlen(&ts->txbuf)) // If any Tx data left..
tcp_tsestab_tx(ts,0); // If unsent, send it
else // All data sent: close conn
{
if (upcall) // Do upcall
tcp_upcall(ts, TCP_CLOSE,ts->server);
tcp_newstate(ts, TCP_FINWT1);
tcp_make_sock(ts, TFIN+TACK, 0);
}
}
else if(ts->state==TCP_FINWT1)
{
if (rflags&TFIN && ts->rxseq==ts->rxbuf.in)
{
if (rflags&TACK && ts->rxack==ts->txbuf.trial+1)
tcp_newstate(ts, TCP_TWAIT); // If ACK+FIN, close
else if (!(rflags&TACK) || ts->rxack==ts->txbuf.trial)
tcp_newstate(ts, TCP_CLING); // If FIN, wait for ACK
tcp_make_sock(ts, TACK, 0);
}
else if (rflags&TACK && ts->rxack==ts->txbuf.trial+1)
tcp_newstate(ts, TCP_FINWT2); // If just ACK, half-close
}
else if(ts->state==TCP_FINWT2) // Half-closed: awaiting FIN
{
if (rflags&TFIN && ts->rxseq==ts->rxbuf.in)
{
tcp_newstate(ts, TCP_TWAIT); // Got FIN, close
tcp_make_sock(ts, TACK, 0);
}
}
else if(ts->state==TCP_CLING) // Closing: need final ACK
{
if (rflags==TACK && ts->rxseq==ts->rxbuf.in+1)
tcp_newstate(ts, TCP_CLOSED);
}
else if(ts->state==TCP_TWAIT) // Timed wait: just close!
tcp_newstate(ts, TCP_CLOSED);
else
tcp_newstate(ts, TCP_CLOSED);
}
/* Put Rx data into an established socket, return non-zero if Tx ACK required */
WORD tcp_tsestab_rx(TCP_SOCK *ts, tcppacket *tcp, WORD dlen)
{
WORD oset=0, oldlen, tx=0;
WORD rdlen=0;
DWORD rxdiff;
WORD mss;
if (dlen > 0) // If any data received..
{
oset = gettcp_opt(tcp, &mss);
if(oset>=4)
ts->txmss=mindata(mss,(WORD)TCP_MAXDATA);
rxdiff = ts->rxseq - ts->rxbuf.in; // Find posn w.r.t. last data
if (rxdiff == 0) // If next block, accept it
rdlen = (WORD)dlen;
else if (rxdiff < 0) // If part or all is a repeat
{
oldlen = -(WORD)rxdiff; // ..read in new part
if (oldlen<=ts->rxwin && dlen>oldlen)
{
rdlen = dlen - oldlen;
oset += (WORD)oldlen;
}
}
if (rdlen) // Read the data in
buff_in(&ts->rxbuf, &tcp->tcpdata[oset], rdlen);
tx = tcp->t.flags&TPUSH || rxdiff<0 || buff_dlen(&ts->rxbuf)>ts->rxwin/2;
}
return(tx);
}
/* Prepare Tx frame containing outgoing data for connection, return frame len
** If 'force' is non-zero, make frame even if there is no data */
void tcp_tsestab_tx(TCP_SOCK *ts,WORD force)
{
WORD tdlen, txlen=0;
tdlen = mindata(buff_untriedlen(&ts->txbuf), ts->txmss);
tdlen = mindata(tdlen, ts->txwin-buff_trylen(&ts->txbuf));
if (tdlen>0 && !force)
force = buff_trylen(&ts->txbuf)==0 || tdlen < ts->txmss/2;
if (force)
tcp_make_sock(ts, (BYTE)(TACK+ts->connflags), (WORD)tdlen);
// return(txlen);
}
void tcp_remake_sock(TCP_SOCK *ts)
{
WORD txlen=0;
if (ts->state==TCP_EST||ts->state==TCP_CLWAIT||ts->state==TCP_ACLOSE)
{
ts->txbuf.trial = ts->txbuf.out;
tcp_tsestab_tx(ts, 1);
}
else if (ts->state)
tcp_make_sock(ts, ts->txflags, 0);
// return(txlen);
}
//reset tcp with netcard type
void tcp_reset_net(netcard *netbuf, WORD rdlen)
{
tcppacket *tcp;
DWORD ack;
WORD port,hlen;
tcp=(tcppacket *)netbuf->etherframe.packet;
ack = tcp->t.seq + (rdlen>0?rdlen:0);
if (tcp->t.flags & (TSYN+TFIN))
ack++;
tcp->t.seq = tcp->t.ack; // Set seq and ack values
tcp->t.ack = ack;
tcp->t.window = 0; // Window size, srce & dest port nums
port=tcp->t.sport;
tcp->t.sport=tcp->t.dport;
tcp->t.dport = port;
hlen = sizeof(tcpheader); // TCP header len
tcp->t.hlen = (BYTE)(hlen<<2); // Set TCP header len, and flags
tcp->t.flags = TRST+TACK;
tcp->t.urgent = tcp->t.check = 0;
tcp->t.check = ~tcp_check(tcp, mypara.myip, tcp->i.srcip, hlen);
ip_send(netbuf,tcp->i.srcip,&netbuf->etherframe.sourcenodeid[0],PTCP,hlen);
}
//reset tcp in TCP_SOCK type
void tcp_reset_ts(TCP_SOCK *ts, WORD rdlen)
{
DWORD ack;
ack = ts->rxseq + (rdlen>0?rdlen:0);
if (ts->rxbuf.flags & (TSYN+TFIN))
ack++;
ts->txbuf.flags=TRST+TACK;
ts->txbuf.acknum=ack;
ts->txbuf.seqnum=ts->rxack;
ts->txbuf.dlen=0;
ts->txbuf.yesornot=1;
ts->rxwin=0;
}
/*----------------------------------------
----------------------------------------*/
WORD tcp_upcall(TCP_SOCK *ts, CONN_STATE conn,BYTE server)
{
WORD ok;
if(server) ok=server_action(ts,conn);
else ok=client_action(ts,conn);
return ok;
}
/*EOF*/