📄 tcp-advw.cc
字号:
/* * 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; } /* * If we have a timestamp reply, update smoothed * round trip time. If no timestamp is present but * transmit timer is running and timed sequence * number was acked, update smoothed round trip time. * Since we now have an rtt measurement, cancel the * timer backoff (cf., Phil Karn's retransmit alg.). * Recompute the initial retransmit timer. * * If all outstanding data is acked, stop retransmit * If there is more data to be acked, restart retransmit * timer, using current (possibly backed-off) value. */ newack(pkt); // handle timers, update highest_ack_ --pipe_; /* * if this is a partial ACK, invoke whatever we should */ int partial = pack(pkt); if (partial) pack_action(pkt); else ack_action(pkt); /* * if this is an ACK with an ECN indication, handle this */ if (fh->ecnecho()) ecn(highest_ack_); // updated by newack(), above // CHECKME: handling of rtx timer if (ackno == maxseq_) { needoutput = TRUE; } /* * If no data (only SYN) was ACK'd, * skip rest of ACK processing. */ if (ackno == (highest_ack_ + 1)) goto step6; // if we are delaying initial cwnd growth (probably due to // large initial windows), then only open cwnd if data has // been received /* * When new data is acked, open the congestion window. * If the window gives us less than ssthresh packets * in flight, open exponentially (maxseg per packet). * Otherwise open about linearly: maxseg per window * (maxseg^2 / cwnd per packet). */ if ((!delay_growth_ || (rcv_nxt_ > 0)) && last_state_ == TCPS_ESTABLISHED) { if (!partial || open_cwnd_on_pack_) opencwnd(); } // K: added state check in equal but diff way if ((state_ >= TCPS_FIN_WAIT_1) && (ackno == maxseq_)) { ourfinisacked = TRUE; } else { ourfinisacked = FALSE; } // additional processing when we're in special states switch (state_) { /* * In FIN_WAIT_1 STATE in addition to the processing * for the ESTABLISHED state if our FIN is now acknowledged * then enter FIN_WAIT_2. */ case TCPS_FIN_WAIT_1: /* doing active close */ if (ourfinisacked) { // got the ACK, now await incoming FIN newstate(TCPS_FIN_WAIT_2); cancel_timers(); needoutput = FALSE; } break; /* * In CLOSING STATE in addition to the processing for * the ESTABLISHED state if the ACK acknowledges our FIN * then enter the TIME-WAIT state, otherwise ignore * the segment. */ case TCPS_CLOSING: /* simultaneous active close */; if (ourfinisacked) { newstate(TCPS_CLOSED); cancel_timers(); } break; /* * In LAST_ACK, we may still be waiting for data to drain * and/or to be acked, as well as for the ack of our FIN. * If our FIN is now acknowledged, * enter the closed state and return. */ case TCPS_LAST_ACK: /* passive close */ // K: added state change here if (ourfinisacked) { newstate(TCPS_CLOSED);#ifdef notdefcancel_timers(); // DOES THIS BELONG HERE?, probably (see tcp_cose#endif finish(); goto drop; } else { // should be a FIN we've seen hdr_ip* iph = hdr_ip::access(pkt); fprintf(stderr, "%f: %d.%d>%d.%d FullTcpAgent::recv(%s) received non-ACK (state:%d)\n", now(), iph->saddr(), iph->sport(), iph->daddr(), iph->dport(), name(), state_); } break; /* no case for TIME_WAIT in simulator */ } // inner switch } // outer switchstep6: /* real TCP handles window updates and URG data here *//* dodata: this label is in the "real" code.. here only for reference */ /* * DATA processing */ if ((datalen > 0 || (tiflags & TH_FIN)) && TCPS_HAVERCVDFIN(state_) == 0) { if (tcph->seqno() == rcv_nxt_ && rq_.empty()) { // see the "TCP_REASS" macro for this code: // got the in-order packet we were looking // for, nobody is in the reassembly queue, // so this is the common case... // note: in "real" TCP we must also be in // ESTABLISHED state to come here, because // data arriving before ESTABLISHED is // queued in the reassembly queue. Since we // don't really have a process anyhow, just // accept the data here as-is (i.e. don't // require being in ESTABLISHED state) flags_ |= TF_DELACK; rcv_nxt_ += datalen; rcv_wnd_ -= datalen; if (datalen) recvBytes(datalen); // notify app. of "delivery" tiflags = tcph->flags() & TH_FIN; // give to "application" here needoutput = need_send(); } else { // see the "tcp_reass" function: // not the one we want next (or it // is but there's stuff on the reass queue); // do whatever we need to do for out-of-order // segments or hole-fills. Also, // send an ACK to the other side right now. // K: some changes here, figure out int rcv_nxt_old_ = rcv_nxt_; // notify app. if changes tiflags = reass(pkt); if (rcv_nxt_ > rcv_nxt_old_) recvBytes(rcv_nxt_ - rcv_nxt_old_); if (tiflags & TH_PUSH) { // K: APPLICATION recv needoutput = need_send(); } else { flags_ |= TF_ACKNOW; } } } else { /* * we're closing down or this is a pure ACK that * wasn't handled by the header prediction part above * (e.g. because cwnd < wnd) */ // K: this is deleted tiflags &= ~TH_FIN; } /* * if FIN is received, ACK the FIN * (let user know if we could do so) */ if (tiflags & TH_FIN) { if (TCPS_HAVERCVDFIN(state_) == 0) { flags_ |= TF_ACKNOW; rcv_nxt_++; } switch (state_) { /* * In SYN_RECEIVED and ESTABLISHED STATES * enter the CLOSE_WAIT state. * (passive close) */ case TCPS_SYN_RECEIVED: case TCPS_ESTABLISHED: newstate(TCPS_CLOSE_WAIT); break; /* * If still in FIN_WAIT_1 STATE FIN has not been acked so * enter the CLOSING state. * (simultaneous close) */ case TCPS_FIN_WAIT_1: newstate(TCPS_CLOSING); break; /* * In FIN_WAIT_2 state enter the TIME_WAIT state, * starting the time-wait timer, turning off the other * standard timers. * (in the simulator, just go to CLOSED) * (completion of active close) */ case TCPS_FIN_WAIT_2: newstate(TCPS_CLOSED); cancel_timers(); break; } } /* end of if FIN bit on */ if (needoutput || (flags_ & TF_ACKNOW)) send_much(1, REASON_NORMAL, maxburst_); else if (curseq_ >= highest_ack_ || infinite_send_) send_much(0, REASON_NORMAL, maxburst_); // K: which state to return to when nothing left? if (!halfclose_ && state_ == TCPS_CLOSE_WAIT && highest_ack_ == maxseq_) usrclosed(); // Packet::free(pkt); // haoboy: Is here the place for done{} of active close? // It cannot be put in the switch above because we might need to do // send_much() (an ACK) if (state_ == TCPS_CLOSED) Tcl::instance().evalf("%s done", this->name()); return;dropafterack: flags_ |= TF_ACKNOW; send_much(1, REASON_NORMAL, maxburst_); goto drop;dropwithreset: /* we should be sending an RST here, but can't in simulator */ if (tiflags & TH_ACK) { sendpacket(ackno, 0, 0x0, 0, REASON_NORMAL); } else { int ack = tcph->seqno() + datalen; if (tiflags & TH_SYN) ack--; sendpacket(0, ack, TH_ACK, 0, REASON_NORMAL); }drop:// Packet::free(pkt); return;}void AdvwTcpAgent::newack(Packet* pkt){ hdr_tcp *tcph = hdr_tcp::access(pkt); register int ackno = tcph->ackno(); int progress = (ackno > highest_ack_); if (ackno == maxseq_) { cancel_rtx_timer(); // all data ACKd } else if (progress) { set_rtx_timer(); } // advance the ack number if this is for new data if (progress) { highest_ack_ = ackno; if (app_) app_->upcall_send(); } // if we have suffered a retransmit timeout, t_seqno_ // will have been reset to highest_ ack. If the // receiver has cached some data above t_seqno_, the // new-ack value could (should) jump forward. We must // update t_seqno_ here, otherwise we would be doing // go-back-n. if (t_seqno_ < highest_ack_) t_seqno_ = highest_ack_; // seq# to send next /* * Update RTT only if it's OK to do so from info in the flags header. * This is needed for protocols in which intermediate agents * in the network intersperse acks (e.g., ack-reconstructors) for * various reasons (without violating e2e semantics). */ hdr_flags *fh = hdr_flags::access(pkt); if (!fh->no_ts_) { if (ts_option_) { recent_age_ = now(); recent_ = tcph->ts(); rtt_update(now() - tcph->ts_echo()); } else if (rtt_active_ && ackno > rtt_seq_) { // got an RTT sample, record it t_backoff_ = 1; rtt_active_ = FALSE; rtt_update(now() - rtt_ts_); } } else { printf("no_ts\n"); } return;}voidAdvwTcpAgent::sendpacket(int seqno, int ackno, int pflags, int datalen, int reason){ Packet* p = allocpkt(); hdr_tcp *tcph = hdr_tcp::access(p); /* build basic header w/options */ tcph->seqno() = seqno; tcph->ackno() = ackno; tcph->flags() = pflags; tcph->reason() |= reason; // make tcph->reason look like ns1 pkt->flags? tcph->sa_length() = 0; // may be increased by build_options() tcph->hlen() = tcpip_base_hdr_size_; tcph->hlen() += build_options(tcph); tcph->advwin() = rcv_wnd_; /* ECT, ECN, and congestion action */ hdr_flags *fh = hdr_flags::access(p); fh->ect() = ect_; // on after mutual agreement on ECT if (ecn_ && !ect_) // initializing; ect = 0, ecnecho = 1 fh->ecnecho() = 1; else { fh->ecnecho() = recent_ce_; } fh->cong_action() = cong_action_; /* actual size is data length plus header length */ hdr_cmn *ch = hdr_cmn::access(p); ch->size() = datalen + tcph->hlen(); if (datalen <= 0) ++nackpack_; else { ++ndatapack_; ndatabytes_ += datalen; last_send_time_ = now(); // time of last data } if (reason == REASON_TIMEOUT || reason == REASON_DUPACK) { ++nrexmitpack_; nrexmitbytes_ += datalen; } last_ack_sent_ = ackno; send(p, 0);}int AdvwTcpAgent::command(int argc, const char*const* argv){ Tcl& tcl = Tcl::instance(); if(strcmp(argv[1], "inf-rcv")==0) { if(strcmp(argv[2], "true")==0) infinite_rcv_ = TRUE; if(strcmp(argv[2], "false")==0) infinite_rcv_ = FALSE; return TCL_OK; } if(strcmp(argv[1], "recv")==0) { tcp_command_block_receive(atoi(argv[2])); return TCL_OK; } return FullTcpAgent::command(argc, argv);} AdvwTcpApplication::AdvwTcpApplication(): Application(){}AdvwTcpApplication::~AdvwTcpApplication(){}int AdvwTcpApplication::upcall_recv(Packet *){ printf("AdvwTcpApplication upcall_recv\n"); }void AdvwTcpApplication::upcall_send(){}void AdvwTcpApplication::upcall_closing() {}int AdvwTcpApplication::command(int argc, const char*const* argv){ Tcl& tcl = Tcl::instance(); if (argc == 3) { if (strcmp(argv[1], "attach-agent") == 0) { agent_ = (Agent*) TclObject::lookup(argv[2]); if (agent_ == 0) { tcl.resultf("no such agent %s", argv[2]); return(TCL_ERROR); } awagent_ = (AdvwTcpAgent *)agent_; awagent_->attachApp(this); return(TCL_OK); } } return Application::command(argc, argv);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -