📄 tcp_fsm.c
字号:
/*
* TCP_FSM - the state machine for TCP processing.
* previously this was in pctcp.c and tcp_handler()
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "copyrigh.h"
#include "wattcp.h"
#include "chksum.h"
#include "strings.h"
#include "misc.h"
#include "language.h"
#include "pcconfig.h"
#include "pcqueue.h"
#include "pcsed.h"
#include "pcpkt.h"
#include "ip_out.h"
#include "pcdbug.h"
#include "pcstat.h"
#include "pctcp.h"
#include "tcp_fsm.h"
#if !defined(USE_UDP_ONLY)
#define flag_SYNACK (tcp_FlagSYN | tcp_FlagACK)
typedef int (*tcp_StateProc) (tcp_Socket**, const in_Header*, tcp_Header*, int);
static int tcp_listen_state (tcp_Socket**, const in_Header*, tcp_Header*, int);
static int tcp_synsent_state (tcp_Socket**, const in_Header*, tcp_Header*, int);
static int tcp_synrec_state (tcp_Socket**, const in_Header*, tcp_Header*, int);
static int tcp_estab_state (tcp_Socket**, const in_Header*, tcp_Header*, int);
static int tcp_estcl_state (tcp_Socket**, const in_Header*, tcp_Header*, int);
static int tcp_finwt1_state (tcp_Socket**, const in_Header*, tcp_Header*, int);
static int tcp_finwt2_state (tcp_Socket**, const in_Header*, tcp_Header*, int);
static int tcp_closewt_state (tcp_Socket**, const in_Header*, tcp_Header*, int);
static int tcp_closing_state (tcp_Socket**, const in_Header*, tcp_Header*, int);
static int tcp_lastack_state (tcp_Socket**, const in_Header*, tcp_Header*, int);
static int tcp_timewt_state (tcp_Socket**, const in_Header*, tcp_Header*, int);
static void tcp_ProcessData (tcp_Socket *s, tcp_Header *tcp, int len, int flags);
static int tcp_ProcessAck (tcp_Socket *s, tcp_Header *tcp, long *unack);
static tcp_StateProc tcp_state_tab [] = {
tcp_listen_state, /* tcp_StateLISTEN : listening for connection */
tcp_synsent_state, /* tcp_StateSYNSENT : SYN sent, active open */
tcp_synrec_state, /* tcp_StateSYNREC : SYN received, SYN+ACK sent. */
tcp_estab_state, /* tcp_StateESTAB : established */
tcp_estcl_state, /* tcp_StateESTCL : established, but will FIN */
tcp_finwt1_state, /* tcp_StateFINWT1 : sent FIN */
tcp_finwt2_state, /* tcp_StateFINWT2 : sent FIN, received FIN+ACK */
tcp_closewt_state, /* tcp_StateCLOSWT : received FIN waiting for close */
tcp_closing_state, /* tcp_StateCLOSING : sent FIN, received FIN (waiting for FIN+ACK) */
tcp_lastack_state, /* tcp_StateLASTACK : FIN received, ACK+FIN sent */
tcp_timewt_state, /* tcp_StateTIMEWT : dally after sending final FIN+ACK */
};
/*
* _tcp_fsm - Our TCP-input state-machine.
*
* Returns 1 to tcp_handler() if a retransmission is required
* immediately or when RTO expires.
*/
int _tcp_fsm (tcp_Socket **sp, const in_Header *ip)
{
tcp_Header *tcp;
tcp_Socket *s = *sp;
WORD len;
BYTE flags;
if ((signed)s->state < tcp_StateLISTEN || s->state >= tcp_StateCLOSED)
return (0); /* cannot happen! */
len = in_GetHdrLen (ip); /* len of IP header */
tcp = (tcp_Header*) ((BYTE*)ip + len); /* tcp frame pointer */
flags = tcp->flags & tcp_FlagMASK; /* get TCP-head flags */
return (*tcp_state_tab[s->state]) (sp, ip, tcp, flags);
}
/*
* LISTEN state
*/
static int tcp_listen_state (tcp_Socket **sp, const in_Header *ip,
tcp_Header *tcp, int flags)
{
tcp_Socket *s = *sp;
SET_PEER_MAC_ADDR (s, ip); /* save his ethernet address */
if (flags & tcp_FlagSYN)
{
#if defined(USE_BSD_FUNC)
/*
* Append the TCB `s' to the listen-queue. The new TCB on output
* from `_tcp_syn_hook' (_sock_append) is the clone of `s' on
* input unless the listen-queue is full.
*/
if (_tcp_syn_hook && (*_tcp_syn_hook)(&s) < 0)
{
/* Append failed due to queue full or (temporary) memory shortage.
* Silently discard SYN. TCB `s' is unchanged.
*/
CLR_PEER_MAC_ADDR (s);
return (0);
}
#endif
if (ip->tos > s->tos)
s->tos = ip->tos;
/*
* if (ip->tos < s->tos):
* RFC 793 says we should close connection.
* We best not do that since SunOS ignores TOS
*/
s->acknum = intel (tcp->seqnum) + 1;
s->flags = flag_SYNACK;
s->state = tcp_StateSYNREC;
s->unhappy = TRUE;
TCP_SEND (s); /* respond immediately */
s->timeout = set_timeout (tcp_TIMEOUT);
STAT (tcpstats.tcps_connattempt++);
}
else
{
if (!(flags & tcp_FlagRST)) /* don't answer RST */
TCP_RESET (s, ip, tcp); /* send a reset */
STAT (tcpstats.tcps_conndrops++);
CLR_PEER_MAC_ADDR (s);
}
return (0);
}
/*
* SYNSENT state
*/
static int tcp_synsent_state (tcp_Socket **sp, const in_Header *ip,
tcp_Header *tcp, int flags)
{
tcp_Socket *s = *sp;
if (flags & tcp_FlagSYN)
{
if (ip->tos > s->tos)
s->tos = ip->tos;
s->flags = tcp_FlagACK;
s->timeout = set_timeout (tcp_TIMEOUT);
/* FlagACK means connection established, else SYNREC
*/
if (flags & tcp_FlagACK)
{
/* but is it for the correct session ?
*/
if (tcp->acknum == intel(s->seqnum + 1))
{
int len = intel16 (ip->length) - in_GetHdrLen (ip);
s->state = tcp_StateESTAB;
s->seqnum++; /* good increment */
s->acknum = intel (tcp->seqnum) + 1;
tcp_ProcessData (s, tcp, len, flags); /* someone may try it */
s->unhappy = TRUE; /* rely on their attempts */
TCP_SEND (s); /* !! maybe use TCP_SENDSOON() to merge application
data into ACK */
}
else
{
/* wrong ack, force a RST and resend SYN now
*/
s->flags = tcp_FlagRST;
s->unhappy = TRUE;
TCP_SEND (s);
s->seqnum = INIT_SEQ(); /* !! should we set a new seq-num? */
s->flags = tcp_FlagSYN;
#if defined(USE_DEBUG)
s->last_seqnum[0] = s->last_seqnum[1] = 0;
#endif
TCP_SENDSOON (s); /* !! was TCP_SEND() */
}
/* !!to-do: recalculate RTT-timer
*/
}
else
{
s->acknum++;
s->state = tcp_StateSYNREC;
return (0);
}
}
else
{
TCP_RESET (s, ip, tcp);
return (0); /* Added 18-Aug 1999, GV */
}
return (1);
}
/*
* SYNREC state
*/
static int tcp_synrec_state (tcp_Socket **sp, const in_Header *ip,
tcp_Header *tcp, int flags)
{
tcp_Socket *s = *sp;
if (flags & tcp_FlagSYN) /* retransmitted SYN */
{
s->flags = flag_SYNACK;
s->unhappy = TRUE;
TCP_SEND (s);
s->timeout = set_timeout (tcp_TIMEOUT);
return (0);
}
if ((flags & tcp_FlagACK) && (intel(tcp->acknum) == (s->seqnum + 1)))
{
s->window = intel16 (tcp->window);
if (s->window > MAX_WINDOW)
s->window = MAX_WINDOW;
s->seqnum++;
s->flags = tcp_FlagACK;
s->state = tcp_StateESTAB;
s->timeout = 0UL; /* never timeout */
s->unhappy = FALSE;
return (0);
}
ARGSUSED (ip);
return (1);
}
/*
* ESTABLISHED state
*/
static int tcp_estab_state (tcp_Socket **sp, const in_Header *ip,
tcp_Header *tcp, int flags)
{
tcp_Socket *s = *sp;
int len;
long ldiff;
DWORD ack;
/* handle lost SYN
*/
if ((flags & tcp_FlagSYN) && (flags & tcp_FlagACK))
{
TCP_SEND (s);
return (0);
}
if (!(flags & tcp_FlagACK)) /* must ack something */
return (0);
s->timeout = 0UL; /* we do not timeout at this point */
if (!tcp_ProcessAck(s,tcp,&ldiff))
{
#if defined(USE_DEBUG)
if (debug_on > 1)
(*_printf)("tcphandler confused so set unacked "
"back to 0 from %u\r\n", s->unacked);
#endif
STAT (tcpstats.tcps_persistdrop++); /* !! a better counter? */
s->unacked = 0;
}
if (s->unacked < 0)
s->unacked = 0;
s->flags = tcp_FlagACK;
len = intel16 (ip->length) - in_GetHdrLen (ip);
ack = s->acknum;
tcp_ProcessData (s, tcp, len, flags);
if (s->state != tcp_StateCLOSWT &&
(flags & tcp_FlagFIN) &&
s->missed_seg[0] == 0L &&
ack == intel(tcp->seqnum))
{
s->acknum++;
if (s->err_msg == NULL)
s->err_msg = _LANG("Connection closed");
TCP_SEND (s);
s->locflags |= LF_GOT_FIN;
s->flags |= tcp_FlagFIN; /* for the tcp_Retransmitter() */
s->unhappy = TRUE;
s->timeout = set_timeout (tcp_LASTACK_TIME); /* Added AGW 6 Jan 2001 */
s->state = tcp_StateLASTACK;
}
/*
* Eliminate the spurious ACK messages bug.
* For the window update, the length should be the
* data length only, so exclude the TCP header size
* -- Joe <jdhagen@itis.com>
*/
len -= (tcp->offset << 2);
if ((ldiff > 0 && s->datalen > 0) || len > 0)
{
/* Need to ACK and update window, but how urgent ??
* Added new window update -GV 21-Nov-1998
*/
UINT winfree = s->maxrdatalen - s->rdatalen;
UINT treshhold = max (s->max_seg/2, winfree);
if (debug_on > 1)
(*_printf) ("fastACK: ldiff %ld, th %u\r\n", ldiff, treshhold);
/* Need a better criteria for doing Fast-ACK
*/
if (s->missed_seg[0] || ldiff > 0 || len > treshhold)
{
s->karn_count = 0;
s->flags |= tcp_FlagPUSH; /* !! added */
TCP_SEND (s);
}
else
TCP_SENDSOON (s); /* delayed ACK */
}
return (0);
}
/*
* ESTAB_CLOSE state
*/
static int tcp_estcl_state (tcp_Socket **sp, const in_Header *ip,
tcp_Header *tcp, int flags)
{
tcp_estab_state (sp, ip, tcp, flags);
_tcp_close (*sp);
return (0);
}
/*
* CLOSE_WAIT state
*/
static int tcp_closewt_state (tcp_Socket **sp, const in_Header *ip,
tcp_Header *tcp, int flags)
{
return tcp_estab_state (sp, ip, tcp, flags);
}
/*
* FIN_WAIT1 state -
*/
static int tcp_finwt1_state (tcp_Socket **sp, const in_Header *ip,
tcp_Header *tcp, int flags)
{
long ldiff;
int len = intel16 (ip->length) - in_GetHdrLen (ip);
DWORD ack, seq;
tcp_Socket *s = *sp;
/* They have not necessarily read all the data
* yet, we must still supply it as requested
*/
if (tcp_ProcessAck(s,tcp,&ldiff))
{
if (ldiff == 0 || s->unacked < 0)
s->unacked = 0;
}
/* they may still be transmitting data, we must read it
*/
tcp_ProcessData (s, tcp, len, flags);
ack = intel (tcp->acknum);
seq = intel (tcp->seqnum);
/* check if other tcp has ACK'ed all sent data and
* is ready to change states.
*/
if (s->missed_seg[0] == 0L && (flags & flag_SYNACK) == flag_SYNACK)
{
if (seq == s->acknum)
{
s->acknum++; /* we must ACK their FIN! */
if (ack >= s->seqnum + 1)
{
/* Not simultaneous close (they've ACKed our FIN)
* We need to ACK their FIN and move to TIME_WAIT
*/
s->seqnum++;
s->timeout = set_timeout (tcp_TIMEWT_TO);
s->state = tcp_StateTIMEWT;
}
else
{
/* Simultaneous close (haven't ACKed our FIN yet)
* We need to ACK their FIN and move to CLOSING
*/
s->timeout = set_timeout (tcp_TIMEOUT); /* !! S. Lawson, added 12.Nov 1999 */
s->state = tcp_StateCLOSING;
}
s->flags = tcp_FlagACK;
s->unhappy = FALSE;
TCP_SEND (s);
}
}
else if (flags & tcp_FlagACK)
{
/* other side is legitimately acking our FIN
*/
if ((ack == s->seqnum + 1) &&
(seq == s->acknum) &&
(s->datalen == 0))
{
if (!(s->locflags & LF_LINGER))
{
_tcp_unthread (s); /* enters tcp_StateCLOSED */
return (0);
}
s->seqnum++;
s->state = tcp_StateFINWT2;
s->unhappy = FALSE; /* we don't send anything */
s->timeout = set_timeout (tcp_TIMEOUT);
}
else if ((ack == s->seqnum + 1) && (seq == s->acknum + 1))
{
/* !! added 30-Aug 1999 GV
* Try to stop that annoying retransmission bug/feature(?)
* from FreeBSD 4.x which increments both SEQ and ACK.
*/
s->seqnum++;
s->acknum++;
s->flags = tcp_FlagRST;
s->unhappy = FALSE;
s->karn_count = 0;
s->datalen = 0;
TCP_SEND (s);
_tcp_unthread (s);
return (0);
}
}
return (1);
}
/*
* FIN_WAIT2 state
*/
static int tcp_finwt2_state (tcp_Socket **sp, const in_Header *ip,
tcp_Header *tcp, int flags)
{
tcp_Socket *s = *sp;
int len = intel16 (ip->length) - in_GetHdrLen (ip);
DWORD ack, seq;
/* They may still be transmitting data, we must read it
*/
ack = intel (tcp->acknum);
seq = intel (tcp->seqnum);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -