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 + -
显示快捷键?