summaryrefslogtreecommitdiffstats
path: root/cfe/cfe/net/net_tcp.c
diff options
context:
space:
mode:
Diffstat (limited to 'cfe/cfe/net/net_tcp.c')
-rwxr-xr-xcfe/cfe/net/net_tcp.c2215
1 files changed, 2215 insertions, 0 deletions
diff --git a/cfe/cfe/net/net_tcp.c b/cfe/cfe/net/net_tcp.c
new file mode 100755
index 0000000..377c3c8
--- /dev/null
+++ b/cfe/cfe/net/net_tcp.c
@@ -0,0 +1,2215 @@
+/* *********************************************************************
+ * Broadcom Common Firmware Environment (CFE)
+ *
+ * TCP Protocol File: net_tcp.c
+ *
+ * This file contains a very simple TCP. The basic goals of this
+ * tcp are to be "good enough for firmware." We try to be
+ * correct in our protocol implementation, but not very fancy.
+ * In particular, we don't deal with out-of-order segments,
+ * we don't hesitate to copy data more then necessary, etc.
+ * We strive to implement important protocol features
+ * like slow start, nagle, etc., but even these things are
+ * subsetted and simplified as much as possible.
+ *
+ * Current "todo" list:
+ * slow start
+ * good testing of retransmissions,
+ * round-trip delay calculations
+ * Process received TCP options, particularly segment size
+ * Ignore urgent data (remove from datastream)
+ *
+ * Author: Mitch Lichtenberg (mpl@broadcom.com)
+ *
+ *********************************************************************
+ *
+ * Copyright 2000,2001,2002,2003
+ * Broadcom Corporation. All rights reserved.
+ *
+ * This software is furnished under license and may be used and
+ * copied only in accordance with the following terms and
+ * conditions. Subject to these conditions, you may download,
+ * copy, install, use, modify and distribute modified or unmodified
+ * copies of this software in source and/or binary form. No title
+ * or ownership is transferred hereby.
+ *
+ * 1) Any source code used, modified or distributed must reproduce
+ * and retain this copyright notice and list of conditions
+ * as they appear in the source file.
+ *
+ * 2) No right is granted to use any trade name, trademark, or
+ * logo of Broadcom Corporation. The "Broadcom Corporation"
+ * name may not be used to endorse or promote products derived
+ * from this software without the prior written permission of
+ * Broadcom Corporation.
+ *
+ * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED
+ * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT
+ * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN
+ * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR 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), EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ ********************************************************************* */
+
+
+#include "lib_types.h"
+#include "lib_string.h"
+#include "lib_queue.h"
+#include "lib_malloc.h"
+#include "lib_printf.h"
+
+#include "net_ebuf.h"
+#include "net_ether.h"
+
+#include "net_ip.h"
+#include "net_ip_internal.h"
+
+#include "cfe_timer.h"
+
+#include "cfe_error.h"
+
+#include "net_tcpbuf.h"
+#include "net_tcp_internal.h"
+#include "net_tcp.h"
+
+/* *********************************************************************
+ * Config
+ ********************************************************************* */
+
+//#define _TCP_DEBUG_
+
+/* *********************************************************************
+ * Structures
+ ********************************************************************* */
+
+struct tcp_info_s {
+ void *ti_ref; /* ref data for IP layer */
+ ip_info_t *ti_ipinfo; /* IP layer handle */
+ cfe_timer_t ti_fasttimer; /* 200ms timer */
+ queue_t ti_tcblist; /* list of known TCBs */
+ int ti_onqueue; /* number of TCBs on queue */
+ uint32_t ti_iss; /* initial sequence number */
+ tcb_t *ti_ports[TCP_MAX_PORTS]; /* table of active sockets */
+};
+
+/* *********************************************************************
+ * Forward Declarations
+ ********************************************************************* */
+
+static void _tcp_protosend(tcp_info_t *ti,tcb_t *tcb);
+static int _tcp_rx_callback(void *ref,ebuf_t *buf,uint8_t *destaddr,uint8_t *srcaddr);
+static void _tcp_output(tcp_info_t *ti,tcb_t *tcb);
+static void _tcp_aborttcb(tcp_info_t *ti,tcb_t *tcb);
+static void _tcp_closetcb(tcp_info_t *ti,tcb_t *tcb);
+static tcb_t *_tcp_find_lclport(tcp_info_t *ti,uint16_t port);
+static void _tcp_freetcb(tcp_info_t *ti,tcb_t *tcb);
+static void _tcp_sendctlmsg(tcp_info_t *ti,tcb_t *tcb,uint16_t flags,int timeout);
+
+
+/* *********************************************************************
+ * Macros
+ ********************************************************************* */
+
+#ifdef _TCP_DEBUG_
+#define _tcp_setstate(tcb,state) (tcb)->tcb_state = (state); \
+ printf("tcp state = " #state "\n");
+#define DEBUGMSG(x) printf x
+#else
+#define _tcp_setstate(tcb,state) (tcb)->tcb_state = (state);
+#define DEBUGMSG(x)
+#endif
+
+#define _tcp_preparectlmsg(tcb,flags) (tcb)->tcb_txflags = (flags) ; \
+ (tcb)->tcb_flags |= TCB_FLG_SENDMSG;
+
+#define _tcp_canceltimers(tcb) \
+ TIMER_CLEAR((tcb)->tcb_timer_retx); \
+ TIMER_CLEAR((tcb)->tcb_timer_keep); \
+ TIMER_CLEAR((tcb)->tcb_timer_2msl); \
+ TIMER_CLEAR((tcb)->tcb_timer_pers);
+
+
+/* *********************************************************************
+ * Globals
+ ********************************************************************* */
+
+#ifdef _TCP_DEBUG_
+int _tcp_dumpflags = 1;
+#else
+int _tcp_dumpflags = 0;
+#endif
+
+/* *********************************************************************
+ * _tcp_init(ipi,ref)
+ *
+ * Initialize the TCP module. We set up our data structures
+ * and register ourselves with the IP layer.
+ *
+ * Input parameters:
+ * ipi - IP information
+ * ref - will be passed back to IP as needed
+ *
+ * Return value:
+ * tcp_info_t structure, or NULL if problems
+ ********************************************************************* */
+
+tcp_info_t *_tcp_init(ip_info_t *ipi,void *ref)
+{
+ tcp_info_t *ti;
+ int idx;
+
+ ti = (tcp_info_t *) KMALLOC(sizeof(tcp_info_t),0);
+ if (!ti) return NULL;
+
+ ti->ti_ref = ref;
+ ti->ti_ipinfo = ipi;
+
+ /*
+ * Start the "fast" timer
+ */
+
+ TIMER_SET(ti->ti_fasttimer,TCP_FAST_TIMER);
+
+ /*
+ * Initialize the TCB list
+ */
+
+ q_init(&(ti->ti_tcblist));
+
+ for (idx = 0; idx < TCP_MAX_PORTS; idx++) {
+ ti->ti_ports[idx] = NULL;
+ }
+
+ ti->ti_onqueue = 0;
+
+ /*
+ * Set up the initial sequence number
+ */
+
+ extern int32_t _getticks(void); /* return value of CP0 COUNT */
+ ti->ti_iss = (uint32_t) _getticks(); /* XXX nonportable */
+
+ /*
+ * Register our protocol with IP
+ */
+
+ _ip_register(ipi,IPPROTO_TCP,_tcp_rx_callback,ti);
+
+ return ti;
+}
+
+
+/* *********************************************************************
+ * _tcp_uninit(info)
+ *
+ * De-initialize the TCP layer, unregistering from the IP layer.
+ *
+ * Input parameters:
+ * info - our tcp_info_t, from _tcp_init()
+ *
+ * Return value:
+ * nothing
+ ********************************************************************* */
+
+void _tcp_uninit(tcp_info_t *info)
+{
+ tcb_t *tcb;
+
+ /*
+ * Destroy all allocated TCBs, forcefully.
+ */
+
+ while (!q_isempty(&(info->ti_tcblist))) {
+ tcb = (tcb_t *) q_getfirst(&(info->ti_tcblist));
+ /* tcp_freetcb removes tcb from the queue */
+ _tcp_freetcb(info,tcb);
+ }
+
+ /*
+ * Deregister with IP
+ */
+
+ _ip_deregister(info->ti_ipinfo,IPPROTO_TCP);
+
+ /*
+ * Free up the info structure
+ */
+
+ KFREE(info);
+}
+
+
+/* *********************************************************************
+ * _tcp_freetcb(ti,tcb)
+ *
+ * Called when the TIME_WAIT timer expires, we use this to
+ * free up the TCB for good.
+ *
+ * Input parameters:
+ * ti - tcp information
+ * tcb - tcb to free
+ *
+ * Return value:
+ * nothing
+ ********************************************************************* */
+
+static void _tcp_freetcb(tcp_info_t *ti,tcb_t *tcb)
+{
+ /*
+ * Undo socket number
+ */
+
+ if (tcb->tcb_socknum >= 0) ti->ti_ports[tcb->tcb_socknum] = NULL;
+ tcb->tcb_socknum = -1;
+
+ /*
+ * Remove from queue
+ */
+
+ ti->ti_onqueue--;
+ q_dequeue(&(tcb->tcb_qb));
+
+ /*
+ * Free buffers (could probably be done in tcb_destroy)
+ */
+
+ tmb_free(&(tcb->tcb_txbuf));
+ tmb_free(&(tcb->tcb_rxbuf));
+
+ /*
+ * Free the TCB
+ */
+
+ KFREE(tcb);
+
+}
+
+/* *********************************************************************
+ * _tcp_socket(info)
+ *
+ * Create a new tcp socket (a new tcb structure is allocated
+ * and entered into the socket table)
+ *
+ * Input parameters:
+ * info - tcp information
+ *
+ * Return value:
+ * new socket number, or <0 if an error occured
+ ********************************************************************* */
+
+int _tcp_socket(tcp_info_t *info)
+{
+ int idx;
+ tcb_t *tcb;
+
+ /*
+ * Find an empty slot.
+ */
+
+ for (idx = 0; idx < TCP_MAX_PORTS; idx++) {
+ if (!info->ti_ports[idx]) break;
+ }
+
+ if (idx == TCP_MAX_PORTS) {
+ return CFE_ERR_NOHANDLES;
+ }
+
+ /*
+ * See if we can create another TCB
+ */
+
+ if (info->ti_onqueue >= TCP_MAX_TCBS) return CFE_ERR_NOMEM;
+
+ /*
+ * Allocate data structures
+ */
+
+ tcb = KMALLOC(sizeof(tcb_t),0);
+ if (!tcb) return CFE_ERR_NOMEM;
+
+ memset(tcb,0,sizeof(tcb_t));
+
+ if (tmb_alloc(&tcb->tcb_txbuf,TCP_BUF_SIZE) < 0) goto error;
+ if (tmb_alloc(&tcb->tcb_rxbuf,TCP_BUF_SIZE) < 0) goto error;
+
+ /*
+ * XXX Temp: our MTU is always 1400. We could/should
+ * XXX get this from the lower layer.
+ */
+
+ tcb->tcb_mtu = 1400;
+
+ /*
+ * Default socket flags
+ */
+
+ tcb->tcb_sockflags = TCPFLG_NBIO;
+
+ /*
+ * Set up initial state. Find an empty port number.
+ * note that the way we do this is pretty gruesome, but it will
+ * work for our small TCP, where the number of TCBs outstanding
+ * will be very small compared to the port number space.
+ *
+ * Try to look up the port number we want - if we find it, increment
+ * it and try again until we find an unused one.
+ * Stay away from ports 0..1023.
+ */
+
+ _tcp_setstate(tcb,TCPSTATE_CLOSED);
+ tcb->tcb_lclport = (uint16_t) (((uint16_t) cfe_ticks) + 1024);
+
+ while (_tcp_find_lclport(info,tcb->tcb_lclport) != NULL) {
+ tcb->tcb_lclport++;
+ if (tcb->tcb_lclport == 0) tcb->tcb_lclport = 1024;
+ }
+
+ /*
+ * Remember this socket in the table
+ */
+
+ info->ti_ports[idx] = tcb;
+ tcb->tcb_socknum = idx;
+
+ info->ti_onqueue++;
+ q_enqueue(&(info->ti_tcblist),&(tcb->tcb_qb));
+
+ return idx;
+
+error:
+ tmb_free(&(tcb->tcb_txbuf));
+ tmb_free(&(tcb->tcb_rxbuf));
+ KFREE(tcb);
+ return CFE_ERR_NOMEM;
+}
+
+
+/* *********************************************************************
+ * _tcp_connect(ti,s,dest,port)
+ *
+ * Connect a socket to a remote port.
+ *
+ * Input parameters:
+ * ti - TCP information
+ * s - socket number, allocated via _tcp_create
+ * dest - destination IP address
+ * port - destination port number
+ *
+ * Return value:
+ * 0 if ok
+ * else error code
+ ********************************************************************* */
+
+int _tcp_connect(tcp_info_t *ti,int s,uint8_t *dest,uint16_t port)
+{
+ tcb_t *tcb;
+
+ tcb = ti->ti_ports[s];
+ if (!tcb) return CFE_ERR_INV_PARAM;
+
+ memcpy(tcb->tcb_peeraddr,dest,IP_ADDR_LEN);
+ tcb->tcb_peerport = port;
+
+ tcb->tcb_rcvnext = 0;
+ tcb->tcb_rcvack = 0;
+
+ tcb->tcb_sendnext = ti->ti_iss;
+ tcb->tcb_sendunack = ti->ti_iss;
+ tcb->tcb_sendwindow = 0;
+
+ tmb_init(&tcb->tcb_txbuf);
+ tmb_init(&tcb->tcb_rxbuf);
+
+ TIMER_SET(tcb->tcb_timer_keep,TCP_KEEPALIVE_TIMER);
+ _tcp_setstate(tcb,TCPSTATE_SYN_SENT);
+
+ _tcp_sendctlmsg(ti,tcb,TCPFLG_SYN,TCP_RETX_TIMER);
+
+ return 0;
+}
+
+
+/* *********************************************************************
+ * _tcp_close(ti,s)
+ *
+ * Disconnect a TCP socket nicely. Sends a FIN packet to get
+ * us into the disconnect state.
+ *
+ * Input parameters:
+ * ti - tcp information
+ * s - socket number
+ *
+ * Return value:
+ * 0 if ok
+ * else error code
+ ********************************************************************* */
+
+int _tcp_close(tcp_info_t *ti,int s)
+{
+ tcb_t *tcb;
+
+ tcb = ti->ti_ports[s];
+ if (!tcb) return CFE_ERR_INV_PARAM;
+
+#if defined(CONFIG_MIPS_BRCM)
+ /*
+ * Flush output data.
+ */
+
+ _tcp_output(ti,tcb);
+#endif
+
+ /*
+ * Reclaim this socket number for future use
+ */
+
+ ti->ti_ports[s] = NULL;
+ tcb->tcb_socknum = -1;
+
+ /*
+ * Decide what action to take based on current state
+ */
+
+ switch (tcb->tcb_state) {
+ case TCPSTATE_SYN_RECEIVED:
+ case TCPSTATE_ESTABLISHED:
+ /*
+ * Transmit the FIN/ACK and wait for a FIN/ACK.
+ *
+ * XXX probably want to wait till all unacked data is
+ * acked before sending the fin?? This would be sort
+ * of like the "linger" option
+ */
+ _tcp_setstate(tcb,TCPSTATE_FINWAIT_1);
+ _tcp_sendctlmsg(ti,tcb,TCPFLG_ACK | TCPFLG_FIN,TCP_RETX_TIMER);
+ break;
+
+ case TCPSTATE_CLOSED:
+ case TCPSTATE_LISTEN:
+ case TCPSTATE_SYN_SENT:
+ /*
+ * Disconnect during our attempt, or from some
+ * idle state that does not require sending anything.
+ * Go back to CLOSED.
+ */
+ _tcp_closetcb(ti,tcb);
+ _tcp_freetcb(ti,tcb);
+ break;
+
+ case TCPSTATE_CLOSE_WAIT:
+ _tcp_setstate(tcb,TCPSTATE_LAST_ACK);
+ _tcp_sendctlmsg(ti,tcb,TCPFLG_ACK | TCPFLG_FIN,TCP_RETX_TIMER);
+ break;
+
+ case TCPSTATE_TIME_WAIT:
+ case TCPSTATE_FINWAIT_1:
+ case TCPSTATE_FINWAIT_2:
+ case TCPSTATE_CLOSING:
+ case TCPSTATE_LAST_ACK:
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+
+
+/* *********************************************************************
+ * _tcp_aborttcb(ti,tcb)
+ *
+ * Forcefully terminate a TCP connection. Sends an RST packet
+ * to nuke the other end. The socket is forced into the CLOSED
+ * state.
+ *
+ * Input parameters:
+ * ti - tcp information
+ * tcb -tcb to abort
+ *
+ * Return value:
+ * nothing
+ ********************************************************************* */
+
+static void _tcp_aborttcb(tcp_info_t *ti,tcb_t *tcb)
+{
+ DEBUGMSG(("tcp_abort from state %d\n",tcb->tcb_state));
+
+ /*
+ * Decide what action to take based on current state
+ * If we're in SYN_SENT, RECEIVED, ESTABLISHED, FINWAIT_1,
+ * FINWAIT_2, CLOSING, LAST_ACK, or CLOSE_WAIT we've sent
+ * some traffic on this TCB, so send an RST to kill off
+ * the remote TCB.
+ */
+
+ if (TCPSTATE_IN_SET(tcb->tcb_state,M_TCPSTATE_ABORTSTATES)) {
+ /* Send RST with no timeout, don't retransmit it. */
+ _tcp_canceltimers(tcb);
+ _tcp_sendctlmsg(ti,tcb,TCPFLG_ACK | TCPFLG_RST,0);
+ }
+
+ /*
+ * No matter what, it's now CLOSED.
+ */
+
+ _tcp_closetcb(ti,tcb);
+
+}
+
+
+/* *********************************************************************
+ * _tcp_closetcb(ti,tcb)
+ *
+ * Close a TCB, switching the state to "closed" and resetting
+ * internal variables. The TCB is *not* freed.
+ *
+ * Input parameters:
+ * ti - tcp information
+ * tcb - tcb to close
+ *
+ * Return value:
+ * nothing
+ ********************************************************************* */
+
+static void _tcp_closetcb(tcp_info_t *ti,tcb_t *tcb)
+{
+ /*
+ * Set state to "closed" and reset timers
+ */
+
+ _tcp_setstate(tcb,TCPSTATE_CLOSED);
+ tcb->tcb_flags = 0;
+ _tcp_canceltimers(tcb);
+
+ /*
+ * Reinitialize the buffers to waste the stored send data and
+ * clear out any receive data
+ */
+
+ tmb_init(&tcb->tcb_txbuf);
+ tmb_init(&tcb->tcb_rxbuf);
+}
+
+/* *********************************************************************
+ * _tcp_send(ti,s,buf,len)
+ *
+ * Queue some data to be transmitted via a TCP socket
+ *
+ * Input parameters:
+ * ti - TCP information
+ * s - socket number
+ * buf - buffer of data to send
+ * len - size of buffer to send
+ *
+ * Return value:
+ * number of bytes queued
+ * <0 if an error occured
+ ********************************************************************* */
+
+int _tcp_send(tcp_info_t *ti,int s,uint8_t *buf,int len)
+{
+ tcb_t *tcb;
+ int retlen;
+ int curlen;
+
+ tcb = ti->ti_ports[s];
+ if (!tcb) return CFE_ERR_INV_PARAM;
+
+ /*
+ * State must be ESTABLISHED or CLOSE_WAIT. CLOSE_WAIT
+ * means we've received a fin, but we can still send in
+ * the outbound direction.
+ */
+
+ if (!TCPSTATE_IN_SET(tcb->tcb_state,M_TCPSTATE_SEND_OK)) {
+ return CFE_ERR_NOTCONN;
+ }
+
+ /*
+ * Copy the user data into the transmit buffer.
+ */
+
+ curlen = tmb_curlen(&(tcb->tcb_txbuf));
+ retlen = tmb_copyin(&(tcb->tcb_txbuf),buf,len,TRUE);
+
+ /*
+ * Cause some output on the device.
+ */
+
+ /*
+ * Nagle: Call _tcp_output only if there is no outstanding
+ * unacknowledged data. The way our transmit buffer
+ * works, it only holds unacknowledged data, so this
+ * test is easy. It isn't really 100% correct to
+ * do it this way, but the effect is the same; we will
+ * not transmit tinygrams.
+ */
+
+ if ((curlen == 0) || (tcb->tcb_sockflags & TCPFLG_NODELAY)) {
+ _tcp_output(ti,tcb);
+ }
+
+ return retlen;
+}
+
+/* *********************************************************************
+ * _tcp_recv(ti,s,buf,len)
+ *
+ * Get buffered receive data from the TCP socket
+ *
+ * Input parameters:
+ * ti - tcp information
+ * s - socket number
+ * buf - pointer to receive buffer area
+ * len - size of receive buffer area
+ *
+ * Return value:
+ * number of bytes received
+ * <0 if an error occured
+ * ==0 if no data available (or tcp session is closed)
+ ********************************************************************* */
+
+int _tcp_recv(tcp_info_t *ti,int s,uint8_t *buf,int len)
+{
+ tcb_t *tcb;
+ int retlen;
+
+ tcb = ti->ti_ports[s];
+ if (!tcb) return CFE_ERR_INV_PARAM;
+
+ if (!TCPSTATE_IN_SET(tcb->tcb_state,M_TCPSTATE_RECV_OK)) {
+ return CFE_ERR_NOTCONN;
+ }
+
+ retlen = tmb_copyout(&(tcb->tcb_rxbuf),buf,len,TRUE);
+
+ /*
+ * If we've drained all of the data out of the buffer
+ * send an ack. This isn't ideal, but it will
+ * prevent us from keeping the window closed.
+ */
+
+// if (retlen && (tmb_curlen(&(tcb->tcb_rxbuf)) == 0)) {
+// _tcp_preparectlmsg(tcb,TCPFLG_ACK);
+// _tcp_protosend(ti,tcb);
+// tcb->tcb_flags &= ~TCB_FLG_DLYACK;
+// }
+
+ return retlen;
+}
+
+
+/* *********************************************************************
+ * _tcp_bind(ti,s,port)
+ *
+ * "bind" a TCP socket to a particular port - this sets the
+ * outbound local (source) port number.
+ *
+ * Input parameters:
+ * ti - tcp information
+ * s - socket number
+ * port - port number
+ *
+ * Return value:
+ * 0 if ok
+ * else error code
+ ********************************************************************* */
+
+int _tcp_bind(tcp_info_t *ti,int s,uint16_t port)
+{
+ tcb_t *tcb;
+
+ tcb = ti->ti_ports[s];
+ if (!tcb) return CFE_ERR_INV_PARAM;
+
+ /* XXX test state - must be in 'closed' */
+
+ if (_tcp_find_lclport(ti,port)) return CFE_ERR_ADDRINUSE;
+
+ tcb->tcb_lclport = port;
+
+ return 0;
+}
+
+/* *********************************************************************
+ * _tcp_setflags(ti,s,flags)
+ *
+ * Set per-socket flags.
+ *
+ * Input parameters:
+ * ti - tcp information
+ * s - socket number
+ * flags - flags to set
+ *
+ * Return value:
+ * 0 if ok
+ * else error code
+ ********************************************************************* */
+
+int _tcp_setflags(tcp_info_t *ti,int s,unsigned int flags)
+{
+ tcb_t *tcb;
+
+ tcb = ti->ti_ports[s];
+ if (!tcb) return CFE_ERR_INV_PARAM;
+
+ tcb->tcb_sockflags = flags;
+
+ return 0;
+}
+
+/* *********************************************************************
+ * _tcp_getflags(ti,s,flags)
+ *
+ * Get per-socket flags.
+ *
+ * Input parameters:
+ * ti - tcp information
+ * s - socket number
+ * flags - pointer to int to receive flags
+ *
+ * Return value:
+ * 0 if ok
+ * else error code
+ ********************************************************************* */
+
+int _tcp_getflags(tcp_info_t *ti,int s,unsigned int *flags)
+{
+ tcb_t *tcb;
+
+ tcb = ti->ti_ports[s];
+ if (!tcb) return CFE_ERR_INV_PARAM;
+
+ *flags = tcb->tcb_sockflags;
+
+ return 0;
+}
+
+
+/* *********************************************************************
+ * _tcp_peeraddr(ti,s,addr,port)
+ *
+ * Return the address of the computer on the other end of this
+ * connection.
+ *
+ * Input parameters:
+ * ti - tcp information
+ * s - socket number
+ * addr - receives IP address of remote
+ * port - port number
+ *
+ * Return value:
+ * 0 if ok
+ * else error code
+ ********************************************************************* */
+
+int _tcp_peeraddr(tcp_info_t *ti,int s,uint8_t *addr,uint16_t *port)
+{
+ tcb_t *tcb;
+
+ tcb = ti->ti_ports[s];
+ if (!tcb) return CFE_ERR_INV_PARAM;
+
+ /*
+ * Test for any of the states where we believe the peeraddr in the tcb
+ * is valid.
+ */
+
+ if (!TCPSTATE_IN_SET(tcb->tcb_state,M_TCPSTATE_PEERADDR_OK)) {
+ return CFE_ERR_NOTCONN;
+ }
+
+ if (addr) memcpy(addr,tcb->tcb_peeraddr,IP_ADDR_LEN);
+ if (port) *port = tcb->tcb_peerport;
+
+ return 0;
+}
+
+/* *********************************************************************
+ * _tcp_listen(ti,s,port)
+ *
+ * Set a socket for listening mode, allowing inbound connections
+ * to occur.
+ *
+ * Input parameters:
+ * ti - tcp information
+ * s - socket nunber
+ * port - port number to listen for (implicit tcp_bind)
+ *
+ * Return value:
+ * 0 if ok
+ * else error code
+ ********************************************************************* */
+
+int _tcp_listen(tcp_info_t *ti,int s,uint16_t port)
+{
+ tcb_t *tcb;
+ queue_t *qb;
+
+ /*
+ * See if another TCB is listening to this port
+ */
+
+ for (qb = ti->ti_tcblist.q_next; qb != &(ti->ti_tcblist); qb = qb->q_next) {
+ tcb = (tcb_t *) qb;
+
+ if ((tcb->tcb_lclport == port) &&
+ (tcb->tcb_state == TCPSTATE_LISTEN) &&
+ (tcb->tcb_peerport == 0)) return CFE_ERR_ADDRINUSE;
+
+ }
+
+ /*
+ * Otherwise, we're good to go.
+ */
+
+ tcb = ti->ti_ports[s];
+ if (!tcb) return CFE_ERR_INV_PARAM;
+
+ tcb->tcb_lclport = port;
+ _tcp_setstate(tcb,TCPSTATE_LISTEN);
+
+ tcb->tcb_sendnext = 0;
+ tcb->tcb_sendunack = 0;
+ tcb->tcb_sendwindow = 0;
+ _tcp_canceltimers(tcb);
+
+ tcb->tcb_rcvnext = 0;
+ tcb->tcb_rcvack = 0;
+
+ tmb_init(&tcb->tcb_txbuf);
+ tmb_init(&tcb->tcb_rxbuf);
+
+ tcb->tcb_txflags = 0;
+
+ return 0;
+}
+
+/* *********************************************************************
+ * _tcp_status(ti,s,connflag,rxready,rxeof)
+ *
+ * Get status information about the TCP session
+ *
+ * Input parameters:
+ * ti - tcp information
+ * s - socket nunber
+ * connflag - points to an int to receive connection status
+ * (1=connected,0=not connected)
+ * rxready - number of bytes in receive queue
+ * rxeof - returns 1 if we've been FINed.
+ *
+ * Return value:
+ * 0 if ok
+ * else error code
+ ********************************************************************* */
+
+int _tcp_status(tcp_info_t *ti,int s,unsigned int *connflag,int *rxready,int *rxeof)
+{
+ tcb_t *tcb;
+ int rxlen;
+
+ tcb = ti->ti_ports[s];
+ if (!tcb) return CFE_ERR_INV_PARAM;
+
+ rxlen = tmb_curlen(&(tcb->tcb_rxbuf));
+
+ /*
+ * We consider the connection "connected" if it's established
+ * or it's in CLOSE_WAIT (FIN received) but we have not drained
+ * all of the receive data.
+ *
+ * Otherwise: If it's not in one of the connection establishment states,
+ * it's not connected.
+ */
+
+ if (connflag) {
+ if ((tcb->tcb_state == TCPSTATE_ESTABLISHED) ||
+ ((rxlen != 0) && (tcb->tcb_state == TCPSTATE_CLOSE_WAIT))) {
+ *connflag = TCPSTATUS_CONNECTED;
+ }
+ else if (TCPSTATE_IN_SET(tcb->tcb_state,M_TCPSTATE_CONNINPROGRESS)) {
+ *connflag = TCPSTATUS_CONNECTING;
+ }
+ else {
+ *connflag = TCPSTATUS_NOTCONN;
+ }
+ }
+
+ if (rxready) {
+ *rxready = rxlen;
+ }
+
+ if (rxeof) {
+ *rxeof = 0;
+ if ((tcb->tcb_state == TCPSTATE_CLOSE_WAIT) ||
+ (tcb->tcb_state == TCPSTATE_LAST_ACK)) {
+ *rxeof = 1;
+ }
+ }
+
+ return 0;
+}
+
+/* *********************************************************************
+ * _tcp_debug(ti,s,arg)
+ *
+ * Perform debug functions on a socket
+ *
+ * Input parameters:
+ * ti - tcp information
+ * s - socket number
+ * arg - argument
+ *
+ * Return value:
+ * 0 if ok
+ * else error code
+ ********************************************************************* */
+
+int _tcp_debug(tcp_info_t *ti,int s,int arg)
+{
+ tcb_t *tcb;
+
+ tcb = ti->ti_ports[s];
+ if (!tcb) return CFE_ERR_INV_PARAM;
+
+ printf("State=%d SendNext=%u SendUnack=%u ",
+ tcb->tcb_state,tcb->tcb_sendnext,tcb->tcb_sendunack);
+ printf("SendWin=%d Ack=%u Rxlen=%d\n",
+ tcb->tcb_sendwindow,
+ tcb->tcb_rcvack,tmb_curlen(&(tcb->tcb_rxbuf)));
+
+ return 0;
+}
+
+
+#ifdef _TCP_DEBUG_
+/* *********************************************************************
+ * _tcp_dumppktstate(label,flags,ack,seq,len)
+ *
+ * A debug function.
+ *
+ * Input parameters:
+ * label - printed before packet state
+ * flags - transmit flags for packet
+ * ack,seq,len - from the packet
+ *
+ * Return value:
+ * nothing
+ ********************************************************************* */
+static void _tcp_dumppktstate(char *label,uint16_t flags,
+ uint32_t ack,uint32_t seq,int len)
+{
+ printf("%s: ",label);
+ if (flags & TCPFLG_FIN) printf("FIN ");
+ if (flags & TCPFLG_SYN) printf("SYN ");
+ if (flags & TCPFLG_RST) printf("RST ");
+ if (flags & TCPFLG_PSH) printf("PSH ");
+ if (flags & TCPFLG_ACK) printf("ACK ");
+ if (flags & TCPFLG_URG) printf("URG ");
+ printf("Ack=%u Seq=%u Data=%d\n",ack,seq,len);
+}
+#endif
+
+
+/* *********************************************************************
+ * _tcp_output(ti,tcb)
+ *
+ * Try to perform some output on this TCB. If there is
+ * data to send and we can transmit it (i.e., the remote's
+ * receive window will allow it), segment the data from the
+ * buffer and transmit it, updating local state variables.
+ *
+ * Input parameters:
+ * ti - tcp information
+ * tcb - the tcb to send data on
+ *
+ * Return value:
+ * nothing
+ ********************************************************************* */
+
+static void _tcp_output(tcp_info_t *ti,tcb_t *tcb)
+{
+ ebuf_t *b;
+ int tcplen;
+ int window;
+ uint16_t flags;
+ uint8_t *cksumptr;
+ uint8_t *tcphdr;
+ uint16_t cksum;
+ int hdrlen;
+ uint8_t pseudoheader[12];
+ int offset;
+ uint32_t sendmax;
+ uint32_t windowmax;
+ uint32_t cwndmax;
+
+ /*
+ * sendmax is (one plus) the highest sequence number we have
+ * data for.
+ *
+ * windowmax is (one plus) the highest sequence number in the
+ * send window.
+ *
+ * cwndmax is (one plus) the highest sequence number in the
+ * congestion window.
+ */
+
+ sendmax = tcb->tcb_sendunack + tmb_curlen(&(tcb->tcb_txbuf));
+ windowmax = tcb->tcb_sendunack + tcb->tcb_sendwindow;
+ cwndmax = tcb->tcb_sendunack + 5*1400;
+
+ /*
+ * We'll send up to 'sendmax', 'cwndmax', or 'windowmax' bytes, whichever
+ * is sooner. Set 'sendmax' to the earliest sequence number.
+ */
+
+ if (TCPSEQ_GT(sendmax,windowmax)) sendmax = windowmax;
+ if (TCPSEQ_GT(sendmax,cwndmax)) sendmax = cwndmax;
+
+ /*
+ * The (receive) window is whatever it takes to fill the buffer.
+ */
+
+ window = tmb_remlen(&(tcb->tcb_rxbuf));
+ if (window > 65000) window = 65000;
+
+ /*
+ * Spit out packets until "sendnext" either catches up
+ * or exceeds the remote window
+ */
+
+ while (TCPSEQ_LT(tcb->tcb_sendnext,sendmax)) {
+
+ /*
+ * This is the offset into the transmit buffer to start with.
+ * Offset 0 in the transmit buffer corresponds to "send unack"
+ * received acks move this pointer.
+ */
+
+ offset = tcb->tcb_sendnext - tcb->tcb_sendunack;
+
+ /*
+ * Allocate a buffer and remember the pointer to where
+ * the header starts.
+ */
+
+ b = _ip_alloc(ti->ti_ipinfo);
+ if (!b) break;
+
+ tcphdr = ebuf_ptr(b) + ebuf_length(b);
+
+ flags = TCPFLG_ACK | TCPHDRFLG(TCP_HDR_LENGTH);
+ hdrlen = TCP_HDR_LENGTH;
+
+ /*
+ * Fill in the fields according to the RFC.
+ */
+
+ tcb->tcb_rcvack = tcb->tcb_rcvnext; /* Update our "most recent ack" */
+
+ ebuf_append_u16_be(b,tcb->tcb_lclport); /* Local and remote ports */
+ ebuf_append_u16_be(b,tcb->tcb_peerport);
+ ebuf_append_u32_be(b,tcb->tcb_sendnext); /* sequence and ack numbers */
+ ebuf_append_u32_be(b,tcb->tcb_rcvack);
+ ebuf_append_u16_be(b,flags); /* Flags */
+ ebuf_append_u16_be(b,window); /* Window size */
+ cksumptr = ebuf_ptr(b) + ebuf_length(b);
+ ebuf_append_u16_be(b,0); /* dummy checksum for calculation */
+ ebuf_append_u16_be(b,0); /* Urgent Pointer (not used) */
+
+ /*
+ * Append the data, copying pieces out of the transmit buffer
+ * without adjusting its pointers.
+ */
+
+ tcplen = tmb_copyout2(&(tcb->tcb_txbuf),
+ b->eb_ptr + b->eb_length,
+ offset,
+ tcb->tcb_mtu);
+
+ b->eb_length += tcplen;
+
+#ifdef _TCP_DEBUG_
+ if (_tcp_dumpflags) _tcp_dumppktstate("TX_DATA",flags,
+ tcb->tcb_sendnext,
+ tcb->tcb_rcvack,tcplen);
+#endif
+
+ /*
+ * Adjust the "send next" sequence number to account for what
+ * we're about to send.
+ */
+
+ tcb->tcb_sendnext += tcplen;
+
+
+ /*
+ * Build the pseudoheader, which is part of the checksum calculation
+ */
+
+ _ip_getaddr(ti->ti_ipinfo,&pseudoheader[0]);
+ memcpy(&pseudoheader[4],tcb->tcb_peeraddr,IP_ADDR_LEN);
+ pseudoheader[8] = 0;
+ pseudoheader[9] = IPPROTO_TCP;
+ pseudoheader[10] = ((tcplen+hdrlen) >> 8) & 0xFF;
+ pseudoheader[11] = ((tcplen+hdrlen) & 0xFF);
+
+ /*
+ * Checksum the packet and insert the checksum into the header
+ */
+
+ cksum = ip_chksum(0,pseudoheader,sizeof(pseudoheader));
+ cksum = ip_chksum(cksum,tcphdr,tcplen + hdrlen);
+ cksum = ~cksum;
+ cksumptr[0] = (cksum >> 8) & 0xFF;
+ cksumptr[1] = (cksum & 0xFF);
+
+ /*
+ * Transmit the packet. The layer below us will free it.
+ */
+
+ _ip_send(ti->ti_ipinfo,b,tcb->tcb_peeraddr,IPPROTO_TCP);
+
+ /*
+ * Set the timer that we'll use to wait for an acknowledgement.
+ * If this timer goes off, we'll rewind "sendnext" back to
+ * "sendunack" and transmit stuff again.
+ */
+
+ TIMER_SET(tcb->tcb_timer_retx,TCP_RETX_TIMER);
+
+ /*
+ * We just sent an ack, so cancel the delayed ack timer.
+ */
+
+ tcb->tcb_flags &= ~TCB_FLG_DLYACK;
+
+ }
+
+}
+
+/* *********************************************************************
+ * _tcp_protosend(ti,tcb)
+ *
+ * Transmit "protocol messages" on the tcb. This is used for
+ * sending SYN, FIN, ACK, and other control packets.
+ *
+ * Input parameters:
+ * ti - tcp infomration
+ * tcb - tcb we're interested in
+ *
+ * Return value:
+ * nothing
+ ********************************************************************* */
+
+static void _tcp_protosend(tcp_info_t *ti,tcb_t *tcb)
+{
+ ebuf_t *b;
+ int tcplen;
+ int window;
+ uint16_t flags;
+ uint8_t *cksumptr;
+ uint8_t *tcphdr;
+ uint16_t cksum;
+ int hdrlen;
+ uint8_t pseudoheader[12];
+
+ /*
+ * Allocate a buffer and remember the pointer to where
+ * the header starts.
+ */
+
+ b = _ip_alloc(ti->ti_ipinfo);
+ if (!b) return;
+
+ tcphdr = ebuf_ptr(b) + ebuf_length(b);
+
+ /*
+ * We're going to send something, so we can clear the flag.
+ * Also clear the delay-ack flag since everything's an ack.
+ */
+
+ tcb->tcb_flags &= ~(TCB_FLG_SENDMSG | TCB_FLG_DLYACK);
+
+ /*
+ * Build the TCP header
+ */
+
+ /* The flags are the current flags + the header length */
+ if (tcb->tcb_txflags & TCPFLG_SYN) {
+ flags = tcb->tcb_txflags | (TCPHDRFLG(TCP_HDR_LENGTH + 4));
+ hdrlen = TCP_HDR_LENGTH + 4;
+ }
+ else {
+ flags = tcb->tcb_txflags | (TCPHDRFLG(TCP_HDR_LENGTH));
+ hdrlen = TCP_HDR_LENGTH;
+ }
+
+
+#ifdef _TCP_DEBUG_
+ if (_tcp_dumpflags) _tcp_dumppktstate("TX_CTL",flags,
+ tcb->tcb_sendnext,
+ tcb->tcb_rcvack,0);
+#endif
+
+ /*
+ * The (receive) window is whatever it takes to fill the buffer.
+ */
+
+ window = tmb_remlen(&(tcb->tcb_rxbuf));
+ if (window > 65000) window = 65000;
+
+ /*
+ * Fill in the fields according to the RFC.
+ */
+
+ tcb->tcb_rcvack = tcb->tcb_rcvnext; /* update last tx ack */
+
+ ebuf_append_u16_be(b,tcb->tcb_lclport); /* Local and remote ports */
+ ebuf_append_u16_be(b,tcb->tcb_peerport);
+ ebuf_append_u32_be(b,tcb->tcb_sendnext); /* sequence and ack numbers */
+ ebuf_append_u32_be(b,tcb->tcb_rcvack);
+ ebuf_append_u16_be(b,flags); /* Flags */
+ ebuf_append_u16_be(b,window); /* Window size */
+ cksumptr = ebuf_ptr(b) + ebuf_length(b);
+ ebuf_append_u16_be(b,0); /* dummy checksum for calculation */
+ ebuf_append_u16_be(b,0); /* Urgent Pointer (not used) */
+
+ /*
+ * Append TCP options on SYN packet
+ */
+
+ if (flags & TCPFLG_SYN) {
+ ebuf_append_u16_be(b,TCP_MAX_SEG_OPT);
+ ebuf_append_u16_be(b,TCP_MAX_SEG_SIZE);
+ }
+
+ tcplen = 0; /* don't transmit data here */
+
+ /*
+ * SYN and FIN packets consume a sequence number, so
+ * increment the "sendnext" variable. If we need to retransmit
+ * these segments, we'll wind "sendnext" back to "sendunack"
+ *
+ * XXX: Can you send a SYN and FIN at the same time?
+ */
+
+ if (flags & (TCPFLG_SYN | TCPFLG_FIN)) tcb->tcb_sendnext++;
+
+ /*
+ * Build the pseudoheader, which is part of the checksum calculation
+ */
+
+ _ip_getaddr(ti->ti_ipinfo,&pseudoheader[0]);
+ memcpy(&pseudoheader[4],tcb->tcb_peeraddr,IP_ADDR_LEN);
+ pseudoheader[8] = 0;
+ pseudoheader[9] = IPPROTO_TCP;
+ pseudoheader[10] = ((tcplen+hdrlen) >> 8) & 0xFF;
+ pseudoheader[11] = ((tcplen+hdrlen) & 0xFF);
+
+ /*
+ * Checksum the packet and insert the checksum into the header
+ */
+
+ cksum = ip_chksum(0,pseudoheader,sizeof(pseudoheader));
+ cksum = ip_chksum(cksum,tcphdr,tcplen + hdrlen);
+ cksum = ~cksum;
+ cksumptr[0] = (cksum >> 8) & 0xFF;
+ cksumptr[1] = (cksum & 0xFF);
+
+ /*
+ * Transmit the packet. The layer below us will free it.
+ */
+
+ _ip_send(ti->ti_ipinfo,b,tcb->tcb_peeraddr,IPPROTO_TCP);
+}
+
+/* *********************************************************************
+ * _tcp_sendreset(ti,dstipaddr,ack,srcport,dstport)
+ *
+ * Transmit "protocol messages" on the tcb. This is used for
+ * sending SYN, FIN, ACK, and other control packets.
+ *
+ * Input parameters:
+ * ti - tcp infomration
+ * tcb - tcb we're interested in
+ *
+ * Return value:
+ * nothing
+ ********************************************************************* */
+
+static void _tcp_sendreset(tcp_info_t *ti,uint8_t *dstipaddr,uint32_t ack,
+ uint16_t srcport,uint16_t dstport)
+{
+ tcb_t tcb; /* fake TCB so we can use _tcp_protosend */
+
+ memset(&tcb,0,sizeof(tcb));
+ memcpy(tcb.tcb_peeraddr,dstipaddr,IP_ADDR_LEN);
+ tcb.tcb_peerport = dstport;
+ tcb.tcb_lclport = srcport;
+ tcb.tcb_txflags = TCPFLG_RST|TCPFLG_ACK;
+ tcb.tcb_rcvnext = ack;
+
+ _tcp_protosend(ti,&tcb);
+}
+
+
+/* *********************************************************************
+ * _tcp_sendctlmsg(ti,tcb,flags,timeout)
+ *
+ * Set up for and transmit a control message. This is usually
+ * called on a state transition where we need to send a control
+ * message like a SYN or FIN, with a timeout. We reset the
+ * retry counter, set the retransmit timer, and fire off the message
+ * right here.
+ *
+ * Input parameters:
+ * ti - tcp information
+ * tcb - the tcb for this TCP session
+ * flags - packet flags (TCPFLG_xxx)
+ * timeout - timeout value , in ticks
+ *
+ * Return value:
+ * nothing
+ ********************************************************************* */
+
+static void _tcp_sendctlmsg(tcp_info_t *ti,tcb_t *tcb,uint16_t flags,int timeout)
+{
+ tcb->tcb_txflags = flags;
+ tcb->tcb_retrycnt = 0;
+
+ if (timeout >= 0) {
+ if (timeout) {
+ TIMER_SET(tcb->tcb_timer_retx,timeout);
+ }
+ else {
+ TIMER_CLEAR(tcb->tcb_timer_retx);
+ }
+ }
+
+ _tcp_protosend(ti,tcb);
+}
+
+
+/* *********************************************************************
+ * _tcp_find_lclport(ti,port)
+ *
+ * Find the specified local port - mostly to see if it is
+ * already in use.
+ *
+ * Input parameters:
+ * ti - tcp information
+ * port - local port
+ *
+ * Return value:
+ * tcb owning this port, or NULL if none found
+ ********************************************************************* */
+static tcb_t *_tcp_find_lclport(tcp_info_t *ti,uint16_t port)
+{
+ tcb_t *tcb = NULL;
+ queue_t *qb;
+
+ for (qb = ti->ti_tcblist.q_next; qb != &(ti->ti_tcblist); qb = qb->q_next) {
+ tcb = (tcb_t *) qb;
+
+ if (tcb->tcb_lclport == port) return tcb;
+
+ }
+
+ return NULL;
+
+}
+
+/* *********************************************************************
+ * _tcp_find_tcb(ti,srcport,dstport,saddr)
+ *
+ * Locate the TCB in the active TCBs. This is used to match
+ * an inbound TCP packet to a TCB.
+ *
+ * Input parameters:
+ * ti - tcp information
+ * srcport,dstport - source and dest ports from TCP header
+ * saddr - source IP address of sending host
+ *
+ * Return value:
+ * tcb pointer or NULL if no TCB was found
+ ********************************************************************* */
+
+static tcb_t *_tcp_find_tcb(tcp_info_t *ti,uint16_t srcport,uint16_t dstport,uint8_t *saddr)
+{
+ tcb_t *tcb = NULL;
+ queue_t *qb;
+
+ for (qb = ti->ti_tcblist.q_next; qb != &(ti->ti_tcblist); qb = qb->q_next) {
+ tcb = (tcb_t *) qb;
+
+ /* Try active TCBs */
+ if ((tcb->tcb_peerport != 0) &&
+ (tcb->tcb_lclport == dstport) &&
+ (tcb->tcb_peerport == srcport) &&
+ (memcmp(saddr,tcb->tcb_peeraddr,IP_ADDR_LEN) == 0)) break;
+ }
+
+ /* Found it on our active list. */
+ if (qb != &(ti->ti_tcblist)) return tcb;
+
+ /* no dice, try listening ports */
+ for (qb = ti->ti_tcblist.q_next; qb != &(ti->ti_tcblist); qb = qb->q_next) {
+ tcb = (tcb_t *) qb;
+
+ /* Try active TCBs */
+ if ((tcb->tcb_peerport == 0) &&
+ (tcb->tcb_lclport == dstport)) break;
+ }
+
+ /* Found it on our passive list. */
+ if (qb != &(ti->ti_tcblist)) return tcb;
+
+ return NULL;
+}
+
+/* *********************************************************************
+ * _tcp_procack(ti,tcb,ack,flags)
+ *
+ * Process a received acknowledgement.
+ *
+ * Input parameters:
+ * ti - tcp information
+ * tcb - tcb for this tcb session
+ * ack - acknum from received packet
+ * flags - flags from received packet
+ *
+ * Return value:
+ * nothing
+ ********************************************************************* */
+
+static void _tcp_procack(tcp_info_t *ti,tcb_t *tcb,uint32_t ack,uint16_t flags)
+{
+ int ackamt;
+ int unacklen;
+
+ /* This is the number of unacknowledged bytes we have */
+ unacklen = tmb_curlen(&(tcb->tcb_txbuf));
+
+ switch (tcb->tcb_state) {
+
+ case TCPSTATE_ESTABLISHED:
+ /*
+ * The ack is valid if it's in the range of
+ * unacknowledged data we're holding onto.
+ *
+ * sendunack < ack <= (sendunack+datalen)
+ *
+ * Do the first comparison as "<=" instead of just "<"
+ * so we can catch duplicate acks.
+ */
+
+ if (!(TCPSEQ_LEQ(tcb->tcb_sendunack,ack) &&
+ TCPSEQ_LEQ(ack,(tcb->tcb_sendunack+unacklen)))) {
+ DEBUGMSG(("Ack is out of range: %u\n",ack));
+ return;
+ }
+
+ /*
+ * Compute the # of bytes spanned by this ack
+ */
+
+ ackamt = TCPSEQ_DIFF(ack,tcb->tcb_sendunack);
+
+ /*
+ * If we actually acked something, adjust the tx buffer
+ * to remove the bytes covered by the ack, then update
+ * the 'next' sequence number so we'll start there next
+ * time.
+ */
+
+ if (ackamt > 0) {
+ tmb_adjust(&tcb->tcb_txbuf,ackamt);
+ tcb->tcb_txflags = TCPFLG_ACK;
+ tcb->tcb_sendunack = ack;
+ tcb->tcb_dup_ackcnt = 0; /* not a duplicate */
+ }
+ else {
+ tcb->tcb_dup_ackcnt++;
+ //DEBUGMSG(("Duplicate ack received\n"));
+ /*
+ * Duplicate ack received
+ * XXX This is where we'd be doing stuff for
+ * XXX slow-start, fast retransmit, etc.
+ */
+ }
+
+ /*
+ * If we're all caught up, we can cancel the
+ * retransmit timer. Otherwise, try to do
+ * some output on the interface. This will
+ * reset the retransmit timer if we did anything.
+ */
+
+ if (tmb_curlen(&(tcb->tcb_txbuf)) != 0) {
+ tcb->tcb_flags |= TCB_FLG_OUTPUT;
+ }
+ else {
+ TIMER_CLEAR(tcb->tcb_timer_retx);
+ }
+
+ break;
+
+ case TCPSTATE_FINWAIT_1:
+ ackamt = TCPSEQ_DIFF(ack,tcb->tcb_sendunack);
+ if (ackamt == 1) {
+ tcb->tcb_sendunack = ack;
+ if (flags & TCPFLG_FIN) {
+ _tcp_setstate(tcb,TCPSTATE_TIME_WAIT);
+ _tcp_canceltimers(tcb);
+ _tcp_preparectlmsg(tcb,TCPFLG_ACK);
+ TIMER_SET(tcb->tcb_timer_2msl,TCP_TIMEWAIT_TIMER);
+ }
+ else {
+ _tcp_setstate(tcb,TCPSTATE_FINWAIT_2);
+ }
+ }
+ break;
+
+ }
+}
+
+/* *********************************************************************
+ * _tcp_procdata(ti,tcb,seqnum,flags,buf)
+ *
+ * Process data in a received TCP packet - we'll put the
+ * data into the receive ring buffer, process the seqnum
+ * field, and prepare to send an ack.
+ *
+ * Input parameters:
+ * ti - tcp information
+ * tcb - tcb describing TCP session
+ * seqnum,flags - from the header of the received TCP packet
+ * buf - ebuf contianing data
+ *
+ * Return value:
+ * nothing
+ ********************************************************************* */
+
+static void _tcp_procdata(tcp_info_t *ti,tcb_t *tcb,uint32_t seqnum,
+ uint16_t flags,ebuf_t *buf)
+{
+ int datalen;
+ uint8_t *bp;
+ int rxwin,rwin;
+ uint32_t endseqnum;
+ uint32_t endrxwindow;
+ int reallen;
+
+ /*
+ * Figure out how much we're going to take. This should not
+ * exceed our buffer size because we advertised a window
+ * only big enough to fill our buffer.
+ */
+
+ bp = ebuf_ptr(buf); /* pointer to TCP data */
+ datalen = ebuf_remlen(buf); /* Size of TCP data */
+
+ /*
+ * If there's no data in the buffer, we can skip the data-related
+ * stuff and check for a possible FIN.
+ */
+
+ if (datalen != 0) {
+
+ /*
+ * Figure out if the data fits within the window. We don't deal
+ * with out-of-order segments, so the test is relatively
+ * simple: The received segment must contain (or match)
+ * the sequence number from "rcvnext" to the end of the receive
+ * window.
+ *
+ * Therefore: seqnum <= rcvnext < seqnum+datalen
+ */
+
+ rxwin = tmb_remlen(&(tcb->tcb_rxbuf)); /* receive window size */
+ endseqnum = (seqnum + datalen); /* seq # of end of segment */
+ endrxwindow = tcb->tcb_rcvack + rxwin; /* end of receive window */
+
+ /*
+ * The segment might include data outside the receive window
+ * (it's not supposed to, but it can). Crop the 'endseqnum'
+ * value at the rx window if necessary. Keep the earlier seqnum.
+ */
+//
+// XXX This is just plain broken - "endrxwindow" is the wrong thing to test.
+// if (TCPSEQ_LT(endrxwindow,endseqnum)) {
+// endseqnum = endrxwindow;
+// }
+
+ /*
+ * Test to see if the data is in sequence.
+ */
+
+ rwin = (TCPSEQ_LEQ(seqnum,tcb->tcb_rcvnext) &&
+ TCPSEQ_LT(tcb->tcb_rcvnext,endseqnum));
+
+ if (!rwin) {
+ DEBUGMSG(("Dropping out-of-order packet %u %u %u\n",seqnum,
+ tcb->tcb_rcvnext,endseqnum));
+ return;
+ }
+
+ /*
+ * The actual amount of new data is the distance from
+ * the "rcvnext" pointer to the end sequence number.
+ * typically this will be the entire packet, but we
+ * handle the case of receiving a partial duplicate segment.
+ * Do this by shortening the data length we're going to
+ * copy and adjusting the buffer pointer to point past the
+ * duplicate data. Normally this won't do anything.
+ */
+
+ reallen = endseqnum - tcb->tcb_rcvnext;
+ bp += (tcb->tcb_rcvnext - seqnum);
+
+ if (reallen != datalen) {
+ DEBUGMSG(("newdata(%d) does not match real length(%d)\n",reallen,datalen));
+ }
+
+ if (reallen > 0) {
+
+ /*
+ * Copy the data into the receive buffer. In the
+ * unlikely event that it doesn't fit, update the
+ * length so we'll only ack what we took.
+ */
+
+ reallen = tmb_copyin(&(tcb->tcb_rxbuf),bp,reallen,TRUE);
+
+ tcb->tcb_rcvnext += reallen;
+
+ /*
+ * Set the delayed-ack flag. If it's already set,
+ * then we've already received some data without acking
+ * it, so send the ack now, to encourage us to
+ * ack every other segment at least.
+ */
+
+ if (tcb->tcb_flags & TCB_FLG_DLYACK) {
+ _tcp_preparectlmsg(tcb,TCPFLG_ACK);
+ }
+ else {
+ tcb->tcb_flags |= TCB_FLG_DLYACK;
+ }
+ }
+ }
+
+ /*
+ * Handle the case of data in a FIN packet.
+ */
+
+ if (flags & TCPFLG_FIN) {
+
+ tcb->tcb_rcvnext++; /* Consume the FIN */
+
+ DEBUGMSG(("FIN rcvd in %d ack %u\n",tcb->tcb_state,tcb->tcb_rcvnext));
+
+ switch (tcb->tcb_state) {
+ case TCPSTATE_ESTABLISHED:
+
+ /*
+ * If a FIN is received in the ESTABLISHED state,
+ * go to CLOSE_WAIT.
+ */
+
+ tcb->tcb_flags &= ~TCB_FLG_DLYACK;
+ _tcp_setstate(tcb,TCPSTATE_CLOSE_WAIT);
+ _tcp_sendctlmsg(ti,tcb,TCPFLG_ACK,-1);
+ break;
+
+ case TCPSTATE_FINWAIT_1:
+
+ /*
+ * Two choices: Either we got a FIN or a FIN ACK.
+ * In either case, send an ack. The difference
+ * is what state we end up in.
+ */
+
+ if (flags & TCPFLG_ACK) {
+ /* Received: FIN ACK - go directly to TIME_WAIT */
+ _tcp_canceltimers(tcb);
+ _tcp_preparectlmsg(tcb,TCPFLG_ACK);
+ _tcp_setstate(tcb,TCPSTATE_TIME_WAIT);
+#if defined(CONFIG_MIPS_BRCM)
+ TIMER_SET(tcb->tcb_timer_2msl,TCP_SEND_TIMER);
+#else
+ TIMER_SET(tcb->tcb_timer_2msl,TCP_TIMEWAIT_TIMER);
+#endif
+ }
+ else {
+ /* Received: FIN - simultaneous close, go to CLOSING */
+ _tcp_setstate(tcb,TCPSTATE_CLOSING);
+ _tcp_preparectlmsg(tcb,TCPFLG_ACK);
+ }
+ break;
+
+ case TCPSTATE_FINWAIT_2:
+
+ /*
+ * Received a FIN in FINWAIT_2 - just transmit
+ * an ack and go to TIME_WAIT.
+ */
+
+ _tcp_canceltimers(tcb);
+ _tcp_preparectlmsg(tcb,TCPFLG_ACK);
+ _tcp_setstate(tcb,TCPSTATE_TIME_WAIT);
+ TIMER_SET(tcb->tcb_timer_2msl,TCP_TIMEWAIT_TIMER);
+ break;
+ }
+ }
+
+}
+
+/* *********************************************************************
+ * _tcp_rx_callback(ref,buf,destaddr,srcaddr)
+ *
+ * The IP layer calls this routine when a TCP packet is received.
+ * We dispatch the packet to appropriate handlers from here.
+ *
+ * Input parameters:
+ * ref - our tcp information (held by the ip stack for us)
+ * buf - the ebuf that we received
+ * destaddr,srcaddr - destination and source IP addresses
+ *
+ * Return value:
+ * ETH_DROP or ETH_KEEP, depending if we keep the packet or not
+ ********************************************************************* */
+
+static int _tcp_rx_callback(void *ref,ebuf_t *buf,uint8_t *destaddr,uint8_t *srcaddr)
+{
+ uint8_t pseudoheader[12];
+ int tcplen;
+ uint16_t calccksum;
+ uint16_t origcksum;
+ uint8_t *tcphdr;
+ uint16_t srcport;
+ uint16_t dstport;
+ uint32_t seqnum;
+ uint32_t acknum;
+ uint16_t window;
+ uint16_t flags;
+ tcb_t *tcb;
+ tcp_info_t *ti = (tcp_info_t *) ref;
+
+ /*
+ * get a pointer to the TCP header
+ */
+
+ tcplen = ebuf_length(buf);
+ tcphdr = ebuf_ptr(buf);
+
+ /*
+ * construct the pseudoheader for the cksum calculation
+ */
+
+ memcpy(&pseudoheader[0],srcaddr,IP_ADDR_LEN);
+ memcpy(&pseudoheader[4],destaddr,IP_ADDR_LEN);
+ pseudoheader[8] = 0;
+ pseudoheader[9] = IPPROTO_TCP;
+ pseudoheader[10] = (tcplen >> 8) & 0xFF;
+ pseudoheader[11] = (tcplen & 0xFF);
+
+ origcksum = ((uint16_t) tcphdr[16] << 8) | (uint16_t) tcphdr[17];
+ tcphdr[16] = tcphdr[17] = 0;
+
+ calccksum = ip_chksum(0,pseudoheader,sizeof(pseudoheader));
+ calccksum = ip_chksum(calccksum,tcphdr,tcplen);
+ calccksum = ~calccksum;
+
+ if (calccksum != origcksum) {
+ return ETH_DROP;
+ }
+
+ /* Read the other TCP header fields from the packet */
+
+ ebuf_get_u16_be(buf,srcport);
+ ebuf_get_u16_be(buf,dstport);
+ ebuf_get_u32_be(buf,seqnum);
+ ebuf_get_u32_be(buf,acknum);
+ ebuf_get_u16_be(buf,flags);
+ ebuf_get_u16_be(buf,window);
+ ebuf_skip(buf,4); /* skip checksum and urgent pointer */
+
+ /* Skip options in header */
+ if (TCPHDRSIZE(flags) < TCP_HDR_LENGTH) {
+ return ETH_DROP;
+ }
+ ebuf_skip(buf,(TCPHDRSIZE(flags) - TCP_HDR_LENGTH));
+
+ /*
+ * Okay, start looking for a matching TCB. If no matching TCB,
+ * send a RST.
+ * XXX Extra credit: rate-limit the RSTs.
+ */
+
+ tcb = _tcp_find_tcb(ti,srcport,dstport,srcaddr);
+ if (!tcb) {
+ DEBUGMSG(("Invalid TCB from %I, srcport=%u dstport=%u\n",srcaddr,srcport,dstport));
+ _tcp_sendreset(ti,srcaddr,seqnum+1,dstport,srcport);
+ return ETH_DROP; /* drop packet if no matching port */
+ }
+
+ /*
+ * Any activity on a TCB is enough to reset the keepalive timer
+ */
+
+ if (TCPSTATE_IN_SET(tcb->tcb_state,M_TCPSTATE_RESETKEEPALIVE)) {
+ TIMER_SET(tcb->tcb_timer_keep,TCP_KEEPALIVE_TIMER);
+ }
+
+ /*
+ * Some debugging
+ */
+
+#ifdef _TCP_DEBUG_
+ if (_tcp_dumpflags) _tcp_dumppktstate("Received",flags,
+ acknum,seqnum,ebuf_length(buf));
+#endif
+
+ /*
+ * If someone tries to reset us, just kill off the port.
+ * (except: if we were in SYN_RECEIVED, go back to LISTEN)
+ */
+
+ if (flags & TCPFLG_RST) {
+ if (tcb->tcb_state == TCPSTATE_SYN_RECEIVED) {
+ _tcp_setstate(tcb,TCPSTATE_LISTEN);
+ }
+ else {
+ _tcp_closetcb(ti,tcb);
+ }
+ return ETH_DROP;
+ }
+
+ /*
+ * Remember the window we got.
+ */
+
+ tcb->tcb_sendwindow = window;
+
+ /*
+ * What we do here depends on the current connection state
+ * Most of this is just from the connection state diagram we've
+ * all see way too often.
+ */
+
+ switch ( tcb->tcb_state ) {
+
+ case TCPSTATE_LISTEN:
+ if (flags & TCPFLG_SYN) {
+ tcb->tcb_rcvnext = seqnum + 1;
+ tcb->tcb_peerport = srcport;
+ memcpy(tcb->tcb_peeraddr,srcaddr,IP_ADDR_LEN);
+ TIMER_SET(tcb->tcb_timer_keep,TCP_KEEPALIVE_TIMER);
+ _tcp_setstate(tcb,TCPSTATE_SYN_RECEIVED);
+ _tcp_sendctlmsg(ti,tcb,TCPFLG_SYN | TCPFLG_ACK,TCP_RETX_TIMER);
+ }
+ return ETH_DROP; /* we ignore everything else */
+ break;
+
+ case TCPSTATE_SYN_SENT:
+
+ /*
+ * The only packets we pay attention to in SYN_SENT
+ * are packets with SYN flags.
+ */
+ if (flags & TCPFLG_SYN) {
+
+ /*
+ * Two choices: Either we got a SYN ACK (normal)
+ * or just a SYN (simultaneous open, rare)
+ */
+
+ if ((flags & TCPFLG_ACK) &&
+ (acknum == (tcb->tcb_sendunack + 1))) {
+ /*
+ * If we received a SYN ACK and the acknum
+ * matches our SYN's seqnum + 1, we want
+ * to transition from SYN_SENT to ESTABLISHED.
+ */
+ TIMER_SET(tcb->tcb_timer_keep,TCP_KEEPALIVE_TIMER);
+ _tcp_setstate(tcb,TCPSTATE_ESTABLISHED);
+ tcb->tcb_sendunack = acknum;
+ tcb->tcb_sendnext = tcb->tcb_sendunack;
+ tcb->tcb_rcvnext = seqnum + 1;
+ /*
+ * Send an ack, but don't bother with the timer.
+ * If the remote does not get our ack, it will
+ * retransmit the SYN ACK and we'll ack it from
+ * the "ESTABLISHED" state.
+ * Actually, is that really true?
+ */
+ _tcp_sendctlmsg(ti,tcb,TCPFLG_ACK,0);
+ }
+ else {
+ DEBUGMSG(("Simultaneous open: SYN received in SYN_SENT state\n"));
+ tcb->tcb_rcvnext++; /* consume the SYN */
+ _tcp_setstate(tcb,TCPSTATE_SYN_RECEIVED);
+ _tcp_sendctlmsg(ti,tcb,TCPFLG_SYN|TCPFLG_ACK,TCP_RETX_TIMER);
+ }
+ }
+
+ return ETH_DROP;
+ break;
+
+ case TCPSTATE_SYN_RECEIVED:
+ /*
+ * If we've received a SYN and someone sends us a SYN,
+ * and the sequence numbers don't match,
+ * send back a SYN ACK. (this doesn't sound right,
+ * does it? It's sort of what we do from LISTEN)
+ */
+
+ if (flags & TCPFLG_SYN) {
+ _tcp_sendctlmsg(ti,tcb,TCPFLG_SYN | TCPFLG_ACK,TCP_RETX_TIMER);
+ }
+
+ /*
+ * If we got an ack and the acknum is correct,
+ * switch the state to 'ESTABLISHED' and cancel
+ * the timer. We're ready to rock.
+ */
+
+ if ((flags & TCPFLG_ACK) &&
+ (acknum == (tcb->tcb_sendunack + 1))) {
+ _tcp_sendctlmsg(ti,tcb,TCPFLG_ACK,0);
+ tcb->tcb_sendunack = acknum;
+ tcb->tcb_sendnext = tcb->tcb_sendunack;
+ TIMER_SET(tcb->tcb_timer_keep,TCP_KEEPALIVE_TIMER);
+ _tcp_setstate(tcb,TCPSTATE_ESTABLISHED);
+ }
+
+ return ETH_DROP;
+ break;
+
+ case TCPSTATE_ESTABLISHED:
+ case TCPSTATE_FINWAIT_1:
+ case TCPSTATE_FINWAIT_2:
+
+ /*
+ * ESTABLISHED, FINWAIT_1, and FINWAIT_2 can all
+ * carry data. Process the data here. If the
+ * segment also contains a FIN, these routines
+ * will handle that.
+ */
+
+ if (flags & TCPFLG_ACK) {
+ _tcp_procack(ti,tcb,acknum,flags);
+ }
+ _tcp_procdata(ti,tcb,seqnum,flags,buf);
+ break;
+
+ case TCPSTATE_CLOSING:
+ if (acknum == (tcb->tcb_sendunack + 1)) {
+ _tcp_setstate(tcb,TCPSTATE_TIME_WAIT);
+ _tcp_canceltimers(tcb);
+ TIMER_SET(tcb->tcb_timer_2msl,TCP_TIMEWAIT_TIMER);
+ }
+ break;
+
+ case TCPSTATE_LAST_ACK:
+ if (acknum == (tcb->tcb_sendunack + 1)) {
+ /* Ack matches, just close the TCB here. */
+ _tcp_closetcb(ti,tcb);
+ /* and free it. */
+ _tcp_freetcb(ti,tcb);
+ }
+ else {
+ _tcp_sendctlmsg(ti,tcb,TCPFLG_ACK | TCPFLG_FIN,TCP_RETX_TIMER);
+ }
+ break;
+
+ case TCPSTATE_TIME_WAIT:
+ if (!(flags & TCPFLG_RST)) {
+ tcb->tcb_txflags = TCPFLG_ACK;
+ TIMER_SET(tcb->tcb_timer_2msl,TCP_TIMEWAIT_TIMER);
+ _tcp_protosend(ti,tcb);
+ }
+ break;
+ }
+
+ /*
+ * If we're expected to transmit something, do it now.
+ */
+
+ if (tcb->tcb_flags & TCB_FLG_OUTPUT) {
+ _tcp_output(ti,tcb);
+ tcb->tcb_flags &= ~(TCB_FLG_OUTPUT | TCB_FLG_SENDMSG);
+ }
+
+ if (tcb->tcb_flags & TCB_FLG_SENDMSG) {
+ _tcp_protosend(ti,tcb);
+ }
+
+ /* We always make a copy of the data, so drop the packet. */
+ return ETH_DROP;
+}
+
+/* *********************************************************************
+ * _tcp_fasttimo(ti)
+ *
+ * Handle "fast timeout" for active TCP sockets.
+ *
+ * Input parameters:
+ * ti - tcp_info structure
+ *
+ * Return value:
+ * nothing
+ ********************************************************************* */
+static void _tcp_fasttimo(tcp_info_t *ti)
+{
+ tcb_t *tcb;
+ queue_t *qb;
+
+ /*
+ * First, reset the timer so we'll end up here
+ * again in another 200ms
+ */
+
+ TIMER_SET(ti->ti_fasttimer,TCP_FAST_TIMER); /* 200ms */
+
+ /*
+ * Now, walk down the list of TCBs and handle
+ * all the timers.
+ */
+
+ for (qb = ti->ti_tcblist.q_next; qb != &(ti->ti_tcblist); qb = qb->q_next) {
+ tcb = (tcb_t *) qb;
+ if (tcb->tcb_flags & TCB_FLG_DLYACK) {
+ tcb->tcb_flags &= ~TCB_FLG_DLYACK;
+ _tcp_sendctlmsg(ti,tcb,TCPFLG_ACK,-1);
+ }
+ }
+
+ /*
+ * While we're here, increment TCP's initial sequence number.
+ * BSD suggests 128000 every second, so we'll add 25600 every 200ms.
+ */
+
+ ti->ti_iss += 25600;
+
+}
+
+
+/* *********************************************************************
+ * _tcp_poll(arg)
+ *
+ * CFE calls this routine periodically to allow us to check
+ * and update timers, etc.
+ *
+ * Input parameters:
+ * arg - our tcp information (held by the timer module for us)
+ *
+ * Return value:
+ * nothing
+ ********************************************************************* */
+
+void _tcp_poll(void *arg)
+{
+ tcb_t *tcb;
+ queue_t *qb;
+ tcp_info_t *ti = (tcp_info_t *) arg;
+
+ /*
+ * Handle the "fast" timer. We do this every 200ms
+ */
+
+ if (TIMER_EXPIRED(ti->ti_fasttimer)) {
+ _tcp_fasttimo(ti);
+ /* timer will be reset by the _tcp_fasttimo routine */
+ }
+
+ /*
+ * Check the TCBs for the "slow" timers.
+ */
+
+ for (qb = ti->ti_tcblist.q_next; qb != &(ti->ti_tcblist); qb = qb->q_next) {
+ tcb = (tcb_t *) qb;
+
+ /*
+ * Check the retransmit timer. This is used during connection
+ * attempts to retransmit control messages, or also during
+ * regular data transfer to retransmit messages that are not
+ * acknowledged.
+ *
+ * XXX use the computed round-trip timer here
+ */
+
+ if (TIMER_EXPIRED(tcb->tcb_timer_retx)) {
+ TIMER_CLEAR(tcb->tcb_timer_retx); /* unless it is reset */
+
+ DEBUGMSG(("Retransmit timer expired, retrycnt=%d\n",tcb->tcb_retrycnt));
+
+ switch (tcb->tcb_state) {
+ case TCPSTATE_ESTABLISHED:
+ /*
+ * wind the send seqnum back to the last unacknowledged data,
+ * and send it out again.
+ */
+ TIMER_SET(tcb->tcb_timer_retx,TCP_RETX_TIMER);
+ tcb->tcb_retrycnt++;
+ tcb->tcb_sendnext = tcb->tcb_sendunack;
+ _tcp_output(ti,tcb);
+ break;
+
+ case TCPSTATE_SYN_SENT:
+ TIMER_SET(tcb->tcb_timer_retx,(TCP_RETX_TIMER << tcb->tcb_retrycnt));
+ tcb->tcb_retrycnt++;
+ tcb->tcb_sendnext = tcb->tcb_sendunack;
+ _tcp_protosend(ti,tcb);
+ break;
+
+ }
+ }
+
+ /*
+ * Check the keepalive timer. This is used during connection
+ * attempts to time out the connection, and _can_ be used for
+ * keepalives during established sessions.
+ *
+ * Our TCP does not bother with keepalive messages.
+ */
+
+ if (TIMER_EXPIRED(tcb->tcb_timer_keep)) {
+ TIMER_CLEAR(tcb->tcb_timer_keep); /* unless it is reset */
+ DEBUGMSG(("Keepalive timer expired in state %d\n",tcb->tcb_state));
+ if (TCPSTATE_IN_SET(tcb->tcb_state,M_TCPSTATE_CONNINPROGRESS)) {
+ DEBUGMSG(("Canceling pending connection\n"));
+ _tcp_aborttcb(ti,tcb);
+ }
+ }
+
+ /*
+ * Check the 2MSL timer. This is used in the TIME_WAIT state
+ * to return the tcb to the free list after the time wait
+ * period elapses.
+ */
+
+ if (TIMER_EXPIRED(tcb->tcb_timer_2msl)) {
+ TIMER_CLEAR(tcb->tcb_timer_2msl); /* will not be reset */
+ DEBUGMSG(("2MSL timer expired in state %d\n",tcb->tcb_state));
+ if (tcb->tcb_state == TCPSTATE_TIME_WAIT) {
+ DEBUGMSG(("Freeing TCB\n"));
+ _tcp_closetcb(ti,tcb);
+ _tcp_freetcb(ti,tcb);
+ }
+ }
+
+ }
+
+}
+
+