📄 tcp-full.cc
字号:
/* * this is the simulated form of the header prediction * predicate. While not really necessary for a simulation, it * follows the code base more closely and can sometimes help to reveal * odd behavior caused by the implementation structure.. * * Here's the comment from the real TCP: * * Header prediction: check for the two common cases * of a uni-directional data xfer. If the packet has * no control flags, is in-sequence, the window didn't * change and we're not retransmitting, it's a * candidate. If the length is zero and the ack moved * forward, we're the sender side of the xfer. Just * free the data acked & wake any higher level process * that was blocked waiting for space. If the length * is non-zero and the ack didn't move, we're the * receiver side. If we're getting packets in-order * (the reassembly queue is empty), add the data to * the socket buffer and note that we need a delayed ack. * Make sure that the hidden state-flags are also off. * Since we check for TCPS_ESTABLISHED above, it can only * be TF_NEEDSYN. */intFullTcpAgent::predict_ok(Packet* pkt){ hdr_tcp *tcph = hdr_tcp::access(pkt); hdr_flags *fh = hdr_flags::access(pkt); /* not the fastest way to do this, but perhaps clearest */ int p1 = (state_ == TCPS_ESTABLISHED); // ready int p2 = ((tcph->flags() & (TH_SYN|TH_FIN|TH_ACK)) == TH_ACK); // ACK int p3 = ((flags_ & TF_NEEDFIN) == 0); // don't need fin int p4 = (!ts_option_ || fh->no_ts_ || (tcph->ts() >= recent_)); // tsok int p5 = (tcph->seqno() == rcv_nxt_); // in-order data int p6 = (t_seqno_ == maxseq_); // not re-xmit int p7 = (!ecn_ || fh->ecnecho() == 0); // no ECN int p8 = (tcph->sa_length() == 0); // no SACK info return (p1 && p2 && p3 && p4 && p5 && p6 && p7 && p8);}/* * fast_retransmit using the given seqno * perform fast RTX, set recover_, set last_cwnd_action */intFullTcpAgent::fast_retransmit(int seq){ // we are now going to fast-retransmit and willtrace that event trace_event("FAST_RETX"); recover_ = maxseq_; // recovery target last_cwnd_action_ = CWND_ACTION_DUPACK; return(foutput(seq, REASON_DUPACK)); // send one pkt}/* * real tcp determines if the remote * side should receive a window update/ACK from us, and often * results in sending an update every 2 segments, thereby * giving the familiar 2-packets-per-ack behavior of TCP. * Here, we don't advertise any windows, so we just see if * there's at least 'segs_per_ack_' pkts not yet acked * * also, provide for a segs-per-ack "threshold" where * we generate 1-ack-per-seg until enough stuff * (spa_thresh_ bytes) has been received from the other side * This idea came from vj/kmn in BayTcp. Added 8/21/01. */intFullTcpAgent::need_send(){ if (flags_ & TF_ACKNOW) return TRUE; int spa = (spa_thresh_ > 0 && ((rcv_nxt_ - irs_) < spa_thresh_)) ? 1 : segs_per_ack_; return ((rcv_nxt_ - last_ack_sent_) >= (spa * maxseg_));}/* * determine whether enough time has elapsed in order to * conclude a "restart" is necessary (e.g. a slow-start) * * for now, keep track of this similarly to how rtt_update() does */intFullTcpAgent::idle_restart(){ if (last_send_time_ < 0.0) { // last_send_time_ isn't set up yet, we shouldn't // do the idle_restart return (0); } double tao = now() - last_send_time_; if (!ts_option_) { double tickoff = fmod(last_send_time_ + boot_time_, tcp_tick_); tao = int((tao + tickoff) / tcp_tick_) * tcp_tick_; } return (tao > t_rtxcur_); // verify this CHECKME}/* * tcp-full's version of set_initial_window()... over-rides * the one in tcp.cc */voidFullTcpAgent::set_initial_window(){ syn_ = TRUE; // full-tcp always models SYN exchange TcpAgent::set_initial_window();} /* * main reception path - * called from the agent that handles the data path below in its muxing mode * advance() is called when connection is established with size sent from * user/application agent * * This is a fairly complex function. It operates generally as follows: * do header prediction for simple cases (pure ACKS or data) * if in LISTEN and we get a SYN, begin initializing connection * if in SYN_SENT and we get an ACK, complete connection init * trim any redundant data from received dataful segment * deal with ACKS: * if in SYN_RCVD, complete connection init then go on * see if ACK is old or at the current highest_ack * if at current high, is the threshold reached or not * if so, maybe do fast rtx... otherwise drop or inflate win * deal with incoming data * deal with FIN bit on in arriving packet */voidFullTcpAgent::recv(Packet *pkt, Handler*){ hdr_tcp *tcph = hdr_tcp::access(pkt); // TCP header hdr_cmn *th = hdr_cmn::access(pkt); // common header (size, etc) hdr_flags *fh = hdr_flags::access(pkt); // flags (CWR, CE, bits) int needoutput = FALSE; int ourfinisacked = FALSE; int dupseg = FALSE; // recv'd dup data segment int todrop = 0; // duplicate DATA cnt in seg last_state_ = state_; int datalen = th->size() - tcph->hlen(); // # payload bytes int ackno = tcph->ackno(); // ack # from packet int tiflags = tcph->flags() ; // tcp flags from packet//if (state_ != TCPS_ESTABLISHED || (tiflags&(TH_SYN|TH_FIN))) {//fprintf(stdout, "%f(%s)in state %s recv'd this packet: ", now(), name(), statestr(state_));//prpkt(pkt);//} /* * Acknowledge FIN from passive closer even in TCPS_CLOSED state * (since we lack TIME_WAIT state and RST packets, * the loss of the FIN packet from the passive closer will make that * endpoint retransmit the FIN forever) * -F. Hernandez-Campos 8/6/00 */ if ( (state_ == TCPS_CLOSED) && (tiflags & TH_FIN) ) { goto dropafterack; } /* * Don't expect to see anything while closed */ if (state_ == TCPS_CLOSED) { fprintf(stderr, "%f: FullTcp(%s): recv'd pkt in CLOSED state: ", now(), name()); prpkt(pkt); goto drop; } /* * Process options if not in LISTEN state, * else do it below */ if (state_ != TCPS_LISTEN) dooptions(pkt); /* * if we are using delayed-ACK timers and * no delayed-ACK timer is set, set one. * They are set to fire every 'interval_' secs, starting * at time t0 = (0.0 + k * interval_) for some k such * that t0 > now */ if (delack_interval_ > 0.0 && (delack_timer_.status() != TIMER_PENDING)) { int last = int(now() / delack_interval_); delack_timer_.resched(delack_interval_ * (last + 1.0) - now()); } /* * sanity check for ECN: shouldn't be seeing a CE bit if * ECT wasn't set on the packet first. If we see this, we * probably have a misbehaving router... */ if (fh->ce() && !fh->ect()) { fprintf(stderr, "%f: FullTcpAgent::recv(%s): warning: CE bit on, but ECT false!\n", now(), name()); } /* * Try header prediction: in seq data or in seq pure ACK * with no funny business */ if (!nopredict_ && predict_ok(pkt)) { /* * 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(); } // // generate a stream of ecnecho bits until we see a true // cong_action bit // if (ecn_) { if (fh->ce() && fh->ect()) { // no CWR from peer yet... arrange to // keep sending ECNECHO recent_ce_ = TRUE; } else if (fh->cwr()) { // got CWR response from peer.. stop // sending ECNECHO bits recent_ce_ = FALSE; } } // Header predication basically looks to see // if the incoming packet is an expected pure ACK // or an expected data segment if (datalen == 0) { // check for a received pure ACK in the correct range.. // also checks to see if we are wnd_ limited // (we don't change cwnd at all below), plus // not being in fast recovery and not a partial ack. // If we are in fast // recovery, go below so we can remember to deflate // the window if we need to if (ackno > highest_ack_ && ackno < maxseq_ && cwnd_ >= wnd_ && !fastrecov_) { newack(pkt); // update timers, highest_ack_ send_much(0, REASON_NORMAL, maxburst_); Packet::free(pkt); return; } } else if (ackno == highest_ack_ && rq_.empty()) { // check for pure incoming segment // the next data segment we're awaiting, and // that there's nothing sitting in the reassem- // bly queue // give to "application" here // note: DELACK is inspected only by // tcp_fasttimo() in real tcp. Every 200 ms // this routine scans all tcpcb's looking for // DELACK segments and when it finds them // changes DELACK to ACKNOW and calls tcp_output() rcv_nxt_ += datalen; flags_ |= TF_DELACK; recvBytes(datalen); // notify application of "delivery" // // special code here to simulate the operation // of a receiver who always consumes data, // resulting in a call to tcp_output Packet::free(pkt); if (need_send()) send_much(1, REASON_NORMAL, maxburst_); return; } } /* header prediction */ // // header prediction failed // (e.g. pure ACK out of valid range, SACK present, etc)... // do slow path processing // // the following switch does special things for these states: // TCPS_LISTEN, TCPS_SYN_SENT // switch (state_) { /* * If the segment contains an ACK then it is bad and do reset. * If it does not contain a SYN then it is not interesting; drop it. * Otherwise initialize tp->rcv_nxt, and tp->irs, iss is already * selected, and send a segment: * <SEQ=ISS><ACK=RCV_NXT><CTL=SYN,ACK> * Initialize tp->snd_nxt to tp->iss. * Enter SYN_RECEIVED state, and process any other fields of this * segment in this state. */ case TCPS_LISTEN: /* awaiting peer's SYN */ if (tiflags & TH_ACK) { fprintf(stderr, "%f: FullTcpAgent(%s): warning: recv'd ACK while in LISTEN: ", now(), name()); prpkt(pkt); // don't want ACKs in LISTEN goto dropwithreset; } if ((tiflags & TH_SYN) == 0) { fprintf(stderr, "%f: FullTcpAgent(%s): warning: recv'd NON-SYN while in LISTEN\n", now(), name()); prpkt(pkt); // any non-SYN is discarded goto drop; } /* * 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; // check for a ECN-SYN with ECE|CWR if (ecn_ && fh->ecnecho() && fh->cong_action()) { ect_ = TRUE; } if (fid_ == 0) { // XXX: sort of hack... If we do not // have a special flow ID, pick up that // of the sender (active opener) hdr_ip* iph = hdr_ip::access(pkt); fid_ = iph->flowid(); } 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 for our SYN: ", now(), name()); prpkt(pkt); goto dropwithreset; } if ((tiflags & TH_SYN) == 0) { fprintf(stderr, "%f: FullTcpAgent::recv(%s): no SYN for our SYN: ", now(), name()); prpkt(pkt); goto drop; } /* looks like an ok SYN or SYN+ACK */#ifdef notdefcancel_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); if (tiflags & TH_ACK) { // SYN+ACK (our SYN was acked) // CHECKME // Check ECN-SYN+ACK packet if (ecn_ && fh->ecnecho() && !fh->cong_action()) ect_ = TRUE; 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -