Kernel: Add system for hardware checksum offload on received packets

This commit is contained in:
2026-06-29 00:24:48 +03:00
parent c0bd07174d
commit a6ad9b9b2e
13 changed files with 82 additions and 31 deletions

View File

@@ -41,7 +41,7 @@ namespace Kernel
ARPTable& arp_table() { return *m_arp_table; } ARPTable& arp_table() { return *m_arp_table; }
BAN::ErrorOr<void> handle_ipv4_packet(NetworkInterface&, BAN::ConstByteSpan); BAN::ErrorOr<void> handle_ipv4_packet(NetworkInterface&, BAN::ConstByteSpan, uint32_t validated_cksums);
virtual void unbind_socket(uint16_t port) override; virtual void unbind_socket(uint16_t port) override;
virtual BAN::ErrorOr<void> bind_socket_with_target(BAN::RefPtr<NetworkSocket>, const sockaddr* target_address, socklen_t target_address_len) override; virtual BAN::ErrorOr<void> bind_socket_with_target(BAN::RefPtr<NetworkSocket>, const sockaddr* target_address, socklen_t target_address_len) override;

View File

@@ -23,6 +23,13 @@ namespace Kernel
ARP = 0x0806, ARP = 0x0806,
}; };
enum NetworkChecksum : uint32_t
{
CKSUM_IPV4 = 1 << 0,
CKSUM_TCP = 1 << 1,
CKSUM_UDP = 1 << 2,
};
class NetworkInterface : public CharacterDevice class NetworkInterface : public CharacterDevice
{ {
BAN_NON_COPYABLE(NetworkInterface); BAN_NON_COPYABLE(NetworkInterface);

View File

@@ -27,7 +27,7 @@ namespace Kernel
BAN::ErrorOr<BAN::RefPtr<Socket>> create_socket(Socket::Domain, Socket::Type, mode_t, uid_t, gid_t); BAN::ErrorOr<BAN::RefPtr<Socket>> create_socket(Socket::Domain, Socket::Type, mode_t, uid_t, gid_t);
BAN::ErrorOr<void> connect_sockets(Socket::Domain, BAN::RefPtr<Socket>, BAN::RefPtr<Socket>); BAN::ErrorOr<void> connect_sockets(Socket::Domain, BAN::RefPtr<Socket>, BAN::RefPtr<Socket>);
void on_receive(NetworkInterface&, BAN::ConstByteSpan); void on_receive(NetworkInterface&, BAN::ConstByteSpan, uint32_t validated_cksums);
private: private:
NetworkManager() {} NetworkManager() {}

View File

@@ -35,7 +35,7 @@ namespace Kernel
virtual void get_protocol_header(BAN::ByteSpan header, BAN::ConstByteSpan payload, uint16_t dst_port, PseudoHeader) = 0; virtual void get_protocol_header(BAN::ByteSpan header, BAN::ConstByteSpan payload, uint16_t dst_port, PseudoHeader) = 0;
virtual NetworkProtocol protocol() const = 0; virtual NetworkProtocol protocol() const = 0;
virtual void receive_packet(BAN::ConstByteSpan, const sockaddr* sender, socklen_t sender_len) = 0; virtual void receive_packet(BAN::ConstByteSpan, const sockaddr* sender, socklen_t sender_len, uint32_t validated_cksums) = 0;
bool is_bound() const { return m_address_len >= static_cast<socklen_t>(sizeof(sa_family_t)) && m_address.ss_family != AF_UNSPEC; } bool is_bound() const { return m_address_len >= static_cast<socklen_t>(sizeof(sa_family_t)) && m_address.ss_family != AF_UNSPEC; }
in_port_t bound_port() const in_port_t bound_port() const

View File

@@ -68,7 +68,7 @@ namespace Kernel
BAN::ErrorOr<long> ioctl_impl(int, void*) override; BAN::ErrorOr<long> ioctl_impl(int, void*) override;
void receive_packet(BAN::ConstByteSpan, const sockaddr* sender, socklen_t sender_len) override; void receive_packet(BAN::ConstByteSpan, const sockaddr* sender, socklen_t sender_len, uint32_t validated_cksums) override;
bool can_read_impl() const override; bool can_read_impl() const override;
bool can_write_impl() const override; bool can_write_impl() const override;

View File

@@ -31,7 +31,7 @@ namespace Kernel
void get_protocol_header(BAN::ByteSpan header, BAN::ConstByteSpan payload, uint16_t dst_port, PseudoHeader) override; void get_protocol_header(BAN::ByteSpan header, BAN::ConstByteSpan payload, uint16_t dst_port, PseudoHeader) override;
protected: protected:
void receive_packet(BAN::ConstByteSpan, const sockaddr* sender, socklen_t sender_len) override; void receive_packet(BAN::ConstByteSpan, const sockaddr* sender, socklen_t sender_len, uint32_t validated_cksums) override;
BAN::ErrorOr<void> connect_impl(const sockaddr*, socklen_t) override; BAN::ErrorOr<void> connect_impl(const sockaddr*, socklen_t) override;
BAN::ErrorOr<void> bind_impl(const sockaddr* address, socklen_t address_len) override; BAN::ErrorOr<void> bind_impl(const sockaddr* address, socklen_t address_len) override;

View File

@@ -341,7 +341,7 @@ namespace Kernel
NetworkManager::get().on_receive(*this, BAN::ConstByteSpan { NetworkManager::get().on_receive(*this, BAN::ConstByteSpan {
reinterpret_cast<const uint8_t*>(m_rx_buffer_region->vaddr() + rx_current * E1000_RX_BUFFER_SIZE), reinterpret_cast<const uint8_t*>(m_rx_buffer_region->vaddr() + rx_current * E1000_RX_BUFFER_SIZE),
descriptor.length descriptor.length
}); }, 0);
m_rx_lock.lock(); m_rx_lock.lock();

View File

@@ -238,7 +238,7 @@ namespace Kernel
return {}; return {};
} }
BAN::ErrorOr<void> IPv4Layer::handle_ipv4_packet(NetworkInterface& interface, BAN::ConstByteSpan packet) BAN::ErrorOr<void> IPv4Layer::handle_ipv4_packet(NetworkInterface& interface, BAN::ConstByteSpan packet, uint32_t validated_cksums)
{ {
if (packet.size() < sizeof(IPv4Header)) if (packet.size() < sizeof(IPv4Header))
{ {
@@ -247,7 +247,7 @@ namespace Kernel
} }
auto& ipv4_header = packet.as<const IPv4Header>(); auto& ipv4_header = packet.as<const IPv4Header>();
if (calculate_internet_checksum(BAN::ConstByteSpan::from(ipv4_header)) != 0) if (!(validated_cksums & CKSUM_IPV4) && calculate_internet_checksum(BAN::ConstByteSpan::from(ipv4_header)) != 0)
{ {
dwarnln_if(DEBUG_IPV4, "IPv4 packet checksum failed"); dwarnln_if(DEBUG_IPV4, "IPv4 packet checksum failed");
return {}; return {};
@@ -386,7 +386,7 @@ namespace Kernel
sender.sin_family = AF_INET; sender.sin_family = AF_INET;
sender.sin_port = BAN::host_to_network_endian(src_port); sender.sin_port = BAN::host_to_network_endian(src_port);
sender.sin_addr.s_addr = src_ipv4.raw; sender.sin_addr.s_addr = src_ipv4.raw;
bound_socket->receive_packet(ipv4_data, reinterpret_cast<const sockaddr*>(&sender), sizeof(sender)); bound_socket->receive_packet(ipv4_data, reinterpret_cast<const sockaddr*>(&sender), sizeof(sender), validated_cksums);
return {}; return {};
} }

View File

@@ -109,10 +109,7 @@ namespace Kernel
m_buffer_lock.unlock(InterruptState::Enabled); m_buffer_lock.unlock(InterruptState::Enabled);
NetworkManager::get().on_receive(*this, { NetworkManager::get().on_receive(*this, BAN::ConstByteSpan { descriptor.addr, descriptor.size }, 0);
descriptor.addr,
descriptor.size,
});
m_buffer_lock.lock(); m_buffer_lock.lock();

View File

@@ -148,7 +148,7 @@ namespace Kernel
return {}; return {};
} }
void NetworkManager::on_receive(NetworkInterface& interface, BAN::ConstByteSpan packet) void NetworkManager::on_receive(NetworkInterface& interface, BAN::ConstByteSpan packet, uint32_t validated_cksums)
{ {
if (packet.size() < sizeof(EthernetHeader)) if (packet.size() < sizeof(EthernetHeader))
return; return;
@@ -163,7 +163,7 @@ namespace Kernel
dwarnln("ARP: {}", ret.error()); dwarnln("ARP: {}", ret.error());
break; break;
case EtherType::IPv4: case EtherType::IPv4:
if (auto ret = m_ipv4_layer->handle_ipv4_packet(interface, packet_data); ret.is_error()) if (auto ret = m_ipv4_layer->handle_ipv4_packet(interface, packet_data, validated_cksums); ret.is_error())
dwarnln("IPv4; {}", ret.error()); dwarnln("IPv4; {}", ret.error());
break; break;
default: default:

View File

@@ -272,10 +272,8 @@ namespace Kernel
{ {
m_rx_lock.unlock(InterruptState::Enabled); m_rx_lock.unlock(InterruptState::Enabled);
NetworkManager::get().on_receive(*this, BAN::ConstByteSpan { const uint8_t* packet_data = reinterpret_cast<const uint8_t*>(m_rx_buffer_region->vaddr() + m_rx_head * s_buffer_size);
reinterpret_cast<const uint8_t*>(m_rx_buffer_region->vaddr() + m_rx_head * s_buffer_size), NetworkManager::get().on_receive(*this, BAN::ConstByteSpan { packet_data, packet_length }, 0);
packet_length
});
m_rx_lock.lock(); m_rx_lock.lock();
} }

View File

@@ -572,7 +572,7 @@ namespace Kernel
dprintln_if(DEBUG_TCP, " seq {}", (uint32_t)header.seq_number); dprintln_if(DEBUG_TCP, " seq {}", (uint32_t)header.seq_number);
} }
void TCPSocket::receive_packet(BAN::ConstByteSpan buffer, const sockaddr* sender, socklen_t sender_len) void TCPSocket::receive_packet(BAN::ConstByteSpan packet, const sockaddr* sender, socklen_t sender_len, uint32_t validated_cksums)
{ {
if (m_state == State::Listen) if (m_state == State::Listen)
{ {
@@ -585,9 +585,10 @@ namespace Kernel
}(); }();
if (socket) if (socket)
return socket->receive_packet(buffer, sender, sender_len); return socket->receive_packet(packet, sender, sender_len, validated_cksums);
} }
if (!(validated_cksums & CKSUM_TCP))
{ {
uint16_t checksum = 0; uint16_t checksum = 0;
@@ -603,23 +604,23 @@ namespace Kernel
.src_ipv4 = BAN::IPv4Address(addr_in.sin_addr.s_addr), .src_ipv4 = BAN::IPv4Address(addr_in.sin_addr.s_addr),
.dst_ipv4 = interface->get_ipv4_address(), .dst_ipv4 = interface->get_ipv4_address(),
.protocol = NetworkProtocol::TCP, .protocol = NetworkProtocol::TCP,
.length = buffer.size(), .length = packet.size(),
}; };
const BAN::ConstByteSpan buffers[] { const BAN::ConstByteSpan buffers[] {
BAN::ConstByteSpan::from(pseudo_header), BAN::ConstByteSpan::from(pseudo_header),
buffer packet
}; };
checksum = calculate_internet_checksum({ buffers, sizeof(buffers) / sizeof(*buffers) }); checksum = calculate_internet_checksum({ buffers, sizeof(buffers) / sizeof(*buffers) });
} }
else else
{ {
dwarnln("No tcp checksum validation for socket family {}", sender->sa_family); dwarnln("no TCP checksum validation for socket family {}", sender->sa_family);
return; return;
} }
if (checksum != 0) if (checksum != 0)
{ {
dprintln("Checksum does not match"); dwarnln("checksum does not match");
return; return;
} }
} }
@@ -628,7 +629,7 @@ namespace Kernel
const bool hungup_before = has_hungup_impl(); const bool hungup_before = has_hungup_impl();
auto& header = buffer.as<const TCPHeader>(); const auto& header = packet.as<const TCPHeader>();
dprintln_if(DEBUG_TCP, "receiving {} {8b}", (uint8_t)m_state, header.flags); dprintln_if(DEBUG_TCP, "receiving {} {8b}", (uint8_t)m_state, header.flags);
dprintln_if(DEBUG_TCP, " ack {}", (uint32_t)header.ack_number); dprintln_if(DEBUG_TCP, " ack {}", (uint32_t)header.ack_number);
@@ -768,7 +769,7 @@ namespace Kernel
if (header.ack_number > m_send_window.current_ack) if (header.ack_number > m_send_window.current_ack)
m_send_window.current_ack = header.ack_number; m_send_window.current_ack = header.ack_number;
auto payload = buffer.slice(header.data_offset * sizeof(uint32_t)); auto payload = packet.slice(header.data_offset * sizeof(uint32_t));
if (header.seq_number < expected_seq) if (header.seq_number < expected_seq)
{ {

View File

@@ -57,21 +57,69 @@ namespace Kernel
header.checksum = 0xFFFF; header.checksum = 0xFFFF;
} }
void UDPSocket::receive_packet(BAN::ConstByteSpan packet, const sockaddr* sender, socklen_t sender_len) void UDPSocket::receive_packet(BAN::ConstByteSpan packet, const sockaddr* sender, socklen_t sender_len, uint32_t validated_cksums)
{ {
auto payload = packet.slice(sizeof(UDPHeader)); const auto header = packet.as<const UDPHeader>();
if (!(validated_cksums & CKSUM_UDP) && header.checksum)
{
uint16_t checksum = 0;
if (sender->sa_family == AF_INET)
{
auto interface_or_error = interface(sender, sender_len);
if (interface_or_error.is_error())
return;
auto interface = interface_or_error.release_value();
auto& addr_in = *reinterpret_cast<const sockaddr_in*>(sender);
const PseudoHeader pseudo_header {
.src_ipv4 = BAN::IPv4Address(addr_in.sin_addr.s_addr),
.dst_ipv4 = interface->get_ipv4_address(),
.protocol = NetworkProtocol::UDP,
.length = packet.size(),
};
const UDPHeader udp_header {
.src_port = header.src_port,
.dst_port = header.dst_port,
.length = header.length,
.checksum = 0,
};
const BAN::ConstByteSpan buffers[] {
BAN::ConstByteSpan::from(pseudo_header),
BAN::ConstByteSpan::from(udp_header),
packet.slice(sizeof(UDPHeader))
};
checksum = calculate_internet_checksum({ buffers, sizeof(buffers) / sizeof(*buffers) });
}
else
{
dwarnln("no UDP checksum validation for socket family {}", sender->sa_family);
return;
}
const bool checksum_valid1 = (header.checksum == checksum);
const bool checksum_valid2 = (header.checksum == 0xFFFF && checksum == 0);
if (!checksum_valid1 && !checksum_valid2)
{
dwarnln("checksum does not match {4h}", checksum);
return;
}
}
SpinLockGuard _(m_packet_lock); SpinLockGuard _(m_packet_lock);
if (m_packets.full()) if (m_packets.full())
{ {
dprintln("Packet buffer full, dropping packet"); dwarnln("Packet buffer full, dropping packet");
return; return;
} }
const auto payload = packet.slice(sizeof(UDPHeader));
if (m_packet_total_size + payload.size() > m_packet_buffer->size()) if (m_packet_total_size + payload.size() > m_packet_buffer->size())
{ {
dprintln("Packet buffer full, dropping packet"); dwarnln("Packet buffer full, dropping packet");
return; return;
} }