📄 nettcp.c
字号:
*/
if(tcb->rcvcnt != 0)
OSSemPost(tcb->readSem);
/* process FIN bit (p 75) */
if(tcpHdr->flags & FIN){
tcb->flags |= FORCE; /* Always respond with an ACK */
switch(tcb->state){
case SYN_RECEIVED:
case ESTABLISHED:
tcb->rcv.nxt++;
setState(tcb, CLOSE_WAIT);
break;
case FINWAIT1:
tcb->rcv.nxt++;
if(tcb->sndcnt == 0) {
/* Our FIN has been acked; bypass CLOSING state */
setState(tcb, TIME_WAIT);
tcb->retransTime = OSTimeGet() + MSL2 * TICKSPERSEC;
TCPDEBUG((tcb->traceLevel, TL_TCP, "tcpInput[%d]: Timer %lu in %s",
(int)(tcb - & tcbs[0]),
tcb->retransTime - OSTimeGet(), tcbStates[tcb->state]));
timeoutJiffy(&tcb->resendTimer, tcb->retransTime, resendTimeout, tcb);
} else {
setState(tcb, CLOSING);
}
break;
case FINWAIT2:
tcb->rcv.nxt++;
setState(tcb, TIME_WAIT);
tcb->retransTime = OSTimeGet() + MSL2 * TICKSPERSEC;
TCPDEBUG((tcb->traceLevel, TL_TCP, "tcpInput[%d]: Timer %lu in %s",
(int)(tcb - & tcbs[0]),
tcb->retransTime - OSTimeGet(), tcbStates[tcb->state]));
timeoutJiffy(&tcb->resendTimer, tcb->retransTime, resendTimeout, tcb);
break;
case CLOSE_WAIT:
case CLOSING:
case LAST_ACK:
break; /* Ignore */
case TIME_WAIT: /* p 76 */
tcb->retransTime = OSTimeGet() + MSL2 * TICKSPERSEC;
TCPDEBUG((tcb->traceLevel, TL_TCP, "tcpInput[%d]: Timer %lu in %s",
(int)(tcb - & tcbs[0]),
tcb->retransTime - OSTimeGet(), tcbStates[tcb->state]));
timeoutJiffy(&tcb->resendTimer, tcb->retransTime, resendTimeout, tcb);
break;
}
}
/*
* We're done with this segment header. If there was any data, it was
* dealt with earlier.
*/
nFreeChain(inBuf);
inBuf = NULL;
/*
* Scan the resequencing queue, looking for a segment we can handle,
* and freeing all those that are now obsolete.
*/
while(segLen < 0
&& nQHEAD(tcb->reseq)
&& seqGE(tcb->rcv.nxt, nQHEADSORT(tcb->reseq))) {
nDEQUEUE(tcb->reseq, inBuf);
ipHdr = nBUFTOPTR(inBuf, IPHdr *);
ipHeadLen = ipHdr->ip_hl * 4;
tcpHdr = (TCPHdr *)((char *)ipHdr + ipHeadLen);
tcpHeadLen = tcpHdr->tcpOff * 4;
segLen = ipHdr->ip_len - ipHeadLen - tcpHeadLen;
if ((segLen = trimSeg(tcb, tcpHdr, inBuf, ipHeadLen + tcpHeadLen, segLen)) < 0) {
nFreeChain(inBuf);
inBuf = NULL;
}
}
}
tcpOutput(tcb); /* Send any necessary ack */
}
/*
* Get and set parameters for the given connection.
* Return 0 on success, an error code on failure.
*/
int tcpIOCtl(u_int td, int cmd, void *arg)
{
TCPCB *tcb = &tcbs[td];
int st = 0;
if (td >= MAXTCP || tcb->prev == tcb)
st = TCPERR_PARAM;
else {
switch(cmd) {
case TCPCTLG_UPSTATUS: /* Get the TCP up status. */
if (arg)
*(int *)arg = (tcb->state == ESTABLISHED);
else
st = TCPERR_PARAM;
break;
case TCPCTLG_RCVCNT: /* Get the bytes in the receive queue. */
if (arg)
*(int *)arg = (int)tcb->rcvcnt;
else
st = TCPERR_PARAM;
break;
case TCPCTLG_KEEPALIVE: /* Get the TCP keepalive period. */
if (arg)
*(int *)arg = (int)(tcb->keepAlive / TICKSPERSEC);
else
st = TCPERR_PARAM;
break;
case TCPCTLS_KEEPALIVE: /* Set the TCP keepalive period. */
if (arg)
tcb->keepAlive = (u_long)(*(int *)arg) * TICKSPERSEC;
else
st = TCPERR_PARAM;
break;
case TCPCTLG_TRACELEVEL: /* Get the TCP trace level. */
if (arg)
*(int *)arg = tcb->traceLevel;
else
st = TCPERR_PARAM;
break;
case TCPCTLS_TRACELEVEL: /* Set the TCP trace level. */
if (arg)
tcb->traceLevel = *(int *)arg;
else
st = TCPERR_PARAM;
break;
default:
st = TCPERR_PARAM;
break;
}
}
return st;
}
/**********************************/
/*** LOCAL FUNCTION DEFINITIONS ***/
/**********************************/
#if ECHO_SUPPORT > 0
/*
* tcpEcho - The TCP echo server. This will handle a single TCP echo
* connection at a time.
*/
#pragma argsused
static void tcpEcho(void *arg)
{
#define TCPECHOBUFSZ 50
struct sockaddr_in localAddr, peerAddr;
int td, ntd, inCnt, outCnt;
char tBuf[TCPECHOBUFSZ];
localAddr.ipAddr = localHost;
localAddr.sin_port = TCPPORT_ECHO;
if ((td = tcpOpen()) < 0) {
ECHODEBUG((LOG_ERR, TL_ECHO, "tcpEcho: Unable to open a TCB (%d)", td));
} else if ((inCnt = 10) != 0
&& (outCnt = tcpIOCtl(td, TCPCTLS_KEEPALIVE, &inCnt)) < 0) {
ECHODEBUG((LOG_ERR, TL_ECHO, "tcpEcho: Error %d setting keep alive", outCnt));
} else for (;;) {
if ((inCnt = tcpBind(td, &localAddr)) < 0) {
ECHODEBUG((LOG_ERR, TL_ECHO, "tcpEcho: Error %d on bind", inCnt));
break;
} else if ((ntd = tcpAccept(td, &peerAddr)) < 0) {
ECHODEBUG((LOG_ERR, TL_ECHO, "tcpEcho: Error %d on accept", ntd));
break;
} else {
ECHODEBUG((LOG_INFO, TL_ECHO, "tcpEcho: connect %s:%u",
ip_ntoa(htonl(peerAddr.sin_addr.s_addr)),
peerAddr.sin_port));
while ((inCnt = tcpRead(ntd, tBuf, TCPECHOBUFSZ)) >= 0) {
if ((outCnt = tcpWrite(ntd, tBuf, inCnt)) < inCnt) {
if (outCnt != TCPERR_EOF) {
ECHODEBUG((LOG_ERR, TL_ECHO, "tcpEcho: TCP write err %d", outCnt));
}
break;
}
}
if (inCnt != TCPERR_EOF) {
ECHODEBUG((LOG_ERR, TL_ECHO, "tcpEcho: TCP read err %d", inCnt));
}
}
if (ntd >= 0) {
ECHODEBUG((LOG_INFO, TL_ECHO, "tcpEcho: disconnect %d %s:%u", ntd,
ip_ntoa(htonl(peerAddr.sin_addr.s_addr)),
peerAddr.sin_port));
if ((inCnt = tcpDisconnect(ntd)) < 0) {
ECHODEBUG((LOG_ERR, TL_ECHO, "tcpEcho: Disconnect err %d", inCnt));
break;
} else if ((inCnt = tcpWait(ntd)) < 0) {
ECHODEBUG((LOG_ERR, TL_ECHO, "tcpEcho: Close wait err %d", inCnt));
break;
}
}
}
if (td >= 0 && td != ntd) {
ECHODEBUG((LOG_INFO, TL_ECHO, "tcpEcho: disconnect %d %s:%u", td,
ip_ntoa(htonl(peerAddr.sin_addr.s_addr)),
peerAddr.sin_port));
if ((inCnt = tcpDisconnect(td)) < 0) {
ECHODEBUG((LOG_ERR, TL_ECHO, "tcpEcho: Disconnect err %d", inCnt));
}
}
OSTaskDel(OS_PRIO_SELF);
}
#endif
/*
* resendTimeout - The function invoked when the resend timer expires.
*/
static void resendTimeout(void *arg)
{
register TCPCB *tcb = (TCPCB *)arg;
if(tcb == NULL) {
TCPDEBUG((LOG_ERR, TL_TCP, "resendTimeout: Null arg"));
return;
} else if (tcb->state == CLOSED) {
TCPDEBUG((LOG_ERR, TL_TCP, "resendTimeout: Connection closed"));
return;
}
/* Make sure the timer has stopped (we might have been kicked) */
timerClear(&tcb->resendTimer);
TCPDEBUG((tcb->traceLevel, TL_TCP, "resendTimeout[%d]: state=%s",
(int)(tcb - &tcbs[0]),
tcbStates[tcb->state]));
/*
* Check if the timer was set (i.e. there is unacknowledged output
* or we're in TIME_WAIT or in FINWAIT2).
*/
if (tcb->snd.una != tcb->snd.nxt
|| tcb->state == TIME_WAIT || (tcb->state == FINWAIT2)) {
/* If the timer hasn't expired, reset it. */
if (diffTime(tcb->retransTime) > 0) {
TCPDEBUG((tcb->traceLevel, TL_TCP,
"resendTimeout[%d]: Timer reset for %lu in %s",
(int)(tcb - & tcbs[0]),
tcb->retransTime - OSTimeGet(), tcbStates[tcb->state]));
timeoutJiffy(&tcb->resendTimer, tcb->retransTime, resendTimeout, tcb);
/* Otherwise resend the last unacknowledged segment. */
} else {
/* CRITICAL - Prevent tcpInput from updating retransCnt at same time. */
OS_ENTER_CRITICAL();
/*
* If it's the 2MSL timer that has expired or if we've timed out in
* FINWAIT2 close the connection without error.
*/
if (tcb->state == TIME_WAIT
|| (tcb->state == FINWAIT2 && tcb->freeOnClose)) {
OS_EXIT_CRITICAL();
closeSelf(tcb, 0);
/* Check if we've timed out on retransmissions. */
} else if (tcb->retransCnt++ >= MAXRETRANS) {
OS_EXIT_CRITICAL();
closeSelf(tcb, TCPERR_TIMEOUT);
/* Normal retransmission timeout - reset pointers and invoke tcpOutput(). */
} else {
OS_EXIT_CRITICAL();
/*
* Wait for tcpOutput() to clear critical section before setting
* up for resend.
*/
OSSemPend(tcb->mutex, 0);
tcb->flags |= RETRAN; /* Indicate > 1 transmission */
tcb->backoff++;
tcb->snd.ptr = tcb->snd.una;
/* Reduce slowstart threshold to half current window */
tcb->ssthresh = tcb->cwind / 2;
tcb->ssthresh = MAX(tcb->ssthresh, tcb->mss);
/* Shrink congestion window to 1 packet */
tcb->cwind = tcb->mss;
OSSemPost(tcb->mutex);
tcpOutput(tcb);
}
}
}
}
/*
* keepTimeout - The function invoked when the keep alive timer expires.
*/
static void keepTimeout(void *arg)
{
register TCPCB *tcb = (TCPCB *)arg;
/* Check if we're doing keep alives. */
if (tcb->keepAlive
&& (tcb->state == ESTABLISHED
|| tcb->state == CLOSE_WAIT)) {
/*
* If we've received something less recently than the keep alive time,
* then reset the timer.
*/
if ((long)(OSTimeGet() - tcb->keepTime) < 0) {
TCPDEBUG((tcb->traceLevel, TL_TCP,
"keepTimeout[%d]: Keepalive reset for %lu in %s",
(int)(tcb - & tcbs[0]),
tcb->keepTime - OSTimeGet(),
tcbStates[tcb->state]));
timeoutJiffy(
&tcb->keepTimer,
tcb->keepTime,
keepTimeout,
tcb);
/*
* If we've exceeded our maximum keep alive timeouts, close the
* connection.
*/
} else if (tcb->keepProbes++ >= MAXKEEPTIMES) {
TCPDEBUG((LOG_WARNING, TL_TCP,
"keepTimeout[%d]: Keepalive expired - closing",
(int)(tcb - & tcbs[0])));
closeSelf(tcb, TCPERR_TIMEOUT);
/*
* Reset the timer and send a keep alive probe.
*/
} else {
tcb->keepTime = OSTimeGet() + tcb->keepAlive;
TCPDEBUG((tcb->traceLevel, TL_TCP,
"keepTimeout[%d]: Keepalive set for %lu in %s",
(int)(tcb - & tcbs[0]),
tcb->keepTime - OSTimeGet(),
tcbStates[tcb->state]));
timeoutJiffy(
&tcb->keepTimer,
tcb->keepTime,
keepTimeout,
tcb);
OS_ENTER_CRITICAL();
tcb->flags |= FORCE | KEEPALIVE;
OS_EXIT_CRITICAL();
tcpOutput(tcb);
}
}
}
static void setState(TCPCB *tcb, TCPState newState)
{
register TCPState oldState;
OS_ENTER_CRITICAL();
if ((oldState = tcb->state) != newState) {
tcb->state = newState;
OS_EXIT_CRITICAL();
TCPDEBUG((tcb->traceLevel, TL_TCP, "setState[%d]: %s from %s",
(int)(tcb - &tcbs[0]),
tcbStates[newState], tcbStates[oldState]));
switch(newState){
case FINWAIT2:
/*
* Limit the time that we'll wait for the other end to close.
* XXX To support a half close, you'll need to test for something
* here.
*/
tcb->retransTime = OSTimeGet() + MAXFINWAIT2 * TICKSPERSEC;
TCPDEBUG((tcb->traceLevel + 1, TL_TCP, "setState[%d]: Timer %lu in %s",
(int)(tcb - & tcbs[0]),
tcb->retransTime - OSTimeGet(), tcbStates[tcb->state]));
timeoutJiffy(&tcb->resendTimer, tcb->retransTime, resendTimeout, tcb);
/* FALL THROUGH... */
case CLOSED:
case ESTABLISHED:
case LISTEN:
case SYN_SENT:
case SYN_RECEIVED:
case FINWAIT1:
case CLOSE_WAIT:
case CLOSING:
case LAST_ACK:
case TIME_WAIT:
/*
* Inform user processes that the state has changed. This leaves it
* up to the individual processes to decide if the new state is
* significant and if not, then to pend again. Unfortunately
* uC/OS cannot tell us how many processes are waiting on any of
* these so we assume that there is at most one and if there is
* not one, it won't hurt doing the extra posts.
*/
OSSemPost(tcb->connectSem);
OSSemPost(tcb->readSem);
OSSemPost(tcb->writeSem);
break;
default:
trace(LOG_ERR, "setState: Invalid new state %d old %d",
newState, oldState);
tcb->state = oldState;
break;
}
/* If we're closed then free or unlink the control block. */
if (newState == CLOSED) {
if (tcb->freeOnClose)
tcbFree(tcb);
else
tcbUnlink(tcb);
}
} else {
OS_EXIT_CRITICAL();
}
}
/*
* Process an incoming acknowledgement and window indication.
* From RFC page 70.
* Return zero if successful, ACKDROP if the segment should
* be dropped, ACKRESET if the segment should be rejected,
* and ACKCLOSE if the connection is closed.
*/
static int procInFlags(TCPCB *tcb, TCPHdr *tcpHdr, IPHdr *ipHdr)
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -