diff --git a/net/tcp.hh b/net/tcp.hh index b79b6494e9..dea3dd0506 100644 --- a/net/tcp.hh +++ b/net/tcp.hh @@ -160,6 +160,7 @@ private: }; struct connid_hash; class tcb { + using clock_type = std::chrono::high_resolution_clock; // Instead of tracking state through an enum, track individual // bits of the state. This reduces duplication in state handling. bool _local_syn_sent = false; @@ -174,6 +175,11 @@ private: ipaddr _foreign_ip; uint16_t _local_port; uint16_t _foreign_port; + struct unacked_packet { + packet p; + clock_type::time_point tx_time; + unsigned nr_transmits; + }; struct send { tcp_seq unacknowledged; tcp_seq next; @@ -184,7 +190,7 @@ private: tcp_seq wl1; tcp_seq wl2; tcp_seq initial; - std::deque data; + std::deque data; std::deque unsent; uint32_t unsent_len = 0; bool closed = false; @@ -204,6 +210,9 @@ private: } _rcv; tcp_option _option; timer _delayed_ack; + static constexpr std::chrono::milliseconds _retransmit_timeout{2000}; + static constexpr uint16_t _max_nr_retransmit{5}; + timer _retransmit; public: tcb(tcp& t, connid id); void input(tcp_hdr* th, packet p); @@ -224,6 +233,8 @@ private: void trim_receive_data_after_window(); bool should_send_ack(); void clear_delayed_ack(); + void retransmit(); + void cleanup(); packet get_transmit_packet(); friend class connection; }; @@ -393,6 +404,7 @@ tcp::tcb::tcb(tcp& t, connid id) , _local_port(id.local_port) , _foreign_port(id.foreign_port) { _delayed_ack.set_callback([this] { output(); }); + _retransmit.set_callback([this] { retransmit(); }); } template @@ -526,8 +538,7 @@ void tcp::tcb::input(tcp_hdr* th, packet p) { // FIXME: Implement TIME-WAIT state if (both_closed()) { - clear_delayed_ack(); - remove_from_tcbs(); + cleanup(); return; } } @@ -553,12 +564,12 @@ void tcp::tcb::input(tcp_hdr* th, packet p) { auto data_ack = th->ack - th->f_fin; if (data_ack > _snd.unacknowledged && data_ack <= _snd.next) { while (!_snd.data.empty() - && (_snd.unacknowledged + _snd.data.front().len() <= data_ack)) { - _snd.unacknowledged += _snd.data.front().len(); + && (_snd.unacknowledged + _snd.data.front().p.len() <= data_ack)) { + _snd.unacknowledged += _snd.data.front().p.len(); _snd.data.pop_front(); } if (_snd.unacknowledged < data_ack) { - _snd.data.front().trim_front(data_ack - _snd.unacknowledged); + _snd.data.front().p.trim_front(data_ack - _snd.unacknowledged); _snd.unacknowledged = data_ack; } } @@ -567,8 +578,7 @@ void tcp::tcb::input(tcp_hdr* th, packet p) { _snd.unacknowledged += 1; _snd.next += 1; if (both_closed()) { - clear_delayed_ack(); - remove_from_tcbs(); + cleanup(); return; } } @@ -645,9 +655,6 @@ void tcp::tcb::output() { uint8_t options_size = 0; packet p = get_transmit_packet(); auto len = p.len(); - if (len) { - _snd.data.push_back(p.share()); - } if (!_local_syn_acked) { options_size = _option.get_size(); @@ -697,6 +704,12 @@ void tcp::tcb::output() { oi.tcp_hdr_len = sizeof(tcp_hdr) + options_size; p.set_offload_info(oi); + if (len) { + if (!_retransmit.armed()) { + _retransmit.arm_periodic(_retransmit_timeout * 2); + } + _snd.data.push_back({p.share(), clock_type::now(), 0}); + } _tcp.send(_local_ip, _foreign_ip, std::move(p)); } @@ -772,6 +785,30 @@ void tcp::tcb::trim_receive_data_after_window() { abort(); } +template +void tcp::tcb::retransmit() { + using namespace std::chrono; + // If there are unacked data, retransmit them all + if (!_snd.data.empty()) { + for (auto&& x : _snd.data) { + auto time_span = duration_cast(high_resolution_clock::now() - x.tx_time); + // TODO: Delete connection when max num of retransmission is reached. + if (time_span > _retransmit_timeout && x.nr_transmits < _max_nr_retransmit) { + x.nr_transmits++; + _tcp.send(_local_ip, _foreign_ip, x.p.share()); + } + } + } +} + +template +void tcp::tcb::cleanup() { + _snd.data.clear(); + _retransmit.cancel(); + clear_delayed_ack(); + remove_from_tcbs(); +} + template void tcp::connection::close_read() { } @@ -781,6 +818,13 @@ void tcp::connection::close_write() { _tcb->close(); } +template +constexpr std::chrono::milliseconds tcp::tcb::_retransmit_timeout; + +template +constexpr uint16_t tcp::tcb::_max_nr_retransmit; + + }