1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 11:48:10 +00:00

Kernel: Avoid allocations when sending IP packets

Previously we'd allocate buffers when sending packets. This patch
avoids these allocations by using the NetworkAdapter's packet queue.

At the same time this also avoids copying partially constructed
packets in order to prepend Ethernet and/or IPv4 headers. It also
properly truncates UDP and raw IP packets.
This commit is contained in:
Gunnar Beutner 2021-05-26 05:35:05 +02:00 committed by Andreas Kling
parent f8310b7796
commit b436dd138b
7 changed files with 94 additions and 117 deletions

View file

@ -10,7 +10,6 @@
#include <Kernel/Heap/kmalloc.h>
#include <Kernel/Lock.h>
#include <Kernel/Net/EtherType.h>
#include <Kernel/Net/EthernetFrameHeader.h>
#include <Kernel/Net/LoopbackAdapter.h>
#include <Kernel/Net/NetworkAdapter.h>
#include <Kernel/Process.h>
@ -76,15 +75,15 @@ void NetworkAdapter::send(const MACAddress& destination, const ARPPacket& packet
send_raw({ (const u8*)eth, size_in_bytes });
}
KResult NetworkAdapter::send_ipv4(const IPv4Address& source_ipv4, const MACAddress& destination_mac, const IPv4Address& destination_ipv4, IPv4Protocol protocol, const UserOrKernelBuffer& payload, size_t payload_size, u8 ttl)
void NetworkAdapter::fill_in_ipv4_header(PacketWithTimestamp& packet, IPv4Address const& source_ipv4, MACAddress const& destination_mac, IPv4Address const& destination_ipv4, IPv4Protocol protocol, size_t payload_size, u8 ttl)
{
size_t ipv4_packet_size = sizeof(IPv4Packet) + payload_size;
if (ipv4_packet_size > mtu())
return send_ipv4_fragmented(source_ipv4, destination_mac, destination_ipv4, protocol, payload, payload_size, ttl);
VERIFY(ipv4_packet_size <= mtu());
size_t ethernet_frame_size = sizeof(EthernetFrameHeader) + sizeof(IPv4Packet) + payload_size;
auto buffer = NetworkByteBuffer::create_zeroed(ethernet_frame_size);
auto& eth = *(EthernetFrameHeader*)buffer.data();
size_t ethernet_frame_size = ipv4_payload_offset() + payload_size;
VERIFY(packet.buffer.size() == ethernet_frame_size);
memset(packet.buffer.data(), 0, ipv4_payload_offset());
auto& eth = *(EthernetFrameHeader*)packet.buffer.data();
eth.set_source(mac_address());
eth.set_destination(destination_mac);
eth.set_ether_type(EtherType::IPv4);
@ -98,53 +97,6 @@ KResult NetworkAdapter::send_ipv4(const IPv4Address& source_ipv4, const MACAddre
ipv4.set_ident(1);
ipv4.set_ttl(ttl);
ipv4.set_checksum(ipv4.compute_checksum());
m_packets_out++;
m_bytes_out += ethernet_frame_size;
if (!payload.read(ipv4.payload(), payload_size))
return EFAULT;
send_raw({ (const u8*)&eth, ethernet_frame_size });
return KSuccess;
}
KResult NetworkAdapter::send_ipv4_fragmented(const IPv4Address& source_ipv4, const MACAddress& destination_mac, const IPv4Address& destination_ipv4, IPv4Protocol protocol, const UserOrKernelBuffer& payload, size_t payload_size, u8 ttl)
{
// packets must be split on the 64-bit boundary
auto packet_boundary_size = (mtu() - sizeof(IPv4Packet) - sizeof(EthernetFrameHeader)) & 0xfffffff8;
auto fragment_block_count = (payload_size + packet_boundary_size) / packet_boundary_size;
auto last_block_size = payload_size - packet_boundary_size * (fragment_block_count - 1);
auto number_of_blocks_in_fragment = packet_boundary_size / 8;
auto identification = get_good_random<u16>();
size_t ethernet_frame_size = mtu();
for (size_t packet_index = 0; packet_index < fragment_block_count; ++packet_index) {
auto is_last_block = packet_index + 1 == fragment_block_count;
auto packet_payload_size = is_last_block ? last_block_size : packet_boundary_size;
auto buffer = NetworkByteBuffer::create_zeroed(ethernet_frame_size);
auto& eth = *(EthernetFrameHeader*)buffer.data();
eth.set_source(mac_address());
eth.set_destination(destination_mac);
eth.set_ether_type(EtherType::IPv4);
auto& ipv4 = *(IPv4Packet*)eth.payload();
ipv4.set_version(4);
ipv4.set_internet_header_length(5);
ipv4.set_source(source_ipv4);
ipv4.set_destination(destination_ipv4);
ipv4.set_protocol((u8)protocol);
ipv4.set_length(sizeof(IPv4Packet) + packet_payload_size);
ipv4.set_has_more_fragments(!is_last_block);
ipv4.set_ident(identification);
ipv4.set_ttl(ttl);
ipv4.set_fragment_offset(packet_index * number_of_blocks_in_fragment);
ipv4.set_checksum(ipv4.compute_checksum());
m_packets_out++;
m_bytes_out += ethernet_frame_size;
if (!payload.read(ipv4.payload(), packet_index * packet_boundary_size, packet_payload_size))
return EFAULT;
send_raw({ (const u8*)&eth, ethernet_frame_size });
}
return KSuccess;
}
void NetworkAdapter::did_receive(ReadonlyBytes payload)
@ -195,6 +147,8 @@ RefPtr<PacketWithTimestamp> NetworkAdapter::acquire_packet_buffer(size_t size)
if (m_unused_packets.is_empty()) {
auto buffer = KBuffer::create_with_size(size, Region::Access::Read | Region::Access::Write, "Packet Buffer", AllocationStrategy::AllocateNow);
auto packet = adopt_ref_if_nonnull(new PacketWithTimestamp { move(buffer), kgettimeofday() });
if (!packet)
return nullptr;
packet->buffer.set_size(size);
return packet;
}
@ -208,6 +162,8 @@ RefPtr<PacketWithTimestamp> NetworkAdapter::acquire_packet_buffer(size_t size)
auto buffer = KBuffer::create_with_size(size, Region::Access::Read | Region::Access::Write, "Packet Buffer", AllocationStrategy::AllocateNow);
packet = adopt_ref_if_nonnull(new PacketWithTimestamp { move(buffer), kgettimeofday() });
if (!packet)
return nullptr;
packet->buffer.set_size(size);
return packet;
}