ns2-tcp-tcp.cc

 /* -*-    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: /cvsroot/nsnam/ns-2/tcp/tcp.cc,v 1.182 2011/06/20 04:51:46 tom_henderson Exp $ (LBL)";
#endif #include <stdlib.h>
#include <math.h>
#include <sys/types.h>
#include <iostream>
#include "ip.h"
#include "tcp.h"
#include "flags.h"
#include "random.h"
#include "basetrace.h"
#include "hdr_qs.h" 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_(), dupacks_(), curseq_(), highest_ack_(),
cwnd_(), ssthresh_(), maxseq_(), count_(),
rtt_active_(), rtt_seq_(-), rtt_ts_(0.0),
lastreset_(0.0), closed_(), t_rtt_(), t_srtt_(), t_rttvar_(),
t_backoff_(), ts_peer_(), ts_echo_(), tss(NULL), tss_size_(),
rtx_timer_(this), delsnd_timer_(this), burstsnd_timer_(this),
first_decrease_(), fcnt_(), nrexmit_(), restart_bugfix_(),
cong_action_(), ecn_burst_(), ecn_backoff_(), ect_(),
use_rtt_(), qs_requested_(), qs_approved_(),
qs_window_(), qs_cwnd_(), frto_()
{
#ifdef TCP_DELAY_BIND_ALL
// defined since Dec 1999.
#else /* ! TCP_DELAY_BIND_ALL */
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("ncwndcuts1_", &ncwndcuts1_);
#endif /* TCP_DELAY_BIND_ALL */ } 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("max_connects_");
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("SetCWRonRetransmit_");
delay_bind_init_one("old_ecn_");
delay_bind_init_one("bugfix_ss_");
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("bugFix_ack_");
delay_bind_init_one("bugFix_ts_");
delay_bind_init_one("lessCareful_");
delay_bind_init_one("slow_start_restart_");
delay_bind_init_one("restart_bugfix_");
delay_bind_init_one("timestamps_");
delay_bind_init_one("ts_resetRTO_");
delay_bind_init_one("maxburst_");
delay_bind_init_one("aggressive_maxburst_");
delay_bind_init_one("maxcwnd_");
delay_bind_init_one("numdupacks_");
delay_bind_init_one("numdupacksFrac_");
delay_bind_init_one("exitFastRetrans_");
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("max_ssthresh_");
delay_bind_init_one("cwnd_range_");
delay_bind_init_one("timerfix_");
delay_bind_init_one("rfc2988_");
delay_bind_init_one("singledup_");
delay_bind_init_one("LimTransmitFix_");
delay_bind_init_one("rate_request_");
delay_bind_init_one("qs_enabled_");
delay_bind_init_one("tcp_qs_recovery_");
delay_bind_init_one("qs_request_mode_");
delay_bind_init_one("qs_thresh_");
delay_bind_init_one("qs_rtt_");
delay_bind_init_one("print_request_"); delay_bind_init_one("frto_enabled_");
delay_bind_init_one("sfrto_enabled_");
delay_bind_init_one("spurious_response_"); #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("ncwndcuts1_");
#endif /* TCP_DELAY_BIND_ALL */ Agent::delay_bind_init_all(); reset();
} 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, "max_connects_", &max_connects_, 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, "SetCWRonRetransmit_", &SetCWRonRetransmit_, tracer)) return TCL_OK;
if (delay_bind_bool(varName, localName, "old_ecn_", &old_ecn_ , tracer)) return TCL_OK;
if (delay_bind_bool(varName, localName, "bugfix_ss_", &bugfix_ss_ , 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, "bugFix_ack_", &bugfix_ack_, tracer)) return TCL_OK;
if (delay_bind_bool(varName, localName, "bugFix_ts_", &bugfix_ts_ , tracer)) return TCL_OK;
if (delay_bind_bool(varName, localName, "lessCareful_", &less_careful_ , tracer)) return TCL_OK;
if (delay_bind_bool(varName, localName, "timestamps_", &ts_option_ , tracer)) return TCL_OK;
if (delay_bind_bool(varName, localName, "ts_resetRTO_", &ts_resetRTO_, 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(varName, localName, "maxburst_", &maxburst_ , tracer)) return TCL_OK;
if (delay_bind_bool(varName, localName, "aggressive_maxburst_", &aggressive_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, "numdupacksFrac_", &numdupacksFrac_, tracer)) return TCL_OK;
if (delay_bind_bool(varName, localName, "exitFastRetrans_", &exitFastRetrans_, 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, "noFastRetrans_", &noFastRetrans_, tracer)) return TCL_OK;
if (delay_bind_bool(varName, localName, "precisionReduce_", &precision_reduce_, 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(varName, localName, "max_ssthresh_", &max_ssthresh_, tracer)) return TCL_OK;
if (delay_bind(varName, localName, "cwnd_range_", &cwnd_range_, 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;
if (delay_bind(varName, localName, "singledup_", &singledup_ , tracer)) return TCL_OK;
if (delay_bind_bool(varName, localName, "LimTransmitFix_", &LimTransmitFix_ , tracer)) return TCL_OK;
if (delay_bind(varName, localName, "rate_request_", &rate_request_ , tracer)) return TCL_OK;
if (delay_bind_bool(varName, localName, "qs_enabled_", &qs_enabled_ , tracer)) return TCL_OK;
if (delay_bind_bool(varName, localName, "tcp_qs_recovery_", &tcp_qs_recovery_, tracer)) return TCL_OK;
if (delay_bind(varName, localName, "qs_request_mode_", &qs_request_mode_, tracer)) return TCL_OK;
if (delay_bind(varName, localName, "qs_thresh_", &qs_thresh_, tracer)) return TCL_OK;
if (delay_bind(varName, localName, "qs_rtt_", &qs_rtt_, tracer)) return TCL_OK;
if (delay_bind_bool(varName, localName, "print_request_", &print_request_, tracer)) return TCL_OK;
if (delay_bind_bool(varName, localName, "frto_enabled_", &frto_enabled_, tracer)) return TCL_OK;
if (delay_bind_bool(varName, localName, "sfrto_enabled_", &sfrto_enabled_, tracer)) return TCL_OK;
if (delay_bind_bool(varName, localName, "spurious_response_", &spurious_response_, 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, "ncwndcuts1_", &ncwndcuts1_ , tracer)) return TCL_OK; #endif return Agent::delay_bind_dispatch(varName, localName, tracer);
} #define TCP_WRK_SIZE 512
/* Print out all the traced variables whenever any one is changed */
void
TcpAgent::traceAll() {
if (!channel_)
return; double curtime;
Scheduler& s = Scheduler::instance();
char wrk[TCP_WRK_SIZE]; curtime = &s ? s.clock() : ;
snprintf(wrk, TCP_WRK_SIZE,
"time: %-12.9f saddr: %-2d sport: %-2d daddr: %-2d dport:"
" %-2d maxseq: %-4d hiack: %-4d seqno: %-4d cwnd: %-6.3f"
" ssthresh: %-3d dupacks: %-2d rtt: %-10.9f srtt: %-10.9f"
" rttvar: %-10.9f bkoff: %-d fid: %-2d wnd: %-6.3f\n",
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_),
int(fid_), double(wnd_));
(void)Tcl_Write(channel_, wrk, -);
} /* Print out just the variable that is modified */
void
TcpAgent::traceVar(TracedVar* v)
{
if (!channel_)
return; double curtime;
Scheduler& s = Scheduler::instance();
char wrk[TCP_WRK_SIZE]; curtime = &s ? s.clock() : ; // XXX comparing addresses is faster than comparing names
if (v == &cwnd_)
snprintf(wrk, TCP_WRK_SIZE,
"%-8.5f %-2d %-2d %-2d %-2d %s %-6.3f\n",
curtime, addr(), port(), daddr(), dport(),
v->name(), double(*((TracedDouble*) v)));
else if (v == &t_rtt_)
snprintf(wrk, TCP_WRK_SIZE,
"%-8.5f %-2d %-2d %-2d %-2d %s %-6.3f\n",
curtime, addr(), port(), daddr(), dport(),
v->name(), int(*((TracedInt*) v))*tcp_tick_);
else if (v == &t_srtt_)
snprintf(wrk, TCP_WRK_SIZE,
"%-8.5f %-2d %-2d %-2d %-2d %s %-6.3f\n",
curtime, addr(), port(), daddr(), dport(),
v->name(),
(int(*((TracedInt*) v)) >> T_SRTT_BITS)*tcp_tick_);
else if (v == &t_rttvar_)
snprintf(wrk, TCP_WRK_SIZE,
"%-8.5f %-2d %-2d %-2d %-2d %s %-6.3f\n",
curtime, addr(), port(), daddr(), dport(),
v->name(),
int(*((TracedInt*) v))*tcp_tick_/4.0);
else
snprintf(wrk, TCP_WRK_SIZE,
"%-8.5f %-2d %-2d %-2d %-2d %s %d\n",
curtime, addr(), port(), daddr(), dport(),
v->name(), int(*((TracedInt*) v))); (void)Tcl_Write(channel_, wrk, -);
} 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;
syn_connects_ = ;
} else
cwnd_ = initial_window();
} void
TcpAgent::reset_qoption()
{
int now = (int)(Scheduler::instance().clock()/tcp_tick_ + 0.5); T_start = now ;
RTT_count = ;
RTT_prev = ;
RTT_goodcount = ;
F_counting = ;
W_timed = - ;
F_full = ;
Backoffs = ;
} void
TcpAgent::reset()
{
rtt_init();
rtt_seq_ = -;
/*XXX lookup variables */
dupacks_ = ;
curseq_ = ;
set_initial_window(); t_seqno_ = ;
maxseq_ = -;
last_ack_ = -;
highest_ack_ = -;
//highest_ack_ = 1;
ssthresh_ = int(wnd_);
if (max_ssthresh_ > && max_ssthresh_ < ssthresh_)
ssthresh_ = max_ssthresh_;
wnd_restart_ = .;
awnd_ = wnd_init_ / 2.0;
recover_ = ;
closed_ = ;
last_cwnd_action_ = ;
boot_time_ = Random::uniform(tcp_tick_);
first_decrease_ = ;
/* W.N.: for removing packets from previous incarnations */
lastreset_ = Scheduler::instance().clock(); /* Now these variables will be reset
- Debojyoti Dutta 12th Oct'2000 */ ndatapack_ = ;
ndatabytes_ = ;
nackpack_ = ;
nrexmitbytes_ = ;
nrexmit_ = ;
nrexmitpack_ = ;
necnresponses_ = ;
ncwndcuts_ = ;
ncwndcuts1_ = ;
cancel_timers(); // suggested by P. Anelli. if (control_increase_) {
prev_highest_ack_ = highest_ack_ ;
} if (wnd_option_ == ) {
// HighSpeed TCP
hstcp_.low_p = 1.5/(low_window_*low_window_);
double highLowWin = log(high_window_)-log(low_window_);
double highLowP = log(high_p_) - log(hstcp_.low_p);
hstcp_.dec1 =
0.5 - log(low_window_) * (high_decrease_ - 0.5)/highLowWin;
hstcp_.dec2 = (high_decrease_ - 0.5)/highLowWin;
hstcp_.p1 =
log(hstcp_.low_p) - log(low_window_) * highLowP/highLowWin;
hstcp_.p2 = highLowP/highLowWin;
} if (QOption_) {
int now = (int)(Scheduler::instance().clock()/tcp_tick_ + 0.5);
T_last = now ;
T_prev = now ;
W_used = ;
if (EnblRTTCtr_) {
reset_qoption();
}
}
} /*
* Initialize variables for the retransmit timer.
*/
void TcpAgent::rtt_init()
{
t_rtt_ = ;
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_ = ;
} 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_ && !use_rtt_)
timeout = minrto_ * t_backoff_;
else
timeout = t_rtxcur_ * t_backoff_;
} else {
// only of interest for backwards compatibility
timeout = t_rtxcur_ * t_backoff_;
if (timeout < minrto_)
timeout = minrto_;
} if (timeout > maxrto_)
timeout = maxrto_; if (timeout < 2.0 * tcp_tick_) {
if (timeout < ) {
fprintf(stderr, "TcpAgent: negative RTO! (%f)\n",
timeout);
exit();
} else if (use_rtt_ && timeout < tcp_tick_)
timeout = tcp_tick_;
else
timeout = 2.0 * tcp_tick_;
}
use_rtt_ = ;
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_ < )
t_rtt_ = ;
//
// t_srtt_ has 3 bits to the right of the binary point
// t_rttvar_ has 2
// Thus "t_srtt_ >> T_SRTT_BITS" is the actual srtt,
// and "t_srtt_" is 8*srtt.
// Similarly, "t_rttvar_ >> T_RTTVAR_BITS" is the actual rttvar,
// and "t_rttvar_" is 4*rttvar.
//
if (t_srtt_ != ) {
register short delta;
delta = t_rtt_ - (t_srtt_ >> T_SRTT_BITS); // d = (m - a0)
if ((t_srtt_ += delta) <= ) // a1 = 7/8 a0 + 1/8 m
t_srtt_ = ;
if (delta < )
delta = -delta;
delta -= (t_rttvar_ >> T_RTTVAR_BITS);
if ((t_rttvar_ += delta) <= ) // var1 = 3/4 var0 + 1/4 |d|
t_rttvar_ = ;
} else {
t_srtt_ = t_rtt_ << T_SRTT_BITS; // srtt = rtt
t_rttvar_ = t_rtt_ << (T_RTTVAR_BITS-); // 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_; return;
} void TcpAgent::rtt_backoff()
{
if (t_backoff_ < || (rfc2988_ && rtt_timeout() < maxrto_))
t_backoff_ <<= ;
// RFC2988 allows a maximum for the backed-off RTO of 60 seconds.
// This is applied by maxrto_. if (t_backoff_ > ) {
/*
* 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_ = ;
}
} /*
* 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 < ) {
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 = ;
Packet* p = allocpkt();
hdr_tcp *tcph = hdr_tcp::access(p);
hdr_flags* hf = hdr_flags::access(p);
hdr_ip *iph = hdr_ip::access(p);
int databytes = hdr_cmn::access(p)->size();
tcph->seqno() = seqno;
tcph->ts() = Scheduler::instance().clock();
int is_retransmit = (seqno < maxseq_); // Mark packet for diagnosis purposes if we are in Quick-Start Phase
if (qs_approved_) {
hf->qs() = ;
} // store timestamps, with bugfix_ts_. From Andrei Gurtov.
// (A real TCP would use scoreboard for this.)
if (bugfix_ts_ && tss==NULL) {
tss = (double*) calloc(tss_size_, sizeof(double));
if (tss==NULL) exit();
}
//dynamically grow the timestamp array if it's getting full
if (bugfix_ts_ && ((seqno - highest_ack_) > tss_size_* 0.9)) {
double *ntss;
ntss = (double*) calloc(tss_size_*, sizeof(double));
printf("%p resizing timestamp table\n", this);
if (ntss == NULL) exit();
for (int i=; i<tss_size_; i++)
ntss[(highest_ack_ + i) % (tss_size_ * )] =
tss[(highest_ack_ + i) % tss_size_];
free(tss);
tss_size_ *= ;
tss = ntss;
} if (tss!=NULL)
tss[seqno % tss_size_] = tcph->ts(); tcph->ts_echo() = ts_peer_;
tcph->reason() = reason;
tcph->last_rtt() = int(int(t_rtt_)*tcp_tick_*); if (ecn_) {
hf->ect() = ; // ECN-capable transport
}
if (cong_action_ && (!is_retransmit || SetCWRonRetransmit_)) {
hf->cong_action() = TRUE; // Congestion action.
cong_action_ = FALSE;
}
/* Check if this is the initial SYN packet. */
if (seqno == ) {
if (syn_) {
databytes = ;
if (maxseq_ == -) {
curseq_ += ; /*increment only on initial SYN*/
}
hdr_cmn::access(p)->size() = tcpip_base_hdr_size_;
++syn_connects_;
//fprintf(stderr, "TCPAgent: syn_connects_ %d max_connects_ %d\n",
// syn_connects_, max_connects_);
if (max_connects_ > &&
syn_connects_ > max_connects_) {
// Abort the connection.
// What is the best way to abort the connection?
curseq_ = ;
rtx_timer_.resched();
return;
}
}
if (ecn_) {
hf->ecnecho() = ;
// hf->cong_action() = 1;
hf->ect() = ;
}
if (qs_enabled_) {
hdr_qs *qsh = hdr_qs::access(p); // dataout is kilobytes queued for sending
int dataout = (curseq_ - maxseq_ - ) * (size_ + headersize()) / ;
int qs_rr = rate_request_;
if (qs_request_mode_ == && qs_rtt_ > ) {
// PS: Avoid making unnecessary QS requests
// use a rough estimation of RTT in qs_rtt_
// to calculate the desired rate from dataout.
// printf("dataout %d qs_rr %d qs_rtt_ %d\n",
// dataout, qs_rr, qs_rtt_);
if (dataout * / qs_rtt_ < qs_rr) {
qs_rr = dataout * / qs_rtt_;
}
// printf("request %d\n", qs_rr);
// qs_thresh_ is minimum number of unsent
// segments needed to activate QS request
// printf("curseq_ %d maxseq_ %d qs_thresh_ %d\n",
// int(curseq_), int(maxseq_), qs_thresh_);
if ((curseq_ - maxseq_ - ) < qs_thresh_) {
qs_rr = ;
}
} if (qs_rr > ) {
if (print_request_)
printf("QS request (before encoding): %d KBps\n", qs_rr);
// QuickStart code from Srikanth Sundarrajan.
qsh->flag() = QS_REQUEST;
qsh->ttl() = Random::integer();
ttl_diff_ = (iph->ttl() - qsh->ttl()) % ;
qsh->rate() = hdr_qs::Bps_to_rate(qs_rr * );
qs_requested_ = ;
} else {
qsh->flag() = QS_DISABLE;
}
}
}
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 = ;
/* call helper function to fill in additional fields */
output_helper(p); ++ndatapack_;
ndatabytes_ += databytes;
send(p, );
if (seqno == curseq_ && seqno > maxseq_)
idle(); // Tell application I have sent everything so far
if (seqno > maxseq_) {
maxseq_ = seqno;
if (!rtt_active_) {
rtt_active_ = ;
if (seqno > rtt_seq_) {
rtt_seq_ = seqno;
rtt_ts_ = Scheduler::instance().clock();
} }
} else {
++nrexmitpack_;
nrexmitbytes_ += databytes;
}
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 == - && curseq_ <= TCP_MAXSEQ)
curseq_ = TCP_MAXSEQ;
else
curseq_ += (nbytes/size_ + (nbytes%size_ ? : ));
send_much(, , maxburst_);
} void TcpAgent::advanceby(int delta)
{
curseq_ += delta;
if (delta > )
closed_ = ;
send_much(, , maxburst_);
} int TcpAgent::command(int argc, const char*const* argv)
{
if (argc == ) {
if (strcmp(argv[], "advance") == ) {
int newseq = atoi(argv[]);
if (newseq > maxseq_)
advanceby(newseq - curseq_);
else
advanceby(maxseq_ - curseq_);
return (TCL_OK);
}
if (strcmp(argv[], "advanceby") == ) {
advanceby(atoi(argv[]));
return (TCL_OK);
}
if (strcmp(argv[], "eventtrace") == ) {
et_ = (EventTrace *)TclObject::lookup(argv[]);
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
* <ftp://ftp.ee.lbl.gov/papers/congavoid.ps.Z>
* (*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[], "persist") == ) {
TcpAgent *other
= (TcpAgent*)TclObject::lookup(argv[]);
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));
} /*
* Returns the window size adjusted to allow <num> segments past recovery
* point to be transmitted on next ack.
*/
int TcpAgent::force_wnd(int num)
{
return recover_ + num - (int)highest_ack_;
} int TcpAgent::window()
{
/*
* If F-RTO is enabled and first ack has come in, temporarily open
* window for sending two segments.
* The F-RTO code is from Pasi Sarolahti. F-RTO is an algorithm
* for detecting spurious retransmission timeouts.
*/
if (frto_ == ) {
return (force_wnd() < wnd_ ?
force_wnd() : (int)wnd_);
} else {
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)
{
send_idle_helper();
int win = window();
int npackets = ; if (!force && delsnd_timer_.status() == TIMER_PENDING)
return;
/* Save time when first packet was sent, for newreno --Allman */
if (t_seqno_ == )
firstsent_ = Scheduler::instance().clock(); if (burstsnd_timer_.status() == TIMER_PENDING)
return;
while (t_seqno_ <= highest_ack_ + win && t_seqno_ < curseq_) {
if (overhead_ == || force || qs_approved_) {
output(t_seqno_, reason);
npackets++;
if (QOption_)
process_qoption_after_send () ;
t_seqno_ ++ ;
if (qs_approved_ == ) {
// delay = effective RTT / window
double delay = (double) t_rtt_ * tcp_tick_ / win;
if (overhead_) {
delsnd_timer_.resched(delay + Random::uniform(overhead_));
} else {
delsnd_timer_.resched(delay);
}
return;
}
} 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_ + ;
rtt_active_ = ;
} /*
* 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_ < )
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;
} /*
* Limited Slow-Start for large congestion windows.
* This should only be called when max_ssthresh_ is non-zero.
*/
double TcpAgent::limited_slow_start(double cwnd, int max_ssthresh, double increment)
{
if (max_ssthresh <= ) {
return increment;
} else {
double increment1 = 0.0;
int round = int(cwnd / (double(max_ssthresh)/2.0));
if (round > ) {
increment1 = 1.0/double(round);
}
if (increment < increment1) {
return increment1;
} else {
return increment;
}
}
} /*
* For retrieving numdupacks_.
*/
int TcpAgent::numdupacks(double cwnd)
{
int cwndfraction = (int) cwnd/numdupacksFrac_;
if (numdupacks_ > cwndfraction) {
return numdupacks_;
} else {
return cwndfraction;
}
} /*
* Calculating the decrease parameter for highspeed TCP.
*/
double TcpAgent::decrease_param()
{
double decrease;
// OLD:
// decrease = linear(log(cwnd_), log(low_window_), 0.5, log(high_window_), high_decrease_);
// NEW (but equivalent):
decrease = hstcp_.dec1 + log(cwnd_) * hstcp_.dec2;
return decrease;
} /*
* Calculating the increase parameter for highspeed TCP.
*/
double TcpAgent::increase_param()
{
double increase, decrease, p, answer;
/* extending the slow-start for high-speed TCP */ /* 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.
// For an efficient implementation, this would just be looked up
// in a table, with the increase and decrease being a function of the
// congestion window. if (cwnd_ <= low_window_) {
answer = / cwnd_;
return answer;
} else if (cwnd_ >= hstcp_.cwnd_last_ &&
cwnd_ < hstcp_.cwnd_last_ + cwnd_range_) {
// cwnd_range_ can be set to 0 to be disabled,
// or can be set from 1 to 100
answer = hstcp_.increase_last_ / cwnd_;
return answer;
} else {
// OLD:
// p = exp(linear(log(cwnd_), log(low_window_), log(hstcp_.low_p), log(high_window_), log(high_p_)));
// NEW, but equivalent:
p = exp(hstcp_.p1 + log(cwnd_) * hstcp_.p2);
decrease = decrease_param();
// OLD:
// increase = cwnd_*cwnd_*p *(2.0*decrease)/(2.0 - decrease);
// NEW, but equivalent:
increase = cwnd_ * cwnd_ * p /(/decrease - 0.5);
// if (increase > max_increase) {
// increase = max_increase;
// }
answer = increase / cwnd_;
hstcp_.cwnd_last_ = cwnd_;
hstcp_.increase_last_ = increase;
return answer;
}
} /*
* open up the congestion window
*/
void TcpAgent::opencwnd()
{
double increment;
if (cwnd_ < ssthresh_) {
/* slow-start (exponential) */
cwnd_ += ;
} else {
/* linear */
double f;
switch (wnd_option_) {
case :
if (++count_ >= cwnd_) {
count_ = ;
++cwnd_;
}
break; case :
/* This is the standard algorithm. */
increment = increase_num_ / cwnd_;
if ((last_cwnd_action_ == ||
last_cwnd_action_ == CWND_ACTION_TIMEOUT)
&& max_ssthresh_ > ) {
increment = limited_slow_start(cwnd_,
max_ssthresh_, increment);
}
cwnd_ += increment;
break; case :
/* These are window increase algorithms
* for experimental purposes only. */
/* This is the Constant-Rate increase algorithm
* from the 1991 paper by S. Floyd on "Connections
* with Multiple Congested Gateways".
* The window is increased by roughly
* wnd_const_*RTT^2 packets per round-trip time. */
f = (t_srtt_ >> T_SRTT_BITS) * tcp_tick_;
f *= f;
f *= wnd_const_;
/* f = wnd_const_ * RTT^2 */
f += fcnt_;
if (f > cwnd_) {
fcnt_ = ;
++cwnd_;
} else
fcnt_ = f;
break; case :
/* The window is increased by roughly
* awnd_^2 * wnd_const_ packets per RTT,
* for awnd_ the average congestion window. */
f = awnd_;
f *= f;
f *= wnd_const_;
f += fcnt_;
if (f > cwnd_) {
fcnt_ = ;
++cwnd_;
} else
fcnt_ = f;
break; case :
/* The window is increased by roughly
* awnd_ * wnd_const_ packets per RTT,
* for awnd_ the average congestion window. */
f = awnd_;
f *= wnd_const_;
f += fcnt_;
if (f > cwnd_) {
fcnt_ = ;
++cwnd_;
} else
fcnt_ = f;
break;
case :
/* The window is increased by roughly wnd_const_*RTT
* packets per round-trip time, as discussed in
* the 1992 paper by S. Floyd on "On Traffic
* Phase Effects in Packet-Switched Gateways". */
f = (t_srtt_ >> T_SRTT_BITS) * tcp_tick_;
f *= wnd_const_;
f += fcnt_;
if (f > cwnd_) {
fcnt_ = ;
++cwnd_;
} else
fcnt_ = f;
break;
case :
/* binomial controls */
cwnd_ += increase_num_ / (cwnd_*pow(cwnd_,k_parameter_));
break;
case :
/* high-speed TCP, RFC 3649 */
increment = increase_param();
if ((last_cwnd_action_ == ||
last_cwnd_action_ == CWND_ACTION_TIMEOUT)
&& max_ssthresh_ > ) {
increment = limited_slow_start(cwnd_,
max_ssthresh_, increment);
}
cwnd_ += increment;
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;
} // Shigeyuki Osada 2012/01/08
void TcpAgent::recv_helper(Packet* pkt)
{
hdr_tcp *tcph = hdr_tcp::access(pkt);
//double now = Scheduler::instance().clock();
//hdr_ip *iph = hdr_ip::access(pkt);
//cout << now << ": Server recv pkt with win "
// << tcph->wnd()
// << " seqno: " << tcph->seqno()
// << " nodeid: " << iph->daddr()
// << "\n";
if (tcph->wnd() > ) {
// wnd == 0 can be occuerd but this is not considered.
// future works. Shigeyuki Osada 2012/01/08
wnd_ = tcph->wnd();
}
return;
} void
TcpAgent::slowdown(int how)
{
double decrease; /* added for highspeed - sylvia */
double win, halfwin, decreasewin;
int slowstart = ;
++ncwndcuts_;
if (!(how & TCP_IDLE) && !(how & NO_OUTSTANDING_DATA)){
++ncwndcuts1_;
}
// we are in slowstart for sure if cwnd < ssthresh
if (cwnd_ < ssthresh_)
slowstart = ;
if (precision_reduce_) {
halfwin = windowd() / ;
if (wnd_option_ == ) {
/* binomial controls */
decreasewin = windowd() - (1.0-decrease_num_)*pow(windowd(),l_parameter_);
} else if (wnd_option_ == && (cwnd_ > low_window_)) {
/* experimental highspeed TCP */
decrease = decrease_param();
//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() / );
halfwin = (double) temp;
if (wnd_option_ == ) {
/* binomial controls */
temp = (int)(window() - (1.0-decrease_num_)*pow(window(),l_parameter_));
} else if ((wnd_option_ == ) && (cwnd_ > low_window_)) {
/* experimental highspeed TCP */
decrease = decrease_param();
//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_ == || 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_ < *cwnd_/)
ssthresh_ = (int)(*cwnd_/);
if (how & CLOSE_CWND_HALF)
// For the first decrease, decrease by half
// even for non-standard values of decrease_num_.
if (first_decrease_ == || 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_ < )
cwnd_ = ;
}
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_ = ;
else if (how & CLOSE_CWND_HALF_WAY) {
// cwnd_ = win - (win - W_used)/2 ;
cwnd_ = W_used + decrease_num_ * (win - W_used);
if (cwnd_ < )
cwnd_ = ;
}
if (ssthresh_ < )
ssthresh_ = ;
if (how & (CLOSE_CWND_HALF|CLOSE_CWND_RESTART|CLOSE_CWND_INIT|CLOSE_CWND_ONE))
cong_action_ = TRUE; fcnt_ = count_ = ;
if (first_decrease_ == )
first_decrease_ = ;
// for event tracing slow start
if (cwnd_ == || 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_ = ;
last_ack_ = tcph->seqno();
prev_highest_ack_ = highest_ack_ ;
highest_ack_ = last_ack_; if (t_seqno_ < last_ack_ + )
t_seqno_ = last_ack_ + ;
/*
* 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_) {
ts_echo_=tcph->ts_echo();
rtt_update(now - tcph->ts_echo());
if (ts_resetRTO_ && (!ect_ || !ecn_backoff_ ||
!hdr_flags::access(pkt)->ecnecho())) {
// From Andrei Gurtov
/*
* Don't end backoff if still in ECN-Echo with
* a congestion window of 1 packet.
*/
t_backoff_ = ;
ecn_backoff_ = ;
}
}
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_ = ;
ecn_backoff_ = ;
}
rtt_active_ = ;
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_ = ;
} else ecn_backoff_ = ;
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 ;
else
return ;
} void TcpAgent::recv_newack_helper(Packet *pkt) {
//hdr_tcp *tcph = hdr_tcp::access(pkt);
newack(pkt);
if (qs_window_ && highest_ack_ >= qs_window_) {
// All segments in the QS window have been acknowledged.
// We can exit the Quick-Start phase.
qs_window_ = ;
}
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() == )))
opencwnd();
}
if (ect_) {
if (!hdr_flags::access(pkt)->ecnecho())
ecn_backoff_ = ;
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_ = ;
/* if the connection is done, call finish() */
if ((highest_ack_ >= curseq_-) && !closed_) {
closed_ = ;
finish();
}
if (QOption_ && curseq_ == highest_ack_ +) {
cancel_rtx_timer();
}
if (frto_ == ) {
/*
* New ack after RTO. If F-RTO is enabled, try to transmit new
* previously unsent segments.
* If there are no new data or receiver window limits the
* transmission, revert to traditional recovery.
*/
if (recover_ + >= highest_ack_ + wnd_ ||
recover_ + >= curseq_) {
frto_ = ;
} else if (highest_ack_ == recover_) {
/*
* F-RTO step 2a) RTO retransmission fixes whole
* window => cancel F-RTO
*/
frto_ = ;
} else {
t_seqno_ = recover_ + ;
frto_ = ;
}
} else if (frto_ == ) {
/*
* Second new ack after RTO. If F-RTO is enabled, RTO can be
* declared spurious
*/
spurious_timeout();
}
} /*
* Set the initial window.
*/
double
TcpAgent::initial_window()
{
// If Quick-Start Request was approved, use that as a basis for
// initial window
if (qs_cwnd_) {
return (qs_cwnd_);
}
//
// init_option = 1: static iw of wnd_init_
//
if (wnd_init_option_ == ) {
return (wnd_init_);
}
else if (wnd_init_option_ == ) {
// do iw according to Internet draft
if (size_ <= ) {
return (4.0);
} else if (size_ < ) {
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_ ) {
output(t_seqno_, );
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_) ||
(bugfix_ss_ && highest_ack_ == )) {
// (highest_ack_ == 0) added to allow Fast Retransmit
// when the first data packet is dropped.
// Bug report from Mark Allman.
goto tahoe_action;
} if (ecn_ && last_cwnd_action_ == CWND_ACTION_ECN) {
last_cwnd_action_ = CWND_ACTION_DUPACK;
slowdown(CLOSE_CWND_ONE);
reset_rtx_timer(,);
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:
recover_ = maxseq_;
if (!lossQuickStart()) {
// we are now going to fast-retransmit and willtrace that event
trace_event("FAST_RETX");
last_cwnd_action_ = CWND_ACTION_DUPACK;
slowdown(CLOSE_SSTHRESH_HALF|CLOSE_CWND_ONE);
}
reset_rtx_timer(,);
return;
} /*
* When exiting QuickStart, reduce the congestion window to the
* size that was actually used.
*/
void TcpAgent::endQuickStart()
{
qs_approved_ = ;
qs_cwnd_ = ;
qs_window_ = maxseq_;
int new_cwnd = maxseq_ - last_ack_;
if (new_cwnd > && new_cwnd < cwnd_) {
cwnd_ = new_cwnd;
if (cwnd_ < initial_window())
cwnd_ = initial_window();
}
} void TcpAgent::processQuickStart(Packet *pkt)
{
// QuickStart code from Srikanth Sundarrajan.
hdr_tcp *tcph = hdr_tcp::access(pkt);
hdr_qs *qsh = hdr_qs::access(pkt);
double now = Scheduler::instance().clock();
int app_rate; // printf("flag: %d ttl: %d ttl_diff: %d rate: %d\n", qsh->flag(),
// qsh->ttl(), ttl_diff_, qsh->rate());
qs_requested_ = ;
qs_approved_ = ;
if (qsh->flag() == QS_RESPONSE && qsh->ttl() == ttl_diff_ &&
qsh->rate() > ) {
app_rate = (int) ((hdr_qs::rate_to_Bps(qsh->rate()) *
(now - tcph->ts_echo())) / (size_ + headersize()));
if (print_request_) {
double num1 = hdr_qs::rate_to_Bps(qsh->rate());
double time = now - tcph->ts_echo();
int size = size_ + headersize();
printf("Quick Start request, rate: %g Bps, encoded rate: %d\n",
num1, qsh->rate());
printf("Quick Start request, window %d rtt: %4.2f pktsize: %d\n",
app_rate, time, size);
}
if (app_rate > initial_window()) {
qs_cwnd_ = app_rate;
qs_approved_ = ;
}
} else { // Quick Start rejected
#ifdef QS_DEBUG
printf("Quick Start rejected\n");
#endif
} } /*
* ACK has been received, hook from recv()
*/
void TcpAgent::recv_frto_helper(Packet *pkt)
{
hdr_tcp *tcph = hdr_tcp::access(pkt);
if (tcph->seqno() == last_ack_ && frto_ != ) {
/*
* Duplicate ACK while in F-RTO indicates that the
* timeout was valid. Go to slow start retransmissions.
*/
t_seqno_ = highest_ack_ + ;
cwnd_ = frto_;
frto_ = ; // Must zero dupacks (in order to trigger send_much at recv)
// dupacks is increased in recv after exiting this function
dupacks_ = -;
}
} /*
* A spurious timeout has been detected. Do appropriate actions.
*/
void TcpAgent::spurious_timeout()
{
frto_ = ; switch (spurious_response_) {
case :
default:
/*
* Full revert of congestion window
* (FlightSize before last acknowledgment)
*/
cwnd_ = t_seqno_ - prev_highest_ack_;
break; case :
/*
* cwnd = reduced ssthresh (approx. half of the earlier pipe)
*/
cwnd_ = ssthresh_; break;
case :
/*
* slow start, but without retransmissions
*/
cwnd_ = ; break;
} /*
* Revert ssthresh to size before retransmission timeout
*/
ssthresh_ = pipe_prev_; /* If timeout was spurious, bugfix is not needed */
recover_ = highest_ack_ - ;
} /*
* Loss occurred in Quick-Start window.
* If Quick-Start is enabled, packet loss in the QS phase, during slow-start,
* should trigger slow start instead of the regular fast retransmit.
* We use variable tcp_qs_recovery_ to toggle this behaviour on and off.
* If tcp_qs_recovery_ is true, initiate slow start to probe for
* a correct window size.
*
* Return value: non-zero if Quick-Start specific loss recovery took place
*/
int TcpAgent::lossQuickStart()
{
if (qs_window_ && tcp_qs_recovery_) {
//recover_ = maxseq_;
//reset_rtx_timer(1,0);
slowdown(CLOSE_CWND_INIT);
// reset ssthresh to half of W-D/2?
qs_window_ = ;
output(last_ack_ + , TCP_REASON_DUPACK);
return ;
}
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);
int valid_ack = ;
if (qs_approved_ == && tcph->seqno() > last_ack_)
endQuickStart();
if (qs_requested_ == )
processQuickStart(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);
recv_frto_helper(pkt);
/* grow cwnd and check if the connection is done */
if (tcph->seqno() > last_ack_) {
recv_newack_helper(pkt);
if (last_ack_ == && 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()); if (tcph->seqno() >= last_ack_)
// Check if ACK is valid. Suggestion by Mark Allman.
valid_ack = ;
Packet::free(pkt);
/*
* Try to send more data.
*/
if (valid_ack || aggressive_maxburst_)
send_much(, , 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(, 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"); frto_ = ;
// Set pipe_prev as per Eifel Response
pipe_prev_ = (window() > ssthresh_) ?
window() : (int)ssthresh_; if (cwnd_ < ) cwnd_ = ;
if (qs_approved_ == ) qs_approved_ = ;
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_ == - && wnd_init_option_ == )
/*
* First packet dropped, so don't use larger
* initial windows.
*/
wnd_init_option_ = ;
else if ((highest_ack_ == -) &&
(wnd_init_option_ == ) && (wnd_init_ > )
&& bugfix_ss_)
/*
* First packet dropped, so don't use larger
* initial windows. Bugfix from Mark Allman.
*/
wnd_init_ = ;
if (highest_ack_ == maxseq_ && restart_bugfix_)
/*
* if there is no outstanding data, don't cut
* down ssthresh_.
*/
slowdown(CLOSE_CWND_ONE|NO_OUTSTANDING_DATA);
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);
if (frto_enabled_ || sfrto_enabled_) {
frto_ = ;
}
}
else {
++nrexmit_;
last_cwnd_action_ = CWND_ACTION_TIMEOUT;
slowdown(CLOSE_SSTHRESH_HALF|CLOSE_CWND_RESTART);
if (frto_enabled_ || sfrto_enabled_) {
frto_ = ;
}
}
}
/* if there is no outstanding data, don't back off rtx timer */
if (highest_ack_ == maxseq_ && restart_bugfix_) {
reset_rtx_timer(,);
}
else {
reset_rtx_timer(,);
}
last_cwnd_action_ = CWND_ACTION_TIMEOUT;
send_much(, 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 */
output(last_ack_ + , TCP_REASON_DUPACK);
eln_last_rxmit_ = last_ack_+;
} else
send_much(, , 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 = ;
if (first_time == ) {
fprintf(stderr, "the TcpAgent::closecwnd() function is now deprecated, please use the function slowdown() instead\n");
}
switch (how) {
case :
/* timeouts */
ssthresh_ = int( window() / );
if (ssthresh_ < )
ssthresh_ = ;
cwnd_ = int(wnd_restart_);
break; case :
/* Reno dup acks, or after a recent congestion indication. */
// cwnd_ = window()/2;
cwnd_ = decrease_num_ * window();
ssthresh_ = int(cwnd_);
if (ssthresh_ < )
ssthresh_ = ;
break; case :
/* Tahoe dup acks
* after a recent congestion indication */
cwnd_ = wnd_init_;
break; case :
/* Retransmit timeout, but no outstanding data. */
cwnd_ = int(wnd_init_);
break;
case :
/* Tahoe dup acks */
ssthresh_ = int( window() / );
if (ssthresh_ < )
ssthresh_ = ;
cwnd_ = ;
break; default:
abort();
}
fcnt_ = .;
count_ = ;
} /*
* 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|TCP_IDLE) ;
for (int i = ; i < (tcp_now - T_last)/rto; i ++) {
slowdown(CWND_HALF_WITH_MIN|TCP_IDLE);
}
T_prev = tcp_now ;
W_used = ;
}
T_last = tcp_now ;
if (t_seqno_ == highest_ack_+ window()) {
T_prev = tcp_now ;
W_used = ;
}
else if (t_seqno_ == curseq_-) {
// 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|TCP_IDLE);
slowdown(CLOSE_CWND_HALF_WAY|TCP_IDLE);
T_prev = tcp_now ;
W_used = ;
}
}
} 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 < )
rtt = ;
if (tcp_now - T_last >= *rtt) {
// The sender has been idle.
int RTTs ;
RTTs = (tcp_now -T_last)*RTT_goodcount/(rtt*) ;
RTTs = RTTs - Backoffs ;
Backoffs = ;
if (RTTs > ) {
slowdown(THREE_QUARTER_SSTHRESH|TCP_IDLE) ;
for (int i = ; i < RTTs ; i ++) {
slowdown(CWND_HALF_WITH_MIN|TCP_IDLE);
RTT_prev = RTT_count ;
W_used = ;
}
}
}
T_last = tcp_now ;
if (tcp_now - T_start >= *rtt) {
if ((RTT_count > RTT_goodcount) || (F_full == )) {
RTT_goodcount = RTT_count ;
if (RTT_goodcount < ) RTT_goodcount = ;
}
RTT_prev = RTT_prev - RTT_count ;
RTT_count = ;
T_start = tcp_now ;
F_full = ;
}
if (t_seqno_ == highest_ack_ + window()) {
W_used = ;
F_full = ;
RTT_prev = RTT_count ;
}
else if (t_seqno_ == curseq_-) {
// 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 >= ) {
// The sender has been application-limited.
slowdown(THREE_QUARTER_SSTHRESH|TCP_IDLE) ;
slowdown(CLOSE_CWND_HALF_WAY|TCP_IDLE);
RTT_prev = RTT_count ;
Backoffs ++ ;
W_used = ;
}
}
if (F_counting == ) {
W_timed = t_seqno_ ;
F_counting = ;
}
} void TcpAgent::process_qoption_after_ack (int seqno)
{
if (F_counting == ) {
if (seqno >= W_timed) {
RTT_count ++ ;
F_counting = ;
}
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 != )
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 != )
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();
}

tcp.cc的更多相关文章

  1. TCP的.cc文件代码解释(中文)

    #ifndef lint static const char rcsid[] =     "@(#) $Header: /nfs/jade/vint/CVSROOT/ns-2/tcp/tcp ...

  2. [置顶] NS2中TCP拥塞控制仿真过程中盲点解析

    最近利用NS2做TCP拥塞控制协议的仿真,发现很多变量的方法含义都是解释的不清楚,给核心模块修改带来很多麻烦,所以决定用最准确的语言解释成员变量.方法,术语等的含义.限于个人水平,若有错误请留言指正! ...

  3. 网络-05-端口号-F5-负载均衡设-linux端口详解大全--TCP注册端口号大全备

    [root@test1:Standby] config # [root@test1:Standby] config # [root@test1:Standby] config # [root@test ...

  4. tcp-full.cc

    ns2--tcp-full.cc /* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */ /* * Copy ...

  5. 深入浅出 BPF TCP 拥塞算法实现原理

    本文地址:https://www.ebpf.top/post/ebpf_struct_ops 1. 前言 eBPF 的飞轮仍然在快速转动,自从 Linux 内核 5.6 版本支持 eBPF 程序修改 ...

  6. CoDel Test Script

    This TCL script is retrieved from http://www.pollere.net/CoDel.html in November 2013 :) # Codel test ...

  7. CloudFoundry.yml修订

    --- name: CFRELEASE02 director_uuid: fdd46e30-f2c5-41dc-9662-0976fdac5716 releases: - name: cf versi ...

  8. CentOS 7 服务端口表

    # Note that it is presently the policy of IANA to assign a single well-known# port number for both T ...

  9. 拒绝服务(DoS)理解、防御与实现

    一.说明 我一直不明白为什么拒绝服务,初学着喜欢拿来装逼.媒体喜欢吹得神乎其神.公司招聘也喜欢拿来做标准,因为我觉得拒绝服务和社会工程学就是最名不副实的两样东西.当然由于自己不明确拒绝服务在代码上是怎 ...

随机推荐

  1. J15W-J45W黄铜截止阀厂家,J15W-J45W黄铜截止阀价格 - 专题栏目 - 无极资讯网

    无极资讯网 首页 最新资讯 最新图集 最新标签   搜索 J15W-J45W黄铜截止阀 无极资讯网精心为您挑选了(J15W-J45W黄铜截止阀)信息,其中包含了(J15W-J45W黄铜截止阀)厂家,( ...

  2. Oracle 分区表(转)

    原文地址:http://love-flying-snow.iteye.com/blog/573303 废话少说,直接讲分区语法. Oracle表分区分为四种:范围分区,散列分区,列表分区和复合分区. ...

  3. Web测试注意事项

    参考文章:http://www.51testing.com/html/07/n-3723307.html 总结下遇到的web测试的时候需要注意的地方: 页面分辨率:  通常是计算机的默认分辨率,但是还 ...

  4. CentOS 升级 openSSH

    openSSH作为linux远程连接工具,容易受到攻击,必须更新版本来解决,低版本有如下等漏洞: a. OpenSSH 远程代码执行漏洞(CVE-2016-10009) b.  OpenSSH aut ...

  5. Golang教程:switch 语句

    switch 是一个条件语句,用于将一个表达式的求值结果与可能的值的列表进行匹配,并根据匹配结果执行相应的代码.可以认为 switch 语句是编写多个 if-else 子句的替代方式. 举例是说明问题 ...

  6. MySQL触发器基本使用

    文章参考:这里 MySQL中,创建触发器的基本语法: CREATE TRIGGER trigger_name trigger_time trigger_event ON tbl_name FOR EA ...

  7. bootstrap-select在angular上的应用

    1.bootstrap-select 依赖bootstrap.js ,又依赖jQuery,这些都可以用requirejs来处理. 2.一般bootstrap-select 都放在具体的模块上,而是动态 ...

  8. Nginx - 简易图片服务器

    安装 主要使用Nginx和vsftpd. 安装方面可以直接从nginx官网上下载,或者... yum install nginx 如果没有yum源则需要自行添加再进行install. yum inst ...

  9. Java基础(十二)IO输入输出

    一.IO 概述 1.IO 概念 IO:I 代表 Input 输入:O 代表 Output 输出. Java 中 IO 是以流为基础进行输入输出,所有的数据被串行化(保存)写入输出流,或者从输入流读入. ...

  10. Js窗口嵌套

    <script type="text/javascript"> if (window.parent.window != window) { window.top.loc ...