📄 nettcp.c
字号:
tcbLink(tcb);
setState(tcb, LISTEN);
/* Fall through... */
case LISTEN:
if (tcb->flags & CLONE) {
OS_ENTER_CRITICAL();
while(!ntcb && !st) {
if (tcb->state != LISTEN) {
st = TCPERR_CONNECT;
} else if (!listenQEmpty(tcb)) {
listenQPop(tcb, &ntcb);
} else {
OS_EXIT_CRITICAL();
if (!timeout || (dTime = diffJTime(abortTime)) > 0)
OSSemPend(tcb->connectSem, (UINT)dTime);
else
st = TCPERR_TIMEOUT; /* Abort on timeout. */
OS_ENTER_CRITICAL();
}
}
OS_EXIT_CRITICAL();
} else {
while(CLOSED < tcb->state && tcb->state < ESTABLISHED) {
if (!timeout || (dTime = diffJTime(abortTime)) > 0)
OSSemPend(tcb->connectSem, (UINT)dTime);
else {
closeSelf(tcb, TCPERR_TIMEOUT);
tcbUnlink(tcb);
}
}
if (tcb->state == ESTABLISHED)
ntcb = tcb;
else if (tcb->closeReason)
st = tcb->closeReason;
else
st = TCPERR_CONNECT;
}
if (!st) {
ntcb->flags &= ~CLONE;
peerAddr->ipAddr = ntohl(tcb->ipDstAddr);
peerAddr->sin_port = ntohs(tcb->tcpDstPort);
st = (int)(ntcb - &tcbs[0]);
TCPDEBUG((tcb->traceLevel, TL_TCP, "tcpAccept[%d]: %s:%u",
(int)(tcb - &tcbs[0]),
ip_ntoa(tcb->ipDstAddr), ntohs(tcb->tcpDstPort)));
} else {
TCPDEBUG((tcb->traceLevel, TL_TCP, "tcpAccept[%d]: at %s => %d",
(int)(tcb - &tcbs[0]), tcbStates[tcb->state], st));
}
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;
}
}
return st;
}
/*
* Read from a connected TCP connection. If a timeout is non-zero, we block
* until the requested data is received or the timeout expires. Otherwise
* we block only until at least one byte has been received or an error
* occurs.
* Note: Ideally we would return less than len bytes only if a PUSH flag
* was received however this is yet to be implemented.
* Return the number of bytes read on success, an error code on failure.
*/
int tcpRead(u_int td, void *s, u_int len)
{
return tcpReadJiffy(td, s, len, 0);
}
int tcpReadJiffy(u_int td, void *s1, u_int len, u_int timeout)
{
char *s = (char*)s1;
TCPCB *tcb = &tcbs[td];
u_long abortTime;
long dTime = timeout;
u_int i;
int st = 0;
if (timeout)
abortTime = jiffyTime() + timeout;
if (td >= MAXTCP || tcb->prev == tcb)
st = TCPERR_PARAM;
else if (tcb->state == CLOSED
|| tcb->ipSrcAddr == 0
|| tcb->tcpSrcPort == 0
|| tcb->ipDstAddr == 0
|| tcb->tcpDstPort == 0)
st = TCPERR_CONNECT;
/*
* Loop here until either we have received something, hit a snag, or had
* our connection closed.
*/
else while (len) {
/* If there's something in the receive buffer, start with that. */
if (tcb->rcvBuf) {
i = nTrim(s, &tcb->rcvBuf, len);
TCPDEBUG((tcb->traceLevel + 1, TL_TCP, "tcpRead[%d]: %u:%.*H",
td, i, min(60, i * 2), s));
st += i;
len -= i;
s += i;
OS_ENTER_CRITICAL();
tcb->rcvcnt -= i;
OS_EXIT_CRITICAL();
/*
* If there's something in the receive queue, dequeue the next segment.
* If successful, adjust our receive window.
*/
} else if (tcb->rcvcnt != 0) {
nDEQUEUE(&tcb->rcvq, tcb->rcvBuf);
if (tcb->rcvBuf) {
OS_ENTER_CRITICAL();
i = tcb->rcv.wnd;
/*
* Since we queue buffer chains and not characters, the receive
* window is adjusted by multiples of buffer lengths.
*
* XXX Here we assume that normally each buffer chain is of
* length 1. If this is not the case, it may be better to
* adjust the buffer size rather than complicate this. Only
* if you want to support greatly varying segment sizes would
* it be worth tracking the number of buffers in each chain.
*/
if ((tcb->rcv.wnd += NBUFSZ) > TCP_DEFWND)
tcb->rcv.wnd = TCP_DEFWND;
/* Do a window update if it was closed. */
if (i == 0) {
tcb->flags |= FORCE;
OS_EXIT_CRITICAL();
tcpOutput(tcb);
} else {
OS_EXIT_CRITICAL();
}
}
/*
* We've emptied the receive queue. If we've copied something and
* there's no timeout, let's return what we've got rather than
* waiting for more.
*
* XXX We should only exit here if a PUSH segment was received.
*/
} else if (st && !timeout)
len = 0;
/*
* If we're expecting something to come in, wait for it. Otherwise,
* return EOF.
*/
else switch(tcb->state) {
case LISTEN:
case SYN_SENT:
case SYN_RECEIVED:
case ESTABLISHED:
case FINWAIT1:
case FINWAIT2:
if (!timeout || (dTime = diffJTime(abortTime)) > 0)
OSSemPend(tcb->readSem, (UINT)dTime);
else
len = 0; /* Abort on timeout. */
break;
case CLOSED:
case CLOSE_WAIT:
case CLOSING:
case LAST_ACK:
case TIME_WAIT:
if (tcb->closeReason)
st = tcb->closeReason;
else
st = TCPERR_EOF;
len = 0;
break;
}
}
return st;
}
/*
* Write to a connected TCP connection. This blocks until either all bytes
* are queued, the timeout is reached, or an error occurs.
* Return the number of bytes written on success, an error code on failure.
*/
int tcpWrite(u_int td, const void *s, u_int len)
{
return tcpWriteJiffy(td, s, len, 0);
}
int tcpWriteJiffy(u_int td, const void *s1, u_int len, u_int timeout)
{
const char *s = (char*)s1;
TCPCB *tcb = &tcbs[td];
NBuf *outBuf = NULL;
u_long abortTime;
long dTime = timeout;
u_int segSize;
int sendSize;
int st = 0;
if (timeout)
abortTime = jiffyTime() + timeout;
if (td >= MAXTCP || tcb->prev == tcb)
st = TCPERR_PARAM;
else if (tcb->state == CLOSED
|| tcb->ipSrcAddr == 0
|| tcb->tcpSrcPort == 0
|| tcb->ipDstAddr == 0
|| tcb->tcpDstPort == 0) {
st = TCPERR_CONNECT;
}
/*
* Loop here until either we have queued our data, hit a snag, had
* our connection closed, or timed out.
*/
else while (len) {
/*
* Wait for space in the queue to queue up what we've got up to
* a full length segment.
*/
OS_ENTER_CRITICAL();
sendSize = tcb->snd.wnd - tcb->sndcnt;
OS_EXIT_CRITICAL();
sendSize = MIN(sendSize, len);
sendSize = MIN(sendSize, tcb->mss);
/*
* Block if we can't send anything or if we've got our quota of
* outstanding segments already in the queue. It's up to the
* input side to wake us up when things open up.
*/
if (sendSize <= 0 || tcb->sndq.qLen >= TCP_MAXQUEUE) {
if (!timeout || (dTime = diffJTime(abortTime)) > 0)
OSSemPend(tcb->writeSem, (UINT)dTime);
else
len = 0; /* Abort on timeout. */
/*
* Get a network buffer to fill. Ensure that enough buffers remain
* on the free list to enable receiving an acknowledgement on a full
* length segment.
* If we fail, poll for free buffers until we get one or we time out.
*/
} else if (!outBuf) {
if (nBUFSFREE() < tcb->minFreeBufs + (sendSize / NBUFSZ) * 2) {
if (!timeout || (dTime = diffJTime(abortTime)) > 0)
OSSemPend(tcb->writeSem, MIN((UINT)dTime, WRITESLEEP));
else
len = 0; /* Abort on timeout. */
} else {
nGET(outBuf);
}
/* Loop again and update the open size. */
/*
* Prepare and queue whatever we can.
*/
} else {
nAPPEND(outBuf, s, sendSize, segSize);
if (segSize > 0) {
TCPDEBUG((tcb->traceLevel + 1, TL_TCP, "tcpWrite[%d]: %u:%.*H",
td, segSize, min(60, segSize * 2), s));
len -= segSize;
s += segSize;
switch(tcb->state) {
case SYN_SENT:
case SYN_RECEIVED:
case ESTABLISHED:
case CLOSE_WAIT:
OSSemPend(tcb->mutex, 0);
nENQUEUE(&tcb->sndq, outBuf);
OSSemPost(tcb->mutex);
outBuf = NULL;
st += segSize;
OS_ENTER_CRITICAL();
tcb->sndcnt += segSize;
OS_EXIT_CRITICAL();
tcpOutput(tcb);
break;
case LISTEN:
case FINWAIT1:
case FINWAIT2:
case CLOSING:
case LAST_ACK:
case TIME_WAIT:
case CLOSED:
nFreeChain(outBuf);
len = 0;
if (tcb->closeReason)
st = tcb->closeReason;
else
st = TCPERR_EOF;
break;
}
}
}
}
return st;
}
/*
* tcpWait - Wait for the connection to be closed. Normally this will be
* done after a disconnect before trying to reuse the TCB. This will fail
* if the connection is not closing.
* Returns 0 on success or an error code if the connection is not
* closing.
*/
int tcpWait(u_int td)
{
TCPCB *tcb = &tcbs[td];
int st = 0;
/* Here we allow the TCB to be on the free list. */
if (td >= MAXTCP)
st = TCPERR_PARAM;
else if (tcb->state != CLOSED && tcb->state < FINWAIT1)
st = TCPERR_CONNECT;
else while (tcb->state != CLOSED)
OSSemPend(tcb->connectSem, 0);
return st;
}
/*
* Receive an incoming datagram. This is called from IP with the IP and
* TCP headers intact at the head of the buffer chain.
*/
void tcpInput(NBuf *inBuf, u_int ipHeadLen)
{
TCPCB *tcb;
Connection conn;
u_int tcpHeadLen; /* Length of TCP header. */
int16 segLen; /* TCP segment length exclusive of flags. */
IPHdr *ipHdr; /* Ptr to IP header in output buffer. */
TCPHdr *tcpHdr; /* Ptr to TCP header in output buffer. */
u_int chkSum;
static chkFail = 0;
if (inBuf == NULL) {
TCPDEBUG((LOG_ERR, TL_TCP, "tcpInput: Null input dropped"));
return;
}
/*
* Strip off the IP options. The TCP checksum includes fields from the
* IP header but without the options.
*/
if (ipHeadLen > sizeof(IPHdr)) {
inBuf = ipOptStrip(inBuf, ipHeadLen);
ipHeadLen = sizeof(IPHdr);
}
/*
* Get IP and TCP header together in first nBuf.
*/
if (inBuf->len < sizeof(TCPHdr) + sizeof(IPHdr)) {
if ((inBuf = nPullup(inBuf, sizeof(TCPHdr) + ipHeadLen)) == 0) {
STATS(tcpStats.runt.val++;)
TCPDEBUG((LOG_ERR, TL_TCP, "tcpInput: Runt packet dropped"));
#if DEBUG_SUPPORT > 0
nDumpChain(inBuf);
#endif
return;
}
}
ipHdr = nBUFTOPTR(inBuf, IPHdr *);
/*
* Note: We use ipHeadLen below just in case we kept an option with
* the IP header.
*/
tcpHdr = (TCPHdr *)((char *)ipHdr + ipHeadLen);
/*
* Prepare the header for the TCP checksum. The TCP checksum is
* computed on a pseudo IP header as well as the TCP header and
* the data segment. The pseudo IP header includes the length
* (not including the length of the IP header), protocol, source
* address and destination address fields. We prepare this by
* clearing the TTL field and loading the length in the IP checksum
* field.
*/
ipHdr->ip_ttl = 0;
ipHdr->ip_sum = htons(ipHdr->ip_len - sizeof(IPHdr));
/* Validate the TCP checksum including fields from IP TTL. */
if ((chkSum = inChkSum(inBuf, ipHdr->ip_len - 8, 8)) != 0) {
/* Checksum failed, ignore segment completely */
STATS(tcpStats.checksum.val++;)
TCPDEBUG((LOG_ERR, TL_TCP, "tcpInput: Bad checksum %X", chkSum));
#if DEBUG_SUPPORT > 0
nDumpChain(inBuf);
#endif
if (++chkFail > 3) {
/* Break point. */
TCPDEBUG((LOG_ERR, TL_TCP, "tcpInput: Serious checksum issue here."));
}
nFreeChain(inBuf);
return;
}
/* Convert needed TCP fields to host byte order. */
if ((tcpHeadLen = tcpHdr->tcpOff * 4) < sizeof(TCPHdr)) {
/* TCP header is too small */
STATS(tcpStats.runt.val++;)
TCPDEBUG((LOG_ERR, TL_TCP, "tcpInput: Bad TCP header len %u", tcpHeadLen));
#if DEBUG_SUPPORT > 0
nDumpChain(inBuf);
#endif
nFreeChain(inBuf);
return;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -