首页 > 代码库 > tcp-full.cc
tcp-full.cc
ns2--tcp-full.cc
1 /* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */ 2 3 /* 4 * Copyright (c) Intel Corporation 2001. All rights reserved. 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 /* 19 * Copyright (c) 1997, 1998 The Regents of the University of California. 20 * All rights reserved. 21 * 22 * Redistribution and use in source and binary forms, with or without 23 * modification, are permitted provided that the following conditions 24 * are met: 25 * 1. Redistributions of source code must retain the above copyright 26 * notice, this list of conditions and the following disclaimer. 27 * 2. Redistributions in binary form must reproduce the above copyright 28 * notice, this list of conditions and the following disclaimer in the 29 * documentation and/or other materials provided with the distribution. 30 * 3. All advertising materials mentioning features or use of this software 31 * must display the following acknowledgement: 32 * This product includes software developed by the Network Research 33 * Group at Lawrence Berkeley National Laboratory. 34 * 4. Neither the name of the University nor of the Laboratory may be used 35 * to endorse or promote products derived from this software without 36 * specific prior written permission. 37 * 38 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS‘‘ AND 39 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 40 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 41 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 42 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 43 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 44 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 45 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 46 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 47 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 48 * SUCH DAMAGE. 49 */ 50 51 /* 52 * 53 * Full-TCP : A two-way TCP very similar to the 4.4BSD version of Reno TCP. 54 * This version also includes variants Tahoe, NewReno, and SACK. 55 * 56 * This code below has received a fairly major restructuring (Aug. 2001). 57 * The ReassemblyQueue structure is now removed to a separate module and 58 * entirely re-written. 59 * Also, the SACK functionality has been re-written (almost) entirely. 60 * -KF [kfall@intel.com] 61 * 62 * This code below was motivated in part by code contributed by 63 * Kathie Nichols (nichols@baynetworks.com). The code below is based primarily 64 * on the 4.4BSD TCP implementation. -KF [kfall@ee.lbl.gov] 65 * 66 * Kathie Nichols and Van Jacobson have contributed significant bug fixes, 67 * especially with respect to the the handling of sequence numbers during 68 * connection establishment/clearin. Additional fixes have followed 69 * theirs. 70 * 71 * Fixes for gensack() and ReassemblyQueue::add() contributed by Richard 72 * Mortier <Richard.Mortier@cl.cam.ac.uk> 73 * 74 * Some warnings and comments: 75 * this version of TCP will not work correctly if the sequence number 76 * goes above 2147483648 due to sequence number wrap 77 * 78 * this version of TCP by default sends data at the beginning of a 79 * connection in the "typical" way... That is, 80 * A ------> SYN ------> B 81 * A <----- SYN+ACK ---- B 82 * A ------> ACK ------> B 83 * A ------> data -----> B 84 * 85 * there is no dynamic receiver‘s advertised window. The advertised 86 * window is simulated by simply telling the sender a bound on the window 87 * size (wnd_). 88 * 89 * in real TCP, a user process performing a read (via PRU_RCVD) 90 * calls tcp_output each time to (possibly) send a window 91 * update. Here we don‘t have a user process, so we simulate 92 * a user process always ready to consume all the receive buffer 93 * 94 * Notes: 95 * wnd_, wnd_init_, cwnd_, ssthresh_ are in segment units 96 * sequence and ack numbers are in byte units 97 * 98 * Futures: 99 * there are different existing TCPs with respect to how 100 * ack‘s are handled on connection startup. Some delay 101 * the ack for the first segment, which can cause connections 102 * to take longer to start up than if we be sure to ack it quickly. 103 * 104 * some TCPs arrange for immediate ACK generation if the incoming segment 105 * contains the PUSH bit 106 * 107 * 108 */ 109 110 #ifndef lint 111 static const char rcsid[] = 112 "@(#) $Header: /cvsroot/nsnam/ns-2/tcp/tcp-full.cc,v 1.130 2010/03/08 05:54:54 tom_henderson Exp $ (LBL)"; 113 #endif 114 115 #include "ip.h" 116 #include "tcp-full.h" 117 #include "flags.h" 118 #include "random.h" 119 #include "template.h" 120 121 #ifndef TRUE 122 #define TRUE 1 123 #endif 124 125 #ifndef FALSE 126 #define FALSE 0 127 #endif 128 129 /* 130 * Tcl Linkage for the following: 131 * Agent/TCP/FullTcp, Agent/TCP/FullTcp/Tahoe, 132 * Agent/TCP/FullTcp/Newreno, Agent/TCP/FullTcp/Sack 133 * 134 * See tcl/lib/ns-default.tcl for init methods for 135 * Tahoe, Newreno, and Sack 136 */ 137 138 static class FullTcpClass : public TclClass { 139 public: 140 FullTcpClass() : TclClass("Agent/TCP/FullTcp") {} 141 TclObject* create(int, const char*const*) { 142 return (new FullTcpAgent()); 143 } 144 } class_full; 145 146 static class TahoeFullTcpClass : public TclClass { 147 public: 148 TahoeFullTcpClass() : TclClass("Agent/TCP/FullTcp/Tahoe") {} 149 TclObject* create(int, const char*const*) { 150 // ns-default sets reno_fastrecov_ to false 151 return (new TahoeFullTcpAgent()); 152 } 153 } class_tahoe_full; 154 155 static class NewRenoFullTcpClass : public TclClass { 156 public: 157 NewRenoFullTcpClass() : TclClass("Agent/TCP/FullTcp/Newreno") {} 158 TclObject* create(int, const char*const*) { 159 // ns-default sets open_cwnd_on_pack_ to false 160 return (new NewRenoFullTcpAgent()); 161 } 162 } class_newreno_full; 163 164 static class SackFullTcpClass : public TclClass { 165 public: 166 SackFullTcpClass() : TclClass("Agent/TCP/FullTcp/Sack") {} 167 TclObject* create(int, const char*const*) { 168 // ns-default sets reno_fastrecov_ to false 169 // ns-default sets open_cwnd_on_pack_ to false 170 return (new SackFullTcpAgent()); 171 } 172 } class_sack_full; 173 174 /* 175 * Delayed-binding variable linkage 176 */ 177 178 void 179 FullTcpAgent::delay_bind_init_all() 180 { 181 delay_bind_init_one("segsperack_"); 182 delay_bind_init_one("segsize_"); 183 delay_bind_init_one("tcprexmtthresh_"); 184 delay_bind_init_one("iss_"); 185 delay_bind_init_one("nodelay_"); 186 delay_bind_init_one("data_on_syn_"); 187 delay_bind_init_one("dupseg_fix_"); 188 delay_bind_init_one("dupack_reset_"); 189 delay_bind_init_one("close_on_empty_"); 190 delay_bind_init_one("signal_on_empty_"); 191 delay_bind_init_one("interval_"); 192 delay_bind_init_one("ts_option_size_"); 193 delay_bind_init_one("reno_fastrecov_"); 194 delay_bind_init_one("pipectrl_"); 195 delay_bind_init_one("open_cwnd_on_pack_"); 196 delay_bind_init_one("halfclose_"); 197 delay_bind_init_one("nopredict_"); 198 delay_bind_init_one("ecn_syn_"); 199 delay_bind_init_one("ecn_syn_wait_"); 200 delay_bind_init_one("debug_"); 201 delay_bind_init_one("spa_thresh_"); 202 203 TcpAgent::delay_bind_init_all(); 204 205 reset(); 206 } 207 208 int 209 FullTcpAgent::delay_bind_dispatch(const char *varName, const char *localName, TclObject *tracer) 210 { 211 if (delay_bind(varName, localName, "segsperack_", &segs_per_ack_, tracer)) return TCL_OK; 212 if (delay_bind(varName, localName, "segsize_", &maxseg_, tracer)) return TCL_OK; 213 if (delay_bind(varName, localName, "tcprexmtthresh_", &tcprexmtthresh_, tracer)) return TCL_OK; 214 if (delay_bind(varName, localName, "iss_", &iss_, tracer)) return TCL_OK; 215 if (delay_bind(varName, localName, "spa_thresh_", &spa_thresh_, tracer)) return TCL_OK; 216 if (delay_bind_bool(varName, localName, "nodelay_", &nodelay_, tracer)) return TCL_OK; 217 if (delay_bind_bool(varName, localName, "data_on_syn_", &data_on_syn_, tracer)) return TCL_OK; 218 if (delay_bind_bool(varName, localName, "dupseg_fix_", &dupseg_fix_, tracer)) return TCL_OK; 219 if (delay_bind_bool(varName, localName, "dupack_reset_", &dupack_reset_, tracer)) return TCL_OK; 220 if (delay_bind_bool(varName, localName, "close_on_empty_", &close_on_empty_, tracer)) return TCL_OK; 221 if (delay_bind_bool(varName, localName, "signal_on_empty_", &signal_on_empty_, tracer)) return TCL_OK; 222 if (delay_bind_time(varName, localName, "interval_", &delack_interval_, tracer)) return TCL_OK; 223 if (delay_bind(varName, localName, "ts_option_size_", &ts_option_size_, tracer)) return TCL_OK; 224 if (delay_bind_bool(varName, localName, "reno_fastrecov_", &reno_fastrecov_, tracer)) return TCL_OK; 225 if (delay_bind_bool(varName, localName, "pipectrl_", &pipectrl_, tracer)) return TCL_OK; 226 if (delay_bind_bool(varName, localName, "open_cwnd_on_pack_", &open_cwnd_on_pack_, tracer)) return TCL_OK; 227 if (delay_bind_bool(varName, localName, "halfclose_", &halfclose_, tracer)) return TCL_OK; 228 if (delay_bind_bool(varName, localName, "nopredict_", &nopredict_, tracer)) return TCL_OK; 229 if (delay_bind_bool(varName, localName, "ecn_syn_", &ecn_syn_, tracer)) return TCL_OK; 230 if (delay_bind(varName, localName, "ecn_syn_wait_", &ecn_syn_wait_, tracer)) return TCL_OK; 231 if (delay_bind_bool(varName, localName, "debug_", &debug_, tracer)) return TCL_OK; 232 233 return TcpAgent::delay_bind_dispatch(varName, localName, tracer); 234 } 235 236 void 237 SackFullTcpAgent::delay_bind_init_all() 238 { 239 delay_bind_init_one("clear_on_timeout_"); 240 delay_bind_init_one("sack_rtx_cthresh_"); 241 delay_bind_init_one("sack_rtx_bthresh_"); 242 delay_bind_init_one("sack_block_size_"); 243 delay_bind_init_one("sack_option_size_"); 244 delay_bind_init_one("max_sack_blocks_"); 245 delay_bind_init_one("sack_rtx_threshmode_"); 246 FullTcpAgent::delay_bind_init_all(); 247 } 248 249 int 250 SackFullTcpAgent::delay_bind_dispatch(const char *varName, const char *localName, TclObject *tracer) 251 { 252 if (delay_bind_bool(varName, localName, "clear_on_timeout_", &clear_on_timeout_, tracer)) return TCL_OK; 253 if (delay_bind(varName, localName, "sack_rtx_cthresh_", &sack_rtx_cthresh_, tracer)) return TCL_OK; 254 if (delay_bind(varName, localName, "sack_rtx_bthresh_", &sack_rtx_bthresh_, tracer)) return TCL_OK; 255 if (delay_bind(varName, localName, "sack_rtx_threshmode_", &sack_rtx_threshmode_, tracer)) return TCL_OK; 256 if (delay_bind(varName, localName, "sack_block_size_", &sack_block_size_, tracer)) return TCL_OK; 257 if (delay_bind(varName, localName, "sack_option_size_", &sack_option_size_, tracer)) return TCL_OK; 258 if (delay_bind(varName, localName, "max_sack_blocks_", &max_sack_blocks_, tracer)) return TCL_OK; 259 return FullTcpAgent::delay_bind_dispatch(varName, localName, tracer); 260 } 261 262 int 263 FullTcpAgent::command(int argc, const char*const* argv) 264 { 265 // would like to have some "connect" primitive 266 // here, but the problem is that we get called before 267 // the simulation is running and we want to send a SYN. 268 // Because no routing exists yet, this fails. 269 // Instead, see code in advance(). 270 // 271 // listen can happen any time because it just changes state_ 272 // 273 // close is designed to happen at some point after the 274 // simulation is running (using an ns ‘at‘ command) 275 276 if (argc == 2) { 277 if (strcmp(argv[1], "listen") == 0) { 278 // just a state transition 279 listen(); 280 return (TCL_OK); 281 } 282 if (strcmp(argv[1], "close") == 0) { 283 usrclosed(); 284 return (TCL_OK); 285 } 286 } 287 if (argc == 3) { 288 if (strcmp(argv[1], "advance") == 0) { 289 advanceby(atoi(argv[2])); 290 return (TCL_OK); 291 } 292 if (strcmp(argv[1], "advanceby") == 0) { 293 advanceby(atoi(argv[2])); 294 return (TCL_OK); 295 } 296 if (strcmp(argv[1], "advance-bytes") == 0) { 297 advance_bytes(atoi(argv[2])); 298 return (TCL_OK); 299 } 300 } 301 if (argc == 4) { 302 if (strcmp(argv[1], "sendmsg") == 0) { 303 sendmsg(atoi(argv[2]), argv[3]); 304 return (TCL_OK); 305 } 306 } 307 return (TcpAgent::command(argc, argv)); 308 } 309 310 /* 311 * "User Interface" Functions for Full TCP 312 * advanceby(number of packets) 313 * advance_bytes(number of bytes) 314 * sendmsg(int bytes, char* buf) 315 * listen 316 * close 317 */ 318 319 /* 320 * the ‘advance‘ interface to the regular tcp is in packet 321 * units. Here we scale this to bytes for full tcp. 322 * 323 * ‘advance‘ is normally called by an "application" (i.e. data source) 324 * to signal that there is something to send 325 * 326 * ‘curseq_‘ is the sequence number of the last byte provided 327 * by the application. In the case where no data has been supplied 328 * by the application, curseq_ is the iss_. 329 */ 330 void 331 FullTcpAgent::advanceby(int np) 332 { 333 // XXX hack: 334 // because np is in packets and a data source 335 // may pass a *huge* number as a way to tell us 336 // to go forever, just look for the huge number 337 // and if it‘s there, pre-divide it 338 if (np >= 0x10000000) 339 np /= maxseg_; 340 341 advance_bytes(np * maxseg_); 342 return; 343 } 344 345 /* 346 * the byte-oriented interface: advance_bytes(int nbytes) 347 */ 348 349 void 350 FullTcpAgent::advance_bytes(int nb) 351 { 352 353 // 354 // state-specific operations: 355 // if CLOSED or LISTEN, reset and try a new active open/connect 356 // if ESTABLISHED, queue and try to send more 357 // if SYN_SENT or SYN_RCVD, just queue 358 // if above ESTABLISHED, we are closing, so don‘t allow 359 // 360 361 switch (state_) { 362 363 case TCPS_CLOSED: 364 case TCPS_LISTEN: 365 reset(); 366 curseq_ = iss_ + nb; 367 connect(); // initiate new connection 368 break; 369 370 case TCPS_ESTABLISHED: 371 case TCPS_SYN_SENT: 372 case TCPS_SYN_RECEIVED: 373 if (curseq_ < iss_) 374 curseq_ = iss_; 375 curseq_ += nb; 376 break; 377 378 default: 379 if (debug_) 380 fprintf(stderr, "%f: FullTcpAgent::advance(%s): cannot advance while in state %s\n", 381 now(), name(), statestr(state_)); 382 383 } 384 385 if (state_ == TCPS_ESTABLISHED) 386 send_much(0, REASON_NORMAL, maxburst_); 387 388 return; 389 } 390 391 /* 392 * If MSG_EOF is set, by setting close_on_empty_ to TRUE, we ensure that 393 * a FIN will be sent when the send buffer emptys. 394 * If DAT_EOF is set, the callback function done_data is called 395 * when the send buffer empty 396 * 397 * When (in the future?) FullTcpAgent implements T/TCP, avoidance of 3-way 398 * handshake can be handled in this function. 399 */ 400 void 401 FullTcpAgent::sendmsg(int nbytes, const char *flags) 402 { 403 if (flags && strcmp(flags, "MSG_EOF") == 0) 404 close_on_empty_ = TRUE; 405 if (flags && strcmp(flags, "DAT_EOF") == 0) 406 signal_on_empty_ = TRUE; 407 408 if (nbytes == -1) { 409 infinite_send_ = TRUE; 410 advance_bytes(0); 411 } else 412 advance_bytes(nbytes); 413 } 414 415 /* 416 * do an active open 417 * (in real TCP, see tcp_usrreq, case PRU_CONNECT) 418 */ 419 void 420 FullTcpAgent::connect() 421 { 422 newstate(TCPS_SYN_SENT); // sending a SYN now 423 sent(iss_, foutput(iss_, REASON_NORMAL)); 424 return; 425 } 426 427 /* 428 * be a passive opener 429 * (in real TCP, see tcp_usrreq, case PRU_LISTEN) 430 * (for simulation, make this peer‘s ptype ACKs) 431 */ 432 void 433 FullTcpAgent::listen() 434 { 435 newstate(TCPS_LISTEN); 436 type_ = PT_ACK; // instead of PT_TCP 437 } 438 439 440 /* 441 * This function is invoked when the sender buffer is empty. It in turn 442 * invokes the Tcl done_data procedure that was registered with TCP. 443 */ 444 445 void 446 FullTcpAgent::bufferempty() 447 { 448 signal_on_empty_=FALSE; 449 Tcl::instance().evalf("%s done_data", this->name()); 450 } 451 452 453 /* 454 * called when user/application performs ‘close‘ 455 */ 456 457 void 458 FullTcpAgent::usrclosed() 459 { 460 curseq_ = maxseq_ - 1; // now, no more data 461 infinite_send_ = FALSE; // stop infinite send 462 463 switch (state_) { 464 case TCPS_CLOSED: 465 case TCPS_LISTEN: 466 cancel_timers(); 467 newstate(TCPS_CLOSED); 468 finish(); 469 break; 470 case TCPS_SYN_SENT: 471 newstate(TCPS_CLOSED); 472 /* fall through */ 473 case TCPS_LAST_ACK: 474 flags_ |= TF_NEEDFIN; 475 send_much(1, REASON_NORMAL, maxburst_); 476 break; 477 case TCPS_SYN_RECEIVED: 478 case TCPS_ESTABLISHED: 479 newstate(TCPS_FIN_WAIT_1); 480 flags_ |= TF_NEEDFIN; 481 send_much(1, REASON_NORMAL, maxburst_); 482 break; 483 case TCPS_CLOSE_WAIT: 484 newstate(TCPS_LAST_ACK); 485 flags_ |= TF_NEEDFIN; 486 send_much(1, REASON_NORMAL, maxburst_); 487 break; 488 case TCPS_FIN_WAIT_1: 489 case TCPS_FIN_WAIT_2: 490 case TCPS_CLOSING: 491 /* usr asked for a close more than once [?] */ 492 if (debug_) 493 fprintf(stderr, 494 "%f FullTcpAgent(%s): app close in bad state %s\n", 495 now(), name(), statestr(state_)); 496 break; 497 default: 498 if (debug_) 499 fprintf(stderr, 500 "%f FullTcpAgent(%s): app close in unknown state %s\n", 501 now(), name(), statestr(state_)); 502 } 503 504 return; 505 } 506 507 /* 508 * Utility type functions 509 */ 510 511 void 512 FullTcpAgent::cancel_timers() 513 { 514 515 // cancel: rtx, burstsend, delsnd 516 TcpAgent::cancel_timers(); 517 // cancel: delack 518 delack_timer_.force_cancel(); 519 } 520 521 void 522 FullTcpAgent::newstate(int state) 523 { 524 //printf("%f(%s): state changed from %s to %s\n", 525 //now(), name(), statestr(state_), statestr(state)); 526 527 state_ = state; 528 } 529 530 void 531 FullTcpAgent::prpkt(Packet *pkt) 532 { 533 hdr_tcp *tcph = hdr_tcp::access(pkt); // TCP header 534 hdr_cmn *th = hdr_cmn::access(pkt); // common header (size, etc) 535 //hdr_flags *fh = hdr_flags::access(pkt); // flags (CWR, CE, bits) 536 hdr_ip* iph = hdr_ip::access(pkt); 537 int datalen = th->size() - tcph->hlen(); // # payload bytes 538 539 fprintf(stdout, " [%d:%d.%d>%d.%d] (hlen:%d, dlen:%d, seq:%d, ack:%d, flags:0x%x (%s), salen:%d, reason:0x%x)\n", 540 th->uid(), 541 iph->saddr(), iph->sport(), 542 iph->daddr(), iph->dport(), 543 tcph->hlen(), 544 datalen, 545 tcph->seqno(), 546 tcph->ackno(), 547 tcph->flags(), flagstr(tcph->flags()), 548 tcph->sa_length(), 549 tcph->reason()); 550 } 551 552 char * 553 FullTcpAgent::flagstr(int hflags) 554 { 555 // update this if tcp header flags change 556 static char *flagstrs[28] = { 557 "<null>", "<FIN>", "<SYN>", "<SYN,FIN>", // 0-3 558 "<?>", "<?,FIN>", "<?,SYN>", "<?,SYN,FIN>", // 4-7 559 "<PSH>", "<PSH,FIN>", "<PSH,SYN>", "<PSH,SYN,FIN>", // 0x08-0x0b 560 /* do not use <??, in next line because that‘s an ANSI trigraph */ 561 "<?>", "<?,FIN>", "<?,SYN>", "<?,SYN,FIN>", // 0x0c-0x0f 562 "<ACK>", "<ACK,FIN>", "<ACK,SYN>", "<ACK,SYN,FIN>", // 0x10-0x13 563 "<ACK>", "<ACK,FIN>", "<ACK,SYN>", "<ACK,SYN,FIN>", // 0x14-0x17 564 "<PSH,ACK>", "<PSH,ACK,FIN>", "<PSH,ACK,SYN>", "<PSH,ACK,SYN,FIN>", // 0x18-0x1b 565 }; 566 if (hflags < 0 || (hflags > 28)) { 567 /* Added strings for CWR and ECE -M. Weigle 6/27/02 */ 568 if (hflags == 72) 569 return ("<ECE,PSH>"); 570 else if (hflags == 80) 571 return ("<ECE,ACK>"); 572 else if (hflags == 88) 573 return ("<ECE,PSH,ACK>"); 574 else if (hflags == 152) 575 return ("<CWR,PSH,ACK>"); 576 else if (hflags == 153) 577 return ("<CWR,PSH,ACK,FIN>"); 578 else 579 return ("<invalid>"); 580 } 581 return (flagstrs[hflags]); 582 } 583 584 char * 585 FullTcpAgent::statestr(int state) 586 { 587 static char *statestrs[TCP_NSTATES] = { 588 "CLOSED", "LISTEN", "SYN_SENT", "SYN_RCVD", 589 "ESTABLISHED", "CLOSE_WAIT", "FIN_WAIT_1", "CLOSING", 590 "LAST_ACK", "FIN_WAIT_2" 591 }; 592 if (state < 0 || (state >= TCP_NSTATES)) 593 return ("INVALID"); 594 return (statestrs[state]); 595 } 596 597 void 598 DelAckTimer::expire(Event *) { 599 a_->timeout(TCP_TIMER_DELACK); 600 } 601 602 /* 603 * reset to starting point, don‘t set state_ here, 604 * because our starting point might be LISTEN rather 605 * than CLOSED if we‘re a passive opener 606 */ 607 void 608 FullTcpAgent::reset() 609 { 610 cancel_timers(); // cancel timers first 611 TcpAgent::reset(); // resets most variables 612 rq_.clear(); // clear reassembly queue 613 rtt_init(); // zero rtt, srtt, backoff 614 615 last_ack_sent_ = -1; 616 rcv_nxt_ = -1; 617 pipe_ = 0; 618 rtxbytes_ = 0; 619 flags_ = 0; 620 t_seqno_ = iss_; 621 maxseq_ = -1; 622 irs_ = -1; 623 last_send_time_ = -1.0; 624 if (ts_option_) 625 recent_ = recent_age_ = 0.0; 626 else 627 recent_ = recent_age_ = -1.0; 628 629 fastrecov_ = FALSE; 630 631 closed_ = 0; 632 close_on_empty_ = FALSE; 633 634 if (ecn_syn_) 635 ecn_syn_next_ = 1; 636 else 637 ecn_syn_next_ = 0; 638 639 } 640 641 /* 642 * This function is invoked when the connection is done. It in turn 643 * invokes the Tcl finish procedure that was registered with TCP. 644 * This function mimics tcp_close() 645 */ 646 647 void 648 FullTcpAgent::finish() 649 { 650 Tcl::instance().evalf("%s done", this->name()); 651 } 652 /* 653 * headersize: 654 * how big is an IP+TCP header in bytes; include options such as ts 655 * this function should be virtual so others (e.g. SACK) can override 656 */ 657 int 658 FullTcpAgent::headersize() 659 { 660 int total = tcpip_base_hdr_size_; 661 if (total < 1) { 662 fprintf(stderr, 663 "%f: FullTcpAgent(%s): warning: tcpip hdr size is only %d bytes\n", 664 now(), name(), tcpip_base_hdr_size_); 665 } 666 667 if (ts_option_) 668 total += ts_option_size_; 669 670 return (total); 671 } 672 673 /* 674 * flags that are completely dependent on the tcp state 675 * these are used for the next outgoing packet in foutput() 676 * (in real TCP, see tcp_fsm.h, the "tcp_outflags" array) 677 */ 678 int 679 FullTcpAgent::outflags() 680 { 681 // in real TCP an RST is added in the CLOSED state 682 static int tcp_outflags[TCP_NSTATES] = { 683 TH_ACK, /* 0, CLOSED */ 684 0, /* 1, LISTEN */ 685 TH_SYN, /* 2, SYN_SENT */ 686 TH_SYN|TH_ACK, /* 3, SYN_RECEIVED */ 687 TH_ACK, /* 4, ESTABLISHED */ 688 TH_ACK, /* 5, CLOSE_WAIT */ 689 TH_FIN|TH_ACK, /* 6, FIN_WAIT_1 */ 690 TH_FIN|TH_ACK, /* 7, CLOSING */ 691 TH_FIN|TH_ACK, /* 8, LAST_ACK */ 692 TH_ACK, /* 9, FIN_WAIT_2 */ 693 /* 10, TIME_WAIT --- not used in simulator */ 694 }; 695 696 if (state_ < 0 || (state_ >= TCP_NSTATES)) { 697 fprintf(stderr, "%f FullTcpAgent(%s): invalid state %d\n", 698 now(), name(), state_); 699 return (0x0); 700 } 701 702 return (tcp_outflags[state_]); 703 } 704 705 /* 706 * reaass() -- extract the appropriate fields from the packet 707 * and pass this info the ReassemblyQueue add routine 708 * 709 * returns the TCP header flags representing the "or" of 710 * the flags contained in the adjacent sequence # blocks 711 */ 712 713 int 714 FullTcpAgent::reass(Packet* pkt) 715 { 716 hdr_tcp *tcph = hdr_tcp::access(pkt); 717 hdr_cmn *th = hdr_cmn::access(pkt); 718 719 int start = tcph->seqno(); 720 int end = start + th->size() - tcph->hlen(); 721 int tiflags = tcph->flags(); 722 int fillshole = (start == rcv_nxt_); 723 int flags; 724 725 // end contains the seq of the last byte of 726 // in the packet plus one 727 728 if (start == end && (tiflags & TH_FIN) == 0) { 729 fprintf(stderr, "%f: FullTcpAgent(%s)::reass() -- bad condition - adding non-FIN zero-len seg\n", 730 now(), name()); 731 abort(); 732 } 733 734 flags = rq_.add(start, end, tiflags, 0); 735 736 //present: 737 // 738 // If we‘ve never received a SYN (unlikely) 739 // or this is an out of order addition, no reason to coalesce 740 // 741 742 if (TCPS_HAVERCVDSYN(state_) == 0 || !fillshole) { 743 return (0x00); 744 } 745 // 746 // If we get some data in SYN_RECVD, no need to present to user yet 747 // 748 if (state_ == TCPS_SYN_RECEIVED && (end > start)) 749 return (0x00); 750 751 // clear out data that has been passed, up to rcv_nxt_, 752 // collects flags 753 754 flags |= rq_.cleartonxt(); 755 756 return (flags); 757 } 758 759 /* 760 * utility function to set rcv_next_ during inital exchange of seq #s 761 */ 762 763 int 764 FullTcpAgent::rcvseqinit(int seq, int dlen) 765 { 766 return (seq + dlen + 1); 767 } 768 769 /* 770 * build a header with the timestamp option if asked 771 */ 772 int 773 FullTcpAgent::build_options(hdr_tcp* tcph) 774 { 775 int total = 0; 776 if (ts_option_) { 777 tcph->ts() = now(); 778 tcph->ts_echo() = recent_; 779 total += ts_option_size_; 780 } else { 781 tcph->ts() = tcph->ts_echo() = -1.0; 782 } 783 return (total); 784 } 785 786 /* 787 * pack() -- is the ACK a partial ACK? (not past recover_) 788 */ 789 790 int 791 FullTcpAgent::pack(Packet *pkt) 792 { 793 hdr_tcp *tcph = hdr_tcp::access(pkt); 794 /* Added check for fast recovery. -M. Weigle 5/2/02 */ 795 return (fastrecov_ && tcph->ackno() >= highest_ack_ && 796 tcph->ackno() < recover_); 797 } 798 799 /* 800 * baseline reno TCP exists fast recovery on a partial ACK 801 */ 802 803 void 804 FullTcpAgent::pack_action(Packet*) 805 { 806 if (reno_fastrecov_ && fastrecov_ && cwnd_ > double(ssthresh_)) { 807 cwnd_ = double(ssthresh_); // retract window if inflated 808 } 809 fastrecov_ = FALSE; 810 //printf("%f: EXITED FAST RECOVERY\n", now()); 811 dupacks_ = 0; 812 } 813 814 /* 815 * ack_action -- same as partial ACK action for base Reno TCP 816 */ 817 818 void 819 FullTcpAgent::ack_action(Packet* p) 820 { 821 FullTcpAgent::pack_action(p); 822 } 823 824 825 /* 826 * sendpacket: 827 * allocate a packet, fill in header fields, and send 828 * also keeps stats on # of data pkts, acks, re-xmits, etc 829 * 830 * fill in packet fields. Agent::allocpkt() fills 831 * in most of the network layer fields for us. 832 * So fill in tcp hdr and adjust the packet size. 833 * 834 * Also, set the size of the tcp header. 835 */ 836 void 837 FullTcpAgent::sendpacket(int seqno, int ackno, int pflags, int datalen, int reason, Packet *p) 838 { 839 if (!p) p = allocpkt(); 840 hdr_tcp *tcph = hdr_tcp::access(p); 841 hdr_flags *fh = hdr_flags::access(p); 842 843 /* build basic header w/options */ 844 845 tcph->seqno() = seqno; 846 tcph->ackno() = ackno; 847 tcph->flags() = pflags; 848 tcph->reason() |= reason; // make tcph->reason look like ns1 pkt->flags? 849 tcph->sa_length() = 0; // may be increased by build_options() 850 tcph->hlen() = tcpip_base_hdr_size_; 851 tcph->hlen() += build_options(tcph); 852 853 /* 854 * Explicit Congestion Notification (ECN) related: 855 * Bits in header: 856 * ECT (EC Capable Transport), 857 * ECNECHO (ECHO of ECN Notification generated at router), 858 * CWR (Congestion Window Reduced from RFC 2481) 859 * States in TCP: 860 * ecn_: I am supposed to do ECN if my peer does 861 * ect_: I am doing ECN (ecn_ should be T and peer does ECN) 862 */ 863 864 if (datalen > 0 && ecn_ ){ 865 // set ect on data packets 866 fh->ect() = ect_; // on after mutual agreement on ECT 867 } else if (ecn_ && ecn_syn_ && ecn_syn_next_ && (pflags & TH_SYN) && (pflags & TH_ACK)) { 868 // set ect on syn/ack packet, if syn packet was negotiating ECT 869 fh->ect() = ect_; 870 } else { 871 /* Set ect() to 0. -M. Weigle 1/19/05 */ 872 fh->ect() = 0; 873 } 874 if (ecn_ && ect_ && recent_ce_ ) { 875 // This is needed here for the ACK in a SYN, SYN/ACK, ACK 876 // sequence. 877 pflags |= TH_ECE; 878 } 879 // fill in CWR and ECE bits which don‘t actually sit in 880 // the tcp_flags but in hdr_flags 881 if ( pflags & TH_ECE) { 882 fh->ecnecho() = 1; 883 } else { 884 fh->ecnecho() = 0; 885 } 886 if ( pflags & TH_CWR ) { 887 fh->cong_action() = 1; 888 } 889 else { 890 /* Set cong_action() to 0 -M. Weigle 1/19/05 */ 891 fh->cong_action() = 0; 892 } 893 894 /* actual size is data length plus header length */ 895 896 hdr_cmn *ch = hdr_cmn::access(p); 897 ch->size() = datalen + tcph->hlen(); 898 899 if (datalen <= 0) 900 ++nackpack_; 901 else { 902 ++ndatapack_; 903 ndatabytes_ += datalen; 904 last_send_time_ = now(); // time of last data 905 } 906 if (reason == REASON_TIMEOUT || reason == REASON_DUPACK || reason == REASON_SACK) { 907 ++nrexmitpack_; 908 nrexmitbytes_ += datalen; 909 } 910 911 last_ack_sent_ = ackno; 912 913 //if (state_ != TCPS_ESTABLISHED) { 914 //printf("%f(%s)[state:%s]: sending pkt ", now(), name(), statestr(state_)); 915 //prpkt(p); 916 //} 917 918 send(p, 0); 919 920 return; 921 } 922 923 // 924 // reset_rtx_timer: called during a retransmission timeout 925 // to perform exponential backoff. Also, note that because 926 // we have performed a retransmission, our rtt timer is now 927 // invalidated (indicate this by setting rtt_active_ false) 928 // 929 void 930 FullTcpAgent::reset_rtx_timer(int /* mild */) 931 { 932 // cancel old timer, set a new one 933 /* if there is no outstanding data, don‘t back off rtx timer * 934 * (Fix from T. Kelly.) */ 935 if (!(highest_ack_ == maxseq_ && restart_bugfix_)) { 936 rtt_backoff(); // double current timeout 937 } 938 set_rtx_timer(); // set new timer 939 rtt_active_ = FALSE; // no timing during this window 940 } 941 942 /* 943 * see if we should send a segment, and if so, send it 944 * (may be ACK or data) 945 * return the number of data bytes sent (count a SYN or FIN as 1 each) 946 * 947 * simulator var, desc (name in real TCP) 948 * -------------------------------------- 949 * maxseq_, largest seq# we‘ve sent plus one (snd_max) 950 * flags_, flags regarding our internal state (t_state) 951 * pflags, a local used to build up the tcp header flags (flags) 952 * curseq_, is the highest sequence number given to us by "application" 953 * highest_ack_, the highest ACK we‘ve seen for our data (snd_una-1) 954 * seqno, the next seq# we‘re going to send (snd_nxt) 955 */ 956 int 957 FullTcpAgent::foutput(int seqno, int reason) 958 { 959 // if maxseg_ not set, set it appropriately 960 // Q: how can this happen? 961 962 if (maxseg_ == 0) 963 maxseg_ = size_ - headersize(); 964 else 965 size_ = maxseg_ + headersize(); 966 967 int is_retransmit = (seqno < maxseq_); 968 int quiet = (highest_ack_ == maxseq_); 969 int pflags = outflags(); 970 int syn = (seqno == iss_); 971 int emptying_buffer = FALSE; 972 int buffered_bytes = (infinite_send_) ? TCP_MAXSEQ : 973 curseq_ - highest_ack_ + 1; 974 975 int win = window() * maxseg_; // window (in bytes) 976 int off = seqno - highest_ack_; // offset of seg in window 977 int datalen; 978 //int amtsent = 0; 979 980 // be careful if we have not received any ACK yet 981 if (highest_ack_ < 0) { 982 if (!infinite_send_) 983 buffered_bytes = curseq_ - iss_;; 984 off = seqno - iss_; 985 } 986 987 if (syn && !data_on_syn_) 988 datalen = 0; 989 else if (pipectrl_) 990 datalen = buffered_bytes - off; 991 else 992 datalen = min(buffered_bytes, win) - off; 993 994 if ((signal_on_empty_) && (!buffered_bytes) && (!syn)) 995 bufferempty(); 996 997 // 998 // in real TCP datalen (len) could be < 0 if there was window 999 // shrinkage, or if a FIN has been sent and neither ACKd nor1000 // retransmitted. Only this 2nd case concerns us here...1001 //1002 if (datalen < 0) {1003 datalen = 0;1004 } else if (datalen > maxseg_) {1005 datalen = maxseg_;1006 }1007 1008 //1009 // this is an option that causes us to slow-start if we‘ve1010 // been idle for a "long" time, where long means a rto or longer1011 // the slow-start is a sort that does not set ssthresh1012 //1013 1014 if (slow_start_restart_ && quiet && datalen > 0) {1015 if (idle_restart()) {1016 slowdown(CLOSE_CWND_INIT);1017 }1018 }1019 1020 //1021 // see if sending this packet will empty the send buffer1022 // a dataless SYN packet counts also1023 //1024 1025 if (!infinite_send_ && ((seqno + datalen) > curseq_ || 1026 (syn && datalen == 0))) {1027 emptying_buffer = TRUE;1028 //1029 // if not a retransmission, notify application that 1030 // everything has been sent out at least once.1031 //1032 if (!syn) {1033 idle();1034 if (close_on_empty_ && quiet) {1035 flags_ |= TF_NEEDCLOSE;1036 }1037 }1038 pflags |= TH_PUSH;1039 //1040 // if close_on_empty set, we are finished1041 // with this connection; close it1042 //1043 } else {1044 /* not emptying buffer, so can‘t be FIN */1045 pflags &= ~TH_FIN;1046 }1047 if (infinite_send_ && (syn && datalen == 0))1048 pflags |= TH_PUSH; // set PUSH for dataless SYN1049 1050 /* sender SWS avoidance (Nagle) */1051 1052 if (datalen > 0) {1053 // if full-sized segment, ok1054 if (datalen == maxseg_)1055 goto send;1056 // if Nagle disabled and buffer clearing, ok1057 if ((quiet || nodelay_) && emptying_buffer)1058 goto send;1059 // if a retransmission1060 if (is_retransmit)1061 goto send;1062 // if big "enough", ok...1063 // (this is not a likely case, and would1064 // only happen for tiny windows)1065 if (datalen >= ((wnd_ * maxseg_) / 2.0))1066 goto send;1067 }1068 1069 if (need_send())1070 goto send;1071 1072 /*1073 * send now if a control packet or we owe peer an ACK1074 * TF_ACKNOW can be set during connection establishment and1075 * to generate acks for out-of-order data1076 */1077 1078 if ((flags_ & (TF_ACKNOW|TF_NEEDCLOSE)) ||1079 (pflags & (TH_SYN|TH_FIN))) {1080 goto send;1081 }1082 1083 /* 1084 * No reason to send a segment, just return.1085 */ 1086 return 0;1087 1088 send:1089 1090 // is a syn or fin?1091 1092 syn = (pflags & TH_SYN) ? 1 : 0;1093 int fin = (pflags & TH_FIN) ? 1 : 0;1094 1095 /* setup ECN syn and ECN SYN+ACK packet headers */1096 if (ecn_ && syn && !(pflags & TH_ACK)){1097 pflags |= TH_ECE;1098 pflags |= TH_CWR;1099 }1100 if (ecn_ && syn && (pflags & TH_ACK)){1101 pflags |= TH_ECE;1102 pflags &= ~TH_CWR;1103 }1104 else if (ecn_ && ect_ && cong_action_ && 1105 (!is_retransmit || SetCWRonRetransmit_)) {1106 /* 1107 * Don‘t set CWR for a retranmitted SYN+ACK (has ecn_ 1108 * and cong_action_ set).1109 * -M. Weigle 6/19/021110 *1111 * SetCWRonRetransmit_ was changed to true,1112 * allowing CWR on retransmitted data packets. 1113 * See test ecn_burstyEcn_reno_full 1114 * in test-suite-ecn-full.tcl.1115 * - Sally Floyd, 6/5/08.1116 */1117 /* set CWR if necessary */1118 pflags |= TH_CWR;1119 /* Turn cong_action_ off: Added 6/5/08, Sally Floyd. */1120 cong_action_ = FALSE;1121 }1122 1123 /* moved from sendpacket() -M. Weigle 6/19/02 */1124 //1125 // although CWR bit is ordinarily associated with ECN,1126 // it has utility within the simulator for traces. Thus, set1127 // it even if we aren‘t doing ECN1128 //1129 if (datalen > 0 && cong_action_ && !is_retransmit) {1130 pflags |= TH_CWR;1131 }1132 1133 /* set ECE if necessary */1134 if (ecn_ && ect_ && recent_ce_ ) {1135 pflags |= TH_ECE;1136 }1137 1138 /* 1139 * Tack on the FIN flag to the data segment if close_on_empty_1140 * was previously set-- avoids sending a separate FIN1141 */ 1142 if (flags_ & TF_NEEDCLOSE) {1143 flags_ &= ~TF_NEEDCLOSE;1144 if (state_ <= TCPS_ESTABLISHED && state_ != TCPS_CLOSED)1145 {1146 pflags |=TH_FIN;1147 fin = 1; /* FIN consumes sequence number */1148 newstate(TCPS_FIN_WAIT_1);1149 }1150 }1151 sendpacket(seqno, rcv_nxt_, pflags, datalen, reason);1152 1153 /* 1154 * Data sent (as far as we can tell).1155 * Any pending ACK has now been sent.1156 */ 1157 flags_ &= ~(TF_ACKNOW|TF_DELACK);1158 1159 /*1160 * if we have reacted to congestion recently, the1161 * slowdown() procedure will have set cong_action_ and1162 * sendpacket will have copied that to the outgoing pkt1163 * CWR field. If that packet contains data, then1164 * it will be reliably delivered, so we are free to turn off the1165 * cong_action_ state now If only a pure ACK, we keep the state1166 * around until we actually send a segment1167 */1168 1169 int reliable = datalen + syn + fin; // seq #‘s reliably sent1170 /* 1171 * Don‘t reset cong_action_ until we send new data.1172 * -M. Weigle 6/19/021173 */1174 if (cong_action_ && reliable > 0 && !is_retransmit)1175 cong_action_ = FALSE;1176 1177 // highest: greatest sequence number sent + 11178 // and adjusted for SYNs and FINs which use up one number1179 1180 int highest = seqno + reliable;1181 if (highest > maxseq_) {1182 maxseq_ = highest;1183 //1184 // if we are using conventional RTT estimation,1185 // establish timing on this segment1186 //1187 if (!ts_option_ && rtt_active_ == FALSE) {1188 rtt_active_ = TRUE; // set timer1189 rtt_seq_ = seqno; // timed seq #1190 rtt_ts_ = now(); // when set1191 }1192 }1193 1194 /*1195 * Set retransmit timer if not currently set,1196 * and not doing an ack or a keep-alive probe.1197 * Initial value for retransmit timer is smoothed1198 * round-trip time + 2 * round-trip time variance.1199 * Future values are rtt + 4 * rttvar.1200 */1201 if (rtx_timer_.status() != TIMER_PENDING && reliable) {1202 set_rtx_timer(); // no timer pending, schedule one1203 }1204 1205 return (reliable);1206 }1207 1208 /*1209 *1210 * send_much: send as much data as we are allowed to. This is1211 * controlled by the "pipectrl_" variable. If pipectrl_ is set1212 * to FALSE, then we are working as a normal window-based TCP and1213 * we are allowed to send whatever the window allows.1214 * If pipectrl_ is set to TRUE, then we are allowed to send whatever1215 * pipe_ allows us to send. One tricky part is to make sure we1216 * do not overshoot the receiver‘s advertised window if we are1217 * in (pipectrl_ == TRUE) mode.1218 */1219 1220 void1221 FullTcpAgent::send_much(int force, int reason, int maxburst)1222 {1223 int npackets = 0; // sent so far1224 1225 //if ((int(t_seqno_)) > 1)1226 //printf("%f: send_much(f:%d, win:%d, pipectrl:%d, pipe:%d, t_seqno:%d, topwin:%d, maxseq_:%d\n",1227 //now(), force, win, pipectrl_, pipe_, int(t_seqno_), topwin, int(maxseq_));1228 1229 if (!force && (delsnd_timer_.status() == TIMER_PENDING))1230 return;1231 1232 while (1) {1233 1234 /*1235 * note that if output decides to not actually send1236 * (e.g. because of Nagle), then if we don‘t break out1237 * of this loop, we can loop forever at the same1238 * simulated time instant1239 */1240 int amt;1241 int seq = nxt_tseq();1242 if (!force && !send_allowed(seq))1243 break;1244 // Q: does this need to be here too?1245 if (!force && overhead_ != 0 &&1246 (delsnd_timer_.status() != TIMER_PENDING)) {1247 delsnd_timer_.resched(Random::uniform(overhead_));1248 return;1249 }1250 if ((amt = foutput(seq, reason)) <= 0)1251 break;1252 if ((outflags() & TH_FIN))1253 --amt; // don‘t count FINs1254 sent(seq, amt);1255 force = 0;1256 1257 if ((outflags() & (TH_SYN|TH_FIN)) ||1258 (maxburst && ++npackets >= maxburst))1259 break;1260 }1261 return;1262 }1263 1264 /*1265 * base TCP: we are allowed to send a sequence number if it1266 * is in the window1267 */1268 int1269 FullTcpAgent::send_allowed(int seq)1270 {1271 int win = window() * maxseg_;1272 int topwin = curseq_; // 1 seq number past the last byte we can send1273 1274 if ((topwin > highest_ack_ + win) || infinite_send_)1275 topwin = highest_ack_ + win; 1276 1277 return (seq < topwin);1278 }1279 /*1280 * Process an ACK1281 * this version of the routine doesn‘t necessarily1282 * require the ack to be one which advances the ack number1283 *1284 * if this ACKs a rtt estimate1285 * indicate we are not timing1286 * reset the exponential timer backoff (gamma)1287 * update rtt estimate1288 * cancel retrans timer if everything is sent and ACK‘d, else set it1289 * advance the ack number if appropriate1290 * update segment to send next if appropriate1291 */1292 void1293 FullTcpAgent::newack(Packet* pkt)1294 {1295 hdr_tcp *tcph = hdr_tcp::access(pkt);1296 1297 register int ackno = tcph->ackno();1298 int progress = (ackno > highest_ack_);1299 1300 if (ackno == maxseq_) {1301 cancel_rtx_timer(); // all data ACKd1302 } else if (progress) {1303 set_rtx_timer();1304 }1305 1306 // advance the ack number if this is for new data1307 if (progress)1308 highest_ack_ = ackno;1309 1310 // if we have suffered a retransmit timeout, t_seqno_1311 // will have been reset to highest_ ack. If the1312 // receiver has cached some data above t_seqno_, the1313 // new-ack value could (should) jump forward. We must1314 // update t_seqno_ here, otherwise we would be doing1315 // go-back-n.1316 1317 if (t_seqno_ < highest_ack_)1318 t_seqno_ = highest_ack_; // seq# to send next1319 1320 /*1321 * Update RTT only if it‘s OK to do so from info in the flags header.1322 * This is needed for protocols in which intermediate agents1323 1324 * in the network intersperse acks (e.g., ack-reconstructors) for1325 * various reasons (without violating e2e semantics).1326 */1327 hdr_flags *fh = hdr_flags::access(pkt);1328 1329 if (!fh->no_ts_) {1330 if (ts_option_) {1331 recent_age_ = now();1332 recent_ = tcph->ts();1333 rtt_update(now() - tcph->ts_echo());1334 if (ts_resetRTO_ && (!ect_ || !ecn_backoff_ ||1335 !hdr_flags::access(pkt)->ecnecho())) { 1336 // From Andrei Gurtov1337 //1338 // Don‘t end backoff if still in ECN-Echo with1339 // a congestion window of 1 packet.1340 t_backoff_ = 1;1341 }1342 } else if (rtt_active_ && ackno > rtt_seq_) {1343 // got an RTT sample, record it1344 // "t_backoff_ = 1;" deleted by T. Kelly.1345 rtt_active_ = FALSE;1346 rtt_update(now() - rtt_ts_);1347 }1348 if (!ect_ || !ecn_backoff_ ||1349 !hdr_flags::access(pkt)->ecnecho()) {1350 /*1351 * Don‘t end backoff if still in ECN-Echo with1352 * a congestion window of 1 packet.1353 * Fix from T. Kelly.1354 */1355 t_backoff_ = 1;1356 ecn_backoff_ = 0;1357 }1358 1359 }1360 return;1361 }1362 1363 /*1364 * this is the simulated form of the header prediction1365 * predicate. While not really necessary for a simulation, it1366 * follows the code base more closely and can sometimes help to reveal1367 * odd behavior caused by the implementation structure..1368 *1369 * Here‘s the comment from the real TCP:1370 *1371 * Header prediction: check for the two common cases1372 * of a uni-directional data xfer. If the packet has1373 * no control flags, is in-sequence, the window didn‘t1374 * change and we‘re not retransmitting, it‘s a1375 * candidate. If the length is zero and the ack moved1376 * forward, we‘re the sender side of the xfer. Just1377 * free the data acked & wake any higher level process1378 * that was blocked waiting for space. If the length1379 * is non-zero and the ack didn‘t move, we‘re the1380 * receiver side. If we‘re getting packets in-order1381 * (the reassembly queue is empty), add the data to1382 * the socket buffer and note that we need a delayed ack.1383 * Make sure that the hidden state-flags are also off.1384 * Since we check for TCPS_ESTABLISHED above, it can only1385 * be TF_NEEDSYN.1386 */1387 1388 int1389 FullTcpAgent::predict_ok(Packet* pkt)1390 {1391 hdr_tcp *tcph = hdr_tcp::access(pkt);1392 hdr_flags *fh = hdr_flags::access(pkt);1393 1394 /* not the fastest way to do this, but perhaps clearest */1395 1396 int p1 = (state_ == TCPS_ESTABLISHED); // ready1397 int p2 = ((tcph->flags() & (TH_SYN|TH_FIN|TH_ACK)) == TH_ACK); // ACK1398 int p3 = ((flags_ & TF_NEEDFIN) == 0); // don‘t need fin1399 int p4 = (!ts_option_ || fh->no_ts_ || (tcph->ts() >= recent_)); // tsok1400 int p5 = (tcph->seqno() == rcv_nxt_); // in-order data1401 int p6 = (t_seqno_ == maxseq_); // not re-xmit1402 int p7 = (!ecn_ || fh->ecnecho() == 0); // no ECN1403 int p8 = (tcph->sa_length() == 0); // no SACK info1404 1405 return (p1 && p2 && p3 && p4 && p5 && p6 && p7 && p8);1406 }1407 1408 /*1409 * fast_retransmit using the given seqno1410 * perform fast RTX, set recover_, set last_cwnd_action1411 */1412 1413 int1414 FullTcpAgent::fast_retransmit(int seq)1415 {1416 // we are now going to fast-retransmit and willtrace that event1417 trace_event("FAST_RETX");1418 1419 recover_ = maxseq_; // recovery target1420 last_cwnd_action_ = CWND_ACTION_DUPACK;1421 return(foutput(seq, REASON_DUPACK)); // send one pkt1422 }1423 1424 /*1425 * real tcp determines if the remote1426 * side should receive a window update/ACK from us, and often1427 * results in sending an update every 2 segments, thereby1428 * giving the familiar 2-packets-per-ack behavior of TCP.1429 * Here, we don‘t advertise any windows, so we just see if1430 * there‘s at least ‘segs_per_ack_‘ pkts not yet acked1431 *1432 * also, provide for a segs-per-ack "threshold" where1433 * we generate 1-ack-per-seg until enough stuff1434 * (spa_thresh_ bytes) has been received from the other side1435 * This idea came from vj/kmn in BayTcp. Added 8/21/01.1436 */1437 1438 int1439 FullTcpAgent::need_send()1440 {1441 if (flags_ & TF_ACKNOW)1442 return TRUE;1443 1444 int spa = (spa_thresh_ > 0 && ((rcv_nxt_ - irs_) < spa_thresh_)) ?1445 1 : segs_per_ack_;1446 1447 return ((rcv_nxt_ - last_ack_sent_) >= (spa * maxseg_));1448 }1449 1450 /*1451 * determine whether enough time has elapsed in order to1452 * conclude a "restart" is necessary (e.g. a slow-start)1453 *1454 * for now, keep track of this similarly to how rtt_update() does1455 */1456 1457 int1458 FullTcpAgent::idle_restart()1459 {1460 if (last_send_time_ < 0.0) {1461 // last_send_time_ isn‘t set up yet, we shouldn‘t1462 // do the idle_restart1463 return (0);1464 }1465 1466 double tao = now() - last_send_time_;1467 if (!ts_option_) {1468 double tickoff = fmod(last_send_time_ + boot_time_,1469 tcp_tick_);1470 tao = int((tao + tickoff) / tcp_tick_) * tcp_tick_;1471 }1472 1473 return (tao > t_rtxcur_); // verify this CHECKME1474 }1475 1476 /*1477 * tcp-full‘s version of set_initial_window()... over-rides1478 * the one in tcp.cc1479 */1480 void1481 FullTcpAgent::set_initial_window()1482 {1483 syn_ = TRUE; // full-tcp always models SYN exchange1484 TcpAgent::set_initial_window();1485 } 1486 1487 /*1488 * main reception path - 1489 * called from the agent that handles the data path below in its muxing mode1490 * advance() is called when connection is established with size sent from1491 * user/application agent1492 *1493 * This is a fairly complex function. It operates generally as follows:1494 * do header prediction for simple cases (pure ACKS or data)1495 * if in LISTEN and we get a SYN, begin initializing connection1496 * if in SYN_SENT and we get an ACK, complete connection init1497 * trim any redundant data from received dataful segment1498 * deal with ACKS:1499 * if in SYN_RCVD, complete connection init then go on1500 * see if ACK is old or at the current highest_ack1501 * if at current high, is the threshold reached or not1502 * if so, maybe do fast rtx... otherwise drop or inflate win1503 * deal with incoming data1504 * deal with FIN bit on in arriving packet1505 */1506 void1507 FullTcpAgent::recv(Packet *pkt, Handler*)1508 {1509 hdr_tcp *tcph = hdr_tcp::access(pkt); // TCP header1510 hdr_cmn *th = hdr_cmn::access(pkt); // common header (size, etc)1511 hdr_flags *fh = hdr_flags::access(pkt); // flags (CWR, CE, bits)1512 1513 int needoutput = FALSE;1514 int ourfinisacked = FALSE;1515 int dupseg = FALSE; // recv‘d dup data segment1516 int todrop = 0; // duplicate DATA cnt in seg1517 1518 last_state_ = state_;1519 1520 int datalen = th->size() - tcph->hlen(); // # payload bytes1521 int ackno = tcph->ackno(); // ack # from packet1522 int tiflags = tcph->flags() ; // tcp flags from packet1523 1524 //if (state_ != TCPS_ESTABLISHED || (tiflags&(TH_SYN|TH_FIN))) {1525 //fprintf(stdout, "%f(%s)in state %s recv‘d this packet: ", now(), name(), statestr(state_));1526 //prpkt(pkt);1527 //}1528 1529 /* 1530 * Acknowledge FIN from passive closer even in TCPS_CLOSED state1531 * (since we lack TIME_WAIT state and RST packets,1532 * the loss of the FIN packet from the passive closer will make that1533 * endpoint retransmit the FIN forever)1534 * -F. Hernandez-Campos 8/6/001535 */1536 if ( (state_ == TCPS_CLOSED) && (tiflags & TH_FIN) ) {1537 goto dropafterack;1538 }1539 1540 /*1541 * Don‘t expect to see anything while closed1542 */1543 1544 if (state_ == TCPS_CLOSED) {1545 if (debug_) {1546 fprintf(stderr, "%f: FullTcp(%s): recv‘d pkt in CLOSED state: ",1547 now(), name());1548 prpkt(pkt);1549 }1550 goto drop;1551 }1552 1553 /*1554 * Process options if not in LISTEN state,1555 * else do it below1556 */1557 if (state_ != TCPS_LISTEN)1558 dooptions(pkt);1559 1560 /*1561 * if we are using delayed-ACK timers and1562 * no delayed-ACK timer is set, set one.1563 * They are set to fire every ‘interval_‘ secs, starting1564 * at time t0 = (0.0 + k * interval_) for some k such1565 * that t0 > now1566 */1567 if (delack_interval_ > 0.0 &&1568 (delack_timer_.status() != TIMER_PENDING)) {1569 int last = int(now() / delack_interval_);1570 delack_timer_.resched(delack_interval_ * (last + 1.0) - now());1571 }1572 1573 /*1574 * Try header prediction: in seq data or in seq pure ACK1575 * with no funny business1576 */1577 if (!nopredict_ && predict_ok(pkt)) {1578 /*1579 * If last ACK falls within this segment‘s sequence numbers,1580 * record the timestamp.1581 * See RFC1323 (now RFC1323 bis)1582 */1583 if (ts_option_ && !fh->no_ts_ &&1584 tcph->seqno() <= last_ack_sent_) {1585 /*1586 * this is the case where the ts value is newer than1587 * the last one we‘ve seen, and the seq # is the one1588 * we expect [seqno == last_ack_sent_] or older1589 */1590 recent_age_ = now();1591 recent_ = tcph->ts();1592 }1593 1594 //1595 // generate a stream of ecnecho bits until we see a true1596 // cong_action bit1597 //1598 1599 if (ecn_) {1600 if (fh->ce() && fh->ect()) {1601 // no CWR from peer yet... arrange to1602 // keep sending ECNECHO1603 recent_ce_ = TRUE;1604 } else if (fh->cwr()) {1605 // got CWR response from peer.. stop1606 // sending ECNECHO bits1607 recent_ce_ = FALSE;1608 }1609 }1610 1611 // Header predication basically looks to see1612 // if the incoming packet is an expected pure ACK1613 // or an expected data segment1614 1615 if (datalen == 0) {1616 // check for a received pure ACK in the correct range..1617 // also checks to see if we are wnd_ limited1618 // (we don‘t change cwnd at all below), plus1619 // not being in fast recovery and not a partial ack.1620 // If we are in fast1621 // recovery, go below so we can remember to deflate1622 // the window if we need to1623 if (ackno > highest_ack_ && ackno < maxseq_ &&1624 cwnd_ >= wnd_ && !fastrecov_) {1625 newack(pkt); // update timers, highest_ack_1626 send_much(0, REASON_NORMAL, maxburst_);1627 Packet::free(pkt);1628 return;1629 }1630 } else if (ackno == highest_ack_ && rq_.empty()) {1631 // check for pure incoming segment1632 // the next data segment we‘re awaiting, and1633 // that there‘s nothing sitting in the reassem-1634 // bly queue1635 // give to "application" here1636 // note: DELACK is inspected only by1637 // tcp_fasttimo() in real tcp. Every 200 ms1638 // this routine scans all tcpcb‘s looking for1639 // DELACK segments and when it finds them1640 // changes DELACK to ACKNOW and calls tcp_output()1641 rcv_nxt_ += datalen;1642 flags_ |= TF_DELACK;1643 recvBytes(datalen); // notify application of "delivery"1644 //1645 // special code here to simulate the operation1646 // of a receiver who always consumes data,1647 // resulting in a call to tcp_output1648 Packet::free(pkt);1649 if (need_send())1650 send_much(1, REASON_NORMAL, maxburst_);1651 return;1652 }1653 } /* header prediction */1654 1655 1656 //1657 // header prediction failed1658 // (e.g. pure ACK out of valid range, SACK present, etc)...1659 // do slow path processing1660 1661 //1662 // the following switch does special things for these states:1663 // TCPS_LISTEN, TCPS_SYN_SENT1664 //1665 1666 switch (state_) {1667 1668 /*1669 * If the segment contains an ACK then it is bad and do reset.1670 * If it does not contain a SYN then it is not interesting; drop it.1671 * Otherwise initialize tp->rcv_nxt, and tp->irs, iss is already1672 * selected, and send a segment:1673 * <SEQ=ISS><ACK=RCV_NXT><CTL=SYN,ACK>1674 * Initialize tp->snd_nxt to tp->iss.1675 * Enter SYN_RECEIVED state, and process any other fields of this1676 * segment in this state.1677 */1678 1679 case TCPS_LISTEN: /* awaiting peer‘s SYN */1680 1681 if (tiflags & TH_ACK) {1682 if (debug_) {1683 fprintf(stderr,1684 "%f: FullTcpAgent(%s): warning: recv‘d ACK while in LISTEN: ",1685 now(), name());1686 prpkt(pkt);1687 }1688 // don‘t want ACKs in LISTEN1689 goto dropwithreset;1690 }1691 if ((tiflags & TH_SYN) == 0) {1692 if (debug_) {1693 fprintf(stderr, "%f: FullTcpAgent(%s): warning: recv‘d NON-SYN while in LISTEN\n",1694 now(), name());1695 prpkt(pkt);1696 }1697 // any non-SYN is discarded1698 goto drop;1699 }1700 1701 /*1702 * must by a SYN (no ACK) at this point...1703 * in real tcp we would bump the iss counter here also1704 */1705 dooptions(pkt);1706 irs_ = tcph->seqno();1707 t_seqno_ = iss_; /* tcp_sendseqinit() macro in real tcp */1708 rcv_nxt_ = rcvseqinit(irs_, datalen);1709 flags_ |= TF_ACKNOW;1710 1711 // check for a ECN-SYN with ECE|CWR1712 if (ecn_ && fh->ecnecho() && fh->cong_action()) {1713 ect_ = TRUE;1714 }1715 1716 1717 if (fid_ == 0) {1718 // XXX: sort of hack... If we do not1719 // have a special flow ID, pick up that1720 // of the sender (active opener)1721 hdr_ip* iph = hdr_ip::access(pkt);1722 fid_ = iph->flowid();1723 }1724 1725 newstate(TCPS_SYN_RECEIVED);1726 goto trimthenstep6;1727 1728 /*1729 * If the state is SYN_SENT:1730 * if seg contains an ACK, but not for our SYN, drop the input.1731 * if seg does not contain SYN, then drop it.1732 * Otherwise this is an acceptable SYN segment1733 * initialize tp->rcv_nxt and tp->irs1734 * if seg contains ack then advance tp->snd_una1735 * if SYN has been acked change to ESTABLISHED else SYN_RCVD state1736 * arrange for segment to be acked (eventually)1737 * continue processing rest of data/controls, beginning with URG1738 */1739 1740 case TCPS_SYN_SENT: /* we sent SYN, expecting SYN+ACK (or SYN) */1741 1742 /* drop if it‘s a SYN+ACK and the ack field is bad */1743 if ((tiflags & TH_ACK) &&1744 ((ackno <= iss_) || (ackno > maxseq_))) {1745 // not an ACK for our SYN, discard1746 if (debug_) {1747 fprintf(stderr, "%f: FullTcpAgent::recv(%s): bad ACK for our SYN: ",1748 now(), name());1749 prpkt(pkt);1750 }1751 goto dropwithreset;1752 }1753 1754 if ((tiflags & TH_SYN) == 0) {1755 if (debug_) {1756 fprintf(stderr, "%f: FullTcpAgent::recv(%s): no SYN for our SYN: ",1757 now(), name());1758 prpkt(pkt);1759 }1760 goto drop;1761 }1762 1763 /* looks like an ok SYN or SYN+ACK */1764 // If ecn_syn_wait is set to 2:1765 // Check if CE-marked SYN/ACK packet, then just send an ACK1766 // packet with ECE set, and drop the SYN/ACK packet.1767 // Don‘t update TCP state. 1768 if (tiflags & TH_ACK) 1769 {1770 if (ecn_ && fh->ecnecho() && !fh->cong_action() && ecn_syn_wait_ == 2) 1771 // if SYN/ACK packet and ecn_syn_wait_ == 21772 {1773 if ( fh->ce() ) 1774 // If SYN/ACK packet is CE-marked1775 {1776 //cancel_rtx_timer();1777 //newack(pkt);1778 set_rtx_timer();1779 sendpacket(t_seqno_, rcv_nxt_, TH_ACK|TH_ECE, 0, 0);1780 goto drop;1781 }1782 }1783 }1784 1785 1786 #ifdef notdef1787 cancel_rtx_timer(); // cancel timer on our 1st SYN [does this belong!?]1788 #endif1789 irs_ = tcph->seqno(); // get initial recv‘d seq #1790 rcv_nxt_ = rcvseqinit(irs_, datalen);1791 1792 if (tiflags & TH_ACK) {1793 // SYN+ACK (our SYN was acked)1794 if (ecn_ && fh->ecnecho() && !fh->cong_action()) {1795 ect_ = TRUE;1796 if ( fh->ce() ) 1797 recent_ce_ = TRUE;1798 }1799 highest_ack_ = ackno;1800 cwnd_ = initial_window();1801 1802 #ifdef notdef1803 /*1804 * if we didn‘t have to retransmit the SYN,1805 * use its rtt as our initial srtt & rtt var.1806 */1807 if (t_rtt_) {1808 double tao = now() - tcph->ts();1809 rtt_update(tao);1810 }1811 #endif1812 1813 /*1814 * if there‘s data, delay ACK; if there‘s also a FIN1815 * ACKNOW will be turned on later.1816 */1817 if (datalen > 0) {1818 flags_ |= TF_DELACK; // data there: wait1819 } else {1820 flags_ |= TF_ACKNOW; // ACK peer‘s SYN1821 }1822 1823 /*1824 * Received <SYN,ACK> in SYN_SENT[*] state.1825 * Transitions:1826 * SYN_SENT --> ESTABLISHED1827 * SYN_SENT* --> FIN_WAIT_11828 */1829 1830 if (flags_ & TF_NEEDFIN) {1831 newstate(TCPS_FIN_WAIT_1);1832 flags_ &= ~TF_NEEDFIN;1833 tiflags &= ~TH_SYN;1834 } else {1835 newstate(TCPS_ESTABLISHED);1836 }1837 1838 // special to ns:1839 // generate pure ACK here.1840 // this simulates the ordinary connection establishment1841 // where the ACK of the peer‘s SYN+ACK contains1842 // no data. This is typically caused by the way1843 // the connect() socket call works in which the1844 // entire 3-way handshake occurs prior to the app1845 // being able to issue a write() [which actually1846 // causes the segment to be sent].1847 sendpacket(t_seqno_, rcv_nxt_, TH_ACK, 0, 0);1848 } else {1849 // Check ECN-SYN packet1850 if (ecn_ && fh->ecnecho() && fh->cong_action())1851 ect_ = TRUE;1852 1853 // SYN (no ACK) (simultaneous active opens)1854 flags_ |= TF_ACKNOW;1855 cancel_rtx_timer();1856 newstate(TCPS_SYN_RECEIVED);1857 /*1858 * decrement t_seqno_: we are sending a1859 * 2nd SYN (this time in the form of a1860 * SYN+ACK, so t_seqno_ will have been1861 * advanced to 2... reduce this1862 */1863 t_seqno_--; // CHECKME1864 }1865 1866 trimthenstep6:1867 /*1868 * advance the seq# to correspond to first data byte1869 */1870 tcph->seqno()++;1871 1872 if (tiflags & TH_ACK)1873 goto process_ACK;1874 1875 goto step6;1876 1877 case TCPS_LAST_ACK:1878 /* 1879 * The only way we‘re in LAST_ACK is if we‘ve already1880 * received a FIN, so ignore all retranmitted FINS.1881 * -M. Weigle 7/23/021882 */1883 if (tiflags & TH_FIN) {1884 goto drop;1885 }1886 break;1887 case TCPS_CLOSING:1888 break;1889 } /* end switch(state_) */1890 1891 /*1892 * States other than LISTEN or SYN_SENT.1893 * First check timestamp, if present.1894 * Then check that at least some bytes of segment are within1895 * receive window. If segment begins before rcv_nxt,1896 * drop leading data (and SYN); if nothing left, just ack.1897 *1898 * RFC 1323 PAWS: If we have a timestamp reply on this segment1899 * and it‘s less than ts_recent, drop it.1900 */1901 1902 if (ts_option_ && !fh->no_ts_ && recent_ && tcph->ts() < recent_) {1903 if ((now() - recent_age_) > TCP_PAWS_IDLE) {1904 /*1905 * this is basically impossible in the simulator,1906 * but here it is...1907 */1908 /*1909 * Invalidate ts_recent. If this segment updates1910 * ts_recent, the age will be reset later and ts_recent1911 * will get a valid value. If it does not, setting1912 * ts_recent to zero will at least satisfy the1913 * requirement that zero be placed in the timestamp1914 * echo reply when ts_recent isn‘t valid. The1915 * age isn‘t reset until we get a valid ts_recent1916 * because we don‘t want out-of-order segments to be1917 * dropped when ts_recent is old.1918 */1919 recent_ = 0.0;1920 } else {1921 fprintf(stderr, "%f: FullTcpAgent(%s): dropped pkt due to bad ts\n",1922 now(), name());1923 goto dropafterack;1924 }1925 }1926 1927 // check for redundant data at head/tail of segment1928 // note that the 4.4bsd [Net/3] code has1929 // a bug here which can cause us to ignore the1930 // perfectly good ACKs on duplicate segments. The1931 // fix is described in (Stevens, Vol2, p. 959-960).1932 // This code is based on that correction.1933 //1934 // In addition, it has a modification so that duplicate segments1935 // with dup acks don‘t trigger a fast retransmit when dupseg_fix_1936 // is enabled.1937 //1938 // Yet one more modification: make sure that if the received1939 // segment had datalen=0 and wasn‘t a SYN or FIN that1940 // we don‘t turn on the ACKNOW status bit. If we were to1941 // allow ACKNOW to be turned on, normal pure ACKs that happen1942 // to have seq #s below rcv_nxt can trigger an ACK war by1943 // forcing us to ACK the pure ACKs1944 //1945 // Update: if we have a dataless FIN, don‘t really want to1946 // do anything with it. In particular, would like to1947 // avoid ACKing an incoming FIN+ACK while in CLOSING1948 //1949 todrop = rcv_nxt_ - tcph->seqno(); // how much overlap?1950 1951 if (todrop > 0 && ((tiflags & (TH_SYN)) || datalen > 0)) {1952 //printf("%f(%s): trim 1..todrop:%d, dlen:%d\n",now(), name(), todrop, datalen);1953 if (tiflags & TH_SYN) {1954 tiflags &= ~TH_SYN;1955 tcph->seqno()++;1956 th->size()--; // XXX Must decrease packet size too!!1957 // Q: Why?.. this is only a SYN1958 todrop--;1959 }1960 //1961 // see Stevens, vol 2, p. 960 for this check;1962 // this check is to see if we are dropping1963 // more than this segment (i.e. the whole pkt + a FIN),1964 // or just the whole packet (no FIN)1965 //1966 if ((todrop > datalen) ||1967 (todrop == datalen && ((tiflags & TH_FIN) == 0))) {1968 //printf("%f(%s): trim 2..todrop:%d, dlen:%d\n",now(), name(), todrop, datalen);1969 /*1970 * Any valid FIN must be to the left of the window.1971 * At this point the FIN must be a duplicate or out1972 * of sequence; drop it.1973 */1974 1975 tiflags &= ~TH_FIN;1976 1977 /*1978 * Send an ACK to resynchronize and drop any data.1979 * But keep on processing for RST or ACK.1980 */1981 1982 flags_ |= TF_ACKNOW;1983 todrop = datalen;1984 dupseg = TRUE; // *completely* duplicate1985 1986 }1987 1988 /*1989 * Trim duplicate data from the front of the packet1990 */1991 1992 tcph->seqno() += todrop;1993 th->size() -= todrop; // XXX Must decrease size too!!1994 // why? [kf]..prob when put in RQ1995 datalen -= todrop;1996 1997 } /* data trim */1998 1999 /*2000 * If we are doing timstamps and this packet has one, and2001 * If last ACK falls within this segment‘s sequence numbers,2002 * record the timestamp.2003 * See RFC1323 (now RFC1323 bis)2004 */2005 if (ts_option_ && !fh->no_ts_ && tcph->seqno() <= last_ack_sent_) {2006 /*2007 * this is the case where the ts value is newer than2008 * the last one we‘ve seen, and the seq # is the one we expect2009 * [seqno == last_ack_sent_] or older2010 */2011 recent_age_ = now();2012 recent_ = tcph->ts();2013 }2014 2015 if (tiflags & TH_SYN) {2016 if (debug_) {2017 fprintf(stderr, "%f: FullTcpAgent::recv(%s) received unexpected SYN (state:%d): ",2018 now(), name(), state_);2019 prpkt(pkt);2020 }2021 goto dropwithreset;2022 }2023 2024 if ((tiflags & TH_ACK) == 0) {2025 /*2026 * Added check for state != SYN_RECEIVED. We will receive a 2027 * duplicate SYN in SYN_RECEIVED when our SYN/ACK was dropped.2028 * We should just ignore the duplicate SYN (our timeout for 2029 * resending the SYN/ACK is about the same as the client‘s 2030 * timeout for resending the SYN), but give no error message. 2031 * -M. Weigle 07/24/012032 */2033 if (state_ != TCPS_SYN_RECEIVED) {2034 if (debug_) {2035 fprintf(stderr, "%f: FullTcpAgent::recv(%s) got packet lacking ACK (state:%d): ",2036 now(), name(), state_);2037 prpkt(pkt);2038 }2039 }2040 goto drop;2041 }2042 2043 /*2044 * Ack processing.2045 */2046 2047 switch (state_) {2048 case TCPS_SYN_RECEIVED: /* want ACK for our SYN+ACK */2049 if (ackno < highest_ack_ || ackno > maxseq_) {2050 // not in useful range2051 if (debug_) {2052 fprintf(stderr, "%f: FullTcpAgent(%s): ack(%d) not in range while in SYN_RECEIVED: ",2053 now(), name(), ackno);2054 prpkt(pkt);2055 }2056 goto dropwithreset;2057 }2058 2059 if (ecn_ && ect_ && ecn_syn_ && fh->ecnecho() && ecn_syn_wait_ == 2) 2060 {2061 // The SYN/ACK packet was ECN-marked.2062 // Reset the rtx timer, send another SYN/ACK packet2063 // immediately, and drop the ACK packet.2064 // Do not move to TCPS_ESTB state or update TCP variables.2065 cancel_rtx_timer();2066 ecn_syn_next_ = 0;2067 foutput(iss_, REASON_NORMAL);2068 wnd_init_option_ = 1;2069 wnd_init_ = 1;2070 goto drop;2071 } 2072 if (ecn_ && ect_ && ecn_syn_ && fh->ecnecho() && ecn_syn_wait_ < 2) {2073 // The SYN/ACK packet was ECN-marked.2074 if (ecn_syn_wait_ == 1) {2075 // A timer will be called in ecn().2076 cwnd_ = 1;2077 use_rtt_ = 1; //KK, wait for timeout() period2078 } else {2079 // Congestion window will be halved in ecn().2080 cwnd_ = 2;2081 }2082 } else {2083 cwnd_ = initial_window();2084 }2085 2086 /*2087 * Make transitions:2088 * SYN-RECEIVED -> ESTABLISHED2089 * SYN-RECEIVED* -> FIN-WAIT-12090 */2091 if (flags_ & TF_NEEDFIN) {2092 newstate(TCPS_FIN_WAIT_1);2093 flags_ &= ~TF_NEEDFIN;2094 } else {2095 newstate(TCPS_ESTABLISHED);2096 }2097 2098 /* fall into ... */2099 2100 2101 /*2102 * In ESTABLISHED state: drop duplicate ACKs; ACK out of range2103 * ACKs. If the ack is in the range2104 * tp->snd_una < ti->ti_ack <= tp->snd_max2105 * then advance tp->snd_una to ti->ti_ack and drop2106 * data from the retransmission queue.2107 *2108 * note that state TIME_WAIT isn‘t used2109 * in the simulator2110 */2111 2112 case TCPS_ESTABLISHED:2113 case TCPS_FIN_WAIT_1:2114 case TCPS_FIN_WAIT_2:2115 case TCPS_CLOSE_WAIT:2116 case TCPS_CLOSING:2117 case TCPS_LAST_ACK:2118 2119 //2120 // look for ECNs in ACKs, react as necessary2121 //2122 2123 if (fh->ecnecho() && (!ecn_ || !ect_)) {2124 fprintf(stderr,2125 "%f: FullTcp(%s): warning, recvd ecnecho but I am not ECN capable!\n",2126 now(), name());2127 }2128 2129 //2130 // generate a stream of ecnecho bits until we see a true2131 // cong_action bit2132 // 2133 if (ecn_) {2134 if (fh->ce() && fh->ect())2135 recent_ce_ = TRUE;2136 else if (fh->cwr()) 2137 recent_ce_ = FALSE;2138 }2139 2140 //2141 // If ESTABLISHED or starting to close, process SACKS2142 //2143 2144 if (state_ >= TCPS_ESTABLISHED && tcph->sa_length() > 0) {2145 process_sack(tcph);2146 }2147 2148 //2149 // ACK indicates packet left the network2150 // try not to be fooled by data2151 //2152 2153 if (fastrecov_ && (datalen == 0 || ackno > highest_ack_))2154 pipe_ -= maxseg_;2155 2156 // look for dup ACKs (dup ack numbers, no data)2157 //2158 // do fast retransmit/recovery if at/past thresh2159 if (ackno <= highest_ack_) {2160 // a pure ACK which doesn‘t advance highest_ack_2161 if (datalen == 0 && (!dupseg_fix_ || !dupseg)) {2162 2163 /*2164 * If we have outstanding data2165 * this is a completely2166 * duplicate ack,2167 * the ack is the biggest we‘ve2168 * seen and we‘ve seen exactly our rexmt2169 * threshhold of them, assume a packet2170 * has been dropped and retransmit it.2171 *2172 * We know we‘re losing at the current2173 * window size so do congestion avoidance.2174 *2175 * Dup acks mean that packets have left the2176 * network (they‘re now cached at the receiver)2177 * so bump cwnd by the amount in the receiver2178 * to keep a constant cwnd packets in the2179 * network.2180 */2181 2182 if ((rtx_timer_.status() != TIMER_PENDING) ||2183 ackno < highest_ack_) {2184 // Q: significance of timer not pending?2185 // ACK below highest_ack_2186 oldack();2187 } else if (++dupacks_ == tcprexmtthresh_) {2188 // ACK at highest_ack_ AND meets threshold2189 //trace_event("FAST_RECOVERY");2190 dupack_action(); // maybe fast rexmt2191 goto drop;2192 2193 } else if (dupacks_ > tcprexmtthresh_) {2194 // ACK at highest_ack_ AND above threshole2195 //trace_event("FAST_RECOVERY");2196 extra_ack();2197 2198 // send whatever window allows2199 send_much(0, REASON_DUPACK, maxburst_);2200 goto drop;2201 }2202 } else {2203 // non zero-length [dataful] segment2204 // with a dup ack (normal for dataful segs)2205 // (or window changed in real TCP).2206 if (dupack_reset_) {2207 dupacks_ = 0;2208 fastrecov_ = FALSE;2209 }2210 }2211 break; /* take us to "step6" */2212 } /* end of dup/old acks */2213 2214 /*2215 * we‘ve finished the fast retransmit/recovery period2216 * (i.e. received an ACK which advances highest_ack_)2217 * The ACK may be "good" or "partial"2218 */2219 2220 process_ACK:2221 2222 if (ackno > maxseq_) {2223 // ack more than we sent(!?)2224 if (debug_) {2225 fprintf(stderr, "%f: FullTcpAgent::recv(%s) too-big ACK (maxseq:%d): ",2226 now(), name(), int(maxseq_));2227 prpkt(pkt);2228 }2229 goto dropafterack;2230 }2231 2232 /*2233 * If we have a timestamp reply, update smoothed2234 * round trip time. If no timestamp is present but2235 * transmit timer is running and timed sequence2236 * number was acked, update smoothed round trip time.2237 * Since we now have an rtt measurement, cancel the2238 * timer backoff (cf., Phil Karn‘s retransmit alg.).2239 * Recompute the initial retransmit timer.2240 *2241 * If all outstanding data is acked, stop retransmit2242 * If there is more data to be acked, restart retransmit2243 * timer, using current (possibly backed-off) value.2244 */2245 newack(pkt); // handle timers, update highest_ack_2246 2247 /*2248 * if this is a partial ACK, invoke whatever we should2249 * note that newack() must be called before the action2250 * functions, as some of them depend on side-effects2251 * of newack()2252 */2253 2254 int partial = pack(pkt);2255 2256 if (partial)2257 pack_action(pkt);2258 else2259 ack_action(pkt);2260 2261 /*2262 * if this is an ACK with an ECN indication, handle this2263 * but not if it is a syn packet2264 */2265 if (fh->ecnecho() && !(tiflags&TH_SYN) )2266 if (fh->ecnecho()) {2267 // Note from Sally: In one-way TCP,2268 // ecn() is called before newack()...2269 ecn(highest_ack_); // updated by newack(), above2270 // "set_rtx_timer();" from T. Kelly.2271 if (cwnd_ < 1)2272 set_rtx_timer();2273 }2274 // CHECKME: handling of rtx timer2275 if (ackno == maxseq_) {2276 needoutput = TRUE;2277 }2278 2279 /*2280 * If no data (only SYN) was ACK‘d,2281 * skip rest of ACK processing.2282 */2283 if (ackno == (highest_ack_ + 1))2284 goto step6;2285 2286 // if we are delaying initial cwnd growth (probably due to2287 // large initial windows), then only open cwnd if data has2288 // been received2289 // Q: check when this happens2290 /*2291 * When new data is acked, open the congestion window.2292 * If the window gives us less than ssthresh packets2293 * in flight, open exponentially (maxseg per packet).2294 * Otherwise open about linearly: maxseg per window2295 * (maxseg^2 / cwnd per packet).2296 */2297 if ((!delay_growth_ || (rcv_nxt_ > 0)) &&2298 last_state_ == TCPS_ESTABLISHED) {2299 if (!partial || open_cwnd_on_pack_) {2300 if (!ect_ || !hdr_flags::access(pkt)->ecnecho())2301 opencwnd();2302 }2303 }2304 2305 if ((state_ >= TCPS_FIN_WAIT_1) && (ackno == maxseq_)) {2306 ourfinisacked = TRUE;2307 }2308 2309 //2310 // special additional processing when our state2311 // is one of the closing states:2312 // FIN_WAIT_1, CLOSING, LAST_ACK2313 2314 switch (state_) {2315 /*2316 * In FIN_WAIT_1 STATE in addition to the processing2317 * for the ESTABLISHED state if our FIN is now acknowledged2318 * then enter FIN_WAIT_2.2319 */2320 case TCPS_FIN_WAIT_1: /* doing active close */2321 if (ourfinisacked) {2322 // got the ACK, now await incoming FIN2323 newstate(TCPS_FIN_WAIT_2);2324 cancel_timers();2325 needoutput = FALSE;2326 }2327 break;2328 2329 /*2330 * In CLOSING STATE in addition to the processing for2331 * the ESTABLISHED state if the ACK acknowledges our FIN2332 * then enter the TIME-WAIT state, otherwise ignore2333 * the segment.2334 */2335 case TCPS_CLOSING: /* simultaneous active close */;2336 if (ourfinisacked) {2337 newstate(TCPS_CLOSED);2338 cancel_timers();2339 }2340 break;2341 /*2342 * In LAST_ACK, we may still be waiting for data to drain2343 * and/or to be acked, as well as for the ack of our FIN.2344 * If our FIN is now acknowledged,2345 * enter the closed state and return.2346 */2347 case TCPS_LAST_ACK: /* passive close */2348 // K: added state change here2349 if (ourfinisacked) {2350 newstate(TCPS_CLOSED);2351 finish(); // cancels timers, erc2352 reset(); // for connection re-use (bug fix from ns-users list)2353 goto drop;2354 } else {2355 // should be a FIN we‘ve seen2356 if (debug_) {2357 fprintf(stderr, "%f: FullTcpAgent(%s)::received non-ACK (state:%d): ",2358 now(), name(), state_);2359 prpkt(pkt);2360 }2361 }2362 break;2363 2364 /* no case for TIME_WAIT in simulator */2365 } // inner state_ switch (closing states)2366 } // outer state_ switch (ack processing)2367 2368 step6:2369 2370 /*2371 * Processing of incoming DATAful segments.2372 * Code above has already trimmed redundant data.2373 *2374 * real TCP handles window updates and URG data here also2375 */2376 2377 /* dodata: this label is in the "real" code.. here only for reference */2378 2379 if ((datalen > 0 || (tiflags & TH_FIN)) &&2380 TCPS_HAVERCVDFIN(state_) == 0) {2381 2382 //2383 // the following ‘if‘ implements the "real" TCP2384 // TCP_REASS macro2385 //2386 2387 if (tcph->seqno() == rcv_nxt_ && rq_.empty()) {2388 // got the in-order packet we were looking2389 // for, nobody is in the reassembly queue,2390 // so this is the common case...2391 // note: in "real" TCP we must also be in2392 // ESTABLISHED state to come here, because2393 // data arriving before ESTABLISHED is2394 // queued in the reassembly queue. Since we2395 // don‘t really have a process anyhow, just2396 // accept the data here as-is (i.e. don‘t2397 // require being in ESTABLISHED state)2398 flags_ |= TF_DELACK;2399 rcv_nxt_ += datalen;2400 tiflags = tcph->flags() & TH_FIN;2401 2402 // give to "application" here2403 // in "real" TCP, this is sbappend() + sorwakeup()2404 if (datalen)2405 recvBytes(datalen); // notify app. of "delivery"2406 needoutput = need_send();2407 } else {2408 // see the "tcp_reass" function:2409 // not the one we want next (or it2410 // is but there‘s stuff on the reass queue);2411 // do whatever we need to do for out-of-order2412 // segments or hole-fills. Also,2413 // send an ACK (or SACK) to the other side right now.2414 // Note that we may have just a FIN here (datalen = 0)2415 int rcv_nxt_old_ = rcv_nxt_; // notify app. if changes2416 tiflags = reass(pkt);2417 if (rcv_nxt_ > rcv_nxt_old_) {2418 // if rcv_nxt_ has advanced, must have2419 // been a hole fill. In this case, there2420 // is something to give to application2421 recvBytes(rcv_nxt_ - rcv_nxt_old_);2422 }2423 flags_ |= TF_ACKNOW;2424 2425 if (tiflags & TH_PUSH) {2426 //2427 // ???: does this belong here2428 // K: APPLICATION recv2429 needoutput = need_send();2430 }2431 }2432 } else {2433 /*2434 * we‘re closing down or this is a pure ACK that2435 * wasn‘t handled by the header prediction part above2436 * (e.g. because cwnd < wnd)2437 */2438 // K: this is deleted2439 tiflags &= ~TH_FIN;2440 }2441 2442 /*2443 * if FIN is received, ACK the FIN2444 * (let user know if we could do so)2445 */2446 2447 if (tiflags & TH_FIN) {2448 if (TCPS_HAVERCVDFIN(state_) == 0) {2449 flags_ |= TF_ACKNOW;2450 rcv_nxt_++;2451 }2452 switch (state_) {2453 /*2454 * In SYN_RECEIVED and ESTABLISHED STATES2455 * enter the CLOSE_WAIT state.2456 * (passive close)2457 */2458 case TCPS_SYN_RECEIVED:2459 case TCPS_ESTABLISHED:2460 newstate(TCPS_CLOSE_WAIT);2461 break;2462 2463 /*2464 * If still in FIN_WAIT_1 STATE FIN has not been acked so2465 * enter the CLOSING state.2466 * (simultaneous close)2467 */2468 case TCPS_FIN_WAIT_1:2469 newstate(TCPS_CLOSING);2470 break;2471 /*2472 * In FIN_WAIT_2 state enter the TIME_WAIT state,2473 * starting the time-wait timer, turning off the other2474 * standard timers.2475 * (in the simulator, just go to CLOSED)2476 * (completion of active close)2477 */2478 case TCPS_FIN_WAIT_2:2479 newstate(TCPS_CLOSED);2480 cancel_timers();2481 break;2482 }2483 } /* end of if FIN bit on */2484 2485 if (needoutput || (flags_ & TF_ACKNOW))2486 send_much(1, REASON_NORMAL, maxburst_);2487 else if (curseq_ >= highest_ack_ || infinite_send_)2488 send_much(0, REASON_NORMAL, maxburst_);2489 // K: which state to return to when nothing left?2490 2491 if (!halfclose_ && state_ == TCPS_CLOSE_WAIT && highest_ack_ == maxseq_)2492 usrclosed();2493 2494 Packet::free(pkt);2495 2496 // haoboy: Is here the place for done{} of active close? 2497 // It cannot be put in the switch above because we might need to do2498 // send_much() (an ACK)2499 if (state_ == TCPS_CLOSED) 2500 Tcl::instance().evalf("%s done", this->name());2501 2502 return;2503 2504 //2505 // various ways of dropping (some also ACK, some also RST)2506 //2507 2508 dropafterack:2509 flags_ |= TF_ACKNOW;2510 send_much(1, REASON_NORMAL, maxburst_);2511 goto drop;2512 2513 dropwithreset:2514 /* we should be sending an RST here, but can‘t in simulator */2515 if (tiflags & TH_ACK) {2516 sendpacket(ackno, 0, 0x0, 0, REASON_NORMAL);2517 } else {2518 int ack = tcph->seqno() + datalen;2519 if (tiflags & TH_SYN)2520 ack--;2521 sendpacket(0, ack, TH_ACK, 0, REASON_NORMAL);2522 }2523 drop:2524 Packet::free(pkt);2525 return;2526 }2527 2528 /* 2529 * Dupack-action: what to do on a DUP ACK. After the initial check2530 * of ‘recover‘ below, this function implements the following truth2531 * table:2532 * 2533 * bugfix ecn last-cwnd == ecn action 2534 * 2535 * 0 0 0 full_reno_action2536 * 0 0 1 full_reno_action [impossible]2537 * 0 1 0 full_reno_action2538 * 0 1 1 1/2 window, return 2539 * 1 0 0 nothing 2540 * 1 0 1 nothing [impossible]2541 * 1 1 0 nothing 2542 * 1 1 1 1/2 window, return2543 */ 2544 2545 void2546 FullTcpAgent::dupack_action()2547 { 2548 2549 int recovered = (highest_ack_ > recover_);2550 2551 fastrecov_ = TRUE;2552 rtxbytes_ = 0;2553 2554 if (recovered || (!bug_fix_ && !ecn_) 2555 || (last_cwnd_action_ == CWND_ACTION_DUPACK)2556 || ( highest_ack_ == 0)) {2557 goto full_reno_action;2558 } 2559 2560 if (ecn_ && last_cwnd_action_ == CWND_ACTION_ECN) {2561 slowdown(CLOSE_CWND_HALF);2562 cancel_rtx_timer();2563 rtt_active_ = FALSE;2564 (void)fast_retransmit(highest_ack_);2565 return; 2566 } 2567 2568 if (bug_fix_) {2569 /*2570 * The line below, for "bug_fix_" true, avoids2571 * problems with multiple fast retransmits in one2572 * window of data.2573 */ 2574 return; 2575 }2576 2577 full_reno_action: 2578 slowdown(CLOSE_SSTHRESH_HALF|CLOSE_CWND_HALF);2579 cancel_rtx_timer();2580 rtt_active_ = FALSE;2581 recover_ = maxseq_;2582 (void)fast_retransmit(highest_ack_);2583 // we measure cwnd in packets,2584 // so don‘t scale by maxseg_2585 // as real TCP does2586 cwnd_ = double(ssthresh_) + double(dupacks_);2587 return;2588 }2589 2590 void2591 FullTcpAgent::timeout_action()2592 {2593 recover_ = maxseq_;2594 2595 if (cwnd_ < 1.0) {2596 if (debug_) {2597 fprintf(stderr, "%f: FullTcpAgent(%s):: resetting cwnd from %f to 1\n",2598 now(), name(), double(cwnd_));2599 }2600 cwnd_ = 1.0;2601 }2602 2603 if (last_cwnd_action_ == CWND_ACTION_ECN) {2604 slowdown(CLOSE_CWND_ONE);2605 } else {2606 slowdown(CLOSE_SSTHRESH_HALF|CLOSE_CWND_RESTART);2607 last_cwnd_action_ = CWND_ACTION_TIMEOUT;2608 }2609 reset_rtx_timer(1);2610 t_seqno_ = (highest_ack_ < 0) ? iss_ : int(highest_ack_);2611 fastrecov_ = FALSE;2612 dupacks_ = 0;2613 }2614 /*2615 * deal with timers going off.2616 * 2 types for now:2617 * retransmission timer (rtx_timer_)2618 * delayed ack timer (delack_timer_)2619 * delayed send (randomization) timer (delsnd_timer_)2620 *2621 * real TCP initializes the RTO as 6 sec2622 * (A + 2D, where A=0, D=3), [Stevens p. 305]2623 * and thereafter uses2624 * (A + 4D, where A and D are dynamic estimates)2625 *2626 * note that in the simulator t_srtt_, t_rttvar_ and t_rtt_2627 * are all measured in ‘tcp_tick_‘-second units2628 */2629 2630 void2631 FullTcpAgent::timeout(int tno)2632 {2633 2634 /*2635 * Due to F. Hernandez-Campos‘ fix in recv(), we may send an ACK2636 * while in the CLOSED state. -M. Weigle 7/24/012637 */2638 if (state_ == TCPS_LISTEN) {2639 // shouldn‘t be getting timeouts here2640 if (debug_) {2641 fprintf(stderr, "%f: FullTcpAgent(%s): unexpected timeout %d in state %s\n",2642 now(), name(), tno, statestr(state_));2643 }2644 return;2645 }2646 2647 switch (tno) {2648 2649 case TCP_TIMER_RTX:2650 /* retransmit timer */2651 ++nrexmit_;2652 timeout_action();2653 /* fall thru */2654 case TCP_TIMER_DELSND:2655 /* for phase effects */2656 send_much(1, PF_TIMEOUT, maxburst_);2657 break;2658 2659 case TCP_TIMER_DELACK:2660 if (flags_ & TF_DELACK) {2661 flags_ &= ~TF_DELACK;2662 flags_ |= TF_ACKNOW;2663 send_much(1, REASON_NORMAL, 0);2664 }2665 delack_timer_.resched(delack_interval_);2666 break;2667 default:2668 fprintf(stderr, "%f: FullTcpAgent(%s) Unknown Timeout type %d\n",2669 now(), name(), tno);2670 }2671 return;2672 }2673 2674 void2675 FullTcpAgent::dooptions(Packet* pkt)2676 {2677 // interesting options: timestamps (here),2678 // CC, CCNEW, CCECHO (future work perhaps?)2679 2680 hdr_flags *fh = hdr_flags::access(pkt);2681 hdr_tcp *tcph = hdr_tcp::access(pkt);2682 2683 if (ts_option_ && !fh->no_ts_) {2684 if (tcph->ts() < 0.0) {2685 fprintf(stderr,2686 "%f: FullTcpAgent(%s) warning: ts_option enabled in this TCP, but appears to be disabled in peer\n",2687 now(), name());2688 } else if (tcph->flags() & TH_SYN) {2689 flags_ |= TF_RCVD_TSTMP;2690 recent_ = tcph->ts();2691 recent_age_ = now();2692 }2693 }2694 2695 return;2696 }2697 2698 //2699 // this shouldn‘t ever happen2700 //2701 void2702 FullTcpAgent::process_sack(hdr_tcp*)2703 {2704 fprintf(stderr, "%f: FullTcpAgent(%s) Non-SACK capable FullTcpAgent received a SACK\n",2705 now(), name());2706 return;2707 }2708 2709 2710 /*2711 * ****** Tahoe ******2712 *2713 * for TCP Tahoe, we force a slow-start as the dup ack2714 * action. Also, no window inflation due to multiple dup2715 * acks. The latter is arranged by setting reno_fastrecov_2716 * false [which is performed by the Tcl init function for Tahoe in2717 * ns-default.tcl].2718 */2719 2720 /* 2721 * Tahoe2722 * Dupack-action: what to do on a DUP ACK. After the initial check2723 * of ‘recover‘ below, this function implements the following truth2724 * table:2725 * 2726 * bugfix ecn last-cwnd == ecn action 2727 * 2728 * 0 0 0 full_tahoe_action2729 * 0 0 1 full_tahoe_action [impossible]2730 * 0 1 0 full_tahoe_action2731 * 0 1 1 1/2 window, return2732 * 1 0 0 nothing 2733 * 1 0 1 nothing [impossible]2734 * 1 1 0 nothing 2735 * 1 1 1 1/2 window, return2736 */2737 2738 void2739 TahoeFullTcpAgent::dupack_action()2740 { 2741 int recovered = (highest_ack_ > recover_);2742 2743 fastrecov_ = TRUE;2744 rtxbytes_ = 0;2745 2746 if (recovered || (!bug_fix_ && !ecn_) || highest_ack_ == 0) {2747 goto full_tahoe_action;2748 }2749 2750 if (ecn_ && last_cwnd_action_ == CWND_ACTION_ECN) {2751 // slow start on ECN2752 last_cwnd_action_ = CWND_ACTION_DUPACK;2753 slowdown(CLOSE_CWND_ONE);2754 set_rtx_timer();2755 rtt_active_ = FALSE;2756 t_seqno_ = highest_ack_;2757 return; 2758 }2759 2760 if (bug_fix_) {2761 /*2762 * The line below, for "bug_fix_" true, avoids2763 * problems with multiple fast retransmits in one2764 * window of data.2765 */ 2766 return; 2767 }2768 2769 full_tahoe_action:2770 // slow-start and reset ssthresh2771 trace_event("FAST_RETX");2772 recover_ = maxseq_;2773 last_cwnd_action_ = CWND_ACTION_DUPACK;2774 slowdown(CLOSE_SSTHRESH_HALF|CLOSE_CWND_ONE); // cwnd->12775 set_rtx_timer();2776 rtt_active_ = FALSE;2777 t_seqno_ = highest_ack_;2778 send_much(0, REASON_NORMAL, 0);2779 return; 2780 } 2781 2782 /*2783 * ****** Newreno ******2784 *2785 * for NewReno, a partial ACK does not exit fast recovery,2786 * and does not reset the dup ACK counter (which might trigger fast2787 * retransmits we don‘t want). In addition, the number of packets2788 * sent in response to an ACK is limited to recov_maxburst_ during2789 * recovery periods.2790 */2791 2792 NewRenoFullTcpAgent::NewRenoFullTcpAgent() : save_maxburst_(-1)2793 {2794 bind("recov_maxburst_", &recov_maxburst_);2795 }2796 2797 void2798 NewRenoFullTcpAgent::pack_action(Packet*)2799 {2800 (void)fast_retransmit(highest_ack_);2801 cwnd_ = double(ssthresh_);2802 if (save_maxburst_ < 0) {2803 save_maxburst_ = maxburst_;2804 maxburst_ = recov_maxburst_;2805 }2806 return;2807 }2808 2809 void2810 NewRenoFullTcpAgent::ack_action(Packet* p)2811 {2812 if (save_maxburst_ >= 0) {2813 maxburst_ = save_maxburst_;2814 save_maxburst_ = -1;2815 }2816 FullTcpAgent::ack_action(p);2817 return;2818 }2819 2820 /*2821 *2822 * ****** SACK ******2823 *2824 * for Sack, receiver part must report SACK data2825 * sender part maintains a ‘scoreboard‘ (sq_) that2826 * records what it hears from receiver2827 * sender fills holes during recovery and obeys2828 * "pipe" style control until recovery is complete2829 */2830 2831 void2832 SackFullTcpAgent::reset()2833 {2834 sq_.clear(); // no SACK blocks2835 /* Fixed typo. -M. Weigle 6/17/02 */2836 sack_min_ = h_seqno_ = -1; // no left edge of SACK blocks2837 FullTcpAgent::reset();2838 }2839 2840 2841 int2842 SackFullTcpAgent::hdrsize(int nsackblocks)2843 {2844 int total = FullTcpAgent::headersize();2845 // use base header size plus SACK option size2846 if (nsackblocks > 0) {2847 total += ((nsackblocks * sack_block_size_)2848 + sack_option_size_);2849 }2850 return (total);2851 }2852 2853 void2854 SackFullTcpAgent::dupack_action()2855 {2856 2857 int recovered = (highest_ack_ > recover_);2858 2859 fastrecov_ = TRUE;2860 rtxbytes_ = 0;2861 pipe_ = maxseq_ - highest_ack_ - sq_.total();2862 2863 //printf("%f: SACK DUPACK-ACTION:pipe_:%d, sq-total:%d, bugfix:%d, cwnd:%d, highest_ack:%d, recover_:%d\n",2864 //now(), pipe_, sq_.total(), bug_fix_, int(cwnd_), int(highest_ack_), recover_);2865 2866 if (recovered || (!bug_fix_ && !ecn_)) {2867 goto full_sack_action;2868 } 2869 2870 if (ecn_ && last_cwnd_action_ == CWND_ACTION_ECN) {2871 /* 2872 * Received ECN notification and 3 DUPACKs in same 2873 * window. Don‘t cut cwnd again, but retransmit lost2874 * packet. -M. Weigle 6/19/022875 */2876 last_cwnd_action_ = CWND_ACTION_DUPACK;2877 cancel_rtx_timer();2878 rtt_active_ = FALSE;2879 int amt = fast_retransmit(highest_ack_);2880 pipectrl_ = TRUE;2881 h_seqno_ = highest_ack_ + amt;2882 send_much(0, REASON_DUPACK, maxburst_);2883 return; 2884 }2885 2886 if (bug_fix_) {2887 /* 2888 * The line below, for "bug_fix_" true, avoids2889 * problems with multiple fast retransmits in one2890 * window of data.2891 */ 2892 2893 //printf("%f: SACK DUPACK-ACTION BUGFIX RETURN:pipe_:%d, sq-total:%d, bugfix:%d, cwnd:%d\n",2894 //now(), pipe_, sq_.total(), bug_fix_, int(cwnd_));2895 return; 2896 }2897 2898 full_sack_action: 2899 trace_event("FAST_RECOVERY");2900 slowdown(CLOSE_SSTHRESH_HALF|CLOSE_CWND_HALF);2901 cancel_rtx_timer();2902 rtt_active_ = FALSE;2903 2904 // these initiate SACK-style "pipe" recovery2905 pipectrl_ = TRUE;2906 recover_ = maxseq_; // where I am when recovery starts2907 2908 int amt = fast_retransmit(highest_ack_);2909 h_seqno_ = highest_ack_ + amt;2910 2911 //printf("%f: FAST-RTX seq:%d, h_seqno_ is now:%d, pipe:%d, cwnd:%d, recover:%d\n",2912 //now(), int(highest_ack_), h_seqno_, pipe_, int(cwnd_), recover_);2913 2914 send_much(0, REASON_DUPACK, maxburst_);2915 2916 return;2917 }2918 2919 void2920 SackFullTcpAgent::pack_action(Packet*)2921 {2922 if (!sq_.empty() && sack_min_ < highest_ack_) {2923 sack_min_ = highest_ack_;2924 sq_.cleartonxt();2925 }2926 pipe_ -= maxseg_; // see comment in tcp-sack1.cc2927 if (h_seqno_ < highest_ack_)2928 h_seqno_ = highest_ack_;2929 }2930 2931 void2932 SackFullTcpAgent::ack_action(Packet*)2933 {2934 //printf("%f: EXITING fast recovery, recover:%d\n",2935 //now(), recover_);2936 fastrecov_ = pipectrl_ = FALSE;2937 if (!sq_.empty() && sack_min_ < highest_ack_) {2938 sack_min_ = highest_ack_;2939 sq_.cleartonxt();2940 }2941 dupacks_ = 0;2942 2943 /* 2944 * Update h_seqno_ on new ACK (same as for partial ACKS)2945 * -M. Weigle 6/3/052946 */2947 if (h_seqno_ < highest_ack_)2948 h_seqno_ = highest_ack_;2949 }2950 2951 //2952 // receiver side: if there are things in the reassembly queue,2953 // build the appropriate SACK blocks to carry in the SACK2954 //2955 int2956 SackFullTcpAgent::build_options(hdr_tcp* tcph)2957 {2958 int total = FullTcpAgent::build_options(tcph);2959 2960 if (!rq_.empty()) {2961 int nblk = rq_.gensack(&tcph->sa_left(0), max_sack_blocks_);2962 tcph->sa_length() = nblk;2963 total += (nblk * sack_block_size_) + sack_option_size_;2964 } else {2965 tcph->sa_length() = 0;2966 }2967 return (total);2968 }2969 2970 void2971 SackFullTcpAgent::timeout_action()2972 {2973 FullTcpAgent::timeout_action();2974 2975 //2976 // original SACK spec says the sender is2977 // supposed to clear out its knowledge of what2978 // the receiver has in the case of a timeout2979 // (on the chance the receiver has renig‘d).2980 // Here, this happens when clear_on_timeout_ is2981 // enabled.2982 //2983 2984 if (clear_on_timeout_) {2985 sq_.clear();2986 sack_min_ = highest_ack_;2987 }2988 2989 return;2990 }2991 2992 void2993 SackFullTcpAgent::process_sack(hdr_tcp* tcph)2994 {2995 //2996 // Figure out how many sack blocks are2997 // in the pkt. Insert each block range2998 // into the scoreboard2999 //3000 3001 if (max_sack_blocks_ <= 0) {3002 fprintf(stderr,3003 "%f: FullTcpAgent(%s) warning: received SACK block but I am not SACK enabled\n",3004 now(), name());3005 return;3006 } 3007 3008 int slen = tcph->sa_length(), i;3009 for (i = 0; i < slen; ++i) {3010 /* Added check for FIN -M. Weigle 5/21/02 */3011 if (((tcph->flags() & TH_FIN) == 0) && 3012 tcph->sa_left(i) >= tcph->sa_right(i)) {3013 fprintf(stderr,3014 "%f: FullTcpAgent(%s) warning: received illegal SACK block [%d,%d]\n",3015 now(), name(), tcph->sa_left(i), tcph->sa_right(i));3016 continue;3017 }3018 sq_.add(tcph->sa_left(i), tcph->sa_right(i), 0); 3019 }3020 3021 return;3022 }3023 3024 int3025 SackFullTcpAgent::send_allowed(int seq)3026 {3027 // not in pipe control, so use regular control3028 if (!pipectrl_)3029 return (FullTcpAgent::send_allowed(seq));3030 3031 // don‘t overshoot receiver‘s advertised window3032 int topawin = highest_ack_ + int(wnd_) * maxseg_;3033 if (seq >= topawin) {3034 //printf("%f: SEND(%d) NOT ALLOWED DUE TO AWIN:%d, pipe:%d, cwnd:%d\n",3035 //now(), seq, topawin, pipe_, int(cwnd_));3036 return FALSE;3037 }3038 3039 /*3040 * If not in ESTABLISHED, don‘t send anything we don‘t have3041 * -M. Weigle 7/18/023042 */3043 if (state_ != TCPS_ESTABLISHED && seq > curseq_)3044 return FALSE;3045 3046 // don‘t overshoot cwnd_3047 int cwin = int(cwnd_) * maxseg_;3048 return (pipe_ < cwin);3049 }3050 3051 3052 //3053 // Calculate the next seq# to send by send_much. If we are recovering and3054 // we have learned about data cached at the receiver via a SACK,3055 // we may want something other than new data (t_seqno)3056 //3057 3058 int3059 SackFullTcpAgent::nxt_tseq()3060 {3061 3062 int in_recovery = (highest_ack_ < recover_);3063 int seq = h_seqno_;3064 3065 if (!in_recovery) {3066 //if (int(t_seqno_) > 1)3067 //printf("%f: non-recovery nxt_tseq called w/t_seqno:%d\n",3068 //now(), int(t_seqno_));3069 //sq_.dumplist();3070 return (t_seqno_);3071 }3072 3073 int fcnt; // following count-- the3074 // count field in the block3075 // after the seq# we are about3076 // to send3077 int fbytes; // fcnt in bytes3078 3079 //if (int(t_seqno_) > 1)3080 //printf("%f: recovery nxt_tseq called w/t_seqno:%d, seq:%d, mode:%d\n",3081 //now(), int(t_seqno_), seq, sack_rtx_threshmode_);3082 //sq_.dumplist();3083 3084 while ((seq = sq_.nexthole(seq, fcnt, fbytes)) > 0) {3085 // if we have a following block3086 // with a large enough count3087 // we should use the seq# we get3088 // from nexthole()3089 if (sack_rtx_threshmode_ == 0 ||3090 (sack_rtx_threshmode_ == 1 && fcnt >= sack_rtx_cthresh_) ||3091 (sack_rtx_threshmode_ == 2 && fbytes >= sack_rtx_bthresh_) ||3092 (sack_rtx_threshmode_ == 3 && (fcnt >= sack_rtx_cthresh_ || fbytes >= sack_rtx_bthresh_)) ||3093 (sack_rtx_threshmode_ == 4 && (fcnt >= sack_rtx_cthresh_ && fbytes >= sack_rtx_bthresh_))) {3094 3095 //if (int(t_seqno_) > 1)3096 //printf("%f: nxt_tseq<hole> returning %d\n",3097 //now(), int(seq));3098 // adjust h_seqno, as we may have3099 // been "jumped ahead" by learning3100 // about a filled hole3101 if (seq > h_seqno_)3102 h_seqno_ = seq;3103 return (seq);3104 } else if (fcnt <= 0)3105 break;3106 else {3107 seq += maxseg_;3108 }3109 }3110 //if (int(t_seqno_) > 1)3111 //printf("%f: nxt_tseq<top> returning %d\n",3112 //now(), int(t_seqno_));3113 return (t_seqno_);3114 }
tcp-full.cc
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。