Kernel: Add system for hardware checksum offload on received packets
This commit is contained in:
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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() {}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|
||||||
|
|||||||
@@ -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 {};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user