summaryrefslogtreecommitdiff
path: root/Kernel
diff options
context:
space:
mode:
authorGunnar Beutner <gbeutner@serenityos.org>2021-05-26 06:26:20 +0200
committerAndreas Kling <kling@serenityos.org>2021-05-26 23:09:28 +0200
commit49dd4e5193635980dc28625c87a627330d8070d5 (patch)
treeaddc8ce5aeef66492120e1664e4af9798632187e /Kernel
parentb436dd138bd5fa9ceecc44493827fd7de4230632 (diff)
downloadserenity-49dd4e5193635980dc28625c87a627330d8070d5.zip
Kernel: Block when writing to TCP sockets when the send window is full
Previously we'd just dump those packets into the network adapter's send queue and hope for the best. Instead we should wait until the peer has sent TCP ACK packets. Ideally this would parse the TCP window size option from the SYN or SYN|ACK packet, but for now we just assume the window size is 64 kB.
Diffstat (limited to 'Kernel')
-rw-r--r--Kernel/Net/TCPSocket.cpp17
-rw-r--r--Kernel/Net/TCPSocket.h8
2 files changed, 24 insertions, 1 deletions
diff --git a/Kernel/Net/TCPSocket.cpp b/Kernel/Net/TCPSocket.cpp
index e6e3150f3b..076e2d2be1 100644
--- a/Kernel/Net/TCPSocket.cpp
+++ b/Kernel/Net/TCPSocket.cpp
@@ -245,6 +245,7 @@ KResult TCPSocket::send_tcp_packet(u16 flags, const UserOrKernelBuffer* payload,
if (tcp_packet.has_syn() || payload_size > 0) {
Locker locker(m_not_acked_lock);
m_not_acked.append({ m_sequence_number, move(packet), ipv4_payload_offset, *routing_decision.adapter });
+ m_not_acked_size += payload_size;
enqueue_for_retransmit();
}
@@ -269,6 +270,10 @@ void TCPSocket::receive_tcp_packet(const TCPPacket& packet, u16 size)
auto old_adapter = packet.adapter.strong_ref();
if (old_adapter)
old_adapter->release_packet_buffer(*packet.buffer);
+ TCPPacket& tcp_packet = *(TCPPacket*)(packet.buffer->buffer.data() + packet.ipv4_payload_offset);
+ auto payload_size = packet.buffer->buffer.data() + packet.buffer->buffer.size() - (u8*)tcp_packet.payload();
+ m_not_acked_size -= payload_size;
+ evaluate_block_conditions();
m_not_acked.take_first();
removed++;
} else {
@@ -566,4 +571,16 @@ void TCPSocket::retransmit_packets()
}
}
+bool TCPSocket::can_write(const FileDescription& file_description, size_t size) const
+{
+ if (!IPv4Socket::can_write(file_description, size))
+ return false;
+
+ if (!file_description.is_blocking())
+ return true;
+
+ Locker lock(m_not_acked_lock);
+ return m_not_acked_size + size <= m_send_window_size;
+}
+
}
diff --git a/Kernel/Net/TCPSocket.h b/Kernel/Net/TCPSocket.h
index 7dab47d57f..c3ac398301 100644
--- a/Kernel/Net/TCPSocket.h
+++ b/Kernel/Net/TCPSocket.h
@@ -157,6 +157,8 @@ public:
virtual KResult close() override;
+ virtual bool can_write(const FileDescription&, size_t) const override;
+
protected:
void set_direction(Direction direction) { m_direction = direction; }
@@ -200,8 +202,9 @@ private:
int tx_counter { 0 };
};
- Lock m_not_acked_lock { "TCPSocket unacked packets" };
+ mutable Lock m_not_acked_lock { "TCPSocket unacked packets" };
SinglyLinkedList<OutgoingPacket> m_not_acked;
+ size_t m_not_acked_size { 0 };
u32 m_duplicate_acks { 0 };
@@ -212,6 +215,9 @@ private:
static constexpr u32 maximum_retransmits = 5;
Time m_last_retransmit_time;
u32 m_retransmit_attempts { 0 };
+
+ // FIXME: Parse window size TCP option from the peer
+ u32 m_send_window_size { 64 * KiB };
};
}