📄 tcpnetd.c
字号:
#endif m * nmp; so_t * sop; use_critical;/* common code for net deliver event in all states. 9.4.6 [103..159] */ trace0(tcp_trace, "tcp_net_deliver:\n"); if ((svp = mp->m_svp) == (tcpsv_t *)0) { debug0(tcp_debug, "tcp_net_deliver: sending reset, no state vector\n"); return (st)tcp_reset; } tcphp = (TCPH_T *)mp->m_cp; flags = NetToHost16(&tcphp[TCPH_FLAGS]); length = tcp_dl(mp); /* size of data */ trace1(tcp_tnetd, "tcp_net_deliver: data length = %d\n", length);#ifdef DEBUG critical; sop = svp->sv_sop; if (sop == (so_t *)0) debug2(tcp_debug, "tcp_net_deliver: [*], %s, %s\n", tcp_st(svp->sv_state), tcp_pf(flags)); else debug3(tcp_debug, "tcp_net_deliver: [%d], %s, %s\n", sop->so_index, tcp_st(svp->sv_state), tcp_pf(flags)); normal;#endif switch (svp->sv_state) { case CLOSED: /* 'tcp_reset' will not send the reset if RST is set */ debug0(tcp_debug, "tcp_net_deliver: sending reset, state is CLOSED\n"); return (st)tcp_reset; case LISTEN: return (st)tcp_passive; case SYN_SENT: return (st)tcp_active;#ifdef TCP_TRANSACTION_TCP /* Processing described in last paragraph of section 3.3 of RFC 1644: If we receive a SYN with a CC option for a t/tcp connection in the LAST_ACK state and the SYN passes the TAO test (using the CCrecv value in the state vector), then that means that the final ACK from the peer must have been lost, and this SYN serves to acknowledge the FIN we sent out for this connection. So we finish off this connection, then run this segment back thru "tcp_target" so it will be targeted to the listening socket which spawned the connection we are finishing off, thus creating a new connection (provided the listening socket still exists. On the other hand, if this packet is not a SYN or does not have a CC option or does not pass the TAO test, then it falls thru to be handled by the normal processing below. If it were a SYN that doesn't pass the TAO test, it would be silently discarded just below here where the CC option is checked. */ /* NOTE: Section 2.4 of RFC 1644 says either CC or CC.NEW option gets treated this way. */ case LAST_ACK: if ( (SYN_ON(flags)) && (svp->sv_t_tcp_flags & SV_TRANSACTION) ) { TCPO_T *seg_cc_option; seg_cc_option = tcp_find_option(tcphp, TCPO_CC_KIND); if (seg_cc_option == (TCPO_T *) 0 ) { seg_cc_option = tcp_find_option(tcphp, TCPO_CCNEW_KIND); } /* if */ if (seg_cc_option != (TCPO_T *) 0) { u32 seg_cc = NetToHost32(&(seg_cc_option[TCPO_CC_VAL])); /* We have a CC or CC.NEW -- do the special TAO test */ if ( MODULO32(seg_cc_option , >, svp->sv_ccrecv) ) { /* passed the special TAO test -- close out the state vector and recycle this message thru tcp_target */ tcp_rslf(svp, AB_UC); return (st) tcp_target; } /* if */ } /* if */ } /* if */ break;#endif /* TCP_TRANSACTION_TCP */ } /* switch */ /* common code for states : SYN_RECVD, ESTAB, CLOSE_WAIT, CLOSING, FIN1_WAIT, * FIN2_WAIT, LAST_ACK and TIME_WAIT. */#ifdef TCP_TRANSACTION_TCP /* The following covers RFC 1644, section 3.4, R3.1 and R4: any segment other than a reset segment which contains a CC option that does not match the one we recorded in our control block when processing the SYN is unacceptable and is dropped. */ if ( (RST_OFF(flags)) && (svp->sv_t_tcp_flags & SV_TRANSACTION) ) { u32 peer_cc_option; TCPO_T *cc_option; cc_option = tcp_find_option(tcphp, TCPO_CC_KIND); if (cc_option != ((TCPO_T *) 0) ) { peer_cc_option = NetToHost32(&(cc_option[TCPO_CC_VAL])); if (peer_cc_option != svp->sv_ccrecv) { return (st)mp->m_dispfn; } } }#endif /* TCP_TRANSACTION_TCP */ ackno = NetToHost32(&tcphp[TCPH_ACKNO]); seqno = NetToHost32(&tcphp[TCPH_SEQNO]); netend = seqno + length;#ifdef TCP_TIMESTAMPS if (svp->sv_flags2 & SV_TIMESTAMPS_ENABLED) { TCPO_T *tmstmp_option; tmstmp_option = tcp_find_option(tcphp, TCPO_TMSTMP_KIND); if (tmstmp_option != ((TCPO_T *) 0) ) { got_peer_tmstmp = true; peer_tmstmp = NetToHost32(&(tmstmp_option[TCPO_TMSTMP_TSVAL])); } /* if */ } /* if */#endif /* TCP_TIMESTAMPS */ critical; if ( svp->sv_flags & SV_RFINFLG ) /* FIN seen -- nothing more expected */ /* Actually, in this context, there is an additional * case, which is that in the TIME_WAIT state, a SYN * can arrive for a subseqent reuse of the same * connection. The sequence # of the SYN must be the * next expected. */ rwind = 0; else if ((sop = sv_valid_sop(svp, sop)) != (so_t *)0) { /* Some progress has been made! */ sop->so_flags |= F_SO_MAKING_PROGRESS; if ( sop->so_rq.gq_inuse ) rwind = rwnd(sop); else rwind = 1; } else /* FIN not yet received -- leave room for it */ rwind = 1; normal; winend = MU32(svp->sv_rnxt) + rwind; trace2(tcp_tnetd, "tcp_net_deliver: ackno = %d, seqno = %d\n", ackno, seqno); trace2(tcp_tnetd, "tcp_net_deliver: netend = %d, winend = %d\n", netend, winend); trace2(tcp_tnetd, "tcp_net_deliver: rwind = %d, sv_rnxt = %d\n", rwind, MU32(svp->sv_rnxt));#ifdef TCP_PAWS /* RFC 1323 PAWS requires us (if the incoming segment has a timestamp) to make sure that it is not an earlier timestamp than our current "ts.recent" -- if it is, treat it as an invalid segment.*/ /* Exceptions to this rule are: -- if the segment is a TCP RESET, do not reject based on timestamp -- if the connection has been idle for 24 days or more, invalidate our ts.recent (because that is way more than any realistic MSL, and the peer-generated timestamps could well have wrapped during this idle period. */ paws_test_passes = true; if (RST_OFF(flags)) { /* for resets, PAWS test is bypassed */ if (DOING_PAWS(svp)) { if (got_peer_tmstmp) { if (MODULO32(peer_tmstmp, <, svp->sv_recent_rx_tmstmp)) { /* Is our ts.recent still valid? Depends on how long this connection has been idle */ if ( MODULO32(t_clicks, - , svp->sv_rx_tmstmp_update_time) <= TCP_PAWS_MAX_IDLE_TICKS ) { /* ts.recent is still valid, therefore the incoming segment is to be rejected */ paws_test_passes = false; } /* is ts.recent still valid? */ } /* if incoming timestamp is earlier than our ts.recent */ } /* if incoming segment has a timestamp */ } /* if socket is configured to use PAWS mechanism */ } /* if the incoming segment is not a reset */ /* All of the "normal" validation processing below is to be done only if we pass the "protection against wrapped sequences" check. If the check failed, we will fall thru to invalid processing below */ if (paws_test_passes) #endif /* TCP_PAWS */ /* sequence number status check, 9.4.6.2.17 [127] */ if (rwind == 0) { /* Note from MBM: It is possible that we are receiving retransmitted data that we've already received and processed (e.g., the peer may have too quick a retransmit timer). It would be OK to discard the segment except for the possibility that it contains an updated ACK that we should process. So if the ACK bit is set, and since we can't accept any data right now anyway, strip the data and otherwise fiddle with the segment to make it look like a pure ACK, then let it get processed as such. */ /* P.S. I hope I'm not committing a BOAKYAG. */ if (ACK_ON(flags)) { /* its an ACK. Strip the data (including FIN) */ mp->m_tp = (mp->m_cp + tcphl(tcphp)); flags &= ~FIN; HostToNet16(&tcphp[TCPH_FLAGS], (u16)(NetToHost16(&tcphp[TCPH_FLAGS]) & (u16)~FIN)); HostToNet32(&tcphp[TCPH_SEQNO], MU32(svp->sv_rnxt)); seqno = NetToHost32(&tcphp[TCPH_SEQNO]); length = 0; } /* Note from MBM: This case here will handle incoming "probes" during a time when we are advertising a window of zero. If an ACK segment with zero data is received, the following test will fail (because the seqno will be one behind the next byte we expect to see), and it will fall thru to the "invalid" processing below. That processing sends an ACK, which will tell the remote end that our receive window is still zero. */ if (UC32M(seqno, ==, svp->sv_rnxt)) { goto valid; } /* Note from MBM: The following test catches the case of a "half open connection" where the other end has sent a FIN and gone away, anything it gets from us after that causes the other end to send us a reset. Without this test here, we would end up falling through to the "invalid" processing below and disposing of this reset without taking any action on it, thus we might continue sending and/or retransmitting data. */ if (RST_ON(flags)) { goto valid; } } else if (length == 0) { /* MBM -- I don't really understand this check here -- the segment has no data (nor is it a FIN or a SYN) -- so why are we checking that its sequence number falls within our window? Is this some kind of protection against old ACKs from previous connections that have been wandering around the network? */ if (MC32U(svp->sv_rnxt, <=, seqno) && MODULO32(seqno, <, winend)) goto valid; } else { /* non-zero length, segment has data and/or its a SYN or a FIN */ /* Does the beginning of the segment fall within our current receive window? */ if (MC32U(svp->sv_rnxt, <=, seqno) && MODULO32(seqno, <, winend)) goto valid; /* Well, beginning of segment does not fall within our window. But does the end of the segment fall with our window? */ else if (MC32U(svp->sv_rnxt, <, netend) && MODULO32(netend, <=, winend)) goto valid; /* Well, neither of the segment's endpoints fall within our window. But does it span our window and therefore contain data which falls within our window. */ else if (MC32U(svp->sv_rnxt, >, seqno) && MODULO32(winend, <, netend)) goto valid; } trace0(tcp_tnetd, "tcp_net_deliver: seq# is INVALID\n"); /* The code below is the "invalid" processing that is done if the various validity checks above fail. */#ifdef MSD_DEBUG if (msd_debug) os_printf("tcp_net_deliver: seq# is invalid: rnxt %ld, seqno %ld, netend %ld, winend %ld\n", svp->sv_rnxt, seqno, netend, winend);#endif if (RST_OFF(flags)) { tcp_sndack(mp, svp); /* MBM NOTE: The following removed from Fusion 6.0 -- seems no good reason for it. The peer's retransmit timer backoff should take care of this, if it is retransmitting too quickly. Furthermore, we now have a configureable (via socket option) ack delay time. */#ifdef NEED_ADJUST_ACK_DELAY if (rwind && length) /* The remote side may be retransmitting; this could * be because our ACK got lost or because we're not * sending ACK's rapidly enough; we'll be conservative * here and reduce our ACK timer value -- the following * will cause it to be reduced to 7/8 its present value */ tcp_adjust_ack_delay(svp, (u32)0L);#endif } return (st)mp->m_dispfn;valid: if (length && MODULO32(netend, >, winend)) { /* trim excess */ int trim_bias, size; /* trim the FIN first */ if (FIN_ON(flags)) { flags &= ~FIN; HostToNet16(&tcphp[TCPH_FLAGS], (u16)(NetToHost16(&tcphp[TCPH_FLAGS]) & (u16)~FIN)); trim_bias = 1; } else trim_bias = 0; /* check for a SYN */ if (SYN_ON(flags)) /* adjust what we trim to account for the SYN -- we * never remove it, however */ ++trim_bias; size = (int)(netend - winend) - trim_bias; if (m_dsize(mp) >= size) (mp)->m_tp -= size; length = tcp_dl(mp); /* recalculate size of data */ /* 'netend' is invalid below this point! */ } trace0(tcp_tnetd, "tcp_net_deliver: seq# is VALID\n"); if (RST_ON(flags)) {#ifdef MSD_DEBUG if (msd_debug) { sop = sv_valid_sop(svp, sop); if (sop) os_printf("tcp_net_deliver[%d], got a RESET\n", sop->so_index ); else os_printf("tcp_net_deliver[*], got a RESET\n"); }#endif tcp_rslf(svp, AB_RA); /* Reset self changes state to CLOSED */ return (st)mp->m_dispfn; } if (tcp_spmatch(mp) == NO) { tcp_rslf(svp, FNS_ECONNABORTED); mp->m_sop = (so_t *)0; /* the socket doesn't exist anymore */ mp->m_soindx = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -