void Connection::transmit(TCP::Packet_ptr packet) { if(!rttm.active and packet->end() == cb.SND.NXT) { //printf("<TCP::Connection::transmit> Starting RTT measurement.\n"); rttm.start(); } //if(packet->seq() + packet->data_length() != cb.SND.NXT) //printf("<TCP::Connection::transmit> rseq=%u rack=%u\n", // packet->seq() - cb.ISS, packet->ack() - cb.IRS); debug2("<TCP::Connection::transmit> TX %s\n", packet->to_string().c_str()); host_.transmit(packet); if(packet->has_data() and !rtx_timer.active) { rtx_start(); } }
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_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 write queue if(!writeq.empty()) 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_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_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; }