mirror of
https://github.com/RGBCube/serenity
synced 2025-07-24 20:57:35 +00:00
Kernel: Send Fragmented IPv4 packets if payload size > mtu
This adds IPv4 fragmentation, so now we can send huuuuuuge packets properly.
This commit is contained in:
parent
592f218151
commit
b3d7c5d9de
3 changed files with 70 additions and 2 deletions
|
@ -40,6 +40,11 @@ enum class IPv4Protocol : u16 {
|
||||||
UDP = 17,
|
UDP = 17,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class IPv4PacketFlags : u16 {
|
||||||
|
DontFragment = 0x4000,
|
||||||
|
MoreFragments = 0x2000,
|
||||||
|
};
|
||||||
|
|
||||||
NetworkOrdered<u16> internet_checksum(const void*, size_t);
|
NetworkOrdered<u16> internet_checksum(const void*, size_t);
|
||||||
|
|
||||||
class [[gnu::packed]] IPv4Packet
|
class [[gnu::packed]] IPv4Packet
|
||||||
|
@ -75,6 +80,28 @@ public:
|
||||||
void* payload() { return this + 1; }
|
void* payload() { return this + 1; }
|
||||||
const void* payload() const { return this + 1; }
|
const void* payload() const { return this + 1; }
|
||||||
|
|
||||||
|
u16 flags_and_fragment() const { return m_flags_and_fragment; }
|
||||||
|
u16 fragment_offset() const { return ((u16)m_flags_and_fragment & 0x2fff); }
|
||||||
|
u16 flags() const { return (((u16)m_flags_and_fragment) & (((u16)IPv4PacketFlags::MoreFragments) | ((u16)IPv4PacketFlags::DontFragment))); }
|
||||||
|
|
||||||
|
void set_has_more_fragments(bool more_fragments)
|
||||||
|
{
|
||||||
|
if (more_fragments)
|
||||||
|
m_flags_and_fragment = (u16)m_flags_and_fragment | ((u16)IPv4PacketFlags::MoreFragments);
|
||||||
|
else
|
||||||
|
m_flags_and_fragment = (u16)m_flags_and_fragment & ((u16)IPv4PacketFlags::MoreFragments);
|
||||||
|
}
|
||||||
|
void set_fragment_offset(u16 offset)
|
||||||
|
{
|
||||||
|
m_flags_and_fragment = flags() | (offset & 0x2fff);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_a_fragment() const
|
||||||
|
{
|
||||||
|
// either has More-Fragments set, or has a fragment offset
|
||||||
|
return (((u16)m_flags_and_fragment) & ((u16)IPv4PacketFlags::MoreFragments)) || ((u16)m_flags_and_fragment & 0x2fff);
|
||||||
|
}
|
||||||
|
|
||||||
u16 payload_size() const { return m_length - sizeof(IPv4Packet); }
|
u16 payload_size() const { return m_length - sizeof(IPv4Packet); }
|
||||||
|
|
||||||
NetworkOrdered<u16> compute_checksum() const
|
NetworkOrdered<u16> compute_checksum() const
|
||||||
|
@ -97,6 +124,7 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(sizeof(IPv4Packet) == 20);
|
static_assert(sizeof(IPv4Packet) == 20);
|
||||||
|
const LogStream& operator<<(const LogStream& stream, const IPv4Packet& packet);
|
||||||
|
|
||||||
inline NetworkOrdered<u16> internet_checksum(const void* ptr, size_t count)
|
inline NetworkOrdered<u16> internet_checksum(const void* ptr, size_t count)
|
||||||
{
|
{
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include <Kernel/Net/EthernetFrameHeader.h>
|
#include <Kernel/Net/EthernetFrameHeader.h>
|
||||||
#include <Kernel/Net/LoopbackAdapter.h>
|
#include <Kernel/Net/LoopbackAdapter.h>
|
||||||
#include <Kernel/Net/NetworkAdapter.h>
|
#include <Kernel/Net/NetworkAdapter.h>
|
||||||
|
#include <Kernel/Random.h>
|
||||||
#include <LibBareMetal/StdLib.h>
|
#include <LibBareMetal/StdLib.h>
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
@ -103,8 +104,8 @@ void NetworkAdapter::send_ipv4(const MACAddress& destination_mac, const IPv4Addr
|
||||||
{
|
{
|
||||||
size_t ipv4_packet_size = sizeof(IPv4Packet) + payload_size;
|
size_t ipv4_packet_size = sizeof(IPv4Packet) + payload_size;
|
||||||
if (ipv4_packet_size > mtu()) {
|
if (ipv4_packet_size > mtu()) {
|
||||||
// FIXME: Implement IP fragmentation.
|
send_ipv4_fragmented(destination_mac, destination_ipv4, protocol, payload, payload_size, ttl);
|
||||||
ASSERT_NOT_REACHED();
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t ethernet_frame_size = sizeof(EthernetFrameHeader) + sizeof(IPv4Packet) + payload_size;
|
size_t ethernet_frame_size = sizeof(EthernetFrameHeader) + sizeof(IPv4Packet) + payload_size;
|
||||||
|
@ -129,6 +130,44 @@ void NetworkAdapter::send_ipv4(const MACAddress& destination_mac, const IPv4Addr
|
||||||
send_raw((const u8*)ð, ethernet_frame_size);
|
send_raw((const u8*)ð, ethernet_frame_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NetworkAdapter::send_ipv4_fragmented(const MACAddress& destination_mac, const IPv4Address& destination_ipv4, IPv4Protocol protocol, const u8* 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 = ByteBuffer::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(ipv4_address());
|
||||||
|
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;
|
||||||
|
memcpy(ipv4.payload(), payload + packet_index * packet_boundary_size, packet_payload_size);
|
||||||
|
send_raw((const u8*)ð, ethernet_frame_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void NetworkAdapter::did_receive(const u8* data, size_t length)
|
void NetworkAdapter::did_receive(const u8* data, size_t length)
|
||||||
{
|
{
|
||||||
InterruptDisabler disabler;
|
InterruptDisabler disabler;
|
||||||
|
|
|
@ -64,6 +64,7 @@ public:
|
||||||
|
|
||||||
void send(const MACAddress&, const ARPPacket&);
|
void send(const MACAddress&, const ARPPacket&);
|
||||||
void send_ipv4(const MACAddress&, const IPv4Address&, IPv4Protocol, const u8* payload, size_t payload_size, u8 ttl);
|
void send_ipv4(const MACAddress&, const IPv4Address&, IPv4Protocol, const u8* payload, size_t payload_size, u8 ttl);
|
||||||
|
void send_ipv4_fragmented(const MACAddress&, const IPv4Address&, IPv4Protocol, const u8* payload, size_t payload_size, u8 ttl);
|
||||||
|
|
||||||
size_t dequeue_packet(u8* buffer, size_t buffer_size);
|
size_t dequeue_packet(u8* buffer, size_t buffer_size);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue