From 032d9d706521c2b0dc1003364b5fc2cf6316bf4f Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Wed, 13 Mar 2019 23:14:30 +0100 Subject: [PATCH] IPv4: More hacking on bringing up TCP support. This was a bit more complicated than I expected, but it's moving forward. --- Kernel/IPv4Socket.cpp | 134 ++++++++++++++++++++++++++++++++++------ Kernel/IPv4Socket.h | 25 ++++++++ Kernel/NetworkOrdered.h | 2 +- Kernel/NetworkTask.cpp | 15 ++++- Kernel/Process.cpp | 17 +++-- Kernel/TCP.h | 25 ++++++-- Userland/tc.cpp | 8 ++- 7 files changed, 193 insertions(+), 33 deletions(-) diff --git a/Kernel/IPv4Socket.cpp b/Kernel/IPv4Socket.cpp index 9e427fd969..9d3a9b70e6 100644 --- a/Kernel/IPv4Socket.cpp +++ b/Kernel/IPv4Socket.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -97,6 +98,16 @@ KResult IPv4Socket::connect(const sockaddr* address, socklen_t address_size) m_destination_address = IPv4Address((const byte*)&ia.sin_addr.s_addr); m_destination_port = ntohs(ia.sin_port); + if (type() == SOCK_STREAM) { + // FIXME: Figure out the adapter somehow differently. + auto* adapter = NetworkAdapter::from_ipv4_address(IPv4Address(192, 168, 5, 2)); + if (!adapter) + ASSERT_NOT_REACHED(); + send_tcp_packet(*adapter, TCPFlags::SYN); + m_tcp_state = TCPState::Connecting1; + return KSuccess; + } + return KSuccess; } @@ -164,10 +175,77 @@ void IPv4Socket::allocate_source_port_if_needed() } } +struct [[gnu::packed]] TCPPseudoHeader { + IPv4Address source; + IPv4Address destination; + byte zero; + byte protocol; + NetworkOrdered payload_size; +}; + +NetworkOrdered IPv4Socket::compute_tcp_checksum(const IPv4Address& source, const IPv4Address& destination, const TCPPacket& packet, word payload_size) +{ + TCPPseudoHeader pseudo_header { source, destination, 0, (byte)IPv4Protocol::TCP, sizeof(TCPPacket) + payload_size }; + + dword checksum = 0; + auto* w = (const NetworkOrdered*)&pseudo_header; + for (size_t i = 0; i < sizeof(pseudo_header) / sizeof(word); ++i) { + checksum += w[i]; + if (checksum > 0xffff) + checksum = (checksum >> 16) + (checksum & 0xffff); + } + w = (const NetworkOrdered*)&packet; + for (size_t i = 0; i < sizeof(packet) / sizeof(word); ++i) { + checksum += w[i]; + if (checksum > 0xffff) + checksum = (checksum >> 16) + (checksum & 0xffff); + } + ASSERT(packet.data_offset() * 4 == sizeof(TCPPacket)); + w = (const NetworkOrdered*)packet.payload(); + for (size_t i = 0; i < payload_size / sizeof(word); ++i) { + checksum += w[i]; + if (checksum > 0xffff) + checksum = (checksum >> 16) + (checksum & 0xffff); + } + if (payload_size & 1) + ASSERT_NOT_REACHED(); + return ~(checksum & 0xffff); +} + +void IPv4Socket::send_tcp_packet(NetworkAdapter& adapter, word flags, const void* payload, size_t payload_size) +{ + auto buffer = ByteBuffer::create_zeroed(sizeof(TCPPacket) + payload_size); + auto& tcp_packet = *(TCPPacket*)(buffer.pointer()); + tcp_packet.set_source_port(m_source_port); + tcp_packet.set_destination_port(m_destination_port); + tcp_packet.set_window_size(1024); + tcp_packet.set_sequence_number(m_tcp_sequence_number); + tcp_packet.set_data_offset(5); + tcp_packet.set_flags(flags); + + if (flags & TCPFlags::ACK) + tcp_packet.set_ack_number(m_tcp_ack_number); + + if (flags == TCPFlags::SYN) { + ++m_tcp_sequence_number; + } else { + m_tcp_sequence_number += payload_size; + } + + memcpy(tcp_packet.payload(), payload, payload_size); + tcp_packet.set_checksum(compute_tcp_checksum(adapter.ipv4_address(), m_destination_address, tcp_packet, payload_size)); + kprintf("sending tcp packet from %s:%u to %s:%u!\n", + adapter.ipv4_address().to_string().characters(), + source_port(), + m_destination_address.to_string().characters(), + m_destination_port); + adapter.send_ipv4(MACAddress(), m_destination_address, IPv4Protocol::TCP, move(buffer)); +} + ssize_t IPv4Socket::sendto(const void* data, size_t data_length, int flags, const sockaddr* addr, socklen_t addr_length) { (void)flags; - if (addr_length != sizeof(sockaddr_in)) + if (addr && addr_length != sizeof(sockaddr_in)) return -EINVAL; // FIXME: Find the adapter some better way! auto* adapter = NetworkAdapter::from_ipv4_address(IPv4Address(192, 168, 5, 2)); @@ -176,12 +254,12 @@ ssize_t IPv4Socket::sendto(const void* data, size_t data_length, int flags, cons ASSERT_NOT_REACHED(); } - if (addr->sa_family != AF_INET) { - kprintf("sendto: Bad address family: %u is not AF_INET!\n", addr->sa_family); - return -EAFNOSUPPORT; - } - if (addr) { + if (addr->sa_family != AF_INET) { + kprintf("sendto: Bad address family: %u is not AF_INET!\n", addr->sa_family); + return -EAFNOSUPPORT; + } + auto& ia = *(const sockaddr_in*)addr; m_destination_address = IPv4Address((const byte*)&ia.sin_addr.s_addr); m_destination_port = ntohs(ia.sin_port); @@ -191,12 +269,8 @@ ssize_t IPv4Socket::sendto(const void* data, size_t data_length, int flags, cons kprintf("sendto: destination=%s:%u\n", m_destination_address.to_string().characters(), m_destination_port); - // FIXME: If we can't find the right MAC address, block until it's available? - // I feel like this should happen in a layer below this code. - MACAddress mac_address; - if (type() == SOCK_RAW) { - adapter->send_ipv4(mac_address, m_destination_address, (IPv4Protocol)protocol(), ByteBuffer::copy((const byte*)data, data_length)); + adapter->send_ipv4(MACAddress(), m_destination_address, (IPv4Protocol)protocol(), ByteBuffer::copy((const byte*)data, data_length)); return data_length; } @@ -212,7 +286,12 @@ ssize_t IPv4Socket::sendto(const void* data, size_t data_length, int flags, cons source_port(), m_destination_address.to_string().characters(), m_destination_port); - adapter->send_ipv4(mac_address, m_destination_address, IPv4Protocol::UDP, move(buffer)); + adapter->send_ipv4(MACAddress(), m_destination_address, IPv4Protocol::UDP, move(buffer)); + return data_length; + } + + if (type() == SOCK_STREAM) { + send_tcp_packet(*adapter, 0, data, data_length); return data_length; } @@ -222,7 +301,7 @@ ssize_t IPv4Socket::sendto(const void* data, size_t data_length, int flags, cons ssize_t IPv4Socket::recvfrom(void* buffer, size_t buffer_length, int flags, sockaddr* addr, socklen_t* addr_length) { (void)flags; - if (*addr_length < sizeof(sockaddr_in)) + if (addr_length && *addr_length < sizeof(sockaddr_in)) return -EINVAL; #ifdef IPV4_SOCKET_DEBUG @@ -256,10 +335,13 @@ ssize_t IPv4Socket::recvfrom(void* buffer, size_t buffer_length, int flags, sock ASSERT(!packet_buffer.is_null()); auto& ipv4_packet = *(const IPv4Packet*)(packet_buffer.pointer()); - auto& ia = *(sockaddr_in*)addr; - memcpy(&ia.sin_addr, &m_destination_address, sizeof(IPv4Address)); - ia.sin_family = AF_INET; - *addr_length = sizeof(sockaddr_in); + if (addr) { + auto& ia = *(sockaddr_in*)addr; + memcpy(&ia.sin_addr, &m_destination_address, sizeof(IPv4Address)); + ia.sin_family = AF_INET; + ASSERT(addr_length); + *addr_length = sizeof(sockaddr_in); + } if (type() == SOCK_RAW) { ASSERT(buffer_length >= ipv4_packet.payload_size()); @@ -271,11 +353,26 @@ ssize_t IPv4Socket::recvfrom(void* buffer, size_t buffer_length, int flags, sock auto& udp_packet = *static_cast(ipv4_packet.payload()); ASSERT(udp_packet.length() >= sizeof(UDPPacket)); // FIXME: This should be rejected earlier. ASSERT(buffer_length >= (udp_packet.length() - sizeof(UDPPacket))); - ia.sin_port = htons(udp_packet.destination_port()); + if (addr) { + auto& ia = *(sockaddr_in*)addr; + ia.sin_port = htons(udp_packet.destination_port()); + } memcpy(buffer, udp_packet.payload(), udp_packet.length() - sizeof(UDPPacket)); return udp_packet.length() - sizeof(UDPPacket); } + if (type() == SOCK_STREAM) { + auto& tcp_packet = *static_cast(ipv4_packet.payload()); + size_t payload_size = packet_buffer.size() - sizeof(TCPPacket); + ASSERT(buffer_length >= payload_size); + if (addr) { + auto& ia = *(sockaddr_in*)addr; + ia.sin_port = htons(tcp_packet.destination_port()); + } + memcpy(buffer, tcp_packet.payload(), payload_size); + return payload_size; + } + ASSERT_NOT_REACHED(); } @@ -287,4 +384,5 @@ void IPv4Socket::did_receive(ByteBuffer&& packet) #ifdef IPV4_SOCKET_DEBUG kprintf("IPv4Socket(%p): did_receive %d bytes, packets in queue: %d\n", this, packet.size(), m_receive_queue.size_slow()); #endif + } diff --git a/Kernel/IPv4Socket.h b/Kernel/IPv4Socket.h index 0e15497330..dbfb5c606d 100644 --- a/Kernel/IPv4Socket.h +++ b/Kernel/IPv4Socket.h @@ -7,6 +7,18 @@ #include #include +class NetworkAdapter; +class TCPPacket; + +enum TCPState { + Disconnected, + Connecting1, + Connecting2, + Connected, + Disconnecting1, + Disconnecting2, +}; + class IPv4Socket final : public Socket { public: static Retained create(int type, int protocol); @@ -35,11 +47,20 @@ public: word source_port() const { return m_source_port; } word destination_port() const { return m_destination_port; } + void send_tcp_packet(NetworkAdapter&, word flags, const void* payload = nullptr, size_t = 0); + void set_tcp_state(TCPState state) { m_tcp_state = state; } + TCPState tcp_state() const { return m_tcp_state; } + void set_tcp_ack_number(dword n) { m_tcp_ack_number = n; } + void set_tcp_sequence_number(dword n) { m_tcp_sequence_number = n; } + dword tcp_ack_number() const { return m_tcp_ack_number; } + dword tcp_sequence_number() const { return m_tcp_sequence_number; } + private: IPv4Socket(int type, int protocol); virtual bool is_ipv4() const override { return true; } void allocate_source_port_if_needed(); + NetworkOrdered compute_tcp_checksum(const IPv4Address& source, const IPv4Address& destination, const TCPPacket&, word payload_size); bool m_bound { false }; int m_attached_fds { 0 }; @@ -53,6 +74,10 @@ private: word m_source_port { 0 }; word m_destination_port { 0 }; + dword m_tcp_sequence_number { 0 }; + dword m_tcp_ack_number { 0 }; + TCPState m_tcp_state { Disconnected }; + Lock m_lock; bool m_can_read { false }; }; diff --git a/Kernel/NetworkOrdered.h b/Kernel/NetworkOrdered.h index 4905a82b99..b5dc404103 100644 --- a/Kernel/NetworkOrdered.h +++ b/Kernel/NetworkOrdered.h @@ -44,5 +44,5 @@ public: operator T() const { return convert_between_host_and_network(m_network_value); } private: - T m_network_value; + T m_network_value { 0 }; }; diff --git a/Kernel/NetworkTask.cpp b/Kernel/NetworkTask.cpp index 8382fd8ca8..9902bd8777 100644 --- a/Kernel/NetworkTask.cpp +++ b/Kernel/NetworkTask.cpp @@ -263,7 +263,7 @@ void handle_tcp(const EthernetFrameHeader& eth, int frame_size) auto& tcp_packet = *static_cast(ipv4_packet.payload()); #ifdef TCP_DEBUG - kprintf("handle_tcp: source=%s:%u, destination=%s:%u seq=%u, ack=%u, flags=%w, window_size=%u\n", + kprintf("handle_tcp: source=%s:%u, destination=%s:%u seq_no=%u, ack_no=%u, flags=%w (%s %s), window_size=%u\n", ipv4_packet.source().to_string().characters(), tcp_packet.source_port(), ipv4_packet.destination().to_string().characters(), @@ -271,6 +271,8 @@ void handle_tcp(const EthernetFrameHeader& eth, int frame_size) tcp_packet.sequence_number(), tcp_packet.ack_number(), tcp_packet.flags(), + tcp_packet.has_syn() ? "SYN" : "", + tcp_packet.has_ack() ? "ACK" : "", tcp_packet.window_size() ); #endif @@ -288,5 +290,16 @@ void handle_tcp(const EthernetFrameHeader& eth, int frame_size) LOCKER(socket->lock()); ASSERT(socket->type() == SOCK_STREAM); ASSERT(socket->source_port() == tcp_packet.destination_port()); + + size_t payload_size = ipv4_packet.payload_size() - sizeof(TCPPacket); + + if (tcp_packet.has_syn() && tcp_packet.has_ack()) { + socket->set_tcp_ack_number(socket->tcp_sequence_number() + payload_size + 1); + socket->send_tcp_packet(*adapter, TCPFlags::ACK); + kprintf("Connected!\n"); + socket->set_tcp_state(Connected); + return; + } + socket->did_receive(ByteBuffer::copy((const byte*)&ipv4_packet, sizeof(IPv4Packet) + ipv4_packet.payload_size())); } diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index c749d06c98..2a5883ebdc 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -1073,6 +1073,7 @@ void Process::crash() { ASSERT_INTERRUPTS_DISABLED(); ASSERT(state() != Dead); + m_termination_signal = SIGSEGV; dump_regions(); ASSERT(is_ring3()); @@ -2533,7 +2534,7 @@ ssize_t Process::sys$sendto(const Syscall::SC_sendto_params* params) if (!validate_read(data, data_length)) return -EFAULT; - if (!validate_read(addr, addr_length)) + if (addr && !validate_read(addr, addr_length)) return -EFAULT; auto* descriptor = file_descriptor(sockfd); if (!descriptor) @@ -2559,17 +2560,21 @@ ssize_t Process::sys$recvfrom(const Syscall::SC_recvfrom_params* params) if (!validate_write(buffer, buffer_length)) return -EFAULT; - if (!validate_read_typed(addr_length)) - return -EFAULT; - if (!validate_read(addr, *addr_length)) - return -EFAULT; + if (addr_length) { + if (!validate_read_typed(addr_length)) + return -EFAULT; + if (!validate_read(addr, *addr_length)) + return -EFAULT; + } else if (addr) { + return -EINVAL; + } auto* descriptor = file_descriptor(sockfd); if (!descriptor) return -EBADF; if (!descriptor->is_socket()) return -ENOTSOCK; auto& socket = *descriptor->socket(); - kprintf("recvfrom %p (%u), flags=%u, addr: %p (%u)\n", buffer, buffer_length, flags, addr, *addr_length); + kprintf("recvfrom %p (%u), flags=%u, addr: %p (%p)\n", buffer, buffer_length, flags, addr, addr_length); return socket.recvfrom(buffer, buffer_length, flags, addr, addr_length); } diff --git a/Kernel/TCP.h b/Kernel/TCP.h index 7bc0a6dd26..6552702c07 100644 --- a/Kernel/TCP.h +++ b/Kernel/TCP.h @@ -2,6 +2,17 @@ #include +struct TCPFlags { +enum : word { + FIN = 0x01, + SYN = 0x02, + RST = 0x04, + PUSH = 0x08, + ACK = 0x10, + URG = 0x20 +}; +}; + class [[gnu::packed]] TCPPacket { public: TCPPacket() { } @@ -19,8 +30,14 @@ public: dword ack_number() const { return m_ack_number; } void set_ack_number(dword number) { m_ack_number = number; } - word flags() const { return m_flags; } - void set_flags(word flags) { m_flags = flags; } + word flags() const { return m_flags_and_data_offset & 0x1ff; } + void set_flags(word flags) { m_flags_and_data_offset = (m_flags_and_data_offset & ~0x1ff) | (flags & 0x1ff); } + + bool has_syn() const { return flags() & TCPFlags::SYN; } + bool has_ack() const { return flags() & TCPFlags::ACK; } + + byte data_offset() const { return (m_flags_and_data_offset & 0xf000) >> 12; } + void set_data_offset(word data_offset) { m_flags_and_data_offset = (m_flags_and_data_offset & ~0xf000) | data_offset << 12; } word window_size() const { return m_window_size; } void set_window_size(word window_size) { m_window_size = window_size; } @@ -40,10 +57,10 @@ private: NetworkOrdered m_sequence_number; NetworkOrdered m_ack_number; - NetworkOrdered m_flags; + NetworkOrdered m_flags_and_data_offset; NetworkOrdered m_window_size; NetworkOrdered m_checksum; NetworkOrdered m_urgent; }; -static_assert(sizeof(UDPPacket) == 8); +static_assert(sizeof(TCPPacket) == 20); diff --git a/Userland/tc.cpp b/Userland/tc.cpp index 711b4fc3f1..a89bd37e1d 100644 --- a/Userland/tc.cpp +++ b/Userland/tc.cpp @@ -51,11 +51,13 @@ int main(int argc, char** argv) char buffer[BUFSIZ]; const char* msg = "Test message"; - send(fd, (const char*)msg, strlen(msg), 0); + rc = send(fd, (const char*)msg, strlen(msg), 0); + if (rc < 0) { + perror("send"); + return 1; + } printf("Message sent.\n"); - struct sockaddr_in src_addr; - socklen_t src_addr_len = sizeof(src_addr); ssize_t nrecv = recv(fd, buffer, sizeof(buffer), 0); if (nrecv < 0) { perror("recvfrom");