summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Kernel/Net/NetworkTask.cpp19
-rw-r--r--Kernel/Net/TCPSocket.cpp83
-rw-r--r--Kernel/Net/TCPSocket.h15
3 files changed, 101 insertions, 16 deletions
diff --git a/Kernel/Net/NetworkTask.cpp b/Kernel/Net/NetworkTask.cpp
index 1820b79028..7e95cf3f4f 100644
--- a/Kernel/Net/NetworkTask.cpp
+++ b/Kernel/Net/NetworkTask.cpp
@@ -30,6 +30,7 @@ static void handle_udp(const IPv4Packet&, const Time& packet_timestamp);
static void handle_tcp(const IPv4Packet&, const Time& packet_timestamp);
static void send_delayed_tcp_ack(RefPtr<TCPSocket> socket);
static void flush_delayed_tcp_acks();
+static void retransmit_tcp_packets();
static Thread* network_task = nullptr;
static HashTable<RefPtr<TCPSocket>>* delayed_ack_sockets;
@@ -90,6 +91,7 @@ void NetworkTask_main(void*)
for (;;) {
flush_delayed_tcp_acks();
+ retransmit_tcp_packets();
size_t packet_size = dequeue_packet(buffer, buffer_size, packet_timestamp);
if (!packet_size) {
auto timeout_time = Time::from_milliseconds(500);
@@ -606,4 +608,21 @@ void handle_tcp(const IPv4Packet& ipv4_packet, const Time& packet_timestamp)
}
}
+void retransmit_tcp_packets()
+{
+ // We must keep the sockets alive until after we've unlocked the hash table
+ // in case retransmit_packets() realizes that it wants to close the socket.
+ NonnullRefPtrVector<TCPSocket, 16> sockets;
+ {
+ Locker locker(TCPSocket::sockets_for_retransmit().lock(), LockMode::Shared);
+ for (auto& socket : TCPSocket::sockets_for_retransmit().resource())
+ sockets.append(*socket);
+ }
+
+ for (auto& socket : sockets) {
+ Locker socket_locker(socket.lock());
+ socket.retransmit_packets();
+ }
+}
+
}
diff --git a/Kernel/Net/TCPSocket.cpp b/Kernel/Net/TCPSocket.cpp
index 00c89f6a32..26422ecec3 100644
--- a/Kernel/Net/TCPSocket.cpp
+++ b/Kernel/Net/TCPSocket.cpp
@@ -132,6 +132,7 @@ void TCPSocket::release_for_accept(RefPtr<TCPSocket> socket)
TCPSocket::TCPSocket(int protocol)
: IPv4Socket(SOCK_STREAM, protocol)
{
+ m_last_retransmit_time = kgettimeofday();
}
TCPSocket::~TCPSocket()
@@ -139,6 +140,8 @@ TCPSocket::~TCPSocket()
Locker locker(sockets_by_tuple().lock());
sockets_by_tuple().resource().remove(tuple());
+ dequeue_for_retransmit();
+
dbgln_if(TCP_SOCKET_DEBUG, "~TCPSocket in state {}", to_string(state()));
}
@@ -221,13 +224,6 @@ KResult TCPSocket::send_tcp_packet(u16 flags, const UserOrKernelBuffer* payload,
tcp_packet.set_checksum(compute_tcp_checksum(local_address(), peer_address(), tcp_packet, payload_size));
- if (tcp_packet.has_syn() || payload_size > 0) {
- Locker locker(m_not_acked_lock);
- m_not_acked.append({ m_sequence_number, move(buffer) });
- send_outgoing_packets(routing_decision);
- return KSuccess;
- }
-
auto packet_buffer = UserOrKernelBuffer::for_kernel_buffer(buffer.data());
auto result = routing_decision.adapter->send_ipv4(
local_address(), routing_decision.next_hop, peer_address(), IPv4Protocol::TCP,
@@ -237,19 +233,23 @@ KResult TCPSocket::send_tcp_packet(u16 flags, const UserOrKernelBuffer* payload,
m_packets_out++;
m_bytes_out += buffer_size;
+ if (tcp_packet.has_syn() || payload_size > 0) {
+ Locker locker(m_not_acked_lock);
+ m_not_acked.append({ m_sequence_number, move(buffer) });
+ enqueue_for_retransmit();
+ }
+
return KSuccess;
}
-void TCPSocket::send_outgoing_packets(RoutingDecision& routing_decision)
+void TCPSocket::do_retransmit_packets()
{
- auto now = kgettimeofday();
+ auto routing_decision = route_to(peer_address(), local_address(), bound_interface());
+ if (routing_decision.is_zero())
+ return;
Locker locker(m_not_acked_lock, Lock::Mode::Shared);
for (auto& packet : m_not_acked) {
- auto diff = now - packet.tx_time;
- if (diff <= Time::from_nanoseconds(500'000'000))
- continue;
- packet.tx_time = now;
packet.tx_counter++;
if constexpr (TCP_SOCKET_DEBUG) {
@@ -314,6 +314,11 @@ void TCPSocket::receive_tcp_packet(const TCPPacket& packet, u16 size)
}
}
+ if (m_not_acked.is_empty()) {
+ m_retransmit_attempts = 0;
+ dequeue_for_retransmit();
+ }
+
dbgln_if(TCP_SOCKET_DEBUG, "TCPSocket: receive_tcp_packet acknowledged {} packets", removed);
}
@@ -436,7 +441,10 @@ KResult TCPSocket::protocol_connect(FileDescription& description, ShouldBlock sh
VERIFY(setup_state() == SetupState::Completed);
if (has_error()) { // TODO: check unblock_flags
m_role = Role::None;
- return ECONNREFUSED;
+ if (error() == TCPSocket::Error::RetransmitTimeout)
+ return ETIMEDOUT;
+ else
+ return ECONNREFUSED;
}
return KSuccess;
}
@@ -514,4 +522,51 @@ KResult TCPSocket::close()
return result;
}
+static AK::Singleton<Lockable<HashTable<TCPSocket*>>> s_sockets_for_retransmit;
+
+Lockable<HashTable<TCPSocket*>>& TCPSocket::sockets_for_retransmit()
+{
+ return *s_sockets_for_retransmit;
+}
+
+void TCPSocket::enqueue_for_retransmit()
+{
+ Locker locker(sockets_for_retransmit().lock());
+ sockets_for_retransmit().resource().set(this);
+}
+
+void TCPSocket::dequeue_for_retransmit()
+{
+ Locker locker(sockets_for_retransmit().lock());
+ sockets_for_retransmit().resource().remove(this);
+}
+
+void TCPSocket::retransmit_packets()
+{
+ auto now = kgettimeofday();
+
+ // RFC6298 says we should have at least one second between retransmits. According to
+ // RFC1122 we must do exponential backoff - even for SYN packets.
+ i64 retransmit_interval = 1;
+ for (decltype(m_retransmit_attempts) i = 0; i < m_retransmit_attempts; i++)
+ retransmit_interval *= 2;
+
+ if (m_last_retransmit_time > now - Time::from_seconds(retransmit_interval))
+ return;
+
+ dbgln_if(TCP_SOCKET_DEBUG, "TCPSocket({}) handling retransmit", this);
+
+ m_last_retransmit_time = now;
+ ++m_retransmit_attempts;
+
+ if (m_retransmit_attempts > maximum_retransmits) {
+ set_state(TCPSocket::State::Closed);
+ set_error(TCPSocket::Error::RetransmitTimeout);
+ set_setup_state(Socket::SetupState::Completed);
+ return;
+ }
+
+ do_retransmit_packets();
+}
+
}
diff --git a/Kernel/Net/TCPSocket.h b/Kernel/Net/TCPSocket.h
index 990cb46e8c..767b2dc405 100644
--- a/Kernel/Net/TCPSocket.h
+++ b/Kernel/Net/TCPSocket.h
@@ -93,6 +93,7 @@ public:
FINDuringConnect,
RSTDuringConnect,
UnexpectedFlagsDuringConnect,
+ RetransmitTimeout,
};
static const char* to_string(Error error)
@@ -136,7 +137,6 @@ public:
KResult send_ack(bool allow_duplicate = false);
KResult send_tcp_packet(u16 flags, const UserOrKernelBuffer* = nullptr, size_t = 0);
- void send_outgoing_packets(RoutingDecision&);
void receive_tcp_packet(const TCPPacket&, u16 size);
bool should_delay_next_ack() const;
@@ -152,6 +152,9 @@ public:
void release_to_originator();
void release_for_accept(RefPtr<TCPSocket>);
+ static Lockable<HashTable<TCPSocket*>>& sockets_for_retransmit();
+ void retransmit_packets();
+
virtual KResult close() override;
protected:
@@ -173,6 +176,10 @@ private:
virtual KResult protocol_bind() override;
virtual KResult protocol_listen() override;
+ void do_retransmit_packets();
+ void enqueue_for_retransmit();
+ void dequeue_for_retransmit();
+
WeakPtr<TCPSocket> m_originator;
HashMap<IPv4SocketTuple, NonnullRefPtr<TCPSocket>> m_pending_release_for_accept;
Direction m_direction { Direction::Unspecified };
@@ -190,7 +197,6 @@ private:
u32 ack_number { 0 };
ByteBuffer buffer;
int tx_counter { 0 };
- Time tx_time {};
};
Lock m_not_acked_lock { "TCPSocket unacked packets" };
@@ -200,6 +206,11 @@ private:
u32 m_last_ack_number_sent { 0 };
Time m_last_ack_sent_time;
+
+ // FIXME: Make this configurable (sysctl)
+ static constexpr u32 maximum_retransmits = 5;
+ Time m_last_retransmit_time;
+ u32 m_retransmit_attempts { 0 };
};
}