📄 nettcp.c
字号:
/*****************************************************************************
* nettcp.c - Network Transport Control Protocol program file.
*
* Copyright (c) 1998 by Global Election Systems Inc. All rights reserved.
*
* The authors hereby grant permission to use, copy, modify, distribute,
* and license this software and its documentation for any purpose, provided
* that existing copyright notices are retained in all copies and that this
* notice and the following disclaimer are included verbatim in any
* distributions. No written agreement, license, or royalty fee is required
* for any of the authorized uses.
*
* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS 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 THE 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.
*
******************************************************************************
* REVISION HISTORY (please don't use tabs!)
*
*(yyyy-mm-dd)
* 1998-02-02 Guy Lancaster <glanca@gesn.com>, Global Election Systems Inc.
* Original based on ka9q and BSD codes.
* 2001-05-18 Mads Christiansen <mads@mogi.dk>, Partner Voxtream
* Added support for running uC/IP in a single proces and on ethernet.
* Bugfix in resendTimeout, diffTime -> diffJTime!
* 2001-06-07 Robert Dickenson <odin@pnc.com.au>, Cognizant Pty Ltd.
* Quick fix to tcpInput for when OSSemCreate hadn't been called.
*
******************************************************************************
* NOTES
*
* MAXIMUM WINDOW
* We use a signed short int for the segment size adjustment (trimSeg()) to
* allow returning error codes. Thus our maximum segment size must be <=
* INT_MAX (i.e. 32767) rather than MAX_UINT. This is not a problem
* considering that we are using a PPP link over a serial link.
*
* HEADER CACHE
* The header values are all loaded in the header caches before being
* written to the outgoing segment so that a debugger can see the values
* of the header last sent.
******************************************************************************
* TO DO
*
* - Implement a SENDFIN flag in the tcb flags and use it in tcpOutput().
* - FINISH close!
*****************************************************************************/
#include "netconf.h"
#include <string.h>
#include "net.h"
#include "nettimer.h"
#include "netbuf.h"
#if MD5_SUPPORT > 0
#include "netrand.h"
#endif
#include "netmagic.h"
//#include "devio.h"
#include "netip.h"
#include "netiphdr.h"
#include "nettcp.h"
#include "nettcphd.h"
#include <stdio.h>
#include "netdebug.h"
#include "netos.h"
#pragma warning (push)
#pragma warning (disable: 4761) // integral size mismatch in argument; conversion supplied
#pragma warning (disable: 4018) // signed/unsigned mismatch
/*************************/
/*** LOCAL DEFINITIONS ***/
/*************************/
/* Configuration */
#define MAXTCP 12 /* Maximum TCP connections incl listeners. */
#define TCPTTL 64 /* Default time-to-live for TCP datagrams. */
#define OPTSPACE 5*4 /* TCP options space - must be a multiple of 4. */
#define NTCB 16 /* # TCB hash table headers */
#define MAXRETRANS 12 /* Maximum retransmissions. */
#define MAXKEEPTIMES 10 /* Maximum keep alive probe timeouts. */
#define MAXLISTEN 2 /* Maximum queued cloned listen connections. */
#define MAXFINWAIT2 600L /* Max time in seconds to wait for peer FIN. */
#define WRITESLEEP TICKSPERSEC /* Sleep time write waits for buffers (jiffies). */
#define STACK_SIZE NETSTACK /* Minimal stack. */
/*
* TCP connection control flag masks.
*/
#define FORCE 1 /* We owe the other end an ACK or window update */
#define CLONE 2 /* Server-type TCB, cloned on incoming SYN */
#define RETRAN 4 /* A retransmission has occurred */
#define ACTIVE 8 /* TCB created with an active open */
#define SYNACK 16 /* Our SYN has been acked */
#define KEEPALIVE 32 /* Send a keepalive probe */
/* Round trip timing parameters */
#define AGAIN 8 /* Average RTT gain = 1/8 */
#define DGAIN 4 /* Mean deviation gain = 1/4 */
#define MSL2 30 /* Guess at two maximum-segment lifetimes in seconds */
/* procInFlags return codes. */
#define ACKOK 0 /* OK to process segment. */
#define ACKDROP -1 /* Drop the segment. */
#define ACKRESET -2 /* Return segment as a reset. */
#define ACKCLOSE -3 /* Close the connection. */
/************************/
/*** LOCAL DATA TYPES ***/
/************************/
/*
* Combined TCP/IP headers with no options. Used to cached the headers.
*/
typedef struct TcpIPHdr_s {
IPHdr ipHdr; /* IP header - no options. */
TCPHdr tcpHdr; /* TCP header. tcpSeq, ack, off, & win
* are in host byte order.
*/
char options[OPTSPACE]; /* Cache for TCP options. */
} TCPIPHdr;
/*
* TCP session close reason codes.
*/
typedef enum {
NORMAL = 0, /* Normal close */
RESET = 1, /* Reset by other end */
TIMEOUT = 2, /* Excessive retransmissions */
NETWORK = 3 /* Network problem (ICMP message) */
} TCPReason;
/*
* TCP connection control block.
*/
typedef struct TCPCB_s {
struct TCPCB_s *prev; /* Linked list pointers for hash table */
struct TCPCB_s *next;
Connection conn; /* Connection struct for hash lookup. */
TCPState state; /* Connection state */
int freeOnClose; /* Flag set to free TCB on close. */
int closeReason; /* Reason for closing - TCPERR_ or 0 */
int traceLevel; /* Trace level this connection. */
/*
* Send sequence variables.
*/
struct {
u_int32_t una; /* First unacknowledged sequence number */
u_int32_t nxt; /* Next sequence num to be sent for the first time */
u_int32_t ptr; /* Working transmission pointer */
u_int16_t wnd; /* Other end's offered receive window */
u_int32_t wl1; /* Sequence number used for last window update */
u_int32_t wl2; /* Ack number used for last window update */
} snd;
u_int32_t iss; /* Initial send sequence number */
u_int16_t cwind; /* Congestion window */
u_int16_t ssthresh; /* Slow-start threshold */
u_int32_t resent; /* Count of bytes retransmitted */
/* Receive sequence variables */
struct {
u_int32_t nxt; /* Incoming sequence number expected next */
// u_int16_t wnd; /* Our offered receive window */
short wnd; /* Our offered receive window */
u_int16_t up; /* Receive urgent pointer */
} rcv;
u_int32_t irs; /* Initial receive sequence number */
u_int16_t mss; /* Maximum segment size */
u_int32_t rerecv; /* Count of duplicate bytes received */
int minFreeBufs; /* Minimum free buffers before we'll queue something. */
char backoff; /* Backoff interval */
char flags; /* Control flags */
int listenQOpen; /* Max queued listen connections. */
int listenQHead; /* Head of cloned TCB queue. */
int listenQTail; /* Tail of cloned TCB queue. */
struct TCPCB_s
*listenQ[MAXLISTEN + 1]; /* Circular queue of clones. */
NBufQHdr rcvq; /* Receive queue */
u_int16_t rcvcnt; /* Bytes on receive queue. */
NBuf *rcvBuf; /* Hold one buffer while we trim it. */
NBufQHdr sndq; /* Send queue */
u_int16_t sndcnt; /* Number of unacknowledged sequence numbers on
* send queue. NB: includes SYN and FIN, which don't
* actually appear on sndq!
*/
NBufQHdr *reseq; /* Out-of-order segment queue */
Timer resendTimer; /* Timeout timer */
u_int32 retransTime; /* Retransmission time - 0 for none. */
u_int retransCnt; /* Retransmission count at current wl2. */
u_int32 rttStart; /* Start time for round trip measurement. */
u_int32_t rttseq; /* Sequence number being timed */
u_int32_t srtt; /* Smoothed round trip time, milliseconds */
u_int32_t mdev; /* Mean deviation, milliseconds */
u_long keepAlive; /* Keepalive in Jiffys - 0 for none. */
int keepProbes; /* Number of keepalive probe timeouts. */
u_long keepTime; /* Jiffy time of keepalive timeout. */
Timer keepTimer; /* Keep alive timer */
OS_EVENT *connectSem; /* Semaphore for connect requests. */
OS_EVENT *readSem; /* Semaphore for read function. */
OS_EVENT *writeSem; /* Semaphore for write function. */
OS_EVENT *mutex; /* Mutex for tcpOutput TCB variables. */
TCPIPHdr hdrCache; /* Cached TCP/IP header. */
char *optionsPtr; /* Ptr into TCP options area. */
#if ONETASK_SUPPORT > 0
// When running in a single task, we want to use callback functions...
void (*receiveEvent)(int td, u_long cnt); /* Called when new data has arrived */
void (*transmitEvent)(int td, u_long cnt); /* Called when ready to transmit more data */
void (*stateEvent)(int td, TCPState oldState, TCPState newState); /* Called when connection state changes */
#endif
} TCPCB;
/*
* Shorthand for common fields.
*/
#define ipVersion hdrCache.ipHdr.ip_v
#define ipHdrLen hdrCache.ipHdr.ip_hl
#define ipTOS hdrCache.ipHdr.ip_tos
#define ipLen hdrCache.ipHdr.ip_len /* Host byte order! */
#define ipIdent hdrCache.ipHdr.ip_id /* Host byte order! */
#define ipTTL hdrCache.ipHdr.ip_ttl
#define ipProto hdrCache.ipHdr.ip_p
#define ipSrcAddr hdrCache.ipHdr.ip_src.s_addr /* Network byte order! */
#define ipDstAddr hdrCache.ipHdr.ip_dst.s_addr /* Network byte order! */
#define tcpSrcPort hdrCache.tcpHdr.srcPort /* Network byte order! */
#define tcpDstPort hdrCache.tcpHdr.dstPort /* Network byte order! */
#define tcpSeq hdrCache.tcpHdr.seq /* Network byte order! */
#define tcpAck hdrCache.tcpHdr.ack /* Network byte order! */
#define tcpHdrLen hdrCache.tcpHdr.tcpOff
#define tcpFlags hdrCache.tcpHdr.flags
#define tcpWin hdrCache.tcpHdr.win /* Network byte order! */
#define tcpCkSum hdrCache.tcpHdr.ckSum
#define tcpUrgent hdrCache.tcpHdr.urgent /* Network byte order! */
#define tcpOptions hdrCache.options
/***********************************/
/*** LOCAL FUNCTION DECLARATIONS ***/
/***********************************/
#if ECHO_SUPPORT > 0
static void tcpEcho(void *arg);
#endif
static void resendTimeout(void *arg);
static void keepTimeout(void *arg);
static void setState(TCPCB *tcb, TCPState newState);
static int procInFlags(TCPCB *tcb, TCPHdr *tcpHdr, IPHdr *ipHdr);
static void tcbInit(register TCPCB *tcb);
static void tcbUpdate(register TCPCB *tcb, register TCPHdr *tcpHdr);
static void procSyn(register TCPCB *tcb, TCPHdr *tcpHdr);
static void sendSyn(register TCPCB *tcb);
static void closeSelf(register TCPCB *tcb, int reason);
static u_int32_t newISS(void);
static void tcpOutput(TCPCB *tcb);
static u_int tcbHash(Connection *conn);
static void tcbLink(register TCPCB *tcb);
static void tcbUnlink(register TCPCB *tcb);
static TCPCB * tcbLookup(Connection *conn);
static void tcbFree(TCPCB *tcb);
static void tcpReset(
NBuf *inBuf, /* The input segment. */
IPHdr *ipHdr, /* The IP header in the segment. */
TCPHdr *tcpHdr, /* The TCP header in the segment. */
u_int16_t segLen /* The TCP segment length. */
);
static INT tcpdValid(UINT tcpd);
/*
* trimSeg - Trim segment to fit window.
* Return the new segment length, -1 if segment is unaccepable.
*/
static int trimSeg(
register TCPCB *tcb,
register TCPHdr *tcpHdr,
NBuf *nb,
u_int hdrLen,
u_int16_t segLen
);
/*
* backOff - Backoff function - the subject of much research.
*
* Use binary exponential up to retry #4, and quadratic after that
* This yields the sequence
* 1, 2, 4, 8, 16, 25, 36, 49, 64, 81, 100 ...
*/
#define backOff(n) ((n) <= 4 ? 1 << (n) : (n) * (n))
/*
* Sequence number comparisons.
*/
#define seqWithin(x, low, high) \
(((low) <= (high)) ? ((low) <= (x) && (x) <= (high)) : ((low) >= (x) && (x) >= (high)))
#define seqLT(x, y) ((long)((x) - (y)) < 0)
#define seqLE(x,y) ((long)((x) - (y)) <= 0)
#define seqGT(x,y) ((long)((x) - (y)) > 0)
#define seqGE(x,y) ((long)((x) - (y)) >= 0)
/*
* Determine if the given sequence number is in our receiver window.
* NB: must not be used when window is closed!
*/
#define inWindow(tcb, seq) \
seqWithin((seq), (tcb)->rcv.nxt, (u_int32_t)((tcb)->rcv.nxt + (tcb)->rcv.wnd - 1))
/*
* Put a data in host order into a char array in network order
* and advance the pointer.
*/
#define put32(cp, x) (*((u_int32 *)(cp))++ = ntohl(x))
#define put16(cp, x) (*((u_int16_t *)(cp))++ = ntohs(x))
/*
* Operators for the cloned listen connection queue. These should be
* used within a critical section.
*/
#define listenQLen(tcb) \
((tcb)->listenQHead > (tcb)->listenQTail \
? (tcb)->listenQHead - (tcb)->listenQTail \
: (tcb)->listenQTail - (tcb)->listenQHead)
#define listenQEmpty(tcb) ((tcb)->listenQHead == (tcb)->listenQTail)
#define listenQPush(tcb, ntcb) { \
OS_ENTER_CRITICAL(); \
if (listenQLen((tcb)) < (tcb)->listenQOpen) { \
(tcb)->listenQ[(tcb)->listenQHead] = (ntcb); \
(tcb)->listenQHead = ((tcb)->listenQHead + 1) % MAXLISTEN; \
} \
OS_EXIT_CRITICAL(); \
}
#define listenQPop(tcb, ntcbp) { \
if ((tcb)->listenQHead != (tcb)->listenQTail) { \
*(ntcbp) = (tcb)->listenQ[(tcb)->listenQTail]; \
(tcb)->listenQTail = ((tcb)->listenQTail + 1) % MAXLISTEN; \
} else \
(ntcb) = NULL; \
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -