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; }