Exemple #1
0
void TCP::transmit(tcp::Packet_ptr packet) {
  // Generate checksum.
  packet->set_tcp_checksum();
  debug2("<TCP::transmit> %s\n", packet->to_string().c_str());

  // Stat increment bytes transmitted and packets transmitted
  (*bytes_tx_) += packet->tcp_data_length();
  (*packets_tx_)++;

  _network_layer_out(std::move(packet));
}
/*
	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;
		};
	}
}
void Connection::parse_options(TCP::Packet_ptr packet) {
  assert(packet->has_tcp_options());
  debug("<TCP::parse_options> Parsing options. Offset: %u, Options: %u \n",
        packet->offset(), packet->tcp_options_length());

  auto* opt = packet->tcp_options();

  while((char*)opt < packet->tcp_data()) {

    auto* option = (TCP::Option*)opt;

    switch(option->kind) {

    case Option::END: {
      return;
    }

    case Option::NOP: {
      opt++;
      break;
    }

    case Option::MSS: {
      // unlikely
      if(option->length != 4)
        throw TCPBadOptionException{Option::MSS, "length != 4"};
      // unlikely
      if(!packet->isset(SYN))
        throw TCPBadOptionException{Option::MSS, "Non-SYN packet"};

      auto* opt_mss = (Option::opt_mss*)option;
      uint16_t mss = ntohs(opt_mss->mss);
      cb.SND.MSS = mss;
      debug2("<TCP::parse_options@Option:MSS> MSS: %u \n", mss);
      opt += option->length;
      break;
    }

    default:
      return;
    }
  }
}
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;
}