0
mirror of https://github.com/Indemsys/Frequency_Inverter.git synced 2026-05-12 14:28:29 +00:00
Files
2022-01-04 12:22:53 +02:00

4185 lines
126 KiB
C

/**HEADER********************************************************************
*
* Copyright (c) 2008 Freescale Semiconductor;
* All Rights Reserved
*
* Copyright (c) 2004-2008 Embedded Access Inc.;
* All Rights Reserved
*
* Copyright (c) 1989-2008 ARC International;
* All Rights Reserved
*
***************************************************************************
*
* THIS SOFTWARE IS PROVIDED BY FREESCALE "AS IS" AND ANY EXPRESSED OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL FREESCALE OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*
**************************************************************************
*
* $FileName: tcp.c$
* $Version : 3.8.17.0$
* $Date : Jun-27-2012$
*
* Comments:
*
* For more details, refer to RFC793 (which contains some errors)
* and RFC1122 (which documents those errors and adds lots more
* useful details).
*
*END************************************************************************/
#include <rtcs.h>
#include "rtcs_prv.h"
#include "ticker.h" /* for timer definitions */
#include "tcpip.h" /* for TCPIP definitions */
#include "ip_prv.h" /* for IP internal definitions */
#include "tcp_prv.h" /* for TCP internal definitions */
#include "socket.h" /* used by TCP_Process_getname */
/*FUNCTION*-------------------------------------------------------------
*
* Function Name : TCP_Init
* Returned Values : int_32
* Comments :
*
* Initialize the TCP layer.
* Returns 0 or positive error number
*
*END*-----------------------------------------------------------------*/
uint_32 TCP_Init
(
void
)
{ /* Body */
TCP_CFG_STRUCT_PTR tcp_cfg;
uint_32 status;
tcp_cfg = RTCS_mem_alloc_zero(sizeof(TCP_CFG_STRUCT));
if (tcp_cfg == NULL) {
return RTCSERR_OUT_OF_MEMORY;
}
_mem_set_type(tcp_cfg, MEM_TYPE_TCP_CFG_STRUCT);
RTCS_setcfg(TCP,tcp_cfg);
tcp_cfg->qh = NULL;
tcp_cfg->qhead = &tcp_cfg->qh;
tcp_cfg->lasttime = 0;
tcp_cfg->TCBhead = NULL;
tcp_cfg->Rchunkhead = NULL;
tcp_cfg->SbufNodehead = NULL;
tcp_cfg->Rchunkfree = NULL;
tcp_cfg->SbufNodefree = NULL;
tcp_cfg->next_port = IPPORT_USERRESERVED;
tcp_cfg->acktq = NULL;
tcp_cfg->xmittq = NULL;
tcp_cfg->sndtq = NULL;
tcp_cfg->rcvtq = NULL;
tcp_cfg->DEFER_TAIL = &tcp_cfg->DEFER_HEAD;
tcp_cfg->CONN_COUNT = 0;
tcp_cfg->HALF_OPEN_TCB_COUNT = 0;
/*
** Global used for TCP_RTO_MIN because we may want to let our
** users adjust this.
*/
_TCP_rto_min = TCP_RTO_MIN;
/*
** Install the TCP tick server
*/
TCP_tick = TCP_Tick_server;
#if RTCSCFG_ENABLE_IP4
IP_open(IPPROTO_TCP, TCP4_Service_packet, tcp_cfg, &status);
if(status != RTCS_OK)
{
return status;
}
#endif /* RTCSCFG_ENABLE_IP4 */
#if RTCSCFG_ENABLE_IP6
IP6_open(IPPROTO_TCP, TCP6_Service_packet, tcp_cfg, &status);
#endif /* RTCSCFG_ENABLE_IP6 */
return status;
} /* Endbody */
/*FUNCTION*-------------------------------------------------------------
*
* Function Name : TCP_Event
* Returned Values : None.
* Comments :
*
* Signal the occurrence of an event
* (of the type which the user (control/wait task) may wait upon).
*
* This is used for non-error events; error/terminating events should
* call TCP_Close_TCB(), closing the connection.
*
*END*-----------------------------------------------------------------*/
void TCP_Event
(
TCB_STRUCT_PTR tcb, /* IN/OUT - TCP context */
int_16 event /* IN - event that occurred */
)
{ /* Body */
RTCSLOG_FNE3(RTCSLOG_FN_TCP_Event, tcb, event);
tcb->status |= event;
switch ( event ) {
case TCPS_FINRECV:
case TCPS_FINACKED:
if ( (~tcb->status & (TCPS_FINRECV|TCPS_FINACKED)) == 0 ) {
tcb->status |= TCPS_FINDONE;
event |= TCPS_FINDONE; /* for Signal()'s */
} /* Endif */
break;
default:
/* Do nothing */
break;
} /* End Switch */
RTCSLOG_FNX1(RTCSLOG_FN_TCP_Event);
} /* Endbody */
/*FUNCTION*-------------------------------------------------------------
*
* Function Name : TCP_Process_create
* Returned Values : None.
* Comments :
*
* Called when the application creates a SOCK_STREAM socket.
*
* This function allocates a TCB.
*
*END*-----------------------------------------------------------------*/
void TCP_Process_create
(
TCP_PARM_PTR req_ptr /* IN/OUT - the open request */
)
{ /* Body */
TCB_STRUCT_PTR tcb; /* New TCB */
tcb = (TCB_STRUCT_PTR)RTCS_mem_alloc_zero(sizeof(TCB_STRUCT));
if (tcb == NULL) {
RTCS_log_error(ERROR_TCP, RTCSERR_TCP_OPEN_FAILED, 0, 0, 0);
RTCSCMD_complete(req_ptr, RTCSERR_OUT_OF_MEMORY);
return;
} /* Endif */
_mem_set_type(tcb, MEM_TYPE_TCB);
tcb->VALID = TCB_VALID_ID;
tcb->SOCKET = req_ptr->SOCKET;
/* Local and remote ports */
tcb->local_port = 0;
tcb->local_host = INADDR_ANY;
tcb->remote_port= 0;
tcb->remote_host = INADDR_ANY;
#if RTCSCFG_ENABLE_IP6
IN6_ADDR_COPY((in6_addr*)(&(in6addr_any)),&(tcb->local_ipv6_host));
IN6_ADDR_COPY((in6_addr*)(&(in6addr_any)),&(tcb->remote_ipv6_host));
#endif
req_ptr->TCB_PTR = tcb;
RTCSCMD_complete(req_ptr, RTCS_OK);
} /* Endbody */
/*FUNCTION*-------------------------------------------------------------
*
* Function Name : TCP_Process_bind46
* Returned Values : None.
* Comments :
*
* Called when the application binds a SOCK_STREAM socket.
*
* This function chooses a local port for a TCB.
*
*END*-----------------------------------------------------------------*/
void TCP_Process_bind
(
TCP_PARM_PTR req_ptr /* IN/OUT - the bind request */
)
{ /* Body */
TCP_CFG_STRUCT_PTR tcp_cfg; /* TCP Layer constants */
TCB_STRUCT_PTR tcb,
tcb2; /* Temporary pointer */
boolean local_host_zero =0;
boolean local_host_eq=0;
boolean local_port_eq=0;
boolean remote_port_zero=0;
boolean local_result =1;
tcp_cfg = RTCS_getcfg(TCP);
tcb = req_ptr->TCB_PTR;
if ( tcb == NULL )
{
RTCSCMD_complete(req_ptr, RTCSERR_TCP_CONN_RLSD);
return;
}
/* setup tcb localhost address */
#if RTCSCFG_ENABLE_IP6
if(req_ptr->AF == AF_INET)
{
#endif
tcb->local_host = req_ptr->IP_ADDR;
#if RTCSCFG_ENABLE_IP6
}
else if(req_ptr->AF == AF_INET6)
{
IN6_ADDR_COPY(&(req_ptr->IPV6_ADDR),&(tcb->local_ipv6_host));
/* add binded scope_id here */
tcb->if_scope_id = req_ptr->IF_SCOPE_ID;
}
else
{
RTCS_log_error(ERROR_TCP, RTCSERR_TCPIP_INVALID_ARGUMENT, 0, 0, 0);
}
#endif
/* setup tcb localhost port */
tcb->local_port = req_ptr->TCP_PORT;
/*
** Scan for available port if none specified
*/
if ( tcb->local_port == 0 ) {
uint_16 first_port = tcp_cfg->next_port;
do {
tcb->local_port = tcp_cfg->next_port;
if (--tcp_cfg->next_port < IPPORT_RESERVED)
{
tcp_cfg->next_port = IPPORT_USERRESERVED;
} /* Endif */
if (tcp_cfg->next_port == first_port)
{
RTCS_log_error(ERROR_TCP, RTCSERR_TCP_NO_MORE_PORTS, 0, 0, 0);
RTCSCMD_complete(req_ptr, RTCSERR_TCP_NO_MORE_PORTS);
return;
} /* Endif */
/* is port used? */
#if RTCSCFG_ENABLE_IP6
if(req_ptr->AF == AF_INET)
{
#endif
for (
tcb2=tcp_cfg->TCBhead;
(tcb2 != NULL) &&((tcb2->local_host &&(tcb2->local_host != tcb->local_host)) || (tcb2->local_port != tcb->local_port)||(tcb2->af != req_ptr->AF ));
tcb2=tcb2->next
)
{
} /* Endfor */
#if RTCSCFG_ENABLE_IP6
}
else if(req_ptr->AF == AF_INET6)
{
for (
tcb2=tcp_cfg->TCBhead;
(tcb2 != NULL) &&
(
(
(!IN6_ARE_ADDR_EQUAL(&(tcb2->local_ipv6_host), &in6addr_any )) &&
(!IN6_ARE_ADDR_EQUAL(&(tcb2->local_ipv6_host), &(tcb->local_ipv6_host) ))
)||
(tcb2->local_port != tcb->local_port)||
(tcb2->af != req_ptr->AF )||
(tcb2->if_scope_id != req_ptr->IF_SCOPE_ID)
);
tcb2=tcb2->next
)
{
} /* Endfor */
}
#endif
} while (tcb2 != NULL);
/*
** assuming no more than (IPPORT_USERRESERVED -
** IPPORT_RESERVED) ports will ever be opened at once,
** this is currently 5000-1024 = 3976 ports (+1)
*/
}
else
{
/* is requested port used? */
#if RTCSCFG_ENABLE_IP6
if(req_ptr->AF == AF_INET)
{
#endif
for(
tcb2=tcp_cfg->TCBhead;
tcb2 != NULL &&
(
(
tcb2->local_host &&
tcb2->local_host != tcb->local_host
) ||
(tcb2->local_port != tcb->local_port) ||
(tcb2->remote_port != 0) ||
(tcb2->af != req_ptr->AF )
);
tcb2=tcb2->next
)
{
} /* Endfor */
#if RTCSCFG_ENABLE_IP6
}
else if(req_ptr->AF == AF_INET6)
{
for(
tcb2=tcp_cfg->TCBhead;
(tcb2 != NULL) &&
(
(
(!IN6_ARE_ADDR_EQUAL(&(tcb2->local_ipv6_host), &in6addr_any ))&&
(!IN6_ARE_ADDR_EQUAL(&(tcb2->local_ipv6_host), &(tcb->local_ipv6_host) ))
) ||
(tcb2->local_port != tcb->local_port) ||
(tcb2->remote_port != 0)||
(tcb2->af != req_ptr->AF )||
(tcb2->if_scope_id != req_ptr->IF_SCOPE_ID)
);
tcb2=tcb2->next
)
{
}
}
#endif
if (tcb2 != NULL) {
RTCS_log_error(ERROR_TCP, RTCSERR_TCP_ADDR_IN_USE, 0, 0, 0);
RTCSCMD_complete(req_ptr, RTCSERR_TCP_ADDR_IN_USE);
return;
} /* Endif */
} /* Endif */
tcb->state = BOUND;
/*BIND table will provide bind family information */
tcb->af = req_ptr->AF;
tcb->next = tcp_cfg->TCBhead;
tcp_cfg->TCBhead = tcb;
RTCSCMD_complete(req_ptr, RTCS_OK);
} /* Endbody */
/*FUNCTION*-------------------------------------------------------------
*
* Function Name : TCP_Process_open
* Returned Values : None.
* Comments :
*
* Process TcpOpen() requests, within TCP server. Support IPv4 and IPv6
*
* On successful creation of the TCB and associated buffers:
*
* if this is an active open, the TCB state is set to SYN_SENT;
* if this is a passive open, the TCB state is set to LISTEN.
*
*END*-----------------------------------------------------------------*/
void TCP_Process_open
(
TCP_PARM_PTR req_ptr /* IN/OUT - the open request */
)
{ /* Body */
TCP_CFG_STRUCT_PTR tcp_cfg; /* TCP Layer constants */
TCP_OPTIONS_PTR coptions; /* list of TCP options */
TCB_STRUCT_PTR tcb, /* Newly allocated TCB */
tcb2; /* Temporary pointer */
uint_32 sbsize, /* Send buffer size (bytes) */
rbsize; /* Receive buffer size (bytes) */
_ip_address remote_host_v4; /* remote IPv4 address */
#if RTCSCFG_ENABLE_IP6
in6_addr remote_host_v6; /* remote IPv6 address */
#endif
uint_32 result; /* Returned status */
tcp_cfg = RTCS_getcfg(TCP);
tcb = req_ptr->TCB_PTR;
if ( tcb == NULL ) {
TCP_PO_FAIL (RTCSERR_TCP_CONN_RLSD);
}
tcp_cfg->CONN_COUNT++;
/*
** Process TCP options
*/
coptions = req_ptr->OPTIONS_PTR;
tcb->sndrtomax = 2 * TCP_MSL;
/*
** determine if the bypass of checksums are set
*/
tcb->bypass_rx_chksum = _TCP_bypass_rx;
tcb->bypass_tx_chksum = _TCP_bypass_tx;
/* tcpsecure advice in draft-ietf-tcpm-tcpsecure-00.txt */
tcb->tcpsecuredraft0 = coptions->tcpsecuredraft0;
/* Send ring buffer size */
if ( coptions->tbsize >= 0 ) {
sbsize = (uint_32)coptions->tbsize;
} else {
sbsize = TCP_DEFAULT_SBSIZE;
} /* Endif */
/* Receive ring buffer size */
if ( coptions->rbsize >= 0 ) {
rbsize = (uint_32)coptions->rbsize;
} else {
rbsize = TCP_DEFAULT_RBSIZE;
} /* Endif */
/* User timeout (R1) */
if ( coptions->utimeout >= 0 ) {
tcb->sndto_1 = coptions->utimeout;
} else {
tcb->sndto_1 = 0;
} /* Endif */
/* Connection timeout (R2) */
if ( coptions->timeout > 0 ) {
tcb->sndto_2 = coptions->timeout;
} else {
tcb->sndto_2 = 2 * tcb->sndrtomax;
} /* Endif */
/* Initial retransmission timeout */
if ( coptions->rto > 0 ) {
tcb->sndrto = coptions->rto;
} else {
tcb->sndrto = TCP_INITIAL_RTO_DEFAULT;
} /* Endif */
/* Maximum retransmission timeout */
if ( coptions->maxrto > 0 ) {
tcb->sndrtomax = coptions->maxrto;
} /* Endif */
/* Maximum receive window size */
if ( coptions->maxrcvwnd > 0 ) {
tcb->rcvwndmax = coptions->maxrcvwnd;
} else {
tcb->rcvwndmax = 0xffff;
} /* Endif */
/* INTERNET type of service */
if ( coptions->tos > 0 ) {
tcb->tos = coptions->tos & 7;
} else {
tcb->tos = 0;
} /* Endif */
/* INTERNET precedence */
if ( coptions->precede != 0xFF ) {
tcb->tos &= 0xE0;
tcb->tos |= coptions->precede << 5;
} /* Endif */
/* Remote host and port */
#if RTCSCFG_ENABLE_IP4
if(req_ptr->AF == AF_INET)
{
remote_host_v4 = coptions->remhost;
}
#if RTCSCFG_ENABLE_IP6
else
#endif
#endif
#if RTCSCFG_ENABLE_IP6
if(req_ptr->AF == AF_INET6)
{
IN6_ADDR_COPY(&(coptions->remhost_ipv6),&(remote_host_v6));
}
#endif /* RTCSCFG_ENABLE_IP6 */
tcb->remote_port = coptions->remport;
/* Keepalive timeout */
if ( coptions->keepalive > 0 ) {
tcb->keepaliveto = coptions->keepalive * 60000L;
} else {
tcb->keepaliveto = 0;
} /* Endif */
/* TIME_WAIT timeout */
if ( coptions->timewaitto ) {
tcb->timewaitto = coptions->timewaitto;
} else {
tcb->timewaitto = TCP_WAITTIMEOUT;
} /* Endif */
/* Active or passive open? */
if ( coptions->active ) {
tcb->options |= TCPO_ACTIVE;
} else {
tcb->options &= ~TCPO_ACTIVE;
} /* Endif */
/* Non-blocking or blocking commands */
if ( coptions->nowait ) {
tcb->options |= TCPO_NOWAIT;
} else {
tcb->options &= ~TCPO_NOWAIT;
} /* Endif */
/* Use Nagle algorithm? */
if ( coptions->nonagle ) {
tcb->options |= TCPO_NONAGLE;
} else {
tcb->options &= ~TCPO_NONAGLE;
} /* Endif */
/* Allow switch from ring buffer to user buffer? */
if ( coptions->noswrbuf ) {
tcb->options |= TCPO_NOSWITCH;
} else {
tcb->options &=~TCPO_NOSWITCH;
} /* Endif */
/* delay ack timeout */
tcb->delayackto = coptions->delayackto;
/* Process option results */
if ( tcb->options & TCPO_ACTIVE )
{
#if RTCSCFG_ENABLE_IP4
if(req_ptr->AF == AF_INET)
{
if ( remote_host_v4 == INADDR_ANY || tcb->remote_port == 0 )
{
TCP_PO_FAIL(RTCSERR_TCPIP_DESTADDR_REQUIRED);
} /* Endif */
if ( IN_MULTICAST(remote_host_v4) )
{
TCP_PO_FAIL(RTCSERR_TCP_ADDR_NA);
} /* Endif */
}
#if RTCSCFG_ENABLE_IP6
else
#endif
#endif
#if RTCSCFG_ENABLE_IP6
if(req_ptr->AF == AF_INET6)
{
if (IN6_ARE_ADDR_EQUAL(&remote_host_v6, &in6addr_any)|| (tcb->remote_port == 0))
{
TCP_PO_FAIL(RTCSERR_TCPIP_DESTADDR_REQUIRED);
} /* Endif */
if (IN6_IS_ADDR_MULTICAST(&remote_host_v6))
{
TCP_PO_FAIL(RTCSERR_TCP_ADDR_NA);
} /* Endif */
}
#endif /* RTCSCFG_ENABLE_IP6 */
} /* Endif */
/* Supports windows of up to 16 bits in size*/
if ( tcb->rcvwndmax > 0xffff ) {
tcb->rcvwndmax = 0xffff;
} /* Endif */
/*
** Verify that requested ports/hosts combination, if fully
** specified, isn't already used
*/
#if RTCSCFG_ENABLE_IP4
if(req_ptr->AF == AF_INET)
{
if ( tcb->local_port != 0 && tcb->remote_port != 0 && remote_host_v4 != INADDR_ANY)
{
for ( tcb2=tcp_cfg->TCBhead;
tcb2 != NULL && /* Can ignore this TCB if its: */
( tcb2->local_port != tcb->local_port || /* Local port not a match OR */
tcb2->remote_port == 0 || /* Remote port not specified OR */
tcb2->remote_port != tcb->remote_port || /* Remote port not a match OR */
tcb2->remote_host == INADDR_ANY || /* Remote host not specified OR */
tcb2->remote_host != remote_host_v4 || /* Remote host not a match OR */
tcb2->local_host != tcb->local_host ); /* Local address not a match */
tcb2 = tcb2->next) /* Otherwise, comb. is in use */
{
} /* Endfor */
if ( tcb2 != NULL )
{
RTCS_log_error( ERROR_TCP, RTCSERR_TCP_OPEN_FAILED,RTCSERR_TCP_ADDR_IN_USE, 0, 0);
TCP_PO_FAIL(RTCSERR_TCP_ADDR_IN_USE);
} /* Endif */
} /* Endif */
}
#if RTCSCFG_ENABLE_IP6
else
#endif
#endif
#if RTCSCFG_ENABLE_IP6
if(req_ptr->AF == AF_INET6)
{
if (
(tcb->local_port != 0) &&
(tcb->remote_port != 0) &&
(!IN6_ARE_ADDR_EQUAL(&remote_host_v6, &in6addr_any))
)
{
for ( tcb2=tcp_cfg->TCBhead;
tcb2 != NULL && /* Can ignore this TCB if its: */
( tcb2->local_port != tcb->local_port || /* Local port not a match OR */
tcb2->remote_port == 0 || /* Remote port not specified OR */
tcb2->remote_port != tcb->remote_port || /* Remote port not a match OR */
IN6_ARE_ADDR_EQUAL(&(tcb2->remote_ipv6_host), &in6addr_any) || /* Remote host not specified OR */
!IN6_ARE_ADDR_EQUAL(&(tcb2->remote_ipv6_host), &remote_host_v6) || /* Remote host not a match OR */
!IN6_ARE_ADDR_EQUAL(&(tcb2->local_ipv6_host), &(tcb->local_ipv6_host)) /* Local address not a match */
);
tcb2 = tcb2->next) /* Otherwise, comb. is in use */
{
} /* Endfor */
if ( tcb2 != NULL )
{
RTCS_log_error( ERROR_TCP,RTCSERR_TCP_OPEN_FAILED,RTCSERR_TCP_ADDR_IN_USE,0, 0);
TCP_PO_FAIL(RTCSERR_TCP_ADDR_IN_USE);
} /* Endif */
} /* Endif */
}
#endif /* RTCSCFG_ENABLE_IP6 */
/* Allocate send buffer */
if ((sbsize != 0) && (tcb->options & TCPO_ACTIVE)) {
tcb->sndringbuf = (uchar _PTR_)RTCS_mem_alloc(sbsize);
if ( tcb->sndringbuf == NULL ) {
RTCS_log_error( ERROR_TCP, RTCSERR_TCP_OPEN_FAILED, RTCSERR_TCPIP_NO_BUFFS, 0, 0);
TCP_PO_FAIL(RTCSERR_TCPIP_NO_BUFFS);
} /* Endif */
_mem_set_type(tcb->sndringbuf, MEM_TYPE_TCP_TX_WINDOW);
} /* Endif */
tcb->sndringlen = sbsize;
/* Allocate receive buffer */
if ((rbsize != 0) && (tcb->options & TCPO_ACTIVE)) {
tcb->rcvringbuf = (uchar _PTR_)RTCS_mem_alloc(rbsize);
if ( tcb->rcvringbuf == NULL ) {
RTCS_log_error( ERROR_TCP, RTCSERR_TCP_OPEN_FAILED, RTCSERR_TCPIP_NO_BUFFS, 0, 0);
TCP_PO_FAIL(RTCSERR_TCPIP_NO_BUFFS);
} /* Endif */
_mem_set_type(tcb->rcvringbuf, MEM_TYPE_TCP_RX_WINDOW);
/* Use ring buffer by default */
tcb->rcvbuf = tcb->rcvringbuf;
} /* Endif */
tcb->rcvringlen = rbsize;
tcb->rcvlen = rbsize;
/* Fill remainder of new TCB;
*/
tcb->state = LISTEN;
#if RTCSCFG_ENABLE_IP4
if(req_ptr->AF == AF_INET)
{
if ( remote_host_v4 != INADDR_ANY )
{
tcb->rcvmss = IP_MTU(tcb->local_host, remote_host_v4) - (uint_16) (IP_HEADSIZE + TCP_HEADSIZE);
} /* Endif */
}
#if RTCSCFG_ENABLE_IP6
else
#endif
#endif
#if RTCSCFG_ENABLE_IP6
if(req_ptr->AF == AF_INET6)
{
if (!IN6_ARE_ADDR_EQUAL(&remote_host_v6, &in6addr_any))
{
tcb->rcvmss = IP6_MTU(&(tcb->local_ipv6_host),&remote_host_v6) - (uint_16) (IP6_HEADSIZE + TCP_HEADSIZE);
} /* Endif */
}
#endif /* RTCSCFG_ENABLE_IP6 */
tcb->sndmss = TCP_MSS_DEFAULT; /* default, as per RFC1122, 4.2.2.6 */
tcb->sndmax = tcb->sndmss; /* - options */
/* options is length of any IP and */
/* TCP options sent */
tcb->rcvmax = 1;
tcb->rcv2full = 1; /* this way we ACK a SYN */
if ( tcb->sndrto < _TCP_rto_min ) {
tcb->sndrto = _TCP_rto_min;
} /* Endif */
tcb->sndrtta = 0; /* initial RTT, as per RFC1122, */
/* 4.2.3.1 */
tcb->sndrttd = tcb->sndrto << 1; /* because rto == rtta + 2*rttd, and */
/* rttd is scaled by 2^2 (by 4) */
tcb->sndcwnd = tcb->sndmax; /* init. cwnd to 1 packet */
tcb->sndcwndmax = tcb->sndcwnd; /* for debugging statistics... */
tcb->sndstresh = TCP_WINDOW_MAX; /* at least as big as any eventual */
/* send window*/
/* Soft timeout (R1) must occur *before* hard timeout (R2) (see RFC1122
** section 4.2.3.5 for more details on these timeouts)
*/
if ( tcb->sndto_2 != 0 ) {
if ( tcb->sndto_2 < TCP_SENDTIMEOUT_MIN ) {
tcb->sndto_2 = TCP_SENDTIMEOUT_MIN;
} /* Endif */
/* If sndto_2 is set, sndto_1 must also be set; if it isn't, set it
* to half of sndto_2 (user can always ignore soft timeout events)
* Either way, sndto_1 may never exceed half of sndto_2
*/
if ( (tcb->sndto_1 == 0) || (tcb->sndto_1 > (tcb->sndto_2 >> 1)) ) {
tcb->sndto_1 = tcb->sndto_2 >> 1;
} /* Endif */
tcb->sndto_2 -= tcb->sndto_1; /* store delta only */
} /* Endif */
tcb->sndprobeto = tcb->sndrto;
tcb->sndprobetomax = tcb->sndrtomax;
/* Keep-alive packets should be sent *very* infrequently, usually at
* least 2 hours apart; here just do a reasonableness check;
* 10 times RTO seems a reasonable minimum.
*/
if ( tcb->keepaliveto != 0 && tcb->keepaliveto < 10 * tcb->sndrto ) {
tcb->keepaliveto = 10 * tcb->sndrto;
} /* Endif */
/* init timers */
tcb->sndacktq.t.donelist = (TimeQ _PTR_ _PTR_)&(tcp_cfg->acktq);
tcb->sndacktq.tcb = tcb;
tcb->sndxmittq.t.donelist = (TimeQ _PTR_ _PTR_)&(tcp_cfg->xmittq);
tcb->sndxmittq.tcb = tcb;
tcb->sndtq.t.donelist = (TimeQ _PTR_ _PTR_)&(tcp_cfg->sndtq);
tcb->sndtq.tcb = tcb;
tcb->rcvtq.t.donelist = (TimeQ _PTR_ _PTR_)&(tcp_cfg->rcvtq);
tcb->rcvtq.tcb = tcb;
/* Allocate space for storing timestamps of sent packets;
* pointers are used for the circular queue because they are faster
* than indexes (which require multiplies)
*/
tcb->sndclks = 2 * sbsize / tcb->sndmax + 1;
/* max nb of clock samples */
if ( tcb->sndclks < 3 ) { /* minimum reasonable nb */
tcb->sndclks = 3;
} /* Endif */
tcb->sndclkavail = tcb->sndclks;
tcb->sndclk = (SndClock _PTR_)RTCS_mem_alloc(tcb->sndclks*sizeof(SndClock));
if ( tcb->sndclk == NULL ) {
RTCS_log_error( ERROR_TCP, RTCSERR_OUT_OF_MEMORY,
tcb->sndclks*sizeof(SndClock), 3, 0);
TCP_PO_FAIL(RTCSERR_OUT_OF_MEMORY);
} /* Endif */
_mem_set_type(tcb->sndclk, MEM_TYPE_TCP_SEND_CLOCK);
tcb->sndclkhead = tcb->sndclk;
tcb->sndclktail = tcb->sndclk;
tcb->sndclkend = tcb->sndclk + tcb->sndclks;
#if RTCSCFG_ENABLE_IP4
if(req_ptr->AF == AF_INET)
{
tcb->remote_host = remote_host_v4;
}
#if RTCSCFG_ENABLE_IP6
else
#endif
#endif
#if RTCSCFG_ENABLE_IP6
if(req_ptr->AF == AF_INET6)
{
IN6_ADDR_COPY(&(remote_host_v6),&(tcb->remote_ipv6_host));
/* add scope_id here */
tcb->if_scope_id = req_ptr->IF_SCOPE_ID;
}
#endif /* RTCSCFG_ENABLE_IP6 */
tcb->conn_pending = 0;
/* TcpOpen succeeded */
#if RTCSCFG_ENABLE_TCP_STATS
tcp_cfg->STATS.ST_CONN_OPEN++;
if ( tcb->options & TCPO_ACTIVE )
{
tcp_cfg->STATS.ST_CONN_ACTIVE++;
}
else
{
tcp_cfg->STATS.ST_CONN_PASSIVE++;
} /* Endif */
#endif
if( tcb->options & TCPO_ACTIVE )
{
/* Looking for the best local interface to connection to remote host. */
#if RTCSCFG_ENABLE_IP4
if(req_ptr->AF == AF_INET)
{
if (tcb->local_host == INADDR_ANY)
{
tcb->local_host = IP_route_find(remote_host_v4, 0);
if (tcb->local_host == INADDR_ANY)
{
TCP_PO_FAIL(RTCSERR_TCP_HOST_UNREACH);
} /* Endif */
} /* Endif */
}
#if RTCSCFG_ENABLE_IP6
else
#endif
#endif
#if RTCSCFG_ENABLE_IP6
if(req_ptr->AF == AF_INET6)
{
if (IN6_ARE_ADDR_EQUAL(&(tcb->local_ipv6_host), &in6addr_any))
{
IN6_ADDR_COPY((in6_addr*)IP6_route_find(&remote_host_v6, 0),&(tcb->local_ipv6_host));
if (IN6_ARE_ADDR_EQUAL(&(tcb->local_ipv6_host), &in6addr_any))
{
TCP_PO_FAIL(RTCSERR_TCP_HOST_UNREACH);
} /* Endif */
} /* Endif */
}
#endif /* RTCSCFG_ENABLE_IP6 */
/* Hang on to open requests until established */
req_ptr->NEXT = tcp_cfg->OPENS;
tcp_cfg->OPENS = req_ptr;
tcb->state = SYN_SENT;
/*set inet family*/
tcb->af = req_ptr->AF;
TCP_Start_sending(tcb, tcp_cfg);
}
else
{
RTCSCMD_complete(req_ptr, RTCS_OK);
} /* Endif */
return;
failopen:
if ( tcb != NULL ) {
if ( tcb->sndringbuf != NULL ) {
_mem_free(tcb->sndringbuf);
tcb->sndringbuf = NULL;
} /* Endif */
if ( tcb->rcvringbuf != NULL ) {
_mem_free(tcb->rcvringbuf);
tcb->rcvringbuf = NULL;
} /* Endif */
if ( tcb->sndclk != NULL ) {
_mem_free(tcb->sndclk);
tcb->sndclk = NULL;
} /* Endif */
} /* Endif */
RTCSCMD_complete(req_ptr, result);
return;
} /* Endbody */
/*FUNCTION*-------------------------------------------------------------
*
* Function Name : TCP_Process_accept
* Returned Values : None.
* Comments :
*
* Called when the application accept()s a new connection.
*
*END*-----------------------------------------------------------------*/
void TCP_Process_accept
(
TCP_PARM_PTR req_ptr /* IN/OUT - the accept request */
)
{ /* Body */
TCP_CFG_STRUCT_PTR tcp_cfg; /* TCP Layer constants */
TCB_STRUCT_PTR tcb;
TCB_STRUCT_PTR _PTR_ search_ptr;
tcp_cfg = RTCS_getcfg(TCP);
tcb = req_ptr->TCB_PTR;
if ( tcb == NULL ) {
RTCSCMD_complete(req_ptr, RTCSERR_TCP_CONN_RLSD);
return;
}
search_ptr = &tcp_cfg->NOSOCK_TCB;
if (tcb->conn_pending) {
tcb->conn_pending--;
/* Check if a listening TCB has spawned another TCB */
while (*search_ptr) {
if ((*search_ptr)->LISTEN_TCB == tcb &&
(*search_ptr)->conn_pending)
{
/* Found a match */
(*search_ptr)->conn_pending--;
req_ptr->TCB_PTR = *search_ptr;
*search_ptr = (*search_ptr)->NOSOCK_NEXT;
req_ptr->TCB_PTR->SOCKET = req_ptr->SOCKET;
((SOCKET_STRUCT_PTR)req_ptr->SOCKET)->TCB_PTR = req_ptr->TCB_PTR;
req_ptr->TCB_PTR->LISTEN_TCB = NULL;
RTCSCMD_complete(req_ptr, RTCS_OK);
return;
} /* Endif */
search_ptr = &(*search_ptr)->NOSOCK_NEXT;
} /* Endwhile */
/* Didn't find any TCBs. The TCB must have been released by a RST */
RTCSCMD_complete(req_ptr, RTCSERR_TCP_CONN_RESET);
} else {
/* Hang on to open requests until established */
req_ptr->NEXT = tcp_cfg->OPENS;
tcp_cfg->OPENS = req_ptr;
} /* Endif */
} /* Endbody */
/*FUNCTION*-------------------------------------------------------------
*
* Function Name : TCP_Process_signal
* Returned Values : None.
* Comments :
*
* Process TCP timeouts.
*
*END*-----------------------------------------------------------------*/
void TCP_Process_signal
(
void
)
{ /* Body */
TCP_CFG_STRUCT_PTR tcp_cfg;
TCB_STRUCT_PTR tcb;
TcbTimeQ _PTR_ tq;
int_32 result;
tcp_cfg = RTCS_getcfg(TCP);
RTCSLOG_FNE5(RTCSLOG_FN_TCP_Process_signal, tcp_cfg->xmittq, tcp_cfg->acktq, tcp_cfg->rcvtq, tcp_cfg->sndtq);
/*
** Process any (re)transmission timeouts
*/
while (1) {
tq = tcp_cfg->xmittq;
if ( tq == NULL ) {
break;
} /* Endif */
TCP_Timer_remove(&tq->t); /* remove from its list */
tcb = tq->tcb;
DEBUGTCP(printf("\nTCP: (re)transmission timeout, tcb @ %p", tcb));
if ( tcb->VALID == TCB_VALID_ID ) {
if ( tcb->state == TIME_WAIT ) { /* was it a time-wait timer? */
/*
** 2MSL timeout expired
*/
TCP_Close_TCB(tcb, RTCS_OK, tcp_cfg);
TCP_Process_release(tcb, tcp_cfg);
continue; /* at start of LOOP */
} /* Endif */
/*
** Are we probing a zero-sized send window?
*/
if ( tcb->sndxmitwhat == TCP_PROBE ) {
/*
** exponential backoff, clamped
*/
if ( (tcb->sndprobeto <<= 1) > tcb->sndprobetomax ) {
tcb->sndprobeto = tcb->sndprobetomax;
} /* Endif */
TCP_Transmit(tcb, tcp_cfg, FALSE, TRUE);
/* will restart xmittq with probeto */
} else { /* else we must be retransmitting (sndxmitwhat EQ TCP_XMIT) */
if ( tcb->sndxmitwhat != TCP_XMIT ) {
RTCS_log_error( ERROR_TCP, RTCSERR_TCP_REXMIT_PROBLEM,
(uint_32)tcb->sndxmitwhat, (uint_32)tcb, 0 );
} /* Endif */
/* congestion avoidance */
if ( tcb->sndwnd < tcb->sndcwnd ) {
tcb->sndstresh = tcb->sndwnd >> 1;
} else {
tcb->sndstresh = tcb->sndcwnd >> 1;
} /* Endif */
tcb->sndcwnd = tcb->sndmax; /* slow-start */
if ( (tcb->sndrto <<= 1) > tcb->sndrtomax ) {
/*
** exponential backoff
*/
tcb->sndrto = tcb->sndrtomax; /* clamped */
} /* Endif */
TCP_Transmit(tcb, tcp_cfg, TRUE, TRUE); /* force retransmission */
tcb->rexmts++;
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_TX_DATA_DUP++);
/*
** restart the timeout
*/
result =
TCP_Timer_start( &tq->t, /* the timer */
tcb->sndrto, /* the timeout value */
0, /* no reload */
tcp_cfg->qhead ); /* the timer queue head */
if ( result != RTCS_OK ) {
RTCS_log_error( ERROR_TCP, RTCSERR_TCP_TIMER_FAILURE,
(uint_32)result, (uint_32)tcb, 0 );
} /* Endif */
} /* Endif */
} /* Endif */
} /* EndWhile */
/*
** Then, process any ACK timers (Ack's to send)
*/
while (1) {
tq = tcp_cfg->acktq;
if ( tq == NULL ) {
break;
} /* Endif */
TCP_Timer_remove(&tq->t); /* remove from its list */
tcb = tq->tcb;
if ( tcb->VALID == TCB_VALID_ID ) {
DEBUGTCP(printf("\nTCP: ACK timer, tcb @ %p", tcb));
TCP_Transmit(tcb, tcp_cfg, FALSE, TRUE);
/*
** If sender idle, restart keep-alive timer
*/
if ( tcb->sndlen == 0 && tcb->keepaliveto != 0 ) {
TCP_Send_keepalive(tcb,tcp_cfg);
} /* Endif */
} /* Endif */
} /* EndWhile */
/*
** Process receive timers
*/
while (1) {
tq = tcp_cfg->rcvtq;
if ( tq == NULL ) {
break;
} /* Endif */
TCP_Timer_remove(&tq->t); /* remove from its list */
tcb = tq->tcb;
DEBUGTCP(printf("\nTCP: receive timer, tcb @ %p", tcb));
if ( tcb->VALID == TCB_VALID_ID ) {
if ( tcb->rcvHead != NULL ) { /* should be true, but check anyway */
TCP_Reply_receive(tcb, tcp_cfg, RTCSERR_TCP_TIMED_OUT);
/*
** Setup next receive
*/
while ( tcb->rcvHead != NULL &&
! TCP_Setup_receive(tcb->rcvHead, tcb, tcp_cfg)
) {
} /* Endwhile */
} /* Endif */
} /* Endif */
} /* EndWhile */
/*
** Finally, process any Send timers (transmission not ack'd
** within timeout)
*/
while (1) {
tq = tcp_cfg->sndtq;
if ( tq == NULL ) {
break;
} /* Endif */
TCP_Timer_remove(&tq->t); /* remove from its list */
tcb = tq->tcb;
DEBUGTCP(printf("\nTCP: send timer, tcb @ %p", tcb));
if ( tcb->VALID == TCB_VALID_ID ) {
if ( tcb->state != CLOSED && tcb->sndtohard ) {
/*
** hard timeout reached
*/
TCP_Close_TCB(tcb, RTCSERR_TCP_TIMED_OUT, tcp_cfg);
if (tcb->SOCKET == 0) { /* Free TCB if unused */
TCP_Process_release(tcb, tcp_cfg);
} /* Endif */
} else { /* soft timeout reached */
/*
** User timeout; sent data not acked in time
*/
TCP_Event(tq->tcb, TCPS_TIMEOUT);
if ( tcb->sndto_2 != 0 ) { /* start hard timeout... */
result =
TCP_Timer_start( &tq->t, /* the timer */
tcb->sndto_2, /* the timeout value */
0, /* no reload */
tcp_cfg->qhead ); /* the timer queue head */
if ( result != RTCS_OK ) {
RTCS_log_error( ERROR_TCP, RTCSERR_TCP_TIMER_FAILURE,
(uint_32)result, (uint_32)tcb, 1 );
} /* Endif */
tcb->sndtohard = TRUE;
} /* Endif */
/*
** Tell IP about it...
*/
/* IP_Advise_delivery_problem( 0,tcb->icb,tcb->rem_host,tcb->tos );
*/
tcb->sndtmouts++;
/* tcp_cfg->STATS.ST_SNDTMOUTS++; */
} /* Endif */
} /* Endif */
}
RTCSLOG_FNX1(RTCSLOG_FN_TCP_Process_signal);
} /* Endbody */
/*FUNCTION*-------------------------------------------------------------
*
* Function Name : TCP_Return_open
* Returned Values : None.
* Comments :
*
* Finds and returns the TcpOpen or TcpListen request back to the
* upper layer.
*
*END*-----------------------------------------------------------------*/
void TCP_Return_open
(
TCB_STRUCT_PTR tcb, /* IN/OUT - TCP context */
uint_32 errnum, /* IN - reason for returning */
TCP_CFG_STRUCT_PTR tcp_cfg /* IN/OUT - TCP layer constants */
)
{ /* Body */
TCP_PARM_PTR req_ptr; /* upper layer request */
TCP_PARM_PTR prev_ptr; /* previous upper layer request */
TCB_STRUCT_PTR _PTR_ search_ptr = &tcp_cfg->NOSOCK_TCB;
/* Return open request to upper layer */
prev_ptr = NULL;
req_ptr = tcp_cfg->OPENS;
while (req_ptr != NULL &&
tcb != req_ptr->TCB_PTR &&
tcb->LISTEN_TCB != req_ptr->TCB_PTR) {
prev_ptr = req_ptr;
req_ptr = req_ptr->NEXT;
} /* Endwhile */
if (req_ptr != NULL) {
if (prev_ptr == NULL) {
tcp_cfg->OPENS = req_ptr->NEXT;
} else {
prev_ptr->NEXT = req_ptr->NEXT;
} /* Endif */
req_ptr->NEXT = NULL;
/* Remove the TCB from the list of TCBS spawned by the listening TCB */
while (*search_ptr) {
if (*search_ptr == tcb) {
*search_ptr = (*search_ptr)->NOSOCK_NEXT;
tcb->SOCKET = req_ptr->SOCKET;
((SOCKET_STRUCT_PTR)req_ptr->SOCKET)->TCB_PTR = tcb;
tcb->LISTEN_TCB = NULL;
break;
} /* Endif */
search_ptr = &(*search_ptr)->NOSOCK_NEXT;
} /* Endwhile */
req_ptr->TCB_PTR = tcb;
RTCSCMD_complete(req_ptr, errnum);
} else {
if (tcb->state != CLOSED) { /* Only increment count if we receive a new connection */
tcb->conn_pending++;
} /* Endid */
if (tcb->LISTEN_TCB == NULL) {
SOCK_select_signal(tcb->SOCKET, tcb->state);
} else {
if (tcb->state != CLOSED) {
/* Only increment count if we receive a new connection */
tcb->LISTEN_TCB->conn_pending++;
} /* Endif */
SOCK_select_signal(tcb->LISTEN_TCB->SOCKET, tcb->state);
} /* Endif */
} /* Endif */
} /* Endbody */
/*FUNCTION*-------------------------------------------------------------
*
* Function Name : TCP_Clone_tcb
* Returned Values : A new TCB in the SYN_RECEIVED state (or NULL)
* Comments :
*
* Takes a listening TCB in the listen state and creates a new TCB to be
* placed in the SYN_RECEIVED state.
*
*END*-----------------------------------------------------------------*/
TCB_STRUCT_PTR TCP_Clone_tcb
(
TCB_STRUCT_PTR listen_ptr /* IN - the TCB to clone */
)
{ /* Body */
TCB_STRUCT_PTR new_tcb;
TCP_CFG_STRUCT_PTR tcp_cfg = RTCS_getcfg(TCP);
new_tcb = RTCS_mem_alloc_zero(sizeof(TCB_STRUCT));
/* Make sure memory allocation succeeded */
if (new_tcb == NULL) {
return NULL;
} /* Endif */
_mem_set_type(new_tcb, MEM_TYPE_TCB);
/* Clone the TCB */
_mem_copy(listen_ptr, new_tcb, sizeof(TCB_STRUCT));
new_tcb->sndringbuf = (uchar _PTR_)RTCS_mem_alloc(new_tcb->sndringlen);
new_tcb->rcvringbuf = (uchar _PTR_)RTCS_mem_alloc(new_tcb->rcvringlen);
new_tcb->sndclk = RTCS_mem_alloc(new_tcb->sndclks*sizeof(SndClock));
/* Check if memory allocation succeeded */
if (new_tcb->sndringbuf == NULL ||
new_tcb->rcvringbuf == NULL ||
new_tcb->sndclk == NULL)
{
/* Free memory */
if (new_tcb->sndringbuf) {
_mem_free(new_tcb->sndringbuf);
} /* Endif */
if (new_tcb->rcvringbuf) {
_mem_free(new_tcb->rcvringbuf);
} /* Endif */
if (new_tcb->sndclk) {
_mem_free(new_tcb->sndclk);
} /* Endif */
_mem_free(new_tcb);
return NULL;
} /* Endif */
_mem_set_type(new_tcb->sndclk, MEM_TYPE_TCP_SEND_CLOCK);
_mem_set_type(new_tcb->sndringbuf, MEM_TYPE_TCP_TX_WINDOW);
_mem_set_type(new_tcb->rcvringbuf, MEM_TYPE_TCP_RX_WINDOW);
/* Initialize new TCB */
new_tcb->next = tcp_cfg->TCBhead;
tcp_cfg->TCBhead = new_tcb;
new_tcb->SOCKET = 0;
new_tcb->LISTEN_TCB = listen_ptr;
new_tcb->rcvbuf = new_tcb->rcvringbuf;
new_tcb->conn_pending = 0;
tcp_cfg->CONN_COUNT++;
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_CONN_OPEN++);
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_CONN_PASSIVE++);
/* Setup timer queues */
new_tcb->rcvtq.tcb = new_tcb;
new_tcb->sndxmittq.tcb = new_tcb;
new_tcb->sndacktq.tcb = new_tcb;
new_tcb->sndtq.tcb = new_tcb;
/* Setup send clocks */
new_tcb->sndclkhead = new_tcb->sndclk;
new_tcb->sndclktail = new_tcb->sndclk;
new_tcb->sndclkend = new_tcb->sndclk + new_tcb->sndclks;
new_tcb->NOSOCK_NEXT = tcp_cfg->NOSOCK_TCB;
tcp_cfg->NOSOCK_TCB = new_tcb;
#if RTCSCFG_TCP_MAX_HALF_OPEN
new_tcb->tcb_spawn_time = RTCS_time_get();
/* The first element will be stored in index 0 */
tcp_cfg->HALF_OPEN_TCB_LIST[tcp_cfg->HALF_OPEN_TCB_COUNT] = new_tcb;
tcp_cfg->HALF_OPEN_TCB_COUNT++;
#endif
/* New TCB needs to inherit the TX options */
new_tcb->TX = listen_ptr->TX;
return new_tcb;
} /* Endbody */
/*FUNCTION*-------------------------------------------------------------
*
* Function Name : TCP6_Process_packet
* Returned Values : None.
* Comments :
*
* Process an incoming TCP packet. Note that the PCB is freed by
* the routine it is consumed in.
*
*END*-----------------------------------------------------------------*/
#if RTCSCFG_ENABLE_IP6
void TCP6_Process_packet
(
TCB_STRUCT_PTR tcb, /* IN/OUT - TCP context */
RTCSPCB_PTR pcb, /* IN/OUT - the incoming packet */
TCP_CFG_STRUCT_PTR tcp_cfg /* IN/OUT - TCP layer constants */
)
{ /* Body */
TCP_HEADER_PTR seg;
uint_16 seglen;
uint_16 flags;
uint_16 optlen;
uchar _PTR_ opt;
uint_32 seq,ack;
uint_16 urg;
int_32 result; /* returned result */
boolean seg_okay;
/*IPv6*/
in6_addr ipv6_source;
seg_okay = TRUE;
seg = (TCP_HEADER _PTR_)RTCSPCB_DATA(pcb);
flags = ntohs(seg->flags);
RTCSLOG_FNE5(RTCSLOG_FN_TCP_Process_packet, tcb, pcb, tcp_cfg, flags);
RTCSPCB_next(pcb, (flags & DATAOFS_MASK) >> (DATAOFS_SHIFT - 2));
/*
** Drop packets that have illegal/abnormal flag combinations, as these
** are almost certainly malicious.
**
** - SYN FIN is probably the best known illegal combination. Remember
** that SYN is used to start a connection, while FIN is used to end
** an existing connection. It is nonsensical to perform both actions
** at the same time. Many scanning tools use SYN FIN packets, because
** many intrusion detection systems did not catch these in the past,
** although most do so now. You can safely assume that any SYN FIN
** packets you see are malicious.
**
** - SYN FIN PSH, SYN FIN RST, SYN FIN RST PSH, and other variants on
** SYN FIN also exist. These packets may be used by attackers who are
** aware that intrusion detection systems may be looking for packets
** with just the SYN and FIN bits set, not additional bits set. Again,
** these are clearly malicious.
**
** - Packets should never contain just a FIN flag. FIN packets are
** frequently used for port scans, network mapping and other stealth
** activities.
**
** - Some packets have absolutely no flags set at all; these are referred
** to as "null" packets. It is illegal to have a packet with no flags set.
*/
if ( ((flags & SYN) && (flags & FIN)) ||
(flags == FIN) ||
(flags == 0) ) {
TCP_PP_DROP;
}
/*
** compute SEG.LEN
*/
seglen = RTCSPCB_SIZE(pcb); /* socket layer data size */
if ( flags & SYN ) {
seglen++;
} /* Endif */
if ( flags & FIN ) {
seglen++;
} /* Endif */
/* using IPv6 copy macros to copy the IPv6 address structure */
IN6_ADDR_COPY((in6_addr *)((IP6_HEADER_PTR)RTCSPCB_DATA_NETWORK(pcb))->SOURCE, &ipv6_source);
/*
** Process options
*/
opt = (uchar _PTR_)seg + sizeof(TCP_HEADER);
while ( opt < RTCSPCB_DATA(pcb) ) {
switch ( (ntohc(opt)) ) {
case OPT_PAD:
opt = RTCSPCB_DATA(pcb);
break;
case OPT_NOOP:
opt++;
break;
case OPT_MSS:
{ /* Scope */
uint_16 mss;
optlen = ntohc(opt+1);
if ( optlen == 0 ) {
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_BAD_OPTION++);
TCP_PP_DROP;
} /* Endif */
mss = ntohs(opt+2);
/*
** Compare with tcb->rcvmss since it is set to
** our maximum allowable, and doesn't change:
*/
if ( tcb->rcvmss != 0 && mss > tcb->rcvmss ) {
mss = tcb->rcvmss;
} /* Endif */
tcb->sndmss = mss;
tcb->sndmax = mss; /* minus length of any IP and TCP options; */
if ( mss < tcb->rcvmax ) {
tcb->rcvmax = mss;
/*
** Will reply after 1.75*mss bytes have been received.
*/
tcb->rcv2full = mss + (mss>>1) + (mss>>2);
} /* Endif */
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_OPT_MSS++);
opt += optlen;
break;
} /* Endscope */
default:
/*
** All options will have a length byte
*/
optlen = ntohc(opt+1);
if ( optlen == 0 ) {
/*
** should send ICMP parameter problem
*/
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_BAD_OPTION++);
TCP_PP_DROP;
} /* Endif */
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_OPT_OTHER++);
opt += optlen;
} /* End Switch */
} /* Endwhile */
/*
** Process packet:
**
** (RFC 793 sec.3.9, 'SEGMENT ARRIVES' (pages 65-76);
** steps are indicated as FIRST, SECOND, THIRD, etc. as in the RFC)
*/
seq = ntohl(seg->seq);
ack = ntohl(seg->ack);
urg = ntohs(seg->urgent);
switch ( tcb->state ) {
/*
** This case is executed only when in a invalid state, since
** packets aren't queued on CLOSED connections, and closing
** connections (see TCP_Close_TCB()) disown their queued packets...
*/
case CLOSED:
if( (flags & RST) == 0 )
{
TCP_STATE_DEBUG(("state = CLOSED, replying with RST on tcb %p\n", tcb));
TCP6_Send_reply_reset(tcb,
&ipv6_source, /* source address */
seg, /* received packet */
seglen, /* length of received packet */
tcp_cfg, /* TCP layer data */
FALSE
);
} /* Endif */
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_LATE_DATA++);
TCP_PP_DROP;
case LISTEN:
if ( (flags & RST) != 0 ) { /* FIRST */
TCP_STATE_DEBUG(("state = LISTEN, heard reset on tcb %p, dropping.\n", tcb));
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_RESET++);
TCP_PP_DROP;
} /* Endif */
if( (flags & ACK) != 0 )
{ /* SECOND */
TCP6_Send_reply_reset(tcb,
&ipv6_source, /* source address */
seg, /* received packet */
seglen, /* length of received packet */
tcp_cfg, /* TCP layer data */
tcb->bypass_tx_chksum
);
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_BAD_ACK++);
TCP_PP_DROP;
} /* Endif */
if ( (flags & SYN) != 0 ) { /* THIRD */
#if RTCSCFG_TCP_MAX_HALF_OPEN
if ((tcp_cfg->HALF_OPEN_TCB_COUNT >= RTCSCFG_TCP_MAX_HALF_OPEN)
#if RTCSCFG_TCP_MAX_CONNECTIONS
|| (tcp_cfg->CONN_COUNT >= RTCSCFG_TCP_MAX_CONNECTIONS)
#endif
) {
TCP_half_open_TCB_close(tcp_cfg);
} /* Endif */
#endif
#if RTCSCFG_TCP_MAX_CONNECTIONS
if (tcp_cfg->CONN_COUNT >= RTCSCFG_TCP_MAX_CONNECTIONS) {
TCP_PP_DROP;
} /* Endif */
#endif
CHECK_SECURITY;
CHECK_PRECEDENCE;
tcb = TCP_Clone_tcb(tcb);
if (tcb == NULL) {
TCP_PP_DROP;
} /* Endif */
if ( IN6_ARE_ADDR_EQUAL(&tcb->local_ipv6_host, &in6addr_any))
{
IN6_ADDR_COPY((in6_addr *)((IP6_HEADER_PTR)RTCSPCB_DATA_NETWORK(pcb))->DEST, &(tcb->local_ipv6_host));
} /* Endif */
if ( IN6_ARE_ADDR_EQUAL(&tcb->remote_ipv6_host, &in6addr_any) )
{
IN6_ADDR_COPY(&ipv6_source,&(tcb->remote_ipv6_host));
tcb->rcvmss = IP6_MTU(&(tcb->local_ipv6_host),&ipv6_source) - (uint_16) (IP6_HEADSIZE + TCP_HEADSIZE);
if(tcb->sndmss > tcb->rcvmss)
{
tcb->sndmss = tcb->rcvmss;
}
tcb->sndmax = tcb->sndmss;
} /* Endif */
tcb->remote_port = ntohs(seg->source_port);
/*
** TCP_Start_receiving sets state to SYN_RECEIVED
*/
TCP_Start_receiving(tcb, seq, tcp_cfg);
/*
** also below, in case SYN_SENT
*/
TCP_Start_sending(tcb, tcp_cfg);
/*
** Release the packet
*/
RTCSLOG_PCB_FREE(pcb, RTCS_OK);
RTCSPCB_free(pcb);
}
else
{
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_SYN_EXPECTED++);
TCP_PP_DROP; /* FOURTH */
} /* Endif */
break;
case SYN_SENT:
if ( (flags & ACK) != 0 ) { /* FIRST */
if ( LE32(ack, tcb->sndiss) ||
GT32(ack, tcb->sndnxt) )
{
/*
** invalid ACK: drop, and reply if reset
*/
if ( ! (flags & RST) ) {
TCP_STATE_DEBUG(("state = SYN_SENT, heard invalid ACK on tcb %p, sending RST\n", tcb));
TCP6_Send_reply_reset(
tcb,
&ipv6_source, /* source address */
seg, /* received packet */
seglen, /* length of received packet */
tcp_cfg, /* TCP layer data */
tcb->bypass_tx_chksum
);
} /* Endif */
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_BAD_ACK++);
TCP_PP_DROP;
} /* Endif */
if ( LT32(ack, tcb->snduna) ) {
/*
** unnacceptable ACK (already ack'ed): drop
*/
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_BAD_ACK++);
TCP_PP_DROP;
} /* Endif */
} /* Endif */
if ( (flags & RST) != 0 ) { /* SECOND */
TCP_STATE_DEBUG(("state = SYN_SENT, heard RST on tcb %p\n", tcb));
if ( (flags & ACK) != 0 ) {
TCP_STATE_DEBUG(("state = SYN_SENT, heard RST+ACK on tcb %p, closing&dropping\n", tcb));
TCP_Close_TCB(tcb, RTCSERR_TCP_CONN_RESET, tcp_cfg);
TCP_Process_release(tcb, tcp_cfg);
} else {
TCP_STATE_DEBUG(("state = SYN_SENT, ACK was not set so not closing & yes dropping on tcb %p\n", tcb));
}/* Endif */
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_RESET++);
TCP_PP_DROP;
} /* Endif */
CHECK_SECURITY; /* else reset & DROP */ /* THIRD */
if ( (flags & ACK) != 0 ) {
CHECK_EXACT_PRECEDENCE; /* else reset & DROP */
} else {
CHECK_PRECEDENCE; /* may raise; if can't, reset & DROP */
} /* Endif */
if ( (flags & SYN) != 0 ) { /* FOURTH */
TCP_Start_receiving(tcb, seq, tcp_cfg); /* State=>SYN_RCVD */
if ( (flags & ACK) != 0 ) {
if ( GE32(ack, tcb->snduna) ) {
TCP_Process_ack(tcb, seg, tcp_cfg);
} /* Endif */
} /* Endif */
if ( GT32(tcb->snduna, tcb->sndiss) ) {
tcb->state = ESTABLISHED;
TCP_Update_send_window(tcb,seg);
TCP_Return_open(tcb, RTCS_OK, tcp_cfg);
TCP_Event(tcb, TCPS_STARTED);
TCP_Transmit(tcb, tcp_cfg, FALSE, TRUE);
goto step6; /* named from RFC793... */
} else {
/* force retransmit of SYN */
TCP_Transmit(tcb, tcp_cfg, TRUE, TRUE);
} /* Endif */
/*
** any data or other controls are ignored, not queued
** for processing after state becomes ESTABLISHED
*/
/*
** Free the packet
*/
RTCSLOG_PCB_FREE(pcb, RTCS_OK);
RTCSPCB_free(pcb);
} else {
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_SYN_EXPECTED++);
TCP_PP_DROP; /* FIFTH */
} /* Endif */
break;
/* all included in default
** case SYN_RECEIVED:
** case ESTABLISHED:
** case FINWAIT_1:
** case FINWAIT_2:
** case CLOSE_WAIT:
** case CLOSING:
** case LAST_ACK:
** case TIME_WAIT:
*/
default:
/**
** From RFC 793:
**
** There are four cases for the acceptability test for an incoming
** segment:
**
** Segment Receive Test
** Length Window
** ------- ------- -------------------------------------------
**
** 0 0 SEG.SEQ = RCV.NXT
**
** 0 >0 RCV.NXT =< SEG.SEQ < RCV.NXT+RCV.WND
**
** >0 0 not acceptable
**
** >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, URGs and
** RSTs.
**
** If an incoming segment is not acceptable, an acknowledgment
** should be sent in reply (unless the RST bit is set, if so drop
** the segment and return):
**
** <SEQ=SND.NXT><ACK=RCV.NXT><CTL=ACK>
**
** After sending the acknowledgment, drop the unacceptable segment
** and return.
*/
/*
** Handles all cases explicitly. seg_okay is used later in this procedure
** to drop an un-acceptable segment after processing its RST/ACK/URG fields
** in the case where the window is 0. Note, seglen may have been set to 1 to
** handle SYN or FIN flags.
*/
{ /* Scope */
uint_16 rwindow;
boolean okay;
rwindow = tcb->rcvwndedge - tcb->rcvnxt;
if ( (rwindow == 0) && /* Case 1 */
(seglen == 0 || (flags & SYN && seglen == 1)) &&
(seq == tcb->rcvnxt))
{
okay = TRUE;
seg_okay = TRUE;
} else if ( (rwindow > 0) && /* Case 2 */
(seglen == 0 || (flags & SYN && seglen == 1)) &&
(LE32(tcb->rcvnxt,seq) &&
LT32(seq,tcb->rcvwndedge)))
{
okay = TRUE;
seg_okay = TRUE;
} else if ((flags & FIN) &&
LT32(tcb->rcvnxt,seq))
{
okay = TRUE;
seg_okay = FALSE;
} else if ( (seglen > 0) && /* Case 4 */
(rwindow > 0) &&
((LE32(tcb->rcvnxt,seq) &&
LT32(seq,tcb->rcvwndedge)) ||
(LE32(tcb->rcvnxt,seq+seglen-1) &&
LT32(seq+seglen-1,tcb->rcvwndedge))))
{
okay = TRUE;
seg_okay = TRUE;
} else { /* Case 3 or others failed */
seg_okay = FALSE;
if ( (rwindow == 0) && (flags & (RST | URG | ACK)) != 0 ) {
RTCSPCB_SIZE(pcb) = 0;
okay = TRUE;
/*tcp_cfg->STATS.ST_RX_ACC_BADSEGS++;*/
} else {
okay = FALSE;
} /* Endif */
} /* Endif */
if ( ! okay ) {
/*
** segment unacceptable, sent ACK
*/
if ( (flags & RST) == 0 ) {
TCP_Must_send_ack(tcb, tcp_cfg);
} /* Endif */
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_BAD_DATA++);
TCP_PP_DROP;
} /* Endif */
} /* Endscope */
/*
** Process the RST
*/
if ( (flags & RST) != 0 ) { /* SECOND */
TCP_STATE_DEBUG(("state = default(%d), heard reset on tcb %p, ", tcb->state, tcb));
/* draft-ietf-tcpm-tcpsecure-00.txt section 2.2 */
if (tcb->tcpsecuredraft0) {
if (seq == tcb->rcvnxt) {
TCP_STATE_DEBUG(("RST is valid, closing&dropping.\n"));
TCP_Close_TCB(tcb, RTCSERR_TCP_CONN_RESET, tcp_cfg); /* aborted */
TCP_Process_release(tcb, tcp_cfg);
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_RESET++);
TCP_PP_DROP;
}
else
{
TCP_STATE_DEBUG(("RST is suspicious, acking&dropping\n"));
TCP_Must_send_ack(tcb, tcp_cfg);
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_RESET++);
TCP_PP_DROP;
} /* Endif */
} else {
TCP_Close_TCB(tcb, RTCSERR_TCP_CONN_RESET, tcp_cfg); /* aborted */
TCP_Process_release(tcb, tcp_cfg);
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_RESET++);
TCP_PP_DROP;
}
TCP_STATE_DEBUG(("\n"));
} /* Endif */
if ( tcb->state == SYN_RECEIVED ) { /* THIRD */
CHECK_SECURITY; /* else reset & DROP */
} else {
CHECK_TIGHT_SECURITY;
/*
** else reset & TCP_Close_TCB(tcb,RTCSERR_TCP_CONN_RESET) & DROP
*/
} /* Endif */
/*
** Process the SYN
*/
if ( (flags & SYN) != 0 ) { /* FOURTH */
if (tcb->tcpsecuredraft0) {
/*
** draft-ietf-tcpm-tcpsecure-00.txt section 3.2
*/
if (seq == tcb->rcvnxt) { /* SYN and seq is exact match */
tcb->ackmodifier = 1; /* ack number of ack pkt will be decremented by 1 later */
}
else /* SYN and seq is acceptable even though not exact */
{
tcb->ackmodifier = 0;
}
TCP_Must_send_ack(tcb, tcp_cfg);
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_SYN_NOT_EXPECTED++);
TCP_PP_DROP;
}
else
{
/*
** shouldn't receive SYN
*/
TCP6_Send_reply_reset(
tcb,
&ipv6_source, /* source address */
seg, /* received packet */
seglen, /* length of received packet */
tcp_cfg, /* TCP layer data */
tcb->bypass_tx_chksum
);
if ( tcb->state != LISTEN ) {
TCP_Close_TCB(tcb, RTCSERR_TCP_CONN_RESET, tcp_cfg); /* state=>CLOSED */
if (tcb->SOCKET == 0) {
TCP_Process_release(tcb, tcp_cfg);
} /* Endif */
} /* Endif */
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_SYN_NOT_EXPECTED++);
TCP_PP_DROP;
} /* Endif */
} /* Endif */
/*
** Process the ACK
*/
if ( (flags & ACK) == 0 ) { /* FIFTH */
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_ACK_EXPECTED++);
if ( ! seg_okay ) {
TCP_Must_send_ack(tcb, tcp_cfg);
} /* Endif */
TCP_PP_DROP;
} /* Endif */
switch ( tcb->state ) {
case SYN_RECEIVED:
if ( LT32(ack, tcb->snduna) ||
GT32(ack, tcb->sndnxt) )
{
TCP6_Send_reply_reset(
tcb,
&ipv6_source, /* source address */
seg, /* received packet */
seglen, /* length of received packet */
tcp_cfg, /* TCP layer data */
tcb->bypass_tx_chksum
);
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_BAD_ACK++);
TCP_PP_DROP;
} /* Endif */
tcb->state = ESTABLISHED;
#if RTCSCFG_TCP_MAX_HALF_OPEN
TCB_HALF_OPEN_DROP;
#endif
TCP_Update_send_window(tcb, seg);
TCP_Return_open(tcb, RTCS_OK, tcp_cfg);
TCP_Event(tcb, TCPS_STARTED);
/*FALLTHROUGH*/
case ESTABLISHED:
case CLOSE_WAIT:
case FINWAIT_1:
case FINWAIT_2:
case CLOSING:
if ( GT32(ack, tcb->sndnxt) ) {
/*
** ACK of unsent data
*/
TCP_Must_send_ack(tcb, tcp_cfg);
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_BAD_ACK++);
TCP_PP_DROP;
} /* Endif */
if ( GE32(ack, tcb->snduna) ) {
/*
** Update send window
*/
if ( LT32(tcb->sndwl1, seq) ||
(tcb->sndwl1 == seq && LE32(tcb->sndwl2,ack))
) {
TCP_Update_send_window(tcb, seg);
} /* Endif */
TCP_Process_ack(tcb, seg, tcp_cfg);
} /* Endif */
switch ( tcb->state ) {
case FINWAIT_1:
/*
** if FIN is acked...
*/
if ( GT32(ack, tcb->sndbufseq + tcb->sndlen) ) {
tcb->state = FINWAIT_2;
} else {
break;
} /* Endif */
/*FALLTHROUGH*/
case FINWAIT_2:
/*
** acknowledge Close() if transmit queue empty
** (should already be empty)
*/
TCP_Event(tcb, TCPS_FINACKED);
break;
case CLOSING:
/* if FIN is acked... */
if ( GT32(ack, tcb->sndbufseq + tcb->sndlen) ) {
tcb->state = TIME_WAIT;
tcb->sndxmitwhat = TCP_IDLE; /* Ensure we're not probing */
result =
TCP_Timer_start( &tcb->sndxmittq.t, /* the timer */
tcb->timewaitto, /* the timeout */
0, /* no reload */
tcp_cfg->qhead ); /* timer q head */
if ( result != RTCS_OK ) {
RTCS_log_error( ERROR_TCP,
RTCSERR_TCP_TIMER_FAILURE,
(uint_32)result,
(uint_32)tcb,
2 );
} /* Endif */
} else {
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_BAD_ACK++);
TCP_PP_DROP;
} /* Endif */
break;
default:
break;
} /* End Switch */
break;
case LAST_ACK:
/*
** if FIN is acked...
*/
if ( ack == tcb->sndnxt ) { /* all data acked */
TCP_Event(tcb, TCPS_FINACKED);
TCP_Close_TCB(tcb, RTCS_OK, tcp_cfg);
TCP_Process_release(tcb, tcp_cfg);
TCP_PP_DROP;
} /* Endif */
break;
case TIME_WAIT:
break;
} /* End Switch */
step6: /* SIXTH */
switch ( tcb->state ) {
case ESTABLISHED:
case FINWAIT_1:
case FINWAIT_2:
/*
** Check URG
*/
if ( (flags & URG) != 0 ) {
if ( (tcb->status & TCPS_RURGENT) == 0 ||
GE32(seq + urg, tcb->rcvup)
) {
tcb->rcvup = seq + urg + 1;
TCP_Event(tcb, TCPS_RURGENT);
} /* Endif */
} /* Endif */
if ( ! seg_okay ) { /* Handled all URG/RST/ACK so drop it */
TCP_Must_send_ack(tcb, tcp_cfg);
TCP_PP_DROP;
} /* Endif */
TCP_Process_data(tcb, tcp_cfg, seg, pcb); /* SEVENTH */
break;
default:
break;
} /* End Switch */
if ( ! seg_okay ) { /* Handled all URG/RST/ACK so drop it */
TCP_Must_send_ack(tcb, tcp_cfg);
TCP_PP_DROP;
} /* Endif */
switch ( tcb->state ) {
/*
** do not process FIN for these states:
*/
case SYN_SENT:
TCP_PP_DROP;
default:
break;
} /* End Switch */
if ( (flags & FIN) != 0 ) { /* EIGHTH */
/*
** Record receipt of FIN, if this is the first time we get it
*/
if ( (tcb->status & TCPS_FINRECV) == 0 ) {
tcb->rcvnxt++;
} /* Endif */
/*
** Signal 'connection closing' (receiving side)
*/
TCP_Event(tcb,TCPS_FINRECV);
/*
** FIN implies PUSHing undelivered received data to user,
** So terminate any Receive requests
*/
while ( tcb->rcvHead != NULL ) {
TCP_Reply_receive(tcb, tcp_cfg, RTCSERR_TCP_CONN_CLOSING);
/*
** Setup next receive
*/
while ( tcb->rcvHead != NULL &&
! TCP_Setup_receive(tcb->rcvHead, tcb, tcp_cfg)
) {
} /* Endwhile */
} /* Endwhile */
/*
** Notify socket layer in case some task is blocked on select()
** waiting for data on this socket.
*/
SOCK_select_signal(tcb->SOCKET, tcb->state);
tcb->rcvwndedge = tcb->rcvnxt; /* zero receive window */
TCP_Must_send_ack(tcb, tcp_cfg); /* ACK the FIN */
if (tcb->keepaliveto) {
tcb->keepaliveto = 0;
}
switch ( tcb->state ) {
case SYN_RECEIVED:
case ESTABLISHED:
tcb->state = CLOSE_WAIT;
break;
case FINWAIT_1:
/*
** if FIN is ACKed
*/
if ( GT32(ack, tcb->sndbufseq + tcb->sndlen) ) {
/*FALLTHROUGH*/
} else {
tcb->state = CLOSING;
TCP_PP_DROP;
} /* Endif */
/*FALLTHROUGH*/
case FINWAIT_2:
tcb->state = TIME_WAIT;
tcb->sndxmitwhat = TCP_IDLE; /* Ensure we're not probing */
result = TCP_Timer_start( &tcb->sndxmittq.t, /* the timer */
tcb->timewaitto, /* the timeout */
0, /* no reload */
tcp_cfg->qhead ); /* queue head */
if ( result != RTCS_OK ) {
RTCS_log_error( ERROR_TCP, RTCSERR_TCP_TIMER_FAILURE,
(uint_32)result, (uint_32)tcb, 3 );
} /* Endif */
TCP_PP_DROP;
default:
break;
} /* End Switch */
} /* Endif */
RTCSLOG_PCB_FREE(pcb, RTCS_OK);
RTCSPCB_free(pcb);
RTCSLOG_FNX3(RTCSLOG_FN_TCP_Process_packet, tcb, 0);
return;
} /* End Switch */
RTCSLOG_FNX3(RTCSLOG_FN_TCP_Process_packet, tcb, 1);
return;
tcp_pp_drop:
RTCSLOG_PCB_FREE(pcb, RTCS_OK);
RTCSPCB_free(pcb);
RTCSLOG_FNX3(RTCSLOG_FN_TCP_Process_packet, tcb, 2);
return;
} /* Endbody */
#endif /* RTCSCFG_ENABLE_IP6 */
/*FUNCTION*-------------------------------------------------------------
*
* Function Name : TCP_Process_packet
* Returned Values : None.
* Comments :
*
* Process an incoming TCP packet. Note that the PCB is freed by
* the routine it is consumed in.
*
*END*-----------------------------------------------------------------*/
#if RTCSCFG_ENABLE_IP4
void TCP_Process_packet
(
TCB_STRUCT_PTR tcb, /* IN/OUT - TCP context */
RTCSPCB_PTR pcb, /* IN/OUT - the incoming packet */
TCP_CFG_STRUCT_PTR tcp_cfg /* IN/OUT - TCP layer constants */
)
{ /* Body */
_ip_address source;
TCP_HEADER_PTR seg;
uint_16 seglen;
uint_16 flags;
uint_16 optlen;
uchar _PTR_ opt;
uint_32 seq,ack;
uint_16 urg;
int_32 result; /* returned result */
boolean seg_okay;
seg_okay = TRUE;
seg = (TCP_HEADER _PTR_)RTCSPCB_DATA(pcb);
flags = ntohs(seg->flags);
RTCSLOG_FNE5(RTCSLOG_FN_TCP_Process_packet, tcb, pcb, tcp_cfg, flags);
RTCSPCB_next(pcb, (flags & DATAOFS_MASK) >> (DATAOFS_SHIFT - 2));
/*
** Drop packets that have illegal/abnormal flag combinations, as these
** are almost certainly malicious.
**
** - SYN FIN is probably the best known illegal combination. Remember
** that SYN is used to start a connection, while FIN is used to end
** an existing connection. It is nonsensical to perform both actions
** at the same time. Many scanning tools use SYN FIN packets, because
** many intrusion detection systems did not catch these in the past,
** although most do so now. You can safely assume that any SYN FIN
** packets you see are malicious.
**
** - SYN FIN PSH, SYN FIN RST, SYN FIN RST PSH, and other variants on
** SYN FIN also exist. These packets may be used by attackers who are
** aware that intrusion detection systems may be looking for packets
** with just the SYN and FIN bits set, not additional bits set. Again,
** these are clearly malicious.
**
** - Packets should never contain just a FIN flag. FIN packets are
** frequently used for port scans, network mapping and other stealth
** activities.
**
** - Some packets have absolutely no flags set at all; these are referred
** to as "null" packets. It is illegal to have a packet with no flags set.
*/
if ( ((flags & SYN) && (flags & FIN)) ||
(flags == FIN) ||
(flags == 0) ) {
TCP_PP_DROP;
}
/*
** compute SEG.LEN
*/
seglen = RTCSPCB_SIZE(pcb); /* socket layer data size */
if ( flags & SYN ) {
seglen++;
} /* Endif */
if ( flags & FIN ) {
seglen++;
} /* Endif */
source = IP_source(pcb);
/*
** Process options
*/
opt = (uchar _PTR_)seg + sizeof(TCP_HEADER);
while ( opt < RTCSPCB_DATA(pcb) ) {
switch ( (ntohc(opt)) ) {
case OPT_PAD:
opt = RTCSPCB_DATA(pcb);
break;
case OPT_NOOP:
opt++;
break;
case OPT_MSS:
{ /* Scope */
uint_16 mss;
optlen = ntohc(opt+1);
if ( optlen == 0 ) {
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_BAD_OPTION++);
TCP_PP_DROP;
} /* Endif */
mss = ntohs(opt+2);
/*
** Compare with tcb->rcvmss since it is set to
** our maximum allowable, and doesn't change:
*/
if ( tcb->rcvmss != 0 && mss > tcb->rcvmss ) {
mss = tcb->rcvmss;
} /* Endif */
tcb->sndmss = mss;
tcb->sndmax = mss; /* minus length of any IP and TCP options; */
if ( mss < tcb->rcvmax ) {
tcb->rcvmax = mss;
/*
** Will reply after 1.75*mss bytes have been received.
*/
tcb->rcv2full = mss + (mss>>1) + (mss>>2);
} /* Endif */
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_OPT_MSS++);
opt += optlen;
break;
} /* Endscope */
default:
/*
** All options will have a length byte
*/
optlen = ntohc(opt+1);
if ( optlen == 0 ) {
/*
** should send ICMP parameter problem
*/
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_BAD_OPTION++);
TCP_PP_DROP;
} /* Endif */
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_OPT_OTHER++);
opt += optlen;
} /* End Switch */
} /* Endwhile */
/*
** Process packet:
**
** (RFC 793 sec.3.9, 'SEGMENT ARRIVES' (pages 65-76);
** steps are indicated as FIRST, SECOND, THIRD, etc. as in the RFC)
*/
seq = ntohl(seg->seq);
ack = ntohl(seg->ack);
urg = ntohs(seg->urgent);
switch ( tcb->state ) {
/*
** This case is executed only when in a invalid state, since
** packets aren't queued on CLOSED connections, and closing
** connections (see TCP_Close_TCB()) disown their queued packets...
*/
case CLOSED:
if ( (flags & RST) == 0 ) {
TCP_STATE_DEBUG(("state = CLOSED, replying with RST on tcb %p\n", tcb));
TCP_Send_reply_reset(tcb,
source, /* source address */
seg, /* received packet */
seglen, /* length of received packet */
tcp_cfg, /* TCP layer data */
FALSE
);
} /* Endif */
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_LATE_DATA++);
TCP_PP_DROP;
case LISTEN:
if ( (flags & RST) != 0 ) { /* FIRST */
TCP_STATE_DEBUG(("state = LISTEN, heard reset on tcb %p, dropping.\n", tcb));
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_RESET++);
TCP_PP_DROP;
} /* Endif */
if ( (flags & ACK) != 0 ) { /* SECOND */
TCP_Send_reply_reset(tcb,
source, /* source address */
seg, /* received packet */
seglen, /* length of received packet */
tcp_cfg, /* TCP layer data */
tcb->bypass_tx_chksum
);
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_BAD_ACK++);
TCP_PP_DROP;
} /* Endif */
if ( (flags & SYN) != 0 ) { /* THIRD */
#if RTCSCFG_TCP_MAX_HALF_OPEN
if ((tcp_cfg->HALF_OPEN_TCB_COUNT >= RTCSCFG_TCP_MAX_HALF_OPEN)
#if RTCSCFG_TCP_MAX_CONNECTIONS
|| (tcp_cfg->CONN_COUNT >= RTCSCFG_TCP_MAX_CONNECTIONS)
#endif
) {
TCP_half_open_TCB_close(tcp_cfg);
} /* Endif */
#endif
#if RTCSCFG_TCP_MAX_CONNECTIONS
if (tcp_cfg->CONN_COUNT >= RTCSCFG_TCP_MAX_CONNECTIONS) {
TCP_PP_DROP;
} /* Endif */
#endif
CHECK_SECURITY;
CHECK_PRECEDENCE;
tcb = TCP_Clone_tcb(tcb);
if (tcb == NULL) {
TCP_PP_DROP;
} /* Endif */
if ( tcb->local_host == INADDR_ANY ) {
tcb->local_host = IP_dest(pcb);
} /* Endif */
if ( tcb->remote_host == INADDR_ANY ) {
tcb->remote_host = source;
tcb->rcvmss = IP_MTU(tcb->local_host, source)
- (uint_16) (IP_HEADSIZE + TCP_HEADSIZE);
if (tcb->sndmss > tcb->rcvmss) {
tcb->sndmss = tcb->rcvmss;
}
tcb->sndmax = tcb->sndmss;
} /* Endif */
tcb->remote_port = ntohs(seg->source_port);
/*
** TCP_Start_receiving sets state to SYN_RECEIVED
*/
TCP_Start_receiving(tcb, seq, tcp_cfg);
/*
** also below, in case SYN_SENT
*/
TCP_Start_sending(tcb, tcp_cfg);
/*
** Release the packet
*/
RTCSLOG_PCB_FREE(pcb, RTCS_OK);
RTCSPCB_free(pcb);
} else {
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_SYN_EXPECTED++);
TCP_PP_DROP; /* FOURTH */
} /* Endif */
break;
case SYN_SENT:
if ( (flags & ACK) != 0 ) { /* FIRST */
if ( LE32(ack, tcb->sndiss) ||
GT32(ack, tcb->sndnxt) )
{
/*
** invalid ACK: drop, and reply if reset
*/
if ( ! (flags & RST) ) {
TCP_STATE_DEBUG(("state = SYN_SENT, heard invalid ACK on tcb %p, sending RST\n", tcb));
TCP_Send_reply_reset(tcb,
source, /* source address */
seg, /* received packet */
seglen, /* length of received packet */
tcp_cfg, /* TCP layer data */
tcb->bypass_tx_chksum
);
} /* Endif */
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_BAD_ACK++);
TCP_PP_DROP;
} /* Endif */
if ( LT32(ack, tcb->snduna) ) {
/*
** unnacceptable ACK (already ack'ed): drop
*/
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_BAD_ACK++);
TCP_PP_DROP;
} /* Endif */
} /* Endif */
if ( (flags & RST) != 0 ) { /* SECOND */
TCP_STATE_DEBUG(("state = SYN_SENT, heard RST on tcb %p\n", tcb));
if ( (flags & ACK) != 0 ) {
TCP_STATE_DEBUG(("state = SYN_SENT, heard RST+ACK on tcb %p, closing&dropping\n", tcb));
TCP_Close_TCB(tcb, RTCSERR_TCP_CONN_RESET, tcp_cfg);
TCP_Process_release(tcb, tcp_cfg);
} else {
TCP_STATE_DEBUG(("state = SYN_SENT, ACK was not set so not closing & yes dropping on tcb %p\n", tcb));
}/* Endif */
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_RESET++);
TCP_PP_DROP;
} /* Endif */
CHECK_SECURITY; /* else reset & DROP */ /* THIRD */
if ( (flags & ACK) != 0 ) {
CHECK_EXACT_PRECEDENCE; /* else reset & DROP */
} else {
CHECK_PRECEDENCE; /* may raise; if can't, reset & DROP */
} /* Endif */
if ( (flags & SYN) != 0 ) { /* FOURTH */
TCP_Start_receiving(tcb, seq, tcp_cfg); /* State=>SYN_RCVD */
if ( (flags & ACK) != 0 ) {
if ( GE32(ack, tcb->snduna) ) {
TCP_Process_ack(tcb, seg, tcp_cfg);
} /* Endif */
} /* Endif */
if ( GT32(tcb->snduna, tcb->sndiss) ) {
tcb->state = ESTABLISHED;
TCP_Update_send_window(tcb,seg);
TCP_Return_open(tcb, RTCS_OK, tcp_cfg);
TCP_Event(tcb, TCPS_STARTED);
TCP_Transmit(tcb, tcp_cfg, FALSE, TRUE);
goto step6; /* named from RFC793... */
} else {
/* force retransmit of SYN */
TCP_Transmit(tcb, tcp_cfg, TRUE, TRUE);
} /* Endif */
/*
** any data or other controls are ignored, not queued
** for processing after state becomes ESTABLISHED
*/
/*
** Free the packet
*/
RTCSLOG_PCB_FREE(pcb, RTCS_OK);
RTCSPCB_free(pcb);
} else {
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_SYN_EXPECTED++);
TCP_PP_DROP; /* FIFTH */
} /* Endif */
break;
/* all included in default
** case SYN_RECEIVED:
** case ESTABLISHED:
** case FINWAIT_1:
** case FINWAIT_2:
** case CLOSE_WAIT:
** case CLOSING:
** case LAST_ACK:
** case TIME_WAIT:
*/
default:
/**
** From RFC 793:
**
** There are four cases for the acceptability test for an incoming
** segment:
**
** Segment Receive Test
** Length Window
** ------- ------- -------------------------------------------
**
** 0 0 SEG.SEQ = RCV.NXT
**
** 0 >0 RCV.NXT =< SEG.SEQ < RCV.NXT+RCV.WND
**
** >0 0 not acceptable
**
** >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, URGs and
** RSTs.
**
** If an incoming segment is not acceptable, an acknowledgment
** should be sent in reply (unless the RST bit is set, if so drop
** the segment and return):
**
** <SEQ=SND.NXT><ACK=RCV.NXT><CTL=ACK>
**
** After sending the acknowledgment, drop the unacceptable segment
** and return.
*/
/*
** Handles all cases explicitly. seg_okay is used later in this procedure
** to drop an un-acceptable segment after processing its RST/ACK/URG fields
** in the case where the window is 0. Note, seglen may have been set to 1 to
** handle SYN or FIN flags.
*/
{ /* Scope */
uint_16 rwindow;
boolean okay;
rwindow = tcb->rcvwndedge - tcb->rcvnxt;
if ( (rwindow == 0) && /* Case 1 */
(seglen == 0 || (flags & SYN && seglen == 1)) &&
(seq == tcb->rcvnxt))
{
okay = TRUE;
seg_okay = TRUE;
} else if ( (rwindow > 0) && /* Case 2 */
(seglen == 0 || (flags & SYN && seglen == 1)) &&
(LE32(tcb->rcvnxt,seq) &&
LT32(seq,tcb->rcvwndedge)))
{
okay = TRUE;
seg_okay = TRUE;
} else if ((flags & FIN) &&
LT32(tcb->rcvnxt,seq))
{
okay = TRUE;
seg_okay = FALSE;
} else if ( (seglen > 0) && /* Case 4 */
(rwindow > 0) &&
((LE32(tcb->rcvnxt,seq) &&
LT32(seq,tcb->rcvwndedge)) ||
(LE32(tcb->rcvnxt,seq+seglen-1) &&
LT32(seq+seglen-1,tcb->rcvwndedge))))
{
okay = TRUE;
seg_okay = TRUE;
} else { /* Case 3 or others failed */
seg_okay = FALSE;
if ( (rwindow == 0) && (flags & (RST | URG | ACK)) != 0 ) {
RTCSPCB_SIZE(pcb) = 0;
okay = TRUE;
/*tcp_cfg->STATS.ST_RX_ACC_BADSEGS++;*/
} else {
okay = FALSE;
} /* Endif */
} /* Endif */
if ( ! okay ) {
/*
** segment unacceptable, sent ACK
*/
if ( (flags & RST) == 0 ) {
TCP_Must_send_ack(tcb, tcp_cfg);
} /* Endif */
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_BAD_DATA++);
TCP_PP_DROP;
} /* Endif */
} /* Endscope */
/*
** Process the RST
*/
if ( (flags & RST) != 0 ) { /* SECOND */
TCP_STATE_DEBUG(("state = default(%d), heard reset on tcb %p, ", tcb->state, tcb));
/* draft-ietf-tcpm-tcpsecure-00.txt section 2.2 */
if (tcb->tcpsecuredraft0) {
if (seq == tcb->rcvnxt) {
TCP_STATE_DEBUG(("RST is valid, closing&dropping.\n"));
TCP_Close_TCB(tcb, RTCSERR_TCP_CONN_RESET, tcp_cfg); /* aborted */
TCP_Process_release(tcb, tcp_cfg);
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_RESET++);
TCP_PP_DROP;
}
else
{
TCP_STATE_DEBUG(("RST is suspicious, acking&dropping\n"));
TCP_Must_send_ack(tcb, tcp_cfg);
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_RESET++);
TCP_PP_DROP;
} /* Endif */
} else {
TCP_Close_TCB(tcb, RTCSERR_TCP_CONN_RESET, tcp_cfg); /* aborted */
TCP_Process_release(tcb, tcp_cfg);
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_RESET++);
TCP_PP_DROP;
}
TCP_STATE_DEBUG(("\n"));
} /* Endif */
if ( tcb->state == SYN_RECEIVED ) { /* THIRD */
CHECK_SECURITY; /* else reset & DROP */
} else {
CHECK_TIGHT_SECURITY;
/*
** else reset & TCP_Close_TCB(tcb,RTCSERR_TCP_CONN_RESET) & DROP
*/
} /* Endif */
/*
** Process the SYN
*/
if ( (flags & SYN) != 0 ) { /* FOURTH */
if (tcb->tcpsecuredraft0) {
/*
** draft-ietf-tcpm-tcpsecure-00.txt section 3.2
*/
if (seq == tcb->rcvnxt) { /* SYN and seq is exact match */
tcb->ackmodifier = 1; /* ack number of ack pkt will be decremented by 1 later */
}
else /* SYN and seq is acceptable even though not exact */
{
tcb->ackmodifier = 0;
}
TCP_Must_send_ack(tcb, tcp_cfg);
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_SYN_NOT_EXPECTED++);
TCP_PP_DROP;
}
else
{
/*
** shouldn't receive SYN
*/
TCP_Send_reply_reset(tcb,
source, /* source address */
seg, /* received packet */
seglen, /* length of received packet */
tcp_cfg, /* TCP layer data */
tcb->bypass_tx_chksum
);
if ( tcb->state != LISTEN ) {
TCP_Close_TCB(tcb, RTCSERR_TCP_CONN_RESET, tcp_cfg); /* state=>CLOSED */
if (tcb->SOCKET == 0) {
TCP_Process_release(tcb, tcp_cfg);
} /* Endif */
} /* Endif */
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_SYN_NOT_EXPECTED++);
TCP_PP_DROP;
} /* Endif */
} /* Endif */
/*
** Process the ACK
*/
if ( (flags & ACK) == 0 ) { /* FIFTH */
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_ACK_EXPECTED++);
if ( ! seg_okay ) {
TCP_Must_send_ack(tcb, tcp_cfg);
} /* Endif */
TCP_PP_DROP;
} /* Endif */
switch ( tcb->state ) {
case SYN_RECEIVED:
if ( LT32(ack, tcb->snduna) ||
GT32(ack, tcb->sndnxt) )
{
TCP_Send_reply_reset(tcb,
source, /* source address */
seg, /* received packet */
seglen, /* length of received packet */
tcp_cfg, /* TCP layer data */
tcb->bypass_tx_chksum
);
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_BAD_ACK++);
TCP_PP_DROP;
} /* Endif */
tcb->state = ESTABLISHED;
#if RTCSCFG_TCP_MAX_HALF_OPEN
TCB_HALF_OPEN_DROP;
#endif
TCP_Update_send_window(tcb, seg);
TCP_Return_open(tcb, RTCS_OK, tcp_cfg);
TCP_Event(tcb, TCPS_STARTED);
/*FALLTHROUGH*/
case ESTABLISHED:
case CLOSE_WAIT:
case FINWAIT_1:
case FINWAIT_2:
case CLOSING:
if ( GT32(ack, tcb->sndnxt) ) {
/*
** ACK of unsent data
*/
TCP_Must_send_ack(tcb, tcp_cfg);
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_BAD_ACK++);
TCP_PP_DROP;
} /* Endif */
if ( GE32(ack, tcb->snduna) ) {
/*
** Update send window
*/
if ( LT32(tcb->sndwl1, seq) ||
(tcb->sndwl1 == seq && LE32(tcb->sndwl2,ack))
) {
TCP_Update_send_window(tcb, seg);
} /* Endif */
TCP_Process_ack(tcb, seg, tcp_cfg);
} /* Endif */
switch ( tcb->state ) {
case FINWAIT_1:
/*
** if FIN is acked...
*/
if ( GT32(ack, tcb->sndbufseq + tcb->sndlen) ) {
tcb->state = FINWAIT_2;
} else {
break;
} /* Endif */
/*FALLTHROUGH*/
case FINWAIT_2:
/*
** acknowledge Close() if transmit queue empty
** (should already be empty)
*/
TCP_Event(tcb, TCPS_FINACKED);
break;
case CLOSING:
/* if FIN is acked... */
if ( GT32(ack, tcb->sndbufseq + tcb->sndlen) ) {
tcb->state = TIME_WAIT;
tcb->sndxmitwhat = TCP_IDLE; /* Ensure we're not probing */
result =
TCP_Timer_start( &tcb->sndxmittq.t, /* the timer */
tcb->timewaitto, /* the timeout */
0, /* no reload */
tcp_cfg->qhead ); /* timer q head */
if ( result != RTCS_OK ) {
RTCS_log_error( ERROR_TCP,
RTCSERR_TCP_TIMER_FAILURE,
(uint_32)result,
(uint_32)tcb,
2 );
} /* Endif */
} else {
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_BAD_ACK++);
TCP_PP_DROP;
} /* Endif */
break;
default:
break;
} /* End Switch */
break;
case LAST_ACK:
/*
** if FIN is acked...
*/
if ( ack == tcb->sndnxt ) { /* all data acked */
TCP_Event(tcb, TCPS_FINACKED);
TCP_Close_TCB(tcb, RTCS_OK, tcp_cfg);
TCP_Process_release(tcb, tcp_cfg);
TCP_PP_DROP;
} /* Endif */
break;
case TIME_WAIT:
break;
} /* End Switch */
step6: /* SIXTH */
switch ( tcb->state ) {
case ESTABLISHED:
case FINWAIT_1:
case FINWAIT_2:
/*
** Check URG
*/
if ( (flags & URG) != 0 ) {
if ( (tcb->status & TCPS_RURGENT) == 0 ||
GE32(seq + urg, tcb->rcvup)
) {
tcb->rcvup = seq + urg + 1;
TCP_Event(tcb, TCPS_RURGENT);
} /* Endif */
} /* Endif */
if ( ! seg_okay ) { /* Handled all URG/RST/ACK so drop it */
TCP_Must_send_ack(tcb, tcp_cfg);
TCP_PP_DROP;
} /* Endif */
TCP_Process_data(tcb, tcp_cfg, seg, pcb); /* SEVENTH */
break;
default:
break;
} /* End Switch */
if ( ! seg_okay ) { /* Handled all URG/RST/ACK so drop it */
TCP_Must_send_ack(tcb, tcp_cfg);
TCP_PP_DROP;
} /* Endif */
switch ( tcb->state ) {
/*
** do not process FIN for these states:
*/
case SYN_SENT:
TCP_PP_DROP;
default:
break;
} /* End Switch */
if ( (flags & FIN) != 0 ) { /* EIGHTH */
/*
** Record receipt of FIN, if this is the first time we get it
*/
if ( (tcb->status & TCPS_FINRECV) == 0 ) {
tcb->rcvnxt++;
} /* Endif */
/*
** Signal 'connection closing' (receiving side)
*/
TCP_Event(tcb,TCPS_FINRECV);
/*
** FIN implies PUSHing undelivered received data to user,
** So terminate any Receive requests
*/
while ( tcb->rcvHead != NULL ) {
TCP_Reply_receive(tcb, tcp_cfg, RTCSERR_TCP_CONN_CLOSING);
/*
** Setup next receive
*/
while ( tcb->rcvHead != NULL &&
! TCP_Setup_receive(tcb->rcvHead, tcb, tcp_cfg)
) {
} /* Endwhile */
} /* Endwhile */
/*
** Notify socket layer in case some task is blocked on select()
** waiting for data on this socket.
*/
SOCK_select_signal(tcb->SOCKET, tcb->state);
tcb->rcvwndedge = tcb->rcvnxt; /* zero receive window */
TCP_Must_send_ack(tcb, tcp_cfg); /* ACK the FIN */
if (tcb->keepaliveto) {
tcb->keepaliveto = 0;
}
switch ( tcb->state ) {
case SYN_RECEIVED:
case ESTABLISHED:
tcb->state = CLOSE_WAIT;
break;
case FINWAIT_1:
/*
** if FIN is ACKed
*/
if ( GT32(ack, tcb->sndbufseq + tcb->sndlen) ) {
/*FALLTHROUGH*/
} else {
tcb->state = CLOSING;
TCP_PP_DROP;
} /* Endif */
/*FALLTHROUGH*/
case FINWAIT_2:
tcb->state = TIME_WAIT;
tcb->sndxmitwhat = TCP_IDLE; /* Ensure we're not probing */
result = TCP_Timer_start( &tcb->sndxmittq.t, /* the timer */
tcb->timewaitto, /* the timeout */
0, /* no reload */
tcp_cfg->qhead ); /* queue head */
if ( result != RTCS_OK ) {
RTCS_log_error( ERROR_TCP, RTCSERR_TCP_TIMER_FAILURE,
(uint_32)result, (uint_32)tcb, 3 );
} /* Endif */
TCP_PP_DROP;
default:
break;
} /* End Switch */
} /* Endif */
RTCSLOG_PCB_FREE(pcb, RTCS_OK);
RTCSPCB_free(pcb);
RTCSLOG_FNX3(RTCSLOG_FN_TCP_Process_packet, tcb, 0);
return;
} /* End Switch */
RTCSLOG_FNX3(RTCSLOG_FN_TCP_Process_packet, tcb, 1);
return;
tcp_pp_drop:
RTCSLOG_PCB_FREE(pcb, RTCS_OK);
RTCSPCB_free(pcb);
RTCSLOG_FNX3(RTCSLOG_FN_TCP_Process_packet, tcb, 2);
return;
} /* Endbody */
#endif //RTCSCFG_ENABLE_IP4
/*FUNCTION*-------------------------------------------------------------
*
* Function Name : TCP6_Service_packet
* Returned Values : int_32
* Comments :
*
* Process incoming TCP6 packets, possibly forward them
* to the TCP6 server. This is called from IP6.
*
* Checks:
* - for multicast packets (discards)
* - for invalid source address
* - that a valid TCB exists for this packet
* - for bad checksums
* - for TCB in CLOSED state
*
*END*-----------------------------------------------------------------*/
#if RTCSCFG_ENABLE_IP6
void TCP6_Service_packet
(
RTCSPCB_PTR pcb, /* IN/OUT - the receive packet */
void _PTR_ cfg /* IN/OUT - TCP layer constants */
)
{ /* Body */
TCP_HEADER _PTR_ seg;
int_32 chksum;
uint_16 seglen;
uint_16 flags;
uint_16 dataofs;
TCB_STRUCT_PTR tcb;
TCP_CFG_STRUCT_PTR tcp_cfg; /* TCP layer constants */
/*IPv6 part************/
in6_addr ipv6source;
in6_addr ipv6dest;
/**/
uchar in_family=0;
/**/
RTCSLOG_FNE3(RTCSLOG_FN_TCP_Service_packet, pcb, cfg);
tcp_cfg = (TCP_CFG_STRUCT_PTR)cfg;
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.COMMON.ST_RX_TOTAL++);
seg = (TCP_HEADER _PTR_)RTCSPCB_DATA(pcb);
RTCSPCB_DATA_TRANSPORT(pcb) = RTCSPCB_DATA(pcb);
/* Extracting family type from packet*/
in_family = ntohc((pcb)->NETWORK_LAYER);
in_family = (in_family&0xF0)>>4;
/* using IPv6 copy macros to copy the IPv6 address structure */
IN6_ADDR_COPY((in6_addr *)((IP6_HEADER_PTR)RTCSPCB_DATA_NETWORK(pcb))->SOURCE, &ipv6source);
IN6_ADDR_COPY((in6_addr *)((IP6_HEADER_PTR)RTCSPCB_DATA_NETWORK(pcb))->DEST, &ipv6dest);
if (IN6_IS_ADDR_MULTICAST(&ipv6dest))
{ /* also check for invld dest addr */
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_MULTICASTS++);
RTCSLOG_PCB_FREE(pcb, RTCSERR_TCP_BAD_HEADER);
TCP_DROP;
} /* Endif */
if (IN6_ARE_ADDR_EQUAL(&ipv6source, &in6addr_any))
{
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_BAD_SOURCE++);
RTCSLOG_PCB_FREE(pcb, RTCSERR_TCP_BAD_HEADER);
TCP_DROP;
} /* Endif */
flags = ntohs(seg->flags);
/*
** Find a receiving TCB;
** Assuming this is much quicker than computing the checksum, do this
** first; this way we might not have to compute checksum for packets
** we don't want (but may have to reply a RST anyway)...
*/
{ /* Scope */
register uint_16 rport, dport, sport;
in6_addr ipv6_rhost;
in6_addr ipv6_lhost;
TCB_STRUCT_PTR tcbany = NULL;
TCB_STRUCT_PTR tcbspec = NULL;
dport = ntohs(seg->dest_port);
sport = ntohs(seg->source_port);
for ( tcb = tcp_cfg->TCBhead; tcb != NULL; tcb = tcb->next ) {
rport = tcb->remote_port;
/* if we inside TCP6_servece_packet function, we need set family type to AF_INET6 */
/* it will be used for transmit answer */
IN6_ADDR_COPY(&tcb->remote_ipv6_host, &ipv6_rhost);
IN6_ADDR_COPY(&tcb->local_ipv6_host, &ipv6_lhost);
if ( tcb->local_port != dport ||
(rport != 0 && rport != sport) ||
(
(!IN6_ARE_ADDR_EQUAL(&ipv6_rhost, &in6addr_any)) &&
(!IN6_ARE_ADDR_EQUAL(&ipv6_rhost, &ipv6source))
) ||
(
(!IN6_ARE_ADDR_EQUAL(&ipv6_lhost, &in6addr_any)) &&
(!IN6_ARE_ADDR_EQUAL(&ipv6_lhost, &ipv6dest))
) ||
tcb->state == BOUND
)
{
continue;
} /* Endif */
/*
** Found candidate TCB...
*/
if(rport != 0 && (!IN6_ARE_ADDR_EQUAL(&ipv6_rhost, &in6addr_any)))
{ /* is conn'n all specified? */
if(tcb->state == TIME_WAIT)
{
tcbspec = NULL;
tcbany = NULL;
TCP_STATE_DEBUG(("setting RST flag in %p\n", tcb));
flags |= RST;
htons(seg->flags, flags);
break;
}
else
{
tcbspec = tcb;
continue;
}
} /* Endif */
/*
** Connection not completely specified in TCB, there might be a TCB
** which is more specific;
**
** Give priority to TCB's which have specified one of rport
** or rhost, over a TCB which specified neither:
*/
if(
(
rport != 0 ||
(!IN6_ARE_ADDR_EQUAL(&ipv6_rhost, &in6addr_any)) ||
tcbany == NULL ||
(tcb->af == AF_INET6)
) &&
(
(tcb->if_scope_id ==0)||
(tcb->if_scope_id == ip6_if_get_scope_id(pcb->IFSRC))
)
)
{
tcbany = tcb;
} /* Endif */
} /* Endfor */
tcb = tcbspec;
if ( tcb == NULL ) {
tcb = tcbany;
} /* Endif */
} /* Endscope */
RTCSLOG_PCB_READ(pcb, RTCS_LOGCTRL_PROTO(IPPROTO_TCP), 0);
if ( tcb == NULL ) {
/*tcp_cfg->STATS.ST_NOCLIENTS++; */
if ( flags & RST ) {
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.COMMON.ST_TX_DISCARDED++);
RTCSLOG_PCB_FREE(pcb, RTCS_OK);
TCP_DROP;
} /* Endif */
/*
** otherwise must reply with a RST (below)...
*/
} /* Endif */
/*
** Check the checksum unless we're directed to bypass it.
*/
if(!tcb || !tcb->bypass_rx_chksum)
{
/*IP6 use different method for calculation checksum*/
chksum = IP6_Sum_pseudo(RTCSPCB_SIZE(pcb), pcb);
chksum = IP_Sum_PCB(chksum, pcb);
if (chksum != 0xFFFF) {
/*
** bad chksum, discarding packet
*/
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_BAD_CHECKSUM++);
RTCSLOG_PCB_FREE(pcb, RTCSERR_TCP_BAD_CHECKSUM);
TCP_DROP;
} /* Endif */
} /* Endif */
/*
** skip header
*/
dataofs = (flags & DATAOFS_MASK) >> (DATAOFS_SHIFT - 2);
/*
** Also check for CLOSED state, because we mustn't queue a packet
** on a closed connection (it may be lost).
*/
if (tcb == NULL || tcb->state == CLOSED || tcb->state == TIME_WAIT) {
if ( flags & RST ) {
TCP_STATE_DEBUG(("state = CLOSED || TIME_WAIT, heard reset on tcb %p, dropping.\n", tcb));
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_RESET++);
RTCSLOG_PCB_FREE(pcb, RTCS_OK);
TCP_DROP;
} /* Endif */
seglen = RTCSPCB_SIZE(pcb) - dataofs;
if ( flags & SYN ) {
seglen++;
} /* Endif */
if ( flags & FIN ) {
seglen++;
} /* Endif */
TCP6_Send_reply_reset(tcb,
&ipv6source, /* source address */
seg, /* received packet */
seglen, /* length of received packet */
tcp_cfg, /* TCP layer data */
tcb ? tcb->bypass_tx_chksum : FALSE
);
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_LATE_DATA++);
RTCSLOG_PCB_FREE(pcb, RTCS_OK);
TCP_DROP;
} /* Endif */
/*
** Packet may now be processed, so send upwards.
*/
TCP6_Process_packet(tcb, pcb, tcp_cfg); /* (frees the PCB) */
RTCSLOG_FNX2(RTCSLOG_FN_TCP_Service_packet, 0);
return;
tcp_sp_drop:
RTCSPCB_free(pcb);
RTCSLOG_FNX2(RTCSLOG_FN_TCP_Service_packet, 1);
return;
} /* Endbody */
#endif //RTCSCFG_ENABLE_IP6
/*FUNCTION*-------------------------------------------------------------
*
* Function Name : TCP_Service_packet
* Returned Values : int_32
* Comments :
*
* Process incoming TCP packets, possibly forward them
* to the TCP server. This is called from IP.
*
* Checks:
* - for multicast packets (discards)
* - for invalid source address
* - that a valid TCB exists for this packet
* - for bad checksums
* - for TCB in CLOSED state
*
*END*-----------------------------------------------------------------*/
#if RTCSCFG_ENABLE_IP4
void TCP4_Service_packet
(
RTCSPCB_PTR pcb, /* IN/OUT - the receive packet */
void _PTR_ cfg /* IN/OUT - TCP layer constants */
)
{ /* Body */
_ip_address source,
dest;
TCP_HEADER _PTR_ seg;
int_32 chksum;
uint_16 seglen;
uint_16 flags;
uint_16 dataofs;
TCB_STRUCT_PTR tcb;
TCP_CFG_STRUCT_PTR tcp_cfg; /* TCP layer constants */
RTCSLOG_FNE3(RTCSLOG_FN_TCP_Service_packet, pcb, cfg);
tcp_cfg = (TCP_CFG_STRUCT_PTR)cfg;
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.COMMON.ST_RX_TOTAL++);
seg = (TCP_HEADER _PTR_)RTCSPCB_DATA(pcb);
RTCSPCB_DATA_TRANSPORT(pcb) = RTCSPCB_DATA(pcb);
source = IP_source(pcb);
dest = IP_dest(pcb);
if (IN_MULTICAST(dest)) { /* also check for invld dest addr */
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_MULTICASTS++);
RTCSLOG_PCB_FREE(pcb, RTCSERR_TCP_BAD_HEADER);
TCP_DROP;
} /* Endif */
if (source == INADDR_ANY) {
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_BAD_SOURCE++);
RTCSLOG_PCB_FREE(pcb, RTCSERR_TCP_BAD_HEADER);
TCP_DROP;
} /* Endif */
flags = ntohs(seg->flags);
/*
** Find a receiving TCB;
** Assuming this is much quicker than computing the checksum, do this
** first; this way we might not have to compute checksum for packets
** we don't want (but may have to reply a RST anyway)...
*/
{ /* Scope */
register uint_16 rport, dport, sport;
register _ip_address rhost, lhost;
TCB_STRUCT_PTR tcbany = NULL;
TCB_STRUCT_PTR tcbspec = NULL;
dport = ntohs(seg->dest_port);
sport = ntohs(seg->source_port);
for ( tcb = tcp_cfg->TCBhead; tcb != NULL; tcb = tcb->next ) {
rport = tcb->remote_port;
rhost = tcb->remote_host;
lhost = tcb->local_host;
if ( tcb->local_port != dport ||
(rport != 0 && rport != sport) ||
(rhost != INADDR_ANY && rhost != source) ||
(lhost != INADDR_ANY && lhost != dest) ||
tcb->state == BOUND)
{
continue;
} /* Endif */
/*
** Found candidate TCB...
*/
if ( rport != 0 && rhost != INADDR_ANY ) { /* is conn'n all specified? */
if (tcb->state == TIME_WAIT) {
tcbspec = NULL;
tcbany = NULL;
TCP_STATE_DEBUG(("setting RST flag in %p\n", tcb));
flags |= RST;
htons(seg->flags, flags);
break;
} else {
tcbspec = tcb;
continue;
}
} /* Endif */
/*
** Connection not completely specified in TCB, there might be a TCB
** which is more specific;
**
** Give priority to TCB's which have specified one of rport
** or rhost, over a TCB which specified neither:
*/
if ( rport != 0 || rhost != INADDR_ANY || tcbany == NULL || (tcb->af == AF_INET) ) {
tcbany = tcb;
} /* Endif */
} /* Endfor */
tcb = tcbspec;
if ( tcb == NULL )
{
tcb = tcbany;
} /* Endif */
} /* Endscope */
RTCSLOG_PCB_READ(pcb, RTCS_LOGCTRL_PROTO(IPPROTO_TCP), 0);
if ( tcb == NULL ) {
/*tcp_cfg->STATS.ST_NOCLIENTS++; */
if ( flags & RST ) {
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.COMMON.ST_TX_DISCARDED++);
RTCSLOG_PCB_FREE(pcb, RTCS_OK);
TCP_DROP;
} /* Endif */
/*
** otherwise must reply with a RST (below)...
*/
} /* Endif */
/*
** Check the checksum unless we're directed to bypass it.
*/
if (!tcb || !tcb->bypass_rx_chksum) {
chksum = IP_Sum_pseudo(RTCSPCB_SIZE(pcb), pcb, -1);
chksum = IP_Sum_PCB(chksum, pcb);
if (chksum != 0xFFFF) {
/*
** bad chksum, discarding packet
*/
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_BAD_CHECKSUM++);
RTCSLOG_PCB_FREE(pcb, RTCSERR_TCP_BAD_CHECKSUM);
TCP_DROP;
} /* Endif */
} /* Endif */
/*
** skip header
*/
dataofs = (flags & DATAOFS_MASK) >> (DATAOFS_SHIFT - 2);
/*
** Also check for CLOSED state, because we mustn't queue a packet
** on a closed connection (it may be lost).
*/
if (tcb == NULL || tcb->state == CLOSED || tcb->state == TIME_WAIT) {
if ( flags & RST ) {
TCP_STATE_DEBUG(("state = CLOSED || TIME_WAIT, heard reset on tcb %p, dropping.\n", tcb));
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_RESET++);
RTCSLOG_PCB_FREE(pcb, RTCS_OK);
TCP_DROP;
} /* Endif */
seglen = RTCSPCB_SIZE(pcb) - dataofs;
if ( flags & SYN ) {
seglen++;
} /* Endif */
if ( flags & FIN ) {
seglen++;
} /* Endif */
TCP_Send_reply_reset(
tcb,
source, /* source address */
seg, /* received packet */
seglen, /* length of received packet */
tcp_cfg, /* TCP layer data */
tcb ? tcb->bypass_tx_chksum : FALSE
);
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_LATE_DATA++);
RTCSLOG_PCB_FREE(pcb, RTCS_OK);
TCP_DROP;
} /* Endif */
/*
** Packet may now be processed, so send upwards.
*/
TCP_Process_packet(tcb, pcb, tcp_cfg); /* (frees the PCB) */
RTCSLOG_FNX2(RTCSLOG_FN_TCP_Service_packet, 0);
return;
tcp_sp_drop:
RTCSPCB_free(pcb);
RTCSLOG_FNX2(RTCSLOG_FN_TCP_Service_packet, 1);
return;
} /* Endbody */
#endif //RTCSCFG_ENABLE_IP4
/*FUNCTION*-------------------------------------------------------------
*
* Function Name : TCP6_Service_packet
* Returned Values : int_32
* Comments :
*
* Process incoming TCP6 or TCP4 packets, possibly forward them
* to the TCP server. This function support IPv6 and IPv4.
*
* Checks:
* - for multicast packets (discards)
* - for invalid source address
* - that a valid TCB exists for this packet
* - for bad checksums
* - for TCB in CLOSED state
*
*END*-----------------------------------------------------------------*/
void TCP_Service_packet
(
RTCSPCB_PTR pcb, /* IN/OUT - the receive packet */
void _PTR_ cfg /* IN/OUT - TCP layer constants */
)
{ /* Body */
TCP_HEADER _PTR_ seg;
int_32 chksum;
uint_16 seglen;
uint_16 flags;
uint_16 dataofs;
TCB_STRUCT_PTR tcb;
TCP_CFG_STRUCT_PTR tcp_cfg; /* TCP layer constants */
/* IPv4 part */
#if RTCSCFG_ENABLE_IP4
_ip_address ipv4source;
_ip_address ipv4dest;
#endif
/* IPv6 part */
#if RTCSCFG_ENABLE_IP6
in6_addr ipv6source;
in6_addr ipv6dest;
#endif
/*-/\-*/
uchar in_family = 0;
uint_32 error = 0;
/*-\/-*/
RTCSLOG_FNE3(RTCSLOG_FN_TCP_Service_packet, pcb, cfg);
tcp_cfg = (TCP_CFG_STRUCT_PTR)cfg;
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.COMMON.ST_RX_TOTAL++);
seg = (TCP_HEADER _PTR_)RTCSPCB_DATA(pcb);
RTCSPCB_DATA_TRANSPORT(pcb) = RTCSPCB_DATA(pcb);
/* Extracting family type from packet*/
in_family = ntohc((pcb)->NETWORK_LAYER);
in_family = (in_family&0xF0)>>4;
/* Checking family type*/
error = 1;
#if RTCSCFG_ENABLE_IP4
if (in_family == IPV4)
{
error = 0;
}
#endif
#if RTCSCFG_ENABLE_IP6
if (in_family == IPV6)
{
error = 0;
}
#endif
if (error)
{ /* If inet protocol is not IPv4 or IPv6 go out with error. */
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_BAD_SOURCE++);
RTCSLOG_PCB_FREE(pcb, RTCSERR_TCP_BAD_HEADER);
TCP_DROP;
}
/* copy the IP address from packet */
#if RTCSCFG_ENABLE_IP4
if(in_family == IPV4)
{
ipv4source = IP_source(pcb);
ipv4dest = IP_dest(pcb);
if (IN_MULTICAST(ipv4dest))
{ /* also check for invld dest addr */
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_MULTICASTS++);
RTCSLOG_PCB_FREE(pcb, RTCSERR_TCP_BAD_HEADER);
TCP_DROP;
} /* Endif */
if (ipv4source == INADDR_ANY)
{
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_BAD_SOURCE++);
RTCSLOG_PCB_FREE(pcb, RTCSERR_TCP_BAD_HEADER);
TCP_DROP;
} /* Endif */
}
#endif
#if RTCSCFG_ENABLE_IP6
if(in_family == IPV6)
{
IN6_ADDR_COPY((in6_addr *)((IP6_HEADER_PTR)RTCSPCB_DATA_NETWORK(pcb))->SOURCE, &ipv6source);
IN6_ADDR_COPY((in6_addr *)((IP6_HEADER_PTR)RTCSPCB_DATA_NETWORK(pcb))->DEST, &ipv6dest);
if (IN6_IS_ADDR_MULTICAST(&ipv6dest))
{ /* also check for invld dest addr */
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_MULTICASTS++);
RTCSLOG_PCB_FREE(pcb, RTCSERR_TCP_BAD_HEADER);
TCP_DROP;
} /* Endif */
if (IN6_ARE_ADDR_EQUAL(&ipv6source, &in6addr_any))
{
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_BAD_SOURCE++);
RTCSLOG_PCB_FREE(pcb, RTCSERR_TCP_BAD_HEADER);
TCP_DROP;
} /* Endif */
}
#endif
flags = ntohs(seg->flags);
/*
** Find a receiving TCB;
** Assuming this is much quicker than computing the checksum, do this
** first; this way we might not have to compute checksum for packets
** we don't want (but may have to reply a RST anyway)...
*/
{ /* Scope */
register uint_16 rport, dport, sport;
#if RTCSCFG_ENABLE_IP4
register _ip_address ipv4_rhost;
register _ip_address ipv4_lhost;
#endif
#if RTCSCFG_ENABLE_IP6
in6_addr ipv6_rhost;
in6_addr ipv6_lhost;
#endif
TCB_STRUCT_PTR tcbany = NULL;
TCB_STRUCT_PTR tcbspec = NULL;
dport = ntohs(seg->dest_port);
sport = ntohs(seg->source_port);
#if RTCSCFG_ENABLE_IP4
if(in_family == IPV4)
{
for ( tcb = tcp_cfg->TCBhead; tcb != NULL; tcb = tcb->next )
{
/* We need check SOCKET family.If SOCKET was opened for another family,try another socket */
if((tcb->SOCKET))
{
if(((SOCKET_STRUCT_PTR)(tcb->SOCKET))->AF != AF_INET)
{
continue;
}
}
rport = tcb->remote_port;
/* we need setup family type to AF_INET */
/* it will be used for transmit answer */
tcb->af = AF_INET;
ipv4_rhost = tcb->remote_host;
ipv4_lhost = tcb->local_host;
if ( tcb->local_port != dport ||
(rport != 0 && rport != sport) ||
(
ipv4_rhost != INADDR_ANY &&
ipv4_rhost != ipv4source
) ||
(
ipv4_lhost != INADDR_ANY &&
ipv4_lhost != ipv4dest
) ||
tcb->state == BOUND
)
{
continue;
} /* Endif */
/*
** Found candidate TCB...
*/
if ( rport != 0 && ipv4_rhost != INADDR_ANY )
{ /* is conn'n all specified? */
if(tcb->state == TIME_WAIT)
{
tcbspec = NULL;
tcbany = NULL;
TCP_STATE_DEBUG(("setting RST flag in %p\n", tcb));
flags |= RST;
htons(seg->flags, flags);
break;
}
else
{
tcbspec = tcb;
continue;
}
} /* Endif */
/*
** Connection not completely specified in TCB, there might be a TCB
** which is more specific;
**
** Give priority to TCB's which have specified one of rport
** or rhost, over a TCB which specified neither:
*/
if( rport != 0 || ipv4_rhost != INADDR_ANY || tcbany == NULL || (tcb->af == AF_INET))
{
tcbany = tcb;
} /* Endif */
} /* Endfor */
}/*End if IPV4*/
#endif
#if RTCSCFG_ENABLE_IP6
if(in_family == IPV6)
{
for ( tcb = tcp_cfg->TCBhead; tcb != NULL; tcb = tcb->next )
{
/* We need check SOCKET family.If SOCKET was opened for another family, try another socket. */
if((tcb->SOCKET))
{
if(((SOCKET_STRUCT_PTR)(tcb->SOCKET))->AF != AF_INET6)
{
continue;
}
}
rport = tcb->remote_port;
/* if we inside TCP6_servece_packet function, we need set family type to AF_INET6 */
/* it will be used for transmit answer */
tcb->af = AF_INET6;
IN6_ADDR_COPY(&tcb->remote_ipv6_host, &ipv6_rhost);
IN6_ADDR_COPY(&tcb->local_ipv6_host, &ipv6_lhost);
if ( tcb->local_port != dport ||
(rport != 0 && rport != sport) ||
(
(!IN6_ARE_ADDR_EQUAL(&ipv6_rhost, &in6addr_any)) &&
(!IN6_ARE_ADDR_EQUAL(&ipv6_rhost, &ipv6source))
) ||
(
(!IN6_ARE_ADDR_EQUAL(&ipv6_lhost, &in6addr_any)) &&
(!IN6_ARE_ADDR_EQUAL(&ipv6_lhost, &ipv6dest))
) ||
tcb->state == BOUND
)
{
continue;
} /* Endif */
/*
** Found candidate TCB...
*/
if(rport != 0 && (!IN6_ARE_ADDR_EQUAL(&ipv6_rhost, &in6addr_any)))
{ /* is conn'n all specified? */
if(tcb->state == TIME_WAIT)
{
tcbspec = NULL;
tcbany = NULL;
TCP_STATE_DEBUG(("setting RST flag in %p\n", tcb));
flags |= RST;
htons(seg->flags, flags);
break;
}
else
{
tcbspec = tcb;
continue;
}
} /* Endif */
/*
** Connection not completely specified in TCB, there might be a TCB
** which is more specific;
**
** Give priority to TCB's which have specified one of rport
** or rhost, over a TCB which specified neither:
*/
if( rport != 0 || (!IN6_ARE_ADDR_EQUAL(&ipv6_rhost, &in6addr_any)) || tcbany == NULL || (tcb->af == AF_INET6))
{
tcbany = tcb;
} /* Endif */
} /* Endfor */
}/*End if IPV6*/
#endif
tcb = tcbspec;
if ( tcb == NULL )
{
tcb = tcbany;
} /* Endif */
} /* Endscope */
RTCSLOG_PCB_READ(pcb, RTCS_LOGCTRL_PROTO(IPPROTO_TCP), 0);
if ( tcb == NULL )
{
/*tcp_cfg->STATS.ST_NOCLIENTS++; */
if ( flags & RST )
{
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.COMMON.ST_TX_DISCARDED++);
RTCSLOG_PCB_FREE(pcb, RTCS_OK);
TCP_DROP;
}
/*
** otherwise must reply with a RST (below)...
*/
}
/*
** Check the checksum unless we're directed to bypass it.
*/
if(!tcb || !tcb->bypass_rx_chksum)
{
#if RTCSCFG_ENABLE_IP4
if(in_family == IPV4)
{
chksum = IP_Sum_pseudo(RTCSPCB_SIZE(pcb), pcb, -1);
}
#endif
#if RTCSCFG_ENABLE_IP6
if(in_family == IPV6)
{
/*IP6 use different method for calculation checksum*/
chksum = IP6_Sum_pseudo(RTCSPCB_SIZE(pcb), pcb);
}
#endif
chksum = IP_Sum_PCB(chksum, pcb);
if (chksum != 0xFFFF)
{
/*
** bad chksum, discarding packet
*/
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_BAD_CHECKSUM++);
RTCSLOG_PCB_FREE(pcb, RTCSERR_TCP_BAD_CHECKSUM);
TCP_DROP;
} /* Endif */
} /* Endif */
/*
** skip header
*/
dataofs = (flags & DATAOFS_MASK) >> (DATAOFS_SHIFT - 2);
/*
** Also check for CLOSED state, because we mustn't queue a packet
** on a closed connection (it may be lost).
*/
if (tcb == NULL || tcb->state == CLOSED || tcb->state == TIME_WAIT)
{
if ( flags & RST )
{
TCP_STATE_DEBUG(("state = CLOSED || TIME_WAIT, heard reset on tcb %p, dropping.\n", tcb));
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_RESET++);
RTCSLOG_PCB_FREE(pcb, RTCS_OK);
TCP_DROP;
} /* Endif */
seglen = RTCSPCB_SIZE(pcb) - dataofs;
if( flags & SYN )
{
seglen++;
} /* Endif */
if( flags & FIN )
{
seglen++;
} /* Endif */
#if RTCSCFG_ENABLE_IP4
if(in_family == IPV4)
{
TCP_Send_reply_reset ( tcb,
ipv4source, /* source address */
seg, /* received packet */
seglen, /* length of received packet */
tcp_cfg, /* TCP layer data */
tcb ? tcb->bypass_tx_chksum : FALSE
);
}
#endif
#if RTCSCFG_ENABLE_IP6
if(in_family == IPV6)
{
TCP6_Send_reply_reset ( tcb,
&ipv6source, /* source address */
seg, /* received packet */
seglen, /* length of received packet */
tcp_cfg, /* TCP layer data */
tcb ? tcb->bypass_tx_chksum : FALSE
);
}
#endif
IF_TCP_STATS_ENABLED(tcp_cfg->STATS.ST_RX_LATE_DATA++);
RTCSLOG_PCB_FREE(pcb, RTCS_OK);
TCP_DROP;
} /* Endif */
/*
** Packet may now be processed, so send upwards.
*/
#if RTCSCFG_ENABLE_IP4
if(in_family == IPV4)
{
TCP_Process_packet(tcb, pcb, tcp_cfg); /* (frees the PCB) */
}
#endif
#if RTCSCFG_ENABLE_IP6
if(in_family == IPV6)
{
TCP6_Process_packet(tcb, pcb, tcp_cfg); /* (frees the PCB) */
}
#endif
RTCSLOG_FNX2(RTCSLOG_FN_TCP_Service_packet, 0);
return;
tcp_sp_drop:
RTCSPCB_free(pcb);
RTCSLOG_FNX2(RTCSLOG_FN_TCP_Service_packet, 1);
return;
} /* Endbody */
/* EOF */