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; }
|
||||
|
||||
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 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,
|
||||
};
|
||||
|
||||
enum NetworkChecksum : uint32_t
|
||||
{
|
||||
CKSUM_IPV4 = 1 << 0,
|
||||
CKSUM_TCP = 1 << 1,
|
||||
CKSUM_UDP = 1 << 2,
|
||||
};
|
||||
|
||||
class NetworkInterface : public CharacterDevice
|
||||
{
|
||||
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<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:
|
||||
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 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; }
|
||||
in_port_t bound_port() const
|
||||
|
||||
@@ -68,7 +68,7 @@ namespace Kernel
|
||||
|
||||
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_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;
|
||||
|
||||
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> bind_impl(const sockaddr* address, socklen_t address_len) override;
|
||||
|
||||
@@ -341,7 +341,7 @@ namespace Kernel
|
||||
NetworkManager::get().on_receive(*this, BAN::ConstByteSpan {
|
||||
reinterpret_cast<const uint8_t*>(m_rx_buffer_region->vaddr() + rx_current * E1000_RX_BUFFER_SIZE),
|
||||
descriptor.length
|
||||
});
|
||||
}, 0);
|
||||
|
||||
m_rx_lock.lock();
|
||||
|
||||
|
||||
@@ -238,7 +238,7 @@ namespace Kernel
|
||||
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))
|
||||
{
|
||||
@@ -247,7 +247,7 @@ namespace Kernel
|
||||
}
|
||||
|
||||
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");
|
||||
return {};
|
||||
@@ -386,7 +386,7 @@ namespace Kernel
|
||||
sender.sin_family = AF_INET;
|
||||
sender.sin_port = BAN::host_to_network_endian(src_port);
|
||||
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 {};
|
||||
}
|
||||
|
||||
@@ -109,10 +109,7 @@ namespace Kernel
|
||||
|
||||
m_buffer_lock.unlock(InterruptState::Enabled);
|
||||
|
||||
NetworkManager::get().on_receive(*this, {
|
||||
descriptor.addr,
|
||||
descriptor.size,
|
||||
});
|
||||
NetworkManager::get().on_receive(*this, BAN::ConstByteSpan { descriptor.addr, descriptor.size }, 0);
|
||||
|
||||
m_buffer_lock.lock();
|
||||
|
||||
|
||||
@@ -148,7 +148,7 @@ namespace Kernel
|
||||
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))
|
||||
return;
|
||||
@@ -163,7 +163,7 @@ namespace Kernel
|
||||
dwarnln("ARP: {}", ret.error());
|
||||
break;
|
||||
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());
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -272,10 +272,8 @@ namespace Kernel
|
||||
{
|
||||
m_rx_lock.unlock(InterruptState::Enabled);
|
||||
|
||||
NetworkManager::get().on_receive(*this, BAN::ConstByteSpan {
|
||||
reinterpret_cast<const uint8_t*>(m_rx_buffer_region->vaddr() + m_rx_head * s_buffer_size),
|
||||
packet_length
|
||||
});
|
||||
const uint8_t* packet_data = 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);
|
||||
|
||||
m_rx_lock.lock();
|
||||
}
|
||||
|
||||
@@ -572,7 +572,7 @@ namespace Kernel
|
||||
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)
|
||||
{
|
||||
@@ -585,9 +585,10 @@ namespace Kernel
|
||||
}();
|
||||
|
||||
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;
|
||||
|
||||
@@ -603,23 +604,23 @@ namespace Kernel
|
||||
.src_ipv4 = BAN::IPv4Address(addr_in.sin_addr.s_addr),
|
||||
.dst_ipv4 = interface->get_ipv4_address(),
|
||||
.protocol = NetworkProtocol::TCP,
|
||||
.length = buffer.size(),
|
||||
.length = packet.size(),
|
||||
};
|
||||
const BAN::ConstByteSpan buffers[] {
|
||||
BAN::ConstByteSpan::from(pseudo_header),
|
||||
buffer
|
||||
packet
|
||||
};
|
||||
checksum = calculate_internet_checksum({ buffers, sizeof(buffers) / sizeof(*buffers) });
|
||||
}
|
||||
else
|
||||
{
|
||||
dwarnln("No tcp checksum validation for socket family {}", sender->sa_family);
|
||||
dwarnln("no TCP checksum validation for socket family {}", sender->sa_family);
|
||||
return;
|
||||
}
|
||||
|
||||
if (checksum != 0)
|
||||
{
|
||||
dprintln("Checksum does not match");
|
||||
dwarnln("checksum does not match");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -628,7 +629,7 @@ namespace Kernel
|
||||
|
||||
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, " ack {}", (uint32_t)header.ack_number);
|
||||
@@ -768,7 +769,7 @@ namespace Kernel
|
||||
if (header.ack_number > m_send_window.current_ack)
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -57,21 +57,69 @@ namespace Kernel
|
||||
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);
|
||||
|
||||
if (m_packets.full())
|
||||
{
|
||||
dprintln("Packet buffer full, dropping packet");
|
||||
dwarnln("Packet buffer full, dropping packet");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto payload = packet.slice(sizeof(UDPHeader));
|
||||
|
||||
if (m_packet_total_size + payload.size() > m_packet_buffer->size())
|
||||
{
|
||||
dprintln("Packet buffer full, dropping packet");
|
||||
dwarnln("Packet buffer full, dropping packet");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user