📄 nettcp.c
字号:
NTOHL(tcpHdr->seq);
NTOHL(tcpHdr->ack);
NTOHS(tcpHdr->win);
NTOHS(tcpHdr->urgent);
segLen = ipHdr->ip_len - sizeof(IPHdr) - tcpHeadLen;
/* Find the connection if any. */
conn.localIPAddr = ipHdr->ip_dst.s_addr;
conn.localPort = tcpHdr->dstPort;
conn.remoteIPAddr = ipHdr->ip_src.s_addr;
conn.remotePort = tcpHdr->srcPort;
if((tcb = tcbLookup(&conn)) == NULL) {
TCPCB *ntcb;
if(!(tcpHdr->flags & SYN)) {
/* No open TCB for this connection so reject */
tcpReset(inBuf, ipHdr, tcpHdr, segLen);
return;
}
/*
* Check for a LISTEN on this connection request.
*/
conn.remoteIPAddr = 0;
conn.remotePort = 0;
if((tcb = tcbLookup(&conn)) == NULL) {
/*
* Could be a LISTEN with a null local address.
*/
conn.localIPAddr = 0;
if((tcb = tcbLookup(&conn)) == NULL) {
/* No unspecified LISTEN so reject */
tcpReset(inBuf, ipHdr, tcpHdr, segLen);
return;
}
}
/* We've found a server listen socket, so clone the TCB */
if(tcb->flags & CLONE) {
OS_EVENT *connectSem; /* Semaphore for connections. */
OS_EVENT *readSem; /* Semaphore for read function. */
OS_EVENT *writeSem; /* Semaphore for write function. */
OS_EVENT *mutex; /* Semaphore for mutex. */
/*
* If no room in the listen queue, we have to reject the connection.
*/
if (tcb->listenQOpen < listenQLen(tcb)) {
tcpReset(inBuf, ipHdr, tcpHdr, segLen);
return;
}
/* Get a free TCB. */
OS_ENTER_CRITICAL();
if ((ntcb = topTcpCB) == NULL) {
OS_EXIT_CRITICAL();
/* This may fail, but we should at least try */
tcpReset(inBuf, ipHdr, tcpHdr, segLen);
return;
} else {
topTcpCB = topTcpCB->next;
ntcb->next = ntcb; /* Next -> self => neither free nor linked. */
ntcb->prev = NULL; /* Always NULL when neither free nor linked. */
STATS(if (--tcpStats.curFree.val < tcpStats.minFree.val)
tcpStats.minFree.val = tcpStats.curFree.val;)
OS_EXIT_CRITICAL();
}
/* Duplicate the TCB but must preserve the semaphores. */
connectSem = ntcb->connectSem;
readSem = ntcb->readSem;
writeSem = ntcb->writeSem;
mutex = ntcb->mutex;
memcpy(ntcb, tcb, sizeof(TCPCB));
ntcb->connectSem = connectSem;
ntcb->readSem = readSem;
ntcb->writeSem = writeSem;
ntcb->mutex = mutex;
/*
* Put this on the parent's accept queue.
*/
listenQPush(tcb, ntcb);
tcb = ntcb;
/* Otherwise we use the original TCB. */
} else {
tcbUnlink(tcb); /* It'll be put back on later */
}
/* Load the local address and remote address and port into the TCB. */
tcb->ipSrcAddr = tcb->conn.localIPAddr = ipHdr->ip_dst.s_addr;
tcb->ipDstAddr = tcb->conn.remoteIPAddr = ipHdr->ip_src.s_addr;
tcb->tcpDstPort = tcb->conn.remotePort = tcpHdr->srcPort;
/* 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);
/* NOW put it on the right hash chain */
tcbLink(tcb);
}
TCPDEBUG((tcb->traceLevel, TL_TCP, "tcpInput[%d]: %s:%u->%s:%u %d@%lu",
(int)(tcb - & tcbs[0]),
ip_ntoa2(ipHdr->ip_src.s_addr), ntohs(tcpHdr->srcPort),
ip_ntoa(ipHdr->ip_dst.s_addr), ntohs(tcpHdr->dstPort),
segLen, tcpHdr->seq));
TCPDEBUG((tcb->traceLevel, TL_TCP, " %s %lu win %u",
tcpFlagLabel[tcpHdr->flags & TCPFLAGLABELMASK],
tcpHdr->ack, tcpHdr->win));
TCPDEBUG((tcb->traceLevel + 2, TL_TCP, " IP: %.*H",
ipHeadLen * 2, (char *)ipHdr));
TCPDEBUG((tcb->traceLevel + 2, TL_TCP, " TCP: %.*H",
tcpHeadLen * 2, (char *)tcpHdr));
if (segLen > 0) {
if (tcpHeadLen + sizeof(IPHdr) < inBuf->len) {
TCPDEBUG((tcb->traceLevel, TL_TCP, " DATA: %.*H",
MIN(segLen, 20) * 2, (char *)tcpHdr + tcpHeadLen));
} else if (inBuf->nextBuf) {
NBuf *n0 = inBuf->nextBuf;
TCPDEBUG((tcb->traceLevel, TL_TCP, " DATA: %.*H",
MIN(segLen, 20) * 2, nBUFTOPTR(n0, char *)));
}
}
/*
* If we're doing keep alive, update the keep alive timer.
*/
if (tcb->keepAlive) {
tcb->keepTime = OSTimeGet() + tcb->keepAlive;
tcb->keepProbes = 0;
TCPDEBUG((tcb->traceLevel + 1, TL_TCP, "tcpInput: Keepalive set for %lu",
(int)(tcb - & tcbs[0]),
tcb->keepTime - OSTimeGet()));
timeoutJiffy(
&tcb->keepTimer,
tcb->keepTime,
keepTimeout,
tcb);
}
/* Do unsynchronized-state processing (p. 64-68) */
switch(tcb->state){
case CLOSED:
if(tcpHdr->flags & RST) {
TCPDEBUG((tcb->traceLevel - 1, TL_TCP, "tcpInput[%d]: Dropping RESET on CLOSED",
(int)(tcb - & tcbs[0])));
STATS(tcpStats.resetIn.val++;)
nFreeChain(inBuf);
} else
tcpReset(inBuf, ipHdr, tcpHdr, segLen);
return;
case LISTEN:
if(tcpHdr->flags & RST) {
/*
* XXX - What would it mean if we got a reset on a listening
* connection? After all, we shouldn't have sent anything!
*/
TCPDEBUG((tcb->traceLevel - 1, TL_TCP, "tcpInput[%d]: Dropping RESET on LISTEN",
(int)(tcb - & tcbs[0])));
STATS(tcpStats.resetIn.val++;)
nFreeChain(inBuf);
return;
}
if(tcpHdr->flags & ACK){
tcpReset(inBuf, ipHdr, tcpHdr, segLen);
return;
}
if(tcpHdr->flags & SYN){
/*
* Security check (RFC 793 pg 65) skipped here.
*
* Check incoming precedence (RFC 793 pg 66) and if it's
* greater than ours, upgrade ours. In fact we actually
* adopt it's entire TOS.
*/
if(IPTOS_PREC(ipHdr->ip_tos) > IPTOS_PREC(tcb->ipTOS)) {
TCPDEBUG((tcb->traceLevel - 1, TL_TCP,
"tcpInput[%d]: Changing TOS from %d to %d",
(int)(tcb - & tcbs[0]), tcb->ipTOS, ipHdr->ip_tos));
tcb->ipTOS = ipHdr->ip_tos;
}
STATS(tcpStats.conin.val++;)
procSyn(tcb, tcpHdr);
sendSyn(tcb);
setState(tcb, SYN_RECEIVED);
/* If the segment contains no data then we're done. */
if(segLen == 0 && !(tcpHdr->flags & FIN)) {
nFreeChain(inBuf);
tcpOutput(tcb);
return;
}
} else {
TCPDEBUG((tcb->traceLevel - 1, TL_TCP, "tcpInput[%d]: Dropping non-SYN in LISTEN",
(int)(tcb - & tcbs[0])));
nFreeChain(inBuf);
return;
}
/* At this point the segment contains data - continue processing. */
break;
case SYN_SENT:
if(tcpHdr->flags & ACK){
if(!seqWithin(tcpHdr->ack, tcb->iss + 1, tcb->snd.nxt)) {
tcpReset(inBuf, ipHdr, tcpHdr, segLen);
return;
}
}
if(tcpHdr->flags & RST){ /* p 67 */
if(tcpHdr->flags & ACK){
/*
* The ack must be acceptable since we just checked it.
* This is how the remote side refuses connect requests.
*/
closeSelf(tcb, TCPERR_RESET);
}
TCPDEBUG((tcb->traceLevel, TL_TCP, "tcpInput[%d]: Dropping RESET on SYN-SENT",
(int)(tcb - & tcbs[0])));
STATS(tcpStats.resetIn.val++;)
nFreeChain(inBuf);
return;
}
/* (Security check skipped here) */
/* Check incoming precedence; it must match if there's an ACK */
if(tcpHdr->flags & ACK) {
if(IPTOS_PREC(ipHdr->ip_tos) != IPTOS_PREC(tcb->ipTOS)) {
TCPDEBUG((LOG_WARNING, TL_TCP, "tcpInput[%d]: in TOS PREC %u != our PREC %u",
(int)(tcb - &tcbs[0]),
IPTOS_PREC(ipHdr->ip_tos),
IPTOS_PREC(tcb->ipTOS)));
tcpReset(inBuf, ipHdr, tcpHdr, segLen);
return;
}
} else {
if(IPTOS_PREC(ipHdr->ip_tos) > IPTOS_PREC(tcb->ipTOS)) {
TCPDEBUG((tcb->traceLevel - 1, TL_TCP,
"tcpInput[%d]: Changing TOS from %d to %d",
(int)(tcb - & tcbs[0]), tcb->ipTOS, ipHdr->ip_tos));
tcb->ipTOS = ipHdr->ip_tos;
}
}
if(tcpHdr->flags & SYN){
procSyn(tcb, tcpHdr);
if(tcpHdr->flags & ACK){
/*
* Our SYN has been acked, otherwise the ACK
* wouldn't have been valid.
*/
tcbUpdate(tcb, tcpHdr);
setState(tcb,ESTABLISHED);
} else {
setState(tcb,SYN_RECEIVED);
}
/* If no data then we're done. */
if(segLen == 0 && !(tcpHdr->flags & FIN)) {
nFreeChain(inBuf);
tcpOutput(tcb);
return;
}
/* Ignore segment if neither SYN or RST is set */
} else {
TCPDEBUG((tcb->traceLevel, TL_TCP, "tcpInput[%d]: Dropping non-SYN in SYN-SENT",
(int)(tcb - & tcbs[0])));
STATS(tcpStats.resetIn.val++;)
nFreeChain(inBuf);
return;
}
/* At this point there is valid data in the segment so continue processing. */
break;
}
/*
* We reach this point directly in any synchronized state. Note that
* if we fell through from LISTEN or SYN_SENT processing because of a
* data-bearing SYN, then window trimming and sequence testing "cannot
* fail".
*/
/*
* Trim segment to fit receive window. If none of the segment is
* acceptable, then if the segment isn't a reset, resend the last
* sent ACK.
*/
if ((segLen = trimSeg(tcb, tcpHdr, inBuf, ipHeadLen + tcpHeadLen, segLen)) < 0) {
if(!(tcpHdr->flags & RST)){
tcb->flags |= FORCE;
tcpOutput(tcb);
}
TCPDEBUG((tcb->traceLevel - 1, TL_TCP, "tcpInput[%d]: Dropping unacceptable segment in %s",
(int)(tcb - & tcbs[0]),
tcbStates[tcb->state]));
STATS(tcpStats.resetIn.val++;)
nFreeChain(inBuf);
return;
}
/*
* Check the segment's flags and if OK and the ACK field is set, process
* the acknowledgement field here. RFC 793 specifies that this is to
* be done when the segment begins with the next expected octet
* (i.e. at the top of the loop below) but we do it here so that we
* clear what we can from the output queue BEFORE we drop this due
* to a shortage of buffers or queue it in the resequencing queue.
*/
switch(procInFlags(tcb, tcpHdr, ipHdr)) {
case ACKCLOSE:
closeSelf(tcb, 0);
/*** Fall through... ***/
case ACKDROP:
nFreeChain(inBuf);
return;
case ACKRESET:
tcpReset(inBuf, ipHdr, tcpHdr, segLen);
return;
}
/*
* Before continuing, check that there are enough free buffers for normal
* operation. If not, we'll drop something. If this is the next
* data expected, drop chains from the resequencing queue until we've
* cleared sufficient space. If we're still short of buffers, drop this
* segment.
*/
if (nBUFSFREE() < tcb->minFreeBufs) {
if(tcpHdr->seq == tcb->rcv.nxt) {
while(nQHEAD(tcb->reseq) && nBUFSFREE() < tcb->minFreeBufs) {
NBuf *segBuf;
nDEQUEUE(tcb->reseq, segBuf);
TCPDEBUG((tcb->traceLevel - 1, TL_TCP,
"tcpInput[%d]: Clearing reseq queue",
(int)(tcb - & tcbs[0])));
nFreeChain(segBuf);
}
}
if (nBUFSFREE() < tcb->minFreeBufs) {
TCPDEBUG((tcb->traceLevel - 1, TL_TCP,
"tcpInput[%d]: Drop due to insufficient free bufs",
(int)(tcb - & tcbs[0])));
nFreeChain(inBuf);
inBuf = NULL;
}
/*
* If this segment isn't the next one expected and there's data
* or flags associated with it, put it on the resequencing
* queue, resend the current ACK, and return.
* NOTE: This may queue duplicate or overlapping segments.
*/
} else if(tcpHdr->seq != tcb->rcv.nxt
&& (segLen > 0 || (tcpHdr->flags & FIN))) {
TCPDEBUG((tcb->traceLevel, TL_TCP, "tcpInput[%d]: Queued %u",
(int)(tcb - & tcbs[0]),
segLen));
nEnqSort(tcb->reseq, inBuf, tcpHdr->seq);
inBuf = NULL;
tcb->flags |= FORCE;
tcpOutput(tcb);
}
/*
* This loop first processes the current segment, and then
* repeats while it can process segments from the resequencing queue.
*/
while (inBuf) {
/*
* We reach this point with an acceptable segment; all data and flags
* are in the window, and the starting sequence number equals rcv.nxt
* (p. 70)
*/
/* (URGent bit processing skipped here) */
/* Process the segment text, if any, beginning at rcv.nxt (p. 74) */
if(segLen != 0){
NBuf *segBuf = nSplit(inBuf, ipHeadLen + tcpHeadLen);
switch(tcb->state){
case SYN_RECEIVED:
case ESTABLISHED:
case FINWAIT1:
case FINWAIT2:
/*
* Place the segment data on receive queue. Keep the headers
* in a separate segment until we finish processing them below.
*/
nENQUEUE(&tcb->rcvq, segBuf);
OS_ENTER_CRITICAL();
tcb->rcvcnt += segLen;
tcb->rcv.nxt += segLen;
/*
* 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) < 0)
tcb->rcv.wnd = 0;
tcb->flags |= FORCE;
OS_EXIT_CRITICAL();
break;
default:
/* Ignore segment text */
nFreeChain(segBuf);
TCPDEBUG((LOG_WARNING, TL_TCP, "tcpInput[%d]: State %d - dropped",
(int)(tcb - & tcbs[0]),
tcb->state));
break;
}
}
/*
* Signal pending reads that data has arrived.
*
* This is done before sending an acknowledgement in case the
* application is running at a higher priority and wants to piggyback
* some reply data.
*
* It's also done before processing FIN so that the CLOSED
* state will occur after the user has had a chance to read
* the last of the incoming data with a priority higher than
* we're running.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -