Example #1
0
/*
	Where the magic happens.
*/
void Connection::receive(TCP::Packet_ptr incoming) {
	// Let state handle what to do when incoming packet arrives, and modify the outgoing packet.
	signal_packet_received(incoming);
	// Change window accordingly. 
	control_block.SND.WND = incoming->win();
	switch(state_->handle(*this, incoming)) {
		case State::OK: {
			// Do nothing.
			break;
		}
		case State::CLOSED: {
			debug("<TCP::Connection::receive> State handle finished with CLOSED. We're done, ask host() to delete the connection. \n");
			signal_close();
			break;
		};
		case State::CLOSE: {
			debug("<TCP::Connection::receive> State handle finished with CLOSE. onDisconnect has been called, close the connection. \n");
			state_->close(*this);
			break;
		};
	}
}
bool Connection::handle_ack(TCP::Packet_ptr in) {
  // dup ack
  /*
    1. Same ACK as latest received
    2. outstanding data
    3. packet is empty
    4. is not an wnd update
  */
  if(in->ack() == cb.SND.UNA and flight_size()
    and !in->has_tcp_data() and cb.SND.WND == in->win()
    and !in->isset(SYN) and !in->isset(FIN))
  {
    dup_acks_++;
    on_dup_ack();
    return false;
  } // < dup ack

  // new ack
  else if(in->ack() >= cb.SND.UNA) {

    if( cb.SND.WL1 < in->seq() or ( cb.SND.WL1 == in->seq() and cb.SND.WL2 <= in->ack() ) )
    {
      cb.SND.WND = in->win();
      cb.SND.WL1 = in->seq();
      cb.SND.WL2 = in->ack();
      //printf("<Connection::handle_ack> Window update (%u)\n", cb.SND.WND);
    }

    acks_rcvd_++;

    debug("<Connection::handle_ack> New ACK#%u: %u FS: %u %s\n", acks_rcvd_,
      in->ack() - cb.ISS, flight_size(), fast_recovery ? "[RECOVERY]" : "");

    // [RFC 6582] p. 8
    prev_highest_ack_ = cb.SND.UNA;
    highest_ack_ = in->ack();

    // used for cwnd calculation (Reno)
    size_t bytes_acked = in->ack() - cb.SND.UNA;
    cb.SND.UNA = in->ack();

    // ack everything in rtx queue
    if(rtx_timer.active)
      rtx_ack(in->ack());

    // update cwnd when congestion avoidance?
    bool cong_avoid_rtt = false;

    // if measuring round trip time, stop
    if(rttm.active) {
      rttm.stop();
      cong_avoid_rtt = true;
    }

    // no fast recovery
    if(!fast_recovery) {
      //printf("<Connection::handle_ack> Not in Recovery\n");
      dup_acks_ = 0;
      cb.recover = cb.SND.NXT;

      // slow start
      if(cb.slow_start()) {
        reno_increase_cwnd(bytes_acked);
        debug2("<Connection::handle_ack> Slow start. cwnd=%u uw=%u\n",
          cb.cwnd, usable_window());
      }

      // congestion avoidance
      else {
        // increase cwnd once per RTT
        cb.cwnd += std::max(SMSS()*SMSS()/cb.cwnd, (uint32_t)1);
        debug2("<Connection::handle_ack> Congestion avoidance. cwnd=%u uw=%u\n",
          cb.cwnd, usable_window());
      } // < congestion avoidance

      // try to write
      //if(can_send() and acks_rcvd_ % 2 == 1)
      if(can_send())
        send_much();

      // if data, let state continue process
      if(in->has_tcp_data() or in->isset(FIN))
        return true;

    } // < !fast recovery

    // we're in fast recovery
    else {
      //printf("<Connection::handle_ack> In Recovery\n");
      // partial ack
      if(!reno_full_ack(in->ack())) {
        debug("<Connection::handle_ack> Partial ACK\n");
        reno_deflate_cwnd(bytes_acked);
        //printf("<TCP::Connection::handle_ack> Recovery - Partial ACK\n");
        retransmit();

        //dup_acks_ = 0;

        if(!reno_fpack_seen) {
          rtx_reset();
          reno_fpack_seen = true;
        }

        // send one segment if possible
        if(can_send()) {
          debug("<Connection::handle_ack> Sending one packet during recovery.\n");
          limited_tx();
        } else {
          debug("<Connection::handle_ack> Can't send during recovery - usable window is closed.\n");
        }

        if(in->has_tcp_data() or in->isset(FIN))
          return true;
      } // < partial ack

      // full ack
      else {
        debug("<Connection::handle_ack> Full ACK.\n");
        dup_acks_ = 0;
        finish_fast_recovery();
      } // < full ack

    } // < fast recovery

  } // < new ack

  // ACK outside
  else {
    return true;
  }
  return false;
}