⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 nettcp.c

📁 一个uCOS-II下的tcpip协议栈实现
💻 C
📖 第 1 页 / 共 5 页
字号:
	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 + -