Kernel: Implement loopback network interface

This commit is contained in:
Bananymous 2025-05-11 03:22:19 +03:00
parent a8844ddd28
commit 8e0a56b49a
12 changed files with 164 additions and 21 deletions

View File

@ -55,6 +55,7 @@ set(KERNEL_SOURCES
kernel/Networking/E1000/E1000.cpp
kernel/Networking/E1000/E1000E.cpp
kernel/Networking/IPv4Layer.cpp
kernel/Networking/Loopback.cpp
kernel/Networking/NetworkInterface.cpp
kernel/Networking/NetworkLayer.cpp
kernel/Networking/NetworkManager.cpp

View File

@ -20,6 +20,7 @@ namespace Kernel
NVMeController,
NVMeNamespace,
Ethernet,
Loopback,
TmpFS,
};

View File

@ -34,7 +34,8 @@ namespace Kernel
protected:
E1000(PCI::Device& pci_device)
: m_pci_device(pci_device)
: NetworkInterface(Type::Ethernet)
, m_pci_device(pci_device)
{ }
BAN::ErrorOr<void> initialize();

View File

@ -0,0 +1,39 @@
#pragma once
#include <kernel/Networking/NetworkInterface.h>
namespace Kernel
{
class LoopbackInterface : public NetworkInterface
{
public:
static constexpr size_t buffer_size = BAN::numeric_limits<uint16_t>::max() + 1;
public:
static BAN::ErrorOr<BAN::RefPtr<LoopbackInterface>> create();
BAN::MACAddress get_mac_address() const override { return {}; }
bool link_up() override { return true; }
int link_speed() override { return 1000; }
size_t payload_mtu() const override { return buffer_size - sizeof(EthernetHeader); }
protected:
LoopbackInterface()
: NetworkInterface(Type::Loopback)
{}
BAN::ErrorOr<void> send_bytes(BAN::MACAddress destination, EtherType protocol, BAN::ConstByteSpan) override;
bool can_read_impl() const override { return false; }
bool can_write_impl() const override { return false; }
bool has_error_impl() const override { return false; }
private:
SpinLock m_buffer_lock;
BAN::UniqPtr<VirtualRange> m_buffer;
};
}

View File

@ -32,10 +32,11 @@ namespace Kernel
enum class Type
{
Ethernet,
Loopback,
};
public:
NetworkInterface();
NetworkInterface(Type);
virtual ~NetworkInterface() {}
virtual BAN::MACAddress get_mac_address() const = 0;
@ -49,6 +50,8 @@ namespace Kernel
BAN::IPv4Address get_gateway() const { return m_gateway; }
void set_gateway(BAN::IPv4Address new_gateway) { m_gateway = new_gateway; }
Type type() const { return m_type; }
virtual bool link_up() = 0;
virtual int link_speed() = 0;

View File

@ -22,7 +22,7 @@ namespace Kernel
BAN::ErrorOr<void> add_interface(PCI::Device& pci_device);
BAN::Vector<BAN::RefPtr<NetworkInterface>> interfaces() { return m_interfaces; }
BAN::Vector<BAN::RefPtr<NetworkInterface>>& interfaces() { return m_interfaces; }
BAN::ErrorOr<BAN::RefPtr<Socket>> create_socket(Socket::Domain, Socket::Type, mode_t, uid_t, gid_t);
@ -31,6 +31,8 @@ namespace Kernel
private:
NetworkManager() {}
BAN::ErrorOr<void> add_interface(BAN::RefPtr<NetworkInterface>);
private:
BAN::UniqPtr<IPv4Layer> m_ipv4_layer;
BAN::Vector<BAN::RefPtr<NetworkInterface>> m_interfaces;

View File

@ -26,7 +26,8 @@ namespace Kernel
protected:
RTL8169(PCI::Device& pci_device)
: m_pci_device(pci_device)
: NetworkInterface(Type::Ethernet)
, m_pci_device(pci_device)
{ }
BAN::ErrorOr<void> initialize();

View File

@ -44,10 +44,22 @@ namespace Kernel
if (ipv4_address == s_broadcast_ipv4)
return s_broadcast_mac;
const auto netmask = interface.get_netmask();
const bool same_subnet = ipv4_address.mask(netmask) == interface.get_ipv4_address().mask(netmask);
if (interface.type() == NetworkInterface::Type::Loopback)
{
if (!same_subnet)
return BAN::Error::from_errno(EADDRNOTAVAIL);
return BAN::MACAddress {};
}
ASSERT(interface.type() == NetworkInterface::Type::Ethernet);
if (interface.get_ipv4_address() == BAN::IPv4Address { 0 })
return BAN::Error::from_errno(EINVAL);
if (interface.get_ipv4_address().mask(interface.get_netmask()) != ipv4_address.mask(interface.get_netmask()))
if (!same_subnet)
ipv4_address = interface.get_gateway();
{

View File

@ -107,9 +107,6 @@ namespace Kernel
BAN::ErrorOr<void> IPv4Layer::bind_socket_to_address(BAN::RefPtr<NetworkSocket> socket, const sockaddr* address, socklen_t address_len)
{
if (NetworkManager::get().interfaces().empty())
return BAN::Error::from_errno(EADDRNOTAVAIL);
if (!address || address_len < (socklen_t)sizeof(sockaddr_in))
return BAN::Error::from_errno(EINVAL);
if (address->sa_family != AF_INET)
@ -119,6 +116,22 @@ namespace Kernel
const uint16_t port = BAN::host_to_network_endian(sockaddr_in.sin_port);
if (port == NetworkSocket::PORT_NONE)
return bind_socket_to_unused(socket, address, address_len);
const auto ipv4 = BAN::IPv4Address { sockaddr_in.sin_addr.s_addr };
BAN::RefPtr<NetworkInterface> bind_interface;
for (auto interface : NetworkManager::get().interfaces())
{
if (interface->type() != NetworkInterface::Type::Loopback)
bind_interface = interface;
const auto netmask = interface->get_netmask();
if (ipv4.mask(netmask) != interface->get_ipv4_address().mask(netmask))
continue;
bind_interface = interface;
break;
}
if (!bind_interface)
return BAN::Error::from_errno(EADDRNOTAVAIL);
SpinLockGuard _(m_bound_socket_lock);
@ -126,9 +139,7 @@ namespace Kernel
return BAN::Error::from_errno(EADDRINUSE);
TRY(m_bound_sockets.insert(port, TRY(socket->get_weak_ptr())));
// FIXME: actually determine proper interface
auto interface = NetworkManager::get().interfaces().front();
socket->bind_interface_and_port(interface.ptr(), port);
socket->bind_interface_and_port(bind_interface.ptr(), port);
return {};
}

View File

@ -0,0 +1,49 @@
#include <kernel/Networking/Loopback.h>
#include <kernel/Networking/NetworkManager.h>
namespace Kernel
{
BAN::ErrorOr<BAN::RefPtr<LoopbackInterface>> LoopbackInterface::create()
{
auto* loopback_ptr = new LoopbackInterface();
if (loopback_ptr == nullptr)
return BAN::Error::from_errno(ENOMEM);
auto loopback = BAN::RefPtr<LoopbackInterface>::adopt(loopback_ptr);
loopback->m_buffer = TRY(VirtualRange::create_to_vaddr_range(
PageTable::kernel(),
KERNEL_OFFSET,
BAN::numeric_limits<vaddr_t>::max(),
buffer_size,
PageTable::Flags::ReadWrite | PageTable::Flags::Present,
true
));
loopback->set_ipv4_address({ 127, 0, 0, 1 });
loopback->set_netmask({ 255, 0, 0, 0 });
return loopback;
}
BAN::ErrorOr<void> LoopbackInterface::send_bytes(BAN::MACAddress destination, EtherType protocol, BAN::ConstByteSpan buffer)
{
ASSERT(buffer.size() + sizeof(EthernetHeader) <= buffer_size);
SpinLockGuard _(m_buffer_lock);
uint8_t* buffer_vaddr = reinterpret_cast<uint8_t*>(m_buffer->vaddr());
auto& ethernet_header = *reinterpret_cast<EthernetHeader*>(buffer_vaddr);
ethernet_header.dst_mac = destination;
ethernet_header.src_mac = get_mac_address();
ethernet_header.ether_type = protocol;
memcpy(buffer_vaddr + sizeof(EthernetHeader), buffer.data(), buffer.size());
NetworkManager::get().on_receive(*this, BAN::ConstByteSpan {
buffer_vaddr,
buffer.size() + sizeof(EthernetHeader)
});
return {};
}
}

View File

@ -9,22 +9,39 @@
namespace Kernel
{
static dev_t get_network_rdev_minor()
static BAN::Atomic<dev_t> s_ethernet_rdev_minor = 0;
static BAN::Atomic<dev_t> s_loopback_rdev_minor = 0;
static dev_t get_rdev(NetworkInterface::Type type)
{
static dev_t minor = 0;
return minor++;
switch (type)
{
case NetworkInterface::Type::Ethernet:
return makedev(DeviceNumber::Ethernet, s_ethernet_rdev_minor++);
case NetworkInterface::Type::Loopback:
return makedev(DeviceNumber::Ethernet, s_loopback_rdev_minor++);
}
ASSERT_NOT_REACHED();
}
NetworkInterface::NetworkInterface()
NetworkInterface::NetworkInterface(Type type)
: CharacterDevice(0400, 0, 0)
, m_type(Type::Ethernet)
, m_rdev(makedev(DeviceNumber::Ethernet, get_network_rdev_minor()))
, m_type(type)
, m_rdev(get_rdev(type))
{
ASSERT(minor(m_rdev) < 10);
ASSERT(m_type == Type::Ethernet);
switch (type)
{
case Type::Ethernet:
strcpy(m_name, "ethx");
m_name[3] = minor(m_rdev) + '0';
break;
case Type::Loopback:
strcpy(m_name, "lox");
m_name[2] = minor(m_rdev) + '0';
break;
}
}
}

View File

@ -4,6 +4,7 @@
#include <kernel/Networking/E1000/E1000.h>
#include <kernel/Networking/E1000/E1000E.h>
#include <kernel/Networking/ICMP.h>
#include <kernel/Networking/Loopback.h>
#include <kernel/Networking/NetworkManager.h>
#include <kernel/Networking/RTL8169/RTL8169.h>
#include <kernel/Networking/TCPSocket.h>
@ -19,6 +20,7 @@ namespace Kernel
{
ASSERT(!s_instance);
auto manager = TRY(BAN::UniqPtr<NetworkManager>::create());
TRY(manager->add_interface(TRY(LoopbackInterface::create())));
manager->m_ipv4_layer = TRY(IPv4Layer::create());
s_instance = BAN::move(manager);
return {};
@ -62,9 +64,13 @@ namespace Kernel
ASSERT(interface);
return add_interface(interface);
}
BAN::ErrorOr<void> NetworkManager::add_interface(BAN::RefPtr<NetworkInterface> interface)
{
TRY(m_interfaces.push_back(interface));
DevFileSystem::get().add_device(interface);
return {};
}