?? 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
*
* 98-02-02 Guy Lancaster <glanca@gesn.com>, Global Election Systems Inc.
* Original based on ka9q and BSD codes.
******************************************************************************
* 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 "typedefs.h"
#include "avconfig.h"
#include <string.h>
#include "stdio.h" // Required for debug.h
#include "timer.h"
#include "avos.h"
#include "netbuf.h"
#include "devio.h"
#include "net.h"
#include "netmagic.h"
#include "netip.h"
#include "netiphdr.h"
#include "nettcp.h"
#include "nettcphd.h"
#include "debug.h"
/*************************/
/*** LOCAL DEFINITIONS ***/
/*************************/
/* Configuration */
#define MAXTCP 6 /* 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 connection states.
*/
typedef enum {
CLOSED = 0, /* Must be 0 */
LISTEN = 1,
SYN_SENT= 2,
SYN_RECEIVED = 3,
ESTABLISHED = 4,
FINWAIT1 = 5,
FINWAIT2 = 6,
CLOSE_WAIT = 7,
CLOSING = 8,
LAST_ACK = 9,
TIME_WAIT = 10
} TCPState;
/*
* 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 {
int32 una; /* First unacknowledged sequence number */
int32 nxt; /* Next sequence num to be sent for the first time */
int32 ptr; /* Working transmission pointer */
int16 wnd; /* Other end's offered receive window */
int32 wl1; /* Sequence number used for last window update */
int32 wl2; /* Ack number used for last window update */
} snd;
int32 iss; /* Initial send sequence number */
int16 cwind; /* Congestion window */
int16 ssthresh; /* Slow-start threshold */
int32 resent; /* Count of bytes retransmitted */
/* Receive sequence variables */
struct {
int32 nxt; /* Incoming sequence number expected next */
int16 wnd; /* Our offered receive window */
int16 up; /* Receive urgent pointer */
} rcv;
int32 irs; /* Initial receive sequence number */
int16 mss; /* Maximum segment size */
int32 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 */
int16 rcvcnt; /* Bytes on receive queue. */
NBuf *rcvBuf; /* Hold one buffer while we trim it. */
NBufQHdr sndq; /* Send queue */
int16 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. */
int32 rttseq; /* Sequence number being timed */
int32 srtt; /* Smoothed round trip time, milliseconds */
int32 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. */
} 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 ***/
/***********************************/
static void tcpEcho(void *arg);
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 int32 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. */
int16 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,
int16 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, (int32)((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 *)(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; \
}
/******************************/
/*** PUBLIC DATA STRUCTURES ***/
/******************************/
const DevDef tcpdef = {
tcpdValid,
tcpdValid,
tcpRead,
tcpWrite,
NULL,
NULL,
tcpIOCtl
};
#if STATS_SUPPORT > 0
TCPStats tcpStats;
#endif
/*****************************/
/*** LOCAL DATA STRUCTURES ***/
/*****************************/
/*
* TCP Control block free list.
*/
TCPCB tcbs[MAXTCP];
TCPCB *topTcpCB; /* Ptr to top TCB on free list. */
TCPCB *tcbTbl[NTCB]; /* Hash table for lookup. */
u_int16 tcpFreePort = TCP_DEFPORT; /* Initial local port. */
int32 newISNOffset; /* Offset for the next sequence number. */
/* TCB state labels for debugging. */
char *tcbStates[] = {
"CLOSED",
"LISTEN",
"SYN_SENT",
"SYN_RECEIVED",
"ESTABLISHED",
"FINWAIT1",
"FINWAIT2",
"CLOSE_WAIT",
"CLOSING",
"LAST_ACK",
"TIME_WAIT"
};
/* TCP Header Flag labels. */
#define TCPFLAGLABELMASK 0x1F /* We don't display URGENT. */
const char *tcpFlagLabel[] = {
"NONE", /* 0 */
"FIN", /* 1 */
"SYN", /* 2 */
"SYN+FIN", /* 3 = 2 + 1 */
"RST", /* 4 */
"RST+FIN", /* 5 = 4 + 1 */
"RST+SYN", /* 6 = 4 + 2 */
"RST+S+F", /* 7 = 4 + 2 + 1 */
"PUSH", /* 8 */
"PUSH+FIN", /* 9 = 8 + 1 */
"PUSH+SYN", /* 10 = 8 + 2 */
"PUSH+S+F", /* 11 = 8 + 2 + 1 */
"PUSH+RST", /* 12 = 8 + 4 */
"PUSH+R+F", /* 13 = 8 + 4 + 1 */
"PUSH+R+S", /* 14 = 8 + 4 + 2 */
"PUSH+R+S+F", /* 15 = 8 + 4 + 2 + 1 */
"ACK", /* 16 */
"ACK+FIN", /* 17 = 16 + 1 */
"ACK+SYN", /* 18 = 16 + 2 */
"ACK+S+F", /* 19 = 16 + 2 + 1 */
"ACK+RST", /* 20 = 16 + 4 */
"ACK+R+F", /* 21 = 16 + 4 + 1 */
"ACK+R+S", /* 22 = 16 + 4 + 2 */
"ACK+R+S+F", /* 23 = 16 + 4 + 2 + 1 */
"ACK+PUSH", /* 24 = 16 + 8 */
"ACK+P+F", /* 25 = 16 + 8 + 1 */
"ACK+P+S", /* 26 = 16 + 8 + 2 */
"ACK+P+S+F", /* 27 = 16 + 8 + 2 + 1 */
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -