/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */ /* * Copyright (c) 1991-1997 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the Computer Systems * Engineering Group at Lawrence Berkeley Laboratory. * 4. Neither the name of the University nor of the Laboratory may be used * to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char rcsid[] = "@(#) $Header: /nfs/jade/vint/CVSROOT/ns-2/tcp/tcp.cc,v 1.132 2002/03/29 05:06:33 sfloyd Exp $ (LBL)"; #endif #include #include #include "ip.h" #include "tcp.h" #include "flags.h" #include "random.h" #include "basetrace.h" //sale >>> #include "formula.h" #include "mmy-formula-with-inverse.h" //sale <<< int hdr_tcp::offset_; static class TCPHeaderClass : public PacketHeaderClass { public: TCPHeaderClass() : PacketHeaderClass("PacketHeader/TCP", sizeof(hdr_tcp)) { bind_offset(&hdr_tcp::offset_); } } class_tcphdr; static class TcpClass : public TclClass { public: TcpClass() : TclClass("Agent/TCP") {} TclObject* create(int , const char*const*) { return (new TcpAgent()); } } class_tcp; TcpAgent::TcpAgent() : Agent(PT_TCP), t_seqno_(0), t_rtt_(0), t_srtt_(0), t_rttvar_(0), t_backoff_(0), ts_peer_(0), rtx_timer_(this), delsnd_timer_(this), burstsnd_timer_(this), dupacks_(0), curseq_(0), highest_ack_(0), cwnd_(0), ssthresh_(0), count_(0), fcnt_(0), rtt_active_(0), rtt_seq_(-1), rtt_ts_(0.0), maxseq_(0), cong_action_(0), ecn_burst_(0), ecn_backoff_(0), ect_(0), restart_bugfix_(1), closed_(0), nrexmit_(0), first_decrease_(1), lastreset_(0.0), timer_(this) { #ifdef TCP_DELAY_BIND_ALL //sale >>> Maxseq_sale_ = 0; n_loss_int_used = 1; n_rtt_ = 0.11; n_rtt_sum_ = 0.11; n_rtt_num_ = 1; // size_ = 40; n_hsz = 100000; n_NumFeedback_ = 1; n_adjust_history_after_ss = 0; n_numsamples = -1; n_discount = 1; n_printLoss_ = 0; n_smooth_ = 1 ; n_minlc = 4; n_algo = 1; // maxint_ = 1000; n_history = 0.75; n_sum_bytes = 1; n_time_last = 0; n_rate_last = 0; //sale <<< //sale >>> // printf("CHECK 1\n"); bind("eln_", &eln_); n_sum_bytes = 0; n_time_last = 0; n_rate_last = 0; bind("ndatapack_", &ndatapack_); bind("ndatabytes_", &ndatabytes_); bind("nrexmitpack_", &nrexmitpack_); bind("nrexmitbytes_", &nrexmitbytes_); //sale <<< //sale >>> bind("n_loss_int_used_", &n_loss_int_used); bind("n_rtt_", &n_rtt_); bind("n_rtt_sum_", &n_rtt_sum_); bind("n_rtt_num_", &n_rtt_num_); //from TFRC bind("n_InitHistorySize_", &n_hsz); bind("n_NumFeedback_", &n_NumFeedback_); bind ("n_AdjustHistoryAfterSS_", &n_adjust_history_after_ss); bind ("n_printLoss_", &n_printLoss_); bind ("n_algo_", &n_algo); // algo for loss estimation // for WALI ONLY bind ("n_NumSamples_", &n_numsamples); bind ("n_discount_", &n_discount); bind ("n_smooth_", &n_smooth_); // EWMA use only bind ("n_history_", &n_history); // EWMA history // for RBPH use only bind("n_minlc_", &n_minlc); //sale!!! original: rtt_ = 0; n_rtt_ = 0.11; n_rtt_sum_ = 0.11; n_rtt_num_ = 1; n_tzero_ = 1.0; //1 second RFC 1323 // last_timestamp_ = 0; // last_arrival_ = 0; n_last_report_sent=0; // maxseq = -1; We now use maxseq_ n_rcvd_since_last_report = 0; // losses_since_last_report = 0; n_loss_seen_yet = 0; n_lastloss = 0; // n_lastloss_round_id = -1 ; // UrgentFlag = 0 ; n_rtvec_ = NULL; n_tsvec_ = NULL; n_lossvec_ = NULL; // used by WALI and EWMA n_last_sample = 0; // used only for WALI n_false_sample = 0; n_sample = NULL ; n_weights = NULL ; n_mult = NULL ; n_sample_count = 1 ; n_mult_factor_ = 1.0; n_init_WALI_flag = 0; // used only for EWMA n_avg_loss_int = -1 ; n_loss_int = 0 ; // used only bu RBPH n_sendrate = 0 ; // current send rate //sale <<< bind("t_seqno_", &t_seqno_); bind("rtt_", &t_rtt_); bind("srtt_", &t_srtt_); bind("rttvar_", &t_rttvar_); bind("backoff_", &t_backoff_); bind("dupacks_", &dupacks_); bind("seqno_", &curseq_); bind("ack_", &highest_ack_); bind("cwnd_", &cwnd_); bind("ssthresh_", &ssthresh_); bind("maxseq_", &maxseq_); bind("ndatapack_", &ndatapack_); bind("ndatabytes_", &ndatabytes_); bind("nackpack_", &nackpack_); bind("nrexmit_", &nrexmit_); bind("nrexmitpack_", &nrexmitpack_); bind("nrexmitbytes_", &nrexmitbytes_); bind("necnresponses_", &necnresponses_); bind("ncwndcuts_", &ncwndcuts_); bind("singledup_", &singledup_); #else /* ! TCP_DELAY_BIND_ALL */ // not delay-bound because delay-bound tracevars aren't yet supported //not active bind("t_seqno_", &t_seqno_); bind("rtt_", &t_rtt_); bind("srtt_", &t_srtt_); bind("rttvar_", &t_rttvar_); bind("backoff_", &t_backoff_); bind("dupacks_", &dupacks_); bind("seqno_", &curseq_); bind("ack_", &highest_ack_); bind("cwnd_", &cwnd_); bind("ssthresh_", &ssthresh_); bind("maxseq_", &maxseq_); bind("ndatapack_", &ndatapack_); bind("ndatabytes_", &ndatabytes_); bind("nackpack_", &nackpack_); bind("nrexmit_", &nrexmit_); bind("nrexmitpack_", &nrexmitpack_); bind("nrexmitbytes_", &nrexmitbytes_); bind("necnresponses_", &necnresponses_); bind("ncwndcuts_", &ncwndcuts_); bind("singledup_", &singledup_); #endif /* TCP_DELAY_BIND_ALL */ //double curseq__ = curseq_; //printf("CHECK2, n_hsz= %d, TCPDELAYBINDALL: %d curseq=%d\n", n_hsz, TCP_DELAY_BIND_ALL, curseq__); //exit(1); } void TcpAgent::delay_bind_init_all() { // Defaults for bound variables should be set in ns-default.tcl. delay_bind_init_one("window_"); delay_bind_init_one("windowInit_"); delay_bind_init_one("windowInitOption_"); delay_bind_init_one("syn_"); delay_bind_init_one("windowOption_"); delay_bind_init_one("windowConstant_"); delay_bind_init_one("windowThresh_"); delay_bind_init_one("delay_growth_"); delay_bind_init_one("overhead_"); delay_bind_init_one("tcpTick_"); delay_bind_init_one("ecn_"); delay_bind_init_one("old_ecn_"); delay_bind_init_one("eln_"); delay_bind_init_one("eln_rxmit_thresh_"); delay_bind_init_one("packetSize_"); delay_bind_init_one("tcpip_base_hdr_size_"); delay_bind_init_one("ts_option_size_"); delay_bind_init_one("bugFix_"); delay_bind_init_one("slow_start_restart_"); delay_bind_init_one("restart_bugfix_"); delay_bind_init_one("timestamps_"); delay_bind_init_one("maxburst_"); delay_bind_init_one("maxcwnd_"); delay_bind_init_one("numdupacks_"); delay_bind_init_one("maxrto_"); delay_bind_init_one("minrto_"); delay_bind_init_one("srtt_init_"); delay_bind_init_one("rttvar_init_"); delay_bind_init_one("rtxcur_init_"); delay_bind_init_one("T_SRTT_BITS"); delay_bind_init_one("T_RTTVAR_BITS"); delay_bind_init_one("rttvar_exp_"); delay_bind_init_one("awnd_"); delay_bind_init_one("decrease_num_"); delay_bind_init_one("increase_num_"); delay_bind_init_one("k_parameter_"); delay_bind_init_one("l_parameter_"); delay_bind_init_one("trace_all_oneline_"); delay_bind_init_one("nam_tracevar_"); delay_bind_init_one("QOption_"); delay_bind_init_one("EnblRTTCtr_"); delay_bind_init_one("control_increase_"); delay_bind_init_one("noFastRetrans_"); delay_bind_init_one("precisionReduce_"); delay_bind_init_one("oldCode_"); delay_bind_init_one("useHeaders_"); delay_bind_init_one("low_window_"); delay_bind_init_one("high_window_"); delay_bind_init_one("high_p_"); delay_bind_init_one("high_decrease_"); delay_bind_init_one("timerfix_"); delay_bind_init_one("rfc2988_"); #ifdef TCP_DELAY_BIND_ALL // out because delay-bound tracevars aren't yet supported delay_bind_init_one("t_seqno_"); delay_bind_init_one("rtt_"); delay_bind_init_one("srtt_"); delay_bind_init_one("rttvar_"); delay_bind_init_one("backoff_"); delay_bind_init_one("dupacks_"); delay_bind_init_one("seqno_"); delay_bind_init_one("ack_"); delay_bind_init_one("cwnd_"); delay_bind_init_one("ssthresh_"); delay_bind_init_one("maxseq_"); delay_bind_init_one("ndatapack_"); delay_bind_init_one("ndatabytes_"); delay_bind_init_one("nackpack_"); delay_bind_init_one("nrexmit_"); delay_bind_init_one("nrexmitpack_"); delay_bind_init_one("nrexmitbytes_"); delay_bind_init_one("necnresponses_"); delay_bind_init_one("ncwndcuts_"); delay_bind_init_one("singledup_"); //sale >>> delay_bind_init_one("n_TCP_rate_"); delay_bind_init_one("n_est_loss_"); delay_bind_init_one("n_TFRC_rate_"); delay_bind_init_one("n_ploss_"); delay_bind_init_one("n_EQ_rate_"); delay_bind_init_one("n_ratio_TCP_TFRC_"); delay_bind_init_one("n_ratio_TCP_EQ_"); delay_bind_init_one("n_loss_int_used_"); // delay_bind_init_one("n_rtt_"); delay_bind_init_one("n_rtt_sum_"); delay_bind_init_one("n_rtt_num_"); // delay_bind_init_one("n_packetSize_"); delay_bind_init_one("n_InitHistorySize_"); delay_bind_init_one("n_NumFeedback_"); delay_bind_init_one("n_AdjustHistoryAfterSS_"); delay_bind_init_one("n_NumSamples_"); delay_bind_init_one("n_discount_"); delay_bind_init_one("n_printLoss_"); delay_bind_init_one("n_smooth_"); delay_bind_init_one("n_minlc_"); delay_bind_init_one("n_algo_"); delay_bind_init_one("n_maxint_"); delay_bind_init_one("n_history_"); //sale <<< #endif /* TCP_DELAY_BIND_ALL */ Agent::delay_bind_init_all(); //printf("BEFORE RESET\n"); reset(); //printf("AFTER RESET\n"); } int TcpAgent::delay_bind_dispatch(const char *varName, const char *localName, TclObject *tracer) { if (delay_bind(varName, localName, "window_", &wnd_, tracer)) return TCL_OK; if (delay_bind(varName, localName, "windowInit_", &wnd_init_, tracer)) return TCL_OK; if (delay_bind(varName, localName, "windowInitOption_", &wnd_init_option_, tracer)) return TCL_OK; if (delay_bind_bool(varName, localName, "syn_", &syn_, tracer)) return TCL_OK; if (delay_bind(varName, localName, "windowOption_", &wnd_option_ , tracer)) return TCL_OK; if (delay_bind(varName, localName, "windowConstant_", &wnd_const_, tracer)) return TCL_OK; if (delay_bind(varName, localName, "windowThresh_", &wnd_th_ , tracer)) return TCL_OK; if (delay_bind_bool(varName, localName, "delay_growth_", &delay_growth_ , tracer)) return TCL_OK; if (delay_bind(varName, localName, "overhead_", &overhead_, tracer)) return TCL_OK; if (delay_bind(varName, localName, "tcpTick_", &tcp_tick_, tracer)) return TCL_OK; if (delay_bind_bool(varName, localName, "ecn_", &ecn_, tracer)) return TCL_OK; if (delay_bind_bool(varName, localName, "old_ecn_", &old_ecn_ , tracer)) return TCL_OK; if (delay_bind(varName, localName, "eln_", &eln_ , tracer)) return TCL_OK; if (delay_bind(varName, localName, "eln_rxmit_thresh_", &eln_rxmit_thresh_ , tracer)) return TCL_OK; if (delay_bind(varName, localName, "packetSize_", &size_ , tracer)) return TCL_OK; if (delay_bind(varName, localName, "tcpip_base_hdr_size_", &tcpip_base_hdr_size_, tracer)) return TCL_OK; if (delay_bind(varName, localName, "ts_option_size_", &ts_option_size_, tracer)) return TCL_OK; if (delay_bind_bool(varName, localName, "bugFix_", &bug_fix_ , tracer)) return TCL_OK; if (delay_bind_bool(varName, localName, "slow_start_restart_", &slow_start_restart_ , tracer)) return TCL_OK; if (delay_bind_bool(varName, localName, "restart_bugfix_", &restart_bugfix_ , tracer)) return TCL_OK; if (delay_bind_bool(varName, localName, "timestamps_", &ts_option_ , tracer)) return TCL_OK; if (delay_bind(varName, localName, "maxburst_", &maxburst_ , tracer)) return TCL_OK; if (delay_bind(varName, localName, "maxcwnd_", &maxcwnd_ , tracer)) return TCL_OK; if (delay_bind(varName, localName, "numdupacks_", &numdupacks_, tracer)) return TCL_OK; if (delay_bind(varName, localName, "maxrto_", &maxrto_ , tracer)) return TCL_OK; if (delay_bind(varName, localName, "minrto_", &minrto_ , tracer)) return TCL_OK; if (delay_bind(varName, localName, "srtt_init_", &srtt_init_ , tracer)) return TCL_OK; if (delay_bind(varName, localName, "rttvar_init_", &rttvar_init_ , tracer)) return TCL_OK; if (delay_bind(varName, localName, "rtxcur_init_", &rtxcur_init_ , tracer)) return TCL_OK; if (delay_bind(varName, localName, "T_SRTT_BITS", &T_SRTT_BITS , tracer)) return TCL_OK; if (delay_bind(varName, localName, "T_RTTVAR_BITS", &T_RTTVAR_BITS , tracer)) return TCL_OK; if (delay_bind(varName, localName, "rttvar_exp_", &rttvar_exp_ , tracer)) return TCL_OK; if (delay_bind(varName, localName, "awnd_", &awnd_ , tracer)) return TCL_OK; if (delay_bind(varName, localName, "decrease_num_", &decrease_num_, tracer)) return TCL_OK; if (delay_bind(varName, localName, "increase_num_", &increase_num_, tracer)) return TCL_OK; if (delay_bind(varName, localName, "k_parameter_", &k_parameter_, tracer)) return TCL_OK; if (delay_bind(varName, localName, "l_parameter_", &l_parameter_, tracer)) return TCL_OK; if (delay_bind_bool(varName, localName, "trace_all_oneline_", &trace_all_oneline_ , tracer)) return TCL_OK; if (delay_bind_bool(varName, localName, "nam_tracevar_", &nam_tracevar_ , tracer)) return TCL_OK; if (delay_bind(varName, localName, "QOption_", &QOption_ , tracer)) return TCL_OK; if (delay_bind(varName, localName, "EnblRTTCtr_", &EnblRTTCtr_ , tracer)) return TCL_OK; if (delay_bind(varName, localName, "control_increase_", &control_increase_ , tracer)) return TCL_OK; if (delay_bind_bool(varName, localName, "oldCode_", &oldCode_, tracer)) return TCL_OK; if (delay_bind_bool(varName, localName, "useHeaders_", &useHeaders_, tracer)) return TCL_OK; if (delay_bind(varName, localName, "low_window_", &low_window_, tracer)) return TCL_OK; if (delay_bind(varName, localName, "high_window_", &high_window_, tracer)) return TCL_OK; if (delay_bind(varName, localName, "high_p_", &high_p_, tracer)) return TCL_OK; if (delay_bind(varName, localName, "high_decrease_", &high_decrease_, tracer)) return TCL_OK; if (delay_bind_bool(varName, localName, "timerfix_", &timerfix_, tracer)) return TCL_OK; if (delay_bind_bool(varName, localName, "rfc2988_", &rfc2988_, tracer)) return TCL_OK; #ifdef TCP_DELAY_BIND_ALL // not if (delay-bound delay-bound tracevars aren't yet supported if (delay_bind(varName, localName, "t_seqno_", &t_seqno_ , tracer)) return TCL_OK; if (delay_bind(varName, localName, "rtt_", &t_rtt_ , tracer)) return TCL_OK; if (delay_bind(varName, localName, "srtt_", &t_srtt_ , tracer)) return TCL_OK; if (delay_bind(varName, localName, "rttvar_", &t_rttvar_ , tracer)) return TCL_OK; if (delay_bind(varName, localName, "backoff_", &t_backoff_ , tracer)) return TCL_OK; if (delay_bind(varName, localName, "dupacks_", &dupacks_ , tracer)) return TCL_OK; if (delay_bind(varName, localName, "seqno_", &curseq_ , tracer)) return TCL_OK; if (delay_bind(varName, localName, "ack_", &highest_ack_ , tracer)) return TCL_OK; if (delay_bind(varName, localName, "cwnd_", &cwnd_ , tracer)) return TCL_OK; if (delay_bind(varName, localName, "ssthresh_", &ssthresh_ , tracer)) return TCL_OK; if (delay_bind(varName, localName, "maxseq_", &maxseq_ , tracer)) return TCL_OK; if (delay_bind(varName, localName, "ndatapack_", &ndatapack_ , tracer)) return TCL_OK; if (delay_bind(varName, localName, "ndatabytes_", &ndatabytes_ , tracer)) return TCL_OK; if (delay_bind(varName, localName, "nackpack_", &nackpack_ , tracer)) return TCL_OK; if (delay_bind(varName, localName, "nrexmit_", &nrexmit_ , tracer)) return TCL_OK; if (delay_bind(varName, localName, "nrexmitpack_", &nrexmitpack_ , tracer)) return TCL_OK; if (delay_bind(varName, localName, "nrexmitbytes_", &nrexmitbytes_ , tracer)) return TCL_OK; if (delay_bind(varName, localName, "necnresponses_", &necnresponses_ , tracer)) return TCL_OK; if (delay_bind(varName, localName, "ncwndcuts_", &ncwndcuts_ , tracer)) return TCL_OK; if (delay_bind(varName, localName, "singledup_", &singledup_ , tracer)) return TCL_OK; if (delay_bind_bool(varName, localName, "noFastRetrans_", &noFastRetrans_, tracer)) return TCL_OK; if (delay_bind_bool(varName, localName, "precisionReduce_", &precision_reduce_, tracer)) return TCL_OK; //sale >>> if (delay_bind(varName,localName,"TCP_rate_",&n_TCP_rate_,tracer)) return TCL_OK; if (delay_bind(varName,localName,"est_loss_",&n_est_loss_,tracer)) return TCL_OK; if (delay_bind(varName,localName,"TFRC_rate_",&n_TFRC_rate_,tracer)) return TCL_OK; if (delay_bind(varName,localName,"ploss_",&n_ploss_,tracer)) return TCL_OK; if (delay_bind(varName,localName,"EQ_rate_",&n_EQ_rate_,tracer)) return TCL_OK; if (delay_bind(varName,localName,"ratio_TCP_TFRC_",&n_ratio_TCP_TFRC_,tracer)) return TCL_OK; if (delay_bind(varName,localName,"ratio_TCP_EQ_",&n_ratio_TCP_EQ_,tracer)) return TCL_OK; //sale <<< #endif return Agent::delay_bind_dispatch(varName, localName, tracer); } /* Print out all the traced variables whenever any one is changed */ void TcpAgent::traceAll() { double curtime; Scheduler& s = Scheduler::instance(); char wrk[500]; int n; curtime = &s ? s.clock() : 0; sprintf(wrk,"time: %-8.5f saddr: %-2d sport: %-2d daddr: %-2d dport:" " %-2d maxseq: %-4d hiack: %-4d seqno: %-4d cwnd: %-6.3f" " ssthresh: %-3d dupacks: %-2d rtt: %-6.3f srtt: %-6.3f" " rttvar: %-6.3f bkoff: %-d", curtime, addr(), port(), daddr(), dport(), int(maxseq_), int(highest_ack_), int(t_seqno_), double(cwnd_), int(ssthresh_), int(dupacks_), int(t_rtt_)*tcp_tick_, (int(t_srtt_) >> T_SRTT_BITS)*tcp_tick_, int(t_rttvar_)*tcp_tick_/4.0, int(t_backoff_)); n = strlen(wrk); wrk[n] = '\n'; wrk[n+1] = 0; if (channel_) (void)Tcl_Write(channel_, wrk, n+1); wrk[n] = 0; return; } /* Print out just the variable that is modified */ void TcpAgent::traceVar(TracedVar* v) { double curtime; Scheduler& s = Scheduler::instance(); char wrk[500]; int n; curtime = &s ? s.clock() : 0; if (!strcmp(v->name(), "cwnd_") || !strcmp(v->name(), "maxrto_")) sprintf(wrk,"%-8.5f %-2d %-2d %-2d %-2d %s %-6.3f", curtime, addr(), port(), daddr(), dport(), v->name(), double(*((TracedDouble*) v))); else if (!strcmp(v->name(), "minrto_")) sprintf(wrk,"%-8.5f %-2d %-2d %-2d %-2d %s %-6.3f", curtime, addr(), port(), daddr(), dport(), v->name(), double(*((TracedDouble*) v))); else if (!strcmp(v->name(), "rtt_")) sprintf(wrk,"%-8.5f %-2d %-2d %-2d %-2d %s %-6.3f", curtime, addr(), port(), daddr(), dport(), v->name(), int(*((TracedInt*) v))*tcp_tick_); else if (!strcmp(v->name(), "srtt_")) sprintf(wrk,"%-8.5f %-2d %-2d %-2d %-2d %s %-6.3f", curtime, addr(), port(), daddr(), dport(), v->name(), (int(*((TracedInt*) v)) >> T_SRTT_BITS)*tcp_tick_); else if (!strcmp(v->name(), "rttvar_")) sprintf(wrk,"%-8.5f %-2d %-2d %-2d %-2d %s %-6.3f", curtime, addr(), port(), daddr(), dport(), v->name(), int(*((TracedInt*) v))*tcp_tick_/4.0); else sprintf(wrk,"%-8.5f %-2d %-2d %-2d %-2d %s %d", curtime, addr(), port(), daddr(), dport(), v->name(), int(*((TracedInt*) v))); n = strlen(wrk); wrk[n] = '\n'; wrk[n+1] = 0; if (channel_) (void)Tcl_Write(channel_, wrk, n+1); wrk[n] = 0; return; } void TcpAgent::trace(TracedVar* v) { if (nam_tracevar_) { Agent::trace(v); } else if (trace_all_oneline_) traceAll(); else traceVar(v); } // // in 1-way TCP, syn_ indicates we are modeling // a SYN exchange at the beginning. If this is true // and we are delaying growth, then use an initial // window of one. If not, we do whatever initial_window() // says to do. // void TcpAgent::set_initial_window() { if (syn_ && delay_growth_) cwnd_ = 1.0; else cwnd_ = initial_window(); } void TcpAgent::reset_qoption() { int now = (int)(Scheduler::instance().clock()/tcp_tick_ + 0.5); T_start = now ; RTT_count = 0 ; RTT_prev = 0 ; RTT_goodcount = 1 ; F_counting = 0 ; W_timed = -1 ; F_full = 0 ; Backoffs = 0 ; //sale >>> n_TCP_rate_ = 0; n_est_loss_ = 0; n_TFRC_rate_ = 0; n_ploss_ = 0; n_EQ_rate_ = 0; n_ratio_TCP_TFRC_ = 0; n_ratio_TCP_EQ_ = 0; //sale <<< } void TcpAgent::reset() { rtt_init(); rtt_seq_ = -1; /*XXX lookup variables */ dupacks_ = 0; curseq_ = 0; set_initial_window(); t_seqno_ = 0; maxseq_ = -1; last_ack_ = -1; highest_ack_ = -1; ssthresh_ = int(wnd_); wnd_restart_ = 1.; awnd_ = wnd_init_ / 2.0; recover_ = 0; closed_ = 0; last_cwnd_action_ = 0; boot_time_ = Random::uniform(tcp_tick_); first_decrease_ = 1; /* W.N.: for removing packets from previous incarnations */ lastreset_ = Scheduler::instance().clock(); /* Now these variables will be reset - Debojyoti Dutta 12th Oct'2000 */ ndatapack_ = 0; ndatabytes_ = 0; nackpack_ = 0; nrexmitbytes_ = 0; nrexmit_ = 0; nrexmitpack_ = 0; necnresponses_ = 0; ncwndcuts_ = 0; //sale >>> n_loss_int_used = 1; n_rtt_ = 0.11; n_rtt_sum_ = 0.11; n_rtt_num_ = 1; // n_size_ = 40; n_hsz = 100; n_NumFeedback_ = 1; n_adjust_history_after_ss = 0; n_numsamples = -1; n_discount = 1; n_printLoss_ = 0; n_smooth_ = 1 ; n_minlc = 4; n_algo = 1; // maxint_ = 1000; n_history = 0.75; n_sum_bytes = 0; n_time_last = 0; n_rate_last = 0; //sale <<< if (control_increase_) { prev_highest_ack_ = highest_ack_ ; } if (QOption_) { int now = (int)(Scheduler::instance().clock()/tcp_tick_ + 0.5); T_last = now ; T_prev = now ; W_used = 0 ; if (EnblRTTCtr_) { reset_qoption(); } } } /* * Initialize variables for the retransmit timer. */ void TcpAgent::rtt_init() { t_rtt_ = 0; t_srtt_ = int(srtt_init_ / tcp_tick_) << T_SRTT_BITS; t_rttvar_ = int(rttvar_init_ / tcp_tick_) << T_RTTVAR_BITS; t_rtxcur_ = rtxcur_init_; t_backoff_ = 1; } double TcpAgent::rtt_timeout() { double timeout; if (rfc2988_) { // Correction from Tom Kelly to be RFC2988-compliant, by // clamping minrto_ before applying t_backoff_. if (t_rtxcur_ < minrto_) timeout = minrto_ * t_backoff_; else timeout = t_rtxcur_ * t_backoff_; } else { timeout = t_rtxcur_ * t_backoff_; if (timeout < minrto_) timeout = minrto_; } if (timeout > maxrto_) timeout = maxrto_; if (timeout < 2.0 * tcp_tick_) { if (timeout < 0) { fprintf(stderr, "TcpAgent: negative RTO! (%f)\n", timeout); exit(1); } timeout = 2.0 * tcp_tick_; } return (timeout); } /* This has been modified to use the tahoe code. */ void TcpAgent::rtt_update(double tao) { double now = Scheduler::instance().clock(); if (ts_option_) t_rtt_ = int(tao /tcp_tick_ + 0.5); else { double sendtime = now - tao; sendtime += boot_time_; double tickoff = fmod(sendtime, tcp_tick_); t_rtt_ = int((tao + tickoff) / tcp_tick_); } if (t_rtt_ < 1) t_rtt_ = 1; // // srtt has 3 bits to the right of the binary point // rttvar has 2 // if (t_srtt_ != 0) { register short delta; delta = t_rtt_ - (t_srtt_ >> T_SRTT_BITS); // d = (m - a0) if ((t_srtt_ += delta) <= 0) // a1 = 7/8 a0 + 1/8 m t_srtt_ = 1; if (delta < 0) delta = -delta; delta -= (t_rttvar_ >> T_RTTVAR_BITS); if ((t_rttvar_ += delta) <= 0) // var1 = 3/4 var0 + 1/4 |d| t_rttvar_ = 1; } else { t_srtt_ = t_rtt_ << T_SRTT_BITS; // srtt = rtt t_rttvar_ = t_rtt_ << (T_RTTVAR_BITS-1); // rttvar = rtt / 2 } // // Current retransmit value is // (unscaled) smoothed round trip estimate // plus 2^rttvar_exp_ times (unscaled) rttvar. // t_rtxcur_ = (((t_rttvar_ << (rttvar_exp_ + (T_SRTT_BITS - T_RTTVAR_BITS))) + t_srtt_) >> T_SRTT_BITS ) * tcp_tick_; // sale >>> n_rtt_ = (int(t_srtt_)>>T_SRTT_BITS)*tcp_tick_; // sale <<< return; } void TcpAgent::rtt_backoff() { if (t_backoff_ < 64) t_backoff_ <<= 1; if (t_backoff_ > 8) { /* * If backed off this far, clobber the srtt * value, storing it in the mean deviation * instead. */ t_rttvar_ += (t_srtt_ >> T_SRTT_BITS); t_srtt_ = 0; } } /* * headersize: * how big is an IP+TCP header in bytes; include options such as ts * this function should be virtual so others (e.g. SACK) can override */ int TcpAgent::headersize() { int total = tcpip_base_hdr_size_; if (total < 1) { fprintf(stderr, "TcpAgent(%s): warning: tcpip hdr size is only %d bytes\n", name(), tcpip_base_hdr_size_); } if (ts_option_) total += ts_option_size_; return (total); } void TcpAgent::output(int seqno, int reason) { int force_set_rtx_timer = 0; Packet* p = allocpkt(); hdr_tcp *tcph = hdr_tcp::access(p); hdr_flags* hf = hdr_flags::access(p); int databytes = hdr_cmn::access(p)->size(); tcph->seqno() = seqno; tcph->ts() = Scheduler::instance().clock(); tcph->ts_echo() = ts_peer_; tcph->reason() = reason; //sale >>> double now = Scheduler::instance().clock(); //printf("sending packet number %d, time: %f\n", seqno, now); //sale <<< //sale>>> if(ndatapack_==0) { n_last_report_sent = now; // printf("Last report sent = %f\n",n_last_report_sent); } //sale<<< if (ecn_) { hf->ect() = 1; // ECN-capable transport } if (cong_action_) { hf->cong_action() = TRUE; // Congestion action. cong_action_ = FALSE; } /* Check if this is the initial SYN packet. */ if (seqno == 0) { if (syn_) { databytes = 0; curseq_ += 1; hdr_cmn::access(p)->size() = tcpip_base_hdr_size_; } if (ecn_) { hf->ecnecho() = 1; // hf->cong_action() = 1; hf->ect() = 0; } } else if (useHeaders_ == true) { hdr_cmn::access(p)->size() += headersize(); } hdr_cmn::access(p)->size(); /* if no outstanding data, be sure to set rtx timer again */ if (highest_ack_ == maxseq_) force_set_rtx_timer = 1; /* call helper function to fill in additional fields */ output_helper(p); //printf("CHECK4.3, seqno=%d, n_hsz:%d\n", seqno, n_hsz); //printf("CHECK2.5\n"); //exit(1); ++ndatapack_; ndatabytes_ += databytes; send(p, 0); //sale >>> //printf("CHECK3\n"); //exit(1); if (maxseq_ < 0) { //printf("CHECK4.4, seqno=%d, n_hsz:%d\n", seqno, n_hsz); n_rtvec_=(double *)malloc(sizeof(double)*n_hsz); n_tsvec_=(double *)malloc(sizeof(double)*n_hsz); n_lossvec_=(char *)malloc(sizeof(double)*n_hsz); if (n_rtvec_ && n_lossvec_ && n_tsvec_) { int i; for (i = 0; i < n_hsz ; i ++) { n_lossvec_[i] = UNKNOWN; n_rtvec_[i] = -1; n_tsvec_[i] = -1; } for (i = 0; i <= (maxseq_+1) ; i++) { n_lossvec_[i] = NOLOSS ; n_rtvec_[i] = now ; //we dont' use this n_tsvec_[i] = now ; } } else { printf ("error allocating memory for packet buffers\n"); abort (); } } //printf("CHECK4\n"); //exit(1); //sale <<< if (seqno == curseq_ && seqno > maxseq_) idle(); // Tell application I have sent everything so far if (seqno > maxseq_) { maxseq_ = seqno; //sale >>> //printf("CHECK4.5, seqno=%d, n_hsz:%d\n", seqno, n_hsz); //Maxseq_sale_ = seqno; n_lossvec_[seqno%n_hsz] = RCVD; //actually SEND n_rtvec_[seqno%n_hsz]=now; //we don't use this n_tsvec_[seqno%n_hsz]=now; // printf("SENT PACKET NO %d, MAXSEQ = %d, time= %f\n", seqno, (maxseq_+1-1), n_tsvec_[seqno%n_hsz]); //sale <<< if (!rtt_active_) { rtt_active_ = 1; if (seqno > rtt_seq_) { rtt_seq_ = seqno; rtt_ts_ = Scheduler::instance().clock(); } } } else { //sale >>> //printf("REXMIT: %d\n", seqno); //exit(1); //original // ++nrexmitpack_; // nrexmitbytes_ += databytes; //end original //we: //they have a bug(?) here because the packets are not sent in order if (n_tsvec_[seqno%n_hsz] < 0.0){ n_lossvec_[seqno%n_hsz] = RCVD; //actually SEND n_rtvec_[seqno%n_hsz]=now; //we don't use this n_tsvec_[seqno%n_hsz]=now; //printf("SENT PACKET NO %d, MAXSEQ = %d, time= %f\n", seqno, (maxseq_+1-1), n_tsvec_[seqno%n_hsz]); } else { ++nrexmitpack_; nrexmitbytes_ += databytes; //printf("REXMIT1: %d\n", seqno); //exit(1); //printf("RXMIT PACKET NO %d, MAXSEQ = %d, time= %f\n", seqno, (maxseq_+1-1), n_tsvec_[seqno%n_hsz]); n_lossvec_[seqno%n_hsz] = LOST; //only this should compute less throughput //printf("REXMIT2: %d\n", seqno); //exit(1); //but we want to compute the upper bound, and remove losses within //the same rtt //printf("loss_int_used = %d\n", loss_int_used); if (n_loss_int_used == 1) { //printf("n_loss_int_used=1\n"); double time = n_tsvec_[seqno%n_hsz] - n_rtt_; //printf ("START: rtt_: %3.3f bound_time:%3.3f\n", rtt_, time); if (seqno>=1){ int ind=0; for (int i=seqno-1;(i>=0)&&(n_tsvec_[i%n_hsz]>=time);i--) { if (n_lossvec_[i%n_hsz] == LOST) { n_lossvec_[seqno%n_hsz]=NOLOSS; ind=1; //printf ("START1: loss %d found at %3.3f time\n", i, n_tsvec_[i%n_hsz]); } } if (ind == 0) { //you didn't find a loss in the range -> this is really loss Maxseq_sale_ = seqno; } } } else { Maxseq_sale_ = seqno; } //end else } //end else //printf("REXMIT3: %d\n", seqno); //exit(1); //and we do not set time here, because the time is //already set when it was sent if (nrexmitpack_ == 1) { //first loss indication //we should have inferred the rtt_ by now //do the comparioson here or //call the function to update the history? //start computing the throughput //printf("AAAA\n"); n_compute_thr(0); } else { //We immediately compute the throughput //printf("BBBB\n"); n_compute_thr(1); } } //end else //sale <<< //} //sale -> delete this later if (!(rtx_timer_.status() == TIMER_PENDING) || force_set_rtx_timer) /* No timer pending. Schedule one. */ set_rtx_timer(); } /* * Must convert bytes into packets for one-way TCPs. * If nbytes == -1, this corresponds to infinite send. We approximate * infinite by a very large number (TCP_MAXSEQ). */ void TcpAgent::sendmsg(int nbytes, const char* /*flags*/) { if (nbytes == -1 && curseq_ <= TCP_MAXSEQ) curseq_ = TCP_MAXSEQ; else curseq_ += (nbytes/size_ + (nbytes%size_ ? 1 : 0)); send_much(0, 0, maxburst_); } void TcpAgent::advanceby(int delta) { curseq_ += delta; if (delta > 0) closed_ = 0; send_much(0, 0, maxburst_); } int TcpAgent::command(int argc, const char*const* argv) { if (argc == 3) { if (strcmp(argv[1], "advance") == 0) { int newseq = atoi(argv[2]); if (newseq > maxseq_) advanceby(newseq - curseq_); else advanceby(maxseq_ - curseq_); return (TCL_OK); } if (strcmp(argv[1], "advanceby") == 0) { advanceby(atoi(argv[2])); return (TCL_OK); } if (strcmp(argv[1], "eventtrace") == 0) { et_ = (EventTrace *)TclObject::lookup(argv[2]); return (TCL_OK); } /* * Curtis Villamizar's trick to transfer tcp connection * parameters to emulate http persistent connections. * * Another way to do the same thing is to open one tcp * object and use start/stop/maxpkts_ or advanceby to control * how much data is sent in each burst. * With a single connection, slow_start_restart_ * should be configured as desired. * * This implementation (persist) may not correctly * emulate pure-BSD-based systems which close cwnd * after the connection goes idle (slow-start * restart). See appendix C in * Jacobson and Karels ``Congestion * Avoidance and Control'' at * * (*not* the original * '88 paper) for why BSD does this. See * ``Performance Interactions Between P-HTTP and TCP * Implementations'' in CCR 27(2) for descriptions of * what other systems do the same. * */ if (strcmp(argv[1], "persist") == 0) { TcpAgent *other = (TcpAgent*)TclObject::lookup(argv[2]); cwnd_ = other->cwnd_; awnd_ = other->awnd_; ssthresh_ = other->ssthresh_; t_rtt_ = other->t_rtt_; t_srtt_ = other->t_srtt_; t_rttvar_ = other->t_rttvar_; t_backoff_ = other->t_backoff_; return (TCL_OK); } } return (Agent::command(argc, argv)); } int TcpAgent::window() { return (cwnd_ < wnd_ ? (int)cwnd_ : (int)wnd_); } double TcpAgent::windowd() { return (cwnd_ < wnd_ ? (double)cwnd_ : (double)wnd_); } /* * Try to send as much data as the window will allow. The link layer will * do the buffering; we ask the application layer for the size of the packets. */ void TcpAgent::send_much(int force, int reason, int maxburst) { //printf("SNDMUCH!!!\n"); //exit(1); send_idle_helper(); int win = window(); int npackets = 0; //printf("SENDMUCH===\n"); //exit(1); if (!force && delsnd_timer_.status() == TIMER_PENDING) return; /* Save time when first packet was sent, for newreno --Allman */ if (t_seqno_ == 0) firstsent_ = Scheduler::instance().clock(); if (burstsnd_timer_.status() == TIMER_PENDING) return; while (t_seqno_ <= highest_ack_ + win && t_seqno_ < curseq_) { double t_seqno__ = t_seqno_; double highest_ack__ = highest_ack_; double win__ = win; double curseq__ = curseq_; //printf("t_seqno_ = %f, highest_ack: = %f, win=%f, curseq= %f\n",t_seqno__, highest_ack__, win__, curseq__); if (overhead_ == 0 || force) { //printf("CALL1\n"); //exit(1); output(t_seqno_, reason); npackets++; if (QOption_) process_qoption_after_send () ; t_seqno_ ++ ; } else if (!(delsnd_timer_.status() == TIMER_PENDING)) { /* * Set a delayed send timeout. */ delsnd_timer_.resched(Random::uniform(overhead_)); return; } win = window(); if (maxburst && npackets == maxburst) break; } /* call helper function */ send_helper(maxburst); } /* * We got a timeout or too many duplicate acks. Clear the retransmit timer. * Resume the sequence one past the last packet acked. * "mild" is 0 for timeouts and Tahoe dup acks, 1 for Reno dup acks. * "backoff" is 1 if the timer should be backed off, 0 otherwise. */ void TcpAgent::reset_rtx_timer(int mild, int backoff) { if (backoff) rtt_backoff(); set_rtx_timer(); if (!mild) t_seqno_ = highest_ack_ + 1; rtt_active_ = 0; } /* * Set retransmit timer using current rtt estimate. By calling resched(), * it does not matter whether the timer was already running. */ void TcpAgent::set_rtx_timer() { rtx_timer_.resched(rtt_timeout()); } /* * Set new retransmission timer if not all outstanding * or available data acked, or if we are unable to send because * cwnd is less than one (as when the ECN bit is set when cwnd was 1). * Otherwise, if a timer is still outstanding, cancel it. */ void TcpAgent::newtimer(Packet* pkt) { hdr_tcp *tcph = hdr_tcp::access(pkt); /* * t_seqno_, the next packet to send, is reset (decreased) * to highest_ack_ + 1 after a timeout, * so we also have to check maxseq_, the highest seqno sent. * In addition, if the packet sent after the timeout has * the ECN bit set, then the returning ACK caused cwnd_ to * be decreased to less than one, and we can't send another * packet until the retransmit timer again expires. * So we have to check for "cwnd_ < 1" as well. */ if (t_seqno_ > tcph->seqno() || tcph->seqno() < maxseq_ || cwnd_ < 1) set_rtx_timer(); else cancel_rtx_timer(); } /* * for experimental, high-speed TCP */ double TcpAgent::linear(double x, double x_1, double y_1, double x_2, double y_2) { // The y coordinate factor ranges from y_1 to y_2 // as the x coordinate ranges from x_1 to x_2. double y = y_1 + ((y_2 - y_1) * ((x - x_1)/(x_2-x_1))); return y; } /* * open up the congestion window */ void TcpAgent::opencwnd() { if (cwnd_ < ssthresh_) { /* slow-start (exponential) */ cwnd_ += 1; } else { /* linear */ double f; switch (wnd_option_) { case 0: if (++count_ >= cwnd_) { count_ = 0; ++cwnd_; } break; case 1: /* This is the standard algorithm. */ cwnd_ += increase_num_ / cwnd_; break; case 2: /* These are window increase algorithms * for experimental purposes only. */ f = (t_srtt_ >> T_SRTT_BITS) * tcp_tick_; f *= f; f *= wnd_const_; f += fcnt_; if (f > cwnd_) { fcnt_ = 0; ++cwnd_; } else fcnt_ = f; break; case 3: f = awnd_; f *= f; f *= wnd_const_; f += fcnt_; if (f > cwnd_) { fcnt_ = 0; ++cwnd_; } else fcnt_ = f; break; case 4: f = awnd_; f *= wnd_const_; f += fcnt_; if (f > cwnd_) { fcnt_ = 0; ++cwnd_; } else fcnt_ = f; break; case 5: f = (t_srtt_ >> T_SRTT_BITS) * tcp_tick_; f *= wnd_const_; f += fcnt_; if (f > cwnd_) { fcnt_ = 0; ++cwnd_; } else fcnt_ = f; break; case 6: /* binomial controls */ cwnd_ += increase_num_ / (cwnd_*pow(cwnd_,k_parameter_)); break; case 8: double increase, decrease, p, max_increase; /* for highspeed TCP -- from Sylvia Ratnasamy, */ /* modifications by Sally Floyd and Evandro de Souza */ // p ranges from 1.5/W^2 at congestion window low_window_, to // high_p_ at congestion window high_window_, on a log-log scale. // The decrease factor ranges from 0.5 to high_decrease // as the window ranges from low_window to high_window, // as the log of the window. if(cwnd_ <= low_window_) { cwnd_ += 1 / cwnd_ ; } else { //p = exp(log(0.120) - log(cwnd_)); double low_p = 1.5/(low_window_*low_window_); p = exp(linear(log(cwnd_), log(low_window_), log(low_p), log(high_window_), log(high_p_))); decrease = linear(log(cwnd_), log(low_window_), 0.5, log(high_window_), high_decrease_); increase = (cwnd_ * cwnd_ * 2.0 * decrease * p)/(2.0 - decrease); // max_increase = 157.8; // if (increase > max_increase) { // increase = max_increase; // } cwnd_ += (increase / cwnd_) ; } break; default: #ifdef notdef /*XXX*/ error("illegal window option %d", wnd_option_); #endif abort(); } } // if maxcwnd_ is set (nonzero), make it the cwnd limit if (maxcwnd_ && (int(cwnd_) > maxcwnd_)) cwnd_ = maxcwnd_; return; } void TcpAgent::slowdown(int how) { double decrease; /* added for highspeed - sylvia */ double win, halfwin, decreasewin; int slowstart = 0; ++ncwndcuts_; // we are in slowstart for sure if cwnd < ssthresh if (cwnd_ < ssthresh_) slowstart = 1; if (precision_reduce_) { halfwin = windowd() / 2; if (wnd_option_ == 6) { /* binomial controls */ decreasewin = windowd() - (1.0-decrease_num_)*pow(windowd(),l_parameter_); } else if (wnd_option_ == 8 && (cwnd_ > low_window_)) { /* experimental highspeed TCP */ decrease = linear(log(cwnd_), log(low_window_), 0.5, log(high_window_), high_decrease_); //if (decrease < 0.1) // decrease = 0.1; decrease_num_ = decrease; decreasewin = windowd() - (decrease * windowd()); } else { decreasewin = decrease_num_ * windowd(); } win = windowd(); } else { int temp; temp = (int)(window() / 2); halfwin = (double) temp; if (wnd_option_ == 6) { /* binomial controls */ temp = (int)(window() - (1.0-decrease_num_)*pow(window(),l_parameter_)); } else if ((wnd_option_ == 8) && (cwnd_ > low_window_)) { /* experimental highspeed TCP */ decrease = linear(log(cwnd_), log(low_window_), 0.5, log(high_window_), high_decrease_); //if (decrease < 0.1) // decrease = 0.1; decrease_num_ = decrease; temp = (int)(windowd() - (decrease * windowd())); } else { temp = (int)(decrease_num_ * window()); } decreasewin = (double) temp; win = (double) window(); } if (how & CLOSE_SSTHRESH_HALF) // For the first decrease, decrease by half // even for non-standard values of decrease_num_. if (first_decrease_ == 1 || slowstart || last_cwnd_action_ == CWND_ACTION_TIMEOUT) { // Do we really want halfwin instead of decreasewin // after a timeout? ssthresh_ = (int) halfwin; } else { ssthresh_ = (int) decreasewin; } else if (how & THREE_QUARTER_SSTHRESH) if (ssthresh_ < 3*cwnd_/4) ssthresh_ = (int)(3*cwnd_/4); if (how & CLOSE_CWND_HALF) // For the first decrease, decrease by half // even for non-standard values of decrease_num_. if (first_decrease_ == 1 || slowstart || decrease_num_ == 0.5) { cwnd_ = halfwin; } else cwnd_ = decreasewin; else if (how & CWND_HALF_WITH_MIN) { // We have not thought about how non-standard TCPs, with // non-standard values of decrease_num_, should respond // after quiescent periods. cwnd_ = decreasewin; if (cwnd_ < 1) cwnd_ = 1; } else if (how & CLOSE_CWND_RESTART) cwnd_ = int(wnd_restart_); else if (how & CLOSE_CWND_INIT) cwnd_ = int(wnd_init_); else if (how & CLOSE_CWND_ONE) cwnd_ = 1; else if (how & CLOSE_CWND_HALF_WAY) { // cwnd_ = win - (win - W_used)/2 ; cwnd_ = W_used + decrease_num_ * (win - W_used); if (cwnd_ < 1) cwnd_ = 1; } if (ssthresh_ < 2) ssthresh_ = 2; if (how & (CLOSE_CWND_HALF|CLOSE_CWND_RESTART|CLOSE_CWND_INIT|CLOSE_CWND_ONE)) cong_action_ = TRUE; fcnt_ = count_ = 0; if (first_decrease_ == 1) first_decrease_ = 0; // for event tracing slow start if (cwnd_ == 1 || slowstart) // Not sure if this is best way to capture slow_start // This is probably tracing a superset of slowdowns of // which all may not be slow_start's --Padma, 07/'01. trace_event("SLOW_START"); } /* * Process a packet that acks previously unacknowleged data. */ void TcpAgent::newack(Packet* pkt) { 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*? */ if (!timerfix_) newtimer(pkt); dupacks_ = 0; last_ack_ = tcph->seqno(); prev_highest_ack_ = highest_ack_ ; 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 (ts_option_) rtt_update(now - tcph->ts_echo()); 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; if (!ts_option_) rtt_update(now - rtt_ts_); } } if (timerfix_) newtimer(pkt); /* update average window */ awnd_ *= 1.0 - wnd_th_; awnd_ += wnd_th_ * cwnd_; } /* * Respond either to a source quench or to a congestion indication bit. * This is done at most once a roundtrip time; after a source quench, * another one will not be done until the last packet transmitted before * the previous source quench has been ACKed. * * Note that this procedure is called before "highest_ack_" is * updated to reflect the current ACK packet. */ void TcpAgent::ecn(int seqno) { if (seqno > recover_ || last_cwnd_action_ == CWND_ACTION_TIMEOUT) { recover_ = maxseq_; last_cwnd_action_ = CWND_ACTION_ECN; if (cwnd_ <= 1.0) { if (ecn_backoff_) rtt_backoff(); else ecn_backoff_ = 1; } else ecn_backoff_ = 0; slowdown(CLOSE_CWND_HALF|CLOSE_SSTHRESH_HALF); ++necnresponses_ ; // added by sylvia to count number of ecn responses } } /* * Is the connection limited by the network (instead of by a lack * of data from the application? */ int TcpAgent::network_limited() { int win = window () ; if (t_seqno_ > (prev_highest_ack_ + win)) return 1; else return 0; } void TcpAgent::recv_newack_helper(Packet *pkt) { //hdr_tcp *tcph = hdr_tcp::access(pkt); newack(pkt); if (!ect_ || !hdr_flags::access(pkt)->ecnecho() || (old_ecn_ && ecn_burst_)) { /* If "old_ecn", this is not the first ACK carrying ECN-Echo * after a period of ACKs without ECN-Echo. * Therefore, open the congestion window. */ /* if control option is set, and the sender is not window limited, then do not increase the window size */ if (!control_increase_ || (control_increase_ && (network_limited() == 1))) opencwnd(); } if (ect_) { if (!hdr_flags::access(pkt)->ecnecho()) ecn_backoff_ = 0; if (!ecn_burst_ && hdr_flags::access(pkt)->ecnecho()) ecn_burst_ = TRUE; else if (ecn_burst_ && ! hdr_flags::access(pkt)->ecnecho()) ecn_burst_ = FALSE; } if (!ect_ && hdr_flags::access(pkt)->ecnecho() && !hdr_flags::access(pkt)->cong_action()) ect_ = 1; /* if the connection is done, call finish() */ if ((highest_ack_ >= curseq_-1) && !closed_) { closed_ = 1; finish(); } if (QOption_ && curseq_ == highest_ack_ +1) { cancel_rtx_timer(); } } /* * Set the initial window. */ double TcpAgent::initial_window() { // // init_option = 1: static iw of wnd_init_ // if (wnd_init_option_ == 1) { return (wnd_init_); } else if (wnd_init_option_ == 2) { // do iw according to Internet draft if (size_ <= 1095) { return (4.0); } else if (size_ < 2190) { return (3.0); } else { return (2.0); } } // XXX what should we return here??? fprintf(stderr, "Wrong number of wnd_init_option_ %d\n", wnd_init_option_); abort(); return (2.0); // XXX make msvc happy. } /* * Dupack-action: what to do on a DUP ACK. After the initial check * of 'recover' below, this function implements the following truth * table: * * bugfix ecn last-cwnd == ecn action * * 0 0 0 tahoe_action * 0 0 1 tahoe_action [impossible] * 0 1 0 tahoe_action * 0 1 1 slow-start, return * 1 0 0 nothing * 1 0 1 nothing [impossible] * 1 1 0 nothing * 1 1 1 slow-start, return */ /* * A first or second duplicate acknowledgement has arrived, and * singledup_ is enabled. * If the receiver's advertised window permits, and we are exceeding our * congestion window by less than numdupacks_, then send a new packet. */ void TcpAgent::send_one() { if (t_seqno_ <= highest_ack_ + wnd_ && t_seqno_ < curseq_ && t_seqno_ <= highest_ack_ + cwnd_ + dupacks_ ) { //printf("CALL2\n"); output(t_seqno_, 0); if (QOption_) process_qoption_after_send () ; t_seqno_ ++ ; // send_helper(); ?? } return; } void TcpAgent::dupack_action() { int recovered = (highest_ack_ > recover_); if (recovered || (!bug_fix_ && !ecn_)) { goto tahoe_action; } if (ecn_ && last_cwnd_action_ == CWND_ACTION_ECN) { last_cwnd_action_ = CWND_ACTION_DUPACK; slowdown(CLOSE_CWND_ONE); reset_rtx_timer(0,0); return; } if (bug_fix_) { /* * The line below, for "bug_fix_" true, avoids * problems with multiple fast retransmits in one * window of data. */ return; } tahoe_action: // we are now going to fast-retransmit and willtrace that event trace_event("FAST_RETX"); recover_ = maxseq_; last_cwnd_action_ = CWND_ACTION_DUPACK; slowdown(CLOSE_SSTHRESH_HALF|CLOSE_CWND_ONE); reset_rtx_timer(0,0); return; } /* * main reception path - should only see acks, otherwise the * network connections are misconfigured */ void TcpAgent::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 /* W.N.: check if this is from a previous incarnation */ if (tcph->ts() < lastreset_) { // Remove packet and do nothing Packet::free(pkt); return; } ++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 (hdr_flags::access(pkt)->eln_ && eln_) { tcp_eln(pkt); return; } if (++dupacks_ == numdupacks_ && !noFastRetrans_) { dupack_action(); } else if (dupacks_ < numdupacks_ && singledup_ ) { send_one(); } } if (QOption_ && EnblRTTCtr_) process_qoption_after_ack (tcph->seqno()); Packet::free(pkt); /* * Try to send more data. */ send_much(0, 0, maxburst_); } /* * Process timeout events other than rtx timeout. Having this as a separate * function allows derived classes to make alterations/enhancements (e.g., * response to new types of timeout events). */ void TcpAgent::timeout_nonrtx(int tno) { if (tno == TCP_TIMER_DELSND) { /* * delayed-send timer, with random overhead * to avoid phase effects */ send_much(1, TCP_REASON_TIMEOUT, maxburst_); } } void TcpAgent::timeout(int tno) { /* retransmit timer */ if (tno == TCP_TIMER_RTX) { // There has been a timeout - will trace this event trace_event("TIMEOUT"); if (cwnd_ < 1) cwnd_ = 1; if (highest_ack_ == maxseq_ && !slow_start_restart_) { /* * TCP option: * If no outstanding data, then don't do anything. */ // Should this return be here? // What if CWND_ACTION_ECN and cwnd < 1? // return; } else { recover_ = maxseq_; if (highest_ack_ == -1 && wnd_init_option_ == 2) /* * First packet dropped, so don't use larger * initial windows. */ wnd_init_option_ = 1; if (highest_ack_ == maxseq_ && restart_bugfix_) /* * if there is no outstanding data, don't cut * down ssthresh_. */ slowdown(CLOSE_CWND_ONE); else if (highest_ack_ < recover_ && last_cwnd_action_ == CWND_ACTION_ECN) { /* * if we are in recovery from a recent ECN, * don't cut down ssthresh_. */ slowdown(CLOSE_CWND_ONE); } else { ++nrexmit_; last_cwnd_action_ = CWND_ACTION_TIMEOUT; slowdown(CLOSE_SSTHRESH_HALF|CLOSE_CWND_RESTART); } } /* if there is no outstanding data, don't back off rtx timer */ if (highest_ack_ == maxseq_ && restart_bugfix_) { 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); } } /* * Check if the packet (ack) has the ELN bit set, and if it does, and if the * last ELN-rxmitted packet is smaller than this one, then retransmit the * packet. Do not adjust the cwnd when this happens. */ void TcpAgent::tcp_eln(Packet *pkt) { //int eln_rxmit; hdr_tcp *tcph = hdr_tcp::access(pkt); int ack = tcph->seqno(); if (++dupacks_ == eln_rxmit_thresh_ && ack > eln_last_rxmit_) { /* Retransmit this packet */ //printf("CALL3\n"); output(last_ack_ + 1, TCP_REASON_DUPACK); eln_last_rxmit_ = last_ack_+1; } else send_much(0, 0, maxburst_); Packet::free(pkt); return; } /* * This function is invoked when the connection is done. It in turn * invokes the Tcl finish procedure that was registered with TCP. */ void TcpAgent::finish() { Tcl::instance().evalf("%s done", this->name()); } void RtxTimer::expire(Event*) { a_->timeout(TCP_TIMER_RTX); } void DelSndTimer::expire(Event*) { a_->timeout(TCP_TIMER_DELSND); } void BurstSndTimer::expire(Event*) { a_->timeout(TCP_TIMER_BURSTSND); } /* * THE FOLLOWING FUNCTIONS ARE OBSOLETE, but REMAIN HERE * DUE TO OTHER PEOPLE's TCPs THAT MIGHT USE THEM * * These functions are now replaced by ecn() and slowdown(), * respectively. */ /* * Respond either to a source quench or to a congestion indication bit. * This is done at most once a roundtrip time; after a source quench, * another one will not be done until the last packet transmitted before * the previous source quench has been ACKed. */ void TcpAgent::quench(int how) { if (highest_ack_ >= recover_) { recover_ = maxseq_; last_cwnd_action_ = CWND_ACTION_ECN; closecwnd(how); } } /* * close down the congestion window */ void TcpAgent::closecwnd(int how) { static int first_time = 1; if (first_time == 1) { fprintf(stderr, "the TcpAgent::closecwnd() function is now deprecated, please use the function slowdown() instead\n"); } switch (how) { case 0: /* timeouts */ ssthresh_ = int( window() / 2 ); if (ssthresh_ < 2) ssthresh_ = 2; cwnd_ = int(wnd_restart_); break; case 1: /* Reno dup acks, or after a recent congestion indication. */ // cwnd_ = window()/2; cwnd_ = decrease_num_ * window(); ssthresh_ = int(cwnd_); if (ssthresh_ < 2) ssthresh_ = 2; break; case 2: /* Tahoe dup acks * after a recent congestion indication */ cwnd_ = wnd_init_; break; case 3: /* Retransmit timeout, but no outstanding data. */ cwnd_ = int(wnd_init_); break; case 4: /* Tahoe dup acks */ ssthresh_ = int( window() / 2 ); if (ssthresh_ < 2) ssthresh_ = 2; cwnd_ = 1; break; default: abort(); } fcnt_ = 0.; count_ = 0; } /* * Check if the sender has been idle or application-limited for more * than an RTO, and if so, reduce the congestion window. */ void TcpAgent::process_qoption_after_send () { int tcp_now = (int)(Scheduler::instance().clock()/tcp_tick_ + 0.5); int rto = (int)(t_rtxcur_/tcp_tick_) ; /*double ct = Scheduler::instance().clock();*/ if (!EnblRTTCtr_) { if (tcp_now - T_last >= rto) { // The sender has been idle. slowdown(THREE_QUARTER_SSTHRESH) ; for (int i = 0 ; i < (tcp_now - T_last)/rto; i ++) { slowdown(CWND_HALF_WITH_MIN); } T_prev = tcp_now ; W_used = 0 ; } T_last = tcp_now ; if (t_seqno_ == highest_ack_+ window()) { T_prev = tcp_now ; W_used = 0 ; } else if (t_seqno_ == curseq_-1) { // The sender has no more data to send. int tmp = t_seqno_ - highest_ack_ ; if (tmp > W_used) W_used = tmp ; if (tcp_now - T_prev >= rto) { // The sender has been application-limited. slowdown(THREE_QUARTER_SSTHRESH); slowdown(CLOSE_CWND_HALF_WAY); T_prev = tcp_now ; W_used = 0 ; } } } else { rtt_counting(); } } /* * Check if the sender has been idle or application-limited for more * than an RTO, and if so, reduce the congestion window, for a TCP sender * that "counts RTTs" by estimating the number of RTTs that fit into * a single clock tick. */ void TcpAgent::rtt_counting() { int tcp_now = (int)(Scheduler::instance().clock()/tcp_tick_ + 0.5); int rtt = (int(t_srtt_) >> T_SRTT_BITS) ; if (rtt < 1) rtt = 1 ; if (tcp_now - T_last >= 2*rtt) { // The sender has been idle. int RTTs ; RTTs = (tcp_now -T_last)*RTT_goodcount/(rtt*2) ; RTTs = RTTs - Backoffs ; Backoffs = 0 ; if (RTTs > 0) { slowdown(THREE_QUARTER_SSTHRESH) ; for (int i = 0 ; i < RTTs ; i ++) { slowdown(CWND_HALF_WITH_MIN); RTT_prev = RTT_count ; W_used = 0 ; } } } T_last = tcp_now ; if (tcp_now - T_start >= 2*rtt) { if ((RTT_count > RTT_goodcount) || (F_full == 1)) { RTT_goodcount = RTT_count ; if (RTT_goodcount < 1) RTT_goodcount = 1 ; } RTT_prev = RTT_prev - RTT_count ; RTT_count = 0 ; T_start = tcp_now ; F_full = 0; } if (t_seqno_ == highest_ack_ + window()) { W_used = 0 ; F_full = 1 ; RTT_prev = RTT_count ; } else if (t_seqno_ == curseq_-1) { // The sender has no more data to send. int tmp = t_seqno_ - highest_ack_ ; if (tmp > W_used) W_used = tmp ; if (RTT_count - RTT_prev >= 2) { // The sender has been application-limited. slowdown(THREE_QUARTER_SSTHRESH) ; slowdown(CLOSE_CWND_HALF_WAY); RTT_prev = RTT_count ; Backoffs ++ ; W_used = 0; } } if (F_counting == 0) { W_timed = t_seqno_ ; F_counting = 1 ; } } void TcpAgent::process_qoption_after_ack (int seqno) { if (F_counting == 1) { if (seqno >= W_timed) { RTT_count ++ ; F_counting = 0 ; } else { if (dupacks_ == numdupacks_) RTT_count ++ ; } } } void TcpAgent::trace_event(char *eventtype) { if (et_ == NULL) return; int seqno = t_seqno_; char *wrk = et_->buffer(); char *nwrk = et_->nbuffer(); if (wrk != 0) sprintf(wrk, "E "TIME_FORMAT" %d %d TCP %s %d %d %d", et_->round(Scheduler::instance().clock()), // time addr(), // owner (src) node id daddr(), // dst node id eventtype, // event type fid_, // flow-id seqno, // current seqno int(cwnd_) //cong. window ); if (nwrk != 0) sprintf(nwrk, "E -t "TIME_FORMAT" -o TCP -e %s -s %d.%d -d %d.%d", et_->round(Scheduler::instance().clock()), // time eventtype, // event type addr(), // owner (src) node id port(), // owner (src) port id daddr(), // dst node id dport() // dst port id ); et_->trace(); } //sale >>> /* * Schedule this report, and set timer for the next one. */ void TcpAgent::n_compute_thr(int ind) { double now = Scheduler::instance().clock(); //printf("Compute_thr, time=%f\n",now); //double pe; if (ind == 0) { //first time //this is just for now - have to change later n_rate_last = ndatabytes_/now; n_est_loss_=n_est_loss(); //printf("Before History adjusted, p = %f\n",pe); n_est_loss_=n_adjust_history(now); //printf("After History adjusted, p = %f\n",pe); } else { //not the first time n_est_loss_=n_est_loss(); } //update rtt //double pe = n_est_loss(); double nnrexmitbytes_ = nrexmitbytes_; double nndatabytes_ = ndatabytes_; n_ploss_ = nnrexmitbytes_/(nndatabytes_+nnrexmitbytes_); if (n_est_loss_ > 0.0) { //estimate throughput n_bval_ = 1; double TFRC_local_rate_ = p_to_b(n_est_loss_, n_rtt_, n_tzero_, size_, n_bval_); double rtt_ave_ = n_rtt_sum_/n_rtt_num_; //printf("rtt_sum_: %f, n_rtt_num_: %f n_rtt_ave_: %f\n",n_rtt_sum_, n_rtt_num_, n_rtt_ave_); double comp_rate_1_ = p_to_b(n_ploss_, rtt_ave_, n_tzero_, size_, n_bval_); //print both throughputs double last_round_bytes = n_rate_last*(now-n_time_last); n_sum_bytes += last_round_bytes; n_TCP_rate_ = (ndatabytes_ + nrexmitbytes_)/now; n_est_loss_ = n_est_loss_; n_TFRC_rate_ = n_sum_bytes/now; // n_ploss_ = n_ploss_; n_EQ_rate_ = comp_rate_1_; n_ratio_TCP_TFRC_ = n_TCP_rate_/n_TFRC_rate_; n_ratio_TCP_EQ_ = n_TCP_rate_/n_EQ_rate_; double TCP_rate__ = (ndatabytes_ + nrexmitbytes_)/now; double est_loss__ = n_est_loss_; double TFRC_rate__ = n_TFRC_rate_; //this is average TFRC rate computed by now double ploss__ = n_ploss_; double EQ_rate__ = n_EQ_rate_; double ratio_TCP_TFRC__ = TCP_rate__/TFRC_rate__; double ratio_TCP_EQ__ = TCP_rate__/EQ_rate__; //PRINTING // printf("time: %7.5f TCP_rate: %5.3f, est_loss: %1.7f, TFRC_rate: %5.3f, ploss: %1.7f, EQ_rate: %5.3f, ratio_TCP_TFRC: %2.2f, ratio_TCP_EQ: %2.2f\n", now, TCP_rate__, est_loss__, TFRC_rate__, ploss__, EQ_rate__, ratio_TCP_TFRC__, ratio_TCP_EQ__); // printf ("time: %7.5f rtt: %4.4f est_loss: %1.7f comp_rate: %5.3f comp_Bytes: %7.1f sent_Bytes: %d\n", now, rtt_, pe, comp_rate_, n_sum_bytes, (ndatabytes_ + nrexmitbytes_)); //printf ("time: %7.5f rtt: %4.4f est_loss: %1.7f comp_rate: %5.3f comp_Bytes: %7.1f sent_Bytes: %d ratioTCP/TFRC: %2.2f tot_loss_by_now: %2.4f comp_tot_Bytes %2.2f ratioTCP/EQ: %f\n", now, rtt_, pe, comp_rate_, n_sum_bytes, (ndatabytes_ + nrexmitbytes_), ((ndatabytes_+nrexmitbytes_)/n_sum_bytes), ploss_, comp_rate_1_*now, ((ndatabytes_+nrexmitbytes_)/(comp_rate_1_*now))); n_rate_last = TFRC_local_rate_; n_time_last = now; } // printf("n_rtt_ = %f, s_rtt= %-6.3f\n", n_rtt_, (int(t_srtt_)>>T_SRTT_BITS)*tcp_tick_); // schedule next report rtt/NumFeedback_ later if (n_rtt_ > 0.0 && n_NumFeedback_ > 0) timer_.resched(1.5*n_rtt_/n_NumFeedback_); } //sale <<< //sale >>> void TcpAgentTimer::expire(Event*) { a_->n_compute_thr(1); } //sale <<< //sale >>> //from TFRC double TcpAgent::n_est_loss () { double p = 0 ; switch (n_algo) { case WALI: p = n_est_loss_WALI () ; break; case EWMA: p = n_est_loss_EWMA () ; break; case RBPH: p = n_est_loss_RBPH () ; break; case EBPH: p = n_est_loss_EBPH () ; break; default: printf ("invalid algo specified\n"); abort(); break ; } return p; } void TcpAgent::n_print_loss(int sample, double ave_interval) { double now = Scheduler::instance().clock(); double drops = 1/ave_interval; // This is ugly to include this twice, but the first one is // for backward compatibility with earlier scripts. printf ("time: %7.5f loss_rate: %7.5f \n", now, drops); printf ("time: %7.5f sample 0: %5d loss_rate: %7.5f \n", now, sample, drops); //printf ("time: %7.5f send_rate: %7.5f\n", now, n_sendrate); //printf ("time: %7.5f maxseq: %d\n", now, maxseq); } void TcpAgent::n_print_loss_all(int *sample) { double now = Scheduler::instance().clock(); printf ("%f: sample 0: %5d 1: %5d 2: %5d 3: %5d 4: %5d\n", now, sample[0], sample[1], sample[2], sample[3], sample[4]); } //////////////////////////////////////// // algo specific code ///////////////// /////////////////////////////////////// //// /// WALI Code //// double TcpAgent::n_est_loss_WALI () { int i; double ave_interval1, ave_interval2; int ds ; if (!n_init_WALI_flag) { n_init_WALI () ; } // sample[i] counts the number of packets since the i-th loss event // sample[0] contains the most recent sample. //for (i = n_last_sample; i <= maxseq_ ; i ++) { for (i = n_last_sample; i <= Maxseq_sale_ ; i ++) { n_sample[0]++; if (n_lossvec_[i%n_hsz] == LOST || n_lossvec_[i%n_hsz] == ECNLOST) { // new loss event // double now = Scheduler::instance().clock(); n_sample_count ++; n_shift_array (n_sample, n_numsamples+1, 0); n_multiply_array(n_mult, n_numsamples+1, n_mult_factor_); n_shift_array (n_mult, n_numsamples+1, 1.0); n_mult_factor_ = 1.0; } } //n_last_sample = maxseq_+1 ; n_last_sample = Maxseq_sale_+1 ; if (n_sample_count>n_numsamples+1) // The array of loss intervals is full. ds=n_numsamples+1; else ds=n_sample_count; if (n_sample_count == 1 && n_false_sample == 0) // no losses yet return 0; //do we need to discount weights? if (n_sample_count > 1 && n_discount && n_sample[0] > 0) { double ave = n_weighted_average(1, ds, 1.0, n_mult, n_weights, n_sample); int factor = 2; double ratio = (factor*ave)/n_sample[0]; double min_ratio = 0.5; if ( ratio < 1.0) { // the most recent loss interval is very large n_mult_factor_ = ratio; if (n_mult_factor_ < min_ratio) n_mult_factor_ = min_ratio; } } // Calculations including the most recent loss interval. ave_interval1 = n_weighted_average(0, ds, n_mult_factor_, n_mult, n_weights, n_sample); // The most recent loss interval does not end in a loss // event. Include the most recent interval in the // calculations only if this increases the estimated loss // interval. ave_interval2 = n_weighted_average(1, ds, n_mult_factor_, n_mult, n_weights, n_sample); if (ave_interval2 > ave_interval1) ave_interval1 = ave_interval2; if (ave_interval1 > 0) { if (n_printLoss_ > 0) { n_print_loss(n_sample[0], ave_interval1); n_print_loss_all(n_sample); } return 1/ave_interval1; } else return 999; } // Calculate the weighted average. double TcpAgent::n_weighted_average(int start, int end, double factor, double *m, double *w, int *sample) { int i; double wsum = 0; double answer = 0; if (n_smooth_ == 1 && start == 0) { if (end == n_numsamples+1) { // the array is full, but we don't want to uses // the last loss interval in the array end = end-1; } // effectively shift the weight arrays for (i = start ; i < end; i++) if (i==0) wsum += m[i]*w[i+1]; else wsum += factor*m[i]*w[i+1]; for (i = start ; i < end; i++) if (i==0) answer += m[i]*w[i+1]*sample[i]/wsum; else answer += factor*m[i]*w[i+1]*sample[i]/wsum; return answer; } else { for (i = start ; i < end; i++) if (i==0) wsum += m[i]*w[i]; else wsum += factor*m[i]*w[i]; for (i = start ; i < end; i++) if (i==0) answer += m[i]*w[i]*sample[i]/wsum; else answer += factor*m[i]*w[i]*sample[i]/wsum; return answer; } } // Shift array a[] up, starting with a[sz-2] -> a[sz-1]. void TcpAgent::n_shift_array(int *a, int sz, int defval) { int i ; for (i = sz-2 ; i >= 0 ; i--) { a[i+1] = a[i] ; } a[0] = defval; } void TcpAgent::n_shift_array(double *a, int sz, double defval) { int i ; for (i = sz-2 ; i >= 0 ; i--) { a[i+1] = a[i] ; } a[0] = defval; } // Multiply array by value, starting with array index 1. // Array index 0 of the unshifted array contains the most recent interval. void TcpAgent::n_multiply_array(double *a, int sz, double multiplier) { int i ; for (i = 1; i <= sz-1; i++) { double old = a[i]; a[i] = old * multiplier ; } } double TcpAgent::n_adjust_history (double ts) { int i; double p; //for (i = maxseq_; i >= 0 ; i --) { for (i = Maxseq_sale_; i >= 0 ; i --) { if (n_lossvec_[i%n_hsz] == LOST || n_lossvec_[i%n_hsz] == ECNLOST ) { n_lossvec_[i%n_hsz] = NOLOSS; } } n_lastloss = ts; //sale!!! n_lastloss_round_id = round_id ; p=mmy_b_to_p(n_est_thput()*size_, n_rtt_, n_tzero_, size_, 1); //printf("p=%f\n",p); n_false_sample = (int)(1.0/p); n_sample[1] = n_false_sample; n_sample[0] = 0; n_sample_count++; if (n_printLoss_) { n_print_loss_all (n_sample); } n_false_sample = -1 ; return p; } //compute estimated throughput for report. double TcpAgent::n_est_thput () { double time_for_rcv_rate; double now = Scheduler::instance().clock(); double thput = 1 ; if ((n_rtt_ > 0) && ((now - n_last_report_sent) >= n_rtt_)) { // more than an RTT since the last report time_for_rcv_rate = (now - n_last_report_sent); //sale!!! if (time_for_rcv_rate > 0 && rcvd_since_last_report > 0) { if (time_for_rcv_rate > 0 && ndatapack_ > 0) { //sale!!!thput = rcvd_since_last_report/time_for_rcv_rate; thput = ndatapack_/time_for_rcv_rate; } } else { // count number of packets received in the last RTT if (n_rtt_ > 0){ double last = n_rtvec_[maxseq_%n_hsz]; int rcvd = 0; int i = maxseq_; while (i > 0) { if (n_lossvec_[i%n_hsz] == RCVD) { if ((n_rtvec_[i%n_hsz] + n_rtt_) > last) rcvd++; else break ; } i--; } if (rcvd > 0) thput = rcvd/n_rtt_; //sale!!! printf("Rcvd=%f,rtt=%f,Thput=%f\n",rcvd, rtt_, thput); } } //printf("Thput=%f\n",thput); return thput ; } void TcpAgent::n_init_WALI () { int i; if (n_numsamples < 0) n_numsamples = DEFAULT_NUMSAMPLES ; if (n_smooth_ == 1) { n_numsamples = n_numsamples + 1; } n_sample = (int *)malloc((n_numsamples+1)*sizeof(int)); n_weights = (double *)malloc((n_numsamples+1)*sizeof(double)); n_mult = (double *)malloc((n_numsamples+1)*sizeof(double)); for (i = 0 ; i < n_numsamples+1 ; i ++) { n_sample[i] = 0 ; } if (n_smooth_ == 1) { int mid = int(n_numsamples/2); for (i = 0; i < mid; i ++) { n_weights[i] = 1.0; } for (i = mid; i <= n_numsamples; i ++){ n_weights[i] = 1.0 - (i-mid)/(mid + 1.0); } } else { int mid = int(n_numsamples/2); for (i = 0; i < mid; i ++) { n_weights[i] = 1.0; } for (i = mid; i <= n_numsamples; i ++){ n_weights[i] = 1.0 - (i+1-mid)/(mid + 1.0); } } for (i = 0; i < n_numsamples+1; i ++) { n_mult[i] = 1.0 ; } n_init_WALI_flag = 1; // initialization done } /////////////////////////// // EWMA ////////////////// ////////////////////////// double TcpAgent::n_est_loss_EWMA () { double p1, p2 ; for (int i = n_last_sample; i <= maxseq_ ; i ++) { n_loss_int++; if (n_lossvec_[i%n_hsz] == LOST || n_lossvec_[i%n_hsz] == ECNLOST ) { if (n_avg_loss_int < 0) { n_avg_loss_int = n_loss_int ; } else { n_avg_loss_int = n_history*n_avg_loss_int + (1-n_history)*n_loss_int ; } n_loss_int = 0 ; } } n_last_sample = maxseq_+1 ; if (n_avg_loss_int < 0) { p1 = 0; } else { p1 = 1.0/n_avg_loss_int ; } if (n_loss_int == 0) { p2 = p1 ; } else { p2 = 1.0/(n_history*n_avg_loss_int + (1-n_history)*n_loss_int) ; } if (p2 < p1) { p1 = p2 ; } if (n_printLoss_ > 0) { if (p1 > 0) n_print_loss(n_loss_int, 1.0/p1); else n_print_loss(n_loss_int, 0.00001); n_print_loss_all(n_sample); } return p1 ; } /////////////////////////// // RBPH ////////////////// ////////////////////////// double TcpAgent::n_est_loss_RBPH () { double numpkts = n_hsz ; double p ; // how many pkts we should go back? if (n_sendrate > 0 && n_rtt_ > 0) { //sale!!! double x = mmy_b_to_p(n_sendrate, n_rtt_, n_tzero_, size_, 1); double x = 1; if (x > 0) numpkts = n_minlc/x ; else numpkts = n_hsz ; } // that number must be below maxseq_ and n_hsz if (numpkts > maxseq_) numpkts = maxseq_ ; if (numpkts > n_hsz) numpkts = n_hsz ; int lc = 0; int pc = 0; int i = maxseq_ ; // first see if how many lc's we find in numpkts while (pc < numpkts) { pc ++ ; if (n_lossvec_[i%n_hsz] == LOST || n_lossvec_[i%n_hsz] == ECNLOST ) lc ++ ; i -- ; } // if not enough lsos events, keep going back ... if (lc < n_minlc) { // but only as far as the history allows ... numpkts = maxseq_ ; if (numpkts > n_hsz) numpkts = n_hsz ; while ((lc < n_minlc) && (pc < numpkts)) { pc ++ ; if (n_lossvec_[i%n_hsz] == LOST || n_lossvec_[i%n_hsz] == ECNLOST ) lc ++ ; i -- ; } } if (pc == 0) p = 0; else p = (double)lc/(double)pc ; if (n_printLoss_ > 0) { if (p > 0) n_print_loss(0, 1.0/p); else n_print_loss(0, 0.00001); n_print_loss_all(n_sample); } return p ; } /////////////////////////// // EBPH ////////////////// ////////////////////////// double TcpAgent::n_est_loss_EBPH () { double numpkts = n_hsz ; double p ; int lc = 0; int pc = 0; int i = maxseq_ ; numpkts = maxseq_ ; if (numpkts > n_hsz) numpkts = n_hsz ; while ((lc < n_minlc) && (pc < numpkts)) { pc ++ ; if (n_lossvec_[i%n_hsz] == LOST || n_lossvec_[i%n_hsz] == ECNLOST) lc ++ ; i -- ; } if (pc == 0) p = 0; else p = (double)lc/(double)pc ; if (n_printLoss_ > 0) { if (p > 0) n_print_loss(0, 1.0/p); else n_print_loss(0, 0.00001); n_print_loss_all(n_sample); } return p ; } //sale <<<