⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tcp_fsm.c

📁 开放源码的编译器open watcom 1.6.0版的源代码
💻 C
📖 第 1 页 / 共 2 页
字号:

/*
 *  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 + -