📄 nettcp.c
字号:
"ACK+P+R", /* 28 = 16 + 8 + 4 */
"A+P+R+F", /* 29 = 16 + 8 + 4 + 1 */
"A+P+R+S", /* 30 = 16 + 8 + 4 + 2 */
"A+P+R+S+F" /* 31 = 16 + 8 + 4 + 2 + 1 */
};
char tcpEchoStack[STACK_SIZE]; /* The TCP echo task stack. */
/***********************************/
/*** PUBLIC FUNCTION DEFINITIONS ***/
/***********************************/
/*
* Initialize the TCP subsystem.
*/
void tcpInit(void)
{
int i;
/* The TCB free list. */
memset(tcbs, 0, sizeof(tcbs));
topTcpCB = &tcbs[0];
for (i = 0; i < MAXTCP; i++) {
tcbs[i].next = &tcbs[i + 1];
/* Prev referencing self indicates that it's on the free list. */
tcbs[i].prev = &tcbs[i];
timerCreate(&tcbs[i].resendTimer);
timerCreate(&tcbs[i].keepTimer);
tcbs[i].state = CLOSED;
}
tcbs[MAXTCP - 1].next = NULL;
/* The TCB hash table. */
memset(&tcbTbl, 0, sizeof(tcbTbl));
/* The TCP stats. */
#if STATS_SUPPORT > 0
memset(&tcpStats, 0, sizeof(tcpStats));
tcpStats.headLine.fmtStr = "\t\tTCP STATISTICS\r\n";
tcpStats.curFree.fmtStr = "\tCURRENT FREE: %5lu\r\n";
tcpStats.curFree.val = MAXTCP;
tcpStats.minFree.fmtStr = "\tMINIMUM FREE: %5lu\r\n";
tcpStats.minFree.val = MAXTCP;
tcpStats.runt.fmtStr = "\tRUNT HEADERS: %5lu\r\n";
tcpStats.checksum.fmtStr = "\tBAD CHECKSUM: %5lu\r\n";
tcpStats.conout.fmtStr = "\tOUT CONNECTS: %5lu\r\n";
tcpStats.conin.fmtStr = "\tIN CONNECTS : %5lu\r\n";
tcpStats.resetOut.fmtStr = "\tRESETS SENT : %5lu\r\n";
tcpStats.resetIn.fmtStr = "\tRESETS REC'D: %5lu\r\n";
#endif
/* The new sequence number offset. */
newISNOffset = magic();
/* Start the TCP echo server. */
OSTaskCreate(tcpEcho, NULL, tcpEchoStack + STACK_SIZE, PRI_ECHO);
}
/*
* Return a new TCP descriptor on success or an error code (negative) on
* failure.
*/
int tcpOpen(void)
{
int st;
TCPCB *tcb;
OS_ENTER_CRITICAL();
if ((tcb = topTcpCB) != NULL) {
topTcpCB = topTcpCB->next;
STATS(if (--tcpStats.curFree.val < tcpStats.minFree.val)
tcpStats.minFree.val = tcpStats.curFree.val;)
}
OS_EXIT_CRITICAL();
if (!tcb)
st = TCPERR_ALLOC;
else {
st = (int)(tcb - &tcbs[0]);
tcb->next = tcb; /* Self ref => unlinked. */
tcb->prev = NULL; /* Always NULL when neither free nor linked. */
tcb->freeOnClose = 0;
tcb->traceLevel = LOG_INFO;
tcb->keepAlive = 0;
tcb->keepProbes = 0;
/* Grab semaphores. */
if (!tcb->connectSem)
if ((tcb->connectSem = OSSemCreate(0)) == NULL)
st = TCPERR_ALLOC;
if (!tcb->readSem)
if ((tcb->readSem = OSSemCreate(0)) == NULL)
st = TCPERR_ALLOC;
if (!tcb->writeSem)
if ((tcb->writeSem = OSSemCreate(0)) == NULL)
st = TCPERR_ALLOC;
if (!tcb->mutex)
if ((tcb->mutex = OSSemCreate(1)) == NULL)
st = TCPERR_ALLOC;
TCPDEBUG((tcb->traceLevel, TL_TCP, "tcpOpen[%d]: Opened", st));
}
return st;
}
/*
* Close a TCP connection and release the descriptor.
* Any outstanding packets in the queues are dropped.
* Return 0 on success when the peer acknowledges our message
* or an error code on failure.
*/
int tcpClose(u_int td)
{
int st = 0;
TCPCB *tcb = &tcbs[td];
/* Protect from race on tcb->state. */
OS_ENTER_CRITICAL();
if (td >= MAXTCP || tcb->prev == tcb) {
OS_EXIT_CRITICAL();
st = TCPERR_PARAM;
} else if (tcb->state == CLOSED) {
OS_EXIT_CRITICAL();
TCPDEBUG((tcb->traceLevel, TL_TCP, "tcpClose[%d]: Freeing closed", td));
tcbFree(tcb);
} else {
/*
* Initiate a half-close on our side by sending a FIN. The
* freeOnClose flag is set so the TCB will be freed when the
* state reaches CLOSED. Note that a timer will limit the
* time that we wait in FINWAIT2.
*/
tcb->freeOnClose = !0;
OS_EXIT_CRITICAL();
st = tcpDisconnect(td);
TCPDEBUG((tcb->traceLevel, TL_TCP, "tcpClose[%d]: Closed", td));
}
return st;
}
/*
* Bind an IP address and port number in the sockaddr structure as our
* address on a TCP connection.
* Note: The IP address must be zero (wild) or equal to localHost since that
* is all that ipDispatch() will recognize. You can only bind a CLOSED
* connection.
* Return 0 on success, an error code on failure.
*/
int tcpBind(u_int td, struct sockaddr_in *myAddr)
{
int st = 0;
TCPCB *tcb = &tcbs[td];
if (td >= MAXTCP || tcb->prev == tcb || !myAddr)
st = TCPERR_PARAM;
else if (myAddr->ipAddr != 0 && myAddr->ipAddr != localHost)
st = TCPERR_INVADDR;
else if (tcb->state != CLOSED)
st = TCPERR_CONNECT; /* Can't bind an active connection. */
else {
tcb->ipSrcAddr = htonl(myAddr->ipAddr);
tcb->tcpSrcPort = htons(myAddr->sin_port);
TCPDEBUG((tcb->traceLevel, TL_TCP, "tcpBind[%d]: to %s:%u mss %d",
(int)(tcb - &tcbs[0]),
ip_ntoa(tcb->ipSrcAddr), ntohs(tcb->tcpSrcPort),
tcb->mss));
}
return st;
}
/*
* Establish a connection with a remote host. Unless tcpBind() has been called,
* the local IP address and port number are generated automatically.
* Return 0 on success, an error code on failure.
*/
int tcpConnectJiffy(u_int td, const struct sockaddr_in *remoteAddr, u_char tos, u_int timeout)
{
int st = 0;
TCPCB *tcb = &tcbs[td];
u_long abortTime;
long dTime = timeout;
if (timeout)
abortTime = jiffyTime() + timeout;
if (td >= MAXTCP || tcb->prev == tcb || !remoteAddr)
st = TCPERR_PARAM;
else if (remoteAddr->ipAddr == 0 || remoteAddr->sin_port == 0)
st = TCPERR_INVADDR;
else if (tcb->ipSrcAddr == 0 && localHost == 0)
st = TCPERR_CONFIG;
else if (tcb->state != CLOSED)
st = TCPERR_CONNECT; /* Already connected! */
else {
tcbInit(tcb);
tcb->ipTOS = tos;
if (tcb->ipSrcAddr == 0)
tcb->ipSrcAddr = htonl(localHost);
if (tcb->tcpSrcPort == 0) {
OS_ENTER_CRITICAL();
tcb->tcpSrcPort = htons(tcpFreePort++);
OS_EXIT_CRITICAL();
}
tcb->ipDstAddr = htonl(remoteAddr->ipAddr);
tcb->tcpDstPort = htons(remoteAddr->sin_port);
/* Initialize connection parameters. */
tcb->rcv.wnd = TCP_DEFWND;
tcb->mss = ipMTU(tcb->ipDstAddr) - sizeof(IPHdr) - sizeof(TCPHdr);
tcb->mss = MAX(tcb->mss, TCP_MINMSS);
tcb->minFreeBufs = ((tcb->mss + NBUFSZ) / NBUFSZ);
/*
* Load the connection structure and link the TCB into the connection
* table so that tcpInput can find it.
*/
tcb->conn.remoteIPAddr = tcb->ipDstAddr;
tcb->conn.remotePort = tcb->tcpDstPort;
tcb->conn.localIPAddr = tcb->ipSrcAddr;
tcb->conn.localPort = tcb->tcpSrcPort;
tcbLink(tcb);
TCPDEBUG((tcb->traceLevel, TL_TCP, "tcpConnect[%d]: to %s:%u mss %d",
(int)(tcb - &tcbs[0]),
ip_ntoa(tcb->ipDstAddr), ntohs(tcb->tcpDstPort),
tcb->mss));
/* Send SYN, go into SYN_SENT state */
tcb->flags |= ACTIVE;
sendSyn(tcb);
setState(tcb, SYN_SENT);
tcpOutput(tcb);
STATS(tcpStats.conout.val++;)
/* Wait for connection or failure. */
while(tcb->state != ESTABLISHED && !st) {
if (tcb->state == CLOSED) {
/*
* Post the connect semaphore in case another task was also
* waiting on it. Unlikely for connect but a single extra
* post costs little and improves robustness.
*/
OSSemPost(tcb->connectSem);
if (tcb->closeReason)
st = tcb->closeReason;
else
st = TCPERR_EOF;
} else if (!timeout || (dTime = diffJTime(abortTime)) > 0) {
OSSemPend(tcb->connectSem, (UINT)dTime);
} else { /* Abort on timeout. */
closeSelf(tcb, TCPERR_TIMEOUT);
tcbUnlink(tcb);
}
}
}
TCPDEBUG((tcb->traceLevel, TL_TCP, "tcpConnect[%d]: %s",
(int)(tcb - &tcbs[0]), tcbStates[tcb->state]));
return st;
}
/*
* tcpDisconnect - Tell the peer that we will not be sending any more data
* (i.e. perform a half close on a connection). tcpRead() will then
* wait until the connection closes.
* Return 0 when the peer acknowledges our message or an error code on
* failure.
*/
int tcpDisconnect(u_int td)
{
int st = 0;
TCPCB *tcb = &tcbs[td];
TCPDEBUG((tcb->traceLevel, TL_TCP, "tcpDisconnect[%d]: state %s",
(int)(tcb - &tcbs[0]), tcbStates[tcb->state]));
if (td >= MAXTCP || tcb->prev == tcb)
st = TCPERR_PARAM;
else {
switch(tcb->state){
case LISTEN:
case SYN_SENT:
/*
* We haven't established a connection yet so we can just
* close this.
*/
closeSelf(tcb, 0);
break;
case SYN_RECEIVED:
case ESTABLISHED:
/*
* Initiate a half-close on our side by sending a FIN.
* Our FIN is indicated by setting sndcnt to the number of bytes in
* the output queue + 1.
*/
tcb->sndcnt++;
tcb->snd.nxt++;
setState(tcb, FINWAIT1);
tcpOutput(tcb);
break;
case CLOSE_WAIT:
/*
* The peer has initiated a half-close. We'll ACK it and complete
* the close by sending a FIN and waiting for it to be acknowledged.
* Our FIN is indicated by setting sndcnt to the number of bytes in
* the output queue + 1.
*/
tcb->sndcnt++;
tcb->snd.nxt++;
setState(tcb, LAST_ACK);
tcpOutput(tcb);
break;
case FINWAIT1:
case FINWAIT2:
case LAST_ACK:
case CLOSING:
case TIME_WAIT:
/* Do nothing - we're already closing! */
break;
case CLOSED:
/* Nothing to do! */
break;
}
}
return st;
}
/*
* Set the number of backLog connections which will be queued to be picked up
* by calls to accept. Without this call, no connection will be opened until
* tcpAccept() or tcpConnect() is called.
* Return the actual size of the queue on success, an error code on failure.
*/
int tcpListen(u_int td, int backLog)
{
int st = 0;
TCPCB *tcb = &tcbs[td];
if (td >= MAXTCP || tcb->prev == tcb)
st = TCPERR_PARAM;
else if (tcb->tcpSrcPort == 0)
st = TCPERR_CONFIG;
else {
switch(tcb->state){
case CLOSED:
tcbInit(tcb);
tcb->conn.localIPAddr = tcb->ipSrcAddr;
tcb->conn.localPort = tcb->tcpSrcPort;
/* XXX Do we want 0 or left over address? */
tcb->conn.remoteIPAddr = 0;
tcb->conn.remotePort = 0;
tcbLink(tcb);
setState(tcb, LISTEN);
/* Fall through... */
case LISTEN:
st = tcb->listenQOpen = MIN(backLog, MAXLISTEN);
tcb->flags |= CLONE;
TCPDEBUG((tcb->traceLevel, TL_TCP, "tcpListen[%d]: %s:%u #%u",
(int)(tcb - &tcbs[0]),
ip_ntoa(tcb->ipSrcAddr), tcb->tcpSrcPort,
tcb->listenQOpen));
break;
case SYN_SENT:
case SYN_RECEIVED:
case ESTABLISHED:
case CLOSE_WAIT:
case FINWAIT1:
case FINWAIT2:
case CLOSING:
case LAST_ACK:
case TIME_WAIT:
st = TCPERR_CONNECT;
break;
}
}
TCPDEBUG((tcb->traceLevel, TL_TCP, "tcpListen[%d]: %s => %d",
(int)(tcb - &tcbs[0]), tcbStates[tcb->state], st));
return st;
}
/*
* Pick up a connection opened by a remote host. tcpBind() must be used to
* specify the local address (possibly zero) and port number (non-zero) for
* the connection. Unless tcpListen() has been called, no connection will
* be accepted until this is called.
* Return a new TCP descriptor for the opened connection on success, an
* error code on failure. The peer's IP and port values are returned
* in peerAddr.
*/
#pragma argsused
int tcpAcceptJiffy(u_int td, struct sockaddr_in *peerAddr, u_int timeout)
{
int st = 0;
TCPCB *ntcb = NULL;
TCPCB *tcb = &tcbs[td];
u_long abortTime;
long dTime = timeout;
TCPDEBUG((tcb->traceLevel, TL_TCP, "tcpAccept[%d]: accepting %s:%u",
(int)(tcb - &tcbs[0]),
ip_ntoa(tcb->ipSrcAddr), ntohs(tcb->tcpSrcPort)));
if (timeout)
abortTime = jiffyTime() + timeout;
if (td >= MAXTCP || tcb->prev == tcb)
st = TCPERR_PARAM;
else if (tcb->tcpSrcPort == 0)
st = TCPERR_CONFIG;
else {
switch(tcb->state){
case CLOSED:
tcbInit(tcb);
tcb->conn.localIPAddr = tcb->ipSrcAddr;
tcb->conn.localPort = tcb->tcpSrcPort;
/* XXX Do we want 0 or whatever's in peerAddr? */
tcb->conn.remoteIPAddr = 0;
tcb->conn.remotePort = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -