Kernel/Net: Start work on IPv6

This commit introduces the first bit of IPv6 support into
SerenityOS. An IPv6 packet header structure allows NetworkTask to
recognize and log correctly shaped IPv6 packets, but nothing is done
with those yet. A new logging flag, IPV6_DEBUG, allows debugging of
IPv6 internals.

Co-Authored-By: sdomi <ja@sdomi.pl>
This commit is contained in:
kleines Filmröllchen 2024-09-09 19:58:15 +02:00 committed by Tim Schumacher
parent 93951597a5
commit 11d46a4a00
6 changed files with 140 additions and 1 deletions

View file

@ -143,6 +143,10 @@
#cmakedefine01 IPV4_DEBUG
#endif
#ifndef IPV6_DEBUG
#cmakedefine01 IPV6_DEBUG
#endif
#ifndef IPV4_SOCKET_DEBUG
#cmakedefine01 IPV4_SOCKET_DEBUG
#endif

85
Kernel/Net/IP/IPv6.h Normal file
View file

@ -0,0 +1,85 @@
/*
* Copyright (c) 2024, kleines Filmröllchen <filmroellchen@serenityos.org>
* Copyright (c) 2024, sdomi <ja@sdomi.pl>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Assertions.h>
#include <AK/Endian.h>
#include <AK/IPv6Address.h>
#include <AK/Types.h>
#include <Kernel/Net/IP/IP.h>
namespace Kernel {
// Header extensions and special values only.
// For transport protocol numbers, see Kernel::TransportProtocol
enum class IPv6NextHeader : u8 {
HopByHopOption = 0,
Routing = 43,
Fragment = 44,
NoNextHeader = 59,
DestinationOptions = 60,
};
// https://datatracker.ietf.org/doc/html/rfc8200#section-3
class [[gnu::packed]] IPv6PacketHeader {
public:
u8 version() const { return (m_version_and_traffic >> 28) & 0xf; }
void set_version(u8 version) { m_version_and_traffic = (m_version_and_traffic & ~0xf0000000) | (version << 28); }
u8 traffic_class() const { return (m_version_and_traffic >> 20) & 0x0ff; }
void set_traffic_class(u8 traffic_class) { m_version_and_traffic = (m_version_and_traffic & ~0x0ff00000) | (traffic_class << 20); }
u32 flow_label() const { return m_version_and_traffic & 0xfffff; }
void set_flow_label(u32 flow_label) { m_version_and_traffic = (m_version_and_traffic & ~0xfffff) | flow_label; }
u16 length() const { return m_length; }
void set_length(u16 length) { m_length = length; }
// Aka. TTL
u8 hop_limit() const { return m_hop_limit; }
void set_hop_limit(u8 hop_limit) { m_hop_limit = hop_limit; }
u8 next_header() const { return m_next_header; }
void set_next_header(u8 next_header) { m_next_header = next_header; }
IPv6Address const& source() const { return m_source; }
void set_source(IPv6Address const& address) { m_source = address; }
IPv6Address const& destination() const { return m_destination; }
void set_destination(IPv6Address const& address) { m_destination = address; }
void* payload() { return &m_payload[0]; }
void const* payload() const { return &m_payload[0]; }
u16 payload_size() const { return m_length; }
private:
NetworkOrdered<u32> m_version_and_traffic;
NetworkOrdered<u16> m_length;
u8 m_next_header { static_cast<u8>(IPv6NextHeader::NoNextHeader) };
u8 m_hop_limit { 0 };
IPv6Address m_source;
IPv6Address m_destination;
u8 m_payload[0];
};
static_assert(AssertSize<IPv6PacketHeader, 10 * 32 / 8>());
// https://www.rfc-editor.org/rfc/rfc2460
// section 8.1, checksumming
struct [[gnu::packed]] IPv6PseudoHeader {
IPv6Address source_address;
IPv6Address target_address;
NetworkOrdered<u32> packet_length;
u8 zero[3] { 0 };
TransportProtocol next_header;
};
static_assert(AssertSize<IPv6PseudoHeader, 2 * 16 + 8>());
}

View file

@ -23,6 +23,7 @@
#include <Kernel/Net/IP/ARP.h>
#include <Kernel/Net/IP/IP.h>
#include <Kernel/Net/IP/IPv4.h>
#include <Kernel/Net/IP/IPv6.h>
namespace Kernel {
@ -108,6 +109,7 @@ public:
constexpr size_t layer3_payload_offset() const { return sizeof(EthernetFrameHeader); }
constexpr size_t ipv4_payload_offset() const { return layer3_payload_offset() + sizeof(IPv4Packet); }
constexpr size_t ipv6_payload_offset() const { return layer3_payload_offset() + sizeof(IPv6PacketHeader); }
Function<void()> on_receive;

View file

@ -1,5 +1,7 @@
/*
* Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2024, sdomi <ja@sdomi.pl>
* Copyright (c) 2024, kleines Filmröllchen <filmroellchen@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -13,6 +15,7 @@
#include <Kernel/Net/IP/ARP.h>
#include <Kernel/Net/IP/IP.h>
#include <Kernel/Net/IP/IPv4.h>
#include <Kernel/Net/IP/IPv6.h>
#include <Kernel/Net/IP/Socket.h>
#include <Kernel/Net/LoopbackAdapter.h>
#include <Kernel/Net/NetworkTask.h>
@ -29,6 +32,7 @@ namespace Kernel {
static void handle_arp(EthernetFrameHeader const&, size_t frame_size);
static void handle_ipv4(EthernetFrameHeader const&, size_t frame_size, UnixDateTime const& packet_timestamp, RefPtr<NetworkAdapter> adapter);
static void handle_icmp(EthernetFrameHeader const&, IPv4Packet const&, UnixDateTime const& packet_timestamp, RefPtr<NetworkAdapter> adapter);
static void handle_ipv6(EthernetFrameHeader const&, size_t frame_size, UnixDateTime const& packet_timestamp, RefPtr<NetworkAdapter> adapter);
static void handle_udp(IPv4Packet const&, UnixDateTime const& packet_timestamp);
static void handle_tcp(IPv4Packet const&, UnixDateTime const& packet_timestamp, RefPtr<NetworkAdapter> adapter);
static void send_delayed_tcp_ack(TCPSocket& socket);
@ -121,7 +125,7 @@ void NetworkTask_main(void*)
handle_ipv4(eth, packet_size, meta.packet_timestamp, meta.adapter);
break;
case EtherType::IPv6:
// ignore
handle_ipv6(eth, packet_size, meta.packet_timestamp, meta.adapter);
break;
default:
dbgln_if(ETHERNET_DEBUG, "NetworkTask: Unknown ethernet type {:#04x}", eth.ether_type());
@ -225,6 +229,48 @@ void handle_ipv4(EthernetFrameHeader const& eth, size_t frame_size, UnixDateTime
}
}
void handle_ipv6(EthernetFrameHeader const& eth, size_t frame_size, UnixDateTime const& packet_timestamp, RefPtr<NetworkAdapter> adapter)
{
(void)packet_timestamp;
constexpr size_t minimum_ipv6_frame_size = sizeof(EthernetFrameHeader) + sizeof(IPv6PacketHeader);
if (frame_size < minimum_ipv6_frame_size) {
dbgln("handle_ipv6: Frame too small ({}, need {})", frame_size, minimum_ipv6_frame_size);
return;
}
auto& packet = *static_cast<IPv6PacketHeader const*>(eth.payload());
size_t const actual_ipv6_packet_length = frame_size - adapter->layer3_payload_offset();
size_t const payload_length = frame_size - adapter->ipv6_payload_offset();
if (packet.length() < payload_length) {
dbgln("handle_ipv6: IPv6 packet too short ({}, need {})", packet.length(), sizeof(IPv6PacketHeader));
return;
}
if (packet.length() > payload_length) {
dbgln("handle_ipv6: IPv6 packet claims to be longer than it is ({}, actually {})", packet.length(), actual_ipv6_packet_length);
return;
}
dbgln_if(IPV6_DEBUG, "handle_ipv6: source={}, destination={}", packet.source(), packet.destination());
// TODO: Update ARP tables.
// TODO: skip or handle next headers that are not the next layer protocol header.
switch (static_cast<TransportProtocol>(packet.next_header())) {
case TransportProtocol::UDP:
dbgln_if(IPV6_DEBUG, "handle_ipv6: TODO: got UDP packet, what to do with it?");
break;
case TransportProtocol::TCP:
dbgln_if(IPV6_DEBUG, "handle_ipv6: TODO: got TCP packet, what to do with it?");
break;
default:
dbgln_if(IPV6_DEBUG, "handle_ipv6: Unhandled protocol {:#02x}", packet.next_header());
break;
}
}
void handle_icmp(EthernetFrameHeader const& eth, IPv4Packet const& ipv4_packet, UnixDateTime const& packet_timestamp, RefPtr<NetworkAdapter> adapter)
{
auto& icmp_header = *static_cast<ICMPHeader const*>(ipv4_packet.payload());

View file

@ -7,6 +7,7 @@
#pragma once
#include <AK/IPv4Address.h>
#include <AK/IPv6Address.h>
#include <AK/RefPtr.h>
#include <Kernel/Locking/MutexProtected.h>
#include <Kernel/Net/NetworkAdapter.h>

View file

@ -87,6 +87,7 @@ set(INTERRUPT_DEBUG ON)
set(IOAPIC_DEBUG ON)
set(IO_DEBUG ON)
set(IPV4_DEBUG ON)
set(IPV6_DEBUG ON)
set(IPV4_SOCKET_DEBUG ON)
set(IRQ_DEBUG ON)
set(ISO9660_DEBUG ON)