From 9858be636f4f3ad94b0551cf840adb7dd0d5538f Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Tue, 12 Mar 2019 11:44:38 +0100 Subject: Kernel: Fix up the ICMP implementation to generate correct Echo replies. Serenity now responds to ping. :^) --- Kernel/EthernetFrameHeader.h | 2 -- Kernel/ICMP.h | 4 ++-- Kernel/IPv4Packet.h | 52 ++++++++++++++++++++++++++++++++------------ Kernel/NetworkAdapter.cpp | 4 ++-- Kernel/NetworkTask.cpp | 32 ++++++++++++++++++++------- 5 files changed, 66 insertions(+), 28 deletions(-) diff --git a/Kernel/EthernetFrameHeader.h b/Kernel/EthernetFrameHeader.h index 5a079231e2..f6f74b42bb 100644 --- a/Kernel/EthernetFrameHeader.h +++ b/Kernel/EthernetFrameHeader.h @@ -26,7 +26,5 @@ private: dword m_payload[0]; }; -typedef dword EthernetFrameCheckSequence; - static_assert(sizeof(EthernetFrameHeader) == 14); diff --git a/Kernel/ICMP.h b/Kernel/ICMP.h index 6155d431ed..05f4aebe82 100644 --- a/Kernel/ICMP.h +++ b/Kernel/ICMP.h @@ -23,7 +23,7 @@ public: void set_code(byte b) { m_code = b; } word checksum() const { return ntohs(m_checksum); } - void set_checksum(word w) { m_checksum = htons(w); } + void set_checksum(word w) { m_checksum = w; } const void* payload() const { return this + 1; } void* payload() { return this + 1; } @@ -31,7 +31,7 @@ public: private: byte m_type { 0 }; byte m_code { 0 }; - word m_checksum { 0 }; + NetworkOrdered m_checksum { 0 }; // NOTE: The rest of the header is 4 bytes }; diff --git a/Kernel/IPv4Packet.h b/Kernel/IPv4Packet.h index ae7df3e415..2cd167582b 100644 --- a/Kernel/IPv4Packet.h +++ b/Kernel/IPv4Packet.h @@ -2,6 +2,7 @@ #include #include +#include struct IPv4Protocol { enum { @@ -9,6 +10,8 @@ enum { }; }; +NetworkOrdered internet_checksum(const void*, size_t); + class [[gnu::packed]] IPv4Packet { public: byte version() const { return (m_version_and_ihl >> 4) & 0xf; } @@ -17,11 +20,11 @@ public: byte internet_header_length() const { return m_version_and_ihl & 0xf; } void set_internet_header_length(byte ihl) { m_version_and_ihl = (m_version_and_ihl & 0xf0) | (ihl & 0x0f); } - word length() const { return ntohs(m_length); } - void set_length(word length) { m_length = htons(length); } + word length() const { return m_length; } + void set_length(word length) { m_length = length; } - word ident() const { return ntohs(m_ident); } - void set_ident(word ident) { m_ident = htons(ident); } + word ident() const { return m_ident; } + void set_ident(word ident) { m_ident = ident; } byte ttl() const { return m_ttl; } void set_ttl(byte ttl) { m_ttl = ttl; } @@ -29,8 +32,8 @@ public: byte protocol() const { return m_protocol; } void set_protocol(byte protocol) { m_protocol = protocol; } - word checksum() const { return ntohs(m_checksum); } - void set_checksum(word checksum) { m_checksum = htons(checksum); } + word checksum() const { return m_checksum; } + void set_checksum(word checksum) { m_checksum = checksum; } const IPv4Address& source() const { return m_source; } void set_source(const IPv4Address& address) { m_source = address; } @@ -41,17 +44,38 @@ public: void* payload() { return this + 1; } const void* payload() const { return this + 1; } + NetworkOrdered compute_checksum() const + { + ASSERT(!m_checksum); + return internet_checksum(this, sizeof(IPv4Packet)); + } + private: - byte m_version_and_ihl; - byte m_dscp_and_ecn; - word m_length; - word m_ident; - word m_flags_and_fragment; - byte m_ttl; - byte m_protocol; - word m_checksum; + byte m_version_and_ihl { 0 }; + byte m_dscp_and_ecn { 0 }; + NetworkOrdered m_length; + NetworkOrdered m_ident; + NetworkOrdered m_flags_and_fragment; + byte m_ttl { 0 }; + NetworkOrdered m_protocol; + NetworkOrdered m_checksum; IPv4Address m_source; IPv4Address m_destination; }; static_assert(sizeof(IPv4Packet) == 20); + +inline NetworkOrdered internet_checksum(const void* ptr, size_t count) +{ + dword checksum = 0; + auto* w = (const word*)ptr; + while (count > 1) { + checksum += convert_between_host_and_network(*w++); + if (checksum & 0x80000000) + checksum = (checksum & 0xffff) | (checksum >> 16); + count -= 2; + } + while (checksum >> 16) + checksum = (checksum & 0xffff) + (checksum >> 16); + return ~checksum & 0xffff; +} diff --git a/Kernel/NetworkAdapter.cpp b/Kernel/NetworkAdapter.cpp index bac685520d..260cee7b19 100644 --- a/Kernel/NetworkAdapter.cpp +++ b/Kernel/NetworkAdapter.cpp @@ -14,7 +14,7 @@ NetworkAdapter::~NetworkAdapter() void NetworkAdapter::send(const MACAddress& destination, const ARPPacket& packet) { - int size_in_bytes = sizeof(EthernetFrameHeader) + sizeof(ARPPacket) + sizeof(EthernetFrameCheckSequence); + int size_in_bytes = sizeof(EthernetFrameHeader) + sizeof(ARPPacket); auto* eth = (EthernetFrameHeader*)kmalloc(size_in_bytes); eth->set_source(mac_address()); eth->set_destination(destination); @@ -26,7 +26,7 @@ void NetworkAdapter::send(const MACAddress& destination, const ARPPacket& packet void NetworkAdapter::send_ipv4(const MACAddress& destination, const void* packet, size_t packet_size) { - size_t size_in_bytes = sizeof(EthernetFrameHeader) + packet_size + sizeof(EthernetFrameCheckSequence); + size_t size_in_bytes = sizeof(EthernetFrameHeader) + packet_size; auto* eth = (EthernetFrameHeader*)kmalloc(size_in_bytes); eth->set_source(mac_address()); eth->set_destination(destination); diff --git a/Kernel/NetworkTask.cpp b/Kernel/NetworkTask.cpp index 6718254a38..24ac28c329 100644 --- a/Kernel/NetworkTask.cpp +++ b/Kernel/NetworkTask.cpp @@ -36,7 +36,7 @@ void NetworkTask_main() sleep(100); continue; } - if (packet.size() < (int)(sizeof(EthernetFrameHeader) + sizeof(EthernetFrameCheckSequence))) { + if (packet.size() < (int)(sizeof(EthernetFrameHeader))) { kprintf("NetworkTask: Packet is too small to be an Ethernet packet! (%d)\n", packet.size()); continue; } @@ -63,7 +63,7 @@ void NetworkTask_main() void handle_arp(const EthernetFrameHeader& eth, int frame_size) { - constexpr int minimum_arp_frame_size = sizeof(EthernetFrameHeader) + sizeof(ARPPacket) + sizeof(EthernetFrameCheckSequence); + constexpr int minimum_arp_frame_size = sizeof(EthernetFrameHeader) + sizeof(ARPPacket); if (frame_size < minimum_arp_frame_size) { kprintf("handle_arp: Frame too small (%d, need %d)\n", frame_size, minimum_arp_frame_size); return; @@ -131,7 +131,7 @@ void handle_arp(const EthernetFrameHeader& eth, int frame_size) void handle_ipv4(const EthernetFrameHeader& eth, int frame_size) { - constexpr int minimum_ipv4_frame_size = sizeof(EthernetFrameHeader) + sizeof(IPv4Packet) + sizeof(EthernetFrameCheckSequence); + constexpr int minimum_ipv4_frame_size = sizeof(EthernetFrameHeader) + sizeof(IPv4Packet); if (frame_size < minimum_ipv4_frame_size) { kprintf("handle_ipv4: Frame too small (%d, need %d)\n", frame_size, minimum_ipv4_frame_size); return; @@ -159,14 +159,23 @@ void handle_icmp(const EthernetFrameHeader& eth, int frame_size) (void)frame_size; auto& ipv4_packet = *static_cast(eth.payload()); auto& icmp_header = *static_cast(ipv4_packet.payload()); - kprintf("handle_icmp: type=%b, code=%b\n", icmp_header.type(), icmp_header.code()); - +#ifdef ICMP_DEBUG + kprintf("handle_icmp: source=%s, destination=%d type=%b, code=%b\n", + ipv4_packet.source().to_string().characters(), + ipv4_packet.destination().to_string().characters(), + icmp_header.type(), + icmp_header.code() + ); +#endif auto& e1000 = *E1000NetworkAdapter::the(); if (ipv4_packet.destination() == e1000.ipv4_address()) { - // It's for me! if (icmp_header.type() == ICMPType::EchoRequest) { auto& request = reinterpret_cast(icmp_header); - kprintf("ICMP echo request: id=%u, seq=%u, len=%u\n", (int)request.identifier, (int)request.sequence_number, ipv4_packet.length() - sizeof(ICMPEchoPacket)); + kprintf("handle_icmp: EchoRequest from %s: id=%u, seq=%u\n", + ipv4_packet.source().to_string().characters(), + (word)request.identifier, + (word)request.sequence_number + ); byte* response_buffer = (byte*)kmalloc(ipv4_packet.length()); memset(response_buffer, 0, ipv4_packet.length()); struct [[gnu::packed]] EchoResponse { @@ -180,11 +189,18 @@ void handle_icmp(const EthernetFrameHeader& eth, int frame_size) response.ipv4.set_destination(ipv4_packet.source()); response.ipv4.set_protocol(IPv4Protocol::ICMP); response.ipv4.set_length(ipv4_packet.length()); + response.ipv4.set_ident(1); + response.ipv4.set_ttl(64); + response.ipv4.set_checksum(response.ipv4.compute_checksum()); response.icmp_echo.header.set_type(ICMPType::EchoReply); response.icmp_echo.header.set_code(0); response.icmp_echo.identifier = request.identifier; response.icmp_echo.sequence_number = request.sequence_number; - memcpy(response.icmp_echo.payload(), request.payload(), ipv4_packet.length() - sizeof(EchoResponse)); + size_t icmp_packet_length = ipv4_packet.length() - sizeof(IPv4Packet); + size_t icmp_payload_length = ipv4_packet.length() - sizeof(EchoResponse); + memcpy(response.icmp_echo.payload(), request.payload(), icmp_payload_length); + + response.icmp_echo.header.set_checksum(internet_checksum(&response.icmp_echo, icmp_packet_length)); e1000.send_ipv4(eth.source(), response_buffer, ipv4_packet.length()); kfree(response_buffer); } -- cgit v1.2.3