📄 tcp-sock.cc
字号:
case TCPS_LISTEN: /* awaiting peer's SYN */ if (tiflags & TH_ACK) { goto dropwithreset; } if ((tiflags & TH_SYN) == 0) { goto drop; } if(listen_only_) { if(app_) ((NSSocket *)app_)->upcall_accept(pkt); return; } /* * must by a SYN (no ACK) at this point... * in real tcp we would bump the iss counter here also */ dooptions(pkt); irs_ = tcph->seqno(); t_seqno_ = iss_; /* tcp_sendseqinit() macro in real tcp */ rcv_nxt_ = rcvseqinit(irs_, datalen); flags_ |= TF_ACKNOW; if (ecn_ && fh->ecnecho()) { ect_ = TRUE; } newstate(TCPS_SYN_RECEIVED); goto trimthenstep6; /* * If the state is SYN_SENT: * if seg contains an ACK, but not for our SYN, drop the input. * if seg does not contain SYN, then drop it. * Otherwise this is an acceptable SYN segment * initialize tp->rcv_nxt and tp->irs * if seg contains ack then advance tp->snd_una * if SYN has been acked change to ESTABLISHED else SYN_RCVD state * arrange for segment to be acked (eventually) * continue processing rest of data/controls, beginning with URG */ case TCPS_SYN_SENT: /* we sent SYN, expecting SYN+ACK (or SYN) */ /* drop if it's a SYN+ACK and the ack field is bad */ if ((tiflags & TH_ACK) && ((ackno <= iss_) || (ackno > maxseq_))) { // not an ACK for our SYN, discard fprintf(stderr, "%f: FullTcpAgent::recv(%s): bad ACK (%d) for our SYN(%d)\n", now(), name(), int(ackno), int(maxseq_)); goto dropwithreset; } if ((tiflags & TH_SYN) == 0) { fprintf(stderr, "%f: FullTcpAgent::recv(%s): no SYN for our SYN(%d)\n", now(), name(), int(maxseq_)); goto drop; } /* looks like an ok SYN or SYN+ACK */#ifdef notdef cancel_rtx_timer(); // cancel timer on our 1st SYN [does this belong!?]#endif irs_ = tcph->seqno(); // get initial recv'd seq # rcv_nxt_ = rcvseqinit(irs_, datalen); /* * we are seeing either a SYN or SYN+ACK. For pure SYN, * ecnecho tells us our peer is ecn-capable. For SYN+ACK, * it's acking our SYN, so it already knows we're ecn capable, * so it can just turn on ect */ if (ecn_ && (fh->ecnecho() || fh->ect())) ect_ = TRUE; if (tiflags & TH_ACK) { // SYN+ACK (our SYN was acked) // CHECKME highest_ack_ = ackno; cwnd_ = initial_window();#ifdef notdef /* * if we didn't have to retransmit the SYN, * use its rtt as our initial srtt & rtt var. */ if (t_rtt_) { double tao = now() - tcph->ts(); rtt_update(tao); }#endif /* * if there's data, delay ACK; if there's also a FIN * ACKNOW will be turned on later. */ if (datalen > 0) { flags_ |= TF_DELACK; // data there: wait } else { flags_ |= TF_ACKNOW; // ACK peer's SYN } /* * Received <SYN,ACK> in SYN_SENT[*] state. * Transitions: * SYN_SENT --> ESTABLISHED * SYN_SENT* --> FIN_WAIT_1 */ int newst = FALSE; if (flags_ & TF_NEEDFIN) { newstate(TCPS_FIN_WAIT_1); flags_ &= ~TF_NEEDFIN; tiflags &= ~TH_SYN; } else { newstate(TCPS_ESTABLISHED);#ifdef PDNS hdr_rti *rtih = hdr_rti::access(pkt); dst_ipaddr_ = rtih->ipsrc(); dst_ipport_ = rtih->ipsrcport(); if(GetLocalIP(dst_ipaddr_)) dst_= iph->src();#else dst_ = iph->src();#endif newst = TRUE; } // special to ns: // generate pure ACK here. // this simulates the ordinary connection establishment // where the ACK of the peer's SYN+ACK contains // no data. This is typically caused by the way // the connect() socket call works in which the // entire 3-way handshake occurs prior to the app // being able to issue a write() [which actually // causes the segment to be sent]. //sendpacket(t_seqno_, rcv_nxt_, TH_ACK, 0, 0); sendpacket(t_seqno_, rcv_nxt_, TH_ACK, 0, NULL, 0); if(app_ && newst) ((NSSocket *)app_)->upcall_connected(pkt); } else { // SYN (no ACK) (simultaneous active opens) flags_ |= TF_ACKNOW; cancel_rtx_timer(); newstate(TCPS_SYN_RECEIVED); /* * decrement t_seqno_: we are sending a * 2nd SYN (this time in the form of a * SYN+ACK, so t_seqno_ will have been * advanced to 2... reduce this */ t_seqno_--; // CHECKME }trimthenstep6: /* * advance the seq# to correspond to first data byte */ tcph->seqno()++; if (tiflags & TH_ACK) goto process_ACK; goto step6; case TCPS_LAST_ACK: case TCPS_CLOSING: break; } /* end switch(state_) */ /* * States other than LISTEN or SYN_SENT. * First check timestamp, if present. * Then check that at least some bytes of segment are within * receive window. If segment begins before rcv_nxt, * drop leading data (and SYN); if nothing left, just ack. * * RFC 1323 PAWS: If we have a timestamp reply on this segment * and it's less than ts_recent, drop it. */ if (ts_option_ && !fh->no_ts_ && recent_ && tcph->ts() < recent_) { if ((now() - recent_age_) > TCP_PAWS_IDLE) { /* * this is basically impossible in the simulator, * but here it is... */ /* * Invalidate ts_recent. If this segment updates * ts_recent, the age will be reset later and ts_recent * will get a valid value. If it does not, setting * ts_recent to zero will at least satisfy the * requirement that zero be placed in the timestamp * echo reply when ts_recent isn't valid. The * age isn't reset until we get a valid ts_recent * because we don't want out-of-order segments to be * dropped when ts_recent is old. */ recent_ = 0.0; } else { goto dropafterack; } } // check for redundant data at head/tail of segment // note that the 4.4bsd [Net/3] code has // a bug here which can cause us to ignore the // perfectly good ACKs on duplicate segments. The // fix is described in (Stevens, Vol2, p. 959-960). // This code is based on that correction. // // In addition, it has a modification so that duplicate segments // with dup acks don't trigger a fast retransmit when dupseg_fix_ // is enabled. // // Yet one more modification: make sure that if the received // segment had datalen=0 and wasn't a SYN or FIN that // we don't turn on the ACKNOW status bit. If we were to // allow ACKNO to be turned on, normal pure ACKs that happen // to have seq #s below rcv_nxt can trigger an ACK war by // forcing us to ACK the pure ACKs // todrop = rcv_nxt_ - tcph->seqno(); // how much overlap? if (todrop > 0 && ((tiflags & (TH_SYN|TH_FIN)) || datalen > 0)) { if (tiflags & TH_SYN) { tiflags &= ~TH_SYN; tcph->seqno()++; th->size()--; // XXX Must decrease packet size too!! todrop--; } // // see Stevens, vol 2, p. 960 for this check; // this check is to see if we are dropping // more than this segment (i.e. the whole pkt + a FIN), // or just the whole packet (no FIN) // if (todrop > datalen || (todrop == datalen && (tiflags & TH_FIN) == 0)) { /* * Any valid FIN must be to the left of the window. * At this point the FIN must be a duplicate or out * of sequence; drop it. */ tiflags &= ~TH_FIN; /* * Send an ACK to resynchronize and drop any data. * But keep on processing for RST or ACK. */ flags_ |= TF_ACKNOW; todrop = datalen; dupseg = TRUE; } tcph->seqno() += todrop; th->size() -= todrop; // XXX Must decrease size too!! datalen -= todrop; } /* * If last ACK falls within this segment's sequence numbers, * record the timestamp. * See RFC1323 (now RFC1323 bis) */ if (ts_option_ && !fh->no_ts_ && tcph->seqno() <= last_ack_sent_) { /* * this is the case where the ts value is newer than * the last one we've seen, and the seq # is the one we expect * [seqno == last_ack_sent_] or older */ recent_age_ = now(); recent_ = tcph->ts(); } if (tiflags & TH_SYN) { fprintf(stderr, "%f: FullTcpAgent::recv(%s) received unexpected SYN (state:%d)\n", now(), name(), state_); goto dropwithreset; } // K: added TH_SYN, but it is already known here! if ((tiflags & TH_ACK) == 0) { fprintf(stderr, "%f: FullTcpAgent::recv(%s) got packet lacking ACK (seq %d)\n", now(), name(), tcph->seqno()); goto drop; } /* * Ack processing. */ switch (state_) { case TCPS_SYN_RECEIVED: /* want ACK for our SYN+ACK */ if (ackno < highest_ack_ || ackno > maxseq_) { // not in useful range goto dropwithreset; } /* * Make transitions: * SYN-RECEIVED -> ESTABLISHED * SYN-RECEIVED* -> FIN-WAIT-1 */ if (flags_ & TF_NEEDFIN) { newstate(TCPS_FIN_WAIT_1); flags_ &= ~TF_NEEDFIN; } else { newstate(TCPS_ESTABLISHED); if(app_) ((NSSocket *)app_)->upcall_passconn(); } cwnd_ = initial_window(); /* fall into ... */ /* * In ESTABLISHED state: drop duplicate ACKs; ACK out of range * ACKs. If the ack is in the range * tp->snd_una < ti->ti_ack <= tp->snd_max * then advance tp->snd_una to ti->ti_ack and drop * data from the retransmission queue. * * note that state TIME_WAIT isn't used * in the simulator */ case TCPS_ESTABLISHED: case TCPS_FIN_WAIT_1: case TCPS_FIN_WAIT_2: case TCPS_CLOSE_WAIT: case TCPS_CLOSING: case TCPS_LAST_ACK: // // look for ECNs in ACKs, react as necessary // if (fh->ecnecho() && (!ecn_ || !ect_)) { fprintf(stderr, "%f: FullTcp(%s): warning, recvd ecnecho but I am not ECN capable!\n", now(), name()); } // // generate a stream of ecnecho bits until we see a true // cong_action bit // if (ecn_) { if (fh->ce() && fh->ect()) recent_ce_ = TRUE; else if (fh->cong_action()) recent_ce_ = FALSE; } // look for dup ACKs (dup ack numbers, no data) // // do fast retransmit/recovery if at/past thresh if (ackno <= highest_ack_) { // an ACK which doesn't advance highest_ack_ if (datalen == 0 && (!dupseg_fix_ || !dupseg)) { --pipe_; // ACK indicates pkt cached @ receiver /* * If we have outstanding data * this is a completely * duplicate ack, * the ack is the biggest we've * seen and we've seen exactly our rexmt * threshhold of them, assume a packet * has been dropped and retransmit it. * * We know we're losing at the current * window size so do congestion avoidance. * * Dup acks mean that packets have left the * network (they're now cached at the receiver) * so bump cwnd by the amount in the receiver * to keep a constant cwnd packets in the * network. */ if ((rtx_timer_.status() != TIMER_PENDING) || ackno != highest_ack_) { // not timed, or re-ordered ACK dupacks_ = 0; } else if (++dupacks_ == tcprexmtthresh_) { fastrecov_ = TRUE; /* re-sync the pipe_ estimate */ pipe_ = maxseq_ - highest_ack_; pipe_ /= maxseg_; pipe_ -= (dupacks_ + 1); pipe_ = int(cwnd_) - dupacks_ - 1; dupack_action(); // maybe fast rexmt goto drop; } else if (dupacks_ > tcprexmtthresh_) { if (reno_fastrecov_) { // we just measure cwnd in // packets, so don't scale by // maxseg_ as real // tcp does cwnd_++; } send_much(0, REASON_NORMAL, maxburst_); goto drop; } } else { // non zero-length [dataful] segment // with a dup ack (normal for dataful segs) // (or window changed in real TCP). if (dupack_reset_) { dupacks_ = 0; fastrecov_ = FALSE; } } break; /* take us to "step6" */ } /* end of dup acks */ /* * we've finished the fast retransmit/recovery period * (i.e. received an ACK which advances highest_ack_) * The ACK may be "good" or "partial" */process_ACK: if (ackno > maxseq_) { // ack more than we sent(!?) fprintf(stderr, "%f: FullTcpAgent::recv(%s) too-big ACK (ack: %d, maxseq:%d)\n", now(), name(), int(ackno), int(maxseq_)); goto dropafterack; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -