/* ********************************************************************************************************* * uC/TCP-IP * The Embedded TCP/IP Suite * * (c) Copyright 2003-2007; Micrium, Inc.; Weston, FL * * All rights reserved. Protected by international copyright laws. * * uC/TCP-IP is provided in source form for FREE evaluation, for educational * use or peaceful research. If you plan on using uC/TCP-IP in a commercial * product you need to contact Micrium to properly license its use in your * product. We provide ALL the source code for your convenience and to help * you experience uC/TCP-IP. The fact that the source code is provided does * NOT mean that you can use it without paying a licensing fee. * * Knowledge of the source code may NOT be used to develop a similar product. * * Please help us continue to provide the Embedded community with the finest * software available. Your honesty is greatly appreciated. ********************************************************************************************************* */ /* ********************************************************************************************************* * * NETWORK TCP LAYER * (TRANSMISSION CONTROL PROTOCOL) * * Filename : net_tcp.c * Version : V1.89 * Programmer(s) : ITJ ********************************************************************************************************* * Note(s) : (1) Supports Transmission Control Protocol as described in RFC #793 with the following * restrictions/constraints : * * (a) TCP Security & Precedence NOT supported RFC # 793, Section 3.6 * * (b) TCP Urgent Data NOT supported RFC # 793, Section 3.7 * 'The Communication of * Urgent Information' * * (c) The following TCP options NOT supported : * * (1) Window Scale RFC #1072, Section 2 * RFC #1323, Section 2 * (2) Selective Acknowledgement (SACK) RFC #1072, Section 3 * RFC #2018 * RFC #2883 * (3) TCP Echo RFC #1072, Section 4 * (4) Timestamp RFC #1323, Section 3.2 * (5) Protection Against Wrapped Sequences (PAWS) RFC #1323, Section 4 * * (d) #### IP-Options-to-TCP-Connection RFC #1122, Section 4.2.3.8 * Handling NOT supported * * (e) #### ICMP-Error-Message-to-TCP-Connection RFC #1122, Section 4.2.3.9 * Handling NOT currently supported * * (2) TCP Layer assumes/requires Network Socket Layer (see 'net_sock.h MODULE Note #1a2'). ********************************************************************************************************* */ /* ********************************************************************************************************* * INCLUDE FILES ********************************************************************************************************* */ #define NET_TCP_MODULE #include /* ********************************************************************************************************* * MODULE * * Note(s) : (1) See 'net_tcp.h MODULE'. ********************************************************************************************************* */ #ifdef NET_TCP_MODULE_PRESENT /*$PAGE*/ /* ********************************************************************************************************* * LOCAL DEFINES ********************************************************************************************************* */ /* ********************************************************************************************************* * TCP CONNECTION CONFIGURATION CODE DEFINES * * Note(s) : (1) (a) TCP connection configuration codes used as arguments for various NetTCP_ConnCfg() functions. * * (b) TCP connection configuration codes bit-field flags logically OR'd. ********************************************************************************************************* */ #define NET_TCP_CONN_CFG_NONE DEF_BIT_NONE #define NET_TCP_CONN_CFG_MAX_SEG_SIZE_LOCAL DEF_BIT_00 #define NET_TCP_CONN_CFG_MAX_SEG_SIZE_REMOTE DEF_BIT_01 #define NET_TCP_CONN_CFG_MAX_SEG_SIZE_CONN DEF_BIT_02 #define NET_TCP_CONN_CFG_MAX_SEG_SIZE_ALL (NET_TCP_CONN_CFG_MAX_SEG_SIZE_LOCAL | \ NET_TCP_CONN_CFG_MAX_SEG_SIZE_REMOTE | \ NET_TCP_CONN_CFG_MAX_SEG_SIZE_CONN ) #define NET_TCP_CONN_CFG_MAX_SEG_SIZE_MASK NET_TCP_CONN_CFG_MAX_SEG_SIZE_ALL #define NET_TCP_CONN_CFG_WIN_SIZE_RX DEF_BIT_02 #define NET_TCP_CONN_CFG_WIN_SIZE_TX DEF_BIT_03 #define NET_TCP_CONN_CFG_WIN_SIZE_CONN (NET_TCP_CONN_CFG_WIN_SIZE_RX | \ NET_TCP_CONN_CFG_WIN_SIZE_TX ) #define NET_TCP_CONN_CFG_WIN_SIZE_ALL NET_TCP_CONN_CFG_WIN_SIZE_CONN #define NET_TCP_CONN_CFG_WIN_SIZE_MASK NET_TCP_CONN_CFG_WIN_SIZE_ALL #define NET_TCP_CONN_CFG_TX_RTT_RTO DEF_BIT_04 #define NET_TCP_CONN_CFG_ALL (NET_TCP_CONN_CFG_NONE | \ NET_TCP_CONN_CFG_MAX_SEG_SIZE_ALL | \ NET_TCP_CONN_CFG_WIN_SIZE_ALL | \ NET_TCP_CONN_CFG_TX_RTT_RTO ) /*$PAGE*/ /* ********************************************************************************************************* * TCP CONNECTION CLOSE/FREE CODE DEFINES * * Note(s) : (1) (a) TCP connection close codes used as arguments for various NetTCP_ConnClose() functions. * * (b) TCP connection close codes bit-field flags logically OR'd. * * (2) Available TCP connection free codes are identical to TCP connection close codes. ********************************************************************************************************* */ #define NET_TCP_CONN_CLOSE_NONE DEF_BIT_NONE #define NET_TCP_CONN_CLOSE_CONN_NONE DEF_BIT_NONE #define NET_TCP_CONN_CLOSE_CONN_TX_RESET DEF_BIT_00 #define NET_TCP_CONN_CLOSE_CONN_ALL (NET_TCP_CONN_CLOSE_CONN_NONE | \ NET_TCP_CONN_CLOSE_CONN_TX_RESET) #define NET_TCP_CONN_CLOSE_CONN_MASK NET_TCP_CONN_CLOSE_CONN_ALL #define NET_TCP_CONN_CLOSE_TMR_NONE DEF_BIT_NONE #define NET_TCP_CONN_CLOSE_TMR_TX_IDLE DEF_BIT_04 #define NET_TCP_CONN_CLOSE_TMR_TX_SILLY_WIN DEF_BIT_05 #define NET_TCP_CONN_CLOSE_TMR_TX_ZERO_WIN DEF_BIT_06 #define NET_TCP_CONN_CLOSE_TMR_TX_ACK_DLY DEF_BIT_07 #define NET_TCP_CONN_CLOSE_TMR_RE_TX DEF_BIT_08 #define NET_TCP_CONN_CLOSE_TMR_KEEP_ALIVE DEF_BIT_09 #define NET_TCP_CONN_CLOSE_TMR_TIMEOUT DEF_BIT_10 #define NET_TCP_CONN_CLOSE_TMR_ALL (NET_TCP_CONN_CLOSE_TMR_NONE | \ NET_TCP_CONN_CLOSE_TMR_TX_IDLE | \ NET_TCP_CONN_CLOSE_TMR_TX_SILLY_WIN | \ NET_TCP_CONN_CLOSE_TMR_TX_ZERO_WIN | \ NET_TCP_CONN_CLOSE_TMR_TX_ACK_DLY | \ NET_TCP_CONN_CLOSE_TMR_RE_TX | \ NET_TCP_CONN_CLOSE_TMR_KEEP_ALIVE | \ NET_TCP_CONN_CLOSE_TMR_TIMEOUT ) #define NET_TCP_CONN_CLOSE_TMR_MASK NET_TCP_CONN_CLOSE_TMR_ALL #define NET_TCP_CONN_CLOSE_ALL (NET_TCP_CONN_CLOSE_NONE | \ NET_TCP_CONN_CLOSE_CONN_ALL | \ NET_TCP_CONN_CLOSE_TMR_ALL ) #define NET_TCP_CONN_FREE_NONE NET_TCP_CONN_CLOSE_NONE #define NET_TCP_CONN_FREE_TMR_NONE NET_TCP_CONN_CLOSE_TMR_NONE #define NET_TCP_CONN_FREE_TMR_TX_IDLE NET_TCP_CONN_CLOSE_TMR_TX_IDLE #define NET_TCP_CONN_FREE_TMR_TX_SILLY_WIN NET_TCP_CONN_CLOSE_TMR_TX_SILLY_WIN #define NET_TCP_CONN_FREE_TMR_TX_ZERO_WIN NET_TCP_CONN_CLOSE_TMR_TX_ZERO_WIN #define NET_TCP_CONN_FREE_TMR_TX_ACK_DLY NET_TCP_CONN_CLOSE_TMR_TX_ACK_DLY #define NET_TCP_CONN_FREE_TMR_RE_TX NET_TCP_CONN_CLOSE_TMR_RE_TX #define NET_TCP_CONN_FREE_TMR_KEEP_ALIVE NET_TCP_CONN_CLOSE_TMR_KEEP_ALIVE #define NET_TCP_CONN_FREE_TMR_TIMEOUT NET_TCP_CONN_CLOSE_TMR_TIMEOUT #define NET_TCP_CONN_FREE_TMR_ALL NET_TCP_CONN_CLOSE_TMR_ALL #define NET_TCP_CONN_FREE_TMR_MASK NET_TCP_CONN_CLOSE_TMR_MASK #define NET_TCP_CONN_FREE_ALL NET_TCP_CONN_CLOSE_ALL /* ********************************************************************************************************* * LOCAL CONSTANTS ********************************************************************************************************* */ /*$PAGE*/ /* ********************************************************************************************************* * LOCAL DATA TYPES ********************************************************************************************************* */ /* ********************************************************************************************************* * TCP SEQUENCE CODE DATA TYPE ********************************************************************************************************* */ typedef CPU_INT08U NET_TCP_SEQ_CODE; /* ********************************************************************************************************* * TCP ACKNOWLEDGEMENT CODE DATA TYPE ********************************************************************************************************* */ typedef CPU_INT08U NET_TCP_ACK_CODE; /* ********************************************************************************************************* * TCP CONNECTION RESET CODE DATA TYPE ********************************************************************************************************* */ typedef CPU_INT08U NET_TCP_RESET_CODE; /* ********************************************************************************************************* * TCP WINDOW CODE DATA TYPE ********************************************************************************************************* */ typedef CPU_INT08U NET_TCP_WIN_CODE; /* ********************************************************************************************************* * TCP CALCULATION CODE DATA TYPE ********************************************************************************************************* */ typedef CPU_INT08U NET_TCP_CALC_CODE; /* ********************************************************************************************************* * TCP CONFIGURATION CODE DATA TYPE ********************************************************************************************************* */ typedef CPU_INT08U NET_TCP_CFG_CODE; /* ********************************************************************************************************* * TCP CLOSE CODE DATA TYPE ********************************************************************************************************* */ typedef CPU_INT16U NET_TCP_CLOSE_CODE; /* ********************************************************************************************************* * TCP FREE CODE DATA TYPE ********************************************************************************************************* */ typedef NET_TCP_CLOSE_CODE NET_TCP_FREE_CODE; /* ********************************************************************************************************* * LOCAL TABLES ********************************************************************************************************* */ /* ********************************************************************************************************* * LOCAL GLOBAL VARIABLES ********************************************************************************************************* */ /*$PAGE*/ /* ********************************************************************************************************* * LOCAL FUNCTION PROTOTYPES ********************************************************************************************************* */ /* --------- RX FNCTS --------- */ #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) static void NetTCP_RxPktValidateBuf (NET_BUF_HDR *pbuf_hdr, NET_ERR *perr); #endif static void NetTCP_RxPktValidate (NET_BUF *pbuf, NET_BUF_HDR *pbuf_hdr, NET_TCP_HDR *ptcp_hdr, NET_ERR *perr); static void NetTCP_RxPktValidateOpt (NET_BUF_HDR *pbuf_hdr, NET_TCP_HDR *ptcp_hdr, CPU_INT08U tcp_hdr_len_size, NET_ERR *perr); static CPU_BOOLEAN NetTCP_RxPktValidateOptMaxSegSize (NET_BUF_HDR *pbuf_hdr, CPU_INT08U *popt, CPU_INT08U *popt_len, NET_ERR *perr); static void NetTCP_RxPktDemuxSeg (NET_BUF *pbuf, NET_BUF_HDR *pbuf_hdr, NET_ERR *perr); static void NetTCP_RxPktConnHandler (NET_BUF *pbuf, NET_BUF_HDR *pbuf_hdr, NET_ERR *perr); static void NetTCP_RxPktConnHandlerListen (NET_TCP_CONN *pconn, NET_BUF *pbuf, NET_BUF_HDR *pbuf_hdr, NET_ERR *perr); static void NetTCP_RxPktConnHandlerSyncRxd (NET_TCP_CONN *pconn, NET_BUF *pbuf, NET_BUF_HDR *pbuf_hdr, NET_ERR *perr); static void NetTCP_RxPktConnHandlerSyncTxd (NET_TCP_CONN *pconn, NET_BUF *pbuf, NET_BUF_HDR *pbuf_hdr, NET_ERR *perr); static void NetTCP_RxPktConnHandlerConn (NET_TCP_CONN *pconn, NET_BUF *pbuf, NET_BUF_HDR *pbuf_hdr, NET_ERR *perr); static void NetTCP_RxPktConnHandlerFinWait1 (NET_TCP_CONN *pconn, NET_BUF *pbuf, NET_BUF_HDR *pbuf_hdr, NET_ERR *perr); static void NetTCP_RxPktConnHandlerFinWait2 (NET_TCP_CONN *pconn, NET_BUF *pbuf, NET_BUF_HDR *pbuf_hdr, NET_ERR *perr); static void NetTCP_RxPktConnHandlerClosing (NET_TCP_CONN *pconn, NET_BUF *pbuf, NET_BUF_HDR *pbuf_hdr, NET_ERR *perr); static void NetTCP_RxPktConnHandlerTimeWait (NET_TCP_CONN *pconn, NET_BUF *pbuf, NET_BUF_HDR *pbuf_hdr, NET_ERR *perr); static void NetTCP_RxPktConnHandlerCloseWait (NET_TCP_CONN *pconn, NET_BUF *pbuf, NET_BUF_HDR *pbuf_hdr, NET_ERR *perr); static void NetTCP_RxPktConnHandlerLastAck (NET_TCP_CONN *pconn, NET_BUF *pbuf, NET_BUF_HDR *pbuf_hdr, NET_ERR *perr); /*$PAGE*/ static void NetTCP_RxPktConnHandlerSeg (NET_TCP_CONN *pconn, NET_TCP_ACK_CODE ack_code, NET_BUF *pbuf, NET_BUF_HDR *pbuf_hdr, NET_ERR *perr); static void NetTCP_RxPktConnHandlerCfgConn (NET_TCP_CONN *pconn); static void NetTCP_RxPktConnHandlerRxQ_Sync (NET_TCP_CONN *pconn, NET_BUF *pbuf, NET_BUF_HDR *pbuf_hdr, NET_ERR *perr); static void NetTCP_RxPktConnHandlerRxQ_Conn (NET_TCP_CONN *pconn, NET_BUF *pbuf, NET_BUF_HDR *pbuf_hdr, NET_ERR *perr); static void NetTCP_RxPktConnHandlerRxQ_AppData (NET_TCP_CONN *pconn, NET_ERR *perr); static void NetTCP_RxPktConnHandlerTxWinRemote (NET_TCP_CONN *pconn, NET_TCP_ACK_CODE ack_code, NET_BUF_HDR *pbuf_hdr, NET_ERR *perr); static void NetTCP_RxPktConnHandlerReTxQ (NET_TCP_CONN *pconn, NET_TCP_ACK_CODE ack_code, NET_BUF_HDR *pbuf_hdr, NET_ERR *perr); static CPU_BOOLEAN NetTCP_RxPktConnHandlerListenQ_IsAvail(NET_TCP_CONN *pconn, NET_ERR *perr); static void NetTCP_RxPktConnHandlerSignalConn (NET_TCP_CONN *pconn, NET_TCP_CONN_STATE state, NET_ERR *perr); static void NetTCP_RxPktConnHandlerSignalClose (NET_TCP_CONN *pconn, CPU_BOOLEAN data_avail, NET_ERR *perr); static NET_TCP_SEQ_CODE NetTCP_RxPktConnIsValidSeq (NET_TCP_CONN *pconn, NET_BUF_HDR *pbuf_hdr, NET_ERR *perr); static NET_TCP_ACK_CODE NetTCP_RxPktConnIsValidAck (NET_TCP_CONN *pconn, NET_BUF_HDR *pbuf_hdr, NET_ERR *perr); static NET_TCP_RESET_CODE NetTCP_RxPktConnIsValidReset (NET_TCP_CONN *pconn, NET_BUF_HDR *pbuf_hdr, NET_ERR *perr); static void NetTCP_RxPktFree (NET_BUF *pbuf_q); static void NetTCP_RxPktDiscard (NET_BUF *pbuf, NET_ERR *perr); static void NetTCP_RxConnWinSizeCfg (NET_TCP_CONN *pconn); static void NetTCP_RxConnWinSizeCfgUpdateTh (NET_TCP_CONN *pconn); static void NetTCP_RxConnWinSizeHandler (NET_TCP_CONN *pconn, NET_BUF_HDR *pbuf_hdr, NET_TCP_WIN_SIZE win_update_size, NET_TCP_WIN_CODE win_update_code); /*$PAGE*/ /* --------- TX FNCTS --------- */ static void NetTCP_TxConnWinSizeCfg (NET_TCP_CONN *pconn); static void NetTCP_TxConnWinSizeCfgCongCtrl (NET_TCP_CONN *pconn); static void NetTCP_TxConnWinSizeCfgMinTh (NET_TCP_CONN *pconn); static void NetTCP_TxConnWinSizeHandlerCfgd (NET_TCP_CONN *pconn, NET_TCP_WIN_SIZE win_update_size, NET_TCP_WIN_CODE win_update_code, NET_ERR *perr); static void NetTCP_TxConnWinSizeHandlerCongCtrl (NET_TCP_CONN *pconn, NET_BUF_HDR *pbuf_hdr, NET_TCP_ACK_CODE ack_code, NET_TCP_WIN_SIZE win_update_size, NET_TCP_WIN_CODE win_update_code, NET_ERR *perr); static void NetTCP_TxConnWinSizeCalcSlowStartTh (NET_TCP_CONN *pconn); static void NetTCP_TxConnWinSizeCongSet (NET_TCP_CONN *pconn, NET_TCP_WIN_CODE win_inc_code); static void NetTCP_TxConnWinSizeCongInc (NET_TCP_CONN *pconn, NET_BUF_HDR *pbuf_hdr, NET_TCP_WIN_SIZE win_update_size, NET_TCP_WIN_CODE win_inc_code); static void NetTCP_TxConnWinSizeUpdateAvail (NET_TCP_CONN *pconn); static void NetTCP_TxConnWinSizeDupAckCtrlReset (NET_TCP_CONN *pconn); static void NetTCP_TxConnWinSizeDupAckCtrlUpdate (NET_TCP_CONN *pconn, NET_BUF_HDR *pbuf_hdr, CPU_BOOLEAN reset_ctr); static void NetTCP_TxConnWinSizeZeroWinHandler (NET_TCP_CONN *pconn, NET_TCP_WIN_CODE win_update_code, NET_TCP_CLOSE_CODE close_code); static void NetTCP_TxConnWinSizeZeroWinTimeout (void *pconn_timeout); /*$PAGE*/ static void NetTCP_TxConnSync (NET_TCP_CONN *pconn, NET_BUF_HDR *pbuf_hdr, NET_ERR *perr); static void NetTCP_TxConnClose (NET_TCP_CONN *pconn, NET_ERR *perr); static void NetTCP_TxConnAck (NET_TCP_CONN *pconn, NET_BUF_HDR *pbuf_hdr, NET_TCP_ACK_CODE tx_ack_code, NET_TCP_CLOSE_CODE close_code, NET_ERR *perr); static void NetTCP_TxConnAckDlyReset (NET_TCP_CONN *pconn, CPU_BOOLEAN tmr_free); static void NetTCP_TxConnAckDlyTimeout (void *pconn_timeout); static void NetTCP_TxConnReset (NET_TCP_CONN *pconn, NET_BUF_HDR *pbuf_hdr, NET_TCP_RESET_CODE tx_reset_code, NET_TCP_CLOSE_CODE close_code, NET_ERR *perr); static void NetTCP_TxConnProbe (NET_TCP_CONN *pconn, CPU_BOOLEAN tx_probe_data_octet, NET_TCP_CLOSE_CODE close_code, NET_ERR *perr); static void NetTCP_TxConnTxQ (NET_TCP_CONN *pconn, NET_BUF_HDR *pbuf_hdr, NET_TCP_ACK_CODE tx_ack_code, CPU_BOOLEAN tx_q_timeout, NET_TCP_CLOSE_CODE close_code, NET_ERR *perr); static void NetTCP_TxConnTxQ_TimeoutIdle (void *pconn_timeout); static void NetTCP_TxConnTxQ_TimeoutIdleSet (NET_TCP_CONN *pconn); static void NetTCP_TxConnTxQ_TimeoutIdleClr (NET_TCP_CONN *pconn); static void NetTCP_TxConnTxQ_TimeoutSillyWin (void *pconn_timeout); static void NetTCP_TxConnReTxQ (NET_TCP_CONN *pconn, CPU_BOOLEAN re_tx_q_timeout, NET_TCP_CLOSE_CODE close_code, NET_ERR *perr); static void NetTCP_TxConnReTxQ_Timeout (void *pconn_timeout); static void NetTCP_TxConnReTxQ_TimeoutSet (NET_TCP_CONN *pconn, CPU_BOOLEAN re_tx_q_timeout, NET_TCP_CLOSE_CODE close_code, NET_ERR *perr); static void NetTCP_TxConnPrepareSegAddrs (NET_TCP_CONN *pconn, CPU_INT08U *psrc_port, CPU_INT08U *psrc_addr, CPU_INT16U src_port_len, CPU_INT16U src_addr_len, CPU_INT08U *pdest_port, CPU_INT08U *pdest_addr, CPU_INT16U dest_port_len, CPU_INT16U dest_addr_len, NET_ERR *perr); /*$PAGE*/ static void NetTCP_TxConnRTT_Init (NET_TCP_CONN *pconn); static void NetTCP_TxConnRTT_Reset (NET_TCP_CONN *pconn); static void NetTCP_TxConnRTT_CalcUpdate (NET_TCP_CONN *pconn); static void NetTCP_TxConnRTO_Init (NET_TCP_CONN *pconn); static void NetTCP_TxConnRTO_CfgMaxTimeout (NET_TCP_CONN *pconn); static void NetTCP_TxConnRTO_CalcUpdate (NET_TCP_CONN *pconn); static NET_TCP_TIMEOUT_MS NetTCP_TxConnRTO_CalcBackOff (NET_TCP_CONN *pconn, NET_TCP_TIMEOUT_MS rto_ms); static void NetTCP_TxConnRTT_RTO_Init (NET_TCP_CONN *pconn); static void NetTCP_TxConnRTT_RTO_Calc (NET_TCP_CONN *pconn, NET_TCP_CALC_CODE calc_code, NET_TCP_TX_RTT_TS_MS rtt_ts_txd_ms, NET_TCP_TX_RTT_TS_MS rtt_ts_rxd_ms); static void NetTCP_TxPktHandler (NET_BUF *pbuf, NET_IP_ADDR src_addr, NET_TCP_PORT_NBR src_port, NET_IP_ADDR dest_addr, NET_TCP_PORT_NBR dest_port, NET_TCP_SEQ_NBR seq_nbr, NET_TCP_SEQ_NBR ack_nbr, NET_TCP_WIN_SIZE win_size, NET_IP_TOS TOS, NET_IP_TTL TTL, CPU_INT16U flags_tcp, CPU_INT16U flags_ip, void *popts_tcp, void *popts_ip, NET_ERR *perr); #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) static void NetTCP_TxPktValidate (NET_BUF *pbuf, NET_BUF_HDR *pbuf_hdr, NET_TCP_PORT_NBR src_port, NET_TCP_PORT_NBR dest_port, NET_TCP_SEQ_NBR seq_nbr, NET_TCP_SEQ_NBR ack_nbr, NET_TCP_WIN_SIZE win_size, CPU_INT16U flags_tcp, void *popts_tcp, NET_ERR *perr); static void NetTCP_TxPktValidateOpt (void *popts_tcp, CPU_INT16U flags_tcp, NET_ERR *perr); static void NetTCP_TxPktValidateOptMaxSegSize (void *popt_tcp, CPU_INT08U *popt_len, void **popt_next, CPU_INT16U flags_tcp, NET_ERR *perr); #endif /*$PAGE*/ static void NetTCP_TxPkt (NET_BUF *pbuf, NET_BUF_HDR *pbuf_hdr, NET_IP_ADDR src_addr, NET_TCP_PORT_NBR src_port, NET_IP_ADDR dest_addr, NET_TCP_PORT_NBR dest_port, NET_TCP_SEQ_NBR seq_nbr, NET_TCP_SEQ_NBR ack_nbr, NET_TCP_WIN_SIZE win_size, NET_IP_TOS TOS, NET_IP_TTL TTL, CPU_INT16U flags_tcp, CPU_INT16U flags_ip, void *popts_tcp, void *popts_ip, NET_ERR *perr); static CPU_INT08U NetTCP_TxPktPrepareOpt (void *popts_tcp, CPU_INT08U *popt_hdr, NET_ERR *perr); static void NetTCP_TxPktPrepareOptMaxSegSize (void *popts_tcp, CPU_INT08U *popt_hdr, CPU_INT08U *popt_len, void **popt_next, NET_ERR *perr); static void NetTCP_TxPktPrepareHdr (NET_BUF *pbuf, NET_BUF_HDR *pbuf_hdr, CPU_INT16U tcp_hdr_len_tot, CPU_INT08U tcp_opt_len_tot, NET_IP_ADDR src_addr, NET_TCP_PORT_NBR src_port, NET_IP_ADDR dest_addr, NET_TCP_PORT_NBR dest_port, NET_TCP_SEQ_NBR seq_nbr, NET_TCP_SEQ_NBR ack_nbr, NET_TCP_WIN_SIZE win_size, CPU_INT16U flags_tcp, CPU_INT32U *ptcp_hdr_opts, NET_ERR *perr); static void NetTCP_TxPktFree (NET_BUF *pbuf_q); static void NetTCP_TxPktDiscard (NET_BUF *pbuf, NET_ERR *perr); /*$PAGE*/ /* ------ TCP CONN FNCTS ------ */ static void NetTCP_ConnCfg (NET_TCP_CONN *pconn, NET_TCP_CFG_CODE cfg_code); static void NetTCP_ConnCfgMaxSegSize (NET_TCP_CONN *pconn); static void NetTCP_ConnClose (NET_TCP_CONN *pconn, NET_BUF_HDR *pbuf_hdr, CPU_BOOLEAN close_conn_app, NET_TCP_CLOSE_CODE close_code); static void NetTCP_ConnCloseHandler (NET_TCP_CONN *pconn, CPU_BOOLEAN close_conn_app, NET_TCP_CLOSE_CODE close_code); static void NetTCP_ConnCloseTimeout (void *pconn_timeout); static void NetTCP_ConnClosingTimeoutDataAvail (void *pconn_timeout); static void NetTCP_ConnFreeHandler (NET_TCP_CONN *pconn, NET_TCP_FREE_CODE free_code); static void NetTCP_ConnFreeTmr (NET_TCP_CONN *pconn, NET_TCP_FREE_CODE free_code); static void NetTCP_ConnFreeBufQ (NET_BUF **pbuf_q_head, NET_BUF **pbuf_q_tail); static void NetTCP_ConnClr (NET_TCP_CONN *pconn); static void NetTCP_ConnCopy (NET_TCP_CONN *pconn_dest, NET_TCP_CONN *pconn_src); static void NetTCP_ConnDiscard (NET_TCP_CONN *pconn); /* ********************************************************************************************************* * LOCAL CONFIGURATION ERRORS ********************************************************************************************************* */ /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_Init() * * Description : (1) Initialize Transmission Control Protocol Layer : * * (a) Perform TCP Module/OS initialization * (b) Perform TCP Module/BSP initialization * (c) Initialize TCP connection pool * (d) Initialize TCP connection table * (e) Initialize TCP statistics & error counters * * * Argument(s) : perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_NONE TCP layer successfully initialized. * * - RETURNED BY NetOS_TCP_Init() : -- * NET_OS_ERR_INIT_TCP_RX_Q TCP receive queue(s) NOT * successfully initialized. * NET_OS_ERR_INIT_TCP_RX_Q_TIMEOUT TCP receive queue timeout(s) NOT * successfully configured. * NET_OS_ERR_INIT_TCP_TX_Q TCP transmit queue(s) NOT * successfully initialized. * NET_OS_ERR_INIT_TCP_TX_Q_TIMEOUT TCP transmit queue timeout(s) NOT * successfully configured. * * Return(s) : none. * * Caller(s) : Net_Init(). * * This function is an INTERNAL network protocol suite function & MUST NOT be called by * application function(s). * * Note(s) : (2) TCP connection pool MUST be initialized PRIOR to initializing the pool with pointers to * TCP connections. ********************************************************************************************************* */ void NetTCP_Init (NET_ERR *perr) { NET_TCP_CONN *pconn; NET_TCP_CONN_QTY i; NET_ERR stat_err; /* --------------- PERFORM TCP/OS INIT --------------- */ NetOS_TCP_Init(perr); /* Create TCP obj(s). */ if (*perr != NET_OS_ERR_NONE) { return; } /* --------------- PERFORM TCP/BSP INIT --------------- */ NetTCP_InitTxSeqNbr(); /* Init tx seq nbr ctr. */ /* ------------- INIT TCP CONN POOL/STATS ------------- */ NetTCP_ConnPoolPtr = (NET_TCP_CONN *)0; /* Init-clr TCP conn pool (see Note #2). */ NetStat_PoolInit((NET_STAT_POOL *)&NetTCP_ConnPoolStat, (NET_STAT_POOL_QTY) NET_TCP_CFG_NBR_CONN, (NET_ERR *)&stat_err); /* ---------------- INIT TCP CONN TBL ----------------- */ pconn = &NetTCP_ConnTbl[0]; for (i = 0; i < NET_TCP_CFG_NBR_CONN; i++) { pconn->Type = NET_TCP_TYPE_CONN; /* Init each TCP conn type/id--NEVER modify. */ pconn->ID = (NET_TCP_CONN_ID)i; pconn->ConnState = NET_TCP_CONN_STATE_FREE; /* Init each TCP conn as free/NOT used. */ pconn->Flags = NET_TCP_FLAG_NONE; #if (NET_DBG_CFG_MEM_CLR_EN == DEF_ENABLED) NetTCP_ConnClr(pconn); #endif /* Free each TCP conn to TCP conn pool (see Note #2). */ pconn->NextPtr = (void *)NetTCP_ConnPoolPtr; NetTCP_ConnPoolPtr = (NET_TCP_CONN *)pconn; pconn++; } /*$PAGE*/ /* ------------- INIT TCP STAT & ERR CTRS ------------- */ #if (NET_CTR_CFG_STAT_EN == DEF_ENABLED) NetTCP_StatRxPktCtr = 0; NetTCP_StatRxSegProcessedCtr = 0; NetTCP_StatTxSegCtr = 0; NetTCP_StatTxSegConnSyncCtr = 0; NetTCP_StatTxSegConnCloseCtr = 0; NetTCP_StatTxSegConnAckCtr = 0; NetTCP_StatTxSegConnResetCtr = 0; NetTCP_StatTxSegConnProbeCtr = 0; NetTCP_StatTxSegConnTxQ_Ctr = 0; NetTCP_StatTxSegConnReTxQ_Ctr = 0; #endif #if (NET_CTR_CFG_ERR_EN == DEF_ENABLED) NetTCP_ErrNullPtrCtr = 0; NetTCP_ErrNoneAvailCtr = 0; NetTCP_ErrNotUsedCtr = 0; NetTCP_ErrRxHdrLenCtr = 0; NetTCP_ErrRxHdrSegLenCtr = 0; NetTCP_ErrRxHdrPortSrcCtr = 0; NetTCP_ErrRxHdrPortDestCtr = 0; NetTCP_ErrRxHdrFlagsCtr = 0; NetTCP_ErrRxHdrChkSumCtr = 0; NetTCP_ErrRxHdrOptsCtr = 0; NetTCP_ErrRxDestCtr = 0; NetTCP_ErrRxPktDiscardedCtr = 0; NetTCP_ErrTxOptTypeCtr = 0; NetTCP_ErrTxPktDiscardedCtr = 0; NetTCP_ErrConnInvalidCtr = 0; NetTCP_ErrConnInvalidOpCtr = 0; NetTCP_ErrConnInvalidStateCtr = 0; NetTCP_ErrConnCloseCtr = 0; #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) NetTCP_ErrRxInvalidBufIxCtr = 0; NetTCP_ErrRxHdrDataLenCtr = 0; NetTCP_ErrTxInvalidProtocolCtr = 0; NetTCP_ErrTxInvalidSizeCtr = 0; NetTCP_ErrTxInvalidBufIxCtr = 0; NetTCP_ErrTxHdrDataLenCtr = 0; NetTCP_ErrTxHdrPortSrcCtr = 0; NetTCP_ErrTxHdrPortDestCtr = 0; NetTCP_ErrTxHdrFlagsCtr = 0; NetTCP_ErrTxHdrOptLenCtr = 0; NetTCP_ErrTxHdrOptCfgCtr = 0; NetTCP_ErrConnInvalidTypeCtr = 0; #endif #endif *perr = NET_TCP_ERR_NONE; } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_Rx() * * Description : (1) Process received segments & demultiplex to socket or application layer connection : * * (a) Validate TCP packet * (b) Demultiplex TCP packet to TCP connection * (c) Handle/Process TCP segment * (d) Return TCP error code(s) * * (2) Although TCP data units are typically referred to as 'segments' (see RFC #793, Section 3.1), * the term 'TCP packet' (see RFC #1983, 'packet') is used for TCP Receive until the packet is * validated as a TCP segment. * * * Argument(s) : pbuf Pointer to network buffer that received TCP packet. * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_NONE TCP segment successfully received & processed. * NET_ERR_INIT_INCOMPLETE Network initialization NOT complete. * * ---- RETURNED BY NetTCP_RxPktDiscard() : ----- * NET_ERR_RX Receive error; packet discarded. * * Return(s) : none. * * Caller(s) : NetIP_RxPktDemuxDatagram(). * * This function is an INTERNAL network protocol suite function & MUST NOT be called by * application function(s). * * Note(s) : (3) NetTCP_Rx() blocked until network initialization completes. * * (4) TCP receive statistics updated in NetTCP_RxPktConnHandler(); do NOT re-update. * * (a) Network buffer already freed & error counter already incremented in previous * function(s). * * See also 'NetTCP_RxPktConnHandler() Note #2'. ********************************************************************************************************* */ /*$PAGE*/ void NetTCP_Rx (NET_BUF *pbuf, NET_ERR *perr) { #if ((((NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) && \ (NET_CTR_CFG_ERR_EN == DEF_ENABLED)) || \ (NET_CTR_CFG_STAT_EN == DEF_ENABLED)) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif NET_BUF_HDR *pbuf_hdr; NET_TCP_HDR *ptcp_hdr; #if (NET_ERR_CFG_ARG_CHK_EXT_EN == DEF_ENABLED) if (Net_InitDone != DEF_YES) { /* If init NOT complete, exit rx (see Note #3). */ *perr = NET_ERR_INIT_INCOMPLETE; return; } #endif #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) /* ------------------- VALIDATE PTR ------------------- */ if (pbuf == (NET_BUF *)0) { NetTCP_RxPktDiscard(pbuf, perr); NET_CTR_ERR_INC(NetTCP_ErrNullPtrCtr); return; } #endif NET_CTR_STAT_INC(NetTCP_StatRxPktCtr); /* ----------------- VALIDATE TCP PKT ----------------- */ pbuf_hdr = &pbuf->Hdr; #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) NetTCP_RxPktValidateBuf(pbuf_hdr, perr); /* Validate rx'd buf. */ switch (*perr) { case NET_TCP_ERR_NONE: break; case NET_ERR_INVALID_PROTOCOL: case NET_BUF_ERR_INVALID_IX: default: NetTCP_RxPktDiscard(pbuf, perr); return; /* Prevent 'break NOT reachable' compiler warning. */ } #endif ptcp_hdr = (NET_TCP_HDR *)&pbuf->Data[pbuf_hdr->TCP_UDP_HdrDataIx]; NetTCP_RxPktValidate(pbuf, pbuf_hdr, ptcp_hdr, perr); /* Validate rx'd pkt. */ /* -------------- DEMUX PKT TO TCP CONN --------------- */ switch (*perr) { case NET_TCP_ERR_NONE: NetTCP_RxPktDemuxSeg(pbuf, pbuf_hdr, perr); break; case NET_TCP_ERR_INVALID_PORT_NBR: case NET_TCP_ERR_INVALID_LEN_HDR: case NET_TCP_ERR_INVALID_LEN_SEG: case NET_TCP_ERR_INVALID_LEN_DATA: case NET_TCP_ERR_INVALID_FLAG: case NET_TCP_ERR_INVALID_CHK_SUM: case NET_TCP_ERR_INVALID_OPT_LEN: case NET_TCP_ERR_INVALID_OPT_END: case NET_TCP_ERR_INVALID_OPT_NBR: default: NetTCP_RxPktDiscard(pbuf, perr); return; /* Prevent 'break NOT reachable' compiler warning. */ } /* --------------- HANDLE TCP PKT/CONN ---------------- */ switch (*perr) { /* Chk err from NetTCP_RxPktDemuxSeg(). */ case NET_TCP_ERR_NONE: NetTCP_RxPktConnHandler(pbuf, pbuf_hdr, perr); break; case NET_CONN_ERR_INVALID_FAMILY: case NET_CONN_ERR_INVALID_CONN: case NET_ERR_RX_DEST: default: NetTCP_RxPktDiscard(pbuf, perr); return; /* Prevent 'break NOT reachable' compiler warning. */ } /*$PAGE*/ /* ---------------- RTN TCP ERR CODES ----------------- */ switch (*perr) { /* Chk err from NetTCP_RxPktConnHandler(). */ case NET_TCP_ERR_NONE: /* See Note #4. */ break; case NET_TCP_ERR_CONN_DATA_NONE: case NET_TCP_ERR_CONN_DATA_VALID: case NET_TCP_ERR_CONN_CLOSED: case NET_TCP_ERR_CONN_RESET_VALID: *perr = NET_TCP_ERR_NONE; break; case NET_ERR_RX: /* See Note #4a. */ return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_ERR_INIT_INCOMPLETE: case NET_TCP_ERR_INVALID_CONN: case NET_TCP_ERR_CONN_NOT_USED: case NET_TCP_ERR_INVALID_CONN_STATE: default: NetTCP_RxPktDiscard(pbuf, perr); return; /* Prevent 'break NOT reachable' compiler warning. */ } } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_RxAppData() * * Description : (1) Deframe application data from TCP connection's enqueued TCP segment(s) : * * (a) Wait on TCP connection application receive queue for packet buffer(s) * (b) Deframe application data from enqueued TCP segment(s) * (c) Update TCP connection application receive queue * (1) Free TCP packet buffer(s) * (d) Update TCP connection receive window * * * Argument(s) : conn_id_tcp Handle identifier of TCP connection to receive application data. * * pdata_buf Pointer to application buffer to receive application data. * --------- Argument validated in NetSock_RxDataHandlerStream(). * * data_buf_len Size of application receive buffer (in octets). * ------------ Argument validated in NetSock_RxDataHandlerStream(). * * flags Flags to select receive options; bit-field flags logically OR'd : * ----- * NET_TCP_FLAG_NONE No TCP receive flags selected. * NET_TCP_FLAG_RX_DATA_PEEK Receive TCP application data without consuming * the data; i.e. data NOT removed from TCP * connection's application receive queue(s). * NET_TCP_FLAG_RX_BLOCK Receive TCP application data with blocking, * if flag set; without blocking, if clear. * * Argument validated in NetSock_RxDataHandlerStream(). * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_NONE TCP connection application data successfully * deframed; check return value for number of * data octets received. * NET_TCP_ERR_INVALID_CONN_STATE Invalid TCP connection state. * NET_TCP_ERR_INVALID_CONN_OP Invalid TCP connection operation. * NET_TCP_ERR_CONN_NOT_USED TCP connection NOT currently used. * NET_TCP_ERR_CONN_FAIL TCP connection operation(s) failed. * NET_TCP_ERR_CONN_DATA_INVALID TCP connection application receive queue contains * invalid or improperly sequenced data. * * NET_TCP_ERR_RX_Q_CLOSED TCP connection application receive queue closed * (see Note #3e3B). * NET_TCP_ERR_RX_Q_EMPTY TCP connection application receive queue empty * (see Note #3e2B). * * ------- RETURNED BY NetTCP_ConnIsUsed() : ------- * NET_ERR_INIT_INCOMPLETE Network initialization NOT complete. * NET_TCP_ERR_INVALID_CONN Invalid TCP connection number. * * ------ RETURNED BY NetOS_TCP_RxQ_Wait() : ------- * NET_TCP_ERR_RX_Q_SIGNAL_ABORT TCP connection receive queue signal aborted; * TCP connection closed/aborted. * NET_TCP_ERR_RX_Q_SIGNAL_FAULT TCP connection receive queue signal fault. * * ---------- RETURNED BY NetOS_Lock() : ----------- * NET_OS_ERR_LOCK Network access NOT acquired. * * Return(s) : Total application data octets deframed into receive buffer, if NO errors. * * 0, otherwise. * * Caller(s) : NetSock_RxDataHandlerStream(). * * This function is an INTERNAL network protocol suite function & SHOULD NOT be called by * application function(s). *$PAGE* * Note(s) : (2) NetTCP_RxAppData() blocked until network initialization completes. * * See 'NetTCP_ConnIsUsed() Note #1'. * * (3) RFC #793, Section 3.9 'Event Processing : RECEIVE Call' specifies how to handle receive * data requests from the application layer : * * (a) For the "CLOSED STATE ... return 'error: connection does not exist'". * * (b) (1) For the "LISTEN STATE, SYN-SENT STATE, SYN-RECEIVED STATE ... queue for processing * after entering ESTABLISHED state". * * (2) The application layer may request to receive application data before the TCP * connection enters the connected state. Such requests will block or return * no data. * * See also 'NetTCP_RxPktConnHandlerRxQ_AppData() Note #1bA' * & Note #5b2. * * (c) For the "ESTABLISHED STATE, FIN-WAIT-1 STATE, FIN-WAIT-2 STATE" ... * * (1) ... that "if insufficient incoming segments are queued to satisfy the request, * queue the request". * * (2) "Reassemble queued incoming segments into receive buffer and return to user." * * (d) For the "CLOSE-WAIT STATE ... since the remote side has already sent FIN, RECEIVEs * must be satisfied by the text already on hand, but not yet delivered to the user. * If no text is awaiting delivery, the RECEIVE will get a 'error: connection closing' * response. Otherwise, any remaining text can be used to satisfy the RECEIVE". * * (e) (1) For the "CLOSING STATE, LAST-ACK STATE, TIME-WAIT STATE ... return 'error: * connection closing'". * * (2) Typically, these states will have already received ALL remainining closing * data from the closing remote host. * * (A) However, in case all receive data from the remote host has NOT yet been * received, application layer receives from these states are permitted until * the TCP connection's sequence receive state is closed & the TCP connection's * application receive queue is empty. * * (B) If all receive data from the remote host has NOT yet been received but the * application receive queue is currently empty, return application receive * queue empty error(s). * * (3) Once a TCP connection has closed & ALL receive data has been received by the * application layer : * * (A) Close the TCP connection; * (B) Return application receive queue closed error. *$PAGE* * (4) (a) RFC #793, Section 3.3 states that : * * (1) "Every octet of data sent over a TCP connection has a sequence number." * (2) In addition, "SYN and FIN ... control information ... [is] implicitly assign[ed] * sequence numbers ... [but] is not physically carried in the segment data space". * * (A) "For sequence number purposes, the SYN is considered to occur before the * first actual data octet of the segment in which it occurs," ... * (B) "While the FIN is considered to occur after the last actual data octet in * a segment in which it occurs." * * (3) "The segment length (SEG.LEN) includes both data and sequence space occupying * controls. * * * ----- ------------------------ Synchronization * ^ | Initial SEQ # (SYN) | <--- Sequence Number * | ------------------------ (see Note #4a2A) * | | Data Octet # 1 | --- * | | Data Octet # 2 | ^ * | Data Octet # 3 | | * TCP Connection | . | | Data Octet * Sequences | . | | Sequence Number(s) * (see Note #4a) | . | | (see Note #4a1) * | Data Octet # (N - 2) | | * | | Data Octet # (N - 1) | v * | | Data Octet # N | --- Closing * | ------------------------ Sequence Number * v | Close SEQ # (FIN) | <--- (see Note #4a2B) * ----- ------------------------ * * * See also 'NetTCP_TxConnSync() Note #3' * & 'NetTCP_TxConnClose() Note #2'. * * (b) Therefore, since TCP synchronization or close sequence numbers are NOT actual data * sequences or octets that are transmitted or received; segments which include these * TCP control sequence numbers MUST adjust TCP data sequence numbers & TCP segment * lengths by these TCP control sequence numbers. * * (1) A TCP data segment MUST adjust its data index by the possible synchronization * control sequence. * * (A) On a received TCP segment's first data read, the segment's base data index * is NOT offset -- even though for a synchronization segment, this base data * index starts on the sequence number following the synchronization control * sequence. * * (B) On any additional received TCP segment data reads, the segment's base data * index MUST be offset by possible synchronization control sequence. * * (2) TCP connections & TCP data segments MUST advance their sequence numbers & adjust * their segment lengths by possible TCP control sequences. * * (A) A TCP connection MUST advance its sequence numbers by : * * (1) Possible synchronization sequence; but only on the first, initial access * to a TCP synchronization segment; ... * (2) All accessed data sequence(s); ... * (3) Possible closing sequence; which SHOULD only occur once on the * last access to a TCP closing segment. * * (B) (1) A received TCP segment MUST advance its sequence numbers by : * * (a) Possible synchronization sequence; but only on the first, initial * access to a TCP synchronization segment; ... * (b) All accessed data sequence(s). * * (2) (a) A received TCP segment MUST adjust its total segment length by : * * (1) Possible synchronization sequence; but only on the first, initial * access to a TCP synchronization segment; ... * (2) All accessed data sequence(s). * * (3) Possible closing sequence adjustment MAY be ignored since * all effective segment data handling occurs prior to accessing any * trailing closing sequence. * * (b) A received TCP segment MUST adjust its data segment length ONLY by : * * (b) All accessed data sequence(s). *$PAGE* * (5) (a) Stream-type connections receive all data octets in one or more non-distinct packets. * In other words, the application data is NOT bounded by any specific packet(s); rather, * it is contiguous & sequenced from one packet to the next. * * (b) Therefore, the TCP connection receive queue is signaled ONLY when data is received for * a connection where data was previously unavailable. * * (c) Consequently, it is typical -- but NOT absolutely required -- that a single application * task only receive or request to receive application data from a TCP connection. * * See also 'net_sock.c NetSock_RxDataHandlerStream() Note #2'. * * (6) Since pointer arithmetic is based on the specific pointer data type & inherent pointer * data type size, pointer arithmetic operands : * * (a) MUST be in terms of the specific pointer data type & data type size; ... * (b) SHOULD NOT & in some cases MUST NOT be cast to other data types or data type sizes. * * (7) RFC #793, Section 3.7 'Data Communication : Managing the Window' states that "the window * sent in each segment indicates the range of sequence numbers the sender of the window * (the data receiver) is currently prepared to accept. There is an assumption that this * is related to the currently available data buffer space available for this connection * ... One strategy would be to ... [update the] information when the window is larger". * * See also 'NetTCP_RxPktConnHandlerRxQ_Sync() Note #5', * 'NetTCP_RxPktConnHandlerRxQ_Conn() Note #6', * & 'NetTCP_RxConnWinSizeHandler() Note #2a'. * * (8) (a) #### Since segments enqueued to a TCP connection's application receive queue have * already been acknowledged to the remote host & since no mechanism exists for a TCP * connection to re-request previously acknowledged segments, any TCP connection whose * application receive queue becomes corrupted MUST be closed to prevent the application * layer from receiving the corrupted data. * * (b) For any internal errors where the TCP connection's application receive queue is NOT * corrupted, the TCP connection is NOT closed. Thus, the application layer may try to * re-receive the application data. * * However, a TCP connection may deadlock due to persistant internal errors -- i.e. the * internal errors prevent the TCP connection from deframing application data from the * application receive queue which also prevents the TCP connection from receiving * additional application data (see Note ####). Thus, exception handling code in the * application layer SHOULD eventually detect & close any TCP connection deadlocked due * to internal errors. ********************************************************************************************************* */ /*$PAGE*/ CPU_INT16U NetTCP_RxAppData (NET_TCP_CONN_ID conn_id_tcp, void *pdata_buf, CPU_INT16U data_buf_len, CPU_INT16U flags, NET_ERR *perr) { #if ((NET_CTR_CFG_ERR_EN == DEF_ENABLED) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) CPU_INT16U seg_len_close; NET_TCP_SEQ_NBR seq_nbr_cur; #endif CPU_BOOLEAN block; CPU_BOOLEAN conn_closed; CPU_BOOLEAN q_prevly_empty; CPU_BOOLEAN peek; CPU_BOOLEAN frag_adv; NET_TCP_CONN *pconn; NET_BUF *pbuf_head; NET_BUF *pbuf_seg; NET_BUF *pbuf_seg_prev; NET_BUF *pbuf_seg_next; NET_BUF *pbuf_frag; NET_BUF *pbuf_frag_next; NET_BUF_HDR *pbuf_seg_hdr; NET_BUF_HDR *pbuf_seg_prev_hdr; NET_BUF_HDR *pbuf_seg_next_hdr; NET_BUF_HDR *pbuf_frag_hdr; NET_BUF_SIZE seg_len_avail; NET_BUF_SIZE data_ix_frag; NET_BUF_SIZE data_ix_pkt; NET_BUF_SIZE data_len_pkt; CPU_INT16U data_len_buf_rem; CPU_INT16U data_len_tot; CPU_INT16U seg_len_data; CPU_INT16U seg_len_data_rem; CPU_INT16U seg_len_data_tot; CPU_INT16U seg_len_sync; CPU_INT16U seg_len_sync_init; CPU_INT08U *p_data; NET_TCP_SEQ_NBR seq_nbr_init; NET_TCP_SEQ_NBR seq_nbr_ix; NET_ERR err; /*$PAGE*/ #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) /* -------------- VALIDATE TCP CONN USED -------------- */ (void)NetTCP_ConnIsUsed(conn_id_tcp, perr); if (*perr != NET_TCP_ERR_NONE) { return (0); } #endif /* ---------------- VALIDATE TCP CONN ----------------- */ pconn = &NetTCP_ConnTbl[conn_id_tcp]; conn_closed = DEF_NO; switch (pconn->ConnState) { case NET_TCP_CONN_STATE_FREE: NET_CTR_ERR_INC(NetTCP_ErrNotUsedCtr); *perr = NET_TCP_ERR_CONN_NOT_USED; return (0); /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_CLOSED: /* See Note #3a. */ NET_CTR_ERR_INC(NetTCP_ErrConnInvalidOpCtr); *perr = NET_TCP_ERR_INVALID_CONN_OP; return (0); /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_LISTEN: /* See Note #3b. */ case NET_TCP_CONN_STATE_SYNC_RXD: case NET_TCP_CONN_STATE_SYNC_RXD_PASSIVE: case NET_TCP_CONN_STATE_SYNC_RXD_ACTIVE: case NET_TCP_CONN_STATE_SYNC_TXD: case NET_TCP_CONN_STATE_CONN: /* See Note #3c. */ case NET_TCP_CONN_STATE_FIN_WAIT_1: case NET_TCP_CONN_STATE_FIN_WAIT_2: break; case NET_TCP_CONN_STATE_CLOSE_WAIT: /* See Note #3d. */ case NET_TCP_CONN_STATE_CLOSING: /* See Note #3e. */ case NET_TCP_CONN_STATE_TIME_WAIT: case NET_TCP_CONN_STATE_LAST_ACK: case NET_TCP_CONN_STATE_CLOSING_DATA_AVAIL: /* See Note #3e3. */ if (pconn->RxQ_State == NET_TCP_RX_Q_STATE_CONN_CLOSED) { conn_closed = DEF_YES; } break; case NET_TCP_CONN_STATE_NONE: default: NetTCP_ConnClose((NET_TCP_CONN *)pconn, (NET_BUF_HDR *)0, (CPU_BOOLEAN )DEF_YES, (NET_TCP_CLOSE_CODE)NET_TCP_CONN_CLOSE_ALL); NET_CTR_ERR_INC(NetTCP_ErrConnInvalidStateCtr); *perr = NET_TCP_ERR_INVALID_CONN_STATE; return (0); /* Prevent 'break NOT reachable' compiler warning. */ } /*$PAGE*/ /* ------------ WAIT ON TCP CONN APP RX Q ------------- */ if (pconn->RxQ_App_Head == (NET_BUF *)0) { /* If no rx'd data pkts; ... */ if (conn_closed != DEF_NO) { /* ... & conn closed (see Note #3e3), ... */ if (pconn->ConnState == NET_TCP_CONN_STATE_CLOSING_DATA_AVAIL) { /* ... close TCP conn (see Note #3e3A) ... */ NetTCP_ConnCloseHandler(pconn, pconn->ConnCloseAppFlag, NET_TCP_CONN_CLOSE_ALL); } *perr = NET_TCP_ERR_RX_Q_CLOSED; /* ... & rtn rx Q closed err (see Note #3e3B); ... */ return (0); } block = DEF_BIT_IS_SET(flags, NET_TCP_FLAG_RX_BLOCK); if (block != DEF_YES) { /* ... & non-blocking rx ... */ *perr = NET_TCP_ERR_RX_Q_EMPTY; /* ... rtn rx Q empty err. */ return (0); } NetOS_Unlock(); NetOS_TCP_RxQ_Wait(conn_id_tcp, perr); NetOS_Lock(&err); if ( err != NET_OS_ERR_NONE) { *perr = err; /* Rtn err from NetOS_Lock(). */ return (0); } if (*perr != NET_TCP_ERR_NONE) { return (0); /* Rtn err from NetOS_TCP_RxQ_Wait(). */ } if (pconn->RxQ_App_Head == (NET_BUF *)0) { /* If still NO rx'd data pkts, ... */ *perr = NET_TCP_ERR_RX_Q_EMPTY; /* ... rtn rx Q empty err. */ return (0); } q_prevly_empty = DEF_YES; } else { NetOS_TCP_RxQ_Clr(conn_id_tcp, &err); /* Clr any possible async rx Q signal. */ q_prevly_empty = DEF_NO; } /*$PAGE*/ /* ----------- DEFRAME TCP CONN RX APP DATA ----------- */ pbuf_head = (NET_BUF *) pconn->RxQ_App_Head; pbuf_seg = (NET_BUF *) pbuf_head; pbuf_seg_prev = (NET_BUF *) 0; p_data = (CPU_INT08U *) pdata_buf; data_len_buf_rem = (CPU_INT16U ) data_buf_len; data_len_tot = (CPU_INT16U ) 0; #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) pbuf_seg_hdr = (NET_BUF_HDR *)&pbuf_seg->Hdr; seq_nbr_cur = (NET_TCP_SEQ_NBR) pbuf_seg_hdr->TCP_SeqNbr; #endif while ((pbuf_seg != (NET_BUF *)0) && /* Copy app rx data from TCP conn q'd seg(s). */ (data_len_buf_rem > 0)) { pbuf_seg_hdr = (NET_BUF_HDR *)&pbuf_seg->Hdr; pbuf_seg_next = (NET_BUF *) pbuf_seg_hdr->NextPrimListPtr; pbuf_frag = (NET_BUF *) pbuf_seg; #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) if (seq_nbr_cur != pbuf_seg_hdr->TCP_SeqNbr) { /* If next q'd seg's seq nbr NOT consecutive, ... */ /* ... close TCP conn (see Note #8a). */ NetTCP_ConnClose((NET_TCP_CONN *)pconn, (NET_BUF_HDR *)0, (CPU_BOOLEAN )DEF_YES, (NET_TCP_CLOSE_CODE)NET_TCP_CONN_CLOSE_ALL); *perr = NET_TCP_ERR_CONN_DATA_INVALID; return (0); } #endif /* Init seg lens. */ seg_len_data = pbuf_seg_hdr->TCP_SegLenData; seg_len_data_rem = seg_len_data; seg_len_data_tot = 0; seg_len_sync = 0; seg_len_sync_init = 0; if (pbuf_seg_hdr->TCP_SegSync == DEF_YES) { seg_len_sync = NET_TCP_SEG_LEN_SYNC; } /* If still init seg len, cfg init sync seg len. */ if (pbuf_seg_hdr->TCP_SegLen == pbuf_seg_hdr->TCP_SegLenInit) { seg_len_sync_init = seg_len_sync; } #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) seg_len_close = 0; if (pbuf_seg_hdr->TCP_SegClose == DEF_YES) { seg_len_close = NET_TCP_SEG_LEN_CLOSE; } #endif /* Calc start seq nbr ix into seg data (see Note #4b1). */ seq_nbr_init = (NET_TCP_SEQ_NBR)(pbuf_seg_hdr->TCP_SeqNbrInit + seg_len_sync - seg_len_sync_init); seq_nbr_ix = (NET_TCP_SEQ_NBR)(pbuf_seg_hdr->TCP_SeqNbr - seq_nbr_init); data_ix_frag = (NET_BUF_SIZE ) seq_nbr_ix; frag_adv = DEF_YES; while ((pbuf_frag != (NET_BUF *)0) && (frag_adv == DEF_YES)) { pbuf_frag_hdr = (NET_BUF_HDR *)&pbuf_frag->Hdr; /* While seg's frag data ix >= cur frag data len ... */ if (data_ix_frag >= (NET_BUF_SIZE)pbuf_frag_hdr->DataLen) { data_ix_frag -= (NET_BUF_SIZE)pbuf_frag_hdr->DataLen; pbuf_frag_next = (NET_BUF *)pbuf_frag_hdr->NextBufPtr; pbuf_frag = (NET_BUF *)pbuf_frag_next; /* ... adv to next seg frag. */ } else { frag_adv = DEF_NO; } } /*$PAGE*/ while ((pbuf_frag != (NET_BUF *)0) && /* Copy app rx data from avail seg pkt buf(s). */ (data_len_buf_rem > 0) && (seg_len_data_rem > 0)) { pbuf_frag_hdr = (NET_BUF_HDR *)&pbuf_frag->Hdr; pbuf_frag_next = (NET_BUF *) pbuf_frag_hdr->NextBufPtr; seg_len_avail = pbuf_frag_hdr->DataLen - data_ix_frag; if (seg_len_avail > (NET_BUF_SIZE)seg_len_data_rem) { /* If seg frag pkt data len > rem seg len, ... */ seg_len_avail = (NET_BUF_SIZE)seg_len_data_rem; /* ... limit copy to rem seg len. */ } if (data_len_buf_rem > seg_len_avail) { /* If rem data buf len > seg frag pkt data len, ... */ data_len_pkt = (NET_BUF_SIZE)seg_len_avail; /* ... copy all pkt buf data len. */ } else { data_len_pkt = (NET_BUF_SIZE)data_len_buf_rem; /* Else limit copy to rem data buf len. */ } /* Calc ix into seg frag's data. */ data_ix_pkt = (NET_BUF_SIZE)pbuf_frag_hdr->DataIx + data_ix_frag; data_ix_frag = 0; NetBuf_DataRd((NET_BUF *) pbuf_frag, (NET_BUF_SIZE) data_ix_pkt, (NET_BUF_SIZE) data_len_pkt, (CPU_INT08U *) p_data, (NET_ERR *)&err); if ( err != NET_BUF_ERR_NONE) { /* See Note #7b. */ *perr = NET_TCP_ERR_CONN_FAIL; return (0); } /* Update data ptr & lens. */ p_data += data_len_pkt; /* MUST NOT cast ptr operand (see Note #6b). */ data_len_tot += (CPU_INT16U)data_len_pkt; data_len_buf_rem -= (CPU_INT16U)data_len_pkt; seg_len_data_tot += (CPU_INT16U)data_len_pkt; seg_len_data_rem -= (CPU_INT16U)data_len_pkt; pbuf_frag = pbuf_frag_next; } if (data_len_buf_rem > 0) { /* If rem data buf len > 0, adv to next q'd seg. */ pbuf_seg_prev = pbuf_seg; pbuf_seg = pbuf_seg_next; #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) if (seg_len_data_tot != seg_len_data) { /* If calc'd seg data len != actual seg data len, ... */ /* ... close TCP conn (see Note #8a). */ NetTCP_ConnClose((NET_TCP_CONN *)pconn, (NET_BUF_HDR *)0, (CPU_BOOLEAN )DEF_YES, (NET_TCP_CLOSE_CODE)NET_TCP_CONN_CLOSE_ALL); *perr = NET_TCP_ERR_CONN_DATA_INVALID; return (0); } /* Adv seq nbr by cur seg's : ... */ seq_nbr_cur += (NET_TCP_SEQ_NBR)seg_len_sync_init; /* ... init sync seg len (see Note #4b2A1), ... */ seq_nbr_cur += (NET_TCP_SEQ_NBR)seg_len_data; /* ... data seg len (see Note #4b2A2), ... */ seq_nbr_cur += (NET_TCP_SEQ_NBR)seg_len_close; /* ... & close seg len (see Note #4b2A3). */ #endif } } /*$PAGE*/ /* ----------- UPDATE TCP CONN APP RX Q ----------- */ peek = DEF_BIT_IS_SET(flags, NET_TCP_FLAG_RX_DATA_PEEK); if (peek == DEF_YES) { /* If peek opt req'd ... */ if (q_prevly_empty == DEF_YES) { /* ... & TCP conn app rx Q prev'ly empty, ... */ NetOS_TCP_RxQ_Signal(conn_id_tcp, &err); /* ... signal app rx Q ... */ /* ... to negate non-consuming peek (see Note #5b). */ if (err != NET_TCP_ERR_NONE) { /* If app rx Q signal failed, ... */ peek = DEF_NO; /* ... consume pkt buf(s). */ } } } if (peek != DEF_YES) { /* If peek opt NOT req'd, unlink ALL seg pkt buf(s) */ /* .. whose data was entirely consumed. */ if (pbuf_seg != (NET_BUF *)0) { /* If TCP conn app rx Q NOT empty after data rd(s) */ if (seg_len_data_rem > 0) { /* .. & cur seg's rem data len > 0; .. */ pbuf_seg_hdr->PrevPrimListPtr = (void *)0; /* .. unlink from prev q'd seg(s), .. */ pconn->RxQ_App_Head = (NET_BUF *)pbuf_seg;/* .. set new TCP conn app rx Q head, .. */ /* .. & update seg's seq nbr (see Note #4b2B1) .. */ pbuf_seg_hdr->TCP_SeqNbr += (CPU_INT32U)(seg_len_data_tot + seg_len_sync_init); /* .. & seg's seg lens (see Note #4b2B2). */ pbuf_seg_hdr->TCP_SegLen -= (CPU_INT16U)(seg_len_data_tot + seg_len_sync_init); pbuf_seg_hdr->TCP_SegLenData -= (CPU_INT16U) seg_len_data_tot; if (pbuf_seg_prev != (NET_BUF *)0) { /* If prev q'd seg(s) avail, ... */ pbuf_seg_prev_hdr = &pbuf_seg_prev->Hdr; pbuf_seg_prev_hdr->NextPrimListPtr = (void *)0; /* ... unlink from app rx Q ... */ NetTCP_RxPktFree(pbuf_head); /* ... & free ALL rd seg pkt buf(s). */ } } else { /* Else if cur seg's rem data len = 0 ... */ if (pbuf_seg_next != (NET_BUF *)0) { /* ... & rem rx q'd seg(s) avail, ... */ /* ... unlink cur seg from rem q'd seg(s) ... */ pbuf_seg_next_hdr = &pbuf_seg_next->Hdr; pbuf_seg_next_hdr->PrevPrimListPtr = (void *)0; pbuf_seg_hdr->NextPrimListPtr = (void *)0; /* ... set new TCP conn app rx Q head; ... */ pconn->RxQ_App_Head = (NET_BUF *)pbuf_seg_next; } else { /* ... & NO rem rx q'd seg(s) avail, ... */ /* ... unlink ALL q'd seg(s); ... */ pconn->RxQ_App_Head = (NET_BUF *)0; pconn->RxQ_App_Tail = (NET_BUF *)0; } NetTCP_RxPktFree(pbuf_head); /* ... & free rd seg pkt buf(s) from app rx Q. */ } } else { /* Else clr app rx Q ... */ pconn->RxQ_App_Head = (NET_BUF *)0; pconn->RxQ_App_Tail = (NET_BUF *)0; NetTCP_RxPktFree(pbuf_head); /* ... & free ALL seg pkt buf(s) from app rx Q. */ } /* --------- UPDATE TCP CONN RX WIN SIZE ---------- */ /* Inc TCP conn's rx win size (see Note #7). */ NetTCP_RxConnWinSizeHandler((NET_TCP_CONN *)pconn, (NET_BUF_HDR *)0, (NET_TCP_WIN_SIZE)data_len_tot, (NET_TCP_WIN_CODE)NET_TCP_CONN_RX_WIN_INC); } *perr = NET_TCP_ERR_NONE; return (data_len_tot); } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxConnReq() * * Description : (1) Transmit TCP connection request : * * (a) Configure TCP connections' IP parameters * (b) Transmit TCP connection request * (c) Update TCP connection state * * * Argument(s) : conn_id_tcp Handle identifier of TCP connection to transmit connection request. * * TOS Specific TOS to transmit TCP/IP packet * (see 'net_ip.h IP HEADER TYPE OF SERVICE (TOS) DEFINES'). * * TTL Specific TTL to transmit TCP/IP packet (see RFC #1122, Section 3.2.1.7) : * * NET_IP_HDR_TTL_MIN 1 minimum TTL transmit value * NET_IP_HDR_TTL_MAX 255 maximum TTL transmit value * NET_IP_HDR_TTL_DFLT default TTL transmit value * NET_IP_HDR_TTL_NONE 0 replace with default TTL * * flags_ip Flags to select IP transmit options; bit-field flags logically OR'd : * * NET_IP_FLAG_NONE No IP transmit flags selected. * NET_IP_FLAG_TX_DONT_FRAG Set IP 'Don't Frag' flag. * * popts_ip Pointer to one or more IP options configuration data structures : * * NULL NO IP transmit options configuration. * NET_IP_OPT_CFG_ROUTE_TS Route &/or Internet Timestamp options * configuration. * NET_IP_OPT_CFG_SECURITY Security options configuration * (see 'net_ip.c Note #1f'). * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_CONN_NOT_USED TCP connection NOT currently used. * NET_TCP_ERR_INVALID_CONN_STATE Invalid TCP connection state. * NET_TCP_ERR_INVALID_CONN_OP Invalid TCP connection operation. * * ---- RETURNED BY NetTCP_ConnIsUsed() : ----- * NET_ERR_INIT_INCOMPLETE Network initialization NOT complete. * NET_TCP_ERR_INVALID_CONN Invalid TCP connection number. * * ---- RETURNED BY NetTCP_TxConnSync() : ----- * NET_TCP_ERR_NONE TCP connection request successfully * transmitted. * NET_TCP_ERR_CONN_FAIL TCP connection operation(s) failed. * NET_TCP_ERR_CONN_FAULT TCP connection fault; connection(s) aborted. * NET_TCP_ERR_NONE_AVAIL Resources NOT available. * NET_CONN_ERR_INVALID_FAMILY Invalid connection family. * NET_CONN_ERR_INVALID_ADDR Invalid TCP connection address. * NET_CONN_ERR_INVALID_ADDR_LEN Invalid TCP connection address length. * NET_TCP_ERR_TX TCP transmit error. * NET_ERR_TX Transmit error. * * Return(s) : none. * * Caller(s) : NetSock_ConnHandlerStream(). * * This function is an INTERNAL network protocol suite function & SHOULD NOT be called by * application function(s). * * Note(s) : (2) NetTCP_TxConnReq() blocked until network initialization completes. * * See 'NetTCP_ConnIsUsed() Note #1'. * * (3) On ANY errors, return error to TCP connection caller & allow caller to retry or close * connection(s) but do NOT close TCP connection. * * (4) #### IP options currently NOT implemented. ********************************************************************************************************* */ /*$PAGE*/ void NetTCP_TxConnReq (NET_TCP_CONN_ID conn_id_tcp, NET_IP_TOS TOS, NET_IP_TTL TTL, CPU_INT16U flags_ip, void *popts_ip, NET_ERR *perr) { #if ((NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) && \ (NET_CTR_CFG_ERR_EN == DEF_ENABLED) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif NET_TCP_CONN *pconn; #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) /* -------------- VALIDATE TCP CONN USED -------------- */ (void)NetTCP_ConnIsUsed(conn_id_tcp, perr); if (*perr != NET_TCP_ERR_NONE) { return; } #endif pconn = &NetTCP_ConnTbl[conn_id_tcp]; #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) /* ------------- VALIDATE TCP CONN STATE -------------- */ switch (pconn->ConnState) { case NET_TCP_CONN_STATE_FREE: NET_CTR_ERR_INC(NetTCP_ErrNotUsedCtr); *perr = NET_TCP_ERR_CONN_NOT_USED; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_CLOSED: case NET_TCP_CONN_STATE_LISTEN: break; case NET_TCP_CONN_STATE_SYNC_RXD: case NET_TCP_CONN_STATE_SYNC_RXD_PASSIVE: case NET_TCP_CONN_STATE_SYNC_RXD_ACTIVE: case NET_TCP_CONN_STATE_SYNC_TXD: case NET_TCP_CONN_STATE_CONN: case NET_TCP_CONN_STATE_FIN_WAIT_1: case NET_TCP_CONN_STATE_FIN_WAIT_2: case NET_TCP_CONN_STATE_CLOSING: case NET_TCP_CONN_STATE_TIME_WAIT: case NET_TCP_CONN_STATE_CLOSE_WAIT: case NET_TCP_CONN_STATE_LAST_ACK: case NET_TCP_CONN_STATE_CLOSING_DATA_AVAIL: NET_CTR_ERR_INC(NetTCP_ErrConnInvalidOpCtr); *perr = NET_TCP_ERR_INVALID_CONN_OP; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_NONE: default: NetTCP_ConnClose((NET_TCP_CONN *)pconn, (NET_BUF_HDR *)0, (CPU_BOOLEAN )DEF_YES, (NET_TCP_CLOSE_CODE)NET_TCP_CONN_CLOSE_CONN_ALL); NET_CTR_ERR_INC(NetTCP_ErrConnInvalidStateCtr); *perr = NET_TCP_ERR_INVALID_CONN_STATE; return; /* Prevent 'break NOT reachable' compiler warning. */ } #endif /* -------------- CFG TCP CONN IP PARAMS -------------- */ pconn->TxIP_TOS = TOS; pconn->TxIP_TTL = TTL; pconn->TxIP_Flags = flags_ip; (void)&popts_ip; /* Prevent compiler warning (see Note #4). */ /* ----------------- TX TCP CONN REQ ------------------ */ NetTCP_TxConnSync((NET_TCP_CONN *)pconn, (NET_BUF_HDR *)0, (NET_ERR *)perr); if (*perr != NET_TCP_ERR_NONE) { /* See Note #3. */ return; } /* -------------- UPDATE TCP CONN STATE --------------- */ pconn->ConnState = NET_TCP_CONN_STATE_SYNC_TXD; *perr = NET_TCP_ERR_NONE; } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxConnAppData() * * Description : (1) Prepare & transmit data from Application layer(s) via TCP connection : * * (a) Validate application data * * (b) Transmit application data via TCP Transmit : * (1) Validate TCP connection * (2) Wait on TCP connection transmit queue * (3) Prepare TCP data segment(s) : * (A) TCP segment addresses * (B) Prepare application data : * (1) Get buffer(s) for application data * (2) Copy application data into TCP packet buffer(s) * (3) Update TCP segment data controls * (4) Update application data controls * (4) Update TCP connection transmit window * * (c) Update TCP connection : * (1) Append TCP transmit segment(s) to TCP connection transmit queue * (2) Update TCP connection sequence number(s) * * (d) Transmit TCP data segment(s) via TCP Transmit * * * Argument(s) : conn_id_tcp Handle identifier of connection to transmit application data. * * p_data Pointer to application data. * ------ Argument validated in NetSock_TxDataHandlerStream(). * * data_len Length of application data (in octets) [see Note #5]. * -------- Argument validated in NetSock_TxDataHandlerStream(). * * flags Flags to select transmit options; bit-field flags logically OR'd : * ----- * NET_TCP_FLAG_NONE No TCP transmit flags selected. * NET_TCP_FLAG_TX_BLOCK Transmit TCP application data with blocking, * if flag set; without blocking, if clear. * * Argument validated in NetSock_TxDataHandlerStream(). * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_NONE Application data successfully prepared & * transmitted via TCP layer. * NET_TCP_ERR_NULL_PTR Argument 'p_data' passed a NULL pointer. * NET_TCP_ERR_INVALID_DATA_SIZE Argument 'data_len' passed an invalid size. * NET_TCP_ERR_INVALID_CONN_STATE Invalid TCP connection state. * NET_TCP_ERR_INVALID_CONN_OP Invalid TCP connection operation. * NET_TCP_ERR_CONN_NOT_USED TCP connection NOT currently used. * NET_TCP_ERR_CONN_FAULT TCP connection fault; connection(s) aborted. * NET_TCP_ERR_CONN_DATA_INVALID TCP connection application transmit queue contains * invalid or improperly sequenced data. * NET_TCP_ERR_TX_Q_FULL TCP connection transmit queue full. * NET_TCP_ERR_TX_Q_SUSPEND TCP connection transmit queue temporarily suspended. * NET_TCP_ERR_NONE_AVAIL Resources NOT available. * * -------- RETURNED BY NetTCP_ConnIsUsed() : --------- * NET_ERR_INIT_INCOMPLETE Network initialization NOT complete. * NET_TCP_ERR_INVALID_CONN Invalid TCP connection number. * * -------- RETURNED BY NetOS_TCP_TxQ_Wait() : -------- * NET_TCP_ERR_TX_Q_SIGNAL_ABORT TCP connection transmit queue signal aborted; * TCP connection closed/aborted. * NET_TCP_ERR_TX_Q_SIGNAL_FAULT TCP connection transmit queue signal fault. * * --------- RETURNED BY NetTCP_TxConnTxQ() : --------- * NET_TCP_ERR_CONN_CLOSE TCP connection closed. * NET_TCP_ERR_CONN_ACK_NONE TCP connection acknowledgement NOT requested. * NET_TCP_ERR_CONN_ACK_DLYD TCP connection acknowledgement transmit delayed. * NET_TCP_ERR_CONN_ACK_PREVLY_TXD TCP connection acknowledgement previously * transmitted for segment. * NET_TCP_ERR_CONN_ACK_INVALID TCP connection acknowledgement NOT valid for * current TCP connection state. * NET_TCP_ERR_INVALID_LEN_SEG Invalid TCP sequence-segment length. * * NET_CONN_ERR_INVALID_FAMILY Invalid connection family. * NET_CONN_ERR_INVALID_ADDR Invalid TCP connection address. * NET_CONN_ERR_INVALID_ADDR_LEN Invalid TCP connection address length. * * NET_TCP_ERR_TX TCP transmit error. * NET_ERR_TX Transmit error. * * ------------ RETURNED BY NetOS_Lock() : ------------ * NET_OS_ERR_LOCK Network access NOT acquired. * * Return(s) : Number of data octets transmitted, if NO errors. * * 0, otherwise. * * Caller(s) : NetSock_TxDataHandlerStream(). * * This function is an INTERNAL network protocol suite function & SHOULD NOT be called by * application function(s). *$PAGE* * Note(s) : (2) NetTCP_TxConnAppData() blocked until network initialization completes. * * See 'NetTCP_ConnIsUsed() Note #1'. * * (3) RFC #793, Section 3.9 'Event Processing : SEND Call' specifies how to handle transmit * data requests from the application layer : * * (a) For the "CLOSED STATE ... return 'error: connection does not exist'". * * (b) For the "LISTEN STATE" ... * * (1) "If the foreign socket is specified, then" ... * * (A) "Change the connection from passive to active," ... * (B) "select an ISS" ... * (C) "Send a SYN segment," ... * (D) "Set" ... * (1) "SND.UNA to ISS," ... * (2) "SND.NXT to ISS+1." * (E) "Enter SYN-SENT state." * (F) "Data associated with SEND may be" ... * (1) "sent with SYN segment" ... * (2) (a) "or queued for transmission after entering ESTABLISHED state." * (b) "If there is no room to queue the request, respond with 'error: * insufficient resources'." * * (2) "If Foreign [sic] socket was not specified, then return 'error: foreign socket * unspecified'." * * (A) #### NOT yet implemented; see also * 'net_sock.c NetSock_TxDataHandlerStream() Note #2aA'. * * (c) For the "SYN-SENT STATE, SYN-RECEIVED STATE" ... * * (1) "Queue the data for transmission after entering ESTABLISHED state." * (2) "If no space to queue, respond with 'error: insufficient resources'." * * (d) For the "ESTABLISHED STATE, CLOSE-WAIT STATE" ... * * (1) (A) "Segmentize the buffer" ... * (B) "and send it with a piggybacked ackknowledgment" ... * (1) "(acknowledgment value = RCV.NXT)" * (2) "If there is no insufficient space to remember this buffer, simply return * 'error: insufficient resources'." * * (e) For the "FIN-WAIT-1 STATE, FIN-WAIT-2 STATE, CLOSING STATE, LAST-ACK STATE, * TIME-WAIT STATE" ... * * (1) "return 'error: connection closing'" ... * (2) "and do not service request." *$PAGE* * (4) TCP segments with transmit data are sequenced into the TCP connection's transmit * queue to await transmission at the appropriate time(s) [see 'NetTCP_TxConnTxQ() * Note #1b']. * * (a) Transmit TCP segments are inserted into a doubly-linked Transmit Queue, ordered * consecutively by sequence number(s) [see also Note #4b]. * * (1) 'TxQ_Head' points to the head of the Transmit Queue; * 'TxQ_Tail' points to the tail of the Transmit Queue. * * (2) Segment buffers' 'PrevPrimListPtr' & 'NextPrimListPtr' doubly-link each * segment to form the Transmit Queue. * * (b) Transmit data is packetized into TCP segments in sequence-order. Therefore, new * transmit segments are sequenced after previously packetized segments starting at * the tail of the Transmit Queue. * * (c) Segments at the head of the Transmit Queue are available & ready to be transmitted * via the Internet Transmit Layer. * * * | | * |<------- TCP Connection Transmit Queue ------->| * | (see Note #4) | * * Segments Ready Segments Sequenced * to Transmit into Transmit Queue * start at head starting at tail * (see Note #4c) (see Note #4b) * * | NextPrimListPtr | * | (see Note #4a2) | * v | v * | * Head of ------- ------- v ------- ------- (see Note #4a1) * Transmit ---->| |------>| |------>| |------>| | * Queue | | | | | | | | Tail of * | |<------| |<------| |<------| |<---- Transmit * (see Note #4a1) | | | | ^ | | | | Queue * | | | | | | | | | * ------- ------- | ------- ------- * | * PrevPrimListPtr * (see Note #4a2) * *$PAGE* * (5) 'data_len' of 0 octets NOT allowed. * * (6) Certain network connections MUST periodically suspend network transmit(s) to handle * network receive packet(s). To protect TCP connections from transmit corruption while * suspended, ALL TCP data transmits & TCP transmit queue handling MUST be blocked for * suspended connections until the connection is no longer suspended. * * See also 'NetTCP_TxConnTxQ() Note #10b2A2'. * * (7) (a) RFC #793, Section 3.8 'Interfaces : User/TCP Interface : TCP User Commands : Send' * states that : * * (1) "If the PUSH flag is set, the data must be transmitted promptly to the receiver, * and the PUSH bit will be set in the last TCP segment created from the buffer." * * (2) "If the PUSH flag is not set, the data may be combined with data from subsequent * SENDs for transmission efficiency." * * (b) RFC #1122, Section 4.2.2.2 states that : * * (1) "When an application issues a series of SEND calls without setting the PUSH * flag, the TCP MAY aggregate the data internally without sending it." * * (2) "The PSH bit is not a record marker and is independent of segment boundaries. * The transmitter SHOULD collapse successive PSH bits when it packetizes data, * to send the largest possible segment. * * (3) (A) "A TCP MAY implement PUSH flags on SEND calls." * * (B) (1) "If PUSH flags are not implemented, then the sending TCP: ... * * (a) must not buffer data indefinitely, and ... * (b) MUST set the PSH bit in the last buffered segment (i.e., when * there is no more queued data to be sent)." * * (1) However, NO RFC specifies whether the PUSH bit should be * set ONLY in the last buffered segment. Therefore, is is * assumed that the PUSH bit MAY be set in the last buffered * segment of each call to SEND. * * (2) "When the PUSH flag is not implemented on SEND calls, i.e., when * the application/TCP interface uses a pure streaming model, * responsibility for aggregating any tiny data fragments to form * reasonable sized segments is partially borne by the application * layer." * * (4) (A) "An application program is logically required to set the PUSH flag in a * SEND call whenever it needs to force delivery of the data to avoid a * communication deadlock. However, a TCP SHOULD send a maximum-sized * segment whenever possible, to improve performance." * * (B) "Generally, an interactive application protocol must set the PUSH flag * at least in the last SEND call in each command or response sequence." * * (8) Since pointer arithmetic is based on the specific pointer data type & inherent pointer * data type size, pointer arithmetic operands : * * (a) MUST be in terms of the specific pointer data type & data type size; ... * (b) SHOULD NOT & in some cases MUST NOT be cast to other data types or data type sizes. * * (9) Network buffers allocated for TCP connection transmit SHOULD be fully used even if * the TCP connection's configured transmit window is zero. * * See also 'NetTCP_TxConnWinSizeHandlerCfgd() Note #2'. * * (10) (a) Since segments enqueued to a TCP connection's transmit queue have already been * reported as transmitted to the application & since no mechanism exists for a TCP * connection to re-request previously transmitted data, any TCP connection whose * transmit queue becomes corrupted MUST be closed to force the application layer to * abort &/or recover from the corrupted data. * * (b) For any internal errors where the TCP connection's transmit queue is NOT corrupted, * the TCP connection is NOT closed. Thus, the application layer may try to re-transmit * the application data. * * (c) On ANY transmit error, any remaining application data transmit is immediately aborted. * * See also 'NetTCP_TxConnTxQ() Note #12'. * & 'NetTCP_TxConnReTxQ() Note #11'. ********************************************************************************************************* */ /*$PAGE*/ CPU_INT16U NetTCP_TxConnAppData (NET_TCP_CONN_ID conn_id_tcp, void *p_data, CPU_INT16U data_len, CPU_INT16U flags, NET_ERR *perr) { #if ((NET_CTR_CFG_ERR_EN == DEF_ENABLED) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif NET_TCP_CONN *pconn; NET_IP_ADDR src_addr; NET_IP_ADDR dest_addr; NET_TCP_PORT_NBR src_port; NET_TCP_PORT_NBR dest_port; NET_BUF *pbuf; NET_BUF *pbuf_head; NET_BUF *pbuf_tail; NET_BUF *pbuf_q_tail; NET_BUF_HDR *pbuf_hdr; NET_BUF_HDR *pbuf_hdr_head; NET_BUF_HDR *pbuf_hdr_tail; NET_BUF_HDR *pbuf_hdr_q_tail; NET_BUF_SIZE buf_size_max; NET_BUF_SIZE buf_size_max_data; NET_BUF_SIZE buf_size_max_tail; NET_BUF_SIZE buf_size_max_tail_data; NET_BUF_SIZE data_ix_pkt; NET_BUF_SIZE data_ix_pkt_tail; NET_BUF_SIZE data_len_pkt_tail; NET_BUF_SIZE data_len_pkt_rem; NET_BUF_SIZE data_len_pkt; NET_BUF_SIZE data_len_mss; CPU_INT16U data_len_rem; CPU_INT16U data_len_tot; CPU_INT16U flags_tcp; CPU_INT08U *p_data_pkt; CPU_BOOLEAN tx_q_append; CPU_BOOLEAN tx_data; CPU_BOOLEAN tx_err; CPU_BOOLEAN block; NET_TCP_SEQ_NBR seq_nbr; NET_ERR err; #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) /* ---------------- VALIDATE APP DATA ----------------- */ if (p_data == (void *)0) { *perr = NET_TCP_ERR_NULL_PTR; return (0); } if (data_len < 1) { /* Validate data len (see Note #5). */ NET_CTR_ERR_INC(NetTCP_ErrTxInvalidSizeCtr); *perr = NET_TCP_ERR_INVALID_DATA_SIZE; return (0); } /* -------------- VALIDATE TCP CONN USED -------------- */ (void)NetTCP_ConnIsUsed(conn_id_tcp, perr); if (*perr != NET_TCP_ERR_NONE) { return (0); } #endif /*$PAGE*/ /* ---------------- VALIDATE TCP CONN ----------------- */ pconn = &NetTCP_ConnTbl[conn_id_tcp]; switch (pconn->ConnState) { /* Validate conn state. */ case NET_TCP_CONN_STATE_FREE: NET_CTR_ERR_INC(NetTCP_ErrNotUsedCtr); *perr = NET_TCP_ERR_CONN_NOT_USED; return (0); /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_CLOSED: /* See Note #3a. */ NET_CTR_ERR_INC(NetTCP_ErrConnInvalidOpCtr); *perr = NET_TCP_ERR_INVALID_CONN_OP; return (0); /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_LISTEN: /* See Note #3b. */ /* #### NOT yet implemented (see Note #3bA). */ *perr = NET_TCP_ERR_INVALID_CONN_OP; return (0); /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_SYNC_RXD: /* See Note #3c. */ case NET_TCP_CONN_STATE_SYNC_RXD_PASSIVE: case NET_TCP_CONN_STATE_SYNC_RXD_ACTIVE: case NET_TCP_CONN_STATE_SYNC_TXD: tx_data = DEF_NO; break; case NET_TCP_CONN_STATE_CONN: /* See Note #3d. */ case NET_TCP_CONN_STATE_CLOSE_WAIT: tx_data = DEF_YES; break; case NET_TCP_CONN_STATE_FIN_WAIT_1: /* See Note #3e. */ case NET_TCP_CONN_STATE_FIN_WAIT_2: case NET_TCP_CONN_STATE_CLOSING: case NET_TCP_CONN_STATE_TIME_WAIT: case NET_TCP_CONN_STATE_LAST_ACK: case NET_TCP_CONN_STATE_CLOSING_DATA_AVAIL: NET_CTR_ERR_INC(NetTCP_ErrConnInvalidOpCtr); *perr = NET_TCP_ERR_INVALID_CONN_OP; return (0); /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_NONE: default: NetTCP_ConnClose((NET_TCP_CONN *)pconn, (NET_BUF_HDR *)0, (CPU_BOOLEAN )DEF_YES, (NET_TCP_CLOSE_CODE)NET_TCP_CONN_CLOSE_ALL); NET_CTR_ERR_INC(NetTCP_ErrConnInvalidStateCtr); *perr = NET_TCP_ERR_INVALID_CONN_STATE; return (0); /* Prevent 'break NOT reachable' compiler warning. */ } switch (pconn->TxQ_State) { /* Validate tx Q state. */ case NET_TCP_TX_Q_STATE_CONN: case NET_TCP_TX_Q_STATE_CONN_CLOSING: break; case NET_TCP_TX_Q_STATE_CONN_SUSPEND: /* See Note #6. */ *perr = NET_TCP_ERR_TX_Q_SUSPEND; return (0); /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_TX_Q_STATE_NONE: case NET_TCP_TX_Q_STATE_CLOSED: case NET_TCP_TX_Q_STATE_CONN_CLOSED: default: *perr = NET_TCP_ERR_INVALID_CONN_STATE; return (0); /* Prevent 'break NOT reachable' compiler warning. */ } /*$PAGE*/ /* -------------- WAIT ON TCP CONN TX Q --------------- */ if (tx_data == DEF_YES) { /* If tx rdy, ... */ if (data_len < 1) { /* ... but NO tx data avail, ... */ /* ... tx q'd data OR immed ack; ... */ NetTCP_TxConnTxQ((NET_TCP_CONN *)pconn, (NET_BUF_HDR *)0, (NET_TCP_ACK_CODE )NET_TCP_CONN_TX_ACK_IMMED, (CPU_BOOLEAN )DEF_NO, (NET_TCP_CLOSE_CODE)NET_TCP_CONN_CLOSE_ALL, (NET_ERR *)perr); return (0); } /* ... Else if tx data avail, ... */ if (pconn->TxWinSizeCfgdRem < 1) { /* ... but tx Q full ... */ block = DEF_BIT_IS_SET(flags, NET_TCP_FLAG_TX_BLOCK); if (block != DEF_YES) { /* ... & non-blocking tx, ... */ *perr = NET_TCP_ERR_TX_Q_FULL; /* ... rtn tx Q full err. */ return (0); } NetOS_Unlock(); NetOS_TCP_TxQ_Wait(conn_id_tcp, perr); NetOS_Lock(&err); if ( err != NET_OS_ERR_NONE) { *perr = err; /* Rtn err from NetOS_Lock(). */ return (0); } if (*perr != NET_TCP_ERR_NONE) { return (0); /* Rtn err from NetOS_TCP_TxQ_Wait(). */ } if (pconn->TxWinSizeCfgdRem < 1) { /* If tx Q still full, ... */ *perr = NET_TCP_ERR_TX_Q_FULL; /* ... rtn tx Q full err. */ return (0); } } else { NetOS_TCP_TxQ_Clr(conn_id_tcp, &err); /* Clr any possible async tx Q signal. */ } } /* ------- PREPARE APP TX DATA INTO TCP SEG(S) -------- */ /* Prepare seg addrs. */ NetTCP_TxConnPrepareSegAddrs((NET_TCP_CONN *) pconn, (CPU_INT08U *)&src_port, (CPU_INT08U *)&src_addr, (CPU_INT16U ) sizeof(src_port), (CPU_INT16U ) sizeof(src_addr), (CPU_INT08U *)&dest_port, (CPU_INT08U *)&dest_addr, (CPU_INT16U ) sizeof(dest_port), (CPU_INT16U ) sizeof(dest_addr), (NET_ERR *)&err); if ( err != NET_TCP_ERR_NONE) { *perr = NET_TCP_ERR_CONN_FAULT; return (0); } /*$PAGE*/ pbuf_head = (NET_BUF *)0; pbuf_tail = (NET_BUF *)0; p_data_pkt = (CPU_INT08U *)p_data; tx_q_append = (CPU_BOOLEAN )DEF_YES; data_len_mss = (NET_BUF_SIZE)pconn->MaxSegSizeConn; data_len_rem = (CPU_INT16U )data_len; data_len_tot = (CPU_INT16U )0; /* Prepare TCP tx seg hdr ctrls. */ seq_nbr = pconn->TxSeqNbrNextQ; flags_tcp = NET_TCP_FLAG_NONE | NET_TCP_FLAG_TX_ACK; if (pconn->TxQ_Tail != (NET_BUF *)0) { /* If tx Q NOT empty; ... */ pbuf = pconn->TxQ_Tail; pbuf_hdr = &pbuf->Hdr; data_ix_pkt_tail = (NET_BUF_SIZE)pbuf_hdr->DataIx; data_len_pkt_tail = (NET_BUF_SIZE)pbuf_hdr->TCP_SegLenData; /* ... calc tail seg's max data size, ... */ buf_size_max_tail = NetBuf_GetMaxSize(pbuf, data_ix_pkt_tail); buf_size_max_tail_data = DEF_MIN(buf_size_max_tail, data_len_mss); #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) if (data_len_pkt_tail > buf_size_max_tail_data) { /* If seg len > max data size, tx Q data seg invalid; */ /* ... close TCP conn (see Note #10a). */ NetTCP_ConnClose((NET_TCP_CONN *)pconn, (NET_BUF_HDR *)0, (CPU_BOOLEAN )DEF_YES, (NET_TCP_CLOSE_CODE)NET_TCP_CONN_CLOSE_ALL); *perr = NET_TCP_ERR_CONN_DATA_INVALID; return (0); } #endif if (data_len_pkt_tail < buf_size_max_tail_data) { /* ... & if avail data space on tx Q's tail seg, ... */ /* ... append data on tx Q tail seg (see Note #7b1). */ data_ix_pkt = data_ix_pkt_tail + data_len_pkt_tail; data_len_pkt_rem = buf_size_max_tail_data - data_len_pkt_tail; if (data_len_rem > (NET_BUF_SIZE)data_len_pkt_rem) {/* If data len rem > pkt data len rem, ... */ data_len_pkt = (NET_BUF_SIZE)data_len_pkt_rem; /* ... limit pkt data len to pkt data len rem. */ } else { data_len_pkt = (NET_BUF_SIZE)data_len_rem; /* Else limit pkt data len to data len rem. */ } NetBuf_DataWr((NET_BUF *) pbuf, /* Wr app data into TCP tx buf. */ (NET_BUF_SIZE) data_ix_pkt, (NET_BUF_SIZE) data_len_pkt, (CPU_INT08U *) p_data_pkt, (NET_ERR *)&err); if ( err != NET_BUF_ERR_NONE) { /* If wr err, tx Q data corrupted; ... */ /* ... close TCP conn (see Note #10a). */ NetTCP_ConnClose((NET_TCP_CONN *)pconn, (NET_BUF_HDR *)0, (CPU_BOOLEAN )DEF_YES, (NET_TCP_CLOSE_CODE)NET_TCP_CONN_CLOSE_ALL); *perr = NET_TCP_ERR_CONN_FAULT; return (0); } /* Update TCP seg tx buf ctrls. */ pbuf_hdr->DataLen += (NET_BUF_SIZE)data_len_pkt; pbuf_hdr->TotLen += (NET_BUF_SIZE)data_len_pkt; pbuf_hdr->TCP_SegLenInit += (CPU_INT16U )data_len_pkt; pbuf_hdr->TCP_SegLen += (CPU_INT16U )data_len_pkt; pbuf_hdr->TCP_SegLenData += (CPU_INT16U )data_len_pkt; pbuf_head = (NET_BUF *)pbuf; /* Set tx Q tail as first seg onto tx app data chain. */ pbuf_tail = (NET_BUF *)pbuf; tx_q_append = (CPU_BOOLEAN)DEF_NO; /* Update data ptr & lens. */ p_data_pkt += data_len_pkt; /* MUST NOT cast ptr operand (see Note #8b). */ data_len_tot += (CPU_INT16U)data_len_pkt; data_len_rem -= (CPU_INT16U)data_len_pkt; seq_nbr += (NET_TCP_SEQ_NBR)data_len_pkt; /* Dec TCP conn's tx win size (see Note #9). */ NetTCP_TxConnWinSizeHandlerCfgd((NET_TCP_CONN *) pconn, (NET_TCP_WIN_SIZE) data_len_pkt, (NET_TCP_WIN_CODE) NET_TCP_CONN_TX_WIN_DEC, (NET_ERR *)&err); if ( err != NET_TCP_ERR_NONE) { *perr = NET_TCP_ERR_CONN_FAULT; return (0); } } } /*$PAGE*/ /* Calc max data size. */ data_ix_pkt = NET_BUF_DATA_TX_IX; buf_size_max = NetBuf_GetMaxSize((NET_BUF *)0, (NET_BUF_SIZE)data_ix_pkt); buf_size_max_data = DEF_MIN(buf_size_max, data_len_mss); tx_err = DEF_NO; while ((data_len_rem > 0) && /* Prepare TCP seg(s) for ALL app data ... */ (pconn->TxWinSizeCfgdRem > 0) && /* ... as net rsrc(s)/err(s) permit. */ (tx_err == DEF_NO)) { if (data_len_rem > (NET_BUF_SIZE)buf_size_max_data) { /* If data len rem > max data size, ... */ data_len_pkt = (NET_BUF_SIZE)buf_size_max_data; /* ... limit pkt data len to max data size. */ } else { data_len_pkt = (NET_BUF_SIZE)data_len_rem; /* Else limit pkt data len to data len rem. */ } pbuf = NetBuf_Get((NET_BUF_SIZE) data_len_pkt, /* Get app data tx buf. */ (NET_BUF_SIZE) data_ix_pkt, (CPU_INT16U ) NET_BUF_FLAG_NONE, (NET_ERR *)&err); if (err != NET_BUF_ERR_NONE) { tx_err = DEF_YES; } if (tx_err == DEF_NO) { NetBuf_DataWr((NET_BUF *) pbuf, /* Wr app data into TCP tx buf. */ (NET_BUF_SIZE) data_ix_pkt, (NET_BUF_SIZE) data_len_pkt, (CPU_INT08U *) p_data_pkt, (NET_ERR *)&err); if (err != NET_BUF_ERR_NONE) { NetTCP_TxPktDiscard(pbuf, &err); tx_err = DEF_YES; } } if (tx_err == DEF_NO) { /* Init TCP seg(s) tx buf ctrls. */ pbuf_hdr = &pbuf->Hdr; pbuf_hdr->DataIx = (CPU_INT16U )data_ix_pkt; pbuf_hdr->DataLen = (NET_BUF_SIZE)data_len_pkt; pbuf_hdr->TotLen = (NET_BUF_SIZE)pbuf_hdr->DataLen; pbuf_hdr->ProtocolHdrType = NET_PROTOCOL_TYPE_APP; pbuf_hdr->TCP_UDP_PortSrc = (NET_PORT_NBR)src_port; pbuf_hdr->IP_AddrSrc = (CPU_INT32U )src_addr; pbuf_hdr->TCP_UDP_PortDest = (NET_PORT_NBR)dest_port; pbuf_hdr->IP_AddrDest = (CPU_INT32U )dest_addr; pbuf_hdr->TCP_SegLenInit = (CPU_INT16U )data_len_pkt; pbuf_hdr->TCP_SegLen = (CPU_INT16U )pbuf_hdr->TCP_SegLenInit; pbuf_hdr->TCP_SegLenData = (CPU_INT16U )pbuf_hdr->TCP_SegLenInit; pbuf_hdr->TCP_SegSync = (CPU_BOOLEAN )DEF_NO; pbuf_hdr->TCP_SegClose = (CPU_BOOLEAN )DEF_NO; pbuf_hdr->TCP_SegAck = (CPU_BOOLEAN )DEF_YES; pbuf_hdr->TCP_SegReset = (CPU_BOOLEAN )DEF_NO; pbuf_hdr->TCP_SeqNbrInit = (CPU_INT32U )seq_nbr; pbuf_hdr->TCP_SeqNbr = (CPU_INT32U )pbuf_hdr->TCP_SeqNbrInit; pbuf_hdr->TCP_Flags = (CPU_INT16U )flags_tcp; if (pbuf_tail != (NET_BUF *)0) { /* If tx app data chain NOT empty, ... */ /* ... append seg(s) @ chain tail. */ pbuf_hdr_tail = (NET_BUF_HDR *)&pbuf_tail->Hdr; pbuf_hdr_tail->NextPrimListPtr = (void *) pbuf; pbuf_hdr->PrevPrimListPtr = (void *) pbuf_tail; pbuf_tail = (NET_BUF *) pbuf; } else { /* Else add seg as first seg onto tx app data chain. */ pbuf_head = (NET_BUF *) pbuf; pbuf_tail = (NET_BUF *) pbuf; } /* Update data ptr & lens. */ p_data_pkt += data_len_pkt; /* MUST NOT cast ptr operand (see Note #8b). */ data_len_tot += (CPU_INT16U)data_len_pkt; data_len_rem -= (CPU_INT16U)data_len_pkt; seq_nbr += (NET_TCP_SEQ_NBR)data_len_pkt; /* Dec TCP conn's tx win size (see Note #9). */ NetTCP_TxConnWinSizeHandlerCfgd((NET_TCP_CONN *) pconn, (NET_TCP_WIN_SIZE) data_len_pkt, (NET_TCP_WIN_CODE) NET_TCP_CONN_TX_WIN_DEC, (NET_ERR *)&err); if ( err != NET_TCP_ERR_NONE) { *perr = NET_TCP_ERR_CONN_FAULT; return (0); } } } /*$PAGE*/ if (pbuf_head == (NET_BUF *)0) { /* If NO data seg'd, rtn no rsrc(s) err ... */ *perr = NET_TCP_ERR_NONE_AVAIL; /* ... (see Notes #3b1F2b, #3c2, & #3d2). */ return (0); } DEF_BIT_SET(pbuf_hdr->TCP_Flags, NET_TCP_FLAG_TX_PUSH); /* Set PUSH flag in last q'd tx seg (see Note #7b3B1b). */ /* ----------------- UPDATE TCP CONN ------------------ */ if (pconn->TxQ_Tail != (NET_BUF *)0) { /* If tx Q NOT empty ... */ if (tx_q_append == DEF_YES) { /* ... & tx app data chain NOT already on tx Q, ... */ /* ... append seg(s) @ Q tail (see Note #4b). */ pbuf_q_tail = (NET_BUF *) pconn->TxQ_Tail; pbuf_hdr_q_tail = (NET_BUF_HDR *)&pbuf_q_tail->Hdr; pbuf_hdr_q_tail->NextPrimListPtr = (void *) pbuf_head; pbuf_hdr_head = (NET_BUF_HDR *)&pbuf_head->Hdr; pbuf_hdr_head->PrevPrimListPtr = (void *) pbuf_q_tail; } pconn->TxQ_Tail = (NET_BUF *)pbuf_tail; } else { /* Else add seg(s) to empty tx Q. */ pconn->TxQ_Head = (NET_BUF *)pbuf_head; pconn->TxQ_Tail = (NET_BUF *)pbuf_tail; } /* Update TCP conn tx seq nbr(s). */ pconn->TxSeqNbrNextQ = seq_nbr; /* ---------------- TX TCP DATA SEG(S) ---------------- */ if (tx_data == DEF_YES) { NetTCP_TxConnTxQ((NET_TCP_CONN *)pconn, (NET_BUF_HDR *)0, (NET_TCP_ACK_CODE )NET_TCP_CONN_TX_ACK_NONE, (CPU_BOOLEAN )DEF_NO, (NET_TCP_CLOSE_CODE)NET_TCP_CONN_CLOSE_ALL, (NET_ERR *)perr); switch (*perr) { case NET_TCP_ERR_NONE: case NET_TCP_ERR_CONN_ACK_NONE: case NET_TCP_ERR_CONN_ACK_DLYD: case NET_TCP_ERR_CONN_ACK_PREVLY_TXD: case NET_ERR_TX: /* Ignore transitory tx err(s). */ break; case NET_ERR_INIT_INCOMPLETE: case NET_TCP_ERR_CONN_NOT_USED: case NET_TCP_ERR_CONN_CLOSE: case NET_TCP_ERR_CONN_FAULT: case NET_TCP_ERR_CONN_FAIL: case NET_TCP_ERR_CONN_ACK_INVALID: case NET_TCP_ERR_INVALID_CONN_STATE: case NET_TCP_ERR_INVALID_CONN_OP: case NET_TCP_ERR_INVALID_LEN_SEG: case NET_TCP_ERR_NONE_AVAIL: case NET_TCP_ERR_TX: case NET_CONN_ERR_INVALID_FAMILY: case NET_CONN_ERR_INVALID_ADDR: case NET_CONN_ERR_INVALID_ADDR_LEN: default: return (0); /* Prevent 'break NOT reachable' compiler warning. */ } } *perr = NET_TCP_ERR_NONE; return (data_len_tot); } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_ConnGet() * * Description : (1) Allocate & initialize a TCP connection : * * (a) Get a TCP connection * (b) Validate TCP connection * (c) Initialize TCP connection * (d) Update TCP connection pool statistics * (e) Return TCP connection handle identifier * OR * Null identifier & error code, on failure * * (2) The TCP connection pool is implemented as a stack : * * (a) 'NetTCP_ConnPoolPtr' points to the head of the TCP connection pool. * * (b) TCP connections' 'NextPtr's link each TCP connection to form the TCP connection pool stack. * * (c) TCP connections are inserted & removed at the head of the TCP connection pool stack. * * * TCP connections are * inserted & removed * at the head * (see Note #2c) * * | NextPtr * | (see Note #2b) * v | * | * ------- ------- v ------- ------- * TCP Connection ---->| |------>| |------>| |------>| | * Pool Pointer | | | | | | | | * | | | | | | | | * (see Note #2a) ------- ------- ------- ------- * * | | * |<------- Pool of Free TCP Connections -------->| * | (see Note #2) | * * * Argument(s) : perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_NONE TCP connection successfully allocated & * initialized. * NET_TCP_ERR_NONE_AVAIL NO available TCP connections to allocate. * NET_TCP_ERR_INVALID_CONN_TYPE TCP connection is NOT a valid type. * * Return(s) : TCP connection handle identifier, if NO errors. * * NET_TCP_CONN_ID_NONE, otherwise. * * Caller(s) : various. * * This function is an INTERNAL network protocol suite function & MUST NOT be called by * application function(s). * * Note(s) : (3) (a) TCP connection pool is accessed by 'NetTCP_ConnPoolPtr' during execution of * * (1) NetTCP_Init() * (2) NetTCP_ConnGet() * (3) NetTCP_ConnFree() * * (b) Since the primary tasks of the network protocol suite are prevented from running * concurrently (see 'net.h Note #2'), it is NOT necessary to protect the shared * resources of the connection pool since no asynchronous access from other network * tasks is possible. ********************************************************************************************************* */ /*$PAGE*/ NET_TCP_CONN_ID NetTCP_ConnGet (NET_ERR *perr) { #if ((NET_CTR_CFG_ERR_EN == DEF_ENABLED) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif NET_TCP_CONN *pconn; NET_TCP_CONN_ID conn_id_tcp; NET_ERR stat_err; /* ------------------- GET TCP CONN ------------------- */ if (NetTCP_ConnPoolPtr != (NET_TCP_CONN *)0) { /* If TCP conn pool NOT empty, get TCP conn from pool. */ pconn = (NET_TCP_CONN *)NetTCP_ConnPoolPtr; NetTCP_ConnPoolPtr = (NET_TCP_CONN *)pconn->NextPtr; } else { /* If none avail, rtn err. */ NET_CTR_ERR_INC(NetTCP_ErrNoneAvailCtr); *perr = NET_TCP_ERR_NONE_AVAIL; return (NET_TCP_CONN_ID_NONE); } #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) /* ---------------- VALIDATE TCP CONN ----------------- */ if (pconn->Type != NET_TCP_TYPE_CONN) { NetTCP_ConnDiscard(pconn); NET_CTR_ERR_INC(NetTCP_ErrConnInvalidTypeCtr); *perr = NET_TCP_ERR_INVALID_CONN_TYPE; return (NET_TCP_CONN_ID_NONE); } #endif /* ------------------ INIT TCP CONN ------------------- */ NetTCP_ConnClr(pconn); DEF_BIT_SET(pconn->Flags, NET_TCP_FLAG_USED); /* Set TCP conn as used. */ pconn->ConnState = NET_TCP_CONN_STATE_CLOSED; /* ------------ UPDATE TCP CONN POOL STATS ------------ */ NetStat_PoolEntryUsedInc(&NetTCP_ConnPoolStat, &stat_err); /* ----------------- RTN TCP CONN ID ------------------ */ conn_id_tcp = pconn->ID; *perr = NET_TCP_ERR_NONE; return (conn_id_tcp); } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_ConnFree() * * Description : Free a TCP connection. * * Argument(s) : conn_id_tcp Handle identifier of TCP connection to free. * * Return(s) : none. * * Caller(s) : NetSock_Listen(), * NetSock_GetConnTransport(). * * This function is an INTERNAL network protocol suite function & MUST NOT be called by * application function(s). * * Note(s) : (1) #### To prevent freeing a TCP connection already freed via previous TCP connection * free, NetTCP_ConnFree() checks if the TCP connection is used BEFORE freeing the * TCP connection. * * This prevention is only best-effort since any invalid duplicate TCP connection frees * MAY be asynchronous to potentially valid TCP connection gets. Thus the invalid TCP * connection free(s) MAY corrupt the TCP connection's valid operation(s). * * However, since the primary tasks of the network protocol suite are prevented from * running concurrently (see 'net.h Note #2'), it is NOT necessary to protect TCP * connection resources from possible corruption since no asynchronous access from * other network tasks is possible. ********************************************************************************************************* */ void NetTCP_ConnFree (NET_TCP_CONN_ID conn_id_tcp) { #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) NET_ERR err; #endif NET_TCP_CONN *pconn; /* ---------------- VALIDATE TCP CONN ----------------- */ if (conn_id_tcp == NET_TCP_CONN_ID_NONE) { return; } #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) /* -------------- VALIDATE TCP CONN USED -------------- */ (void)NetTCP_ConnIsUsed(conn_id_tcp, &err); if (err != NET_TCP_ERR_NONE) { /* If TCP conn NOT used, ... */ return; /* ... rtn but do NOT free (see Note #1). */ } #endif /* ------------------ FREE TCP CONN ------------------- */ pconn = &NetTCP_ConnTbl[conn_id_tcp]; NetTCP_ConnFreeHandler(pconn, NET_TCP_CONN_FREE_ALL); } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_ConnCloseFromConn() * * Description : Close a TCP connection via a network connection. * * (1) When a network connection closes a TCP connection, the TCP connection : * * (a) (1) Closes NO other network connection(s), * (2) MUST NOT recursively re-close other network connection(s); * * (b) SHOULD clear network connection(s)' handle identifiers. * * See also 'net_sock.c NetSock_CloseFromConn() Note #1', * & 'net_conn.c NetConn_CloseFromTransport() Note #1b'. * * * Argument(s) : conn_id_tcp Handle identifier of TCP connection to close. * * Return(s) : none. * * Caller(s) : NetConn_CloseTransport(). * * This function is an INTERNAL network protocol suite function & MUST NOT be called by * application function(s). * * Note(s) : (2) #### To prevent closing a TCP connection already closed via previous TCP connection * close, NetTCP_ConnCloseFromConn() checks if the TCP connection is used BEFORE closing * the TCP connection. * * This prevention is only best-effort since any invalid duplicate TCP connection closes * MAY be asynchronous to potentially valid TCP connection gets. Thus the invalid TCP * connection closes(s) MAY corrupt the TCP connection's valid operation(s). * * However, since the primary tasks of the network protocol suite are prevented from * running concurrently (see 'net.h Note #2'), it is NOT necessary to protect TCP * connection resources from possible corruption since no asynchronous access from * other network tasks is possible. ********************************************************************************************************* */ void NetTCP_ConnCloseFromConn (NET_TCP_CONN_ID conn_id_tcp) { #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) NET_ERR err; #endif NET_TCP_CONN *pconn; /* ---------------- VALIDATE TCP CONN ----------------- */ if (conn_id_tcp == NET_TCP_CONN_ID_NONE) { return; } #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) /* -------------- VALIDATE TCP CONN USED -------------- */ (void)NetTCP_ConnIsUsed(conn_id_tcp, &err); if (err != NET_TCP_ERR_NONE) { /* If TCP conn NOT used, ... */ return; /* ... rtn but do NOT close (see Note #2). */ } #endif /* ------------------ CLOSE TCP CONN ------------------ */ pconn = &NetTCP_ConnTbl[conn_id_tcp]; NetTCP_ConnClose((NET_TCP_CONN *)pconn, (NET_BUF_HDR *)0, (CPU_BOOLEAN )DEF_NO, /* See Note #1. */ (NET_TCP_CLOSE_CODE)NET_TCP_CONN_CLOSE_ALL); } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_ConnSetID_Conn() * * Description : Set a TCP connection's network connection handle identifier. * * Argument(s) : conn_id_tcp Handle identifier of TCP connection to set. * * conn_id Handle identifier of network connection. * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_NONE TCP connection's network connection handle * identifier successfully set. * NET_TCP_ERR_INVALID_CONN_ID Invalid network connection handle identifier. * * ----- RETURNED BY NetTCP_ConnIsUsed() : ----- * NET_ERR_INIT_INCOMPLETE Network initialization NOT complete. * NET_TCP_ERR_INVALID_CONN Invalid TCP connection number. * NET_TCP_ERR_CONN_NOT_USED TCP connection NOT currently used. * * ------ RETURNED BY NetConn_IsUsed() : ------- * NET_CONN_ERR_INVALID_CONN Invalid network connection number. * NET_CONN_ERR_NOT_USED Network connection NOT currently used. * * Return(s) : none. * * Caller(s) : NetSock_GetConnTransport(). * * This function is an INTERNAL network protocol suite function & MUST NOT be called by * application function(s). * * Note(s) : none. ********************************************************************************************************* */ void NetTCP_ConnSetID_Conn (NET_TCP_CONN_ID conn_id_tcp, NET_CONN_ID conn_id, NET_ERR *perr) { NET_TCP_CONN *pconn; #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) /* -------------- VALIDATE TCP CONN USED -------------- */ (void)NetTCP_ConnIsUsed(conn_id_tcp, perr); if (*perr != NET_TCP_ERR_NONE) { return; } /* --------------- VALIDATE NET CONN ID --------------- */ if (conn_id != NET_CONN_ID_NONE) { (void)NetConn_IsUsed(conn_id, perr); if (*perr != NET_CONN_ERR_NONE) { return; } } #endif pconn = &NetTCP_ConnTbl[conn_id_tcp]; pconn->ID_Conn = conn_id; /* Set TCP conn's conn id. */ *perr = NET_TCP_ERR_NONE; } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_ConnSetStateListen() * * Description : Set TCP connection to LISTEN state. * * Argument(s) : conn_id_tcp Handle identifier of TCP connection to set LISTEN state. * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_NONE TCP connection state successfully set to LISTEN. * NET_TCP_ERR_CONN_NOT_USED TCP connection NOT currently used. * NET_TCP_ERR_INVALID_CONN_ID Invalid network connection handle identifier. * NET_TCP_ERR_INVALID_CONN_STATE Invalid TCP connection state. * NET_TCP_ERR_INVALID_CONN_OP Invalid TCP connection operation. * * ------ RETURNED BY NetTCP_ConnIsUsed() : ------- * NET_ERR_INIT_INCOMPLETE Network initialization NOT complete. * NET_TCP_ERR_INVALID_CONN Invalid TCP connection number. * * Return(s) : none. * * Caller(s) : NetSock_Listen(). * * This function is an INTERNAL network protocol suite function & SHOULD NOT be called by * application function(s). * * Note(s) : none. ********************************************************************************************************* */ void NetTCP_ConnSetStateListen (NET_TCP_CONN_ID conn_id_tcp, NET_ERR *perr) { #if ((NET_CTR_CFG_ERR_EN == DEF_ENABLED) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) CPU_BOOLEAN used; NET_CONN_ID conn_id; NET_CONN_ID conn_id_transport; NET_ERR err; #endif NET_TCP_CONN *pconn; #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) /* -------------- VALIDATE TCP CONN USED -------------- */ (void)NetTCP_ConnIsUsed(conn_id_tcp, perr); if (*perr != NET_TCP_ERR_NONE) { return; } #endif pconn = &NetTCP_ConnTbl[conn_id_tcp]; #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) /* ---------------- VALIDATE CONN IDs ----------------- */ conn_id = pconn->ID_Conn; used = NetConn_IsUsed(conn_id, &err); if (used != DEF_YES) { *perr = NET_TCP_ERR_INVALID_CONN_ID; return; } conn_id_transport = NetConn_ID_TransportGet(conn_id, &err); if (conn_id_tcp != (NET_TCP_CONN_ID)conn_id_transport) { *perr = NET_TCP_ERR_INVALID_CONN_ID; return; } #endif /*$PAGE*/ switch (pconn->ConnState) { case NET_TCP_CONN_STATE_FREE: NET_CTR_ERR_INC(NetTCP_ErrNotUsedCtr); *perr = NET_TCP_ERR_CONN_NOT_USED; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_CLOSED: pconn->ConnState = NET_TCP_CONN_STATE_LISTEN; break; case NET_TCP_CONN_STATE_LISTEN: case NET_TCP_CONN_STATE_SYNC_RXD: case NET_TCP_CONN_STATE_SYNC_RXD_PASSIVE: case NET_TCP_CONN_STATE_SYNC_RXD_ACTIVE: case NET_TCP_CONN_STATE_SYNC_TXD: case NET_TCP_CONN_STATE_CONN: case NET_TCP_CONN_STATE_FIN_WAIT_1: case NET_TCP_CONN_STATE_FIN_WAIT_2: case NET_TCP_CONN_STATE_CLOSING: case NET_TCP_CONN_STATE_TIME_WAIT: case NET_TCP_CONN_STATE_CLOSE_WAIT: case NET_TCP_CONN_STATE_LAST_ACK: case NET_TCP_CONN_STATE_CLOSING_DATA_AVAIL: NET_CTR_ERR_INC(NetTCP_ErrConnInvalidOpCtr); *perr = NET_TCP_ERR_INVALID_CONN_OP; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_NONE: default: NetTCP_ConnClose((NET_TCP_CONN *)pconn, (NET_BUF_HDR *)0, (CPU_BOOLEAN )DEF_YES, (NET_TCP_CLOSE_CODE)NET_TCP_CONN_CLOSE_ALL); NET_CTR_ERR_INC(NetTCP_ErrConnInvalidStateCtr); *perr = NET_TCP_ERR_INVALID_CONN_STATE; return; /* Prevent 'break NOT reachable' compiler warning. */ } *perr = NET_TCP_ERR_NONE; } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_ConnReqClose() * * Description : (1) Request TCP connection close : * * (a) Handle TCP connection close See Note #2a * (b) Update TCP connection timer See Note #2b * * * Argument(s) : conn_id_tcp Handle identifier of TCP connection to request close. * * conn_close_code Indicate whether to close transport connection : * * NET_CONN_CLOSE_FULL Close TCP connection but do NOT allow * closing receive data to be available. * NET_CONN_CLOSE_HALF Close TCP connection but allow * closing receive data to be available. * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_NONE TCP connection close successfully requested. * NET_TCP_ERR_CONN_NOT_USED TCP connection NOT currently used. * NET_TCP_ERR_CONN_FAIL TCP connection operation(s) failed. * NET_TCP_ERR_INVALID_CONN_ID Invalid network connection handle identifier. * NET_TCP_ERR_INVALID_CONN_STATE Invalid TCP connection state. * NET_TCP_ERR_INVALID_CONN_OP Invalid TCP connection operation. * * ----- RETURNED BY NetTCP_ConnIsUsed() : ----- * NET_ERR_INIT_INCOMPLETE Network initialization NOT complete. * NET_TCP_ERR_INVALID_CONN Invalid TCP connection number. * * ---- RETURNED BY NetTCP_TxConnClose() : ----- * NET_ERR_INIT_INCOMPLETE Network initialization NOT complete. * NET_TCP_ERR_NONE_AVAIL Resources NOT available. * NET_TCP_ERR_CONN_ACK_INVALID TCP connection acknowledgement NOT valid for * current TCP connection state. * NET_TCP_ERR_INVALID_LEN_SEG Invalid TCP sequence-segment length. * NET_CONN_ERR_INVALID_FAMILY Invalid connection family. * NET_CONN_ERR_INVALID_ADDR Invalid TCP connection address. * NET_CONN_ERR_INVALID_ADDR_LEN Invalid TCP connection address length. * NET_TCP_ERR_TX TCP transmit error. * * Return(s) : none. * * Caller(s) : NetSock_CloseHandlerStream(). * * This function is an INTERNAL network protocol suite function & SHOULD NOT be called by * application function(s). *$PAGE* * Note(s) : (2) (a) RFC #793, Section 3.9 'Event Processing : CLOSE Call' specifies how to handle * close requests from the application layer : * * (1) For the "CLOSED STATE ... return 'error: connection does not exist'". * * (2) For the "LISTEN STATE ... any outstanding RECEIVEs are returned with 'error: * closing' responses. Delete TCB [and] enter CLOSED state". * * (3) For the "SYN-SENT STATE ... Delete the TCB and return 'error: closing' * responses to any queued SENDs, or RECEIVEs". * * (4) For the "SYN-RECEIVED STATE" ... * * (A) "if no SENDs have been issued and there is no pending data to send," ... * (1) "then form a FIN segment and send it," ... * (2) "and enter FIN-WAIT-1 state;" ... * * (B) "otherwise queue for processing after entering ESTABLISHED state." * * (5) For the "ESTABLISHED STATE" ... * * (A) "Queue this until all preceding SENDs have been segmentized," ... * (B) "then form a FIN segment and send it" ... * (C) "In any case, enter FIN-WAIT-1 state." * * (6) For the "FIN-WAIT-1 STATE, FIN-WAIT-2 STATE" ... * * (A) "Strictly speaking, this is an error and should receive a 'error: connection * closing' response." * * (B) "An 'ok' response would be acceptable, too, as long as a second FIN is not * emitted (the first FIN may be retransmitted though)." * * (7) For the "CLOSE-WAIT STATE" ... * * (A) "Queue this request until all preceding SENDs have been segmentized;" ... * (B) "then send a FIN segment," ... * (C) (1) "enter CLOSING state." * (2) RFC #1122, Section 4.2.2.20.(a) amends the state transition to "enter * LAST-ACK state, not CLOSING". * * (8) For the "CLOSING STATE, LAST-ACK STATE, TIME-WAIT STATE ... respond with 'error: * connection closing'". * * (b) RFC #793, Section 3.9 'Event Processing : CLOSE Call' does NOT specify which timeout * values to set for each state transition to closing state(s). * * #### Therefore, the following timeout values will be used for the following close * state transitions : * * (1) SYN-RECEIVED STATE -> FIN-WAIT-1 STATE TCP User/Connection timeout * See also 'NetTCP_RxPktConnHandlerSyncRxd() Note #3'. * * (2) ESTABLISHED STATE -> FIN-WAIT-1 STATE TCP User/Connection timeout * See also 'NetTCP_RxPktConnHandlerConn() Note #3'. * * (3) CLOSE-WAIT STATE -> LAST-ACK STATE TCP Time-Wait / Two TCP Maximum * Segment Lifetimes timeout * See also 'NetTCP_RxPktConnHandlerLastAck() Note #3'. * * (c) RFC #1122, Section 4.2.2.13 states that "a TCP connection may terminate in two ways" : * * (1) "The normal TCP close sequence using a FIN handshake." * (2) "An 'abort' in which one or more RST segments are sent and the connection state * is immediately discarded." * * (3) (a) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check ACK Field : * FIN-WAIT-2 STATE' states that "if the retransmission queue is empty, the user's * CLOSE can be acknowledged". * * (b) However, TCP connection should signal the application layer that "the user's close * [is] acknowledged" whenever its re-transmit queue becomes &/or is empty : * * (1) Transition from LISTEN to CLOSED * (2) Transition from SYN-SENT to CLOSED * * See also 'NetTCP_RxPktConnHandlerSignalClose() Note #1'. * * (4) On ANY errors, network resources MUST be appropriately freed : * * (a) Close the TCP connection. * (b) #### Do NOT close the network or application connection(s); application layer * responsible for closing remaining connection(s)? ********************************************************************************************************* */ /*$PAGE*/ void NetTCP_ConnReqClose (NET_TCP_CONN_ID conn_id_tcp, CPU_INT08U conn_close_code, NET_ERR *perr) { #if ((NET_CTR_CFG_ERR_EN == DEF_ENABLED) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) CPU_BOOLEAN used; NET_CONN_ID conn_id; NET_CONN_ID conn_id_transport; #endif NET_TCP_CONN *pconn; NET_TCP_TIMEOUT_SEC timeout_sec; NET_TMR_TICK timeout_tick; NET_ERR err; #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) /* -------------- VALIDATE TCP CONN USED -------------- */ (void)NetTCP_ConnIsUsed(conn_id_tcp, perr); if (*perr != NET_TCP_ERR_NONE) { return; } #endif pconn = &NetTCP_ConnTbl[conn_id_tcp]; #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) /* ---------------- VALIDATE CONN IDs ----------------- */ conn_id = pconn->ID_Conn; used = NetConn_IsUsed(conn_id, &err); if (used != DEF_YES) { *perr = NET_TCP_ERR_INVALID_CONN_ID; return; } conn_id_transport = NetConn_ID_TransportGet(conn_id, &err); if (conn_id_tcp != (NET_TCP_CONN_ID)conn_id_transport) { *perr = NET_TCP_ERR_INVALID_CONN_ID; return; } #endif /*$PAGE*/ /* ---------------- HANDLE CONN CLOSE ----------------- */ switch (pconn->ConnState) { case NET_TCP_CONN_STATE_FREE: NET_CTR_ERR_INC(NetTCP_ErrNotUsedCtr); *perr = NET_TCP_ERR_CONN_NOT_USED; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_CLOSED: /* See Note #2a1. */ NET_CTR_ERR_INC(NetTCP_ErrConnInvalidOpCtr); *perr = NET_TCP_ERR_INVALID_CONN_OP; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_LISTEN: /* See Note #2a2. */ case NET_TCP_CONN_STATE_SYNC_TXD: /* See Note #2a3. */ /* Signal app conn close (see Note #3b) ... */ NetTCP_RxPktConnHandlerSignalClose(pconn, DEF_NO, &err); /* ... & close TCP conn. */ NetTCP_ConnCloseHandler(pconn, pconn->ConnCloseAppFlag, NET_TCP_CONN_CLOSE_ALL); *perr = NET_TCP_ERR_NONE; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_SYNC_RXD: /* See Note #2a4. */ case NET_TCP_CONN_STATE_SYNC_RXD_PASSIVE: case NET_TCP_CONN_STATE_SYNC_RXD_ACTIVE: case NET_TCP_CONN_STATE_CONN: /* See Note #2a5. */ NetTCP_TxConnClose(pconn, perr); /* Tx TCP conn close (see Note #2a5B), ... */ if (*perr != NET_TCP_ERR_NONE) { return; } /* ... enter fin-wait-1 state (see Note #2a5C), ... */ pconn->ConnState = NET_TCP_CONN_STATE_FIN_WAIT_1; pconn->TxQ_State = NET_TCP_TX_Q_STATE_CONN_CLOSING; pconn->ConnCloseCode = conn_close_code; timeout_sec = pconn->TimeoutConn_sec; /* ... & reset conn tmr (see Notes #2b1 & #2b2). */ break; case NET_TCP_CONN_STATE_CLOSE_WAIT: /* See Note #2a7. */ NetTCP_TxConnClose(pconn, perr); /* Tx TCP conn close (see Note #2a7B), ... */ if (*perr != NET_TCP_ERR_NONE) { return; } /* ... enter last-ack state (see Note #2a7C2), ... */ pconn->ConnState = NET_TCP_CONN_STATE_LAST_ACK; pconn->TxQ_State = NET_TCP_TX_Q_STATE_CONN_CLOSING; pconn->ConnCloseCode = conn_close_code; /* ... & start time-wait tmr (see Note #2b3). */ timeout_sec = pconn->TimeoutMaxSeg_sec * NET_TCP_CONN_TIMEOUT_MAX_SEG_SCALAR; break; case NET_TCP_CONN_STATE_FIN_WAIT_1: /* See Note #2a6. */ case NET_TCP_CONN_STATE_FIN_WAIT_2: case NET_TCP_CONN_STATE_TIME_WAIT: /* See Note #2a8. */ case NET_TCP_CONN_STATE_CLOSING: case NET_TCP_CONN_STATE_LAST_ACK: case NET_TCP_CONN_STATE_CLOSING_DATA_AVAIL: NET_CTR_ERR_INC(NetTCP_ErrConnInvalidOpCtr); *perr = NET_TCP_ERR_INVALID_CONN_OP; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_NONE: default: NetTCP_ConnClose((NET_TCP_CONN *)pconn, (NET_BUF_HDR *)0, (CPU_BOOLEAN )DEF_YES, (NET_TCP_CLOSE_CODE)NET_TCP_CONN_CLOSE_ALL); NET_CTR_ERR_INC(NetTCP_ErrConnInvalidStateCtr); *perr = NET_TCP_ERR_INVALID_CONN_STATE; return; /* Prevent 'break NOT reachable' compiler warning. */ } /*$PAGE*/ /* -------------------- UPDATE TMR -------------------- */ timeout_tick = (NET_TMR_TICK)timeout_sec * NET_TMR_TIME_TICK_PER_SEC; if (pconn->TimeoutTmr != (NET_TMR *)0) { NetTmr_Set((NET_TMR *) pconn->TimeoutTmr, (CPU_FNCT_PTR) NetTCP_ConnCloseTimeout, (NET_TMR_TICK) timeout_tick, (NET_ERR *)&err); } else { pconn->TimeoutTmr = NetTmr_Get((void *) pconn, (CPU_FNCT_PTR) NetTCP_ConnCloseTimeout, (NET_TMR_TICK) timeout_tick, (CPU_INT16U ) NET_TMR_FLAG_NONE, (NET_ERR *)&err); } if (err != NET_TMR_ERR_NONE) { NetTCP_ConnClose((NET_TCP_CONN *)pconn, (NET_BUF_HDR *)0, (CPU_BOOLEAN )pconn->ConnCloseAppFlag, (NET_TCP_CLOSE_CODE)NET_TCP_CONN_CLOSE_ALL); *perr = NET_TCP_ERR_CONN_FAIL; return; } *perr = NET_TCP_ERR_NONE; } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_ConnCfgMaxSegSizeLocal() * * Description : Configure TCP connection's local maximum segment size. * * Argument(s) : conn_id_tcp Handle identifier of TCP connection to configure local maximum segment size. * * max_seg_size Desired maximum segment size. * * Return(s) : DEF_OK, Local maximum segment size successfully configured. * * DEF_FAIL, otherwise. * * Caller(s) : Application. * * This function is a network protocol suite application interface (API) function & MAY be * called by application function(s). * * Note(s) : (1) RFC #793, Section 3.1 'Header Format : Options : Maximum Segment Size' states that * a TCP connection advertises its "maximum receive segment size ... only ... in the * initial connection request (i.e., in segments with the SYN control bit set)". * * Thus any configuration of the local receive maximum segment size MUST be performed * by the application layer PRIOR to any TCP connection request/synchronization. ********************************************************************************************************* */ CPU_BOOLEAN NetTCP_ConnCfgMaxSegSizeLocal (NET_TCP_CONN_ID conn_id_tcp, NET_TCP_SEG_SIZE max_seg_size) { #if (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL) CPU_SR cpu_sr; #endif NET_TCP_CONN *pconn; #if ((NET_ERR_CFG_ARG_CHK_EXT_EN == DEF_ENABLED) || \ (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED)) /* ---------------- VALIDATE TCP CONN ----------------- */ if (conn_id_tcp < NET_TCP_CONN_ID_MIN) { return (DEF_FAIL); } if (conn_id_tcp > NET_TCP_CONN_ID_MAX) { return (DEF_FAIL); } #endif /* -------------- VALIDATE MAX SEG SIZE --------------- */ #if (NET_TCP_MAX_SEG_SIZE_MIN > 0) if (max_seg_size < NET_TCP_MAX_SEG_SIZE_MIN) { max_seg_size = NET_TCP_MAX_SEG_SIZE_MIN; } #endif #if ((NET_TCP_MAX_SEG_SIZE_MAX < DEF_INT_08U_MAX_VAL) || \ ((NET_TCP_MAX_SEG_SIZE_MAX < DEF_INT_16U_MAX_VAL) && (NET_TCP_MAX_SEG_SIZE_MAX > DEF_INT_08U_MAX_VAL)) || \ ((NET_TCP_MAX_SEG_SIZE_MAX < DEF_INT_32U_MAX_VAL) && (NET_TCP_MAX_SEG_SIZE_MAX > DEF_INT_16U_MAX_VAL))) if (max_seg_size > NET_TCP_MAX_SEG_SIZE_MAX) { max_seg_size = NET_TCP_MAX_SEG_SIZE_MAX; } #endif /* ------------ CFG TCP CONN MAX SEG SIZE ------------- */ pconn = &NetTCP_ConnTbl[conn_id_tcp]; CPU_CRITICAL_ENTER(); pconn->MaxSegSizeLocal = max_seg_size; CPU_CRITICAL_EXIT(); return (DEF_OK); } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_ConnCfgRxWinSize() * * Description : Configure TCP connection's receive window size. * * Argument(s) : conn_id_tcp Handle identifier of TCP connection to configure receive window size. * * win_size Desired receive window size. * * Return(s) : DEF_OK, Receive window size successfully configured. * * DEF_FAIL, otherwise. * * Caller(s) : Application. * * This function is a network protocol suite application interface (API) function & MAY be * called by application function(s). * * Note(s) : (1) A TCP connection's receive window controls should NOT be updated until after the * following TCP connection control(s) have been configured : * * (a) TCP connection's connection maximum segment size ('MaxSegSizeConn') * [see 'NetTCP_RxConnWinSizeCfgUpdateTh() Note #2b'] ********************************************************************************************************* */ CPU_BOOLEAN NetTCP_ConnCfgRxWinSize (NET_TCP_CONN_ID conn_id_tcp, NET_TCP_WIN_SIZE win_size) { #if (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL) CPU_SR cpu_sr; #endif NET_TCP_CONN *pconn; #if ((NET_ERR_CFG_ARG_CHK_EXT_EN == DEF_ENABLED) || \ (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED)) /* ---------------- VALIDATE TCP CONN ----------------- */ if (conn_id_tcp < NET_TCP_CONN_ID_MIN) { return (DEF_FAIL); } if (conn_id_tcp > NET_TCP_CONN_ID_MAX) { return (DEF_FAIL); } #endif /* --------------- VALIDATE RX WIN SIZE --------------- */ #if (NET_TCP_WIN_SIZE_MIN > 0) if (win_size < NET_TCP_WIN_SIZE_MIN) { win_size = NET_TCP_WIN_SIZE_MIN; } #endif #if ((NET_TCP_WIN_SIZE_MAX < DEF_INT_08U_MAX_VAL) || \ ((NET_TCP_WIN_SIZE_MAX < DEF_INT_16U_MAX_VAL) && (NET_TCP_WIN_SIZE_MAX > DEF_INT_08U_MAX_VAL)) || \ ((NET_TCP_WIN_SIZE_MAX < DEF_INT_32U_MAX_VAL) && (NET_TCP_WIN_SIZE_MAX > DEF_INT_16U_MAX_VAL))) if (win_size > NET_TCP_WIN_SIZE_MAX) { win_size = NET_TCP_WIN_SIZE_MAX; } #endif /* --------- CFG TCP CONN RX WIN SIZE --------- */ pconn = &NetTCP_ConnTbl[conn_id_tcp]; CPU_CRITICAL_ENTER(); if (pconn->MaxSegSizeConn == NET_TCP_MAX_SEG_SIZE_NONE) { /* If TCP conn MSS NOT cfg'd (see Note #1a), .. */ CPU_CRITICAL_EXIT(); return (DEF_FAIL); /* .. do NOT cfg TCP conn rx win. */ } pconn->RxWinSizeCfgd = win_size; /* Cfg new rx win size. */ if (pconn->RxWinSizeCfgdActual <= pconn->RxWinSizeCfgd) { /* If actual <= cfg'd rx win size, ... */ pconn->RxWinSizeCfgdActual = pconn->RxWinSizeCfgd; /* ... update actual rx win size. */ pconn->RxWinSizeCfgdActualUpdateRem = 0; } else { /* Else cfg rem'ing win size update val. */ pconn->RxWinSizeCfgdActualUpdateRem = pconn->RxWinSizeCfgdActual - pconn->RxWinSizeCfgd; } NetTCP_RxConnWinSizeCfg(pconn); /* Cfg rx win ctrls. */ CPU_CRITICAL_EXIT(); return (DEF_OK); } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_ConnCfgReTxMaxTh() * * Description : Configure TCP connection's maximum number of same segment retransmissions. * * Argument(s) : conn_id_tcp Handle identifier of TCP connection to configure maximum number of * same segment retransmissions. * * nbr_re_tx Desired maximum number of same segment retransmissions. * * Return(s) : DEF_OK, Maximum number of same segment retransmissions successfully configured. * * DEF_FAIL, otherwise. * * Caller(s) : Application. * * This function is a network protocol suite application interface (API) function & MAY be * called by application function(s). * * Note(s) : none. ********************************************************************************************************* */ CPU_BOOLEAN NetTCP_ConnCfgReTxMaxTh (NET_TCP_CONN_ID conn_id_tcp, CPU_INT16U nbr_max_re_tx) { #if (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL) CPU_SR cpu_sr; #endif NET_TCP_CONN *pconn; #if ((NET_ERR_CFG_ARG_CHK_EXT_EN == DEF_ENABLED) || \ (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED)) /* ---------------- VALIDATE TCP CONN ----------------- */ if (conn_id_tcp < NET_TCP_CONN_ID_MIN) { return (DEF_FAIL); } if (conn_id_tcp > NET_TCP_CONN_ID_MAX) { return (DEF_FAIL); } #endif /* ---------------- VALIDATE RE-TX TH ----------------- */ #if (NET_TCP_RE_TX_TH_MIN > DEF_INT_16U_MIN_VAL) if (nbr_max_re_tx < NET_TCP_RE_TX_TH_MIN) { nbr_max_re_tx = NET_TCP_RE_TX_TH_MIN; } #endif #if (NET_TCP_RE_TX_TH_MAX < DEF_INT_16U_MAX_VAL) if (nbr_max_re_tx > NET_TCP_RE_TX_TH_MAX) { nbr_max_re_tx = NET_TCP_RE_TX_TH_MAX; } #endif /* -------------- CFG TCP CONN RE-TX TH --------------- */ pconn = &NetTCP_ConnTbl[conn_id_tcp]; CPU_CRITICAL_ENTER(); pconn->TxSegReTxTh = nbr_max_re_tx; CPU_CRITICAL_EXIT(); return (DEF_OK); } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_ConnCfgReTxMaxTimeout() * * Description : Configure TCP connection's maximum retransmission timeout. * * Argument(s) : conn_id_tcp Handle identifier of TCP connection to configure retransmission * maximum timeout value. * * timeout_sec Desired value for TCP connection retransmission maximum timeout * (in seconds). * * Return(s) : DEF_OK, TCP connection retransmission maximum timeout successfully configured. * * DEF_FAIL, otherwise. * * Caller(s) : Application. * * This function is a network protocol suite application interface (API) function & MAY be * called by application function(s). * * Note(s) : (1) (a) RFC #2988, Section 2.4 states that "a maximum value MAY be placed on RTO * provided it is at least 60 seconds". * * (b) RFC #1122, Section 4.2.3.1 states that "the recommended ... RTO ... upper * bound should be 2*MSL". * * (c) Stevens, TCP/IP Illustrated, Volume 1, 8th Printing, Section 21.2, Page 299 * states that "the timeout value ... [has] an upper limit of 64 seconds". ******************************************************************************************************** */ CPU_BOOLEAN NetTCP_ConnCfgReTxMaxTimeout (NET_TCP_CONN_ID conn_id_tcp, NET_TCP_TIMEOUT_SEC timeout_sec) { #if (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL) CPU_SR cpu_sr; #endif NET_TCP_CONN *pconn; #if ((NET_ERR_CFG_ARG_CHK_EXT_EN == DEF_ENABLED) || \ (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED)) /* ---------------- VALIDATE TCP CONN ----------------- */ if (conn_id_tcp < NET_TCP_CONN_ID_MIN) { return (DEF_FAIL); } if (conn_id_tcp > NET_TCP_CONN_ID_MAX) { return (DEF_FAIL); } #endif /* ---------------- VALIDATE RTO MAX ------------------ */ /* See Note #1. */ #if (NET_TCP_TX_RTO_MAX_TIMEOUT_MIN_SEC > 0) if (timeout_sec < NET_TCP_TX_RTO_MAX_TIMEOUT_MIN_SEC) { timeout_sec = NET_TCP_TX_RTO_MAX_TIMEOUT_MIN_SEC; } #endif #if ((NET_TCP_TX_RTO_MAX_TIMEOUT_MAX_SEC < DEF_INT_08U_MAX_VAL) || \ ((NET_TCP_TX_RTO_MAX_TIMEOUT_MAX_SEC < DEF_INT_16U_MAX_VAL) && (NET_TCP_TX_RTO_MAX_TIMEOUT_MAX_SEC > DEF_INT_08U_MAX_VAL)) || \ ((NET_TCP_TX_RTO_MAX_TIMEOUT_MAX_SEC < DEF_INT_32U_MAX_VAL) && (NET_TCP_TX_RTO_MAX_TIMEOUT_MAX_SEC > DEF_INT_16U_MAX_VAL))) if (timeout_sec > NET_TCP_TX_RTO_MAX_TIMEOUT_MAX_SEC) { timeout_sec = NET_TCP_TX_RTO_MAX_TIMEOUT_MAX_SEC; } #endif /* --------------- CFG TCP CONN RTO MAX --------------- */ pconn = &NetTCP_ConnTbl[conn_id_tcp]; CPU_CRITICAL_ENTER(); pconn->TxRTT_RTO_Max_sec = timeout_sec; NetTCP_TxConnRTO_CfgMaxTimeout(pconn); /* Cfg RTO ctrls. */ CPU_CRITICAL_EXIT(); return (DEF_OK); } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_ConnCfgTxAckImmedRxdPushEn() * * Description : Configure TCP connection's transmit immediate acknowledgement for received & pushed TCP * segments enable. * * Argument(s) : conn_id_tcp Handle identifier of TCP connection to configure transmit immediate * acknowledgement for received & pushed TCP segments enable. * * tx_immed_ack_en Desired value for TCP connection transmit immediate acknowledgement * for received & pushed TCP segments enable : * * DEF_ENABLED TCP connection acknowledgements * immediately transmitted for any * pushed TCP segments received. * * DEF_DISABLED TCP connection acknowledgements NOT * immediately transmitted for any * pushed TCP segments received. * * * Return(s) : DEF_OK, TCP connection transmit immediate acknowledgement for received & pushed TCP * segments enable successfully configured. * * DEF_FAIL, otherwise. * * Caller(s) : Application. * * This function is a network protocol suite application interface (API) function & MAY be * called by application function(s). * * Note(s) : (1) See 'NetTCP_TxConnAck() Note #4a4A1'. ********************************************************************************************************* */ CPU_BOOLEAN NetTCP_ConnCfgTxAckImmedRxdPushEn (NET_TCP_CONN_ID conn_id_tcp, CPU_BOOLEAN tx_immed_ack_en) { #if (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL) CPU_SR cpu_sr; #endif NET_TCP_CONN *pconn; #if ((NET_ERR_CFG_ARG_CHK_EXT_EN == DEF_ENABLED) || \ (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED)) /* ---------------- VALIDATE TCP CONN ----------------- */ if (conn_id_tcp < NET_TCP_CONN_ID_MIN) { return (DEF_FAIL); } if (conn_id_tcp > NET_TCP_CONN_ID_MAX) { return (DEF_FAIL); } #endif /* ------------- VALIDATE TX IMMED ACK EN ------------- */ switch (tx_immed_ack_en) { case DEF_ENABLED: case DEF_DISABLED: break; default: tx_immed_ack_en = DEF_ENABLED; /* See Note #1. */ break; } /* ----------- CFG TCP CONN TX IMMED ACK EN ----------- */ pconn = &NetTCP_ConnTbl[conn_id_tcp]; CPU_CRITICAL_ENTER(); pconn->TxAckImmedRxdPushEn = tx_immed_ack_en; CPU_CRITICAL_EXIT(); return (DEF_OK); } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_ConnIsUsed() * * Description : Validate TCP connection in use. * * Argument(s) : conn_id_tcp Handle identifier of TCP connection to validate. * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_NONE TCP connection successfully validated as * in use. * NET_ERR_INIT_INCOMPLETE Network initialization NOT complete. * NET_TCP_ERR_INVALID_CONN Invalid TCP connection number. * NET_TCP_ERR_CONN_NOT_USED TCP connection NOT currently used. * * Return(s) : DEF_YES, TCP connection valid & in use. * * DEF_NO, TCP connection invalid or NOT in use. * * Caller(s) : various. * * This function is an INTERNAL network protocol suite function & SHOULD NOT be called by * application function(s). * * Note(s) : (1) NetTCP_ConnIsUsed() blocked until network initialization completes. * * (2) NetTCP_ConnIsUsed() MUST be called with the global network lock already acquired. ********************************************************************************************************* */ CPU_BOOLEAN NetTCP_ConnIsUsed (NET_TCP_CONN_ID conn_id_tcp, NET_ERR *perr) { #if ((NET_CTR_CFG_ERR_EN == DEF_ENABLED) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif NET_TCP_CONN *pconn; CPU_BOOLEAN used; #if (NET_ERR_CFG_ARG_CHK_EXT_EN == DEF_ENABLED) if (Net_InitDone != DEF_YES) { /* If init NOT complete, exit (see Note #1). */ *perr = NET_ERR_INIT_INCOMPLETE; return (DEF_NO); } #endif /* --------------- VALIDATE TCP CONN ID --------------- */ if (conn_id_tcp < NET_TCP_CONN_ID_MIN) { NET_CTR_ERR_INC(NetTCP_ErrConnInvalidCtr); *perr = NET_TCP_ERR_INVALID_CONN; return (DEF_NO); } if (conn_id_tcp > NET_TCP_CONN_ID_MAX) { NET_CTR_ERR_INC(NetTCP_ErrConnInvalidCtr); *perr = NET_TCP_ERR_INVALID_CONN; return (DEF_NO); } /* -------------- VALIDATE TCP CONN USED -------------- */ pconn = &NetTCP_ConnTbl[conn_id_tcp]; used = DEF_BIT_IS_SET(pconn->Flags, NET_TCP_FLAG_USED); if (used != DEF_YES) { NET_CTR_ERR_INC(NetTCP_ErrNotUsedCtr); *perr = NET_TCP_ERR_CONN_NOT_USED; return (DEF_NO); } *perr = NET_TCP_ERR_NONE; return (DEF_YES); } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_ConnPoolStatGet() * * Description : Get TCP connection's statistics pool. * * Argument(s) : none. * * Return(s) : TCP connection statistics pool, if NO errors. * * NULL statistics pool, otherwise. * * Caller(s) : Application. * * This function is a network protocol suite application interface (API) function & MAY be * called by application function(s). * * Note(s) : (1) NetTCP_ConnPoolStatGet() blocked until network initialization completes; return NULL * statistics pool. ********************************************************************************************************* */ NET_STAT_POOL NetTCP_ConnPoolStatGet (void) { #if (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL) CPU_SR cpu_sr; #endif #if (NET_ERR_CFG_ARG_CHK_EXT_EN == DEF_ENABLED) NET_ERR err; #endif NET_STAT_POOL stat_pool; #if (NET_ERR_CFG_ARG_CHK_EXT_EN == DEF_ENABLED) if (Net_InitDone != DEF_YES) { /* If init NOT complete, ... */ NetStat_PoolClr(&stat_pool, &err); return (stat_pool); /* ... rtn NULL stat pool (see Note #1). */ } #endif CPU_CRITICAL_ENTER(); stat_pool = NetTCP_ConnPoolStat; CPU_CRITICAL_EXIT(); return (stat_pool); } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_ConnPoolStatResetMaxUsed() * * Description : Reset TCP connection's statistics pool's maximum number of entries used. * * Argument(s) : none. * * Return(s) : none. * * Caller(s) : Application. * * This function is a network protocol suite application interface (API) function & MAY be * called by application function(s). * * Note(s) : none. ********************************************************************************************************* */ void NetTCP_ConnPoolStatResetMaxUsed (void) { NET_ERR err; NetStat_PoolResetUsedMax(&NetTCP_ConnPoolStat, &err); } /*$PAGE*/ /* ********************************************************************************************************* ********************************************************************************************************* * LOCAL FUNCTIONS ********************************************************************************************************* ********************************************************************************************************* */ /* ********************************************************************************************************* * NetTCP_RxPktValidateBuf() * * Description : Validate received buffer header as TCP protocol. * * Argument(s) : pbuf_hdr Pointer to network buffer header that received TCP packet. * -------- Argument validated in NetTCP_Rx(). * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_NONE Received buffer's TCP header validated. * NET_ERR_INVALID_PROTOCOL Buffer's protocol type is NOT TCP. * NET_BUF_ERR_INVALID_IX Invalid buffer index. * * Return(s) : none. * * Caller(s) : NetTCP_Rx(). * * Note(s) : none. ********************************************************************************************************* */ #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) static void NetTCP_RxPktValidateBuf (NET_BUF_HDR *pbuf_hdr, NET_ERR *perr) { #if ((NET_CTR_CFG_ERR_EN == DEF_ENABLED) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif /* --------------- VALIDATE TCP BUF HDR --------------- */ if (pbuf_hdr->ProtocolHdrType != NET_PROTOCOL_TYPE_TCP) { NET_CTR_ERR_INC(Net_ErrInvalidProtocolCtr); *perr = NET_ERR_INVALID_PROTOCOL; return; } if (pbuf_hdr->TCP_UDP_HdrDataIx == NET_BUF_IX_NONE) { NET_CTR_ERR_INC(NetTCP_ErrRxInvalidBufIxCtr); *perr = NET_BUF_ERR_INVALID_IX; return; } *perr = NET_TCP_ERR_NONE; } #endif /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_RxPktValidate() * * Description : (1) Validate received TCP packet : * * (a) Get TCP packet RTT timestamp received. See 'NetTCP_TxConnRTT_RTO_Calc() * Note #2a2A2' * * (b) (1) Validate the received packet's following TCP header fields : * * (A) Source Port * (B) Destination Port * (C) Header Length * (D) Segment Length See Note #3 * (E) Flags * (F) Check-Sum See Note #4 * (G) Options * * (2) Validation ignores the following TCP header fields : * * (A) Sequence Number * (B) Acknowledgement Number * (C) Window Advertisement * (D) Urgent Pointer See 'net_tcp.c Note #1b' * * (c) Convert the following TCP header fields from network-order to host-order : * * (1) Source Port See Note #1cB1 * (2) Destination Port See Note #1cB2 * (3) Sequence Number See Note #1cB3 * (4) Acknowledgement Number See Note #1cB4 * (5) Header Length/Flags See Note #1cB5 * (6) Window Advertisement See Note #1cB6 * (7) Check-Sum See Note #4f * (8) Urgent Pointer See 'net_tcp.c Note #1b' * * (A) These fields are NOT converted directly in the received packet buffer's * data area but are converted in local or network buffer variables ONLY. * * (B) The following TCP header fields are converted & stored in network buffer * variables : * * (1) Source Port * (2) Destination Port * (3) Sequence Number * (4) Acknowledgement Number * (5) Header Length/Flags * (6) Window Advertisement * * (d) Update network buffer's protocol controls. * * * Argument(s) : pbuf Pointer to network buffer that received TCP packet. * ---- Argument checked in NetTCP_Rx(). * * pbuf_hdr Pointer to network buffer header. * -------- Argument validated in NetTCP_Rx(). * * ptcp_hdr Pointer to received packet's TCP header. * -------- Argument validated in NetTCP_Rx()/NetTCP_RxPktValidateBuf(). * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_NONE Received packet validated. * NET_TCP_ERR_INVALID_PORT_NBR Invalid TCP port number. * NET_TCP_ERR_INVALID_LEN_HDR Invalid TCP header length. * NET_TCP_ERR_INVALID_LEN_SEG Invalid TCP segment length. * NET_TCP_ERR_INVALID_LEN_DATA Invalid TCP data length. * NET_TCP_ERR_INVALID_FLAG Invalid TCP flags. * NET_TCP_ERR_INVALID_CHK_SUM Invalid TCP check-sum. * * - RETURNED BY NetTCP_RxPktValidateOpt() : - * NET_TCP_ERR_INVALID_OPT_LEN Invalid TCP option length. * NET_TCP_ERR_INVALID_OPT_END Invalid TCP option list ending. * NET_TCP_ERR_INVALID_OPT_NBR Invalid TCP option number of same option. * * Return(s) : none. * * Caller(s) : NetTCP_Rx(). *$PAGE* * Note(s) : (2) See 'net_tcp.h TCP HEADER' for TCP header format. * * (3) Since TCP segment headers do NOT contain a segment length field, the TCP Segment Length * is assumed to be the remaining IP Datagram Length. * * (4) (a) TCP header Check-Sum field MUST be validated BEFORE (or AFTER) any multi-octet words * are converted from network-order to host-order since "the sum of 16-bit integers can * be computed in either byte order" [RFC #1071, Section 2.(B)]. * * In other words, the TCP Segment Check-Sum CANNOT be validated AFTER SOME but NOT ALL * multi-octet words have been converted from network-order to host-order. * * (b) However, ALL received packets' multi-octet words are converted in local or network * buffer variables ONLY (see Note #1cA). Therefore, TCP Segment Check-Sum may be * validated at any point. * * (c) The TCP Segment Check-Sum MUST be validated AFTER the TCP segment length has been * validated so that the total TCP Segment Length (in octets) will already be calculated * for the TCP Check-Sum calculation. * * For efficiency, the TCP Segment Check-Sum is validated AFTER all other TCP header * fields have been validated. Thus, the iteration-intensive TCP Segment Check-Sum is * calculated only after all other TCP header fields have been quickly validated. * * (d) (1) In addition to the TCP segment header & data, the TCP Check-Sum calculation * includes "a 96-bit pseudo header conceptually prefixed to the TCP header ... * [which] contains the Source Address, the Destination Address, the Protocol, * and TCP length" (see RFC #793, Section 3.1 'Header Format : Checksum'). * * (2) Since network check-sum functions REQUIRE that 16-bit one's-complement check- * sum calculations be performed on headers & data arranged in network-order (see * 'net_util.c NetUtil_16BitOnesCplChkSumDataVerify() Note #4'), TCP pseudo-header * values MUST be set or converted to network-order. * * (e) RFC #793, Section 3.1 'Header Format : Checksum' specifies that "if a segment contains * an odd number of header and text octets ... the last octet is padded ... with zeros to * form a 16-bit word for checksum purposes". * * See also 'net_util.c NetUtil_16BitSumDataCalc() Note #8'. * * (f) After the TCP Segment Check-Sum is validated, it is NOT necessary to convert the Check- * Sum from network-order to host-order since it is NOT required for further processing. * * (5) (a) Since the minimum network buffer size MUST be configured such that the entire TCP * header MUST be received in a single packet (see 'net_buf.h NETWORK BUFFER INDEX & * SIZE DEFINES Note #1c'), after the TCP header size is decremented from the first * packet buffer's remaining number of data octets, any remaining octets MUST be user * &/or application data octets. * * (1) Note that the 'Data' index is updated regardless of a null-size data length. * * (b) If additional packet buffers exist, the remaining IP datagram 'Data' MUST be user * &/or application data. Therefore, the 'Data' length does NOT need to be adjusted * but the 'Data' index MUST be updated. * * (c) #### Total TCP Segment Length & Data Length is duplicated in ALL fragmented packet * buffers (may NOT be necessary; remove if unnecessary). * * (6) RFC #1122, Sections 3.2.1 & 3.2.2 require that IP & ICMP packets with certain invalid * header fields be "silently discarded". However, NO RFC specifies how TCP should handle * received segments with invalid header fields. * * Therefore, it is assumed that ALL TCP segments with ANY invalid header fields SHOULD * be silently discarded. ********************************************************************************************************* */ static void NetTCP_RxPktValidate (NET_BUF *pbuf, NET_BUF_HDR *pbuf_hdr, NET_TCP_HDR *ptcp_hdr, NET_ERR *perr) { #if ((NET_CTR_CFG_ERR_EN == DEF_ENABLED) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif CPU_INT08U tcp_hdr_len_tot; CPU_INT16U tcp_hdr_len; CPU_INT16U tcp_tot_len; CPU_INT16U tcp_data_len; CPU_INT16U tcp_flags; CPU_INT16U tcp_flags_reserved; CPU_BOOLEAN tcp_chk_sum_valid; NET_TCP_PSEUDO_HDR tcp_pseudo_hdr; NET_BUF *pbuf_next; NET_BUF_HDR *pbuf_next_hdr; /*$PAGE*/ /* ---------------- GET TCP RTT RX TS ----------------- */ pbuf_hdr->TCP_RTT_TS_Rxd_ms = (CPU_INT32U)NetTCP_TxConnRTT_GetTS_ms(); /* ---------------- VALIDATE TCP PORTS ---------------- */ NET_UTIL_VAL_COPY_GET_NET_16(&pbuf_hdr->TCP_UDP_PortSrc, &ptcp_hdr->PortSrc); if (pbuf_hdr->TCP_UDP_PortSrc == NET_TCP_PORT_NBR_RESERVED) { NET_CTR_ERR_INC(NetTCP_ErrRxHdrPortSrcCtr); *perr = NET_TCP_ERR_INVALID_PORT_NBR; return; } NET_UTIL_VAL_COPY_GET_NET_16(&pbuf_hdr->TCP_UDP_PortDest, &ptcp_hdr->PortDest); if (pbuf_hdr->TCP_UDP_PortDest == NET_TCP_PORT_NBR_RESERVED) { NET_CTR_ERR_INC(NetTCP_ErrRxHdrPortDestCtr); *perr = NET_TCP_ERR_INVALID_PORT_NBR; return; } /* --------------- VALIDATE TCP HDR LEN --------------- */ /* See 'net_tcp.h TCP HEADER Note #2'. */ NET_UTIL_VAL_COPY_GET_NET_16(&pbuf_hdr->TCP_HdrLen_Flags, &ptcp_hdr->HdrLen_Flags); tcp_hdr_len = pbuf_hdr->TCP_HdrLen_Flags & NET_TCP_HDR_LEN_MASK; tcp_hdr_len >>= NET_TCP_HDR_LEN_SHIFT; if (tcp_hdr_len < NET_TCP_HDR_LEN_MIN) { /* If hdr len < min hdr len, rtn err. */ NET_CTR_ERR_INC(NetTCP_ErrRxHdrLenCtr); *perr = NET_TCP_ERR_INVALID_LEN_HDR; return; } #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) if (tcp_hdr_len > NET_TCP_HDR_LEN_MAX) { /* If hdr len > max hdr len, rtn err. */ NET_CTR_ERR_INC(NetTCP_ErrRxHdrLenCtr); *perr = NET_TCP_ERR_INVALID_LEN_HDR; return; } #endif tcp_hdr_len_tot = tcp_hdr_len * NET_TCP_HDR_LEN_WORD_SIZE; /* ------------- VALIDATE TCP SEG TOT LEN ------------- */ tcp_tot_len = pbuf_hdr->IP_DatagramLen; /* See Note #3. */ pbuf_hdr->TCP_UDP_TotLen = tcp_tot_len; if (pbuf_hdr->TCP_UDP_TotLen < NET_TCP_TOT_LEN_MIN) { /* If seg tot len < min tot len, rtn err. */ NET_CTR_ERR_INC(NetTCP_ErrRxHdrSegLenCtr); *perr = NET_TCP_ERR_INVALID_LEN_SEG; return; } if (pbuf_hdr->TCP_UDP_TotLen > NET_TCP_TOT_LEN_MAX) { /* If seg tot len > max tot len, rtn err. */ NET_CTR_ERR_INC(NetTCP_ErrRxHdrSegLenCtr); *perr = NET_TCP_ERR_INVALID_LEN_SEG; return; } /* ---------------- VALIDATE TCP FLAGS ---------------- */ /* See 'net_tcp.h TCP HEADER Note #2'. */ #if 1 /* ???? Allow invalid reserved flags for rx'd segs? */ tcp_flags = pbuf_hdr->TCP_HdrLen_Flags & NET_TCP_HDR_FLAG_MASK; tcp_flags_reserved = DEF_BIT_IS_SET_ANY(tcp_flags, NET_TCP_HDR_FLAG_RESERVED); if (tcp_flags_reserved != DEF_NO) { /* If reserved flag bits set, rtn err. */ NET_CTR_ERR_INC(NetTCP_ErrRxHdrFlagsCtr); *perr = NET_TCP_ERR_INVALID_FLAG; return; } #endif pbuf_hdr->TCP_SegSync = DEF_BIT_IS_SET(pbuf_hdr->TCP_HdrLen_Flags, NET_TCP_HDR_FLAG_SYNC ); pbuf_hdr->TCP_SegClose = DEF_BIT_IS_SET(pbuf_hdr->TCP_HdrLen_Flags, NET_TCP_HDR_FLAG_CLOSE); pbuf_hdr->TCP_SegAck = DEF_BIT_IS_SET(pbuf_hdr->TCP_HdrLen_Flags, NET_TCP_HDR_FLAG_ACK ); pbuf_hdr->TCP_SegReset = DEF_BIT_IS_SET(pbuf_hdr->TCP_HdrLen_Flags, NET_TCP_HDR_FLAG_RESET); /*$PAGE*/ /* ---------------- VALIDATE TCP OPTS ----------------- */ if (tcp_hdr_len_tot > NET_TCP_HDR_SIZE_MIN) { /* If hdr len > min, validate/process TCP opts. */ NetTCP_RxPktValidateOpt(pbuf_hdr, ptcp_hdr, tcp_hdr_len_tot, perr); if (*perr != NET_TCP_ERR_NONE) { return; } } /* --------------- VALIDATE TCP CHK SUM --------------- */ /* See Note #4. */ /* Prepare TCP chk sum pseudo-hdr (see Note #4d). */ tcp_pseudo_hdr.AddrSrc = (NET_IP_ADDR)NET_UTIL_HOST_TO_NET_32(pbuf_hdr->IP_AddrSrc); tcp_pseudo_hdr.AddrDest = (NET_IP_ADDR)NET_UTIL_HOST_TO_NET_32(pbuf_hdr->IP_AddrDest); tcp_pseudo_hdr.Zero = (CPU_INT08U )0x00; tcp_pseudo_hdr.Protocol = (CPU_INT08U )NET_IP_HDR_PROTOCOL_TCP; tcp_pseudo_hdr.TotLen = (CPU_INT16U )NET_UTIL_HOST_TO_NET_16(pbuf_hdr->TCP_UDP_TotLen); tcp_chk_sum_valid = NetUtil_16BitOnesCplChkSumDataVerify((void *) pbuf, (void *)&tcp_pseudo_hdr, (CPU_INT16U) NET_TCP_PSEUDO_HDR_SIZE, (NET_ERR *) perr); if (tcp_chk_sum_valid != DEF_OK) { NET_CTR_ERR_INC(NetTCP_ErrRxHdrChkSumCtr); *perr = NET_TCP_ERR_INVALID_CHK_SUM; return; } #if 0 /* See Note #4f. */ (void)NET_UTIL_VAL_GET_NET_16(&ptcp_hdr->ChkSum); #endif /* ---------------- CONVERT TCP FIELDS ---------------- */ /* See Note #1c. */ NET_UTIL_VAL_COPY_GET_NET_32(&pbuf_hdr->TCP_SeqNbr, &ptcp_hdr->SeqNbr); NET_UTIL_VAL_COPY_GET_NET_32(&pbuf_hdr->TCP_AckNbr, &ptcp_hdr->AckNbr); NET_UTIL_VAL_COPY_GET_NET_16(&pbuf_hdr->TCP_WinSize, &ptcp_hdr->WinSize); /* ----------------- UPDATE BUF CTRLS ----------------- */ /* Calc TCP data len/ix (see Note #5a). */ pbuf_hdr->TCP_UDP_HdrLen = tcp_hdr_len_tot; #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) if (pbuf_hdr->TCP_UDP_HdrLen > tcp_tot_len) { NET_CTR_ERR_INC(NetTCP_ErrRxHdrDataLenCtr); *perr = NET_TCP_ERR_INVALID_LEN_DATA; return; } if (pbuf_hdr->TCP_UDP_HdrLen > pbuf_hdr->DataLen) { NET_CTR_ERR_INC(NetTCP_ErrRxHdrDataLenCtr); *perr = NET_TCP_ERR_INVALID_LEN_DATA; return; } #endif tcp_data_len = tcp_tot_len - pbuf_hdr->TCP_UDP_HdrLen; pbuf_hdr->TCP_UDP_DataLen = tcp_data_len; pbuf_hdr->TCP_SegLenInit = pbuf_hdr->TCP_UDP_DataLen; pbuf_hdr->TCP_SegLen = pbuf_hdr->TCP_SegLenInit; pbuf_hdr->TCP_SegLenData = pbuf_hdr->TCP_SegLenInit; pbuf_hdr->TCP_SeqNbrInit = pbuf_hdr->TCP_SeqNbr; pbuf_hdr->DataLen -= (NET_BUF_SIZE) pbuf_hdr->TCP_UDP_HdrLen; pbuf_hdr->DataIx = (CPU_INT16U )(pbuf_hdr->TCP_UDP_HdrDataIx + pbuf_hdr->TCP_UDP_HdrLen); pbuf_hdr->ProtocolHdrType = NET_PROTOCOL_TYPE_APP; pbuf_next = (NET_BUF *)pbuf_hdr->NextBufPtr; while (pbuf_next != (NET_BUF *)0) { /* Calc ALL pkt bufs' data len/ix (see Note #5b). */ pbuf_next_hdr = &pbuf_next->Hdr; pbuf_next_hdr->DataIx = pbuf_next_hdr->TCP_UDP_HdrDataIx; pbuf_next_hdr->TCP_UDP_HdrLen = 0; /* NULL TCP hdr len in each pkt buf. */ pbuf_next_hdr->TCP_UDP_TotLen = tcp_tot_len; /* Dup TCP tot len & ... */ pbuf_next_hdr->TCP_UDP_DataLen = tcp_data_len; /* ... data len in each pkt buf (see Note #5c). */ pbuf_next_hdr->TCP_SegLenInit = pbuf_next_hdr->TCP_UDP_DataLen; pbuf_next_hdr->TCP_SegLen = pbuf_next_hdr->TCP_SegLenInit; pbuf_next_hdr->TCP_SegLenData = pbuf_next_hdr->TCP_SegLenInit; pbuf_hdr->ProtocolHdrType = NET_PROTOCOL_TYPE_APP; pbuf_next = (NET_BUF *)pbuf_next_hdr->NextBufPtr; } *perr = NET_TCP_ERR_NONE; } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_RxPktValidateOpt() * * Description : Validate & process received packet's TCP options. * * Argument(s) : pbuf_hdr Pointer to network buffer header that received TCP packet. * -------- Argument validated in NetTCP_Rx(). * * ptcp_hdr Pointer to received packet's TCP header. * -------- Argument validated in NetTCP_Rx()/NetTCP_RxPktValidateBuf(). * * tcp_hdr_len_size Length of received packet's TCP header. * ---------------- Argument validated in NetTCP_RxPktValidate(). * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_NONE TCP options validated & processed. * NET_TCP_ERR_INVALID_OPT_LEN Invalid TCP option length. * NET_TCP_ERR_INVALID_OPT_END Invalid TCP option list ending. * NET_TCP_ERR_INVALID_OPT_NBR Invalid TCP option number of same option. * * Return(s) : none. * * Caller(s) : NetTCP_RxPktValidate(). * * Note(s) : (1) (a) See 'net_tcp.h TCP HEADER OPTIONS DEFINES' for supported TCP options' summary. * * (b) See 'net_tcp.c Note #1c' for unsupported TCP options. * * See also Note #2b3. * * (2) (a) RFC # 793, Section 3.1 'Options' states that each option is "a multiple of 8 bits * in length" and "may begin on any octet boundary". * * (1) Since TCP options are NOT required or guaranteed to align multi-octet words on * appropriate word boundaries, ALL TCP options are decoded & processed a single * octet at a time. * * (b) RFC #1122, Section 4.2.2.5 states that ... : * * (1) "A TCP MUST be able to receive a TCP option in any segment." * * (2) "A TCP MUST ignore without error any TCP option it does not implement, assuming * that the option has a field length" ... * * (3) "All TCP options defined in the future will have length fields." * * See also 'net_tcp.h TCP HEADER OPTIONS DEFINES Note #2b'. ********************************************************************************************************* */ static void NetTCP_RxPktValidateOpt (NET_BUF_HDR *pbuf_hdr, NET_TCP_HDR *ptcp_hdr, CPU_INT08U tcp_hdr_len_size, NET_ERR *perr) { #if ((NET_CTR_CFG_ERR_EN == DEF_ENABLED) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif CPU_INT08U *popts; CPU_INT08U *popt; CPU_INT08U opt_list_len_size; CPU_INT08U opt_list_len_rem; CPU_INT08U opt_len; CPU_INT08U opt_nbr_max_seg_size; CPU_BOOLEAN opt_err; CPU_BOOLEAN opt_list_end; opt_list_len_size = tcp_hdr_len_size - NET_TCP_HDR_SIZE_MIN; /* Calc opt list len size. */ #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) /* -------- VALIDATE TCP HDR OPT LIST SIZE -------- */ if (opt_list_len_size > NET_TCP_HDR_OPT_SIZE_MAX) { /* If tot opt len > max opt size, ... */ NET_CTR_ERR_INC(NetTCP_ErrRxHdrOptsCtr); *perr = NET_TCP_ERR_INVALID_OPT_LEN; /* ... rtn err. */ return; } if ((opt_list_len_size % NET_TCP_HDR_OPT_SIZE_WORD) != 0) { /* If tot opt len NOT multiple of opt size, ... */ NET_CTR_ERR_INC(NetTCP_ErrRxHdrOptsCtr); *perr = NET_TCP_ERR_INVALID_OPT_LEN; /* ... rtn err. */ return; } #endif /*$PAGE*/ opt_err = DEF_NO; opt_list_end = DEF_NO; opt_nbr_max_seg_size = 0; popts = (CPU_INT08U *)&ptcp_hdr->Opts[0]; opt_list_len_rem = opt_list_len_size; /* ------------- DECODE/VALIDATE TCP OPTS ------------- */ while (opt_list_len_rem > 0) { /* Process each opt in list (see Notes #1 & #2). */ switch (*popts) { case NET_TCP_HDR_OPT_END_LIST: /* ------------------- END OPT LIST ------------------- */ opt_list_end = DEF_YES; /* Mark end of opt list. */ opt_len = NET_TCP_HDR_OPT_LEN_END_LIST; break; case NET_TCP_HDR_OPT_NOP: /* --------------------- NOP OPT ---------------------- */ #if 1 /* ???? NOP's invalid after END? */ if (opt_list_end != DEF_NO) { /* If opt found AFTER end of opt list, rtn err. */ NET_CTR_ERR_INC(NetTCP_ErrRxHdrOptsCtr); *perr = NET_TCP_ERR_INVALID_OPT_END; return; } #endif opt_len = NET_TCP_HDR_OPT_LEN_NOP; break; case NET_TCP_HDR_OPT_MAX_SEG_SIZE: /* ----------------- MAX SEG SIZE OPT ----------------- */ if (opt_list_end != DEF_NO) { /* If opt found AFTER end of opt list, rtn err. */ NET_CTR_ERR_INC(NetTCP_ErrRxHdrOptsCtr); *perr = NET_TCP_ERR_INVALID_OPT_END; return; } if (opt_nbr_max_seg_size > 0) { /* If > 1 max seg size opt, rtn err. */ NET_CTR_ERR_INC(NetTCP_ErrRxHdrOptsCtr); *perr = NET_TCP_ERR_INVALID_OPT_NBR; return; } opt_nbr_max_seg_size++; opt_err = NetTCP_RxPktValidateOptMaxSegSize(pbuf_hdr, popts, &opt_len, perr); break; #if 0 /* --------------- UNSUPPORTED TCP OPTS --------------- */ /* See Notes #1b & #2b2. */ case NET_TCP_HDR_OPT_WIN_SCALE: case NET_TCP_HDR_OPT_SACK_PERMIT: case NET_TCP_HDR_OPT_SACK: case NET_TCP_HDR_OPT_ECHO_REQ: case NET_TCP_HDR_OPT_ECHO_REPLY: case NET_TCP_HDR_OPT_TS: /* 'break' intentionally omitted; do NOT move from the */ /* ... following case : 'default'. */ #endif default: /* ----------------- UNKNOWN TCP OPTS ----------------- */ popt = popts; popt++; opt_len = *popt; /* Validate opt len (see Note #2b3). */ if (opt_len < NET_TCP_HDR_OPT_LEN_MIN_LEN) { /* If opt len < min opt len, rtn err. */ NET_CTR_ERR_INC(NetTCP_ErrRxHdrOptsCtr); *perr = NET_TCP_ERR_INVALID_OPT_LEN; return; } break; } if (opt_err != DEF_NO) { /* If ANY opt errs, rtn err. */ return; } if (opt_len > opt_list_len_rem) { /* If opt len > rem opt list len, rtn err. */ NET_CTR_ERR_INC(NetTCP_ErrRxHdrOptsCtr); *perr = NET_TCP_ERR_INVALID_OPT_LEN; return; } opt_list_len_rem -= opt_len; popts += opt_len; } *perr = NET_TCP_ERR_NONE; } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_RxPktValidateOptMaxSegSize() * * Description : Validate & process received TCP Maximum Segment Size option. * * Argument(s) : pbuf_hdr Pointer to network buffer header that received TCP packet. * -------- Argument validated in NetTCP_Rx(). * * popt Pointer to Maximum Segment Size option. * ---- Argument validated in NetTCP_RxPktValidateOpt(). * * popt_len Pointer to variable that will receive the TCP option length (in octets). * -------- Argument validated in NetTCP_RxPktValidateOpt(). * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_NONE TCP option validated & processed. * NET_TCP_ERR_INVALID_OPT_LEN Invalid TCP option length. * * Return(s) : DEF_NO, NO Maximum Segment Size option error. * * DEF_YES, otherwise. * * Caller(s) : NetTCP_RxPktValidateOpt(). * * Note(s) : (1) See 'net_tcp.h TCP HEADER OPTIONS DEFINES Note #2b1' for TCP Maximum Segment Size * option summary. * * (2) (a) RFC # 793, Section 3.1 'Options' states that each option is "a multiple of 8 bits * in length" and "may begin on any octet boundary". * * (b) Since TCP options are NOT required or guaranteed to align multi-octet words on * appropriate word boundaries, ALL TCP options are decoded & processed a single * octet at a time. ********************************************************************************************************* */ static CPU_BOOLEAN NetTCP_RxPktValidateOptMaxSegSize (NET_BUF_HDR *pbuf_hdr, CPU_INT08U *popt, CPU_INT08U *popt_len, NET_ERR *perr) { #if ((NET_CTR_CFG_ERR_EN == DEF_ENABLED) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif NET_TCP_SEG_SIZE max_seg_size; *popt_len = NET_TCP_HDR_OPT_LEN_MAX_SEG_SIZE; popt++; if (*popt != *popt_len) { /* If opt len != max seg size opt len, rtn err. */ NET_CTR_ERR_INC(NetTCP_ErrRxHdrOptsCtr); *perr = NET_TCP_ERR_INVALID_OPT_LEN; return (DEF_YES); } /* Calc max seg size (see Note #2b). */ popt++; max_seg_size = *popt; max_seg_size <<= DEF_OCTET_NBR_BITS; popt++; max_seg_size += *popt; if (max_seg_size > NET_TCP_MAX_SEG_SIZE_MAX) { /* If max seg size > max, rtn err. */ NET_CTR_ERR_INC(NetTCP_ErrRxHdrOptsCtr); *perr = NET_TCP_ERR_INVALID_OPT_LEN; return (DEF_YES); } pbuf_hdr->TCP_MaxSegSize = (CPU_INT16U)max_seg_size; *perr = NET_TCP_ERR_NONE; return (DEF_NO); } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_RxPktDemuxSeg() * * Description : (1) Demultiplex received packet to appropriate TCP connection : * * (a) Search connection lists for connection whose local &/or remote addresses are * identical to the received packet's destination & source addresses. * * (b) Update network buffer's connection controls. * * * Argument(s) : pbuf Pointer to network buffer that received TCP packet. * ---- Argument checked in NetTCP_Rx(). * * pbuf_hdr Pointer to network buffer header. * -------- Argument validated in NetTCP_Rx(). * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_NONE Received packet successfully demultiplexed * to appropriate TCP connection. * NET_CONN_ERR_INVALID_FAMILY Invalid network connection family. * NET_CONN_ERR_INVALID_CONN Invalid network connection number(s). * NET_ERR_RX_DEST Invalid destination; no connection available * for received packet. * * Return(s) : none. * * Caller(s) : NetTCP_Rx(). * * Note(s) : (2) The 'SRCH CONN LIST(S) FOR PKT/CONN ADDR(S)' pre-processor 'else'-conditional code will * never be compiled/linked since 'net_conn.h' ensures that the family type configuration * constant (NET_CONN_CFG_FAMILY) is configured with an appropriate family type value * (see 'net_conn.h CONFIGURATION ERRORS'). The 'else'-conditional code is included for * completeness & as an extra precaution in case 'net_conn.h' is incorrectly modified. * * (3) (a) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : CLOSED [State]' states * that "an incoming segment ... causes a RST to be sent in response". * * (b) RFC #792, Section 'Destination Unreachable Message : Description' states that * "if, in the destination host, the IP module cannot deliver the datagram because * the indicated ... process port is not active, the destination host may send a * destination unreachable message to the source host". ********************************************************************************************************* */ /*$PAGE*/ static void NetTCP_RxPktDemuxSeg (NET_BUF *pbuf, NET_BUF_HDR *pbuf_hdr, NET_ERR *perr) { #if ((NET_CTR_CFG_ERR_EN == DEF_ENABLED) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif #if (NET_CONN_CFG_FAMILY == NET_CONN_FAMILY_IP_V4_SOCK) NET_IP_ADDR addr_ip; #endif CPU_INT08U addr_local[NET_CONN_CFG_ADDR_LEN]; CPU_INT08U addr_remote[NET_CONN_CFG_ADDR_LEN]; NET_TCP_PORT_NBR port_nbr; NET_CONN_LIST_IX protocol_ix; NET_CONN_FAMILY family; NET_CONN_ID conn_id; NET_CONN_ID conn_id_transport; NET_CONN_ID conn_id_app; NET_ERR err; /* ---- SRCH CONN LIST(S) FOR PKT/CONN ADDR(S) ---- */ #if (NET_CONN_CFG_FAMILY == NET_CONN_FAMILY_IP_V4_SOCK) family = NET_CONN_FAMILY_IP_V4_SOCK; protocol_ix = NET_CONN_LIST_IX_IP_V4_SOCK_TCP; /* Cfg srch local addr as pkt dest addr. */ port_nbr = (NET_TCP_PORT_NBR)NET_UTIL_HOST_TO_NET_16(pbuf_hdr->TCP_UDP_PortDest); addr_ip = (NET_IP_ADDR )NET_UTIL_HOST_TO_NET_32(pbuf_hdr->IP_AddrDest); Mem_Copy((void *)&addr_local[NET_CONN_ADDR_IP_IX_PORT], (void *)&port_nbr, (CPU_SIZE_T) NET_CONN_ADDR_IP_LEN_PORT); Mem_Copy((void *)&addr_local[NET_CONN_ADDR_IP_IX_ADDR], (void *)&addr_ip, (CPU_SIZE_T) NET_CONN_ADDR_IP_LEN_ADDR); /* Cfg srch remote addr as pkt src addr. */ port_nbr = (NET_TCP_PORT_NBR)NET_UTIL_HOST_TO_NET_16(pbuf_hdr->TCP_UDP_PortSrc); addr_ip = (NET_IP_ADDR )NET_UTIL_HOST_TO_NET_32(pbuf_hdr->IP_AddrSrc); Mem_Copy((void *)&addr_remote[NET_CONN_ADDR_IP_IX_PORT], (void *)&port_nbr, (CPU_SIZE_T) NET_CONN_ADDR_IP_LEN_PORT); Mem_Copy((void *)&addr_remote[NET_CONN_ADDR_IP_IX_ADDR], (void *)&addr_ip, (CPU_SIZE_T) NET_CONN_ADDR_IP_LEN_ADDR); #else /* See Note #2. */ *perr = NET_CONN_ERR_INVALID_FAMILY; return; #endif /*$PAGE*/ conn_id = NetConn_Srch((NET_CONN_FAMILY ) family, /* Srch for TCP conn whose local/remote addrs ... */ (NET_CONN_LIST_IX ) protocol_ix, /* ... are identical to pkt dest/src addrs. */ (NET_CONN_LIST_TYPE) NET_CONN_LIST_TYPE_ALL, (CPU_BOOLEAN ) DEF_YES, (CPU_INT08U *)&addr_local[0], (CPU_INT08U *)&addr_remote[0], (NET_CONN_ADDR_LEN ) NET_CONN_CFG_ADDR_LEN, (NET_CONN_ID *)&conn_id_transport, (NET_CONN_ID *)&conn_id_app); if (conn_id == NET_CONN_ID_NONE) { /* If complete TCP conn NOT found, .. */ conn_id = NetConn_Srch((NET_CONN_FAMILY ) family, /* .. srch for TCP half-conn whose local addr .. */ (NET_CONN_LIST_IX ) protocol_ix, /* .. is identical to pkt dest addr. */ (NET_CONN_LIST_TYPE) NET_CONN_LIST_TYPE_ALL, (CPU_BOOLEAN ) DEF_NO, (CPU_INT08U *)&addr_local[0], (CPU_INT08U *) 0, (NET_CONN_ADDR_LEN ) NET_CONN_CFG_ADDR_LEN, (NET_CONN_ID *)&conn_id_transport, (NET_CONN_ID *)&conn_id_app); if (conn_id == NET_CONN_ID_NONE) { /* If NO TCP conn found, ... */ NetTCP_TxConnReset((NET_TCP_CONN *) 0, /* ... tx TCP conn reset (see Note #3a), ... */ (NET_BUF_HDR *) pbuf_hdr, (NET_TCP_RESET_CODE) NET_TCP_CONN_TX_RESET, (NET_TCP_CLOSE_CODE) NET_TCP_CONN_CLOSE_ALL, (NET_ERR *)&err); NetICMP_TxMsgErr(pbuf, /* ... tx ICMP port unreach (see Note #3b), ... */ NET_ICMP_MSG_TYPE_DEST_UNREACH, NET_ICMP_MSG_CODE_DEST_PORT, NET_ICMP_MSG_PTR_NONE, &err); NET_CTR_ERR_INC(NetTCP_ErrRxDestCtr); *perr = NET_ERR_RX_DEST; /* ... & rtn dest err. */ return; } else { pbuf_hdr->ConnType = NET_CONN_TYPE_CONN_HALF; } } else { pbuf_hdr->ConnType = NET_CONN_TYPE_CONN_FULL; } /* ------------ UPDATE BUF CONN CTRLS ------------- */ #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) if (conn_id == NET_CONN_ID_NONE) { *perr = NET_CONN_ERR_INVALID_CONN; return; } if (conn_id_transport == NET_CONN_ID_NONE) { *perr = NET_CONN_ERR_INVALID_CONN; return; } #endif pbuf_hdr->Conn_ID = (CPU_INT16S)conn_id; pbuf_hdr->Conn_ID_Transport = (CPU_INT16S)conn_id_transport; pbuf_hdr->Conn_ID_App = (CPU_INT16S)conn_id_app; *perr = NET_TCP_ERR_NONE; } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_RxPktConnHandler() * * Description : (1) Handle received TCP packets for its TCP connection : * * (a) Demultiplex TCP packet to appropriate TCP connection state handler * (b) Free/Discard TCP packet * (c) Update receive statistics See Note #2 * * * Argument(s) : pbuf Pointer to network buffer that received TCP packet. * ---- Argument checked in NetTCP_Rx(). * * pbuf_hdr Pointer to network buffer header. * -------- Argument validated in NetTCP_Rx(). * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_CONN_DATA_NONE Received packet successfully handled; but NO * data queued to receive queue(s). * NET_TCP_ERR_CONN_DATA_VALID Received packet successfully handled & valid * data queued for later processing. * NET_TCP_ERR_CONN_RESET_VALID Received reset segment successfully handled; * i.e. the TCP connection was reset. * NET_TCP_ERR_CONN_CLOSED TCP connection successfully closed. * * NET_TCP_ERR_INVALID_CONN_STATE Invalid TCP connection state. * * ---- RETURNED BY NetTCP_ConnIsUsed() : ----- * NET_ERR_INIT_INCOMPLETE Network initialization NOT complete. * NET_TCP_ERR_INVALID_CONN Invalid TCP connection number. * NET_TCP_ERR_CONN_NOT_USED TCP connection NOT currently used. * * --- RETURNED BY NetTCP_RxPktDiscard() : ---- * NET_ERR_RX Receive error; packet discarded. * * Return(s) : none. * * Caller(s) : NetTCP_Rx(). * * Note(s) : (2) (a) SOME TCP receive statistics updated in NetTCP_RxPktDiscard(); do NOT re-update. * * (b) Update TCP receive statistics even if any transitory transmit error(s) occur in * TCP connection state handler functions. * * (3) RFC #1122, Section 4.2.2.13 states that "a host MAY implement a 'half-duplex' TCP * close sequence ... i.e., closed in only one direction, and a host is permitted to * continue sending data in the open direction on a half-closed connection". * * (a) "A host ... that has called CLOSE cannot continue to read data from the * connection. If such a host issues a CLOSE call while received data is * still pending ... its TCP SHOULD send a RST to show that data was lost." * * However, since it does NOT seem reasonable to allow a half-closed connection * "to continue sending data in the open direction" (i.e. from the host that did * NOT issue a CLOSE call) but prevent the receiving host (i.e. the host that DID * issue the CLOSE call) from receiving the transmitted data; it is assumed that * the host that issued the CLOSE call MUST be allowed to receive data transmitted * from the other host that has NOT yet issued its CLOSE. * * (b) However, "if such a host issues a CLOSE call ... [and] new data is received * after CLOSE is called, its TCP SHOULD send a RST to show that data was lost". * * In other words, since a TCP connection in the connection-closing-data-available * state is closed to further TCP data or controls, a TCP reset is transmitted as * for the CLOSED state. * * (c) "Some systems have not implemented half-closed connections." ********************************************************************************************************* */ /*$PAGE*/ static void NetTCP_RxPktConnHandler (NET_BUF *pbuf, NET_BUF_HDR *pbuf_hdr, NET_ERR *perr) { #if (((NET_CTR_CFG_STAT_EN == DEF_ENABLED) || \ (NET_CTR_CFG_ERR_EN == DEF_ENABLED)) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif NET_TCP_CONN_ID conn_id_tcp; NET_TCP_CONN *pconn; NET_ERR err; conn_id_tcp = (NET_TCP_CONN_ID)pbuf_hdr->Conn_ID_Transport; #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) /* -------------- VALIDATE TCP CONN USED -------------- */ (void)NetTCP_ConnIsUsed(conn_id_tcp, perr); if (*perr != NET_TCP_ERR_NONE) { return; } #endif /*$PAGE*/ pconn = &NetTCP_ConnTbl[conn_id_tcp]; switch (pconn->ConnState) { case NET_TCP_CONN_STATE_FREE: NET_CTR_ERR_INC(NetTCP_ErrNotUsedCtr); *perr = NET_TCP_ERR_CONN_NOT_USED; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_LISTEN: NetTCP_RxPktConnHandlerListen(pconn, pbuf, pbuf_hdr, perr); switch (*perr) { case NET_TCP_ERR_CONN_DATA_NONE: NetTCP_RxPktFree(pbuf); /* 'break' intentionally omitted; do NOT move from the */ /* ... following case : 'NET_TCP_ERR_CONN_DATA_VALID'. */ case NET_TCP_ERR_CONN_DATA_VALID: NET_CTR_STAT_INC(NetTCP_StatRxSegProcessedCtr); break; case NET_TCP_ERR_CONN_NOT_USED: case NET_TCP_ERR_CONN_FAIL: case NET_TCP_ERR_CONN_SEQ_INVALID: case NET_TCP_ERR_CONN_ACK_INVALID: case NET_TCP_ERR_CONN_RESET_INVALID: case NET_TCP_ERR_CONN_SEQ_FIN_INVALID: case NET_TCP_ERR_CONN_LISTEN_Q_MAX: case NET_TCP_ERR_INVALID_CONN_ID: case NET_TCP_ERR_INVALID_CONN_STATE: case NET_TCP_ERR_INVALID_CONN_OP: case NET_TCP_ERR_INVALID_LEN_SEG: case NET_TCP_ERR_NONE_AVAIL: case NET_TCP_ERR_TX: case NET_ERR_TX: case NET_CONN_ERR_NOT_USED: case NET_CONN_ERR_INVALID_CONN: case NET_CONN_ERR_INVALID_FAMILY: case NET_CONN_ERR_INVALID_ADDR: case NET_CONN_ERR_INVALID_ADDR_LEN: default: NetTCP_RxPktDiscard(pbuf, perr); return; /* Prevent 'break NOT reachable' compiler warning. */ } break; case NET_TCP_CONN_STATE_SYNC_RXD: case NET_TCP_CONN_STATE_SYNC_RXD_PASSIVE: case NET_TCP_CONN_STATE_SYNC_RXD_ACTIVE: NetTCP_RxPktConnHandlerSyncRxd(pconn, pbuf, pbuf_hdr, perr); switch (*perr) { case NET_TCP_ERR_CONN_RESET_VALID: case NET_TCP_ERR_CONN_DATA_NONE: NetTCP_RxPktFree(pbuf); /* 'break' intentionally omitted; do NOT move from the */ /* ... following case : 'NET_TCP_ERR_CONN_DATA_VALID'. */ case NET_TCP_ERR_CONN_DATA_VALID: NET_CTR_STAT_INC(NetTCP_StatRxSegProcessedCtr); break; case NET_ERR_INIT_INCOMPLETE: case NET_TCP_ERR_CONN_NOT_USED: case NET_TCP_ERR_CONN_CLOSE: case NET_TCP_ERR_CONN_FAULT: case NET_TCP_ERR_CONN_FAIL: case NET_TCP_ERR_CONN_SEQ_SYNC_INVALID: case NET_TCP_ERR_CONN_SEQ_INVALID: case NET_TCP_ERR_CONN_ACK_NONE: case NET_TCP_ERR_CONN_ACK_INVALID: case NET_TCP_ERR_CONN_RESET_INVALID: case NET_TCP_ERR_CONN_DATA_INVALID: case NET_TCP_ERR_CONN_DATA_DUP: case NET_TCP_ERR_INVALID_CONN_STATE: case NET_TCP_ERR_INVALID_CONN_OP: case NET_TCP_ERR_INVALID_LEN_SEG: case NET_TCP_ERR_TX: case NET_TCP_ERR_TX_Q_SIGNAL_FAULT: case NET_TCP_ERR_RE_TX_SEG_TH: case NET_TCP_ERR_NONE_AVAIL: case NET_CONN_ERR_INVALID_FAMILY: case NET_CONN_ERR_INVALID_ADDR: case NET_CONN_ERR_INVALID_ADDR_LEN: default: NetTCP_RxPktDiscard(pbuf, perr); return; /* Prevent 'break NOT reachable' compiler warning. */ } break; /*$PAGE*/ case NET_TCP_CONN_STATE_SYNC_TXD: NetTCP_RxPktConnHandlerSyncTxd(pconn, pbuf, pbuf_hdr, perr); switch (*perr) { case NET_TCP_ERR_CONN_RESET_VALID: case NET_TCP_ERR_CONN_DATA_NONE: NetTCP_RxPktFree(pbuf); /* 'break' intentionally omitted; do NOT move from the */ /* ... following case : 'NET_TCP_ERR_CONN_DATA_VALID'. */ case NET_TCP_ERR_CONN_DATA_VALID: NET_CTR_STAT_INC(NetTCP_StatRxSegProcessedCtr); break; case NET_ERR_INIT_INCOMPLETE: case NET_TCP_ERR_CONN_NOT_USED: case NET_TCP_ERR_CONN_CLOSE: case NET_TCP_ERR_CONN_FAULT: case NET_TCP_ERR_CONN_FAIL: case NET_TCP_ERR_CONN_SEQ_INVALID: case NET_TCP_ERR_CONN_ACK_INVALID: case NET_TCP_ERR_CONN_RESET_INVALID: case NET_TCP_ERR_CONN_SEQ_FIN_INVALID: case NET_TCP_ERR_CONN_DATA_INVALID: case NET_TCP_ERR_CONN_DATA_DUP: case NET_TCP_ERR_INVALID_CONN_STATE: case NET_TCP_ERR_INVALID_CONN_OP: case NET_TCP_ERR_INVALID_LEN_SEG: case NET_TCP_ERR_TX: case NET_TCP_ERR_TX_Q_SIGNAL_FAULT: case NET_TCP_ERR_RE_TX_SEG_TH: case NET_TCP_ERR_NONE_AVAIL: case NET_CONN_ERR_INVALID_FAMILY: case NET_CONN_ERR_INVALID_ADDR: case NET_CONN_ERR_INVALID_ADDR_LEN: default: NetTCP_RxPktDiscard(pbuf, perr); return; /* Prevent 'break NOT reachable' compiler warning. */ } break; case NET_TCP_CONN_STATE_CONN: NetTCP_RxPktConnHandlerConn(pconn, pbuf, pbuf_hdr, perr); switch (*perr) { case NET_TCP_ERR_CONN_RESET_VALID: case NET_TCP_ERR_CONN_DATA_NONE: NetTCP_RxPktFree(pbuf); /* 'break' intentionally omitted; do NOT move from the */ /* ... following case : 'NET_TCP_ERR_CONN_DATA_VALID'. */ case NET_TCP_ERR_CONN_DATA_VALID: NET_CTR_STAT_INC(NetTCP_StatRxSegProcessedCtr); break; case NET_ERR_INIT_INCOMPLETE: case NET_TCP_ERR_CONN_NOT_USED: case NET_TCP_ERR_CONN_CLOSE: case NET_TCP_ERR_CONN_FAULT: case NET_TCP_ERR_CONN_FAIL: case NET_TCP_ERR_CONN_SEQ_SYNC_INVALID: case NET_TCP_ERR_CONN_SEQ_INVALID: case NET_TCP_ERR_CONN_ACK_NONE: case NET_TCP_ERR_CONN_ACK_INVALID: case NET_TCP_ERR_CONN_RESET_INVALID: case NET_TCP_ERR_CONN_DATA_INVALID: case NET_TCP_ERR_CONN_DATA_DUP: case NET_TCP_ERR_INVALID_CONN_STATE: case NET_TCP_ERR_INVALID_CONN_OP: case NET_TCP_ERR_INVALID_LEN_SEG: case NET_TCP_ERR_RX_Q_FULL: case NET_TCP_ERR_RX_Q_SIGNAL_FAULT: case NET_TCP_ERR_TX: case NET_TCP_ERR_TX_Q_SIGNAL_FAULT: case NET_TCP_ERR_RE_TX_SEG_TH: case NET_TCP_ERR_NONE_AVAIL: case NET_CONN_ERR_INVALID_FAMILY: case NET_CONN_ERR_INVALID_ADDR: case NET_CONN_ERR_INVALID_ADDR_LEN: default: NetTCP_RxPktDiscard(pbuf, perr); return; /* Prevent 'break NOT reachable' compiler warning. */ } break; /*$PAGE*/ case NET_TCP_CONN_STATE_FIN_WAIT_1: NetTCP_RxPktConnHandlerFinWait1(pconn, pbuf, pbuf_hdr, perr); switch (*perr) { case NET_TCP_ERR_CONN_RESET_VALID: case NET_TCP_ERR_CONN_DATA_NONE: NetTCP_RxPktFree(pbuf); /* 'break' intentionally omitted; do NOT move from the */ /* ... following case : 'NET_TCP_ERR_CONN_DATA_VALID'. */ case NET_TCP_ERR_CONN_DATA_VALID: NET_CTR_STAT_INC(NetTCP_StatRxSegProcessedCtr); break; case NET_ERR_INIT_INCOMPLETE: case NET_TCP_ERR_CONN_NOT_USED: case NET_TCP_ERR_CONN_CLOSE: case NET_TCP_ERR_CONN_FAULT: case NET_TCP_ERR_CONN_FAIL: case NET_TCP_ERR_CONN_SEQ_SYNC_INVALID: case NET_TCP_ERR_CONN_SEQ_INVALID: case NET_TCP_ERR_CONN_ACK_NONE: case NET_TCP_ERR_CONN_ACK_INVALID: case NET_TCP_ERR_CONN_RESET_INVALID: case NET_TCP_ERR_CONN_DATA_INVALID: case NET_TCP_ERR_CONN_DATA_DUP: case NET_TCP_ERR_INVALID_CONN_ID: case NET_TCP_ERR_INVALID_CONN_STATE: case NET_TCP_ERR_INVALID_CONN_OP: case NET_TCP_ERR_INVALID_LEN_SEG: case NET_TCP_ERR_RX_Q_FULL: case NET_TCP_ERR_RX_Q_SIGNAL_FAULT: case NET_TCP_ERR_TX: case NET_TCP_ERR_TX_Q_SIGNAL_FAULT: case NET_TCP_ERR_RE_TX_SEG_TH: case NET_TCP_ERR_NONE_AVAIL: case NET_CONN_ERR_INVALID_FAMILY: case NET_CONN_ERR_INVALID_ADDR: case NET_CONN_ERR_INVALID_ADDR_LEN: case NET_CONN_ERR_INVALID_CONN: case NET_CONN_ERR_NOT_USED: default: NetTCP_RxPktDiscard(pbuf, perr); return; /* Prevent 'break NOT reachable' compiler warning. */ } break; case NET_TCP_CONN_STATE_FIN_WAIT_2: NetTCP_RxPktConnHandlerFinWait2(pconn, pbuf, pbuf_hdr, perr); switch (*perr) { case NET_TCP_ERR_CONN_RESET_VALID: case NET_TCP_ERR_CONN_DATA_NONE: NetTCP_RxPktFree(pbuf); /* 'break' intentionally omitted; do NOT move from the */ /* ... following case : 'NET_TCP_ERR_CONN_DATA_VALID'. */ case NET_TCP_ERR_CONN_DATA_VALID: NET_CTR_STAT_INC(NetTCP_StatRxSegProcessedCtr); break; case NET_ERR_INIT_INCOMPLETE: case NET_TCP_ERR_CONN_NOT_USED: case NET_TCP_ERR_CONN_FAULT: case NET_TCP_ERR_CONN_FAIL: case NET_TCP_ERR_CONN_SEQ_SYNC_INVALID: case NET_TCP_ERR_CONN_SEQ_INVALID: case NET_TCP_ERR_CONN_ACK_NONE: case NET_TCP_ERR_CONN_ACK_INVALID: case NET_TCP_ERR_CONN_RESET_INVALID: case NET_TCP_ERR_CONN_DATA_INVALID: case NET_TCP_ERR_CONN_DATA_DUP: case NET_TCP_ERR_INVALID_CONN_STATE: case NET_TCP_ERR_INVALID_CONN_OP: case NET_TCP_ERR_INVALID_LEN_SEG: case NET_TCP_ERR_RX_Q_FULL: case NET_TCP_ERR_RX_Q_SIGNAL_FAULT: case NET_TCP_ERR_NONE_AVAIL: default: NetTCP_RxPktDiscard(pbuf, perr); return; /* Prevent 'break NOT reachable' compiler warning. */ } break; /*$PAGE*/ case NET_TCP_CONN_STATE_CLOSING: NetTCP_RxPktConnHandlerClosing(pconn, pbuf, pbuf_hdr, perr); switch (*perr) { case NET_TCP_ERR_CONN_RESET_VALID: case NET_TCP_ERR_CONN_DATA_NONE: NetTCP_RxPktFree(pbuf); /* 'break' intentionally omitted; do NOT move from the */ /* ... following case : 'NET_TCP_ERR_CONN_DATA_VALID'. */ case NET_TCP_ERR_CONN_DATA_VALID: NET_CTR_STAT_INC(NetTCP_StatRxSegProcessedCtr); break; case NET_ERR_INIT_INCOMPLETE: case NET_TCP_ERR_CONN_NOT_USED: case NET_TCP_ERR_CONN_FAULT: case NET_TCP_ERR_CONN_FAIL: case NET_TCP_ERR_CONN_SEQ_SYNC_INVALID: case NET_TCP_ERR_CONN_SEQ_INVALID: case NET_TCP_ERR_CONN_ACK_NONE: case NET_TCP_ERR_CONN_ACK_INVALID: case NET_TCP_ERR_CONN_RESET_INVALID: case NET_TCP_ERR_CONN_DATA_INVALID: case NET_TCP_ERR_CONN_DATA_DUP: case NET_TCP_ERR_INVALID_CONN_ID: case NET_TCP_ERR_INVALID_CONN_STATE: case NET_TCP_ERR_INVALID_CONN_OP: case NET_TCP_ERR_INVALID_LEN_SEG: case NET_TCP_ERR_RX_Q_FULL: case NET_TCP_ERR_RX_Q_SIGNAL_FAULT: case NET_TCP_ERR_TX: case NET_TCP_ERR_TX_Q_SIGNAL_FAULT: case NET_TCP_ERR_RE_TX_SEG_TH: case NET_TCP_ERR_NONE_AVAIL: case NET_CONN_ERR_INVALID_FAMILY: case NET_CONN_ERR_INVALID_ADDR: case NET_CONN_ERR_INVALID_ADDR_LEN: case NET_CONN_ERR_INVALID_CONN: case NET_CONN_ERR_NOT_USED: default: NetTCP_RxPktDiscard(pbuf, perr); return; /* Prevent 'break NOT reachable' compiler warning. */ } break; case NET_TCP_CONN_STATE_TIME_WAIT: NetTCP_RxPktConnHandlerTimeWait(pconn, pbuf, pbuf_hdr, perr); switch (*perr) { case NET_TCP_ERR_CONN_RESET_VALID: case NET_TCP_ERR_CONN_DATA_NONE: NetTCP_RxPktFree(pbuf); /* 'break' intentionally omitted; do NOT move from the */ /* ... following case : 'NET_TCP_ERR_CONN_DATA_VALID'. */ case NET_TCP_ERR_CONN_DATA_VALID: NET_CTR_STAT_INC(NetTCP_StatRxSegProcessedCtr); break; case NET_ERR_INIT_INCOMPLETE: case NET_TCP_ERR_CONN_NOT_USED: case NET_TCP_ERR_CONN_FAULT: case NET_TCP_ERR_CONN_FAIL: case NET_TCP_ERR_CONN_SEQ_SYNC_INVALID: case NET_TCP_ERR_CONN_SEQ_INVALID: case NET_TCP_ERR_CONN_ACK_NONE: case NET_TCP_ERR_CONN_ACK_INVALID: case NET_TCP_ERR_CONN_RESET_INVALID: case NET_TCP_ERR_CONN_DATA_INVALID: case NET_TCP_ERR_CONN_DATA_DUP: case NET_TCP_ERR_INVALID_CONN_STATE: case NET_TCP_ERR_INVALID_CONN_OP: case NET_TCP_ERR_INVALID_LEN_SEG: case NET_TCP_ERR_RX_Q_FULL: case NET_TCP_ERR_RX_Q_SIGNAL_FAULT: case NET_TCP_ERR_NONE_AVAIL: default: NetTCP_RxPktDiscard(pbuf, perr); return; /* Prevent 'break NOT reachable' compiler warning. */ } break; /*$PAGE*/ case NET_TCP_CONN_STATE_CLOSE_WAIT: NetTCP_RxPktConnHandlerCloseWait(pconn, pbuf, pbuf_hdr, perr); switch (*perr) { case NET_TCP_ERR_CONN_RESET_VALID: case NET_TCP_ERR_CONN_DATA_NONE: NetTCP_RxPktFree(pbuf); /* 'break' intentionally omitted; do NOT move from the */ /* ... following case : 'NET_TCP_ERR_CONN_DATA_VALID'. */ case NET_TCP_ERR_CONN_DATA_VALID: NET_CTR_STAT_INC(NetTCP_StatRxSegProcessedCtr); break; case NET_ERR_INIT_INCOMPLETE: case NET_TCP_ERR_CONN_NOT_USED: case NET_TCP_ERR_CONN_FAULT: case NET_TCP_ERR_CONN_FAIL: case NET_TCP_ERR_CONN_SEQ_SYNC_INVALID: case NET_TCP_ERR_CONN_SEQ_INVALID: case NET_TCP_ERR_CONN_ACK_NONE: case NET_TCP_ERR_CONN_ACK_INVALID: case NET_TCP_ERR_CONN_RESET_INVALID: case NET_TCP_ERR_CONN_DATA_INVALID: case NET_TCP_ERR_CONN_DATA_DUP: case NET_TCP_ERR_INVALID_CONN_STATE: case NET_TCP_ERR_INVALID_CONN_OP: case NET_TCP_ERR_INVALID_LEN_SEG: case NET_TCP_ERR_RX_Q_FULL: case NET_TCP_ERR_RX_Q_SIGNAL_FAULT: case NET_TCP_ERR_TX: case NET_TCP_ERR_TX_Q_SIGNAL_FAULT: case NET_TCP_ERR_RE_TX_SEG_TH: case NET_TCP_ERR_NONE_AVAIL: case NET_CONN_ERR_INVALID_FAMILY: case NET_CONN_ERR_INVALID_ADDR: case NET_CONN_ERR_INVALID_ADDR_LEN: default: NetTCP_RxPktDiscard(pbuf, perr); return; /* Prevent 'break NOT reachable' compiler warning. */ } break; case NET_TCP_CONN_STATE_LAST_ACK: NetTCP_RxPktConnHandlerLastAck(pconn, pbuf, pbuf_hdr, perr); switch (*perr) { case NET_TCP_ERR_CONN_RESET_VALID: case NET_TCP_ERR_CONN_DATA_NONE: case NET_TCP_ERR_CONN_CLOSED: NetTCP_RxPktFree(pbuf); /* 'break' intentionally omitted; do NOT move from the */ /* ... following case : 'NET_TCP_ERR_CONN_DATA_VALID'. */ case NET_TCP_ERR_CONN_DATA_VALID: NET_CTR_STAT_INC(NetTCP_StatRxSegProcessedCtr); break; case NET_ERR_INIT_INCOMPLETE: case NET_TCP_ERR_CONN_NOT_USED: case NET_TCP_ERR_CONN_FAULT: case NET_TCP_ERR_CONN_FAIL: case NET_TCP_ERR_CONN_SEQ_SYNC_INVALID: case NET_TCP_ERR_CONN_SEQ_INVALID: case NET_TCP_ERR_CONN_ACK_NONE: case NET_TCP_ERR_CONN_ACK_INVALID: case NET_TCP_ERR_CONN_RESET_INVALID: case NET_TCP_ERR_CONN_DATA_INVALID: case NET_TCP_ERR_CONN_DATA_DUP: case NET_TCP_ERR_INVALID_CONN_STATE: case NET_TCP_ERR_INVALID_CONN_OP: case NET_TCP_ERR_INVALID_LEN_SEG: case NET_TCP_ERR_RX_Q_FULL: case NET_TCP_ERR_RX_Q_SIGNAL_FAULT: case NET_TCP_ERR_TX: case NET_TCP_ERR_TX_Q_SIGNAL_FAULT: case NET_TCP_ERR_RE_TX_SEG_TH: case NET_TCP_ERR_NONE_AVAIL: case NET_CONN_ERR_INVALID_FAMILY: case NET_CONN_ERR_INVALID_ADDR: case NET_CONN_ERR_INVALID_ADDR_LEN: default: NetTCP_RxPktDiscard(pbuf, perr); return; /* Prevent 'break NOT reachable' compiler warning. */ } break; /*$PAGE*/ case NET_TCP_CONN_STATE_CLOSING_DATA_AVAIL: NetTCP_TxConnReset((NET_TCP_CONN *) 0, /* Tx TCP conn reset (see Note #3b). */ (NET_BUF_HDR *) pbuf_hdr, (NET_TCP_RESET_CODE) NET_TCP_CONN_TX_RESET, (NET_TCP_CLOSE_CODE) NET_TCP_CONN_CLOSE_ALL, (NET_ERR *)&err); NetTCP_RxPktDiscard(pbuf, perr); return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_NONE: case NET_TCP_CONN_STATE_CLOSED: default: NetTCP_ConnClose(pconn, pbuf_hdr, DEF_YES, NET_TCP_CONN_CLOSE_ALL); NET_CTR_ERR_INC(NetTCP_ErrConnInvalidStateCtr); *perr = NET_TCP_ERR_INVALID_CONN_STATE; return; /* Prevent 'break NOT reachable' compiler warning. */ } } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_RxPktConnHandlerListen() * * Description : (1) Handle TCP connection in LISTEN state : * * (a) Validate received TCP packet for current TCP connection state : * * (1) Reset (RST) See Note #2a * (2) Acknowledgement (ACK) See Note #2b * (3) Synchronization (SYN) See Note #2c * * (b) Check if TCP connection listen queue is available See Note #3 * * (c) Prepare/configure TCP connection : * * (1) Clone new TCP connection from current TCP listen connection, See Note #5a * if half-connection : * * (A) Get connections * (B) Set connection identification handles * (C) Set connection addresses * (D) Add connection into connection list * (E) Copy connection from half-connection * * (2) Re-configure current TCP listen connection, See Note #5b * if full-connection * * (d) Update TCP connection : * (1) Handle received TCP segment See Note #2c2 * (2) Configure TCP connection remote host maximum segment size See Note #7 * * (e) Transmit TCP connection synchronization for valid received See Note #2c3 * TCP connection reqeusts * * (f) Update TCP connection state See Note #2c * * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_RxPktConnHandler(). * * pbuf Pointer to network buffer that received TCP packet. * ---- Argument checked in NetTCP_Rx(). * * pbuf_hdr Pointer to network buffer header that received TCP packet. * -------- Argument validated in NetTCP_Rx(). * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_CONN_SEQ_INVALID Received segment's sequence number is * NOT valid for current TCP connection. * NET_TCP_ERR_CONN_ACK_INVALID Received segment's acknowledgement number is * NOT valid for current TCP connection. * NET_TCP_ERR_CONN_SEQ_FIN_INVALID Received segment's finish/close flag is * NOT valid for current TCP connection. * NET_TCP_ERR_CONN_RESET_INVALID Received segment's reset flag is * NOT valid for current TCP connection. * * NET_TCP_ERR_CONN_LISTEN_Q_MAX TCP connection listen queue is NOT available * queue a new connection. * * NET_TCP_ERR_CONN_FAIL TCP connection operation(s) failed. * NET_TCP_ERR_NONE_AVAIL Resources NOT available. * * NET_CONN_ERR_INVALID_FAMILY Invalid network connection family. * * ------ RETURNED BY NetTCP_RxPktConnIsValidReset() : ------ * ------- RETURNED BY NetTCP_RxPktConnIsValidAck() : ------- * ------- RETURNED BY NetTCP_RxPktConnIsValidSeq() : ------- * NET_TCP_ERR_CONN_NOT_USED TCP connection NOT currently used. * NET_TCP_ERR_INVALID_CONN_STATE Invalid TCP connection state. * NET_TCP_ERR_INVALID_LEN_SEG Invalid TCP sequence-segment length. * * - RETURNED BY NetTCP_RxPktConnHandlerListenQ_IsAvail() : - * NET_ERR_INIT_INCOMPLETE Network initialization NOT complete. * NET_TCP_ERR_INVALID_CONN_ID Invalid application connection. * NET_TCP_ERR_INVALID_CONN_OP Invalid TCP connection operation. * NET_CONN_ERR_INVALID_CONN Invalid network connection number. * NET_CONN_ERR_NOT_USED Network connection NOT currently used. * * ----------- RETURNED BY NetTCP_TxConnSync() : ------------ * NET_TCP_ERR_CONN_FAULT TCP connection fault; connection(s) aborted. * NET_CONN_ERR_INVALID_ADDR Invalid TCP connection address. * NET_CONN_ERR_INVALID_ADDR_LEN Invalid TCP connection address length. * NET_TCP_ERR_TX TCP transmit error. * NET_ERR_TX Transmit error. * * ------- RETURNED BY NetTCP_RxPktConnHandlerSeg() : ------- * NET_TCP_ERR_CONN_DATA_NONE Received packet successfully handled; but NO * data queued to receive queue(s). * NET_TCP_ERR_CONN_DATA_VALID Received packet successfully handled & valid * data queued for later processing (see Note #2c2). * * Return(s) : none. * * Caller(s) : NetTCP_RxPktConnHandler(). *$PAGE* * Note(s) : (2) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : LISTEN [State]' states that : * * (a) "An incoming RST should be ignored." * * (b) "Any acknowledgment is bad if it arrives on a connection still in the LISTEN state. * An acceptable reset segment should be formed for any arriving ACK-bearing segment." * * (c) "If the SYN bit is set ... the connection state should be changed to SYN-RECEIVED" ... * * (1) "Set RCV.NXT to SEG.SEQ+1, IRS is set to SEG.SEQ ..." * (A) See Note #2c3 * * (2) "And any other control or text should be queued for processing later." * * (A) If any control or text is queued for later processing, the next sequence octet to * receive (RCV.NXT) MUST include the length of this received segment (SEG.LEN) : * * (1) RCV.NXT = SEG.SEQ + SEG.LEN + 1 * * (3) "ISS should be selected and a SYN segment sent." * * (d) "Any other control or text-bearing segment (not containing SYN) must have an ACK and * thus would be discarded by the ACK processing ... So you are unlikely to get here, * but if you do, drop the segment, and return." * * (e) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check FIN Bit' states to * "not process the FIN if the state is CLOSED, LISTEN or SYN-SENT since the SEG.SEQ cannot * be validated; drop the segment". * * (3) (a) Stevens, TCP/IP Illustrated, Volume 1, 8th Printing, Section 18.11, Pages 257-258 states * that : * * (1) "Each listening end point has a fixed length queue of connections that have been * accepted by TCP (i.e., the three-way handshake is complete), but not yet accepted * by the application." * * (3) "When a connection request arrives (i.e., the SYN segment), ... the current number * of connections already queued for this listening end point [is checked] to see * whether to accept the connection or not." * * (4) "If there is room on this listening end point's queue for this new connection, ... * the TCP module ACKs the SYN and completes the connection." * * (5) "If there is not room on the queue for the new connection" ... : * * (A) "TCP just ignores the received SYN." * (B) "Nothing is sent back (i.e., no RST segment)." * * (b) (A) Wright/Stevens, TCP/IP Illustrated, Volume 2, 3rd Printing, Section 15.9, Page 455 * reiterates that : * * (2) A "listen ... socket ... limit[s] ... the number of connections that can be * queued on the socket," ... * * (5) "after which the socket layer refuses to queue additional connection requests. * When this occurs, TCP ignores incoming connection requests." * * (B) Wright/Stevens, TCP/IP Illustrated, Volume 2, 3rd Printing, Section 28.2, Page 930 * also states that : * * (5) (A) "By silently dropping the segment" ... * (B) "and not replying with an RST," ... * (C) "The client's connection request should time out, causing the client to * retransmit the SYN." * * (C) Stevens, TCP/IP Illustrated, Volume 1, 8th Printing, Section 18.11, Pages 259-260 * summarizes that : * * (5) (A) "TCP ignores the incoming SYN when the queue is full," ... * (B) "and doesn't respond with an RST," ... * * (C) (1) "because ... this condition could change in a short while ... [and] by * ignoring the SYN, the server forces the client TCP to re-transmit the * SYN later, hoping that the queue will then have room for the new * connection". * * (2) Whereas page 259-260 counters that "if the server's TCP responded with * a reset, the client's active open would abort". * * See also 'NetTCP_RxPktConnHandlerListenQ_IsAvail() Note #1'. *$PAGE* * (4) The 'NET_CONN_CFG_FAMILY' pre-processor 'else'-conditional code will never be compiled/linked * since 'net_conn.h' ensures that the family type configuration constant (NET_CONN_CFG_FAMILY) * is configured with an appropriate family type value (see 'net_conn.h CONFIGURATION ERRORS'). * The 'else'-conditional code is included for completeness & as an extra precaution in case * 'net_conn.h' is incorrectly modified. * * (5) (a) (1) If any received TCP connection request to a LISTEN-state connection is a half-connection * -- i.e. a connection with ONLY the local address specified/configured -- then a new * connection is cloned from the LISTEN-state connection to handle each unique connection * request. * * (2) The following connection parameters are cloned/copied from the LISTEN-state connection : * * (A) Application connection handle identifier * (1) When the cloned connection is fully connected/established, it is queued to the * LISTEN-state connection's application connection as a cloned network connection * until the connection is accepted & a new application connection is created. * * See also 'net_sock.c NetSock_Accept() Notes #1d & #1e'. * * (B) (1) TCP connection receive parameters * (2) TCP connection transmit parameters * (3) TCP connection timeout values * * See also 'NetTCP_ConnCopy() Note #1'. * * (b) If any received TCP connection request to a LISTEN-state connection is a full-connection -- * i.e. a connection with BOTH the local & remote addresses specified/configured -- then the * LISTEN-state connection is re-configured to handle the received connection request. * * (6) On ANY TCP LISTEN-connection preparation errors, network resources MUST be appropriately freed : * * (a) If NO TCP connections available, NO resources need be freed. * (b) If NO network connections available, ONLY the TCP connection need be closed. * (c) If network connection preparation fails, the TCP & network connection MUST both * be closed. However, the TCP connection need NOT close application connection(s). * * See also 'NetTCP_ConnCloseHandler() Note #2b1C1'. * * (d) If any remaining TCP connection preparation fails, the TCP & network connection MUST * both be closed. The application connection(s) SHOULD NOT be closed since it is cloned * from the LISTEN-state connection (see Note #4), but network connections MAY need to be * de-referenced from the application connection(s). * * See also 'NetTCP_ConnCloseHandler() Note #2b'; * & 'net_conn.c NetConn_CloseApp() Note #1' * & 'net_sock.c NetSock_FreeConnFromSock() Note #2'. * * (7) RFC #1122, Section 4.2.2.6 states that ... : * * (a) A "TCP SHOULD send an MSS (Maximum Segment Size) option in every SYN segment". * * (b) "If an MSS option is not received at connection setup, TCP MUST assume a default * send MSS of 536." * * (8) TCP connection timeout for LISTEN state is implemented by TCP connection retransmission * function(s) (see 'NetTCP_TxConnReTxQ() Note #3c'). ********************************************************************************************************* */ /*$PAGE*/ static void NetTCP_RxPktConnHandlerListen (NET_TCP_CONN *pconn, NET_BUF *pbuf, NET_BUF_HDR *pbuf_hdr, NET_ERR *perr) { #if (NET_CONN_CFG_FAMILY == NET_CONN_FAMILY_IP_V4_SOCK) NET_IP_ADDR addr_ip; #endif CPU_INT08U addr_local[NET_CONN_CFG_ADDR_LEN]; CPU_INT08U addr_remote[NET_CONN_CFG_ADDR_LEN]; CPU_BOOLEAN q_avail; NET_TCP_SEQ_CODE seq_code; NET_TCP_ACK_CODE ack_code; NET_TCP_RESET_CODE reset_code; NET_TCP_PORT_NBR port_nbr; NET_CONN_FAMILY conn_family; NET_CONN_LIST_IX conn_protocol_ix; NET_CONN_ID conn_id; NET_CONN_ID conn_id_clone; NET_CONN_ID conn_id_clone_app; NET_TCP_CONN_ID conn_id_clone_tcp; NET_TCP_CONN *pconn_clone; NET_TCP_CONN *pconn_tx_sync; NET_ERR err; NET_ERR err_rtn; /* ---------- VALIDATE RX'D TCP PKT ----------- */ /* Chk for rx'd fin/close. */ if (pbuf_hdr->TCP_SegClose != DEF_NO) { /* If invalid fin/close rx'd, ... */ *perr = NET_TCP_ERR_CONN_SEQ_FIN_INVALID; return; /* ... ignore TCP pkt (see Note #2e). */ } /* Chk for rx'd reset. */ reset_code = NetTCP_RxPktConnIsValidReset(pconn, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return; } if (reset_code != NET_TCP_CONN_RX_RESET_NONE) { /* If reset rx'd, ... */ *perr = NET_TCP_ERR_CONN_RESET_INVALID; return; /* ... ignore TCP pkt (see Note #2a). */ } /* Chk for rx'd ack. */ ack_code = NetTCP_RxPktConnIsValidAck(pconn, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return; } if (ack_code != NET_TCP_CONN_RX_ACK_NONE) { /* If ack rx'd, ... */ /* ... tx TCP conn reset (see Note #2b). */ NetTCP_TxConnReset(pconn, pbuf_hdr, NET_TCP_CONN_TX_RESET, NET_TCP_CONN_CLOSE_ALL, &err); *perr = NET_TCP_ERR_CONN_ACK_INVALID; return; } /* Chk rx'd seq nbr. */ seq_code = NetTCP_RxPktConnIsValidSeq(pconn, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return; } if (seq_code != NET_TCP_CONN_RX_SEQ_SYNC) { /* If sync NOT rx'd, ... */ *perr = NET_TCP_ERR_CONN_SEQ_INVALID; return; /* ... ignore TCP pkt (see Note #2d). */ } /* Else sync avail, update seg lens. */ pbuf_hdr->TCP_SegLenInit += NET_TCP_SEG_LEN_SYNC; pbuf_hdr->TCP_SegLen += NET_TCP_SEG_LEN_SYNC; /*$PAGE*/ /* ---------- CHK TCP CONN LISTEN Q ----------- */ q_avail = NetTCP_RxPktConnHandlerListenQ_IsAvail(pconn, perr); /* Chk TCP listen Q avail. */ if (*perr != NET_TCP_ERR_NONE) { return; } if (q_avail != DEF_YES) { /* If TCP listen Q NOT avail (see Note #3a5), */ *perr = NET_TCP_ERR_CONN_LISTEN_Q_MAX; return; /* ... ignore TCP pkt (see Note #3a5A). */ } /* ------------- PREPARE TCP CONN ------------- */ if (pbuf_hdr->ConnType != NET_CONN_TYPE_CONN_FULL) { /* If pkt demux'd to half-conn, clone new conn */ /* .. from LISTEN half-conn (see Note #5a). */ /* ---------------- GET CONNS ----------------- */ conn_id_clone_tcp = NetTCP_ConnGet(&err); if ( err != NET_TCP_ERR_NONE) { /* See Note #6a. */ *perr = NET_TCP_ERR_NONE_AVAIL; return; } pconn_clone = &NetTCP_ConnTbl[conn_id_clone_tcp]; #if (NET_CONN_CFG_FAMILY == NET_CONN_FAMILY_IP_V4_SOCK) conn_family = NET_CONN_FAMILY_IP_V4_SOCK; conn_protocol_ix = NET_CONN_LIST_IX_IP_V4_SOCK_TCP; #else /* See Notes #4 & #6d. */ NetTCP_ConnClose(pconn_clone, pbuf_hdr, DEF_YES, NET_TCP_CONN_CLOSE_ALL); *perr = NET_CONN_ERR_INVALID_FAMILY; return; #endif conn_id_clone = NetConn_Get(conn_family, conn_protocol_ix, &err); if ( err != NET_CONN_ERR_NONE) { /* See Note #6b. */ NetTCP_ConnClose(pconn_clone, pbuf_hdr, DEF_NO, NET_TCP_CONN_CLOSE_ALL); *perr = NET_TCP_ERR_NONE_AVAIL; return; } /* --------------- SET CONN IDs --------------- */ pconn_clone->ID_Conn = conn_id_clone; NetConn_ID_TransportSet((NET_CONN_ID) conn_id_clone, (NET_CONN_ID) conn_id_clone_tcp, (NET_ERR *)&err); if ( err != NET_CONN_ERR_NONE) { /* See Note #6c. */ NetTCP_ConnClose(pconn_clone, pbuf_hdr, DEF_NO, NET_TCP_CONN_CLOSE_ALL); *perr = NET_TCP_ERR_CONN_FAIL; return; } conn_id = pconn->ID_Conn; conn_id_clone_app = NetConn_ID_AppGet((NET_CONN_ID) conn_id, /* Get half-conn's app conn id & ... */ (NET_ERR *)&err); if ( err != NET_CONN_ERR_NONE) { /* See Note #6c. */ NetTCP_ConnClose(pconn_clone, pbuf_hdr, DEF_NO, NET_TCP_CONN_CLOSE_ALL); *perr = NET_TCP_ERR_CONN_FAIL; return; } NetConn_ID_AppCloneSet((NET_CONN_ID) conn_id_clone, /* ... set as cloned conn's ... */ (NET_CONN_ID) conn_id_clone_app, /* ... app conn clone id (see Note #5a2A1). */ (NET_ERR *)&err); if ( err != NET_CONN_ERR_NONE) { /* See Note #6c. */ NetTCP_ConnClose(pconn_clone, pbuf_hdr, DEF_NO, NET_TCP_CONN_CLOSE_ALL); *perr = NET_TCP_ERR_CONN_FAIL; return; } /*$PAGE*/ /* -------------- CFG CONN ADDRS -------------- */ #if (NET_CONN_CFG_FAMILY == NET_CONN_FAMILY_IP_V4_SOCK) /* Cfg local addr as pkt dest addr. */ port_nbr = (NET_TCP_PORT_NBR)NET_UTIL_HOST_TO_NET_16(pbuf_hdr->TCP_UDP_PortDest); addr_ip = (NET_IP_ADDR )NET_UTIL_HOST_TO_NET_32(pbuf_hdr->IP_AddrDest); Mem_Copy((void *)&addr_local[NET_CONN_ADDR_IP_IX_PORT], (void *)&port_nbr, (CPU_SIZE_T) NET_CONN_ADDR_IP_LEN_PORT); Mem_Copy((void *)&addr_local[NET_CONN_ADDR_IP_IX_ADDR], (void *)&addr_ip, (CPU_SIZE_T) NET_CONN_ADDR_IP_LEN_ADDR); /* Cfg remote addr as pkt src addr. */ port_nbr = (NET_TCP_PORT_NBR)NET_UTIL_HOST_TO_NET_16(pbuf_hdr->TCP_UDP_PortSrc); addr_ip = (NET_IP_ADDR )NET_UTIL_HOST_TO_NET_32(pbuf_hdr->IP_AddrSrc); Mem_Copy((void *)&addr_remote[NET_CONN_ADDR_IP_IX_PORT], (void *)&port_nbr, (CPU_SIZE_T) NET_CONN_ADDR_IP_LEN_PORT); Mem_Copy((void *)&addr_remote[NET_CONN_ADDR_IP_IX_ADDR], (void *)&addr_ip, (CPU_SIZE_T) NET_CONN_ADDR_IP_LEN_ADDR); #else /* See Notes #4 & #6d. */ NetTCP_ConnClose(pconn_clone, pbuf_hdr, DEF_YES, NET_TCP_CONN_CLOSE_ALL); *perr = NET_CONN_ERR_INVALID_FAMILY; return; #endif NetConn_AddrLocalSet((NET_CONN_ID ) conn_id_clone, (CPU_INT08U *)&addr_local[0], (NET_CONN_ADDR_LEN) NET_CONN_CFG_ADDR_LEN, (CPU_BOOLEAN ) DEF_NO, (NET_ERR *)&err); if ( err != NET_CONN_ERR_NONE) { /* See Note #6c. */ NetTCP_ConnClose(pconn_clone, pbuf_hdr, DEF_NO, NET_TCP_CONN_CLOSE_ALL); *perr = NET_TCP_ERR_CONN_FAIL; return; } NetConn_AddrRemoteSet((NET_CONN_ID ) conn_id_clone, (CPU_INT08U *)&addr_remote[0], (NET_CONN_ADDR_LEN) NET_CONN_CFG_ADDR_LEN, (CPU_BOOLEAN ) DEF_NO, (NET_ERR *)&err); if ( err != NET_CONN_ERR_NONE) { /* See Note #6c. */ NetTCP_ConnClose(pconn_clone, pbuf_hdr, DEF_NO, NET_TCP_CONN_CLOSE_ALL); *perr = NET_TCP_ERR_CONN_FAIL; return; } /* Add conn into server conn list. */ NetConn_ListAdd(conn_id_clone, NET_CONN_LIST_TYPE_SERVER, &err); if ( err != NET_CONN_ERR_NONE) { /* See Note #6c. */ NetTCP_ConnClose(pconn_clone, pbuf_hdr, DEF_NO, NET_TCP_CONN_CLOSE_ALL); *perr = NET_TCP_ERR_CONN_FAIL; return; } /* Copy half-conn state to full conn. */ NetTCP_ConnCopy(pconn_clone, pconn); pconn_tx_sync = pconn_clone; } else { /* Else conn to listen TCP conn (see Note #5b). */ pconn_tx_sync = pconn; } /*$PAGE*/ /* ------------- HANDLE RX'D SEG -------------- */ NetTCP_RxPktConnHandlerSeg(pconn_tx_sync, ack_code, pbuf, pbuf_hdr, &err_rtn); switch (err_rtn) { case NET_TCP_ERR_CONN_DATA_NONE: case NET_TCP_ERR_CONN_DATA_VALID: break; case NET_TCP_ERR_CONN_NOT_USED: case NET_TCP_ERR_INVALID_CONN_STATE: case NET_TCP_ERR_INVALID_CONN_OP: default: /* See Note #6d. */ NetTCP_ConnClose(pconn_tx_sync, pbuf_hdr, pconn_tx_sync->ConnCloseAppFlag, NET_TCP_CONN_CLOSE_ALL); *perr = err_rtn; return; /* Prevent 'break NOT reachable' warning. */ } /* ------------- UPDATE TCP CONN -------------- */ /* Cfg remote max seg size as advertised ... */ /* ... by remote host (see Note #7). */ pconn_tx_sync->MaxSegSizeRemote = (pbuf_hdr->TCP_MaxSegSize != NET_TCP_MAX_SEG_SIZE_NONE) ? pbuf_hdr->TCP_MaxSegSize : NET_TCP_MAX_SEG_SIZE_DFLT_TX; /* ------------- TX TCP CONN SYNC ------------- */ NetTCP_TxConnSync(pconn_tx_sync, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { /* See Note #6d. */ NetTCP_ConnClose(pconn_tx_sync, pbuf_hdr, pconn_tx_sync->ConnCloseAppFlag, NET_TCP_CONN_CLOSE_ALL); return; } /* ---------- UPDATE TCP CONN STATE ----------- */ pconn_tx_sync->ConnState = NET_TCP_CONN_STATE_SYNC_RXD_PASSIVE; *perr = err_rtn; } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_RxPktConnHandlerSyncRxd() * * Description : (1) Handle TCP connection in SYN-RECEIVED state : * * (a) Validate received TCP packet for current TCP connection state : * * (1) Sequence Number (SEQ) See Note #2a * (2) Reset (RST) See Note #2b * (3) Synchronization (SYN) See Note #2c * (4) Acknowledgement (ACK) See Note #2d * (5) Finish/Close (FIN) See Note #2e * * (b) Update TCP connection : * (1) Handle received TCP segment See Note #2d2A1 * (2) Update TCP connection state See Notes #2d2A1a & #2e4 * (3) Update TCP connection timer * * (c) Acknowledge TCP connection * (1) Signal TCP/application connection complete * (2) Handle TCP connection received data See Note #2d2A1 * (3) Transmit TCP connection data See Notes #2d2A2 & #2e3 * * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_RxPktConnHandler(). * * pbuf Pointer to network buffer that received TCP packet. * ---- Argument checked in NetTCP_Rx(). * * pbuf_hdr Pointer to network buffer header that received TCP packet. * -------- Argument validated in NetTCP_Rx(). * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_CONN_SEQ_SYNC_INVALID Received segment's synchronization is * NOT valid for current TCP connection. * NET_TCP_ERR_CONN_SEQ_INVALID Received segment's sequence number is * NOT valid for current TCP connection. * NET_TCP_ERR_CONN_ACK_NONE Received segment's acknowledgement is * NOT available. * NET_TCP_ERR_CONN_ACK_INVALID Received segment's acknowledgement number is * NOT valid for current TCP connection. * NET_TCP_ERR_CONN_RESET_VALID Received segment's reset flag is valid * for current TCP connection; i.e. reset * the TCP connection. * NET_TCP_ERR_CONN_RESET_INVALID Received segment's reset flag is NOT valid * for current TCP connection. * * NET_TCP_ERR_CONN_FAIL TCP connection operation(s) failed. * NET_TCP_ERR_NONE_AVAIL Resources NOT available. * * ----- RETURNED BY NetTCP_RxPktConnIsValidSeq() : ----- * ----- RETURNED BY NetTCP_RxPktConnIsValidAck() : ----- * ---- RETURNED BY NetTCP_RxPktConnIsValidReset() : ---- * NET_TCP_ERR_CONN_NOT_USED TCP connection NOT currently used. * NET_TCP_ERR_INVALID_CONN_STATE Invalid TCP connection state. * NET_TCP_ERR_INVALID_LEN_SEG Invalid TCP sequence-segment length. * * ----- RETURNED BY NetTCP_RxPktConnHandlerSeg() : ----- * NET_TCP_ERR_CONN_DATA_NONE Received packet successfully handled; but NO * data to queue to receive queue(s). * NET_TCP_ERR_CONN_DATA_VALID Received packet successfully handled & valid * data processed (see Note #2d2A1). * NET_TCP_ERR_CONN_DATA_INVALID Received packet contains invalid segment * data; NOT queued to receive queue(s). * NET_TCP_ERR_CONN_DATA_DUP Received packet contains duplicate segment * data; NOT queued to receive queue(s). * * NET_ERR_INIT_INCOMPLETE Network initialization NOT complete. * * NET_TCP_ERR_INVALID_CONN_OP Invalid TCP connection operation. * * NET_TCP_ERR_TX TCP transmit error. * NET_TCP_ERR_TX_Q_SIGNAL_FAULT TCP connection transmit queue signal fault. * NET_TCP_ERR_RE_TX_SEG_TH TCP connection closed due to excessive retransmission. * * ---------- RETURNED BY NetTCP_TxConnTxQ() : ---------- * NET_TCP_ERR_CONN_CLOSE TCP connection closed. * NET_TCP_ERR_CONN_FAULT TCP connection fault; connection(s) aborted. * NET_CONN_ERR_INVALID_FAMILY Invalid connection family. * NET_CONN_ERR_INVALID_ADDR Invalid TCP connection address. * NET_CONN_ERR_INVALID_ADDR_LEN Invalid TCP connection address length. * * Return(s) : none. * * Caller(s) : NetTCP_RxPktConnHandler(). *$PAGE* * Note(s) : (2) TCP connections in the SYN-RECEIVED state are handled as follows : * * (a) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check Sequence Number' * states that in the "SYN-RECEIVED, ESTABLISHED STATE, FIN-WAIT-1 STATE, FIN-WAIT-2 * STATE, CLOSE-WAIT STATE, CLOSING STATE, LAST-ACK STATE, TIME-WAIT STATE" that ... * * (1) "Segments are processed in sequence ... processing is done in SEG.SEQ order." * * (2) "Initial tests on arrival are used to discard old duplicates." * * (3) "If a segment's contents straddle the boundary between old and new, only the * new parts should be processed." * * (4) (A) "If an incoming segment is not acceptable," ... * * (B) "an acknowledgment should be sent in reply" ... * * (C) "(unless the RST bit is set, if so drop the segment)". * * See also Notes #2b2Aa & #2b2C. * * See also 'NetTCP_RxPktConnIsValidSeq() Note #1d'. * * (b) (1) (A) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check RST Bit : * SYN-RECEIVED STATE' states that "if the RST bit is set" and ... * * (1) "If this connection was initiated with a passive OPEN (i.e., came from * the LISTEN state), then return this connection to the LISTEN state." * * (2) "If this connection was initiated with an active OPEN (i.e., came from * the SYN-SENT state) then the connection was refused, signal the user * 'connection refused' ... enter the CLOSED state." * * (B) RFC #793, Section 3.4 'Establishing a Connection : Reset Processing' * reiterates that "if the receiver ... of a RST ... was in SYN-RECEIVED state * and had previously been in the LISTEN state, then the receiver returns to * the LISTEN state, otherwise the receiver aborts the connection and goes to * the CLOSED state". * * (C) However, since TCP connections opened from the LISTEN state are cloned from * the original LISTEN-state TCP connection, it is NOT necessary to return ANY * reset TCP connection from the SYN-RECEIVED state back to the LISTEN state. * * See also 'NetTCP_RxPktConnIsValidReset() Note #2a4B'. * * (2) (A) RFC Draft-IETF-TCPm-TCPSecure #00, Section 2.2 amends the "handling of * a segment with the RST when in a synchronized state" to "provide some * protection against ... blind reset attack[s] using the RST bit" : * * (a) "If the RST bit is set and the sequence number is outside the expected * window, silently drop the segment." * * (b) "If the RST bit is exactly the next expected sequence number [sic], reset * the connection"; it is assumed that this should read "if the RST bit is * set and the sequence number is exactly the next expected sequence number, * reset the connection." * * (c) "If the RST bit is set and the sequence number does not exactly match * the next expected sequence value, yet is within the acceptable window * (RCV.NXT <= SEG.SEQ < RCV.NXT+RCV.WND) send an acknowledgment." * * (B) ???? Although RFC Draft-IETF-TCPm-TCPSecure #00 explicitly states that this * amendment applies only to the "handling of a ... RST ... when in a synchronized * state", it is assumed that this should also apply to the SYN-RECEIVED state. * * See also 'NetTCP_RxPktConnIsValidReset() Note #2a5B'. * * (C) ???? In addition, RFC Draft-IETF-TCPm-TCPSecure #00 does NOT provide a precedence * priority for handling TCP segments received with BOTH the RST & SYN bits set. * * ???? Therefore, since it does NOT seem reasonable to reset a TCP connection * due to a TCP segment that also attempted to synchronize the TCP connection, * it is assumed that the amended handling of the SYN bit should take precedence * over the amended handling of the RST bit. * * See also Note #2c2. *$PAGE* * (c) (1) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check SYN Bit' * states that in the "SYN-RECEIVED [STATE], ESTABLISHED STATE, FIN-WAIT STATE-1, * FIN-WAIT STATE-2, CLOSE-WAIT STATE, CLOSING STATE, LAST-ACK STATE, TIME-WAIT * STATE" that ... * * (A) "If the SYN is in the window it is an error, send a reset, any outstanding * RECEIVEs and SEND[s] should receive 'reset' responses, all segment queues * should be flushed, the user should also receive an unsolicited general * 'connection reset' signal[, and] enter the CLOSED state." * * (B) But "if the SYN is not in the window this step would not have been reached * and an ack would have been sent". * * (2) (A) HOWEVER, RFC Draft-IETF-TCPm-TCPSecure #00, Section 3.2 amends the "handling * of a segment with the SYN bit set in the synchronized state ... [by] handling * ... the SYN bit" as follows : * * (a) "If the SYN bit is set and the sequence number is outside the * expected window, send an ACK back to the peer." * * (b) "If the SYN bit is set and the sequence number is an exact * match to the next expected sequence (RCV.NXT == SEG.SEQ) * then send an ACK segment ... but ... subtract one from * value being acknowledged." * * (c) "If the SYN bit is set and the sequence number is acceptable, * i.e.: (RCV.NXT <= SEG.SEQ <= RCV.NXT+RCV.WND) then send an * ACK segment." * * (B) ???? Although RFC Draft-IETF-TCPm-TCPSecure #00 explicitly states that this * amendment applies only to the "handling of a ... SYN ... in a synchronized * state", it is assumed that this should also apply to the SYN-RECEIVED state. * * (d) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check ACK Field' states * that ... : * * (1) "If the ACK bit is off drop the segment." * * (2) "If the ACK bit is on [and in the] SYN-RECEIVED STATE" ... * * (A) (1) "If SND.UNA < SEG.ACK <= SND.NXT then" ... * * (a) "enter the ESTABLISHED state" ... * (b) "and continue processing." * * (2) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : SYN-SENT * [State]' states to "send ... [any] data or controls which were queued * for transmission" when the SYN-SENT state transitions to the ESTABLISHED * state. * * Although this section is the only section to state that any data or * controls should be sent when transitioning from the SYN-SENT state * to the ESTABLISHED state, it is assumed that any data or controls * should also be sent for the transition from the SYN-RECEIVED state * to the ESTABLISHED state. * * (B) "If the segment acknowledgment is not acceptable, form a reset segment ... * and send it." * * (e) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check FIN Bit' states * that "if the FIN bit is set" ... * * (1) "signal the user 'connection closing' and return any pending RECEIVEs with * same message," ... * * (2) "advance RCV.NXT over the FIN," ... * * (3) "send an acknowledgment for the FIN" ... * * (4) And the "SYN-RECEIVED STATE enter[s] the CLOSE-WAIT state". *$PAGE* * (3) (a) RFC #793, Section 3.9 'Event Processing : USER TIMEOUT : USER TIMEOUT' states that * "for any state if the user timeout expires, flush all queues, signal the user * 'error : connection aborted due to user timeout' ... [and] enter the CLOSED state". * * (b) However, NO RFC specifies or suggests any mechanism to implement/handle user timeouts. * * ???? Therefore, it is assumed that ANY TCP connection that receives a valid TCP data * or control segment should reset its user connection timer. * * (c) (1) Once a TCP connection enters the connected state, most of its TCP connection * timers should be reset. * * (2) However, the following timers MAY already have been allocated & initialized, * & MUST NOT be reset : * * (A) Transmit Idle timer 'TxQ_IdleTmr' * (B) Re-transmit timer 'ReTxQ_Tmr' ********************************************************************************************************* */ static void NetTCP_RxPktConnHandlerSyncRxd (NET_TCP_CONN *pconn, NET_BUF *pbuf, NET_BUF_HDR *pbuf_hdr, NET_ERR *perr) { NET_TCP_SEQ_CODE seq_code; NET_TCP_ACK_CODE ack_code; NET_TCP_RESET_CODE reset_code; NET_TCP_FREE_CODE free_code; NET_TCP_CONN_STATE state; NET_TCP_TIMEOUT_SEC timeout_sec; NET_TMR_TICK timeout_tick; NET_ERR err; NET_ERR err_rtn; /*$PAGE*/ /* -------------- VALIDATE RX'D TCP PKT --------------- */ /* Chk for rx'd fin/close. */ if (pbuf_hdr->TCP_SegClose == DEF_YES) { /* If fin/close avail, update seg lens. */ pbuf_hdr->TCP_SegLenInit += NET_TCP_SEG_LEN_CLOSE; pbuf_hdr->TCP_SegLen += NET_TCP_SEG_LEN_CLOSE; } /* Chk rx'd seq nbr. */ seq_code = NetTCP_RxPktConnIsValidSeq(pconn, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return; } switch (seq_code) { case NET_TCP_CONN_RX_SEQ_VALID: break; case NET_TCP_CONN_RX_SEQ_SYNC: /* If invalid sync rx'd, ... */ case NET_TCP_CONN_RX_SEQ_SYNC_INVALID: /* ... tx TCP conn ack (see Notes #2c2 & #2b2C). */ NetTCP_TxConnAck(pconn, pbuf_hdr, NET_TCP_CONN_TX_ACK_FAULT, NET_TCP_CONN_CLOSE_ALL, &err); *perr = NET_TCP_ERR_CONN_SEQ_SYNC_INVALID; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_RX_SEQ_NONE: case NET_TCP_CONN_RX_SEQ_INVALID: /* If invalid seq rx'd (see Note #2a4A), ... */ default: if (pbuf_hdr->TCP_SegReset != DEF_YES) { /* ... & reset NOT rx'd (see Note #2a4C), ... */ /* ... tx TCP conn ack (see Note #2a4B). */ NetTCP_TxConnAck(pconn, pbuf_hdr, NET_TCP_CONN_TX_ACK_FAULT, NET_TCP_CONN_CLOSE_ALL, &err); } *perr = NET_TCP_ERR_CONN_SEQ_INVALID; return; /* Prevent 'break NOT reachable' compiler warning. */ } /* Chk for rx'd reset. */ reset_code = NetTCP_RxPktConnIsValidReset(pconn, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return; } switch (reset_code) { case NET_TCP_CONN_RX_RESET_NONE: break; case NET_TCP_CONN_RX_RESET_VALID: /* If valid reset rx'd, ... */ /* ... close TCP conn (see Note #2b2Ab). */ NetTCP_ConnClose(pconn, pbuf_hdr, pconn->ConnCloseAppFlag, NET_TCP_CONN_CLOSE_ALL); *perr = NET_TCP_ERR_CONN_RESET_VALID; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_RX_RESET_INVALID: /* If invalid reset rx'd, ... */ default: /* ... tx TCP conn ack (see Note #2b2Ac). */ NetTCP_TxConnAck(pconn, pbuf_hdr, NET_TCP_CONN_TX_ACK_FAULT, NET_TCP_CONN_CLOSE_ALL, &err); *perr = NET_TCP_ERR_CONN_RESET_INVALID; return; /* Prevent 'break NOT reachable' compiler warning. */ } /* Chk for rx'd ack. */ ack_code = NetTCP_RxPktConnIsValidAck(pconn, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return; } switch (ack_code) { case NET_TCP_CONN_RX_ACK_VALID: break; case NET_TCP_CONN_RX_ACK_NONE: /* If NO ack rx'd, ... */ *perr = NET_TCP_ERR_CONN_ACK_NONE; /* ... ignore TCP pkt (see Note #2d1). */ return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_RX_ACK_INVALID: /* If invalid ack rx'd, ... */ case NET_TCP_CONN_RX_ACK_DUP: case NET_TCP_CONN_RX_ACK_PREV: default: /* ... tx TCP conn reset (see Note #2d2B). */ NetTCP_TxConnReset(pconn, pbuf_hdr, NET_TCP_CONN_TX_RESET, NET_TCP_CONN_CLOSE_ALL, &err); *perr = NET_TCP_ERR_CONN_ACK_INVALID; return; /* Prevent 'break NOT reachable' compiler warning. */ } /*$PAGE*/ /* ----------------- HANDLE RX'D SEG ------------------ */ NetTCP_RxPktConnHandlerSeg(pconn, ack_code, pbuf, pbuf_hdr, &err_rtn); switch (err_rtn) { case NET_TCP_ERR_CONN_DATA_NONE: case NET_TCP_ERR_CONN_DATA_VALID: break; case NET_TCP_ERR_CONN_DATA_DUP: /* ???? Allow dup data to update TCP conn? */ *perr = err_rtn; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_ERR_INIT_INCOMPLETE: case NET_TCP_ERR_CONN_NOT_USED: case NET_TCP_ERR_CONN_FAIL: case NET_TCP_ERR_CONN_ACK_INVALID: case NET_TCP_ERR_CONN_DATA_INVALID: case NET_TCP_ERR_INVALID_CONN_STATE: case NET_TCP_ERR_INVALID_CONN_OP: case NET_TCP_ERR_TX: case NET_TCP_ERR_TX_Q_SIGNAL_FAULT: case NET_TCP_ERR_RE_TX_SEG_TH: default: NetTCP_ConnClose(pconn, pbuf_hdr, pconn->ConnCloseAppFlag, NET_TCP_CONN_CLOSE_ALL); *perr = err_rtn; return; /* Prevent 'break NOT reachable' compiler warning. */ } /* ----------------- UPDATE TCP CONN ------------------ */ state = pconn->ConnState; if (pbuf_hdr->TCP_SegClose != DEF_YES) { /* If fin/close NOT rx'd, ... */ pconn->ConnState = NET_TCP_CONN_STATE_CONN; /* ... chng to conn'd state (see Note #2d2A1a). */ } else { /* Else chng to close-wait state (see Note #2e4). */ pconn->ConnState = NET_TCP_CONN_STATE_CLOSE_WAIT; } pconn->TxQ_State = NET_TCP_TX_Q_STATE_CONN; /* ------------------- UPDATE TMRs -------------------- */ free_code = NET_TCP_CONN_FREE_TMR_ALL; /* See Note #3c1. */ DEF_BIT_CLR(free_code, NET_TCP_CONN_FREE_TMR_TX_IDLE); /* See Note #3c2A. */ DEF_BIT_CLR(free_code, NET_TCP_CONN_FREE_TMR_RE_TX); /* See Note #3c2B. */ NetTCP_ConnFreeTmr(pconn, free_code); /* Free TCP conn tmr(s) [see Note #3c]. */ /* Get TCP conn tmr. */ timeout_sec = pconn->TimeoutConn_sec; /* Start conn tmr (see Note #3b). */ timeout_tick = (NET_TMR_TICK)timeout_sec * NET_TMR_TIME_TICK_PER_SEC; pconn->TimeoutTmr = NetTmr_Get((void *) pconn, (CPU_FNCT_PTR) NetTCP_ConnCloseTimeout, (NET_TMR_TICK) timeout_tick, (CPU_INT16U ) NET_TMR_FLAG_NONE, (NET_ERR *)&err); if ( err != NET_TMR_ERR_NONE) { NetTCP_ConnClose(pconn, pbuf_hdr, pconn->ConnCloseAppFlag, NET_TCP_CONN_CLOSE_ALL); *perr = NET_TCP_ERR_NONE_AVAIL; return; } /*$PAGE*/ /* ------------------- ACK TCP CONN ------------------- */ NetTCP_RxPktConnHandlerSignalConn(pconn, state, &err); /* Signal app conn (see Note #1c1). */ if ( err != NET_TCP_ERR_NONE) { NetTCP_ConnClose(pconn, pbuf_hdr, pconn->ConnCloseAppFlag, NET_TCP_CONN_CLOSE_ALL); *perr = NET_TCP_ERR_CONN_FAIL; return; } NetTCP_RxPktConnHandlerRxQ_AppData(pconn, &err); /* Handle TCP conn rx'd data (see Note #2d2A1). */ if ( err != NET_TCP_ERR_NONE) { /* #### On err, close TCP conn? */ *perr = NET_TCP_ERR_CONN_FAIL; return; } if (pconn->ConnState == NET_TCP_CONN_STATE_CONN) { /* If conn'd, tx any tx data (see Note #1c3). */ NetTCP_TxConnTxQ(pconn, pbuf_hdr, NET_TCP_CONN_TX_ACK_NONE, DEF_NO, NET_TCP_CONN_CLOSE_ALL, perr); switch (*perr) { case NET_TCP_ERR_NONE: case NET_TCP_ERR_CONN_ACK_NONE: case NET_TCP_ERR_CONN_ACK_INVALID: case NET_TCP_ERR_CONN_ACK_DLYD: case NET_TCP_ERR_CONN_ACK_PREVLY_TXD: case NET_ERR_TX: /* Ignore transitory tx err(s). */ break; case NET_ERR_INIT_INCOMPLETE: case NET_TCP_ERR_CONN_NOT_USED: case NET_TCP_ERR_CONN_CLOSE: case NET_TCP_ERR_CONN_FAULT: case NET_TCP_ERR_CONN_FAIL: case NET_TCP_ERR_INVALID_CONN_STATE: case NET_TCP_ERR_INVALID_CONN_OP: case NET_TCP_ERR_INVALID_LEN_SEG: case NET_TCP_ERR_NONE_AVAIL: case NET_TCP_ERR_TX: case NET_CONN_ERR_INVALID_FAMILY: case NET_CONN_ERR_INVALID_ADDR: case NET_CONN_ERR_INVALID_ADDR_LEN: default: return; /* Prevent 'break NOT reachable' compiler warning. */ } } else { /* Else tx TCP conn ack (see Note #2e3). */ NetTCP_TxConnAck(pconn, pbuf_hdr, NET_TCP_CONN_TX_ACK_IMMED, NET_TCP_CONN_CLOSE_ALL, perr); switch (*perr) { case NET_TCP_ERR_NONE: case NET_TCP_ERR_CONN_ACK_NONE: case NET_TCP_ERR_CONN_ACK_INVALID: case NET_TCP_ERR_CONN_ACK_DLYD: case NET_TCP_ERR_CONN_ACK_PREVLY_TXD: break; case NET_TCP_ERR_CONN_NOT_USED: case NET_TCP_ERR_CONN_FAULT: case NET_TCP_ERR_INVALID_CONN_STATE: case NET_TCP_ERR_INVALID_CONN_OP: case NET_TCP_ERR_INVALID_LEN_SEG: case NET_TCP_ERR_NONE_AVAIL: default: return; /* Prevent 'break NOT reachable' compiler warning. */ } } *perr = err_rtn; } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_RxPktConnHandlerSyncTxd() * * Description : (1) Handle TCP connection in SYN-SENT state : * * (a) Validate received TCP packet for current TCP connection state : * * (1) Acknowledgement (ACK) See Note #2a * (2) Reset (RST) See Note #2b * (3) Synchronization (SYN) See Note #2c * * (b) Update TCP connection : * (1) Configure TCP connection remote host maximum segment size See Note #3 * (2) Handle received TCP segment See Note #2c * (3) Update TCP connection state See Notes #2c3A1 & #2c3B1 * (4) Update TCP connection timer * * (c) Acknowledge TCP connection * (1) Signal TCP/application connection complete * (2) Handle TCP connection received data See Note #2c3A3 * (3) Transmit TCP connection acknowledgement & data See Note #2c3A2 * * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_RxPktConnHandler(). * * pbuf Pointer to network buffer that received TCP packet. * ---- Argument checked in NetTCP_Rx(). * * pbuf_hdr Pointer to network buffer header that received TCP packet. * -------- Argument validated in NetTCP_Rx(). * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_CONN_SEQ_INVALID Received segment's sequence number is * NOT valid for current TCP connection. * NET_TCP_ERR_CONN_ACK_INVALID Received segment's acknowledgement number is * NOT valid for current TCP connection. * NET_TCP_ERR_CONN_SEQ_FIN_INVALID Received segment's finish/close flag is * NOT valid for current TCP connection. * NET_TCP_ERR_CONN_RESET_VALID Received segment's reset flag is valid * for current TCP connection; i.e. reset * the TCP connection. * NET_TCP_ERR_CONN_RESET_INVALID Received segment's reset flag is NOT valid * for current TCP connection. * * NET_TCP_ERR_CONN_FAIL TCP connection operation(s) failed. * NET_TCP_ERR_NONE_AVAIL Resources NOT available. * * ----- RETURNED BY NetTCP_RxPktConnIsValidSeq() : ----- * ----- RETURNED BY NetTCP_RxPktConnIsValidAck() : ----- * ---- RETURNED BY NetTCP_RxPktConnIsValidReset() : ---- * NET_TCP_ERR_CONN_NOT_USED TCP connection NOT currently used. * NET_TCP_ERR_INVALID_CONN_STATE Invalid TCP connection state. * NET_TCP_ERR_INVALID_LEN_SEG Invalid TCP sequence-segment length. * * ----- RETURNED BY NetTCP_RxPktConnHandlerSeg() : ----- * NET_TCP_ERR_CONN_DATA_NONE Received packet successfully handled; but NO * data to queue to receive queue(s). * NET_TCP_ERR_CONN_DATA_VALID Received packet successfully handled & * valid data queued for processing * (see Notes #2c3A3 & #2c3B3). * NET_TCP_ERR_CONN_DATA_INVALID Received packet contains invalid segment * data; NOT queued to receive queue(s). * NET_TCP_ERR_CONN_DATA_DUP Received packet contains duplicate segment * data; NOT queued to receive queue(s). * * NET_ERR_INIT_INCOMPLETE Network initialization NOT complete. * * NET_TCP_ERR_INVALID_CONN_OP Invalid TCP connection operation. * * NET_TCP_ERR_TX TCP transmit error. * NET_TCP_ERR_TX_Q_SIGNAL_FAULT TCP connection transmit queue signal fault. * NET_TCP_ERR_RE_TX_SEG_TH TCP connection closed due to excessive retransmission. * * ---------- RETURNED BY NetTCP_TxConnTxQ() : ---------- * NET_TCP_ERR_CONN_CLOSE TCP connection closed. * NET_TCP_ERR_CONN_FAULT TCP connection fault; connection(s) aborted. * NET_CONN_ERR_INVALID_FAMILY Invalid connection family. * NET_CONN_ERR_INVALID_ADDR Invalid TCP connection address. * NET_CONN_ERR_INVALID_ADDR_LEN Invalid TCP connection address length. * * Return(s) : none. * * Caller(s) : NetTCP_RxPktConnHandler(). *$PAGE* * Note(s) : (2) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : SYN-SENT [State]' states to : * * (a) "First check the ACK bit[;] if the ACK bit is set" : * * (1) "If SEG.ACK =< ISS, or SEG.ACK > SND.NXT, send a reset (unless the RST bit is * set) ... and discard the segment." * * (2) "If SND.UNA < SEG.ACK =< SND.NXT then the ACK is acceptable." * * See also 'NetTCP_RxPktConnIsValidAck() Note #1c1A'. * * (b) "Second check the RST bit[;] if the RST bit is set [and] if the ACK was acceptable * then signal the user 'error: connection reset', drop the segment, [and] enter * CLOSED state." * * See also 'NetTCP_RxPktConnIsValidReset() Notes #2a3 & #2a5C'. * * (c) "Fourth check the SYN bit[;] if the SYN bit is on and ... acceptable then" ... * * (1) "RCV.NXT is set to SEG.SEQ+1, IRS is set to SEG.SEQ." * (A) See also Note #2c3B3a * * (2) (A) "SND.UNA should be advanced to equal SEG.ACK (if there is an ACK)" ... * * (B) "and any segments on the retransmission queue which are thereby acknowledged * should be removed." * * (3) (A) "If SND.UNA > ISS (our SYN has been ACKed)," ... * * (1) "change the connection state to ESTABLISHED" ... * * (2) (a) "form an ACK segment ... and send it." * * (b) "Data or controls which were queued for transmission may be included." * * (3) "If there are other controls or text in the segment then continue processing." * * (4) RFC #1122, Section 4.2.2.20.(c) adds that "when the connection enters * ESTABLISHED state, the following variables must be set" : * * (a) SND.WND <- SEG.WND * (b) SND.WL1 <- SEG.SEQ * (c) SND.WL2 <- SEG.ACK * * (B) "Otherwise" ... * * (1) "enter SYN-RECEIVED," ... * * (2) "form an SYN,ACK segment ... and send it." * * (3) "If there are other controls or text in the segment, queue them for later * processing after the ESTABLISHED state has been reached." * * (a) If any control or text is queued for later processing, the next sequence * octet to receive (RCV.NXT) MUST include the length of this received * segment (SEG.LEN) : * * (1) RCV.NXT = SEG.SEQ + SEG.LEN + 1 * * (d) "Fifth, if neither of the SYN or RST bits is set then drop the segment." * * (e) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check FIN Bit' states * to "not process the FIN if the state is CLOSED, LISTEN or SYN-SENT since the SEG.SEQ * cannot be validated; drop the segment". *$PAGE* * (3) RFC #1122, Section 4.2.2.6 states that ... : * * (a) A "TCP SHOULD send an MSS (Maximum Segment Size) option in every SYN segment". * * (b) "If an MSS option is not received at connection setup, TCP MUST assume a default * send MSS of 536." * * (4) (a) TCP connection timeout for SYN-SENT state is implemented by TCP connection retransmission * function(s) (see 'NetTCP_TxConnReTxQ() Note #3c'). * * (b) (1) RFC #793, Section 3.9 'Event Processing : USER TIMEOUT : USER TIMEOUT' states that * "for any state if the user timeout expires, flush all queues, signal the user * 'error : connection aborted due to user timeout' ... [and] enter the CLOSED state". * * (2) However, NO RFC specifies or suggests any mechanism to implement/handle user timeouts. * * ???? Therefore, it is assumed that ANY TCP connection that receives a valid TCP data * or control segment should reset its user connection timer. * * (c) (1) Once a TCP connection enters the connected state, most of its TCP connection * timers should be reset. * * (2) However, the following timers MAY already have been allocated & initialized, * & MUST NOT be reset : * * (A) Transmit Idle timer 'TxQ_IdleTmr' * (B) Re-transmit timer 'ReTxQ_Tmr' ********************************************************************************************************* */ /*$PAGE*/ static void NetTCP_RxPktConnHandlerSyncTxd (NET_TCP_CONN *pconn, NET_BUF *pbuf, NET_BUF_HDR *pbuf_hdr, NET_ERR *perr) { NET_TCP_SEQ_CODE seq_code; NET_TCP_ACK_CODE ack_code; NET_TCP_RESET_CODE reset_code; NET_TCP_FREE_CODE free_code; NET_TCP_CONN_STATE state; NET_TCP_TIMEOUT_SEC timeout_sec; NET_TMR_TICK timeout_tick; NET_ERR err; NET_ERR err_rtn; /* -------------- VALIDATE RX'D TCP PKT --------------- */ /* Chk for rx'd fin/close. */ if (pbuf_hdr->TCP_SegClose != DEF_NO) { /* If invalid fin/close rx'd, ... */ *perr = NET_TCP_ERR_CONN_SEQ_FIN_INVALID; return; /* ... ignore TCP pkt (see Note #2e). */ } /* Chk for rx'd ack. */ ack_code = NetTCP_RxPktConnIsValidAck(pconn, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return; } if (ack_code == NET_TCP_CONN_RX_ACK_INVALID) { /* If invalid ack rx'd, ... */ /* ... tx TCP conn reset (see Note #2a1). */ NetTCP_TxConnReset(pconn, pbuf_hdr, NET_TCP_CONN_TX_RESET, NET_TCP_CONN_CLOSE_ALL, &err); *perr = NET_TCP_ERR_CONN_ACK_INVALID; return; } /* Chk for rx'd reset. */ reset_code = NetTCP_RxPktConnIsValidReset(pconn, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return; } switch (reset_code) { case NET_TCP_CONN_RX_RESET_NONE: break; case NET_TCP_CONN_RX_RESET_VALID: /* If valid reset rx'd, close TCP conn (see Note #2b). */ NetTCP_ConnClose(pconn, pbuf_hdr, pconn->ConnCloseAppFlag, NET_TCP_CONN_CLOSE_ALL); *perr = NET_TCP_ERR_CONN_RESET_VALID; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_RX_RESET_INVALID: default: *perr = NET_TCP_ERR_CONN_RESET_INVALID; return; /* Prevent 'break NOT reachable' compiler warning. */ } /* Chk rx'd seq nbr. */ seq_code = NetTCP_RxPktConnIsValidSeq(pconn, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return; } if (seq_code != NET_TCP_CONN_RX_SEQ_SYNC) { /* If sync NOT rx'd, ... */ *perr = NET_TCP_ERR_CONN_SEQ_INVALID; return; /* ... ignore TCP pkt (see Note #2d). */ } /* Else sync avail, update seg lens. */ pbuf_hdr->TCP_SegLenInit += NET_TCP_SEG_LEN_SYNC; pbuf_hdr->TCP_SegLen += NET_TCP_SEG_LEN_SYNC; /*$PAGE*/ /* ----------------- UPDATE TCP CONN ------------------ */ /* Cfg remote max seg size ... */ /* ... as advertised by remote host (see Note #3). */ pconn->MaxSegSizeRemote = (pbuf_hdr->TCP_MaxSegSize != NET_TCP_MAX_SEG_SIZE_NONE) ? pbuf_hdr->TCP_MaxSegSize : NET_TCP_MAX_SEG_SIZE_DFLT_TX; /* ----------------- HANDLE RX'D SEG ------------------ */ NetTCP_RxPktConnHandlerSeg(pconn, ack_code, pbuf, pbuf_hdr, &err_rtn); switch (err_rtn) { case NET_TCP_ERR_CONN_DATA_NONE: case NET_TCP_ERR_CONN_DATA_VALID: break; case NET_TCP_ERR_CONN_DATA_DUP: /* ???? Allow dup data to update TCP conn? */ *perr = err_rtn; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_ERR_INIT_INCOMPLETE: case NET_TCP_ERR_CONN_NOT_USED: case NET_TCP_ERR_CONN_FAIL: case NET_TCP_ERR_CONN_ACK_INVALID: case NET_TCP_ERR_CONN_DATA_INVALID: case NET_TCP_ERR_INVALID_CONN_STATE: case NET_TCP_ERR_INVALID_CONN_OP: case NET_TCP_ERR_TX: case NET_TCP_ERR_TX_Q_SIGNAL_FAULT: case NET_TCP_ERR_RE_TX_SEG_TH: default: NetTCP_ConnClose(pconn, pbuf_hdr, pconn->ConnCloseAppFlag, NET_TCP_CONN_CLOSE_ALL); *perr = err_rtn; return; /* Prevent 'break NOT reachable' compiler warning. */ } /* ----------------- UPDATE TCP CONN ------------------ */ if (ack_code == NET_TCP_CONN_RX_ACK_VALID) { /* If valid ack rx'd (see Note #2a1), ... */ state = pconn->ConnState; pconn->ConnState = NET_TCP_CONN_STATE_CONN; /* ... chng to conn'd state (see Note #2c3A1). */ pconn->TxQ_State = NET_TCP_TX_Q_STATE_CONN; } else { /* Else chng to sync rx'd state (see Note #2c3B1). */ pconn->ConnState = NET_TCP_CONN_STATE_SYNC_RXD_ACTIVE; } /* ------------------- UPDATE TMRs -------------------- */ free_code = NET_TCP_CONN_FREE_TMR_ALL; /* See Note #4c1. */ DEF_BIT_CLR(free_code, NET_TCP_CONN_FREE_TMR_TX_IDLE); /* See Note #4c2A. */ DEF_BIT_CLR(free_code, NET_TCP_CONN_FREE_TMR_RE_TX); /* See Note #4c2B. */ NetTCP_ConnFreeTmr(pconn, free_code); /* Free TCP conn tmr(s) [see Note #4c]. */ if (pconn->ConnState == NET_TCP_CONN_STATE_CONN) { /* If conn'd, get TCP conn tmr. */ timeout_sec = pconn->TimeoutConn_sec; /* Start conn tmr (see Note #4b2). */ timeout_tick = (NET_TMR_TICK)timeout_sec * NET_TMR_TIME_TICK_PER_SEC; pconn->TimeoutTmr = NetTmr_Get((void *) pconn, (CPU_FNCT_PTR) NetTCP_ConnCloseTimeout, (NET_TMR_TICK) timeout_tick, (CPU_INT16U ) NET_TMR_FLAG_NONE, (NET_ERR *)&err); if ( err != NET_TMR_ERR_NONE) { NetTCP_ConnClose(pconn, pbuf_hdr, pconn->ConnCloseAppFlag, NET_TCP_CONN_CLOSE_ALL); *perr = NET_TCP_ERR_NONE_AVAIL; return; } } /*$PAGE*/ /* ------------------- ACK TCP CONN ------------------- */ if (pconn->ConnState == NET_TCP_CONN_STATE_CONN) { /* If conn'd, ... */ NetTCP_RxPktConnHandlerSignalConn(pconn, state, &err); /* ... signal app conn (see Note #1c1); ... */ if ( err != NET_TCP_ERR_NONE) { NetTCP_ConnClose(pconn, pbuf_hdr, pconn->ConnCloseAppFlag, NET_TCP_CONN_CLOSE_ALL); *perr = NET_TCP_ERR_CONN_FAIL; return; } NetTCP_RxPktConnHandlerRxQ_AppData(pconn, &err); /* ... handle TCP conn rx'd data (see Note #2d2A1); ... */ if ( err != NET_TCP_ERR_NONE) { /* #### On err, close TCP conn? */ *perr = NET_TCP_ERR_CONN_FAIL; return; } /* ... & tx ack & any tx data (see Note #1c3). */ NetTCP_TxConnTxQ(pconn, pbuf_hdr, NET_TCP_CONN_TX_ACK_IMMED, DEF_NO, NET_TCP_CONN_CLOSE_ALL, perr); switch (*perr) { case NET_TCP_ERR_NONE: case NET_TCP_ERR_CONN_ACK_NONE: case NET_TCP_ERR_CONN_ACK_INVALID: case NET_TCP_ERR_CONN_ACK_DLYD: case NET_TCP_ERR_CONN_ACK_PREVLY_TXD: case NET_ERR_TX: /* Ignore transitory tx err(s). */ break; case NET_ERR_INIT_INCOMPLETE: case NET_TCP_ERR_CONN_NOT_USED: case NET_TCP_ERR_CONN_CLOSE: case NET_TCP_ERR_CONN_FAULT: case NET_TCP_ERR_CONN_FAIL: case NET_TCP_ERR_INVALID_CONN_STATE: case NET_TCP_ERR_INVALID_CONN_OP: case NET_TCP_ERR_INVALID_LEN_SEG: case NET_TCP_ERR_NONE_AVAIL: case NET_TCP_ERR_TX: case NET_CONN_ERR_INVALID_FAMILY: case NET_CONN_ERR_INVALID_ADDR: case NET_CONN_ERR_INVALID_ADDR_LEN: default: return; /* Prevent 'break NOT reachable' compiler warning. */ } } else { /* Else tx TCP conn sync/ack (see Note #2c3B2). */ NetTCP_TxConnSync(pconn, pbuf_hdr, &err); if ( err != NET_TCP_ERR_NONE) { NetTCP_ConnClose(pconn, pbuf_hdr, pconn->ConnCloseAppFlag, NET_TCP_CONN_CLOSE_ALL); *perr = NET_TCP_ERR_CONN_CLOSE; return; } } *perr = err_rtn; } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_RxPktConnHandlerConn() * * Description : (1) Handle TCP connection in ESTABLISHED state : * * (a) Validate received TCP packet for current TCP connection state : * * (1) Sequence Number (SEQ) See Note #2a * (2) Reset (RST) See Note #2b * (3) Synchronization (SYN) See Note #2c * (4) Acknowledgement (ACK) See Note #2d * (5) Finish/Close (FIN) See Note #2f * * (b) Update TCP connection : * (1) Handle received TCP segment See Notes #2d & #2e * (2) Update TCP connection state See Note #2f5 * (3) Update TCP connection timer See Note #3 * * (c) Transmit TCP connection acknowledgement & data See Notes #2e2A & #2f3 * * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_RxPktConnHandler(). * * pbuf Pointer to network buffer that received TCP packet. * ---- Argument checked in NetTCP_Rx(). * * pbuf_hdr Pointer to network buffer header that received TCP packet. * -------- Argument validated in NetTCP_Rx(). * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_CONN_SEQ_SYNC_INVALID Received segment's synchronization is * NOT valid for current TCP connection. * NET_TCP_ERR_CONN_SEQ_INVALID Received segment's sequence number is * NOT valid for current TCP connection. * NET_TCP_ERR_CONN_ACK_NONE Received segment's acknowledgement is * NOT available. * NET_TCP_ERR_CONN_ACK_INVALID Received segment's acknowledgement number is * NOT valid for current TCP connection. * NET_TCP_ERR_CONN_RESET_VALID Received segment's reset flag is valid * for current TCP connection; i.e. reset * the TCP connection. * NET_TCP_ERR_CONN_RESET_INVALID Received segment's reset flag is NOT valid * for current TCP connection. * * NET_TCP_ERR_CONN_FAIL TCP connection operation(s) failed. * * ----- RETURNED BY NetTCP_RxPktConnIsValidSeq() : ----- * ----- RETURNED BY NetTCP_RxPktConnIsValidAck() : ----- * ---- RETURNED BY NetTCP_RxPktConnIsValidReset() : ---- * NET_TCP_ERR_CONN_NOT_USED TCP connection NOT currently used. * NET_TCP_ERR_INVALID_CONN_STATE Invalid TCP connection state. * NET_TCP_ERR_INVALID_LEN_SEG Invalid TCP sequence-segment length. * * ----- RETURNED BY NetTCP_RxPktConnHandlerSeg() : ----- * NET_TCP_ERR_CONN_DATA_NONE Received packet successfully handled; but NO * data to queue to receive queue(s). * NET_TCP_ERR_CONN_DATA_VALID Received packet successfully handled & valid * data queued for processing. * NET_TCP_ERR_CONN_DATA_INVALID Received packet contains invalid segment * data; NOT queued to receive queue(s). * NET_TCP_ERR_CONN_DATA_DUP Received packet contains duplicate segment * data; NOT queued to receive queue(s). * * NET_ERR_INIT_INCOMPLETE Network initialization NOT complete. * * NET_TCP_ERR_INVALID_CONN_OP Invalid TCP connection operation. * * NET_TCP_ERR_RX_Q_FULL TCP connection receive queue full. * NET_TCP_ERR_RX_Q_SIGNAL_FAULT TCP connection receive queue signal fault. * NET_TCP_ERR_TX_Q_SIGNAL_FAULT TCP connection transmit queue signal fault. * NET_TCP_ERR_RE_TX_SEG_TH TCP connection closed due to excessive retransmission. * NET_TCP_ERR_TX TCP transmit error. * * ---------- RETURNED BY NetTCP_TxConnTxQ() : ---------- * NET_TCP_ERR_CONN_CLOSE TCP connection closed. * NET_TCP_ERR_CONN_FAULT TCP connection fault; connection(s) aborted. * NET_TCP_ERR_NONE_AVAIL Resources NOT available. * NET_CONN_ERR_INVALID_FAMILY Invalid connection family. * NET_CONN_ERR_INVALID_ADDR Invalid TCP connection address. * NET_CONN_ERR_INVALID_ADDR_LEN Invalid TCP connection address length. * * Return(s) : none. * * Caller(s) : NetTCP_RxPktConnHandler(). *$PAGE* * Note(s) : (2) TCP connections in the ESTABLISHED state are handled as follows : * * (a) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check Sequence Number' * states that in the "SYN-RECEIVED, ESTABLISHED STATE, FIN-WAIT-1 STATE, FIN-WAIT-2 * STATE, CLOSE-WAIT STATE, CLOSING STATE, LAST-ACK STATE, TIME-WAIT STATE" that ... * * (1) "Segments are processed in sequence ... processing is done in SEG.SEQ order." * * (2) "Initial tests on arrival are used to discard old duplicates." * * (3) "If a segment's contents straddle the boundary between old and new, only the * new parts should be processed." * * (4) (A) "If an incoming segment is not acceptable," ... * * (B) "an acknowledgment should be sent in reply" ... * * (C) "(unless the RST bit is set, if so drop the segment)". * * See also Notes #2b2Aa & #2b2B. * * See also 'NetTCP_RxPktConnIsValidSeq() Note #1d'. * * (b) (1) (A) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check RST Bit' states * that in the "ESTABLISHED, FIN-WAIT-1, FIN-WAIT-2, CLOSE-WAIT" states that "if the * RST bit is set then, any outstanding RECEIVEs and SEND[s] should receive 'reset' * responses. All segment queues should be flushed. Users should also receive an * unsolicited general 'connection reset' signal[, and] enter the CLOSED state". * * (B) RFC #793, Section 3.4 'Establishing a Connection : Reset Processing' reiterates * that "if the receiver ... of a RST ... was in any other state [other than LISTEN * or SYN-RECEIVED], it aborts the connection and advises the user and goes to the * CLOSED state". * * (2) (A) RFC Draft-IETF-TCPm-TCPSecure #00, Section 2.2 amends the "handling of a segment * with the RST bit when in a synchronized state" to "provide some protection against * ... blind reset attack[s] using the RST bit" : * * (a) "If the RST bit is set and the sequence number is outside the expected * window, silently drop the segment." * * (b) "If the RST bit is exactly the next expected sequence number [sic], reset * the connection"; it is assumed that this should read "if the RST bit is * set and the sequence number is exactly the next expected sequence number, * reset the connection." * * (c) "If the RST bit is set and the sequence number does not exactly match * the next expected sequence value, yet is within the acceptable window * (RCV.NXT <= SEG.SEQ < RCV.NXT+RCV.WND) send an acknowledgment." * * (B) ???? In addition, RFC Draft-IETF-TCPm-TCPSecure #00 does NOT provide a precedence * priority for handling TCP segments received with BOTH the RST & SYN bits set. * * ???? Therefore, since it does NOT seem reasonable to reset a TCP connection * due to a TCP segment that also attempted to synchronize the TCP connection, * it is assumed that the amended handling of the SYN bit should take precedence * over the amended handling of the RST bit. * * See also Note #2c2. * * (c) (1) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check SYN Bit' * states that in the "SYN-RECEIVED [STATE], ESTABLISHED STATE, FIN-WAIT STATE-1, * FIN-WAIT STATE-2, CLOSE-WAIT STATE, CLOSING STATE, LAST-ACK STATE, TIME-WAIT * STATE" that ... * * (A) "If the SYN is in the window it is an error, send a reset, any outstanding * RECEIVEs and SEND[s] should receive 'reset' responses, all segment queues * should be flushed, the user should also receive an unsolicited general * 'connection reset' signal[, and] enter the CLOSED state." * * (B) But "if the SYN is not in the window this step would not have been reached * and an ack would have been sent". * * (2) HOWEVER, RFC Draft-IETF-TCPm-TCPSecure #00, Section 3.2 amends the "handling * of a segment with the SYN bit set in the synchronized state ... [by] handling * ... the SYN bit" as follows : * * (a) "If the SYN bit is set and the sequence number is outside the * expected window, send an ACK back to the peer." * * (b) "If the SYN bit is set and the sequence number is an exact * match to the next expected sequence (RCV.NXT == SEG.SEQ) * then send an ACK segment ... but ... subtract one from * value being acknowledged." * * (c) "If the SYN bit is set and the sequence number is acceptable, * i.e.: (RCV.NXT <= SEG.SEQ <= RCV.NXT+RCV.WND) then send an * ACK segment." *$PAGE* * (d) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check ACK Field' * states that ... : * * (1) "If the ACK bit is off drop the segment." * * (2) "If the ACK bit is on [and in the] ESTABLISHED STATE" ... * * (A) "If SND.UNA < SEG.ACK <= SND.NXT then" ... * * (1) "set SND.UNA <- SEG.ACK." * * (2) "Any segments on the retransmission queue which are thereby entirely * acknowledged are removed." * * (3) "the send window should be updated" : * * (a) (1) (A) "If ((SND.WL1 < SEG.SEQ) or" ... * * (B) (1) "(SND.WL1 = SEG.SEQ and" ... * (2) "SND.WL2 <= SEG.ACK))," ... * * (2) (A) "set SND.WND <- SEG.WND," ... * (B) "set SND.WL1 <- SEG.SEQ," ... * (C) "set SND.WL2 <- SEG.ACK." * * (b) "Note that SND.WND is an offset from SND.UNA, that SND.WL1 records the * sequence number of the last segment used to update SND.WND, and that * SND.WL2 records the acknowledgment number of the last segment used to * update SND.WND. The check here preveents using old segments to update * the window." * * (B) (1) "If the ACK is a duplicate (SEG.ACK <= SND.UNA), it can be ignored." * * (2) RFC #1122, Section 4.2.2.20.(g) amends the transmit window update criteria * for the segment's acknowledgement to include SND.UNA : "The window should * updated if SND.UNA <= SEG.ACK <= SND.NXT." * * See also 'NetTCP_RxPktConnHandlerTxWinRemote() Note #1b2'. * * (C) "If the ACK acks something not yet sent (SEG.ACK > SND.NXT) then send an ACK * [and] drop the segment." * * (e) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Process Segment Text' * states that for the "ESTABLISHED STATE, FIN-WAIT-1 STATE, FIN-WAIT-2 STATE ... it * is possible to deliver segment text to user RECEIVE buffers" : * * (1) "If the segment ... carries [a] PUSH flag, then the user is informed [and] the * buffer is returned." * * (2) (A) (1) "When the TCP takes responsibility for delivering the data to the user it * must also acknowledge the receipt of the data." * * (2) "This acknowledgment should be piggybacked on a segment being transmitted * if possible without incurring undue delay." * * (B) "Once the TCP takes responsibility for the data" ... * * (1) "it advances RCV.NXT over the data accepted," ... * * (2) "adjusts RCV.WND as appropriate to the current buffer availability" ... * * (3) (a) "The total of RCV.NXT and RCV.WND should not be reduced." * * (b) RFC #793, Section 3.7 'Data Communication : Managing the Window' & * RFC #1122, Section 4.2.2.16 confirm that "a TCP receiver SHOULD NOT * shrink the window"; i.e. "advertise a much smaller window without * having accepted that much data". * * (f) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check FIN Bit' states * that "if the FIN bit is set" ... * * (1) "signal the user 'connection closing' and return any pending RECEIVEs with * same message," ... * * (2) "advance RCV.NXT over the FIN," ... * * (3) "send an acknowledgment for the FIN" ... * * (4) "FIN implies PUSH for any segment text not yet delivered to the user" ... * * (5) And the "ESTABLISHED STATE enter[s] the CLOSE-WAIT state". *$PAGE* * (3) (a) RFC #793, Section 3.9 'Event Processing : USER TIMEOUT : USER TIMEOUT' states that * "for any state if the user timeout expires, flush all queues, signal the user * 'error : connection aborted due to user timeout' ... [and] enter the CLOSED state". * * (b) However, NO RFC specifies or suggests any mechanism to implement/handle user timeouts. * * ???? Therefore, it is assumed that ANY TCP connection that receives a valid TCP data * or control segment should reset its user connection timer. ********************************************************************************************************* */ static void NetTCP_RxPktConnHandlerConn (NET_TCP_CONN *pconn, NET_BUF *pbuf, NET_BUF_HDR *pbuf_hdr, NET_ERR *perr) { NET_TCP_SEQ_CODE seq_code; NET_TCP_ACK_CODE ack_code; NET_TCP_RESET_CODE reset_code; NET_TCP_TIMEOUT_SEC timeout_sec; NET_TMR_TICK timeout_tick; NET_ERR err; NET_ERR err_rtn; /*$PAGE*/ /* -------------- VALIDATE RX'D TCP PKT --------------- */ /* Chk for rx'd fin/close. */ if (pbuf_hdr->TCP_SegClose == DEF_YES) { /* If fin/close avail, update seg lens. */ pbuf_hdr->TCP_SegLenInit += NET_TCP_SEG_LEN_CLOSE; pbuf_hdr->TCP_SegLen += NET_TCP_SEG_LEN_CLOSE; } /* Chk rx'd seq nbr. */ seq_code = NetTCP_RxPktConnIsValidSeq(pconn, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return; } switch (seq_code) { case NET_TCP_CONN_RX_SEQ_VALID: break; case NET_TCP_CONN_RX_SEQ_SYNC: /* If invalid sync rx'd, ... */ case NET_TCP_CONN_RX_SEQ_SYNC_INVALID: /* ... tx TCP conn ack (see Notes #2c2 & #2b2B). */ NetTCP_TxConnAck(pconn, pbuf_hdr, NET_TCP_CONN_TX_ACK_FAULT, NET_TCP_CONN_CLOSE_ALL, &err); *perr = NET_TCP_ERR_CONN_SEQ_SYNC_INVALID; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_RX_SEQ_NONE: case NET_TCP_CONN_RX_SEQ_INVALID: /* If invalid seq rx'd (see Note #2a4A), ... */ default: if (pbuf_hdr->TCP_SegReset != DEF_YES) { /* ... & reset NOT rx'd (see Note #2a4C), ... */ /* ... tx TCP conn ack (see Note #2a4B). */ NetTCP_TxConnAck(pconn, pbuf_hdr, NET_TCP_CONN_TX_ACK_FAULT, NET_TCP_CONN_CLOSE_ALL, &err); } *perr = NET_TCP_ERR_CONN_SEQ_INVALID; return; /* Prevent 'break NOT reachable' compiler warning. */ } /* Chk for rx'd reset. */ reset_code = NetTCP_RxPktConnIsValidReset(pconn, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return; } switch (reset_code) { case NET_TCP_CONN_RX_RESET_NONE: break; case NET_TCP_CONN_RX_RESET_VALID: /* If valid reset rx'd, ... */ /* ... close TCP conn (see Note #2b1A). */ NetTCP_ConnClose(pconn, pbuf_hdr, pconn->ConnCloseAppFlag, NET_TCP_CONN_CLOSE_ALL); *perr = NET_TCP_ERR_CONN_RESET_VALID; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_RX_RESET_INVALID: /* If invalid reset rx'd, ... */ default: /* ... tx TCP conn ack (see Note #2b2Ac). */ NetTCP_TxConnAck(pconn, pbuf_hdr, NET_TCP_CONN_TX_ACK_FAULT, NET_TCP_CONN_CLOSE_ALL, &err); *perr = NET_TCP_ERR_CONN_RESET_INVALID; return; /* Prevent 'break NOT reachable' compiler warning. */ } /* Chk for rx'd ack. */ ack_code = NetTCP_RxPktConnIsValidAck(pconn, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return; } switch (ack_code) { case NET_TCP_CONN_RX_ACK_VALID: case NET_TCP_CONN_RX_ACK_DUP: case NET_TCP_CONN_RX_ACK_PREV: break; case NET_TCP_CONN_RX_ACK_NONE: /* If NO ack rx'd, ... */ *perr = NET_TCP_ERR_CONN_ACK_NONE; /* ... ignore TCP pkt (see Note #2d1). */ return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_RX_ACK_INVALID: /* If invalid ack rx'd, ... */ default: /* ... tx TCP conn ack (see Note #2d2C). */ NetTCP_TxConnAck(pconn, pbuf_hdr, NET_TCP_CONN_TX_ACK_FAULT, NET_TCP_CONN_CLOSE_ALL, &err); *perr = NET_TCP_ERR_CONN_ACK_INVALID; return; /* Prevent 'break NOT reachable' compiler warning. */ } /*$PAGE*/ /* ----------------- HANDLE RX'D SEG ------------------ */ NetTCP_RxPktConnHandlerSeg(pconn, ack_code, pbuf, pbuf_hdr, &err_rtn); switch (err_rtn) { case NET_TCP_ERR_CONN_DATA_NONE: case NET_TCP_ERR_CONN_DATA_VALID: break; case NET_TCP_ERR_CONN_DATA_DUP: /* ???? Allow dup data to update TCP conn? */ *perr = err_rtn; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_ERR_INIT_INCOMPLETE: case NET_TCP_ERR_CONN_NOT_USED: case NET_TCP_ERR_CONN_FAIL: case NET_TCP_ERR_CONN_ACK_INVALID: case NET_TCP_ERR_CONN_DATA_INVALID: case NET_TCP_ERR_INVALID_CONN_STATE: case NET_TCP_ERR_INVALID_CONN_OP: case NET_TCP_ERR_INVALID_LEN_SEG: case NET_TCP_ERR_RX_Q_FULL: case NET_TCP_ERR_RX_Q_SIGNAL_FAULT: case NET_TCP_ERR_TX: case NET_TCP_ERR_TX_Q_SIGNAL_FAULT: case NET_TCP_ERR_RE_TX_SEG_TH: default: NetTCP_ConnClose(pconn, pbuf_hdr, pconn->ConnCloseAppFlag, NET_TCP_CONN_CLOSE_ALL); *perr = err_rtn; return; /* Prevent 'break NOT reachable' compiler warning. */ } /* ----------------- UPDATE TCP CONN ------------------ */ if (pbuf_hdr->TCP_SegClose == DEF_YES) { /* If fin/close rx'd, ... */ pconn->ConnState = NET_TCP_CONN_STATE_CLOSE_WAIT; /* ... chng to close-wait state (see Note #2f5). */ } /* -------------------- UPDATE TMR -------------------- */ timeout_sec = pconn->TimeoutConn_sec; /* Reset conn tmr (see Note #3b). */ timeout_tick = (NET_TMR_TICK)timeout_sec * NET_TMR_TIME_TICK_PER_SEC; if (pconn->TimeoutTmr != (NET_TMR *)0) { NetTmr_Set((NET_TMR *) pconn->TimeoutTmr, (CPU_FNCT_PTR) NetTCP_ConnCloseTimeout, (NET_TMR_TICK) timeout_tick, (NET_ERR *)&err); } else { pconn->TimeoutTmr = NetTmr_Get((void *) pconn, (CPU_FNCT_PTR) NetTCP_ConnCloseTimeout, (NET_TMR_TICK) timeout_tick, (CPU_INT16U ) NET_TMR_FLAG_NONE, (NET_ERR *)&err); } if ( err != NET_TMR_ERR_NONE) { NetTCP_ConnClose(pconn, pbuf_hdr, pconn->ConnCloseAppFlag, NET_TCP_CONN_CLOSE_ALL); *perr = NET_TCP_ERR_CONN_FAIL; return; } /*$PAGE*/ /* --------------- TX TCP CONN ACK/DATA --------------- */ ack_code = ((pbuf_hdr->TCP_SegAckTxReqCode == NET_TCP_CONN_TX_ACK_IMMED) || (pbuf_hdr->TCP_SegClose == DEF_YES)) /* See Note #2f3. */ ? NET_TCP_CONN_TX_ACK_IMMED : NET_TCP_CONN_TX_ACK; /* Tx ack & any tx data (see Note #1c). */ NetTCP_TxConnTxQ(pconn, pbuf_hdr, ack_code, DEF_NO, NET_TCP_CONN_CLOSE_ALL, perr); switch (*perr) { case NET_TCP_ERR_NONE: case NET_TCP_ERR_CONN_ACK_NONE: case NET_TCP_ERR_CONN_ACK_INVALID: case NET_TCP_ERR_CONN_ACK_DLYD: case NET_TCP_ERR_CONN_ACK_PREVLY_TXD: case NET_ERR_TX: /* Ignore transitory tx err(s). */ break; case NET_ERR_INIT_INCOMPLETE: case NET_TCP_ERR_CONN_NOT_USED: case NET_TCP_ERR_CONN_CLOSE: case NET_TCP_ERR_CONN_FAULT: case NET_TCP_ERR_CONN_FAIL: case NET_TCP_ERR_INVALID_CONN_STATE: case NET_TCP_ERR_INVALID_CONN_OP: case NET_TCP_ERR_INVALID_LEN_SEG: case NET_TCP_ERR_NONE_AVAIL: case NET_TCP_ERR_TX: case NET_CONN_ERR_INVALID_FAMILY: case NET_CONN_ERR_INVALID_ADDR: case NET_CONN_ERR_INVALID_ADDR_LEN: default: return; /* Prevent 'break NOT reachable' compiler warning. */ } *perr = err_rtn; } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_RxPktConnHandlerFinWait1() * * Description : (1) Handle TCP connection in FIN-WAIT-1 state : * * (a) Validate received TCP packet for current TCP connection state : * * (1) Sequence Number (SEQ) See Note #2a * (2) Reset (RST) See Note #2b * (3) Synchronization (SYN) See Note #2c * (4) Acknowledgement (ACK) See Note #2d * (5) Finish/Close (FIN) See Note #2f * * (b) Update TCP connection : * (1) Handle received TCP segment See Notes #2d & #2e * (2) Signal TCP/application connection close See Note #2d2B2 * (3) Update TCP connection state : See Notes #2f5A1, #2f5A2b1, & #2f5B * (A) Configure TCP connection timeout value * (B) Configure TCP connection timeout function * (4) Update TCP connection timer(s) See Notes #2f5A1, #2f5A2b2, & #3 * * (c) Transmit TCP connection acknowledgement & data See Notes #2e2A & #2f3 * * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_RxPktConnHandler(). * * pbuf Pointer to network buffer that received TCP packet. * ---- Argument checked in NetTCP_Rx(). * * pbuf_hdr Pointer to network buffer header that received TCP packet. * -------- Argument validated in NetTCP_Rx(). * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_CONN_SEQ_SYNC_INVALID Received segment's synchronization is * NOT valid for current TCP connection. * NET_TCP_ERR_CONN_SEQ_INVALID Received segment's sequence number is * NOT valid for current TCP connection. * NET_TCP_ERR_CONN_ACK_NONE Received segment's acknowledgement is * NOT available. * NET_TCP_ERR_CONN_ACK_INVALID Received segment's acknowledgement number is * NOT valid for current TCP connection. * NET_TCP_ERR_CONN_RESET_VALID Received segment's reset flag is valid * for current TCP connection; i.e. reset * the TCP connection. * NET_TCP_ERR_CONN_RESET_INVALID Received segment's reset flag is NOT valid * for current TCP connection. * * NET_TCP_ERR_CONN_FAIL TCP connection operation(s) failed. * * ----- RETURNED BY NetTCP_RxPktConnIsValidSeq() : ----- * ----- RETURNED BY NetTCP_RxPktConnIsValidAck() : ----- * ---- RETURNED BY NetTCP_RxPktConnIsValidReset() : ---- * NET_TCP_ERR_CONN_NOT_USED TCP connection NOT currently used. * NET_TCP_ERR_INVALID_CONN_STATE Invalid TCP connection state. * NET_TCP_ERR_INVALID_LEN_SEG Invalid TCP sequence-segment length. * * ----- RETURNED BY NetTCP_RxPktConnHandlerSeg() : ----- * NET_TCP_ERR_CONN_DATA_NONE Received packet successfully handled; but NO * data to queue to receive queue(s). * NET_TCP_ERR_CONN_DATA_VALID Received packet successfully handled & valid * data queued for processing. * NET_TCP_ERR_CONN_DATA_INVALID Received packet contains invalid segment * data; NOT queued to receive queue(s). * NET_TCP_ERR_CONN_DATA_DUP Received packet contains duplicate segment * data; NOT queued to receive queue(s). * * NET_ERR_INIT_INCOMPLETE Network initialization NOT complete. * * NET_TCP_ERR_INVALID_CONN_OP Invalid TCP connection operation. * * NET_TCP_ERR_RX_Q_FULL TCP connection receive queue full. * NET_TCP_ERR_RX_Q_SIGNAL_FAULT TCP connection receive queue signal fault. * NET_TCP_ERR_TX_Q_SIGNAL_FAULT TCP connection transmit queue signal fault. * NET_TCP_ERR_RE_TX_SEG_TH TCP connection closed due to excessive retransmission. * NET_TCP_ERR_TX TCP transmit error. * * - RETURNED BY NetTCP_RxPktConnHandlerSignalClose() : - * NET_TCP_ERR_INVALID_CONN_ID Invalid application connection. * NET_CONN_ERR_INVALID_FAMILY Invalid network connection family. * NET_CONN_ERR_INVALID_CONN Invalid network connection number. * NET_CONN_ERR_NOT_USED Network connection NOT currently used. * * ---------- RETURNED BY NetTCP_TxConnTxQ() : ---------- * NET_TCP_ERR_CONN_CLOSE TCP connection closed. * NET_TCP_ERR_CONN_FAULT TCP connection fault; connection(s) aborted. * NET_TCP_ERR_NONE_AVAIL Resources NOT available. * NET_CONN_ERR_INVALID_FAMILY Invalid connection family. * NET_CONN_ERR_INVALID_ADDR Invalid TCP connection address. * NET_CONN_ERR_INVALID_ADDR_LEN Invalid TCP connection address length. *$PAGE* * Return(s) : none. * * Caller(s) : NetTCP_RxPktConnHandler(). * * Note(s) : (2) TCP connections in the FIN-WAIT-1 state are handled as follows : * * (a) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check Sequence Number' * states that in the "SYN-RECEIVED, ESTABLISHED STATE, FIN-WAIT-1 STATE, FIN-WAIT-2 * STATE, CLOSE-WAIT STATE, CLOSING STATE, LAST-ACK STATE, TIME-WAIT STATE" that ... * * (1) "Segments are processed in sequence ... processing is done in SEG.SEQ order." * * (2) "Initial tests on arrival are used to discard old duplicates." * * (3) "If a segment's contents straddle the boundary between old and new, only the * new parts should be processed." * * (4) (A) "If an incoming segment is not acceptable," ... * * (B) "an acknowledgment should be sent in reply" ... * * (C) "(unless the RST bit is set, if so drop the segment)". * * See also Notes #2b2Aa & #2b2B. * * See also 'NetTCP_RxPktConnIsValidSeq() Note #1d'. * * (b) (1) (A) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check RST Bit' states * that in the "ESTABLISHED, FIN-WAIT-1, FIN-WAIT-2, CLOSE-WAIT" states that "if the * RST bit is set then, any outstanding RECEIVEs and SEND[s] should receive 'reset' * responses. All segment queues should be flushed. Users should also receive an * unsolicited general 'connection reset' signal[, and] enter the CLOSED state". * * (B) RFC #793, Section 3.4 'Establishing a Connection : Reset Processing' reiterates * that "if the receiver ... of a RST ... was in any other state [other than LISTEN * or SYN-RECEIVED], it aborts the connection and advises the user and goes to the * CLOSED state". * * (2) (A) RFC Draft-IETF-TCPm-TCPSecure #00, Section 2.2 amends the "handling of a segment * with the RST bit when in a synchronized state" to "provide some protection against * ... blind reset attack[s] using the RST bit" : * * (a) "If the RST bit is set and the sequence number is outside the expected * window, silently drop the segment." * * (b) "If the RST bit is exactly the next expected sequence number [sic], reset * the connection"; it is assumed that this should read "if the RST bit is * set and the sequence number is exactly the next expected sequence number, * reset the connection." * * (c) "If the RST bit is set and the sequence number does not exactly match * the next expected sequence value, yet is within the acceptable window * (RCV.NXT <= SEG.SEQ < RCV.NXT+RCV.WND) send an acknowledgment." * * (B) ???? In addition, RFC Draft-IETF-TCPm-TCPSecure #00 does NOT provide a precedence * priority for handling TCP segments received with BOTH the RST & SYN bits set. * * ???? Therefore, since it does NOT seem reasonable to reset a TCP connection * due to a TCP segment that also attempted to synchronize the TCP connection, * it is assumed that the amended handling of the SYN bit should take precedence * over the amended handling of the RST bit. * * See also Note #2c2. * * (c) (1) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check SYN Bit' * states that in the "SYN-RECEIVED [STATE], ESTABLISHED STATE, FIN-WAIT STATE-1, * FIN-WAIT STATE-2, CLOSE-WAIT STATE, CLOSING STATE, LAST-ACK STATE, TIME-WAIT * STATE" that ... * * (A) "If the SYN is in the window it is an error, send a reset, any outstanding * RECEIVEs and SEND[s] should receive 'reset' responses, all segment queues * should be flushed, the user should also receive an unsolicited general * 'connection reset' signal[, and] enter the CLOSED state." * * (B) But "if the SYN is not in the window this step would not have been reached * and an ack would have been sent". * * (2) HOWEVER, RFC Draft-IETF-TCPm-TCPSecure #00, Section 3.2 amends the "handling * of a segment with the SYN bit set in the synchronized state ... [by] handling * ... the SYN bit" as follows : * * (a) "If the SYN bit is set and the sequence number is outside the * expected window, send an ACK back to the peer." * * (b) "If the SYN bit is set and the sequence number is an exact * match to the next expected sequence (RCV.NXT == SEG.SEQ) * then send an ACK segment ... but ... subtract one from * value being acknowledged." * * (c) "If the SYN bit is set and the sequence number is acceptable, * i.e.: (RCV.NXT <= SEG.SEQ <= RCV.NXT+RCV.WND) then send an * ACK segment." *$PAGE* * (d) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check ACK Field' * states that ... : * * (1) "If the ACK bit is off drop the segment." * * (2) "If the ACK bit is on" ... * * (A) And in the "ESTABLISHED STATE" ... * * (1) "If SND.UNA < SEG.ACK <= SND.NXT then" ... * * (a) "set SND.UNA <- SEG.ACK." * * (b) "Any segments on the retransmission queue which are thereby entirely * acknowledged are removed." * * (c) "the send window should be updated" : * * (1) (A) (1) "If ((SND.WL1 < SEG.SEQ) or" ... * * (2) (a) "(SND.WL1 = SEG.SEQ and" ... * (b) "SND.WL2 <= SEG.ACK))," ... * * (B) (1) "set SND.WND <- SEG.WND," ... * (2) "set SND.WL1 <- SEG.SEQ," ... * (3) "set SND.WL2 <- SEG.ACK." * * (2) "Note that SND.WND is an offset from SND.UNA, that SND.WL1 records the * sequence number of the last segment used to update SND.WND, and that * SND.WL2 records the acknowledgment number of the last segment used to * update SND.WND. The check here preveents using old segments to update * the window." * * (2) (a) "If the ACK is a duplicate (SEG.ACK <= SND.UNA), it can be ignored." * * (b) RFC #1122, Section 4.2.2.20.(g) amends the transmit window update criteria * for the segment's acknowledgement to include SND.UNA : "The window should * updated if SND.UNA <= SEG.ACK <= SND.NXT." * * See also 'NetTCP_RxPktConnHandlerTxWinRemote() Note #1b2'. * * (3) "If the ACK acks something not yet sent (SEG.ACK > SND.NXT) then send an ACK * [and] drop the segment." * * (B) (1) For the "FIN-WAIT-1 STATE" : * * (a) "in addition to the processing for the ESTABLISHED state" ... * (b) "if our FIN is now acknowledged then" ... * (1) "enter FIN-WAIT-2 [state] and continue processing in that state." * * (2) (a) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check ACK Field : * FIN-WAIT-2 STATE' states that "if the retransmission queue is empty, the user's * CLOSE can be acknowledged". * * (b) However, a TCP connection transitions into the FIN-WAIT-2 state if & only if * the TCP connection's close request was previously acknowledged while in the * FIN-WAIT-1 state. Thus, the TCP connection's re-transmit queue should * already be empty prior to entering the FIN-WAIT-2 state. * * ???? Therefore, a TCP connection in the FIN-WAIT-1 state should : * * (1) Signal the application layer that "the user's close [is] acknowledged" * whenever its re-transmit queue becomes empty. * * (2) Close all unused timers (see also Note #2f5A1b). *$PAGE* * (e) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Process Segment Text' * states that for the "ESTABLISHED STATE, FIN-WAIT-1 STATE, FIN-WAIT-2 STATE ... it * is possible to deliver segment text to user RECEIVE buffers" : * * (1) "If the segment ... carries [a] PUSH flag, then the user is informed [and] the * buffer is returned." * * (2) (A) (1) "When the TCP takes responsibility for delivering the data to the user it * must also acknowledge the receipt of the data." * * (2) "This acknowledgment should be piggybacked on a segment being transmitted * if possible without incurring undue delay." * * (B) "Once the TCP takes responsibility for the data" ... * * (1) "it advances RCV.NXT over the data accepted," ... * * (2) "adjusts RCV.WND as appropriate to the current buffer availability" ... * * (3) (a) "The total of RCV.NXT and RCV.WND should not be reduced." * * (b) RFC #793, Section 3.7 'Data Communication : Managing the Window' & * RFC #1122, Section 4.2.2.16 confirm that "a TCP receiver SHOULD NOT * shrink the window"; i.e. "advertise a much smaller window without * having accepted that much data". * * (f) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check FIN Bit' states * that "if the FIN bit is set" ... * * (1) "signal the user 'connection closing' and return any pending RECEIVEs with * same message," ... * * (2) "advance RCV.NXT over the FIN," ... * * (3) "send an acknowledgment for the FIN" ... * * (4) "FIN implies PUSH for any segment text not yet delivered to the user" ... * * (5) For the "FIN-WAIT-1 STATE" : * * (A) "If our FIN has been ACKed (perhaps in this segment), then" ... * * (1) "enter TIME-WAIT [state]" ... * (a) "start the time-wait timer, ... * (1) A TCP connection should be closed WITHOUT fault following a TCP * connection TIME-WAIT timeout. * * (b) "turn off the other timers;" ... * * (2) However, it is possible that some closing received data from the remote * host is available but has NOT yet been received by the application layer. * * (a) Therefore, if the application receive queue is closed, then enter the * TIME-WAIT state. * * (b) (1) But if the application receive queue is NOT closed, then enter the * connection-closing-data-available state to allow the application * layer to receive the remaining receive data. * * (2) (A) To satisfy the required time-wait timeout of two maximum segment * lifetimes (see Note #2f5A1a), the time-wait timeout is intially * used to provide the application layer sufficient time to receive * the closing received data. * * (B) If after the time-wait timeout expires, the application receive * queue is still not empty, the user connection timeout is used * to provide the application layer additional time to receive the * closing received data. * * See also Note #3. * * (B) "otherwise, enter the CLOSING state." *$PAGE* * (3) (a) RFC #793, Section 3.9 'Event Processing : USER TIMEOUT : USER TIMEOUT' states that * "for any state if the user timeout expires, flush all queues, signal the user * 'error : connection aborted due to user timeout' ... [and] enter the CLOSED state". * * (b) However, NO RFC specifies or suggests any mechanism to implement/handle user timeouts. * * ???? Therefore, it is assumed that ANY TCP connection that receives a valid TCP data * or control segment should reset its user connection timer. * * (4) Since the mechanisms of TCP connection close are independent of the application layer * close; any external application layer close error(s) are ignored. ********************************************************************************************************* */ static void NetTCP_RxPktConnHandlerFinWait1 (NET_TCP_CONN *pconn, NET_BUF *pbuf, NET_BUF_HDR *pbuf_hdr, NET_ERR *perr) { NET_TCP_SEQ_CODE seq_code; NET_TCP_ACK_CODE ack_code; NET_TCP_RESET_CODE reset_code; CPU_BOOLEAN data_avail; NET_TCP_TIMEOUT_SEC timeout_sec; NET_TMR_TICK timeout_tick; CPU_FNCT_PTR timeout_fnct; NET_TCP_FREE_CODE free_code; NET_ERR err; NET_ERR err_rtn; /*$PAGE*/ /* -------------- VALIDATE RX'D TCP PKT --------------- */ /* Chk for rx'd fin/close. */ if (pbuf_hdr->TCP_SegClose == DEF_YES) { /* If fin/close avail, update seg lens. */ pbuf_hdr->TCP_SegLenInit += NET_TCP_SEG_LEN_CLOSE; pbuf_hdr->TCP_SegLen += NET_TCP_SEG_LEN_CLOSE; } /* Chk rx'd seq nbr. */ seq_code = NetTCP_RxPktConnIsValidSeq(pconn, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return; } switch (seq_code) { case NET_TCP_CONN_RX_SEQ_VALID: break; case NET_TCP_CONN_RX_SEQ_SYNC: /* If invalid sync rx'd, ... */ case NET_TCP_CONN_RX_SEQ_SYNC_INVALID: /* ... tx TCP conn ack (see Notes #2c2 & #2b2B). */ NetTCP_TxConnAck(pconn, pbuf_hdr, NET_TCP_CONN_TX_ACK_FAULT, NET_TCP_CONN_CLOSE_ALL, &err); *perr = NET_TCP_ERR_CONN_SEQ_SYNC_INVALID; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_RX_SEQ_NONE: case NET_TCP_CONN_RX_SEQ_INVALID: /* If invalid seq rx'd (see Note #2a4A), ... */ default: if (pbuf_hdr->TCP_SegReset != DEF_YES) { /* ... & reset NOT rx'd (see Note #2a4C), ... */ /* ... tx TCP conn ack (see Note #2a4B). */ NetTCP_TxConnAck(pconn, pbuf_hdr, NET_TCP_CONN_TX_ACK_FAULT, NET_TCP_CONN_CLOSE_ALL, &err); } *perr = NET_TCP_ERR_CONN_SEQ_INVALID; return; /* Prevent 'break NOT reachable' compiler warning. */ } /* Chk for rx'd reset. */ reset_code = NetTCP_RxPktConnIsValidReset(pconn, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return; } switch (reset_code) { case NET_TCP_CONN_RX_RESET_NONE: break; case NET_TCP_CONN_RX_RESET_VALID: /* If valid reset rx'd, ... */ /* ... close TCP conn (see Note #2b1A). */ NetTCP_ConnClose(pconn, pbuf_hdr, pconn->ConnCloseAppFlag, NET_TCP_CONN_CLOSE_ALL); *perr = NET_TCP_ERR_CONN_RESET_VALID; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_RX_RESET_INVALID: /* If invalid reset rx'd, ... */ default: /* ... tx TCP conn ack (see Note #2b2Ac). */ NetTCP_TxConnAck(pconn, pbuf_hdr, NET_TCP_CONN_TX_ACK_FAULT, NET_TCP_CONN_CLOSE_ALL, &err); *perr = NET_TCP_ERR_CONN_RESET_INVALID; return; /* Prevent 'break NOT reachable' compiler warning. */ } /* Chk for rx'd ack. */ ack_code = NetTCP_RxPktConnIsValidAck(pconn, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return; } switch (ack_code) { case NET_TCP_CONN_RX_ACK_VALID: case NET_TCP_CONN_RX_ACK_DUP: case NET_TCP_CONN_RX_ACK_PREV: break; case NET_TCP_CONN_RX_ACK_NONE: /* If NO ack rx'd, ... */ *perr = NET_TCP_ERR_CONN_ACK_NONE; /* ... ignore TCP pkt (see Note #2d1). */ return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_RX_ACK_INVALID: /* If invalid ack rx'd, ... */ default: /* ... tx TCP conn ack (see Note #2d2A3). */ NetTCP_TxConnAck(pconn, pbuf_hdr, NET_TCP_CONN_TX_ACK_FAULT, NET_TCP_CONN_CLOSE_ALL, &err); *perr = NET_TCP_ERR_CONN_ACK_INVALID; return; /* Prevent 'break NOT reachable' compiler warning. */ } /*$PAGE*/ /* ----------------- HANDLE RX'D SEG ------------------ */ NetTCP_RxPktConnHandlerSeg(pconn, ack_code, pbuf, pbuf_hdr, &err_rtn); switch (err_rtn) { case NET_TCP_ERR_CONN_DATA_NONE: case NET_TCP_ERR_CONN_DATA_VALID: break; case NET_TCP_ERR_CONN_DATA_DUP: /* ???? Allow dup data to update TCP conn? */ *perr = err_rtn; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_ERR_INIT_INCOMPLETE: case NET_TCP_ERR_CONN_NOT_USED: case NET_TCP_ERR_CONN_FAIL: case NET_TCP_ERR_CONN_ACK_INVALID: case NET_TCP_ERR_CONN_DATA_INVALID: case NET_TCP_ERR_INVALID_CONN_STATE: case NET_TCP_ERR_INVALID_CONN_OP: case NET_TCP_ERR_INVALID_LEN_SEG: case NET_TCP_ERR_RX_Q_FULL: case NET_TCP_ERR_RX_Q_SIGNAL_FAULT: case NET_TCP_ERR_TX: case NET_TCP_ERR_TX_Q_SIGNAL_FAULT: case NET_TCP_ERR_RE_TX_SEG_TH: default: NetTCP_ConnClose(pconn, pbuf_hdr, pconn->ConnCloseAppFlag, NET_TCP_CONN_CLOSE_ALL); *perr = err_rtn; return; /* Prevent 'break NOT reachable' compiler warning. */ } /* ----------------- UPDATE TCP CONN ------------------ */ timeout_fnct = (CPU_FNCT_PTR)0; if (pconn->TxQ_State == NET_TCP_TX_Q_STATE_CONN_CLOSED) { /* If local conn close ack'd (see Note #2f5A); ... */ /* Closing data avail for half-closed conns ONLY. */ data_avail = ((pconn->ConnCloseCode != NET_CONN_CLOSE_HALF ) || ((pconn->RxQ_State == NET_TCP_RX_Q_STATE_CONN_CLOSED) && (pconn->RxQ_App_Head == (NET_BUF *)0))) ? DEF_NO : DEF_YES; /* ... signal app conn close (see Note #2d2B2b1); ... */ NetTCP_RxPktConnHandlerSignalClose(pconn, data_avail, &err); switch (err) { case NET_TCP_ERR_NONE: case NET_TCP_ERR_CONN_FAIL: /* Ignore any app conn close err(s) [see Note #4]. */ break; case NET_TCP_ERR_CONN_NOT_USED: case NET_TCP_ERR_INVALID_CONN_ID: case NET_TCP_ERR_INVALID_CONN_STATE: case NET_TCP_ERR_INVALID_CONN_OP: case NET_CONN_ERR_INVALID_FAMILY: case NET_CONN_ERR_INVALID_CONN: case NET_CONN_ERR_NOT_USED: default: NetTCP_ConnClose(pconn, pbuf_hdr, DEF_YES, NET_TCP_CONN_CLOSE_ALL); *perr = err; return; /* Prevent 'break NOT reachable' compiler warning. */ } /*$PAGE*/ if (pbuf_hdr->TCP_SegClose == DEF_YES) { /* ... & if fin/close rx'd ... */ if (data_avail != DEF_YES) { /* ... & NO app data avail, ... */ /* ... chng to time-wait state (see Note #2f5A2a); ... */ pconn->ConnState = NET_TCP_CONN_STATE_TIME_WAIT; pconn->ConnCloseTimeoutFaultFlag = DEF_NO; /* ... clr close timeout fault (see Note #2f5A1a1);... */ timeout_fnct = (CPU_FNCT_PTR)NetTCP_ConnCloseTimeout; } else { /* ... else chng to conn-closing-data-avail state ... */ /* ... (see Note #2f5A2b1);... */ pconn->ConnState = NET_TCP_CONN_STATE_CLOSING_DATA_AVAIL; timeout_fnct = (CPU_FNCT_PTR)NetTCP_ConnClosingTimeoutDataAvail; } /* ... & start time-wait tmr (see Notes #2f5A1a & ... */ /* ... #2f5A2b2A). */ timeout_sec = pconn->TimeoutMaxSeg_sec * NET_TCP_CONN_TIMEOUT_MAX_SEG_SCALAR; } else { pconn->ConnState = NET_TCP_CONN_STATE_FIN_WAIT_2; /* ... else chng to fin-wait-2 state (see Note #2d2B1b1)*/ timeout_sec = pconn->TimeoutConn_sec; /* ... & start conn close tmr (see Note #3b). */ timeout_fnct = (CPU_FNCT_PTR)NetTCP_ConnCloseTimeout; } } else { if (pbuf_hdr->TCP_SegClose == DEF_YES) { /* Else if fin/close rx'd, ... */ pconn->ConnState = NET_TCP_CONN_STATE_CLOSING; /* ... chng to closing state (see Note #2f5B) ... */ timeout_sec = pconn->TimeoutConn_sec; /* ... & start conn close tmr (see Note #3b). */ timeout_fnct = (CPU_FNCT_PTR)NetTCP_ConnCloseTimeout; } } /* ------------------- UPDATE TMRs -------------------- */ switch (pconn->ConnState) { case NET_TCP_CONN_STATE_TIME_WAIT: /* If in time-wait (see Note #2f5A1) ... */ case NET_TCP_CONN_STATE_FIN_WAIT_2: case NET_TCP_CONN_STATE_CLOSING_DATA_AVAIL: /* ... or closing state(s) [see Note #2d2B2b2], ... */ free_code = NET_TCP_CONN_FREE_TMR_ALL; /* ... free all TCP conn tmrs (see Note #2f5A1b). */ DEF_BIT_CLR(free_code, NET_TCP_CONN_FREE_TMR_TIMEOUT); NetTCP_ConnFreeTmr(pconn, free_code); break; } if (timeout_fnct != (CPU_FNCT_PTR)0) { timeout_tick = (NET_TMR_TICK)timeout_sec * NET_TMR_TIME_TICK_PER_SEC; if (pconn->TimeoutTmr != (NET_TMR *)0) { NetTmr_Set((NET_TMR *) pconn->TimeoutTmr, (CPU_FNCT_PTR) timeout_fnct, (NET_TMR_TICK) timeout_tick, (NET_ERR *)&err); } else { pconn->TimeoutTmr = NetTmr_Get((void *) pconn, (CPU_FNCT_PTR) timeout_fnct, (NET_TMR_TICK) timeout_tick, (CPU_INT16U ) NET_TMR_FLAG_NONE, (NET_ERR *)&err); } if ( err != NET_TMR_ERR_NONE) { NetTCP_ConnClose(pconn, pbuf_hdr, pconn->ConnCloseAppFlag, NET_TCP_CONN_CLOSE_ALL); *perr = NET_TCP_ERR_CONN_FAIL; return; } } /*$PAGE*/ /* --------------- TX TCP CONN ACK/DATA --------------- */ ack_code = ((pbuf_hdr->TCP_SegAckTxReqCode == NET_TCP_CONN_TX_ACK_IMMED) || (pbuf_hdr->TCP_SegClose == DEF_YES)) /* See Note #2f3. */ ? NET_TCP_CONN_TX_ACK_IMMED : NET_TCP_CONN_TX_ACK; if ((pconn->ConnState == NET_TCP_CONN_STATE_FIN_WAIT_1) || /* If TCP conn tx Q NOT closed, ... */ (pconn->ConnState == NET_TCP_CONN_STATE_CLOSING )) { /* ... tx ack & any tx data (see Note #1c). */ NetTCP_TxConnTxQ(pconn, pbuf_hdr, ack_code, DEF_NO, NET_TCP_CONN_CLOSE_ALL, perr); switch (*perr) { case NET_TCP_ERR_NONE: case NET_TCP_ERR_CONN_ACK_NONE: case NET_TCP_ERR_CONN_ACK_INVALID: case NET_TCP_ERR_CONN_ACK_DLYD: case NET_TCP_ERR_CONN_ACK_PREVLY_TXD: case NET_ERR_TX: /* Ignore transitory tx err(s). */ break; case NET_ERR_INIT_INCOMPLETE: case NET_TCP_ERR_CONN_NOT_USED: case NET_TCP_ERR_CONN_CLOSE: case NET_TCP_ERR_CONN_FAULT: case NET_TCP_ERR_CONN_FAIL: case NET_TCP_ERR_INVALID_CONN_STATE: case NET_TCP_ERR_INVALID_CONN_OP: case NET_TCP_ERR_INVALID_LEN_SEG: case NET_TCP_ERR_NONE_AVAIL: case NET_TCP_ERR_TX: case NET_CONN_ERR_INVALID_FAMILY: case NET_CONN_ERR_INVALID_ADDR: case NET_CONN_ERR_INVALID_ADDR_LEN: default: return; /* Prevent 'break NOT reachable' compiler warning. */ } } else { /* Else tx TCP conn ack (see Note #2f3). */ NetTCP_TxConnAck(pconn, pbuf_hdr, ack_code, NET_TCP_CONN_CLOSE_ALL, perr); switch (*perr) { case NET_TCP_ERR_NONE: case NET_TCP_ERR_CONN_ACK_NONE: case NET_TCP_ERR_CONN_ACK_INVALID: case NET_TCP_ERR_CONN_ACK_DLYD: case NET_TCP_ERR_CONN_ACK_PREVLY_TXD: break; case NET_TCP_ERR_CONN_NOT_USED: case NET_TCP_ERR_CONN_FAULT: case NET_TCP_ERR_INVALID_CONN_STATE: case NET_TCP_ERR_INVALID_CONN_OP: case NET_TCP_ERR_INVALID_LEN_SEG: case NET_TCP_ERR_NONE_AVAIL: default: return; /* Prevent 'break NOT reachable' compiler warning. */ } } *perr = err_rtn; } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_RxPktConnHandlerFinWait2() * * Description : (1) Handle TCP connection in FIN-WAIT-2 state : * * (a) Validate received TCP packet for current TCP connection state : * * (1) Sequence Number (SEQ) See Note #2a * (2) Reset (RST) See Note #2b * (3) Synchronization (SYN) See Note #2c * (4) Acknowledgement (ACK) See Note #2d * (5) Finish/Close (FIN) See Note #2f * * (b) Update TCP connection : * (1) Handle received TCP segment See Notes #2d & #2e * (2) Update TCP connection state : See Notes #2f5A & #2f5B2a * (A) Configure TCP connection timeout value * (B) Configure TCP connection timeout function * (3) Update TCP connection timer(s) See Notes #2f5A, #2f5B2b, & #3 * * (c) Transmit TCP connection acknowledgement See Notes #2e2A & #2f3 * * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_RxPktConnHandler(). * * pbuf Pointer to network buffer that received TCP packet. * ---- Argument checked in NetTCP_Rx(). * * pbuf_hdr Pointer to network buffer header that received TCP packet. * -------- Argument validated in NetTCP_Rx(). * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_CONN_SEQ_SYNC_INVALID Received segment's synchronization is * NOT valid for current TCP connection. * NET_TCP_ERR_CONN_SEQ_INVALID Received segment's sequence number is * NOT valid for current TCP connection. * NET_TCP_ERR_CONN_ACK_NONE Received segment's acknowledgement is * NOT available. * NET_TCP_ERR_CONN_ACK_INVALID Received segment's acknowledgement number is * NOT valid for current TCP connection. * NET_TCP_ERR_CONN_RESET_VALID Received segment's reset flag is valid * for current TCP connection; i.e. reset * the TCP connection. * NET_TCP_ERR_CONN_RESET_INVALID Received segment's reset flag is NOT valid * for current TCP connection. * * NET_TCP_ERR_CONN_FAIL TCP connection operation(s) failed. * * -- RETURNED BY NetTCP_RxPktConnIsValidSeq() : -- * -- RETURNED BY NetTCP_RxPktConnIsValidAck() : -- * - RETURNED BY NetTCP_RxPktConnIsValidReset() : - * NET_TCP_ERR_CONN_NOT_USED TCP connection NOT currently used. * NET_TCP_ERR_INVALID_CONN_STATE Invalid TCP connection state. * NET_TCP_ERR_INVALID_LEN_SEG Invalid TCP sequence-segment length. * * -- RETURNED BY NetTCP_RxPktConnHandlerSeg() : -- * NET_TCP_ERR_CONN_DATA_NONE Received packet successfully handled; but NO * data to queue to receive queue(s). * NET_TCP_ERR_CONN_DATA_VALID Received packet successfully handled & valid * data queued for processing. * NET_TCP_ERR_CONN_DATA_INVALID Received packet contains invalid segment * data; NOT queued to receive queue(s). * NET_TCP_ERR_CONN_DATA_DUP Received packet contains duplicate segment * data; NOT queued to receive queue(s). * * NET_ERR_INIT_INCOMPLETE Network initialization NOT complete. * * NET_TCP_ERR_INVALID_CONN_OP Invalid TCP connection operation. * * NET_TCP_ERR_RX_Q_FULL TCP connection receive queue full. * NET_TCP_ERR_RX_Q_SIGNAL_FAULT TCP connection receive queue signal fault. * * ------- RETURNED BY NetTCP_TxConnAck() : ------- * NET_TCP_ERR_CONN_FAULT TCP connection fault; connection(s) aborted. * NET_TCP_ERR_NONE_AVAIL Resources NOT available. * * Return(s) : none. * * Caller(s) : NetTCP_RxPktConnHandler(). *$PAGE* * Note(s) : (2) TCP connections in the FIN-WAIT-2 state are handled as follows : * * (a) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check Sequence Number' * states that in the "SYN-RECEIVED, ESTABLISHED STATE, FIN-WAIT-1 STATE, FIN-WAIT-2 * STATE, CLOSE-WAIT STATE, CLOSING STATE, LAST-ACK STATE, TIME-WAIT STATE" that ... * * (1) "Segments are processed in sequence ... processing is done in SEG.SEQ order." * * (2) "Initial tests on arrival are used to discard old duplicates." * * (3) "If a segment's contents straddle the boundary between old and new, only the * new parts should be processed." * * (4) (A) "If an incoming segment is not acceptable," ... * * (B) "an acknowledgment should be sent in reply" ... * * (C) "(unless the RST bit is set, if so drop the segment)". * * See also Notes #2b2Aa & #2b2B. * * See also 'NetTCP_RxPktConnIsValidSeq() Note #1d'. * * (b) (1) (A) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check RST Bit' states * that in the "ESTABLISHED, FIN-WAIT-1, FIN-WAIT-2, CLOSE-WAIT" states that "if the * RST bit is set then, any outstanding RECEIVEs and SEND[s] should receive 'reset' * responses. All segment queues should be flushed. Users should also receive an * unsolicited general 'connection reset' signal[, and] enter the CLOSED state". * * (B) RFC #793, Section 3.4 'Establishing a Connection : Reset Processing' reiterates * that "if the receiver ... of a RST ... was in any other state [other than LISTEN * or SYN-RECEIVED], it aborts the connection and advises the user and goes to the * CLOSED state". * * (2) (A) RFC Draft-IETF-TCPm-TCPSecure #00, Section 2.2 amends the "handling of a segment * with the RST bit when in a synchronized state" to "provide some protection against * ... blind reset attack[s] using the RST bit" : * * (a) "If the RST bit is set and the sequence number is outside the expected * window, silently drop the segment." * * (b) "If the RST bit is exactly the next expected sequence number [sic], reset * the connection"; it is assumed that this should read "if the RST bit is * set and the sequence number is exactly the next expected sequence number, * reset the connection." * * (c) "If the RST bit is set and the sequence number does not exactly match * the next expected sequence value, yet is within the acceptable window * (RCV.NXT <= SEG.SEQ < RCV.NXT+RCV.WND) send an acknowledgment." * * (B) ???? In addition, RFC Draft-IETF-TCPm-TCPSecure #00 does NOT provide a precedence * priority for handling TCP segments received with BOTH the RST & SYN bits set. * * ???? Therefore, since it does NOT seem reasonable to reset a TCP connection * due to a TCP segment that also attempted to synchronize the TCP connection, * it is assumed that the amended handling of the SYN bit should take precedence * over the amended handling of the RST bit. * * See also Note #2c2. * * (c) (1) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check SYN Bit' * states that in the "SYN-RECEIVED [STATE], ESTABLISHED STATE, FIN-WAIT STATE-1, * FIN-WAIT STATE-2, CLOSE-WAIT STATE, CLOSING STATE, LAST-ACK STATE, TIME-WAIT * STATE" that ... * * (A) "If the SYN is in the window it is an error, send a reset, any outstanding * RECEIVEs and SEND[s] should receive 'reset' responses, all segment queues * should be flushed, the user should also receive an unsolicited general * 'connection reset' signal[, and] enter the CLOSED state." * * (B) But "if the SYN is not in the window this step would not have been reached * and an ack would have been sent". * * (2) HOWEVER, RFC Draft-IETF-TCPm-TCPSecure #00, Section 3.2 amends the "handling * of a segment with the SYN bit set in the synchronized state ... [by] handling * ... the SYN bit" as follows : * * (a) "If the SYN bit is set and the sequence number is outside the * expected window, send an ACK back to the peer." * * (b) "If the SYN bit is set and the sequence number is an exact * match to the next expected sequence (RCV.NXT == SEG.SEQ) * then send an ACK segment ... but ... subtract one from * value being acknowledged." * * (c) "If the SYN bit is set and the sequence number is acceptable, * i.e.: (RCV.NXT <= SEG.SEQ <= RCV.NXT+RCV.WND) then send an * ACK segment." *$PAGE* * (d) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check ACK Field' * states that ... : * * (1) "If the ACK bit is off drop the segment." * * (2) "If the ACK bit is on" ... * * (A) And in the "ESTABLISHED STATE" ... * * (1) "If SND.UNA < SEG.ACK <= SND.NXT then" ... * * (a) "set SND.UNA <- SEG.ACK." * * (b) "Any segments on the retransmission queue which are thereby entirely * acknowledged are removed." * * (c) "the send window should be updated" : * * (1) (A) (1) "If ((SND.WL1 < SEG.SEQ) or" ... * * (2) (a) "(SND.WL1 = SEG.SEQ and" ... * (b) "SND.WL2 <= SEG.ACK))," ... * * (B) (1) "set SND.WND <- SEG.WND," ... * (2) "set SND.WL1 <- SEG.SEQ," ... * (3) "set SND.WL2 <- SEG.ACK." * * (2) "Note that SND.WND is an offset from SND.UNA, that SND.WL1 records the * sequence number of the last segment used to update SND.WND, and that * SND.WL2 records the acknowledgment number of the last segment used to * update SND.WND. The check here preveents using old segments to update * the window." * * (2) (a) "If the ACK is a duplicate (SEG.ACK <= SND.UNA), it can be ignored." * * (b) RFC #1122, Section 4.2.2.20.(g) amends the transmit window update criteria * for the segment's acknowledgement to include SND.UNA : "The window should * updated if SND.UNA <= SEG.ACK <= SND.NXT." * * See also 'NetTCP_RxPktConnHandlerTxWinRemote() Note #1b2'. * * (3) "If the ACK acks something not yet sent (SEG.ACK > SND.NXT) then send an ACK * [and] drop the segment." * * (B) For the "FIN-WAIT-2 STATE" : * * (1) "in addition to the processing for the ESTABLISHED state" ... * (2) "if the retransmission queue is empty," ... * * (a) (1) "the user's CLOSE can be acknowledged." * (2) However, a TCP connection transitions into the FIN-WAIT-2 state if & * only if the TCP connection's close request was previously acknowledged * while in the FIN-WAIT-1 state. Thus, the TCP connection's re-transmit * queue should already be empty prior to entering the FIN-WAIT-2 state. * * See also 'NetTCP_RxPktConnHandlerFinWait1() Note #2d2B2'. * * (e) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Process Segment Text' * states that for the "ESTABLISHED STATE, FIN-WAIT-1 STATE, FIN-WAIT-2 STATE ... it * is possible to deliver segment text to user RECEIVE buffers" : * * (1) "If the segment ... carries [a] PUSH flag, then the user is informed [and] the * buffer is returned." * * (2) (A) "When the TCP takes responsibility for delivering the data to the user it * must also acknowledge the receipt of the data." * * (B) "Once the TCP takes responsibility for the data" ... * * (1) "it advances RCV.NXT over the data accepted," ... * * (2) "adjusts RCV.WND as appropriate to the current buffer availability" ... * * (3) (a) "The total of RCV.NXT and RCV.WND should not be reduced." * * (b) RFC #793, Section 3.7 'Data Communication : Managing the Window' & * RFC #1122, Section 4.2.2.16 confirm that "a TCP receiver SHOULD NOT * shrink the window"; i.e. "advertise a much smaller window without * having accepted that much data". *$PAGE* * (f) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check FIN Bit' states * that "if the FIN bit is set" ... * * (1) "signal the user 'connection closing' and return any pending RECEIVEs with * same message," ... * * (2) "advance RCV.NXT over the FIN," ... * * (3) "send an acknowledgment for the FIN" ... * * (4) "FIN implies PUSH for any segment text not yet delivered to the user" ... * * (5) For the "FIN-WAIT-2 STATE" : * * (A) "Enter the TIME-WAIT state" ... * (1) "Start the time-wait timer," ... * (a) A TCP connection should be closed WITHOUT fault following a TCP * connection TIME-WAIT timeout. * * (2) "turn off the other timers." * * (B) However, it is possible that some closing received data from the remote * host is available but has NOT yet been received by the application layer. * * (1) Therefore, if the application receive queue is closed, then enter the * TIME-WAIT state. * * (2) (a) But if the application receive queue is NOT closed, then enter the * connection-closing-data-available state to allow the application * layer to receive the remaining receive data. * * (b) (1) To satisfy the required time-wait timeout of two maximum segment * lifetimes (see Note #2f5A1a), the time-wait timeout is intially * used to provide the application layer sufficient time to receive * the closing received data. * * (2) If after the time-wait timeout expires, the application receive * queue is still not empty, the user connection timeout is used * to provide the application layer additional time to receive the * closing received data. * * See also Note #3. * * (3) (a) RFC #793, Section 3.9 'Event Processing : USER TIMEOUT : USER TIMEOUT' states that * "for any state if the user timeout expires, flush all queues, signal the user * 'error : connection aborted due to user timeout' ... [and] enter the CLOSED state". * * (b) However, NO RFC specifies or suggests any mechanism to implement/handle user timeouts. * * ???? Therefore, it is assumed that ANY TCP connection that receives a valid TCP data * or control segment should reset its user connection timer. ********************************************************************************************************* */ static void NetTCP_RxPktConnHandlerFinWait2 (NET_TCP_CONN *pconn, NET_BUF *pbuf, NET_BUF_HDR *pbuf_hdr, NET_ERR *perr) { NET_TCP_SEQ_CODE seq_code; NET_TCP_ACK_CODE ack_code; NET_TCP_RESET_CODE reset_code; CPU_BOOLEAN data_avail; NET_TCP_TIMEOUT_SEC timeout_sec; NET_TMR_TICK timeout_tick; CPU_FNCT_PTR timeout_fnct; NET_TCP_FREE_CODE free_code; NET_ERR err; NET_ERR err_rtn; /*$PAGE*/ /* -------------- VALIDATE RX'D TCP PKT --------------- */ /* Chk for rx'd fin/close. */ if (pbuf_hdr->TCP_SegClose == DEF_YES) { /* If fin/close avail, update seg lens. */ pbuf_hdr->TCP_SegLenInit += NET_TCP_SEG_LEN_CLOSE; pbuf_hdr->TCP_SegLen += NET_TCP_SEG_LEN_CLOSE; } /* Chk rx'd seq nbr. */ seq_code = NetTCP_RxPktConnIsValidSeq(pconn, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return; } switch (seq_code) { case NET_TCP_CONN_RX_SEQ_VALID: break; case NET_TCP_CONN_RX_SEQ_SYNC: /* If invalid sync rx'd, ... */ case NET_TCP_CONN_RX_SEQ_SYNC_INVALID: /* ... tx TCP conn ack (see Notes #2c2 & #2b2B). */ NetTCP_TxConnAck(pconn, pbuf_hdr, NET_TCP_CONN_TX_ACK_FAULT, NET_TCP_CONN_CLOSE_ALL, &err); *perr = NET_TCP_ERR_CONN_SEQ_SYNC_INVALID; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_RX_SEQ_NONE: case NET_TCP_CONN_RX_SEQ_INVALID: /* If invalid seq rx'd (see Note #2a4A), ... */ default: if (pbuf_hdr->TCP_SegReset != DEF_YES) { /* ... & reset NOT rx'd (see Note #2a4C), ... */ /* ... tx TCP conn ack (see Note #2a4B). */ NetTCP_TxConnAck(pconn, pbuf_hdr, NET_TCP_CONN_TX_ACK_FAULT, NET_TCP_CONN_CLOSE_ALL, &err); } *perr = NET_TCP_ERR_CONN_SEQ_INVALID; return; /* Prevent 'break NOT reachable' compiler warning. */ } /* Chk for rx'd reset. */ reset_code = NetTCP_RxPktConnIsValidReset(pconn, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return; } switch (reset_code) { case NET_TCP_CONN_RX_RESET_NONE: break; case NET_TCP_CONN_RX_RESET_VALID: /* If valid reset rx'd, ... */ /* ... close TCP conn (see Note #2b1A). */ NetTCP_ConnClose(pconn, pbuf_hdr, pconn->ConnCloseAppFlag, NET_TCP_CONN_CLOSE_ALL); *perr = NET_TCP_ERR_CONN_RESET_VALID; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_RX_RESET_INVALID: /* If invalid reset rx'd, ... */ default: /* ... tx TCP conn ack (see Note #2b2Ac). */ NetTCP_TxConnAck(pconn, pbuf_hdr, NET_TCP_CONN_TX_ACK_FAULT, NET_TCP_CONN_CLOSE_ALL, &err); *perr = NET_TCP_ERR_CONN_RESET_INVALID; return; /* Prevent 'break NOT reachable' compiler warning. */ } /* Chk for rx'd ack. */ ack_code = NetTCP_RxPktConnIsValidAck(pconn, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return; } switch (ack_code) { case NET_TCP_CONN_RX_ACK_VALID: case NET_TCP_CONN_RX_ACK_DUP: case NET_TCP_CONN_RX_ACK_PREV: break; case NET_TCP_CONN_RX_ACK_NONE: /* If NO ack rx'd, ... */ *perr = NET_TCP_ERR_CONN_ACK_NONE; /* ... ignore TCP pkt (see Note #2d1). */ return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_RX_ACK_INVALID: /* If invalid ack rx'd, ... */ default: /* ... tx TCP conn ack (see Note #2d2A3). */ NetTCP_TxConnAck(pconn, pbuf_hdr, NET_TCP_CONN_TX_ACK_FAULT, NET_TCP_CONN_CLOSE_ALL, &err); *perr = NET_TCP_ERR_CONN_ACK_INVALID; return; /* Prevent 'break NOT reachable' compiler warning. */ } /*$PAGE*/ /* ----------------- HANDLE RX'D SEG ------------------ */ NetTCP_RxPktConnHandlerSeg(pconn, ack_code, pbuf, pbuf_hdr, &err_rtn); switch (err_rtn) { case NET_TCP_ERR_CONN_DATA_NONE: case NET_TCP_ERR_CONN_DATA_VALID: break; case NET_TCP_ERR_CONN_DATA_DUP: /* ???? Allow dup data to update TCP conn? */ *perr = err_rtn; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_ERR_INIT_INCOMPLETE: case NET_TCP_ERR_CONN_NOT_USED: case NET_TCP_ERR_CONN_ACK_INVALID: case NET_TCP_ERR_CONN_DATA_INVALID: case NET_TCP_ERR_INVALID_CONN_STATE: case NET_TCP_ERR_INVALID_CONN_OP: case NET_TCP_ERR_INVALID_LEN_SEG: case NET_TCP_ERR_RX_Q_FULL: case NET_TCP_ERR_RX_Q_SIGNAL_FAULT: default: NetTCP_ConnClose(pconn, pbuf_hdr, pconn->ConnCloseAppFlag, NET_TCP_CONN_CLOSE_ALL); *perr = err_rtn; return; /* Prevent 'break NOT reachable' compiler warning. */ } /* ----------------- UPDATE TCP CONN ------------------ */ if (pbuf_hdr->TCP_SegClose == DEF_YES) { /* If fin/close rx'd, ... */ /* Closing data avail for half-closed conns ONLY. */ data_avail = ((pconn->ConnCloseCode != NET_CONN_CLOSE_HALF ) || ((pconn->RxQ_State == NET_TCP_RX_Q_STATE_CONN_CLOSED) && (pconn->RxQ_App_Head == (NET_BUF *)0))) ? DEF_NO : DEF_YES; if (data_avail != DEF_YES) { /* ... & NO app data avail, ... */ /* ... chng to time-wait state (see Note #2f5B1); ... */ pconn->ConnState = NET_TCP_CONN_STATE_TIME_WAIT; pconn->ConnCloseTimeoutFaultFlag = DEF_NO; /* ... clr close timeout fault (see Note #2f5A1a); ... */ timeout_fnct = (CPU_FNCT_PTR)NetTCP_ConnCloseTimeout; } else { /* ... else chng to conn-closing-data-avail state ... */ /* ... (see Note #2f5B2a); ... */ pconn->ConnState = NET_TCP_CONN_STATE_CLOSING_DATA_AVAIL; timeout_fnct = (CPU_FNCT_PTR)NetTCP_ConnClosingTimeoutDataAvail; } /* ... & start time-wait tmr (see Notes #2f5A1 & ... */ /* ... #2f5B2b1). */ timeout_sec = pconn->TimeoutMaxSeg_sec * NET_TCP_CONN_TIMEOUT_MAX_SEG_SCALAR; } else { timeout_sec = pconn->TimeoutConn_sec; /* Else start conn close tmr (see Note #3b). */ timeout_fnct = (CPU_FNCT_PTR)NetTCP_ConnCloseTimeout; } /*$PAGE*/ /* ------------------- UPDATE TMRs -------------------- */ /* In time-wait or closing state(s) [see Note #2f5A], */ free_code = NET_TCP_CONN_FREE_TMR_ALL; /* ... free all TCP conn tmrs (see Note #2f5A2). */ DEF_BIT_CLR(free_code, NET_TCP_CONN_FREE_TMR_TIMEOUT); NetTCP_ConnFreeTmr(pconn, free_code); timeout_tick = (NET_TMR_TICK)timeout_sec * NET_TMR_TIME_TICK_PER_SEC; if (pconn->TimeoutTmr != (NET_TMR *)0) { NetTmr_Set((NET_TMR *) pconn->TimeoutTmr, (CPU_FNCT_PTR) timeout_fnct, (NET_TMR_TICK) timeout_tick, (NET_ERR *)&err); } else { pconn->TimeoutTmr = NetTmr_Get((void *) pconn, (CPU_FNCT_PTR) timeout_fnct, (NET_TMR_TICK) timeout_tick, (CPU_INT16U ) NET_TMR_FLAG_NONE, (NET_ERR *)&err); } if ( err != NET_TMR_ERR_NONE) { NetTCP_ConnClose(pconn, pbuf_hdr, pconn->ConnCloseAppFlag, NET_TCP_CONN_CLOSE_ALL); *perr = NET_TCP_ERR_CONN_FAIL; return; } /* ----------------- TC TCP CONN ACK ------------------ */ /* Tx TCP conn ack (see Notes #1c). */ NetTCP_TxConnAck(pconn, pbuf_hdr, NET_TCP_CONN_TX_ACK_IMMED, NET_TCP_CONN_CLOSE_ALL, perr); switch (*perr) { case NET_TCP_ERR_NONE: case NET_TCP_ERR_CONN_ACK_NONE: case NET_TCP_ERR_CONN_ACK_INVALID: case NET_TCP_ERR_CONN_ACK_DLYD: case NET_TCP_ERR_CONN_ACK_PREVLY_TXD: break; case NET_TCP_ERR_CONN_NOT_USED: case NET_TCP_ERR_CONN_FAULT: case NET_TCP_ERR_INVALID_CONN_STATE: case NET_TCP_ERR_INVALID_CONN_OP: case NET_TCP_ERR_INVALID_LEN_SEG: case NET_TCP_ERR_NONE_AVAIL: default: return; /* Prevent 'break NOT reachable' compiler warning. */ } *perr = err_rtn; } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_RxPktConnHandlerClosing() * * Description : (1) Handle TCP connection in CLOSING state : * * (a) Validate received TCP packet for current TCP connection state : * * (1) Sequence Number (SEQ) See Note #2a * (2) Reset (RST) See Note #2b * (3) Synchronization (SYN) See Note #2c * (4) Acknowledgement (ACK) See Note #2d * (5) Finish/Close (FIN) See Note #2f * * (b) Update TCP connection : * (1) Handle received TCP segment See Notes #2d & #2e * (2) Update TCP connection state : See Note #2d2B2a1A * (A) Configure TCP connection timeout value * (B) Configure TCP connection timeout function * (3) Update TCP connection timer(s) See Notes #2d2B2a1A, #2d2B2a1B2b, * & #3 * * (c) Transmit TCP connection acknowledgement & data See Notes #2d2B1 & #2f3 * *$PAGE* * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_RxPktConnHandler(). * * pbuf Pointer to network buffer that received TCP packet. * ---- Argument checked in NetTCP_Rx(). * * pbuf_hdr Pointer to network buffer header that received TCP packet. * -------- Argument validated in NetTCP_Rx(). * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_CONN_SEQ_SYNC_INVALID Received segment's synchronization is * NOT valid for current TCP connection. * NET_TCP_ERR_CONN_SEQ_INVALID Received segment's sequence number is * NOT valid for current TCP connection. * NET_TCP_ERR_CONN_ACK_NONE Received segment's acknowledgement is * NOT available. * NET_TCP_ERR_CONN_ACK_INVALID Received segment's acknowledgement number is * NOT valid for current TCP connection. * NET_TCP_ERR_CONN_RESET_VALID Received segment's reset flag is valid * for current TCP connection; i.e. reset * the TCP connection. * NET_TCP_ERR_CONN_RESET_INVALID Received segment's reset flag is NOT valid * for current TCP connection. * * NET_TCP_ERR_CONN_FAIL TCP connection operation(s) failed. * * ----- RETURNED BY NetTCP_RxPktConnIsValidSeq() : ----- * ----- RETURNED BY NetTCP_RxPktConnIsValidAck() : ----- * ---- RETURNED BY NetTCP_RxPktConnIsValidReset() : ---- * NET_TCP_ERR_CONN_NOT_USED TCP connection NOT currently used. * NET_TCP_ERR_INVALID_CONN_STATE Invalid TCP connection state. * NET_TCP_ERR_INVALID_LEN_SEG Invalid TCP sequence-segment length. * * ----- RETURNED BY NetTCP_RxPktConnHandlerSeg() : ----- * NET_TCP_ERR_CONN_DATA_NONE Received packet successfully handled; but NO * data to queue to receive queue(s). * NET_TCP_ERR_CONN_DATA_VALID Received packet successfully handled & valid * data queued for processing. * NET_TCP_ERR_CONN_DATA_INVALID Received packet contains invalid segment * data; NOT queued to receive queue(s). * NET_TCP_ERR_CONN_DATA_DUP Received packet contains duplicate segment * data; NOT queued to receive queue(s). * * NET_ERR_INIT_INCOMPLETE Network initialization NOT complete. * * NET_TCP_ERR_INVALID_CONN_OP Invalid TCP connection operation. * * NET_TCP_ERR_RX_Q_FULL TCP connection receive queue full. * NET_TCP_ERR_RX_Q_SIGNAL_FAULT TCP connection receive queue signal fault. * NET_TCP_ERR_TX_Q_SIGNAL_FAULT TCP connection transmit queue signal fault. * NET_TCP_ERR_RE_TX_SEG_TH TCP connection closed due to excessive retransmission. * NET_TCP_ERR_TX TCP transmit error. * * - RETURNED BY NetTCP_RxPktConnHandlerSignalClose() : - * NET_TCP_ERR_INVALID_CONN_ID Invalid application connection. * NET_CONN_ERR_INVALID_FAMILY Invalid network connection family. * NET_CONN_ERR_INVALID_CONN Invalid network connection number. * NET_CONN_ERR_NOT_USED Network connection NOT currently used. * * ---------- RETURNED BY NetTCP_TxConnTxQ() : ---------- * NET_TCP_ERR_CONN_CLOSE TCP connection closed. * NET_TCP_ERR_CONN_FAULT TCP connection fault; connection(s) aborted. * NET_TCP_ERR_NONE_AVAIL Resources NOT available. * NET_CONN_ERR_INVALID_FAMILY Invalid connection family. * NET_CONN_ERR_INVALID_ADDR Invalid TCP connection address. * NET_CONN_ERR_INVALID_ADDR_LEN Invalid TCP connection address length. *$PAGE* * Return(s) : none. * * Caller(s) : NetTCP_RxPktConnHandler(). * * Note(s) : (2) TCP connections in the CLOSING state are handled as follows : * * (a) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check Sequence Number' * states that in the "SYN-RECEIVED, ESTABLISHED STATE, FIN-WAIT-1 STATE, FIN-WAIT-2 * STATE, CLOSE-WAIT STATE, CLOSING STATE, LAST-ACK STATE, TIME-WAIT STATE" that ... * * (1) "Segments are processed in sequence ... processing is done in SEG.SEQ order." * * (2) "Initial tests on arrival are used to discard old duplicates." * * (3) "If a segment's contents straddle the boundary between old and new, only the * new parts should be processed." * * (4) (A) "If an incoming segment is not acceptable," ... * * (B) "an acknowledgment should be sent in reply" ... * * (C) "(unless the RST bit is set, if so drop the segment)". * * See also Notes #2b2Aa & #2b2B. * * See also 'NetTCP_RxPktConnIsValidSeq() Note #1d'. * * (b) (1) (A) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check RST Bit' * states that in the "CLOSING, LAST-ACK, TIME-WAIT" states that "if the RST bit * is set then, enter the CLOSED state". * * (B) RFC #793, Section 3.4 'Establishing a Connection : Reset Processing' reiterates * that "if the receiver ... of a RST ... was in any other state [other than LISTEN * or SYN-RECEIVED], it aborts the connection and advises the user and goes to the * CLOSED state". * * (2) (A) RFC Draft-IETF-TCPm-TCPSecure #00, Section 2.2 amends the "handling of a segment * with the RST bit when in a synchronized state" to "provide some protection against * ... blind reset attack[s] using the RST bit" : * * (a) "If the RST bit is set and the sequence number is outside the expected * window, silently drop the segment." * * (b) "If the RST bit is exactly the next expected sequence number [sic], reset * the connection"; it is assumed that this should read "if the RST bit is * set and the sequence number is exactly the next expected sequence number, * reset the connection." * * (c) "If the RST bit is set and the sequence number does not exactly match * the next expected sequence value, yet is within the acceptable window * (RCV.NXT <= SEG.SEQ < RCV.NXT+RCV.WND) send an acknowledgment." * * (B) ???? In addition, RFC Draft-IETF-TCPm-TCPSecure #00 does NOT provide a precedence * priority for handling TCP segments received with BOTH the RST & SYN bits set. * * ???? Therefore, since it does NOT seem reasonable to reset a TCP connection * due to a TCP segment that also attempted to synchronize the TCP connection, * it is assumed that the amended handling of the SYN bit should take precedence * over the amended handling of the RST bit. * * See also Note #2c2. * * (c) (1) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check SYN Bit' * states that in the "SYN-RECEIVED [STATE], ESTABLISHED STATE, FIN-WAIT STATE-1, * FIN-WAIT STATE-2, CLOSE-WAIT STATE, CLOSING STATE, LAST-ACK STATE, TIME-WAIT * STATE" that ... * * (A) "If the SYN is in the window it is an error, send a reset, any outstanding * RECEIVEs and SEND[s] should receive 'reset' responses, all segment queues * should be flushed, the user should also receive an unsolicited general * 'connection reset' signal[, and] enter the CLOSED state." * * (B) But "if the SYN is not in the window this step would not have been reached * and an ack would have been sent". * * (2) HOWEVER, RFC Draft-IETF-TCPm-TCPSecure #00, Section 3.2 amends the "handling * of a segment with the SYN bit set in the synchronized state ... [by] handling * ... the SYN bit" as follows : * * (a) "If the SYN bit is set and the sequence number is outside the * expected window, send an ACK back to the peer." * * (b) "If the SYN bit is set and the sequence number is an exact * match to the next expected sequence (RCV.NXT == SEG.SEQ) * then send an ACK segment ... but ... subtract one from * value being acknowledged." * * (c) "If the SYN bit is set and the sequence number is acceptable, * i.e.: (RCV.NXT <= SEG.SEQ <= RCV.NXT+RCV.WND) then send an * ACK segment." *$PAGE* * (d) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check ACK Field' * states that ... : * * (1) "If the ACK bit is off drop the segment." * * (2) "If the ACK bit is on" ... * * (A) And in the "ESTABLISHED STATE" ... * * (1) "If SND.UNA < SEG.ACK <= SND.NXT then" ... * * (a) "set SND.UNA <- SEG.ACK." * * (b) "Any segments on the retransmission queue which are thereby entirely * acknowledged are removed." * * (c) "the send window should be updated" : * * (1) (A) (1) "If ((SND.WL1 < SEG.SEQ) or" ... * * (2) (a) "(SND.WL1 = SEG.SEQ and" ... * (b) "SND.WL2 <= SEG.ACK))," ... * * (B) (1) "set SND.WND <- SEG.WND," ... * (2) "set SND.WL1 <- SEG.SEQ," ... * (3) "set SND.WL2 <- SEG.ACK." * * (2) "Note that SND.WND is an offset from SND.UNA, that SND.WL1 records the * sequence number of the last segment used to update SND.WND, and that * SND.WL2 records the acknowledgment number of the last segment used to * update SND.WND. The check here preveents using old segments to update * the window." * * (2) (a) "If the ACK is a duplicate (SEG.ACK <= SND.UNA), it can be ignored." * * (b) RFC #1122, Section 4.2.2.20.(g) amends the transmit window update criteria * for the segment's acknowledgement to include SND.UNA : "The window should * updated if SND.UNA <= SEG.ACK <= SND.NXT." * * See also 'NetTCP_RxPktConnHandlerTxWinRemote() Note #1b2'. * * (3) "If the ACK acks something not yet sent (SEG.ACK > SND.NXT) then send an ACK * [and] drop the segment." *$PAGE* * (B) For the "CLOSING STATE" : * * (1) "in addition to the processing for the ESTABLISHED state" ... * * (2) (a) "if the ACK acknowledges our FIN then" ... * * (1) (A) "enter the TIME-WAIT state," ... * * (1) The following sections ... : * * (a) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : * Check FIN Bit : FIN-WAIT-1 STATE' * (b) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : * Check FIN Bit : FIN-WAIT-2 STATE' * * (2) ... generalize that when entering "the TIME-WAIT state" to ... * * (a) "Start the time-wait timer," ... * (1) A TCP connection should be closed WITHOUT fault following * a TCP connection TIME-WAIT timeout. * * (b) "turn off the other timers." * * (3) Although these sections are the only sections to state that these * TCP connection timers should be updated when transitioning to the * TIME-WAIT state, it is assumed that these timers should also be * updated for the transition from the CLOSING state to the TIME-WAIT * state. * * (B) However, it is possible that some closing received data from the remote * host is available but has NOT yet been received by the application layer. * * (1) Therefore, if the application receive queue is closed, then enter the * TIME-WAIT state. * * (2) (a) But if the application receive queue is NOT closed, then enter the * connection-closing-data-available state to allow the application * layer to receive the remaining receive data. * * (b) (1) To satisfy the required time-wait timeout of two maximum segment * lifetimes (see Note #2f5A1a), the time-wait timeout is intially * used to provide the application layer sufficient time to receive * the closing received data. * * (2) If after the time-wait timeout expires, the application receive * queue is still not empty, the user connection timeout is used * to provide the application layer additional time to receive the * closing received data. * * See also Note #3. * * (2) (A) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check * ACK Field : FIN-WAIT-2 STATE' states that "if the retransmission * queue is empty, the user's CLOSE can be acknowledged". * * (B) Although this section is the only section to state that the TCP * connection should acknowledge the user's close, it is assumed that * a TCP connection should signal the application layer that "the * user's close [is] acknowledged" whenever its re-transmit queue * becomes empty. * * (b) (1) "otherwise ignore the segment." * * (2) However, it is possible that some but NOT all transmitted data has * been received by the remote host. In other words, the remote host may * have received some but NOT ALL transmitted data preceding this host's * connection close request. * * ???? Therefore, acknowledgements validated by as within the transmit * window MUST be received & processed as in connected states. * * See also 'NetTCP_RxPktConnIsValidAck() Note #1d'. * * See also Note #2e2. *$PAGE* * (e) (1) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Process Segment Text' * states that in the "CLOSE-WAIT STATE, CLOSING STATE, LAST-ACK STATE, TIME-WAIT * STATE" that "this should not occur, since a FIN has been received from the remote * side. Ignore the segment text." * * (2) (A) However, it is possible that some but NOT all data has been received from * the remote host. In other words, the remote host's connection close request * may have received but ALL receive data preceding the connection close request * may NOT have been received. * * ???? Therefore, receive data validated by sequence number as within the * receive window MUST be received & processed as in connected states. * * (B) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Process Segment * Text' states that "once in the ESTABLISHED state, it is possible to deliver * segment text to user RECEIVE buffers". * * See also Note #2d2B2b. * * (f) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check FIN Bit' states * that "if the FIN bit is set" ... * * (1) "signal the user 'connection closing' and return any pending RECEIVEs with * same message," ... * * (2) "advance RCV.NXT over the FIN," ... * * (3) "send an acknowledgment for the FIN" ... * * (4) "FIN implies PUSH for any segment text not yet delivered to the user" ... * * (5) And the "CLOSING STATE ... remain[s] in the CLOSING state". * * (3) (a) RFC #793, Section 3.9 'Event Processing : USER TIMEOUT : USER TIMEOUT' states that * "for any state if the user timeout expires, flush all queues, signal the user * 'error : connection aborted due to user timeout' ... [and] enter the CLOSED state". * * (b) However, NO RFC specifies or suggests any mechanism to implement/handle user timeouts. * * ???? Therefore, it is assumed that ANY TCP connection that receives a valid TCP data * or control segment should reset its user connection timer. * * (4) Since the mechanisms of TCP connection close are independent of the application layer * close; any external application layer close error(s) are ignored. ********************************************************************************************************* */ static void NetTCP_RxPktConnHandlerClosing (NET_TCP_CONN *pconn, NET_BUF *pbuf, NET_BUF_HDR *pbuf_hdr, NET_ERR *perr) { NET_TCP_SEQ_CODE seq_code; NET_TCP_ACK_CODE ack_code; NET_TCP_RESET_CODE reset_code; CPU_BOOLEAN data_avail; NET_TCP_TIMEOUT_SEC timeout_sec; NET_TMR_TICK timeout_tick; CPU_FNCT_PTR timeout_fnct; NET_TCP_FREE_CODE free_code; NET_ERR err; NET_ERR err_rtn; /*$PAGE*/ /* -------------- VALIDATE RX'D TCP PKT --------------- */ /* Chk for rx'd fin/close. */ if (pbuf_hdr->TCP_SegClose == DEF_YES) { /* If fin/close avail, update seg lens. */ pbuf_hdr->TCP_SegLenInit += NET_TCP_SEG_LEN_CLOSE; pbuf_hdr->TCP_SegLen += NET_TCP_SEG_LEN_CLOSE; } /* Chk rx'd seq nbr. */ seq_code = NetTCP_RxPktConnIsValidSeq(pconn, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return; } switch (seq_code) { case NET_TCP_CONN_RX_SEQ_VALID: break; case NET_TCP_CONN_RX_SEQ_SYNC: /* If invalid sync rx'd, ... */ case NET_TCP_CONN_RX_SEQ_SYNC_INVALID: /* ... tx TCP conn ack (see Notes #2c2 & #2b2B). */ NetTCP_TxConnAck(pconn, pbuf_hdr, NET_TCP_CONN_TX_ACK_FAULT, NET_TCP_CONN_CLOSE_ALL, &err); *perr = NET_TCP_ERR_CONN_SEQ_SYNC_INVALID; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_RX_SEQ_NONE: case NET_TCP_CONN_RX_SEQ_INVALID: /* If invalid seq rx'd (see Note #2a4A), ... */ default: if (pbuf_hdr->TCP_SegReset != DEF_YES) { /* ... & reset NOT rx'd (see Note #2a4C), ... */ /* ... tx TCP conn ack (see Note #2a4B). */ NetTCP_TxConnAck(pconn, pbuf_hdr, NET_TCP_CONN_TX_ACK_FAULT, NET_TCP_CONN_CLOSE_ALL, &err); } *perr = NET_TCP_ERR_CONN_SEQ_INVALID; return; /* Prevent 'break NOT reachable' compiler warning. */ } /* Chk for rx'd reset. */ reset_code = NetTCP_RxPktConnIsValidReset(pconn, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return; } switch (reset_code) { case NET_TCP_CONN_RX_RESET_NONE: break; case NET_TCP_CONN_RX_RESET_VALID: /* If valid reset rx'd, ... */ /* ... close TCP conn (see Note #2b1A). */ NetTCP_ConnClose(pconn, pbuf_hdr, pconn->ConnCloseAppFlag, NET_TCP_CONN_CLOSE_ALL); *perr = NET_TCP_ERR_CONN_RESET_VALID; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_RX_RESET_INVALID: /* If invalid reset rx'd, ... */ default: /* ... tx TCP conn ack (see Note #2b2Ac). */ NetTCP_TxConnAck(pconn, pbuf_hdr, NET_TCP_CONN_TX_ACK_FAULT, NET_TCP_CONN_CLOSE_ALL, &err); *perr = NET_TCP_ERR_CONN_RESET_INVALID; return; /* Prevent 'break NOT reachable' compiler warning. */ } /* Chk for rx'd ack. */ ack_code = NetTCP_RxPktConnIsValidAck(pconn, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return; } switch (ack_code) { case NET_TCP_CONN_RX_ACK_VALID: case NET_TCP_CONN_RX_ACK_DUP: case NET_TCP_CONN_RX_ACK_PREV: break; case NET_TCP_CONN_RX_ACK_NONE: /* If NO ack rx'd, ... */ *perr = NET_TCP_ERR_CONN_ACK_NONE; /* ... ignore TCP pkt (see Note #2d1). */ return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_RX_ACK_INVALID: /* If invalid ack rx'd, ... */ default: /* ... tx TCP conn ack (see Note #2d2A3). */ NetTCP_TxConnAck(pconn, pbuf_hdr, NET_TCP_CONN_TX_ACK_FAULT, NET_TCP_CONN_CLOSE_ALL, &err); *perr = NET_TCP_ERR_CONN_ACK_INVALID; return; /* Prevent 'break NOT reachable' compiler warning. */ } /*$PAGE*/ /* ----------------- HANDLE RX'D SEG ------------------ */ NetTCP_RxPktConnHandlerSeg(pconn, ack_code, pbuf, pbuf_hdr, &err_rtn); switch (err_rtn) { case NET_TCP_ERR_CONN_DATA_NONE: case NET_TCP_ERR_CONN_DATA_VALID: break; case NET_TCP_ERR_CONN_DATA_DUP: /* ???? Allow dup data to update TCP conn? */ *perr = err_rtn; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_ERR_INIT_INCOMPLETE: case NET_TCP_ERR_CONN_NOT_USED: case NET_TCP_ERR_CONN_FAIL: case NET_TCP_ERR_CONN_ACK_INVALID: case NET_TCP_ERR_CONN_DATA_INVALID: case NET_TCP_ERR_INVALID_CONN_STATE: case NET_TCP_ERR_INVALID_CONN_OP: case NET_TCP_ERR_INVALID_LEN_SEG: case NET_TCP_ERR_RX_Q_FULL: case NET_TCP_ERR_RX_Q_SIGNAL_FAULT: case NET_TCP_ERR_TX: case NET_TCP_ERR_TX_Q_SIGNAL_FAULT: case NET_TCP_ERR_RE_TX_SEG_TH: default: NetTCP_ConnClose(pconn, pbuf_hdr, pconn->ConnCloseAppFlag, NET_TCP_CONN_CLOSE_ALL); *perr = err_rtn; return; /* Prevent 'break NOT reachable' compiler warning. */ } /* ----------------- UPDATE TCP CONN ------------------ */ if (pconn->TxQ_State == NET_TCP_TX_Q_STATE_CONN_CLOSED) { /* If local conn close ack'd (see Note #2d2B2a); .. */ /* Closing data avail for half-closed conns ONLY. */ data_avail = ((pconn->ConnCloseCode != NET_CONN_CLOSE_HALF ) || ((pconn->RxQ_State == NET_TCP_RX_Q_STATE_CONN_CLOSED) && (pconn->RxQ_App_Head == (NET_BUF *)0))) ? DEF_NO : DEF_YES; /* .. signal app conn close (see Note #2d2B2a2B), .. */ NetTCP_RxPktConnHandlerSignalClose(pconn, data_avail, &err); switch (err) { case NET_TCP_ERR_NONE: case NET_TCP_ERR_CONN_FAIL: /* Ignore any app conn close err(s) [see Note #4]. */ break; case NET_TCP_ERR_CONN_NOT_USED: case NET_TCP_ERR_INVALID_CONN_ID: case NET_TCP_ERR_INVALID_CONN_STATE: case NET_TCP_ERR_INVALID_CONN_OP: case NET_CONN_ERR_INVALID_FAMILY: case NET_CONN_ERR_INVALID_CONN: case NET_CONN_ERR_NOT_USED: default: NetTCP_ConnClose(pconn, pbuf_hdr, DEF_YES, NET_TCP_CONN_CLOSE_ALL); *perr = err; return; /* Prevent 'break NOT reachable' compiler warning. */ } if (data_avail != DEF_YES) { /* .. & if NO app data avail, .. */ /* .. chng to time-wait state (see Note #2d2B2a1B1); */ pconn->ConnState = NET_TCP_CONN_STATE_TIME_WAIT; pconn->ConnCloseTimeoutFaultFlag = DEF_NO; /* .. clr close timeout fault (see Note #2d2B2a1A2a1); */ timeout_fnct = (CPU_FNCT_PTR)NetTCP_ConnCloseTimeout; } else { /* .. else chng to closing-data-avail state .. */ /* .. (see Note #2d2B2a1B2a); */ pconn->ConnState = NET_TCP_CONN_STATE_CLOSING_DATA_AVAIL; timeout_fnct = (CPU_FNCT_PTR)NetTCP_ConnClosingTimeoutDataAvail; } /* .. & start time-wait tmr (see Notes #2d2B2a1A2a & */ /* .. #2d2B2a1B2b1). */ timeout_sec = pconn->TimeoutMaxSeg_sec * NET_TCP_CONN_TIMEOUT_MAX_SEG_SCALAR; } else { timeout_sec = pconn->TimeoutConn_sec; /* Else start conn close tmr (see Note #3b). */ timeout_fnct = (CPU_FNCT_PTR)NetTCP_ConnCloseTimeout; } /*$PAGE*/ /* ------------------- UPDATE TMRs -------------------- */ switch (pconn->ConnState) { case NET_TCP_CONN_STATE_TIME_WAIT: /* If in time-wait (see Note #2d2B2a1A) ... */ case NET_TCP_CONN_STATE_CLOSING_DATA_AVAIL: /* ... or closing state(s) [see Note #2d2B2a1B], ... */ free_code = NET_TCP_CONN_FREE_TMR_ALL; /* ... free all TCP conn tmrs (see Note #2d2B2a1A2b). */ DEF_BIT_CLR(free_code, NET_TCP_CONN_FREE_TMR_TIMEOUT); NetTCP_ConnFreeTmr(pconn, free_code); break; } timeout_tick = (NET_TMR_TICK)timeout_sec * NET_TMR_TIME_TICK_PER_SEC; if (pconn->TimeoutTmr != (NET_TMR *)0) { NetTmr_Set((NET_TMR *) pconn->TimeoutTmr, (CPU_FNCT_PTR) timeout_fnct, (NET_TMR_TICK) timeout_tick, (NET_ERR *)&err); } else { pconn->TimeoutTmr = NetTmr_Get((void *) pconn, (CPU_FNCT_PTR) timeout_fnct, (NET_TMR_TICK) timeout_tick, (CPU_INT16U ) NET_TMR_FLAG_NONE, (NET_ERR *)&err); } if ( err != NET_TMR_ERR_NONE) { NetTCP_ConnClose(pconn, pbuf_hdr, pconn->ConnCloseAppFlag, NET_TCP_CONN_CLOSE_ALL); *perr = NET_TCP_ERR_CONN_FAIL; return; } /*$PAGE*/ /* --------------- TX TCP CONN ACK/DATA --------------- */ ack_code = ((pbuf_hdr->TCP_SegAckTxReqCode == NET_TCP_CONN_TX_ACK_IMMED) || (pbuf_hdr->TCP_SegClose == DEF_YES)) /* See Note #2f3. */ ? NET_TCP_CONN_TX_ACK_IMMED : NET_TCP_CONN_TX_ACK; if (pconn->ConnState == NET_TCP_CONN_STATE_CLOSING) { /* If conn still closing, ... */ /* ... tx ack & any tx data (see Note #1c). */ NetTCP_TxConnTxQ(pconn, pbuf_hdr, ack_code, DEF_NO, NET_TCP_CONN_CLOSE_ALL, perr); switch (*perr) { case NET_TCP_ERR_NONE: case NET_TCP_ERR_CONN_ACK_NONE: case NET_TCP_ERR_CONN_ACK_INVALID: case NET_TCP_ERR_CONN_ACK_DLYD: case NET_TCP_ERR_CONN_ACK_PREVLY_TXD: case NET_ERR_TX: /* Ignore transitory tx err(s). */ break; case NET_ERR_INIT_INCOMPLETE: case NET_TCP_ERR_CONN_NOT_USED: case NET_TCP_ERR_CONN_CLOSE: case NET_TCP_ERR_CONN_FAULT: case NET_TCP_ERR_CONN_FAIL: case NET_TCP_ERR_INVALID_CONN_STATE: case NET_TCP_ERR_INVALID_CONN_OP: case NET_TCP_ERR_INVALID_LEN_SEG: case NET_TCP_ERR_NONE_AVAIL: case NET_TCP_ERR_TX: case NET_CONN_ERR_INVALID_FAMILY: case NET_CONN_ERR_INVALID_ADDR: case NET_CONN_ERR_INVALID_ADDR_LEN: default: return; /* Prevent 'break NOT reachable' compiler warning. */ } } else { /* Else tx TCP conn ack (see Note #2f3). */ NetTCP_TxConnAck(pconn, pbuf_hdr, ack_code, NET_TCP_CONN_CLOSE_ALL, perr); switch (*perr) { case NET_TCP_ERR_NONE: case NET_TCP_ERR_CONN_ACK_NONE: case NET_TCP_ERR_CONN_ACK_INVALID: case NET_TCP_ERR_CONN_ACK_DLYD: case NET_TCP_ERR_CONN_ACK_PREVLY_TXD: break; case NET_TCP_ERR_CONN_NOT_USED: case NET_TCP_ERR_CONN_FAULT: case NET_TCP_ERR_INVALID_CONN_STATE: case NET_TCP_ERR_INVALID_CONN_OP: case NET_TCP_ERR_INVALID_LEN_SEG: case NET_TCP_ERR_NONE_AVAIL: default: return; /* Prevent 'break NOT reachable' compiler warning. */ } } *perr = err_rtn; } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_RxPktConnHandlerTimeWait() * * Description : (1) Handle TCP connection in TIME-WAIT state : * * (a) Validate received TCP packet for current TCP connection state : * * (1) Sequence Number (SEQ) See Note #2a * (2) Reset (RST) See Note #2b * (3) Synchronization (SYN) See Note #2c * (4) Acknowledgement (ACK) See Note #2d * (5) Finish/Close (FIN) See Note #2f * * (b) Update TCP connection : * (1) Handle received TCP segment See Notes #2d & #2e * (2) Update TCP connection timer See Notes #2d2C2 * * (c) Transmit TCP connection acknowledgement See Notes #2d2C1 & #2f3 * * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_RxPktConnHandler(). * * pbuf Pointer to network buffer that received TCP packet. * ---- Argument checked in NetTCP_Rx(). * * pbuf_hdr Pointer to network buffer header that received TCP packet. * -------- Argument validated in NetTCP_Rx(). * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_CONN_SEQ_SYNC_INVALID Received segment's synchronization is * NOT valid for current TCP connection. * NET_TCP_ERR_CONN_SEQ_INVALID Received segment's sequence number is * NOT valid for current TCP connection. * NET_TCP_ERR_CONN_ACK_NONE Received segment's acknowledgement is * NOT available. * NET_TCP_ERR_CONN_ACK_INVALID Received segment's acknowledgement number is * NOT valid for current TCP connection. * NET_TCP_ERR_CONN_RESET_VALID Received segment's reset flag is valid * for current TCP connection; i.e. reset * the TCP connection. * NET_TCP_ERR_CONN_RESET_INVALID Received segment's reset flag is NOT valid * for current TCP connection. * * NET_TCP_ERR_CONN_FAIL TCP connection operation(s) failed. * * -- RETURNED BY NetTCP_RxPktConnIsValidSeq() : -- * -- RETURNED BY NetTCP_RxPktConnIsValidAck() : -- * - RETURNED BY NetTCP_RxPktConnIsValidReset() : - * NET_TCP_ERR_CONN_NOT_USED TCP connection NOT currently used. * NET_TCP_ERR_INVALID_CONN_STATE Invalid TCP connection state. * NET_TCP_ERR_INVALID_LEN_SEG Invalid TCP sequence-segment length. * * -- RETURNED BY NetTCP_RxPktConnHandlerSeg() : -- * NET_TCP_ERR_CONN_DATA_NONE Received packet successfully handled; but NO * data to queue to receive queue(s). * NET_TCP_ERR_CONN_DATA_VALID Received packet successfully handled & valid * data queued for processing. * NET_TCP_ERR_CONN_DATA_INVALID Received packet contains invalid segment * data; NOT queued to receive queue(s). * NET_TCP_ERR_CONN_DATA_DUP Received packet contains duplicate segment * data; NOT queued to receive queue(s). * * NET_ERR_INIT_INCOMPLETE Network initialization NOT complete. * * NET_TCP_ERR_INVALID_CONN_OP Invalid TCP connection operation. * * NET_TCP_ERR_RX_Q_FULL TCP connection receive queue full. * NET_TCP_ERR_RX_Q_SIGNAL_FAULT TCP connection receive queue signal fault. * * ------- RETURNED BY NetTCP_TxConnAck() : ------- * NET_TCP_ERR_CONN_FAULT TCP connection fault; connection(s) aborted. * NET_TCP_ERR_NONE_AVAIL Resources NOT available. * * Return(s) : none. * * Caller(s) : NetTCP_RxPktConnHandler(). *$PAGE* * Note(s) : (2) TCP connections in the TIME-WAIT state are handled as follows : * * (a) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check Sequence Number' * states that in the "SYN-RECEIVED, ESTABLISHED STATE, FIN-WAIT-1 STATE, FIN-WAIT-2 * STATE, CLOSE-WAIT STATE, CLOSING STATE, LAST-ACK STATE, TIME-WAIT STATE" that ... * * (1) "Segments are processed in sequence ... processing is done in SEG.SEQ order." * * (2) "Initial tests on arrival are used to discard old duplicates." * * (3) "If a segment's contents straddle the boundary between old and new, only the * new parts should be processed." * * (4) (A) "If an incoming segment is not acceptable," ... * * (B) "an acknowledgment should be sent in reply" ... * * (C) "(unless the RST bit is set, if so drop the segment)". * * See also Notes #2b2Aa & #2b2B. * * See also 'NetTCP_RxPktConnIsValidSeq() Note #1d'. * * (b) (1) (A) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check RST Bit' * states that in the "CLOSING, LAST-ACK, TIME-WAIT" states that "if the RST bit * is set then, enter the CLOSED state". * * (B) RFC #793, Section 3.4 'Establishing a Connection : Reset Processing' reiterates * that "if the receiver ... of a RST ... was in any other state [other than LISTEN * or SYN-RECEIVED], it aborts the connection and advises the user and goes to the * CLOSED state". * * (2) (A) RFC Draft-IETF-TCPm-TCPSecure #00, Section 2.2 amends the "handling of a segment * with the RST bit when in a synchronized state" to "provide some protection against * ... blind reset attack[s] using the RST bit" : * * (a) "If the RST bit is set and the sequence number is outside the expected * window, silently drop the segment." * * (b) "If the RST bit is exactly the next expected sequence number [sic], reset * the connection"; it is assumed that this should read "if the RST bit is * set and the sequence number is exactly the next expected sequence number, * reset the connection." * * (c) "If the RST bit is set and the sequence number does not exactly match * the next expected sequence value, yet is within the acceptable window * (RCV.NXT <= SEG.SEQ < RCV.NXT+RCV.WND) send an acknowledgment." * * (B) ???? In addition, RFC Draft-IETF-TCPm-TCPSecure #00 does NOT provide a precedence * priority for handling TCP segments received with BOTH the RST & SYN bits set. * * ???? Therefore, since it does NOT seem reasonable to reset a TCP connection * due to a TCP segment that also attempted to synchronize the TCP connection, * it is assumed that the amended handling of the SYN bit should take precedence * over the amended handling of the RST bit. * * See also Note #2c2. * * (c) (1) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check SYN Bit' * states that in the "SYN-RECEIVED [STATE], ESTABLISHED STATE, FIN-WAIT STATE-1, * FIN-WAIT STATE-2, CLOSE-WAIT STATE, CLOSING STATE, LAST-ACK STATE, TIME-WAIT * STATE" that ... * * (A) "If the SYN is in the window it is an error, send a reset, any outstanding * RECEIVEs and SEND[s] should receive 'reset' responses, all segment queues * should be flushed, the user should also receive an unsolicited general * 'connection reset' signal[, and] enter the CLOSED state." * * (B) But "if the SYN is not in the window this step would not have been reached * and an ack would have been sent". * * (2) HOWEVER, RFC Draft-IETF-TCPm-TCPSecure #00, Section 3.2 amends the "handling * of a segment with the SYN bit set in the synchronized state ... [by] handling * ... the SYN bit" as follows : * * (a) "If the SYN bit is set and the sequence number is outside the * expected window, send an ACK back to the peer." * * (b) "If the SYN bit is set and the sequence number is an exact * match to the next expected sequence (RCV.NXT == SEG.SEQ) * then send an ACK segment ... but ... subtract one from * value being acknowledged." * * (c) "If the SYN bit is set and the sequence number is acceptable, * i.e.: (RCV.NXT <= SEG.SEQ <= RCV.NXT+RCV.WND) then send an * ACK segment." *$PAGE* * (d) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check ACK Field' * states that ... : * * (1) "If the ACK bit is off drop the segment." * * (2) "If the ACK bit is on" ... * * (A) "If the ACK is a duplicate (SEG.ACK <= SND.UNA), it can be ignored." * * (B) "If the ACK acks something not yet sent ... then send an ACK [and] drop the segment." * * (C) For the "TIME-WAIT STATE ... the only thing that can arrive in this state is a * retransmission of the remote FIN" : * * (1) "Acknowledge it" ... * (2) "restart the 2 MSL timeout." * * (e) (1) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Process Segment Text' * states that in the "CLOSE-WAIT STATE, CLOSING STATE, LAST-ACK STATE, TIME-WAIT * STATE" that "this should not occur, since a FIN has been received from the remote * side. Ignore the segment text." * * (2) (A) However, it is possible that some but NOT all data has been received from * the remote host. In other words, the remote host's connection close request * may have received but ALL receive data preceding the connection close request * may NOT have been received. * * ???? Therefore, receive data validated by sequence number as within the * receive window MUST be received & processed as in connected states. * * (B) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Process Segment * Text' states that "once in the ESTABLISHED state, it is possible to deliver * segment text to user RECEIVE buffers". * * (f) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check FIN Bit' states * that "if the FIN bit is set" ... * * (1) "signal the user 'connection closing' and return any pending RECEIVEs with * same message," ... * * (2) "advance RCV.NXT over the FIN," ... * * (3) "send an acknowledgment for the FIN" ... * * (4) "FIN implies PUSH for any segment text not yet delivered to the user" ... * * (5) For the "TIME-WAIT STATE" : * * (A) "Remain in the TIME-WAIT state" ... * (B) "Restart the 2 MSL time-wait timeout." * * (3) RFC #793, Section 3.9 'Event Processing : USER TIMEOUT : TIME-WAIT TIMEOUT' states * that "if the time-wait timeout expires on a connection ... enter the CLOSED state". ********************************************************************************************************* */ static void NetTCP_RxPktConnHandlerTimeWait (NET_TCP_CONN *pconn, NET_BUF *pbuf, NET_BUF_HDR *pbuf_hdr, NET_ERR *perr) { NET_TCP_SEQ_CODE seq_code; NET_TCP_ACK_CODE ack_code; NET_TCP_RESET_CODE reset_code; NET_TCP_TIMEOUT_SEC timeout_sec; NET_TMR_TICK timeout_tick; NET_ERR err; NET_ERR err_rtn; /*$PAGE*/ /* -------------- VALIDATE RX'D TCP PKT --------------- */ (void)&pbuf_hdr->TCP_SegClose; /* Ignore any rx'd fin (see Note #2f5A). */ /* Chk rx'd seq nbr. */ seq_code = NetTCP_RxPktConnIsValidSeq(pconn, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return; } switch (seq_code) { case NET_TCP_CONN_RX_SEQ_VALID: break; case NET_TCP_CONN_RX_SEQ_SYNC: /* If invalid sync rx'd, ... */ case NET_TCP_CONN_RX_SEQ_SYNC_INVALID: /* ... tx TCP conn ack (see Notes #2c2 & #2b2B). */ NetTCP_TxConnAck(pconn, pbuf_hdr, NET_TCP_CONN_TX_ACK_FAULT, NET_TCP_CONN_CLOSE_ALL, &err); *perr = NET_TCP_ERR_CONN_SEQ_SYNC_INVALID; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_RX_SEQ_NONE: case NET_TCP_CONN_RX_SEQ_INVALID: /* If invalid seq rx'd (see Note #2a4A), ... */ default: if (pbuf_hdr->TCP_SegReset != DEF_YES) { /* ... & reset NOT rx'd (see Note #2a4C), ... */ /* ... tx TCP conn ack (see Note #2a4B). */ NetTCP_TxConnAck(pconn, pbuf_hdr, NET_TCP_CONN_TX_ACK_FAULT, NET_TCP_CONN_CLOSE_ALL, &err); } *perr = NET_TCP_ERR_CONN_SEQ_INVALID; return; /* Prevent 'break NOT reachable' compiler warning. */ } /* Chk for rx'd reset. */ reset_code = NetTCP_RxPktConnIsValidReset(pconn, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return; } switch (reset_code) { case NET_TCP_CONN_RX_RESET_NONE: break; case NET_TCP_CONN_RX_RESET_VALID: /* If valid reset rx'd, ... */ /* ... close TCP conn (see Note #2b1A). */ NetTCP_ConnClose(pconn, pbuf_hdr, pconn->ConnCloseAppFlag, NET_TCP_CONN_CLOSE_ALL); *perr = NET_TCP_ERR_CONN_RESET_VALID; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_RX_RESET_INVALID: /* If invalid reset rx'd, ... */ default: /* ... tx TCP conn ack (see Note #2b2Ac). */ NetTCP_TxConnAck(pconn, pbuf_hdr, NET_TCP_CONN_TX_ACK_FAULT, NET_TCP_CONN_CLOSE_ALL, &err); *perr = NET_TCP_ERR_CONN_RESET_INVALID; return; /* Prevent 'break NOT reachable' compiler warning. */ } /* Chk for rx'd ack. */ ack_code = NetTCP_RxPktConnIsValidAck(pconn, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return; } switch (ack_code) { case NET_TCP_CONN_RX_ACK_VALID: case NET_TCP_CONN_RX_ACK_DUP: case NET_TCP_CONN_RX_ACK_PREV: break; case NET_TCP_CONN_RX_ACK_NONE: /* If NO ack rx'd, ... */ *perr = NET_TCP_ERR_CONN_ACK_NONE; /* ... ignore TCP pkt (see Note #2d1). */ return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_RX_ACK_INVALID: /* If invalid ack rx'd, ... */ default: /* ... tx TCP conn ack (see Note #2d2B). */ NetTCP_TxConnAck(pconn, pbuf_hdr, NET_TCP_CONN_TX_ACK_FAULT, NET_TCP_CONN_CLOSE_ALL, &err); *perr = NET_TCP_ERR_CONN_ACK_INVALID; return; /* Prevent 'break NOT reachable' compiler warning. */ } /*$PAGE*/ /* ----------------- HANDLE RX'D SEG ------------------ */ NetTCP_RxPktConnHandlerSeg(pconn, ack_code, pbuf, pbuf_hdr, &err_rtn); switch (err_rtn) { case NET_TCP_ERR_CONN_DATA_NONE: case NET_TCP_ERR_CONN_DATA_VALID: break; case NET_TCP_ERR_CONN_DATA_DUP: /* ???? Allow dup data to update TCP conn? */ *perr = err_rtn; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_ERR_INIT_INCOMPLETE: case NET_TCP_ERR_CONN_NOT_USED: case NET_TCP_ERR_CONN_ACK_INVALID: case NET_TCP_ERR_CONN_DATA_INVALID: case NET_TCP_ERR_INVALID_CONN_STATE: case NET_TCP_ERR_INVALID_CONN_OP: case NET_TCP_ERR_INVALID_LEN_SEG: case NET_TCP_ERR_RX_Q_FULL: case NET_TCP_ERR_RX_Q_SIGNAL_FAULT: default: NetTCP_ConnClose(pconn, pbuf_hdr, pconn->ConnCloseAppFlag, NET_TCP_CONN_CLOSE_ALL); *perr = err_rtn; return; /* Prevent 'break NOT reachable' compiler warning. */ } /* ----------------- UPDATE TCP CONN ------------------ */ /* -------------------- UPDATE TMR -------------------- */ /* Reset time-wait tmr (see Note #2d2C2). */ timeout_sec = pconn->TimeoutMaxSeg_sec * NET_TCP_CONN_TIMEOUT_MAX_SEG_SCALAR; timeout_tick = (NET_TMR_TICK)timeout_sec * NET_TMR_TIME_TICK_PER_SEC; if (pconn->TimeoutTmr != (NET_TMR *)0) { NetTmr_Set((NET_TMR *) pconn->TimeoutTmr, (CPU_FNCT_PTR) NetTCP_ConnCloseTimeout, (NET_TMR_TICK) timeout_tick, (NET_ERR *)&err); } else { pconn->TimeoutTmr = NetTmr_Get((void *) pconn, (CPU_FNCT_PTR) NetTCP_ConnCloseTimeout, (NET_TMR_TICK) timeout_tick, (CPU_INT16U ) NET_TMR_FLAG_NONE, (NET_ERR *)&err); } if ( err != NET_TMR_ERR_NONE) { NetTCP_ConnClose(pconn, pbuf_hdr, pconn->ConnCloseAppFlag, NET_TCP_CONN_CLOSE_ALL); *perr = NET_TCP_ERR_CONN_FAIL; return; } /* ----------------- TX TCP CONN ACK ------------------ */ /* Tx TCP conn ack (see Notes #1c). */ NetTCP_TxConnAck(pconn, pbuf_hdr, NET_TCP_CONN_TX_ACK_IMMED, NET_TCP_CONN_CLOSE_ALL, perr); switch (*perr) { case NET_TCP_ERR_NONE: case NET_TCP_ERR_CONN_ACK_NONE: case NET_TCP_ERR_CONN_ACK_INVALID: case NET_TCP_ERR_CONN_ACK_DLYD: case NET_TCP_ERR_CONN_ACK_PREVLY_TXD: break; case NET_TCP_ERR_CONN_NOT_USED: case NET_TCP_ERR_CONN_FAULT: case NET_TCP_ERR_INVALID_CONN_STATE: case NET_TCP_ERR_INVALID_CONN_OP: case NET_TCP_ERR_INVALID_LEN_SEG: case NET_TCP_ERR_NONE_AVAIL: default: return; /* Prevent 'break NOT reachable' compiler warning. */ } *perr = err_rtn; } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_RxPktConnHandlerCloseWait() * * Description : (1) Handle TCP connection in CLOSE-WAIT state : * * (a) Validate received TCP packet for current TCP connection state : * * (1) Sequence Number (SEQ) See Note #2a * (2) Reset (RST) See Note #2b * (3) Synchronization (SYN) See Note #2c * (4) Acknowledgement (ACK) See Note #2d * (5) Finish/Close (FIN) See Note #2f * * (b) Update TCP connection : * (1) Handle received TCP segment See Notes #2d & #2e * (2) Update TCP connection timer See Note #3 * * (c) Transmit TCP connection acknowledgement & data See Notes #2d2B & #2f3 * * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_RxPktConnHandler(). * * pbuf Pointer to network buffer that received TCP packet. * ---- Argument checked in NetTCP_Rx(). * * pbuf_hdr Pointer to network buffer header that received TCP packet. * -------- Argument validated in NetTCP_Rx(). * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_CONN_SEQ_SYNC_INVALID Received segment's synchronization is * NOT valid for current TCP connection. * NET_TCP_ERR_CONN_SEQ_INVALID Received segment's sequence number is * NOT valid for current TCP connection. * NET_TCP_ERR_CONN_ACK_NONE Received segment's acknowledgement is * NOT available. * NET_TCP_ERR_CONN_ACK_INVALID Received segment's acknowledgement number is * NOT valid for current TCP connection. * NET_TCP_ERR_CONN_RESET_VALID Received segment's reset flag is valid * for current TCP connection; i.e. reset * the TCP connection. * NET_TCP_ERR_CONN_RESET_INVALID Received segment's reset flag is NOT valid * for current TCP connection. * * NET_TCP_ERR_CONN_FAIL TCP connection operation(s) failed. * * ----- RETURNED BY NetTCP_RxPktConnIsValidSeq() : ----- * ----- RETURNED BY NetTCP_RxPktConnIsValidAck() : ----- * ---- RETURNED BY NetTCP_RxPktConnIsValidReset() : ---- * NET_TCP_ERR_CONN_NOT_USED TCP connection NOT currently used. * NET_TCP_ERR_INVALID_CONN_STATE Invalid TCP connection state. * NET_TCP_ERR_INVALID_LEN_SEG Invalid TCP sequence-segment length. * * ----- RETURNED BY NetTCP_RxPktConnHandlerSeg() : ----- * NET_TCP_ERR_CONN_DATA_NONE Received packet successfully handled; but NO * data to queue to receive queue(s). * NET_TCP_ERR_CONN_DATA_VALID Received packet successfully handled & valid * data queued for processing. * NET_TCP_ERR_CONN_DATA_INVALID Received packet contains invalid segment * data; NOT queued to receive queue(s). * NET_TCP_ERR_CONN_DATA_DUP Received packet contains duplicate segment * data; NOT queued to receive queue(s). * * NET_ERR_INIT_INCOMPLETE Network initialization NOT complete. * * NET_TCP_ERR_INVALID_CONN_OP Invalid TCP connection operation. * * NET_TCP_ERR_RX_Q_FULL TCP connection receive queue full. * NET_TCP_ERR_RX_Q_SIGNAL_FAULT TCP connection receive queue signal fault. * NET_TCP_ERR_TX_Q_SIGNAL_FAULT TCP connection transmit queue signal fault. * NET_TCP_ERR_RE_TX_SEG_TH TCP connection closed due to excessive retransmission. * NET_TCP_ERR_TX TCP transmit error. * * ---------- RETURNED BY NetTCP_TxConnTxQ() : ---------- * NET_TCP_ERR_CONN_CLOSE TCP connection closed. * NET_TCP_ERR_CONN_FAULT TCP connection fault; connection(s) aborted. * NET_TCP_ERR_NONE_AVAIL Resources NOT available. * NET_CONN_ERR_INVALID_FAMILY Invalid connection family. * NET_CONN_ERR_INVALID_ADDR Invalid TCP connection address. * NET_CONN_ERR_INVALID_ADDR_LEN Invalid TCP connection address length. * * Return(s) : none. * * Caller(s) : NetTCP_RxPktConnHandler(). *$PAGE* * Note(s) : (2) TCP connections in the CLOSE-WAIT state are handled as follows : * * (a) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check Sequence Number' * states that in the "SYN-RECEIVED, ESTABLISHED STATE, FIN-WAIT-1 STATE, FIN-WAIT-2 * STATE, CLOSE-WAIT STATE, CLOSING STATE, LAST-ACK STATE, TIME-WAIT STATE" that ... * * (1) "Segments are processed in sequence ... processing is done in SEG.SEQ order." * * (2) "Initial tests on arrival are used to discard old duplicates." * * (3) "If a segment's contents straddle the boundary between old and new, only the * new parts should be processed." * * (4) (A) "If an incoming segment is not acceptable," ... * * (B) "an acknowledgment should be sent in reply" ... * * (C) "(unless the RST bit is set, if so drop the segment)". * * See also Notes #2b2Aa & #2b2B. * * See also 'NetTCP_RxPktConnIsValidSeq() Note #1d'. * * (b) (1) (A) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check RST Bit' states * that in the "ESTABLISHED, FIN-WAIT-1, FIN-WAIT-2, CLOSE-WAIT" states that "if the * RST bit is set then, any outstanding RECEIVEs and SEND[s] should receive 'reset' * responses. All segment queues should be flushed. Users should also receive an * unsolicited general 'connection reset' signal[, and] enter the CLOSED state". * * (B) RFC #793, Section 3.4 'Establishing a Connection : Reset Processing' reiterates * that "if the receiver ... of a RST ... was in any other state [other than LISTEN * or SYN-RECEIVED], it aborts the connection and advises the user and goes to the * CLOSED state". * * (2) (A) RFC Draft-IETF-TCPm-TCPSecure #00, Section 2.2 amends the "handling of a segment * with the RST bit when in a synchronized state" to "provide some protection against * ... blind reset attack[s] using the RST bit" : * * (a) "If the RST bit is set and the sequence number is outside the expected * window, silently drop the segment." * * (b) "If the RST bit is exactly the next expected sequence number [sic], reset * the connection"; it is assumed that this should read "if the RST bit is * set and the sequence number is exactly the next expected sequence number, * reset the connection." * * (c) "If the RST bit is set and the sequence number does not exactly match * the next expected sequence value, yet is within the acceptable window * (RCV.NXT <= SEG.SEQ < RCV.NXT+RCV.WND) send an acknowledgment." * * (B) ???? In addition, RFC Draft-IETF-TCPm-TCPSecure #00 does NOT provide a precedence * priority for handling TCP segments received with BOTH the RST & SYN bits set. * * ???? Therefore, since it does NOT seem reasonable to reset a TCP connection * due to a TCP segment that also attempted to synchronize the TCP connection, * it is assumed that the amended handling of the SYN bit should take precedence * over the amended handling of the RST bit. * * See also Note #2c2. * * (c) (1) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check SYN Bit' * states that in the "SYN-RECEIVED [STATE], ESTABLISHED STATE, FIN-WAIT STATE-1, * FIN-WAIT STATE-2, CLOSE-WAIT STATE, CLOSING STATE, LAST-ACK STATE, TIME-WAIT * STATE" that ... * * (A) "If the SYN is in the window it is an error, send a reset, any outstanding * RECEIVEs and SEND[s] should receive 'reset' responses, all segment queues * should be flushed, the user should also receive an unsolicited general * 'connection reset' signal[, and] enter the CLOSED state." * * (B) But "if the SYN is not in the window this step would not have been reached * and an ack would have been sent". * * (2) HOWEVER, RFC Draft-IETF-TCPm-TCPSecure #00, Section 3.2 amends the "handling * of a segment with the SYN bit set in the synchronized state ... [by] handling * ... the SYN bit" as follows : * * (a) "If the SYN bit is set and the sequence number is outside the * expected window, send an ACK back to the peer." * * (b) "If the SYN bit is set and the sequence number is an exact * match to the next expected sequence (RCV.NXT == SEG.SEQ) * then send an ACK segment ... but ... subtract one from * value being acknowledged." * * (c) "If the SYN bit is set and the sequence number is acceptable, * i.e.: (RCV.NXT <= SEG.SEQ <= RCV.NXT+RCV.WND) then send an * ACK segment." *$PAGE* * (d) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check ACK Field' * states that ... : * * (1) "If the ACK bit is off drop the segment." * * (2) "If the ACK bit is on" ... * * (A) And in the "ESTABLISHED STATE" ... * * (1) "If SND.UNA < SEG.ACK <= SND.NXT then" ... * * (a) "set SND.UNA <- SEG.ACK." * * (b) "Any segments on the retransmission queue which are thereby entirely * acknowledged are removed." * * (c) "the send window should be updated" : * * (1) (A) (1) "If ((SND.WL1 < SEG.SEQ) or" ... * * (2) (a) "(SND.WL1 = SEG.SEQ and" ... * (b) "SND.WL2 <= SEG.ACK))," ... * * (B) (1) "set SND.WND <- SEG.WND," ... * (2) "set SND.WL1 <- SEG.SEQ," ... * (3) "set SND.WL2 <- SEG.ACK." * * (2) "Note that SND.WND is an offset from SND.UNA, that SND.WL1 records the * sequence number of the last segment used to update SND.WND, and that * SND.WL2 records the acknowledgment number of the last segment used to * update SND.WND. The check here preveents using old segments to update * the window." * * (2) (a) "If the ACK is a duplicate (SEG.ACK <= SND.UNA), it can be ignored." * * (b) RFC #1122, Section 4.2.2.20.(g) amends the transmit window update criteria * for the segment's acknowledgement to include SND.UNA : "The window should * updated if SND.UNA <= SEG.ACK <= SND.NXT." * * See also 'NetTCP_RxPktConnHandlerTxWinRemote() Note #1b2'. * * (3) "If the ACK acks something not yet sent (SEG.ACK > SND.NXT) then send an ACK * [and] drop the segment." * * (B) For the "CLOSE-WAIT STATE ... do the same processing as for the ESTABLISHED state". * * (e) (1) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Process Segment Text' * states that in the "CLOSE-WAIT STATE, CLOSING STATE, LAST-ACK STATE, TIME-WAIT * STATE" that "this should not occur, since a FIN has been received from the remote * side. Ignore the segment text." * * (2) (A) However, it is possible that some but NOT all data has been received from the * remote host. In other words, the remote host's close request may have been * received but ALL receive data preceding the close request may NOT have been * received. * * ???? Therefore, receive data validated by sequence number as within the receive * window MUST be received & processed as in connected states. * * (B) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Process Segment * Text' states that "once in the ESTABLISHED state, it is possible to deliver * segment text to user RECEIVE buffers". * * See also Note #2d2B. *$PAGE* * (f) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check FIN Bit' states * that "if the FIN bit is set" ... * * (1) "signal the user 'connection closing' and return any pending RECEIVEs with * same message," ... * * (2) "advance RCV.NXT over the FIN," ... * * (3) "send an acknowledgment for the FIN" ... * * (4) "FIN implies PUSH for any segment text not yet delivered to the user" ... * * (5) And the "CLOSE-WAIT STATE ... remain[s] in the CLOSE-WAIT state". * * (3) (a) RFC #793, Section 3.9 'Event Processing : USER TIMEOUT : USER TIMEOUT' states that * "for any state if the user timeout expires, flush all queues, signal the user * 'error : connection aborted due to user timeout' ... [and] enter the CLOSED state". * * (b) However, NO RFC specifies or suggests any mechanism to implement/handle user timeouts. * * ???? Therefore, it is assumed that ANY TCP connection that receives a valid TCP data * or control segment should reset its user connection timer. ********************************************************************************************************* */ static void NetTCP_RxPktConnHandlerCloseWait (NET_TCP_CONN *pconn, NET_BUF *pbuf, NET_BUF_HDR *pbuf_hdr, NET_ERR *perr) { NET_TCP_SEQ_CODE seq_code; NET_TCP_ACK_CODE ack_code; NET_TCP_RESET_CODE reset_code; NET_TCP_TIMEOUT_SEC timeout_sec; NET_TMR_TICK timeout_tick; NET_ERR err; NET_ERR err_rtn; /*$PAGE*/ /* -------------- VALIDATE RX'D TCP PKT --------------- */ /* Chk for rx'd fin/close. */ if (pbuf_hdr->TCP_SegClose == DEF_YES) { /* If fin/close avail, update seg lens. */ pbuf_hdr->TCP_SegLenInit += NET_TCP_SEG_LEN_CLOSE; pbuf_hdr->TCP_SegLen += NET_TCP_SEG_LEN_CLOSE; } /* Chk rx'd seq nbr. */ seq_code = NetTCP_RxPktConnIsValidSeq(pconn, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return; } switch (seq_code) { case NET_TCP_CONN_RX_SEQ_VALID: break; case NET_TCP_CONN_RX_SEQ_SYNC: /* If invalid sync rx'd, ... */ case NET_TCP_CONN_RX_SEQ_SYNC_INVALID: /* ... tx TCP conn ack (see Notes #2c2 & #2b2B). */ NetTCP_TxConnAck(pconn, pbuf_hdr, NET_TCP_CONN_TX_ACK_FAULT, NET_TCP_CONN_CLOSE_ALL, &err); *perr = NET_TCP_ERR_CONN_SEQ_SYNC_INVALID; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_RX_SEQ_NONE: case NET_TCP_CONN_RX_SEQ_INVALID: /* If invalid seq rx'd (see Note #2a4A), ... */ default: if (pbuf_hdr->TCP_SegReset != DEF_YES) { /* ... & reset NOT rx'd (see Note #2a4C), ... */ /* ... tx TCP conn ack (see Note #2a4B). */ NetTCP_TxConnAck(pconn, pbuf_hdr, NET_TCP_CONN_TX_ACK_FAULT, NET_TCP_CONN_CLOSE_ALL, &err); } *perr = NET_TCP_ERR_CONN_SEQ_INVALID; return; /* Prevent 'break NOT reachable' compiler warning. */ } /* Chk for rx'd reset. */ reset_code = NetTCP_RxPktConnIsValidReset(pconn, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return; } switch (reset_code) { case NET_TCP_CONN_RX_RESET_NONE: break; case NET_TCP_CONN_RX_RESET_VALID: /* If valid reset rx'd, ... */ /* ... close TCP conn (see Note #2b1A). */ NetTCP_ConnClose(pconn, pbuf_hdr, pconn->ConnCloseAppFlag, NET_TCP_CONN_CLOSE_ALL); *perr = NET_TCP_ERR_CONN_RESET_VALID; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_RX_RESET_INVALID: /* If invalid reset rx'd, ... */ default: /* ... tx TCP conn ack (see Note #2b2Ac). */ NetTCP_TxConnAck(pconn, pbuf_hdr, NET_TCP_CONN_TX_ACK_FAULT, NET_TCP_CONN_CLOSE_ALL, &err); *perr = NET_TCP_ERR_CONN_RESET_INVALID; return; /* Prevent 'break NOT reachable' compiler warning. */ } /* Chk for rx'd ack. */ ack_code = NetTCP_RxPktConnIsValidAck(pconn, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return; } switch (ack_code) { case NET_TCP_CONN_RX_ACK_VALID: case NET_TCP_CONN_RX_ACK_DUP: case NET_TCP_CONN_RX_ACK_PREV: break; case NET_TCP_CONN_RX_ACK_NONE: /* If NO ack rx'd, ... */ *perr = NET_TCP_ERR_CONN_ACK_NONE; /* ... ignore TCP pkt (see Note #2d1). */ return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_RX_ACK_INVALID: /* If invalid ack rx'd, ... */ default: /* ... tx TCP conn ack (see Note #2d2A3). */ NetTCP_TxConnAck(pconn, pbuf_hdr, NET_TCP_CONN_TX_ACK_FAULT, NET_TCP_CONN_CLOSE_ALL, &err); *perr = NET_TCP_ERR_CONN_ACK_INVALID; return; /* Prevent 'break NOT reachable' compiler warning. */ } /*$PAGE*/ /* ----------------- HANDLE RX'D SEG ------------------ */ NetTCP_RxPktConnHandlerSeg(pconn, ack_code, pbuf, pbuf_hdr, &err_rtn); switch (err_rtn) { case NET_TCP_ERR_CONN_DATA_NONE: case NET_TCP_ERR_CONN_DATA_VALID: break; case NET_TCP_ERR_CONN_DATA_DUP: /* ???? Allow dup data to update TCP conn? */ *perr = err_rtn; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_ERR_INIT_INCOMPLETE: case NET_TCP_ERR_CONN_NOT_USED: case NET_TCP_ERR_CONN_FAIL: case NET_TCP_ERR_CONN_ACK_INVALID: case NET_TCP_ERR_CONN_DATA_INVALID: case NET_TCP_ERR_INVALID_CONN_STATE: case NET_TCP_ERR_INVALID_CONN_OP: case NET_TCP_ERR_INVALID_LEN_SEG: case NET_TCP_ERR_RX_Q_FULL: case NET_TCP_ERR_RX_Q_SIGNAL_FAULT: case NET_TCP_ERR_TX: case NET_TCP_ERR_TX_Q_SIGNAL_FAULT: case NET_TCP_ERR_RE_TX_SEG_TH: default: NetTCP_ConnClose(pconn, pbuf_hdr, pconn->ConnCloseAppFlag, NET_TCP_CONN_CLOSE_ALL); *perr = err_rtn; return; /* Prevent 'break NOT reachable' compiler warning. */ } /* ----------------- UPDATE TCP CONN ------------------ */ /* -------------------- UPDATE TMR -------------------- */ timeout_sec = pconn->TimeoutConn_sec; /* Reset conn close tmr (see Note #3b). */ timeout_tick = (NET_TMR_TICK)timeout_sec * NET_TMR_TIME_TICK_PER_SEC; if (pconn->TimeoutTmr != (NET_TMR *)0) { NetTmr_Set((NET_TMR *) pconn->TimeoutTmr, (CPU_FNCT_PTR) NetTCP_ConnCloseTimeout, (NET_TMR_TICK) timeout_tick, (NET_ERR *)&err); } else { pconn->TimeoutTmr = NetTmr_Get((void *) pconn, (CPU_FNCT_PTR) NetTCP_ConnCloseTimeout, (NET_TMR_TICK) timeout_tick, (CPU_INT16U ) NET_TMR_FLAG_NONE, (NET_ERR *)&err); } if ( err != NET_TMR_ERR_NONE) { NetTCP_ConnClose(pconn, pbuf_hdr, pconn->ConnCloseAppFlag, NET_TCP_CONN_CLOSE_ALL); *perr = NET_TCP_ERR_CONN_FAIL; return; } /*$PAGE*/ /* --------------- TX TCP CONN ACK/DATA --------------- */ ack_code = ((pbuf_hdr->TCP_SegAckTxReqCode == NET_TCP_CONN_TX_ACK_IMMED) || (pbuf_hdr->TCP_SegClose == DEF_YES)) /* See Note #2f3. */ ? NET_TCP_CONN_TX_ACK_IMMED : NET_TCP_CONN_TX_ACK; /* Tx ack & any tx data (see Note #1c). */ NetTCP_TxConnTxQ(pconn, pbuf_hdr, ack_code, DEF_NO, NET_TCP_CONN_CLOSE_ALL, perr); switch (*perr) { case NET_TCP_ERR_NONE: case NET_TCP_ERR_CONN_ACK_NONE: case NET_TCP_ERR_CONN_ACK_INVALID: case NET_TCP_ERR_CONN_ACK_DLYD: case NET_TCP_ERR_CONN_ACK_PREVLY_TXD: case NET_ERR_TX: /* Ignore transitory tx err(s). */ break; case NET_ERR_INIT_INCOMPLETE: case NET_TCP_ERR_CONN_NOT_USED: case NET_TCP_ERR_CONN_CLOSE: case NET_TCP_ERR_CONN_FAULT: case NET_TCP_ERR_CONN_FAIL: case NET_TCP_ERR_INVALID_CONN_STATE: case NET_TCP_ERR_INVALID_CONN_OP: case NET_TCP_ERR_INVALID_LEN_SEG: case NET_TCP_ERR_NONE_AVAIL: case NET_TCP_ERR_TX: case NET_CONN_ERR_INVALID_FAMILY: case NET_CONN_ERR_INVALID_ADDR: case NET_CONN_ERR_INVALID_ADDR_LEN: default: return; /* Prevent 'break NOT reachable' compiler warning. */ } *perr = err_rtn; } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_RxPktConnHandlerLastAck() * * Description : (1) Handle TCP connection in LAST-ACK state : * * (a) Validate received TCP packet for current TCP connection state : * * (1) Sequence Number (SEQ) See Note #2a * (2) Reset (RST) See Note #2b * (3) Synchronization (SYN) See Note #2c * (4) Acknowledgement (ACK) See Note #2d * (5) Finish/Close (FIN) See Note #2f * * (b) Update TCP connection : * (1) Handle received TCP segment See Notes #2d & #2e * (2) Update TCP connection state : See Notes #2d2A1a & #2d2A1b2A * (A) Configure TCP connection timeout value * (3) Update TCP connection timer See Notes #2d2A1b2B & #3 * * (c) Transmit TCP connection acknowledgement & data See Notes #2d2B & #2f3 * * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_RxPktConnHandler(). * * pbuf Pointer to network buffer that received TCP packet. * ---- Argument checked in NetTCP_Rx(). * * pbuf_hdr Pointer to network buffer header that received TCP packet. * -------- Argument validated in NetTCP_Rx(). * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_CONN_SEQ_SYNC_INVALID Received segment's synchronization is * NOT valid for current TCP connection. * NET_TCP_ERR_CONN_SEQ_INVALID Received segment's sequence number is * NOT valid for current TCP connection. * NET_TCP_ERR_CONN_ACK_NONE Received segment's acknowledgement is * NOT available. * NET_TCP_ERR_CONN_ACK_INVALID Received segment's acknowledgement number is * NOT valid for current TCP connection. * NET_TCP_ERR_CONN_RESET_VALID Received segment's reset flag is valid * for current TCP connection; i.e. reset * the TCP connection. * NET_TCP_ERR_CONN_RESET_INVALID Received segment's reset flag is NOT valid * for current TCP connection. * * NET_TCP_ERR_CONN_CLOSED TCP connection successfully closed. * NET_TCP_ERR_CONN_FAIL TCP connection operation(s) failed. * * ----- RETURNED BY NetTCP_RxPktConnIsValidSeq() : ----- * ----- RETURNED BY NetTCP_RxPktConnIsValidAck() : ----- * ---- RETURNED BY NetTCP_RxPktConnIsValidReset() : ---- * NET_TCP_ERR_CONN_NOT_USED TCP connection NOT currently used. * NET_TCP_ERR_INVALID_CONN_STATE Invalid TCP connection state. * NET_TCP_ERR_INVALID_LEN_SEG Invalid TCP sequence-segment length. * * ----- RETURNED BY NetTCP_RxPktConnHandlerSeg() : ----- * NET_TCP_ERR_CONN_DATA_NONE Received packet successfully handled; but NO * data to queue to receive queue(s). * NET_TCP_ERR_CONN_DATA_VALID Received packet successfully handled & valid * data queued for processing. * NET_TCP_ERR_CONN_DATA_INVALID Received packet contains invalid segment * data; NOT queued to receive queue(s). * NET_TCP_ERR_CONN_DATA_DUP Received packet contains duplicate segment * data; NOT queued to receive queue(s). * * NET_ERR_INIT_INCOMPLETE Network initialization NOT complete. * * NET_TCP_ERR_INVALID_CONN_OP Invalid TCP connection operation. * * NET_TCP_ERR_RX_Q_FULL TCP connection receive queue full. * NET_TCP_ERR_RX_Q_SIGNAL_FAULT TCP connection receive queue signal fault. * NET_TCP_ERR_TX_Q_SIGNAL_FAULT TCP connection transmit queue signal fault. * NET_TCP_ERR_RE_TX_SEG_TH TCP connection closed due to excessive retransmission. * * ---------- RETURNED BY NetTCP_TxConnTxQ() : ---------- * NET_TCP_ERR_CONN_CLOSE TCP connection closed. * NET_TCP_ERR_CONN_FAULT TCP connection fault; connection(s) aborted. * NET_TCP_ERR_NONE_AVAIL Resources NOT available. * NET_CONN_ERR_INVALID_FAMILY Invalid connection family. * NET_CONN_ERR_INVALID_ADDR Invalid TCP connection address. * NET_CONN_ERR_INVALID_ADDR_LEN Invalid TCP connection address length. * * Return(s) : none. * * Caller(s) : NetTCP_RxPktConnHandler(). *$PAGE* * Note(s) : (2) TCP connections in the LAST-ACK state are handled as follows : * * (a) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check Sequence Number' * states that in the "SYN-RECEIVED, ESTABLISHED STATE, FIN-WAIT-1 STATE, FIN-WAIT-2 * STATE, CLOSE-WAIT STATE, CLOSING STATE, LAST-ACK STATE, TIME-WAIT STATE" that ... * * (1) "Segments are processed in sequence ... processing is done in SEG.SEQ order." * * (2) "Initial tests on arrival are used to discard old duplicates." * * (3) "If a segment's contents straddle the boundary between old and new, only the * new parts should be processed." * * (4) (A) "If an incoming segment is not acceptable," ... * * (B) "an acknowledgment should be sent in reply" ... * * (C) "(unless the RST bit is set, if so drop the segment)". * * See also Notes #2b2Aa & #2b2B. * * See also 'NetTCP_RxPktConnIsValidSeq() Note #1d'. * * (b) (1) (A) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check RST Bit' states * that in the "ESTABLISHED, FIN-WAIT-1, FIN-WAIT-2, CLOSE-WAIT" states that "if the * RST bit is set then, any outstanding RECEIVEs and SEND[s] should receive 'reset' * responses. All segment queues should be flushed. Users should also receive an * unsolicited general 'connection reset' signal[, and] enter the CLOSED state". * * (B) RFC #793, Section 3.4 'Establishing a Connection : Reset Processing' reiterates * that "if the receiver ... of a RST ... was in any other state [other than LISTEN * or SYN-RECEIVED], it aborts the connection and advises the user and goes to the * CLOSED state". * * (2) (A) RFC Draft-IETF-TCPm-TCPSecure #00, Section 2.2 amends the "handling of a segment * with the RST bit when in a synchronized state" to "provide some protection against * ... blind reset attack[s] using the RST bit" : * * (a) "If the RST bit is set and the sequence number is outside the expected * window, silently drop the segment." * * (b) "If the RST bit is exactly the next expected sequence number [sic], reset * the connection"; it is assumed that this should read "if the RST bit is * set and the sequence number is exactly the next expected sequence number, * reset the connection." * * (c) "If the RST bit is set and the sequence number does not exactly match * the next expected sequence value, yet is within the acceptable window * (RCV.NXT <= SEG.SEQ < RCV.NXT+RCV.WND) send an acknowledgment." * * (B) ???? In addition, RFC Draft-IETF-TCPm-TCPSecure #00 does NOT provide a precedence * priority for handling TCP segments received with BOTH the RST & SYN bits set. * * ???? Therefore, since it does NOT seem reasonable to reset a TCP connection * due to a TCP segment that also attempted to synchronize the TCP connection, * it is assumed that the amended handling of the SYN bit should take precedence * over the amended handling of the RST bit. * * See also Note #2c2. * * (c) (1) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check SYN Bit' * states that in the "SYN-RECEIVED [STATE], ESTABLISHED STATE, FIN-WAIT STATE-1, * FIN-WAIT STATE-2, CLOSE-WAIT STATE, CLOSING STATE, LAST-ACK STATE, TIME-WAIT * STATE" that ... * * (A) "If the SYN is in the window it is an error, send a reset, any outstanding * RECEIVEs and SEND[s] should receive 'reset' responses, all segment queues * should be flushed, the user should also receive an unsolicited general * 'connection reset' signal[, and] enter the CLOSED state." * * (B) But "if the SYN is not in the window this step would not have been reached * and an ack would have been sent". * * (2) HOWEVER, RFC Draft-IETF-TCPm-TCPSecure #00, Section 3.2 amends the "handling * of a segment with the SYN bit set in the synchronized state ... [by] handling * ... the SYN bit" as follows : * * (a) "If the SYN bit is set and the sequence number is outside the * expected window, send an ACK back to the peer." * * (b) "If the SYN bit is set and the sequence number is an exact * match to the next expected sequence (RCV.NXT == SEG.SEQ) * then send an ACK segment ... but ... subtract one from * value being acknowledged." * * (c) "If the SYN bit is set and the sequence number is acceptable, * i.e.: (RCV.NXT <= SEG.SEQ <= RCV.NXT+RCV.WND) then send an * ACK segment." *$PAGE* * (d) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check ACK Field' * states that ... : * * (1) "If the ACK bit is off drop the segment." * * (2) "If the ACK bit is on [and in the] LAST-ACK STATE" ... * * (A) "The only thing that can arrive in this state is an acknowledgement of our FIN." * * (1) "If our FIN is now acknowledged" ... * * (a) "enter the CLOSED state." * * (b) However, it is possible that some closing received data from the remote * host is available but has NOT yet been received by the application layer. * * (1) Therefore, if the application receive queue is closed, then close the * TCP connection &/or enter the CLOSED state. * * (2) (A) But if the application receive queue is NOT closed, then enter the * connection-closing-data-available state to allow the application * layer to receive the remaining receive data. * * (B) To provide the application layer sufficient time to receive the * closing received data, the user connection timeout is used as a * connection closing timeout. * * See also Note #2e2. * * (2) (a) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check ACK Field : * FIN-WAIT-2 STATE' states that "if the retransmission queue is empty, the * user's CLOSE can be acknowledged". * * (b) However, TCP connection should signal the application layer that "the user's * close [is] acknowledged" whenever its re-transmit queue becomes &/or is * empty : * * (1) Transition from LAST-ACK to CLOSED * * See also 'NetTCP_RxPktConnHandlerSignalClose() Note #1'. * * (B) However, it is possible that some but NOT all transmitted data has been received * by the remote host. In other words, the remote host may have received some but * NOT ALL receive data preceding this host's close request. * * ???? Therefore, acknowledgements validated as within the transmit window MUST be * received & processed as in connected states. * * See also 'NetTCP_RxPktConnIsValidAck() Note #1d'. * * (e) (1) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Process Segment Text' * states that in the "CLOSE-WAIT STATE, CLOSING STATE, LAST-ACK STATE, TIME-WAIT * STATE" that "this should not occur, since a FIN has been received from the remote * side. Ignore the segment text." * * (2) (A) However, it is possible that some but NOT all data has been received from * the remote host. In other words, the remote host's close request may have * received but ALL receive data preceding the close request may NOT have been * received. * * ???? Therefore, receive data validated by sequence number as within the * receive window MUST be received & processed as in connected states. * * (B) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Process Segment * Text' states that "once in the ESTABLISHED state, it is possible to deliver * segment text to user RECEIVE buffers". * * See also Note #2d2A1b. * * (f) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check FIN Bit' states * that "if the FIN bit is set" ... * * (1) "signal the user 'connection closing' and return any pending RECEIVEs with * same message," ... * * (2) "advance RCV.NXT over the FIN," ... * * (3) "send an acknowledgment for the FIN" ... * * (4) "FIN implies PUSH for any segment text not yet delivered to the user" ... * * (5) And the "LAST-ACK STATE ... remain[s] in the LAST-ACK state". *$PAGE* * (3) (a) RFC #793, Section 3.9 'Event Processing : USER TIMEOUT : USER TIMEOUT' states that * "for any state if the user timeout expires, flush all queues, signal the user * 'error : connection aborted due to user timeout' ... [and] enter the CLOSED state". * * (b) (1) However, a TCP connection enters the LAST-ACK state ONLY after the remote host * has initiated an active close & enters the TIME-WAIT state, waiting for its * time-wait timer to expire before closing its TCP connection. * * (2) ???? Therefore, a TCP connection in the LAST-ACK state should NOT wait for its * last acknowledgement from the remote host in the TIME-WAIT state longer than * the remote host's time-wait timeout of two TCP maximum segment lifetimes. * * (3) ???? Therefore, it is assumed that ANY TCP connection in the LAST-ACK state * that receives a valid TCP data or control segment should reset its time-wait * timer for two TCP maximum segment lifetimes. * * (4) Since the mechanisms of TCP connection close are independent of the application layer * close; any external application layer close error(s) are ignored. ********************************************************************************************************* */ static void NetTCP_RxPktConnHandlerLastAck (NET_TCP_CONN *pconn, NET_BUF *pbuf, NET_BUF_HDR *pbuf_hdr, NET_ERR *perr) { NET_TCP_SEQ_CODE seq_code; NET_TCP_ACK_CODE ack_code; NET_TCP_RESET_CODE reset_code; CPU_BOOLEAN data_avail; CPU_BOOLEAN close_conn; NET_TCP_TIMEOUT_SEC timeout_sec; NET_TMR_TICK timeout_tick; NET_ERR err; NET_ERR err_rtn; /*$PAGE*/ /* -------------- VALIDATE RX'D TCP PKT --------------- */ /* Chk for rx'd fin/close. */ if (pbuf_hdr->TCP_SegClose == DEF_YES) { /* If fin/close avail, update seg lens. */ pbuf_hdr->TCP_SegLenInit += NET_TCP_SEG_LEN_CLOSE; pbuf_hdr->TCP_SegLen += NET_TCP_SEG_LEN_CLOSE; } /* Chk rx'd seq nbr. */ seq_code = NetTCP_RxPktConnIsValidSeq(pconn, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return; } switch (seq_code) { case NET_TCP_CONN_RX_SEQ_VALID: break; case NET_TCP_CONN_RX_SEQ_SYNC: /* If invalid sync rx'd, ... */ case NET_TCP_CONN_RX_SEQ_SYNC_INVALID: /* ... tx TCP conn ack (see Notes #2c2 & #2b2B). */ NetTCP_TxConnAck(pconn, pbuf_hdr, NET_TCP_CONN_TX_ACK_FAULT, NET_TCP_CONN_CLOSE_ALL, &err); *perr = NET_TCP_ERR_CONN_SEQ_SYNC_INVALID; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_RX_SEQ_NONE: case NET_TCP_CONN_RX_SEQ_INVALID: /* If invalid seq rx'd (see Note #2a4A), ... */ default: if (pbuf_hdr->TCP_SegReset != DEF_YES) { /* ... & reset NOT rx'd (see Note #2a4C), ... */ /* ... tx TCP conn ack (see Note #2a4B). */ NetTCP_TxConnAck(pconn, pbuf_hdr, NET_TCP_CONN_TX_ACK_FAULT, NET_TCP_CONN_CLOSE_ALL, &err); } *perr = NET_TCP_ERR_CONN_SEQ_INVALID; return; /* Prevent 'break NOT reachable' compiler warning. */ } /* Chk for rx'd reset. */ reset_code = NetTCP_RxPktConnIsValidReset(pconn, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return; } switch (reset_code) { case NET_TCP_CONN_RX_RESET_NONE: break; case NET_TCP_CONN_RX_RESET_VALID: /* If valid reset rx'd, ... */ /* ... close TCP conn (see Note #2b1A). */ NetTCP_ConnClose(pconn, pbuf_hdr, pconn->ConnCloseAppFlag, NET_TCP_CONN_CLOSE_ALL); *perr = NET_TCP_ERR_CONN_RESET_VALID; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_RX_RESET_INVALID: /* If invalid reset rx'd, ... */ default: /* ... tx TCP conn ack (see Note #2b2Ac). */ NetTCP_TxConnAck(pconn, pbuf_hdr, NET_TCP_CONN_TX_ACK_FAULT, NET_TCP_CONN_CLOSE_ALL, &err); *perr = NET_TCP_ERR_CONN_RESET_INVALID; return; /* Prevent 'break NOT reachable' compiler warning. */ } /* Chk for rx'd ack. */ ack_code = NetTCP_RxPktConnIsValidAck(pconn, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return; } switch (ack_code) { case NET_TCP_CONN_RX_ACK_VALID: case NET_TCP_CONN_RX_ACK_DUP: case NET_TCP_CONN_RX_ACK_PREV: break; case NET_TCP_CONN_RX_ACK_NONE: /* If NO ack rx'd, ... */ *perr = NET_TCP_ERR_CONN_ACK_NONE; /* ... ignore TCP pkt (see Note #2d1). */ return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_RX_ACK_INVALID: /* If invalid ack rx'd, ... */ default: /* ... tx TCP conn ack (see Note #2d2B). */ NetTCP_TxConnAck(pconn, pbuf_hdr, NET_TCP_CONN_TX_ACK_FAULT, NET_TCP_CONN_CLOSE_ALL, &err); *perr = NET_TCP_ERR_CONN_ACK_INVALID; return; /* Prevent 'break NOT reachable' compiler warning. */ } /*$PAGE*/ /* ----------------- HANDLE RX'D SEG ------------------ */ NetTCP_RxPktConnHandlerSeg(pconn, ack_code, pbuf, pbuf_hdr, &err_rtn); switch (err_rtn) { case NET_TCP_ERR_CONN_DATA_NONE: case NET_TCP_ERR_CONN_DATA_VALID: break; case NET_TCP_ERR_CONN_DATA_DUP: /* ???? Allow dup data to update TCP conn? */ *perr = err_rtn; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_ERR_INIT_INCOMPLETE: case NET_TCP_ERR_CONN_NOT_USED: case NET_TCP_ERR_CONN_FAIL: case NET_TCP_ERR_CONN_ACK_INVALID: case NET_TCP_ERR_CONN_DATA_INVALID: case NET_TCP_ERR_INVALID_CONN_STATE: case NET_TCP_ERR_INVALID_CONN_OP: case NET_TCP_ERR_INVALID_LEN_SEG: case NET_TCP_ERR_RX_Q_FULL: case NET_TCP_ERR_RX_Q_SIGNAL_FAULT: case NET_TCP_ERR_TX_Q_SIGNAL_FAULT: case NET_TCP_ERR_RE_TX_SEG_TH: default: NetTCP_ConnClose(pconn, pbuf_hdr, pconn->ConnCloseAppFlag, NET_TCP_CONN_CLOSE_ALL); *perr = err_rtn; return; /* Prevent 'break NOT reachable' compiler warning. */ } /* ----------------- UPDATE TCP CONN ------------------ */ close_conn = DEF_NO; if (pconn->RxQ_State == NET_TCP_RX_Q_STATE_CONN_CLOSED) { /* If remote conn closed (see #2e2A) ... */ /* ... & local conn close ack'd (see #2d2A1) ... */ if (pconn->TxQ_State == NET_TCP_TX_Q_STATE_CONN_CLOSED) { close_conn = DEF_YES; } } if (close_conn == DEF_YES) { /* Closing data avail for half-closed conns ONLY. */ data_avail = ((pconn->ConnCloseCode != NET_CONN_CLOSE_HALF ) || ((pconn->RxQ_State == NET_TCP_RX_Q_STATE_CONN_CLOSED) && (pconn->RxQ_App_Head == (NET_BUF *)0))) ? DEF_NO : DEF_YES; /* ... signal app conn close (see Note #2d2A2b1); ... */ /* Ignore any app conn close err(s) [see Note #4]. */ NetTCP_RxPktConnHandlerSignalClose(pconn, data_avail, &err); if (data_avail != DEF_YES) { /* ... & if NO app data avail, ... */ /* ... close TCP conn (see #2d2A1b1); ... */ NetTCP_ConnCloseHandler(pconn, pconn->ConnCloseAppFlag, NET_TCP_CONN_CLOSE_ALL); *perr = NET_TCP_ERR_CONN_CLOSED; return; } /* ... else chng to closing-data-avail state ... */ /* ... (see Note #2d2A1b2A), ... */ pconn->ConnState = NET_TCP_CONN_STATE_CLOSING_DATA_AVAIL; timeout_sec = pconn->TimeoutUser_sec; /* ... & set user tmr (see Notes #2d2A1b2B). */ } else { /* Else start time-wait tmr (see Note #3b3). */ timeout_sec = pconn->TimeoutMaxSeg_sec * NET_TCP_CONN_TIMEOUT_MAX_SEG_SCALAR; } /*$PAGE*/ /* -------------------- UPDATE TMR -------------------- */ timeout_tick = (NET_TMR_TICK)timeout_sec * NET_TMR_TIME_TICK_PER_SEC; if (pconn->TimeoutTmr != (NET_TMR *)0) { NetTmr_Set((NET_TMR *) pconn->TimeoutTmr, (CPU_FNCT_PTR) NetTCP_ConnCloseTimeout, (NET_TMR_TICK) timeout_tick, (NET_ERR *)&err); } else { pconn->TimeoutTmr = NetTmr_Get((void *) pconn, (CPU_FNCT_PTR) NetTCP_ConnCloseTimeout, (NET_TMR_TICK) timeout_tick, (CPU_INT16U ) NET_TMR_FLAG_NONE, (NET_ERR *)&err); } if ( err != NET_TMR_ERR_NONE) { NetTCP_ConnClose(pconn, pbuf_hdr, pconn->ConnCloseAppFlag, NET_TCP_CONN_CLOSE_ALL); *perr = NET_TCP_ERR_CONN_FAIL; return; } /* --------------- TX TCP CONN ACK/DATA --------------- */ ack_code = ((pbuf_hdr->TCP_SegAckTxReqCode == NET_TCP_CONN_TX_ACK_IMMED) || (pbuf_hdr->TCP_SegClose == DEF_YES)) /* See Note #2f3. */ ? NET_TCP_CONN_TX_ACK_IMMED : NET_TCP_CONN_TX_ACK; /* Tx ack & any tx data (see Note #1c). */ NetTCP_TxConnTxQ(pconn, pbuf_hdr, ack_code, DEF_NO, NET_TCP_CONN_CLOSE_ALL, perr); switch (*perr) { case NET_TCP_ERR_NONE: case NET_TCP_ERR_CONN_ACK_NONE: case NET_TCP_ERR_CONN_ACK_INVALID: case NET_TCP_ERR_CONN_ACK_DLYD: case NET_TCP_ERR_CONN_ACK_PREVLY_TXD: case NET_ERR_TX: /* Ignore transitory tx err(s). */ break; case NET_ERR_INIT_INCOMPLETE: case NET_TCP_ERR_CONN_NOT_USED: case NET_TCP_ERR_CONN_CLOSE: case NET_TCP_ERR_CONN_FAULT: case NET_TCP_ERR_CONN_FAIL: case NET_TCP_ERR_INVALID_CONN_STATE: case NET_TCP_ERR_INVALID_CONN_OP: case NET_TCP_ERR_INVALID_LEN_SEG: case NET_TCP_ERR_NONE_AVAIL: case NET_TCP_ERR_TX: case NET_CONN_ERR_INVALID_FAMILY: case NET_CONN_ERR_INVALID_ADDR: case NET_CONN_ERR_INVALID_ADDR_LEN: default: return; /* Prevent 'break NOT reachable' compiler warning. */ } *perr = err_rtn; } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_RxPktConnHandlerSeg() * * Description : (1) Handle TCP connection's received segments : * * (a) Update TCP connection transmit remote window See Note #5 * (b) Update TCP connection controls on transition to connected state(s) * (c) Handle TCP connection re-transmit queue See Notes #2b & #4 * (d) Update TCP connection transmit congestion controls See Note #6 * (e) Handle TCP connection receive queue(s) See Note #3 * (f) Handle TCP connection receive data See Notes #2a & #3 * * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_RxPktConnHandler(). * * ack_code Indicates the received segment's acknowledgement condition : * -------- * NET_TCP_CONN_RX_ACK_NONE NO received acknowledgement number. * * NET_TCP_CONN_RX_ACK_INVALID Received acknowledgement number * is invalid for the TCP connection. * * NET_TCP_CONN_RX_ACK_VALID Received acknowledgement number * is valid for the TCP connection. * * NET_TCP_CONN_RX_ACK_DUP Received acknowledgement number * is a duplicate for the * TCP connection. * * NET_TCP_CONN_RX_ACK_PREV Received acknowledgement number * is a previous duplicate for the * TCP connection. * * Argument validated in NetTCP_RxPktConnHandler() functions. * * pbuf Pointer to network buffer that received TCP packet. * ---- Argument checked in NetTCP_Rx(). * * pbuf_hdr Pointer to network buffer header that received TCP packet. * -------- Argument validated in NetTCP_Rx(). * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_CONN_ACK_INVALID Valid received segment acknowledgement NOT available. * * -- RETURNED BY NetTCP_RxPktConnHandlerTxWinRemote() : - * NET_TCP_ERR_CONN_NOT_USED TCP connection NOT currently used. * NET_TCP_ERR_INVALID_CONN_STATE Invalid TCP connection state. * NET_TCP_ERR_INVALID_CONN_OP Invalid TCP connection operation. * * ---- RETURNED BY NetTCP_RxPktConnHandlerReTxQ() : ----- * NET_TCP_ERR_CONN_FAIL TCP connection operation(s) failed. * NET_TCP_ERR_TX_Q_SIGNAL_FAULT TCP connection transmit queue signal fault. * * - RETURNED BY NetTCP_TxConnWinSizeHandlerCongCtrl() : - * NET_ERR_INIT_INCOMPLETE Network initialization NOT complete. * NET_TCP_ERR_RE_TX_SEG_TH TCP connection closed due to excessive retransmission. * NET_TCP_ERR_TX TCP transmit error. * * --- RETURNED BY NetTCP_RxPktConnHandlerRxQ_Sync() : --- * --- RETURNED BY NetTCP_RxPktConnHandlerRxQ_Conn() : --- * NET_TCP_ERR_CONN_DATA_NONE Received packet successfully handled; but NO data * to queue to receive queue(s). * NET_TCP_ERR_CONN_DATA_VALID Received packet successfully handled & valid data * queued for application. * * --- RETURNED BY NetTCP_RxPktConnHandlerRxQ_Conn() : --- * NET_TCP_ERR_CONN_DATA_INVALID Received packet contains invalid segment data; * NOT queued to receive queue(s). * NET_TCP_ERR_CONN_DATA_DUP Received packet contains duplicate segment data; * NOT queued to receive queue(s). * * NET_TCP_ERR_CONN_SEQ_INVALID Received segment's sequence number is NOT * valid for current TCP connection. * NET_TCP_ERR_INVALID_LEN_SEG Invalid TCP sequence-segment length. * * -- RETURNED BY NetTCP_RxPktConnHandlerRxQ_AppData() : - * NET_TCP_ERR_RX_Q_FULL TCP connection receive queue full. * NET_TCP_ERR_RX_Q_SIGNAL_FAULT TCP connection receive queue signal fault. * NET_TCP_ERR_RX_Q_SIGNAL_CLR TCP connection receive queue clear failed. * * Return(s) : none. * * Caller(s) : Various NetTCP_RxPktConnHandler() functions. *$PAGE* * Note(s) : (2) (a) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check Sequence Number' * states that "segments are processed in sequence ... processing is done in SEG.SEQ * order" (see also Note #3). * * (b) The following sections generalize that for the SYN-SENT & ESTABLISHED states that * "any segments on the retransmission queue which are ... acknowledged should be * removed" : * * (1) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : SYN-SENT [State] : * Check SYN Bit' * (2) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check ACK Field : * ESTABLISHED STATE' * * (3) TCP segments with receive data are sequenced into the appropriate TCP connection * receive queue(s) to be made available & ready to be read by the application layer. * * See also 'NetTCP_RxPktConnHandlerRxQ_Conn() Note #3' * & 'NetTCP_RxPktConnHandlerRxQ_AppData() Note #2'. * * (4) A TCP connection's re-transmit queue SHOULD be updated ONLY by valid received * acknowledgement segments. * * See also 'NetTCP_RxPktConnHandlerReTxQ() Note #3'. * * (5) RFC #1122, Section 4.2.2.20 generalizes that "when ... SND.UNA < SEG.ACK <= SND.NXT, * the send window should be updated". * * See also 'NetTCP_RxPktConnHandlerTxWinRemote() Note #1'. * * (6) RFC #2581, Section 3.1 states that "the slow start and congestion avoidance algorithms * ... [update] cwnd [TCP transmit congestion control window] for each ACK received that * acknowledges new data". * * (a) A TCP connection's transmit window congestion controls SHOULD be updated : * * (1) After any possible updating of the TCP connection re-transmit queue. * (2) Prior to any possible queuing of data segments onto the TCP connection * receive queue(s). ********************************************************************************************************* */ static void NetTCP_RxPktConnHandlerSeg (NET_TCP_CONN *pconn, NET_TCP_ACK_CODE ack_code, NET_BUF *pbuf, NET_BUF_HDR *pbuf_hdr, NET_ERR *perr) { #if ((NET_CTR_CFG_ERR_EN == DEF_ENABLED) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif NET_ERR err_rtn; switch (pconn->ConnState) { case NET_TCP_CONN_STATE_FREE: NET_CTR_ERR_INC(NetTCP_ErrNotUsedCtr); *perr = NET_TCP_ERR_CONN_NOT_USED; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_LISTEN: NetTCP_RxPktConnHandlerRxQ_Sync(pconn, pbuf, pbuf_hdr, &err_rtn); break; /*$PAGE*/ case NET_TCP_CONN_STATE_SYNC_RXD: case NET_TCP_CONN_STATE_SYNC_RXD_PASSIVE: case NET_TCP_CONN_STATE_SYNC_RXD_ACTIVE: #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) if (ack_code != NET_TCP_CONN_RX_ACK_VALID) { *perr = NET_TCP_ERR_CONN_ACK_INVALID; return; } #endif NetTCP_RxPktConnHandlerTxWinRemote(pconn, ack_code, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return; } NetTCP_RxPktConnHandlerCfgConn(pconn); NetTCP_RxPktConnHandlerReTxQ(pconn, ack_code, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return; } NetTCP_TxConnWinSizeHandlerCongCtrl(pconn, pbuf_hdr, ack_code, 0, NET_TCP_CONN_TX_WIN_SEG_RXD, perr); switch (*perr) { case NET_TCP_ERR_NONE: case NET_ERR_TX: /* Ignore transitory tx err(s). */ break; case NET_ERR_INIT_INCOMPLETE: case NET_TCP_ERR_CONN_NOT_USED: case NET_TCP_ERR_CONN_FAIL: case NET_TCP_ERR_INVALID_CONN_STATE: case NET_TCP_ERR_INVALID_CONN_OP: case NET_TCP_ERR_TX: case NET_TCP_ERR_RE_TX_SEG_TH: return; /* Prevent 'break NOT reachable' compiler warning. */ } NetTCP_RxPktConnHandlerRxQ_Conn(pconn, pbuf, pbuf_hdr, &err_rtn); break; case NET_TCP_CONN_STATE_SYNC_TXD: if (ack_code == NET_TCP_CONN_RX_ACK_VALID) { NetTCP_RxPktConnHandlerTxWinRemote(pconn, ack_code, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return; } NetTCP_RxPktConnHandlerCfgConn(pconn); NetTCP_RxPktConnHandlerReTxQ(pconn, ack_code, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return; } NetTCP_TxConnWinSizeHandlerCongCtrl(pconn, pbuf_hdr, ack_code, 0, NET_TCP_CONN_TX_WIN_SEG_RXD, perr); switch (*perr) { case NET_TCP_ERR_NONE: case NET_ERR_TX: /* Ignore transitory tx err(s). */ break; case NET_ERR_INIT_INCOMPLETE: case NET_TCP_ERR_CONN_NOT_USED: case NET_TCP_ERR_CONN_FAIL: case NET_TCP_ERR_INVALID_CONN_STATE: case NET_TCP_ERR_INVALID_CONN_OP: case NET_TCP_ERR_TX: case NET_TCP_ERR_RE_TX_SEG_TH: return; /* Prevent 'break NOT reachable' compiler warning. */ } NetTCP_RxPktConnHandlerRxQ_Conn(pconn, pbuf, pbuf_hdr, &err_rtn); } else { NetTCP_RxPktConnHandlerRxQ_Sync(pconn, pbuf, pbuf_hdr, &err_rtn); } break; /*$PAGE*/ case NET_TCP_CONN_STATE_CONN: case NET_TCP_CONN_STATE_FIN_WAIT_1: case NET_TCP_CONN_STATE_CLOSING: case NET_TCP_CONN_STATE_CLOSE_WAIT: case NET_TCP_CONN_STATE_LAST_ACK: #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) switch (ack_code) { case NET_TCP_CONN_RX_ACK_VALID: case NET_TCP_CONN_RX_ACK_DUP: case NET_TCP_CONN_RX_ACK_PREV: break; case NET_TCP_CONN_RX_ACK_NONE: case NET_TCP_CONN_RX_ACK_INVALID: default: *perr = NET_TCP_ERR_CONN_ACK_INVALID; return; /* Prevent 'break NOT reachable' compiler warning. */ } #endif NetTCP_RxPktConnHandlerTxWinRemote(pconn, ack_code, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return; } switch (ack_code) { case NET_TCP_CONN_RX_ACK_VALID: /* If valid ack, update re-tx Q (see Note #3). */ case NET_TCP_CONN_RX_ACK_DUP: NetTCP_RxPktConnHandlerReTxQ(pconn, ack_code, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return; } break; } NetTCP_TxConnWinSizeHandlerCongCtrl(pconn, pbuf_hdr, ack_code, 0, NET_TCP_CONN_TX_WIN_SEG_RXD, perr); switch (*perr) { case NET_TCP_ERR_NONE: case NET_ERR_TX: /* Ignore transitory tx err(s). */ break; case NET_ERR_INIT_INCOMPLETE: case NET_TCP_ERR_CONN_NOT_USED: case NET_TCP_ERR_CONN_FAIL: case NET_TCP_ERR_INVALID_CONN_STATE: case NET_TCP_ERR_INVALID_CONN_OP: case NET_TCP_ERR_TX: case NET_TCP_ERR_RE_TX_SEG_TH: return; /* Prevent 'break NOT reachable' compiler warning. */ } NetTCP_RxPktConnHandlerRxQ_Conn(pconn, pbuf, pbuf_hdr, &err_rtn); switch (err_rtn) { case NET_TCP_ERR_CONN_DATA_NONE: case NET_TCP_ERR_CONN_DATA_VALID: break; case NET_TCP_ERR_CONN_DATA_DUP: *perr = err_rtn; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_ERR_CONN_NOT_USED: case NET_TCP_ERR_CONN_DATA_INVALID: case NET_TCP_ERR_INVALID_CONN_STATE: case NET_TCP_ERR_INVALID_LEN_SEG: default: *perr = err_rtn; return; /* Prevent 'break NOT reachable' compiler warning. */ } NetTCP_RxPktConnHandlerRxQ_AppData(pconn, perr); if (*perr != NET_TCP_ERR_NONE) { return; } break; /*$PAGE*/ case NET_TCP_CONN_STATE_FIN_WAIT_2: case NET_TCP_CONN_STATE_TIME_WAIT: #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) switch (ack_code) { case NET_TCP_CONN_RX_ACK_VALID: case NET_TCP_CONN_RX_ACK_DUP: break; case NET_TCP_CONN_RX_ACK_NONE: case NET_TCP_CONN_RX_ACK_INVALID: default: *perr = NET_TCP_ERR_CONN_ACK_INVALID; return; /* Prevent 'break NOT reachable' compiler warning. */ } #endif NetTCP_RxPktConnHandlerRxQ_Conn(pconn, pbuf, pbuf_hdr, &err_rtn); switch (err_rtn) { case NET_TCP_ERR_CONN_DATA_NONE: case NET_TCP_ERR_CONN_DATA_VALID: break; case NET_TCP_ERR_CONN_DATA_DUP: *perr = err_rtn; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_ERR_CONN_NOT_USED: case NET_TCP_ERR_CONN_DATA_INVALID: case NET_TCP_ERR_INVALID_CONN_STATE: case NET_TCP_ERR_INVALID_LEN_SEG: default: *perr = err_rtn; return; /* Prevent 'break NOT reachable' compiler warning. */ } NetTCP_RxPktConnHandlerRxQ_AppData(pconn, perr); if (*perr != NET_TCP_ERR_NONE) { return; } break; case NET_TCP_CONN_STATE_CLOSING_DATA_AVAIL: NET_CTR_ERR_INC(NetTCP_ErrConnInvalidOpCtr); *perr = NET_TCP_ERR_INVALID_CONN_OP; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_NONE: case NET_TCP_CONN_STATE_CLOSED: default: NetTCP_ConnClose(pconn, pbuf_hdr, DEF_YES, NET_TCP_CONN_CLOSE_ALL); NET_CTR_ERR_INC(NetTCP_ErrConnInvalidStateCtr); *perr = NET_TCP_ERR_INVALID_CONN_STATE; return; /* Prevent 'break NOT reachable' compiler warning. */ } *perr = err_rtn; } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_RxPktConnHandlerCfgConn() * * Description : (1) Configure TCP connection's controls on transition to connected state(s) : * * (a) Configure TCP connection maximum segment size control(s) * (b) Configure TCP connection window size controls * * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_RxPktConnHandler(). * * Return(s) : none. * * Caller(s) : NetTCP_RxPktConnHandlerSeg(). * * Note(s) : (2) During TCP connection initialization, some TCP connection controls were previously * configured in NetTCP_ConnGet() when the TCP connection was allocated from the TCP * connection pool. These TCP connection controls do NOT need to be re-configured * but are shown for completeness. ********************************************************************************************************* */ static void NetTCP_RxPktConnHandlerCfgConn (NET_TCP_CONN *pconn) { NET_TCP_CFG_CODE cfg_code; /* Cfg conn (see Note #1). */ cfg_code = NET_TCP_CONN_CFG_NONE | NET_TCP_CONN_CFG_MAX_SEG_SIZE_CONN | NET_TCP_CONN_CFG_WIN_SIZE_ALL; #if 0 /* Cfg'd in NetTCP_ConnGet() [see Note #2]. */ DEF_BIT_SET(cfg_code, NET_TCP_CONN_CFG_TX_RTT_RTO); #endif NetTCP_ConnCfg(pconn, cfg_code); } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_RxPktConnHandlerRxQ_Sync() * * Description : (1) (a) Handle TCP connection's transport receive queue ... : * * (1) Update TCP connection initial receive sequence numbers See Note #2b * (2) Queue received TCP segments onto transport receive queue See Note #3 * (3) Update TCP connection receive window * * (b) ... for the following connection-request/synchronization states : * * (1) LISTEN * (2) SYN-SENT * * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_RxPktConnHandler(). * * pbuf Pointer to network buffer that received TCP packet. * ---- Argument checked in NetTCP_Rx(). * * pbuf_hdr Pointer to network buffer header that received TCP packet. * -------- Argument validated in NetTCP_Rx(). * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_CONN_DATA_NONE Received packet successfully handled; but * NO data to queue to receive queue(s). * NET_TCP_ERR_CONN_DATA_VALID Received packet successfully handled & * data queued to receive queue(s) * for later processing (see Note #2c). * NET_TCP_ERR_INVALID_CONN_STATE Invalid TCP connection receive queue * state. * * Return(s) : none. * * Caller(s) : NetTCP_RxPktConnHandlerSeg(). * * Note(s) : (2) (a) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check Sequence Number' * states that "segments are processed in sequence ... processing is done in SEG.SEQ * order." * * (b) The following sections generalize that for the LISTEN & SYN-SENT states that "if the * SYN bit is on and ... acceptable then ... RCV.NXT is set to SEG.SEQ+1, IRS is set to * SEG.SEQ" : * * (1) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : LISTEN [State] : * Check for SYN' * (2) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : SYN-SENT [State] : * Check SYN Bit' * * (c) (1) The following sections generalize that for the LISTEN & SYN-SENT states that "if * there are other controls or text in the segment, queue them for later processing * after the ESTABLISHED state has been reached" : * * (A) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : LISTEN [State] : * Check for SYN' * (B) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : SYN-SENT [State] : * Check SYN Bit' * * (2) If any control or text is queued for later processing, the next sequence octet to * receive (RCV.NXT) MUST include the length of this received segment (SEG.LEN) : * * (A) RCV.NXT = SEG.SEQ + SEG.LEN + 1 * (3) TCP segments with receive data are sequenced into the appropriate TCP connection * receive queue(s) to be made available & ready to be read by the application layer. * * See also 'NetTCP_RxPktConnHandlerRxQ_Conn() Note #3' * & 'NetTCP_RxPktConnHandlerRxQ_AppData() Note #2'. * * (4) Some buffer controls were previously initialized in NetBuf_Get() when the buffer was * allocated. These buffer controls do NOT need to be re-initialized but are shown for * completeness. * * (5) RFC #793, Section 3.7 'Data Communication : Managing the Window' states that "the window * sent in each segment indicates the range of sequence numbers the sender of the window * (the data receiver) is currently prepared to accept. There is an assumption that this * is related to the currently available data buffer space available for this connection * ... One strategy would be to ... [update the] information when the window" changes. * * See also 'NetTCP_RxAppData() Note #7', * 'NetTCP_RxPktConnHandlerRxQ_Conn() Note #6', * & 'NetTCP_RxConnWinSizeHandler() Note #2a'. ********************************************************************************************************* */ /*$PAGE*/ static void NetTCP_RxPktConnHandlerRxQ_Sync (NET_TCP_CONN *pconn, NET_BUF *pbuf, NET_BUF_HDR *pbuf_hdr, NET_ERR *perr) { NET_TCP_SEG_SIZE seg_len; NET_TCP_SEG_SIZE seg_len_data; #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) /* ------------- VALIDATE RX Q CONN STATE ------------- */ switch (pconn->RxQ_State) { case NET_TCP_RX_Q_STATE_CONN_CLOSED: break; case NET_TCP_RX_Q_STATE_NONE: case NET_TCP_RX_Q_STATE_CLOSED: case NET_TCP_RX_Q_STATE_CONN_SYNC: case NET_TCP_RX_Q_STATE_CONN: case NET_TCP_RX_Q_STATE_CONN_CLOSING: default: *perr = NET_TCP_ERR_INVALID_CONN_STATE; return; /* Prevent 'break NOT reachable' compiler warning. */ } #endif /* Update TCP conn rx seq nbrs (see Notes #2b & #2c2). */ pconn->RxSeqNbrSync = (NET_TCP_SEQ_NBR) pbuf_hdr->TCP_SeqNbr; pconn->RxSeqNbrNext = (NET_TCP_SEQ_NBR)(pbuf_hdr->TCP_SeqNbr + pbuf_hdr->TCP_SegLen); pconn->RxQ_State = NET_TCP_RX_Q_STATE_CONN_SYNC; seg_len = (NET_TCP_SEG_SIZE)pbuf_hdr->TCP_SegLen; if (seg_len > NET_TCP_SEG_LEN_SYNC) { /* If rx'd seg len > sync seg len, ... */ /* ... Q seg to TCP conn rx Q (see Notes #3 & #2c1). */ #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) NetTCP_ConnFreeBufQ(&pconn->RxQ_Transport_Head, &pconn->RxQ_Transport_Tail); #endif pconn->RxQ_Transport_Head = (NET_BUF *)pbuf; pconn->RxQ_Transport_Tail = (NET_BUF *)pbuf; #if 0 /* Init'd in NetBuf_Get() [see Note #4]. */ pbuf_hdr->PrevPrimListPtr = (void *)0; pbuf_hdr->NextPrimListPtr = (void *)0; #endif /* Dec TCP conn's rx win size (see Note #5). */ seg_len_data = (NET_TCP_SEG_SIZE)pbuf_hdr->TCP_SegLenData; NetTCP_RxConnWinSizeHandler((NET_TCP_CONN *)pconn, (NET_BUF_HDR *)pbuf_hdr, (NET_TCP_WIN_SIZE)seg_len_data, (NET_TCP_WIN_CODE)NET_TCP_CONN_RX_WIN_DEC); *perr = NET_TCP_ERR_CONN_DATA_VALID; } else { *perr = NET_TCP_ERR_CONN_DATA_NONE; } } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_RxPktConnHandlerRxQ_Conn() * * Description : (1) (a) Handle TCP connection's transport receive queue ... : * * (1) Update TCP connection receive sequence numbers See Notes #2c & #2e * (2) Insert received TCP segments in sequence order See Notes #3 & #4 * (3) Update TCP connection receive window * * (b) ... for the following connected states : * * (1) SYN-RECEIVED See Notes #1bA & #2b2 * (2) SYN-SENT See Notes #1bA & #2b1 * (3) ESTABLISHED See Note #1bB * (4) FIN-WAIT-1 See Note #1bB * (5) FIN-WAIT-2 See Note #1bB * (6) CLOSING See Note #1bC * (7) TIME-WAIT See Note #1bC * (8) CLOSE-WAIT See Note #1bC * (9) LAST-ACK See Note #1bC * * * (A) For synchronization-to-connected state transitions, segments are queued to * the TCP connection's transport receive queue, but NOT to the TCP connection's * application receive queue, until the application layer is signaled that the * transport layer connection is complete. * * See also 'NetTCP_RxPktConnHandlerSyncRxd() Note #1c1' * & 'NetTCP_RxPktConnHandlerSyncTxd() Note #1c1'. * * (B) For connected states, segments are queued to the TCP connection's * transport &/or application receive queue(s) as appropriate (see Note #3). * * (C) For closing states; closing segments are queued to the TCP connection's * transport &/or application receive queue(s) as for connected states. * * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_RxPktConnHandler(). * * pbuf Pointer to network buffer that received TCP packet. * ---- Argument checked in NetTCP_Rx(). * * pbuf_hdr Pointer to network buffer header that received TCP packet. * -------- Argument validated in NetTCP_Rx(). * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_CONN_DATA_NONE Received packet successfully handled; but * NO data to queue to receive queue(s). * NET_TCP_ERR_CONN_DATA_VALID Received packet successfully handled & * data queued to receive queue(s) * for later processing (see Note #3). * NET_TCP_ERR_CONN_DATA_INVALID Received packet contains invalid segment * data; NOT queued to receive queue(s). * NET_TCP_ERR_CONN_DATA_DUP Received packet contains duplicate segment * data; NOT queued to receive queue(s) * (see Note #2a2). * * NET_TCP_ERR_CONN_SEQ_INVALID Received segment's sequence number is NOT * valid for current TCP connection. * * NET_TCP_ERR_INVALID_CONN_STATE Invalid TCP connection receive queue state. * * - RETURNED BY NetTCP_RxPktConnIsValidSeq() : - * NET_TCP_ERR_CONN_NOT_USED TCP connection NOT currently used. * NET_TCP_ERR_INVALID_LEN_SEG Invalid TCP sequence-segment length. * * Return(s) : none. * * Caller(s) : NetTCP_RxPktConnHandlerSeg(). *$PAGE* * Note(s) : (2) (a) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check Sequence Number' * states that ... : * * (1) "Segments are processed in sequence ... processing is done in SEG.SEQ order." * * (A) Assumes received segment's sequences previously validated. * * (2) "Initial tests on arrival are used to discard old duplicates." * * (3) (A) (1) "If a segment's contents straddle the boundary between old and new, * only the new parts should be processed." * * (2) "One could tailor actual segments to ... the idealized segment that * begins at RCV.NXT and does not exceed the window ... by trimming off * any portions that lie outside the window (including SYN and FIN), * and only processing further if the segment then begins at RCV.NXT. * Segments with higher beginning sequence numbers may be held for later * processing." * * (B) (1) Sequencing received segments with duplicate data that overlaps * multiple previously-received segments' non-contiguous sequence numbers * is data intensive/expensive. * * (2) Therefore, any received segment with duplicate data that overlaps * multiple previously-received segments' non-contiguous sequence numbers * is trimmed of duplicate data starting from the end of the received * segment's data. * * In other words, only the first contiguous, non-duplicate data sequence * starting from the start of the received segment's data is sequenced * into the TCP connection's receive queue(s). * * (C) RFC #1122, Section 4.2.2.20 states that "a TCP SHOULD be capable of queueing * out-of-order TCP segments". * * (b) The following sections generalize that in the "SYN-SENT [or] ... SYN-RECEIVED * STATE[s], ... [that] if the ACK bit is on [and] our SYN has been ACKed ... * then enter the ESTABLISHED state and continue processing" : * * (1) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : SYN-SENT [State] : * Check SYN Bit' * (2) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check ACK Field : * SYN-RECEIVED STATE' * * (c) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : SYN-SENT [State] : * Check SYN Bit' states that "if the SYN bit is on and ... acceptable then ... * RCV.NXT is set to SEG.SEQ+1, IRS is set to SEG.SEQ". * * (d) (1) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : SYN-SENT [State] : * Check SYN Bit' states that "if there are other controls or text in the segment, * queue them for later processing after the ESTABLISHED state has been reached" * (see also Note #2b). * * (2) If any control or text is queued for later processing, the next sequence octet to * receive (RCV.NXT) MUST include the length of this received segment (SEG.LEN) : * * (A) RCV.NXT = SEG.SEQ + SEG.LEN + 1 * * (e) (1) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check FIN Bit' states * that "if the FIN bit is set ... advance RCV.NXT over the FIN". * * (2) However, the next octet to receive is NOT updated with the sequence number of the * last octet to receive. Instead, the generic sequence algorithm maintains & updates * BOTH the next & last octets to receive for the closing TCP connection. *$PAGE* * (3) TCP segments with receive data are sequenced first into the TCP connection's transport * receive queue to be made available & ready to be read by the application layer from the * TCP connection's application receive queue (see 'NetTCP_RxPktConnHandlerRxQ_AppData() * Note #2'). * * (a) Received TCP segments are inserted into a doubly-linked Transport Receive Queue, * sorted by their sequence number(s). * * (1) 'RxQ_Transport_Head' points to the head of the Transport Receive Queue; * 'RxQ_Transport_Tail' points to the tail of the Transport Receive Queue. * * (2) Segment buffers' 'PrevPrimListPtr' & 'NextPrimListPtr' doubly-link each * segment to form the Transport Receive Queue. * * (3) Fragmented segment buffer's 'PrevBufPtr' & 'NextBufPtr' doubly-link each * fragmented segment (see also 'net_ip.c NetIP_RxPktFragReasm() Note #2b3'). * * (b) Typically & most-frequently, TCP segments will be received in sequence-order. * Therefore, the sequence sort algorithm starts at the tail of the Transport * Receive Queue. * * (c) As segments are inserted into the Transport Receive Queue, segments migrate to * the head of the Transport Receive Queue. Queued segments with sequence numbers * that are contiguous from the next expected receive sequence number are ready to * be read by the application layer, so are immediately moved from the Transport * Receive Queue to the Application Receive Queue. * * See also 'NetTCP_RxPktConnHandlerRxQ_AppData() Note #2'. * * * | | * |<-- TCP Connection Transport Receive Queue --->| * | (see Note #3) | * * Segments Moved Segments Sequenced * to Application into Transport * Receive Queue Receive Queue * starting at head starting at tail * (see Note #3c) (see Note #3b) * * | NextPrimListPtr | * | (see Note #3a2) | * v | v * | * Head of ------- ------- v ------- ------- (see Note #3a1) * Receive ---->| |------>| |------>| |------>| | * Queue | | | | | | | | Tail of * | |<------| |<------| |<------| |<---- Receive * (see Note #3a1) | | | | ^ | | | | Queue * | | | | | | | | | * ------- ------- | ------- ------- * | ^ | | ^ * | | PrevPrimListPtr | | * v | (see Note #3a2) v | * ------- ------- * | | | | * | | | | * | | | | * | | | | * | | | | * ------- ------- * | ^ | ^ * NextBufPtr ---> | | <--- PrevBufPtr | | * (see Note #3a3) v | (see Note #3a3) v | * ------- ------- * | | | | * | | | | * | | ------- * | | * | | * ------- * *$PAGE* * (4) RFC #793 does NOT provide nor suggest any logic to determine/handle sequence number * comparisons for sequence number windows that overflow the sequence number space. * * (a) For example, the next sequence octet to receive (RCV.NXT) is typically less than * or equal to the next received sequence octet (SEG.SEQ) : * * (1) RCV.NXT <= SEG.SEQ * * SEG.SEQ itself is also typically less than RCV.NXT plus the current receive window * size (RCV.WND) : * * (2) SEG.SEQ < RCV.NXT + RCV.WND * * However, if (RCV.NXT + RCV.WND) or SEG.SEQ overflows the sequence number space, * these values will be MUCH less than SEG.SEQ or RCV.NXT, respectively, until * SEG.SEQ & RCV.NXT also overflow : * * (3) RCV.NXT + RCV.WND << RCV.NXT * << SEG.SEQ * * (4) SEG.SEQ << RCV.NXT * * (b) Therefore, in order to determine if a received segment's sequence value precedes any * previously received segment(s) in the TCP connection's receive queue(s), the following * unsigned arithmetic comparisons MUST be checked : * * (1) RxQCur.SeqNbr - (SEG.SEQ + 1) < RX.WIN * * (2) RX.NXT - (SEG.SEQ + 1) < SEG.LEN * * Note that these comparisons bound any received segment's sequence within limits set * by the TCP connection's receive window size & next expected receive octet. * * (c) In order to determine if a received segment's sequence value overlaps any previously * received segment(s) in the TCP connection's receive queue, the following unsigned * arithmetic comparisons MUST be checked : * * (1) ( SEG.SEQ + SEG.LEN ) - (RxQNext.SeqNbr + 1) < SEG.LEN * * (2) (RxQCur.SeqNbr + RxQCur.SegLen) - (SEG.SEQ + 1) < RxQCur.SegLen * * See also 'NetTCP_RxPktConnIsValidSeq() Note #2'. * * (5) (a) RFC #2581, Section 3.2 states that "a TCP receiver SHOULD send an immediate ACK" : * * (1) "When an out-of-order segment arrives. The purpose of this ACK is to inform * the sender that a segment was received out-of-order and which sequence number * is expected." * * (2) "In addition, ... when the incoming segment fills in all or part of a gap in * the sequence space." * * See also 'NetTCP_TxConnAck() Note #4a5'. * * (b) Since segments are typically received in sequence order (see Notes #5a1 & #3b) & * since segments received in sequential order are immediately made available & ready * to be read by the application layer (see Note #3c), received segments are out-of- * order AND/OR fill in sequence number gaps whenever : * * (1) A TCP connection's Transport Receive Queue is initially non-empty; * OR * (2) A TCP connection's Transport Receive Queue's head segment's sequence number * does NOT equal the TCP connection's next expected receive sequence number. * * (c) However, since a TCP connection's next expected receive sequence numbers are NOT * updated until both of the TCP connection's transport & application receive queues * have been handled (see 'NetTCP_RxPktConnHandlerRxQ_AppData() Note #1a3'), the * transmission of any immediate acknowledgement MUST follow the handling of BOTH * of the TCP connection's receive queues. * * See also 'NetTCP_RxPktConnHandlerRxQ_AppData() Notes #1a3 & #4'. * * (6) RFC #793, Section 3.7 'Data Communication : Managing the Window' states that "the window * sent in each segment indicates the range of sequence numbers the sender of the window * (the data receiver) is currently prepared to accept. There is an assumption that this * is related to the currently available data buffer space available for this connection * ... One strategy would be to ... [update the] information when the window" changes. * * See also 'NetTCP_RxAppData() Note #7', * 'NetTCP_RxPktConnHandlerRxQ_Sync() Note #5', * & 'NetTCP_RxConnWinSizeHandler() Note #2a'. ********************************************************************************************************* */ /*$PAGE*/ static void NetTCP_RxPktConnHandlerRxQ_Conn (NET_TCP_CONN *pconn, NET_BUF *pbuf, NET_BUF_HDR *pbuf_hdr, NET_ERR *perr) { #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) NET_TCP_SEQ_CODE seq_code; #endif NET_BUF *pbuf_q; NET_BUF *pbuf_q_head; NET_BUF *pbuf_q_prev; NET_BUF *pbuf_q_next; NET_BUF_HDR *pbuf_q_hdr; NET_BUF_HDR *pbuf_q_hdr_head; NET_BUF_HDR *pbuf_q_hdr_next; NET_TCP_SEQ_NBR seq_nbr; NET_TCP_SEQ_NBR seq_nbr_next; NET_TCP_SEQ_NBR seq_nbr_dup; NET_TCP_SEQ_NBR seq_nbr_delta; NET_TCP_SEQ_NBR seq_nbr_win; CPU_BOOLEAN seq_srch_done; CPU_BOOLEAN seq_unordered_prev; CPU_BOOLEAN seq_unordered_cur; CPU_BOOLEAN seq_unordered; /* ----------- UPDATE TCP CONN RX SEQ NBRS ------------ */ if (pconn->ConnState == NET_TCP_CONN_STATE_SYNC_TXD) { #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) switch (pconn->RxQ_State) { case NET_TCP_RX_Q_STATE_CONN_CLOSED: break; case NET_TCP_RX_Q_STATE_NONE: case NET_TCP_RX_Q_STATE_CLOSED: case NET_TCP_RX_Q_STATE_CONN_SYNC: case NET_TCP_RX_Q_STATE_CONN: case NET_TCP_RX_Q_STATE_CONN_CLOSING: default: *perr = NET_TCP_ERR_INVALID_CONN_STATE; return; /* Prevent 'break NOT reachable' compiler warning. */ } #endif /* Init SYN-SENT state (see Notes #2c & #2d2). */ pconn->RxSeqNbrSync = (NET_TCP_SEQ_NBR) pbuf_hdr->TCP_SeqNbr; pconn->RxSeqNbrNext = (NET_TCP_SEQ_NBR)(pbuf_hdr->TCP_SeqNbr + pbuf_hdr->TCP_SegLen); pconn->RxQ_State = NET_TCP_RX_Q_STATE_CONN_SYNC; } else { switch (pconn->RxQ_State) { case NET_TCP_RX_Q_STATE_CONN_SYNC: break; case NET_TCP_RX_Q_STATE_CONN: /* Chk TCP conn closing (see Note #2e). */ if (pbuf_hdr->TCP_SegClose == DEF_YES) { pconn->RxSeqNbrLast = (NET_TCP_SEQ_NBR)(pbuf_hdr->TCP_SeqNbr + pbuf_hdr->TCP_SegLen); pconn->RxSeqNbrClose = (NET_TCP_SEQ_NBR)(pconn->RxSeqNbrLast - NET_TCP_SEG_LEN_CLOSE); pconn->RxQ_State = NET_TCP_RX_Q_STATE_CONN_CLOSING; } /* 'break' intentionally omitted; do NOT move from the */ /* .. following case : 'NET_TCP_RX_Q_STATE_CONN_CLOSED'.*/ case NET_TCP_RX_Q_STATE_CONN_CLOSED: case NET_TCP_RX_Q_STATE_CONN_CLOSING: #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) /* Chk valid rx'd seq nbr. */ seq_code = NetTCP_RxPktConnIsValidSeq(pconn, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return; } if (seq_code != NET_TCP_CONN_RX_SEQ_VALID) { *perr = NET_TCP_ERR_CONN_SEQ_INVALID; return; } #endif break; case NET_TCP_RX_Q_STATE_NONE: case NET_TCP_RX_Q_STATE_CLOSED: default: *perr = NET_TCP_ERR_CONN_DATA_INVALID; return; /* Prevent 'break NOT reachable' compiler warning. */ } } /*$PAGE*/ if (pbuf_hdr->TCP_SegLenData < 1) { /* If seg data len < 1, AND ... */ /* ... NOT in rx conn closing state OR ... */ if ((pconn->RxQ_State != NET_TCP_RX_Q_STATE_CONN_CLOSING) || (pbuf_hdr->TCP_SegClose != DEF_YES)) { /* ... fin/close NOT rx'd; ... */ *perr = NET_TCP_ERR_CONN_DATA_NONE; /* ... rtn data NOT avail. */ return; } /* Else seq & handle closing-ctrl seg. */ } /* ------- INSERT SEG INTO SEQ'D TRANSPORT RX Q ------- */ pbuf_q = (NET_BUF *)pconn->RxQ_Transport_Tail; /* Start seq insert alg from rx Q tail (see Note #3b). */ pbuf_q_next = (NET_BUF *)0; seq_srch_done = DEF_NO; /* Chk init'l rx Q seq order (see Note #5b1). */ seq_unordered_prev = (pconn->RxQ_Transport_Head != (NET_BUF *)0) ? DEF_YES : DEF_NO; while (seq_srch_done == DEF_NO) { /* Srch rx Q to insert rx'd seg into seq nbr position. */ if (pbuf_q != (NET_BUF *)0) { /* While NOT @ rx Q head, chk if seg ... */ pbuf_q_hdr = (NET_BUF_HDR *)&pbuf_q->Hdr; pbuf_q_prev = (NET_BUF *) pbuf_q_hdr->PrevPrimListPtr; /* ... before or after cur rx Q seg (see Note #2a1). */ seq_nbr_next = (NET_TCP_SEQ_NBR) pbuf_q_hdr->TCP_SeqNbr; seq_nbr = (NET_TCP_SEQ_NBR)(pbuf_hdr->TCP_SeqNbr + 1); seq_nbr_delta = (NET_TCP_SEQ_NBR)(seq_nbr_next - seq_nbr); seq_nbr_win = (NET_TCP_SEQ_NBR) pconn->RxWinSizeCfgdActual; if (seq_nbr_delta < seq_nbr_win) { /* If seg's seq nbr < cur rx Q's seq nbr (see Note #4b1)*/ /* ... adv to prev rx Q seg; but chk ... */ /* ... for dup seqs in next rx Q seg. */ seq_nbr_next = (NET_TCP_SEQ_NBR)(pbuf_hdr->TCP_SeqNbr + pbuf_hdr->TCP_SegLen); seq_nbr = (NET_TCP_SEQ_NBR)(pbuf_q_hdr->TCP_SeqNbr + 1); seq_nbr_delta = (NET_TCP_SEQ_NBR)(seq_nbr_next - seq_nbr); seq_nbr_win = (NET_TCP_SEQ_NBR) pbuf_hdr->TCP_SegLen; if (seq_nbr_delta < seq_nbr_win) { /* If seg overlaps next rx Q seqs (see Note #4c1), ... */ seq_nbr_dup = seq_nbr_delta + 1; /* ... trim dup seqs from seg (see Note #2a3); ... */ if (pbuf_hdr->TCP_SegLenData > (CPU_INT16U)seq_nbr_dup) { pbuf_hdr->TCP_SegLenData -= (CPU_INT16U)seq_nbr_dup; pbuf_hdr->TCP_SegLen -= (CPU_INT16U)seq_nbr_dup; } else { /* ... else discard ALL dup seqs (see Note #2a2). */ *perr = NET_TCP_ERR_CONN_DATA_DUP; return; } } /* Adv to prev rx Q seg. */ pbuf_q_next = (NET_BUF *)pbuf_q; pbuf_q = (NET_BUF *)pbuf_q_prev; } else { /* Else insert seg between cur/next rx Q segs. */ /* Chk for dup seqs in cur rx Q seg. */ seq_nbr_next = (NET_TCP_SEQ_NBR)(pbuf_q_hdr->TCP_SeqNbr + pbuf_q_hdr->TCP_SegLen); seq_nbr = (NET_TCP_SEQ_NBR)(pbuf_hdr->TCP_SeqNbr + 1); seq_nbr_delta = (NET_TCP_SEQ_NBR)(seq_nbr_next - seq_nbr); seq_nbr_win = (NET_TCP_SEQ_NBR) pbuf_q_hdr->TCP_SegLen; if (seq_nbr_delta < seq_nbr_win) { /* If seg overlaps prev'ly rx'd seqs (see Note #4c2), */ seq_nbr_dup = seq_nbr_delta + 1; /* ... trim dup seqs from seg (see Note #2a3); ... */ if (pbuf_hdr->TCP_SegLenData > (CPU_INT16U)seq_nbr_dup) { pbuf_hdr->TCP_SegLenData -= (CPU_INT16U)seq_nbr_dup; pbuf_hdr->TCP_SegLen -= (CPU_INT16U)seq_nbr_dup; pbuf_hdr->TCP_SeqNbr = (CPU_INT32U)seq_nbr_next; } else { /* ... else discard ALL dup seqs (see Note #2a2). */ *perr = NET_TCP_ERR_CONN_DATA_DUP; return; } } seq_srch_done = DEF_YES; } /*$PAGE*/ } else { /* Else if @ head of rx Q, entire rx Q srch'd; ... */ /* ... chk for dup seqs prior to TCP conn's ... */ /* ... next expected rx octet. */ seq_nbr_next = (NET_TCP_SEQ_NBR) pconn->RxSeqNbrNext; seq_nbr = (NET_TCP_SEQ_NBR)(pbuf_hdr->TCP_SeqNbr + 1); seq_nbr_delta = (NET_TCP_SEQ_NBR)(seq_nbr_next - seq_nbr); seq_nbr_win = (NET_TCP_SEQ_NBR) pbuf_hdr->TCP_SegLen; if (seq_nbr_delta < seq_nbr_win) { /* If seg overlaps prev'ly rx'd seqs (see Note #4b2), */ seq_nbr_dup = seq_nbr_delta + 1; /* ... trim dup seqs from seg (see Note #2a3); */ if (pbuf_hdr->TCP_SegLenData > (CPU_INT16U)seq_nbr_dup) { pbuf_hdr->TCP_SegLenData -= (CPU_INT16U)seq_nbr_dup; pbuf_hdr->TCP_SegLen -= (CPU_INT16U)seq_nbr_dup; pbuf_hdr->TCP_SeqNbr = (CPU_INT32U)seq_nbr_next; } else { /* ... else discard ALL dup seqs (see Note #2a2). */ *perr = NET_TCP_ERR_CONN_DATA_DUP; return; } } seq_srch_done = DEF_YES; } } /* Insert rx'd seg between cur/next rx Q segs. */ pbuf_hdr->PrevPrimListPtr = (void *)pbuf_q; if (pbuf_q != (NET_BUF *)0) { /* If avail, insert rx'd seg after cur rx Q seg. */ pbuf_q_hdr->NextPrimListPtr = (void *)pbuf; } else { /* Else insert rx'd seg @ rx Q head. */ pconn->RxQ_Transport_Head = (NET_BUF *)pbuf; } pbuf_hdr->NextPrimListPtr = (void *)pbuf_q_next; if (pbuf_q_next != (NET_BUF *)0) { /* If avail, insert rx'd seg before next rx Q seg. */ pbuf_q_hdr_next = (NET_BUF_HDR *)&pbuf_q_next->Hdr; pbuf_q_hdr_next->PrevPrimListPtr = (void *) pbuf; } else { /* Else insert rx'd seg @ rx Q tail. */ pconn->RxQ_Transport_Tail = (NET_BUF *) pbuf; } /* Chk rx'd out-of-order seg(s) [see Note #5a]. */ if (pconn->RxQ_Transport_Head != (NET_BUF *)0) { pbuf_q_head = (NET_BUF *) pconn->RxQ_Transport_Head; pbuf_q_hdr_head = (NET_BUF_HDR *)&pbuf_q_head->Hdr; /* Chk cur rx Q seq order (see Note #5b2). */ seq_unordered_cur = ((NET_TCP_SEQ_NBR)pbuf_q_hdr_head->TCP_SeqNbr != (NET_TCP_SEQ_NBR)pconn->RxSeqNbrNext) ? DEF_YES : DEF_NO; } seq_unordered = ((seq_unordered_prev == DEF_YES) || (seq_unordered_cur == DEF_YES)) ? DEF_YES : DEF_NO; if (seq_unordered != DEF_NO) { /* If out-of-order seg(s) rx'd (see Note #5a) ... */ if (pconn->RxQ_State != NET_TCP_RX_Q_STATE_CONN_SYNC) { /* ... in non-sync state, ... */ /* ... req immed TCP conn ack tx (see Note #5c). */ pbuf_hdr->TCP_SegAckTxReqCode = NET_TCP_CONN_TX_ACK_IMMED; } } /* ----------- UPDATE TCP CONN RX WIN SIZE ------------ */ /* Dec TCP conn's rx win size (see Note #6). */ NetTCP_RxConnWinSizeHandler((NET_TCP_CONN *)pconn, (NET_BUF_HDR *)pbuf_hdr, (NET_TCP_WIN_SIZE)pbuf_hdr->TCP_SegLenData, (NET_TCP_WIN_CODE)NET_TCP_CONN_RX_WIN_DEC); *perr = NET_TCP_ERR_CONN_DATA_VALID; } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_RxPktConnHandlerRxQ_AppData() * * Description : (1) (a) Handle TCP connection's application receive queue ... : * * (1) Remove segments from TCP connection's transport receive queue See Note #3b1B * (2) Update TCP connection transport receive queue * (3) Update TCP connection receive sequence numbers * (4) Move segments onto TCP connection's application receive queue See Note #2 * (5) Update TCP connection receive window See Note #6 * * (b) ... for the following connected states : * * (1) SYN-RECEIVED See Note #1bA * (2) SYN-SENT See Note #1bA * (3) ESTABLISHED See Note #1bB * (4) FIN-WAIT-1 See Note #1bB * (5) FIN-WAIT-2 See Note #1bB * (6) CLOSING See Note #1bC * (7) TIME-WAIT See Note #1bC * (8) CLOSE-WAIT See Note #1bC * (9) LAST-ACK See Note #1bC * * * (A) For synchronization-to-connected state transitions, segments are queued to * the TCP connection's transport receive queue, but NOT to the TCP connection's * application receive queue, until the application layer is signaled that the * transport layer connection is complete. * * See also 'NetTCP_RxPktConnHandlerSyncRxd() Note #1c1' * & 'NetTCP_RxPktConnHandlerSyncTxd() Note #1c1'. * * (B) For connected states, segments are queued to the TCP connection's application * receive queue as appropriate (see Notes #2 & #3b). * * (C) For closing states; closing segments are queued to the TCP connection's * transport &/or application receive queue(s) as for connected states. * * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_RxPktConnHandler(). * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_NONE TCP connection's application receive queue * successfully handled. * NET_TCP_ERR_INVALID_CONN_STATE Invalid TCP connection receive queue state. * * ---- RETURNED BY NetOS_TCP_RxQ_Signal() : ---- * NET_TCP_ERR_RX_Q_FULL TCP connection receive queue full. * NET_TCP_ERR_RX_Q_SIGNAL_FAULT TCP connection receive queue signal fault. * * ----- RETURNED BY NetOS_TCP_RxQ_Clr() : ------ * NET_TCP_ERR_RX_Q_SIGNAL_CLR TCP connection receive queue clear failed. * * Return(s) : none. * * Caller(s) : NetTCP_RxPktConnHandlerSeg(), * NetTCP_RxPktConnHandlerSyncRxd(), * NetTCP_RxPktConnHandlerSyncTxd(). *$PAGE* * Note(s) : (2) TCP segments with receive data that are available & ready to be read by the application * layer are linked in a TCP connection's application receive queue. * * (a) Received TCP segments are moved from the doubly-linked Transport Receive Queue, * sorted by their sequence number(s), onto the Application Receive Queue. * * (1) 'RxQ_App_Head' points to the head of the Application Receive Queue; * 'RxQ_App_Tail' points to the tail of the Application Receive Queue. * * (2) Segment buffers' 'PrevPrimListPtr' & 'NextPrimListPtr' doubly-link each * segment to form the Application Receive Queue. * * (3) Fragmented segment buffer's 'PrevBufPtr' & 'NextBufPtr' doubly-link each * fragmented segment (see also 'net_ip.c NetIP_RxPktFragReasm() Note #2b3'). * * (b) TCP segments are appended in sequence order from the head of a TCP connection's * Transport Receive Queue to the tail of the TCP connection's Application Receive * Queue. * * See also 'NetTCP_RxPktConnHandlerRxQ_Conn() Note #3'. * * (c) Application data is read from segments starting from the head of the Application * Receive Queue. Segments that are read by the application layer are removed from * the Application Receive Queue. * * * | | * |<- TCP Connection Application Receive Queue -->| * | (see Note #2) | * * Segments Read by Segments Appended to * Application Layer Application Receive Queue * starting at head starting at tail * (see Note #2c) (see Note #2b) * * | NextPrimListPtr | * | (see Note #2a2) | * v | v * | * Head of ------- ------- v ------- ------- (see Note #2a1) * Receive ---->| |------>| |------>| |------>| | * Queue | | | | | | | | Tail of * | |<------| |<------| |<------| |<---- Receive * (see Note #2a1) | | | | ^ | | | | Queue * | | | | | | | | | * ------- ------- | ------- ------- * | ^ | | ^ * | | PrevPrimListPtr | | * v | (see Note #2a2) v | * ------- ------- * | | | | * | | | | * | | | | * | | | | * | | | | * ------- ------- * | ^ | ^ * NextBufPtr ---> | | <--- PrevBufPtr | | * (see Note #2a3) v | (see Note #2a3) v | * ------- ------- * | | | | * | | | | * | | ------- * | | * | | * ------- * *$PAGE* * (3) (a) (1) RFC #793, Section 3.8 'Interfaces : User/TCP Interface : TCP User Commands : * Receive' states that : * * (A) "If enough data arrive [sic] to fill the buffer before a PUSH is seen, * the PUSH flag will not be set in the response to the RECEIVE. The * buffer will be filled with as much data as it can hold." * * (B) "If the PUSH is seen before the buffer is filled the buffer will be * returned partially filled and PUSH indicated." * * See Notes #3b1B & #3b1B. * * (2) RFC #1122, Section 4.2.2.2 states that : * * (A) (1) "At the receiver, the PSH bit forces buffered data to be delivered to * the application (even if less than a full buffer has been received)." * * (2) (a) "Conversely, the lack of a PSH bit can be used to avoid unnecessary * wakeup calls to the application process; this can be an important * performance optimization for large timesharing hosts." * * (b) "When a series of segments is received without the PSH bit, a TCP * MAY queue the data internally without passing it to the receiving * application." * * (B) (1) "Passing the PSH bit to the receiving application allows an ... * optimization within the application." * * (2) "RFC-793 ... erroneously implies that a received PSH flag must be passed * to the application layer. Passing a received PSH flag to the application * layer is now OPTIONAL." * * (C) "The PSH bit is not a record marker and is independent of segment boundaries." * * (b) (1) (A) (1) Stevens, TCP/IP Illustrated, Volume 1, 8th Printing, Section 20.5, Page 284 * states that "Berkeley-derived implementations ignore a received PUSH flag * because they normally never delay the delivery of received data to the * application". * * (2) Therefore, for TCP connections in any connected state, received TCP segment * data is made available to the application receive queue as soon as it is * enqueued to the application receive queue. * * (B) Thus, for TCP connections in any connected state, TCP segments are moved from * a TCP connection's transport receive queue to its application receive queue * whenever the transport receive queue's enqueued TCP segments are consecutively * sequenced starting from the TCP connection's next expected receive octet. * * (2) Thus, TCP application-receive PUSH feature is obsoleted & NOT implemented. * * See also 'NetTCP_TxConnAck() Note #4a4'. * * (4) A TCP connection's next expected receive sequence numbers are NOT updated until * both of the TCP connection's transport & application receive queues have been * handled. Thus, the transmission of any TCP connection data or acknowledgements * MUST follow the handling of BOTH of the TCP connection's receive queues. * * See also Note #1a3 & 'NetTCP_RxPktConnHandlerRxQ_Conn() Note #5c'. * * (5) Stream-type connections receive all data octets in one or more non-distinct * packets. In other words, the application data is NOT bounded by any specific * packet(s); rather, it is contiguous & sequenced from one packet to the next. * * Therefore, the TCP connection receive queue is signaled ONLY when data is received * for a connection where data was previously unavailable. *$PAGE* * (6) (a) RFC #793, Section 3.7 'Data Communication : Managing the Window' states that * "the window sent in each segment indicates the range of sequence numbers the * sender of the window (the data receiver) is currently prepared to accept. * There is an assumption that this is related to the currently available data * buffer space available for this connection". * * (b) (1) A TCP connection's advertised receive window MUST NEVER be decreased to zero * if NO receive data is available & ready to be read by the application layer. * * In other words, if NO received data starting from the next expected receive * sequence number(s) is queued, then NO data is available to be read by the * application layer. Therefore, the receive window size MUST NOT be decreased * to zero, otherwise the receive window would deadlock since the application * layer would NOT be able to read & extract any data from the receive window * & the TCP connection would NOT be able to receive any more data into the * receive window. * * (2) (A) In case the advertised receive window size has decreased to zero, ... * (B) & NO data is available to be read by the application layer; ... * (C) then remove & free the last received TCP segment from the tail * of the TCP connection's transport receive queue, ... * (D) & increase the advertised receive window size by this freed * segment's length. * * This is permissible because removing the last received TCP segment from * the TCP connection's transport receive queue does NOT interfere with TCP * communications since the next expected receive sequence number(s) remains * unchanged. * * (3) (A) A TCP connection's receive window SHOULD NOT become deadlocked during * correct operation of TCP communication. However, these TCP receive * zero-sized window cases are included as an extra precaution in the * case that TCP communication is incorrectly handled &/or corrupted. * * (B) A deadlocked TCP connection with a receive window of zero-size with * absolutely NO buffers queued in the TCP connection's transport receive * queue SHOULD NEVER occur. However, the TCP receive window size reset * case is included as an extra precaution in the case that a TCP connection * receive window is incorrectly handled &/or corrupted. * * See also 'NetTCP_RxConnWinSizeHandler() Note #2a1'. ********************************************************************************************************* */ static void NetTCP_RxPktConnHandlerRxQ_AppData (NET_TCP_CONN *pconn, NET_ERR *perr) { NET_BUF *pbuf; NET_BUF *pbuf_head; NET_BUF *pbuf_tail; NET_BUF *pbuf_q_head; NET_BUF *pbuf_q_tail; NET_BUF_HDR *pbuf_hdr; NET_BUF_HDR *pbuf_hdr_head; NET_BUF_HDR *pbuf_hdr_tail; NET_BUF_HDR *pbuf_hdr_q_head; NET_BUF_HDR *pbuf_hdr_q_tail; NET_TCP_SEQ_NBR seq_nbr; NET_TCP_SEQ_NBR seq_nbr_inc; CPU_BOOLEAN done; /*$PAGE*/ /* ---- REMOVE RX'D SEG(S) FROM TRANSPORT RX Q ---- */ switch (pconn->RxQ_State) { /* Cfg starting seq nbr. */ case NET_TCP_RX_Q_STATE_CONN_SYNC: if (pconn->RxQ_Transport_Head == (NET_BUF *)0) { /* If sync seg NOT q'd with rx'd data, ... */ seq_nbr = pconn->RxSeqNbrNext; /* ... start rx seq move from next rx seq. */ } else { /* Else sync seg possibly q'd with rx'd data. */ pbuf_q_head = (NET_BUF *) pconn->RxQ_Transport_Head; pbuf_hdr_q_head = (NET_BUF_HDR *)&pbuf_q_head->Hdr; /* If sync seg q'd, ... */ seq_nbr = (pbuf_hdr_q_head->TCP_SegSync == DEF_YES) ? pconn->RxSeqNbrSync /* ... start rx seq move from sync rx seq; ... */ : pconn->RxSeqNbrNext; /* ... else start rx seq move from next rx seq. */ } pconn->RxQ_State = NET_TCP_RX_Q_STATE_CONN; break; case NET_TCP_RX_Q_STATE_CONN: case NET_TCP_RX_Q_STATE_CONN_CLOSING: seq_nbr = pconn->RxSeqNbrNext; break; case NET_TCP_RX_Q_STATE_CONN_CLOSED: *perr = NET_TCP_ERR_NONE; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_RX_Q_STATE_NONE: case NET_TCP_RX_Q_STATE_CLOSED: default: *perr = NET_TCP_ERR_INVALID_CONN_STATE; return; /* Prevent 'break NOT reachable' compiler warning. */ } pbuf = (NET_BUF *)pconn->RxQ_Transport_Head; pbuf_head = (NET_BUF *)pbuf; pbuf_tail = (NET_BUF *)0; done = DEF_NO; while (done == DEF_NO) { /* Srch for ALL segs with consecutive seq nbrs ... */ /* ... from next expected rx seq. */ if (pbuf != (NET_BUF *)0) { pbuf_hdr = (NET_BUF_HDR *)&pbuf->Hdr; if (pbuf_hdr->TCP_SeqNbr == seq_nbr) { /* If seq consecutive from next expected rx seq, .. */ seq_nbr_inc = (NET_TCP_SEQ_NBR)pbuf_hdr->TCP_SegLen; seq_nbr += (NET_TCP_SEQ_NBR)seq_nbr_inc; /* .. update next expected rx seq. */ pbuf_tail = (NET_BUF *)pbuf; pbuf = (NET_BUF *)pbuf_hdr->NextPrimListPtr; } else { done = DEF_YES; } } else { done = DEF_YES; } } /* -------- UPDATE TCP CONN TRANSPORT RX Q -------- */ if (pbuf != (NET_BUF *)0) { /* If transport rx Q NOT empty, update .. */ /* .. transport rx Q head. */ pbuf_hdr->PrevPrimListPtr = (void *)0; pconn->RxQ_Transport_Head = (NET_BUF *)pbuf; } else { /* Else clr transport rx Q. */ pconn->RxQ_Transport_Head = (NET_BUF *)0; pconn->RxQ_Transport_Tail = (NET_BUF *)0; } pconn->RxSeqNbrNext = seq_nbr; /* Update next expected rx seq. */ if (pconn->RxQ_State == NET_TCP_RX_Q_STATE_CONN_CLOSING) { if (pconn->RxSeqNbrNext == pconn->RxSeqNbrLast) { /* If last seq rx'd, close TCP conn rx. */ pconn->RxQ_State = NET_TCP_RX_Q_STATE_CONN_CLOSED; } } /*$PAGE*/ /* -------- MOVE RX'D SEG(S) ONTO APP RX Q -------- */ if (pbuf_tail != (NET_BUF *)0) { /* If avail, move rx'd seg(s) from transport rx Q */ /* ... onto app rx Q (see Note #3b1B). */ pbuf_hdr_head = (NET_BUF_HDR *)&pbuf_head->Hdr; pbuf_hdr_tail = (NET_BUF_HDR *)&pbuf_tail->Hdr; pbuf_hdr_head->PrevPrimListPtr = (void *) pconn->RxQ_App_Tail; pbuf_hdr_tail->NextPrimListPtr = (void *) 0; if (pconn->RxQ_App_Tail != (NET_BUF *)0) { /* If app rx Q NOT empty, ... */ /* ... append seg(s) @ Q tail (see Note #2b). */ pbuf_q_tail = (NET_BUF *) pconn->RxQ_App_Tail; pbuf_hdr_q_tail = (NET_BUF_HDR *)&pbuf_q_tail->Hdr; pbuf_hdr_q_tail->NextPrimListPtr = (void *) pbuf_head; pconn->RxQ_App_Tail = (NET_BUF *) pbuf_tail; } else { /* Else add seg(s) to empty app rx Q ... */ pconn->RxQ_App_Head = (NET_BUF *) pbuf_head; pconn->RxQ_App_Tail = (NET_BUF *) pbuf_tail; NetOS_TCP_RxQ_Signal(pconn->ID, perr); /* ... & signal non-empty app rx Q (see Note #5). */ if (*perr != NET_TCP_ERR_NONE) { return; } } } else { /* --------------- CHK RX WIN SIZE ---------------- */ if (pconn->RxWinSizeActual < 1) { /* If avail rx win size zero (see Note #6b2A) ... */ if (pconn->RxQ_App_Head == (NET_BUF *)0) { /* ... & NO avail app rx data (see Note #6b2B); ... */ /* ... inc rx win size (see Note #6b). */ pbuf = pconn->RxQ_Transport_Tail; if (pbuf != (NET_BUF *)0) { /* If transport rx Q NOT empty; ... */ /* ... remove last q'd seg (see Note #6b2C), ... */ pbuf_hdr = (NET_BUF_HDR *)&pbuf->Hdr; pbuf_q_tail = (NET_BUF *) pbuf_hdr->PrevPrimListPtr; if (pbuf_q_tail != (NET_BUF *)0) { pconn->RxQ_Transport_Tail = (NET_BUF *) pbuf_q_tail; pbuf_hdr_q_tail = (NET_BUF_HDR *)&pbuf_q_tail->Hdr; pbuf_hdr_q_tail->NextPrimListPtr = (void *) 0; } else { pconn->RxQ_Transport_Head = (NET_BUF *) 0; pconn->RxQ_Transport_Tail = (NET_BUF *) 0; NetOS_TCP_RxQ_Clr(pconn->ID, perr); if (*perr != NET_TCP_ERR_NONE) { NetTCP_ConnClose((NET_TCP_CONN *)pconn, (NET_BUF_HDR *)0, (CPU_BOOLEAN )pconn->ConnCloseAppFlag, (NET_TCP_CLOSE_CODE)NET_TCP_CONN_CLOSE_ALL); return; } } /* ... inc win size by seg's len (see Note #6b2D), */ NetTCP_RxConnWinSizeHandler((NET_TCP_CONN *)pconn, (NET_BUF_HDR *)0, (NET_TCP_WIN_SIZE)pbuf_hdr->TCP_SegLenData, (NET_TCP_WIN_CODE)NET_TCP_CONN_RX_WIN_SET); NetTCP_RxPktFree(pbuf); /* ... & free seg (see Note #6b2C). */ } else { /* Else reset TCP conn rx win size (see Note #6b3B).*/ NetTCP_RxConnWinSizeHandler((NET_TCP_CONN *)pconn, (NET_BUF_HDR *)0, (NET_TCP_WIN_SIZE)0, (NET_TCP_WIN_CODE)NET_TCP_CONN_RX_WIN_RESET); } } } } *perr = NET_TCP_ERR_NONE; } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_RxPktConnHandlerTxWinRemote() * * Description : Handle TCP connection's transmit remote host window update. * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_RxPktConnHandler(). * * ack_code Indicates the received segment's acknowledgement condition : * -------- * NET_TCP_CONN_RX_ACK_VALID Received acknowledgement number is * valid for the TCP connection. * * NET_TCP_CONN_RX_ACK_DUP Received acknowledgement number is a * duplicate for the TCP * connection. * * NET_TCP_CONN_RX_ACK_PREV Received acknowledgement number is a * previous duplicate for the TCP * connection. * * Argument validated in NetTCP_RxPktConnHandler() functions. * * pbuf_hdr Pointer to network buffer header that received TCP packet. * -------- Argument validated in NetTCP_Rx(). * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_NONE TCP connection's transmit remote host * window successfully handled. * NET_TCP_ERR_CONN_NOT_USED TCP connection NOT currently used. * NET_TCP_ERR_INVALID_CONN_STATE Invalid TCP connection state. * NET_TCP_ERR_INVALID_CONN_OP Invalid TCP connection operation. * * Return(s) : none. * * Caller(s) : NetTCP_RxPktConnHandlerSeg(). *$PAGE* * Note(s) : (1) (a) (1) The following sections ... : * * (A) RFC #1122, Section 4.2.2.20.(c) * (B) RFC #1122, Section 4.2.2.20.(f) * * (2) ... generalize that "when the connection enters ESTABLISHED STATE, the following * variables should be set" : * * (A) SND.WND <- SEG.WND * (B) SND.WL1 <- SEG.SEQ * (C) SND.WL2 <- SEG.ACK * * (b) (1) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check ACK Field : * ESTABLISHED STATE' generalizes ... * * (A) ... that for the following connected states ... : * * (1) ESTABLISHED * (2) FIN-WAIT-1 * (3) CLOSING * (4) CLOSE-WAIT * (5) LAST-ACK * (a) See 'NetTCP_RxPktConnHandlerLastAck() Notes #2d2B'. * * (B) ... that "if SND.UNA < SEG.ACK <= SND.NXT, the send window should be updated" : * * (1) If ... * * (a) ((SND.WL1 < SEG.SEQ) or * (b) ((SND.WL1 == SEG.SEQ) and * (c) (SND.WL2 <= SEG.ACK)) ... * * (2) ... [then] set ... * * (a) SND.WND <- SEG.WND * (b) SND.WL1 <- SEG.SEQ * (c) SND.WL2 <- SEG.ACK * * (2) (A) RFC #1122, Section 4.2.2.20.(g) amends the transmit window update criteria * for the segment's acknowledgement to include SND.UNA : "The window should * updated if SND.UNA <= SEG.ACK <= SND.NXT." * * This permits received segments that exactly acknowledge the TCP connection's * last acknowledged transmit sequence octet to update the transmit window in * case the remote host's receive window size is increasing or decreasing. * * (B) However, it does NOT seem reasonable to update a TCP connection's remote * transmit window for any received duplicate acknowledgement segment ; i.e. * an acknowledgement with the exact same sequence numbers & receive window * size advertisement. Otherwise, each received duplicate acknowledgement * would incorrectly update the TCP connection's remote transmit window size. * * Therefore, it seems reasonable & is assumed that the transmit window MUST * be updated for received acknowledgements that exactly acknowledge the TCP * connection's last acknowledged transmit sequence octet, if & only if the * received segment's receive window size advertisement has increased or * decreased since the last received acknowledgement segment. * * See also RFC #1122, Section 4.2.2.16 & 'NetTCP_TxConnAck() Note #4b1B1'. * * See also 'NetTCP_TxConnWinSizeHandlerCongCtrl() Note #3'. *$PAGE* * (2) RFC #793 does NOT provide nor suggest any logic to determine/handle sequence number * comparisons for sequence number windows that overflow the sequence number space. * * (a) (1) For example, in order to update the transmit window, a received segment's * acknowledgement value (SEG.ACK) MUST be greater than or equal to the last * received acknowledgement number that updated the transmit window (SND.WL2) * [see Note #1b1B1c] : * * (A) SEG.ACK >= SND.WL2 * * However, if SEG.ACK overflows the sequence number space, it will be MUCH * less than SND.WL2 until SND.WL2 also overflows : * * (B) SEG.ACK << SND.WL2 * * (2) Therefore, in order to validate a received segment's acknowledgement number * as valid for updating the TCP connection's transmit window, the following * unsigned arithmetic comparison MUST be true : * * (A) (SND.NXT - SEG.ACK) <= (SND.NXT - SND.WL2) * * (b) (1) Alternatively, to update the transmit window, a received segment's sequence * number (SEG.SEQ) MUST be greater than or equal to the last received sequence * number that updated the transmit window (SND.WL1) [see Notes #1b1B1a & #1b1B1b] : * * (A) SEG.SEQ >= SND.WL1 * * (2) Therefore, in order to validate a received segment's sequence number as valid * for updating the TCP connection's transmit window, the following unsigned * arithmetic comparison MUST be true : * * (A) (RCV.NXT + RCV.WND) - SEG.SEQ < (RCV.NXT + RCV.WND) - SND.WL1 * * See also 'NetTCP_RxPktConnIsValidAck() Note #3'. ********************************************************************************************************* */ /*$PAGE*/ static void NetTCP_RxPktConnHandlerTxWinRemote (NET_TCP_CONN *pconn, NET_TCP_ACK_CODE ack_code, NET_BUF_HDR *pbuf_hdr, NET_ERR *perr) { #if ((NET_CTR_CFG_ERR_EN == DEF_ENABLED) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif CPU_BOOLEAN tx_win_update; NET_TCP_SEQ_NBR ack_delta_next; NET_TCP_SEQ_NBR ack_delta_win_update; NET_TCP_SEQ_NBR seq_win; NET_TCP_SEQ_NBR seq_win_delta; NET_TCP_SEQ_NBR seq_win_update_delta; /* ------ VALIDATE TCP CONN TX REMOTE WIN UPDATE ------ */ tx_win_update = DEF_NO; switch (pconn->ConnState) { case NET_TCP_CONN_STATE_FREE: NET_CTR_ERR_INC(NetTCP_ErrNotUsedCtr); *perr = NET_TCP_ERR_CONN_NOT_USED; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_SYNC_RXD: case NET_TCP_CONN_STATE_SYNC_RXD_PASSIVE: case NET_TCP_CONN_STATE_SYNC_RXD_ACTIVE: case NET_TCP_CONN_STATE_SYNC_TXD: #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) /* Validate TCP conn tx Q state. */ switch (pconn->TxQ_State) { case NET_TCP_TX_Q_STATE_CONN_CLOSED: break; case NET_TCP_TX_Q_STATE_NONE: case NET_TCP_TX_Q_STATE_CLOSED: case NET_TCP_TX_Q_STATE_CONN: case NET_TCP_TX_Q_STATE_CONN_CLOSING: case NET_TCP_TX_Q_STATE_CONN_SUSPEND: default: *perr = NET_TCP_ERR_INVALID_CONN_STATE; return; /* Prevent 'break NOT reachable' compiler warning. */ } #endif tx_win_update = DEF_YES; /* Update tx win ctrls (see Note #1a2). */ break; /*$PAGE*/ case NET_TCP_CONN_STATE_CONN: case NET_TCP_CONN_STATE_FIN_WAIT_1: case NET_TCP_CONN_STATE_CLOSING: case NET_TCP_CONN_STATE_CLOSE_WAIT: case NET_TCP_CONN_STATE_LAST_ACK: #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) /* Validate TCP conn tx Q state. */ switch (pconn->TxQ_State) { case NET_TCP_TX_Q_STATE_CONN: case NET_TCP_TX_Q_STATE_CONN_CLOSING: case NET_TCP_TX_Q_STATE_CONN_SUSPEND: break; case NET_TCP_TX_Q_STATE_NONE: case NET_TCP_TX_Q_STATE_CLOSED: case NET_TCP_TX_Q_STATE_CONN_CLOSED: default: *perr = NET_TCP_ERR_INVALID_CONN_STATE; return; /* Prevent 'break NOT reachable' compiler warning. */ } #endif /* Chk tx win ctrls update (see Note #1b1B1). */ /* If seq = last win update seq (see Note #1b1B1b), .. */ if (pbuf_hdr->TCP_SeqNbr == pconn->TxWinUpdateSeqNbr) { ack_delta_next = (NET_TCP_SEQ_NBR)(pconn->TxSeqNbrNext - pbuf_hdr->TCP_AckNbr); ack_delta_win_update = (NET_TCP_SEQ_NBR)(pconn->TxSeqNbrNext - pconn->TxWinUpdateAckNbr); if (ack_delta_next <= ack_delta_win_update) { /* .. (next - ack) <= (next - last win update ack), .. */ /* .. & rx'd ack OR win != last rx'd ack or win, .. */ /* .. update tx win (see Notes #1b1B1c, #1b2B, & #2a2A);*/ tx_win_update = ((pbuf_hdr->TCP_AckNbr != pconn->TxWinUpdateAckNbr ) || (pbuf_hdr->TCP_WinSize != pconn->TxWinUpdateWinSize)) ? DEF_YES : DEF_NO; } } else { seq_win = (NET_TCP_SEQ_NBR)(pconn->RxSeqNbrNext + pconn->RxWinSizeActual); seq_win_delta = (NET_TCP_SEQ_NBR)(seq_win - pbuf_hdr->TCP_SeqNbr); seq_win_update_delta = (NET_TCP_SEQ_NBR)(seq_win - pconn->TxWinUpdateSeqNbr); if (seq_win_delta < seq_win_update_delta) { /* .. else if [(next + win) - seq] < .. */ /* .. [(next + win) - last win update seq], .. */ tx_win_update = DEF_YES; /* .. update tx win (see Notes #1b1B1a & #2b2A). */ } } break; case NET_TCP_CONN_STATE_LISTEN: case NET_TCP_CONN_STATE_FIN_WAIT_2: case NET_TCP_CONN_STATE_TIME_WAIT: case NET_TCP_CONN_STATE_CLOSING_DATA_AVAIL: NET_CTR_ERR_INC(NetTCP_ErrConnInvalidOpCtr); *perr = NET_TCP_ERR_INVALID_CONN_OP; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_NONE: case NET_TCP_CONN_STATE_CLOSED: default: NetTCP_ConnClose(pconn, pbuf_hdr, DEF_YES, NET_TCP_CONN_CLOSE_ALL); NET_CTR_ERR_INC(NetTCP_ErrConnInvalidStateCtr); *perr = NET_TCP_ERR_INVALID_CONN_STATE; return; /* Prevent 'break NOT reachable' compiler warning. */ } /* ---------- UPDATE TCP CONN TX REMOTE WIN ----------- */ if (tx_win_update == DEF_YES) { /* Update tx win ctrls (see Notes #1a2 & #1b1B2). */ NetTCP_TxConnWinSizeHandlerCongCtrl(pconn, pbuf_hdr, ack_code, 0, NET_TCP_CONN_TX_WIN_REMOTE_UPDATE, perr); if (*perr != NET_TCP_ERR_NONE) { return; } } *perr = NET_TCP_ERR_NONE; } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_RxPktConnHandlerReTxQ() * * Description : (1) Handle received valid acknowledgement segments : * * (a) Update TCP connection re-transmit queue : * * (1) Update TCP connection's unacknowledged transmit sequences See Note #2c1 * (2) Remove acknowledged TCP segments from a TCP See Note #2c2 * connection's re-transmit queue * (3) Update TCP connection's round-trip time calculations * (see 'NetTCP_TxConnRTT_RTO_Calc() Note #2a1') * (4) Update TCP connection's re-transmit timeout See Note #8b * (5) Free TCP packet buffer(s) * (6) Start TCP connection's transmit idle timer * (see 'NetTCP_TxConnTxQ_TimeoutIdleSet() Note #2a1') * * (b) Update TCP connection's transmit window(s) : * * (1) Increment TCP connection's configured transmit window size * (2) Increment TCP connection's congetion control transmit window size * * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_RxPktConnHandler(). * * ack_code Indicates the received segment's acknowledgement condition : * -------- * NET_TCP_CONN_RX_ACK_VALID Received acknowledgement number is * valid for the TCP connection. * * NET_TCP_CONN_RX_ACK_DUP Received acknowledgement number is a * duplicate for the TCP connection. * * Argument validated in NetTCP_RxPktConnHandler() functions. * * pbuf_hdr Pointer to network buffer header that received TCP packet. * -------- Argument validated in NetTCP_Rx(). * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_NONE TCP connection's re-transmit queue * successfully handled. * NET_TCP_ERR_CONN_NOT_USED TCP connection NOT currently used. * NET_TCP_ERR_INVALID_CONN_STATE Invalid TCP connection state. * NET_TCP_ERR_INVALID_CONN_OP Invalid TCP connection operation. * * NET_TCP_ERR_CONN_ACK_INVALID Invalid received segment acknowledgement. * NET_TCP_ERR_CONN_DATA_INVALID TCP connection re-transmit queue contains * invalid or improperly sequenced data. * * -- RETURNED BY NetTCP_TxConnReTxQ_TimeoutSet() : -- * NET_TCP_ERR_CONN_FAULT TCP connection fault; connection(s) aborted. * * - RETURNED BY NetTCP_TxConnWinSizeHandlerCfgd() : - * NET_TCP_ERR_TX_Q_SIGNAL_FAULT TCP connection transmit queue signal fault. * * Return(s) : none. * * Caller(s) : NetTCP_RxPktConnHandlerSeg(). *$PAGE* * Note(s) : (2) (a) The following sections ... : * * (1) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : SYN-SENT [State] : * Check SYN Bit' * (2) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check ACK Field : * ESTABLISHED STATE' * * (b) ... generalize that for ... * * (1) ... the following synchronization-to-connected state transitions ... : * * (A) SYN-RECEIVED to ESTABLISHED * (B) SYN-SENT to ESTABLISHED * * (2) ... & for the following connected states ... : * * (A) ESTABLISHED * (B) FIN-WAIT-1 * (C) CLOSING * (D) CLOSE-WAIT * (E) LAST-ACK * (1) See 'NetTCP_RxPktConnHandlerLastAck() Notes #2d2B'. * * (c) ... that ... * * (1) "If SND.UNA < SEG.ACK <= SND.NXT, SND.UNA should be advanced to equal SEG.ACK" : * * (A) SND.UNA <- SEG.ACK * * (2) "Any segments on the retransmission queue which are thereby entirely acknowledged * are removed." * * (3) A TCP connection's re-transmit queue SHOULD be updated ONLY by valid, non-duplicate * received acknowledgement segments. * * (4) Since valid received acknowledgement segments update a TCP connection's last * unacknowledged transmit sequence number ('TxSeqNbrUnackd'), any controls &/or * calculations based on the TCP connection's last unacknowledged transmit sequence * number MUST use the saved/previous value of the TCP connection's last unacknowledged * transmit sequence number ('TxSeqNbrUnackdPrev'). * * (5) Since segments enqueued to a TCP connection's re-transmit queue have already been * transmitted to the remote host & reported to the application layer as having been * transmitted, any TCP connection whose re-transmit queue becomes corrupted MUST be * closed to prevent the further transmit of corrupted data. * * (6) Although network packets are NOT required to ensure that network packet headers or * data will locate on CPU word-aligned addresses; many processors may be more efficient * & may even REQUIRE that memory transfers occur on CPU word-aligned addresses [e.g. * processors or NICs with direct memory access (DMA) capability]. * * Therefore, to ensure appropriate CPU word alignment; any segment in a TCP connection's * re-transmit queue whose transmit sequences are partially acknowledged MUST acknowledge * an exact number of sequences such that the remaining sequences are aligned on a CPU * word-aligned address. * * (a) Since RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check * Sequence Number' states that in the "SYN-RECEIVED, ESTABLISHED STATE, * FIN-WAIT-1 STATE, FIN-WAIT-2 STATE, CLOSE-WAIT STATE, CLOSING STATE, * LAST-ACK STATE, TIME-WAIT STATE" that "if a segment's contents straddle * the boundary between old and new, only the new parts should be processed"; * any data segments partially acknowledged in a TCP connections' re-transmit * queue may be re-transmitted in full or in part since the retransmission of * previously received data is not processed. *$PAGE* * (7) (a) RFC #1122, Section 4.2.3.5 states that "excessive retransmission of the same * segment by TCP indicates some failure of the remote host or the Internet path * ... When the number of transmissions of the same segment reaches a threshold * ... close the connection." * * (b) However, any segment in a TCP connection's re-transmit queue whose transmit * sequences are partially acknowledged SHOULD NOT be considered the same segment * for purposes of excessive retransmission. * * Therefore, it seems reasonable that whenever a TCP connection's re-transmit * queue segment's transmit sequences are partially acknowledged; that segment's * re-transmit counter should be reset. * * See also 'NetTCP_TxConnReTxQ() Note #3'. * * (8) (a) (1) (A) RFC #793, Section 3.7 'Data Communication : Retransmission Timeout' states * that "the Round Trip Time (RTT) ... [is] the elapsed time between" : * * (1) "sending a data octet with a particular sequence number and" ... * (2) "receiving an acknowledgment that covers that sequence number" ... * (3) "(segments sent do not have to match segments received)". * * (B) (1) RFC #2988, Section 3 adds that : * * (a) "Traditionally, TCP implementations have taken one RTT measurement at * a time (typically once per RTT)." * * (2) RFC #2988, Section 1 states that "in some situations it may be beneficial * for a TCP sender to be more conservative than the algorithms detailed in * this document allow. However, a TCP MUST NOT be more aggressive than the * ... algorithms allow". * * See also 'NetTCP_TxConnRTT_RTO_Calc() Note #2a2'. * * (2) RFC #2988, Section 3 states that "TCP MUST use Karn's algorithm ... for taking * RTT samples. That is, RTT samples MUST NOT be made using segments that were * retransmitted (and thus for which it is ambiguous whether the reply was for * the first instance of the packet or a later instance)". * * (A) (1) To determine if any segment(s) from a TCP connection's re-transmit queue * have been re-transmitted (i.e. transmitted more than once), the TCP * connection's transmit unacknowledged & un-re-transmitted sequence numbers * are compared : * * (a) If NO sequences in the TCP connection's re-transmit queue have been * re-transmitted, the TCP connection's transmit unacknowledged & * un-re-transmitted sequence numbers will be equal. * * (b) If ANY sequences in the TCP connection's re-transmit queue have been * re-transmitted, the TCP connection's transmit unacknowledged * sequence number will be less than the un-re-transmitted sequence * number. * * (2) If a received acknowledgement fully acknowledges ALL re-transmitted * segment(s) from a TCP connection's re-transmit queue, the TCP connection * advances its un-re-transmitted sequence number to the received segment's * acknowledgement sequence number. * * (B) Although RTT measurements could be calculated for ALL transmitted segments; * to simplify implementation of Karn's algorithm : * * (1) Only a single RTT measurement is calculated, ... See Note #8a1B1a * (2) (a) per TCP acknowledgement received ... See Note #8a1A2 * AND * (b) the segment at the head of a TCP connection's * re-transmit queue. See Note #8a1A1 * * See also 'NetTCP_TxConnReTxQ() Note #4'. * * (b) RFC #2988, Section 5 states that "the following is the RECOMMENDED algorithm for * managing the retransmission timer" : * * (2) "When all outstanding data has been acknowledged, turn off the retransmission * timer." * * (3) "When an ACK is received that acknowledges new data, restart the retransmission * timer so that it will expire after RTO seconds (for the current value of RTO)." * * See also 'NetTCP_TxConnReTxQ() Note #2b1A'. *$PAGE* * (9) RFC #793 does NOT provide nor suggest any logic to determine/handle sequence number * comparisons for sequence number windows that overflow the sequence number space. * * (a) For example, the next sequence octet to transmit (SND.NXT) is typically greater * than or equal to any received segment's acknowledgement number (SEG.ACK) : * * (1) SND.NXT >= SEG.ACK * * However, if SND.NXT overflows the sequence number space, it will be MUCH less * than SEG.ACK until SEG.ACK also overflows : * * (2) SND.NXT << SEG.ACK * * (b) (1) Therefore, in order to determine if a received segment's acknowledgement value * fully acknowledges previously transmitted segment(s) in the TCP connection's * re-transmit queue, the following unsigned arithmetic comparison MUST be checked : * * (A) (SND.NXT - SEG.ACK) <= SND.NXT - (ReTxQCur.SeqNbr + ReTxQCur.SegLen) * * See also 'NetTCP_RxPktConnIsValidAck() Note #3a2'. * * (2) In order to determine if a received segment's acknowledgement partially * acknowledges a previously received segment in the TCP connection's re- * transmit queue, the following unsigned arithmetic comparison MUST be checked : * * (A) (ReTxQCur.SeqNbr + ReTxQCur.SegLen) - SEG.ACK < ReTxQCur.SegLen * * See also 'NetTCP_RxPktConnHandlerRxQ_Conn() Note #4c2'. * * (3) In order to determine if a received segment's acknowledgement should * advance the TCP connection's un-re-transmitted sequence number(s), the * following unsigned arithmetic comparison MUST be true : * * (A) (SEG.ACK - ReTxQ.UnReTxdSeqNbr) <= (SND.NXT - ReTxQ.UnReTxdSeqNbr) ********************************************************************************************************* */ /*$PAGE*/ static void NetTCP_RxPktConnHandlerReTxQ (NET_TCP_CONN *pconn, NET_TCP_ACK_CODE ack_code, NET_BUF_HDR *pbuf_hdr, NET_ERR *perr) { #if ((NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) && \ (NET_CTR_CFG_ERR_EN == DEF_ENABLED) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) NET_TCP_SEQ_NBR seq_nbr_cur; NET_TCP_SEQ_NBR seq_nbr_delta; NET_TCP_SEQ_NBR seq_nbr_next_qd; #endif NET_TCP_SEQ_NBR ack_delta_seq; NET_TCP_SEQ_NBR ack_delta_seq_align; NET_TCP_SEQ_NBR ack_delta_seq_align_offset; NET_TCP_SEQ_NBR ack_delta_unretxd; NET_TCP_SEQ_NBR ack_delta_next; NET_TCP_SEQ_NBR seq_delta_unretxd; NET_TCP_SEQ_NBR seq_delta_next; NET_TCP_SEQ_NBR seq_nbr_next; NET_TCP_SEQ_NBR seq_nbr; NET_TCP_SEG_SIZE seg_len; NET_TCP_SEG_SIZE seg_len_tot; NET_TCP_SEG_SIZE seg_len_data; NET_TCP_SEG_SIZE seg_len_data_tot; NET_TCP_TX_RTT_TS_MS seg_rtt_ts_txd_ms; NET_TCP_TX_RTT_TS_MS seg_rtt_ts_rxd_ms; CPU_BOOLEAN segs_re_txd; CPU_BOOLEAN seqs_ackd; CPU_BOOLEAN done; CPU_BOOLEAN tmr_update; NET_BUF *pbuf_q; NET_BUF *pbuf_q_head; NET_BUF *pbuf_q_prev; NET_BUF *pbuf_q_next; NET_BUF_HDR *pbuf_q_hdr; NET_BUF_HDR *pbuf_q_head_hdr; NET_BUF_HDR *pbuf_q_prev_hdr; #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) /* ---------------- VALIDATE TCP CONN ----------------- */ switch (pconn->ConnState) { case NET_TCP_CONN_STATE_FREE: NET_CTR_ERR_INC(NetTCP_ErrNotUsedCtr); *perr = NET_TCP_ERR_CONN_NOT_USED; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_SYNC_RXD: case NET_TCP_CONN_STATE_SYNC_RXD_PASSIVE: case NET_TCP_CONN_STATE_SYNC_RXD_ACTIVE: case NET_TCP_CONN_STATE_SYNC_TXD: switch (pconn->TxQ_State) { case NET_TCP_TX_Q_STATE_CONN_CLOSED: switch (ack_code) { case NET_TCP_CONN_RX_ACK_VALID: break; case NET_TCP_CONN_RX_ACK_NONE: case NET_TCP_CONN_RX_ACK_INVALID: case NET_TCP_CONN_RX_ACK_DUP: case NET_TCP_CONN_RX_ACK_PREV: default: *perr = NET_TCP_ERR_CONN_ACK_INVALID; return; /* Prevent 'break NOT reachable' compiler warning. */ } break; case NET_TCP_TX_Q_STATE_NONE: case NET_TCP_TX_Q_STATE_CLOSED: case NET_TCP_TX_Q_STATE_CONN: case NET_TCP_TX_Q_STATE_CONN_CLOSING: case NET_TCP_TX_Q_STATE_CONN_SUSPEND: default: *perr = NET_TCP_ERR_INVALID_CONN_STATE; return; /* Prevent 'break NOT reachable' compiler warning. */ } break; /*$PAGE*/ case NET_TCP_CONN_STATE_CONN: case NET_TCP_CONN_STATE_FIN_WAIT_1: case NET_TCP_CONN_STATE_CLOSING: case NET_TCP_CONN_STATE_CLOSE_WAIT: case NET_TCP_CONN_STATE_LAST_ACK: switch (pconn->TxQ_State) { case NET_TCP_TX_Q_STATE_CONN: case NET_TCP_TX_Q_STATE_CONN_CLOSING: case NET_TCP_TX_Q_STATE_CONN_SUSPEND: switch (ack_code) { case NET_TCP_CONN_RX_ACK_VALID: case NET_TCP_CONN_RX_ACK_DUP: break; case NET_TCP_CONN_RX_ACK_NONE: case NET_TCP_CONN_RX_ACK_INVALID: case NET_TCP_CONN_RX_ACK_PREV: default: *perr = NET_TCP_ERR_CONN_ACK_INVALID; return; /* Prevent 'break NOT reachable' compiler warning. */ } break; case NET_TCP_TX_Q_STATE_NONE: case NET_TCP_TX_Q_STATE_CLOSED: case NET_TCP_TX_Q_STATE_CONN_CLOSED: default: *perr = NET_TCP_ERR_INVALID_CONN_STATE; return; /* Prevent 'break NOT reachable' compiler warning. */ } break; case NET_TCP_CONN_STATE_LISTEN: case NET_TCP_CONN_STATE_FIN_WAIT_2: case NET_TCP_CONN_STATE_TIME_WAIT: case NET_TCP_CONN_STATE_CLOSING_DATA_AVAIL: NET_CTR_ERR_INC(NetTCP_ErrConnInvalidOpCtr); *perr = NET_TCP_ERR_INVALID_CONN_OP; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_NONE: case NET_TCP_CONN_STATE_CLOSED: default: NetTCP_ConnClose(pconn, pbuf_hdr, DEF_YES, NET_TCP_CONN_CLOSE_ALL); NET_CTR_ERR_INC(NetTCP_ErrConnInvalidStateCtr); *perr = NET_TCP_ERR_INVALID_CONN_STATE; return; /* Prevent 'break NOT reachable' compiler warning. */ } #endif /*$PAGE*/ /* ------------- UPDATE UNACK'D TX SEQ(S) ------------- */ #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) seq_nbr_cur = (NET_TCP_SEQ_NBR)pconn->TxSeqNbrUnAckd; seq_nbr_delta = (NET_TCP_SEQ_NBR)pconn->TxSeqNbrUnAckdAlignDelta; #endif /* Save prev tx unack'd seq nbr (see Note #4). */ pconn->TxSeqNbrUnAckdPrev = (NET_TCP_SEQ_NBR)pconn->TxSeqNbrUnAckd; /* Ack prev'ly unack'd tx segs (see Note #2c1). */ pconn->TxSeqNbrUnAckd = (NET_TCP_SEQ_NBR)pbuf_hdr->TCP_AckNbr; if (pconn->TxQ_State == NET_TCP_TX_Q_STATE_CONN_CLOSING) { if (pconn->TxSeqNbrUnAckd == pconn->TxSeqNbrLast) { /* If last tx'd seq ack'd, close TCP conn tx. */ pconn->TxQ_State = NET_TCP_TX_Q_STATE_CONN_CLOSED; } } /* --------- REMOVE ACK'D SEG(S) FROM RE-TX Q --------- */ if (ack_code != NET_TCP_CONN_RX_ACK_VALID) { /* If ack NOT valid, non-dup ack; ... */ *perr = NET_TCP_ERR_NONE; /* ... do NOT update re-tx Q (see Note #3). */ return; } pbuf_q_head = (NET_BUF *)pconn->ReTxQ_Head; pbuf_q_prev = (NET_BUF *)0; pbuf_q = (NET_BUF *)pbuf_q_head; seg_len_tot = 0; seg_len_data_tot = 0; /* Chk re-tx'd seg(s) [see Notes #8a2A1 & #4]. */ segs_re_txd = (pconn->TxSeqNbrUnAckdPrev == pconn->TxSeqNbrUnReTxd) ? DEF_NO : DEF_YES; seqs_ackd = DEF_NO; done = DEF_NO; while ((pbuf_q != (NET_BUF *)0) && /* While NOT @ re-tx Q tail, ... */ (done == DEF_NO)) { /* ... srch for ack'd tx segs to remove (see Note #2c2).*/ pbuf_q_hdr = (NET_BUF_HDR *)&pbuf_q->Hdr; pbuf_q_next = (NET_BUF *) pbuf_q_hdr->NextPrimListPtr; seq_nbr = (NET_TCP_SEQ_NBR ) pbuf_q_hdr->TCP_SeqNbr; seg_len = (NET_TCP_SEG_SIZE) pbuf_q_hdr->TCP_SegLen; seg_len_data = (NET_TCP_SEG_SIZE) seg_len; if (pbuf_q_hdr->TCP_SegSync == DEF_YES) { /* If sync seg ... */ if (seg_len_data >= NET_TCP_SEG_LEN_SYNC) { /* ... & seg len >= sync seg len, ... */ seg_len_data -= NET_TCP_SEG_LEN_SYNC; /* ... dec data seg len by sync seg len. */ } } if (pbuf_q_hdr->TCP_SegClose == DEF_YES) { /* If close seg ... */ if (seg_len_data >= NET_TCP_SEG_LEN_CLOSE) { /* ... & seg len >= close seg len, ... */ seg_len_data -= NET_TCP_SEG_LEN_CLOSE; /* ... dec data seg len by close seg len. */ } } #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) seq_nbr_next_qd = seq_nbr + pconn->TxSeqNbrUnAckdAlignDelta; if (seq_nbr_cur != seq_nbr_next_qd) { /* If next q'd seg's seq nbr NOT consecutive, ... */ /* ... close TCP conn (see Note #5). */ NetTCP_ConnClose(pconn, pbuf_hdr, DEF_YES, NET_TCP_CONN_CLOSE_ALL); *perr = NET_TCP_ERR_CONN_DATA_INVALID; return; } #endif /* Chk if rx'd seg acks cur re-tx Q seg. */ ack_delta_next = (NET_TCP_SEQ_NBR)(pconn->TxSeqNbrNext - pbuf_hdr->TCP_AckNbr); seq_nbr_next = (NET_TCP_SEQ_NBR)(seq_nbr + seg_len); seq_delta_next = (NET_TCP_SEQ_NBR)(pconn->TxSeqNbrNext - seq_nbr_next); if (ack_delta_next <= seq_delta_next) { /* If seg fully acks cur re-tx Q seg (see Note #9b1A), */ pbuf_q_prev = pbuf_q; pbuf_q = pbuf_q_next; /* ... adv to next re-tx Q seg. */ pconn->TxSeqNbrUnAckdAlignDelta = 0; seg_len_tot += seg_len; seg_len_data_tot += seg_len_data; seqs_ackd = DEF_YES; #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) seq_nbr_cur += (NET_TCP_SEQ_NBR)(seg_len - seq_nbr_delta); seq_nbr_delta = (NET_TCP_SEQ_NBR) 0; #endif /*$PAGE*/ } else { /* Else chk partial re-tx Q seg ack (see Note #9b2A) : */ ack_delta_seq = (NET_TCP_SEQ_NBR)(pbuf_hdr->TCP_AckNbr - seq_nbr); if (ack_delta_seq == 0) { /* If NO seqs ack'd for cur re-tx Q seg, ... */ ; /* ... do NOT update seg. */ } else if (ack_delta_seq < seg_len) { /* If SOME seqs ack'd for cur re-tx Q seg, ... */ /* Align partial ack to word boundary (see Note #6). */ ack_delta_seq_align_offset = (NET_TCP_SEQ_NBR )(ack_delta_seq % sizeof(CPU_ALIGN)); ack_delta_seq_align = (NET_TCP_SEQ_NBR )(ack_delta_seq - ack_delta_seq_align_offset); pconn->TxSeqNbrUnAckdAlignDelta = (NET_TCP_SEQ_NBR ) ack_delta_seq_align_offset; /* ... update cur seg's seq nbr & seg len ... */ pbuf_q_hdr->TCP_SeqNbr += (CPU_INT32U ) ack_delta_seq_align; pbuf_q_hdr->TCP_SegLen -= (CPU_INT16U ) ack_delta_seq_align; pbuf_q_hdr->TCP_SegLenData -= (CPU_INT16U ) ack_delta_seq_align; seg_len_tot += (NET_TCP_SEG_SIZE) ack_delta_seq_align; seg_len_data_tot += (NET_TCP_SEG_SIZE) ack_delta_seq_align; /* ... & update TCP tx buf ctrls. */ pbuf_q_hdr->DataIx += (CPU_INT16U ) ack_delta_seq_align; pbuf_q_hdr->DataLen -= (NET_BUF_SIZE ) ack_delta_seq_align; pbuf_q_hdr->TotLen -= (NET_BUF_SIZE ) ack_delta_seq_align; pbuf_q_hdr->TCP_SegReTxCtr = 0; /* Reset re-tx ctr (see Note #7b). */ seqs_ackd = DEF_YES; #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) } else { /* Else fully-ack'd-seg chk failed?; close TCP conn? */ NetTCP_ConnClose(pconn, pbuf_hdr, DEF_YES, NET_TCP_CONN_CLOSE_ALL); *perr = NET_TCP_ERR_CONN_DATA_INVALID; return; #endif } done = DEF_YES; } } /* --------------- UPDATE TCP CONN RTO ---------------- */ if (seqs_ackd == DEF_YES) { /* If ANY re-tx Q seq(s) ack'd .. */ if (segs_re_txd == DEF_NO) { /* .. & NO re-tx Q seg(s) re-tx'd (see Note #8a2); .. */ /* .. get tx'd seg RTT ts (see Note #8a1A1) .. */ pbuf_q_head_hdr = (NET_BUF_HDR *)&pbuf_q_head->Hdr; seg_rtt_ts_txd_ms = (NET_TCP_TX_RTT_TS_MS) pbuf_q_head_hdr->TCP_RTT_TS_Txd_ms; /* .. get rx'd ack RTT ts (see Note #8a1A2) .. */ seg_rtt_ts_rxd_ms = (NET_TCP_TX_RTT_TS_MS) pbuf_hdr->TCP_RTT_TS_Rxd_ms; /* .. & calc/update RTT/RTO (see Note #1a3). */ NetTCP_TxConnRTT_RTO_Calc(pconn, NET_TCP_CONN_TX_RTT_CALC, seg_rtt_ts_txd_ms, seg_rtt_ts_rxd_ms); } ack_delta_unretxd = (NET_TCP_SEQ_NBR)(pbuf_hdr->TCP_AckNbr - pconn->TxSeqNbrUnReTxd); seq_delta_unretxd = (NET_TCP_SEQ_NBR)(pconn->TxSeqNbrNext - pconn->TxSeqNbrUnReTxd); if (ack_delta_unretxd <= seq_delta_unretxd) { /* If (ack - un-re-tx'd) < (next - un-re-tx'd), ... */ /* ... acks ALL re-tx'd seg(s) [see Note #9b3A]; */ pconn->TxSeqNbrUnReTxd = pbuf_hdr->TCP_AckNbr; /* ... adv un-re-tx'd seq(s) to ack (see Note #8a2A2).*/ } } /*$PAGE*/ /* ------------- UPDATE TCP CONN RE-TX Q -------------- */ tmr_update = seqs_ackd; /* Update re-tx Q tmr if ANY re-tx Q seqs ack'd ... */ /* ... (see Note #8b3). */ if (pbuf_q != pbuf_q_head) { /* If ANY re-tx Q segs fully ack'd, update re-tx Q. */ if (pbuf_q != (NET_BUF *)0) { /* If re-tx Q still NOT empty, . .. */ pbuf_q_prev_hdr = (NET_BUF_HDR *)&pbuf_q_prev->Hdr; pbuf_q_prev_hdr->NextPrimListPtr = (void *) 0; /* ... update re-tx Q head. */ pconn->ReTxQ_Head = (NET_BUF *) pbuf_q; pbuf_q_hdr->PrevPrimListPtr = (void *) 0; } else { /* Else clr re-tx Q. */ pconn->ReTxQ_Head = (NET_BUF *)0; pconn->ReTxQ_Tail = (NET_BUF *)0; if (pconn->ReTxQ_Tmr != (NET_TMR *)0) { /* Free re-tx Q tmr (see Note #8b2). */ NetTmr_Free(pconn->ReTxQ_Tmr); } pconn->ReTxQ_Tmr = (NET_TMR *)0; tmr_update = DEF_NO; NetTCP_TxConnTxQ_TimeoutIdleSet(pconn); /* Start tx Q idle tmr (see Note #1a6). */ } NetTCP_TxPktFree(pbuf_q_head); /* Free ALL fully ack'd seg pkt buf(s). */ } if (tmr_update == DEF_YES) { /* Update re-tx Q tmr. */ NetTCP_TxConnReTxQ_TimeoutSet(pconn, DEF_NO, NET_TCP_CONN_CLOSE_ALL, perr); if (*perr != NET_TCP_ERR_NONE) { return; } } /* ----------- UPDATE TCP CONN TX WIN SIZES ----------- */ /* Inc TCP conn's tx win sizes (see Note #1b). */ NetTCP_TxConnWinSizeHandlerCfgd((NET_TCP_CONN *)pconn, (NET_TCP_WIN_SIZE)seg_len_data_tot, (NET_TCP_WIN_CODE)NET_TCP_CONN_TX_WIN_INC, (NET_ERR *)perr); if (*perr != NET_TCP_ERR_NONE) { return; } NetTCP_TxConnWinSizeHandlerCongCtrl((NET_TCP_CONN *)pconn, (NET_BUF_HDR *)0, (NET_TCP_ACK_CODE)NET_TCP_CONN_RX_ACK_NONE, (NET_TCP_WIN_SIZE)seg_len_data_tot, (NET_TCP_WIN_CODE)NET_TCP_CONN_TX_WIN_INC, (NET_ERR *)perr); if (*perr != NET_TCP_ERR_NONE) { return; } *perr = NET_TCP_ERR_NONE; } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_RxPktConnHandlerListenQ_IsAvail() * * Description : Check if application layer listen queue is available to queue a new connection. * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_RxPktConnHandlerListen(). * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_NONE Application layer listen queue successfully * checked; check return value for listen * queue availablity. * * NET_TCP_ERR_CONN_NOT_USED TCP connection NOT currently used. * NET_TCP_ERR_INVALID_CONN_ID Invalid application connection. * NET_TCP_ERR_INVALID_CONN_STATE Invalid TCP connection state. * NET_TCP_ERR_INVALID_CONN_OP Invalid TCP connection operation. * NET_CONN_ERR_INVALID_FAMILY Invalid network connection family. * * ---- RETURNED BY NetConn_ID_AppGet() : ---- * NET_ERR_INIT_INCOMPLETE Network initialization NOT complete. * NET_CONN_ERR_INVALID_CONN Invalid network connection number. * NET_CONN_ERR_NOT_USED Network connection NOT currently used. * * Return(s) : DEF_YES, if application connection's listen queue is available to queue a new connection. * * DEF_NO, otherwise. * * Caller(s) : NetTCP_RxPktConnHandlerListen(). * * Note(s) : (1) (a) Stevens, TCP/IP Illustrated, Volume 1, 8th Printing, Section 18.11, Pages 257-258 states * that : * * (1) "Each listening end point has a fixed length queue of connections that have been * accepted by TCP (i.e., the three-way handshake is complete), but not yet accepted * by the application." * * (2) "The application specifies a limit to this queue, commonly called the backlog" : * * (A) "This backlog must be between 0 and 5, inclusive." * (B) "(Most applications specify the maximum value of 5.)" * * (3) "When a connection request arrives (i.e., the SYN segment), ... the current number * of connections already queued for this listening end point [is checked] to see * whether to accept the connection or not." * * (4) "If there is room on this listening end point's queue for this new connection, ... * the TCP module ACKs the SYN and completes the connection." * * (5) "If there is not room on the queue for the new connection" ... : * * (A) "TCP just ignores the received SYN." * (B) "Nothing is sent back (i.e., no RST segment)." * * (b) (A) Wright/Stevens, TCP/IP Illustrated, Volume 2, 3rd Printing, Section 15.9, Page 455 * reiterates that : * * (2) A "listen ... socket ... specifies a limit on the number of connections that can * be queued on the socket," ... * * (5) "after which the socket layer refuses to queue additional connection requests. * When this occurs, TCP ignores incoming connection requests." * * (B) Wright/Stevens, TCP/IP Illustrated, Volume 2, 3rd Printing, Section 28.2, Page 930 * also states that : * * (5) (A) "By silently dropping the segment" ... * (B) "and not replying with an RST," ... * (C) "The client's connection request should time out, causing the client to * retransmit the SYN." * * (C) Stevens, TCP/IP Illustrated, Volume 1, 8th Printing, Section 18.11, Pages 259-260 * summarizes that : * * (5) (A) "TCP ignores the incoming SYN when the queue is full," ... * (B) "and doesn't respond with an RST," ... * * (C) (1) "because ... this condition could change in a short while ... [and] by * ignoring the SYN, the server forces the client TCP to re-transmit the * SYN later, hoping that the queue will then have room for the new * connection". * * (2) Whereas page 259-260 counters that "if the server's TCP responded with * a reset, the client's active open would abort". * * See also 'net_sock.c NetSock_ListenQ_IsAvail() Note #2'. * * (2) The 'NET_CONN_CFG_FAMILY' pre-processor 'else'-conditional code will never be compiled/linked * since 'net_conn.h' ensures that the family type configuration constant (NET_CONN_CFG_FAMILY) * is configured with an appropriate family type value (see 'net_conn.h CONFIGURATION ERRORS'). * The 'else'-conditional code is included for completeness & as an extra precaution in case * 'net_conn.h' is incorrectly modified. ********************************************************************************************************* */ /*$PAGE*/ static CPU_BOOLEAN NetTCP_RxPktConnHandlerListenQ_IsAvail (NET_TCP_CONN *pconn, NET_ERR *perr) { #if ((NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) && \ (NET_CTR_CFG_ERR_EN == DEF_ENABLED) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif NET_CONN_ID conn_id; NET_CONN_ID conn_id_app; CPU_BOOLEAN q_avail; NET_ERR err; #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) /* ------------- VALIDATE TCP CONN STATE -------------- */ switch (pconn->ConnState) { case NET_TCP_CONN_STATE_FREE: NET_CTR_ERR_INC(NetTCP_ErrNotUsedCtr); *perr = NET_TCP_ERR_CONN_NOT_USED; return (DEF_NO); /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_LISTEN: break; case NET_TCP_CONN_STATE_CLOSED: case NET_TCP_CONN_STATE_SYNC_RXD: case NET_TCP_CONN_STATE_SYNC_RXD_PASSIVE: case NET_TCP_CONN_STATE_SYNC_RXD_ACTIVE: case NET_TCP_CONN_STATE_SYNC_TXD: case NET_TCP_CONN_STATE_CONN: case NET_TCP_CONN_STATE_FIN_WAIT_1: case NET_TCP_CONN_STATE_FIN_WAIT_2: case NET_TCP_CONN_STATE_CLOSING: case NET_TCP_CONN_STATE_TIME_WAIT: case NET_TCP_CONN_STATE_CLOSE_WAIT: case NET_TCP_CONN_STATE_LAST_ACK: NET_CTR_ERR_INC(NetTCP_ErrConnInvalidOpCtr); *perr = NET_TCP_ERR_INVALID_CONN_OP; return (DEF_NO); /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_NONE: default: NetTCP_ConnClose((NET_TCP_CONN *)pconn, (NET_BUF_HDR *)0, (CPU_BOOLEAN )DEF_YES, (NET_TCP_CLOSE_CODE)NET_TCP_CONN_CLOSE_ALL); NET_CTR_ERR_INC(NetTCP_ErrConnInvalidStateCtr); *perr = NET_TCP_ERR_INVALID_CONN_STATE; return (DEF_NO); /* Prevent 'break NOT reachable' compiler warning. */ } #endif conn_id = pconn->ID_Conn; conn_id_app = NetConn_ID_AppGet(conn_id, perr); /* Get TCP listen conn's app conn id. */ if (*perr != NET_CONN_ERR_NONE) { return (DEF_NO); } if (conn_id_app == NET_CONN_ID_NONE) { *perr = NET_TCP_ERR_INVALID_CONN_ID; return (DEF_NO); } /* -------------- CHK APP LISTEN Q AVAIL -------------- */ #if (NET_CONN_CFG_FAMILY == NET_CONN_FAMILY_IP_V4_SOCK) q_avail = NetSock_ListenQ_IsAvail((NET_SOCK_ID) conn_id_app, (NET_ERR *)&err); #else /* See Note #2. */ (void)&q_avail; /* Prevent compiler warnings. */ (void)&err; *perr = NET_CONN_ERR_INVALID_FAMILY; return (DEF_NO); #endif *perr = NET_TCP_ERR_NONE; return (q_avail); } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_RxPktConnHandlerSignalConn() * * Description : (1) Signal application layer that TCP/transport layer connection's state is now connected : * * (a) From passive SYN-RECEIVED state, signal application layer that * connection request received; connection accept now available. * * (b) From active SYN-RECEIVED or SYN-SENT states, signal application layer that * connection request complete. * * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_RxPktConnHandlerSyncRxd(), * NetTCP_RxPktConnHandlerSyncTxd(). * * state Current TCP connection state. * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_NONE Application layer successfully signaled. * NET_TCP_ERR_CONN_NOT_USED TCP connection NOT currently used. * NET_TCP_ERR_CONN_FAIL Application layer signal failed. * NET_TCP_ERR_INVALID_CONN_ID Invalid application connection. * NET_TCP_ERR_INVALID_CONN_STATE Invalid TCP connection state. * NET_TCP_ERR_INVALID_CONN_OP Invalid TCP connection operation. * NET_CONN_ERR_INVALID_FAMILY Invalid network connection family. * * --- RETURNED BY NetConn_ID_AppGet() : ---- * - RETURNED BY NetConn_ID_AppCloneGet() : - * NET_ERR_INIT_INCOMPLETE Network initialization NOT complete. * NET_CONN_ERR_INVALID_CONN Invalid network connection number. * NET_CONN_ERR_NOT_USED Network connection NOT currently used. * * Return(s) : none. * * Caller(s) : NetTCP_RxPktConnHandlerSyncRxd(), * NetTCP_RxPktConnHandlerSyncTxd(). * * Note(s) : (2) The 'NET_CONN_CFG_FAMILY' pre-processor 'else'-conditional code will never be compiled/linked * since 'net_conn.h' ensures that the family type configuration constant (NET_CONN_CFG_FAMILY) * is configured with an appropriate family type value (see 'net_conn.h CONFIGURATION ERRORS'). * The 'else'-conditional code is included for completeness & as an extra precaution in case * 'net_conn.h' is incorrectly modified. ********************************************************************************************************* */ /*$PAGE*/ static void NetTCP_RxPktConnHandlerSignalConn (NET_TCP_CONN *pconn, NET_TCP_CONN_STATE state, NET_ERR *perr) { #if ((NET_CTR_CFG_ERR_EN == DEF_ENABLED) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif NET_CONN_ID conn_id; NET_CONN_ID conn_id_app; NET_ERR err; CPU_BOOLEAN err_conn; conn_id = pconn->ID_Conn; /* ------------------- SIGNAL CONN -------------------- */ switch (state) { case NET_TCP_CONN_STATE_FREE: NET_CTR_ERR_INC(NetTCP_ErrNotUsedCtr); *perr = NET_TCP_ERR_CONN_NOT_USED; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_SYNC_RXD_PASSIVE: /* See Note #1a. */ /* Get app conn id clone. */ conn_id_app = NetConn_ID_AppCloneGet(conn_id, perr); if (*perr != NET_CONN_ERR_NONE) { return; } if (conn_id_app == NET_CONN_ID_NONE) { *perr = NET_TCP_ERR_INVALID_CONN_ID; return; } /* Signal app conn accept. */ #if (NET_CONN_CFG_FAMILY == NET_CONN_FAMILY_IP_V4_SOCK) NetSock_ConnSignalAccept((NET_SOCK_ID) conn_id_app, (NET_CONN_ID) conn_id, (NET_ERR *)&err); *perr = (err != NET_SOCK_ERR_NONE) ? NET_TCP_ERR_CONN_FAIL : NET_TCP_ERR_NONE; #else /* See Note #2. */ (void)&err; /* Prevent compiler warning. */ *perr = NET_CONN_ERR_INVALID_FAMILY; return; #endif if (*perr != NET_TCP_ERR_NONE) { return; } break; case NET_TCP_CONN_STATE_SYNC_RXD_ACTIVE: /* See Note #1b. */ case NET_TCP_CONN_STATE_SYNC_TXD: /* Get app conn id. */ conn_id_app = NetConn_ID_AppGet(conn_id, perr); if (*perr != NET_CONN_ERR_NONE) { return; } if (conn_id_app == NET_CONN_ID_NONE) { *perr = NET_TCP_ERR_INVALID_CONN_ID; return; } /* Signal app conn req. */ #if (NET_CONN_CFG_FAMILY == NET_CONN_FAMILY_IP_V4_SOCK) NetSock_ConnSignalReq((NET_SOCK_ID) conn_id_app, (NET_ERR *)&err); *perr = (err != NET_SOCK_ERR_NONE) ? NET_TCP_ERR_CONN_FAIL : NET_TCP_ERR_NONE; #else /* See Note #2. */ (void)&err; /* Prevent compiler warning. */ *perr = NET_CONN_ERR_INVALID_FAMILY; return; #endif if (*perr != NET_TCP_ERR_NONE) { return; } break; /*$PAGE*/ case NET_TCP_CONN_STATE_SYNC_RXD: /* See Notes #1a & #1b. */ /* Get app conn id. */ err_conn = DEF_NO; conn_id_app = NetConn_ID_AppGet(conn_id, perr); if (*perr != NET_CONN_ERR_NONE) { err_conn = DEF_YES; } if (conn_id_app == NET_CONN_ID_NONE) { err_conn = DEF_YES; } if (err_conn == DEF_YES) { /* If app conn id get failed, get app conn id clone. */ conn_id_app = NetConn_ID_AppCloneGet(conn_id, perr); if (*perr != NET_CONN_ERR_NONE) { return; } if (conn_id_app == NET_CONN_ID_NONE) { *perr = NET_TCP_ERR_INVALID_CONN_ID; return; } } #if (NET_CONN_CFG_FAMILY == NET_CONN_FAMILY_IP_V4_SOCK) /* Signal app conn req. */ NetSock_ConnSignalReq((NET_SOCK_ID) conn_id_app, (NET_ERR *)&err); *perr = (err != NET_SOCK_ERR_NONE) ? NET_TCP_ERR_CONN_FAIL : NET_TCP_ERR_NONE; if (*perr != NET_TCP_ERR_NONE) { return; } /* Signal app conn accept. */ NetSock_ConnSignalAccept((NET_SOCK_ID) conn_id_app, (NET_CONN_ID) conn_id, (NET_ERR *)&err); *perr = (err != NET_SOCK_ERR_NONE) ? NET_TCP_ERR_CONN_FAIL : NET_TCP_ERR_NONE; #else /* See Note #2. */ (void)&err; /* Prevent compiler warning. */ *perr = NET_CONN_ERR_INVALID_FAMILY; return; #endif if (*perr != NET_TCP_ERR_NONE) { return; } break; case NET_TCP_CONN_STATE_CLOSED: case NET_TCP_CONN_STATE_LISTEN: case NET_TCP_CONN_STATE_CONN: case NET_TCP_CONN_STATE_FIN_WAIT_1: case NET_TCP_CONN_STATE_FIN_WAIT_2: case NET_TCP_CONN_STATE_CLOSING: case NET_TCP_CONN_STATE_TIME_WAIT: case NET_TCP_CONN_STATE_CLOSE_WAIT: case NET_TCP_CONN_STATE_LAST_ACK: NET_CTR_ERR_INC(NetTCP_ErrConnInvalidOpCtr); *perr = NET_TCP_ERR_INVALID_CONN_OP; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_NONE: default: NetTCP_ConnClose((NET_TCP_CONN *)pconn, (NET_BUF_HDR *)0, (CPU_BOOLEAN )DEF_YES, (NET_TCP_CLOSE_CODE)NET_TCP_CONN_CLOSE_ALL); NET_CTR_ERR_INC(NetTCP_ErrConnInvalidStateCtr); *perr = NET_TCP_ERR_INVALID_CONN_STATE; return; /* Prevent 'break NOT reachable' compiler warning. */ } *perr = NET_TCP_ERR_NONE; } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_RxPktConnHandlerSignalClose() * * Description : Signal application layer that TCP/transport layer connection's state is now closed. * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_ConnReqClose(), * NetTCP_RxPktConnHandlerFinWait1(), * NetTCP_RxPktConnHandlerClosing(), * NetTCP_RxPktConnHandlerLastAck(). * * data_avail Indicate whether application data is still available on the TCP connection's * application receive queue : * * DEF_YES Application data is available on the * closing TCP connection's application * receive queue. * DEF_NO Application data is NOT available for the * closing TCP connection's application * receive queue. * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_NONE Application layer successfully signaled. * NET_TCP_ERR_CONN_NOT_USED TCP connection NOT currently used. * NET_TCP_ERR_CONN_FAIL Application layer signal failed. * NET_TCP_ERR_INVALID_CONN_ID Invalid application connection. * NET_TCP_ERR_INVALID_CONN_STATE Invalid TCP connection state. * NET_TCP_ERR_INVALID_CONN_OP Invalid TCP connection operation. * NET_CONN_ERR_INVALID_FAMILY Invalid network connection family. * * -- RETURNED BY NetConn_ID_AppGet() : --- * NET_ERR_INIT_INCOMPLETE Network initialization NOT complete. * NET_CONN_ERR_INVALID_CONN Invalid network connection number. * NET_CONN_ERR_NOT_USED Network connection NOT currently used. * * Return(s) : none. * * Caller(s) : NetTCP_ConnReqClose(), * NetTCP_RxPktConnHandlerFinWait1(), * NetTCP_RxPktConnHandlerClosing(), * NetTCP_RxPktConnHandlerLastAck(). * * Note(s) : (1) (a) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check ACK Field : * FIN-WAIT-2 STATE' states that "if the retransmission queue is empty, the user's * CLOSE can be acknowledged". * * (b) However, TCP connection should signal the application layer that "the user's close * [is] acknowledged" whenever its re-transmit queue becomes &/or is empty : * * (1) Transition from LISTEN to CLOSED * (2) Transition from SYN-SENT to CLOSED * * (3) Transition from FIN-WAIT-1 to ... See also 'NetTCP_RxPktConnHandlerFinWait1() * Note #2d2B2' * (A) FIN-WAIT-2 * (B) TIME-WAIT * * (4) Transition from CLOSING to TIME-WAIT See also 'NetTCP_RxPktConnHandlerClosing() * Note #2d2B2a2' * * (5) Transition from LAST-ACK to CLOSED * * (2) Once an application connection has been signaled of its close, the application connection is * responsible for closing its remaining connection(s). * * See also 'NetTCP_ConnCloseHandler() Note #2b1B'. * * (3) The 'NET_CONN_CFG_FAMILY' pre-processor 'else'-conditional code will never be compiled/linked * since 'net_conn.h' ensures that the family type configuration constant (NET_CONN_CFG_FAMILY) * is configured with an appropriate family type value (see 'net_conn.h CONFIGURATION ERRORS'). * The 'else'-conditional code is included for completeness & as an extra precaution in case * 'net_conn.h' is incorrectly modified. ********************************************************************************************************* */ /*$PAGE*/ static void NetTCP_RxPktConnHandlerSignalClose (NET_TCP_CONN *pconn, CPU_BOOLEAN data_avail, NET_ERR *perr) { #if ((NET_CTR_CFG_ERR_EN == DEF_ENABLED) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif NET_CONN_ID conn_id; NET_CONN_ID conn_id_app; NET_ERR err; /* Get conn id's. */ conn_id = pconn->ID_Conn; conn_id_app = NetConn_ID_AppGet(conn_id, perr); if (*perr != NET_CONN_ERR_NONE) { return; } if (conn_id_app == NET_CONN_ID_NONE) { *perr = NET_TCP_ERR_INVALID_CONN_ID; return; } /* ------------- VALIDATE TCP CONN STATE -------------- */ switch (pconn->ConnState) { case NET_TCP_CONN_STATE_FREE: NET_CTR_ERR_INC(NetTCP_ErrNotUsedCtr); *perr = NET_TCP_ERR_CONN_NOT_USED; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_SYNC_RXD_PASSIVE: case NET_TCP_CONN_STATE_SYNC_RXD_ACTIVE: case NET_TCP_CONN_STATE_SYNC_RXD: case NET_TCP_CONN_STATE_CONN: case NET_TCP_CONN_STATE_FIN_WAIT_2: case NET_TCP_CONN_STATE_TIME_WAIT: case NET_TCP_CONN_STATE_CLOSE_WAIT: case NET_TCP_CONN_STATE_CLOSING_DATA_AVAIL: NET_CTR_ERR_INC(NetTCP_ErrConnInvalidOpCtr); *perr = NET_TCP_ERR_INVALID_CONN_OP; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_LISTEN: /* See Note #1b1. */ case NET_TCP_CONN_STATE_SYNC_TXD: /* See Note #1b2. */ case NET_TCP_CONN_STATE_FIN_WAIT_1: /* See Note #1b3. */ case NET_TCP_CONN_STATE_CLOSING: /* See Note #1b4. */ case NET_TCP_CONN_STATE_LAST_ACK: /* See Note #1b5. */ break; case NET_TCP_CONN_STATE_NONE: case NET_TCP_CONN_STATE_CLOSED: default: NetTCP_ConnClose((NET_TCP_CONN *)pconn, (NET_BUF_HDR *)0, (CPU_BOOLEAN )DEF_YES, (NET_TCP_CLOSE_CODE)NET_TCP_CONN_CLOSE_ALL); NET_CTR_ERR_INC(NetTCP_ErrConnInvalidStateCtr); *perr = NET_TCP_ERR_INVALID_CONN_STATE; return; /* Prevent 'break NOT reachable' compiler warning. */ } /*$PAGE*/ /* ------------------- SIGNAL CLOSE ------------------- */ #if (NET_CONN_CFG_FAMILY == NET_CONN_FAMILY_IP_V4_SOCK) /* Signal app conn close. */ NetSock_ConnSignalClose((NET_SOCK_ID) conn_id_app, (CPU_BOOLEAN) data_avail, (NET_ERR *)&err); *perr = (err != NET_SOCK_ERR_NONE) ? NET_TCP_ERR_CONN_FAIL : NET_TCP_ERR_NONE; #else /* See Note #3. */ (void)&data_avail; /* Prevent compiler warnings. */ (void)&err; *perr = NET_CONN_ERR_INVALID_FAMILY; return; #endif if (*perr != NET_TCP_ERR_NONE) { return; } /* Clr app close flag; ... */ pconn->ConnCloseAppFlag = DEF_NO; /* ... app closes rem'ing conn(s) [see Note #2]. */ *perr = NET_TCP_ERR_NONE; } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_RxPktConnIsValidSeq() * * Description : Validate a received segment's sequence number with current TCP connection. * * Argument(s) : pconn Pointer to a TCP connection. * * pbuf_hdr Pointer to network buffer header that received TCP packet. * -------- Argument validated in NetTCP_Rx(). * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_NONE Sequence number successfully validated; * check return value. * NET_TCP_ERR_CONN_NOT_USED TCP connection NOT currently used. * NET_TCP_ERR_INVALID_CONN_STATE Invalid TCP connection state. * NET_TCP_ERR_INVALID_LEN_SEG Invalid TCP sequence-segment length. * * Return(s) : NET_TCP_CONN_RX_SEQ_INVALID, if received sequence number invalid for the TCP connection. * * NET_TCP_CONN_RX_SEQ_VALID, if received sequence number valid for the TCP connection. * * NET_TCP_CONN_RX_SEQ_SYNC_INVALID, if received sequence number is an invalid synchronization * for the TCP connection. * * NET_TCP_CONN_RX_SEQ_SYNC, if received sequence number is a valid synchronization * for the TCP connection. * * Caller(s) : various. * * Note(s) : (1) Validate received TCP connection sequence numbers : * * (A) Some TCP receive sequence number validation logic implemented in previous * functions; include duplicate validation logic in NetTCP_RxPktConnIsValidSeq() * only if debug/validation code is enabled (i.e. NET_ERR_CFG_ARG_CHK_DBG_EN is * DEF_ENABLED in 'net_cfg.h'). * * (a) RFC #793, Section 3.4 'Establishing a Connection : Reset Generation : 1' states * that "if [a] connection [is] ... CLOSED then a reset is sent in response to any * incoming segment". Thus, ALL received segments are invalid. * * (b) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : LISTEN [State]' : * * (1) "If the SYN bit is set ... the connection state should be changed to SYN- * RECEIVED ... and any other control or text should be queued for processing * later ... Note that any other incoming control or data ... will be processed * in the SYN-RECEIVED state." * * Therefore, the ONLY TCP segment data that may be queued in the LISTEN state * MUST be received in a TCP connection request segment. * * (2) Otherwise "any other control or text-bearing segment (not containing SYN) * must have an ACK and thus would be discarded by the ACK processing ... * [so] drop the segment". * * (c) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : SYN-SENT [State] : * Check SYN Bit' : * * (1) "If ... our SYN has been ACKed ... change the connection state to * ESTABLISHED ... [and] if there are other controls or text in the * segment then continue processing" as for the ESTABLISHED state * (see Note #1d). * * (2) "Otherwise enter SYN-RECEIVED ... [and] if there are other controls or * text in the segment, queue them for processing after the ESTABLISHED * state has been reached." * * (3) Otherwise "if neither the SYN or RST bits is set then drop the segment". * * Therefore, the ONLY TCP segment data that may be queued in the SYN-SENT * state MUST be received in a TCP connection request segment. *$PAGE* * (d) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check Sequence * Number' states that for the "SYN-RECEIVED STATE, ESTABLISHED STATE, FIN- * WAIT-1 STATE, FIN-WAIT-2 STATE, CLOSE-WAIT STATE, CLOSING STATE, LAST-ACK * STATE, TIME-WAIT STATE ... there are four cases for the acceptability test * for an incoming segment : * * Local * Segment Receive * Length Window Test * ------- ------- ------------------------------------------------------- * * (1) 0 0 SEG.SEQ = RCV.NXT * * (2) 0 >0 RCV.NXT =< SEG.SEQ < RCV.NXT + RCV.WND * * (3) >0 0 not acceptable * * (4) >0 >0 RCV.NXT =< SEG.SEQ < RCV.NXT + RCV.WND * or RCV.NXT =< SEG.SEQ + SEG.LEN - 1 < RCV.NXT + RCV.WND * * * If the RCV.WND is zero, no segments will be acceptable, but special allowance * should be made to accept valid ACKs ... and RSTs. * * If an incoming segment is not acceptable, an acknowledgment should be sent in * reply". * * See also Note #2. * * (e) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check SYN Bit' * states that in the "SYN-RECEIVED [STATE], ESTABLISHED STATE, FIN-WAIT STATE-1, * FIN-WAIT STATE-2, CLOSE-WAIT STATE, CLOSING STATE, LAST-ACK STATE, TIME-WAIT * STATE ... if the SYN is in the window it is an error, send a reset, any * outstanding RECEIVEs and SEND[s] should receive 'reset' responses, all segment * queues should be flushed, the user should also receive an unsolicited general * 'connection reset' signal[, and] enter the CLOSED state". * * (2) RFC #793 does NOT provide nor suggest any logic to determine/handle sequence number * comparisons for sequence number windows that overflow the sequence number space. * * (a) For example, the next sequence octet to receive (RCV.NXT) is typically less than * or equal to the next received sequence octet (SEG.SEQ) : * * (1) RCV.NXT <= SEG.SEQ * * SEG.SEQ itself is also typically less than RCV.NXT plus the current receive window * size (RCV.WND) : * * (2) SEG.SEQ < (RCV.NXT + RCV.WND) * * However, if (RCV.NXT + RCV.WND) or SEG.SEQ overflows the sequence number space, * these values will be MUCH less than SEG.SEQ or RCV.NXT, respectively, until * SEG.SEQ & RCV.NXT also overflow : * * (3) RCV.NXT + RCV.WND << RCV.NXT * << SEG.SEQ * * (4) SEG.SEQ << RCV.NXT * * (b) Therefore, in order to validate a received segment's sequence value as within the * TCP connection's current receive window, one of the following unsigned arithmetic * comparisons MUST be true : * * (1) (A) (RCV.NXT + RCV.WND) - SEG.SEQ <= (RCV.NXT + RCV.WND) - RCV.NXT * * (B) (RCV.NXT + RCV.WND) - SEG.SEQ <= RCV.WND * * (C) (RCV.NXT + RCV.WND) - SEG.SEQ - 1 < RCV.WND * * (D) (RCV.NXT + RCV.WND) - (SEG.SEQ + 1) < RCV.WND * * (a) Comparison #2b1C's left-hand side decremented by one to modify the * conditional test from less-than-or-equal-to to just less-than in * order to satisfy the exact boundary conditions shown in the incoming * segment acceptability test comparisons (see Notes #1d2 & #1d4). * * (2) Substituting (SEG.SEQ + SEG.LEN - 1) into SEG.SEQ : * * (A) (RCV.NXT + RCV.WND) - (SEG.SEQ + SEG.LEN - 1 + 1) < RCV.WND * * (B) (RCV.NXT + RCV.WND) - (SEG.SEQ + SEG.LEN) < RCV.WND * * (c) In order to determine if a received segment's sequence value is within but NOT * following a closing TCP connection's last valid receive sequence numbers, the * following unsigned arithmetic comparison MUST be checked : * * (1) RX.LAST - (SEG.SEQ + SEG.LEN) <= (RX.LAST - RX.NXT) ********************************************************************************************************* */ /*$PAGE*/ static NET_TCP_SEQ_CODE NetTCP_RxPktConnIsValidSeq (NET_TCP_CONN *pconn, NET_BUF_HDR *pbuf_hdr, NET_ERR *perr) { #if ((NET_CTR_CFG_ERR_EN == DEF_ENABLED) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) NET_TCP_ACK_CODE ack_code; #endif NET_TCP_SEQ_CODE seq_code; NET_TCP_SEQ_NBR seq_nbr; NET_TCP_SEQ_NBR seq_win; NET_TCP_SEQ_NBR seq_win_next; NET_TCP_SEQ_NBR seq_win_delta; *perr = NET_TCP_ERR_NONE; seq_code = NET_TCP_CONN_RX_SEQ_INVALID; if (pconn != (NET_TCP_CONN *)0) { switch (pconn->ConnState) { case NET_TCP_CONN_STATE_FREE: NET_CTR_ERR_INC(NetTCP_ErrNotUsedCtr); *perr = NET_TCP_ERR_CONN_NOT_USED; return (NET_TCP_CONN_RX_SEQ_INVALID); /* Prevent 'break NOT reachable' warning. */ case NET_TCP_CONN_STATE_CLOSED: /* See Note #1a. */ break; case NET_TCP_CONN_STATE_LISTEN: /* See Note #1b. */ if (pbuf_hdr->TCP_SegSync == DEF_YES) { /* If sync rx'd, .. */ seq_code = NET_TCP_CONN_RX_SEQ_SYNC; /* .. sync's seq nbr valid (see Note #1b1). */ } break; case NET_TCP_CONN_STATE_SYNC_TXD: /* See Note #1c. */ #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) /* See Note #1A. */ ack_code = NetTCP_RxPktConnIsValidAck(pconn, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return (NET_TCP_CONN_RX_SEQ_INVALID); } if (ack_code != NET_TCP_CONN_RX_ACK_VALID) { return (NET_TCP_CONN_RX_SEQ_INVALID); } #endif if (pbuf_hdr->TCP_SegSync == DEF_YES) { /* If valid sync rx'd, .. */ seq_code = NET_TCP_CONN_RX_SEQ_SYNC; /* .. sync's seq nbr valid. */ } break; /*$PAGE*/ case NET_TCP_CONN_STATE_SYNC_RXD: /* See Notes #1d & #1e. */ case NET_TCP_CONN_STATE_SYNC_RXD_PASSIVE: case NET_TCP_CONN_STATE_SYNC_RXD_ACTIVE: case NET_TCP_CONN_STATE_CONN: case NET_TCP_CONN_STATE_FIN_WAIT_1: case NET_TCP_CONN_STATE_FIN_WAIT_2: case NET_TCP_CONN_STATE_CLOSING: case NET_TCP_CONN_STATE_TIME_WAIT: case NET_TCP_CONN_STATE_CLOSE_WAIT: case NET_TCP_CONN_STATE_LAST_ACK: if (pbuf_hdr->TCP_SegSync != DEF_NO) { /* If sync rx'd, ... */ return (NET_TCP_CONN_RX_SEQ_SYNC_INVALID); /* ... rtn invalid sync (see Note #1e). */ } if (pconn->RxWinSizeActual > 0) { /* If rx win size > 0, ... */ /* ... chk for seg seq within rx win. */ switch (pconn->RxQ_State) { case NET_TCP_RX_Q_STATE_CONN_SYNC: case NET_TCP_RX_Q_STATE_CONN: seq_win_next = (NET_TCP_SEQ_NBR)(pconn->RxSeqNbrNext + pconn->RxWinSizeActual); seq_nbr = (NET_TCP_SEQ_NBR)(pbuf_hdr->TCP_SeqNbr + 1); seq_win_delta = (NET_TCP_SEQ_NBR)(seq_win_next - seq_nbr); /* If [(next + win) - seq ] < win size, */ if (seq_win_delta < pconn->RxWinSizeActual) { seq_code = NET_TCP_CONN_RX_SEQ_VALID; /* .. seq nbr within rx win (see Note #1d2). */ } else if (pbuf_hdr->TCP_SegLen > 0) { seq_nbr = (NET_TCP_SEQ_NBR)(pbuf_hdr->TCP_SeqNbr + pbuf_hdr->TCP_SegLen); seq_win_delta = (NET_TCP_SEQ_NBR)(seq_win_next - seq_nbr); /* If [(next + win) - (seq + len)] < win size, */ if (seq_win_delta < pconn->RxWinSizeActual) { /* .. seq nbr within rx win (see Note #1d4). */ seq_code = NET_TCP_CONN_RX_SEQ_VALID; } } break; case NET_TCP_RX_Q_STATE_CONN_CLOSED: case NET_TCP_RX_Q_STATE_CONN_CLOSING: seq_win_next = (NET_TCP_SEQ_NBR) pconn->RxSeqNbrLast; seq_nbr = (NET_TCP_SEQ_NBR)(pbuf_hdr->TCP_SeqNbr + pbuf_hdr->TCP_SegLen); seq_win_delta = (NET_TCP_SEQ_NBR)(seq_win_next - seq_nbr); seq_win = (NET_TCP_SEQ_NBR)(pconn->RxSeqNbrLast - pconn->RxSeqNbrNext); if (seq_win_delta <= seq_win) { /* If [last - (seq + len)] <= [last - next], .. */ seq_code = NET_TCP_CONN_RX_SEQ_VALID; /* .. seq nbr within close win (see Note #2c1). */ } break; case NET_TCP_RX_Q_STATE_NONE: case NET_TCP_RX_Q_STATE_CLOSED: default: return (NET_TCP_CONN_RX_SEQ_INVALID); } } else { /* Else chk for valid, zero-len seg. */ if (pbuf_hdr->TCP_SegLen > 0) { /* If seg len > 0, ... */ *perr = NET_TCP_ERR_INVALID_LEN_SEG; /* ... rtn invalid seg len (see Note #1d3). */ return (NET_TCP_CONN_RX_SEQ_INVALID); } if (pbuf_hdr->TCP_SeqNbr == pconn->RxSeqNbrNext) { /* If seq nbr equals next rx seq expected, .. */ seq_code = NET_TCP_CONN_RX_SEQ_VALID; /* .. seq nbr valid (see Note #1d1). */ } } break; case NET_TCP_CONN_STATE_CLOSING_DATA_AVAIL: break; case NET_TCP_CONN_STATE_NONE: default: NetTCP_ConnClose(pconn, pbuf_hdr, DEF_YES, NET_TCP_CONN_CLOSE_ALL); NET_CTR_ERR_INC(NetTCP_ErrConnInvalidStateCtr); *perr = NET_TCP_ERR_INVALID_CONN_STATE; return (NET_TCP_CONN_RX_SEQ_INVALID); /* Prevent 'break NOT reachable' warning. */ } } return (seq_code); } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_RxPktConnIsValidAck() * * Description : Validate a received segment's acknowledgement number with current TCP connection. * * Argument(s) : pconn Pointer to a TCP connection. * * pbuf_hdr Pointer to network buffer header that received TCP packet. * -------- Argument validated in NetTCP_Rx(). * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_NONE Acknowledgement number successfully * validated; check return value. * NET_TCP_ERR_CONN_NOT_USED TCP connection NOT currently used. * NET_TCP_ERR_INVALID_CONN_STATE Invalid TCP connection state. * * Return(s) : NET_TCP_CONN_RX_ACK_INVALID, if received acknowledgement number invalid for the TCP connection. * * NET_TCP_CONN_RX_ACK_VALID, if received acknowledgement number valid for the TCP connection. * * NET_TCP_CONN_RX_ACK_NONE, if NO received acknowledgement number. * * NET_TCP_CONN_RX_ACK_DUP, if received acknowledgement number is a duplicate for the * TCP connection. * * NET_TCP_CONN_RX_ACK_PREV, if received acknowledgement number is a previous duplicate for the * TCP connection. * * Caller(s) : various. * * Note(s) : (1) Validate received TCP connection acknowledgements : * * (a) RFC #793, Section 3.4 'Establishing a Connection : Reset Generation : 1' states * that "if [a] connection [is] ... CLOSED then a reset is sent in response to any * incoming segment". Thus, ALL received segments, including acknowledgement * segments, are invalid. * * (b) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : LISTEN [State] : * Check for ACK' states that "any acknowledgment is bad if it arrives on a * connection still in the LISTEN state". * * (c) (1) The following sections generalize that "if SND.UNA < SEG.ACK =< SND.NXT then * the ACK is acceptable", whereas "if SEG.ACK =< SND.UNA, or SEG.ACK > SND.NXT" * then the acknowledgement is unacceptable : * * (A) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : SYN-SENT * [State] : Check ACK Bit' * (B) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check ACK * Field : SYN-RECEIVED STATE' * (C) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check ACK * Field : ESTABLISHED STATE' * * Including states with similar "processing as for the ESTABLISHED STATE" (see * RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check ACK Field') : * * (D) FIN-WAIT-1 STATE * (E) FIN-WAIT-2 STATE * (F) CLOSING STATE * (G) CLOSE-WAIT STATE * * See also Note #3a. * * (2) However, RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : SYN-SENT * [State] : Check SYN Bit' also states to "check the SYN bit ... if the ACK is * ok, or there is no ACK". * * Therefore, the lack of an acknowledgement in the SYN-SENT state is acceptable. * * (3) However, RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check * ACK Field : ESTABLISHED STATE' also states that in the "ESTABLISHED STATE" * or any state with similar "processing as for the ESTABLISHED STATE ... if * the ACK is a duplicate (SEG.ACK =< SND.UNA), it can be ignored". * * See also Note #3b. * * (d) (1) (A) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check ACK Field : * TIME-WAIT STATE' states that "the only thing that can arrive in this state is * a retransmission of the remote FIN". * * (B) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check ACK Field : * LAST-ACK STATE' states that "the only thing that can arrive in this state is * an acknowledgement of our FIN". * * (2) However, it is possible that some but NOT all transmitted data has been received * by the remote host. In other words, the remote host may have received some but * NOT ALL transmitted data preceding this host's connection close request. * * ???? Therefore, acknowledgements validated by as within the transmit window MUST * be received & processed as in connected states. *$PAGE* * (2) ???? RFC #793 contains multiple errors/inconsistencies regarding the boundary conditions * of TCP sequence/acknowledgement inequalities -- i.e. some of the expressions incorrectly * include or exclude the equality condition. * * RFC #1122, Section 4.2.2.20 corrects some but NOT all of these errors/inconsistencies. * * (3) RFC #793 does NOT provide nor suggest any logic to determine/handle sequence number * comparisons for sequence number windows that overflow the sequence number space. * * (a) (1) For example, the next sequence octet to transmit (SND.NXT) is typically greater * than or equal to the last unacknowledged transmit sequence octet (SND.UNA) : * * (A) SND.NXT >= SND.UNA * * However, when SND.NXT overflows the sequence number space, it will be MUCH less * than SND.UNA until SND.UNA also overflows : * * (B) SND.NXT << SND.UNA * * (2) Therefore, in order to validate a received segment's acknowledgement value * (SEG.ACK) as within the TCP connection's current transmit acknowledgement * window, the following unsigned arithmetic comparison MUST be true : * * (A) (SND.NXT - SEG.ACK) < (SND.NXT - SND.UNA) * * (b) (1) Typically, duplicate acknowledgements are less than or equal to the last * unacknowledged transmit sequence octet (SND.UNA) : * * (A) SEG.ACK <= SND.UNA * * However, when SND.UNA overflows the sequence number space, it will be MUCH less * than duplicate acknowledgements until these duplicates also overflow : * * (B) SEG.ACK >> SND.UNA * * (2) Therefore, in order to validate SEG.ACK as a recent duplicate acknowledgement * for the TCP connection's current transmit acknowledgement window, the following * unsigned arithmetic comparison MUST be true : * * (A) (SND.UNA - SEG.ACK) <= (WIN_SIZE_SCALE * TxWinSizeRemote) * * where * * WIN_SIZE_SCALE is some window-size scalar multiplier * TxWinSizeRemote is the remote host's last-advertised * receive window size, which is this * host's transmit remote window size ********************************************************************************************************* */ static NET_TCP_ACK_CODE NetTCP_RxPktConnIsValidAck (NET_TCP_CONN *pconn, NET_BUF_HDR *pbuf_hdr, NET_ERR *perr) { #if ((NET_CTR_CFG_ERR_EN == DEF_ENABLED) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif NET_TCP_ACK_CODE ack_code; NET_TCP_SEQ_NBR ack_delta_next; NET_TCP_SEQ_NBR ack_delta_unackd; NET_TCP_SEQ_NBR ack_win; NET_TCP_SEQ_NBR ack_dup_win; /*$PAGE*/ *perr = NET_TCP_ERR_NONE; ack_code = NET_TCP_CONN_RX_ACK_INVALID; if (pconn != (NET_TCP_CONN *)0) { switch (pconn->ConnState) { case NET_TCP_CONN_STATE_FREE: NET_CTR_ERR_INC(NetTCP_ErrNotUsedCtr); *perr = NET_TCP_ERR_CONN_NOT_USED; return (NET_TCP_CONN_RX_ACK_INVALID); /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_CLOSED: /* See Note #1a. */ case NET_TCP_CONN_STATE_LISTEN: /* See Note #1b. */ if (pbuf_hdr->TCP_SegAck != DEF_NO) { /* If ack rx'd, ... */ return (NET_TCP_CONN_RX_ACK_INVALID); /* ... rtn invalid ack. */ } ack_code = NET_TCP_CONN_RX_ACK_NONE; break; case NET_TCP_CONN_STATE_SYNC_TXD: if (pbuf_hdr->TCP_SegAck == DEF_NO) { /* If NO ack rx'd, ... */ return (NET_TCP_CONN_RX_ACK_NONE); /* ... rtn valid ack (see Note #1c2). */ } /* 'break' intentionally omitted; do NOT move .. */ /* .. from the following case (see Note #1c1A) : .. */ /* .. 'NET_TCP_CONN_STATE_SYNC_RXD'. */ case NET_TCP_CONN_STATE_SYNC_RXD: /* See Note #1c1B. */ case NET_TCP_CONN_STATE_SYNC_RXD_PASSIVE: case NET_TCP_CONN_STATE_SYNC_RXD_ACTIVE: if (pbuf_hdr->TCP_SegAck != DEF_YES) { /* If NO ack rx'd, ... */ return (NET_TCP_CONN_RX_ACK_NONE); /* ... rtn NO ack. */ } ack_delta_next = (NET_TCP_SEQ_NBR)(pconn->TxSeqNbrNext - pbuf_hdr->TCP_AckNbr); ack_win = (NET_TCP_SEQ_NBR)(pconn->TxSeqNbrNext - pconn->TxSeqNbrUnAckd); if (ack_delta_next < ack_win) { /* If (next - ack) < ack win, ... */ ack_code = NET_TCP_CONN_RX_ACK_VALID; /* ... ack is within ack win (see Note #3a2A). */ } break; case NET_TCP_CONN_STATE_CONN: /* See Note #1c1C. */ case NET_TCP_CONN_STATE_FIN_WAIT_1: /* See Note #1c1D. */ case NET_TCP_CONN_STATE_FIN_WAIT_2: /* See Note #1c1E. */ case NET_TCP_CONN_STATE_CLOSING: /* See Note #1c1F. */ case NET_TCP_CONN_STATE_CLOSE_WAIT: /* See Note #1c1G. */ case NET_TCP_CONN_STATE_TIME_WAIT: /* See Note #1d2. */ case NET_TCP_CONN_STATE_LAST_ACK: /* See Note #1d2. */ if (pbuf_hdr->TCP_SegAck != DEF_YES) { /* If NO ack rx'd, ... */ return (NET_TCP_CONN_RX_ACK_NONE); /* ... rtn NO ack. */ } ack_delta_next = (NET_TCP_SEQ_NBR)(pconn->TxSeqNbrNext - pbuf_hdr->TCP_AckNbr); ack_win = (NET_TCP_SEQ_NBR)(pconn->TxSeqNbrNext - pconn->TxSeqNbrUnAckd); if (ack_delta_next < ack_win) { /* If (next - ack) < ack win, ... */ ack_code = NET_TCP_CONN_RX_ACK_VALID; /* ... ack is within ack win (see Note #3a2A). */ } else { ack_delta_unackd = (NET_TCP_SEQ_NBR)(pconn->TxSeqNbrUnAckd - pbuf_hdr->TCP_AckNbr); if (ack_delta_unackd == 0) { /* If (unackd - ack) = 0, ... */ ack_code = NET_TCP_CONN_RX_ACK_DUP; /* ... ack is dup ack. */ /* ... (see Note #1c3). */ } else { ack_dup_win = (NET_TCP_SEQ_NBR)(NET_TCP_ACK_NBR_DUP_WIN_SIZE_SCALE * pconn->TxWinSizeRemote); if (ack_delta_unackd <= ack_dup_win) { /* If (unackd - ack) <= dup ack win, ... */ ack_code = NET_TCP_CONN_RX_ACK_PREV; /* ... ack is recent dup ack ... */ /* ... (see Notes #1c3 & #3b2A). */ } } } break; case NET_TCP_CONN_STATE_CLOSING_DATA_AVAIL: break; case NET_TCP_CONN_STATE_NONE: default: NetTCP_ConnClose(pconn, pbuf_hdr, DEF_YES, NET_TCP_CONN_CLOSE_ALL); NET_CTR_ERR_INC(NetTCP_ErrConnInvalidStateCtr); *perr = NET_TCP_ERR_INVALID_CONN_STATE; return (NET_TCP_CONN_RX_ACK_INVALID); /* Prevent 'break NOT reachable' compiler warning. */ } } return (ack_code); } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_RxPktConnIsValidReset() * * Description : Validate a received segment's reset flag, if any, with current TCP connection. * * Argument(s) : pconn Pointer to a TCP connection. * * pbuf_hdr Pointer to network buffer header that received TCP packet. * -------- Argument validated in NetTCP_Rx(). * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_NONE Received segment's reset flag successfully * validated; check return value. * NET_TCP_ERR_CONN_NOT_USED TCP connection NOT currently used. * NET_TCP_ERR_INVALID_CONN_STATE Invalid TCP connection state. * * - RETURNED BY NetTCP_RxPktConnIsValidSeq() : - * NET_TCP_ERR_INVALID_LEN_SEG Invalid TCP sequence-segment length. * * Return(s) : NET_TCP_CONN_RX_RESET_INVALID, if received reset flag invalid for the TCP connection. * * NET_TCP_CONN_RX_RESET_VALID, if received reset flag valid for the TCP connection; * i.e. reset the TCP connection. * * NET_TCP_CONN_RX_RESET_NONE, if NO received reset flag. * * Caller(s) : various. * * Note(s) : (1) See the following RFC's for TCP reset validation summary : * * (a) RFC #793, Section 3.4 'Establishing a Connection : Reset Processing' * (b) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES' * (c) RFC Draft-IETF-TCPm-TCPSecure #00, Section 2 * * (2) Validate received TCP connection resets : * * (A) Some TCP receive reset validation logic implemented in previous functions; include * duplicate validation logic in NetTCP_RxPktConnIsValidReset() only if debug/validation * code is enabled (i.e. NET_ERR_CFG_ARG_CHK_DBG_EN is DEF_ENABLED in 'net_cfg.h'). * * (a) (1) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : CLOSED [State]' states * that "if the state is CLOSED ... an incoming segment containing a RST is discarded". * * (2) (A) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : LISTEN [State]' * states that if "the state is LISTEN ... an incoming RST should be ignored". * * (B) RFC #793, Section 3.4 'Establishing a Connection : Reset Processing' reiterates * that "if the receiver ... of a RST ... was in the LISTEN state, it ignores it". * * (3) (A) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : SYN-SENT [State] : * Check RST Bit' states that "if the RST bit is set [and] if the ACK was * acceptable then ... enter CLOSED state". * * (B) RFC #793, Section 3.4 'Establishing a Connection : Reset Processing' reiterates * that "in the SYN-SENT state (a RST received in response to an initial SYN), the * RST is acceptable if the ACK field acknowledges the SYN". * * (1) However, since receiving a TCP connection request/synchronization segment * with no acknowledgement number in the SYN-SENT state is permitted, then * a received TCP reset segment with no acknowledgement number MUST also be * acceptable. * * See also Note #2a5C. *$PAGE* * (4) (A) (1) RFC #793, Section 3.4 'Establishing a Connection : Reset Processing' * summarizes that "in all states except SYN-SENT, all reset (RST) segments * are validated by checking their SEQ-fields. A reset is valid if its * sequence number is in the window". * * (2) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check RST Bit' * reiterates that the received segment's reset is valid if and only if its * sequence number is valid since it follows RFC #793, Section 3.9 'Event * Processing : SEGMENT ARRIVES : Check Sequence Number'. * * (3) RFC #793, Section 3.3 states "that when the receive window is zero no * segments should be acceptable except ACK segments ... However, even when * the receive window is zero, a TCP must process the RST ... fields of all * incoming segments". * * However, this contradicts the following sections which state that a * "received segment's reset is valid if and only if its sequence number if * valid" & that "if an incoming segment is not acceptable ... [and] the RST * bit is set, ... drop the segment" : * * (a) RFC #793, Section 3.4 'Establishing a Connection : Reset Processing' * (b) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check * Sequence Number' * * See also Note #2a5B. * * (B) (1) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check RST Bit' * states that in the "SYN-RECEIVED STATE" that "if the RST bit is set [and] * this connection was initiated with a passive OPEN (i.e., came from the * LISTEN state), then return this connection to the LISTEN state ... If this * connection was initiated with an active OPEN (i.e., came from SYN-SENT state) * then the connection was refused, ... enter the CLOSED state". * * (2) RFC #793, Section 3.4 'Establishing a Connection : Reset Processing' * reiterates that "if the receiver ... of a RST ... was in SYN-RECEIVED state * and had previously been in the LISTEN state, then the receiver returns to * the LISTEN state, otherwise the receiver aborts the connection and goes to * the CLOSED state". * * (3) However, since TCP connections opened from the LISTEN state are cloned from * the original LISTEN-state TCP connection, it is NOT necessary to return ANY * reset TCP connection from the SYN-RECEIVED state back to the LISTEN state. * * See also Note #2a5A. * * (C) (1) RFC #793, Section 3.4 'Establishing a Connection : Reset Processing' states * that "if the receiver ... of a RST ... was in any other state [other than * LISTEN or SYN-RECEIVED], it aborts the connection and advises the user and * goes to the CLOSED state". * * (2) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check RST Bit' * states that in the "ESTABLISHED, FIN-WAIT-1, FIN-WAIT-2, CLOSE-WAIT" states * that "if the RST bit is set then, any outstanding RECEIVEs and SEND[s] should * receive 'reset' responses. All segment queues should be flushed. Users * should also receive an unsolicited general 'connection reset' signal[, and] * enter the CLOSED state". * * (3) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check RST Bit' * states that in the "CLOSING STATE, LAST-ACK STATE, TIME-WAIT" states that * "if the RST bit is set then, enter the CLOSED state". * * See also Note #2a5A. *$PAGE* * (5) (A) RFC Draft-IETF-TCPm-TCPSecure #00, Section 2.2 amends the "handling of a segment * with the RST bit when in a synchronized state" to "provide some protection against * ... blind reset attack[s] using the RST bit" : * * (a) "If the RST bit is set and the sequence number is outside the expected window, * silently drop the segment." * * (b) "If the RST bit is exactly the next expected sequence number [sic], reset the * connection"; it is assumed that this should read "if the RST bit is set and * the sequence number is exactly the next expected sequence number, reset the * connection." * * (c) "If the RST bit is set and the sequence number does not exactly match * the next expected sequence value, yet is within the acceptable window * (RCV.NXT <= SEG.SEQ < RCV.NXT+RCV.WND) send an acknowledgment." * * (B) ???? Although RFC Draft-IETF-TCPm-TCPSecure #00 explicitly states that this * amendment applies only to the "handling of a ... RST ... when in a synchronized * state", it is assumed that this should also apply to the SYN-RECEIVED state. * * (C) (1) ???? In addition, it is assumed that a similar validation should also * apply to the SYN-SENT state. Since the SYN-SENT state validates a * received TCP reset segment based only on the segment's acknowledgement, * a similar validation could require that the acknowledgement "exactly * match the next expected sequence value" -- i.e. the initial connection * synchronization sequence number plus one. * * (2) However, a specific check for the exact acknowledgement is NOT necessary * since NO data is transmitted from the SYN-SENT state. Therefore, the ONLY * value to be acknowledged is the initial connection synchronization sequence * number. * * (D) ???? In addition, RFC Draft-IETF-TCPm-TCPSecure #00 does NOT provide a precedence * priority for handling TCP segments received with BOTH the RST & SYN bits set. * * ???? Therefore, since it does NOT seem reasonable to reset a TCP connection * due to a TCP segment that also attempted to synchronize the TCP connection, * it is assumed that the amended handling of the SYN bit should take precedence * over the amended handling of the RST bit. * * (b) RFC #1122, Section 4.2.2.12 states that "a TCP SHOULD allow a received RST segment * to include data". ********************************************************************************************************* */ static NET_TCP_RESET_CODE NetTCP_RxPktConnIsValidReset (NET_TCP_CONN *pconn, NET_BUF_HDR *pbuf_hdr, NET_ERR *perr) { #if ((NET_CTR_CFG_ERR_EN == DEF_ENABLED) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) NET_TCP_ACK_CODE ack_code; NET_TCP_SEQ_CODE seq_code; #endif NET_TCP_RESET_CODE reset_code; /*$PAGE*/ *perr = NET_TCP_ERR_NONE; if (pbuf_hdr->TCP_SegReset != DEF_YES) { /* If NO reset rx'd, ... */ return (NET_TCP_CONN_RX_RESET_NONE); /* ... rtn NONE. */ } reset_code = NET_TCP_CONN_RX_RESET_INVALID; if (pconn != (NET_TCP_CONN *)0) { switch (pconn->ConnState) { case NET_TCP_CONN_STATE_FREE: NET_CTR_ERR_INC(NetTCP_ErrNotUsedCtr); *perr = NET_TCP_ERR_CONN_NOT_USED; return (NET_TCP_CONN_RX_RESET_INVALID); /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_CLOSED: /* See Note #2a1. */ case NET_TCP_CONN_STATE_LISTEN: /* See Note #2a2. */ break; case NET_TCP_CONN_STATE_SYNC_TXD: /* See Note #2a3. */ #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) /* See Note #2A. */ ack_code = NetTCP_RxPktConnIsValidAck(pconn, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return (NET_TCP_CONN_RX_RESET_INVALID); } if (ack_code == NET_TCP_CONN_RX_ACK_INVALID) { return (NET_TCP_CONN_RX_RESET_INVALID); } #endif reset_code = NET_TCP_CONN_RX_RESET_VALID; break; case NET_TCP_CONN_STATE_SYNC_RXD: /* See Notes #2a4B & #2a5B. */ case NET_TCP_CONN_STATE_SYNC_RXD_PASSIVE: case NET_TCP_CONN_STATE_SYNC_RXD_ACTIVE: case NET_TCP_CONN_STATE_CONN: /* See Notes #2a4C & #2a5A. */ case NET_TCP_CONN_STATE_FIN_WAIT_1: case NET_TCP_CONN_STATE_FIN_WAIT_2: case NET_TCP_CONN_STATE_CLOSING: case NET_TCP_CONN_STATE_TIME_WAIT: case NET_TCP_CONN_STATE_CLOSE_WAIT: case NET_TCP_CONN_STATE_LAST_ACK: #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) /* See Note #2A. */ seq_code = NetTCP_RxPktConnIsValidSeq(pconn, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return (NET_TCP_CONN_RX_RESET_INVALID); } if (seq_code != NET_TCP_CONN_RX_SEQ_VALID) { return (NET_TCP_CONN_RX_RESET_INVALID); } #endif if (pbuf_hdr->TCP_SeqNbr == pconn->RxSeqNbrNext) { /* If seq nbr equals next rx seq expected ... */ reset_code = NET_TCP_CONN_RX_RESET_VALID; /* ... (see Note #2a5Ab), rtn valid reset. */ } break; case NET_TCP_CONN_STATE_CLOSING_DATA_AVAIL: break; case NET_TCP_CONN_STATE_NONE: default: NetTCP_ConnClose(pconn, pbuf_hdr, DEF_YES, NET_TCP_CONN_CLOSE_ALL); NET_CTR_ERR_INC(NetTCP_ErrConnInvalidStateCtr); *perr = NET_TCP_ERR_INVALID_CONN_STATE; return (NET_TCP_CONN_RX_RESET_INVALID); /* Prevent 'break NOT reachable' warning. */ } } return (reset_code); } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_RxPktFree() * * Description : Free network buffer(s). * * Argument(s) : pbuf_q Pointer to network buffer queue. * * Return(s) : none. * * Caller(s) : NetTCP_RxAppData(), * NetTCP_RxPktConnHandler(), * NetTCP_RxPktConnHandlerRxQ_AppData(). * * Note(s) : none. ********************************************************************************************************* */ static void NetTCP_RxPktFree (NET_BUF *pbuf_q) { NetBuf_FreeBufQ_PrimList((NET_BUF *)pbuf_q, (NET_CTR *)0); } /* ********************************************************************************************************* * NetTCP_RxPktDiscard() * * Description : On any TCP Receive errors, discard TCP packet(s) & buffer(s). * * Argument(s) : pbuf Pointer to network buffer. * * perr Pointer to variable that will receive the return error code from this function : * * NET_ERR_RX Receive error; packet discarded. * * Return(s) : none. * * Caller(s) : NetTCP_Rx(), * NetTCP_RxPktConnHandler(). * * Note(s) : none. ********************************************************************************************************* */ static void NetTCP_RxPktDiscard (NET_BUF *pbuf, NET_ERR *perr) { NET_CTR *pctr; #if (NET_CTR_CFG_ERR_EN == DEF_ENABLED) pctr = (NET_CTR *)&NetTCP_ErrRxPktDiscardedCtr; #else pctr = (NET_CTR *) 0; #endif NetBuf_FreeBufQ_PrimList((NET_BUF *)pbuf, (NET_CTR *)pctr); *perr = NET_ERR_RX; } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_RxConnWinSizeCfg() * * Description : (1) Configure TCP connection's receive window controls : * * (a) Configure TCP connection's receive window update threshold * * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_ConnCfg(). * * Return(s) : none. * * Caller(s) : NetTCP_ConnCfg(), * NetTCP_ConnCfgRxWinSize(). * * Note(s) : (2) A TCP connection's receive window controls should NOT be updated until after the * following TCP connection control(s) have been configured : * * (a) TCP connection's configured receive window size ('RxWinSizeCfgd') * [see 'NetTCP_RxConnWinSizeCfgUpdateTh() Note #2a'] * * (b) TCP connection's connection maximum segment size ('MaxSegSizeConn') * [see 'NetTCP_RxConnWinSizeCfgUpdateTh() Note #2b'] ********************************************************************************************************* */ static void NetTCP_RxConnWinSizeCfg (NET_TCP_CONN *pconn) { NetTCP_RxConnWinSizeCfgUpdateTh(pconn); } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_RxConnWinSizeCfgUpdateTh() * * Description : Configure TCP connection's receive window update threshold. * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_RxConnWinSizeCfg(). * * Return(s) : none. * * Caller(s) : NetTCP_RxConnWinSizeCfg(). * * Note(s) : (1) RFC #1122, Section 4.2.3.3 states that "the suggested SWS avoidance algorithm for * the receiver is ... to avoid advancing the right window edge RCV.NXT+RCV.WND ... * until the reduction satisfies" : * * (a) RCV.BUFF - RCV.USER - RCV.WND >= min(Fr * RCV.BUFF, Eff.snd.MSS) * * where * (1) RCV.BUFF Total receive buffer space * (2) RCV.USER Data received but not yet consumed * (3) RCV.WND Space advertised to sender * (4) Fr Fraction whose recommended value is 1/2 * (5) Eff.snd.MSS Effective send MSS for the connection * * (2) A TCP connection's receive window update threshold should NOT be updated until * after the following TCP connection control(s) have been configured : * * (a) TCP connection's configured receive window size ('RxWinSizeCfgd') * [see 'NetTCP_ConnClr() Note #4'] * * (b) TCP connection's connection maximum segment size ('MaxSegSizeConn') * [see 'NetTCP_ConnCfgMaxSegSize() Note #1'] ********************************************************************************************************* */ static void NetTCP_RxConnWinSizeCfgUpdateTh (NET_TCP_CONN *pconn) { NET_TCP_WIN_SIZE rx_win_size_th; /* Cfg silly win min th (see Note #1a). */ rx_win_size_th = (NET_TCP_WIN_SIZE)(((CPU_INT32U)pconn->RxWinSizeCfgd * NET_TCP_RX_SILLY_WIN_NUMER) / NET_TCP_RX_SILLY_WIN_DENOM); pconn->RxWinSizeUpdateTh = DEF_MIN(pconn->MaxSegSizeConn, rx_win_size_th); } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_RxConnWinSizeHandler() * * Description : (1) Handle TCP connection's receive window : * * (a) Handle TCP connection receive window See Notes #2 & #3 * (b) Transmit TCP connection acknowledgement See Note #4 * * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_RxAppData(), * NetTCP_RxPktConnHandlerRxQ_Sync(), * NetTCP_RxPktConnHandlerRxQ_Conn(), * NetTCP_RxPktConnHandlerRxQ_AppData(). * * pbuf_hdr Pointer to network buffer header that received TCP packet. * * win_update_size Size to update TCP connection's receive window (in octets). * * win_update_code Indicate how to update TCP connection receive window : * * NET_TCP_CONN_RX_WIN_RESET Reset TCP connection's available * receive window size. * NET_TCP_CONN_RX_WIN_SET Set TCP connection's available * receive window size. * NET_TCP_CONN_RX_WIN_INC Increment TCP connection's available * receive window size. * NET_TCP_CONN_RX_WIN_DEC Decrement TCP connection's available * receive window size. * * Return(s) : none. * * Caller(s) : NetTCP_RxAppData(), * NetTCP_RxPktConnHandlerRxQ_Sync(), * NetTCP_RxPktConnHandlerRxQ_Conn(), * NetTCP_RxPktConnHandlerRxQ_AppData(). * * Note(s) : (2) RFC #793, Section 3.7 'Data Communication : Managing the Window' states that ... : * * (a) "The window sent in each segment indicates the range of sequence numbers the * sender of the window (the data receiver) is currently prepared to accept. * There is an assumption that this is related to the currently available data * buffer space available for this connection." * * (1) A TCP connection's advertised receive window MUST NEVER be decreased to zero * if NO receive data is available & ready to be read by the application layer. * * In other words, if NO received data starting from the next expected receive * sequence number(s) is queued, then NO data is available to be read by the * application layer. Therefore, the receive window size MUST NOT be decreased * to zero, otherwise the receive window would deadlock since the application * layer would NOT be able to read & extract any data from the receive window * & the TCP connection would NOT be able to receive any more data into the * receive window. * * See also 'NetTCP_RxPktConnHandlerRxQ_AppData() Note #6b'. * * (b) "The mechanisms provided allow a TCP to advertise a large window and to * subsequently advertise a much smaller window without having accepted that * much data. This ... 'shrinking the window' is strongly discouraged. The * robustness principle dictates that TCPs will not shrink the window ... but * will be prepared for such behavior on the part of other TCPs." * * (c) "Note that ... acknowledgments should not be delayed or unnecessary * retransmissions will result. One strategy would be to send an acknowledgment * when a small segment arrives (with out [sic] updating the window information), * and then to send another acknowledgment with new window information when the * window is larger." * * (1) However, the following sections retract this delayed acknowledgement * prohibition & state that "a TCP SHOULD implement a delayed ACK" : * * (A) RFC # 813, Section 5 * (B) RFC #1122, Section 4.2.3.2 * (C) RFC #2581, Section 4.2 * * See also Note #4 & 'NetTCP_TxConnAck() Note #6'. *$PAGE* * (3) (a) RFC #813, Section 2 states that "a bad implementation of the window algorithm * can lead to extremely poor performance ... This particular phenomenon ... has * been given the name of Silly Window Syndrome, or SWS". * * Section 3 elaborates that "SWS is a degeneration in the throughput which develops * ... whenever the acknowledgement of a small segment ... cause[s] another segment * of the same small size to be sent, until ... the network ... becomes clogged with * many small segments, and an equal number of acknowledgements". * * (b) (1) RFC #813, Section 4 states that "the receiver of data can take a very simple * step to eliminate SWS. When it disposes of a small amount of data, it can * artificially reduce the offered window in subsequent acknowledgements, so * that the useable window computed by the sender does not permit the sending * of any further data. * * At some later time, when the receiver has processed a substantially larger * amount of incoming data, the artificial limitation on the offered window * can be removed all at once, so that the sender computes a sudden large jump * rather than a sequence of small jumps in the useable window. * * For a simple implementation, ... artificially reduce the offered window until * the reduction constitutes one half of the available space ... [or] at least * permit one reasonably large segment". * * (2) RFC #1122, Section 4.2.3.3 reiterates that "a TCP MUST include a SWS avoidance * algorithm in the receiver ... This algorithm combines with the delayed ACK * algorithm ... to determine when an ACK segment containing the current window * will really be sent to the receiver [sic]". * * "The solution to receiver SWS is to avoid advancing the right window edge * RCV.NXT+RCV.WND in small increments, even if data is received from the * network in small segments ... The suggested SWS avoidance algorithm for * the receiver is to keep RCV.NXT+RCV.WND fixed until the reduction satisfies : * * (A) RCV.BUFF - RCV.USER - RCV.WND >= min(Fr * RCV.BUFF, Eff.snd.MSS) * * where * (1) RCV.BUFF Total receive buffer space * (2) RCV.USER Data received but not yet consumed * (3) RCV.WND Space advertised to sender * (4) Fr Fraction whose recommended value is 1/2 * (5) Eff.snd.MSS Effective send MSS for the connection * * * Note that the general effect of this algorithm is to advance RCV.WND in * increments of the Eff.snd.MSS." * * (4) (a) RFC #813, Section 5 states that "the receiver of data will refrain from * sending an acknowledgement under certain circumstances ... Postpone * sending an acknowledgement if ... the following conditions hold" : * * (1) "There is no revised window information to be sent back." * * (A) However, if any local receive window size is available to update to * the remote host, an acknowledgement should be immediately transmitted. * * (1) HOWEVER, since local receive window size updates will continually * occur for "a host that is receiving a stream of TCP data segments" * (RFC #1122, Section 4.2.3.2); it is NOT possible to both update the * local receive window & implement a delayed acknowledgement. * * (a) #### Therefore, in order to implement a delayed acknowledgement, * the only local receive window size updates that SHOULD transmit * an immediate acknowledgement : * * (1) Local receive window size sets/resets * (2) Local receive window size increases * (3) Local receive window size decreases to zero-sized * * (b) #### The following local receive window size updates SHOULD NOT * transmit an immediate acknowledgement : * * (1) Local receive window size decreases * * (b) RFC #1122, Section 4.2.3.2 states that "a delayed ACK gives the application an * opportunity to update the window and perhaps to send an immediate response". * * See also 'NetTCP_TxConnAck() Notes #4b1, #6, & #7'. *$PAGE* * (5) Since the configured receive window size may be asynchronously modified by the * application layer, changes in the calculated &/or advertised receive window size * MUST be appropriately validated to any possible new value for the configured * receive window size : * * (a) During receive window size increases, if the currently calculated receive * window size is already greater than the currently configured receive window * size; then the calculated window size is NOT increased, but is allowed to * remain greater than the configured window size until the calculated window * size decreases below the configured window size. * * (b) During receive window size decreases, if the currently calculated receive * window size is already greater than the currently configured receive window * size; then the calculated window size is further decreased, but is allowed to * remain greater than the configured window size until the calculated window * size decreases below the configured window size. * * (c) During receive window size decreases, if the currently configured receive * window size is less than the actual configured receive window size; then * the actual configured receive window size is also decremented by the window * size decrease. ********************************************************************************************************* */ static void NetTCP_RxConnWinSizeHandler (NET_TCP_CONN *pconn, NET_BUF_HDR *pbuf_hdr, NET_TCP_WIN_SIZE win_update_size, NET_TCP_WIN_CODE win_update_code) { CPU_BOOLEAN tx_ack; NET_TCP_ACK_CODE tx_ack_code; NET_TCP_WIN_SIZE win_size_avail; NET_TCP_WIN_SIZE win_size_delta; NET_ERR err; /* ---------- HANDLE TCP CONN RX WIN ---------- */ tx_ack = DEF_NO; switch (win_update_code) { case NET_TCP_CONN_RX_WIN_RESET: default: /* If < cfg'd win size (see Note #5a), ... */ if (pconn->RxWinSizeCalcd < pconn->RxWinSizeCfgd) { /* ... reset calc'd win size to cfg'd win size. */ pconn->RxWinSizeCalcd = pconn->RxWinSizeCfgd; pconn->RxWinSizeCfgdActual = pconn->RxWinSizeCfgd; pconn->RxWinSizeCfgdActualUpdateRem = 0; } if (pconn->RxWinSizeActual < pconn->RxWinSizeCalcd) { /* Do NOT shrink rx win (see Note #2b). */ pconn->RxWinSizeActual = pconn->RxWinSizeCalcd; tx_ack = DEF_YES; tx_ack_code = NET_TCP_CONN_TX_ACK_IMMED; /* See Note #4a1A1a1. */ } break; case NET_TCP_CONN_RX_WIN_SET: if (pconn->RxWinSizeActual < win_update_size) { /* If actual rx win size < win update, ... */ pconn->RxWinSizeActual = win_update_size; /* ... set rx win size. */ pconn->RxWinSizeCalcd = win_update_size; tx_ack = DEF_YES; tx_ack_code = NET_TCP_CONN_TX_ACK_IMMED; /* See Note #4a1A1a1. */ break; } /* Else inc; do NOT shrink win (see Note #2b). */ /* 'break' intentionally omitted; do NOT .. */ /* .. move from the following case : .. */ /* .. 'NET_TCP_CONN_RX_WIN_INC'. */ /*$PAGE*/ case NET_TCP_CONN_RX_WIN_INC: if (win_update_size == 0) { /* If NO win update, MUST NOT update win. */ break; } if (pconn->RxWinSizeCalcd <= pconn->RxWinSizeCfgdActual) { /* If <= cfg'd win size (see Note #5a), ... */ /* ... inc rx win size. */ win_size_avail = pconn->RxWinSizeCfgdActual - pconn->RxWinSizeCalcd; if (win_size_avail > win_update_size) { /* If avail rx win size > win update, ... */ pconn->RxWinSizeCalcd += win_update_size; /* ... inc rx win size by win update. */ } else { /* Else limit to cfg'd rx win size. */ pconn->RxWinSizeCalcd = pconn->RxWinSizeCfgdActual; } if (pconn->RxWinSizeCalcd > pconn->RxWinSizeActual) { win_size_delta = pconn->RxWinSizeCalcd - pconn->RxWinSizeActual; if (win_size_delta >= pconn->RxWinSizeUpdateTh) { /* If calc'd - actual rx win size >= th, ... */ pconn->RxWinSizeActual = pconn->RxWinSizeCalcd;/* ... update rx win size (see Note #3b2). */ tx_ack = DEF_YES; tx_ack_code = NET_TCP_CONN_TX_ACK_IMMED; /* See Note #4a1A1a2. */ } } else { /* Prevent rx win shrink (see Note #2b). */ pconn->RxWinSizeCalcd = pconn->RxWinSizeActual; } } break; case NET_TCP_CONN_RX_WIN_DEC: if (win_update_size == 0) { /* If NO win update, MUST NOT update win. */ break; } if (pconn->RxWinSizeCfgdActualUpdateRem > 0) { /* If cfg'd win update > 0 (see Note #5c), .. */ /* .. cfg actual cfg'd win update to : .. */ if (pconn->RxWinSizeCfgdActualUpdateRem > win_update_size) { win_size_avail = win_update_size; /* .. win update size .. */ } else { /* .. or limit to cfg'd win update rem'ing; .. */ win_size_avail = pconn->RxWinSizeCfgdActualUpdateRem; } pconn->RxWinSizeCfgdActualUpdateRem -= win_size_avail; /* .. dec cfg'd win update rem'ing; & .. */ if (pconn->RxWinSizeCfgdActual > win_size_avail) { pconn->RxWinSizeCfgdActual -= win_size_avail; /* .. dec actual cfg'd win size. */ } else { pconn->RxWinSizeCfgdActual = 0; } } if (pconn->RxWinSizeCalcd > win_update_size) { /* If calc'd rx win size > win update, ... */ pconn->RxWinSizeCalcd -= win_update_size; /* ... dec rx win size by win update. */ } else { pconn->RxWinSizeCalcd = 0; /* Else limit to min rx win size. */ } if (pconn->RxWinSizeActual > 0) { if (pconn->RxWinSizeActual > win_update_size) { /* If actual rx win size > win update, ... */ pconn->RxWinSizeActual -= win_update_size; /* ... dec rx win size by win update. */ tx_ack_code = NET_TCP_CONN_TX_ACK; /* See Note #4a1A1b1. */ } else { pconn->RxWinSizeActual = 0; /* Else limit to min rx win size. */ tx_ack_code = NET_TCP_CONN_TX_ACK_IMMED; /* See Note #4a1A1a3. */ } tx_ack = DEF_YES; } break; } /* --------------- TX TCP CONN ACK ------------ */ if (tx_ack == DEF_YES) { /* If rx win updated, tx ack (see Note #4a1A). */ if (pbuf_hdr != (NET_BUF_HDR *)0) { /* If rx'd pkt avail, req tx ack; but ack ... */ pbuf_hdr->TCP_SegAckTxReqCode = tx_ack_code; /* ... NOT tx'd until after rx'd pkt handling. */ } else { NetTCP_TxConnAck(pconn, pbuf_hdr, tx_ack_code, NET_TCP_CONN_CLOSE_ALL, &err); } } } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxConnWinSizeCfg() * * Description : (1) Configure TCP connection's transmit window controls : * * (a) Configure TCP connection's transmit window congestion controls * (b) Configure TCP connection's transmit window minimum threshold * * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_ConnCfg(). * * Return(s) : none. * * Caller(s) : NetTCP_ConnCfg(). * * Note(s) : (2) A TCP connection's transmit window controls should NOT be updated until after the * following TCP connection control(s) have been configured : * * (a) TCP connection's maximum transmit remote window size ('TxWinSizeRemoteMax') * [see 'NetTCP_TxConnWinSizeCfgCongCtrl() Note #5a' * & 'NetTCP_TxConnWinSizeCfgMinTh() Note #2a'] * * (b) TCP connection's remaining transmit remote window size ('TxWinSizeRemoteRem') * [see 'NetTCP_TxConnWinSizeCfgCongCtrl() Note #5b'] * * (c) TCP connection's connection maximum segment size ('MaxSegSizeConn') * [see 'NetTCP_TxConnWinSizeCfgCongCtrl() Note #5c'] ********************************************************************************************************* */ static void NetTCP_TxConnWinSizeCfg (NET_TCP_CONN *pconn) { NetTCP_TxConnWinSizeCfgCongCtrl(pconn); NetTCP_TxConnWinSizeCfgMinTh(pconn); } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxConnWinSizeCfgCongCtrl() * * Description : (1) Configure TCP connection's transmit window congestion controls : * * (a) Configure TCP connection's transmit window slow start threshold See Note #2 * (b) Configure TCP connection's transmit window congestion controls See Note #3 * (c) Reset TCP connection's transmit window duplicate acknowledgement * controls * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_TxConnWinSizeCfg(), * NetTCP_TxConnWinSizeHandlerCongCtrl(). * * Return(s) : none. * * Caller(s) : NetTCP_TxConnWinSizeCfg(), * NetTCP_TxConnWinSizeHandlerCongCtrl(). * * Note(s) : (2) RFC #2581, Section 3.1 states that "the initial value of ssthresh [slow start * threshold] MAY be arbitrarily high (for example, some implementations use the * size of the advertised window)". * * (a) This amends RFC #2001, Section 2.1 which previously stated that "initialization * for a given connection sets ... ssthresh to 65535 bytes". * * (b) To always ensure a sufficiently high initial value, the slow start threshold * initializes to the TCP connection's maximum remote window size. * * (3) "The initial value of cwnd [congestion window], MUST be less than or equal to 2*SMSS * bytes and MUST NOT be more than 2 segments." * * (a) This amends RFC #2001, Section 2.1 which previously stated that "initialization * for a given connection sets cwnd to one segment". * * (4) (a) RFC #2001, Section 2.2 states that "the TCP output routine never sends more than * the minimum of cwnd and the receiver's advertised window". * * (b) RFC #2581, Section 3.1 reiterates that "the minimum of cwnd and rwnd [receiver's * advertised window] governs data transmission". * * See also 'NetTCP_TxConnWinSizeUpdateAvail() Note #1'. * * (5) A TCP connection's transmit window congestion controls should NOT be updated until * after the following TCP connection control(s) have been configured : * * (a) TCP connection's maximum transmit remote window size ('TxWinSizeRemoteMax') * [see 'NetTCP_RxPktConnHandlerTxWinRemote() Note #1a2A' * & 'NetTCP_TxConnWinSizeHandlerCongCtrl() Notes #3a2A & #3b'] * * (b) TCP connection's remaining transmit remote window size ('TxWinSizeRemoteRem') * [see 'NetTCP_RxPktConnHandlerTxWinRemote() Note #1a2A' * & 'NetTCP_TxConnWinSizeHandlerCongCtrl() Notes #3a2A'] * * (c) TCP connection's connection maximum segment size ('MaxSegSizeConn') * [see 'NetTCP_ConnCfgMaxSegSize() Note #1'] ********************************************************************************************************* */ static void NetTCP_TxConnWinSizeCfgCongCtrl (NET_TCP_CONN *pconn) { /* Cfg tx slow start th (see Note #2b). */ pconn->TxWinSizeSlowStartThInit = (NET_TCP_WIN_SIZE)pconn->TxWinSizeRemoteMax; pconn->TxWinSizeSlowStartTh = (NET_TCP_WIN_SIZE)pconn->TxWinSizeSlowStartThInit; /* Cfg tx cong ctrls (see Note #3a). */ pconn->TxWinSizeCongInit = (NET_TCP_WIN_SIZE)pconn->MaxSegSizeConn * NET_TCP_CONG_WIN_MSS_SCALAR_INIT; pconn->TxWinSizeCongCalcdActual = (NET_TCP_WIN_SIZE)pconn->TxWinSizeCongInit; pconn->TxWinSizeCongCalcdCur = (NET_TCP_WIN_SIZE)0; pconn->TxWinSizeCongRem = (NET_TCP_WIN_SIZE)pconn->TxWinSizeCongCalcdActual; /* Cfg tx win avail (see Note #4). */ NetTCP_TxConnWinSizeUpdateAvail(pconn); NetTCP_TxConnWinSizeDupAckCtrlReset(pconn); /* Reset dup ack ctrls. */ } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxConnWinSizeCfgMinTh() * * Description : Configure TCP connection's transmit silly window minimum threshold. * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_TxConnWinSizeCfg(), * NetTCP_TxConnWinSizeHandlerCongCtrl(). * * Return(s) : none. * * Caller(s) : NetTCP_TxConnWinSizeCfg(), * NetTCP_TxConnWinSizeHandlerCongCtrl(). * * Note(s) : (1) RFC #1122, Section 4.2.3.4 states that "the sender's SWS avoidance algorithm is ... * [to] send data ... if at least a fraction Fs of the maximum window can be sent ... * Fs is a fraction whose recommended value is 1/2". * * (2) A TCP connection's transmit window minimum threshold should NOT be updated until * after the following TCP connection control(s) have been configured : * * (a) TCP connection's maximum transmit remote window size ('TxWinSizeRemoteMax') * [see 'NetTCP_RxPktConnHandlerTxWinRemote() Note #1a2A' * & 'NetTCP_TxConnWinSizeHandlerCongCtrl() Notes #3a2A & #3b'] ********************************************************************************************************* */ static void NetTCP_TxConnWinSizeCfgMinTh (NET_TCP_CONN *pconn) { /* Cfg silly win min th (see Note #1). */ pconn->TxWinSizeMinTh = (NET_TCP_WIN_SIZE)(((CPU_INT32U)pconn->TxWinSizeRemoteMax * NET_TCP_TX_SILLY_WIN_NUMER) / NET_TCP_TX_SILLY_WIN_DENOM); } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxConnWinSizeHandlerCfgd() * * Description : (1) Handle TCP connection's configured transmit window : * * (a) Update TCP connection configured transmit window * (b) Update TCP connection total transmit data queued * * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_TxConnAppData(), * NetTCP_RxPktConnHandlerReTxQ(). * * win_update_size Size to update TCP connection's transmit window (in octets). * * win_update_code Indicate how to update TCP connection transmit window : * * NET_TCP_CONN_TX_WIN_RESET Reset TCP connection's available * transmit window size. * NET_TCP_CONN_TX_WIN_SET Set TCP connection's available * transmit window size. * NET_TCP_CONN_TX_WIN_INC Increment TCP connection's available * transmit window size. * NET_TCP_CONN_TX_WIN_DEC Decrement TCP connection's available * transmit window size. * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_NONE TCP connection's configured transmit window * successfully updated. * * --- RETURNED BY NetOS_TCP_TxQ_Signal() : --- * NET_TCP_ERR_TX_Q_SIGNAL_FAULT TCP connection transmit queue signal fault. * * Return(s) : none. * * Caller(s) : NetTCP_TxConnAppData(), * NetTCP_RxPktConnHandlerReTxQ(). * * Note(s) : (2) (a) RFC #793, Section 3.7 'Data Communication : Managing the Window' states * that "the window sent in each segment indicates the range of sequence * numbers the sender of the window (the data receiver) is currently * prepared to accept. There is an assumption that this is related to the * currently available data buffer space available for this connection". * * (b) Thus, a TCP connection's configured transmit window is constrained by * the available network resources; of which, network buffers are the * primary transmit window network resource. * * However, since network buffers are discrete resources with non-discrete * packet size; it is NOT necessary to enforce exact/strict compliance with * a TCP connection's configured transmit window. * * In other words, network buffers allocated for TCP connection transmit * SHOULD be fully used even if the TCP connection's configured transmit * window is zero. * * (3) Since the configured transmit window size may be asynchronously modified by * the application layer, changes in the remaining transmit window size MUST be * appropriately validated to any possible new value for the configured transmit * window size : * * (a) During transmit window size increases, if the remaining transmit window * size is already greater than the currently configured transmit window * size; then the remaining window size is NOT increased, but is allowed to * remain greater than the configured window size until the remaining window * size decreases below the configured window size. * * (b) During transmit window size decreases, if the remaining transmit window * size is already greater than the currently configured transmit window * size; then the remaining window size is further decreased, but is allowed * to remain greater than the configured window size until the remaining * window size decreases below the configured window size. * * (c) Window size update is NOT validated for transmit window size resets. ********************************************************************************************************* */ /*$PAGE*/ static void NetTCP_TxConnWinSizeHandlerCfgd (NET_TCP_CONN *pconn, NET_TCP_WIN_SIZE win_update_size, NET_TCP_WIN_CODE win_update_code, NET_ERR *perr) { CPU_BOOLEAN q_prevly_full; NET_TCP_WIN_SIZE win_size_avail; NET_TCP_CONN_ID conn_id_tcp; switch (win_update_code) { case NET_TCP_CONN_TX_WIN_RESET: /* Reset cfg'd tx win size (see Note #1c). */ default: pconn->TxWinSizeCfgdRem = pconn->TxWinSizeCfgd; break; case NET_TCP_CONN_TX_WIN_SET: if (pconn->TxWinSizeCfgdRem < win_update_size) { /* If rem cfg'd win size < win update, ... */ pconn->TxWinSizeCfgdRem = win_update_size; /* .. set cfg'd win size. */ break; } /* Else inc; do NOT shrink win. */ /* 'break' intentionally omitted; do NOT .. */ /* .. move from the following case : .. */ /* .. 'NET_TCP_CONN_TX_WIN_INC'. */ case NET_TCP_CONN_TX_WIN_INC: if (win_update_size == 0) { /* If NO win update, do NOT update win. */ break; } q_prevly_full = (pconn->TxWinSizeCfgdRem < 1) ? DEF_YES : DEF_NO; if (pconn->TxWinSizeCfgdRem < pconn->TxWinSizeCfgd) { /* If < cfg'd win size (see Note #3a), ... */ /* ... inc tx win size. */ win_size_avail = pconn->TxWinSizeCfgd - pconn->TxWinSizeCfgdRem; if (win_size_avail > win_update_size) { /* If avail tx win size > win update, ... */ pconn->TxWinSizeCfgdRem += win_update_size; /* ... inc tx win size by win update. */ } else { pconn->TxWinSizeCfgdRem = pconn->TxWinSizeCfgd; /* Else limit to cfg'd tx win size. */ } } if (q_prevly_full == DEF_YES) { /* If tx Q prev'ly full, ... */ conn_id_tcp = pconn->ID; NetOS_TCP_TxQ_Signal(conn_id_tcp, perr); /* ... signal tx Q. */ if (*perr != NET_TCP_ERR_NONE) { /* #### On err, handle tx Q?; close TCP conn? */ NetTCP_ConnClose((NET_TCP_CONN *)pconn, (NET_BUF_HDR *)0, (CPU_BOOLEAN )DEF_YES, (NET_TCP_CLOSE_CODE)NET_TCP_CONN_CLOSE_ALL); return; } } break; case NET_TCP_CONN_TX_WIN_DEC: if (win_update_size == 0) { /* If NO win update, do NOT update win. */ break; } if (pconn->TxWinSizeCfgdRem > win_update_size) { /* If rem tx win size > win update, .. */ pconn->TxWinSizeCfgdRem -= win_update_size; /* .. dec tx win size by win update. */ } else { pconn->TxWinSizeCfgdRem = 0; /* Else limit to min tx win size. */ } break; } *perr = NET_TCP_ERR_NONE; } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxConnWinSizeHandlerCongCtrl() * * Description : (1) Handle TCP connection's transmit window congestion controls : * * (a) Perform slow start / congestion avoidance algorithms See Note #2c2A * (b) Perform fast re-transmit / fast recovery algorithms See Note #2c2B * (c) Handle remote host's receive window updates See Note #3 * * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_RxPktConnHandlerSeg(), * NetTCP_RxPktConnHandlerReTxQ(), * NetTCP_TxConnTxQ(), * NetTCP_TxConnTxQ_TimeoutIdleSet(), * NetTCP_TxConnTxQ_TimeoutIdle(), * NetTCP_TxConnReTxQ(). * * pbuf_hdr Pointer to network buffer header that received TCP packet. * -------- Argument validated in NetTCP_Rx(). * * ack_code Indicates the received segment's acknowledgement condition : * * NET_TCP_CONN_RX_ACK_NONE NO received acknowledgement number. * * NET_TCP_CONN_RX_ACK_INVALID Received acknowledgement number * is invalid for the TCP connection. * * NET_TCP_CONN_RX_ACK_VALID Received acknowledgement number * is valid for the TCP connection. * * NET_TCP_CONN_RX_ACK_DUP Received acknowledgement number * is a duplicate for the * TCP connection. * * NET_TCP_CONN_RX_ACK_PREV Received acknowledgement number * is a previous duplicate for the * TCP connection. * * win_update_size Size to update TCP connection's transmit window (in octets). * * win_update_code Indicate how to update TCP connection transmit window : * * NET_TCP_CONN_TX_WIN_RESET Reset TCP connection's congestion * control transmit window size. * NET_TCP_CONN_TX_WIN_SEG_RXD Update TCP connection's congestion * control transmit window size * based on received segment. * NET_TCP_CONN_TX_WIN_TIMEOUT Update TCP connection's congestion * control transmit window size * based on transmission timeout. * NET_TCP_CONN_TX_WIN_INC Increment TCP connection's congestion * control transmit window sizes. * NET_TCP_CONN_TX_WIN_DEC Decrement TCP connection's * transmit window sizes. * NET_TCP_CONN_TX_WIN_REMOTE_UPDATE Update TCP connection's remote * receive window size. * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_NONE TCP connection's transmit window congestion controls * successfully updated. * NET_TCP_ERR_CONN_FAIL TCP connection operation(s) failed. * * --------- RETURNED BY NetTCP_TxConnReTxQ() : --------- * NET_ERR_INIT_INCOMPLETE Network initialization NOT complete. * NET_TCP_ERR_CONN_NOT_USED TCP connection NOT currently used. * NET_TCP_ERR_INVALID_CONN_STATE Invalid TCP connection state. * NET_TCP_ERR_INVALID_CONN_OP Invalid TCP connection operation. * NET_TCP_ERR_RE_TX_SEG_TH TCP connection closed due to excessive retransmission. * NET_TCP_ERR_TX TCP transmit error. * * Return(s) : none. * * Caller(s) : NetTCP_RxPktConnHandlerSeg(), * NetTCP_RxPktConnHandlerReTxQ(), * NetTCP_RxPktConnHandlerTxWinRemote(), * NetTCP_TxConnTxQ(), * NetTCP_TxConnTxQ_TimeoutIdleSet(), * NetTCP_TxConnTxQ_TimeoutIdle(), * NetTCP_TxConnReTxQ(). *$PAGE* * Note(s) : (2) (a) (1) The following sections define, specify, &/or require "TCP's four intertwined congestion * control algorithms: slow start, congestion avoidance, fast retransmit, and fast recovery" : * * (A) RFC #2001 See Note #2c1 * (B) RFC #2581 See Notes #2c2, #2a2, & #2e * (C) RFC #1122 * (1) Section 4.2.2.15 * (2) Section 4.2.2.21 * (3) Section 4.2.3.9 See 'net_tcp.c Note #1e' * (a) 'Source Quench' See Note #2e2 * (b) 'Destination Unreachable -- Codes 2-4' * * * (2) Although RFC #2581, Section 1 states that "this document is an update of [RFC #2001]"; * both references are included as complementary specifications of the congestion control * algorithms. * * However, since RFC #2581 is the update to RFC #2001, its specifications supercede * those of RFC #2001; & ALL differences are implemented by RFC #2581 specifications. * * (b) (1) (A) RFC #2581, Section 3.1 states that "the slow start and congestion avoidance * algorithms MUST be used by a TCP sender to control the amount of outstanding * data being injected into the network". * * (B) RFC #1122, Section 4.2.2.15 reiterates that "a TCP MUST implement ... 'slow * start' with 'congestion avoidance'". * * (2) (A) RFC #2001, Section 1 states that "the sender's ... congestion window ... is * flow control imposed by the sender, while the advertised window is flow control * imposed by the receiver. The former is based on the sender's assessment of * perceived network congestion; the latter is related to the amount of available * buffer space at the receiver for this connection". * * (B) RFC #2581, Section 3.1 reiterates that "the congestion window ... is a sender- * side limit on the amount of data the sender can transmit into the network * before receiving an acknowledgment (ACK), while the receiver's advertised * window ... is a receiver-side limit on the amount of outstanding data." * * (3) (A) (1) RFC #2001, Section 1 states that "the sender can transmit up to the minimum * of the congestion window and the advertised window." * * (2) RFC #2001, Section 2.2 reiterates that "the TCP output routine never sends * more than the minimum of cwnd and the receiver's advertised window". * * (3) RFC #2581, Section 3.1 also reiterates that "the minimum of cwnd and rwnd * [receiver's advertised window] governs data transmission". * * (B) RFC #2581, Section 3 also states that "a TCP MUST NOT be more aggressive than * the [congestion control] algorithms allow (that is, MUST NOT send data when * the value of cwnd computed by the [congestion control] algorithms would not * allow the data to be sent)". * * (C) TCP transmit resulting from TCP congestion controls update handled by appropriate * TCP connection state receive handler function(s). * * See 'NetTCP_RxPktConnHandlerSyncRxd() Note #1c3', * 'NetTCP_RxPktConnHandlerSyncTxd() Note #1c3', * 'NetTCP_RxPktConnHandlerConn() Note #1c', * 'NetTCP_RxPktConnHandlerFinWait1() Note #1c', * 'NetTCP_RxPktConnHandlerClosing() Note #1c', * 'NetTCP_RxPktConnHandlerCloseWait() Note #1c', * & 'NetTCP_RxPktConnHandlerLastAck() Note #1c'. * * (4) (A) RFC #2001, Sections 3 & 4 state that "since ... a duplicate ACK is caused by * a lost segment or just a reordering of segments, ... [fast retransmit] waits * for a small number of duplicate ACKs to be received ... [as] a strong indication * that a segment has been lost ... [and] then performs a retransmission of what * appears to be the missing segment, without waiting for a retransmission timer * to expire. * * After fast retransmit sends ... the missing segment, congestion avoidance, * but not slow start is performed. This is the fast recovery algorithm". * * (B) RFC #2581, Section 3.2 reiterates that "the TCP sender SHOULD use the 'fast * retransmit' algorithm to detect and repair loss, based on ... the arrival of * 3 duplicate ACKs (4 identical ACKs without the arrival of any other intervening * packets) as an indication that a segment has been lost. After receiving 3 * duplicate ACKs, TCP performs a retransmission of what appears to be the missing * segment, without waiting for the retransmission timer to expire. * * After the fast retransmit algorithm sends ... the missing segment, the 'fast * recovery' algorithm ... can continue to transmit new segments ... (using a * reduced cwnd) ... until a non-duplicate ACK arrives". *$PAGE* * (c) (1) (A) RFC #2001, Section 2 states that the "congestion avoidance and slow start ... * combined algorithm operates as follows" : * * (1) "Initialization for a given connection sets" : * * (a) "cwnd to one segment" ... * (b) "and ssthresh to 65535 bytes." * * (3) (a) (1) "When congestion occurs (indicated by" : * * (A) "a timeout" ... * (B) "or the reception of duplicate ACKs)," ... * * (2) "one-half of the current window size" : * * (A) "the minimum of" : * (1) "cwnd" ... * (2) "and the receiver's advertised window" ... * (B) "but at least two segments" ... * * (3) "is saved in ssthresh [slow start threshold]." * * (b) (1) "Additionally, if the congestion is indicated by a timeout," ... * (2) "cwnd is set to one segment (i.e., slow start)." * * (4) "When new data is acknowledged by the other end, increase cwnd, but the way * it increases depends on whether TCP is performing slow start or congestion * avoidance" : * * (a) (1) (A) (1) (a) "If cwnd is less than or equal to ssthresh," ... * (b) "TCP is in slow start." * * (2) "Slow start continues until TCP is halfway to where it was * when congestion occurred (since it recorded half of the * window size that caused the problem)." * * (B) (1) "Slow start has cwnd begin at one segment," ... * * (2) (a) "and be incremented by one segment" ... * (b) "every time an ACK is received." * * (3) "The increase in cwnd ... [for] slow start increments cwnd * by the number of ACKs received in a round-trip time ... * [which] opens the window exponentially: send one segment, * then two, then four, and so on." * * (2) RFC #2001, Section 1 states that : * * (A) "Early implementations performed slow start only if the other * end was on a different network." * * (B) "Current implementations always perform slow start." * * (b) (1) (A) "If cwnd is [not] less than or equal to ssthresh," ... * (B) "TCP is performing congestion avoidance." * * (2) (A) (1) "cwnd [is] incremented by segsize*segsize/cwnd" ... * (a) "where segsize is the segment size" ... * (b) "and cwnd is maintained in bytes"; * (2) "each time an ACK is received." * * (B) "This is a linear growth of cwnd ... The increase in cwnd should * be at most one segment each round-trip time (regardless how many * ACKs are received in that RTT)." * * (B) RFC #2001, Section 4 states that "the fast retransmit and fast recovery * algorithms are usually implemented together as follows" : * * (1) "When the third duplicate ACK in a row is received" : * * (a) "Set ssthresh to" : * (1) "one-half the current congestion window, cwnd" ... * (2) "but no less than two segments." * * (b) "Retransmit the missing segment." * * (c) "Set cwnd to ssthresh plus 3 times the segment size." * * (2) "Each time another duplicate ACK arrives" : * * (a) "Increment cwnd by the segment size." * (b) "Transmit a packet, if allowed by the new value of cwnd." * * (3) "When the next ACK arrives that acknowledges new data" : * * (a) "Set cwnd to ssthresh" (see Note #2c1B1a). *$PAGE* * (2) (A) RFC #2581, Section 3.1 defines the "slow start and congestion avoidance algorithms" * as follows : * * (1) (a) "The initial value of cwnd, MUST be" : * * (1) "less than or equal to 2*SMSS bytes" ... * (2) "and MUST NOT be more than 2 segments." * * (b) "The initial value of ssthresh MAY be arbitrarily high" : * (1) "some implementations use the size of the advertised window." * * (2) (a) "The slow start algorithm is used when cwnd < ssthresh," ... * (b) "while the congestion avoidance algorithm is used when cwnd > ssthresh." * (c) "When cwnd and ssthresh are equal the sender may use either slow start or * congestion avoidance." * * (3) (a) "During slow start" : * * (1) "a TCP increments cwnd by at most SMSS bytes" ... * (2) "for each ACK received that acknowledges new data." * * (b) "Slow start ends when" : * * (1) "cwnd exceeds ... or ... reaches ... ssthresh" ... * (2) "or when congestion is observed." * * (4) (a) "During congestion avoidance" : * * (1) "cwnd is incremented by 1 full-sized segment per round-trip time (RTT)." * * (2) (A) "One formula commonly used to update cwnd during congestion avoidance * is given" by : * * (2) cwnd += (SMSS * SMSS) / cwnd * * (B) "Another acceptable way to increase cwnd during congestion avoidance * is to count the number of bytes that have been acknowledged by ACKs * for new data" : * * (1) "When the number of bytes acknowledged reaches cwnd," ... * (2) "then cwnd can be incremented by up to SMSS bytes." * * (3) "This adjustment is executed on every incoming non-duplicate ACK." * * (b) "Congestion avoidance continues until congestion is detected." * * (5) "When a TCP sender detects segment loss using the retransmission timer" : * * (a) "the value of ssthresh MUST be set to no more than" : * * (3) ssthresh = max (FlightSize / 2, 2 * SMSS) * * (A) "FlightSize is the amount of outstanding data ... that has been * sent but not yet acknowledged." * * (b) "cwnd MUST be set to no more than ... 1 full-sized segment." * * (c) "Therefore, after retransmitting the dropped segment the TCP sender uses * the slow start algorithm to increase the window." *$PAGE* * (B) RFC #2581, Section 3.2 states that "the fast retransmit and fast recovery algorithms * are usually implemented together as follows" : * * (1) "When the third duplicate ACK is received" (see Note #2d1) : * * (a) "set ssthresh to no more than the value given [by the] equation" * (see Note #2c2A5a3), ... * * (b) "Retransmit the lost segment" * (see Note #2d2), ... * * (c) "set cwnd to ssthresh plus 3*SMSS." * (1) "This artificially 'inflates' the congestion window by the number of * segments (three) that have left the network and which the receiver * has buffered." * * (2) "For each additional duplicate ACK received" : * * (a) "increment cwnd by SMSS." * (1) "This artificially inflates the congestion window in order to reflect * the additional segment that has left the network." * * (b) (1) "Transmit a segment, if allowed by" : * * (A) "the new value of cwnd" ... * (B) "and the receiver's advertised window." * * (2) TCP transmit resulting from TCP congestion controls update handled by * appropriate TCP connection state receive handler function(s). * * See also Note #2b3C. * * (3) (a) "When the next ACK arrives that acknowledges new data," ... * (b) "set cwnd to ssthresh" (see Note #2c2B1a). * * (d) (1) (A) RFC #1122, Section 4.2.2.21 states that "'fast retransmit' ... counts the number of * ... 'redundant' ACK's ... received with" : * * (1) "the same value of SEG.ACK and" ... * (2) "the same right window edge". * * (B) Although it is not directly stated, it is inferred that duplicate acknowledgements * SHOULD be : * * (1) Acknowledgement-only segments * (a) i.e. with NO transmit data See Note #2d1C1 * (1) i.e. with NO TCP segment length * (2) With NO sequence number update * (3) With NO receive window size update See Note #2d1C2 * * (C) Also although RFC #2581, Section 3.2 states that "the TCP sender SHOULD use the * 'fast retransmit' algorithm ... based on ... the arrival of ... 4 identical ACKs * without the arrival of any other intervening packets"; it seems reasonable that * the TCP fast retransmit algorithm MUST NOT consider the arrival of non-duplicate * acknowledgement segments as "intervening packets". * * In other words, TCP fast retransmit MUST ignore the following TCP packets as * duplicate acknowledgement packets : * * (1) TCP data segments * (2) Remote host receive window updates * * (2) RFC #1122, Section 4.2.2.21 reiterates that "with this ... 'fast retransmit' ... * algorithm, the sender uses the redundant ACK's to deduce that a segment has been * lost before the retransmission timer has expired. If more than a threshold number * of such ACK's is received, then the segment containing the octets starting at * SEG.ACK is assumed to have been lost and is retransmitted, without awaiting a * timeout". * * (e) (1) RFC #2581, Section 4.1 states that "after TCP has been idle for a relatively long period * of time ... use slow start to restart transmission" : * * (A) (1) "If the TCP has not sent data in an interval exceeding the retransmission timeout" * (2) "cwnd is reduced to ... no more than ... the value of the restart window" * (see Note #2c2A1). * * See also 'NetTCP_TxConnTxQ_TimeoutIdle() Note #2a'. * * (B) Although NO RFC specifies that a TCP connection's slow start threshold should be * reset following a TCP transmit idle timeout; it seems reasonable to reset the slow * start threshold whenever the TCP transmit congestion window is reset. * * See also 'NetTCP_TxConnWinSizeCfgCongCtrl() Notes #2 & #3'. * * (2) RFC #1122, Section 4.2.3.9 'Source Quench' states that "TCP MUST react to a Source Quench * by slowing transmission on the connection. The RECOMMENDED procedure is ... to trigger a * 'slow start', as if a retransmission timeout had occurred". *$PAGE* * (3) (a) (1) The following sections ... : * * (A) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check ACK Field : * ESTABLISHED STATE' * (B) RFC #1122, Section 4.2.2.20.(c) * (C) RFC #1122, Section 4.2.2.20.(f) * * (2) ... generalize that "the following variables should be set ... [when] the send * window should be updated" : * * (A) SND.WND <- SEG.WND * * (1) A TCP connection's remote host receive window advertisements are relative * to the received window advertisement acknowledgement sequence number, & * are asynchronous to the TCP connection's recent transmitted sequences. * * Therefore, the actual transmit remote window size MUST be compensated by * the difference in the remote host receive window advertisement & the TCP * connection's number of recently transmitted sequences : * * (a) TxWinSizeRemoteActual = SEG.WIN - (SND.NXT - SEG.ACK) * * where * * TxWinSizeRemoteActual TCP connection's actual transmit * remote window size * * (B) SND.WL1 <- SEG.SEQ * (C) SND.WL2 <- SEG.ACK * * (b) RFC #1122, Section 4.2.3.4 'IMPLEMENTATION' states that "because the sender does not * know (directly) the receiver's total buffer space RCV.BUFF ... An approach [that] has * been found to work well is for the sender to calculate Max(SND.WND), the maximum send * window it has seen so far on the connection, and to use this value as an estimate of * RCV.BUFF". * * (c) RFC #1122, Section 4.2.2.17 states that although "a TCP MAY keep its offered receive * window closed indefinitely ... the sending TCP MUST allow the connection to stay open * ... as long as the receiving TCP continues to send acknowledgments in response to ... * probe segments". * * See also 'NetTCP_TxConnWinSizeZeroWinHandler() Note #1'. ********************************************************************************************************* */ /*$PAGE*/ static void NetTCP_TxConnWinSizeHandlerCongCtrl (NET_TCP_CONN *pconn, NET_BUF_HDR *pbuf_hdr, NET_TCP_ACK_CODE ack_code, NET_TCP_WIN_SIZE win_update_size, NET_TCP_WIN_CODE win_update_code, NET_ERR *perr) { CPU_BOOLEAN ack_dup; NET_TCP_WIN_SIZE tx_win_size_remote_actual; NET_TCP_WIN_SIZE tx_win_size_remote_actual_min; switch (win_update_code) { case NET_TCP_CONN_TX_WIN_RESET: default: NetTCP_TxConnWinSizeCfgCongCtrl(pconn); /* Reset tx cong ctrls (see Note #2e). */ break; case NET_TCP_CONN_TX_WIN_SEG_RXD: switch (ack_code) { case NET_TCP_CONN_RX_ACK_VALID: /* -------------- FAST RECOVERY --------------- */ /* If valid ack rx'd after fast re-tx th, .. */ if (pconn->TxWinRxdAckDupCtr >= NET_TCP_FAST_RE_TX_ACK_DUP_TH) { /* .. perform fast recovery (see Note #2c2B3) : */ /* .. set cong win to slow start th .. */ /* .. (see Note #2c2B3b). */ NetTCP_TxConnWinSizeCongSet(pconn, NET_TCP_CONN_TX_WIN_CONG_SET_SLOW_START); NetTCP_TxConnWinSizeUpdateAvail(pconn); /* Update avail tx win (see Note #2b3). */ } else { /* -------- SLOW START / CONG AVOIDANCE ------- */ /* If cong win < slow start th .. */ /* .. (see Notes #2c2A2a), .. */ if (pconn->TxWinSizeCongCalcdActual < pconn->TxWinSizeSlowStartTh) { /* .. perform slow start (see Note #2c2A3) : */ /* .. inc cong win by MSS (see Note #2c2A3a1). */ NetTCP_TxConnWinSizeCongInc(pconn, pbuf_hdr, 0, NET_TCP_CONN_TX_WIN_CONG_INC_SLOW_START); } else { /* If cong win >= slow start th .. */ /* .. (see Note #2c2A2b & #2c2A2c), .. */ /* .. perform cong avoid (see Note #2c2A4) : */ /* .. inc cong win (see Note #2c2A4a2B). */ NetTCP_TxConnWinSizeCongInc(pconn, pbuf_hdr, 0, NET_TCP_CONN_TX_WIN_CONG_INC_CONG_AVOID); } NetTCP_TxConnWinSizeUpdateAvail(pconn); /* Update avail tx win (see Note #2b3A). */ } #if 0 /* Tx avail seg(s) [see Note #2b3C]. */ NetTCP_TxConnTxQ(pconn, pbuf_hdr, ack_code, DEF_NO, NET_TCP_CONN_CLOSE_ALL, perr); switch (*perr) { case NET_TCP_ERR_NONE: case NET_TCP_ERR_CONN_ACK_NONE: case NET_TCP_ERR_CONN_ACK_INVALID: case NET_TCP_ERR_CONN_ACK_DLYD: case NET_TCP_ERR_CONN_ACK_PREVLY_TXD: case NET_ERR_TX: /* Ignore transitory tx err(s). */ break; case NET_ERR_INIT_INCOMPLETE: case NET_TCP_ERR_CONN_NOT_USED: case NET_TCP_ERR_CONN_CLOSE: case NET_TCP_ERR_CONN_FAULT: case NET_TCP_ERR_CONN_FAIL: case NET_TCP_ERR_INVALID_CONN_STATE: case NET_TCP_ERR_INVALID_CONN_OP: case NET_TCP_ERR_INVALID_LEN_SEG: case NET_TCP_ERR_NONE_AVAIL: case NET_TCP_ERR_TX: case NET_CONN_ERR_INVALID_FAMILY: case NET_CONN_ERR_INVALID_ADDR: case NET_CONN_ERR_INVALID_ADDR_LEN: default: return; /* Prevent 'break NOT reachable' warning. */ } #endif /* Reset dup ack ctrls. */ NetTCP_TxConnWinSizeDupAckCtrlUpdate(pconn, pbuf_hdr, DEF_YES); break; /*$PAGE*/ case NET_TCP_CONN_RX_ACK_DUP: /* -------- FAST RE-TX / FAST RECOVERY -------- */ /* Chk dup ack (see Notes #2b4 & #2d1). */ ack_dup = ((pbuf_hdr->TCP_SeqNbr == pconn->TxWinRxdLastSeqNbr ) && (pbuf_hdr->TCP_AckNbr == pconn->TxWinRxdLastAckNbr ) && (pbuf_hdr->TCP_WinSize == pconn->TxWinRxdLastWinSize) && (pbuf_hdr->TCP_SegLen == 0 )) ? DEF_YES : DEF_NO; if (ack_dup == DEF_YES) { /* If dup ack, .. */ pconn->TxWinRxdAckDupCtr++; /* .. inc dup ack ctr. */ /* If equal to fast re-tx th, .. */ if (pconn->TxWinRxdAckDupCtr == NET_TCP_FAST_RE_TX_ACK_DUP_TH) { /* .. perform fast re-tx (see Note #2c2B1) : .. */ /* .. calc slow start th (see Note #2c2B1a); .. */ NetTCP_TxConnWinSizeCalcSlowStartTh(pconn); /* .. re-tx unack'd seg (see Note #2c2B1b); .. */ NetTCP_TxConnReTxQ(pconn, DEF_NO, NET_TCP_CONN_CLOSE_ALL, perr); switch (*perr) { case NET_TCP_ERR_NONE: case NET_ERR_TX: /* Ignore transitory re-tx err(s). */ break; case NET_ERR_INIT_INCOMPLETE: case NET_TCP_ERR_CONN_NOT_USED: case NET_TCP_ERR_CONN_FAIL: case NET_TCP_ERR_INVALID_CONN_STATE: case NET_TCP_ERR_INVALID_CONN_OP: case NET_TCP_ERR_TX: case NET_TCP_ERR_RE_TX_SEG_TH: default: return; /* Prevent 'break NOT reachable' warning. */ } /* .. set cong win to fast recovery th .. */ /* .. (see Note #2c2B1c). */ NetTCP_TxConnWinSizeCongSet(pconn, NET_TCP_CONN_TX_WIN_CONG_SET_FAST_RECOVERY); NetTCP_TxConnWinSizeUpdateAvail(pconn); /* Update avail tx win (see Note #2b3). */ /* Else if > fast re-tx th, .. */ } else if (pconn->TxWinRxdAckDupCtr > NET_TCP_FAST_RE_TX_ACK_DUP_TH) { /* .. perform fast recovery (see Note #2c2B2) : */ /* .. inc cong win by MSS (see Note #2c2B2a); */ NetTCP_TxConnWinSizeCongInc(pconn, pbuf_hdr, 0, NET_TCP_CONN_TX_WIN_CONG_INC_SLOW_START); NetTCP_TxConnWinSizeUpdateAvail(pconn); /* .. update avail tx win (see Note #2b3) .. */ #if 0 /* .. & tx avail seg(s) [see Note #2c2B2b]. */ NetTCP_TxConnTxQ(pconn, pbuf_hdr, ack_code, DEF_NO, NET_TCP_CONN_CLOSE_ALL, perr); switch (*perr) { case NET_TCP_ERR_NONE: case NET_TCP_ERR_CONN_ACK_NONE: case NET_TCP_ERR_CONN_ACK_INVALID: case NET_TCP_ERR_CONN_ACK_DLYD: case NET_TCP_ERR_CONN_ACK_PREVLY_TXD: case NET_ERR_TX: /* Ignore transitory tx err(s). */ break; case NET_ERR_INIT_INCOMPLETE: case NET_TCP_ERR_CONN_NOT_USED: case NET_TCP_ERR_CONN_CLOSE: case NET_TCP_ERR_CONN_FAULT: case NET_TCP_ERR_CONN_FAIL: case NET_TCP_ERR_INVALID_CONN_STATE: case NET_TCP_ERR_INVALID_CONN_OP: case NET_TCP_ERR_INVALID_LEN_SEG: case NET_TCP_ERR_NONE_AVAIL: case NET_TCP_ERR_TX: case NET_CONN_ERR_INVALID_FAMILY: case NET_CONN_ERR_INVALID_ADDR: case NET_CONN_ERR_INVALID_ADDR_LEN: default: return; /* Prevent 'break NOT reachable' warning. */ } #endif } } else { /* Else update dup ack ctrls (see Note #2d1C). */ NetTCP_TxConnWinSizeDupAckCtrlUpdate(pconn, pbuf_hdr, DEF_NO); } break; /*$PAGE*/ case NET_TCP_CONN_RX_ACK_PREV: /* Ignore prev dup acks. */ *perr = NET_TCP_ERR_NONE; return; /* Prevent 'break NOT reachable' warning. */ case NET_TCP_CONN_RX_ACK_NONE: case NET_TCP_CONN_RX_ACK_INVALID: default: *perr = NET_TCP_ERR_CONN_FAIL; return; /* Prevent 'break NOT reachable' warning. */ } break; case NET_TCP_CONN_TX_WIN_TIMEOUT: /* --------------- SLOW START ----------------- */ /* On timeout (see Note #2c2A5), .. */ /* .. perform slow start (see Note #2c2A5c) : */ NetTCP_TxConnWinSizeCalcSlowStartTh(pconn); /* .. calc slow start th (see Note #2c2A5a); .. */ /* .. set cong win to timeout th .. */ /* .. (see Note #2c2A5b). */ NetTCP_TxConnWinSizeCongSet(pconn, NET_TCP_CONN_TX_WIN_CONG_SET_TIMEOUT); NetTCP_TxConnWinSizeUpdateAvail(pconn); /* Update avail tx win (see Note #2b3). */ break; case NET_TCP_CONN_TX_WIN_INC: if (win_update_size == 0) { /* If NO win update, do NOT update win. */ break; } /* Inc rem cong win size. */ NetTCP_TxConnWinSizeCongInc(pconn, pbuf_hdr, win_update_size, NET_TCP_CONN_TX_WIN_CONG_INC_REM); NetTCP_TxConnWinSizeUpdateAvail(pconn); /* Update avail tx win (see Note #2b3). */ break; case NET_TCP_CONN_TX_WIN_DEC: if (win_update_size == 0) { /* If NO win update, do NOT update win. */ break; } if (pconn->TxWinSizeCongRem > win_update_size) { /* If rem cong win size > win update, .. */ pconn->TxWinSizeCongRem -= win_update_size; /* .. dec cong win size by win update. */ } else { pconn->TxWinSizeCongRem = 0; /* Else limit to min cong win size. */ } if (pconn->TxWinSizeRemoteRem > win_update_size) { /* If rem remote win size > win update, .. */ pconn->TxWinSizeRemoteRem -= win_update_size; /* .. dec remote win size by win update. */ } else { pconn->TxWinSizeRemoteRem = 0; /* Else limit to min remote win size. */ } NetTCP_TxConnWinSizeUpdateAvail(pconn); /* Update avail tx win (see Note #2b3). */ break; case NET_TCP_CONN_TX_WIN_REMOTE_UPDATE: /* Calc actual tx remote win (see Note #3a2A1a).*/ tx_win_size_remote_actual = (NET_TCP_WIN_SIZE)(pbuf_hdr->TCP_WinSize - (pconn->TxSeqNbrNext - pbuf_hdr->TCP_AckNbr)); tx_win_size_remote_actual_min = (NET_TCP_WIN_SIZE) DEF_MIN(pbuf_hdr->TCP_WinSize, tx_win_size_remote_actual); /* Update tx remote win (see Note #3a2). */ pconn->TxWinSizeRemote = (NET_TCP_WIN_SIZE) pbuf_hdr->TCP_WinSize; pconn->TxWinSizeRemoteActual = (NET_TCP_WIN_SIZE) tx_win_size_remote_actual_min; pconn->TxWinSizeRemoteRem = (NET_TCP_WIN_SIZE) pconn->TxWinSizeRemoteActual; pconn->TxWinUpdateSeqNbr = (NET_TCP_SEQ_NBR ) pbuf_hdr->TCP_SeqNbr; pconn->TxWinUpdateAckNbr = (NET_TCP_SEQ_NBR ) pbuf_hdr->TCP_AckNbr; pconn->TxWinUpdateWinSize = (NET_TCP_WIN_SIZE) pbuf_hdr->TCP_WinSize; if (pconn->TxWinSizeRemoteMax < pconn->TxWinSizeRemote) { /* If max < updated remote win size, ... */ pconn->TxWinSizeRemoteMax = pconn->TxWinSizeRemote; /* ... set as new max (see Note #3b). */ NetTCP_TxConnWinSizeCfgMinTh(pconn); /* Cfg new tx silly win min th. */ } /* Handle zero win size (see Note #3c). */ NetTCP_TxConnWinSizeZeroWinHandler(pconn, NET_TCP_CONN_TX_WIN_REMOTE_UPDATE, NET_TCP_CONN_CLOSE_ALL); NetTCP_TxConnWinSizeUpdateAvail(pconn); /* Update avail tx win (see Note #2b3). */ break; } *perr = NET_TCP_ERR_NONE; } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxConnWinSizeCalcSlowStartTh() * * Description : Calculate TCP connection's transmit slow start threshold. * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_TxConnWinSizeHandlerCongCtrl(). * * Return(s) : none. * * Caller(s) : NetTCP_TxConnWinSizeHandlerCongCtrl(). * * Note(s) : (1) RFC #2581, Section 3.1 states that ... : * * (a) "the value of ssthresh MUST be set to no more than" : * * (3) ssthresh = max (FlightSize / 2, 2 * SMSS) * * (A) "FlightSize is the amount of outstanding data ... that has been sent but * not yet acknowledged." * * See also 'NetTCP_TxConnWinSizeHandlerCongCtrl() Note #2c2A5a'. ********************************************************************************************************* */ static void NetTCP_TxConnWinSizeCalcSlowStartTh (NET_TCP_CONN *pconn) { NET_TCP_SEQ_NBR tx_data_unackd; NET_TCP_SEQ_NBR tx_data_unackd_th; NET_TCP_SEG_SIZE max_seg_size_th; /* Calc slow start th (see Note #1a3). */ tx_data_unackd = (NET_TCP_SEQ_NBR ) (pconn->TxSeqNbrNext - pconn->TxSeqNbrUnAckd); tx_data_unackd_th = (NET_TCP_SEQ_NBR )((tx_data_unackd * NET_TCP_SST_UNACKD_DATA_NUMER) / NET_TCP_SST_UNACKD_DATA_DENOM); max_seg_size_th = (NET_TCP_SEG_SIZE)pconn->MaxSegSizeConn * NET_TCP_SST_MSS_SCALAR; pconn->TxWinSizeSlowStartTh = (NET_TCP_WIN_SIZE)DEF_MAX(tx_data_unackd_th, max_seg_size_th); } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxConnWinSizeCongSet() * * Description : Set TCP connection's transmit congestion window. * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_TxConnWinSizeHandlerCongCtrl(). * * win_inc_code Indicate how to set TCP connection transmit congestion window : * * NET_TCP_CONN_TX_WIN_CONG_SET_SLOW_START Set TCP connection's congestion * control transmit window size * based on slow start. * * NET_TCP_CONN_TX_WIN_CONG_SET_FAST_RECOVERY Set TCP connection's congestion * control transmit window size * based on fast recovery * (see Note #1a). * * NET_TCP_CONN_TX_WIN_CONG_SET_TIMEOUT Set TCP connection's congestion * control transmit window size * based on transmission timeout * (see Note #1b). * * Return(s) : none. * * Caller(s) : NetTCP_TxConnWinSizeHandlerCongCtrl(). * * Note(s) : (1) (a) RFC #2581, Section 3.2 states that "when the third duplicate ACK is received ... * [for] fast recovery ... set cwnd to ssthresh plus 3*SMSS". * * See also 'NetTCP_TxConnWinSizeHandlerCongCtrl() Note #2c2B1c'. * * (b) RFC #2581, Section 3.1 states that "when a TCP sender detects segment loss using the * retransmission timer ... cwnd MUST be set to no more than ... 1 full-sized segment". * * See also 'NetTCP_TxConnWinSizeHandlerCongCtrl() Note #2c2A5b'. ********************************************************************************************************* */ static void NetTCP_TxConnWinSizeCongSet (NET_TCP_CONN *pconn, NET_TCP_WIN_CODE win_inc_code) { NET_TCP_WIN_SIZE win_size_set; switch (win_inc_code) { case NET_TCP_CONN_TX_WIN_CONG_SET_SLOW_START: win_size_set = (NET_TCP_WIN_SIZE)pconn->TxWinSizeSlowStartTh; break; case NET_TCP_CONN_TX_WIN_CONG_SET_FAST_RECOVERY: /* See Note #1a. */ win_size_set = (NET_TCP_WIN_SIZE)pconn->TxWinSizeSlowStartTh; win_size_set += (NET_TCP_WIN_SIZE)pconn->MaxSegSizeConn * NET_TCP_FAST_RECOVERY_MSS_SCALAR; break; case NET_TCP_CONN_TX_WIN_CONG_SET_TIMEOUT: /* See Note #1b. */ default: win_size_set = (NET_TCP_WIN_SIZE)pconn->MaxSegSizeConn * NET_TCP_CONG_WIN_MSS_SCALAR_TIMEOUT; break; } pconn->TxWinSizeCongCalcdActual = win_size_set; pconn->TxWinSizeCongCalcdCur = 0; pconn->TxWinSizeCongRem = pconn->TxWinSizeCongCalcdActual; } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxConnWinSizeCongInc() * * Description : Increment TCP connection's transmit congestion window. * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_TxConnWinSizeHandlerCongCtrl(). * * pbuf_hdr Pointer to network buffer header that received TCP packet. * -------- Argument validated in NetTCP_Rx(). * * win_update_size Size to increment TCP connection's transmit congestion window (in octets). * * win_inc_code Indicate how to increment TCP connection transmit congestion window : * * NET_TCP_CONN_TX_WIN_CONG_INC_SLOW_START Increment TCP connection's congestion * control transmit window size * based on slow start * (see Note #1a). * * NET_TCP_CONN_TX_WIN_CONG_INC_CONG_AVOID Increment TCP connection's congestion * control transmit window size * based on congestion avoidance * (see Note #1b). * * NET_TCP_CONN_TX_WIN_CONG_INC_REM Increment TCP connection's congestion * control transmit window size * remaining. * * Return(s) : none. * * Caller(s) : NetTCP_TxConnWinSizeHandlerCongCtrl(). * * Note(s) : (1) RFC #2581, Section 3.1 states that ... : * * (a) "During slow start, a TCP increments cwnd by at most SMSS bytes for each ACK received * that acknowledges new data." * * See also 'NetTCP_TxConnWinSizeHandlerCongCtrl() Note #2c2A3a'. * * (b) "During congestion avoidance, cwnd is incremented by 1 full-sized segment per round- * trip time (RTT) ... [An] acceptable way to increase cwnd during congestion avoidance * is to" : * * (1) "Count the number of bytes that have been acknowledged by ACKs for new data." * * (A) The following equation calculates the number of octets that acknowledge * new data for a TCP connection : * * Number of Octets Acknowledged = (SEG.ACK - SND.UNA) * * (B) However, since TCP connection transmit congestion controls are * updated following any TCP connection re-transmit queue handling * (see 'NetTCP_RxPktConnHandlerSeg() Notes #1c & #1d'); the saved * previous value of the TCP connection's last unacknowledged * transmit sequence number ('TxSeqNbrUnackdPrev') MUST be used * (see 'NetTCP_RxPktConnHandlerReTxQ() Note #4'). * * (2) "When the number of bytes acknowledged reaches cwnd," ... * (3) "then cwnd can be incremented by up to SMSS bytes." * * See also 'NetTCP_TxConnWinSizeHandlerCongCtrl() Note #2c2A4'. ********************************************************************************************************* */ /*$PAGE*/ static void NetTCP_TxConnWinSizeCongInc (NET_TCP_CONN *pconn, NET_BUF_HDR *pbuf_hdr, NET_TCP_WIN_SIZE win_update_size, NET_TCP_WIN_CODE win_inc_code) { CPU_BOOLEAN win_size_rem_update; NET_TCP_WIN_SIZE win_size_inc; NET_TCP_WIN_SIZE win_size_inc_mss; NET_TCP_WIN_SIZE win_size_inc_rem; NET_TCP_WIN_SIZE win_size_avail; win_size_rem_update = DEF_NO; win_size_inc_mss = (NET_TCP_WIN_SIZE)pconn->MaxSegSizeConn * NET_TCP_CONG_WIN_MSS_SCALAR_INC; switch (win_inc_code) { case NET_TCP_CONN_TX_WIN_CONG_INC_SLOW_START: /* See Note #1a. */ default: win_size_avail = NET_TCP_WIN_SIZE_MAX - pconn->TxWinSizeCongCalcdActual; if (win_size_inc_mss < win_size_avail) { /* If inc < max avail, .. */ pconn->TxWinSizeCongCalcdActual += win_size_inc_mss; /* .. inc cong win by MSS (see Note #1a). */ } else { pconn->TxWinSizeCongCalcdActual = NET_TCP_WIN_SIZE_MAX; /* Else set cong win to max. */ } pconn->TxWinSizeCongCalcdCur = 0; win_size_rem_update = DEF_YES; win_size_inc_rem = win_size_inc_mss; break; case NET_TCP_CONN_TX_WIN_CONG_INC_CONG_AVOID: /* See Note #1b. */ /* Calc nbr ack'd octets (see Note #1b1). */ win_size_inc = (NET_TCP_WIN_SIZE)((NET_TCP_SEQ_NBR)pbuf_hdr->TCP_AckNbr - pconn->TxSeqNbrUnAckdPrev); win_size_avail = NET_TCP_WIN_SIZE_MAX - pconn->TxWinSizeCongCalcdActual; if (win_size_inc < win_size_avail) { /* If inc < max avail, .. */ pconn->TxWinSizeCongCalcdCur += win_size_inc; /* .. inc nbr ack'd octets (see Note #1b1). */ /* If >= cong win (see Note #1b2), */ /* .. inc cong win by MSS (see Note #1b3). */ if (pconn->TxWinSizeCongCalcdCur >= pconn->TxWinSizeCongCalcdActual) { pconn->TxWinSizeCongCalcdCur -= pconn->TxWinSizeCongCalcdActual; win_size_avail = NET_TCP_WIN_SIZE_MAX - pconn->TxWinSizeCongCalcdActual; if (win_size_inc_mss < win_size_avail) { pconn->TxWinSizeCongCalcdActual += win_size_inc_mss; } else { pconn->TxWinSizeCongCalcdActual = NET_TCP_WIN_SIZE_MAX; } /* Cfg rem cong win inc by MSS. */ win_size_rem_update = DEF_YES; win_size_inc_rem = win_size_inc_mss; } } else { /* Else set cong win to max. */ pconn->TxWinSizeCongCalcdActual = NET_TCP_WIN_SIZE_MAX; win_size_avail = NET_TCP_WIN_SIZE_MAX - pconn->TxWinSizeCongCalcdCur; if (win_size_inc < win_size_avail) { /* If inc < max avail, .. */ pconn->TxWinSizeCongCalcdCur += win_size_inc; /* .. inc nbr ack'd octets (see Note #1b1). */ } else { /* Else set nbr ack'd octets to inc ovf ... */ pconn->TxWinSizeCongCalcdCur = win_size_inc - win_size_avail; /* ... & cfg rem cong win inc by MSS. */ win_size_rem_update = DEF_YES; win_size_inc_rem = win_size_inc_mss; } } break; case NET_TCP_CONN_TX_WIN_CONG_INC_REM: /* Cfg rem cong win inc by win update size. */ win_size_rem_update = DEF_YES; win_size_inc_rem = win_update_size; break; } if (win_size_rem_update == DEF_YES) { /* If rem tx cong win update req'd ... */ if (pconn->TxWinSizeCongRem < pconn->TxWinSizeCongCalcdActual) { /* ... & < actual tx cong win size, ... */ /* ... inc rem tx cong win size. */ win_size_avail = pconn->TxWinSizeCongCalcdActual - pconn->TxWinSizeCongRem; if (win_size_inc_rem < win_size_avail) { /* If avail win size > rem inc, ... */ pconn->TxWinSizeCongRem += win_size_inc_rem; /* ... inc rem tx cong win size by rem inc. */ } else { /* Else limit to actual tx cong win size. */ pconn->TxWinSizeCongRem = pconn->TxWinSizeCongCalcdActual; } } } } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxConnWinSizeUpdateAvail() * * Description : Update TCP connection's available transmit window. * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_TxConnWinSizeHandlerCongCtrl(). * * Return(s) : none. * * Caller(s) : NetTCP_TxConnWinSizeCfgCongCtrl(), * NetTCP_TxConnWinSizeHandlerCongCtrl(). * * Note(s) : (1) (a) RFC #2001, Section 1 states that "the sender can transmit up to the minimum * of the congestion window and the advertised window." * * (b) RFC #2001, Section 2.2 reiterates that "the TCP output routine never sends * more than the minimum of cwnd and the receiver's advertised window". * * (c) RFC #2581, Section 3.1 also reiterates that "the minimum of cwnd and rwnd * [receiver's advertised window] governs data transmission". * * See also 'NetTCP_TxConnWinSizeHandlerCongCtrl() Note #2b3'. ********************************************************************************************************* */ static void NetTCP_TxConnWinSizeUpdateAvail (NET_TCP_CONN *pconn) { /* Calc avail tx win (see Note #1). */ pconn->TxWinSizeAvail = DEF_MIN(pconn->TxWinSizeCongRem, pconn->TxWinSizeRemoteRem); } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxConnWinSizeDupAckCtrlReset() * * Description : Reset TCP connection's transmit window duplicate acknowledgement controls. * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_TxConnWinSizeCfgCongCtrl(). * * Return(s) : none. * * Caller(s) : NetTCP_TxConnWinSizeCfgCongCtrl(). * * Note(s) : none. ********************************************************************************************************* */ static void NetTCP_TxConnWinSizeDupAckCtrlReset (NET_TCP_CONN *pconn) { pconn->TxWinRxdLastSeqNbr = pconn->RxSeqNbrNext; pconn->TxWinRxdLastAckNbr = pconn->TxSeqNbrNext; pconn->TxWinRxdLastWinSize = pconn->TxWinSizeRemote; pconn->TxWinRxdAckDupCtr = 0; } /* ********************************************************************************************************* * NetTCP_TxConnWinSizeDupAckCtrlUpdate() * * Description : Update TCP connection's transmit window duplicate acknowledgement controls. * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_TxConnWinSizeHandlerCongCtrl(). * * pbuf_hdr Pointer to network buffer header that received TCP packet. * -------- Argument validated in NetTCP_Rx(). * * reset_ctr Indicate whether to reset the duplicate acknowledgement counter : * * DEF_YES Reset duplicate acknowledgement counter. * DEF_NO Do NOT reset duplicate acknowledgement counter. * * Return(s) : none. * * Caller(s) : NetTCP_TxConnWinSizeHandlerCongCtrl(). * * Note(s) : none. ********************************************************************************************************* */ static void NetTCP_TxConnWinSizeDupAckCtrlUpdate (NET_TCP_CONN *pconn, NET_BUF_HDR *pbuf_hdr, CPU_BOOLEAN reset_ctr) { pconn->TxWinRxdLastSeqNbr = (NET_TCP_SEQ_NBR )pbuf_hdr->TCP_SeqNbr; pconn->TxWinRxdLastAckNbr = (NET_TCP_SEQ_NBR )pbuf_hdr->TCP_AckNbr; pconn->TxWinRxdLastWinSize = (NET_TCP_WIN_SIZE)pbuf_hdr->TCP_WinSize; if (reset_ctr == DEF_YES) { pconn->TxWinRxdAckDupCtr = 0; } } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxConnWinSizeZeroWinHandler() * * Description : Handle TCP connection's transmit queue zero window. * * Argument(s) : pconn Pointer to TCP connection. * ----- Argument validated in NetTCP_TxConnWinSizeHandlerCongCtrl(), * NetTCP_TxConnWinSizeZeroWinTimeout(). * * win_update_code Indicate how to update TCP connection transmit window : * * NET_TCP_CONN_TX_WIN_REMOTE_UPDATE Handle TCP connection's remote * receive window size update. * NET_TCP_CONN_TX_WIN_TIMEOUT Handle TCP connection's remote * receive zero window size timeout. * * close_code Select which close action(s) to perform; bit-field flags logically OR'd : * * NET_TCP_CONN_CLOSE_NONE Perform NO close actions. * NET_TCP_CONN_CLOSE_ALL Perform ALL close actions. * * NET_TCP_CONN_CLOSE_CONN_TX_RESET Perform close connection transmit reset. * NET_TCP_CONN_CLOSE_CONN_ALL Perform ALL connection close actions. * * NET_TCP_CONN_CLOSE_TMR_TX_IDLE Close transmit idle timer. * NET_TCP_CONN_CLOSE_TMR_TX_SILLY_WIN Close transmit silly window persist timer. * NET_TCP_CONN_CLOSE_TMR_TX_ZERO_WIN Close transmit zero window persist timer. * NET_TCP_CONN_CLOSE_TMR_TX_ACK_DLY Close transmit acknowledgement delay timer. * NET_TCP_CONN_CLOSE_TMR_RE_TX Close re-transmit timer. * NET_TCP_CONN_CLOSE_TMR_KEEP_ALIVE Close connection keep-alive timer. * NET_TCP_CONN_CLOSE_TMR_TIMEOUT Close connection timer. * NET_TCP_CONN_CLOSE_TMR_ALL Close ALL timers. * * See also 'TCP CONNECTION CLOSE/FREE CODE DEFINES'. * * Return(s) : none. * * Caller(s) : NetTCP_TxConnWinSizeZeroWinTimeout(), * NetTCP_TxConnWinSizeHandlerCongCtrl(). * * Note(s) : (1) RFC #1122, Section 4.2.2.17 states that although "a TCP MAY keep its offered receive * window closed indefinitely ... the sending TCP MUST allow the connection to stay open * ... as long as the receiving TCP continues to send acknowledgments in response to ... * probe segments". * * (a) "Probing of zero (offered) windows MUST be supported." * * (1) "If zero window probing is not supported, a connection may hang forever when an * ACK segment that re-opens the window is lost." * * See also Note #1c2. * * (b) (1) "The transmitting host SHOULD send the first zero-window probe" ... * * (A) Although NO RFC specifies whether a TCP zero window probe should or should NOT * include data; Stevens, TCP/IP Illustrated, Volume 1, 8th Printing, Section 22.1, * Page 325 states that "the window probes contain 1 byte of data". * * (2) (A) "when a zero window has existed for the retransmission timeout period," ... * * (B) "and SHOULD increase exponentially the interval between successive probes." * * (1) "Exponential backoff is recommended ... similar to ... the retransmission * algorithm, and it may be possible to combine the two procedures in the * implementation." * * See also 'NetTCP_TxConnRTO_CalcBackOff() Notes #1 & #2'. * * (c) (1) "It is extremely important to remember that ACK (acknowledgment) segments that * contain no data are not reliably transmitted by TCP." * * (2) "This procedure minimizes delay if the zero-window condition is due to a lost * ACK segment containing a window-opening update." ********************************************************************************************************* */ /*$PAGE*/ static void NetTCP_TxConnWinSizeZeroWinHandler (NET_TCP_CONN *pconn, NET_TCP_WIN_CODE win_update_code, NET_TCP_CLOSE_CODE close_code) { #if ((NET_CTR_CFG_ERR_EN == DEF_ENABLED) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif CPU_BOOLEAN tmr_update; NET_TCP_TIMEOUT_MS timeout_ms; NET_TMR_TICK timeout_tick; NET_ERR err; /* ----------- VALIDATE TCP CONN STATE ------------ */ switch (pconn->ConnState) { case NET_TCP_CONN_STATE_FREE: NET_CTR_ERR_INC(NetTCP_ErrNotUsedCtr); return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_CLOSED: case NET_TCP_CONN_STATE_LISTEN: case NET_TCP_CONN_STATE_SYNC_RXD: case NET_TCP_CONN_STATE_SYNC_RXD_PASSIVE: case NET_TCP_CONN_STATE_SYNC_RXD_ACTIVE: case NET_TCP_CONN_STATE_SYNC_TXD: case NET_TCP_CONN_STATE_FIN_WAIT_2: case NET_TCP_CONN_STATE_TIME_WAIT: case NET_TCP_CONN_STATE_CLOSING_DATA_AVAIL: return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_CONN: case NET_TCP_CONN_STATE_FIN_WAIT_1: case NET_TCP_CONN_STATE_CLOSING: case NET_TCP_CONN_STATE_CLOSE_WAIT: case NET_TCP_CONN_STATE_LAST_ACK: break; case NET_TCP_CONN_STATE_NONE: default: NET_CTR_ERR_INC(NetTCP_ErrConnInvalidStateCtr); return; /* Prevent 'break NOT reachable' compiler warning. */ } /*$PAGE*/ /* --------- HANDLE TCP CONN TX ZERO WIN ---------- */ tmr_update = DEF_NO; switch (win_update_code) { case NET_TCP_CONN_TX_WIN_REMOTE_UPDATE: default: if (pconn->TxWinSizeRemote > 0) { /* If remote win size > 0, ... */ if (pconn->TxQ_ZeroWinTmr != (NET_TMR *)0) { /* ... free zero win tmr. */ NetTmr_Free(pconn->TxQ_ZeroWinTmr); pconn->TxQ_ZeroWinTmr = (NET_TMR *)0; } } else { /* Else if remote win size zero ... */ if (pconn->TxQ_ZeroWinTmr != (NET_TMR *)0) { /* ... & NO zero win tmr; ... */ /* ... set first tx zero win probe timeout ... */ timeout_ms = pconn->TxRTT_RTO_ms; /* ... = RTO (see Note #1b2A). */ timeout_tick = pconn->TxRTT_RTO_tick; tmr_update = DEF_YES; } } break; case NET_TCP_CONN_TX_WIN_TIMEOUT: /* On timeout : ... */ /* ... tx TCP zero win probe (see Note #1b1); ... */ NetTCP_TxConnProbe((NET_TCP_CONN *) pconn, (CPU_BOOLEAN ) DEF_YES, /* Tx probe with data (see Note #1b1A). */ (NET_TCP_CLOSE_CODE) close_code, (NET_ERR *)&err); /* Ignore transitory tx err(s). */ /* ... update tx probe timeout (see Note #1b2B). */ timeout_ms = (NET_TCP_TIMEOUT_MS )NetTCP_TxConnRTO_CalcBackOff(pconn, pconn->TxWinZeroWinTimeout_ms); timeout_tick = ((NET_TMR_TICK )timeout_ms * NET_TMR_TIME_TICK_PER_SEC) / DEF_TIME_NBR_mS_PER_SEC; tmr_update = DEF_YES; break; } if (tmr_update == DEF_YES) { /* If tx probe tmr update req'd, ... */ /* ... get tx zero win probe tmr. */ pconn->TxQ_ZeroWinTmr = NetTmr_Get((void *) pconn, (CPU_FNCT_PTR) NetTCP_TxConnWinSizeZeroWinTimeout, (NET_TMR_TICK) timeout_tick, (CPU_INT16U ) NET_TMR_FLAG_NONE, (NET_ERR *)&err); if (err == NET_TMR_ERR_NONE) { /* If NO err(s), cfg tx zero win probe timeout. */ pconn->TxWinZeroWinTimeout_ms = timeout_ms; } /* Else ignore transitory rsrc err(s) ... */ /* ... [see Note #1a1]. */ } } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxConnWinSizeZeroWinTimeout() * * Description : (1) (a) Handle TCP connection's transmit queue zero window persist timeout ... : * * (1) Clear TCP connection's transmit zero window persist timer See Note #4aA1 * (2) Handle TCP connection transmit zero window See Note #2 * * (b) ... for the following states : * * (1) ESTABLISHED * (2) FIN-WAIT-1 * (3) CLOSING * (4) CLOSE-WAIT * (5) LAST-ACK * * * Argument(s) : pconn_timeout Pointer to TCP connection (see Note #3b). * * Return(s) : none. * * Caller(s) : Referenced in NetTCP_TxConnWinSizeZeroWinHandler(). * * Note(s) : (2) RFC #1122, Section 4.2.2.17 states that the "probing of zero (offered) windows * ... SHOULD send the first zero-window probe when a zero window has existed for * the retransmission timeout period and SHOULD increase exponentially the interval * between successive probes". * * See also 'NetTCP_TxConnWinSizeZeroWinHandler() Note #1'. * * (3) Ideally, network timer expiration functions could be defined as '[(void) (OBJECT *)]' * type functions -- even though network timer API functions cast expiration functions * to generic 'CPU_FNCT_PTR' type (i.e. '[(void) (void *)]'). * * (a) (1) Unfortunately, ISO-IEC 9899-1999 ANSI-C, Section 6.3.2.3.7 states that "a * pointer to an object ... may be converted to a pointer to a different object * ... [but] if the resulting pointer is not correctly aligned ... the behavior * is undefined". * * And since compilers may NOT correctly convert 'void' pointers to non-'void' * pointer arguments, network timer expiration functions MUST avoid incorrect * pointer conversion behavior between 'void' pointer parameters & non-'void' * pointer arguments & therefore CANNOT be defined as '[(void) (OBJECT *)]'. * * (2) However, Section 6.3.2.3.1 states that "a pointer to void may be converted * to or from a pointer to any ... object ... A pointer to any ... object ... * may be converted to a pointer to void and back again; the result shall * compare equal to the original pointer". * * (b) Therefore, to correctly convert 'void' pointer objects back to appropriate * network object pointer objects, network timer expiration functions MUST : * * (1) Be defined as 'CPU_FNCT_PTR' type (i.e. '[(void) (void *)]'); & ... * (2) Explicitly cast 'void' pointer arguments to specific object pointers; ... * (A) ... in this case, a 'NET_TCP_CONN' pointer. * * See also 'net_tmr.c NetTmr_Get() Note #3'. * * (4) This function is a network timer expiration function : * * (a) (1) For the following connection timer(s) ... : * * (A) TCP connection transmit zero window persist timer ('TxQ_ZeroWinTmr') * * (2) (A) Clear the timer pointer; ... * (1) Cleared prior to next handler function(s) as an extra precaution to * avoiding re-freeing the timer (see Note #4a2B). * * (B) but do NOT re-free the timer. * * (b) Do NOT set the following close timer flag(s) : * * (1) NET_TCP_CONN_CLOSE_TMR_TX_ZERO_WIN * * (5) Certain network connections MUST periodically suspend network transmit(s) to handle * network receive packet(s). To protect TCP connections from transmit corruption while * suspended, ALL TCP data transmits & TCP transmit queue handling MUST be blocked for * suspended connections until the connection is no longer suspended. * * However, handling the TCP connection's transmit zero window timeout is permitted since * NO TCP data is transmitted & the TCP connection's transmit queue is NOT handled (see * Note #1a). * * See also 'NetTCP_TxConnTxQ() Note #10b2A2', * 'NetTCP_TxConnTxQ_TimeoutIdle() Note #5', * 'NetTCP_TxConnTxQ_TimeoutSillyWin() Note #5', * & 'NetTCP_TxConnReTxQ_Timeout() Note #5'. ********************************************************************************************************* */ /*$PAGE*/ static void NetTCP_TxConnWinSizeZeroWinTimeout (void *pconn_timeout) { #if ((NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) && \ (NET_CTR_CFG_ERR_EN == DEF_ENABLED) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif NET_TCP_CONN *pconn; NET_TCP_CLOSE_CODE close_code; pconn = (NET_TCP_CONN *)pconn_timeout; /* See Note #3b2A. */ close_code = NET_TCP_CONN_CLOSE_ALL; DEF_BIT_CLR(close_code, NET_TCP_CONN_CLOSE_TMR_TX_ZERO_WIN); /* See Note #4b1. */ #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) /* -------------- VALIDATE TCP CONN --------------- */ if (pconn == (NET_TCP_CONN *)0) { NET_CTR_ERR_INC(NetTCP_ErrNullPtrCtr); return; } if (pconn->Type != NET_TCP_TYPE_CONN) { NET_CTR_ERR_INC(NetTCP_ErrConnInvalidTypeCtr); return; } switch (pconn->ConnState) { case NET_TCP_CONN_STATE_FREE: NET_CTR_ERR_INC(NetTCP_ErrNotUsedCtr); return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_CLOSED: case NET_TCP_CONN_STATE_LISTEN: case NET_TCP_CONN_STATE_SYNC_RXD: case NET_TCP_CONN_STATE_SYNC_RXD_PASSIVE: case NET_TCP_CONN_STATE_SYNC_RXD_ACTIVE: case NET_TCP_CONN_STATE_SYNC_TXD: case NET_TCP_CONN_STATE_FIN_WAIT_2: case NET_TCP_CONN_STATE_TIME_WAIT: case NET_TCP_CONN_STATE_CLOSING_DATA_AVAIL: NET_CTR_ERR_INC(NetTCP_ErrConnInvalidOpCtr); return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_CONN: /* See Note #1. */ case NET_TCP_CONN_STATE_FIN_WAIT_1: case NET_TCP_CONN_STATE_CLOSING: case NET_TCP_CONN_STATE_CLOSE_WAIT: case NET_TCP_CONN_STATE_LAST_ACK: switch (pconn->TxQ_State) { case NET_TCP_TX_Q_STATE_CONN: case NET_TCP_TX_Q_STATE_CONN_CLOSING: case NET_TCP_TX_Q_STATE_CONN_SUSPEND: /* See Note #5. */ break; case NET_TCP_TX_Q_STATE_NONE: case NET_TCP_TX_Q_STATE_CLOSED: case NET_TCP_TX_Q_STATE_CONN_CLOSED: default: return; /* Prevent 'break NOT reachable' compiler warning. */ } break; case NET_TCP_CONN_STATE_NONE: default: NetTCP_ConnClose((NET_TCP_CONN *)pconn, (NET_BUF_HDR *)0, (CPU_BOOLEAN )DEF_YES, (NET_TCP_CLOSE_CODE)close_code); NET_CTR_ERR_INC(NetTCP_ErrConnInvalidStateCtr); return; /* Prevent 'break NOT reachable' compiler warning. */ } #endif /* ----- HANDLE TCP CONN TX ZERO WIN TIMEOUT ------ */ pconn->TxQ_ZeroWinTmr = (NET_TMR *)0; /* Clr tx Q zero win tmr (see Note #4a2A1). */ /* Handle tx zero win (see Note #2). */ NetTCP_TxConnWinSizeZeroWinHandler(pconn, NET_TCP_CONN_TX_WIN_TIMEOUT, close_code); } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxConnSync() * * Description : (1) Prepare & transmit TCP connection synchronization : * * (a) Validate TCP connection state * (b) Prepare TCP connection synchronization : * (1) Get timer * (2) Get buffer * (3) Prepare TCP segment : * (A) TCP segment addresses * (B) TCP segment sequence numbers * (C) TCP segment transmit flags : *- (1) SYN * (2) ACK * (D) TCP segment window size * (E) TCP segment options * (F) IP datagram parameters * (G) TCP segment packet buffer controls * (c) Update TCP connection : * (1) Queue TCP connection synchronization packet * (2) Update TCP connection sequence numbers * (d) Transmit TCP connection synchronization * * * (2) (a) RFC #793, Section 3.3 'Sequence Numbers : Initial Sequence Number Selection' states * that "for a [TCP] connection to be established or initialized, ... two TCP's must * synchronize ... each other's initial sequence numbers" by transmitting initial * connection request segments (i.e., segments with the SYN control bit set). * * RFC #793, Section 3.4 states that "this procedure normally is initiated by one TCP * and responded to by another TCP ... [but] works if two TCP simultaneously initiate * the procedure". * * (b) RFC #793, Section 3.9 'Event Processing : OPEN Call : CLOSED STATE' states that after * "a SYN segment ... is sent ... [to] set SND.UNA to ISS [initial send sequence number], * SND.NXT to ISS+1". * * The following sections confirm these sequence number configurations summary : * * (1) RFC #793, Section 3.9 'Event Processing : OPEN Call : LISTEN STATE' * (2) RFC #793, Section 3.9 'Event Processing : SEND Call : LISTEN STATE' * (3) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : LISTEN [State] : * Check for SYN' * * (3) A TCP connection's transmit sequences are initialized when the initial TCP synchronization * segment is queued for transmission : * * (a) 'TxSeqNbrSync' points to the initial, synchronization (SYN) transmit sequence number. * * (b) (1) 'TxSeqNbrNextQ' points to the next transmit sequence number to enqueue data octets. * (2) 'TxSeqNbrNext' points to the next transmit sequence number to transmit. * * * ----- ------------------------ Initial Synchronization * ^ | Initial SEQ # (SYN) | <--- Transmit Sequence Number * | ------------------------ (see Note #3a) * | | Data Octet # 1 | --- * | | Data Octet # 2 | ^ * | | Data Octet # 3 | | * | . | | Next / Queued * TCP Connection | . | | Transmit Sequence Number(s) * Transmit Sequences | . | | (see Note #3b) * (see Note #3) | Data Octet # (N - 2) | | * | Data Octet # (N - 1) | v * | | Data Octet # N | --- * | ------------------------ * | | Close SEQ # (FIN) | * | ------------------------ * v | Last SEQ # | * ----- ------------------------ * * * See also 'NetTCP_TxConnClose() Note #2'. * * *$PAGE* * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_TxConnReq(), * NetTCP_RxPktConnHandlerListen(). * * pbuf_hdr Pointer to network buffer header that received TCP packet. * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_NONE TCP connection synchronization successfully * transmitted. * NET_TCP_ERR_CONN_NOT_USED TCP connection NOT currently used. * NET_TCP_ERR_INVALID_CONN_STATE Invalid TCP connection state. * NET_TCP_ERR_INVALID_CONN_OP Invalid TCP connection operation. * NET_TCP_ERR_NONE_AVAIL Resources NOT available. * * - RETURNED BY NetTCP_TxConnReTxQ_TimeoutSet() : - * NET_TCP_ERR_CONN_FAULT TCP connection fault; connection(s) aborted. * * - RETURNED BY NetTCP_TxConnPrepareSegAddrs() : -- * NET_CONN_ERR_INVALID_FAMILY Invalid connection family. * NET_CONN_ERR_INVALID_ADDR Invalid TCP connection address. * NET_CONN_ERR_INVALID_ADDR_LEN Invalid TCP connection address length. * * ------ RETURNED BY NetTCP_TxPktHandler() : ------ * NET_TCP_ERR_TX TCP transmit error. * * Return(s) : none. * * Caller(s) : NetTCP_TxConnReq(), * NetTCP_RxPktConnHandlerListen(), * NetTCP_RxPktConnHandlerSyncTxd(). * * Note(s) : (4) RFC #1122, Section 4.2.2.6 states that a "TCP SHOULD send an MSS (Maximum Segment Size) * option in every SYN segment". * * (5) Network resources MUST be appropriately allocated/deallocated : * * (a) Increment network buffer's reference counter to include the queued TCP connection * synchronization as a new reference to the network buffer. * * (b) On ANY errors, network resources MUST be appropriately freed : * * (1) For any network resources NOT yet linked to the TCP connection, each * network resource MUST be freed by appropriate function(s). * (2) For all network resources that have been linked to the TCP connection, ALL * network resources are freed by NetTCP_ConnClose(). * * (6) If transmitting a TCP synchronization packet from the SYN-SENT state : * * (a) Connection timeout is reset to initial synchronization timeout value * (b) Previous synchronization sequence number MUST be re-used * (c) Previous synchronization packet in TCP connection's re-transmit queue is freed * * See also 'NetTCP_RxPktConnHandlerSyncTxd() Note #2c3B2'. * * (7) #### IP options currently NOT implemented. ********************************************************************************************************* */ static void NetTCP_TxConnSync (NET_TCP_CONN *pconn, NET_BUF_HDR *pbuf_hdr, NET_ERR *perr) { #if (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL) CPU_SR cpu_sr; #endif NET_BUF_SIZE data_len; NET_BUF_SIZE data_ix; NET_BUF *pseg_sync; NET_BUF_HDR *pseg_sync_hdr; NET_IP_TOS TOS; NET_IP_TTL TTL; NET_IP_ADDR src_addr; NET_IP_ADDR dest_addr; NET_TCP_PORT_NBR src_port; NET_TCP_PORT_NBR dest_port; NET_TCP_SEQ_NBR seq_nbr; NET_TCP_SEQ_NBR ack_nbr; NET_TCP_WIN_SIZE win_size; NET_TCP_OPT_CFG_MAX_SEG_SIZE *popt_cfg_max_seg_size; NET_TCP_OPT_CFG_MAX_SEG_SIZE opt_cfg_max_seg_size; CPU_INT16U flags_tcp; CPU_INT16U flags_ip; NET_ERR err; /*$PAGE*/ #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) /* ----------- VALIDATE TCP CONN STATE ------------ */ switch (pconn->ConnState) { case NET_TCP_CONN_STATE_FREE: NET_CTR_ERR_INC(NetTCP_ErrNotUsedCtr); *perr = NET_TCP_ERR_CONN_NOT_USED; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_CLOSED: case NET_TCP_CONN_STATE_LISTEN: case NET_TCP_CONN_STATE_SYNC_TXD: break; case NET_TCP_CONN_STATE_SYNC_RXD: case NET_TCP_CONN_STATE_SYNC_RXD_PASSIVE: case NET_TCP_CONN_STATE_SYNC_RXD_ACTIVE: case NET_TCP_CONN_STATE_CONN: case NET_TCP_CONN_STATE_FIN_WAIT_1: case NET_TCP_CONN_STATE_FIN_WAIT_2: case NET_TCP_CONN_STATE_CLOSING: case NET_TCP_CONN_STATE_TIME_WAIT: case NET_TCP_CONN_STATE_CLOSE_WAIT: case NET_TCP_CONN_STATE_LAST_ACK: case NET_TCP_CONN_STATE_CLOSING_DATA_AVAIL: NET_CTR_ERR_INC(NetTCP_ErrConnInvalidOpCtr); *perr = NET_TCP_ERR_INVALID_CONN_OP; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_NONE: default: NetTCP_ConnClose(pconn, pbuf_hdr, DEF_YES, NET_TCP_CONN_CLOSE_CONN_ALL); NET_CTR_ERR_INC(NetTCP_ErrConnInvalidStateCtr); *perr = NET_TCP_ERR_INVALID_CONN_STATE; return; /* Prevent 'break NOT reachable' compiler warning. */ } #endif /* ----------- PREPARE TCP CONN SYNC SEG ---------- */ /* ------------------- GET TMR -------------------- */ NetTCP_TxConnReTxQ_TimeoutSet(pconn, DEF_NO, NET_TCP_CONN_CLOSE_ALL, perr); if (*perr != NET_TCP_ERR_NONE) { return; } /* ------------------- GET BUF -------------------- */ data_len = NET_TCP_DATA_LEN_TX_SYNC; data_ix = NET_BUF_DATA_TX_IX; pseg_sync = NetBuf_Get((NET_BUF_SIZE) data_len, (NET_BUF_SIZE) data_ix, (CPU_INT16U ) NET_BUF_FLAG_NONE, (NET_ERR *)&err); if ( err != NET_BUF_ERR_NONE) { /* See Note #5b1. */ *perr = NET_TCP_ERR_NONE_AVAIL; return; } /*$PAGE*/ /* --------------- PREPARE TCP HDR ---------------- */ /* Prepare seg addrs. */ if (pbuf_hdr != (NET_BUF_HDR *)0) { /* If TCP pkt rx'd, cfg TCP tx ... */ /* ... src addr from rx'd TCP pkt dest addr ... */ src_port = (NET_TCP_PORT_NBR)pbuf_hdr->TCP_UDP_PortDest; src_addr = (NET_IP_ADDR )pbuf_hdr->IP_AddrDest; /* .. & dest addr from rx'd TCP pkt src addr. */ dest_port = (NET_TCP_PORT_NBR)pbuf_hdr->TCP_UDP_PortSrc; dest_addr = (NET_IP_ADDR )pbuf_hdr->IP_AddrSrc; } else { /* Else cfg TCP tx pkt addrs from TCP conn addrs. */ NetTCP_TxConnPrepareSegAddrs((NET_TCP_CONN *) pconn, (CPU_INT08U *)&src_port, (CPU_INT08U *)&src_addr, (CPU_INT16U ) sizeof(src_port), (CPU_INT16U ) sizeof(src_addr), (CPU_INT08U *)&dest_port, (CPU_INT08U *)&dest_addr, (CPU_INT16U ) sizeof(dest_port), (CPU_INT16U ) sizeof(dest_addr), (NET_ERR *) perr); if (*perr != NET_TCP_ERR_NONE) { /* See Note #5b1. */ NetBuf_Free(pseg_sync); return; } } /* Prepare TCP sync seq nbrs. */ if (pconn->ConnState != NET_TCP_CONN_STATE_SYNC_TXD) { /* For non-sync-tx'd states (see Note #6b), ... */ NET_TCP_TX_GET_SEQ_NBR(seq_nbr); /* ... get sync seq nbr. */ } else { seq_nbr = pconn->TxSeqNbrSync; } ack_nbr = (pconn->ConnState != NET_TCP_CONN_STATE_CLOSED) ? pconn->RxSeqNbrNext : NET_TCP_ACK_NBR_NONE; /* Prepare TCP tx flags (see Note #1b3C). */ flags_tcp = NET_TCP_FLAG_NONE | NET_TCP_FLAG_TX_SYNC; if (pconn->ConnState != NET_TCP_CONN_STATE_CLOSED) { /* For non-CLOSED state, ... */ DEF_BIT_SET(flags_tcp, NET_TCP_FLAG_TX_ACK); /* ... tx ACK. */ } /* Prepare TCP rx win size. */ win_size = pconn->RxWinSizeActual; /* Prepare TCP max seg size opt (see Note #4). */ popt_cfg_max_seg_size = &opt_cfg_max_seg_size; popt_cfg_max_seg_size->Type = NET_TCP_OPT_CFG_TYPE_MAX_SEG_SIZE; popt_cfg_max_seg_size->MaxSegSize = pconn->MaxSegSizeLocal; popt_cfg_max_seg_size->NextOptPtr = (void *)0; /* Prepare IP params. */ TOS = pconn->TxIP_TOS; TTL = pconn->TxIP_TTL; flags_ip = pconn->TxIP_Flags; /*$PAGE*/ /* Init buf ctrls. */ pseg_sync_hdr = &pseg_sync->Hdr; pseg_sync_hdr->DataIx = (CPU_INT16U )data_ix; pseg_sync_hdr->DataLen = (NET_BUF_SIZE)data_len; pseg_sync_hdr->TotLen = (NET_BUF_SIZE)pseg_sync_hdr->DataLen; pseg_sync_hdr->ProtocolHdrType = NET_PROTOCOL_TYPE_TCP; pseg_sync_hdr->TCP_UDP_PortSrc = (NET_PORT_NBR)src_port; pseg_sync_hdr->IP_AddrSrc = (CPU_INT32U )src_addr; pseg_sync_hdr->TCP_UDP_PortDest = (NET_PORT_NBR)dest_port; pseg_sync_hdr->IP_AddrDest = (CPU_INT32U )dest_addr; pseg_sync_hdr->TCP_SegLenInit = (CPU_INT16U )NET_TCP_SEG_LEN_SYNC; pseg_sync_hdr->TCP_SegLen = (CPU_INT16U )pseg_sync_hdr->TCP_SegLenInit; pseg_sync_hdr->TCP_SegLenLast = (CPU_INT16U )pseg_sync_hdr->TCP_SegLenInit; pseg_sync_hdr->TCP_SegLenData = (CPU_INT16U )0; pseg_sync_hdr->TCP_SegSync = (CPU_BOOLEAN )DEF_YES; pseg_sync_hdr->TCP_SegClose = (CPU_BOOLEAN )DEF_NO; pseg_sync_hdr->TCP_SegAck = (CPU_BOOLEAN )DEF_BIT_IS_SET(flags_tcp, NET_TCP_FLAG_TX_ACK); pseg_sync_hdr->TCP_SegReset = (CPU_BOOLEAN )DEF_NO; pseg_sync_hdr->TCP_SeqNbrInit = (CPU_INT32U )seq_nbr; pseg_sync_hdr->TCP_SeqNbrLast = (CPU_INT32U )pseg_sync_hdr->TCP_SeqNbrInit; pseg_sync_hdr->TCP_SeqNbr = (CPU_INT32U )pseg_sync_hdr->TCP_SeqNbrInit; pseg_sync_hdr->TCP_AckNbr = (CPU_INT32U )ack_nbr; pseg_sync_hdr->TCP_AckNbrLast = (CPU_INT32U )pseg_sync_hdr->TCP_AckNbr; pseg_sync_hdr->TCP_WinSizeLast = (CPU_INT16U )win_size; pseg_sync_hdr->TCP_Flags = (CPU_INT16U )flags_tcp; pseg_sync_hdr->TCP_SegReTxCtr = (CPU_INT16U )0; pseg_sync_hdr->RefCtr++; /* TCP maintains ref until seg ack'd (see Note #5a).*/ /* --------------- UPDATE TCP CONN ---------------- */ /* Q conn sync seg to TCP re-tx Q. */ NetTCP_ConnFreeBufQ(&pconn->ReTxQ_Head, &pconn->ReTxQ_Tail); /* Free re-tx Q (see Note #6c). */ pconn->ReTxQ_Head = pseg_sync; pconn->ReTxQ_Tail = pseg_sync; /* Update TCP conn seq nbrs (see Notes #2 & #3). */ pconn->TxSeqNbrSync = (NET_TCP_SEQ_NBR) seq_nbr; pconn->TxSeqNbrNext = (NET_TCP_SEQ_NBR)(seq_nbr + pseg_sync_hdr->TCP_SegLen); pconn->TxSeqNbrNextQ = (NET_TCP_SEQ_NBR) pconn->TxSeqNbrNext; pconn->TxSeqNbrUnAckdPrev = (NET_TCP_SEQ_NBR) pconn->TxSeqNbrUnAckd; pconn->TxSeqNbrUnAckd = (NET_TCP_SEQ_NBR) pconn->TxSeqNbrSync; pconn->TxSeqNbrUnReTxd = (NET_TCP_SEQ_NBR) pconn->TxSeqNbrUnAckd; /* ------------- TX TCP CONN SYNC SEG ------------- */ NetTCP_TxPktHandler((NET_BUF *)pseg_sync, (NET_IP_ADDR )src_addr, (NET_TCP_PORT_NBR)src_port, (NET_IP_ADDR )dest_addr, (NET_TCP_PORT_NBR)dest_port, (NET_TCP_SEQ_NBR )seq_nbr, (NET_TCP_SEQ_NBR )ack_nbr, (NET_TCP_WIN_SIZE)win_size, (NET_IP_TOS )TOS, (NET_IP_TTL )TTL, (CPU_INT16U )flags_tcp, (CPU_INT16U )flags_ip, (void *)popt_cfg_max_seg_size, (void *)0, /* See Note #7. */ (NET_ERR *)perr); switch (*perr) { case NET_TCP_ERR_NONE: case NET_ERR_INIT_INCOMPLETE: case NET_ERR_TX: /* Ignore transitory tx err(s). */ NET_CTR_STAT_INC(NetTCP_StatTxSegConnSyncCtr); break; case NET_TCP_ERR_TX: default: NetTCP_ConnClose(pconn, pbuf_hdr, pconn->ConnCloseAppFlag, NET_TCP_CONN_CLOSE_CONN_ALL); return; /* Prevent 'break NOT reachable' compiler warning. */ } *perr = NET_TCP_ERR_NONE; } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxConnClose() * * Description : (1) Prepare & transmit TCP connection close : * * (a) Validate TCP connection state * (b) Prepare TCP connection close segment : * (1) Get buffer * (2) Prepare TCP segment : * (A) TCP segment addresses * (B) TCP segment sequence numbers * (C) TCP segment transmit flags : * (1) ACK * (2) FIN * (D) TCP segment window size * (E) IP datagram parameters * (F) TCP segment packet buffer controls * (c) Update TCP connection : * (1) Queue TCP connection close packet * (2) Update TCP connection sequence number(s) * (d) Transmit TCP connection close segment * * * (2) A TCP connection's transmit sequences are closed when the closing TCP segment is queued * for transmission : * * (a) 'TxSeqNbrClose' points to the closing (FIN) transmit sequence number. * * (b) 'TxSeqNbrLast' points to the last sequence number used to close the TCP connection * transmit sequences when acknowledged. * * * ----- ------------------------ * ^ | Initial SEQ # (SYN) | * | ------------------------ * | | Data Octet # 1 | * | | Data Octet # 2 | * | | Data Octet # 3 | * | . | * TCP Connection | . | * Transmit Sequences | . | * (see Note #2) | Data Octet # (N - 2) | * | Data Octet # (N - 1) | * | | Data Octet # N | Closing Transmit * | ------------------------ Sequence Number * | | Close SEQ # (FIN) | <--- (see Note #2a) * | ------------------------ * v | Last SEQ # | <--- Last Transmit * ----- ------------------------ Sequence Number * (see Note #2b) * * * See also 'NetTCP_TxConnSync() Note #3'. * * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_ConnReqClose(). * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_NONE TCP connection close successfully * transmitted. * NET_TCP_ERR_CONN_NOT_USED TCP connection NOT currently used. * NET_TCP_ERR_CONN_FAULT TCP connection fault; connection(s) aborted. * NET_TCP_ERR_INVALID_CONN_STATE Invalid TCP connection state. * NET_TCP_ERR_INVALID_CONN_OP Invalid TCP connection operation. * NET_TCP_ERR_NONE_AVAIL Resources NOT available. * * ------- RETURNED BY NetTCP_TxConnTxQ() : ------- * NET_ERR_INIT_INCOMPLETE Network initialization NOT complete. * NET_TCP_ERR_CONN_FAIL TCP connection operation(s) failed. * NET_TCP_ERR_CONN_ACK_INVALID TCP connection acknowledgement NOT valid for * current TCP connection state. * NET_TCP_ERR_INVALID_LEN_SEG Invalid TCP sequence-segment length. * NET_TCP_ERR_TX TCP transmit error. * * Return(s) : none. * * Caller(s) : NetTCP_ConnReqClose(). *$PAGE* * Note(s) : (3) Network resources MUST be appropriately allocated/deallocated : * * (a) Network buffer's reference counter MUST include the queued TCP connection close as a * new reference to the network buffer. However, this additional reference is handled * by the TCP connection transmit queue (see 'NetTCP_TxConnTxQ() Note #9'). * * (b) On ANY errors, network resources MUST be appropriately freed : * * (1) For any network resources NOT yet linked to the TCP connection, each network * resource MUST be freed by appropriate function(s). * (2) For all network resources that have been linked to the TCP connection, ALL network * resources are freed by NetTCP_ConnClose(). * * (4) Network buffer's reference counter must be incremented to include the TCP connection close * segment to be enqueued to the TCP connection's re-transmit queue as a new reference to the * network buffer. * * However, NetTCP_TxConnTxQ() handles the reference counter increment when it enqueues the * TCP connection close segment queued to the TCP connection's re-transmit queue. * * See 'NetTCP_TxConnTxQ() Note #9'. ********************************************************************************************************* */ static void NetTCP_TxConnClose (NET_TCP_CONN *pconn, NET_ERR *perr) { #if ((((NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) && \ (NET_CTR_CFG_ERR_EN == DEF_ENABLED)) || \ (NET_CTR_CFG_STAT_EN == DEF_ENABLED)) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif NET_TCP_SEG_SIZE seg_len; NET_BUF_SIZE data_len; NET_BUF_SIZE data_ix; NET_BUF *pseg_close; NET_BUF_HDR *pseg_close_hdr; NET_IP_ADDR src_addr; NET_IP_ADDR dest_addr; NET_TCP_PORT_NBR src_port; NET_TCP_PORT_NBR dest_port; NET_TCP_SEQ_NBR seq_nbr; CPU_INT16U flags_tcp; NET_ERR err; #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) /* ----------- VALIDATE TCP CONN STATE ------------ */ switch (pconn->ConnState) { case NET_TCP_CONN_STATE_FREE: NET_CTR_ERR_INC(NetTCP_ErrNotUsedCtr); *perr = NET_TCP_ERR_CONN_NOT_USED; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_CLOSED: case NET_TCP_CONN_STATE_LISTEN: case NET_TCP_CONN_STATE_SYNC_TXD: case NET_TCP_CONN_STATE_FIN_WAIT_1: case NET_TCP_CONN_STATE_FIN_WAIT_2: case NET_TCP_CONN_STATE_CLOSING: case NET_TCP_CONN_STATE_TIME_WAIT: case NET_TCP_CONN_STATE_LAST_ACK: case NET_TCP_CONN_STATE_CLOSING_DATA_AVAIL: NET_CTR_ERR_INC(NetTCP_ErrConnInvalidOpCtr); *perr = NET_TCP_ERR_INVALID_CONN_OP; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_SYNC_RXD: case NET_TCP_CONN_STATE_SYNC_RXD_PASSIVE: case NET_TCP_CONN_STATE_SYNC_RXD_ACTIVE: case NET_TCP_CONN_STATE_CONN: case NET_TCP_CONN_STATE_CLOSE_WAIT: break; case NET_TCP_CONN_STATE_NONE: default: NetTCP_ConnClose((NET_TCP_CONN *)pconn, (NET_BUF_HDR *)0, (CPU_BOOLEAN )DEF_YES, (NET_TCP_CLOSE_CODE)NET_TCP_CONN_CLOSE_ALL); NET_CTR_ERR_INC(NetTCP_ErrConnInvalidStateCtr); *perr = NET_TCP_ERR_INVALID_CONN_STATE; return; /* Prevent 'break NOT reachable' compiler warning. */ } #endif /*$PAGE*/ /* ---------- PREPARE TCP CONN CLOSE SEG ---------- */ seg_len = NET_TCP_SEG_LEN_CLOSE; data_len = NET_TCP_DATA_LEN_TX_CLOSE; if (pconn->TxQ_Tail != (NET_BUF *)0) { /* If tx Q NOT empty ... */ pseg_close = pconn->TxQ_Tail; /* ... update tx Q tail seg as close seg. */ pseg_close_hdr = &pseg_close->Hdr; pseg_close_hdr->DataLen += (NET_BUF_SIZE)data_len; pseg_close_hdr->TotLen += (NET_BUF_SIZE)data_len; pseg_close_hdr->TCP_SegLenInit += (CPU_INT16U )seg_len; pseg_close_hdr->TCP_SegLen += (CPU_INT16U )seg_len; pseg_close_hdr->TCP_SegLenData += (CPU_INT16U )data_len; DEF_BIT_SET(pseg_close_hdr->TCP_Flags, NET_TCP_FLAG_TX_CLOSE); } else { /* Else get/cfg close seg buf. */ /* Get buf. */ data_ix = NET_BUF_DATA_TX_IX; pseg_close = NetBuf_Get((NET_BUF_SIZE) data_len, (NET_BUF_SIZE) data_ix, (CPU_INT16U ) NET_BUF_FLAG_NONE, (NET_ERR *)&err); if ( err != NET_BUF_ERR_NONE) { *perr = NET_TCP_ERR_NONE_AVAIL; return; } /* --------------- PREPARE TCP HDR ---------------- */ /* Prepare seg addrs. */ NetTCP_TxConnPrepareSegAddrs((NET_TCP_CONN *) pconn, (CPU_INT08U *)&src_port, (CPU_INT08U *)&src_addr, (CPU_INT16U ) sizeof(src_port), (CPU_INT16U ) sizeof(src_addr), (CPU_INT08U *)&dest_port, (CPU_INT08U *)&dest_addr, (CPU_INT16U ) sizeof(dest_port), (CPU_INT16U ) sizeof(dest_addr), (NET_ERR *)&err); if ( err != NET_TCP_ERR_NONE) { /* See Note #3b1. */ *perr = NET_TCP_ERR_CONN_FAULT; NetBuf_Free(pseg_close); return; } /* Prepare TCP seq nbr(s). */ seq_nbr = pconn->TxSeqNbrNextQ; /* Prepare TCP tx flags (see Note #1b2C). */ flags_tcp = NET_TCP_FLAG_NONE | NET_TCP_FLAG_TX_ACK | NET_TCP_FLAG_TX_CLOSE; /* Init buf ctrls. */ pseg_close_hdr = &pseg_close->Hdr; pseg_close_hdr->DataIx = (CPU_INT16U )data_ix; pseg_close_hdr->DataLen = (NET_BUF_SIZE)data_len; pseg_close_hdr->TotLen = (NET_BUF_SIZE)pseg_close_hdr->DataLen; pseg_close_hdr->ProtocolHdrType = NET_PROTOCOL_TYPE_TCP; pseg_close_hdr->TCP_UDP_PortSrc = (NET_PORT_NBR)src_port; pseg_close_hdr->IP_AddrSrc = (CPU_INT32U )src_addr; pseg_close_hdr->TCP_UDP_PortDest = (NET_PORT_NBR)dest_port; pseg_close_hdr->IP_AddrDest = (CPU_INT32U )dest_addr; pseg_close_hdr->TCP_SegLenInit = (CPU_INT16U )seg_len; pseg_close_hdr->TCP_SegLen = (CPU_INT16U )pseg_close_hdr->TCP_SegLenInit; pseg_close_hdr->TCP_SegLenData = (CPU_INT16U )data_len; pseg_close_hdr->TCP_SegSync = (CPU_BOOLEAN )DEF_NO; pseg_close_hdr->TCP_SegClose = (CPU_BOOLEAN )DEF_YES; pseg_close_hdr->TCP_SegAck = (CPU_BOOLEAN )DEF_YES; pseg_close_hdr->TCP_SegReset = (CPU_BOOLEAN )DEF_NO; pseg_close_hdr->TCP_SeqNbrInit = (CPU_INT32U )seq_nbr; pseg_close_hdr->TCP_SeqNbr = (CPU_INT32U )pseg_close_hdr->TCP_SeqNbrInit; pseg_close_hdr->TCP_Flags = (CPU_INT16U )flags_tcp; } /*$PAGE*/ /* --------------- UPDATE TCP CONN ---------------- */ /* Q conn close seg to TCP tx Q : ... */ if (pconn->TxQ_Tail == (NET_BUF *)0) { /* ... if tx Q empty, add close seg to empty tx Q. */ pconn->TxQ_Head = (NET_BUF *)pseg_close; pconn->TxQ_Tail = (NET_BUF *)pseg_close; } /* Update TCP conn seq nbrs (see Note #2). */ pconn->TxSeqNbrNextQ += (NET_TCP_SEQ_NBR) seg_len; /* Update next tx Q seq nbr by close seg len. */ pconn->TxSeqNbrLast = (NET_TCP_SEQ_NBR) pconn->TxSeqNbrNextQ; pconn->TxSeqNbrClose = (NET_TCP_SEQ_NBR)(pconn->TxSeqNbrLast - seg_len); /* ------------ TX TCP CONN CLOSE SEG ------------- */ NetTCP_TxConnTxQ((NET_TCP_CONN *)pconn, (NET_BUF_HDR *)0, (NET_TCP_ACK_CODE )NET_TCP_CONN_TX_ACK_NONE, (CPU_BOOLEAN )DEF_NO, (NET_TCP_CLOSE_CODE)NET_TCP_CONN_CLOSE_ALL, (NET_ERR *)perr); switch (*perr) { case NET_TCP_ERR_NONE: case NET_TCP_ERR_CONN_ACK_NONE: case NET_TCP_ERR_CONN_ACK_INVALID: case NET_TCP_ERR_CONN_ACK_DLYD: case NET_TCP_ERR_CONN_ACK_PREVLY_TXD: case NET_ERR_TX: /* Ignore transitory tx err(s). */ break; case NET_ERR_INIT_INCOMPLETE: case NET_TCP_ERR_CONN_NOT_USED: case NET_TCP_ERR_CONN_CLOSE: case NET_TCP_ERR_CONN_FAULT: case NET_TCP_ERR_CONN_FAIL: case NET_TCP_ERR_INVALID_CONN_STATE: case NET_TCP_ERR_INVALID_CONN_OP: case NET_TCP_ERR_INVALID_LEN_SEG: case NET_TCP_ERR_NONE_AVAIL: case NET_TCP_ERR_TX: case NET_CONN_ERR_INVALID_FAMILY: case NET_CONN_ERR_INVALID_ADDR: case NET_CONN_ERR_INVALID_ADDR_LEN: default: return; /* Prevent 'break NOT reachable' compiler warning. */ } NET_CTR_STAT_INC(NetTCP_StatTxSegConnCloseCtr); *perr = NET_TCP_ERR_NONE; } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxConnAck() * * Description : (1) Prepare & transmit a TCP connection acknowledgement : * * (a) Validate TCP connection for TCP acknowledgement See Notes #2, #4, #5, & #6 * (b) Prepare TCP acknowledgement segment : * (1) Get buffer * (2) Prepare TCP segment : * (A) TCP segment addresses * (B) TCP segment sequence numbers See Notes #4a1D1a1, #4a2A1 * & #4a1D3b1B, * (C) TCP segment transmit flags : * (1) ACK * (D) TCP segment window size * (E) IP datagram parameters * (F) TCP segment packet buffer controls * (c) Transmit TCP connection acknowledgement * * * (2) NetTCP_TxConnAck() transmits TCP connection acknowledgements in response to certain * received TCP packets or TCP connection events (see Note #4). TCP acknowledgements * transmitted with other TCP controls &/or data SHOULD NOT be transmitted with * NetTCP_TxConnAck(). * * See also 'NetTCP_TxConnTxQ() Note #2'. * * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in various. * * pbuf_hdr Pointer to network buffer header that received TCP packet. * -------- Argument validated in NetTCP_Rx(). * * tx_ack_code Indicate whether & how to transmit a TCP acknowledgement segment : * * NET_TCP_CONN_TX_ACK_NONE Do NOT transmit a TCP acknowledgement segment. * NET_TCP_CONN_TX_ACK Transmit a TCP acknowledgement segment. * NET_TCP_CONN_TX_ACK_IMMED Transmit a TCP acknowledgement segment * immediately (see Note #4a5). * NET_TCP_CONN_TX_ACK_FAULT Transmit a TCP acknowledgement segment * immediately in response to an invalid * received TCP packet (see Note #4a1). * NET_TCP_CONN_TX_ACK_TIMEOUT Transmit a TCP acknowledgement segment * immediately in response to a delayed * acknowledgement timeout (see Note #6). * * close_code Select which close action(s) to perform; bit-field flags logically OR'd : * * NET_TCP_CONN_CLOSE_NONE Perform NO close actions. * NET_TCP_CONN_CLOSE_ALL Perform ALL close actions. * * NET_TCP_CONN_CLOSE_CONN_TX_RESET Perform close connection transmit reset. * NET_TCP_CONN_CLOSE_CONN_ALL Perform ALL connection close actions. * * NET_TCP_CONN_CLOSE_TMR_TX_IDLE Close transmit idle timer. * NET_TCP_CONN_CLOSE_TMR_TX_SILLY_WIN Close transmit silly window persist timer. * NET_TCP_CONN_CLOSE_TMR_TX_ZERO_WIN Close transmit zero window persist timer. * NET_TCP_CONN_CLOSE_TMR_TX_ACK_DLY Close transmit acknowledgement delay timer. * NET_TCP_CONN_CLOSE_TMR_RE_TX Close re-transmit timer. * NET_TCP_CONN_CLOSE_TMR_KEEP_ALIVE Close connection keep-alive timer. * NET_TCP_CONN_CLOSE_TMR_TIMEOUT Close connection timer. * NET_TCP_CONN_CLOSE_TMR_ALL Close ALL timers. * * See also 'TCP CONNECTION CLOSE/FREE CODE DEFINES'. * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_NONE TCP connection acknowledgement successfully * transmitted. * NET_TCP_ERR_CONN_ACK_NONE TCP connection acknowledgement NOT requested. * NET_TCP_ERR_CONN_ACK_DLYD TCP connection acknowledgement transmit delayed * (see Note #6). * NET_TCP_ERR_CONN_ACK_PREVLY_TXD TCP connection acknowledgement previously * transmitted for segment (see Note #7). * NET_TCP_ERR_CONN_ACK_INVALID TCP connection acknowledgement NOT valid for * current TCP connection state. * NET_TCP_ERR_CONN_NOT_USED TCP connection NOT currently used. * NET_TCP_ERR_CONN_FAULT TCP connection fault; connection(s) aborted. * NET_TCP_ERR_INVALID_CONN_STATE Invalid TCP connection state. * NET_TCP_ERR_INVALID_CONN_OP Invalid TCP connection operation. * NET_TCP_ERR_NONE_AVAIL Resources NOT available. * * -- RETURNED BY NetTCP_RxPktConnIsValidSeq() : -- * NET_TCP_ERR_INVALID_LEN_SEG Invalid TCP sequence-segment length. * * Return(s) : none. * * Caller(s) : various. *$PAGE* * Note(s) : (3) See the following RFC's for TCP acknowledgement generation summary : * * (a) RFC # 793, Section 3.9 'Event Processing : SEGMENT ARRIVES' * (b) RFC # 813, Section 5 * (c) RFC #1122, Section 4.2.3.2 * (d) RFC #2581, Sections 3.2 & 4.2 * (e) RFC Draft-IETF-TCPm-TCPSecure #00, Sections 2 & 3 * * (4) (a) TCP connection acknowledgements are transmitted when certain segments are received : * * (A) Some TCP transmit acknowledgement validation logic implemented in previous * functions; include duplicate validation logic in NetTCP_TxConnAck() only * if debug/validation code is enabled (i.e. NET_ERR_CFG_ARG_CHK_DBG_EN is * DEF_ENABLED in 'net_cfg.h'). * * (1) (A) RFC #793, Section 3.4 'Establishing a Connection : Reset Generation : 1' states that * "if [a] connection does not exist (CLOSED) then a reset is sent in response to any * incoming segment except another reset". * * Thus, ONLY resets are transmitted from the CLOSED state; never acknowledgements. * * (B) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : LISTEN [State]' allows * for the transmission of TCP connection reset & connection synchronization segments * ONLY; never acknowledgement-only segments (see also Note #2). * * (C) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : SYN-SENT [State] : * Check SYN Bit' states that "if ... our SYN has been ACKed ... change the * connection state to ESTABLISHED, form an ACK segment ... and send it". * * Thus, acknowledgements MUST be immediately transmitted in reply to all valid * synchronization segments received. * * (D) (1) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check Sequence * Number' states that for the "SYN-RECEIVED STATE, ESTABLISHED STATE, FIN-WAIT-1 * STATE, FIN-WAIT-2 STATE, CLOSE-WAIT STATE, CLOSING STATE, LAST-ACK STATE, TIME- * WAIT STATE ... if an incoming segment is not acceptable," ... * * (a) "an acknowledgment should be sent in reply" : * * (1) * * (b) (1) "(unless the RST bit is set)". * * (2) However, RFC Draft-IETF-TCPm-TCPSecure #00, Section 2.2 requires * transmitting a TCP acknowledgement upon receipt of certain TCP * segments regardless of whether "the RST bit" is set (see Notes * #4a1D2b1c & #4a1D2b3). * * See also Notes #4a1D2b & #4a1D3b. *$PAGE* * (2) (a) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check RST Bit' * states that "if the RST bit is set ... enter the CLOSED state". * * Note this check is placed following the sequence check to prevent a segment * from an old connection ... from causing an abort of the current connection". * * (b) (1) HOWEVER, RFC Draft-IETF-TCPm-TCPSecure #00, Section 2.2 amends the "handling * of a segment with the RST bit when in a synchronized state" to "provide some * protection against ... blind reset attack[s] using the RST bit" : * * (a) "If the RST bit is set and the sequence number is outside the * expected window, silently drop the segment." * * (b) "If the RST bit is exactly the next expected sequence number [sic], * reset the connection"; it is assumed that this should read "if the * RST bit is set and the sequence number is exactly the next expected * sequence number, reset the connection." * * (c) "If the RST bit is set and the sequence number does not exactly * match the next expected sequence value, yet is within the * acceptable window (RCV.NXT <= SEG.SEQ < RCV.NXT+RCV.WND) send * an acknowledgment." * * (2) ???? Although RFC Draft-IETF-TCPm-TCPSecure #00 explicitly states that this * amendment applies only to the "handling of a ... RST when in a synchronized * state", it is assumed that this should also apply to the SYN-RECEIVED state. * * (3) ???? In addition, RFC Draft-IETF-TCPm-TCPSecure #00 does NOT provide a * precedence priority for handling TCP segments received with BOTH the RST * & SYN bits set. * * ???? Therefore, since it does NOT seem reasonable to reset a TCP connection * due to a TCP segment that also attempted to synchronize the TCP connection, * it is assumed that the amended handling of the SYN bit should take precedence * over the amended handling of the RST bit. * * See Note #4a1D3b. * * (3) (a) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check SYN * Bit' states that for "SYN-RECEIVED [STATE], ESTABLISHED STATE, FIN-WAIT * STATE-1, FIN-WAIT STATE-2, CLOSE-WAIT STATE, CLOSING STATE, LAST-ACK * STATE, TIME-WAIT STATE ... [to next] check the SYN bit ... [and] if * the SYN is in the window it is an error, send a reset ... [and] enter * the CLOSED state ... * * [But] if the SYN is not in the window this step would not have been * reached and an ack would have been sent". * * (b) (1) HOWEVER, RFC Draft-IETF-TCPm-TCPSecure #00, Section 3.2 amends the * "handling of a segment with the SYN bit set in the synchronized state * ... [by] handling ... the SYN bit" as follows : * * (a) "If the SYN bit is set and the sequence number is outside the * expected window, send an ACK back to the peer." * * (b) "If the SYN bit is set and the sequence number is an exact match to * the next expected sequence (RCV.NXT == SEG.SEQ) then send an ACK * segment ... but ... subtract one from value being acknowledged." * * (c) "If the SYN bit is set and the sequence number is acceptable, i.e.: * (RCV.NXT <= SEG.SEQ < RCV.NXT+RCV.WND) then send an ACK segment." * * (2) ???? Although RFC Draft-IETF-TCPm-TCPSecure #00 explicitly states that * this amendment applies only to the "handling of a ... SYN ... in a * synchronized state", it is assumed that this should also apply to the * SYN-RECEIVED state. * * (4) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check ACK Field' * states that if in the "ESTABLISHED STATE" or any state with similar "processing * as for the ESTABLISHED STATE", that "if the ACK acks something not yet sent * (SEG.ACK > SND.NXT) then send an ACK". *$PAGE* * (2) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Process Segment Text', * states that in the "ESTABLISHED STATE, FIN-WAIT-1 STATE, FIN-WAIT-2 STATE ... when * ... TCP takes responsibility for ... data ... it must also acknowledge ... the data" : * * (A) "Send an acknowledgment of the form" : * * (1) * * (B) "This acknowledgment should be piggybacked on a segment being transmitted if * possible without incurring undue delay." * * See also Note #6. * * (3) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check FIN Bit' states * that "if the FIN bit is set, ... send an acknowledgment for the FIN". * * (4) RFC #813, Section 5 states that "the receiver of data will refrain from sending an * acknowledgement under certain circumstances ... The most obvious event on which to * depend is the arrival of another segment. So, if a segment arrives, postpone sending * an acknowledgement if ... the following conditions hold" : * * (A) "The push bit is not set in the segment, since it is a reasonable assumption * that there is more data coming in a subsequent segment." * * (1) However, if the PUSH bit is set in any received segment, an acknowledgement * should be immediately transmitted. * * See also Notes #4b1, #5b2, & #6. * * (5) (A) RFC #2581, Section 3.2 states that "a TCP receiver SHOULD send an immediate ACK" : * * (1) "When an out-of-order segment arrives. The purpose of this ACK is to * inform the sender that a segment was received out-of-order and which * sequence number is expected." * * (2) "In addition, ... when the incoming segment fills in all or part of a * gap in the sequence space. This will generate more timely information * for a sender recovering from a ... retransmission timeout ... [or] a * fast retransmit." * * (B) RFC #1122, Section 4.2.2.21 reiterates that "a TCP MAY send an ACK segment * acknowledging RCV.NXT when a valid segment arrives that is in the window * but not at the left window edge ... One reason for ACKing out-of-order * segments [is] to support ... 'fast retransmit'". * * (b) TCP connection acknowledgements are transmitted for certain TCP connection events : * * (1) RFC #813, Section 5 states that "the receiver of data will refrain from sending an * acknowledgement under certain circumstances ... Postpone sending an acknowledgement * if ... the following conditions hold" : * * (B) "There is no revised window information to be sent back." * * (1) However, if any local receive window size is available to update to the * remote host, an acknowledgement should be immediately transmitted. * * See also Note #4a4 & 'NetTCP_RxConnWinSizeHandler() Note #4'. *$PAGE* * (5) (a) Although NO RFC specifies whether an acknowledgement should or should NOT * be transmitted in response to a received acknowledgement-only segment, it * seems reasonable & is assumed that NO TCP connection acknowledgement should * be transmitted in response to any acknowledgement-only segment(s) received. * * (1) A received TCP segment is considered an acknowledgement-only segment if * the segment was received with zero segment length, i.e. NO received TCP * sequences : * * (A) NO synchronization or close controls) * (B) NO TCP data * * (b) (1) However, since acknowledgements are transmitted in response to various * invalid segments received, some acknowledgements MUST be transmitted * even for certain invalid acknowledgement-only segments. * * See also Note #4a1. * * (2) Also, acknowledgements SHOULD be transmitted for received segments with * the PUSH bit set, even for segments with NO received TCP data. * * See also Note #4a4. * * (3) (A) Also, RFC #1122, Section 4.2.2.17 states that the "probing of zero * (offered) windows MUST be supported. A TCP MAY keep its offered * receive window closed indefinitely. As long as the receiving TCP * continues to send acknowledgements in response to the probe segments". * * (B) In order to always acknowledge a remote host's probing of the local * TCP connection's receive window size : * * (1) Since probe segments : * * (a) may or may NOT contain data, ... * (b) sequence values may or may NOT be within the TCP connection's * current receive window; ... * * See also Note #4a1D1a1 & 'NetTCP_RxPktConnIsValidSeq() Note #1d3'. * * (2) ... acknowledgements SHOULD be transmitted in reply to ALL valid * & SOME invalid received segments from the remote host whenever * the TCP connection's local receive window is zero-sized. * * (6) (a) The following sections state that "a TCP SHOULD implement a delayed ACK" : * * (A) RFC # 813, Section 5 * (B) RFC #1122, Section 4.2.3.2 * (C) RFC #2581, Section 4.2 * * (1) (A) (1) RFC #1122, Section 4.2.3.2 states that "in a stream of full-sized segments * there SHOULD be an ACK for at least every second segment". * * (2) RFC #2581, Section 4.2 reiterates that "an ACK SHOULD be generated for at * least every second full-sized segment". * * (B) However, RFC #2581, Section 4.2 states that "an implementation is deemed to * comply with this requirement ... by acknowledging at least every second segment, * regardless of size". * * (2) (A) (1) RFC #813, Section 5 states that "the receiver of data will refrain from * sending an acknowledgement under certain circumstances, in which case it * must set a timer which will cause the acknowledgement to be sent later". * * (2) RFC #1122, Section 4.2.3.2 states that "an ACK should not be excessively * delayed; in particular, the delay MUST be less than 0.5 seconds". * * (3) RFC #2581, Section 4.2 reiterates that "an ACK ... MUST be generated * within 500 ms of the arrival of the first unacknowledged packet". * * (B) If the acknowledgement delay timeout is configured with a non-zero value, * at least one timer tick MUST be set to ensure that the non-zero timeout * is implemented. * * (b) However, if NO network timer is available to delay the acknowledgement, the TCP * connection acknowledgement SHOULD be immediately transmitted. *$PAGE* * (7) (a) (1) RFC #1122, Section 4.2.2.14 states that "a careless implementation can send * two or more acknowledgment segments per data segment received". * * (2) RFC #1122, Section 4.2.2.20 states that "in general, the processing of * received segments MUST be implemented to aggregate ACK segments whenever * possible. For example, if the TCP is processing a series of queued * segments, it MUST process them all before sending any ACK segments". * * (3) Thus, no more than one acknowledgement SHOULD be transmitted in response * to any received segment. * * (b) "When the application program subsequently consumes the data and increases * the available receive buffer space again, the receiver may send a second * acknowledgement segment to update the window at the sender." * * However, the application layer receives data from a TCP connection's application * receive queue asynchronously & irrespective of distinct or specific TCP packets * (see 'NetTCP_RxAppData() Note #5a'). Thus, any acknowledgement transmissions * triggered during asynchronous application receives CANNOT be associated with, & * thereby limited by, any specific received segments. * * (8) (a) (1) RFC #793, Sections 3.7 & 2.6 state that "TCP uses retransmission ... to ensure * delivery of every segment". * * (2) However, RFC #1122, Section 4.2.2.17 'DISCUSSION' states that "it is extremely * important to remember that ACK (acknowledgment) segments that contain no data * are not reliably transmitted by TCP". * * Therefore, it is assumed that TCP acknowledgement-ONLY segments should NOT be * queued for retransmission but SHOULD be silently discarded. * * (b) (1) The network buffer's reference counter is NOT incremented since the TCP layer * does NOT maintain a reference to any transmitted TCP acknowledgement segments. * * (2) Therefore, the network buffer MUST be freed by lower layer(s). * * See also 'NetTCP_TxConnReset() Note #6', * & 'NetTCP_TxConnProbe() Note #3'. * * (9) On ANY errors, network resources MUST be appropriately freed : * * (a) For any network resources NOT linked to the TCP connection, each network resource * MUST be freed by appropriate function(s). * * (10) #### IP options currently NOT implemented. ********************************************************************************************************* */ /*$PAGE*/ static void NetTCP_TxConnAck (NET_TCP_CONN *pconn, NET_BUF_HDR *pbuf_hdr, NET_TCP_ACK_CODE tx_ack_code, NET_TCP_CLOSE_CODE close_code, NET_ERR *perr) { #if (((NET_CTR_CFG_STAT_EN == DEF_ENABLED) || \ (NET_CTR_CFG_ERR_EN == DEF_ENABLED)) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) NET_TCP_SEQ_CODE seq_code; NET_TCP_RESET_CODE reset_code; #endif CPU_BOOLEAN tx_ack; CPU_BOOLEAN tmr_free; CPU_BOOLEAN push_avail; NET_TMR_TICK timeout_tick; NET_BUF_SIZE data_len; NET_BUF_SIZE data_ix; NET_BUF *pseg_ack; NET_BUF_HDR *pseg_ack_hdr; NET_IP_TOS TOS; NET_IP_TTL TTL; NET_IP_ADDR src_addr; NET_IP_ADDR dest_addr; NET_TCP_PORT_NBR src_port; NET_TCP_PORT_NBR dest_port; NET_TCP_SEQ_NBR seq_nbr; NET_TCP_SEQ_NBR ack_nbr; NET_TCP_SEQ_NBR ack_nbr_delta; NET_TCP_WIN_SIZE win_size; CPU_INT16U flags_tcp; CPU_INT16U flags_ip; NET_ERR err; /* ----------- VALIDATE TCP CONN TX ACK ----------- */ tx_ack = DEF_NO; tmr_free = DEF_YES; ack_nbr_delta = 0; switch (pconn->ConnState) { case NET_TCP_CONN_STATE_FREE: NET_CTR_ERR_INC(NetTCP_ErrNotUsedCtr); *perr = NET_TCP_ERR_CONN_NOT_USED; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_CLOSED: /* See Note #4a1A. */ case NET_TCP_CONN_STATE_LISTEN: /* See Note #4a1B. */ break; case NET_TCP_CONN_STATE_SYNC_TXD: /* See Note #4a1C. */ if (pbuf_hdr != (NET_BUF_HDR *)0) { #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) /* See Note #4aA. */ seq_code = NetTCP_RxPktConnIsValidSeq(pconn, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return; } if (seq_code == NET_TCP_CONN_RX_SEQ_SYNC) { /* If valid sync rx'd, ... */ tx_ack = DEF_YES; /* ... tx TCP conn ack. */ } #else tx_ack = DEF_YES; #endif if (pbuf_hdr->TCP_SegAckTxd != DEF_NO) { /* If prev'ly tx'd TCP conn ack for rx'd seg, .. */ *perr = NET_TCP_ERR_CONN_ACK_PREVLY_TXD; /* .. do NOT re-tx TCP conn ack (see Note #7a3). */ return; } } else { tx_ack = DEF_YES; } break; /*$PAGE*/ case NET_TCP_CONN_STATE_SYNC_RXD: case NET_TCP_CONN_STATE_SYNC_RXD_PASSIVE: case NET_TCP_CONN_STATE_SYNC_RXD_ACTIVE: case NET_TCP_CONN_STATE_CONN: case NET_TCP_CONN_STATE_FIN_WAIT_1: case NET_TCP_CONN_STATE_FIN_WAIT_2: case NET_TCP_CONN_STATE_CLOSING: case NET_TCP_CONN_STATE_TIME_WAIT: case NET_TCP_CONN_STATE_CLOSE_WAIT: case NET_TCP_CONN_STATE_LAST_ACK: if (pbuf_hdr != (NET_BUF_HDR *)0) { if (pbuf_hdr->TCP_SegAckTxd != DEF_NO) { /* If prev'ly tx'd TCP conn ack for rx'd seg, .. */ *perr = NET_TCP_ERR_CONN_ACK_PREVLY_TXD; /* .. do NOT re-tx TCP conn ack (see Note #7a3). */ return; } } switch (tx_ack_code) { case NET_TCP_CONN_TX_ACK_NONE: *perr = NET_TCP_ERR_CONN_ACK_NONE; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_TX_ACK: case NET_TCP_CONN_TX_ACK_IMMED: if (pbuf_hdr != (NET_BUF_HDR *)0) { push_avail = DEF_BIT_IS_SET(pbuf_hdr->TCP_HdrLen_Flags, NET_TCP_HDR_FLAG_PUSH); if (push_avail == DEF_YES) { /* If rx'd seg push'd & en'd, .. */ if (pconn->TxAckImmedRxdPushEn != DEF_DISABLED) { tx_ack = DEF_YES; /* .. tx TCP conn ack (see Note #4a4A1). */ break; } } if (pbuf_hdr->TCP_SegLen == 0) { /* If ack-ONLY seg (see Note #5a1); .. */ if (pconn->RxWinSizeActual > 0) { /* .. & local rx win > 0, .. */ *perr = NET_TCP_ERR_CONN_ACK_INVALID; return; /* .. do NOT tx ack (see Note #5a); .. */ } else { /* .. & local rx win = 0 (see Note #5b3B2), .. */ tx_ack = DEF_YES; /* .. tx TCP conn ack. */ break; } } } if (tx_ack_code == NET_TCP_CONN_TX_ACK_IMMED) { /* If immed ack req'd, ... */ tx_ack = DEF_YES; /* ... tx TCP conn ack. */ break; } if (pconn->TxAckDlyTimeout_ms == 0) { /* If ack dly timeout zero, ... */ tx_ack = DEF_YES; /* ... tx TCP conn ack. */ break; } pconn->TxAckDlyCnt++; /* If ack dly cnt >= th, ... */ if (pconn->TxAckDlyCnt >= NET_TCP_ACK_DLY_CNT_TH) { tx_ack = DEF_YES; /* ... tx TCP conn ack (see Note #6a1B). */ break; } if (pconn->TxAckDlyTmr != (NET_TMR *)0) { /* If ack dly tmr prev'ly started, ... */ *perr = NET_TCP_ERR_CONN_ACK_DLYD; /* ... continue ack dly (see Note #6a2A1). */ return; } timeout_tick = ((NET_TMR_TICK)pconn->TxAckDlyTimeout_ms * NET_TMR_TIME_TICK_PER_SEC) / DEF_TIME_NBR_mS_PER_SEC; if (timeout_tick < 1) { /* If < 1 tick, ... */ timeout_tick = 1; /* ... set at least 1 tick (see Note #6a2B). */ } pconn->TxAckDlyTmr = NetTmr_Get((void *) pconn, (CPU_FNCT_PTR) NetTCP_TxConnAckDlyTimeout, (NET_TMR_TICK) timeout_tick, (CPU_INT16U ) NET_TMR_FLAG_NONE, (NET_ERR *)&err); if ( err == NET_TMR_ERR_NONE) { /* If ack dly tmr avail, ... */ *perr = NET_TCP_ERR_CONN_ACK_DLYD; /* ... start ack dly (see Note #6a2A1). */ return; } else { /* Else tx TCP conn ack (see Note #6b). */ tx_ack = DEF_YES; } break; /*$PAGE*/ case NET_TCP_CONN_TX_ACK_FAULT: /* See Note #4a1D. */ if (pbuf_hdr != (NET_BUF_HDR *)0) { if (pbuf_hdr->TCP_SegSync == DEF_YES) { /* If sync rx'd (see Note #4a1D3b1), ... */ tx_ack = DEF_YES; /* ... tx TCP conn ack; ignore possible reset. */ /* If rx'd sync equal to next rx seq nbr, ... */ if (pbuf_hdr->TCP_SeqNbr == pconn->RxSeqNbrNext) { ack_nbr_delta = 1; /* ... sub 1 from ack (see Note #4a1D3b1B). */ } } else { #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) /* See Note #4aA. */ seq_code = NetTCP_RxPktConnIsValidSeq(pconn, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return; } reset_code = NetTCP_RxPktConnIsValidReset(pconn, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return; } switch (seq_code) { case NET_TCP_CONN_RX_SEQ_VALID: /* If valid seq rx'd .. */ /* .. but invalid reset rx'd, .. */ if (reset_code == NET_TCP_CONN_RX_RESET_INVALID) { tx_ack = DEF_YES; /* .. tx TCP conn ack (see Note #4a1D2b1c). */ } break; case NET_TCP_CONN_RX_SEQ_SYNC: /* If invalid sync rx'd, .. */ case NET_TCP_CONN_RX_SEQ_SYNC_INVALID: tx_ack = DEF_YES; /* .. tx TCP conn ack (see Note #4a1D3b1). */ break; case NET_TCP_CONN_RX_SEQ_NONE: case NET_TCP_CONN_RX_SEQ_INVALID: /* If invalid seq rx'd (see Note #4a1D1) .. */ default: /* .. & reset NOT rx'd (see Note #4a1D1b1), .. */ if (reset_code == NET_TCP_CONN_RX_RESET_NONE) { tx_ack = DEF_YES; /* .. tx TCP conn ack (see Note #4a1D1a). */ } break; } #else tx_ack = DEF_YES; #endif } } else { tx_ack = DEF_YES; } break; case NET_TCP_CONN_TX_ACK_TIMEOUT: tx_ack = DEF_YES; tmr_free = DEF_NO; break; default: break; } break; case NET_TCP_CONN_STATE_CLOSING_DATA_AVAIL: NET_CTR_ERR_INC(NetTCP_ErrConnInvalidOpCtr); *perr = NET_TCP_ERR_INVALID_CONN_OP; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_NONE: default: NetTCP_ConnClose(pconn, pbuf_hdr, DEF_YES, close_code); NET_CTR_ERR_INC(NetTCP_ErrConnInvalidStateCtr); *perr = NET_TCP_ERR_INVALID_CONN_STATE; return; /* Prevent 'break NOT reachable' compiler warning. */ } if (tx_ack != DEF_YES) { /* If NOT valid, abort tx TCP conn ack. */ *perr = NET_TCP_ERR_CONN_ACK_INVALID; return; } NetTCP_TxConnAckDlyReset(pconn, tmr_free); /* Reset ack dly ctrls. */ if (pbuf_hdr != (NET_BUF_HDR *)0) { pbuf_hdr->TCP_SegAckTxd = DEF_YES; /* Set ack tx'd for rx'd seg (see Note #7a). */ } /*$PAGE*/ /* ----------- PREPARE TCP CONN ACK SEG ----------- */ /* If valid, prepare & tx TCP conn ack. */ /* Get buf. */ data_len = NET_TCP_DATA_LEN_TX_ACK; data_ix = NET_BUF_DATA_TX_IX; pseg_ack = NetBuf_Get((NET_BUF_SIZE) data_len, (NET_BUF_SIZE) data_ix, (CPU_INT16U ) NET_BUF_FLAG_NONE, (NET_ERR *)&err); if ( err != NET_BUF_ERR_NONE) { *perr = NET_TCP_ERR_NONE_AVAIL; return; } /* --------------- PREPARE TCP HDR ---------------- */ /* Prepare seg addrs. */ if (pbuf_hdr != (NET_BUF_HDR *)0) { /* If TCP pkt rx'd, cfg TCP tx ... */ /* ... src addr from rx'd TCP pkt dest addr ... */ src_port = (NET_TCP_PORT_NBR)pbuf_hdr->TCP_UDP_PortDest; src_addr = (NET_IP_ADDR )pbuf_hdr->IP_AddrDest; /* .. & dest addr from rx'd TCP pkt src addr. */ dest_port = (NET_TCP_PORT_NBR)pbuf_hdr->TCP_UDP_PortSrc; dest_addr = (NET_IP_ADDR )pbuf_hdr->IP_AddrSrc; } else { /* Else cfg TCP tx pkt addrs from TCP conn addrs. */ NetTCP_TxConnPrepareSegAddrs((NET_TCP_CONN *) pconn, (CPU_INT08U *)&src_port, (CPU_INT08U *)&src_addr, (CPU_INT16U ) sizeof(src_port), (CPU_INT16U ) sizeof(src_addr), (CPU_INT08U *)&dest_port, (CPU_INT08U *)&dest_addr, (CPU_INT16U ) sizeof(dest_port), (CPU_INT16U ) sizeof(dest_addr), (NET_ERR *)&err); if ( err != NET_TCP_ERR_NONE) { /* See Note #9a. */ *perr = NET_TCP_ERR_CONN_FAULT; NetBuf_Free(pseg_ack); return; } } /* Prepare TCP seq nbrs (see Note #1b2B). */ seq_nbr = pconn->TxSeqNbrNext; ack_nbr = pconn->RxSeqNbrNext - ack_nbr_delta; /* Prepare TCP tx flags (see Note #1b2C). */ flags_tcp = NET_TCP_FLAG_NONE | NET_TCP_FLAG_TX_ACK; /* Prepare TCP win size. */ win_size = pconn->RxWinSizeActual; /* Prepare IP params. */ TOS = pconn->TxIP_TOS; TTL = pconn->TxIP_TTL; flags_ip = pconn->TxIP_Flags; /* Init buf ctrls. */ pseg_ack_hdr = &pseg_ack->Hdr; pseg_ack_hdr->DataIx = (CPU_INT16U )data_ix; pseg_ack_hdr->DataLen = (NET_BUF_SIZE)data_len; pseg_ack_hdr->TotLen = (NET_BUF_SIZE)pseg_ack_hdr->DataLen; pseg_ack_hdr->ProtocolHdrType = NET_PROTOCOL_TYPE_TCP; pseg_ack_hdr->TCP_SegSync = (CPU_BOOLEAN )DEF_NO; pseg_ack_hdr->TCP_SegClose = (CPU_BOOLEAN )DEF_NO; pseg_ack_hdr->TCP_SegAck = (CPU_BOOLEAN )DEF_YES; pseg_ack_hdr->TCP_SegReset = (CPU_BOOLEAN )DEF_NO; pseg_ack_hdr->TCP_Flags = flags_tcp; /*$PAGE*/ /* ------------- TX TCP CONN ACK SEG -------------- */ NetTCP_TxPktHandler((NET_BUF *) pseg_ack, (NET_IP_ADDR ) src_addr, (NET_TCP_PORT_NBR) src_port, (NET_IP_ADDR ) dest_addr, (NET_TCP_PORT_NBR) dest_port, (NET_TCP_SEQ_NBR ) seq_nbr, (NET_TCP_SEQ_NBR ) ack_nbr, (NET_TCP_WIN_SIZE) win_size, (NET_IP_TOS ) TOS, (NET_IP_TTL ) TTL, (CPU_INT16U ) flags_tcp, (CPU_INT16U ) flags_ip, (void *) 0, (void *) 0, /* See Note #10. */ (NET_ERR *)&err); /* Ignore transitory tx err(s). */ #if 0 /* Tx buf freed by lower layer(s) [see Note #8b2]. */ NetTCP_TxPktFree(pseg_ack); #endif NET_CTR_STAT_INC(NetTCP_StatTxSegConnAckCtr); *perr = NET_TCP_ERR_NONE; } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxConnAckDlyReset() * * Description : Reset a TCP connection's delayed acknowledgement controls. * * Argument(s) : pconn Pointer to TCP connection to reset delayed acknowledgement controls. * ----- Argument validated in NetTCP_TxConnAck(), * NetTCP_TxConnTxQ(), * NetTCP_TxConnReTxQ_Timeout(). * * tmr_free Indicate whether to free network timer : * * DEF_YES Free network timer for delayed acknowledgement. * DEF_NO Do NOT free network timer for delayed acknowledgement * [Freed by NetTmr_TaskHandler() * via NetTCP_TxConnAckDlyTimeout()]. * * Return(s) : none. * * Caller(s) : NetTCP_TxConnAck(), * NetTCP_TxConnTxQ(), * NetTCP_TxConnReTxQ(). * * Note(s) : (1) A TCP connection's delayed acknowledgement controls SHOULD be reset whenever : * * (a) TCP data segment(s) are transmitted * (b) TCP data segment(s) are re-transmitted * * ... since any transmitted or re-transmitted data segment(s) always transmit an * accompanying acknowledgement. ********************************************************************************************************* */ static void NetTCP_TxConnAckDlyReset (NET_TCP_CONN *pconn, CPU_BOOLEAN tmr_free) { pconn->TxAckDlyCnt = 0; /* Reset ack dly cnts ... */ if (pconn->TxAckDlyTmr != (NET_TMR *)0) { /* ... & free/clr ack dly tmr. */ if (tmr_free == DEF_YES) { NetTmr_Free(pconn->TxAckDlyTmr); } pconn->TxAckDlyTmr = (NET_TMR *)0; } } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxConnAckDlyTimeout() * * Description : (1) Handle TCP connection's delayed acknowledgement transmit for the following connected * states : * * (a) SYN-RECEIVED * (b) ESTABLISHED * (c) FIN-WAIT-1 * (d) FIN-WAIT-2 * (e) CLOSING * (f) TIME-WAIT * (g) CLOSE-WAIT * (h) LAST-ACK * * * Argument(s) : pconn_timeout Pointer to TCP connection to transmit delayed acknowledgement (see Note #2b). * * Return(s) : none. * * Caller(s) : Referenced in NetTCP_TxConnAck(). * * Note(s) : (2) Ideally, network timer expiration functions could be defined as '[(void) (OBJECT *)]' * type functions -- even though network timer API functions cast expiration functions * to generic 'CPU_FNCT_PTR' type (i.e. '[(void) (void *)]'). * * (a) (1) Unfortunately, ISO-IEC 9899-1999 ANSI-C, Section 6.3.2.3.7 states that "a * pointer to an object ... may be converted to a pointer to a different object * ... [but] if the resulting pointer is not correctly aligned ... the behavior * is undefined". * * And since compilers may NOT correctly convert 'void' pointers to non-'void' * pointer arguments, network timer expiration functions MUST avoid incorrect * pointer conversion behavior between 'void' pointer parameters & non-'void' * pointer arguments & therefore CANNOT be defined as '[(void) (OBJECT *)]'. * * (2) However, Section 6.3.2.3.1 states that "a pointer to void may be converted * to or from a pointer to any ... object ... A pointer to any ... object ... * may be converted to a pointer to void and back again; the result shall * compare equal to the original pointer". * * (b) Therefore, to correctly convert 'void' pointer objects back to appropriate * network object pointer objects, network timer expiration functions MUST : * * (1) Be defined as 'CPU_FNCT_PTR' type (i.e. '[(void) (void *)]'); & ... * (2) Explicitly cast 'void' pointer arguments to specific object pointers; ... * (A) ... in this case, a 'NET_TCP_CONN' pointer. * * See also 'net_tmr.c NetTmr_Get() Note #3'. * * (3) This function is a network timer expiration function : * * (a) (1) For the following connection timer(s) : * * (A) TCP connection transmit acknowledgement delay timer ('TxAckDlyTmr') * * (2) (A) Clear the timer pointer; ... * (1) Cleared prior to next handler function(s) as an extra precaution to * avoiding re-freeing the timer (see Note #3a2B). * * (B) but do NOT re-free the timer. * * (b) Do NOT set the following close timer flag(s) : * * (1) NET_TCP_CONN_CLOSE_TMR_TX_ACK_DLY ********************************************************************************************************* */ /*$PAGE*/ static void NetTCP_TxConnAckDlyTimeout (void *pconn_timeout) { #if ((NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) && \ (NET_CTR_CFG_ERR_EN == DEF_ENABLED) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif NET_TCP_CONN *pconn; NET_TCP_CLOSE_CODE close_code; NET_ERR err; pconn = (NET_TCP_CONN *)pconn_timeout; /* See Note #2b2A. */ close_code = NET_TCP_CONN_CLOSE_ALL; DEF_BIT_CLR(close_code, NET_TCP_CONN_CLOSE_TMR_TX_ACK_DLY); /* See Note #3b1. */ #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) /* -------------- VALIDATE PTR ---------------- */ if (pconn == (NET_TCP_CONN *)0) { NET_CTR_ERR_INC(NetTCP_ErrNullPtrCtr); return; } /* -------------- VALIDATE TYPE --------------- */ if (pconn->Type != NET_TCP_TYPE_CONN) { NET_CTR_ERR_INC(NetTCP_ErrConnInvalidTypeCtr); return; } switch (pconn->ConnState) { case NET_TCP_CONN_STATE_FREE: NET_CTR_ERR_INC(NetTCP_ErrNotUsedCtr); return; /* Prevent 'break NOT reachable' warning. */ case NET_TCP_CONN_STATE_CLOSED: case NET_TCP_CONN_STATE_LISTEN: case NET_TCP_CONN_STATE_SYNC_TXD: case NET_TCP_CONN_STATE_CLOSING_DATA_AVAIL: NET_CTR_ERR_INC(NetTCP_ErrConnInvalidOpCtr); return; /* Prevent 'break NOT reachable' warning. */ case NET_TCP_CONN_STATE_SYNC_RXD: /* See Note #1. */ case NET_TCP_CONN_STATE_SYNC_RXD_PASSIVE: case NET_TCP_CONN_STATE_SYNC_RXD_ACTIVE: case NET_TCP_CONN_STATE_CONN: case NET_TCP_CONN_STATE_FIN_WAIT_1: case NET_TCP_CONN_STATE_FIN_WAIT_2: case NET_TCP_CONN_STATE_CLOSING: case NET_TCP_CONN_STATE_TIME_WAIT: case NET_TCP_CONN_STATE_CLOSE_WAIT: case NET_TCP_CONN_STATE_LAST_ACK: break; case NET_TCP_CONN_STATE_NONE: default: NetTCP_ConnClose((NET_TCP_CONN *)pconn, (NET_BUF_HDR *)0, (CPU_BOOLEAN )DEF_YES, (NET_TCP_CLOSE_CODE)close_code); NET_CTR_ERR_INC(NetTCP_ErrConnInvalidStateCtr); return; /* Prevent 'break NOT reachable' warning. */ } #endif /* ------ HANDLE TCP CONN TX ACK TIMEOUT ------ */ pconn->TxAckDlyTmr = (NET_TMR *)0; /* Clr tx ack dly tmr (see Note #3a2A1). */ NetTCP_TxConnAck((NET_TCP_CONN *) pconn, (NET_BUF_HDR *) 0, (NET_TCP_ACK_CODE ) NET_TCP_CONN_TX_ACK_TIMEOUT, (NET_TCP_CLOSE_CODE) close_code, (NET_ERR *)&err); /* Ignore transitory tx err(s). */ } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxConnReset() * * Description : (1) Prepare & transmit a TCP connection reset : * * (a) Validate received TCP packet for TCP reset See Note #4 * (b) Validate TCP connection state for TCP reset See Note #3 * (c) Prepare TCP reset segment : * (1) Get buffer * (2) Prepare TCP segment : * (A) TCP segment addresses * (B) TCP segment sequence numbers See Note #5 * (C) TCP segment transmit flags : * (1) RESET * (2) ACK See Note #5a2A * (D) TCP segment window size * (E) TCP segment packet buffer controls * (d) Transmit TCP connection reset * * * Argument(s) : pconn Pointer to a TCP connection. * * pbuf_hdr Pointer to network buffer header that received TCP packet, if available. * * tx_reset_code Indicate whether & how to transmit a TCP reset segment : * * NET_TCP_CONN_TX_RESET Transmit a TCP reset segment, if permitted * (see Notes #3 & #4). * NET_TCP_CONN_TX_RESET_FAULT Transmit a TCP reset segment immediately for * a closing TCP connection fault. * * close_code Select which close action(s) to perform; bit-field flags logically OR'd : * * NET_TCP_CONN_CLOSE_NONE Perform NO close actions. * NET_TCP_CONN_CLOSE_ALL Perform ALL close actions. * * NET_TCP_CONN_CLOSE_CONN_TX_RESET Perform close connection transmit reset. * NET_TCP_CONN_CLOSE_CONN_ALL Perform ALL connection close actions. * * NET_TCP_CONN_CLOSE_TMR_TX_IDLE Close transmit idle timer. * NET_TCP_CONN_CLOSE_TMR_TX_SILLY_WIN Close transmit silly window persist timer. * NET_TCP_CONN_CLOSE_TMR_TX_ZERO_WIN Close transmit zero window persist timer. * NET_TCP_CONN_CLOSE_TMR_TX_ACK_DLY Close transmit acknowledgement delay timer. * NET_TCP_CONN_CLOSE_TMR_RE_TX Close re-transmit timer. * NET_TCP_CONN_CLOSE_TMR_KEEP_ALIVE Close connection keep-alive timer. * NET_TCP_CONN_CLOSE_TMR_TIMEOUT Close connection timer. * NET_TCP_CONN_CLOSE_TMR_ALL Close ALL timers. * * See also 'TCP CONNECTION CLOSE/FREE CODE DEFINES'. * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_NONE TCP connection reset successfully transmitted. * NET_TCP_ERR_CONN_RESET_INVALID TCP connection reset NOT valid for current TCP * connection state. * NET_TCP_ERR_CONN_NOT_USED TCP connection NOT currently used. * NET_TCP_ERR_CONN_FAULT TCP connection fault; connection(s) aborted. * NET_TCP_ERR_INVALID_CONN_STATE Invalid TCP connection state. * NET_TCP_ERR_NONE_AVAIL Resources NOT available. * * - RETURNED BY NetTCP_RxPktConnIsValidSeq() : - * NET_TCP_ERR_INVALID_LEN_SEG Invalid TCP sequence-segment length. * * Return(s) : none. * * Caller(s) : various. *$PAGE* * Note(s) : (2) See the following RFC's for TCP reset generation summary : * * (a) RFC #793, Section 3.4 'Establishing a Connection : Reset Generation' * (b) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES' * (c) RFC Draft-IETF-TCPm-TCPSecure #00, Section 3 * * (3) TCP connection resets are transmitted : * * (a) When certain invalid segments are received : * * (A) Some TCP transmit reset validation logic implemented in previous functions; * include duplicate validation logic in NetTCP_TxConnReset() only if debug/ * validation code is enabled (i.e. NET_ERR_CFG_ARG_CHK_DBG_EN is DEF_ENABLED * in 'net_cfg.h'). * * (1) (A) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : CLOSED [State]' * states that if the current TCP connection "state is CLOSED", then ... * * (1) "An incoming segment containing a RST is discarded". * (2) "An incoming segment NOT containing a RST causes a RST to be sent". * * (B) RFC #793, Section 3.4 'Establishing a Connection : Reset Generation : 1' * reiterates that "if [a] connection does not exist (CLOSED) then a reset is * sent in response to any incoming segment except another reset". * * (2) (A) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : LISTEN [State] : * Check for ACK' states that "any acknowledgment is bad if it arrives on a * connection still in the LISTEN state. An acceptable reset segment should * be formed for any arriving ACK-bearing segment". * * (B) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : LISTEN [State] : * Check for SYN' also states that "if the security/compartment on the incoming * segment does not exactly match the security/compartment in the TCB ... [or] * if the SEG.PRC is ... not allowed [then] send a reset". * * However, these checks for invalid connection permissions are NOT necessary * since TCP security & precedence NOT supported (see 'net_tcp.c Note #1a'). * * (3) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : SYN-SENT [State] : * Check ACK Bit' states that "if the state is SYN-SENT" & "the ACK bit is set" & * the incoming segment's "SEG.ACK =< ISS, or SEG.ACK > SND.NXT, send a reset". *$PAGE* * (4) (A) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check Sequence * Number' states that for the "SYN-RECEIVED STATE, ESTABLISHED STATE, FIN- * WAIT-1 STATE, FIN-WAIT-2 STATE, CLOSE-WAIT STATE, CLOSING STATE, LAST-ACK * STATE, TIME-WAIT STATE", to "first check [the] sequence number ... [and if * it] is not acceptable, an acknowledgment should be sent in reply ... unless * the RST bit is set". * * (B) (1) (a) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check SYN * Bit' states that for "SYN-RECEIVED [STATE], ESTABLISHED STATE, FIN-WAIT * STATE-1, FIN-WAIT STATE-2, CLOSE-WAIT STATE, CLOSING STATE, LAST-ACK * STATE, TIME-WAIT STATE ... [to next] check the SYN bit ... [and] if * the SYN is in the window it is an error, send a reset, any outstanding * RECEIVEs and SEND[s] should receive 'reset' responses, all segment * queues should be flushed, the user should also receive an unsolicited * general 'connection reset' signal[, and] enter the CLOSED state". * * (b) But "if the SYN is not in the window this step would not have been * reached and an ack would have been sent" (see Note #3a4A). * * (2) (a) HOWEVER, RFC Draft-IETF-TCPm-TCPSecure #00, Section 3.2 amends the * "handling of a segment with the SYN bit set in the synchronized state * ... [by] handling ... the SYN bit" as follows : * * (a) "If the SYN bit is set and the sequence number is outside the * expected window, send an ACK back to the peer." * * (b) "If the SYN bit is set and the sequence number is an exact * match to the next expected sequence (RCV.NXT == SEG.SEQ) * then send an ACK segment ... but ... subtract one from * value being acknowledged." * * (c) "If the SYN bit is set and the sequence number is acceptable, * i.e.: (RCV.NXT <= SEG.SEQ <= RCV.NXT+RCV.WND) then send an * ACK segment." * * (b) ???? Although RFC Draft-IETF-TCPm-TCPSecure #00 explicitly states that * this amendment applies only to the "handling of a ... SYN ... in a * synchronized state", it is assumed that this should also apply to the * SYN-RECEIVED state. * * (C) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check ACK Field' * states to next "check the ACK Field" : * * (1) If in the "SYN-RECEIVED STATE ... [and] if the segment acknowledgment * is not acceptable, form a reset segment". * * (2) If in the "ESTABLISHED STATE" or any state with similar "processing as * for the ESTABLISHED STATE", that "if the ACK acks something not yet sent * (SEG.ACK > SND.NXT) then send an ACK" but "if the ACK is a duplicate * (SEG.ACK =< SND.UNA), it can be ignored". * * (D) RFC #793, Section 3.4 'Establishing a Connection : Reset Generation : 3' * reiterates that for any TCP "connection ... in a synchronized state * (ESTABLISHED, FIN-WAIT-1, FIN-WAIT-2, CLOSE-WAIT, CLOSING, LAST-ACK, * TIME-WAIT), any unacceptable segment (out of window sequence number * or unacceptible [sic] acknowledgment number) must elicit only an empty * acknowledgment segment containing the current send-sequence number and * an acknowledgment indicating the next sequence number expected to be * received". * * (b) (1) When TCP connection fault-closes from the following synchronization/connected/ * closing states : * * (A) SYN-RECEIVED * (B) SYN-SENT * (C) ESTABLISHED * (D) FIN-WAIT-1 * (E) FIN-WAIT-2 * (F) CLOSING * (G) TIME_WAIT * (H) CLOSE-WAIT * (I) LAST-ACK * * (2) Although NO RFC directly states to transmit a TCP reset segment when a TCP * connection fault-closes, it is inferred & seems reasonable that a TCP reset * segment SHOULD be transmitted whenever a TCP connection closes abnormally. *$PAGE* * (4) The following sections reiterate the generalization that "a reset is sent in response * to any [unacceptable segment] ... EXCEPT* another reset"; also "send a reset (UNLESS* * the RST bit is set, if so drop the segment)" : [*emphasis added] * * (a) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : LISTEN [State] : * Check for RST' * (b) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : SYN-SENT [State] : * Check ACK Bit' * (c) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check ACK Field : * SYN-RECEIVED STATE' (see Note #4A) * * (A) This confirms that the received segment does NOT contain a TCP reset control * since it follows RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : * Check RST Bit'. * * (5) (a) (1) The following sections ... : * * (A) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : CLOSED [State]' * (B) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : LISTEN [State]' * (1) Amended by RFC #1122, Section 4.2.2.20.(b) * (C) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : SYN-SENT [State]' * (D) RFC #793, Section 3.9 'Event Processing : SEGMENT ARRIVES : Check ACK Field : * SYN-RECEIVED STATE' * * (2) ... generalize that "the acknowledgment and sequence field values [for the reset * segment to transmit] are selected to make the reset sequence acceptable to the * TCP that sent the offending segment" (see Note #3a) : * * (A) "If the ACK bit is off, sequence number zero is used, * * " * * (B) "If the ACK bit is on, * * " * * (b) However, NO RFC specifies the sequence & acknowledgement numbers to use when * trasmitting a reset segment for a TCP connection that closes due to any fault * condition(s) [see Note #3b]. * * (1) #### Therefore, TCP transmit reset segments for fault-closing TCP connections * should be prepared as follows : * * (A) With the following TCP sequence numbers : * * (1) TCP_SeqNbr = TxSeqNbrUnAckd * * (2) TCP_AckNbr = RxSeqNbrNext * * where * * TCP_SeqNbr TCP transmit reset segment sequence number * TCP_AckNbr TCP transmit reset segment acknowledgement number * TxSeqNbrUnAckd TCP connection's currently unacknowledged transmit * sequence number * RxSeqNbrNext TCP connection's currently expected next receive * sequence number * * (B) With the following TCP segment header flags set : * * (1) RESET * * (2) This TCP transmit reset segment format complies with TCP connection received * reset segment handling as specified in RFC #793, Section 3.9 'Event Processing : * SEGMENT ARRIVES'. * * See also 'NetTCP_RxPktConnIsValidReset() Notes #2a2, 2a3, 2a4A1, & 2a4B1'. *$PAGE* * (6) (a) RFC #793, Sections 3.7 & 2.6 state that "TCP uses retransmission ... to ensure * delivery of every segment". * * However, NO RFC specifies whether TCP connection reset segments should be queued * for retransmission. Therefore, it is assumed that ALL TCP connection reset * segments SHOULD NOT be queued for retransmission but SHOULD be silently discarded. * * (b) (1) The network buffer's reference counter is NOT incremented since the TCP layer * does NOT maintain a reference to any transmitted TCP connection reset segments. * * (2) Therefore, the network buffer MUST be freed by lower layer(s). * * See also 'NetTCP_TxConnAck() Note #8', * & 'NetTCP_TxConnProbe() Note #3'. * * (7) On ANY errors : * * (a) Network resources MUST be appropriately freed : * * (1) For any network resources NOT linked to the TCP connection, each network resource * MUST be freed by appropriate function(s). * * (b) TCP connection MUST NOT be re-closed. * * See also 'NetTCP_ConnClose() Note #5'. ********************************************************************************************************* */ /*$PAGE*/ static void NetTCP_TxConnReset (NET_TCP_CONN *pconn, NET_BUF_HDR *pbuf_hdr, NET_TCP_RESET_CODE tx_reset_code, NET_TCP_CLOSE_CODE close_code, NET_ERR *perr) { #if (((NET_CTR_CFG_ERR_EN == DEF_ENABLED) || \ (NET_CTR_CFG_STAT_EN == DEF_ENABLED)) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) NET_TCP_ACK_CODE ack_code; NET_TCP_SEQ_CODE seq_code; #endif CPU_BOOLEAN tx_reset; NET_BUF_SIZE data_len; NET_BUF_SIZE data_ix; NET_BUF *pseg_reset; NET_BUF_HDR *pseg_reset_hdr; NET_IP_ADDR src_addr; NET_IP_ADDR dest_addr; NET_TCP_PORT_NBR src_port; NET_TCP_PORT_NBR dest_port; NET_TCP_SEQ_NBR seq_nbr; NET_TCP_SEQ_NBR ack_nbr; NET_TCP_WIN_SIZE win_size; CPU_INT16U flags_tcp; NET_ERR err; /* ------------ VALIDATE RX'D TCP PKT ------------- */ if (pbuf_hdr != (NET_BUF_HDR *)0) { if (pbuf_hdr->TCP_SegReset != DEF_NO) { /* If TCP reset pkt rx'd, ... */ *perr = NET_TCP_ERR_CONN_RESET_INVALID; /* ... do NOT tx TCP conn reset (see Note #4). */ return; } } /* ----------- VALIDATE TCP CONN STATE ------------ */ tx_reset = DEF_NO; if (pconn != (NET_TCP_CONN *)0) { switch (pconn->ConnState) { case NET_TCP_CONN_STATE_FREE: NET_CTR_ERR_INC(NetTCP_ErrNotUsedCtr); *perr = NET_TCP_ERR_CONN_NOT_USED; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_CLOSED: /* See Note #3a1. */ tx_reset = DEF_YES; break; case NET_TCP_CONN_STATE_LISTEN: /* See Note #3a2. */ #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) /* See Note #3aA. */ if (pbuf_hdr != (NET_BUF_HDR *)0) { /* If seg rx'd ... */ ack_code = NetTCP_RxPktConnIsValidAck(pconn, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return; } if (ack_code != NET_TCP_CONN_RX_ACK_NONE) { /* ... with ANY (invalid) ack, ... */ tx_reset = DEF_YES; /* ... tx TCP conn reset (see Note #3a2A). */ } } #else tx_reset = DEF_YES; #endif break; /*$PAGE*/ case NET_TCP_CONN_STATE_SYNC_TXD: /* See Note #3a3. */ #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) /* See Note #3aA. */ switch (tx_reset_code) { case NET_TCP_CONN_TX_RESET: default: if (pbuf_hdr != (NET_BUF_HDR *)0) { /* If seg rx'd ... */ ack_code = NetTCP_RxPktConnIsValidAck(pconn, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return; } /* ... with invalid ack, ... */ if (ack_code == NET_TCP_CONN_RX_ACK_INVALID) { tx_reset = DEF_YES; /* ... tx TCP conn reset (see Note #3a3). */ } } else { /* Else no seg rx'd, ... */ tx_reset = DEF_YES; /* ... tx TCP conn reset (see Note #3b1B). */ } break; case NET_TCP_CONN_TX_RESET_FAULT: tx_reset = DEF_YES; break; } #else (void)&tx_reset_code; /* Prevent compiler warning. */ tx_reset = DEF_YES; #endif break; case NET_TCP_CONN_STATE_SYNC_RXD: /* See Note #3a4. */ case NET_TCP_CONN_STATE_SYNC_RXD_PASSIVE: case NET_TCP_CONN_STATE_SYNC_RXD_ACTIVE: #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) /* See Note #3aA. */ switch (tx_reset_code) { case NET_TCP_CONN_TX_RESET: default: if (pbuf_hdr != (NET_BUF_HDR *)0) { /* If seg rx'd ... */ seq_code = NetTCP_RxPktConnIsValidSeq(pconn, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return; } /* ... with invalid seq/sync, ... */ if (seq_code != NET_TCP_CONN_RX_SEQ_VALID) { /* ... tx TCP conn ack (see Notes #3a4A & #3a4B2); */ NetTCP_TxConnAck(pconn, pbuf_hdr, NET_TCP_CONN_TX_ACK_FAULT, NET_TCP_CONN_CLOSE_ALL, &err); *perr = NET_TCP_ERR_CONN_RESET_INVALID; return; } ack_code = NetTCP_RxPktConnIsValidAck(pconn, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return; } /* ... without valid ack, ... */ if (ack_code != NET_TCP_CONN_RX_ACK_VALID) { tx_reset = DEF_YES; /* ... tx TCP conn reset (see Note #3a4C1). */ } } else { /* Else no seg rx'd, ... */ tx_reset = DEF_YES; /* ... tx TCP conn reset (see Note #3b1A). */ } break; case NET_TCP_CONN_TX_RESET_FAULT: tx_reset = DEF_YES; break; } #else (void)&tx_reset_code; /* Prevent compiler warning. */ tx_reset = DEF_YES; #endif break; /*$PAGE*/ case NET_TCP_CONN_STATE_CONN: /* See Note #3a4. */ case NET_TCP_CONN_STATE_FIN_WAIT_1: case NET_TCP_CONN_STATE_FIN_WAIT_2: case NET_TCP_CONN_STATE_CLOSING: case NET_TCP_CONN_STATE_TIME_WAIT: case NET_TCP_CONN_STATE_CLOSE_WAIT: case NET_TCP_CONN_STATE_LAST_ACK: #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) /* See Note #3aA. */ switch (tx_reset_code) { case NET_TCP_CONN_TX_RESET: default: if (pbuf_hdr != (NET_BUF_HDR *)0) { /* If seg rx'd ... */ seq_code = NetTCP_RxPktConnIsValidSeq(pconn, pbuf_hdr, perr); if (*perr != NET_TCP_ERR_NONE) { return; } /* ... with invalid seq/sync, ... */ if (seq_code != NET_TCP_CONN_RX_SEQ_VALID) { /* ... tx TCP conn ack (see Notes #3a4A & #3a4B2). */ NetTCP_TxConnAck(pconn, pbuf_hdr, NET_TCP_CONN_TX_ACK_FAULT, NET_TCP_CONN_CLOSE_ALL, &err); *perr = NET_TCP_ERR_CONN_RESET_INVALID; return; } } else { /* Else no seg rx'd, ... */ tx_reset = DEF_YES; /* ... tx TCP conn reset (see Notes #3b1C - #3b1I).*/ } break; case NET_TCP_CONN_TX_RESET_FAULT: tx_reset = DEF_YES; break; } #else (void)&tx_reset_code; /* Prevent compiler warning. */ tx_reset = DEF_YES; #endif break; case NET_TCP_CONN_STATE_CLOSING_DATA_AVAIL: tx_reset = DEF_YES; break; case NET_TCP_CONN_STATE_NONE: default: if (close_code != NET_TCP_CONN_CLOSE_NONE) { /* If tx reset NOT req'd by TCP conn close fnct(s), */ /* ... close TCP conn (see Note #7b). */ DEF_BIT_CLR(close_code, NET_TCP_CONN_CLOSE_CONN_TX_RESET); NetTCP_ConnClose((NET_TCP_CONN *)pconn, (NET_BUF_HDR *)0, (CPU_BOOLEAN )DEF_YES, (NET_TCP_CLOSE_CODE)close_code); } NET_CTR_ERR_INC(NetTCP_ErrConnInvalidStateCtr); *perr = NET_TCP_ERR_INVALID_CONN_STATE; return; /* Prevent 'break NOT reachable' compiler warning. */ } } else { /* If NO demux'd TCP conn avail, handle as CLOSED. */ if (pbuf_hdr != (NET_BUF_HDR *)0) { /* If rx'd pkt avail, ... */ tx_reset = DEF_YES; /* ... tx reset. */ } } if (tx_reset != DEF_YES) { /* If NOT valid, abort tx TCP conn reset. */ *perr = NET_TCP_ERR_CONN_RESET_INVALID; return; } /*$PAGE*/ /* ---------- PREPARE TCP CONN RESET SEG ---------- */ /* If valid, prepare & tx TCP conn reset. */ /* Get buf. */ data_len = NET_TCP_DATA_LEN_TX_RESET; data_ix = NET_BUF_DATA_TX_IX; pseg_reset = NetBuf_Get((NET_BUF_SIZE) data_len, (NET_BUF_SIZE) data_ix, (CPU_INT16U ) NET_BUF_FLAG_NONE, (NET_ERR *)&err); if ( err != NET_BUF_ERR_NONE) { *perr = NET_TCP_ERR_NONE_AVAIL; return; } /* --------------- PREPARE TCP HDR ---------------- */ /* Prepare seg addrs. */ if (pbuf_hdr != (NET_BUF_HDR *)0) { /* If TCP pkt rx'd, cfg TCP tx ... */ /* ... src addr from rx'd TCP pkt dest addr ... */ src_port = (NET_TCP_PORT_NBR)pbuf_hdr->TCP_UDP_PortDest; src_addr = (NET_IP_ADDR )pbuf_hdr->IP_AddrDest; /* .. & dest addr from rx'd TCP pkt src addr. */ dest_port = (NET_TCP_PORT_NBR)pbuf_hdr->TCP_UDP_PortSrc; dest_addr = (NET_IP_ADDR )pbuf_hdr->IP_AddrSrc; } else { /* Else cfg TCP tx pkt addrs from TCP conn addrs. */ NetTCP_TxConnPrepareSegAddrs((NET_TCP_CONN *) pconn, (CPU_INT08U *)&src_port, (CPU_INT08U *)&src_addr, (CPU_INT16U ) sizeof(src_port), (CPU_INT16U ) sizeof(src_addr), (CPU_INT08U *)&dest_port, (CPU_INT08U *)&dest_addr, (CPU_INT16U ) sizeof(dest_port), (CPU_INT16U ) sizeof(dest_addr), (NET_ERR *)&err); if ( err != NET_TCP_ERR_NONE) { /* See Note #7a. */ *perr = NET_TCP_ERR_CONN_FAULT; NetBuf_Free(pseg_reset); return; } } /* Prepare TCP tx flags (see Note #1c2C). */ flags_tcp = NET_TCP_FLAG_NONE | NET_TCP_FLAG_TX_RESET; /* Prepare TCP seq nbrs (see Note #5). */ if (pbuf_hdr != (NET_BUF_HDR *)0) { if (pbuf_hdr->TCP_SegAck != DEF_NO) { /* If TCP ack rx'd, ... (see Note #5a2B) */ seq_nbr = pbuf_hdr->TCP_AckNbr; /* .. tx seq = ack ... */ ack_nbr = NET_TCP_ACK_NBR_NONE; /* .. & ack = none. */ } else { /* Otherwise, ... (see Note #5a2A) */ seq_nbr = NET_TCP_SEQ_NBR_NONE; /* .. tx seq = 0 (none) ... */ ack_nbr = pbuf_hdr->TCP_SeqNbr /* .. & ack = seg seq ... */ + pbuf_hdr->TCP_SegLen; /* .. + seg len. */ DEF_BIT_SET(flags_tcp, NET_TCP_FLAG_TX_ACK); } } else { /* Else no seg rx'd, ... (see Note #5b1A) */ seq_nbr = pconn->TxSeqNbrUnAckd; /* .. tx seq = tx unack'd ... */ ack_nbr = pconn->RxSeqNbrNext; /* .. & ack = rx next. */ } /* Prepare TCP win size. */ win_size = NET_TCP_WIN_SIZE_NONE; /* Init buf ctrls. */ pseg_reset_hdr = &pseg_reset->Hdr; pseg_reset_hdr->DataIx = (CPU_INT16U )data_ix; pseg_reset_hdr->DataLen = (NET_BUF_SIZE)data_len; pseg_reset_hdr->TotLen = (NET_BUF_SIZE)pseg_reset_hdr->DataLen; pseg_reset_hdr->ProtocolHdrType = NET_PROTOCOL_TYPE_TCP; pseg_reset_hdr->TCP_SegSync = (CPU_BOOLEAN )DEF_NO; pseg_reset_hdr->TCP_SegClose = (CPU_BOOLEAN )DEF_NO; pseg_reset_hdr->TCP_SegAck = (CPU_BOOLEAN )DEF_BIT_IS_SET(flags_tcp, NET_TCP_FLAG_TX_ACK); pseg_reset_hdr->TCP_SegReset = (CPU_BOOLEAN )DEF_YES; pseg_reset_hdr->TCP_Flags = flags_tcp; /*$PAGE*/ /* ------------ TX TCP CONN RESET SEG ------------- */ NetTCP_TxPktHandler((NET_BUF *) pseg_reset, (NET_IP_ADDR ) src_addr, (NET_TCP_PORT_NBR) src_port, (NET_IP_ADDR ) dest_addr, (NET_TCP_PORT_NBR) dest_port, (NET_TCP_SEQ_NBR ) seq_nbr, (NET_TCP_SEQ_NBR ) ack_nbr, (NET_TCP_WIN_SIZE) win_size, (NET_IP_TOS ) NET_IP_TOS_DFLT, (NET_IP_TTL ) NET_IP_TTL_DFLT, (CPU_INT16U ) flags_tcp, (CPU_INT16U ) NET_IP_FLAG_NONE, (void *) 0, (void *) 0, (NET_ERR *)&err); /* Ignore transitory tx err(s). */ #if 0 /* Tx buf freed by lower layer(s) [see Note #6b2]. */ NetTCP_TxPktFree(pseg_reset); #endif NET_CTR_STAT_INC(NetTCP_StatTxSegConnResetCtr); *perr = NET_TCP_ERR_NONE; } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxConnProbe() * * Description : (1) Prepare & transmit a TCP connection probe : * * (a) Validate TCP connection state for TCP probe * (b) Prepare TCP probe segment : See Note #2 * (1) Get buffer * (2) Prepare TCP segment : * (A) TCP segment addresses * (B) TCP segment sequence numbers See Note #2b1 * (C) TCP segment transmit flags : * (1) ACK * (D) TCP segment window size * (E) IP datagram parameters * (F) TCP segment packet buffer controls * (c) Transmit TCP connection probe * * * Argument(s) : pconn Pointer to a TCP connection. * * tx_probe_data_octet Indicate whether to transmit a single data probe octet (see Note #2b2) : * * DEF_YES Transmit data probe octet. * DEF_NO Do NOT transmit data probe octet. * * close_code Select which close action(s) to perform; bit-field flags logically OR'd : * * NET_TCP_CONN_CLOSE_NONE Perform NO close actions. * NET_TCP_CONN_CLOSE_ALL Perform ALL close actions. * * NET_TCP_CONN_CLOSE_CONN_TX_RESET Perform close connection transmit reset. * NET_TCP_CONN_CLOSE_CONN_ALL Perform ALL connection close actions. * * NET_TCP_CONN_CLOSE_TMR_TX_IDLE Close transmit idle timer. * NET_TCP_CONN_CLOSE_TMR_TX_SILLY_WIN Close transmit silly window persist timer. * NET_TCP_CONN_CLOSE_TMR_TX_ZERO_WIN Close transmit zero window persist timer. * NET_TCP_CONN_CLOSE_TMR_TX_ACK_DLY Close transmit acknowledgement delay timer. * NET_TCP_CONN_CLOSE_TMR_RE_TX Close re-transmit timer. * NET_TCP_CONN_CLOSE_TMR_KEEP_ALIVE Close connection keep-alive timer. * NET_TCP_CONN_CLOSE_TMR_TIMEOUT Close connection timer. * NET_TCP_CONN_CLOSE_TMR_ALL Close ALL timers. * * See also 'TCP CONNECTION CLOSE/FREE CODE DEFINES'. * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_NONE TCP connection probe successfully transmitted. * NET_TCP_ERR_CONN_PROBE_INVALID TCP connection probe NOT valid for current * TCP connection state. * NET_TCP_ERR_CONN_NOT_USED TCP connection NOT currently used. * NET_TCP_ERR_CONN_FAULT TCP connection fault; connection(s) aborted. * NET_TCP_ERR_CONN_FAIL TCP connection operation(s) failed. * NET_TCP_ERR_INVALID_CONN_STATE Invalid TCP connection state. * NET_TCP_ERR_INVALID_CONN_OP Invalid TCP connection operation. * NET_TCP_ERR_NONE_AVAIL Resources NOT available. * * Return(s) : none. * * Caller(s) : NetTCP_TxConnWinSizeZeroWinHandler(), * NetTCP_TxConnKeepAlive(). *$PAGE* * Note(s) : (2) TCP connection probes are transmitted for certain TCP conditions : * * (A) Some TCP transmit probe validation logic implemented in previous functions; * include duplicate validation logic in NetTCP_TxConnProbe() only if debug/ * validation code is enabled (i.e. NET_ERR_CFG_ARG_CHK_DBG_EN is DEF_ENABLED * in 'net_cfg.h'). * * (a) RFC #1122, Section 4.2.3.6 specifies a "mechanism [that] periodically probes the * other end of a connection" for the following TCP connection conditions : * * (1) Probing Zero Windows RFC #1122, Section 4.2.2.17 * (see 'NetTCP_TxConnWinSizeZeroWinHandler() Note #1b') * * (2) TCP Keep-Alives RFC #1122, Section 4.2.3.6 * (see 'NetTCP_TxConnKeepAlive() Note #2c') * * (b) "Send a probe segment ... to elicit a response from the peer TCP" : * * (1) "Such a segment generally contains SEG.SEQ = SND.NXT-1" ... * * (A) "Note that on a quiet [or deadlocked] connection SND.NXT = RCV.NXT, so that * this SEG.SEQ will be outside the window. Therefore, the probe causes the * receiver to return an acknowledgment segment." * * (B) "It is extremely important to remember that ACK segments that contain no data * are not reliably transmitted by TCP." * * (2) "and may or may not contain one garbage octet of data." * * (A) "An implementation SHOULD send a [probe] segment with no data." * * (B) (1) "Unfortunately, some misbehaved TCP implementations fail to respond to * a segment with SEG.SEQ = SND.NXT-1 unless the segment contains data." * * (2) Stevens, TCP/IP Illustrated, Volume 1, 8th Printing, Section 23.3, * Page 335 reiterates that "some older implementations based on 4.2BSD * do not respond to these ... probes unless the segment contains data". * * (C) Therefore, RFC #1122, Section 4.2.3.6 states that a TCP "implementation ... * MAY be configurable to send a [probe] segment containing one garbage octet, * for compatibility with erroneous TCP implementations". * * (3) (a) (1) RFC #793, Sections 3.7 & 2.6 state that "TCP uses retransmission ... to ensure * delivery of every segment". * * (2) However, RFC #1122, Section 4.2.2.17 'DISCUSSION' states that "it is extremely * important to remember that ACK (acknowledgment) segments that contain no data * are not reliably transmitted by TCP". * * Therefore, it is assumed that TCP acknowledgement/probe segments should NOT be * queued for retransmission but SHOULD be silently discarded. * * (b) (1) The network buffer's reference counter is NOT incremented since the TCP layer * does NOT maintain a reference to any transmitted TCP probe segments. * * (2) Therefore, the network buffer MUST be freed by lower layer(s). * * See also 'NetTCP_TxConnAck() Note #8', * & 'NetTCP_TxConnReset() Note #6'. * * (4) On ANY errors, network resources MUST be appropriately freed : * * (a) For any network resources NOT linked to the TCP connection, each network resource * MUST be freed by appropriate function(s). * * (5) #### IP options currently NOT implemented. ********************************************************************************************************* */ /*$PAGE*/ static void NetTCP_TxConnProbe (NET_TCP_CONN *pconn, CPU_BOOLEAN tx_probe_data_octet, NET_TCP_CLOSE_CODE close_code, NET_ERR *perr) { #if ((((NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) && \ (NET_CTR_CFG_ERR_EN == DEF_ENABLED)) || \ (NET_CTR_CFG_STAT_EN == DEF_ENABLED)) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) CPU_BOOLEAN tx_probe; #endif NET_BUF_SIZE data_len; NET_BUF_SIZE data_ix; NET_BUF *pseg_probe; NET_BUF_HDR *pseg_probe_hdr; NET_IP_TOS TOS; NET_IP_TTL TTL; NET_IP_ADDR src_addr; NET_IP_ADDR dest_addr; NET_TCP_PORT_NBR src_port; NET_TCP_PORT_NBR dest_port; NET_TCP_SEQ_NBR seq_nbr; NET_TCP_SEQ_NBR ack_nbr; NET_TCP_WIN_SIZE win_size; CPU_INT16U flags_tcp; CPU_INT16U flags_ip; CPU_INT08U probe_data[NET_TCP_DATA_LEN_TX_PROBE_DATA]; NET_ERR err; #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) /* See Note #2A. */ /* ----------- VALIDATE TCP CONN STATE ------------ */ tx_probe = DEF_NO; if (pconn != (NET_TCP_CONN *)0) { switch (pconn->ConnState) { case NET_TCP_CONN_STATE_FREE: NET_CTR_ERR_INC(NetTCP_ErrNotUsedCtr); *perr = NET_TCP_ERR_CONN_NOT_USED; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_CLOSED: case NET_TCP_CONN_STATE_LISTEN: case NET_TCP_CONN_STATE_SYNC_RXD: case NET_TCP_CONN_STATE_SYNC_RXD_PASSIVE: case NET_TCP_CONN_STATE_SYNC_RXD_ACTIVE: case NET_TCP_CONN_STATE_SYNC_TXD: case NET_TCP_CONN_STATE_TIME_WAIT: case NET_TCP_CONN_STATE_LAST_ACK: case NET_TCP_CONN_STATE_CLOSING_DATA_AVAIL: NET_CTR_ERR_INC(NetTCP_ErrConnInvalidOpCtr); *perr = NET_TCP_ERR_INVALID_CONN_OP; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_CONN: case NET_TCP_CONN_STATE_FIN_WAIT_1: case NET_TCP_CONN_STATE_FIN_WAIT_2: case NET_TCP_CONN_STATE_CLOSING: case NET_TCP_CONN_STATE_CLOSE_WAIT: tx_probe = DEF_YES; break; case NET_TCP_CONN_STATE_NONE: default: NetTCP_ConnClose((NET_TCP_CONN *)pconn, (NET_BUF_HDR *)0, (CPU_BOOLEAN )DEF_YES, (NET_TCP_CLOSE_CODE)close_code); NET_CTR_ERR_INC(NetTCP_ErrConnInvalidStateCtr); *perr = NET_TCP_ERR_INVALID_CONN_STATE; return; /* Prevent 'break NOT reachable' compiler warning. */ } } if (tx_probe != DEF_YES) { /* If NOT valid, abort tx TCP conn probe. */ *perr = NET_TCP_ERR_CONN_PROBE_INVALID; return; } #else /* Prevent compiler warning. */ (void)&close_code; #endif /*$PAGE*/ /* ---------- PREPARE TCP CONN PROBE SEG ---------- */ /* If valid, prepare & tx TCP conn probe. */ /* Get buf. */ data_len = (tx_probe_data_octet == DEF_YES) /* Cfg data len for probe data (see Note #2b2C). */ ? NET_TCP_DATA_LEN_TX_PROBE_DATA : NET_TCP_DATA_LEN_TX_PROBE_NO_DATA; data_ix = NET_BUF_DATA_TX_IX; pseg_probe = NetBuf_Get((NET_BUF_SIZE) data_len, (NET_BUF_SIZE) data_ix, (CPU_INT16U ) NET_BUF_FLAG_NONE, (NET_ERR *)&err); if ( err != NET_BUF_ERR_NONE) { *perr = NET_TCP_ERR_NONE_AVAIL; return; } if (tx_probe_data_octet == DEF_YES) { /* If tx probe data req'd, ... */ probe_data[0] = NET_TCP_TX_PROBE_DATA; /* ... prepare data (see Note #2b2) ... */ NetBuf_DataWr((NET_BUF *) pseg_probe, /* ... & wr data into TCP tx buf. */ (NET_BUF_SIZE) data_ix, (NET_BUF_SIZE) data_len, (CPU_INT08U *)&probe_data[0], (NET_ERR *)&err); if ( err != NET_BUF_ERR_NONE) { /* See Note #4a. */ NetBuf_Free(pseg_probe); *perr = NET_TCP_ERR_CONN_FAIL; return; } } /* --------------- PREPARE TCP HDR ---------------- */ /* Prepare seg addrs. */ NetTCP_TxConnPrepareSegAddrs((NET_TCP_CONN *) pconn, (CPU_INT08U *)&src_port, (CPU_INT08U *)&src_addr, (CPU_INT16U ) sizeof(src_port), (CPU_INT16U ) sizeof(src_addr), (CPU_INT08U *)&dest_port, (CPU_INT08U *)&dest_addr, (CPU_INT16U ) sizeof(dest_port), (CPU_INT16U ) sizeof(dest_addr), (NET_ERR *)&err); if ( err != NET_TCP_ERR_NONE) { /* See Note #4a. */ *perr = NET_TCP_ERR_CONN_FAULT; NetBuf_Free(pseg_probe); return; } /* Prepare TCP seq nbrs (see Note #1b2B). */ seq_nbr = pconn->TxSeqNbrNext - 1; /* Sub 1 from probe seq (see Note #2b1). */ ack_nbr = pconn->RxSeqNbrNext; /* Prepare TCP tx flags (see Note #1b2C). */ flags_tcp = NET_TCP_FLAG_NONE | NET_TCP_FLAG_TX_ACK; /* Prepare TCP win size. */ win_size = pconn->RxWinSizeActual; /* Prepare IP params. */ TOS = pconn->TxIP_TOS; TTL = pconn->TxIP_TTL; flags_ip = pconn->TxIP_Flags; /* Init buf ctrls. */ pseg_probe_hdr = &pseg_probe->Hdr; pseg_probe_hdr->DataIx = (CPU_INT16U )data_ix; pseg_probe_hdr->DataLen = (NET_BUF_SIZE)data_len; pseg_probe_hdr->TotLen = (NET_BUF_SIZE)pseg_probe_hdr->DataLen; pseg_probe_hdr->ProtocolHdrType = NET_PROTOCOL_TYPE_TCP; pseg_probe_hdr->TCP_SegSync = (CPU_BOOLEAN )DEF_NO; pseg_probe_hdr->TCP_SegClose = (CPU_BOOLEAN )DEF_NO; pseg_probe_hdr->TCP_SegAck = (CPU_BOOLEAN )DEF_YES; pseg_probe_hdr->TCP_SegReset = (CPU_BOOLEAN )DEF_NO; pseg_probe_hdr->TCP_Flags = flags_tcp; /*$PAGE*/ /* ------------ TX TCP CONN PROBE SEG ------------- */ NetTCP_TxPktHandler((NET_BUF *) pseg_probe, (NET_IP_ADDR ) src_addr, (NET_TCP_PORT_NBR) src_port, (NET_IP_ADDR ) dest_addr, (NET_TCP_PORT_NBR) dest_port, (NET_TCP_SEQ_NBR ) seq_nbr, (NET_TCP_SEQ_NBR ) ack_nbr, (NET_TCP_WIN_SIZE) win_size, (NET_IP_TOS ) TOS, (NET_IP_TTL ) TTL, (CPU_INT16U ) flags_tcp, (CPU_INT16U ) flags_ip, (void *) 0, (void *) 0, /* See Note #5. */ (NET_ERR *)&err); /* Ignore transitory tx err(s). */ #if 0 /* Tx buf freed by lower layer(s) [see Note #3b2]. */ NetTCP_TxPktFree(pseg_probe); #endif NET_CTR_STAT_INC(NetTCP_StatTxSegConnProbeCtr); *perr = NET_TCP_ERR_NONE; } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxConnKeepAlive() * * Description : (1) #### Prepare & transmit a TCP connection keep-alive : * * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in #### * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_NONE TCP connection keep-alive successfully * transmitted. * * Return(s) : none. * * Caller(s) : #### * * Note(s) : (2) RFC #1122, Section 4.2.3.6 states that "implementors MAY include 'keep-alives' in * their TCP implementations, although this practice is not universally accepted ... * A 'keep-alive' mechanism periodically probes the other end of a connection when * the connection is otherwise idle, even when there is no data to be sent". * * (a) "If keep-alives are included," ... * * (1) "the application MUST be able to turn them on or off for each TCP connection," * (2) "and they MUST default to off." * * (b) "Keep-alive packets MUST only be sent when no data or acknowledgement packets have * been received for the connection within an interval" : * * (1) "This interval MUST be configurable" ... * (2) "and MUST default to no less than two hours." * * (c) "To confirm that an idle connection is still active ... send a probe segment ... * to elicit a response from the peer TCP" : * * (1) "Such a segment generally contains SEG.SEQ = SND.NXT-1" ... * * (A) "Note that on a quiet connection SND.NXT = RCV.NXT, so that this SEG.SEQ will * be outside the window. Therefore, the probe causes the receiver to return an * acknowledgment segment, confirming that the connection is still live. If the * peer has dropped the connection ... it will respond with a RST instead of an * acknowledgment segment." * * (2) "and may or may not contain one garbage octet of data." * * (A) "An implementation SHOULD send a keep-alive segment with no data". * * (1) (a) "Unfortunately, some misbehaved TCP implementations fail to respond to * a segment with SEG.SEQ = SND.NXT-1 unless the segment contains data." * * (b) Stevens, TCP/IP Illustrated, Volume 1, 8th Printing, Section 23.3, * Page 335 reiterates that "some older implementations based on 4.2BSD do * not respond to these keepalive probes unless the segment contains data". * * (2) (a) Therefore, RFC #1122, Section 4.2.3.6 states that a TCP "implementation ... * MAY be configurable to send a keep-alive segment containing one garbage * octet, for compatibility with erroneous TCP implementations". * * (b) (1) "Alternatively, an implementation could determine whether a peer * responded correctly to keep-alive packets with no garbage data octet." * * (2) (A) Or as Stevens, TCP/IP Illustrated, Volume 1, 8th Printing, Section * 23.3, Page 335 states, some "systems send the 4.3BSD-style segment * (no data) for the first half of the probe period, and if no response * is received, switch to the 4.2BSD-style segment for the last half". * * (B) Therefore, it seems reasonable to implement the combined BSD 4.3/4.2 * solution since it does NOT require any non-standard configuration * for the garbage data octet & SHOULD be backwards-compatible with all * other systems. * * (3) "It is extremely important to remember that ACK segments that contain no data * are not reliably transmitted by TCP. Consequently, if a keep-alive mechanism * is implemented it MUST NOT interpret failure to respond to any specific probe * as a dead connection." ********************************************************************************************************* */ #if 0 /* #### NOT yet implemented. */ static void NetTCP_TxConnKeepAlive (NET_TCP_CONN *pconn, NET_ERR *perr) { } #endif /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxConnTxQ() * * Description : (1) Transmit TCP data segment(s) from TCP connection transmit queue : * * (a) Configure TCP connection transmit : * * (1) Configure TCP connection transmit acknowledgement request : See Note #1d1 * * (A) Do NOT transmit TCP data/acknowledgement if ... * (1) NO TCP transmit data available or allowed to transmit * AND * (2) NO transmit acknowledgement requested * * (B) Transmit TCP acknowledgement if ... * (1) NO TCP transmit data available or allowed to transmit * BUT * (2) Transmit acknowledgement requested * * (2) Configure TCP connection transmit free timer request See Note #1d4 * * (b) Transmit TCP connection transmit queue data : * * (1) Control TCP connection transmit versus transmit congestion controls : * * (a) See also 'NetTCP_TxConnWinSizeHandlerCongCtrl() Note #2' * & 'NetTCP_TxConnWinSizeUpdateAvail() Note #1' * * (A) Available Transmit Window See Note #1b1a * (B) Transmit Queue Segment See Notes #5 & #8 * (C) Nagle Algorithm See Notes #6, #7b2B, & #8 * (D) Silly Window Syndrome See Notes #7 & #8 * * (2) Update TCP connection transmit queue : * (A) Remove TCP transmit segment(s) from * TCP connection transmit queue See Note #3 * (B) Append TCP transmit segment(s) to * TCP connection re-transmit queue See Note #4 * (C) Update TCP connection re-transmit queue timer * * (3) Update TCP connection : * (A) Update TCP connection sequence number(s) * (B) Update TCP connection transmit congestion window * * (4) Prepare TCP data/acknowledgement segment(s) : * (A) TCP segment addresses * (B) TCP segment sequence numbers * (C) TCP segment transmit flags : * (1) ACK * (D) TCP segment window size * (E) IP datagram parameters * (F) TCP segment packet buffer controls * * (5) Transmit TCP data/acknowledgement segment(s) * * (c) Suspend TCP transmit : See Note #10 * * (1) Handle any network receive packet(s) See Note #10b2A * * (d) Complete TCP connection transmit : * * (1) Transmit TCP connection acknowledgement See Note #1a1 * (2) Clear TCP connection transmit idle timer * (see 'NetTCP_TxConnTxQ_TimeoutIdleClr() Note #2b2') * (3) Reset TCP connection delayed acknowledgement controls * (4) Free TCP connection transmit queue persist timer See Note #1a2 * * * (2) NetTCP_TxConnTxQ() transmits TCP connection data & acknowledgements. TCP acknowledgements * transmitted independently of TCP controls &/or data MAY be transmitted with NetTCP_TxConnAck(). * * See also 'NetTCP_TxConnAck() Note #2'. * *$PAGE* * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in various. * * pbuf_hdr Pointer to network buffer header that received TCP packet. * * tx_ack_code Indicate whether & how to transmit a TCP acknowledgement segment : * * NET_TCP_CONN_TX_ACK_NONE Do NOT transmit a TCP acknowledgement * segment if TCP transmit data * NOT available. * NET_TCP_CONN_TX_ACK Transmit a TCP acknowledgement segment * even if TCP transmit data NOT available. * NET_TCP_CONN_TX_ACK_IMMED Transmit a TCP acknowledgement segment * immediately even if TCP transmit data * NOT available. * * tx_q_timeout Indicate whether the TCP connection transmit queue timed out : * * DEF_NO TCP connection transmit queue did * NOT time out. * DEF_YES TCP connection transmit queue * timed out. * * close_code Select which close action(s) to perform; bit-field flags logically OR'd : * * NET_TCP_CONN_CLOSE_NONE Perform NO close actions. * NET_TCP_CONN_CLOSE_ALL Perform ALL close actions. * * NET_TCP_CONN_CLOSE_CONN_TX_RESET Perform close connection transmit reset. * NET_TCP_CONN_CLOSE_CONN_ALL Perform ALL connection close actions. * * NET_TCP_CONN_CLOSE_TMR_TX_IDLE Close transmit idle timer. * NET_TCP_CONN_CLOSE_TMR_TX_SILLY_WIN Close transmit silly window persist timer. * NET_TCP_CONN_CLOSE_TMR_TX_ZERO_WIN Close transmit zero window persist timer. * NET_TCP_CONN_CLOSE_TMR_TX_ACK_DLY Close transmit acknowledgement delay timer. * NET_TCP_CONN_CLOSE_TMR_RE_TX Close re-transmit timer. * NET_TCP_CONN_CLOSE_TMR_KEEP_ALIVE Close connection keep-alive timer. * NET_TCP_CONN_CLOSE_TMR_TIMEOUT Close connection timer. * NET_TCP_CONN_CLOSE_TMR_ALL Close ALL timers. * * See also 'TCP CONNECTION CLOSE/FREE CODE DEFINES'. * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_NONE TCP connection transmit queue successfully * handled. * NET_TCP_ERR_CONN_NOT_USED TCP connection NOT currently used. * NET_TCP_ERR_CONN_CLOSE TCP connection closed. * NET_TCP_ERR_CONN_FAULT TCP connection fault; connection(s) aborted. * NET_TCP_ERR_INVALID_CONN_STATE Invalid TCP connection state. * NET_TCP_ERR_INVALID_CONN_OP Invalid TCP connection operation. * * ------- RETURNED BY NetTCP_TxConnAck() : ------- * NET_TCP_ERR_CONN_ACK_NONE TCP connection acknowledgement NOT requested. * NET_TCP_ERR_CONN_ACK_DLYD TCP connection acknowledgement transmit delayed. * NET_TCP_ERR_CONN_ACK_PREVLY_TXD TCP connection acknowledgement previously * transmitted for segment. * NET_TCP_ERR_CONN_ACK_INVALID TCP connection acknowledgement NOT valid for * current TCP connection state. * NET_TCP_ERR_NONE_AVAIL Resources NOT available. * NET_TCP_ERR_INVALID_LEN_SEG Invalid TCP sequence-segment length. * NET_CONN_ERR_INVALID_FAMILY Invalid connection family. * NET_CONN_ERR_INVALID_ADDR Invalid TCP connection address. * NET_CONN_ERR_INVALID_ADDR_LEN Invalid TCP connection address length. * * ----- RETURNED BY NetTCP_TxPktHandler() : ------ * NET_ERR_INIT_INCOMPLETE Network initialization NOT complete. * NET_TCP_ERR_TX TCP transmit error (see Note #12a). * NET_ERR_TX Transmit error. * * Return(s) : none. * * Caller(s) : various. *$PAGE* * Note(s) : (3) See 'NetTCP_TxConnAppData() Note #4' for TCP connection Transmit Queue diagram. * * (4) TCP segments that have been transmitted but NOT yet acknowledged are sequenced into * the TCP connection's re-transmit queue to await acknowledgement or retransmission. * * (a) Transmitted TCP segments are inserted into a doubly-linked Re-Transmit Queue, * ordered consecutively by sequence number(s) [see also Note #4b]. * * (1) 'ReTxQ_Head' points to the head of the Re-Transmit Queue; * 'ReTxQ_Tail' points to the tail of the Re-Transmit Queue. * * (2) Segment buffers' 'PrevPrimListPtr' & 'NextPrimListPtr' doubly-link each * segment to form the Re-Transmit Queue. * * (b) Transmit segments are transmitted & await acknowledgement in sequence-order. * Therefore, newly-transmitted segments are sequenced after previously transmitted * segments starting at the tail of the Re-Transmit Queue. * * (c) Segments at the head of the Re-Transmit Queue are awaiting acknowledgement & * removal from the Re-Transmit Queue; & are ready to be re-transmitted until * acknowledged & removed from the queue. * * * | | * |<----- TCP Connection Re-Transmit Queue ------>| * | (see Note #4) | * * Segments Awaiting * Acknowledgement or Segments Sequenced * Re-Transmit into Re-Transmit Queue * start at head starting at tail * (see Note #4c) (see Note #4b) * * | NextPrimListPtr | * | (see Note #4a2) | * v | v * | * Head of ------- ------- v ------- ------- (see Note #4a1) * Re-Transmit ---->| |------>| |------>| |------>| | * Queue | | | | | | | | Tail of * | |<------| |<------| |<------| |<---- Re-Transmit * (see Note #4a1) | | | | ^ | | | | Queue * | | | | | | | | | * ------- ------- | ------- ------- * | * PrevPrimListPtr * (see Note #4a2) * *$PAGE* * (5) In order to simply TCP transmit buffer management, TCP transmit does NOT transmit * partial network-buffer segments (i.e. portions of TCP segments in the same network * buffer). This avoids the complexity of queuing & handling partially transmitted * segments on both the TCP transmit & re-transmit queues. * * (a) However, in order to avoid transmit window deadlock with a remote host's receive * window, the TCP connection's connection maximum segment size MUST be configured * to ensure that full, maximum-segment-sized segments will transmit even for receive * windows less than the default maximum segment size. * * See also 'NetTCP_ConnCfgMaxSegSize() Note #2'. * * (6) (a) (1) RFC #896, Section 'The small-packet problem' states that "there is a special * problem associated with small packets ... the congestion ... can result in * lost datagrams and retransmissions, as well as excessive propagation time ... * In practice, throughput may drop so low that TCP connections are aborted". * * (2) RFC #896, Section 'The solution to the small-packet problem' states that "the * solution to the small-packet problem ... is" : * * (A) "to inhibit the sending of new TCP segments when new outgoing data arrives * from the user" ... * * (B) "if any previously transmitted data on the connection remains unacknowledged." * * (C) "This inhibition is to be unconditional;" : * (1) "no timers," ... * (2) "tests for size of data received, ... * (3) "or other conditions are required." * * (b) (1) RFC #1122, Section 4.2.3.4 states that "the Nagle algorithm is ... as follows" : * * (A) "If there is unacknowledged data (i.e., SND.NXT > SND.UNA)," ... * (B) "then the sending TCP buffers all user data" ... * (1) "(regardless of the PSH bit)," ... * (2) "until" : * (a) "the outstanding data has been acknowledged" ... * (b) "or until the TCP can send a full-sized segment." * * (2) (A) "A TCP SHOULD implement the Nalge Algorithm ... to coalesce short segments." * * (B) (1) "However, there MUST be a way for an application to disable the Nagle * algorithm on an individual connection." * * (2) Thus, it is assumed from these two requirements that the Nagle algorithm * should be enabled by default. * * See also Note #7b2Ba2A. *$PAGE* * (7) (a) RFC #813, Section 2 states that "a bad implementation of the window algorithm can * lead to extremely poor performance ... This particular phenomenon ... has been * given the name of Silly Window Syndrome, or SWS". * * Section 3 elaborates that "SWS is a degeneration in the throughput which develops * ... whenever the acknowledgement of a small segment ... cause[s] another segment * of the same small size to be sent, until ... the network ... becomes clogged with * many small segments, and an equal number of acknowledgements". * * (b) (1) RFC #813, Section 4 states that "there is an algorithm that the sender can use * ... [which] compares the useable window to the offered window, and refrains from * anything if the ratio of useable to offered is less than a certain fraction ... * Until the useable window reaches a certain amount, the sender should simply refuse * to send anything". * * (2) (A) RFC #1122, Section 4.2.3.4 reiterates that "a TCP MUST include a SWS avoidance * algorithm in the sender". However, "the SWS avoidance algorithm ... specified" * in RFC #1122 "is to be used instead of the sender-side algorithm contained in * [RFC #813]". * * (B) (a) RFC #1122, Section 4.2.3.4 states that "the sender's SWS avoidance algorithm * is ... [to] send data" : * * (1) "If a maximum-sized segment can be sent, i.e, [sic] if" : * * (A) "min(D,U) >= Eff.snd.MSS" * (1) (a) This threshold subtly assumes that the amount of data queued * on the TCP connection's transmit queue is queued in maximum- * sized segments. * (b) Although this assumption is typically satisfied since all * new TCP transmit data is usually aggregated & appended onto * the TCP connection's transmit queue in maximum-sized segments * (see 'NetTCP_TxConnAppData() Note #7b1'); to ensure that the * maximum-sized-segment threshold is satisfied the transmit * segment's length is also checked. * * (2) "Or if the data is pushed and all queued data can be sent now, i.e., if" : * * (A) "[SND.NXT = SND.UNA and] ... * (1) "(the bracketed condition is imposed by the Nagle algorithm)" * (2) (a) This condition is required ONLY if the Nagle algorithm * is enabled (see Note #6b2B). * (b) However, if the Nagle algorithm is disabled, the silly * window syndrome threshold conditions MUST be checked * (see Note #7b2Ba3). * * (B) "PUSHED and" ... * (1) See also 'NetTCP_TxConnAppData() Note #7b3B1b'. * * (C) "D <= U" * (1) (a) This threshold subtly assumes that the amount of data queued * on the TCP connection's transmit queue is queued in maximum- * sized segments. * (b) To ensure that the next actual transmit segment is compared * to the available transmit window, the following threshold is * actually checked : * * (1) min(D, SEG.LEN) <= U * * See also Note #7b2Ba1A1. * * (3) "Or if at least a fraction Fs of the maximum window can be sent, i.e., if" : * * (A) "[SND.NXT = SND.UNA and]" ... * (1) This condition is required ONLY if the Nagle algorithm is enabled * (see Note #6b2B). * * (B) "min(D,U) >= Fs * Max(SND.WND)" *$PAGE* * (4) "or if" : * * (A) "data is PUSHed and" ... * (1) Although it is not directly stated, it is inferred that if * the segment(s)' data is NOT pushed, the segment transmit * SHOULD be postponed until the previous transmit silly window * syndrome avoidance conditions have NOT been satisfied (see * Notes #7b2Ba1, #7b2Ba2, & #7b2Ba3). * * (2) See also 'NetTCP_TxConnAppData() Note #7b3B1b'. * * (B) "the override timeout occurs." * (1) Although it is not directly stated, it is inferred that the * transmit silly window override timer should be set when : * * (a) The previous transmit silly window syndrome avoidance * conditions have NOT been satisfied (see Notes #7b2Ba1, * #7b2Ba2, & #7b2Ba3) ... * (b) BUT ... * (1) the TCP segment data is pushed ... * (2) & no current timeout exists. * * (2) However, if NO network timer is available to delay the TCP * data segment(s), the TCP data segment(s) SHOULD be immediately * transmitted. * * See also Note #7b2Bb. * * * where * (A) D Amount of data queued in the sending TCP but not * yet sent * * (B) U 'Useable window' ... i.e., the offered window less * the amount of data sent but not acknowledged : * * (1) U = SND.UNA + SND.WND - SND.NXT * * (2) The 'useable window' is also constrained by the * available window & other TCP congestion controls * (see 'NetTCP_TxConnWinSizeUpdateAvail() Note #1'). * * (C) Eff.snd.MSS Effective send MSS for the connection * (D) SND.NXT Next sequence number to transmit * (E) SND.UNA Oldest unacknowledged sequence number * (F) Max(SND.WND) Maximum send window ... seen ... on the connection * (G) Fs Fraction whose recommended value is 1/2 * * * (b) (1) Although RFC #813, Section 4 stated "that it is not necessary to set * a timer to protect against protocol lockup when postponing the send * operation"; RFC #1122, Section 4.2.3.4 amends that "to avoid a ... * deadlock, it is necessary to have a timeout to force transmission of * data, overriding the SWS avoidance algorithm". * * (2) "The override timeout should be in the range 0.1 - 1.0 seconds." * * (3) "In practice, this timeout should seldom occur." *$PAGE* * (8) (a) Although it is not directly stated, it is inferred that the limitations of * the Nagle & transmit silly window syndrome avoidance algorithms apply only * to discrete, individually-queued data segments & NOT to any stream of data * segments. * * Therefore, a data segment -- especially the last queued data segment in the * TCP connection's transmit queue -- should NOT be constrained by the Nagle & * transmit silly window syndrome avoidance algorithms if this last queued data * segment is immediately transmitted after the transmission of the preceding * queued data segments. * * See also 'NetTCP_TxConnAppData() Notes #7b1 & #7b3B2'. * * (b) Also, although NO RFC specifies the Nagle algorithm's or the transmit silly * window syndrome avoidance algorithm's compliance, effect, or limitation on * TCP connection closes; it does NOT seem reasonable for the Nagle algorithm * or the transmit silly window syndrome avoidance algorithm to inhibit or * delay the transmission of a TCP connection close segment. * * (9) Increment network buffer's reference counter to include the TCP segment now enqueued * to the TCP connection's re-transmit queue as a new reference to the network buffer. * * (10) To balance network receive versus transmit packet loads for certain network connection * types (e.g. stream-type connections), network receive & transmit packets SHOULD be * handled in an APPROXIMATELY balanced ratio. * * (a) Network task priorities & lock mechanisms partially maintain a balanced ratio * between network receive versus transmit packet handling. * * However, the handling of network receive & transmit packets : * * (1) SHOULD be interleaved so that for every few packet(s) received & handled, * several packet(s) should be transmitted; & vice versa. * * (2) SHOULD NOT exclusively handle receive nor transmit packets, even for a * short period of time. * * (b) To implement network receive versus transmit load balancing : * * (2) Certain network connections MUST periodically suspend network transmit(s) * to handle network receive packet(s) : * * (A) Suspend network transmit(s) if network receive packets are available. * * (1) To approximate a balanced ratio of network receive versus transmit * packets handled; the number of consecutive times that a network * transmit suspends itself to check for & handle any network receive * packet(s) SHOULD APPROXIMATELY correspond to the number of queued * network receive packet(s) availabile. * * See also 'net.c Net_RxPktIsAvail() Note #2'. * * (2) To protect TCP connections from transmit corruption while suspended, * ALL TCP data transmits & TCP transmit queue handling MUST be blocked * for suspended connections until the connection is no longer suspended. * * (B) Signal or timeout network transmit suspend(s) to restart transmit(s). * * See also 'net.h NETWORK RECEIVE PACKET MACRO'S Note #1'. * * (11) On ANY errors, network resources MUST be appropriately freed : * * (a) For all network resources that have been linked to the TCP connection, ALL * network resources are freed by NetTCP_ConnClose(). * * (12) (a) Since segments enqueued to a TCP connection's transmit queue have already been * reported as transmitted to the application & since no mechanism exists for a TCP * connection to re-request previously transmitted data, any TCP connection whose * transmit queue(s) becomes corrupted MUST be closed to force the application layer * to abort &/or recover from the corrupted data. * * (b) For any internal errors where the TCP connection's transmit queue is NOT corrupted, * the TCP connection is NOT closed. * * See also 'NetTCP_TxConnAppData() Note #10' * & 'NetTCP_TxConnReTxQ() Note #11'. ********************************************************************************************************* */ /*$PAGE*/ static void NetTCP_TxConnTxQ (NET_TCP_CONN *pconn, NET_BUF_HDR *pbuf_hdr, NET_TCP_ACK_CODE tx_ack_code, CPU_BOOLEAN tx_q_timeout, NET_TCP_CLOSE_CODE close_code, NET_ERR *perr) { #if (((NET_CTR_CFG_STAT_EN == DEF_ENABLED) || \ (NET_CTR_CFG_ERR_EN == DEF_ENABLED)) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif NET_BUF *pseg; NET_BUF *pseg_next; NET_BUF *pbuf_q_tail; NET_BUF_HDR *pseg_hdr; NET_BUF_HDR *pseg_next_hdr; NET_BUF_HDR *pbuf_q_tail_hdr; NET_TMR_TICK timeout_tick; NET_IP_TOS TOS; NET_IP_TTL TTL; NET_IP_ADDR src_addr; NET_IP_ADDR dest_addr; NET_TCP_PORT_NBR src_port; NET_TCP_PORT_NBR dest_port; NET_TCP_SEQ_NBR seq_nbr; NET_TCP_SEQ_NBR ack_nbr; NET_TCP_WIN_SIZE win_size; NET_TCP_WIN_SIZE tx_data_qd; NET_TCP_WIN_SIZE tx_data_min; NET_TCP_WIN_SIZE tx_th_q_min; NET_TCP_TX_Q_STATE state; CPU_INT16U flags_tcp; CPU_INT16U flags_ip; CPU_BOOLEAN tx_ack; CPU_BOOLEAN tx_seg; CPU_BOOLEAN tx_seg_push; CPU_BOOLEAN tx_seg_close; CPU_BOOLEAN tx_segs_txd; CPU_BOOLEAN tx_segs; CPU_BOOLEAN tx_done; CPU_BOOLEAN tx_th_seg; CPU_BOOLEAN tx_th_mss; CPU_BOOLEAN tx_th_nagle; CPU_BOOLEAN tx_th_silly_win; CPU_BOOLEAN tx_tmr_free; CPU_BOOLEAN net_rx_avail; NET_CTR net_rx_nbr; NET_ERR err; NET_ERR err_rtn; /* ------------------ CFG TCP TX ------------------ */ switch (tx_ack_code) { /* Cfg tx ack req. */ case NET_TCP_CONN_TX_ACK_NONE: default: tx_ack = DEF_NO; break; case NET_TCP_CONN_TX_ACK: case NET_TCP_CONN_TX_ACK_IMMED: tx_ack = DEF_YES; break; } /* Cfg tx tmr free req. */ tx_tmr_free = (pconn->TxQ_Head == (NET_BUF *)0) ? DEF_YES : DEF_NO; /*$PAGE*/ switch (pconn->ConnState) { /* Cfg tx Q seg(s). */ case NET_TCP_CONN_STATE_FREE: NET_CTR_ERR_INC(NetTCP_ErrNotUsedCtr); *perr = NET_TCP_ERR_CONN_NOT_USED; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_SYNC_RXD: case NET_TCP_CONN_STATE_SYNC_RXD_PASSIVE: case NET_TCP_CONN_STATE_SYNC_RXD_ACTIVE: case NET_TCP_CONN_STATE_SYNC_TXD: switch (pconn->TxQ_State) { case NET_TCP_TX_Q_STATE_CONN_CLOSED: tx_segs = DEF_YES; break; case NET_TCP_TX_Q_STATE_NONE: case NET_TCP_TX_Q_STATE_CLOSED: case NET_TCP_TX_Q_STATE_CONN: case NET_TCP_TX_Q_STATE_CONN_CLOSING: case NET_TCP_TX_Q_STATE_CONN_SUSPEND: default: *perr = NET_TCP_ERR_INVALID_CONN_STATE; return; /* Prevent 'break NOT reachable' compiler warning. */ } break; case NET_TCP_CONN_STATE_CONN: case NET_TCP_CONN_STATE_FIN_WAIT_1: case NET_TCP_CONN_STATE_CLOSING: case NET_TCP_CONN_STATE_CLOSE_WAIT: case NET_TCP_CONN_STATE_LAST_ACK: switch (pconn->TxQ_State) { case NET_TCP_TX_Q_STATE_CONN: case NET_TCP_TX_Q_STATE_CONN_CLOSING: tx_segs = DEF_YES; break; case NET_TCP_TX_Q_STATE_CONN_CLOSED: case NET_TCP_TX_Q_STATE_CONN_SUSPEND: /* See Note #10b2A2. */ tx_segs = DEF_NO; break; case NET_TCP_TX_Q_STATE_NONE: case NET_TCP_TX_Q_STATE_CLOSED: default: *perr = NET_TCP_ERR_INVALID_CONN_STATE; return; /* Prevent 'break NOT reachable' compiler warning. */ } break; case NET_TCP_CONN_STATE_LISTEN: case NET_TCP_CONN_STATE_FIN_WAIT_2: case NET_TCP_CONN_STATE_TIME_WAIT: case NET_TCP_CONN_STATE_CLOSING_DATA_AVAIL: NET_CTR_ERR_INC(NetTCP_ErrConnInvalidOpCtr); *perr = NET_TCP_ERR_INVALID_CONN_OP; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_NONE: case NET_TCP_CONN_STATE_CLOSED: default: NetTCP_ConnClose(pconn, pbuf_hdr, DEF_YES, close_code); NET_CTR_ERR_INC(NetTCP_ErrConnInvalidStateCtr); *perr = NET_TCP_ERR_INVALID_CONN_STATE; return; /* Prevent 'break NOT reachable' compiler warning. */ } err_rtn = NET_TCP_ERR_NONE; /*$PAGE*/ /* ---------- TX DATA FROM TCP CONN TX Q ---------- */ pseg_next = pconn->TxQ_Head; tx_done = (tx_segs == DEF_YES) ? DEF_NO : DEF_YES; tx_segs_txd = DEF_NO; while (tx_done == DEF_NO) { /* Tx ALL TCP conn tx Q seg(s) ... */ /* ... allowed by cong ctrls (see Note #1b1). */ tx_seg = DEF_NO; pseg = pseg_next; if (pseg != (NET_BUF *)0) { pseg_hdr = (NET_BUF_HDR *)&pseg->Hdr; pseg_next = (NET_BUF *) pseg_hdr->NextPrimListPtr; /* ----------------- CTRL TCP TX ------------------ */ /* Validate tx win cong th's : ... */ if (pconn->TxWinSizeAvail >= pseg_hdr->TCP_SegLenData) {/* ... chk avail tx win >= seg len (see Note #5); */ tx_seg = DEF_YES; /* ... chk prev'ly tx'd seg(s) [see Note #8a]; ... */ /* ... or TCP conn close seg [see Note #8b]; ... */ tx_seg_close = DEF_BIT_IS_SET(pseg_hdr->TCP_Flags, NET_TCP_FLAG_TX_CLOSE); tx_th_seg = ((tx_segs_txd == DEF_YES) || (tx_seg_close == DEF_YES)) ? DEF_OK : DEF_FAIL; if (tx_th_seg != DEF_OK) { /* ... chk MSS th (see Note #7b2Ba1); ... */ tx_data_qd = (NET_TCP_WIN_SIZE)(pconn->TxSeqNbrNextQ - pconn->TxSeqNbrNext); tx_data_min = (NET_TCP_WIN_SIZE) DEF_MIN(tx_data_qd, pseg_hdr->TCP_SegLenData); tx_th_q_min = (NET_TCP_WIN_SIZE) DEF_MIN(tx_data_min, pconn->TxWinSizeAvail); tx_th_mss = (tx_th_q_min >= pconn->MaxSegSizeConn) ? DEF_OK : DEF_FAIL; if (tx_th_mss != DEF_OK) { /* ... chk Nagle th (see Note #7b2Ba2); ... */ tx_seg_push = DEF_BIT_IS_SET(pseg_hdr->TCP_Flags, NET_TCP_FLAG_TX_PUSH); tx_th_nagle = ((pconn->TxWinSizeNagleEn != DEF_DISABLED ) && (pconn->TxSeqNbrNext == pconn->TxSeqNbrUnAckd) && (tx_seg_push == DEF_YES ) && (tx_data_min <= pconn->TxWinSizeAvail)) ? DEF_OK : DEF_FAIL; if (tx_th_nagle != DEF_OK) { /* ... chk silly win th (see Note #7b2Ba3); ... */ tx_th_silly_win = (((pconn->TxWinSizeNagleEn == DEF_DISABLED ) || (pconn->TxSeqNbrNext == pconn->TxSeqNbrUnAckd)) && (tx_th_q_min >= pconn->TxWinSizeMinTh)) ? DEF_OK : DEF_FAIL; if (tx_th_silly_win != DEF_OK) { /* ... chk push timeout (see Note #7b2Ba4). */ if (tx_seg_push == DEF_YES) { /* If data seg pushed (see Note #7b2Ba4A), ... */ if (tx_q_timeout == DEF_NO) { /* ... & no cur timeout (see Note #7b2Ba4B1b2), ... */ /* ... & tx Q tmr NOT yet cfg'd, ... */ if (pconn->TxQ_SillyWinTmr == (NET_TMR *)0) { timeout_tick = ((NET_TMR_TICK)pconn->TxWinSillyWinTimeout_ms * NET_TMR_TIME_TICK_PER_SEC) / DEF_TIME_NBR_mS_PER_SEC; pconn->TxQ_SillyWinTmr = NetTmr_Get((void *) pconn, (CPU_FNCT_PTR) NetTCP_TxConnTxQ_TimeoutSillyWin, (NET_TMR_TICK) timeout_tick, (CPU_INT16U ) NET_TMR_FLAG_NONE, (NET_ERR *)&err); /* ... & tx Q tmr avail, ... */ if (err == NET_TMR_ERR_NONE) { tx_seg = DEF_NO; /* ... dly tx seg (see Note #7b2Ba4B1); ... */ } /* ... else tx seg (see Note #7b2Ba4B2). */ } else { /* Else tx Q tmr already cfg'd, ... */ tx_seg = DEF_NO; /* ... dly tx seg (see Note #7b2Ba4B1). */ } } } else { /* If data seg NOT pushed, ... */ tx_seg = DEF_NO; /* ... dly tx seg (see Note #7b2Ba4A1). */ } } } } } } } /*$PAGE*/ /* ----------------- TX TCP SEGS ------------------ */ if (tx_seg == DEF_YES) { /* If avail & rdy, tx Q seg(s). */ /* ------------ UPDATE TCP CONN TX Q's ------------ */ /* Move seg from tx Q to re-tx Q : */ if (pseg_next != (NET_BUF *)0) { /* If tx Q next seg(s) avail, ... */ /* ... update TCP conn tx Q. */ pseg_next_hdr = (NET_BUF_HDR *)&pseg_next->Hdr; pseg_next_hdr->PrevPrimListPtr = (void *) 0; pconn->TxQ_Head = (NET_BUF *) pseg_next; } else { /* Else clr tx Q. */ pconn->TxQ_Head = (NET_BUF *)0; pconn->TxQ_Tail = (NET_BUF *)0; tx_tmr_free = DEF_YES; } pseg_hdr->PrevPrimListPtr = (void *)pconn->ReTxQ_Tail; pseg_hdr->NextPrimListPtr = (void *)0; if (pconn->ReTxQ_Tail != (NET_BUF *)0) { /* If re-tx Q NOT empty, ... */ /* ... append seg(s) @ Q tail (see Note #4b). */ pbuf_q_tail = (NET_BUF *) pconn->ReTxQ_Tail; pbuf_q_tail_hdr = (NET_BUF_HDR *)&pbuf_q_tail->Hdr; pbuf_q_tail_hdr->NextPrimListPtr = (void *) pseg; pconn->ReTxQ_Tail = (NET_BUF *) pseg; } else { /* Else add seg to empty re-tx Q. */ pconn->ReTxQ_Head = (NET_BUF *) pseg; pconn->ReTxQ_Tail = (NET_BUF *) pseg; } if (pconn->ReTxQ_Tmr == (NET_TMR *)0) { /* If unavail, get & update re-tx Q tmr. */ NetTCP_TxConnReTxQ_TimeoutSet(pconn, DEF_NO, close_code, perr); if (*perr != NET_TCP_ERR_NONE) { return; } } /* --------------- UPDATE TCP CONN ---------------- */ /* Update TCP conn tx seq nbr(s). */ pconn->TxSeqNbrNext += (NET_TCP_SEQ_NBR)pseg_hdr->TCP_SegLen; /* Update TCP conn tx win ctrls (see Note #3a2). */ NetTCP_TxConnWinSizeHandlerCongCtrl((NET_TCP_CONN *) pconn, (NET_BUF_HDR *) 0, (NET_TCP_ACK_CODE) NET_TCP_CONN_RX_ACK_NONE, (NET_TCP_WIN_SIZE) pseg_hdr->TCP_SegLenData, (NET_TCP_WIN_CODE) NET_TCP_CONN_TX_WIN_DEC, (NET_ERR *)&err); if ( err != NET_TCP_ERR_NONE) { *perr = NET_TCP_ERR_CONN_FAULT; return; } /*$PAGE*/ /* --------- PREPARE TCP TX DATA/ACK SEG ---------- */ /* Prepare TCP seg addrs. */ src_port = (NET_TCP_PORT_NBR)pseg_hdr->TCP_UDP_PortSrc; src_addr = (NET_IP_ADDR )pseg_hdr->IP_AddrSrc; dest_port = (NET_TCP_PORT_NBR)pseg_hdr->TCP_UDP_PortDest; dest_addr = (NET_IP_ADDR )pseg_hdr->IP_AddrDest; /* Prepare TCP seq nbrs. */ seq_nbr = (NET_TCP_SEQ_NBR )pseg_hdr->TCP_SeqNbr; ack_nbr = (NET_TCP_SEQ_NBR )pconn->RxSeqNbrNext; /* Prepare TCP tx flags. */ flags_tcp = pseg_hdr->TCP_Flags; /* Prepare TCP win size. */ win_size = pconn->RxWinSizeActual; /* Prepare IP params. */ TOS = pconn->TxIP_TOS; TTL = pconn->TxIP_TTL; flags_ip = pconn->TxIP_Flags; /* Update TCP tx buf ctrls. */ pseg_hdr->TCP_SeqNbrLast = (CPU_INT32U)seq_nbr; pseg_hdr->TCP_AckNbrLast = (CPU_INT32U)ack_nbr; pseg_hdr->TCP_SegLenLast = (CPU_INT16U)pseg_hdr->TCP_SegLen; pseg_hdr->TCP_WinSizeLast = (CPU_INT16U)win_size; pseg_hdr->TCP_SegReTxCtr = (CPU_INT16U)0; pseg_hdr->RefCtr++; /* TCP maintains ref until seg ack'd (see Note #9). */ /* ------------- TX TCP DATA/ACK SEG -------------- */ NetTCP_TxPktHandler((NET_BUF *) pseg, (NET_IP_ADDR ) src_addr, (NET_TCP_PORT_NBR) src_port, (NET_IP_ADDR ) dest_addr, (NET_TCP_PORT_NBR) dest_port, (NET_TCP_SEQ_NBR ) seq_nbr, (NET_TCP_SEQ_NBR ) ack_nbr, (NET_TCP_WIN_SIZE) win_size, (NET_IP_TOS ) TOS, (NET_IP_TTL ) TTL, (CPU_INT16U ) flags_tcp, (CPU_INT16U ) flags_ip, (void *) 0, (void *) 0, (NET_ERR *)&err_rtn); switch (err_rtn) { case NET_TCP_ERR_NONE: /* If NO tx err(s); ... */ tx_segs_txd = DEF_YES; /* ... indicate seg(s) tx'd, ... */ NET_CTR_STAT_INC(NetTCP_StatTxSegConnTxQ_Ctr); /* ... & inc tx seg stat ctr. */ break; case NET_ERR_INIT_INCOMPLETE: /* Else indicate tx done. */ case NET_ERR_TX: tx_done = DEF_YES; break; case NET_TCP_ERR_TX: /* See Note #12a. */ default: NetTCP_ConnClose(pconn, pbuf_hdr, pconn->ConnCloseAppFlag, close_code); *perr = err_rtn; return; /* Prevent 'break NOT reachable' compiler warning. */ } } else { /* Else if seg NOT tx'd, ... */ tx_done = DEF_YES; /* ... indicate tx done. */ } /*$PAGE*/ /* ---------------- SUSPEND TCP TX ---------------- */ if (tx_done == DEF_NO) { /* If tx NOT done .. */ net_rx_nbr = 0; net_rx_avail = Net_RxPktIsAvail(net_rx_nbr); if (net_rx_avail == DEF_YES) { /* .. & net rx avail; .. */ state = pconn->TxQ_State; /* .. save TCP conn tx Q state, .. */ pconn->TxQ_State = NET_TCP_TX_Q_STATE_CONN_SUSPEND; /* .. set TCP conn tx Q state to SUSPEND; .. */ NetOS_Unlock(); do { Net_TxSuspend(); /* .. suspend tx to handle net rx (see Note #10b2A) */ net_rx_nbr++; net_rx_avail = Net_RxPktIsAvail(net_rx_nbr); } while (net_rx_avail == DEF_YES); /* .. while net rx avail (see Note #10b2A1). */ NetOS_Lock(&err); if ( err != NET_OS_ERR_NONE) { NetTCP_ConnClose(pconn, pbuf_hdr, pconn->ConnCloseAppFlag, close_code); *perr = NET_TCP_ERR_CONN_CLOSE; return; } switch (pconn->TxQ_State) { /* Validate TCP conn tx Q state. */ case NET_TCP_TX_Q_STATE_CONN_SUSPEND: /* If still SUSPEND state, .. */ pconn->TxQ_State = state; /* .. restore TCP conn tx Q state. */ break; case NET_TCP_TX_Q_STATE_NONE: case NET_TCP_TX_Q_STATE_CLOSED: /* If CLOSED state, close TCP conn. */ case NET_TCP_TX_Q_STATE_CONN: case NET_TCP_TX_Q_STATE_CONN_CLOSED: case NET_TCP_TX_Q_STATE_CONN_CLOSING: default: NetTCP_ConnClose(pconn, pbuf_hdr, pconn->ConnCloseAppFlag, close_code); *perr = NET_TCP_ERR_CONN_CLOSE; return; /* Prevent 'break NOT reachable' compiler warning. */ } } } } /* --------------- COMPLETE TCP TX ---------------- */ if (tx_segs_txd != DEF_YES) { /* If NO tx Q seg(s) tx'd, ... */ if (tx_ack == DEF_YES) { /* ... but tx ack req'd; ... */ /* ... tx TCP conn ack (see Note #1c1). */ NetTCP_TxConnAck(pconn, pbuf_hdr, tx_ack_code, close_code, &err_rtn); } } else { /* Else if ANY tx Q seg(s) tx'd; .. */ NetTCP_TxConnTxQ_TimeoutIdleClr(pconn); /* .. clr tx Q idle tmr (see Note #1c2), .. */ NetTCP_TxConnAckDlyReset(pconn, DEF_YES); /* .. reset ack dly ctrls, .. */ tx_tmr_free = DEF_YES; /* .. free tx Q tmr. */ } if (tx_tmr_free == DEF_YES) { /* If free tx Q tmr req'd, .. */ if (pconn->TxQ_SillyWinTmr != (NET_TMR *)0) { /* .. & tx Q tmr avail, .. */ if (tx_q_timeout == DEF_NO) { /* .. & NOT timed out, .. */ /* .. free tx Q tmr. */ NetTmr_Free(pconn->TxQ_SillyWinTmr); } pconn->TxQ_SillyWinTmr = (NET_TMR *)0; } } *perr = err_rtn; /* Rtn err from tx handler(s). */ } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxConnTxQ_TimeoutIdle() * * Description : (1) (a) Handle TCP connection's transmit queue idle timeout ... : * * (1) Clear TCP connection's transmit idle timer See Note #4aA1 * (2) Reset TCP connection's transmit congestion window controls See Note #2a * (3) Reset TCP connection's transmit round-trip time controls See Note #2b * * (b) ... for the following states : * * (1) ESTABLISHED * (2) FIN-WAIT-1 * (3) CLOSING * (4) CLOSE-WAIT * (5) LAST-ACK * * * Argument(s) : pconn_timeout Pointer to TCP connection (see Note #3b). * * Return(s) : none. * * Caller(s) : Referenced in NetTCP_TxConnTxQ_TimeoutIdleSet(). * * Note(s) : (2) (a) RFC #2581, Section 4.1 states that "after TCP has been idle for a relatively long period * of time ... use slow start to restart transmission" : * * (1) (A) "When TCP has not received a segment for more than one retransmission timeout," ... * (B) "cwnd is reduced to the value of the restart window." * * (2) However, RFC #2581, Section 4.1 re-states that "using the last time a segment was * received to determine whether or not to decrease cwnd fails to deflate cwnd in the * common case of persistent ... connections ... The reception of [segments] makes the * test for an idle connection fail, and allows the TCP to begin transmission with a * possibly inappropriately large cwnd." * * (A) "Therefore, ... if the TCP has not sent data ... in an interval exceeding the * retransmission timeout" ... * * (B) "a TCP SHOULD set cwnd to no more than RW before beginning transmission." * * See also 'NetTCP_TxConnWinSizeHandlerCongCtrl() Note #2e1'. * * (b) Similarly, although NO RFC specifies that a TCP connection's RTT average & deviation * should be reset following a TCP transmit idle timeout; it seems reasonable to reset a * TCP connection's RTT average & deviation controls whenever a TCP connection's transmit * is idle for a period exceeding the re-transmit timeout. * * See also 'NetTCP_TxConnRTT_RTO_Calc() Note #2a4B'. *$PAGE* * (3) Ideally, network timer expiration functions could be defined as '[(void) (OBJECT *)]' * type functions -- even though network timer API functions cast expiration functions * to generic 'CPU_FNCT_PTR' type (i.e. '[(void) (void *)]'). * * (a) (1) Unfortunately, ISO-IEC 9899-1999 ANSI-C, Section 6.3.2.3.7 states that "a * pointer to an object ... may be converted to a pointer to a different object * ... [but] if the resulting pointer is not correctly aligned ... the behavior * is undefined". * * And since compilers may NOT correctly convert 'void' pointers to non-'void' * pointer arguments, network timer expiration functions MUST avoid incorrect * pointer conversion behavior between 'void' pointer parameters & non-'void' * pointer arguments & therefore CANNOT be defined as '[(void) (OBJECT *)]'. * * (2) However, Section 6.3.2.3.1 states that "a pointer to void may be converted * to or from a pointer to any ... object ... A pointer to any ... object ... * may be converted to a pointer to void and back again; the result shall * compare equal to the original pointer". * * (b) Therefore, to correctly convert 'void' pointer objects back to appropriate * network object pointer objects, network timer expiration functions MUST : * * (1) Be defined as 'CPU_FNCT_PTR' type (i.e. '[(void) (void *)]'); & ... * (2) Explicitly cast 'void' pointer arguments to specific object pointers; ... * (A) ... in this case, a 'NET_TCP_CONN' pointer. * * See also 'net_tmr.c NetTmr_Get() Note #3'. * * (4) This function is a network timer expiration function : * * (a) (1) For the following connection timer(s) ... : * * (A) TCP connection transmit queue idle timer ('TxQ_IdleTmr') * * (2) (A) Clear the timer pointer; ... * (1) Cleared prior to next handler function(s) as an extra precaution to avoiding * re-freeing the timer (see Note #4a2B). * * (B) but do NOT re-free the timer. * * (b) Do NOT set the following close timer flag(s) : * * (1) NET_TCP_CONN_CLOSE_TMR_TX_IDLE * * (5) Certain network connections MUST periodically suspend network transmit(s) to handle * network receive packet(s). To protect TCP connections from transmit corruption while * suspended, ALL TCP data transmits & TCP transmit queue handling MUST be blocked for * suspended connections until the connection is no longer suspended. * * However, handling the TCP connection's transmit queue idle timeout is permitted since * NO TCP data is transmitted & the TCP connection's transmit queue is NOT handled (see * Note #1a). * * See also 'NetTCP_TxConnTxQ() Note #10b2A2', * 'NetTCP_TxConnTxQ_TimeoutSillyWin() Note #5', * 'NetTCP_TxConnReTxQ_Timeout() Note #5', * & 'NetTCP_TxConnWinSizeZeroWinTimeout() Note #5'. ********************************************************************************************************* */ /*$PAGE*/ static void NetTCP_TxConnTxQ_TimeoutIdle (void *pconn_timeout) { #if ((NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) && \ (NET_CTR_CFG_ERR_EN == DEF_ENABLED) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) NET_TCP_CLOSE_CODE close_code; #endif NET_TCP_CONN *pconn; NET_ERR err; pconn = (NET_TCP_CONN *)pconn_timeout; /* See Note #3b2A. */ #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) close_code = NET_TCP_CONN_CLOSE_ALL; DEF_BIT_CLR(close_code, NET_TCP_CONN_CLOSE_TMR_TX_IDLE); /* See Note #4b1. */ /* ------------ VALIDATE TCP CONN ------------- */ if (pconn == (NET_TCP_CONN *)0) { NET_CTR_ERR_INC(NetTCP_ErrNullPtrCtr); return; } if (pconn->Type != NET_TCP_TYPE_CONN) { NET_CTR_ERR_INC(NetTCP_ErrConnInvalidTypeCtr); return; } switch (pconn->ConnState) { case NET_TCP_CONN_STATE_FREE: NET_CTR_ERR_INC(NetTCP_ErrNotUsedCtr); return; /* Prevent 'break NOT reachable' warning. */ case NET_TCP_CONN_STATE_CLOSED: case NET_TCP_CONN_STATE_LISTEN: case NET_TCP_CONN_STATE_SYNC_RXD: case NET_TCP_CONN_STATE_SYNC_RXD_PASSIVE: case NET_TCP_CONN_STATE_SYNC_RXD_ACTIVE: case NET_TCP_CONN_STATE_SYNC_TXD: case NET_TCP_CONN_STATE_FIN_WAIT_2: case NET_TCP_CONN_STATE_TIME_WAIT: case NET_TCP_CONN_STATE_CLOSING_DATA_AVAIL: NET_CTR_ERR_INC(NetTCP_ErrConnInvalidOpCtr); return; /* Prevent 'break NOT reachable' warning. */ case NET_TCP_CONN_STATE_CONN: /* See Note #1. */ case NET_TCP_CONN_STATE_FIN_WAIT_1: case NET_TCP_CONN_STATE_CLOSING: case NET_TCP_CONN_STATE_CLOSE_WAIT: case NET_TCP_CONN_STATE_LAST_ACK: switch (pconn->TxQ_State) { case NET_TCP_TX_Q_STATE_CONN: case NET_TCP_TX_Q_STATE_CONN_CLOSING: case NET_TCP_TX_Q_STATE_CONN_SUSPEND: /* See Note #5. */ break; case NET_TCP_TX_Q_STATE_NONE: case NET_TCP_TX_Q_STATE_CLOSED: case NET_TCP_TX_Q_STATE_CONN_CLOSED: default: return; /* Prevent 'break NOT reachable' warning. */ } break; case NET_TCP_CONN_STATE_NONE: default: NetTCP_ConnClose((NET_TCP_CONN *)pconn, (NET_BUF_HDR *)0, (CPU_BOOLEAN )DEF_YES, (NET_TCP_CLOSE_CODE)close_code); NET_CTR_ERR_INC(NetTCP_ErrConnInvalidStateCtr); return; /* Prevent 'break NOT reachable' warning. */ } #endif /*$PAGE*/ /* ---- HANDLE TCP CONN TX Q IDLE TIMEOUT ----- */ pconn->TxQ_IdleTmr = (NET_TMR *)0; /* Clr tx Q idle tmr (see Note #4a2A1). */ /* Reset tx win ctrls (see Note #2a2B). */ NetTCP_TxConnWinSizeHandlerCongCtrl((NET_TCP_CONN *) pconn, (NET_BUF_HDR *) 0, (NET_TCP_ACK_CODE) NET_TCP_CONN_RX_ACK_NONE, (NET_TCP_WIN_SIZE) 0, (NET_TCP_WIN_CODE) NET_TCP_CONN_TX_WIN_RESET, (NET_ERR *)&err); /* Reset RTT ctrls (see Note #2b). */ NetTCP_TxConnRTT_RTO_Calc(pconn, NET_TCP_CONN_TX_RTT_RESET, NET_TCP_TX_RTT_NONE, NET_TCP_TX_RTT_NONE); } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxConnTxQ_TimeoutIdleSet() * * Description : (1) Start TCP connection's transmit queue idle timeout for the following states : * * (a) SYN-RECEIVED * (b) SYN-SENT * (c) ESTABLISHED * (d) FIN-WAIT-1 * (e) CLOSING * (f) CLOSE-WAIT * (g) LAST-ACK * * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_RxPktConnHandlerReTxQ(). * * Return(s) : none. * * Caller(s) : NetTCP_RxPktConnHandlerReTxQ(). * * Note(s) : (2) (a) RFC #2581, Section 4.1 states that "after TCP has been idle for a relatively long period * of time ... use slow start to restart transmission" : * * (1) "If the TCP has not sent data in an interval exceeding the retransmission timeout" ... * (2) "cwnd is reduced to ... no more than ... the value of the restart window." * * See also 'NetTCP_TxConnTxQ_TimeoutIdle() Note #2a'. * * (b) However, if NO network timer is available to time the transmit queue idle interval, the * TCP connection SHOULD be immediately slow started. ********************************************************************************************************* */ static void NetTCP_TxConnTxQ_TimeoutIdleSet (NET_TCP_CONN *pconn) { #if ((NET_CTR_CFG_ERR_EN == DEF_ENABLED) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif NET_TMR_TICK timeout_tick; NET_ERR err; /*$PAGE*/ /* ------------ VALIDATE TCP CONN ------------- */ switch (pconn->ConnState) { case NET_TCP_CONN_STATE_FREE: NET_CTR_ERR_INC(NetTCP_ErrNotUsedCtr); return; /* Prevent 'break NOT reachable' warning. */ case NET_TCP_CONN_STATE_CLOSED: case NET_TCP_CONN_STATE_LISTEN: case NET_TCP_CONN_STATE_FIN_WAIT_2: case NET_TCP_CONN_STATE_TIME_WAIT: case NET_TCP_CONN_STATE_CLOSING_DATA_AVAIL: NET_CTR_ERR_INC(NetTCP_ErrConnInvalidOpCtr); return; /* Prevent 'break NOT reachable' warning. */ case NET_TCP_CONN_STATE_SYNC_RXD: /* See Note #1a. */ case NET_TCP_CONN_STATE_SYNC_RXD_PASSIVE: case NET_TCP_CONN_STATE_SYNC_RXD_ACTIVE: case NET_TCP_CONN_STATE_SYNC_TXD: /* See Note #1b. */ switch (pconn->TxQ_State) { case NET_TCP_TX_Q_STATE_CONN_CLOSED: break; case NET_TCP_TX_Q_STATE_NONE: case NET_TCP_TX_Q_STATE_CLOSED: case NET_TCP_TX_Q_STATE_CONN: case NET_TCP_TX_Q_STATE_CONN_CLOSING: case NET_TCP_TX_Q_STATE_CONN_SUSPEND: default: return; /* Prevent 'break NOT reachable' warning. */ } break; case NET_TCP_CONN_STATE_CONN: /* See Note #1c. */ case NET_TCP_CONN_STATE_FIN_WAIT_1: case NET_TCP_CONN_STATE_CLOSING: case NET_TCP_CONN_STATE_CLOSE_WAIT: case NET_TCP_CONN_STATE_LAST_ACK: switch (pconn->TxQ_State) { case NET_TCP_TX_Q_STATE_CONN: case NET_TCP_TX_Q_STATE_CONN_CLOSING: case NET_TCP_TX_Q_STATE_CONN_SUSPEND: break; case NET_TCP_TX_Q_STATE_NONE: case NET_TCP_TX_Q_STATE_CLOSED: case NET_TCP_TX_Q_STATE_CONN_CLOSED: default: return; /* Prevent 'break NOT reachable' warning. */ } break; case NET_TCP_CONN_STATE_NONE: default: NET_CTR_ERR_INC(NetTCP_ErrConnInvalidStateCtr); return; /* Prevent 'break NOT reachable' warning. */ } /* ------ START TCP CONN TX Q IDLE TMR ------- */ timeout_tick = pconn->TxRTT_RTO_tick; /* Tx Q idle timeout = RTO (see Note #2a1). */ if (pconn->TxQ_IdleTmr == (NET_TMR *)0) { /* If tx Q idle tmr NOT avail, ... */ pconn->TxQ_IdleTmr = NetTmr_Get((void *) pconn, /* ... get tx Q idle tmr. */ (CPU_FNCT_PTR) NetTCP_TxConnTxQ_TimeoutIdle, (NET_TMR_TICK) timeout_tick, (CPU_INT16U ) NET_TMR_FLAG_NONE, (NET_ERR *)&err); } else { /* Else set tx Q idle tmr. */ NetTmr_Set((NET_TMR *) pconn->TxQ_IdleTmr, (CPU_FNCT_PTR) NetTCP_TxConnTxQ_TimeoutIdle, (NET_TMR_TICK) timeout_tick, (NET_ERR *)&err); } if (err != NET_TMR_ERR_NONE) { /* If any err(s), ... */ /* ... reset tx win ctrls (see Note #2b). */ NetTCP_TxConnWinSizeHandlerCongCtrl((NET_TCP_CONN *) pconn, (NET_BUF_HDR *) 0, (NET_TCP_ACK_CODE) NET_TCP_CONN_RX_ACK_NONE, (NET_TCP_WIN_SIZE) 0, (NET_TCP_WIN_CODE) NET_TCP_CONN_TX_WIN_RESET, (NET_ERR *)&err); } } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxConnTxQ_TimeoutIdleClr() * * Description : (1) Clear TCP connection's transmit queue idle timer for the following states : * * (a) ESTABLISHED * (b) FIN-WAIT-1 * (c) CLOSING * (d) CLOSE-WAIT * (e) LAST-ACK * * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_TxConnTxQ(). * * Return(s) : none. * * Caller(s) : NetTCP_TxConnTxQ(). * * Note(s) : (2) (a) RFC #2581, Section 4.1 states that "after TCP has been idle for a relatively long period * of time ... use slow start to restart transmission" : * * (1) "If the TCP has not sent data in an interval exceeding the retransmission timeout" ... * (2) "cwnd is reduced to ... no more than ... the value of the restart window." * * See also 'NetTCP_TxConnTxQ_TimeoutIdle() Note #2a'. * * (b) (1) However, if the TCP connection's re-transmit queue is NOT empty, then TCP data has * has been transmitted & is awaiting acknowledgement. * * (2) Therefore, NO transmit queue idle timeout is currently needed. ********************************************************************************************************* */ /*$PAGE*/ static void NetTCP_TxConnTxQ_TimeoutIdleClr (NET_TCP_CONN *pconn) { #if ((NET_CTR_CFG_ERR_EN == DEF_ENABLED) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif CPU_BOOLEAN tmr_free; /* ------------ VALIDATE TCP CONN ------------- */ switch (pconn->ConnState) { case NET_TCP_CONN_STATE_FREE: NET_CTR_ERR_INC(NetTCP_ErrNotUsedCtr); return; /* Prevent 'break NOT reachable' warning. */ case NET_TCP_CONN_STATE_CLOSED: case NET_TCP_CONN_STATE_LISTEN: case NET_TCP_CONN_STATE_SYNC_RXD: case NET_TCP_CONN_STATE_SYNC_RXD_PASSIVE: case NET_TCP_CONN_STATE_SYNC_RXD_ACTIVE: case NET_TCP_CONN_STATE_SYNC_TXD: case NET_TCP_CONN_STATE_FIN_WAIT_2: case NET_TCP_CONN_STATE_TIME_WAIT: case NET_TCP_CONN_STATE_CLOSING_DATA_AVAIL: NET_CTR_ERR_INC(NetTCP_ErrConnInvalidOpCtr); return; /* Prevent 'break NOT reachable' warning. */ case NET_TCP_CONN_STATE_CONN: /* See Note #1. */ case NET_TCP_CONN_STATE_FIN_WAIT_1: case NET_TCP_CONN_STATE_CLOSING: case NET_TCP_CONN_STATE_CLOSE_WAIT: case NET_TCP_CONN_STATE_LAST_ACK: switch (pconn->TxQ_State) { case NET_TCP_TX_Q_STATE_CONN: case NET_TCP_TX_Q_STATE_CONN_CLOSING: case NET_TCP_TX_Q_STATE_CONN_SUSPEND: break; case NET_TCP_TX_Q_STATE_NONE: case NET_TCP_TX_Q_STATE_CLOSED: case NET_TCP_TX_Q_STATE_CONN_CLOSED: default: return; /* Prevent 'break NOT reachable' warning. */ } break; case NET_TCP_CONN_STATE_NONE: default: NET_CTR_ERR_INC(NetTCP_ErrConnInvalidStateCtr); return; /* Prevent 'break NOT reachable' warning. */ } /* ------- CLR TCP CONN TX Q IDLE TMR -------- */ tmr_free = (pconn->ReTxQ_Head != (NET_BUF *)0) ? DEF_YES : DEF_NO; if (tmr_free == DEF_YES) { /* If re-tx Q NOT empty (see Note #2b1), ... */ if (pconn->TxQ_IdleTmr != (NET_TMR *)0) { NetTmr_Free(pconn->TxQ_IdleTmr); /* ... free tx Q idle tmr (see Note #2b2). */ pconn->TxQ_IdleTmr = (NET_TMR *)0; } } } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxConnTxQ_TimeoutSillyWin() * * Description : (1) (a) Handle TCP connection's transmit queue silly window timeout ... : * * (1) Clear TCP connection's transmit silly window persist timer See Note #4aA1 * (2) Transmit TCP connection's transmit data See Note #2 * * (b) ... for the following states : * * (1) ESTABLISHED * (2) FIN-WAIT-1 * (3) CLOSING * (4) CLOSE-WAIT * (5) LAST-ACK * * * Argument(s) : pconn_timeout Pointer to TCP connection (see Note #3b). * * Return(s) : none. * * Caller(s) : Referenced in NetTCP_TxConnTxQ(). * * Note(s) : (2) RFC #1122, Section 4.2.3.4 states that on "timeout ... force transmission of data, * overriding the SWS avoidance algorithm". * * See also 'NetTCP_TxConnTxQ() Note #7b2Bb'. * * (3) Ideally, network timer expiration functions could be defined as '[(void) (OBJECT *)]' * type functions -- even though network timer API functions cast expiration functions * to generic 'CPU_FNCT_PTR' type (i.e. '[(void) (void *)]'). * * (a) (1) Unfortunately, ISO-IEC 9899-1999 ANSI-C, Section 6.3.2.3.7 states that "a * pointer to an object ... may be converted to a pointer to a different object * ... [but] if the resulting pointer is not correctly aligned ... the behavior * is undefined". * * And since compilers may NOT correctly convert 'void' pointers to non-'void' * pointer arguments, network timer expiration functions MUST avoid incorrect * pointer conversion behavior between 'void' pointer parameters & non-'void' * pointer arguments & therefore CANNOT be defined as '[(void) (OBJECT *)]'. * * (2) However, Section 6.3.2.3.1 states that "a pointer to void may be converted * to or from a pointer to any ... object ... A pointer to any ... object ... * may be converted to a pointer to void and back again; the result shall * compare equal to the original pointer". * * (b) Therefore, to correctly convert 'void' pointer objects back to appropriate * network object pointer objects, network timer expiration functions MUST : * * (1) Be defined as 'CPU_FNCT_PTR' type (i.e. '[(void) (void *)]'); & ... * (2) Explicitly cast 'void' pointer arguments to specific object pointers; ... * (A) ... in this case, a 'NET_TCP_CONN' pointer. * * See also 'net_tmr.c NetTmr_Get() Note #3'. * * (4) This function is a network timer expiration function : * * (a) (1) For the following connection timer(s) ... : * * (A) TCP connection transmit silly window persist timer ('TxQ_SillyWinTmr') * * (2) (A) Clear the timer pointer; ... * (1) Cleared prior to next handler function(s) as an extra precaution to * avoiding re-freeing the timer (see Note #4a2B). * * (B) but do NOT re-free the timer. * * (b) Do NOT set the following close timer flag(s) : * * (1) NET_TCP_CONN_CLOSE_TMR_TX_SILLY_WIN * * (5) Certain network connections MUST periodically suspend network transmit(s) to handle * network receive packet(s). To protect TCP connections from transmit corruption while * suspended, ALL TCP data transmits & TCP transmit queue handling MUST be blocked for * suspended connections until the connection is no longer suspended. * * (a) The transmit queue silly window timeout is re-configured with one timer tick to * ensure that a non-zero delay is implemented. * * (b) If NO timer is available, a TCP connection will NOT be able to re-schedule the * transmission of its TCP transmit queue data. Thus, the TCP transmit queue data * will be delayed until triggered by any received acknowledgement packets or by * additional data transmits from the applications layer. * * See also 'NetTCP_TxConnTxQ() Note #10b2A2', * 'NetTCP_TxConnTxQ_TimeoutIdle() Note #5', * 'NetTCP_TxConnReTxQ_Timeout() Note #5', * & 'NetTCP_TxConnWinSizeZeroWinTimeout() Note #5'. ********************************************************************************************************* */ /*$PAGE*/ static void NetTCP_TxConnTxQ_TimeoutSillyWin (void *pconn_timeout) { #if ((NET_CTR_CFG_ERR_EN == DEF_ENABLED) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif NET_TCP_CONN *pconn; NET_TCP_CLOSE_CODE close_code; NET_TMR_TICK timeout_tick; NET_ERR err; pconn = (NET_TCP_CONN *)pconn_timeout; /* See Note #3b2A. */ close_code = NET_TCP_CONN_CLOSE_ALL; DEF_BIT_CLR(close_code, NET_TCP_CONN_CLOSE_TMR_TX_SILLY_WIN); /* See Note #4b1. */ /* -------------- VALIDATE TCP CONN --------------- */ #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) if (pconn == (NET_TCP_CONN *)0) { NET_CTR_ERR_INC(NetTCP_ErrNullPtrCtr); return; } if (pconn->Type != NET_TCP_TYPE_CONN) { NET_CTR_ERR_INC(NetTCP_ErrConnInvalidTypeCtr); return; } #endif switch (pconn->ConnState) { case NET_TCP_CONN_STATE_FREE: NET_CTR_ERR_INC(NetTCP_ErrNotUsedCtr); return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_CLOSED: case NET_TCP_CONN_STATE_LISTEN: case NET_TCP_CONN_STATE_SYNC_RXD: case NET_TCP_CONN_STATE_SYNC_RXD_PASSIVE: case NET_TCP_CONN_STATE_SYNC_RXD_ACTIVE: case NET_TCP_CONN_STATE_SYNC_TXD: case NET_TCP_CONN_STATE_FIN_WAIT_2: case NET_TCP_CONN_STATE_TIME_WAIT: case NET_TCP_CONN_STATE_CLOSING_DATA_AVAIL: NET_CTR_ERR_INC(NetTCP_ErrConnInvalidOpCtr); return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_CONN: /* See Note #1. */ case NET_TCP_CONN_STATE_FIN_WAIT_1: case NET_TCP_CONN_STATE_CLOSING: case NET_TCP_CONN_STATE_CLOSE_WAIT: case NET_TCP_CONN_STATE_LAST_ACK: switch (pconn->TxQ_State) { case NET_TCP_TX_Q_STATE_CONN: case NET_TCP_TX_Q_STATE_CONN_CLOSING: break; case NET_TCP_TX_Q_STATE_CONN_SUSPEND: /* See Note #5. */ timeout_tick = (NET_TMR_TICK)1; /* See Note #5a. */ pconn->TxQ_SillyWinTmr = NetTmr_Get((void *) pconn, (CPU_FNCT_PTR) NetTCP_TxConnTxQ_TimeoutSillyWin, (NET_TMR_TICK) timeout_tick, (CPU_INT16U ) NET_TMR_FLAG_NONE, (NET_ERR *)&err); (void)err; /* Ignore transitory rsrc err(s) [see Note #5b]. */ return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_TX_Q_STATE_NONE: case NET_TCP_TX_Q_STATE_CLOSED: case NET_TCP_TX_Q_STATE_CONN_CLOSED: default: return; /* Prevent 'break NOT reachable' compiler warning. */ } break; case NET_TCP_CONN_STATE_NONE: default: NetTCP_ConnClose((NET_TCP_CONN *)pconn, (NET_BUF_HDR *)0, (CPU_BOOLEAN )DEF_YES, (NET_TCP_CLOSE_CODE)close_code); NET_CTR_ERR_INC(NetTCP_ErrConnInvalidStateCtr); return; /* Prevent 'break NOT reachable' compiler warning. */ } /*$PAGE*/ /* ---- HANDLE TCP CONN TX Q SILL WIN TIMEOUT ----- */ pconn->TxQ_SillyWinTmr = (NET_TMR *)0; /* Clr tx Q silly win tmr (see Note #4a2A1). */ NetTCP_TxConnTxQ((NET_TCP_CONN *) pconn, /* Tx Q data (see Note #2). */ (NET_BUF_HDR *) 0, (NET_TCP_ACK_CODE ) NET_TCP_CONN_TX_ACK_NONE, (CPU_BOOLEAN ) DEF_YES, (NET_TCP_CLOSE_CODE) close_code, (NET_ERR *)&err); /* Ignore ALL tx err(s), transitory or fatal. */ } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxConnReTxQ() * * Description : (1) Re-transmit TCP data segment(s) from TCP connection re-transmit queue : * * (a) Update TCP connection : * (1) Update TCP connection's re-transmit queue timeout : * (A) Update TCP connection's re-transmit queue timer See Note #2b * (B) Reset TCP connection's transmit round-trip time See Note #2b2A2 * controls * (2) Update TCP connection's transmit congestion controls : * (A) Update TCP connection's transmit congestion window See Note #5 * (B) Reset TCP connection's delayed acknowledgement controls See Note #6 * * (b) Prepare TCP segment re-transmit : * (1) Prepare unchanged TCP segment for re-transmit See Note #7 * * (2) Prepare updated TCP segment for re-transmit : * (A) TCP segment sequence numbers * (B) TCP segment window size * (C) TCP segment addresses * (D) TCP segment transmit flags * (E) IP datagram parameters * (F) Update TCP segment's last transmit values : * (1) Sequence Number * (2) Acknowledgement Number * (3) Segment Length * (4) Window Size * (G) Unlink TCP segment packet buffer from any other network layer(s) * (H) Update TCP segment packet buffer controls * * (c) Re-transmit TCP segment See Note #2a * * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_TxConnReTxQ_Timeout(), * NetTCP_TxConnWinSizeHandlerCongCtrl(). * * re_tx_q_timeout Indicate whether the TCP connection re-transmit queue timed out : * * DEF_NO TCP connection re-transmit queue did * NOT time out. * DEF_YES TCP connection re-transmit queue * timed out. * * close_code Select which close action(s) to perform; bit-field flags logically OR'd : * * NET_TCP_CONN_CLOSE_NONE Perform NO close actions. * NET_TCP_CONN_CLOSE_ALL Perform ALL close actions. * * NET_TCP_CONN_CLOSE_CONN_TX_RESET Perform close connection transmit reset. * NET_TCP_CONN_CLOSE_CONN_ALL Perform ALL connection close actions. * * NET_TCP_CONN_CLOSE_TMR_TX_IDLE Close transmit idle timer. * NET_TCP_CONN_CLOSE_TMR_TX_SILLY_WIN Close transmit silly window persist timer. * NET_TCP_CONN_CLOSE_TMR_TX_ZERO_WIN Close transmit zero window persist timer. * NET_TCP_CONN_CLOSE_TMR_TX_ACK_DLY Close transmit acknowledgement delay timer. * NET_TCP_CONN_CLOSE_TMR_RE_TX Close re-transmit timer. * NET_TCP_CONN_CLOSE_TMR_KEEP_ALIVE Close connection keep-alive timer. * NET_TCP_CONN_CLOSE_TMR_TIMEOUT Close connection timer. * NET_TCP_CONN_CLOSE_TMR_ALL Close ALL timers. * * See also 'TCP CONNECTION CLOSE/FREE CODE DEFINES'. * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_NONE TCP connection re-transmit queue successfully handled. * NET_TCP_ERR_RE_TX_SEG_TH TCP connection closed due to excessive retransmission. * * --- RETURNED BY NetTCP_TxConnReTxQ_TimeoutSet() : ---- * NET_TCP_ERR_CONN_NOT_USED TCP connection NOT currently used. * NET_TCP_ERR_CONN_FAULT TCP connection fault; connection(s) aborted. * NET_TCP_ERR_INVALID_CONN_STATE Invalid TCP connection state. * NET_TCP_ERR_INVALID_CONN_OP Invalid TCP connection operation. * * -------- RETURNED BY NetTCP_TxPktHandler() : --------- * ------------- RETURNED BY NetIP_ReTx() : ------------- * NET_ERR_INIT_INCOMPLETE Network initialization NOT complete. * NET_ERR_TX Transmit error. * * -------- RETURNED BY NetTCP_TxPktHandler() : --------- * NET_TCP_ERR_TX TCP transmit error (see Note #11a). * * Return(s) : none. * * Caller(s) : NetTCP_TxConnReTxQ_Timeout(), * NetTCP_TxConnWinSizeHandlerCongCtrl(). *$PAGE* * Note(s) : (2) RFC #793, Section 3.9 'Event Processing : USER TIMEOUT : RETRANSMISSION TIMEOUT' * states that "for any state if the retransmission timeout expires on a segment in * the retransmission queue" : * * (A) RFC #1122, Section 4.2.3.1 reiterates that "retransmission of SYN segments * SHOULD use the same algorithm as data segments". * * (a) "Send the segment at the front of the retransmission queue." * * (A) RFC #2988, Section 5.4 reiterates that "when the retransmission timer * expires ... retransmit the earliest segment that has not been acknowledged * by the TCP receiver". * * (1) RFC #1122, Section 4.2.2.16 states that "a TCP receiver SHOULD NOT shrink the * window ... However, a sending TCP MUST be robust against window shrinking ... * If this happens, the sender SHOULD NOT send new data, but SHOULD retransmit * normally the old unacknowledged data between SND.UNA and SND.UNA+SND.WND. The * sender MAY also retransmit old data beyond SND.UNA+SND.WND, but SHOULD NOT time * out the connection if data beyond the right window edge is not acknowledged". * * Therefore, ALL data segments previously transmitted & awaiting acknowledgement * in a TCP connections' re-transmit queue may be re-transmitted regardless of the * TCP connection's current transmit congestion control window size or the remote * host's receive window size. * * (2) (A) RFC #2581, Section 3.2 states that "the fast retransmit algorithm uses the * arrival of 3 duplicate ACKs ... as an indication that a segment has been * lost ... [and] performs a retransmission of what appears to be the missing * segment". * * (B) RFC #1122, Section 4.2.2.21 reiterates that "'fast retransmit' ... uses the * redundant ACK's to deduce that a segment has been lost ... If more than a * threshold number of such ACK's is received, then the segment containing the * octets starting at SEG.ACK is assumed to have been lost and is retransmitted". * * (b) "Reinitialize the retransmission timer." * * (1) RFC #2988, Section 5 states that "an implementation MUST manage the retransmission * timer(s) in such a way that a segment is never retransmitted too early, i.e. less * than one RTO after the previous transmission of that segment". * * (A) "The following is the RECOMMENDED algorithm for managing the retransmission * timer" : * * (1) "Every time a packet containing data is sent (including a retransmission), * if the timer is not running, start it running so that it will expire after * RTO seconds (for the current value of RTO)." * * Therefore, the TCP connection re-transmit queue timer is reset whenever a * segment is re-transmitted. * * (2) "When all outstanding data has been acknowledged, turn off the retransmission * timer." * * (3) "When an ACK is received that acknowledges new data, restart the retransmission * timer so that it will expire after RTO seconds (for the current value of RTO)." * * (B) "When the retransmission timer expires, do the following" : * * (5) "The host MUST set RTO <- RTO * 2 ('back off the timer')." * * See also Note #2b2A1. * * (6) "Start the retransmission timer, such that it expires after RTO seconds * (for the value of RTO after the doubling operation)." *$PAGE* * (2) (A) (1) RFC #1122, Section 4.2.3.1 reiterates that an "implementation MUST also * include 'exponential backoff' for successive RTO values for the same * segment". * * (a) RFC #2988, Section 5.5 states that "when the retransmission timer * expires ... the host MUST set RTO <- RTO * 2 ('back off the timer')". * * Thus, the TCP retransmission timer exponential back-off scalar * value is 2. * * (b) Stevens, TCP/IP Illustrated, Volume 1, 8th Printing, Section 21.2, * Page 299 reiterates that "this doubling is called an 'exponential * backoff'". * * See also 'NetTCP_TxConnRTT_RTO_Calc() Note #2b2'. * * (2) RFC #2988, Section 5 adds "that a TCP implementation MAY clear SRTT [TCP * smoothed round-trip time] and RTTVAR [TCP round-trip time variance] after * backing off the timer multiple times as it is likely that the current SRTT * and RTTVAR are bogus in this situation. Once SRTT and RTTVAR are cleared * they should be initialized with the next RTT sample taken". * * See also 'NetTCP_TxConnRTT_RTO_Calc() Note #2a4A'. * * (B) (1) (a) RFC #2581, Section 3.2 states that "after receiving 3 duplicate ACKs * ... the fast retransmit algorithm ... performs a retransmission of * what appears to be the missing segment, without waiting for the * retransmission timer to expire". * * (b) RFC #1122, Section 4.2.2.21 reiterates that with "'fast retransmit' ... * the [lost] segment ... is retransmitted, without awaiting a timeout". * * (2) If a segment is re-transmitted due to the TCP fast re-transmit algorithm * & NOT due to the TCP connection's re-transmit queue timer expiring, the * TCP connection's re-transmit queue timer is : * * (a) NOT backed-off because the re-transmit queue See Note #2b1B * timer did NOT expire * (b) Restarted with its current RTO value See Note #2b1A1 * * (3) RFC #1122, Section 4.2.3.5 states that "excessive retransmission of the same segment * by TCP indicates some failure of the remote host or the Internet path ... The following * procedure MUST be used to handle excessive retransmissions of data segments" : * * (c) "When the number of transmissions of the same segment reaches a threshold ... * close the connection." * * See also 'NetTCP_RxPktConnHandlerReTxQ() Note #7'. * * (4) RFC #2988, Section 3 states that "TCP MUST use Karn's algorithm ... for taking RTT samples. * That is, RTT samples MUST NOT be made using segments that were retransmitted (and thus for * which it is ambiguous whether the reply was for the first instance of the packet or a later * instance)". * * (a) To ensure implementation of Karn's algorithm, while ANY re-transmitted segment(s) * remain unacknowledged in a TCP connection's re-transmit queue : * * (1) (A) NO RTT calculations are performed; ... See Note #4 * * (B) The TCP connection's re-transmit timeout : * (1) MAY be backed-off ... See Note #2b1B5 * OR * (2) MAY be latched at a previously backed-off value; See Note #2b1B6 * BUT * (3) MUST NOT be updated by RTT calculations. See Note #4a1A * * (2) Each time a TCP connection re-transmits a segment, the TCP connection advances * its un-re-transmitted sequence number to the TCP connection's next sequence * number to transmit. This ensures that NO RTT or RTO calculations are performed * until ALL re-transmitted data is acknowledged & remove from the TCP connection's * re-transmit queue. * * (5) RFC #2581, Section 3.1 states that "when a TCP sender detects segment loss using the * retransmission timer, the value of ssthresh [TCP transmit congestion control slow start * threshold] ... [and] cwnd [TCP transmit congestion control window] MUST be set". * * See also 'NetTCP_TxConnWinSizeHandlerCongCtrl() Notes #2c2A5a & #2c2A5b'. * * (6) A TCP connection's delayed acknowledgement controls : * * (a) SHOULD be reset whenever a TCP connection re-transmits data segment(s) since * any re-transmit always transmits an accompanying acknowledgement; * (b) MAY be reset whenever a TCP connection re-transmits a connection request * segment since NO delayed acknowledgement controls should be active. * * See also 'NetTCP_TxConnAckDlyReset() Note #1'. *$PAGE* * (7) RFC #1122, Section 4.2.2.15 states that "if a retransmitted packet is identical to the * original packet (which implies not only that the data boundaries have not changed, but * also that the window and acknowledgment fields of the header have not changed), then * the same IP Identification field MAY be used". * * In other words, if the following TCP segment header values are unchanged since the * last transmission of the TCP segment, then the TCP segment may be transmitted without * re-calculation of the TCP header : * * (a) Sequence Number * (b) Acknowledgement Number * (c) Segment Length * (d) Window Size * * (8) (a) Some network interfaces require a minimum packet size & may also require that * packets smaller than the minimum packet size be appended with trailing pad * octets. Therefore, all network transmit packets MUST be prepared to satisfy * a possible network interface minimum packet size requirement. * * (b) To ensure that TCP re-transmit segments will be properly prepared to satisfy * a network interface minimum packet size requirement, the following equations * are used to validate a TCP re-transmit segment size : * * (A) (1) TCP Re-transmit Segment Size = Segment's Re-transmit Protocol Header Sizes + * Segment's Remaining Segment Data Size * * (2) = Segment's Previous Protocol Header Sizes + * Segment's Remaining Segment Data Size * * (3) = (Segment's Previous Total Packet Size - * Segment's Previous Data Size) + * Segment's Remaining Segment Data Size * * (B) (1) TCP Re-transmit Minimum Segment Size = Network Interface Minimum Packet Size * * (2) [(Segment's Total Packet Size - * Segment's Data Size) + * Segment's Minimum Segment Data Size ] = Network Interface Minimum Packet Size * * (3) Segment's Minimum Segment Data Size = Network Interface Minimum Packet Size - * (Segment's Total Packet Size - * Segment's Data Size) * * (1) Equation #8bA2 subtly assumes that the TCP re-transmit segment's network * protocol header sizes equal the segment's previously transmitted network * protocol header sizes. In other words, it is assumed that a TCP transmit * segment's network protocol header sizes will remain constant for the * segment's initial transmission & any subsequent retransmissions. * * This assumption is true if & only if the TCP segment is re-transmitted * with no changes in any of the segment's network protocol header sizes, * which typically vary only for changes in the network protocol header * types or in the number of network protocol header options. * * #### However, since TCP segments that have already been transmitted can * NOT modify their segment data (see 'NetTCP_TxConnAppData() Note #10a'), * it seems reasonable that TCP segments that have already been transmitted * will NOT likely modify their network protocol header types & SHOULD NOT * modify their network protocol header options. * * #### Therefore, it seems reasonable that TCP segments that have already * been transmitted will NOT likely vary their network protocol header sizes. * Thus, TCP segments may validate their re-transmit segment size versus any * possible network interface minimum packet size requirement based on their * previously transmitted network protocol header & packet sizes. * * (2) (A) Equation #8bB calculates a TCP re-transmit segment's minimum data size * as required by a possible network interface minimum packet size. If * the TCP re-transmit segment's data size is smaller than the network * interface minimum packet size, the network buffer MUST be checked for * sufficient & available trailing octets, as required by the network * interface layer to append pad octets. * * (B) (1) (a) (1) If the TCP re-transmit segment's minimum data size is smaller * than the required network interface minimum packet size ... * AND * (2) there is insufficient network buffer octets available for * the network interface layer to append pad octets, ... * * (b) then the TCP segment's remaining data octets MUST be moved to * the network buffer's transmit index to provide sufficient * network buffer data octets for the network interface layer to * append pad octets. * * (2) A network interface that appends cleared trailing pad octets MAY be * optimized to skip clearing the trailing pad octets if the network * buffer's memory clear flag is set. Since a partially-acknowledged * TCP segment's network buffer contains previously acknowledged data * octets, the network buffer's memory clear flag MUST be cleared to * ensure that the network interface layer clears the trailing pad * octets. *$PAGE* * (9) If a packet buffer's unlink function is available, it is assumed that the function ... * * (a) Correctly unlinks the packet buffer from any other network protocol layers * AND * (b) Correctly updates the network buffer's reference counter to decrement the * number of network protocol layers that no longer maintain a reference to * the packet buffer. * AND * (c) Clears both the unlink function & object pointers. * * (10) Increment network buffer's reference counter to include the TCP segment STILL enqueued * to the TCP connection's re-transmit queue as a reference to the network buffer. * * (11) (a) Since segments enqueued to a TCP connection's transmit queue have already been * reported as transmitted to the application & since no mechanism exists for a TCP * connection to re-request previously transmitted data, any TCP connection whose * transmit queue(s) becomes corrupted MUST be closed to force the application layer * to abort &/or recover from the corrupted data. * * (b) For any internal errors where the TCP connection's transmit queue is NOT corrupted, * the TCP connection is NOT closed. * * See also 'NetTCP_TxConnAppData() Note #10' * & 'NetTCP_TxConnTxQ() Note #12'. ********************************************************************************************************* */ static void NetTCP_TxConnReTxQ (NET_TCP_CONN *pconn, CPU_BOOLEAN re_tx_q_timeout, NET_TCP_CLOSE_CODE close_code, NET_ERR *perr) { #if ((NET_CTR_CFG_STAT_EN == DEF_ENABLED) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif NET_BUF *pseg; NET_BUF_HDR *pseg_hdr; NET_IP_TOS TOS; NET_IP_TTL TTL; NET_IP_ADDR src_addr; NET_IP_ADDR dest_addr; NET_TCP_PORT_NBR src_port; NET_TCP_PORT_NBR dest_port; NET_TCP_SEQ_NBR seq_nbr; NET_TCP_SEQ_NBR ack_nbr; NET_TCP_SEG_SIZE seg_len; NET_TCP_WIN_SIZE win_size; NET_BUF_SIZE seg_len_tot; NET_BUF_SIZE seg_len_hdr; NET_BUF_SIZE seg_len_data_min; NET_BUF_SIZE buf_size_max; NET_BUF_SIZE data_ix_cur; NET_BUF_SIZE data_ix_re_tx; NET_BUF_SIZE data_len_cur; CPU_INT16U flags_tcp; CPU_INT16U flags_ip; CPU_INT08U *pdata_re_tx; CPU_FNCT_PTR unlink_fnct; CPU_BOOLEAN seg_chngd; CPU_BOOLEAN seg_updated; CPU_BOOLEAN seg_data_moved; NET_ERR err; /*$PAGE*/ /* ----------------- VALIDATE RE-TX Q ----------------- */ if (pconn->ReTxQ_Head == (NET_BUF *)0) { *perr = NET_TCP_ERR_NONE; return; } pseg = pconn->ReTxQ_Head; /* Re-tx seg @ head of re-tx Q (see Note #2a). */ pseg_hdr = &pseg->Hdr; pseg_hdr->TCP_SegReTxCtr++; if (pseg_hdr->TCP_SegReTxCtr >= pconn->TxSegReTxTh) { /* If nbr re-tx's >= th, close TCP conn (see Note #3). */ NetTCP_ConnClose((NET_TCP_CONN *)pconn, (NET_BUF_HDR *)0, (CPU_BOOLEAN )pconn->ConnCloseAppFlag, (NET_TCP_CLOSE_CODE)close_code); *perr = NET_TCP_ERR_RE_TX_SEG_TH; return; } /* ----------------- UPDATE TCP CONN ------------------ */ if (re_tx_q_timeout != DEF_NO) { /* If re-tx Q timeout, ... */ /* ... back-off re-tx Q timeout (see Note #2b1B5) ... */ NetTCP_TxConnRTT_RTO_Calc(pconn, NET_TCP_CONN_TX_RTT_BACKOFF, NET_TCP_TX_RTT_NONE, NET_TCP_TX_RTT_NONE); #if 0 /* Implemented with backoff (see Note #2b2A2). */ /* ... & reset RTT ctrls (see Note #2b2A2). */ NetTCP_TxConnRTT_RTO_Calc(pconn, NET_TCP_CONN_TX_RTT_RESET, NET_TCP_TX_RTT_NONE, NET_TCP_TX_RTT_NONE); #endif } /* Update re-tx Q tmr (see Note #2b1A1). */ NetTCP_TxConnReTxQ_TimeoutSet(pconn, re_tx_q_timeout, close_code, perr); if (*perr != NET_TCP_ERR_NONE) { return; } /* Adv un-re-tx'd seq(s) to ... */ pconn->TxSeqNbrUnReTxd = pconn->TxSeqNbrNext; /* ... not-yet-tx'd seq(s) [see Note #4a2]. */ if (re_tx_q_timeout != DEF_NO) { /* If re-tx Q timeout, ... */ /* ... update tx cong win (see Note #5). */ NetTCP_TxConnWinSizeHandlerCongCtrl((NET_TCP_CONN *)pconn, (NET_BUF_HDR *)0, (NET_TCP_ACK_CODE)NET_TCP_CONN_RX_ACK_NONE, (NET_TCP_WIN_SIZE)0, (NET_TCP_WIN_CODE)NET_TCP_CONN_TX_WIN_TIMEOUT, (NET_ERR *)perr); if (*perr != NET_TCP_ERR_NONE) { return; } } NetTCP_TxConnAckDlyReset(pconn, DEF_YES); /* Reset ack dly ctrls (see Note #6). */ /*$PAGE*/ /* -------------- PREPARE TCP RE-TX SEG --------------- */ /* Prepare TCP seq nbrs. */ seq_nbr = (NET_TCP_SEQ_NBR )pseg_hdr->TCP_SeqNbr; ack_nbr = (NET_TCP_SEQ_NBR )pconn->RxSeqNbrNext; seg_len = (NET_TCP_SEG_SIZE)pseg_hdr->TCP_SegLen; /* Prepare TCP win size. */ win_size = (NET_TCP_WIN_SIZE)pconn->RxWinSizeActual; /* Chk for re-tx seg update (see Note #7). */ seg_updated = ((pseg_hdr->TCP_SeqNbrLast != seq_nbr ) || (pseg_hdr->TCP_AckNbrLast != ack_nbr ) || (pseg_hdr->TCP_SegLenLast != seg_len ) || (pseg_hdr->TCP_WinSizeLast != win_size)) ? DEF_YES : DEF_NO; seg_data_moved = DEF_NO; seg_len_tot = (NET_BUF_SIZE)(pseg_hdr->TotLen - /* Seg cur tot len = prev'ly tx'd/ack'd pkt tot len - */ pseg_hdr->DataLen + /* ... prev'ly tx'd/ack'd pkt data len + */ pseg_hdr->TCP_SegLenData); /* ... rem'ing seg data len */ /* ... (see Note #8bA). */ if (seg_len_tot < NET_IF_PKT_SIZE_MIN) { /* If TCP re-tx seg tot len ... */ /* ... < min IF pkt size (see Note #8b2B1a2), ... */ /* ... calc min IF seg data len (see Note #8bB); ... */ seg_len_hdr = pseg_hdr->TotLen - pseg_hdr->DataLen; seg_len_data_min = NET_IF_FRAME_MIN_SIZE - seg_len_hdr; buf_size_max = NetBuf_GetMaxSize((NET_BUF *)pseg, (NET_BUF_SIZE)pseg_hdr->DataIx); if (seg_len_data_min > buf_size_max) { /* ... & if min IF seg data len ... */ /* ... > max buf size (see Note #8b2B1a2), ... */ data_ix_cur = (NET_BUF_SIZE) pseg_hdr->DataIx; data_ix_re_tx = (NET_BUF_SIZE) NET_BUF_DATA_TX_IX; data_len_cur = (NET_BUF_SIZE) pseg_hdr->TCP_SegLenData; pdata_re_tx = (CPU_INT08U *)&pseg->Data[data_ix_re_tx]; NetBuf_DataRd((NET_BUF *) pseg, /* ... rd rem'ing TCP seg data ... */ (NET_BUF_SIZE) data_ix_cur, (NET_BUF_SIZE) data_len_cur, (CPU_INT08U *) pdata_re_tx, /* ... & move to base re-tx ix (see Note #8b2B1b). */ (NET_ERR *)&err); if (err != NET_BUF_ERR_NONE) { /* See Note #11a. */ NetTCP_ConnClose((NET_TCP_CONN *)pconn, (NET_BUF_HDR *)0, (CPU_BOOLEAN )DEF_YES, (NET_TCP_CLOSE_CODE)close_code); *perr = NET_TCP_ERR_CONN_FAULT; return; } DEF_BIT_CLR(pseg_hdr->Flags, NET_BUF_FLAG_CLR_MEM); /* MUST clr buf mem clr flag (see Note #8b2B2). */ seg_data_moved = DEF_YES; } } seg_chngd = ((seg_updated != DEF_NO) || (seg_data_moved != DEF_NO)) ? DEF_YES : DEF_NO; /*$PAGE*/ if (seg_chngd != DEF_NO) { /* If chng'd, prepare seg for re-tx (see Note #1b2). */ /* Prepare TCP seg addrs. */ src_port = (NET_TCP_PORT_NBR)pseg_hdr->TCP_UDP_PortSrc; src_addr = (NET_IP_ADDR )pseg_hdr->IP_AddrSrc; dest_port = (NET_TCP_PORT_NBR)pseg_hdr->TCP_UDP_PortDest; dest_addr = (NET_IP_ADDR )pseg_hdr->IP_AddrDest; /* Prepare TCP tx flags. */ flags_tcp = pseg_hdr->TCP_Flags; /* Prepare IP params. */ TOS = pconn->TxIP_TOS; TTL = pconn->TxIP_TTL; flags_ip = pconn->TxIP_Flags; /* Update re-tx seg's last tx ctrls (see Note #1b2F). */ pseg_hdr->TCP_SeqNbrLast = (CPU_INT32U)seq_nbr; pseg_hdr->TCP_AckNbrLast = (CPU_INT32U)ack_nbr; pseg_hdr->TCP_SegLenLast = (CPU_INT16U)seg_len; pseg_hdr->TCP_WinSizeLast = (CPU_INT16U)win_size; /* Reset protocol & tot len for re-tx. */ pseg_hdr->ProtocolHdrType = NET_PROTOCOL_TYPE_TCP; pseg_hdr->TotLen = pseg_hdr->DataLen; } unlink_fnct = pseg_hdr->UnlinkFnctPtr; if (unlink_fnct != (CPU_FNCT_PTR)0) { /* If unlink fnct avail, .. */ unlink_fnct((void *)pseg); /* .. unlink seg from other layer(s) [see Note #9]. */ } if (pseg_hdr->RefCtr <= 1) { pseg_hdr->RefCtr++; /* TCP STILL maintains ref to seg (see Note #10). */ } /* ---------------- RE-TX TCP CONN SEG ---------------- */ if (seg_updated == DEF_NO) { /* If seg NOT updated, */ NetIP_ReTx((NET_BUF *)pseg, /* ... re-tx unchng'd seg (see Note #7). */ (NET_ERR *)perr); if (*perr != NET_IP_ERR_NONE) { return; } } else { /* Else re-tx updated seg. */ NetTCP_TxPktHandler((NET_BUF *)pseg, (NET_IP_ADDR )src_addr, (NET_TCP_PORT_NBR)src_port, (NET_IP_ADDR )dest_addr, (NET_TCP_PORT_NBR)dest_port, (NET_TCP_SEQ_NBR )seq_nbr, (NET_TCP_SEQ_NBR )ack_nbr, (NET_TCP_WIN_SIZE)win_size, (NET_IP_TOS )TOS, (NET_IP_TTL )TTL, (CPU_INT16U )flags_tcp, (CPU_INT16U )flags_ip, (void *)0, (void *)0, (NET_ERR *)perr); switch (*perr) { case NET_TCP_ERR_NONE: case NET_ERR_INIT_INCOMPLETE: case NET_ERR_TX: /* Ignore transitory tx err(s). */ break; case NET_TCP_ERR_TX: /* See Note #11a. */ default: NetTCP_ConnClose((NET_TCP_CONN *)pconn, (NET_BUF_HDR *)0, (CPU_BOOLEAN )pconn->ConnCloseAppFlag, (NET_TCP_CLOSE_CODE)close_code); return; /* Prevent 'break NOT reachable' compiler warning. */ } } NET_CTR_STAT_INC(NetTCP_StatTxSegConnReTxQ_Ctr); *perr = NET_TCP_ERR_NONE; } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxConnReTxQ_Timeout() * * Description : (1) (a) Handle TCP connection re-transmit queue timeout ... : * * (1) Clear TCP connection's re-transmit timer See Note #4aA1 * (2) Handle TCP connection re-transmit See Note #2 * * (b) ... for the following synchronization/connected states : * * (1) SYN-RECEIVED * (2) SYN-SENT * (3) ESTABLISHED * (4) FIN-WAIT-1 * (5) CLOSING * (6) CLOSE-WAIT * (7) LAST-ACK * * * Argument(s) : pconn_timeout Pointer to TCP connection to perform re-transmit (see Note #3b). * * Return(s) : none. * * Caller(s) : Referenced in NetTCP_TxConnReTxQ_TimeoutSet(). * * Note(s) : (2) RFC #793, Section 3.9 'Event Processing : USER TIMEOUT : RETRANSMISSION TIMEOUT' * states that "if the retransmission timeout expires ... send the segment at the * front of the retransmission queue ... [and] reinitialize the retransmission timer". * * See also 'NetTCP_TxConnReTxQ() Note #2'. * * (3) Ideally, network timer expiration functions could be defined as '[(void) (OBJECT *)]' * type functions -- even though network timer API functions cast expiration functions * to generic 'CPU_FNCT_PTR' type (i.e. '[(void) (void *)]'). * * (a) (1) Unfortunately, ISO-IEC 9899-1999 ANSI-C, Section 6.3.2.3.7 states that "a * pointer to an object ... may be converted to a pointer to a different object * ... [but] if the resulting pointer is not correctly aligned ... the behavior * is undefined". * * And since compilers may NOT correctly convert 'void' pointers to non-'void' * pointer arguments, network timer expiration functions MUST avoid incorrect * pointer conversion behavior between 'void' pointer parameters & non-'void' * pointer arguments & therefore CANNOT be defined as '[(void) (OBJECT *)]'. * * (2) However, Section 6.3.2.3.1 states that "a pointer to void may be converted * to or from a pointer to any ... object ... A pointer to any ... object ... * may be converted to a pointer to void and back again; the result shall * compare equal to the original pointer". * * (b) Therefore, to correctly convert 'void' pointer objects back to appropriate * network object pointer objects, network timer expiration functions MUST : * * (1) Be defined as 'CPU_FNCT_PTR' type (i.e. '[(void) (void *)]'); & ... * (2) Explicitly cast 'void' pointer arguments to specific object pointers; ... * (A) ... in this case, a 'NET_TCP_CONN' pointer. * * See also 'net_tmr.c NetTmr_Get() Note #3'. * * (4) This function is a network timer expiration function : * * (a) (1) For the following connection timer(s) ... : * * (A) TCP connection re-transmit queue timer ('ReTxQ_Tmr') * * (2) (A) Clear the timer pointer; ... * (1) Cleared prior to next handler function(s) as an extra precaution to * avoiding re-freeing the timer (see Note #4a2B). * * (B) but do NOT re-free the timer. * * (b) Do NOT set the following close timer flag(s) : * * (1) NET_TCP_CONN_CLOSE_TMR_RE_TX * * (5) Certain network connections MUST periodically suspend network transmit(s) to handle * network receive packet(s). To protect TCP connections from transmit corruption while * suspended, ALL TCP data transmits & TCP transmit queue handling MUST be blocked for * suspended connections until the connection is no longer suspended. * * However, handling the TCP connection's re-transmit timeout is permitted since NO TCP * data is transmitted & the TCP connection's transmit queue is NOT handled (see Note #1a). * * See also 'NetTCP_TxConnTxQ() Note #10b2A2', * 'NetTCP_TxConnTxQ_TimeoutIdle() Note #5', * 'NetTCP_TxConnTxQ_TimeoutSillyWin() Note #5', * & 'NetTCP_TxConnWinSizeZeroWinTimeout() Note #5'. ********************************************************************************************************* */ /*$PAGE*/ static void NetTCP_TxConnReTxQ_Timeout (void *pconn_timeout) { #if ((NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) && \ (NET_CTR_CFG_ERR_EN == DEF_ENABLED) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif NET_TCP_CONN *pconn; NET_TCP_CLOSE_CODE close_code; NET_ERR err; pconn = (NET_TCP_CONN *)pconn_timeout; /* See Note #3b2A. */ close_code = NET_TCP_CONN_CLOSE_ALL; DEF_BIT_CLR(close_code, NET_TCP_CONN_CLOSE_TMR_RE_TX); /* See Note #4b1. */ #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) /* ---------------- VALIDATE TCP CONN ----------------- */ if (pconn == (NET_TCP_CONN *)0) { NET_CTR_ERR_INC(NetTCP_ErrNullPtrCtr); return; } if (pconn->Type != NET_TCP_TYPE_CONN) { NET_CTR_ERR_INC(NetTCP_ErrConnInvalidTypeCtr); return; } switch (pconn->ConnState) { case NET_TCP_CONN_STATE_FREE: NET_CTR_ERR_INC(NetTCP_ErrNotUsedCtr); return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_CLOSED: case NET_TCP_CONN_STATE_LISTEN: case NET_TCP_CONN_STATE_FIN_WAIT_2: case NET_TCP_CONN_STATE_TIME_WAIT: case NET_TCP_CONN_STATE_CLOSING_DATA_AVAIL: NET_CTR_ERR_INC(NetTCP_ErrConnInvalidOpCtr); return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_SYNC_RXD: /* See Note #1. */ case NET_TCP_CONN_STATE_SYNC_RXD_PASSIVE: case NET_TCP_CONN_STATE_SYNC_RXD_ACTIVE: case NET_TCP_CONN_STATE_SYNC_TXD: case NET_TCP_CONN_STATE_CONN: case NET_TCP_CONN_STATE_FIN_WAIT_1: case NET_TCP_CONN_STATE_CLOSING: case NET_TCP_CONN_STATE_CLOSE_WAIT: case NET_TCP_CONN_STATE_LAST_ACK: switch (pconn->TxQ_State) { case NET_TCP_TX_Q_STATE_CONN: case NET_TCP_TX_Q_STATE_CONN_CLOSING: case NET_TCP_TX_Q_STATE_CONN_SUSPEND: /* See Note #5. */ break; case NET_TCP_TX_Q_STATE_NONE: case NET_TCP_TX_Q_STATE_CLOSED: case NET_TCP_TX_Q_STATE_CONN_CLOSED: default: return; /* Prevent 'break NOT reachable' compiler warning. */ } break; case NET_TCP_CONN_STATE_NONE: default: NetTCP_ConnClose((NET_TCP_CONN *)pconn, (NET_BUF_HDR *)0, (CPU_BOOLEAN )DEF_YES, (NET_TCP_CLOSE_CODE)close_code); NET_CTR_ERR_INC(NetTCP_ErrConnInvalidStateCtr); return; /* Prevent 'break NOT reachable' compiler warning. */ } #endif /* --------- HANDLE TCP CONN RE-TX Q TIMEOUT ---------- */ pconn->ReTxQ_Tmr = (NET_TMR *)0; /* Clr re-tx Q tmr (see Note #4a2A1). */ NetTCP_TxConnReTxQ(pconn, DEF_YES, close_code, &err); /* Handle re-tx Q (see Note #2). */ (void)err; /* Ignore ALL re-tx err(s), transitory or fatal. */ } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxConnReTxQ_TimeoutSet() * * Description : (1) (a) Set TCP connection's re-transmit queue timer for the following synchronization/ * connected states : * * (1) CLOSED * (2) LISTEN * (3) SYN-RECEIVED * (4) SYN-SENT * (5) ESTABLISHED * (6) FIN-WAIT-1 * (7) CLOSING * (8) CLOSE-WAIT * (9) LAST-ACK * * (A) (1) RFC #793, Section 3.9 'Event Processing : USER TIMEOUT : RETRANSMISSION * TIMEOUT' reiterates that "for any state if the retransmission timeout expires * on a segment in the retransmission queue ... reinitialize the retransmission * timer". * * (b) However, since a TCP connection's transmit & re-transmit queue SHOULD be closed for * the following states, it does NOT seem reasonable to set or reset a TCP connection's * re-transmit queue timer for these states : * * (1) FIN-WAIT-2 * (2) TIME-WAIT * * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_TxConnSync(), * NetTCP_TxConnTxQ(), * NetTCP_TxConnReTxQ(), * NetTCP_RxPktConnHandlerReTxQ(). * * re_tx_q_timeout Indicate whether the TCP connection re-transmit queue timed out : * * DEF_NO TCP connection re-transmit queue did * NOT time out. * DEF_YES TCP connection re-transmit queue * timed out. * * close_code Select which close action(s) to perform; bit-field flags logically OR'd : * * NET_TCP_CONN_CLOSE_NONE Perform NO close actions. * NET_TCP_CONN_CLOSE_ALL Perform ALL close actions. * * NET_TCP_CONN_CLOSE_CONN_TX_RESET Perform close connection transmit reset. * NET_TCP_CONN_CLOSE_CONN_ALL Perform ALL connection close actions. * * NET_TCP_CONN_CLOSE_TMR_TX_IDLE Close transmit idle timer. * NET_TCP_CONN_CLOSE_TMR_TX_SILLY_WIN Close transmit silly window persist timer. * NET_TCP_CONN_CLOSE_TMR_TX_ZERO_WIN Close transmit zero window persist timer. * NET_TCP_CONN_CLOSE_TMR_TX_ACK_DLY Close transmit acknowledgement delay timer. * NET_TCP_CONN_CLOSE_TMR_RE_TX Close re-transmit timer. * NET_TCP_CONN_CLOSE_TMR_KEEP_ALIVE Close connection keep-alive timer. * NET_TCP_CONN_CLOSE_TMR_TIMEOUT Close connection timer. * NET_TCP_CONN_CLOSE_TMR_ALL Close ALL timers. * * See also 'TCP CONNECTION CLOSE/FREE CODE DEFINES'. * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_NONE TCP connection re-transmit queue timer * successfully set. * NET_TCP_ERR_CONN_NOT_USED TCP connection NOT currently used. * NET_TCP_ERR_CONN_FAULT TCP connection fault; connection(s) aborted. * NET_TCP_ERR_INVALID_CONN_STATE Invalid TCP connection state. * NET_TCP_ERR_INVALID_CONN_OP Invalid TCP connection operation. * * Return(s) : none. * * Caller(s) : NetTCP_TxConnSync(), * NetTCP_TxConnTxQ(), * NetTCP_TxConnReTxQ(), * NetTCP_RxPktConnHandlerReTxQ(). * * Note(s) : none. ********************************************************************************************************* */ /*$PAGE*/ static void NetTCP_TxConnReTxQ_TimeoutSet (NET_TCP_CONN *pconn, CPU_BOOLEAN re_tx_q_timeout, NET_TCP_CLOSE_CODE close_code, NET_ERR *perr) { #if ((NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) && \ (NET_CTR_CFG_ERR_EN == DEF_ENABLED) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif NET_TMR_TICK timeout_tick; NET_ERR err; #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) /* ------------- VALIDATE TCP CONN STATE -------------- */ switch (pconn->ConnState) { case NET_TCP_CONN_STATE_FREE: NET_CTR_ERR_INC(NetTCP_ErrNotUsedCtr); *perr = NET_TCP_ERR_CONN_NOT_USED; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_FIN_WAIT_2: /* See Note #1b. */ case NET_TCP_CONN_STATE_TIME_WAIT: case NET_TCP_CONN_STATE_CLOSING_DATA_AVAIL: NET_CTR_ERR_INC(NetTCP_ErrConnInvalidOpCtr); *perr = NET_TCP_ERR_INVALID_CONN_OP; return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_STATE_CLOSED: /* See Note #1a. */ case NET_TCP_CONN_STATE_LISTEN: case NET_TCP_CONN_STATE_SYNC_RXD: case NET_TCP_CONN_STATE_SYNC_RXD_PASSIVE: case NET_TCP_CONN_STATE_SYNC_RXD_ACTIVE: case NET_TCP_CONN_STATE_SYNC_TXD: case NET_TCP_CONN_STATE_CONN: case NET_TCP_CONN_STATE_FIN_WAIT_1: case NET_TCP_CONN_STATE_CLOSING: case NET_TCP_CONN_STATE_CLOSE_WAIT: case NET_TCP_CONN_STATE_LAST_ACK: break; case NET_TCP_CONN_STATE_NONE: default: NetTCP_ConnClose((NET_TCP_CONN *)pconn, (NET_BUF_HDR *)0, (CPU_BOOLEAN )DEF_YES, (NET_TCP_CLOSE_CODE)close_code); NET_CTR_ERR_INC(NetTCP_ErrConnInvalidStateCtr); *perr = NET_TCP_ERR_INVALID_CONN_STATE; return; /* Prevent 'break NOT reachable' compiler warning. */ } #endif /* ------------ SET TCP CONN RE-TX Q TMR ------------- */ timeout_tick = pconn->TxRTT_RTO_tick; if ((pconn->ReTxQ_Tmr != (NET_TMR *)0) && /* If re-tx Q tmr avail .. */ (re_tx_q_timeout == DEF_NO)) { /* .. but NOT timed out, .. */ NetTmr_Set((NET_TMR *) pconn->ReTxQ_Tmr, /* .. reset re-tx Q tmr. */ (CPU_FNCT_PTR) NetTCP_TxConnReTxQ_Timeout, (NET_TMR_TICK) timeout_tick, (NET_ERR *)&err); } else { /* Else get re-tx Q tmr. */ pconn->ReTxQ_Tmr = NetTmr_Get((void *) pconn, (CPU_FNCT_PTR) NetTCP_TxConnReTxQ_Timeout, (NET_TMR_TICK) timeout_tick, (CPU_INT16U ) NET_TMR_FLAG_NONE, (NET_ERR *)&err); } if ( err != NET_TMR_ERR_NONE) { /* #### On err, handle re-tx Q?; close TCP conn? */ NetTCP_ConnClose((NET_TCP_CONN *)pconn, (NET_BUF_HDR *)0, (CPU_BOOLEAN )pconn->ConnCloseAppFlag, (NET_TCP_CLOSE_CODE)close_code); *perr = NET_TCP_ERR_CONN_FAULT; return; } *perr = NET_TCP_ERR_NONE; } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxConnPrepareSegAddrs() * * Description : Prepare TCP transmit segment addresses from TCP connection addresses. * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_TxConnAppData(); * checked in NetTCP_TxConnSync(), * NetTCP_TxConnClose(), * NetTCP_TxConnAck(), * NetTCP_TxConnReset(), * NetTCP_TxConnProbe(). * * psrc_port Pointer to variable buffer that will receive the return source port number * --------- (see Note #1), if NO errors. * * Argument validated in NetTCP_TxConnAppData(), * NetTCP_TxConnSync(), * NetTCP_TxConnClose(), * NetTCP_TxConnAck(), * NetTCP_TxConnReset(), * NetTCP_TxConnProbe(). * * psrc_addr Pointer to variable buffer that will receive the return source address * --------- (see Note #1), if NO errors. * * Argument validated in NetTCP_TxConnAppData(), * NetTCP_TxConnSync(), * NetTCP_TxConnClose(), * NetTCP_TxConnAck(), * NetTCP_TxConnReset(), * NetTCP_TxConnProbe(). * * src_port_len Size of the variable buffer that will receive the return source port number. * * src_addr_len Size of the variable buffer that will receive the return source address. * * pdest_port Pointer to variable buffer that will receive the return destination port number * ---------- (see Note #1), if NO errors. * * Argument validated in NetTCP_TxConnAppData(), * NetTCP_TxConnSync(), * NetTCP_TxConnClose(), * NetTCP_TxConnAck(), * NetTCP_TxConnReset(), * NetTCP_TxConnProbe(). * * pdest_addr Pointer to variable buffer that will receive the return destination address * ---------- (see Note #1), if NO errors. * * Argument validated in NetTCP_TxConnAppData(), * NetTCP_TxConnSync(), * NetTCP_TxConnClose(), * NetTCP_TxConnAck(), * NetTCP_TxConnReset(), * NetTCP_TxConnProbe(). * * dest_port_len Size of the variable buffer that will receive the return destination port number. * * dest_addr_len Size of the variable buffer that will receive the return destination address. * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_NONE TCP connection addresses successfully * prepared. * * NET_CONN_ERR_INVALID_FAMILY Invalid connection family. * NET_CONN_ERR_INVALID_ADDR Invalid TCP connection address. * NET_CONN_ERR_INVALID_ADDR_LEN Invalid TCP connection address length. * * Return(s) : none. * * Caller(s) : NetTCP_TxConnAppData(), * NetTCP_TxConnSync(), * NetTCP_TxConnClose(), * NetTCP_TxConnAck(), * NetTCP_TxConnReset(), * NetTCP_TxConnProbe(). * * Note(s) : (1) Variable buffers to receive the returned port & address values may start on any CPU address, * word-aligned or not. * * See also 'net_util.h NETWORK DATA VALUE MACRO'S Note #2b'. * * (2) The 'NET_CONN_CFG_FAMILY' pre-processor 'else'-conditional code will never be compiled/linked * since 'net_conn.h' ensures that the family type configuration constant (NET_CONN_CFG_FAMILY) * is configured with an appropriate family type value (see 'net_conn.h CONFIGURATION ERRORS'). * The 'else'-conditional code is included for completeness & as an extra precaution in case * 'net_conn.h' is incorrectly modified. ********************************************************************************************************* */ /*$PAGE*/ static void NetTCP_TxConnPrepareSegAddrs (NET_TCP_CONN *pconn, CPU_INT08U *psrc_port, CPU_INT08U *psrc_addr, CPU_INT16U src_port_len, CPU_INT16U src_addr_len, CPU_INT08U *pdest_port, CPU_INT08U *pdest_addr, CPU_INT16U dest_port_len, CPU_INT16U dest_addr_len, NET_ERR *perr) { NET_CONN_ID conn_id; NET_CONN_ADDR_LEN addr_len; CPU_INT08U addr[NET_CONN_CFG_ADDR_LEN]; CPU_INT08U port[NET_CONN_CFG_ADDR_LEN]; CPU_INT08U addr_local[NET_CONN_CFG_ADDR_LEN]; CPU_INT08U addr_remote[NET_CONN_CFG_ADDR_LEN]; NET_ERR err; #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) /* ---------------- VALIDATE ADDR LENS ---------------- */ #if (NET_CONN_CFG_FAMILY == NET_CONN_FAMILY_IP_V4_SOCK) if (src_port_len != NET_CONN_ADDR_IP_LEN_PORT) { *perr = NET_CONN_ERR_INVALID_ADDR_LEN; return; } if (src_addr_len != NET_CONN_ADDR_IP_LEN_ADDR) { *perr = NET_CONN_ERR_INVALID_ADDR_LEN; return; } if (dest_port_len != NET_CONN_ADDR_IP_LEN_PORT) { *perr = NET_CONN_ERR_INVALID_ADDR_LEN; return; } if (dest_addr_len != NET_CONN_ADDR_IP_LEN_ADDR) { *perr = NET_CONN_ERR_INVALID_ADDR_LEN; return; } #else /* See Note #2. */ NetTCP_ConnClose((NET_TCP_CONN *)pconn, (NET_BUF_HDR *)0, (CPU_BOOLEAN )DEF_YES, (NET_TCP_CLOSE_CODE)NET_TCP_CONN_CLOSE_ALL); *perr = NET_CONN_ERR_INVALID_FAMILY; return; #endif #else /* Prevent compiler warnings. */ (void)&src_port_len; (void)&src_addr_len; (void)&dest_port_len; (void)&dest_addr_len; #endif /*$PAGE*/ /* ------------------ PREPARE ADDRS ------------------- */ conn_id = pconn->ID_Conn; addr_len = sizeof(addr_local); NetConn_AddrLocalGet((NET_CONN_ID ) conn_id, /* Get local/src addr. */ (CPU_INT08U *)&addr_local[0], (NET_CONN_ADDR_LEN *)&addr_len, (NET_ERR *)&err); if (err != NET_CONN_ERR_NONE) { *perr = NET_CONN_ERR_INVALID_ADDR; return; } #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) if (addr_len != NET_CONN_CFG_ADDR_LEN) { NetTCP_ConnClose((NET_TCP_CONN *)pconn, (NET_BUF_HDR *)0, (CPU_BOOLEAN )DEF_YES, (NET_TCP_CLOSE_CODE)NET_TCP_CONN_CLOSE_ALL); *perr = NET_CONN_ERR_INVALID_ADDR_LEN; return; } #endif addr_len = sizeof(addr_remote); NetConn_AddrRemoteGet((NET_CONN_ID ) conn_id, /* Get remote/dest addr. */ (CPU_INT08U *)&addr_remote[0], (NET_CONN_ADDR_LEN *)&addr_len, (NET_ERR *)&err); if (err != NET_CONN_ERR_NONE) { *perr = NET_CONN_ERR_INVALID_ADDR; return; } #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) if (addr_len != NET_CONN_CFG_ADDR_LEN) { NetTCP_ConnClose((NET_TCP_CONN *)pconn, (NET_BUF_HDR *)0, (CPU_BOOLEAN )DEF_YES, (NET_TCP_CLOSE_CODE)NET_TCP_CONN_CLOSE_ALL); *perr = NET_CONN_ERR_INVALID_ADDR_LEN; return; } #endif /* Prepare src/dest addrs (see Note #1). */ #if (NET_CONN_CFG_FAMILY == NET_CONN_FAMILY_IP_V4_SOCK) Mem_Copy((void *)&port[0], (void *)&addr_local[NET_CONN_ADDR_IP_IX_PORT], (CPU_SIZE_T) NET_CONN_ADDR_IP_LEN_PORT); Mem_Copy((void *)&addr[0], (void *)&addr_local[NET_CONN_ADDR_IP_IX_ADDR], (CPU_SIZE_T) NET_CONN_ADDR_IP_LEN_ADDR); NET_UTIL_VAL_COPY_GET_NET_16(psrc_port, &port[0]); NET_UTIL_VAL_COPY_GET_NET_32(psrc_addr, &addr[0]); Mem_Copy((void *)&port[0], (void *)&addr_remote[NET_CONN_ADDR_IP_IX_PORT], (CPU_SIZE_T) NET_CONN_ADDR_IP_LEN_PORT); Mem_Copy((void *)&addr[0], (void *)&addr_remote[NET_CONN_ADDR_IP_IX_ADDR], (CPU_SIZE_T) NET_CONN_ADDR_IP_LEN_ADDR); NET_UTIL_VAL_COPY_GET_NET_16(pdest_port, &port[0]); NET_UTIL_VAL_COPY_GET_NET_32(pdest_addr, &addr[0]); #else /* See Note #2. */ NetTCP_ConnClose((NET_TCP_CONN *)pconn, (NET_BUF_HDR *)0, (CPU_BOOLEAN )DEF_YES, (NET_TCP_CLOSE_CODE)NET_TCP_CONN_CLOSE_ALL); *perr = NET_CONN_ERR_INVALID_FAMILY; return; #endif *perr = NET_TCP_ERR_NONE; } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxConnRTT_Init() * * Description : Initialize TCP connection's transmit round-trip time (RTT) controls. * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_TxConnRTT_Reset(), * NetTCP_TxConnRTT_RTO_Init(). * * Return(s) : none. * * Caller(s) : NetTCP_TxConnRTT_Reset(), * NetTCP_TxConnRTT_RTO_Init(). * * Note(s) : (1) (A) RFC #1122, Section 4.2.3.1 states that "the following values SHOULD be * used to initialize the estimation parameters for a new connection" : * * (a) RTT = 0 seconds * * where * RTT Round-Trip Time * * (1) Furthermore, RFC #1122, Section 4.2.3.1.(b) states that "the * smoothed variance is to be initialized to the value that will * result in" these values. * * (B) However, since RFC #2988, Section 2.2 amends the RFC #1122, Section * 4.2.3.1 RTT initialization; the smoothed RTT average & deviation do * NOT truly require explicit initialization. Nonetheless, these RTT * values are initialized to conform with RFC #1122, Section 4.2.3.1. ********************************************************************************************************* */ static void NetTCP_TxConnRTT_Init (NET_TCP_CONN *pconn) { /* Init RTT (see Note #1). */ pconn->TxRTT_Avg_ms_scaled = NET_TCP_TX_RTT_AVG_INIT_MS_SCALED; /* Init RTT avg (see Note #1Aa). */ pconn->TxRTT_Dev_ms_scaled = NET_TCP_TX_RTT_DEV_INIT_MS_SCALED; /* Init RTT dev (see Note #1A1). */ NetTCP_TxConnRTT_CalcUpdate(pconn); } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxConnRTT_Reset() * * Description : Reset TCP connection's transmit round-trip time (RTT) controls. * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_TxConnRTT_RTO_Calc(). * * Return(s) : none. * * Caller(s) : NetTCP_TxConnRTT_RTO_Calc(). * * Note(s) : (1) (a) RFC #2988, Section 5 states "that a TCP implementation MAY clear SRTT and * RTTVAR after backing off the timer multiple times as it is likely that the * current SRTT and RTTVAR are bogus in this situation. Once SRTT and RTTVAR * are cleared they should be initialized with the next RTT sample taken". * * (b) RFC #2581, Section 4.1 states that "after TCP has been idle for ... an * interval exceeding the retransmission timeout ... use slow start to * restart transmission". * * Similarly, although NO RFC specifies that a TCP connection's RTT average & * deviation should be reset following a TCP transmit idle timeout; it seems * reasonable to reset a TCP connection's RTT average & deviation controls * whenever a TCP connection's transmit is idle for a period exceeding the * re-transmit timeout. * * See also 'NetTCP_TxConnRTT_RTO_Calc() Note #2a4'. ********************************************************************************************************* */ static void NetTCP_TxConnRTT_Reset (NET_TCP_CONN *pconn) { NetTCP_TxConnRTT_Init(pconn); pconn->TxRTT_RTO_State = NET_TCP_TX_RTT_RTO_STATE_RESET; } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxConnRTT_CalcUpdate() * * Description : Update TCP connection's transmit round-trip time (RTT) control calculations. * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_TxConnRTT_Init(), * NetTCP_TxConnRTT_RTO_Calc(). * * Return(s) : none. * * Caller(s) : NetTCP_TxConnRTT_Init(), * NetTCP_TxConnRTT_RTO_Calc(). * * Note(s) : (1) A TCP connection's transmit round-trip time controls should NOT be updated until * after the following TCP connection control(s) have been configured : * * (a) TCP connection's transmit round-trip time average (in scaled milliseconds) * ['TxRTT_Avg_ms_scaled'] * * (b) TCP connection's transmit round-trip time deviation (in scaled milliseconds) * ['TxRTT_Dev_ms_scaled'] ********************************************************************************************************* */ static void NetTCP_TxConnRTT_CalcUpdate (NET_TCP_CONN *pconn) { pconn->TxRTT_Avg_ms = (NET_TCP_TIMEOUT_MS)(pconn->TxRTT_Avg_ms_scaled / NET_TCP_TX_RTT_SCALE); pconn->TxRTT_Dev_ms = (NET_TCP_TIMEOUT_MS)(pconn->TxRTT_Dev_ms_scaled / NET_TCP_TX_RTT_SCALE); } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxConnRTO_Init() * * Description : (1) Initialize TCP connection's re-transmit timeout (RTO) controls : * * (a) Initialize RTO See Note #2 * (b) Configure RTO maximum timeout See Note #3 * * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_TxConnRTT_RTO_Init(). * * Return(s) : none. * * Caller(s) : NetTCP_TxConnRTT_RTO_Init(). * * Note(s) : (2) RFC #1122, Section 4.2.3.1 states that "the following values SHOULD be * used to initialize the estimation parameters for a new connection" : * * (b) RTO = 3 seconds * * where * RTO Retransmission Timeout * * (A) RFC #2988, Section 2.1 reiterates that "until a round-trip time (RTT) * measurement has been made ... the sender SHOULD set RTO <- 3 seconds". * * (3) A TCP connection's re-transmit timeout controls should NOT be updated until * after the following TCP connection control(s) have been configured : * * (a) TCP connection's maximum re-transmit timeout (in seconds) ['TxRTT_RTO_Max_sec'] * [see 'NetTCP_TxConnRTO_CfgMaxTimeout() Note #2a'] ********************************************************************************************************* */ static void NetTCP_TxConnRTO_Init (NET_TCP_CONN *pconn) { pconn->TxRTT_RTO_ms_scaled = NET_TCP_TX_RTT_RTO_INIT_MS_SCALED; /* Init RTO (see Note #2). */ NetTCP_TxConnRTO_CalcUpdate(pconn); NetTCP_TxConnRTO_CfgMaxTimeout(pconn); /* Cfg RTO max timeout (see Note #3). */ } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxConnRTO_CfgMaxTimeout() * * Description : Configure TCP connection's maximum re-transmit timeout (RTO). * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_TxConnRTO_Init(), * NetTCP_ConnCfgReTxMaxTimeout(). * * Return(s) : none. * * Caller(s) : NetTCP_TxConnRTO_Init(), * NetTCP_ConnCfgReTxMaxTimeout(). * * Note(s) : (1) (a) RFC #2988, Section 2.4 states that "a maximum value MAY be placed on RTO provided * it is at least 60 seconds". * * (b) RFC #1122, Section 4.2.3.1 states that "the recommended ... RTO ... upper bound * should be 2*MSL". * * (c) Stevens, TCP/IP Illustrated, Volume 1, 8th Printing, Section 21.2, Page 299 states * that "the timeout value ... [has] an upper limit of 64 seconds". * * (2) A TCP connection's maximum re-transmit timeouts (in scaled milliseconds or milliseconds) * should NOT be updated until after the following TCP connection control(s) have been * configured : * * (a) TCP connection's maximum re-transmit timeout (in seconds) ['TxRTT_RTO_Max_sec'] * [see 'NetTCP_ConnClr() Note #11' * & 'NetTCP_ConnCfgReTxMaxTimeout() Note #1'] ********************************************************************************************************* */ static void NetTCP_TxConnRTO_CfgMaxTimeout (NET_TCP_CONN *pconn) { pconn->TxRTT_RTO_Max_ms_scaled = (NET_TCP_TX_RTT_MS_SCALED)pconn->TxRTT_RTO_Max_sec * NET_TCP_TX_RTT_MS_SCALE; pconn->TxRTT_RTO_Max_ms = (NET_TCP_TIMEOUT_MS )pconn->TxRTT_RTO_Max_sec * DEF_TIME_NBR_mS_PER_SEC; } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxConnRTO_CalcUpdate() * * Description : Update TCP connection's re-transmit timeout (RTO) control calculations. * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_TxConnRTO_Init(), * NetTCP_TxConnRTT_RTO_Calc(). * * Return(s) : none. * * Caller(s) : NetTCP_TxConnRTO_Init(), * NetTCP_TxConnRTT_RTO_Calc(). * * Note(s) : (1) A TCP connection's re-transmit timeout controls should NOT be updated until * after the following TCP connection control(s) have been configured : * * (a) TCP connection's re-transmit timeout (in scaled milliseconds) * ['TxRTT_RTO_ms_scaled'] ********************************************************************************************************* */ static void NetTCP_TxConnRTO_CalcUpdate (NET_TCP_CONN *pconn) { pconn->TxRTT_RTO_ms = (NET_TCP_TIMEOUT_MS )(pconn->TxRTT_RTO_ms_scaled / NET_TCP_TX_RTT_SCALE); pconn->TxRTT_RTO_sec = (NET_TCP_TIMEOUT_SEC)(pconn->TxRTT_RTO_ms_scaled / NET_TCP_TX_RTT_MS_SCALE); pconn->TxRTT_RTO_tick = ((NET_TMR_TICK ) pconn->TxRTT_RTO_ms * NET_TMR_TIME_TICK_PER_SEC) / DEF_TIME_NBR_mS_PER_SEC; } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxConnRTO_CalcBackOff() * * Description : Calculate next backed-off re-transmit timeout (RTO) value. * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_TxConnRTT_RTO_Calc(), * NetTCP_TxConnWinSizeZeroWinHandler(). * * rto_ms Current re-transmit timeout value (in milliseconds). * * Return(s) : Backed-off re-transmit timeout value (in milliseconds). * * Caller(s) : NetTCP_TxConnRTT_RTO_Calc(), * NetTCP_TxConnWinSizeZeroWinHandler(). * * Note(s) : (1) (a) RFC #1122, Section 4.2.3.1 states that an "implementation MUST also include * 'exponential backoff' for successive RTO values for the same segment". * * (1) RFC #2988, Section 5.5 states that "when the retransmission timer expires * ... the host MUST set RTO <- RTO * 2 ('back off the timer')". * * Thus, the TCP retransmission timer exponential back-off scalar value is 2. * * (2) Stevens, TCP/IP Illustrated, Volume 1, 8th Printing, Section 21.2, Page 299 * reiterates that "this doubling is called an 'exponential backoff'". * * (b) (1) RFC #2988, Section 2.4 adds that "a maximum value MAY be placed on RTO * provided it is at least 60 seconds". * * (2) RFC #1122, Section 4.2.3.1 states that "the recommended ... RTO ... upper * bound should be 2*MSL". * * (3) Stevens, TCP/IP Illustrated, Volume 1, 8th Printing, Section 21.2, Page 299 * states that "the timeout value is doubled for each retransmission, with an * upper limit of 64 seconds". * * See also 'NetTCP_TxConnRTO_CfgMaxTimeout() Note #1'. * * (2) RFC #1122, Section 4.2.2.17 states that "zero-window probe[s] ... SHOULD increase * exponentially the interval between successive probes ... Exponential backoff is * recommended ... similar to ... the retransmission algorithm, and it may be possible * to combine the two procedures in the implementation". ********************************************************************************************************* */ static NET_TCP_TIMEOUT_MS NetTCP_TxConnRTO_CalcBackOff (NET_TCP_CONN *pconn, NET_TCP_TIMEOUT_MS rto_ms) { NET_TCP_TIMEOUT_MS rto_ms_backoff_calcd; NET_TCP_TIMEOUT_MS rto_ms_backoff; /* Calc backed-off RTO timeout value (see Note #1a). */ rto_ms_backoff_calcd = (rto_ms < pconn->TxRTT_RTO_Max_ms) ? (rto_ms * NET_TCP_TX_RTO_TIMEOUT_BACKOFF_SCALAR) : pconn->TxRTT_RTO_Max_ms; /* Limit backed-off RTO timeout value (see Note #1b). */ rto_ms_backoff = DEF_MIN(rto_ms_backoff_calcd, pconn->TxRTT_RTO_Max_ms); return (rto_ms_backoff); } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxConnRTT_RTO_Init() * * Description : Initialize TCP connection's transmit round-trip time (RTT) & re-transmit timeout (RTO) values. * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_ConnCfg(). * * Return(s) : none. * * Caller(s) : NetTCP_ConnCfg(). * * Note(s) : (1) A TCP connection's re-transmit timeout controls should NOT be updated until * after the following TCP connection control(s) have been configured : * * (a) TCP connection's maximum re-transmit timeout (in seconds) ['TxRTT_RTO_Max_sec'] * [see 'NetTCP_TxConnRTO_Init() Note #3'] ********************************************************************************************************* */ static void NetTCP_TxConnRTT_RTO_Init (NET_TCP_CONN *pconn) { NetTCP_TxConnRTT_Init(pconn); NetTCP_TxConnRTO_Init(pconn); pconn->TxRTT_RTO_State = NET_TCP_TX_RTT_RTO_STATE_INIT; } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxConnRTT_RTO_Calc() * * Description : (1) Calculate TCP connection's transmit round-trip time (RTT) & re-transmit timeout (RTO) values : * * (a) Perform requested RTT/RTO calculation operation(s) * (b) Prepare & perform RTT calculations * (c) Prepare & perform RTO calculations * * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_RxPktConnHandlerReTxQ(), * NetTCP_TxConnReTxQ(), * NetTCP_TxConnTxQ_TimeoutIdle(). * * calc_code Indicate which TCP connection's transmit round-trip time calculations to perform * (see Note #1a) : * * NET_TCP_CONN_TX_RTT_INIT Initialize TCP connection's transmit * round-trip time controls * (see 'NetTCP_TxConnRTT_Init() Note #1' * & 'NetTCP_TxConnRTO_Init() Note #2'). * NET_TCP_CONN_TX_RTT_RESET Reset TCP connection's transmit * round-trip time controls. * * NET_TCP_CONN_TX_RTT_CALC Calculate & update TCP connection's transmit * round-trip time controls. * * NET_TCP_CONN_TX_RTT_BACKOFF Back-off & update TCP connection's transmit * round-trip time controls for a TCP re- * transmit timeout. * * rtt_ts_txd_ms Round-trip timestamp when transmitted (in milliseconds). * * rtt_ts_rxd_ms Round-trip timestamp when received (in milliseconds). * * Return(s) : none. * * Caller(s) : NetTCP_RxPktConnHandlerReTxQ(), * NetTCP_TxConnReTxQ(), * NetTCP_TxConnTxQ_TimeoutIdle(). *$PAGE* * Note(s) : (2) (a) (1) RFC #2988, Section 2 states that "the rules governing the computation of SRTT * (smoothed round-trip time), RTTVAR RTTVAR (round-trip time variation), and RTO * are as follows" : * * (A) RFC #2988, Section 2.2 states that for "the first RTT measurement R ... * the host MUST set" : * * (1) SRTT <- R * (2) RTTVAR <- R/2 * (3) RTO <- SRTT + max(G, K * RTTVAR) * * where * SRTT RTT Smoothed Average * RTTVAR RTT Variance/Deviation * RTO Retransmission Timeout * R RTT First Measurement * (4) R = R * 1 RTT First Average Gain * (5) R/2 = R * 1/2 RTT First Deviation Gain * G RTT Clock Granularity (resolution) * (6) K = 4 RTT-RTO Gain * * * (B) RFC #2988, Section 2.3 states that for "subsequent RTT measurement R' ... * a host MUST set" : * * (1) RTTVAR <- (1 - beta ) * RTTVAR + beta * |SRTT - R'| * (2) SRTT <- (1 - alpha) * SRTT + alpha * R' * (3) RTO <- SRTT + max(G, K * RTTVAR) * * where * SRTT RTT Smoothed Average * RTTVAR RTT Variance/Deviation * RTO Retransmission Timeout * R' RTT Subsequent Measurement(s) * (4) alpha = 1/8 RTT-Average Gain * (5) beta = 1/4 RTT-Deviation Gain * G RTT Clock Granularity (resolution) * (6) K = 4 RTT-RTO Gain * * * (a) RFC #2988, Section 2.3 states tht "updating RTTVAR and SRTT MUST be * computed in ... order ... [since] the value of SRTT used in the update * to RTTVAR is its value before updating SRTT itself". * * (b) To reduce the total number of operations for both RTT calculations, * the equations SHOULD be factored & rearranged as follows : * * (1) (A) RTTVAR <- (1 - beta) * RTTVAR + beta * |SRTT - R'| * * (B) RTTVAR <- RTTVAR - beta * RTTVAR + beta * |SRTT - R'| * * (C) RTTVAR <- RTTVAR + beta * (|SRTT - R'| - RTTVAR) * * (D) RTTVAR <- RTTVAR + beta * (|R' - SRTT| - RTTVAR) * * * (2) (A) SRTT <- (1 - alpha) * SRTT + alpha * R' * * (B) SRTT <- SRTT - alpha * SRTT + alpha * R' * * (C) SRTT <- SRTT + alpha * (R' - SRTT) * * * (2) (A) RFC #793, Section 3.7 'Data Communication : Retransmission Timeout' states * that "the Round Trip Time (RTT) ... [is] the elapsed time between" : * * (1) "sending a data octet with a particular sequence number and" ... * (2) "receiving an acknowledgment that covers that sequence number" ... * (3) "(segments sent do not have to match segments received)". * * (B) (1) RFC #2988, Section 3 adds that : * * (a) "Traditionally, TCP implementations have taken one RTT measurement at * a time (typically once per RTT)." * (b) "A TCP implementation MUST take at least one RTT measurement per RTT * (unless that is not possible per Karn's algorithm) [see Note #2a5]". * (c) "For fairly modest congestion window sizes research suggests that * timing each segment does not lead to a better RTT estimator." * (d) "Additionally, when multiple samples are taken per RTT the alpha and * beta ... may keep an inadequate RTT history." * * (2) RFC #2988, Section 1 states that "in some situations it may be beneficial * for a TCP sender to be more conservative than the algorithms detailed in * this document allow. However, a TCP MUST NOT be more aggressive than the * ... algorithms allow". * * Thus, the following TCP algorithm(s) are permitted; even if the algorithms * delay or decrease the number of received acknowledgements, which thereby * increases the measured time values for RTT samples : * * (a) Karn's Algorithm See Note #2a5 * (b) TCP Delayed Acknowledgements See 'NetTCP_TxConnAck() Note #6' *$PAGE* * (3) Jacobson/Karels, "Congestion Avoidance and Control", Appendix A.2 states that RTT * calculations "should be done in integer arithmetic". RFC #2988, Section 2.3 adds * that RTT calculations "SHOULD be computed using ... 1/8 and ... 1/4" gains (see * Notes #2a1B4 & #2a1B5). * * (4) (A) RFC #2988, Section 5 states "that a TCP implementation MAY clear SRTT and * RTTVAR after backing off the timer multiple times as it is likely that the * current SRTT and RTTVAR are bogus in this situation. Once SRTT and RTTVAR * are cleared they should be initialized with the next RTT sample taken per * [Note #2a1A] rather than using [Note #2a1B]". * * (B) RFC #2581, Section 4.1 states that "after TCP has been idle for ... an * interval exceeding the retransmission timeout ... use slow start to * restart transmission". * * Similarly, although NO RFC specifies that a TCP connection's RTT average & * deviation should be reset following a TCP transmit idle timeout; it seems * reasonable to reset a TCP connection's RTT average & deviation controls * whenever a TCP connection's transmit is idle for a period exceeding the * re-transmit timeout. * * See also 'NetTCP_TxConnTxQ_TimeoutIdle() Note #2a'. * * (5) RFC #2988, Section 3 states that "TCP MUST use Karn's algorithm ... for * taking RTT samples. That is, RTT samples MUST NOT be made using segments * that were retransmitted (and thus for which it is ambiguous whether the reply * was for the first instance of the packet or a later instance)". * * (b) (1) (A) (1) RFC #2988, Section 2.4 states that "whenever RTO is computed, if it * is less than 1 second then the RTO SHOULD be rounded up to 1 second". * * (a) This amends RFC #1122, Section 4.2.3.1 which previously stated * that "the recommended ... RTO ... lower bound ... SHOULD be * measured in fractions of a second". * * (2) RFC #2988, Section 4 states that "there is no requirement for the * clock granularity G used for computing RTT measurements ... However, * if the K*RTTVAR term in the RTO calculation equals zero, the variance * term MUST be rounded to G seconds". * * See also Notes #2a1A3 & #2a1B3. * * (B) (1) RFC #2988, Section 2.4 adds that "a maximum value MAY be placed on * RTO provided it is at least 60 seconds". * * (2) RFC #1122, Section 4.2.3.1 states that "the recommended ... RTO ... * upper bound should be 2*MSL". * * (3) Stevens, TCP/IP Illustrated, Volume 1, 8th Printing, Section 21.2, * Page 299 states that "the timeout value ... [has] an upper limit of * 64 seconds". * * See also 'net_tcp.c NetTCP_TxConnRTO_CfgMaxTimeout() Note #1'. * * (2) RFC #1122, Section 4.2.3.1 reiterates that an "implementation MUST also * include 'exponential backoff' for successive RTO values for the same * segment". * * (a) RFC #2988, Section 5.5 states that "when the retransmission timer * expires ... the host MUST set RTO <- RTO * 2 ('back off the timer')". * * Thus, the TCP retransmission timer exponential back-off scalar * value is 2. * * (b) Stevens, TCP/IP Illustrated, Volume 1, 8th Printing, Section 21.2, * Page 299 reiterates that "this doubling is called an 'exponential * backoff'". * * See also 'NetTCP_TxConnRTO_CalcBackOff() Note #1'. ********************************************************************************************************* */ static void NetTCP_TxConnRTT_RTO_Calc (NET_TCP_CONN *pconn, NET_TCP_CALC_CODE calc_code, NET_TCP_TX_RTT_TS_MS rtt_ts_txd_ms, NET_TCP_TX_RTT_TS_MS rtt_ts_rxd_ms) { NET_TCP_TIMEOUT_MS timeout_ms; NET_TMR_TICK timeout_tick; NET_TCP_TX_RTT_MS rtt_cur_ms; NET_TCP_TX_RTT_MS_SCALED rtt_cur_ms_scaled; NET_TCP_TX_RTT_MS_SCALED rtt_err_ms_scaled; NET_TCP_TX_RTT_MS_SCALED rtt_err_ms_scaled_abs; NET_TCP_TX_RTT_MS_SCALED rtt_avg_ms_scaled; NET_TCP_TX_RTT_MS_SCALED rtt_dev_ms_scaled; NET_TCP_TX_RTT_MS_SCALED rtt_dev_ms_scaled_gain; NET_TCP_TX_RTT_MS_SCALED rtt_dev_ms_scaled_min; NET_TCP_TX_RTT_MS_SCALED rto_ms_scaled; /*$PAGE*/ /* -------------- PERFORM CALC CODE --------------- */ switch (calc_code) { case NET_TCP_CONN_TX_RTT_CALC: break; case NET_TCP_CONN_TX_RTT_BACKOFF: /* Back-off RTO (see Note #2b2). */ timeout_ms = (NET_TCP_TIMEOUT_MS ) NetTCP_TxConnRTO_CalcBackOff(pconn, pconn->TxRTT_RTO_ms); timeout_tick = ((NET_TMR_TICK ) timeout_ms * NET_TMR_TIME_TICK_PER_SEC) / DEF_TIME_NBR_mS_PER_SEC; pconn->TxRTT_RTO_ms = (NET_TCP_TIMEOUT_MS ) timeout_ms; pconn->TxRTT_RTO_sec = (NET_TCP_TIMEOUT_SEC)(timeout_ms / DEF_TIME_NBR_mS_PER_SEC); pconn->TxRTT_RTO_tick = (NET_TMR_TICK ) timeout_tick; /* 'break' intentionally omitted; do NOT move from */ /* ... following case : 'NET_TCP_CONN_TX_RTT_RESET'.*/ case NET_TCP_CONN_TX_RTT_RESET: /* See Note #2a4. */ NetTCP_TxConnRTT_Reset(pconn); return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_TCP_CONN_TX_RTT_INIT: default: NetTCP_TxConnRTT_RTO_Init(pconn); return; /* Prevent 'break NOT reachable' compiler warning. */ } /* -------------- PREPARE RTT CALCS --------------- */ switch (pconn->TxRTT_RTO_State) { case NET_TCP_TX_RTT_RTO_STATE_INIT: case NET_TCP_TX_RTT_RTO_STATE_RESET: case NET_TCP_TX_RTT_RTO_STATE_CALC: rtt_cur_ms = (NET_TCP_TX_RTT_MS )(rtt_ts_rxd_ms - rtt_ts_txd_ms); rtt_cur_ms_scaled = (NET_TCP_TX_RTT_MS_SCALED)(rtt_cur_ms * NET_TCP_TX_RTT_SCALE); break; case NET_TCP_TX_RTT_RTO_STATE_NONE: case NET_TCP_TX_RTT_RTO_STATE_RE_TX: /* See Note #2a5. */ default: return; /* Prevent 'break NOT reachable' compiler warning. */ } /* -------------- PERFORM RTT CALCS --------------- */ switch (pconn->TxRTT_RTO_State) { case NET_TCP_TX_RTT_RTO_STATE_INIT: /* Init RTT calcs (see Notes #2a1A1 & #2a1A2). */ case NET_TCP_TX_RTT_RTO_STATE_RESET: rtt_avg_ms_scaled = (rtt_cur_ms_scaled * NET_TCP_TX_RTT_GAIN_AVG_INIT_NUMER) / NET_TCP_TX_RTT_GAIN_AVG_INIT_DENOM; rtt_dev_ms_scaled = (rtt_cur_ms_scaled * NET_TCP_TX_RTT_GAIN_DEV_INIT_NUMER) / NET_TCP_TX_RTT_GAIN_DEV_INIT_DENOM; pconn->TxRTT_RTO_State = NET_TCP_TX_RTT_RTO_STATE_CALC; break; case NET_TCP_TX_RTT_RTO_STATE_CALC: /* Update RTT calcs (see Notes #2a1Bb1D & #2a1Bb2C).*/ rtt_err_ms_scaled = rtt_cur_ms_scaled - pconn->TxRTT_Avg_ms_scaled; rtt_err_ms_scaled_abs = DEF_ABS(rtt_err_ms_scaled); rtt_dev_ms_scaled = pconn->TxRTT_Dev_ms_scaled + (((rtt_err_ms_scaled_abs - pconn->TxRTT_Dev_ms_scaled) * NET_TCP_TX_RTT_GAIN_DEV_NUMER) / NET_TCP_TX_RTT_GAIN_DEV_DENOM); rtt_avg_ms_scaled = pconn->TxRTT_Avg_ms_scaled + ((rtt_err_ms_scaled * NET_TCP_TX_RTT_GAIN_AVG_NUMER) / NET_TCP_TX_RTT_GAIN_AVG_DENOM); break; case NET_TCP_TX_RTT_RTO_STATE_NONE: case NET_TCP_TX_RTT_RTO_STATE_RE_TX: /* See Note #2a5. */ default: return; /* Prevent 'break NOT reachable' compiler warning. */ } /* ------------------ UPDATE RTT ------------------ */ pconn->TxRTT_Avg_ms_scaled = rtt_avg_ms_scaled; pconn->TxRTT_Dev_ms_scaled = rtt_dev_ms_scaled; NetTCP_TxConnRTT_CalcUpdate(pconn); /*$PAGE*/ /* ------------------- CALC RTO ------------------- */ /* Calc RTO (see Note #2a1B3). */ rtt_dev_ms_scaled_gain = (pconn->TxRTT_Dev_ms_scaled * NET_TCP_TX_RTT_GAIN_RTO_NUMER) / NET_TCP_TX_RTT_GAIN_RTO_DENOM; /* Limit RTT dev (see Note #2b1A2). */ rtt_dev_ms_scaled_min = DEF_MAX(NET_TCP_TX_RTT_TS_CLK_MS_SCALED, rtt_dev_ms_scaled_gain); rto_ms_scaled = pconn->TxRTT_Avg_ms_scaled + rtt_dev_ms_scaled_min; /* Limit RTO (see Note #2b1). */ if (rto_ms_scaled < NET_TCP_TX_RTO_MIN_TIMEOUT_MS_SCALED) { rto_ms_scaled = NET_TCP_TX_RTO_MIN_TIMEOUT_MS_SCALED; } if (rto_ms_scaled > pconn->TxRTT_RTO_Max_ms_scaled) { rto_ms_scaled = pconn->TxRTT_RTO_Max_ms_scaled; } /* ------------------ UPDATE RTO ------------------ */ pconn->TxRTT_RTO_ms_scaled = rto_ms_scaled; NetTCP_TxConnRTO_CalcUpdate(pconn); } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxPktHandler() * * Description : (1) Prepare & transmit TCP packets : * * (a) Validate transmit packet * (b) Transmit TCP packet * (c) Free transmit packet buffer(s) * (d) Update transmit statistics * * * Argument(s) : pbuf Pointer to network buffer to transmit TCP packet. * * src_addr Source IP address. * * src_port Source TCP port. * * dest_addr Destination IP address. * * dest_port Destination TCP port. * * seq_nbr TCP segment sequence number. * * ack_nbr TCP segment acknowledgement sequence number. * * win_size TCP receive window advertisement size. * * TOS Specific TOS to transmit TCP/IP packet * (see 'net_ip.h IP HEADER TYPE OF SERVICE (TOS) DEFINES'). * * TTL Specific TTL to transmit TCP/IP packet (see RFC #1122, Section 3.2.1.7) : * * NET_IP_HDR_TTL_MIN 1 minimum TTL transmit value * NET_IP_HDR_TTL_MAX 255 maximum TTL transmit value * NET_IP_HDR_TTL_DFLT default TTL transmit value * NET_IP_HDR_TTL_NONE 0 replace with default TTL * * flags_tcp Flags to select TCP transmit options; bit-field flags logically OR'd : * * NET_TCP_FLAG_NONE No TCP transmit flags selected. * NET_TCP_FLAG_TX_FIN Set TCP 'FIN' flag. * NET_TCP_FLAG_TX_SYNC Set TCP 'SYN' flag. * NET_TCP_FLAG_TX_RESET Set TCP 'RESET' flag. * NET_TCP_FLAG_TX_PUSH Set TCP 'PUSH' flag. * NET_TCP_FLAG_TX_ACK Set TCP 'ACK' flag. * NET_TCP_FLAG_TX_URGENT Set TCP 'URGENT' flag. * * flags_ip Flags to select IP transmit options; bit-field flags logically OR'd : * * NET_IP_FLAG_NONE No IP transmit flags selected. * NET_IP_FLAG_TX_DONT_FRAG Set IP 'Don't Frag' flag. * * popts_tcp Pointer to one or more TCP options configuration data structures : * * NULL NO TCP transmit options configuration. * NET_TCP_OPT_CFG_MAX_SEG_SIZE TCP Maximum Segment Size options configuration. * * popts_ip Pointer to one or more IP options configuration data structures : * * NULL NO IP transmit options configuration. * NET_IP_OPT_CFG_ROUTE_TS Route &/or Internet Timestamp options configuration. * NET_IP_OPT_CFG_SECURITY Security options configuration * (see 'net_ip.c Note #1f'). * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_NONE TCP segments(s) successfully prepared & transmitted * to IP layer. * NET_TCP_ERR_TX TCP transmit error; TCP segment buffer(s) discarded. * * ----------- RETURNED BY NetTCP_TxPkt() : ----------- * NET_ERR_INIT_INCOMPLETE Network initialization NOT complete. * NET_ERR_TX Transmit error. * * Return(s) : none. * * Caller(s) : NetTCP_TxConnSync(), * NetTCP_TxConnAck(), * NetTCP_TxConnReset(), * NetTCP_TxConnProbe(), * NetTCP_TxConnTxQ(), * NetTCP_TxConnReTxQ_Timeout(). * * Note(s) : (2) Network buffer already freed by lower layer; only increment error counter. ********************************************************************************************************* */ /*$PAGE*/ static void NetTCP_TxPktHandler (NET_BUF *pbuf, NET_IP_ADDR src_addr, NET_TCP_PORT_NBR src_port, NET_IP_ADDR dest_addr, NET_TCP_PORT_NBR dest_port, NET_TCP_SEQ_NBR seq_nbr, NET_TCP_SEQ_NBR ack_nbr, NET_TCP_WIN_SIZE win_size, NET_IP_TOS TOS, NET_IP_TTL TTL, CPU_INT16U flags_tcp, CPU_INT16U flags_ip, void *popts_tcp, void *popts_ip, NET_ERR *perr) { #if (((NET_CTR_CFG_STAT_EN == DEF_ENABLED) || \ (NET_CTR_CFG_ERR_EN == DEF_ENABLED)) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif NET_BUF_HDR *pbuf_hdr; NET_ERR err; #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) /* ------------------- VALIDATE PTR ------------------- */ if (pbuf == (NET_BUF *)0) { NetTCP_TxPktDiscard(pbuf, perr); NET_CTR_ERR_INC(NetTCP_ErrNullPtrCtr); return; } #endif /* --------------- VALIDATE TX TCP PKT ---------------- */ pbuf_hdr = &pbuf->Hdr; #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) NetTCP_TxPktValidate(pbuf, pbuf_hdr, src_port, dest_port, seq_nbr, ack_nbr, win_size, flags_tcp, popts_tcp, perr); switch (*perr) { case NET_TCP_ERR_NONE: break; case NET_ERR_INVALID_PROTOCOL: case NET_BUF_ERR_INVALID_IX: case NET_TCP_ERR_INVALID_LEN_DATA: case NET_TCP_ERR_INVALID_PORT_NBR: case NET_TCP_ERR_INVALID_FLAG: case NET_TCP_ERR_INVALID_OPT_TYPE: case NET_TCP_ERR_INVALID_OPT_CFG: default: NetTCP_TxPktDiscard(pbuf, &err); *perr = NET_TCP_ERR_TX; return; /* Prevent 'break NOT reachable' compiler warning. */ } #endif /*$PAGE*/ /* -------------------- TX TCP PKT -------------------- */ NetTCP_TxPkt(pbuf, pbuf_hdr, src_addr, src_port, dest_addr, dest_port, seq_nbr, ack_nbr, win_size, TOS, TTL, flags_tcp, flags_ip, popts_tcp, popts_ip, perr); /* ---------- FREE TX PKT / UPDATE TX STATS ----------- */ switch (*perr) { case NET_IP_ERR_NONE: NET_CTR_STAT_INC(NetTCP_StatTxSegCtr); *perr = NET_TCP_ERR_NONE; break; case NET_ERR_TX: NET_CTR_ERR_INC(NetTCP_ErrTxPktDiscardedCtr); /* See Note #2. */ return; /* Prevent 'break NOT reachable' compiler warning. */ case NET_ERR_INIT_INCOMPLETE: case NET_TCP_ERR_INVALID_LEN_HDR: case NET_TCP_ERR_INVALID_OPT_TYPE: case NET_TCP_ERR_INVALID_OPT_LEN: case NET_BUF_ERR_INVALID_IX: case NET_BUF_ERR_INVALID_LEN: case NET_UTIL_ERR_NULL_PTR: case NET_UTIL_ERR_NULL_SIZE: case NET_UTIL_ERR_INVALID_PROTOCOL: default: NetTCP_TxPktDiscard(pbuf, &err); *perr = NET_TCP_ERR_TX; return; /* Prevent 'break NOT reachable' compiler warning. */ } } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxPktValidate() * * Description : (1) Validate TCP transmit packet parameters & options : * * (a) Packets with the following invalid parameters will be "silently discarded" : * * (1) Protocols other than supported protocols : * (A) BSD Sockets * (B) TCP See Note #2 * * (2) Data Length * (3) Source Port * (4) Destination Port * (5) Flags * * (b) The following parameters are inherently assumed to be valid : * * (1) Sequence Number * (2) Acknowledgement Number * (3) Receive Window Size * * * Argument(s) : pbuf Pointer to network buffer to transmit TCP packet. * ---- Argument checked in NetTCP_TxPktHandler(). * * pbuf_hdr Pointer to network buffer header. * -------- Argument validated in NetTCP_TxPktHandler(). * * src_port Source TCP port. * * dest_port Destination TCP port. * * seq_nbr TCP segment sequence number. * * ack_nbr TCP segment acknowledgement sequence number. * * win_size TCP receive window advertisement size. * * flags_tcp Flags to select TCP transmit options; bit-field flags logically OR'd : * * NET_TCP_FLAG_NONE No TCP transmit flags selected. * NET_TCP_FLAG_TX_FIN Set TCP 'FIN' flag. * NET_TCP_FLAG_TX_SYNC Set TCP 'SYN' flag. * NET_TCP_FLAG_TX_RESET Set TCP 'RESET' flag. * NET_TCP_FLAG_TX_PUSH Set TCP 'PUSH' flag. * NET_TCP_FLAG_TX_ACK Set TCP 'ACK' flag. * NET_TCP_FLAG_TX_URGENT Set TCP 'URGENT' flag. * * popts_tcp Pointer to one or more TCP options configuration data structures : * * NULL NO TCP transmit options configuration. * NET_TCP_OPT_CFG_MAX_SEG_SIZE TCP Maximum Segment Size options configuration. * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_NONE Transmit packet validated. * NET_ERR_INVALID_PROTOCOL Invalid/unknown protocol type. * NET_BUF_ERR_INVALID_IX Invalid or insufficient buffer index. * NET_TCP_ERR_INVALID_LEN_DATA Invalid protocol/data length. * NET_TCP_ERR_INVALID_PORT_NBR Invalid TCP port number. * NET_TCP_ERR_INVALID_FLAG Invalid TCP flag(s). * * - RETURNED BY NetTCP_TxPktValidateOpt() : - * NET_TCP_ERR_INVALID_OPT_TYPE Invalid TCP option type. * NET_TCP_ERR_INVALID_OPT_CFG Invalid TCP option configuration. * * Return(s) : none. * * Caller(s) : NetTCP_TxPktHandler(). * * Note(s) : none. ********************************************************************************************************* */ /*$PAGE*/ #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) static void NetTCP_TxPktValidate (NET_BUF *pbuf, NET_BUF_HDR *pbuf_hdr, NET_TCP_PORT_NBR src_port, NET_TCP_PORT_NBR dest_port, NET_TCP_SEQ_NBR seq_nbr, NET_TCP_SEQ_NBR ack_nbr, NET_TCP_WIN_SIZE win_size, CPU_INT16U flags_tcp, void *popts_tcp, NET_ERR *perr) { #if ((NET_CTR_CFG_ERR_EN == DEF_ENABLED) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif CPU_INT16U ix; CPU_INT16U len; CPU_INT16U flag_mask; CPU_BOOLEAN flags_tcp_fin_syn; /* ----------------- VALIDATE PROTOCOL ---------------- */ switch (pbuf_hdr->ProtocolHdrType) { case NET_PROTOCOL_TYPE_APP: case NET_PROTOCOL_TYPE_SOCK: case NET_PROTOCOL_TYPE_TCP: ix = (CPU_INT16U)pbuf_hdr->DataIx; len = (CPU_INT16U)pbuf_hdr->DataLen; break; case NET_PROTOCOL_TYPE_NONE: default: NET_CTR_ERR_INC(NetTCP_ErrTxInvalidProtocolCtr); *perr = NET_ERR_INVALID_PROTOCOL; return; /* Prevent 'break NOT reachable' compiler warning. */ } if (ix == NET_BUF_IX_NONE) { NET_CTR_ERR_INC(NetTCP_ErrTxInvalidBufIxCtr); *perr = NET_BUF_ERR_INVALID_IX; return; } if (ix < NET_TCP_HDR_SIZE_MAX) { NET_CTR_ERR_INC(NetTCP_ErrTxInvalidBufIxCtr); *perr = NET_BUF_ERR_INVALID_IX; return; } /* -------------- VALIDATE TOT DATA LEN --------------- */ if (len != pbuf_hdr->TotLen) { NET_CTR_ERR_INC(NetTCP_ErrTxHdrDataLenCtr); *perr = NET_TCP_ERR_INVALID_LEN_DATA; return; } /* ---------------- VALIDATE TCP PORTS ---------------- */ if (src_port == NET_TCP_PORT_NBR_RESERVED) { NET_CTR_ERR_INC(NetTCP_ErrTxHdrPortSrcCtr); *perr = NET_TCP_ERR_INVALID_PORT_NBR; return; } if (dest_port == NET_TCP_PORT_NBR_RESERVED) { NET_CTR_ERR_INC(NetTCP_ErrTxHdrPortDestCtr); *perr = NET_TCP_ERR_INVALID_PORT_NBR; return; } /*$PAGE*/ /* ---------------- VALIDATE TCP FLAGS ---------------- */ flag_mask = NET_TCP_FLAG_NONE | NET_TCP_FLAG_TX_CLOSE | NET_TCP_FLAG_TX_SYNC | NET_TCP_FLAG_TX_RESET | NET_TCP_FLAG_TX_PUSH | NET_TCP_FLAG_TX_ACK | NET_TCP_FLAG_TX_URGENT; if ((flags_tcp & ~flag_mask) != NET_TCP_FLAG_NONE) { /* If any invalid flags req'd, rtn err. */ NET_CTR_ERR_INC(NetTCP_ErrTxHdrFlagsCtr); *perr = NET_TCP_ERR_INVALID_FLAG; return; } #if 1 /* ???? Allow invalid 'SYN'/'FIN' flag combo? */ flag_mask = NET_TCP_FLAG_TX_SYNC | NET_TCP_FLAG_TX_FIN; flags_tcp_fin_syn = DEF_BIT_IS_SET(flags_tcp, flag_mask); if (flags_tcp_fin_syn != DEF_NO) { /* If invalid 'SYN'/'FIN' flag combo req'd, rtn err. */ NET_CTR_ERR_INC(NetTCP_ErrTxHdrFlagsCtr); *perr = NET_TCP_ERR_INVALID_FLAG; return; } #endif /* ---------------- VALIDATE TCP OPTS ----------------- */ if (popts_tcp != (void *)0) { NetTCP_TxPktValidateOpt(popts_tcp, flags_tcp, perr); if (*perr != NET_TCP_ERR_NONE) { return; } } /* ------------- IGNORE VALID TCP FIELDS -------------- */ (void)&seq_nbr; /* Prevent compiler warnings. */ (void)&ack_nbr; (void)&win_size; *perr = NET_TCP_ERR_NONE; } #endif /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxPktValidateOpt() * * Description : Validate TCP transmit option configurations. * * (1) TCP transmit options MUST be configured by appropriate transmit options configuration * data structure(s) passed via 'popts_tcp'; see 'net_tcp.h TCP HEADER OPTION CONFIGURATION * DATA TYPES' for TCP options configuration. * * (2) TCP header allows for a maximum option size of 40 octets (see 'net_tcp.h TCP HEADER * OPTIONS DEFINES Note #3'). * * * Argument(s) : popts_tcp Pointer to one or more TCP options configuration data structures (see Note #1) : * * NULL NO TCP transmit options configuration. * NET_TCP_OPT_CFG_MAX_SEG_SIZE TCP Maximum Segment Size options configuration. * * flags_tcp Flags to select TCP transmit options; bit-field flags logically OR'd : * * NET_TCP_FLAG_NONE No TCP transmit flags selected. * NET_TCP_FLAG_TX_FIN Set TCP 'FIN' flag. * NET_TCP_FLAG_TX_SYNC Set TCP 'SYN' flag. * NET_TCP_FLAG_TX_RESET Set TCP 'RESET' flag. * NET_TCP_FLAG_TX_PUSH Set TCP 'PUSH' flag. * NET_TCP_FLAG_TX_ACK Set TCP 'ACK' flag. * NET_TCP_FLAG_TX_URGENT Set TCP 'URGENT' flag. * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_NONE TCP transmit option configurations validated. * NET_TCP_ERR_INVALID_OPT_TYPE Invalid TCP option type. * NET_TCP_ERR_INVALID_OPT_CFG Invalid TCP option configuration. * * Return(s) : none. * * Caller(s) : NetTCP_TxPktValidate(). * * Note(s) : (3) (a) See 'net_tcp.h TCP HEADER OPTIONS DEFINES' for supported TCP options' summary. * * (b) See 'net_tcp.c Note #1c' for unsupported TCP options. * * (4) The following TCP transmit options MUST be configured exclusively--i.e. only a single * of each of the following TCP options may be configured for any one TCP segment : * * (a) NET_TCP_OPT_CFG_TYPE_MAX_SEG_SIZE ********************************************************************************************************* */ /*$PAGE*/ #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) static void NetTCP_TxPktValidateOpt (void *popts_tcp, CPU_INT16U flags_tcp, NET_ERR *perr) { #if ((NET_CTR_CFG_ERR_EN == DEF_ENABLED) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif CPU_INT08U opt_len_size; CPU_INT08U opt_len; CPU_INT08U opt_nbr_max_seg_size; NET_TYPE *popt_cfg_type; void *popt_cfg; void *popt_next; opt_len_size = 0; opt_nbr_max_seg_size = 0; popt_cfg = popts_tcp; while (popt_cfg != (void *)0) { popt_cfg_type = (NET_TYPE *)popt_cfg; switch (*popt_cfg_type) { case NET_TCP_OPT_CFG_TYPE_MAX_SEG_SIZE: /* ----------------- MAX SEG SIZE OPT ----------------- */ if (opt_nbr_max_seg_size > 0) { /* If > 1 max seg size opt, rtn err. */ NET_CTR_ERR_INC(NetTCP_ErrTxHdrOptCfgCtr); *perr = NET_TCP_ERR_INVALID_OPT_CFG; return; } opt_nbr_max_seg_size++; NetTCP_TxPktValidateOptMaxSegSize(popt_cfg, &opt_len, &popt_next, flags_tcp, perr); break; #if 0 /* --------------- UNSUPPORTED TCP OPTS --------------- */ /* See Note #3b. */ case NET_TCP_OPT_CFG_TYPE_WIN_SCALE: case NET_TCP_OPT_CFG_TYPE_SACK_PERMIT: case NET_TCP_OPT_CFG_TYPE_SACK: case NET_TCP_OPT_CFG_TYPE_ECHO_REQ: case NET_TCP_OPT_CFG_TYPE_ECHO_REPLY: case NET_TCP_OPT_CFG_TYPE_TS: /* 'break' intentionally omitted; do NOT move from the */ /* ... following case : 'NET_TCP_OPT_CFG_TYPE_NONE'. */ #endif case NET_TCP_OPT_CFG_TYPE_NONE: /* ----------------- INVALID TCP OPTS ----------------- */ default: NET_CTR_ERR_INC(NetTCP_ErrTxOptTypeCtr); *perr = NET_TCP_ERR_INVALID_OPT_TYPE; return; /* Prevent 'break NOT reachable' compiler warning. */ } if (*perr != NET_TCP_ERR_NONE) { return; } opt_len_size += opt_len; if (opt_len_size > NET_TCP_HDR_OPT_SIZE_MAX) { /* If tot opt len exceeds max opt len, rtn err. */ NET_CTR_ERR_INC(NetTCP_ErrTxHdrOptLenCtr); *perr = NET_TCP_ERR_INVALID_OPT_LEN; return; } popt_cfg = popt_next; /* Validate next cfg opt. */ } *perr = NET_TCP_ERR_NONE; } #endif /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxPktValidateOptMaxSegSize() * * Description : Validate TCP Maximum Segment Size option configuration. * * (1) See 'net_tcp.h TCP MAXIMUM SEGMENT SIZE OPTION CONFIGURATION DATA TYPE' for valid * TCP Maximum Segment Size option configuration. * * (2) Return option values. * * * Argument(s) : popt_tcp Pointer to TCP Maximum Segment Size option configuration data structure. * -------- Argument checked in NetTCP_TxPktValidateOpt(). * * popt_len Pointer to variable that will receive the TCP Maximum Segment Size option length * -------- (in octets). * * Argument validated in NetTCP_TxPktValidateOpt(). * * popt_next Pointer to variable that will receive the pointer to the next TCP transmit option. * --------- Argument validated in NetTCP_TxPktValidateOpt(). * * flags_tcp Flags to select TCP transmit options; bit-field flags logically OR'd : * * NET_TCP_FLAG_NONE No TCP transmit flags selected. * NET_TCP_FLAG_TX_FIN Set TCP 'FIN' flag. * NET_TCP_FLAG_TX_SYNC Set TCP 'SYN' flag. * NET_TCP_FLAG_TX_RESET Set TCP 'RESET' flag. * NET_TCP_FLAG_TX_PUSH Set TCP 'PUSH' flag. * NET_TCP_FLAG_TX_ACK Set TCP 'ACK' flag. * NET_TCP_FLAG_TX_URGENT Set TCP 'URGENT' flag. * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_NONE TCP Maximum Segment Size option * configuration validated. * NET_TCP_ERR_INVALID_OPT_TYPE Invalid TCP option type. * NET_TCP_ERR_INVALID_OPT_CFG Invalid TCP option configuration. * * Return(s) : none. * * Caller(s) : NetTCP_TxPktValidateOpt(). * * Note(s) : (3) RFC #793, Section 3.1 'Header Format : Options : Maximum Segment Size' states that a * TCP Maximum Segment Size option "must only be sent in the initial connection request * (i.e., in segments with the SYN control bit set)". ********************************************************************************************************* */ /*$PAGE*/ #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) static void NetTCP_TxPktValidateOptMaxSegSize (void *popt_tcp, CPU_INT08U *popt_len, void **popt_next, CPU_INT16U flags_tcp, NET_ERR *perr) { #if ((NET_CTR_CFG_ERR_EN == DEF_ENABLED) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif NET_TCP_OPT_CFG_MAX_SEG_SIZE *popt_cfg_max_seg_size; CPU_BOOLEAN flags_tcp_syn; popt_cfg_max_seg_size = (NET_TCP_OPT_CFG_MAX_SEG_SIZE *)popt_tcp; /* ------------------ VALIDATE TYPE ------------------- */ if (popt_cfg_max_seg_size->Type != NET_TCP_OPT_CFG_TYPE_MAX_SEG_SIZE) { NET_CTR_ERR_INC(NetTCP_ErrTxOptTypeCtr); *perr = NET_TCP_ERR_INVALID_OPT_TYPE; return; } /* -------------- VALIDATE MAX SEG SIZE --------------- */ /* If max seg size > max, rtn err. */ if (popt_cfg_max_seg_size->MaxSegSize > NET_TCP_MAX_SEG_SIZE_MAX) { NET_CTR_ERR_INC(NetTCP_ErrTxHdrOptCfgCtr); *perr = NET_TCP_ERR_INVALID_OPT_CFG; return; } /* -------------- VALIDATE OPT CFG/CTRL --------------- */ flags_tcp_syn = DEF_BIT_IS_SET(flags_tcp, NET_TCP_HDR_FLAG_SYNC); if (flags_tcp_syn != DEF_YES) { /* If 'SYN' bit NOT set, rtn err (see Note #3). */ NET_CTR_ERR_INC(NetTCP_ErrTxHdrOptCfgCtr); *perr = NET_TCP_ERR_INVALID_OPT_CFG; return; } /* ------------------- RTN OPT VALS ------------------- */ *popt_len = NET_TCP_HDR_OPT_LEN_MAX_SEG_SIZE; *popt_next = popt_cfg_max_seg_size->NextOptPtr; *perr = NET_TCP_ERR_NONE; } #endif /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxPkt() * * Description : (1) Prepare TCP header & transmit TCP packet to IP layer : * * (a) Prepare TCP options (if any) * (b) Calculate TCP header buffer controls * (c) Prepare TCP header * (d) Transmit TCP packet * * * Argument(s) : pbuf Pointer to network buffer to transmit TCP packet. * ---- Argument checked in NetTCP_TxPktHandler(). * * pbuf_hdr Pointer to network buffer header. * -------- Argument validated in NetTCP_TxPktHandler(). * * src_addr Source IP address. * * src_port Source TCP port. * -------- Argument checked in NetTCP_TxPktValidate(). * * dest_addr Destination IP address. * * dest_port Destination TCP port. * --------- Argument checked in NetTCP_TxPktValidate(). * * seq_nbr TCP segment sequence number. * ------- Argument validated in NetTCP_TxPktValidate(). * * ack_nbr TCP segment acknowledgement sequence number. * ------- Argument validated in NetTCP_TxPktValidate(). * * win_size TCP receive window advertisement size. * -------- Argument validated in NetTCP_TxPktValidate(). * * TOS Specific TOS to transmit TCP/IP packet * (see 'net_ip.h IP HEADER TYPE OF SERVICE (TOS) DEFINES'). * * TTL Specific TTL to transmit TCP/IP packet (see RFC #1122, Section 3.2.1.7) : * * NET_IP_HDR_TTL_MIN 1 minimum TTL transmit value * NET_IP_HDR_TTL_MAX 255 maximum TTL transmit value * NET_IP_HDR_TTL_DFLT default TTL transmit value * NET_IP_HDR_TTL_NONE 0 replace with default TTL * * flags_tcp Flags to select TCP transmit options; bit-field flags logically OR'd : * --------- * NET_TCP_FLAG_NONE No TCP transmit flags selected. * NET_TCP_FLAG_TX_FIN Set TCP 'FIN' flag. * NET_TCP_FLAG_TX_SYNC Set TCP 'SYN' flag. * NET_TCP_FLAG_TX_RESET Set TCP 'RESET' flag. * NET_TCP_FLAG_TX_PUSH Set TCP 'PUSH' flag. * NET_TCP_FLAG_TX_ACK Set TCP 'ACK' flag. * NET_TCP_FLAG_TX_URGENT Set TCP 'URGENT' flag. * * Argument checked in NetTCP_TxPktValidate(). * * flags_ip Flags to select IP transmit options; bit-field flags logically OR'd : * * NET_IP_FLAG_NONE No IP transmit flags selected. * NET_IP_FLAG_TX_DONT_FRAG Set IP 'Don't Frag' flag. * * popts_tcp Pointer to one or more TCP options configuration data structures : * --------- * NULL NO TCP transmit options configuration. * NET_TCP_OPT_CFG_MAX_SEG_SIZE TCP Maximum Segment Size options configuration. * * Argument checked in NetTCP_TxPktValidate(). *$PAGE* * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_INVALID_LEN_HDR Invalid TCP header length. * * - RETURNED BY NetTCP_TxPktPrepareOpt() : - * NET_TCP_ERR_INVALID_OPT_TYPE Invalid TCP option type. * NET_TCP_ERR_INVALID_OPT_LEN Invalid TCP option length. * * - RETURNED BY NetTCP_TxPktPrepareHdr() : - * NET_BUF_ERR_INVALID_IX Invalid/insufficient buffer index. * NET_BUF_ERR_INVALID_LEN Invalid buffer length. * NET_UTIL_ERR_NULL_PTR Check-sum passed a NULL pointer. * NET_UTIL_ERR_NULL_SIZE Check-sum passed a zero size. * NET_UTIL_ERR_INVALID_PROTOCOL Invalid data packet protocol. * * -------- RETURNED BY NetIP_Tx() : -------- * NET_IP_ERR_NONE TCP/IP packet successfully transmitted. * NET_ERR_INIT_INCOMPLETE Network initialization NOT complete. * NET_ERR_TX Transmit error. * * Return(s) : none. * * Caller(s) : NetTCP_TxPktHandler(). * * Note(s) : none. ********************************************************************************************************* */ /*$PAGE*/ static void NetTCP_TxPkt (NET_BUF *pbuf, NET_BUF_HDR *pbuf_hdr, NET_IP_ADDR src_addr, NET_TCP_PORT_NBR src_port, NET_IP_ADDR dest_addr, NET_TCP_PORT_NBR dest_port, NET_TCP_SEQ_NBR seq_nbr, NET_TCP_SEQ_NBR ack_nbr, NET_TCP_WIN_SIZE win_size, NET_IP_TOS TOS, NET_IP_TTL TTL, CPU_INT16U flags_tcp, CPU_INT16U flags_ip, void *popts_tcp, void *popts_ip, NET_ERR *perr) { CPU_INT08U tcp_opt_len_size; CPU_INT16U tcp_hdr_len_size; NET_TCP_OPT_SIZE tcp_hdr_opts[NET_TCP_HDR_OPT_NBR_MAX]; /* ----------------- PREPARE TCP OPTS ----------------- */ if (popts_tcp != (void *)0) { tcp_opt_len_size = NetTCP_TxPktPrepareOpt((void *) popts_tcp, (CPU_INT08U *)&tcp_hdr_opts[0], (NET_ERR *) perr); if (*perr != NET_TCP_ERR_NONE) { return; } } else { tcp_opt_len_size = 0; } /* ---------------- CALC TCP HDR CTRLS ---------------- */ /* Calc tot TCP hdr len (in octets). */ tcp_hdr_len_size = (CPU_INT16U)(NET_TCP_HDR_SIZE_MIN + tcp_opt_len_size); #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) if (tcp_hdr_len_size > NET_TCP_HDR_SIZE_MAX) { *perr = NET_TCP_ERR_INVALID_LEN_HDR; return; } #endif /* ----------------- PREPARE TCP HDR ------------------ */ NetTCP_TxPktPrepareHdr(pbuf, pbuf_hdr, tcp_hdr_len_size, tcp_opt_len_size, src_addr, src_port, dest_addr, dest_port, seq_nbr, ack_nbr, win_size, flags_tcp, &tcp_hdr_opts[0], perr); if (*perr != NET_TCP_ERR_NONE) { return; } /* -------------------- TX TCP PKT -------------------- */ NetIP_Tx(pbuf, src_addr, dest_addr, TOS, TTL, flags_ip, popts_ip, perr); } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxPktPrepareOpt() * * Description : (1) Prepare TCP header with TCP transmit options : * * (a) Prepare ALL TCP options from configuration * data structure(s) * (b) Pad remaining TCP header octets See RFC #793, Section 3.1 * 'Header Format : Padding' * * (2) TCP transmit options MUST be configured by appropriate options configuration data * structure(s) passed via 'popts_tcp'; see 'net_tcp.h TCP HEADER OPTION CONFIGURATION * DATA TYPES' for TCP options configuration. * * * Argument(s) : popts_tcp Pointer to one or more TCP options configuration data structures : * --------- * NULL NO TCP transmit options configuration. * NET_TCP_OPT_CFG_MAX_SEG_SIZE TCP Maximum Segment Size options configuration. * * Argument checked in NetTCP_TxPkt(). * * popt_hdr Pointer to TCP transmit option buffer to prepare TCP options. * -------- Argument validated in NetTCP_TxPkt(). * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_NONE TCP header options successfully prepared. * NET_TCP_ERR_INVALID_OPT_TYPE Invalid TCP option type. * NET_TCP_ERR_INVALID_OPT_LEN Invalid TCP option length. * * Return(s) : Total TCP option length (in octets), if NO errors. * * 0, otherwise. * * Caller(s) : NetTCP_TxPkt(). * * Note(s) : (3) Transmit arguments & options validated in NetTCP_TxPktValidate()/NetTCP_TxPktValidateOpt() : * (a) Assumes ALL transmit arguments & options are valid. * (b) Assumes total transmit options' lengths are valid. * * (4) TCP header allows for a maximum option size of 40 octets (see 'net_tcp.h TCP HEADER * OPTIONS DEFINES Note #3'). * * (5) (a) RFC # 793, Section 3.1 'Options' states that each option is "a multiple of 8 bits * in length" and "may begin on any octet boundary". * * (b) Since TCP options are NOT required or guaranteed to align multi-octet words on * appropriate word boundaries, ALL TCP options are prepared a single octet at a time. * * (6) Default case already invalidated in NetTCP_TxPktValidateOpt(). However, the default * case is included as an extra precaution in case any of the TCP transmit options types * are incorrectly modified. ********************************************************************************************************* */ /*$PAGE*/ static CPU_INT08U NetTCP_TxPktPrepareOpt (void *popts_tcp, CPU_INT08U *popt_hdr, NET_ERR *perr) { #if ((NET_CTR_CFG_ERR_EN == DEF_ENABLED) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif CPU_INT08U tcp_opt_len_tot; CPU_INT08U tcp_opt_len; CPU_INT08U *popt_cfg_hdr; NET_TYPE *popt_cfg_type; void *popt_next; void *popt_cfg; tcp_opt_len_tot = 0; popt_cfg = popts_tcp; popt_cfg_hdr = popt_hdr; /* ----------------- PREPARE TCP OPTS ----------------- */ while (popt_cfg != (void *)0) { /* Prepare ALL cfg'd TCP opts (see Note #1a). */ popt_cfg_type = (NET_TYPE *)popt_cfg; switch (*popt_cfg_type) { case NET_TCP_OPT_CFG_TYPE_MAX_SEG_SIZE: NetTCP_TxPktPrepareOptMaxSegSize(popt_cfg, popt_cfg_hdr, &tcp_opt_len, &popt_next, perr); break; #if 0 /* --------------- UNSUPPORTED TCP OPTS --------------- */ case NET_TCP_OPT_CFG_TYPE_WIN_SCALE: case NET_TCP_OPT_CFG_TYPE_SACK_PERMIT: case NET_TCP_OPT_CFG_TYPE_SACK: case NET_TCP_OPT_CFG_TYPE_ECHO_REQ: case NET_TCP_OPT_CFG_TYPE_ECHO_REPLY: case NET_TCP_OPT_CFG_TYPE_TS: /* 'break' intentionally omitted; do NOT move from the */ /* ... following case : 'NET_TCP_OPT_CFG_TYPE_NONE'. */ #endif case NET_TCP_OPT_CFG_TYPE_NONE: /* ----------------- INVALID TCP OPTS ----------------- */ default: /* See Note #6. */ NET_CTR_ERR_INC(NetTCP_ErrTxOptTypeCtr); *perr = NET_TCP_ERR_INVALID_OPT_TYPE; return (0); /* Prevent 'break NOT reachable' compiler warning. */ } #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) if (*perr != NET_TCP_ERR_NONE) { /* See Note #3a. */ return (0); } if (tcp_opt_len_tot > NET_TCP_HDR_OPT_SIZE_MAX) { /* See Note #3b. */ NET_CTR_ERR_INC(NetTCP_ErrTxHdrOptLenCtr); *perr = NET_TCP_ERR_INVALID_OPT_LEN; return (0); } #endif tcp_opt_len_tot += tcp_opt_len; popt_cfg_hdr += tcp_opt_len; popt_cfg = popt_next; /* Prepare next cfg opt. */ } /* ------------------- PAD TCP HDR -------------------- */ if (tcp_opt_len_tot > 0) { /* Pad rem'ing TCP hdr octets (see Note #1b). */ while ((tcp_opt_len_tot % NET_TCP_HDR_OPT_SIZE_WORD) && (tcp_opt_len_tot <= NET_TCP_HDR_OPT_SIZE_MAX )) { *popt_cfg_hdr = NET_TCP_HDR_OPT_PAD; popt_cfg_hdr++; tcp_opt_len_tot++; } #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) if (tcp_opt_len_tot > NET_TCP_HDR_OPT_SIZE_MAX) { /* See Note #3b. */ NET_CTR_ERR_INC(NetTCP_ErrTxHdrOptLenCtr); *perr = NET_TCP_ERR_INVALID_OPT_LEN; return (0); } #endif } *perr = NET_TCP_ERR_NONE; return (tcp_opt_len_tot); } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxPktPrepareOptMaxSegSize() * * Description : (1) Prepare TCP header with TCP Maximum Segment Size option : * * (a) Prepare TCP Maximum Segment Size option * (b) Return option values * * * Argument(s) : popts_tcp Pointer to TCP Maximum Segment Size option configuration data structure. * --------- Argument checked in NetTCP_TxPkt(). * * popt_hdr Pointer to TCP transmit option buffer to prepare TCP Maximum Segment Size option. * -------- Argument validated in NetTCP_TxPkt(). * * popt_len Pointer to variable that will receive the returned TCP option length (in octets). * -------- Argument validated in NetTCP_TxPktPrepareOpt(). * * popt_next Pointer to variable that will receive the pointer to the next TCP transmit option. * -------- Argument validated in NetTCP_TxPktPrepareOpt(). * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_NONE TCP Maximum Segment Size option successfully * prepared. * * Return(s) : none. * * Caller(s) : NetTCP_TxPktPrepareOpt(). * * Note(s) : (2) See 'net_tcp.h TCP HEADER OPTIONS DEFINES Note #2b1' for TCP Maximum Segment Size * option summary. * * (3) Transmit arguments & options validated in NetTCP_TxPktValidate()/NetTCP_TxPktValidateOpt() : * (a) Assumes ALL transmit arguments & options are valid. * (b) Assumes total transmit options' lengths are valid. * * (4) (a) RFC # 793, Section 3.1 'Options' states that each option is "a multiple of 8 bits * in length" and "may begin on any octet boundary". * * (b) Since TCP options are NOT required or guaranteed to align multi-octet words on * appropriate word boundaries, ALL TCP options are prepared a single octet at a time. * * (5) #### 'perr' may NOT be necessary (remove if unnecessary). ********************************************************************************************************* */ static void NetTCP_TxPktPrepareOptMaxSegSize (void *popts_tcp, CPU_INT08U *popt_hdr, CPU_INT08U *popt_len, void **popt_next, NET_ERR *perr) { NET_TCP_OPT_CFG_MAX_SEG_SIZE *popt_cfg_max_seg_size; CPU_INT08U *popt_cfg_hdr; CPU_INT08U opt_len; CPU_INT08U max_seg_size_hi; CPU_INT08U max_seg_size_lo; /* ------------- PREPARE MAX SEG SIZE OPT ------------- */ popt_cfg_max_seg_size = (NET_TCP_OPT_CFG_MAX_SEG_SIZE *)popts_tcp; popt_cfg_hdr = popt_hdr; opt_len = NET_TCP_HDR_OPT_LEN_MAX_SEG_SIZE; max_seg_size_hi = (CPU_INT08U)(popt_cfg_max_seg_size->MaxSegSize >> DEF_OCTET_NBR_BITS); max_seg_size_lo = (CPU_INT08U) popt_cfg_max_seg_size->MaxSegSize; *popt_cfg_hdr = NET_TCP_HDR_OPT_MAX_SEG_SIZE; /* Prepare opt type. */ popt_cfg_hdr++; *popt_cfg_hdr = opt_len; /* Prepare opt len. */ popt_cfg_hdr++; *popt_cfg_hdr = max_seg_size_hi; /* Prepare max seg size val. */ popt_cfg_hdr++; *popt_cfg_hdr = max_seg_size_lo; /* ------------------- RTN OPT VALS ------------------- */ *popt_len = opt_len; *popt_next = popt_cfg_max_seg_size->NextOptPtr; *perr = NET_TCP_ERR_NONE; } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxPktPrepareHdr() * * Description : (1) Prepare TCP header : * * (a) Update network buffer's protocol index & length controls. * * (b) Prepare the transmit packet's following TCP header fields : * * (1) Source Port * (2) Destination Port * (3) Sequence Number * (4) Acknowledgement Number * (5) Header Length/Flags * (6) Window Advertisement * (7) Check-Sum See Note #3 * (8) Urgent Pointer See Note #2 * (9) Options * * (c) Convert the following TCP header fields from host-order to network-order : * * (1) Source Port * (2) Destination Port * (3) Sequence Number * (4) Acknowledgement Number * (5) Header Length/Flags * (6) Window Advertisement * (7) Check-Sum See Note #3e * (8) Urgent Pointer See Note #2 * * (d) Get TCP packet RTT timestamp transmitted. See 'NetTCP_TxConnRTT_RTO_Calc() * Note #2a2A1' * *$PAGE* * Argument(s) : pbuf Pointer to network buffer to transmit TCP packet. * ---- Argument checked in NetTCP_TxPktHandler(). * * pbuf_hdr Pointer to network buffer header. * -------- Argument validated in NetTCP_TxPktHandler(). * * tcp_hdr_len_tot Total TCP header length. * --------------- Argument checked in NetTCP_TxPkt(). * * tcp_opt_len_tot Total TCP header options' length. * --------------- Argument checked in NetTCP_TxPktPrepareOpt(). * * src_addr Source IP address. * * src_port Source TCP port. * -------- Argument checked in NetTCP_TxPktValidate(). * * dest_addr Destination IP address. * * dest_port Destination TCP port. * --------- Argument checked in NetTCP_TxPktValidate(). * * seq_nbr TCP segment sequence number. * ------- Argument validated in NetTCP_TxPktValidate(). * * ack_nbr TCP segment acknowledgement sequence number. * ------- Argument validated in NetTCP_TxPktValidate(). * * win_size TCP receive window advertisement size. * -------- Argument validated in NetTCP_TxPktValidate(). * * flags_tcp Flags to select TCP transmit options; bit-field flags logically OR'd : * --------- * NET_TCP_FLAG_NONE No TCP transmit flags selected. * NET_TCP_FLAG_TX_FIN Set TCP 'FIN' flag. * NET_TCP_FLAG_TX_SYNC Set TCP 'SYN' flag. * NET_TCP_FLAG_TX_RESET Set TCP 'RESET' flag. * NET_TCP_FLAG_TX_PUSH Set TCP 'PUSH' flag. * NET_TCP_FLAG_TX_ACK Set TCP 'ACK' flag. * NET_TCP_FLAG_TX_URGENT Set TCP 'URGENT' flag. * * Argument checked in NetTCP_TxPktValidate(). * * ptcp_hdr_opts Pointer to TCP options buffer. * ------------- Argument checked in NetTCP_TxPktPrepareOpt(). * * perr Pointer to variable that will receive the return error code from this function : * * NET_TCP_ERR_NONE TCP header successfully prepared. * * ----------- RETURNED BY NetBuf_DataWr() : ------------ * NET_BUF_ERR_INVALID_IX Invalid buffer index for transmit options. * NET_BUF_ERR_INVALID_LEN Invalid buffer length for transmit options. * * - RETURNED BY NetUtil_16BitOnesCplChkSumDataCalc() : - * NET_UTIL_ERR_NULL_PTR Check-sum passed a NULL pointer. * NET_UTIL_ERR_NULL_SIZE Check-sum passed a zero size. * NET_UTIL_ERR_INVALID_PROTOCOL Invalid data packet protocol. * NET_BUF_ERR_INVALID_IX Invalid buffer index. * * Return(s) : none. * * Caller(s) : NetTCP_TxPkt(). *$PAGE* * Note(s) : (2) Urgent pointer & data NOT supported (see 'net_tcp.c Note #1b'). * * (3) (a) TCP header Check-Sum MUST be calculated AFTER the entire TCP header has been prepared. * In addition, ALL multi-octet words are converted from host-order to network-order * since "the sum of 16-bit integers can be computed in either byte order" [RFC #1071, * Section 2.(B)]. * * (b) TCP header Check-Sum field MUST be cleared to '0' BEFORE the TCP header Check-Sum is * calculated (see RFC #793, Section 3.1 'Header Format : Checksum'). * * (c) (1) In addition to the TCP segment header & data, the TCP Check-Sum calculation * includes "a 96-bit pseudo header conceptually prefixed to the TCP header ... * [which] contains the Source Address, the Destination Address, the Protocol, * and TCP length" (see RFC #793, Section 3.1 'Header Format : Checksum'). * * (2) Since network check-sum functions REQUIRE that 16-bit one's-complement check- * sum calculations be performed on headers & data arranged in network-order (see * 'net_util.c NetUtil_16BitOnesCplChkSumDataCalc() Note #3'), TCP pseudo-header * values MUST be set or converted to network-order. * * (d) RFC #793, Section 3.1 'Header Format : Checksum' specifies that "if a segment contains * an odd number of header and text octets ... the last octet is padded ... with zeros to * form a 16-bit word for checksum purposes". * * See also 'net_util.c NetUtil_16BitSumDataCalc() Note #8'. * * (e) The TCP header Check-Sum field is returned in network-order & MUST NOT be re- * converted back to host-order (see 'net_util.c NetUtil_16BitOnesCplChkSumDataCalc() * Note #4'). ********************************************************************************************************* */ /*$PAGE*/ static void NetTCP_TxPktPrepareHdr (NET_BUF *pbuf, NET_BUF_HDR *pbuf_hdr, CPU_INT16U tcp_hdr_len_tot, CPU_INT08U tcp_opt_len_tot, NET_IP_ADDR src_addr, NET_TCP_PORT_NBR src_port, NET_IP_ADDR dest_addr, NET_TCP_PORT_NBR dest_port, NET_TCP_SEQ_NBR seq_nbr, NET_TCP_SEQ_NBR ack_nbr, NET_TCP_WIN_SIZE win_size, CPU_INT16U flags_tcp, CPU_INT32U *ptcp_hdr_opts, NET_ERR *perr) { NET_TCP_HDR *ptcp_hdr; NET_TCP_PSEUDO_HDR tcp_pseudo_hdr; CPU_INT16U tcp_hdr_len; CPU_INT16U tcp_flags; CPU_INT16U tcp_hdr_len_flags; CPU_INT16U tcp_opt_ix; NET_CHK_SUM tcp_chk_sum; /* ----------------- UPDATE BUF CTRLS ----------------- */ pbuf_hdr->TCP_UDP_HdrLen = tcp_hdr_len_tot; pbuf_hdr->TCP_UDP_HdrDataIx = pbuf_hdr->DataIx - pbuf_hdr->TCP_UDP_HdrLen; pbuf_hdr->TotLen += (NET_BUF_SIZE)pbuf_hdr->TCP_UDP_HdrLen; pbuf_hdr->TCP_UDP_TotLen = (CPU_INT16U )pbuf_hdr->TotLen; pbuf_hdr->TCP_UDP_DataLen = (CPU_INT16U )pbuf_hdr->DataLen; pbuf_hdr->ProtocolHdrType = NET_PROTOCOL_TYPE_TCP; /* ----------------- PREPARE TCP HDR ------------------ */ ptcp_hdr = (NET_TCP_HDR *)&pbuf->Data[pbuf_hdr->TCP_UDP_HdrDataIx]; /* ---------------- PREPARE TCP PORTS ----------------- */ NET_UTIL_VAL_COPY_SET_NET_16(&ptcp_hdr->PortSrc, &src_port); NET_UTIL_VAL_COPY_SET_NET_16(&ptcp_hdr->PortDest, &dest_port); /* --------------- PREPARE TCP SEQ NBRS --------------- */ NET_UTIL_VAL_COPY_SET_NET_32(&ptcp_hdr->SeqNbr, &seq_nbr); NET_UTIL_VAL_COPY_SET_NET_32(&ptcp_hdr->AckNbr, &ack_nbr); /* ------------ PREPARE TCP HDR LEN/FLAGS ------------- */ tcp_hdr_len = pbuf_hdr->TCP_UDP_HdrLen / NET_TCP_HDR_LEN_WORD_SIZE; tcp_hdr_len <<= NET_TCP_HDR_LEN_SHIFT; tcp_flags = NET_TCP_HDR_FLAG_NONE; tcp_flags |= flags_tcp; #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) tcp_hdr_len &= NET_TCP_HDR_LEN_MASK; tcp_flags &= NET_TCP_HDR_FLAG_MASK; #endif tcp_hdr_len_flags = tcp_hdr_len | tcp_flags; NET_UTIL_VAL_COPY_SET_NET_16(&ptcp_hdr->HdrLen_Flags, &tcp_hdr_len_flags); /* ----------------- PREPARE TCP WIN ------------------ */ NET_UTIL_VAL_COPY_SET_NET_16(&ptcp_hdr->WinSize, &win_size); /* -------------- PREPARE TCP URGENT PTR -------------- */ /* See Note #2. */ NET_UTIL_VAL_SET_NET_16(&ptcp_hdr->UrgentPtr, NET_TCP_HDR_URG_PTR_NONE); /*$PAGE*/ /* ----------------- PREPARE TCP OPTS ----------------- */ if (tcp_opt_len_tot > 0) { tcp_opt_ix = pbuf_hdr->TCP_UDP_HdrDataIx + NET_TCP_HDR_OPT_IX; NetBuf_DataWr((NET_BUF *)pbuf, (NET_BUF_SIZE)tcp_opt_ix, (NET_BUF_SIZE)tcp_opt_len_tot, (CPU_INT08U *)ptcp_hdr_opts, (NET_ERR *)perr); #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) if (*perr != NET_BUF_ERR_NONE) { return; } #endif } /* --------------- PREPARE TCP CHK SUM ---------------- */ NET_UTIL_VAL_SET_NET_16(&ptcp_hdr->ChkSum, 0x0000); /* Clr TCP chk sum (see Note #3b). */ /* Cfg TCP chk sum pseudo-hdr (see Note #3c). */ tcp_pseudo_hdr.AddrSrc = (NET_IP_ADDR)NET_UTIL_HOST_TO_NET_32(src_addr); tcp_pseudo_hdr.AddrDest = (NET_IP_ADDR)NET_UTIL_HOST_TO_NET_32(dest_addr); tcp_pseudo_hdr.Zero = (CPU_INT08U )0x00; tcp_pseudo_hdr.Protocol = (CPU_INT08U )NET_IP_HDR_PROTOCOL_TCP; tcp_pseudo_hdr.TotLen = (CPU_INT16U )NET_UTIL_HOST_TO_NET_16(pbuf_hdr->TCP_UDP_TotLen); /* Calc TCP chk sum. */ tcp_chk_sum = NetUtil_16BitOnesCplChkSumDataCalc((void *) pbuf, (void *)&tcp_pseudo_hdr, (CPU_INT16U) NET_TCP_PSEUDO_HDR_SIZE, (NET_ERR *) perr); if (*perr != NET_UTIL_ERR_NONE) { return; } NET_UTIL_VAL_COPY_16(&ptcp_hdr->ChkSum, &tcp_chk_sum); /* Copy TCP chk sum in net order (see Note #3e). */ /* ---------------- GET TCP RTT TX TS ----------------- */ pbuf_hdr->TCP_RTT_TS_Txd_ms = (CPU_INT32U)NetTCP_TxConnRTT_GetTS_ms(); *perr = NET_TCP_ERR_NONE; } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_TxPktFree() * * Description : Free network buffer(s). * * Argument(s) : pbuf_q Pointer to network buffer queue. * * Return(s) : none. * * Caller(s) : NetTCP_RxPktConnHandlerReTxQ(), * NetTCP_TxConnAck(), * NetTCP_TxConnReset(). * * Note(s) : (1) (a) Although TCP Transmit initially requests the network buffer for transmit, the * TCP layer maintains a reference to the buffer for possible retransmission. * * (b) Therefore, even though the network interface layer frees ALL unreferenced * buffers after successful transmission [see 'net_if_pkt.c NetIF_Pkt_TxPktFree()' * or 'net_if_char.c NetIF_Char_TxPktFree()'], the TCP layer MUST free transmit * buffers which are still referenced. ********************************************************************************************************* */ static void NetTCP_TxPktFree (NET_BUF *pbuf_q) { NetBuf_FreeBufQ_PrimList((NET_BUF *)pbuf_q, (NET_CTR *)0); } /* ********************************************************************************************************* * NetTCP_TxPktDiscard() * * Description : On any Transmit packet errors, discard TCP packet(s) & buffer(s). * * Argument(s) : pbuf Pointer to network buffer. * * perr Pointer to variable that will receive the return error code from this function : * * NET_ERR_TX Transmit error. * * Return(s) : none. * * Caller(s) : NetTCP_TxConnAppData(), * NetTCP_TxPktHandler(). * * Note(s) : (1) Since some TCP Transmit packets are passed the network buffer from other TCP functions, * they may NOT be the buffers' only references & MUST therefore check buffers' reference * counter before freeing buffers. ********************************************************************************************************* */ static void NetTCP_TxPktDiscard (NET_BUF *pbuf, NET_ERR *perr) { NET_CTR *pctr; #if (NET_CTR_CFG_ERR_EN == DEF_ENABLED) pctr = (NET_CTR *)&NetTCP_ErrTxPktDiscardedCtr; #else pctr = (NET_CTR *) 0; #endif NetBuf_FreeBufQ_PrimList((NET_BUF *)pbuf, (NET_CTR *)pctr); *perr = NET_ERR_TX; } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_ConnCfg() * * Description : (1) Configure TCP connection's controls : * * (a) Configure TCP connection's connection maximum segment size See Note #2 * (b) Configure TCP connection's receive window controls See Note #3 * (c) Configure TCP connection's transmit window controls See Note #4 * (d) Initialize TCP connection's transmit round-trip time & * re-transmit timeout controls * * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_RxPktConnHandlerListen(), * NetTCP_RxPktConnHandlerSyncTxd(), * NetTCP_ConnClr(). * * cfg_code Select which close action(s) to perform; bit-field flags logically OR'd : * * NET_TCP_CONN_CFG_NONE Perform NO configuration actions. * NET_TCP_CONN_CFG_ALL Perform ALL configuration actions. * * NET_TCP_CONN_CFG_MAX_SEG_SIZE_LOCAL Configure local maximum segment size. * NET_TCP_CONN_CFG_MAX_SEG_SIZE_REMOTE Configure remote maximum segment size. * NET_TCP_CONN_CFG_MAX_SEG_SIZE_CONN Configure connection maximum segment size. * NET_TCP_CONN_CFG_MAX_SEG_SIZE_ALL Configure ALL maximum segment sizes. * * NET_TCP_CONN_CFG_WIN_SIZE_RX Configure receive window size(s). * NET_TCP_CONN_CFG_WIN_SIZE_TX Configure transmit window size(s). * NET_TCP_CONN_CFG_WIN_SIZE_CONN Configure connection window size(s). * NET_TCP_CONN_CFG_WIN_SIZE_ALL Configure ALL window sizes. * * NET_TCP_CONN_CFG_TX_RTT_RTO Configure transmit round-trip time (RTT) * & re-transmit timeout (RTO) control(s). * * See also 'TCP CONNECTION CONFIGURATION CODE DEFINES'. * * Return(s) : none. * * Caller(s) : NetTCP_RxPktConnHandlerListen(), * NetTCP_RxPktConnHandlerSyncTxd(), * NetTCP_ConnClr(). * * Note(s) : (2) A TCP connection's connection maximum segment size should NOT be updated until * after certain other TCP connection control(s) have been configured. * * See also 'NetTCP_ConnCfgMaxSegSize() Note #3'. * * (3) A TCP connection's receive window controls should NOT be updated until after * certain other TCP connection control(s) have been configured. * * See also 'NetTCP_RxConnWinSizeCfg() Note #2'. * * (4) A TCP connection's transmit window controls should NOT be updated until after * certain other TCP connection control(s) have been configured. * * See also 'NetTCP_TxConnWinSizeCfg() Note #2'. * * (5) A TCP connection's transmit round-trip time & re-transmit timeout controls * should NOT be updated until after certain other TCP connection control(s) * have been configured. * * See also 'NetTCP_TxConnRTT_RTO_Init() Note #1'. ********************************************************************************************************* */ static void NetTCP_ConnCfg (NET_TCP_CONN *pconn, NET_TCP_CFG_CODE cfg_code) { CPU_BOOLEAN cfg_conn_max_seg_size; CPU_BOOLEAN cfg_conn_win_size; CPU_BOOLEAN cfg_conn_tx_rtt_rto; cfg_conn_max_seg_size = DEF_BIT_IS_SET(cfg_code, NET_TCP_CONN_CFG_MAX_SEG_SIZE_CONN); if (cfg_conn_max_seg_size == DEF_YES) { NetTCP_ConnCfgMaxSegSize(pconn); /* Cfg conn max seg size (see Note #2). */ } cfg_conn_win_size = DEF_BIT_IS_SET(cfg_code, NET_TCP_CONN_CFG_WIN_SIZE_ALL); if (cfg_conn_win_size == DEF_YES) { NetTCP_RxConnWinSizeCfg(pconn); /* Cfg rx win ctrls (see Note #3). */ NetTCP_TxConnWinSizeCfg(pconn); /* Cfg tx win ctrls (see Note #4). */ } cfg_conn_tx_rtt_rto = DEF_BIT_IS_SET(cfg_code, NET_TCP_CONN_CFG_TX_RTT_RTO); if (cfg_conn_tx_rtt_rto == DEF_YES) { NetTCP_TxConnRTT_RTO_Init(pconn); /* Init tx RTT / RTO ctrls (see Note #5). */ } } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_ConnCfgMaxSegSize() * * Description : Configure TCP connection's maximum segment size. * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_ConnCfg(). * * Return(s) : none. * * Caller(s) : NetTCP_ConnCfg(). * * Note(s) : (1) RFC #1122, Section 4.2.2.6 states that "the maximum size of a segment that TCP really * sends, the 'effective send MSS', MUST be the smaller of the send MSS ... and ... less * than or equal to ... the maximum size ... that can be received". * * (2) In order to avoid transmit window deadlock with a remote host's receive window, the * TCP connection's connection maximum segment size MUST be configured to ensure that * full, maximum-segment-sized segments will transmit even for receive windows less than * the default maximum segment size. * * (a) RFC #1122, Section 4.2.3.4.(3) states to "send data ... if at least a fraction Fs * of the maximum window can be sent ... Fs is a fraction whose recommended value is * 1/2". * * Thus, it seems reasonable to calculate & limit the remote host's maximum window * size by a similar fraction. * * (3) A TCP connection's connection maximum segment size should NOT be updated until after * the following TCP connection control(s) have been configured : * * (a) TCP connection's local maximum segment size ('MaxSegSizeLocal') * [see 'NetTCP_ConnClr() Note #3a'] * * (b) TCP connection's remote maximum segment size ('MaxSegSizeRemote') * [see 'NetTCP_RxPktConnHandlerListen() Note #7' * & 'NetTCP_RxPktConnHandlerSyncTxd() Note #3'] * * (c) TCP connection's maximum transmit remote window size ('TxWinSizeRemoteMax') * [see 'NetTCP_RxPktConnHandlerTxWinRemote() Note #1a2A' * & 'NetTCP_TxConnWinSizeHandlerCongCtrl() Notes #3a2A & #3b'] ********************************************************************************************************* */ static void NetTCP_ConnCfgMaxSegSize (NET_TCP_CONN *pconn) { NET_TCP_WIN_SIZE remote_win_size_th; NET_TCP_SEG_SIZE remote_max_seg_size; /* Calc remote max seg size (see Note #2). */ remote_win_size_th = (NET_TCP_WIN_SIZE)(((CPU_INT32U)pconn->TxWinSizeRemoteMax * NET_TCP_TX_SILLY_WIN_NUMER) / NET_TCP_TX_SILLY_WIN_DENOM); remote_max_seg_size = (NET_TCP_SEG_SIZE)DEF_MIN(pconn->MaxSegSizeRemote, remote_win_size_th); /* Cfg conn max seg size (see Note #1). */ pconn->MaxSegSizeConn = (NET_TCP_SEG_SIZE)DEF_MIN(pconn->MaxSegSizeLocal, remote_max_seg_size); } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_ConnClose() * * Description : (1) Close a TCP connection due to TCP connection fault(s) : * * (a) Update TCP connection close statistic(s) * (b) Transmit TCP connection reset to remote host See Note #4 * (c) Close TCP connection * * * (2) TCP connection closed internally by TCP layer when certain TCP connection parameters * are corrupted or when certain valid TCP connection operations fail. * * (3) Since the mechanisms of TCP connection close are independent of the application layer * close; TCP connection MAY need to close application layer connection(s). * * See also 'NetTCP_ConnCloseHandler() Note #2b'. * * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in various. * * pbuf_hdr Pointer to network buffer header that received TCP packet. * * close_conn_app Indicate whether to close application connection (see Note #3): * * DEF_YES Close application connection. * DEF_NO Do NOT close application connection. * * close_code Select which close action(s) to perform; bit-field flags logically OR'd : * * NET_TCP_CONN_CLOSE_NONE Perform NO close actions. * NET_TCP_CONN_CLOSE_ALL Perform ALL close actions. * * NET_TCP_CONN_CLOSE_CONN_TX_RESET Perform close connection transmit reset. * NET_TCP_CONN_CLOSE_CONN_ALL Perform ALL connection close actions. * * NET_TCP_CONN_CLOSE_TMR_TX_IDLE Close transmit idle timer. * NET_TCP_CONN_CLOSE_TMR_TX_SILLY_WIN Close transmit silly window persist timer. * NET_TCP_CONN_CLOSE_TMR_TX_ZERO_WIN Close transmit zero window persist timer. * NET_TCP_CONN_CLOSE_TMR_TX_ACK_DLY Close transmit acknowledgement delay timer. * NET_TCP_CONN_CLOSE_TMR_RE_TX Close re-transmit timer. * NET_TCP_CONN_CLOSE_TMR_KEEP_ALIVE Close connection keep-alive timer. * NET_TCP_CONN_CLOSE_TMR_TIMEOUT Close connection timer. * NET_TCP_CONN_CLOSE_TMR_ALL Close ALL timers. * * See also 'TCP CONNECTION CLOSE/FREE CODE DEFINES'. * * Return(s) : none. * * Caller(s) : various. * * Note(s) : (4) Although NO RFC directly states to transmit a TCP reset segment when a TCP connection * fault-closes, it is inferred & seems reasonable that a TCP reset segment SHOULD be * transmitted whenever a TCP connection closes abnormally from any of the following * synchronization/connected/closing states : * * (a) SYN-RECEIVED * (b) SYN-SENT * (c) ESTABLISHED * (d) FIN-WAIT-1 * (e) FIN-WAIT-2 * (f) CLOSING * (g) TIME_WAIT * (h) CLOSE-WAIT * (i) LAST-ACK * * See also 'NetTCP_TxConnReset() Note #3b'. * * (5) On any TCP connection handler function fault(s), TCP connection MUST NEVER be re-closed. * * See also 'NetTCP_ConnCloseHandler() Note #4'. ********************************************************************************************************* */ /*$PAGE*/ static void NetTCP_ConnClose (NET_TCP_CONN *pconn, NET_BUF_HDR *pbuf_hdr, CPU_BOOLEAN close_conn_app, NET_TCP_CLOSE_CODE close_code) { #if ((NET_CTR_CFG_ERR_EN == DEF_ENABLED) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif CPU_BOOLEAN tx_reset; NET_ERR err; /* ---------- UPDATE CLOSE STATS ---------- */ NET_CTR_ERR_INC(NetTCP_ErrConnCloseCtr); /* ---------- TX TCP CONN RESET ----------- */ tx_reset = DEF_BIT_IS_SET(close_code, NET_TCP_CONN_CLOSE_CONN_TX_RESET); if (tx_reset == DEF_YES) { /* Tx TCP conn reset (see Note #4). */ NetTCP_TxConnReset((NET_TCP_CONN *) pconn, (NET_BUF_HDR *) pbuf_hdr, (NET_TCP_RESET_CODE) NET_TCP_CONN_TX_RESET_FAULT, (NET_TCP_CLOSE_CODE) NET_TCP_CONN_CLOSE_NONE, /* MUST NOT re-close TCP conn (see Note #5).*/ (NET_ERR *)&err); } /* ------------ CLOSE TCP CONN ------------ */ NetTCP_ConnCloseHandler(pconn, close_conn_app, close_code); } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_ConnCloseHandler() * * Description : (1) Close a TCP connection : * * (a) Close network connection(s) See Note #2 * (b) Free TCP connection * * * (2) (a) TCP connection's network connection(s) MUST be closed BEFORE the TCP connection * is closed/freed. * * (b) TCP connection's application connection(s) : * * (1) MAY be CLOSED &/or reset : * * (A) For the following TCP connection condition(s), the TCP connection MUST * close the application connection(s) : * * (1) (a) Invalid TCP connection parameters : * (1) Invalid TCP connection state(s) * (2) Invalid/corrupted TCP data queue(s) : * (A) Invalid sequence numbers * (B) Invalid segment lengths * * (b) Fatal TCP transmit fault(s) * (c) TCP connection closing fault(s) * * (2) Invalid network connection configuration * * (B) For the following TCP connection condition(s), the TCP connection SHOULD * close the application connection(s) based on its application close flag * ('ConnCloseAppFlag') : * * (1) Valid TCP connection closing states/timeouts * * (C) For the following TCP connection condition(s), the TCP connection MAY -- * but is NOT required to -- close the application connection(s) : * * (1) Initial TCP connection configuration/preparation * * (2) But possibly NOT freed, since some application connections have NO mechanism * or API to close an application's reference to the connection. * * See also specific application connection close function(s) for additional notes. * * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_ConnClose(), * NetTCP_ConnCloseTimeout(), * various. * * close_conn_app Indicate whether to close application connection (see Note #2b): * * DEF_YES Close application connection. * DEF_NO Do NOT close application connection. * * close_code Select which close action(s) to perform; bit-field flags logically OR'd : * * NET_TCP_CONN_CLOSE_NONE Perform NO close actions. * NET_TCP_CONN_CLOSE_ALL Perform ALL close actions. * * NET_TCP_CONN_CLOSE_CONN_TX_RESET Perform close connection transmit reset. * NET_TCP_CONN_CLOSE_CONN_ALL Perform ALL connection close actions. * * NET_TCP_CONN_CLOSE_TMR_TX_IDLE Close transmit idle timer. * NET_TCP_CONN_CLOSE_TMR_TX_SILLY_WIN Close transmit silly window persist timer. * NET_TCP_CONN_CLOSE_TMR_TX_ZERO_WIN Close transmit zero window persist timer. * NET_TCP_CONN_CLOSE_TMR_TX_ACK_DLY Close transmit acknowledgement delay timer. * NET_TCP_CONN_CLOSE_TMR_RE_TX Close re-transmit timer. * NET_TCP_CONN_CLOSE_TMR_KEEP_ALIVE Close connection keep-alive timer. * NET_TCP_CONN_CLOSE_TMR_TIMEOUT Close connection timer. * NET_TCP_CONN_CLOSE_TMR_ALL Close ALL timers. * * See also 'TCP CONNECTION CLOSE/FREE CODE DEFINES'. * * Return(s) : none. * * Caller(s) : NetTCP_ConnClose(), * NetTCP_ConnCloseTimeout(), * various. * * Note(s) : (3) TCP connection free codes are identical to TCP connection close codes. * * See also 'TCP CONNECTION CLOSE/FREE CODE DEFINES Note #2'. * * (4) On any TCP connection close, TCP connection MUST NEVER be re-closed. ********************************************************************************************************* */ /*$PAGE*/ static void NetTCP_ConnCloseHandler (NET_TCP_CONN *pconn, CPU_BOOLEAN close_conn_app, NET_TCP_CLOSE_CODE close_code) { #if ((NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) && \ (NET_CTR_CFG_ERR_EN == DEF_ENABLED) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) CPU_BOOLEAN used; #endif NET_TCP_FREE_CODE free_code; /* ------------ VALIDATE TCP CONN CLOSE ----------- */ #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) used = DEF_BIT_IS_SET(pconn->Flags, NET_TCP_FLAG_USED); if (used != DEF_YES) { NET_CTR_ERR_INC(NetTCP_ErrNotUsedCtr); return; } #endif switch (pconn->ConnState) { case NET_TCP_CONN_STATE_LISTEN: case NET_TCP_CONN_STATE_SYNC_RXD: case NET_TCP_CONN_STATE_SYNC_RXD_PASSIVE: case NET_TCP_CONN_STATE_SYNC_RXD_ACTIVE: case NET_TCP_CONN_STATE_SYNC_TXD: case NET_TCP_CONN_STATE_CONN: case NET_TCP_CONN_STATE_FIN_WAIT_1: case NET_TCP_CONN_STATE_FIN_WAIT_2: case NET_TCP_CONN_STATE_CLOSING: case NET_TCP_CONN_STATE_TIME_WAIT: case NET_TCP_CONN_STATE_CLOSE_WAIT: case NET_TCP_CONN_STATE_LAST_ACK: case NET_TCP_CONN_STATE_CLOSING_DATA_AVAIL: break; case NET_TCP_CONN_STATE_NONE: case NET_TCP_CONN_STATE_FREE: /* MUST NOT re-close TCP conn if already .. */ case NET_TCP_CONN_STATE_CLOSED: /* .. free/closed (see Note #4). */ default: return; /* Prevent 'break NOT reachable' warning. */ } /* ---------------- CLOSE CONN(S) ----------------- */ NetConn_CloseFromTransport(pconn->ID_Conn, close_conn_app); pconn->ID_Conn = NET_CONN_ID_NONE; /* Clr TCP conn's net conn id. */ /* ---------------- FREE TCP CONN ----------------- */ free_code = (NET_TCP_FREE_CODE)close_code; /* See Note #3. */ NetTCP_ConnFreeHandler(pconn, free_code); } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_ConnCloseTimeout() * * Description : Close a TCP connection on timeout. * * Argument(s) : pconn_timeout Pointer to a TCP connection (see Note #1b). * * Return(s) : none. * * Caller(s) : Referenced in NetTCP_ConnReqClose(), * various NetTCP_RxPktConnHandler() functions. * * Note(s) : (1) Ideally, network timer expiration functions could be defined as '[(void) (OBJECT *)]' * type functions -- even though network timer API functions cast expiration functions * to generic 'CPU_FNCT_PTR' type (i.e. '[(void) (void *)]'). * * (a) (1) Unfortunately, ISO-IEC 9899-1999 ANSI-C, Section 6.3.2.3.7 states that "a * pointer to an object ... may be converted to a pointer to a different object * ... [but] if the resulting pointer is not correctly aligned ... the behavior * is undefined". * * And since compilers may NOT correctly convert 'void' pointers to non-'void' * pointer arguments, network timer expiration functions MUST avoid incorrect * pointer conversion behavior between 'void' pointer parameters & non-'void' * pointer arguments & therefore CANNOT be defined as '[(void) (OBJECT *)]'. * * (2) However, Section 6.3.2.3.1 states that "a pointer to void may be converted * to or from a pointer to any ... object ... A pointer to any ... object ... * may be converted to a pointer to void and back again; the result shall * compare equal to the original pointer". * * (b) Therefore, to correctly convert 'void' pointer objects back to appropriate * network object pointer objects, network timer expiration functions MUST : * * (1) Be defined as 'CPU_FNCT_PTR' type (i.e. '[(void) (void *)]'); & ... * (2) Explicitly cast 'void' pointer arguments to specific object pointers; ... * (A) ... in this case, a 'NET_TCP_CONN' pointer. * * See also 'net_tmr.c NetTmr_Get() Note #3'. * * (2) This function is a network timer expiration function : * * (a) (1) For the following connection timer(s) ... : * * (A) TCP connection timeout timer ('TimeoutTmr') * * (2) (A) Clear the timer pointer; ... * (1) Cleared in NetTCP_ConnFreeTmr() via NetTCP_ConnClose(), * NetTCP_ConnCloseHandler(). * * (B) but do NOT re-free the timer. * * (b) Do NOT set the following close timer flag(s) : * * (1) NET_TCP_CONN_CLOSE_TMR_TIMEOUT ********************************************************************************************************* */ /*$PAGE*/ static void NetTCP_ConnCloseTimeout (void *pconn_timeout) { #if ((NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) && \ (NET_CTR_CFG_ERR_EN == DEF_ENABLED) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif NET_TCP_CONN *pconn; NET_TCP_CLOSE_CODE close_code; pconn = (NET_TCP_CONN *)pconn_timeout; /* See Note #1b2A. */ #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) /* ---------------- VALIDATE PTR ------------------ */ if (pconn == (NET_TCP_CONN *)0) { NET_CTR_ERR_INC(NetTCP_ErrNullPtrCtr); return; } /* ---------------- VALIDATE TYPE ----------------- */ if (pconn->Type != NET_TCP_TYPE_CONN) { NET_CTR_ERR_INC(NetTCP_ErrConnInvalidTypeCtr); return; } #endif /* ---------------- CLOSE TCP CONN ---------------- */ close_code = NET_TCP_CONN_CLOSE_ALL; DEF_BIT_CLR(close_code, NET_TCP_CONN_CLOSE_TMR_TIMEOUT); /* See Note #2b1. */ if (pconn->ConnCloseTimeoutFaultFlag != DEF_NO) { /* If TCP conn timeout fault, ... */ /* ... fault-close TCP conn. */ NetTCP_ConnClose((NET_TCP_CONN *)pconn, (NET_BUF_HDR *)0, (CPU_BOOLEAN )pconn->ConnCloseAppFlag, (NET_TCP_CLOSE_CODE)close_code); } else { /* Else close TCP conn. */ NetTCP_ConnCloseHandler((NET_TCP_CONN *)pconn, (CPU_BOOLEAN )pconn->ConnCloseAppFlag, (NET_TCP_CLOSE_CODE)close_code); } } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_ConnClosingTimeoutDataAvail() * * Description : (1) Handle closing TCP connection with available application data : * * (a) If TCP connection's application receive queue is now empty, * close the TCP connection * (b) If TCP connection's application receive queue is still NOT empty * after the time-wait timeout, set the user connection timeout * * See also 'NetTCP_RxPktConnHandlerFinWait1() Note #2f5A2b', * 'NetTCP_RxPktConnHandlerFinWait2() Note #2f5B2', * & 'NetTCP_RxPktConnHandlerClosing() Note #2d2B2a1B2'. * * * Argument(s) : pconn_timeout Pointer to a TCP connection (see Note #2b). * * Return(s) : none. * * Caller(s) : Referenced in NetTCP_RxPktConnHandlerFinWait1(), * NetTCP_RxPktConnHandlerFinWait2(), * NetTCP_RxPktConnHandlerClosing(). * * Note(s) : (2) Ideally, network timer expiration functions could be defined as '[(void) (OBJECT *)]' * type functions -- even though network timer API functions cast expiration functions * to generic 'CPU_FNCT_PTR' type (i.e. '[(void) (void *)]'). * * (a) (1) Unfortunately, ISO-IEC 9899-1999 ANSI-C, Section 6.3.2.3.7 states that "a * pointer to an object ... may be converted to a pointer to a different object * ... [but] if the resulting pointer is not correctly aligned ... the behavior * is undefined". * * And since compilers may NOT correctly convert 'void' pointers to non-'void' * pointer arguments, network timer expiration functions MUST avoid incorrect * pointer conversion behavior between 'void' pointer parameters & non-'void' * pointer arguments & therefore CANNOT be defined as '[(void) (OBJECT *)]'. * * (2) However, Section 6.3.2.3.1 states that "a pointer to void may be converted * to or from a pointer to any ... object ... A pointer to any ... object ... * may be converted to a pointer to void and back again; the result shall * compare equal to the original pointer". * * (b) Therefore, to correctly convert 'void' pointer objects back to appropriate * network object pointer objects, network timer expiration functions MUST : * * (1) Be defined as 'CPU_FNCT_PTR' type (i.e. '[(void) (void *)]'); & ... * (2) Explicitly cast 'void' pointer arguments to specific object pointers; ... * (A) ... in this case, a 'NET_TCP_CONN' pointer. * * See also 'net_tmr.c NetTmr_Get() Note #3'. * * (3) This function is a network timer expiration function : * * (a) (1) For the following connection timer(s) ... : * * (A) TCP connection timeout timer ('TimeoutTmr') * * (2) (A) Clear the timer pointer; ... * (1) Cleared in NetTCP_ConnFreeTmr() via NetTCP_ConnCloseHandler(), * NetTCP_ConnClose(); or * (2) Reset by NetTmr_Get(). * * (B) but do NOT re-free the timer. * * (b) Do NOT set the following close timer flag(s) : * * (1) NET_TCP_CONN_CLOSE_TMR_TIMEOUT * * (4) RFC #793, Section 3.9 'Event Processing : USER TIMEOUT : USER TIMEOUT' states that * "for any state if the user timeout expires, flush all queues, signal the user * 'error : connection aborted due to user timeout' ... [and] enter the CLOSED state". ********************************************************************************************************* */ /*$PAGE*/ static void NetTCP_ConnClosingTimeoutDataAvail (void *pconn_timeout) { #if ((NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) && \ (NET_CTR_CFG_ERR_EN == DEF_ENABLED) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif NET_TCP_CONN *pconn; NET_TCP_CLOSE_CODE close_code; CPU_BOOLEAN data_avail; NET_TCP_TIMEOUT_SEC timeout_sec; NET_TMR_TICK timeout_tick; NET_ERR err; pconn = (NET_TCP_CONN *)pconn_timeout; /* See Note #2b2A. */ close_code = NET_TCP_CONN_CLOSE_ALL; DEF_BIT_CLR(close_code, NET_TCP_CONN_CLOSE_TMR_TIMEOUT); /* See Note #3b1. */ #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) /* ---------------- VALIDATE PTR ------------------ */ if (pconn == (NET_TCP_CONN *)0) { NET_CTR_ERR_INC(NetTCP_ErrNullPtrCtr); return; } /* ---------------- VALIDATE TYPE ----------------- */ if (pconn->Type != NET_TCP_TYPE_CONN) { NET_CTR_ERR_INC(NetTCP_ErrConnInvalidTypeCtr); return; } #endif /* Closing data avail for half-closed conns ONLY. */ data_avail = ((pconn->ConnCloseCode != NET_CONN_CLOSE_HALF ) || ((pconn->RxQ_State == NET_TCP_RX_Q_STATE_CONN_CLOSED) && (pconn->RxQ_App_Head == (NET_BUF *)0))) ? DEF_NO : DEF_YES; if (data_avail != DEF_YES) { /* If NO app data avail, ... */ /* ... close TCP conn (see Note #1a). */ NetTCP_ConnCloseHandler((NET_TCP_CONN *)pconn, (CPU_BOOLEAN )pconn->ConnCloseAppFlag, (NET_TCP_CLOSE_CODE)close_code); } else { /* Else reset user tmr (see Notes #1b & #4). */ timeout_sec = pconn->TimeoutUser_sec; timeout_tick = (NET_TMR_TICK)timeout_sec * NET_TMR_TIME_TICK_PER_SEC; pconn->TimeoutTmr = NetTmr_Get((void *) pconn, (CPU_FNCT_PTR) NetTCP_ConnCloseTimeout, (NET_TMR_TICK) timeout_tick, (CPU_INT16U ) NET_TMR_FLAG_NONE, (NET_ERR *)&err); if (err != NET_TMR_ERR_NONE) { NetTCP_ConnClose((NET_TCP_CONN *)pconn, (NET_BUF_HDR *)0, (CPU_BOOLEAN )pconn->ConnCloseAppFlag, (NET_TCP_CLOSE_CODE)close_code); return; } pconn->ConnCloseAppFlag = DEF_YES; } } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_ConnFreeHandler() * * Description : (1) Free a TCP connection : * * (a) Free TCP connection timers * (b) Free TCP connection packet buffer queues * (c) Clear TCP connection controls * (d) Free TCP connection back to TCP connection pool * (e) Update TCP connection pool statistics * * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_ConnFree(), * NetTCP_ConnClose(). * * free_code Select which free action(s) to perform; bit-field flags logically OR'd : * * NET_TCP_CONN_FREE_TMR_NONE Free NO timers. * NET_TCP_CONN_FREE_TMR_ALL Free ALL timers. * * NET_TCP_CONN_FREE_TMR_TX_IDLE Free transmit idle timer. * NET_TCP_CONN_FREE_TMR_TX_SILLY_WIN Free transmit silly window persist timer. * NET_TCP_CONN_FREE_TMR_TX_ZERO_WIN Free transmit zero window persist timer. * NET_TCP_CONN_FREE_TMR_TX_ACK_DLY Free transmit acknowledgement delay timer. * NET_TCP_CONN_FREE_TMR_RE_TX Free re-transmit timer. * NET_TCP_CONN_FREE_TMR_KEEP_ALIVE Free connection keep-alive timer. * NET_TCP_CONN_FREE_TMR_TIMEOUT Free connection timer. * * See also 'TCP CONNECTION CLOSE/FREE CODE DEFINES'. * * Return(s) : none. * * Caller(s) : NetTCP_ConnFree(), * NetTCP_ConnClose(). * * Note(s) : (2) ALL network resources linked to the TCP connection MUST be freed PRIOR to TCP connection * free or discard so that no network resources are lost. ********************************************************************************************************* */ /*$PAGE*/ static void NetTCP_ConnFreeHandler (NET_TCP_CONN *pconn, NET_TCP_FREE_CODE free_code) { #if ((NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) && \ (NET_CTR_CFG_ERR_EN == DEF_ENABLED) && \ (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)) CPU_SR cpu_sr; #endif CPU_BOOLEAN discard; NET_ERR err; #if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED) /* ---------------- VALIDATE TYPE ----------------- */ if (pconn->Type != NET_TCP_TYPE_CONN) { NetTCP_ConnDiscard(pconn); NET_CTR_ERR_INC(NetTCP_ErrConnInvalidTypeCtr); return; } #endif /* -------------- FREE TCP CONN TMRs -------------- */ NetTCP_ConnFreeTmr(pconn, free_code); /* -------------- FREE TCP CONN Q's --------------- */ NetTCP_ConnFreeBufQ(&pconn->RxQ_Transport_Head, &pconn->RxQ_Transport_Tail); NetTCP_ConnFreeBufQ(&pconn->RxQ_App_Head , &pconn->RxQ_App_Tail ); NetTCP_ConnFreeBufQ(&pconn->TxQ_Head , &pconn->TxQ_Tail ); NetTCP_ConnFreeBufQ(&pconn->ReTxQ_Head , &pconn->ReTxQ_Tail ); discard = DEF_NO; NetOS_TCP_RxQ_Clr(pconn->ID, &err); /* Clr TCP conn rx Q. */ if (err != NET_TCP_ERR_NONE) { /* If TCP conn rx Q NOT clr'd, ... */ discard = DEF_YES; /* ... discard TCP conn (see Note #2b). */ } NetOS_TCP_RxQ_Abort(pconn->ID, &err); /* Abort wait on TCP conn rx Q. */ NetOS_TCP_TxQ_Clr(pconn->ID, &err); /* Clr TCP conn tx Q. */ if (err != NET_TCP_ERR_NONE) { /* If TCP conn tx Q NOT clr'd, ... */ discard = DEF_YES; /* ... discard TCP conn (see Note #2b). */ } NetOS_TCP_TxQ_Abort(pconn->ID, &err); /* Abort wait on TCP conn tx Q. */ /* ---------- DISCARD TCP CONN ON ERR(S) ---------- */ if (discard != DEF_NO) { /* On TCP conn free err(s), ... */ NetTCP_ConnDiscard(pconn); /* ... discard TCP conn (see Note #2a). */ return; } /* ----------------- CLR TCP CONN ----------------- */ pconn->ConnState = NET_TCP_CONN_STATE_FREE; /* Set TCP conn as freed/NOT used. */ DEF_BIT_CLR(pconn->Flags, NET_TCP_FLAG_USED); #if (NET_DBG_CFG_MEM_CLR_EN == DEF_ENABLED) NetTCP_ConnClr(pconn); #endif /* ---------------- FREE TCP CONN ----------------- */ pconn->NextPtr = (void *)NetTCP_ConnPoolPtr; NetTCP_ConnPoolPtr = (NET_TCP_CONN *)pconn; /* ---------- UPDATE TCP CONN POOL STATS ---------- */ NetStat_PoolEntryUsedDec(&NetTCP_ConnPoolStat, &err); } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_ConnFreeTmr() * * Description : Clear TCP connection's timers. * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_ConnFreeHandler(), * various NetTCP_ConnHandler() functions. * * free_code Select which free action(s) to perform; bit-field flags logically OR'd : * * NET_TCP_CONN_FREE_TMR_NONE Free NO timers. * NET_TCP_CONN_FREE_TMR_ALL Free ALL timers. * * NET_TCP_CONN_FREE_TMR_TX_IDLE Free transmit idle timer. * NET_TCP_CONN_FREE_TMR_TX_SILLY_WIN Free transmit silly window persist timer. * NET_TCP_CONN_FREE_TMR_TX_ZERO_WIN Free transmit zero window persist timer. * NET_TCP_CONN_FREE_TMR_TX_ACK_DLY Free transmit acknowledgement delay timer. * NET_TCP_CONN_FREE_TMR_RE_TX Free re-transmit timer. * NET_TCP_CONN_FREE_TMR_KEEP_ALIVE Free connection keep-alive timer. * NET_TCP_CONN_FREE_TMR_TIMEOUT Free connection timer. * * See also 'TCP CONNECTION CLOSE/FREE CODE DEFINES'. * * Return(s) : none. * * Caller(s) : NetTCP_ConnFreeHandler(), * various NetTCP_ConnHandler() functions. * * Note(s) : none. ********************************************************************************************************* */ /*$PAGE*/ static void NetTCP_ConnFreeTmr (NET_TCP_CONN *pconn, NET_TCP_FREE_CODE free_code) { CPU_BOOLEAN free_tmr; free_tmr = DEF_BIT_IS_SET(free_code, NET_TCP_CONN_FREE_TMR_TX_IDLE); if (free_tmr == DEF_YES) { if (pconn->TxQ_IdleTmr != (NET_TMR *)0) { NetTmr_Free(pconn->TxQ_IdleTmr); pconn->TxQ_IdleTmr = (NET_TMR *)0; } } free_tmr = DEF_BIT_IS_SET(free_code, NET_TCP_CONN_FREE_TMR_TX_SILLY_WIN); if (free_tmr == DEF_YES) { if (pconn->TxQ_SillyWinTmr != (NET_TMR *)0) { NetTmr_Free(pconn->TxQ_SillyWinTmr); pconn->TxQ_SillyWinTmr = (NET_TMR *)0; } } free_tmr = DEF_BIT_IS_SET(free_code, NET_TCP_CONN_FREE_TMR_TX_ZERO_WIN); if (free_tmr == DEF_YES) { if (pconn->TxQ_ZeroWinTmr != (NET_TMR *)0) { NetTmr_Free(pconn->TxQ_ZeroWinTmr); pconn->TxQ_ZeroWinTmr = (NET_TMR *)0; } } free_tmr = DEF_BIT_IS_SET(free_code, NET_TCP_CONN_FREE_TMR_TX_ACK_DLY); if (free_tmr == DEF_YES) { if (pconn->TxAckDlyTmr != (NET_TMR *)0) { NetTmr_Free(pconn->TxAckDlyTmr); pconn->TxAckDlyTmr = (NET_TMR *)0; } } free_tmr = DEF_BIT_IS_SET(free_code, NET_TCP_CONN_FREE_TMR_RE_TX); if (free_tmr == DEF_YES) { if (pconn->ReTxQ_Tmr != (NET_TMR *)0) { NetTmr_Free(pconn->ReTxQ_Tmr); pconn->ReTxQ_Tmr = (NET_TMR *)0; } } free_tmr = DEF_BIT_IS_SET(free_code, NET_TCP_CONN_FREE_TMR_KEEP_ALIVE); if (free_tmr == DEF_YES) { if (pconn->KeepAliveTmr != (NET_TMR *)0) { NetTmr_Free(pconn->KeepAliveTmr); pconn->KeepAliveTmr = (NET_TMR *)0; } } free_tmr = DEF_BIT_IS_SET(free_code, NET_TCP_CONN_FREE_TMR_TIMEOUT); if (free_tmr == DEF_YES) { if (pconn->TimeoutTmr != (NET_TMR *)0) { NetTmr_Free(pconn->TimeoutTmr); pconn->TimeoutTmr = (NET_TMR *)0; } } } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_ConnFreeBufQ() * * Description : Free a TCP connection's buffer queue. * * Argument(s) : pbuf_q_head Pointer to a TCP connection buffer queue's head pointer. * * pbuf_q_tail Pointer to a TCP connection buffer queue's tail pointer. * * Return(s) : none. * * Caller(s) : NetTCP_RxPktConnHandlerRxQ_Sync(), * NetTCP_TxConnSync(), * NetTCP_ConnFreeHandler(). * * Note(s) : none. ********************************************************************************************************* */ static void NetTCP_ConnFreeBufQ (NET_BUF **pbuf_q_head, NET_BUF **pbuf_q_tail) { NET_BUF *pbuf_q; /* Free buf Q. */ pbuf_q = *pbuf_q_head; NetBuf_FreeBufQ_PrimList((NET_BUF *)pbuf_q, (NET_CTR *)0); /* Clr buf Q ptrs to NULL. */ *pbuf_q_head = (NET_BUF *)0; *pbuf_q_tail = (NET_BUF *)0; } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_ConnClr() * * Description : Clear TCP connection controls. * * Argument(s) : pconn Pointer to a TCP connection. * ----- Argument validated in NetTCP_Init(), * NetTCP_ConnGet(), * NetTCP_ConnFreeHandler(). * * Return(s) : none. * * Caller(s) : NetTCP_Init(), * NetTCP_ConnGet(), * NetTCP_ConnFreeHandler(). * * Note(s) : (1) Configured in NetTCP_ConnCfg() : * * (a) Configured in NetTCP_ConnCfgMaxSegSize(). * * (b) Configured in NetTCP_RxConnWinSizeCfg() : * (1) Configured in NetTCP_RxConnWinSizeCfgUpdateTh(). * * (c) Configured in NetTCP_TxConnWinSizeCfg() : * (1) Configured in NetTCP_TxConnWinSizeCfgCongCtrl(). * (2) Configured in NetTCP_TxConnWinSizeCfgMinTh(). * * (d) Initialized in NetTCP_TxConnRTT_RTO_Init(). * (1) Configured in NetTCP_TxConnRTO_CfgMaxTimeout(). * * (2) See 'NetTCP_RxPktConnHandler() Note #3c'. * * (3) (a) See 'net_tcp.h TCP SEGMENT SIZE DEFINES Note #1b1'. * (b) See 'net_tcp.h TCP SEGMENT SIZE DEFINES Note #1b2'. * * (4) See 'net_cfg.h TRANSMISSION CONTROL PROTOCOL LAYER CONFIGURATION Note #2'. * * (5) See 'NetTCP_TxConnTxQ() Note #6b2B2'. * * (6) See 'net_tcp.h TCP CONGESTION CONTROL DEFINES Note #5b4'. * * (7) See 'NetTCP_TxConnWinSizeZeroWinHandler() Note #1b2'. * * (8) See 'net_tcp.h TCP CONGESTION CONTROL DEFINES Note #7'. * * (9) See 'net_tcp.h TCP CONGESTION CONTROL DEFINES Note #6b'. * * (10) See 'NetTCP_TxConnAck() Note #4a4A1'. * * (11) See 'NetTCP_ConnCfgReTxMaxTimeout() Note #1'. * * (12) See #### * * (13) See 'NetTCP_RxPktConnHandlerConn() Note #3a'. * * (14) See 'net_tcp.h TCP CONNECTION TIMEOUT DEFINES Note #1b'. ********************************************************************************************************* */ /*$PAGE*/ static void NetTCP_ConnClr (NET_TCP_CONN *pconn) { NET_TCP_CONN_ID conn_id_tcp; NET_ERR err; pconn->NextPtr = (void *)0; pconn->ID_Conn = NET_CONN_ID_NONE; pconn->ConnState = NET_TCP_CONN_STATE_FREE; pconn->ConnCloseCode = NET_CONN_CLOSE_FULL; /* See Note #2. */ pconn->ConnCloseAppFlag = DEF_YES; pconn->ConnCloseTimeoutFaultFlag = DEF_YES; pconn->MaxSegSizeLocal = NET_TCP_MAX_SEG_SIZE_DFLT_RX; /* See Note #3a. */ pconn->MaxSegSizeRemote = NET_TCP_MAX_SEG_SIZE_DFLT_TX; /* See Note #3b. */ #if 0 /* See Note #1a. */ pconn->MaxSegSizeConn = NET_TCP_MAX_SEG_SIZE_NONE; #endif pconn->RxSeqNbrSync = NET_TCP_SEQ_NBR_NONE; pconn->RxSeqNbrNext = NET_TCP_SEQ_NBR_NONE; pconn->RxSeqNbrLast = NET_TCP_SEQ_NBR_NONE; pconn->RxSeqNbrClose = NET_TCP_SEQ_NBR_NONE; pconn->RxWinSizeCfgd = NET_TCP_CFG_RX_WIN_SIZE_OCTET; /* See Note #4. */ pconn->RxWinSizeCfgdActualUpdateRem = NET_TCP_WIN_SIZE_NONE; pconn->RxWinSizeCfgdActual = pconn->RxWinSizeCfgd; pconn->RxWinSizeCalcd = pconn->RxWinSizeCfgd; pconn->RxWinSizeActual = pconn->RxWinSizeCfgd; #if 0 /* See Note #1b1. */ pconn->RxWinSizeUpdateTh = NET_TCP_WIN_SIZE_NONE; #endif pconn->RxQ_State = NET_TCP_RX_Q_STATE_CONN_CLOSED; pconn->RxQ_Transport_Head = (NET_BUF *)0; pconn->RxQ_Transport_Tail = (NET_BUF *)0; pconn->RxQ_App_Head = (NET_BUF *)0; pconn->RxQ_App_Tail = (NET_BUF *)0; pconn->TxSeqNbrSync = NET_TCP_SEQ_NBR_NONE; pconn->TxSeqNbrNext = NET_TCP_SEQ_NBR_NONE; pconn->TxSeqNbrNextQ = NET_TCP_SEQ_NBR_NONE; pconn->TxSeqNbrUnReTxd = NET_TCP_SEQ_NBR_NONE; pconn->TxSeqNbrUnAckd = NET_TCP_SEQ_NBR_NONE; pconn->TxSeqNbrUnAckdPrev = NET_TCP_SEQ_NBR_NONE; pconn->TxSeqNbrUnAckdAlignDelta = 0; pconn->TxSeqNbrLast = NET_TCP_SEQ_NBR_NONE; pconn->TxSeqNbrClose = NET_TCP_SEQ_NBR_NONE; pconn->TxWinUpdateSeqNbr = NET_TCP_SEQ_NBR_NONE; pconn->TxWinUpdateAckNbr = NET_TCP_ACK_NBR_NONE; pconn->TxWinUpdateWinSize = NET_TCP_WIN_SIZE_NONE; pconn->TxWinSizeCfgd = NET_TCP_CFG_TX_WIN_SIZE_OCTET; /* See Note #4. */ pconn->TxWinSizeCfgdRem = pconn->TxWinSizeCfgd; pconn->TxWinSizeRemote = NET_TCP_WIN_SIZE_NONE; pconn->TxWinSizeRemoteMax = pconn->TxWinSizeRemote; pconn->TxWinSizeRemoteActual = pconn->TxWinSizeRemote; pconn->TxWinSizeRemoteRem = pconn->TxWinSizeRemote; pconn->TxWinSizeNagleEn = DEF_ENABLED; /* See Note #5. */ pconn->TxWinSillyWinTimeout_ms = NET_TCP_TX_SILLY_WIN_TIMEOUT_DFLT_MS; /* See Note #6. */ pconn->TxWinZeroWinTimeout_ms = 0; /* See Note #7. */ #if 0 /* See Note #1c. */ pconn->TxWinRxdAckDupCtr = 0; pconn->TxWinRxdLastSeqNbr = NET_TCP_SEQ_NBR_NONE; pconn->TxWinRxdLastAckNbr = NET_TCP_ACK_NBR_NONE; pconn->TxWinRxdLastWinSize = NET_TCP_WIN_SIZE_NONE; pconn->TxWinSizeSlowStartTh = NET_TCP_WIN_SIZE_NONE; pconn->TxWinSizeSlowStartThInit = NET_TCP_WIN_SIZE_NONE; pconn->TxWinSizeCongInit = NET_TCP_WIN_SIZE_NONE; pconn->TxWinSizeCongCalcdActual = NET_TCP_WIN_SIZE_NONE; pconn->TxWinSizeCongCalcdCur = NET_TCP_WIN_SIZE_NONE; pconn->TxWinSizeCongRem = NET_TCP_WIN_SIZE_NONE; pconn->TxWinSizeAvail = NET_TCP_WIN_SIZE_NONE; pconn->TxWinSizeMinTh = NET_TCP_WIN_SIZE_NONE; #endif /*$PAGE*/ pconn->TxSegReTxTh = NET_TCP_RE_TX_TH_DFLT; /* See Note #8. */ pconn->TxAckDlyTimeout_ms = NET_TCP_CFG_TIMEOUT_CONN_ACK_DLY_MS; /* See Note #9. */ pconn->TxAckDlyTmr = (NET_TMR *)0; pconn->TxAckDlyCnt = 0; pconn->TxAckImmedRxdPushEn = DEF_ENABLED; /* See Note #10. */ pconn->TxRTT_RTO_Max_sec = NET_TCP_TX_RTO_MAX_TIMEOUT_DFLT_SEC; /* See Note #11. */ #if 0 /* See Note #1d. */ pconn->TxRTT_Avg_ms_scaled = NET_TCP_TX_RTT_NONE; pconn->TxRTT_Dev_ms_scaled = NET_TCP_TX_RTT_NONE; pconn->TxRTT_RTO_ms_scaled = NET_TCP_TX_RTO_NONE; pconn->TxRTT_RTO_Max_ms_scaled = NET_TCP_TX_RTO_NONE; /* See Note #1d1. */ pconn->TxRTT_Avg_ms = NET_TCP_TX_RTT_NONE; pconn->TxRTT_Dev_ms = NET_TCP_TX_RTT_NONE; pconn->TxRTT_RTO_ms = NET_TCP_TX_RTO_NONE; pconn->TxRTT_RTO_Max_ms = NET_TCP_TX_RTO_NONE; /* See Note #1d1. */ pconn->TxRTT_RTO_sec = NET_TCP_TX_RTO_NONE; pconn->TxRTT_RTO_tick = NET_TMR_TIME_0S; pconn->TxRTT_RTO_State = NET_TCP_TX_RTT_RTO_STATE_NONE; #endif pconn->TxIP_TOS = NET_IP_TOS_DFLT; pconn->TxIP_TTL = NET_IP_TTL_DFLT; pconn->TxIP_Flags = NET_IP_FLAG_NONE; pconn->TxQ_State = NET_TCP_TX_Q_STATE_CONN_CLOSED; pconn->TxQ_Head = (NET_BUF *)0; pconn->TxQ_Tail = (NET_BUF *)0; pconn->TxQ_IdleTmr = (NET_TMR *)0; pconn->TxQ_SillyWinTmr = (NET_TMR *)0; pconn->TxQ_ZeroWinTmr = (NET_TMR *)0; pconn->ReTxQ_Head = (NET_BUF *)0; pconn->ReTxQ_Tail = (NET_BUF *)0; pconn->ReTxQ_Tmr = (NET_TMR *)0; pconn->KeepAliveTmr = (NET_TMR *)0; pconn->TimeoutTmr = (NET_TMR *)0; pconn->TimeoutConn_sec = NET_TCP_CONN_TIMEOUT_CONN_DFLT_SEC; /* See Note #12. */ pconn->TimeoutUser_sec = NET_TCP_CONN_TIMEOUT_USER_DFLT_SEC; /* See Note #13. */ pconn->TimeoutMaxSeg_sec = NET_TCP_CFG_TIMEOUT_CONN_MAX_SEG_SEC; /* See Note #14. */ pconn->Flags = NET_TCP_FLAG_NONE; NetTCP_ConnCfg(pconn, NET_TCP_CONN_CFG_ALL); /* See Note #1. */ /* -- CFG DFLT TIMEOUT VALS --- */ conn_id_tcp = pconn->ID; NetOS_TCP_RxQ_TimeoutDflt(conn_id_tcp, &err); NetOS_TCP_TxQ_TimeoutDflt(conn_id_tcp, &err); } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_ConnCopy() * * Description : (1) Copy/clone a TCP connection : * * (a) TCP connection state * * (b) Receive parameters : * (1) Receive window size * * (c) Transmit parameters : * (1) Transmit window size * * (2) Re-transmit parameters : * (A) Threshold * (B) Maximum timeout * * (3) IP transmit parameters : * (A) TOS * (B) TTL * (C) IP flags * * (d) TCP connection timeout values * * * Argument(s) : pconn_dest Pointer to TCP connection to receive TCP connection copy. * ---------- Argument validated in NetTCP_RxPktConnHandlerListen(). * * pconn_src Pointer to TCP connection to copy. * --------- Argument validated in NetTCP_RxPktConnHandlerListen(). * * Return(s) : none. * * Caller(s) : NetTCP_RxPktConnHandlerListen(). * * Note(s) : none. ********************************************************************************************************* */ static void NetTCP_ConnCopy (NET_TCP_CONN *pconn_dest, NET_TCP_CONN *pconn_src) { pconn_dest->ConnState = pconn_src->ConnState; pconn_dest->RxWinSizeCfgd = pconn_src->RxWinSizeCfgd; pconn_dest->TxWinSizeCfgd = pconn_src->TxWinSizeCfgd; pconn_dest->TxWinSizeNagleEn = pconn_src->TxWinSizeNagleEn; pconn_dest->TxSegReTxTh = pconn_src->TxSegReTxTh; pconn_dest->TxAckDlyTimeout_ms = pconn_src->TxAckDlyTimeout_ms; pconn_dest->TxAckImmedRxdPushEn = pconn_src->TxAckImmedRxdPushEn; pconn_dest->TxRTT_RTO_Max_sec = pconn_src->TxRTT_RTO_Max_sec; pconn_dest->TxIP_TOS = pconn_src->TxIP_TOS; pconn_dest->TxIP_TTL = pconn_src->TxIP_TTL; pconn_dest->TxIP_Flags = pconn_src->TxIP_Flags; pconn_dest->TimeoutConn_sec = pconn_src->TimeoutConn_sec; pconn_dest->TimeoutUser_sec = pconn_src->TimeoutUser_sec; pconn_dest->TimeoutMaxSeg_sec = pconn_src->TimeoutMaxSeg_sec; } /*$PAGE*/ /* ********************************************************************************************************* * NetTCP_ConnDiscard() * * Description : (1) Discard an invalid/corrupted TCP connection : * * (a) Discard TCP connection from available TCP connection pool See Note #2 * (b) Update TCP connection pool statistics * * (2) Assumes TCP connection is invalid/corrupt & MUST be removed. TCP connection removed * simply by NOT returning the TCP connection back to the TCP connection pool. * * * Argument(s) : pconn Pointer to an invalid/corrupted TCP connection. * * Return(s) : none. * * Caller(s) : NetTCP_ConnGet(), * NetTCP_ConnFree(). * * Note(s) : none. ********************************************************************************************************* */ static void NetTCP_ConnDiscard (NET_TCP_CONN *pconn) { NET_ERR stat_err; /* ----------------- DISCARD TCP CONN ----------------- */ (void)&pconn; /* Prevent compiler warning (see Note #2). */ /* --------------- UPDATE DISCARD STATS --------------- */ NetStat_PoolEntryLostInc(&NetTCP_ConnPoolStat, &stat_err); } /*$PAGE*/ /* ********************************************************************************************************* * MODULE END * * Note(s) : (1) See 'MODULE Note #1' & 'net_tcp.h MODULE Note #1'. ********************************************************************************************************* */ #endif /* End of TCP module include (see Note #1). */