From aff4d22de9dc3cfe3fca66c9d3131872adef4466 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Tue, 11 May 2021 21:09:11 +0200 Subject: [PATCH] Kernel: Set MSS option for outbound TCP SYN packets When the MSS option header is missing the default maximum segment size is 536 which results in lots of very small TCP packets that NetworkTask has to handle. This adds the MSS option header to outbound TCP SYN packets and sets it to an appropriate value depending on the interface's MTU. Note that we do not currently do path MTU discovery so this could cause problems when hops don't fragment packets properly. --- Kernel/Net/TCP.h | 17 +++++++++++++++++ Kernel/Net/TCPSocket.cpp | 37 ++++++++++++++++++++++--------------- Kernel/Net/TCPSocket.h | 2 +- 3 files changed, 40 insertions(+), 16 deletions(-) diff --git a/Kernel/Net/TCP.h b/Kernel/Net/TCP.h index 005825cd0b0..d3a85d8f8c2 100644 --- a/Kernel/Net/TCP.h +++ b/Kernel/Net/TCP.h @@ -21,6 +21,23 @@ struct TCPFlags { }; }; +class [[gnu::packed]] TCPOptionMSS { +public: + TCPOptionMSS(u16 value) + : m_value(value) + { + } + + u16 value() const { return m_value; } + +private: + u8 m_option_kind { 0x02 }; + u8 m_option_length { sizeof(TCPOptionMSS) }; + NetworkOrdered m_value; +}; + +static_assert(sizeof(TCPOptionMSS) == 4); + class [[gnu::packed]] TCPPacket { public: TCPPacket() = default; diff --git a/Kernel/Net/TCPSocket.cpp b/Kernel/Net/TCPSocket.cpp index fa78211056f..e72604fa231 100644 --- a/Kernel/Net/TCPSocket.cpp +++ b/Kernel/Net/TCPSocket.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -168,7 +169,10 @@ KResultOr TCPSocket::protocol_send(const UserOrKernelBuffer& data, size_ KResult TCPSocket::send_tcp_packet(u16 flags, const UserOrKernelBuffer* payload, size_t payload_size) { - const size_t buffer_size = sizeof(TCPPacket) + payload_size; + const bool has_mss_option = flags == TCPFlags::SYN; + const size_t options_size = has_mss_option ? sizeof(TCPOptionMSS) : 0; + const size_t header_size = sizeof(TCPPacket) + options_size; + const size_t buffer_size = header_size + payload_size; auto buffer = ByteBuffer::create_zeroed(buffer_size); auto& tcp_packet = *(TCPPacket*)(buffer.data()); VERIFY(local_port()); @@ -176,7 +180,7 @@ KResult TCPSocket::send_tcp_packet(u16 flags, const UserOrKernelBuffer* payload, tcp_packet.set_destination_port(peer_port()); tcp_packet.set_window_size(NumericLimits::max()); tcp_packet.set_sequence_number(m_sequence_number); - tcp_packet.set_data_offset(sizeof(TCPPacket) / sizeof(u32)); + tcp_packet.set_data_offset(header_size / sizeof(u32)); tcp_packet.set_flags(flags); if (flags & TCPFlags::ACK) @@ -191,19 +195,26 @@ KResult TCPSocket::send_tcp_packet(u16 flags, const UserOrKernelBuffer* payload, m_sequence_number += payload_size; } + auto routing_decision = route_to(peer_address(), local_address(), bound_interface()); + if (routing_decision.is_zero()) + return EHOSTUNREACH; + + if (has_mss_option) { + u16 mss = routing_decision.adapter->mtu() - sizeof(IPv4Packet) - sizeof(TCPPacket); + TCPOptionMSS mss_option { mss }; + VERIFY(buffer.size() >= sizeof(TCPPacket) + sizeof(mss_option)); + memcpy(buffer.data() + sizeof(TCPPacket), &mss_option, sizeof(mss_option)); + } + 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(); + send_outgoing_packets(routing_decision); return KSuccess; } - auto routing_decision = route_to(peer_address(), local_address(), bound_interface()); - if (routing_decision.is_zero()) - return EHOSTUNREACH; - auto packet_buffer = UserOrKernelBuffer::for_kernel_buffer(buffer.data()); auto result = routing_decision.adapter->send_ipv4( routing_decision.next_hop, peer_address(), IPv4Protocol::TCP, @@ -216,12 +227,8 @@ KResult TCPSocket::send_tcp_packet(u16 flags, const UserOrKernelBuffer* payload, return KSuccess; } -void TCPSocket::send_outgoing_packets() +void TCPSocket::send_outgoing_packets(RoutingDecision& routing_decision) { - auto routing_decision = route_to(peer_address(), local_address(), bound_interface()); - if (routing_decision.is_zero()) - return; - auto now = kgettimeofday(); Locker locker(m_not_acked_lock, Lock::Mode::Shared); @@ -311,7 +318,7 @@ NetworkOrdered TCPSocket::compute_tcp_checksum(const IPv4Address& source, c NetworkOrdered payload_size; }; - PseudoHeader pseudo_header { source, destination, 0, (u8)IPv4Protocol::TCP, sizeof(TCPPacket) + payload_size }; + PseudoHeader pseudo_header { source, destination, 0, (u8)IPv4Protocol::TCP, packet.header_size() + payload_size }; u32 checksum = 0; auto* w = (const NetworkOrdered*)&pseudo_header; @@ -321,12 +328,12 @@ NetworkOrdered TCPSocket::compute_tcp_checksum(const IPv4Address& source, c checksum = (checksum >> 16) + (checksum & 0xffff); } w = (const NetworkOrdered*)&packet; - for (size_t i = 0; i < sizeof(packet) / sizeof(u16); ++i) { + for (size_t i = 0; i < packet.header_size() / sizeof(u16); ++i) { checksum += w[i]; if (checksum > 0xffff) checksum = (checksum >> 16) + (checksum & 0xffff); } - VERIFY(packet.data_offset() * 4 == sizeof(TCPPacket)); + VERIFY(packet.data_offset() * 4 == packet.header_size()); w = (const NetworkOrdered*)packet.payload(); for (size_t i = 0; i < payload_size / sizeof(u16); ++i) { checksum += w[i]; diff --git a/Kernel/Net/TCPSocket.h b/Kernel/Net/TCPSocket.h index b7c8f846ee1..37e03e26fa3 100644 --- a/Kernel/Net/TCPSocket.h +++ b/Kernel/Net/TCPSocket.h @@ -129,7 +129,7 @@ public: u32 bytes_out() const { return m_bytes_out; } KResult send_tcp_packet(u16 flags, const UserOrKernelBuffer* = nullptr, size_t = 0); - void send_outgoing_packets(); + void send_outgoing_packets(RoutingDecision&); void receive_tcp_packet(const TCPPacket&, u16 size); static Lockable>& sockets_by_tuple();