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();