tcp-sack1.cc
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:14k
源码类别:

通讯编程

开发平台:

Visual C++

  1. /* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
  2. /*
  3.  * Copyright (c) 1990, 1997 Regents of the University of California.
  4.  * All rights reserved.
  5.  *
  6.  * Redistribution and use in source and binary forms are permitted
  7.  * provided that the above copyright notice and this paragraph are
  8.  * duplicated in all such forms and that any documentation,
  9.  * advertising materials, and other materials related to such
  10.  * distribution and use acknowledge that the software was developed
  11.  * by the University of California, Lawrence Berkeley Laboratory,
  12.  * Berkeley, CA.  The name of the University may not be used to
  13.  * endorse or promote products derived from this software without
  14.  * specific prior written permission.
  15.  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  16.  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  17.  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  18.  */
  19. /* 8/02 Tom Kelly - Made scoreboard a general interface to allow
  20.  *                  easy swapping of scoreboard algorithms.  
  21.  */
  22. #include <stdio.h>
  23. #include <stdlib.h>
  24. #include <sys/types.h>
  25. #include "ip.h"
  26. #include "tcp.h"
  27. #include "flags.h"
  28. #include "scoreboard.h"
  29. #include "scoreboard-rq.h"
  30. #include "random.h"
  31. #define TRUE    1
  32. #define FALSE   0
  33. #define RECOVER_DUPACK  1
  34. #define RECOVER_TIMEOUT 2
  35. #define RECOVER_QUENCH  3
  36. class Sack1TcpAgent : public TcpAgent {
  37.  public:
  38. Sack1TcpAgent();
  39. virtual ~Sack1TcpAgent();
  40. virtual void recv(Packet *pkt, Handler*);
  41. int is_sacked(hdr_tcp *tcph, int seqlo, int seqhi);
  42. void reset();
  43. virtual void timeout(int tno);
  44. virtual void dupack_action();
  45. virtual void partial_ack_action();
  46. void plot();
  47. virtual void send_much(int force, int reason, int maxburst);
  48.  protected:
  49. u_char timeout_; /* boolean: sent pkt from timeout? */
  50. u_char fastrecov_; /* boolean: doing fast recovery? */
  51. int pipe_; /* estimate of pipe size (fast recovery) */ 
  52. int partial_ack_; /* Set to "true" to ensure sending */
  53. /*  a packet on a partial ACK.     */
  54. int next_pkt_; /* Next packet to transmit during Fast */
  55. /*  Retransmit as a result of a partial ack. */
  56. int firstpartial_; /* First of a series of partial acks. */
  57. ScoreBoard* scb_;
  58. static const int SBSIZE=64; /* Initial scoreboard size */
  59. };
  60. static class Sack1TcpClass : public TclClass {
  61. public:
  62. Sack1TcpClass() : TclClass("Agent/TCP/Sack1") {}
  63. TclObject* create(int, const char*const*) {
  64. return (new Sack1TcpAgent());
  65. }
  66. } class_sack;
  67. Sack1TcpAgent::Sack1TcpAgent() : fastrecov_(FALSE), pipe_(-1), next_pkt_(0), firstpartial_(0)
  68. {
  69. bind_bool("partial_ack_", &partial_ack_);
  70. /* Use the Reassembly Queue based scoreboard as
  71.  * ScoreBoard is O(cwnd) which is bad for HSTCP
  72.  * scb_ = new ScoreBoard(new ScoreBoardNode[SBSIZE],SBSIZE);
  73.  */
  74. scb_ = new ScoreBoardRQ();
  75. }
  76. Sack1TcpAgent::~Sack1TcpAgent(){
  77. delete scb_;
  78. }
  79. void Sack1TcpAgent::reset ()
  80. {
  81. scb_->ClearScoreBoard();
  82. TcpAgent::reset ();
  83. }
  84. void Sack1TcpAgent::recv(Packet *pkt, Handler*)
  85. {
  86. hdr_tcp *tcph = hdr_tcp::access(pkt);
  87. int valid_ack = 0;
  88.         if (qs_approved_ == 1 && tcph->seqno() > last_ack_)
  89. endQuickStart();
  90.         if (qs_requested_ == 1)
  91.                 processQuickStart(pkt);
  92. #ifdef notdef
  93. if (pkt->type_ != PT_ACK) {
  94. Tcl::instance().evalf("%s error "received non-ack"",
  95.       name());
  96. Packet::free(pkt);
  97. return;
  98. }
  99. #endif
  100.         /* W.N.: check if this is from a previous incarnation */
  101.         if (tcph->ts() < lastreset_) {
  102.                 // Remove packet and do nothing
  103.                 Packet::free(pkt);
  104.                 return;
  105.         }
  106. ++nackpack_;
  107. int ecnecho = hdr_flags::access(pkt)->ecnecho();
  108. if (ecnecho && ecn_)
  109. ecn(tcph->seqno());
  110.         if (tcph->seqno() >= last_ack_)
  111. // Check if ACK is valid.  Suggestion by Mark Allman.
  112. valid_ack = 1;
  113. /*
  114.  * If DSACK is being used, check for DSACK blocks here.
  115.  * Possibilities:  Check for unnecessary Fast Retransmits.
  116.  */
  117. if (!fastrecov_) {
  118. /* normal... not fast recovery */
  119. if ((int)tcph->seqno() > last_ack_) {
  120. /*
  121.  * regular ACK not in fast recovery... normal
  122.  */
  123. firstpartial_ = 0;
  124. recv_newack_helper(pkt);
  125. timeout_ = FALSE;
  126. scb_->ClearScoreBoard();
  127. if (last_ack_ == 0 && delay_growth_) {
  128. cwnd_ = initial_window();
  129. }
  130. } else if ((int)tcph->seqno() < last_ack_) {
  131. /*NOTHING*/
  132. } else if (timeout_ == FALSE) {
  133. if (tcph->seqno() != last_ack_) {
  134. fprintf(stderr, "pkt seq %d should be %dn" ,
  135. tcph->seqno(), last_ack_);
  136. abort();
  137. }
  138. scb_->UpdateScoreBoard (highest_ack_, tcph);
  139. /*
  140.    * Check for a duplicate ACK.
  141.  * Check that the SACK block actually
  142.  *  acknowledges new data.
  143.    */
  144.           if(scb_->CheckUpdate()) {
  145.     if (++dupacks_ == numdupacks(cwnd_)) {
  146.   /*
  147.    * Assume we dropped just one packet.
  148.    * Retransmit last ack + 1
  149.    * and try to resume the sequence.
  150.    */
  151.       dupack_action();
  152.   } else if (dupacks_ < numdupacks(cwnd_) && singledup_ ) {
  153. send_one();
  154.   }
  155. }
  156. if (sfrto_enabled_ && frto_ == 2) {
  157. /*
  158.  * SACK-based F-RTO: If SACK only acknowledges
  159.  * data that was transmitted before RTO and
  160.  * not acknowledged earlier,
  161.  * the timeout was spurious.
  162.  */
  163. if (scb_->IsChanged() &&
  164.     !is_sacked(tcph, recover_, maxseq_)) {
  165. spurious_timeout();
  166. } else {
  167. t_seqno_ = highest_ack_ + 1;
  168. cwnd_ = frto_;
  169. frto_ = 0;
  170. dupacks_ = 0;
  171. }
  172. }
  173. }
  174.          if (valid_ack || aggressive_maxburst_)
  175. if (dupacks_ == 0)
  176. send_much(FALSE, 0, maxburst_);
  177. } else {
  178. /* we are in fast recovery */
  179. --pipe_;
  180. if ((int)tcph->seqno() >= recover_) {
  181. /* ACK indicates fast recovery is over */
  182. recover_ = 0;
  183. fastrecov_ = FALSE;
  184. newack(pkt);
  185. /* if the connection is done, call finish() */
  186. if ((highest_ack_ >= curseq_-1) && !closed_) {
  187. closed_ = 1;
  188. finish();
  189. }
  190. timeout_ = FALSE;
  191. scb_->ClearScoreBoard();
  192. /* New window: W/2 - K or W/2? */
  193. } else if ((int)tcph->seqno() > highest_ack_) {
  194. /* Not out of fast recovery yet.
  195.  * Update highest_ack_, but not last_ack_. */
  196. highest_ack_ = (int)tcph->seqno();
  197. scb_->UpdateScoreBoard (highest_ack_, tcph);
  198. if (partial_ack_) {
  199.   /* partial_ack_ is needed to guarantee that */
  200.   /*  a new packet is sent in response to a   */
  201.   /*  partial ack.                            */
  202. partial_ack_action();
  203. ++pipe_;
  204. if (firstpartial_ == 0) {
  205. newtimer(pkt);
  206. t_backoff_ = 1;
  207. firstpartial_ = 1;
  208. }
  209. } else {
  210. --pipe_;
  211. newtimer(pkt);
  212. t_backoff_ = 1;
  213.    /* If this partial ACK is from a retransmitted pkt,*/
  214.    /* then we decrement pipe_ again, so that we never */
  215.    /* do worse than slow-start.  If this partial ACK  */
  216.    /* was instead from the original packet, reordered,*/
  217.    /* then this might be too aggressive. */
  218. }
  219. } else if (timeout_ == FALSE) {
  220. /* got another dup ack */
  221. scb_->UpdateScoreBoard (highest_ack_, tcph);
  222.           if(scb_->CheckUpdate()) {
  223.   if (dupacks_ > 0)
  224.            dupacks_++;
  225.   }
  226. }
  227.          if (valid_ack || aggressive_maxburst_)
  228. send_much(FALSE, 0, maxburst_);
  229. }
  230. Packet::free(pkt);
  231. #ifdef notyet
  232. if (trace_)
  233. plot();
  234. #endif
  235. }
  236. /*
  237.  * Returns TRUE if any of the SACK blocks in the current packet (tcph)
  238.  * cover sequence numbers between seqlo and seqhi
  239.  */
  240. int Sack1TcpAgent::is_sacked(hdr_tcp *tcph, int seqlo, int seqhi)
  241. {
  242. int i, sleft, sright;
  243. for (i=0; i < tcph->sa_length(); i++) {
  244. sleft = tcph->sa_left(i);
  245. sright = tcph->sa_right(i);
  246. if ((sright > seqlo && sright <= seqhi) ||
  247.     (sleft >= seqlo && sleft < seqhi) ||
  248.     (sleft < seqlo && sright > seqhi)) {
  249. return TRUE;
  250. }
  251. }
  252. return FALSE;
  253. }
  254. void
  255. Sack1TcpAgent::dupack_action()
  256. {
  257. int recovered = (highest_ack_ > recover_);
  258. if (recovered || (!bug_fix_ && !ecn_) || 
  259. (bugfix_ss_ && highest_ack_ == 0)) {
  260.                 // (highest_ack_ == 0) added to allow Fast Retransmit
  261.                 //  when the first data packet is dropped.
  262.                 //  Bug report from Mark Allman.
  263. goto sack_action;
  264. }
  265.  
  266. if (ecn_ && last_cwnd_action_ == CWND_ACTION_ECN) {
  267. last_cwnd_action_ = CWND_ACTION_DUPACK;
  268. /*
  269.  * What if there is a DUPACK action followed closely by ECN
  270.  * followed closely by a DUPACK action?
  271.  * The optimal thing to do would be to remember all
  272.  * congestion actions from the most recent window
  273.  * of data.  Otherwise "bugfix" might not prevent
  274.  * all unnecessary Fast Retransmits.
  275.  */
  276. reset_rtx_timer(1,0);
  277. /*
  278.  * There are three possibilities: 
  279.  * (1) pipe_ = int(cwnd_) - numdupacks_;
  280.  * (2) pipe_ = window() - numdupacks_;
  281.  * (3) pipe_ = maxseq_ - highest_ack_ - numdupacks_;
  282.  * equation (2) takes into account the receiver's
  283.  * advertised window, and equation (3) takes into
  284.  * account a data-limited sender.
  285.  */
  286. if (singledup_ && LimTransmitFix_) {
  287.   pipe_ = maxseq_ - highest_ack_ - 1;
  288. }
  289. else {
  290.   // numdupacks(cwnd_) packets have left the pipe
  291.   pipe_ = maxseq_ - highest_ack_ - numdupacks(cwnd_);
  292. }
  293. fastrecov_ = TRUE;
  294. scb_->MarkRetran(highest_ack_+1);
  295. output(last_ack_ + 1, TCP_REASON_DUPACK);
  296. return;
  297. }
  298. if (bug_fix_) {
  299. /*
  300.  * The line below, for "bug_fix_" true, avoids
  301.  * problems with multiple fast retransmits in one
  302.  * window of data.
  303.  */
  304. return;
  305. }
  306. sack_action:
  307. recover_ = maxseq_;
  308. if (oldCode_) {
  309.   pipe_ = int(cwnd_) - numdupacks(cwnd_);
  310. } else if (singledup_ && LimTransmitFix_) {
  311.   pipe_ = maxseq_ - highest_ack_ - 1;
  312. }
  313. else {
  314.   // numdupacks(cwnd_) packets have left the pipe
  315.   pipe_ = maxseq_ - highest_ack_ - numdupacks(cwnd_);
  316. }
  317. reset_rtx_timer(1,0);
  318. fastrecov_ = TRUE;
  319. scb_->MarkRetran(highest_ack_+1);
  320.         if (!lossQuickStart()) {
  321.                 // we are now going into fast_recovery and will trace that event
  322.                 trace_event("FAST_RECOVERY");
  323.                 last_cwnd_action_ = CWND_ACTION_DUPACK;
  324.                 slowdown(CLOSE_SSTHRESH_HALF|CLOSE_CWND_HALF);
  325. output(last_ack_ + 1, TCP_REASON_DUPACK); // from top
  326. /*
  327.  * If dynamically adjusting numdupacks_, record information
  328.  *  at this point.
  329.  */
  330. }
  331. return;
  332. }
  333. /*
  334.  * Process a packet that acks previously unacknowleges data, but
  335.  * does not take us out of Fast Retransmit.
  336.  *
  337.  * The need for a mechanism to ensure that Sack TCP sends a packet in
  338.  * response to a partial ACK has been discussed in
  339.  * "Challenges to Reliable Data Transport over Heterogeneous
  340.  * Wireless Networks", Hari Balakrishnan, 1998, and in
  341.  * "Responding to Spurious Timeouts in TCP", Andrei Gurtov and 
  342.  * Reiner Ludwig, 2003. 
  343.  */
  344. void
  345. Sack1TcpAgent::partial_ack_action()
  346. {
  347. if (next_pkt_ < highest_ack_ + 1) {
  348. next_pkt_ = highest_ack_ + 1;
  349. }
  350. // Output two packets in response to a partial ack,
  351. //   so as not to do worse than slow-start.
  352. int i;
  353. for (i = 1; i<=2; i++) {
  354. int getNext = scb_->GetNextUnacked(next_pkt_);
  355. if (getNext > next_pkt_) {
  356. next_pkt_ = getNext;
  357. }
  358. if (t_seqno_ < next_pkt_) {
  359. t_seqno_ = next_pkt_;
  360. }
  361. output(next_pkt_, TCP_REASON_PARTIALACK);
  362. scb_->MarkRetran(next_pkt_);
  363. ++next_pkt_; 
  364. }
  365. return;
  366. }
  367. void Sack1TcpAgent::timeout(int tno)
  368. {
  369. // F-RTO is not allowed if earlier SACK fast recovery is underway.
  370. int no_frto = fastrecov_;
  371. if (tno == TCP_TIMER_RTX) {
  372. /*
  373.  * IF DSACK and dynamic adjustment of numdupacks_,
  374.  *  check whether a smaller value of numdupacks_
  375.  *  would have prevented this retransmit timeout.
  376.  * If DSACK and detection of premature retransmit
  377.  *  timeouts, then save some info here.
  378.  */ 
  379. dupacks_ = 0;
  380. fastrecov_ = FALSE;
  381. timeout_ = TRUE;
  382. if (highest_ack_ > last_ack_)
  383. last_ack_ = highest_ack_;
  384. #ifdef DEBUGSACK1A
  385. printf ("timeout. highest_ack: %i seqno: %i fid: %in", 
  386. (int)highest_ack_, (int)t_seqno_, fid_);
  387. #endif
  388. recover_ = maxseq_;
  389. scb_->ClearScoreBoard();
  390. }
  391. TcpAgent::timeout(tno);
  392. // Overrule frto_ setting that may have been done in TcpAgent
  393. if (no_frto)
  394. frto_ = 0;
  395. }
  396. void Sack1TcpAgent::send_much(int force, int reason, int maxburst)
  397. {
  398. register int found, npacket = 0;
  399. int win = window();
  400. int xmit_seqno;
  401. found = 1;
  402. if (!force && delsnd_timer_.status() == TIMER_PENDING)
  403. return;
  404. /*
  405.  * as long as the pipe is open and there is app data to send...
  406.  */
  407. while (((!fastrecov_ && (t_seqno_ <= last_ack_ + win)) ||
  408. (fastrecov_ && (pipe_ < int(cwnd_)))) 
  409. // && t_seqno_ < curseq_ && found) {
  410. && (last_ack_ + 1) < curseq_ && found) {
  411. if (overhead_ == 0 || force) {
  412. found = 0;
  413. xmit_seqno = scb_->GetNextRetran ();
  414. #ifdef DEBUGSACK1A
  415. printf("highest_ack: %d xmit_seqno: %dn", 
  416. (int)highest_ack_, xmit_seqno);
  417. #endif
  418. if (xmit_seqno == -1) { 
  419. if ((!fastrecov_ && t_seqno_<=highest_ack_+win)||
  420. (fastrecov_ && t_seqno_<=highest_ack_+int(wnd_))) {
  421. if (t_seqno_ < curseq_) {
  422. found = 1;
  423. xmit_seqno = t_seqno_++;
  424. }
  425. #ifdef DEBUGSACK1A
  426. printf("sending %d fastrecovery: %d win %dn",
  427. xmit_seqno, fastrecov_, win);
  428. #endif
  429. }
  430. } else if (recover_>0 && xmit_seqno<=highest_ack_+int(wnd_)) {
  431. found = 1;
  432. scb_->MarkRetran (xmit_seqno);
  433. win = window();
  434. }
  435. if (found) {
  436. output(xmit_seqno, reason);
  437. if (t_seqno_ <= xmit_seqno)
  438. t_seqno_ = xmit_seqno + 1;
  439. npacket++;
  440. pipe_++;
  441. if (QOption_)
  442. process_qoption_after_send () ;
  443.                         if (qs_approved_ == 1) {
  444.                                 double delay = (double) t_rtt_ * tcp_tick_ / cwnd_;
  445.                                 delsnd_timer_.resched(delay);
  446.                                 return;
  447.                         }
  448. }
  449. } else if (!(delsnd_timer_.status() == TIMER_PENDING)) {
  450. /*
  451.  * Set a delayed send timeout.
  452.  * This is only for the simulator,to add some
  453.  *   randomization if speficied.
  454.  */
  455. delsnd_timer_.resched(Random::uniform(overhead_));
  456. return;
  457. }
  458. if (maxburst && npacket == maxburst)
  459. break;
  460. } /* while */
  461. }
  462. void Sack1TcpAgent::plot()
  463. {
  464. #ifdef notyet
  465. double t = Scheduler::instance().clock();
  466. sprintf(trace_->buffer(), "t %g %d rtt %gn", 
  467. t, class_, t_rtt_ * tcp_tick_);
  468. trace_->dump();
  469. sprintf(trace_->buffer(), "t %g %d dev %gn", 
  470. t, class_, t_rttvar_ * tcp_tick_);
  471. trace_->dump();
  472. sprintf(trace_->buffer(), "t %g %d win %fn", t, class_, cwnd_);
  473. trace_->dump();
  474. sprintf(trace_->buffer(), "t %g %d bck %dn", t, class_, t_backoff_);
  475. trace_->dump();
  476. #endif
  477. }