From ec228b0d9072f14d59aa3e499b781d7fbd6e0d07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=85kesson?= Date: Fri, 15 Apr 2016 14:56:02 +0200 Subject: [PATCH 1/2] tcp: extended work on Retransmission and Congestion Control (Reno) --- api/net/tcp.hpp | 237 +++++++++++++++++++++++------- src/net/tcp.cpp | 4 +- src/net/tcp_connection.cpp | 207 ++++++++++++++++++++------ src/net/tcp_connection_states.cpp | 129 +++++++++++----- 4 files changed, 439 insertions(+), 138 deletions(-) diff --git a/api/net/tcp.hpp b/api/net/tcp.hpp index a9af1eb2b0..78a1d496a9 100644 --- a/api/net/tcp.hpp +++ b/api/net/tcp.hpp @@ -363,6 +363,8 @@ namespace net { inline bool isset(TCP::Flag f) { return ntohs(header().offset_flags.whole) & f; } + //TCP::Flag flags() const { return (htons(header().offset_flags.whole) << 8) & 0xFF; } + /// OFFSET, OPTIONS, DATA /// @@ -431,6 +433,10 @@ namespace net { return total; } + bool is_acked_by(const Seq ack) const { + return ack >= (seq() + data_length()); + } + inline std::string to_string() { std::ostringstream os; os << "[ S:" << source().to_string() << " D:" << destination().to_string() @@ -662,9 +668,9 @@ namespace net { class State { public: enum Result { - CLOSED = -1, - OK = 0, - CLOSE = 1 + CLOSED = -1, // This inditactes that a Connection is done and should be closed. + OK = 0, // Does nothing + CLOSE = 1 // This indicates that the CLIENT (peer) has/wants to close their end. }; /* Open a Connection. @@ -779,6 +785,7 @@ namespace net { uint16_t MSS; // Maximum segment size for outgoing segments. uint32_t cwnd; // Congestion window [RFC 5681] + Seq recover; // New Reno [RFC 6582] } SND; // << TCP::Seq ISS; // initial send sequence number @@ -795,7 +802,7 @@ namespace net { uint32_t ssthresh; // slow start threshold [RFC 5681] TCB() { - SND = { 0, 0, TCP::default_window_size, 0, 0, 0, TCP::default_mss, 0 }; + SND = { 0, 0, TCP::default_window_size, 0, 0, 0, TCP::default_mss, 0, 0 }; ISS = 0; RCV = { 0, TCP::default_window_size, 0, 0 }; IRS = 0; @@ -1106,10 +1113,22 @@ namespace net { */ bool queued_; + /* + Retransmission queue + */ + std::deque retransq; + struct { - TCP::Seq ACK = 0; - size_t count = 0; - } dup_acks_; + hw::PIT::Timer_iterator iter; + bool active = false; + size_t i = 0; + } rt_timer; + + + /* + Keep track of duplicate ACK + */ + size_t DUP_ACK = 0; /* Bytes queued for transmission. @@ -1122,20 +1141,22 @@ namespace net { uint64_t time_wait_started; + // [RFC 6298] - struct RoundTripCalc { + // Round Trip Time Measurer + struct RTTM { using timestamp_t = double; using duration_t = double; // clock granularity - static constexpr duration_t CLOCK_G = hw::PIT::frequency().count(); + //static constexpr duration_t CLOCK_G = hw::PIT::frequency().count() / 1000; + static constexpr duration_t CLOCK_G = 0.0011; static constexpr double K = 4.0; static constexpr double alpha = 1.0/8; static constexpr double beta = 1.0/4; - TCP::Seq SEQ; // current sequence number measured timestamp_t t; // tick when measure is started duration_t SRTT; // smoothed round-trip time @@ -1144,16 +1165,25 @@ namespace net { bool active = false; - void start(Seq seq) { - SEQ = seq; + RTTM() : t(OS::uptime()), RTO(1.0), active(false) {} + + void start() { t = OS::uptime(); active = true; } - void stop() { + void stop(bool first = false) { + assert(active); active = false; // round trip time (RTT) - sub_rtt_measurement(OS::uptime() - t); + auto rtt = OS::uptime() - t; + debug2(" RTT: %ums\n", + (uint32_t)(rtt * 1000)); + if(!first) + sub_rtt_measurement(rtt); + else { + first_rtt_measurement(rtt); + } } /* @@ -1165,7 +1195,7 @@ namespace net { where K = 4. */ - void first_rtt_measurement(duration_t R) { + inline void first_rtt_measurement(duration_t R) { SRTT = R; RTTVAR = R/2; update_rto(); @@ -1188,16 +1218,19 @@ namespace net { After the computation, a host MUST update RTO <- SRTT + max (G, K*RTTVAR) */ - void sub_rtt_measurement(duration_t R) { + inline void sub_rtt_measurement(duration_t R) { RTTVAR = (1 - beta) * RTTVAR + beta * std::abs(SRTT-R); SRTT = (1 - alpha) * SRTT + alpha * R; + update_rto(); } - void update_rto() { + inline void update_rto() { RTO = std::max(SRTT + std::max(CLOCK_G, K * RTTVAR), 1.0); + debug2(" RTO updated: %ums\n", + (uint32_t)(RTO * 1000)); } - } round_trip; + } rttm; /// CALLBACK HANDLING /// @@ -1301,7 +1334,7 @@ namespace net { Returns if the connection has a doable write job. */ inline bool has_doable_job() { - return !write_queue.empty() and usable_window() >= MSDS(); + return !write_queue.empty() and usable_window() >= SMSS(); } /* @@ -1363,41 +1396,101 @@ namespace net { inline Connection::TCB& tcb() { return control_block; } inline int32_t usable_window() const { - auto x = (int64_t)control_block.SND.UNA + (int64_t)control_block.SND.WND - (int64_t)control_block.SND.NXT; - return std::min((int32_t) x, (int32_t)control_block.SND.cwnd); + auto x = (int64_t)congestion_window() - (int64_t)control_block.SND.NXT; + return (int32_t) x; } - /// Congestion Control [RFC 5681] /// + /* - inline uint16_t SMSS() const { - return host_.MSS(); + Note: + Made a function due to future use when Window Scaling Option is added. + */ + inline int32_t send_window() const { + return (int32_t)control_block.SND.WND; } - inline uint16_t RMSS() const { - return control_block.SND.MSS; + inline int32_t congestion_window() const { + auto win = control_block.SND.UNA + std::min((int32_t)control_block.SND.cwnd, send_window()); + return win; } - inline int32_t flight_size() const { - return control_block.SND.NXT - control_block.SND.UNA; + /* + Acknowledge a packet + - TCB update, Congestion control handling, RTT calculation and RT handling. + */ + void acknowledge(Seq ACK); + + /// Congestion Control [RFC 5681] /// + + inline void setup_congestion_control() + { reno_init(); } + + inline uint16_t SMSS() const + { return host_.MSS(); } + + inline uint16_t RMSS() const + { return control_block.SND.MSS; } + + inline int32_t flight_size() const + { return control_block.SND.NXT - control_block.SND.UNA; } + + /// Reno /// + + inline void reno_init() { + reno_init_cwnd(3); + reno_init_sshtresh(); } - inline void init_cwnd(uint32_t segments) { + inline void reno_init_cwnd(size_t segments) + { control_block.SND.cwnd = segments*SMSS(); + printf(" Cwnd initilized: %u\n", control_block.SND.cwnd); } - inline void reduce_slow_start_threshold() { + inline void reno_init_sshtresh() + { control_block.ssthresh = control_block.SND.WND; } + + inline bool reno_slow_start() const + { return control_block.SND.cwnd < control_block.ssthresh; } + + inline void reno_increase_cwnd(uint16_t n) + { control_block.SND.cwnd += std::min(n, SMSS()); } + + inline void reno_reduce_ssthresh() { control_block.ssthresh = std::max( (flight_size() / 2), (2 * SMSS()) ); - debug2("TCP::Connection::reduce_slow_start_threshold> Slow start threshold reduced: %u\n", + printf(" Slow start threshold reduced: %u\n", control_block.ssthresh); } - inline void segment_loss_detected() { - reduce_slow_start_threshold(); + inline void reno_fast_retransmit() { + printf(" Fast retransmit initiated.\n"); + retransmit(); + control_block.SND.cwnd = control_block.ssthresh + (3 * SMSS()); } - /* - */ - size_t duplicate_ack(TCP::Seq ack); + inline void reno_loss_detected() { + reno_reduce_ssthresh(); + reno_fast_retransmit(); + } + + inline bool reno_is_dup_ack(TCP::Packet_ptr in) { + bool is_dup_ack = flight_size() > 0 + and !in->has_data() + and !in->isset(FIN) and !in->isset(SYN) + //and ((in->flags() & (FIN | SYN)) == 0) + and in->win() == control_block.SND.WND; + + return is_dup_ack; + } + + inline void reno_dup_ack() { + if(++DUP_ACK == 3) { + printf(" Duplicate ACK - Strike 3!\n"); + reno_loss_detected(); + } else if(DUP_ACK > 3) { + control_block.SND.cwnd += SMSS(); + } + } /* Generate a new ISS. @@ -1421,18 +1514,13 @@ namespace net { */ void transmit(TCP::Packet_ptr); - /* - Retransmit the packet. - */ - void retransmit(TCP::Packet_ptr); - /* Creates a new outgoing packet with the current TCB values and options. */ TCP::Packet_ptr create_outgoing_packet(); /* - */ + */ inline TCP::Packet_ptr outgoing_packet() { return create_outgoing_packet(); } @@ -1441,21 +1529,57 @@ namespace net { /// RETRANSMISSION /// /* - Starts a retransmission timer that retransmits the packet when RTO has passed. + Retransmit the first packet in retransmission queue. + */ + void retransmit(); + + /* + Start retransmission timer. + */ + void rt_start(); - // TODO: Calculate RTO, currently hardcoded to 1 second (1000ms). - */ - void queue_retransmission(TCP::Packet_ptr, size_t rt_attempt = 1); + /* + Stop retransmission timer. + */ + void rt_stop(); + + /* + Restart retransmission timer. + */ + inline void rt_restart() { + rt_stop(); + rt_start(); + } + + /* + Number of retransmission attempts on the packet first in RT-queue + */ + size_t rto_attempt = 0; + + /* + Remove all packets acknowledge by ACK in retransmission queue + */ + void rt_ack_queue(Seq ack); + + /* + Flush the queue (transmit every packet in queue) + */ + void rt_flush(); /* - Measure the elapsed time between sending a data octet with a - particular sequence number and receiving an acknowledgment that - covers that sequence number (segments sent do not have to match - segments received). This measured elapsed time is the Round Trip - Time (RTT). + Delete retransmission queue */ - //std::chrono::milliseconds RTT() const; - std::chrono::milliseconds RTO() const; + void rt_clear(); + + /* + When retransmission times out. + */ + inline void rt_timeout() { + if(rto_attempt++ == 0) + reno_reduce_ssthresh(); + retransmit(); + } + /* Start the time wait timeout for 2*MSL @@ -1576,7 +1700,12 @@ namespace net { /* Show all connections for TCP as a string. */ - std::string status() const; + std::string to_string() const; + + inline std::string status() const + { return to_string(); } + + private: diff --git a/src/net/tcp.cpp b/src/net/tcp.cpp index 609fac7e37..73a25e4f41 100644 --- a/src/net/tcp.cpp +++ b/src/net/tcp.cpp @@ -215,7 +215,7 @@ size_t TCP::send(Connection_ptr conn, Connection::WriteBuffer& buffer) { if(written < buffer.remaining and !conn->is_queued()) { write_queue.push(conn); conn->set_queued(true); - debug2(" %s wrote %u bytes (%u remaining) and is Re-queued.\n", + printf(" %s wrote %u bytes (%u remaining) and is Re-queued.\n", conn->to_string().c_str(), written, buffer.remaining-written); } @@ -230,7 +230,7 @@ size_t TCP::send(Connection_ptr conn, Connection::WriteBuffer& buffer) { TODO: Make sure Recv, Send, In, Out is correct and add them to output. Also, alignment? */ -string TCP::status() const { +string TCP::to_string() const { // Write all connections in a cute list. stringstream ss; ss << "LISTENING SOCKETS:\n"; diff --git a/src/net/tcp_connection.cpp b/src/net/tcp_connection.cpp index b8020b10bd..5dc6eb1ca3 100644 --- a/src/net/tcp_connection.cpp +++ b/src/net/tcp_connection.cpp @@ -25,6 +25,8 @@ using Connection = TCP::Connection; using namespace std; +const TCP::Connection::RTTM::duration_t TCP::Connection::RTTM::CLOCK_G; + /* This is most likely used in a ACTIVE open */ @@ -40,7 +42,7 @@ Connection::Connection(TCP& host, Port local_port, Socket remote) : queued_(false), time_wait_started(0) { - init_cwnd(3); + setup_congestion_control(); } /* @@ -141,12 +143,12 @@ bool Connection::offer(size_t& packets) { return !has_doable_job(); } - +// TODO: This is starting to get complex and ineffective, refactor.. size_t Connection::send(const char* buffer, size_t remaining, size_t& packet_count, bool PUSH) { assert(packet_count && remaining); size_t bytes_written{0}; - while(remaining and packet_count and usable_window() >= MSDS()) { + while(remaining and packet_count and usable_window() >= SMSS()) { // retreive a new packet auto packet = create_outgoing_packet(); // reduce the amount of packets available by one @@ -161,18 +163,20 @@ size_t Connection::send(const char* buffer, size_t remaining, size_t& packet_cou bytes_written += written; remaining -= written; - debug2(" Packet Limit: %u - Written: %u" - " - Remaining: %u - Packet count: %u, Window: %u\n", - packet_limit, written, remaining, packet_count, usable_window()); - // If last packet, add PUSH. - if(!remaining and PUSH) + // TODO: Redefine "push" + if((!remaining or !packet_count or usable_window() < SMSS()) and PUSH) + //if(!remaining and PUSH) packet->set_flag(PSH); // Advance outgoing sequence number (SND.NXT) with the length of the data. control_block.SND.NXT += packet->data_length(); // TODO: Replace with chaining transmit(packet); + + debug2(" Packet Limit: %u - Written: %u" + " - Remaining: %u - Packet count: %u, Window: %u\n", + packet_limit, written, remaining, packet_count, usable_window()); } debug(" Sent %u bytes of data\n", bytes_written); return bytes_written; @@ -251,6 +255,7 @@ void Connection::segment_arrived(TCP::Packet_ptr incoming) { } case State::CLOSED: { debug(" State handle finished with CLOSED. We're done, ask host() to delete the connection. \n"); + rt_clear(); signal_close(); break; }; @@ -294,55 +299,163 @@ TCP::Packet_ptr Connection::create_outgoing_packet() { void Connection::transmit(TCP::Packet_ptr packet) { debug(" Transmitting: %s \n", packet->to_string().c_str()); - host_.transmit(packet); - // Don't think we would like to retransmit reset packets..? - //if(!packet->isset(RST)) - // queue_retransmission(packet); -} + if(!rttm.active) { + //printf(" Starting RTT measurement.\n"); + rttm.start(); + } -void Connection::retransmit(TCP::Packet_ptr packet) { - debug(" Retransmitting: %s \n", packet->to_string().c_str()); host_.transmit(packet); + if(packet->has_data()) + retransq.push_back(packet); + if(!rt_timer.active) + rt_start(); } -void Connection::queue_retransmission(TCP::Packet_ptr packet, size_t rt_attempt) { - const size_t ATTEMPT_LIMIT = 3; - auto self = shared_from_this(); - - if(rt_attempt <= ATTEMPT_LIMIT) { - hw::PIT::instance().onTimeout(RTO() * rt_attempt, - [packet, self, rt_attempt] - { - // Packet hasnt been ACKed. - if(packet->seq() > self->tcb().SND.UNA) { - debug(" Packet unacknowledge, retransmitting...\n"); - if(rt_attempt == 1) - self->segment_loss_detected(); - //packet->set_ack(self->tcb().RCV.NXT); - self->retransmit(packet); - self->queue_retransmission(packet, rt_attempt+1); - } else { - debug2(" Packet acknowledged %s \n", packet->to_string().c_str()); - // Signal user? - } - }); + +/* + As specified in [RFC3390], the SYN/ACK and the acknowledgment of the + SYN/ACK MUST NOT increase the size of the congestion window. + Further, if the SYN or SYN/ACK is lost, the initial window used by a + sender after a correctly transmitted SYN MUST be one segment + consisting of at most SMSS bytes. +*/ +void Connection::acknowledge(Seq ACK) { + DUP_ACK = 0; + size_t bytes_acked = ACK - control_block.SND.UNA; + control_block.SND.UNA = ACK; + + if(reno_slow_start()) { + reno_increase_cwnd(bytes_acked); + debug2(" Slow start - cwnd increased: %u\n", + control_block.SND.cwnd); } + // congestion avoidance else { - printf(" Give up already... Already tried %u times. Time to kill connection?\n", - ATTEMPT_LIMIT); + if(rttm.active) { + reno_increase_cwnd(bytes_acked); + debug2(" Congestion avoidance - cwnd increased: %u\n", + control_block.SND.cwnd); + } + } + + if(rttm.active) + rttm.stop(); + + rt_ack_queue(ACK); +} + +/* + [RFC 6298] + + (5.2) When all outstanding data has been acknowledged, turn off the + retransmission timer. + + (5.3) When an ACK is received that acknowledges new data, restart the + retransmission timer so that it will expire after RTO seconds + (for the current value of RTO). +*/ +void Connection::rt_ack_queue(Seq ack) { + auto x = retransq.size(); + while(!retransq.empty()) { + if(retransq.front()->is_acked_by(ack)) + retransq.pop_front(); + else + break; + } + /* + When all outstanding data has been acknowledged, turn off the + retransmission timer. + */ + if(retransq.empty() and rt_timer.active) { + rt_stop(); + } + /* + When an ACK is received that acknowledges new data, restart the + retransmission timer so that it will expire after RTO seconds + (for the current value of RTO). + */ + else if(x - retransq.size() > 0) { + rto_attempt = 0; + rt_restart(); + } + //printf(" ACK'ed %u packets. Retransq: %u\n", + // x-retransq.size(), retransq.size()); +} + +/* + When the retransmission timer expires, do the following: + + (5.4) Retransmit the earliest segment that has not been acknowledged + by the TCP receiver. + + (5.5) The host MUST set RTO <- RTO * 2 ("back off the timer"). The + maximum value discussed in (2.5) above may be used to provide + an upper bound to this doubling operation. + + (5.6) Start the retransmission timer, such that it expires after RTO + seconds (for the value of RTO after the doubling operation + outlined in 5.5). + + (5.7) If the timer expires awaiting the ACK of a SYN segment and the + TCP implementation is using an RTO less than 3 seconds, the RTO + MUST be re-initialized to 3 seconds when data transmission + begins (i.e., after the three-way handshake completes). +*/ +void Connection::retransmit() { + if(retransq.empty()) + return; + auto packet = retransq.front(); + printf(" Retransmitting: %s \n", packet->to_string().c_str()); + host_.transmit(packet); + /* + Every time a packet containing data is sent (including a + retransmission), if the timer is not running, start it running + so that it will expire after RTO seconds (for the current value + of RTO). + */ + if(!packet->isset(SYN)) { + rttm.RTO *= 2; + } else { + rttm.RTO = 3.0; } - debug2(" Packet queued for retransmission [%u] \n", retransmit_try); + if(!rt_timer.active) + rt_start(); + else + rt_restart(); } -size_t Connection::duplicate_ack(TCP::Seq ack) { - // if its another duplicate, increment count - if(dup_acks_.ACK == ack) - return ++dup_acks_.count; +void Connection::rt_start() { + assert(!rt_timer.active); + auto i = rt_timer.i; + rt_timer.iter = hw::PIT::instance().on_timeout(rttm.RTO, + [this, i] + { + rt_timer.active = false; + printf(" %s Timed out. rt_q: %u, i: %u rt_i: %u\n", + to_string().c_str(), retransq.size(), i, rt_timer.i); + rt_timeout(); + }); + rt_timer.i++; + rt_timer.active = true; +} + +void Connection::rt_stop() { + assert(rt_timer.active); + hw::PIT::instance().stop_timer(rt_timer.iter); + rt_timer.active = false; +} + +void Connection::rt_flush() { + while(!retransq.empty()) { + host_.transmit(retransq.front()); + retransq.pop_front(); + } +} - // if not, set the new ack and set count to 1 - dup_acks_.ACK = ack; - dup_acks_.count = 1; - return dup_acks_.count; +void Connection::rt_clear() { + if(rt_timer.active) + rt_stop(); + retransq.clear(); } TCP::Seq Connection::generate_iss() { diff --git a/src/net/tcp_connection_states.cpp b/src/net/tcp_connection_states.cpp index 074b0af77e..2037a24351 100644 --- a/src/net/tcp_connection_states.cpp +++ b/src/net/tcp_connection_states.cpp @@ -212,24 +212,68 @@ bool Connection::State::check_ack(Connection& tcp, TCP::Packet_ptr in) { drop the segment, and return. */ // Correction: [RFC 1122 p. 94] - if( tcb.SND.UNA < in->ack() and in->ack() <= tcb.SND.NXT ) { - tcb.SND.UNA = in->ack(); + // ACK is inside sequence space + if(in->ack() <= tcb.SND.NXT ) { + // this is a "new" ACK + if(tcb.SND.UNA <= in->ack()) { + /* + If SND.UNA =< SEG.ACK =< SND.NXT, the send window should be + updated. If (SND.WL1 < SEG.SEQ or (SND.WL1 = SEG.SEQ and + SND.WL2 =< SEG.ACK)), set SND.WND <- SEG.WND, set + SND.WL1 <- SEG.SEQ, and set SND.WL2 <- SEG.ACK. + */ + if( tcb.SND.WL1 < in->seq() or ( tcb.SND.WL1 = in->seq() and tcb.SND.WL2 <= in->ack() ) ) { + tcb.SND.WND = in->win(); + tcb.SND.WL1 = in->seq(); + tcb.SND.WL2 = in->ack(); + debug2(" Send window updated: %u \n", tcb.SND.WND); + } + + // this is a NEW ACK + if(tcb.SND.UNA < in->ack()) + { + tcp.acknowledge(in->ack()); + } + // [RFC 5681] + /* + DUPLICATE ACKNOWLEDGMENT: + An acknowledgment is considered a + "duplicate" in the following algorithms when + (a) the receiver of the ACK has outstanding data + (b) the incoming acknowledgment carries no data + (c) the SYN and FIN bits are both off + (d) the acknowledgment number is equal to the greatest acknowledgment + received on the given connection (TCP.UNA from [RFC793]) and + (e) the advertised window in the incoming acknowledgment equals the + advertised window in the last incoming acknowledgment. + + Note that a sender using SACK [RFC2018] MUST NOT send + new data unless the incoming duplicate acknowledgment contains + new SACK information. + */ + // this is a RFC 5681 DUP ACK + //!in->isset(FIN) and !in->isset(SYN) + else if(tcp.reno_is_dup_ack(in)) { + debug2(" Reno Dup ACK %u\n", in->ack()); + tcp.reno_dup_ack(); + } + // this is an RFC 793 DUP ACK + else { + //printf(" RFC 793 Dup ACK %u\n", in->ack()); + } + + + } + // this is an ACK out of order + else { + + } + debug2(" Usable window slided (%i) %u\n", tcp.usable_window(), tcb.SND.cwnd); // tcp.signal_sent(); // return that buffer has been SENT - currently no support to receipt sent buffer. - /* - If SND.UNA < SEG.ACK =< SND.NXT, the send window should be - updated. If (SND.WL1 < SEG.SEQ or (SND.WL1 = SEG.SEQ and - SND.WL2 =< SEG.ACK)), set SND.WND <- SEG.WND, set - SND.WL1 <- SEG.SEQ, and set SND.WL2 <- SEG.ACK. - */ - if( tcb.SND.WL1 < in->seq() or ( tcb.SND.WL1 = in->seq() and tcb.SND.WL2 <= in->ack() ) ) { - tcb.SND.WND = in->win(); - tcb.SND.WL1 = in->seq(); - tcb.SND.WL2 = in->ack(); - debug2(" Send window updated: %u \n", tcb.SND.WND); - } + /* Note that SND.WND is an offset from SND.UNA, that SND.WL1 records the sequence number of the last segment used to update @@ -237,27 +281,10 @@ bool Connection::State::check_ack(Connection& tcp, TCP::Packet_ptr in) { the last segment used to update SND.WND. The check here prevents using old segments to update the window. */ - } - /* If the ACK is a duplicate (SEG.ACK < SND.UNA), it can be ignored. */ - // Correction: [RFC 1122 p. 94] - else if( in->ack() <= tcb.SND.UNA ) { - debug2(" Dup ACK.\n"); - // [RFC 5681] - /* - Note that a sender using SACK [RFC2018] MUST NOT send - new data unless the incoming duplicate acknowledgment contains - new SACK information. - */ - auto dup_count = tcp.duplicate_ack(in->ack()); - if(dup_count == 3) { - debug(" Duplicate ACK strike! (>= 3)\n"); - tcp.reduce_slow_start_threshold(); - } else if(dup_count > 3) { - tcb.SND.cwnd += tcp.SMSS(); - } + } /* If the ACK acks something not yet sent (SEG.ACK > SND.NXT) then send an ACK, drop the segment, and return. */ - else if( in->ack() > tcb.SND.NXT ) { + else { auto packet = tcp.outgoing_packet(); packet->set_flag(ACK); tcp.transmit(packet); @@ -420,6 +447,8 @@ void Connection::State::send_reset(Connection& tcp) { tcp.write_queue_reset(); auto packet = tcp.outgoing_packet(); packet->set_seq(tcp.tcb().SND.NXT).set_ack(0).set_flag(RST); + // flush retransmission queue + tcp.rt_flush(); tcp.transmit(packet); } ///////////////////////////////////////////////////////////////////// @@ -476,6 +505,7 @@ void Connection::Closed::open(Connection& tcp, bool active) { if(!tcp.remote().is_empty()) { auto& tcb = tcp.tcb(); tcb.ISS = tcp.generate_iss(); + tcb.SND.recover = tcb.ISS; // [RFC 6582] auto packet = tcp.outgoing_packet(); packet->set_seq(tcb.ISS).set_flag(SYN); @@ -500,6 +530,7 @@ void Connection::Listen::open(Connection& tcp, bool) { if(!tcp.remote().is_empty()) { auto& tcb = tcp.tcb(); tcb.ISS = tcp.generate_iss(); + tcb.SND.recover = tcb.ISS; // [RFC 6582] auto packet = tcp.outgoing_packet(); packet->set_seq(tcb.ISS).set_flag(SYN); tcb.SND.UNA = tcb.ISS; @@ -776,6 +807,7 @@ State::Result Connection::Listen::handle(Connection& tcp, TCP::Packet_ptr in) { tcb.RCV.NXT = in->seq()+1; tcb.IRS = in->seq(); tcb.ISS = tcp.generate_iss(); + tcb.SND.recover = tcb.ISS; // [RFC 6582] tcb.SND.NXT = tcb.ISS+1; tcb.SND.UNA = tcb.ISS; debug(" Received SYN Packet: %s TCB Updated:\n %s \n", @@ -818,6 +850,9 @@ State::Result Connection::SynSent::handle(Connection& tcp, TCP::Packet_ptr in) { return OK; } // If SND.UNA =< SEG.ACK =< SND.NXT then the ACK is acceptable. + } else { + if(tcp.rttm.active) + tcp.rttm.stop(true); } } @@ -877,6 +912,8 @@ State::Result Connection::SynSent::handle(Connection& tcp, TCP::Packet_ptr in) { tcb.IRS = in->seq(); tcb.SND.UNA = in->ack(); + tcp.rt_ack_queue(in->ack()); + // (our SYN has been ACKed) if(tcb.SND.UNA > tcb.ISS) { tcp.set_state(Connection::Established::instance()); @@ -955,7 +992,7 @@ State::Result Connection::SynReceived::handle(Connection& tcp, TCP::Packet_ptr i and return. */ // Since we create a new connection when it starts listening, we don't wanna do this, but just delete it. - + // TODO: Remove string comparision if(tcp.prev_state().to_string() == Connection::SynSent::instance().to_string()) { tcp.signal_disconnect(Disconnect::REFUSED); } @@ -979,9 +1016,29 @@ State::Result Connection::SynReceived::handle(Connection& tcp, TCP::Packet_ptr i */ if(tcb.SND.UNA <= in->ack() and in->ack() <= tcb.SND.NXT) { debug(" SND.UNA =< SEG.ACK =< SND.NXT, continue in ESTABLISHED. \n"); + if(tcp.rttm.active) + tcp.rttm.stop(true); tcp.set_state(Connection::Established::instance()); + + // Taken from acknowledge (without congestion control) + tcb.SND.UNA = in->ack(); + if(tcp.rttm.active) + tcp.rttm.stop(); + tcp.rt_ack_queue(in->ack()); + + // 7. proccess the segment text + if(in->has_data()) { + process_segment(tcp, in); + } + tcp.signal_connect(); // NOTE: User callback - return tcp.state().handle(tcp,in); // TODO: Fix. This is kinda bad, need to make the above steps again. + + // 8. check FIN bit + if(in->isset(FIN)) { + process_fin(tcp, in); + tcp.set_state(Connection::CloseWait::instance()); + return CLOSE; + } } /* If the segment acknowledgment is not acceptable, form a @@ -1102,6 +1159,7 @@ State::Result Connection::FinWait1::handle(Connection& tcp, TCP::Packet_ptr in) if(in->ack() == tcp.tcb().SND.NXT) { // TODO: I guess or FIN is ACK'ed..? tcp.set_state(TimeWait::instance()); + tcp.rt_stop(); tcp.start_time_wait_timeout(); } else { tcp.set_state(Closing::instance()); @@ -1147,6 +1205,7 @@ State::Result Connection::FinWait2::handle(Connection& tcp, TCP::Packet_ptr in) Start the time-wait timer, turn off the other timers. */ tcp.set_state(Connection::TimeWait::instance()); + tcp.rt_stop(); tcp.start_time_wait_timeout(); } return OK; From ed80618d4bb67aa385a04b2a3e9b01cdf8773a6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=85kesson?= Date: Fri, 15 Apr 2016 15:18:47 +0200 Subject: [PATCH 2/2] messed up merge conflict in f7fc9cd :facepalm: --- src/net/tcp_connection.cpp | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/src/net/tcp_connection.cpp b/src/net/tcp_connection.cpp index b064fea597..3ed5a3075b 100644 --- a/src/net/tcp_connection.cpp +++ b/src/net/tcp_connection.cpp @@ -311,8 +311,6 @@ void Connection::transmit(TCP::Packet_ptr packet) { rt_start(); } -<<<<<<< HEAD - /* As specified in [RFC3390], the SYN/ACK and the acknowledgment of the SYN/ACK MUST NOT increase the size of the congestion window. @@ -471,24 +469,6 @@ void Connection::set_state(State& state) { } -/* - Next compute a Smoothed Round Trip Time (SRTT) as: - - SRTT = ( ALPHA * SRTT ) + ((1-ALPHA) * RTT) - - and based on this, compute the retransmission timeout (RTO) as: - - RTO = min[UBOUND,max[LBOUND,(BETA*SRTT)]] - - where UBOUND is an upper bound on the timeout (e.g., 1 minute), - LBOUND is a lower bound on the timeout (e.g., 1 second), ALPHA is - a smoothing factor (e.g., .8 to .9), and BETA is a delay variance - factor (e.g., 1.3 to 2.0). -*/ -std::chrono::milliseconds Connection::RTO() const { - return 1s; -} - void Connection::start_time_wait_timeout() { debug2(" Time Wait timer started. \n"); time_wait_started = OS::cycles_since_boot();