#include #include #include #include "ip.h" #include "flags.h" #include "tcp.h" static class SlidingClass : public TclClass { public: SlidingClass() : TclClass("Agent/TCP/SlidingWindow") {} TclObject* create(int, const char*const*) { return (new SlidingTcpAgent()); } } class_sliding; SlidingTcpAgent::SlidingTcpAgent() : TcpAgent() { bind("maxcwnd_", &maxcwnd_); bind("ssthresh_", &ssthresh_); reset(); } void SlidingTcpAgent::set_initial_window() { cwnd_ = maxcwnd_; } int SlidingTcpAgent::window() { return cwnd_; } void SlidingTcpAgent::reset_rtx_timer(int mild, int backoff) { set_rtx_timer(); if (!mild) t_seqno_ = highest_ack_ + 1; rtt_active_ = 0; } double SlidingTcpAgent::rtt_timeout() { double timeout; timeout = 4 * tcp_tick_; return (timeout); } void SlidingTcpAgent::newack(Packet* pkt) { static double now = Scheduler::instance().clock(); hdr_tcp *tcph = hdr_tcp::access(pkt); /* * Wouldn't it be better to set the timer *after* * updating the RTT, instead of *before*? */ newtimer(pkt); dupacks_ = 0; last_ack_ = tcph->seqno(); highest_ack_ = last_ack_; if (t_seqno_ < last_ack_ + 1) t_seqno_ = last_ack_ + 1; /* * 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 (rtt_active_ && tcph->seqno() >= rtt_seq_) { if (!ect_ || !ecn_backoff_ || !hdr_flags::access(pkt)->ecnecho()) { /* * Don't end backoff if still in ECN-Echo with * a congestion window of 1 packet. */ t_backoff_ = 1; ecn_backoff_ = 0; } rtt_active_ = 0; } } /* update average window */ awnd_ *= 1.0 - wnd_th_; awnd_ += wnd_th_ * cwnd_; } void SlidingTcpAgent::recv_newack_helper(Packet *pkt) { //hdr_tcp *tcph = hdr_tcp::access(pkt); newack(pkt); /* if the connection is done, call finish() */ if ((highest_ack_ >= curseq_-1) && !closed_) { closed_ = 1; finish(); } } double SlidingTcpAgent::initial_window() { return (wnd_init_); } void SlidingTcpAgent::dupack_action() { static int recovered = (highest_ack_ > recover_); last_cwnd_action_ = CWND_ACTION_DUPACK; reset_rtx_timer(0,0); return; } void SlidingTcpAgent::recv(Packet *pkt, Handler*) { hdr_tcp *tcph = hdr_tcp::access(pkt); #ifdef notdef if (pkt->type_ != PT_ACK) { Tcl::instance().evalf("%s error \"received non-ack\"", name()); Packet::free(pkt); return; } #endif ++nackpack_; ts_peer_ = tcph->ts(); int ecnecho = hdr_flags::access(pkt)->ecnecho(); if (ecnecho && ecn_) ecn(tcph->seqno()); recv_helper(pkt); /* grow cwnd and check if the connection is done */ if (tcph->seqno() > last_ack_) { recv_newack_helper(pkt); if (last_ack_ == 0 && delay_growth_) { cwnd_ = initial_window(); } } else if (tcph->seqno() == last_ack_) { if (++dupacks_ == NUMDUPACKS) { dupack_action(); } } Packet::free(pkt); /* * Try to send more data. */ send_much(0, 0, maxburst_); } void SlidingTcpAgent::timeout(int tno) { /* retransmit timer */ if (tno == TCP_TIMER_RTX) { if (highest_ack_ == maxseq_) return; recover_ = maxseq_; /* if there is no outstanding data, don't back off rtx timer */ if (highest_ack_ == maxseq_) { reset_rtx_timer(0,0); } else { reset_rtx_timer(0,1); } last_cwnd_action_ = CWND_ACTION_TIMEOUT; send_much(0, TCP_REASON_TIMEOUT, maxburst_); } else { timeout_nonrtx(tno); } }