tcpnetd.c

来自「用于嵌入式系统的TCP/IP协议栈及若干服务」· C语言 代码 · 共 1,615 行 · 第 1/5 页

C
1,615
字号
        debug0(tcp_debug, "tcp_net_deliver: sending reset, tcp_spmatch failed\n");        return (st)tcp_reset;    }    if (SYN_ON(flags)  &&  UC32M(seqno, >=, svp->sv_rnxt) &&  MODULO32(seqno, <, winend))    { /* SYN in window */        tcp_rslf(svp, FNS_ENETUNREACH);        mp->m_sop = (so_t *)0;  /* the socket doesn't exist anymore */        mp->m_soindx = 0;        debug0(tcp_debug, "tcp_net_deliver: sending reset, SYN in window\n");        return (st)tcp_reset;    }	/* MBM NOTE: The following block of code removed in Fusion 6.5 . This seems	   to be code whose intention is to allow connections to be re-used even	   if in the TIME_WAIT state. I don't even think this code was ever being	   executed, but whether it was or not, I think its wrong, for the following	   reasons:	     1) It violates RFCs vis-a-vis the TIME_WAIT state for normal (non-transaction		    TCP) connections		 2) It seems to assume that all segments except an initial SYN are required		    to have the ACK flag set (it disposes of the segment). I don't think this 			is true, even though it would be unusual to see that.	   Anyway, in implementing transaction TCP, I found it necessary to remove this	   code because:	        The initial SYN-DATA-FIN segment from the peer is run through here (after		    stripping the SYN flag) if the TAO test passes, in order to process the			data and FIN, and this little section of code was disposing of it. The			assumption that "the logic above has removed any data which may have been			present" is not necessarily true any longer.            Its true that for transaction TCP, TIME_WAIT truncation is possible, That,			however, will be taken care of somewhere else.	*/#if 0	if (ACK_OFF(flags)) {   /* NONE case for both Ack Status tests */        /* Extra code which you won't find in the specs: if the         * targeted state vector is in TIME_WAIT, we want to allow         * a SYN to precipitate reuse of this state vector right now.         * The logic above has removed any data which may have been         * present (recv window would have been 0).  We simply reset         * this state vector (state to CLOSED), then run the message         * back under the nose of 'tcp_target' to allow any listener         * a crack at it. */        if (SYN_ON(flags)  &&  svp->sv_state == TIME_WAIT) {            tcp_rslf(svp, 0);   /* state to CLOSED */            return (st)tcp_target;        }        return (st)mp->m_dispfn;    /* --no action */    }#endif    if (svp->sv_state == SYN_RECVD) {        if (UC32M(ackno, <=, svp->sv_suna) ||  UC32M(ackno, >, svp->sv_snxt)) {            /* ACK status 1 : INVALID */            debug0(tcp_debug, "tcp_net_deliver: sending reset, ACKSTS1 INVALID\n");            return (st)tcp_reset;        }    } else if (UC32M(ackno, >, svp->sv_snxt)) {        /* ACK status 2 : INVALID */        debug0(tcp_debug, "tcp_net_deliver: ACKSTS2 INVALID\n");        tcp_sndack(mp, svp);        return (st)mp->m_dispfn;    }    /* at this point, we have decided that the packet is a keeper;     * therefore, we request a delayed ack in the near future if it     * contains data (if necessary, before we finish processing this	 * segment, we will override this delayed ack with an immediate	 * ack, e.g., if this segment turns out to contain out-of-order	 * data) */    if (length)        tcp_schedule_delayed_ack(svp, SV_DATACK);    switch (svp->sv_state) {      case SYN_RECVD:        if (rwind)            return (st)tcp_copen;   /* conn_open */        break;      case TIME_WAIT:        if (FIN_ON(flags)) {            tcp_sndack(mp, svp);            tcp_stw(svp, (u8)mp->m_ttl);    /* restart time wait */        }        return (st)mp->m_dispfn;      case LAST_ACK: 		  /* intentional fall-thru */      case CLOSING:        if ((svp->sv_flags & SV_SFINFLG) && ACK_ON(flags)   && UC32M(ackno, ==, svp->sv_snxt))        {            if (svp->sv_flags & SV_TFINFLG) {   /* FIN ACKed */                svp->sv_flags &= ~SV_TFINFLG;                M32U(svp->sv_suna, +=, 1);                /* no more urgent data */                stass(svp->sv_surg, svp->sv_snxt);            }            if (svp->sv_state == CLOSING) {                /* 'tcp_stw' changes state to TIME_WAIT */                tcp_stw(svp, (u8)mp->m_ttl);                return (st)mp->m_dispfn;            } else  /* LAST_ACK */                /* 'tcp_rslf' changes state to CLOSED */                tcp_rslf(svp, AB_UC);        }        if (svp->sv_state == LAST_ACK)            return (st)mp->m_dispfn;        break;    }/* common code for states : SYN_RECVD, ESTAB, CLOSE_WAIT, CLOSING, FIN1_WAIT *              and FIN2_WAIT. */    /* Update the send state of this connection according to the window, 	   ack number, and selective acks that are in this segment. */	/* MBM note: For Fusion 6.5, made this conditional on the ACK flag being set,	   because in transaction TCP, we could get down here without that being the	   case for the initial SYN */ 	if (ACK_ON(flags)) {	   tcp_update(mp);	} /* if */#ifdef TCP_TRANSACTION_TCP	/* For transaction TCP, processing of the initial SYN will get down here if it	   contains data. Even though the ACK flag is not set, it will contain a window	   that we need to record so that when the application responds to the SYN with	   data (and likely also a FIN) we will be able to send more than just the SYN-ACK.	   (However, the SYN flag will have been stripped off the segment by this time, so	   we have to infer from various flags/fields in the state vector) */	else {         if (TRANSACTION_DELAYING_SYNACK(svp)) {           svp->sv_swnd = NetToHost16(&tcphp[TCPH_WINDOW]);		} 	}#endif /* TCP_TRANSACTION_TCP */    switch (svp->sv_state) {      case SYN_RECVD:        tcp_estab(svp); /* Enter ESTABlished state */        tcp_urgdata(mp); 	    tcp_urgstate(mp);        fallthru;      case CLOSE_WAIT:      case CLOSING:        return (st)mp->m_dispfn;      case FIN1_WAIT:        if (rwind == 0) {            /* This should be a can't happen situation */            if ((svp->sv_flags & SV_SFINFLG) && ACK_ON(flags) &&                UC32M(ackno, ==, svp->sv_snxt)) /* FIN ACKed */                set_state("tcp_net_deliver", svp, FIN2_WAIT);            return (st)mp->m_dispfn;        }        break;      case FIN2_WAIT:        if (rwind == 0)            return (st)mp->m_dispfn;        break;      case ESTAB:          if (rwind == 0) {            tcp_urgdata(mp);            tcp_urgstate(mp);            return (st)mp->m_dispfn;		  }#ifdef TCP_KEEP_ALIVE    /* we got data/ack from the peer, reset Keep-Alive timer */        /*  check for valid sop */        if (tcp_keep_alive  && ((sop = sv_valid_sop(svp, sop)) != (so_t *)0)            && so_keepalive(sop) && svp->sv_ka_tcb)        {            t_start(svp->sv_ka_tcb, tcp_idle_interval);            svp->sv_ka_retrycnt = (u16)tcp_try_max;            if (svp->sv_ka_type == TRY_GD)                svp->sv_ka_type = GARBAGE_DATA;            else                if (svp->sv_ka_type == TRY_NOGD)                    svp->sv_ka_type = NO_GARBAGE_DATA;        }#endif        break;    }    /* common code for states : ESTAB, FIN1_WAIT and FIN2_WAIT. */    critical;		if (UC32M(seqno, <=, svp->sv_rnxt)) {        normal;        /* this packet contains the next expected byte */		/* MBM note: If the entire segment contained data that we've already received 		   and ACKed, that was taken care of above while validating the packet (before		   going to the "valid" label). So if we are here, the segment contains at least		   some new data */#ifdef TCP_TIMESTAMPS		   /* If timestamps are enabled on this connection (because they were negotiated		   during connection setup) and a timestamp option is present in the segment		   we are processing, update "ts.recent" from that timestamp (per RFC 1323) 		   only if this segment contains the next sequence number after the one we		   most recently acked. */		   /* Question, which RFC 1323 doesn't explicitly address: What should we do		   if timestamps have been negotiated, but the other end isn't sending them,		   or is sending them sporadically? It would seem that the other end would		   get an invalid RTT measurement. Should we do anything about that here? 		   If so, what? The only thing I can think of is to reset the SV_TIMESTAMPS		   enabled flag, i.e., behave as if timestamps hadn't been negotiated, i.e.,		   don't send timestamps and ignore received timestamps. But, I'll defer		   that issue for now. */        if (svp->sv_flags2 & SV_TIMESTAMPS_ENABLED) { 		   if ( UC32M(seqno, <=, svp->sv_last_ack_sent) ) {			   if (got_peer_tmstmp) {			       svp->sv_recent_rx_tmstmp = peer_tmstmp; #ifdef TCP_PAWS				   svp->sv_rx_tmstmp_update_time = t_clicks;#endif			  } /* if segment contains the peer's timestamp */		   } /* if segment contains next data following what we most recently acked */		} /* if timestamps are enabled */#endif   /* TCP_TIMESTAMPS */ 		if (length) {#ifdef  TCP_STATS            ++tcp_rrecv;#endif            /* contains in-order data, so we must 'tcp_naccept' it */            return (st)tcp_naccept;        }#ifdef  TCP_STATS        ++tcp_arecv;#endif        /* bypass 'tcp_naccept' to shorten processing of         * pure ACK's */        smcritical(mp);/* NOTE: The processing of this packet is in a critical section from *  this point on; this guarantees that the ordering of state *  updates will be the same as that of receive queue insertion; */#ifndef AVOID_MSM        return (st)tcp_nstate;#else        return(tcp_nstate(mp));#endif    }    if (length == 0) {        /* this packet contains no data, seqno is ahead of it's time */        normal;        return (st)mp->m_dispfn;    }#ifdef  TCP_STATS    ++tcp_orecv;#endif    	/* This packet contains out-of-order data. We may want to put it into the holding	   queue, but first make sure we still have a socket (the holding queue is	   connected to the socket) */    trace2(tcp_thq, "tcp_net_deliver: o-o-o seq 0x%x len %d\n", seqno, length);    sop = sv_valid_sop(svp, sop);    if ( sop == (so_t *)0 ) {        /* no socket to hold out of order data/FIN */        tcpabort(svp, AB_UC);   /* It died */        return (st)mp->m_dispfn;    }	/* Look thru the holding queue to see if this is a duplicate of a segment already	   recieved and put into the holding queue. If its an exact duplicate, discard this	   segment. If it contains some duplicate data but also some new data, discard the	   previous segment and put this one into the holding queue.	     NOTE from MBM: It is possible that we will end up with segments in the holding 	   queue that have some overlapping data, because some possible scenarios of 	   overlapping data are not being looked at here. But that doesn't really matter -- 	   that will be sorted out when we remove messages from the holding queue after the 	   gaps have been filled.*/    for (nmp = (m *)sop->so_hq.gq_q.q_next; !is_header(&nmp->m_q); 	                                             nmp = (m *)nmp->m_q.q_next) {        /* Does this current message start with the same sequence number as the 		   incoming message? */		if (seqno == NetToHost32(&((TCPH_T *)(nmp->m_cp))[TCPH_SEQNO])) {            /* Yes it does. Does the new message extend beyond the current message? */            if (length > tcp_dl(nmp)) { 				/* Yes it does. So remove the current message from the queue and discard it,				   prior to inserting the new message into the queue */                trace0(tcp_thq, "tcp_net_deliver: replacement\n");                nmp->m_q.q_flags |= F_Q_LOCKED;                (void)msm(nmp, smdispose);                break;  /* insert the new one */            } /* if duplicate segment larger than original */			/* The new message contains no data that is not already in the holding queue,			   so discard the new message, and no need to look any further into the			   holding queue. */            trace0(tcp_thq, "tcp_net_deliver: duplicate\n");            normal;            return (st)mp->m_dispfn;        } /* if duplicate segment */	} /* for loop searching holding queue for duplicate segments */    /* No duplications, some data may overlap, queue it */    normal;    /*   The holding queue size could be improperly updated in case tha last packet            (with FIN on) were to be put in the holding queue. Fusion would send a RESET            instead of a FIN upon closing the TCP connection. The bug did not affect the            receive queue. */    if ( FIN_ON(flags) ) {        length--;	} /* if */    mp->m_udsize  = length;#ifdef TCP_SELACK_ENHANCEMENTS

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?