diff options
author | AnotherTest <ali.mpfard@gmail.com> | 2020-04-02 05:57:49 +0430 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-04-02 14:38:28 +0200 |
commit | b3d7c5d9de37949612665201e728d8ecb27df633 (patch) | |
tree | 5d1ee65364893d13fa630e5c042148ea3ee6a2ab /Kernel | |
parent | 592f218151493fefa2351252c8d4b51750687fea (diff) | |
download | serenity-b3d7c5d9de37949612665201e728d8ecb27df633.zip |
Kernel: Send Fragmented IPv4 packets if payload size > mtu
This adds IPv4 fragmentation, so now we can send huuuuuuge packets
properly.
Diffstat (limited to 'Kernel')
-rw-r--r-- | Kernel/Net/IPv4.h | 28 | ||||
-rw-r--r-- | Kernel/Net/NetworkAdapter.cpp | 43 | ||||
-rw-r--r-- | Kernel/Net/NetworkAdapter.h | 1 |
3 files changed, 70 insertions, 2 deletions
diff --git a/Kernel/Net/IPv4.h b/Kernel/Net/IPv4.h index 8d7322b1de..096670deaa 100644 --- a/Kernel/Net/IPv4.h +++ b/Kernel/Net/IPv4.h @@ -40,6 +40,11 @@ enum class IPv4Protocol : u16 { UDP = 17, }; +enum class IPv4PacketFlags : u16 { + DontFragment = 0x4000, + MoreFragments = 0x2000, +}; + NetworkOrdered<u16> internet_checksum(const void*, size_t); class [[gnu::packed]] IPv4Packet @@ -75,6 +80,28 @@ public: void* payload() { 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); } NetworkOrdered<u16> compute_checksum() const @@ -97,6 +124,7 @@ private: }; 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) { diff --git a/Kernel/Net/NetworkAdapter.cpp b/Kernel/Net/NetworkAdapter.cpp index 255c6698bc..b040be9dcd 100644 --- a/Kernel/Net/NetworkAdapter.cpp +++ b/Kernel/Net/NetworkAdapter.cpp @@ -32,6 +32,7 @@ #include <Kernel/Net/EthernetFrameHeader.h> #include <Kernel/Net/LoopbackAdapter.h> #include <Kernel/Net/NetworkAdapter.h> +#include <Kernel/Random.h> #include <LibBareMetal/StdLib.h> 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; if (ipv4_packet_size > mtu()) { - // FIXME: Implement IP fragmentation. - ASSERT_NOT_REACHED(); + send_ipv4_fragmented(destination_mac, destination_ipv4, protocol, payload, payload_size, ttl); + return; } 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); } +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) { InterruptDisabler disabler; diff --git a/Kernel/Net/NetworkAdapter.h b/Kernel/Net/NetworkAdapter.h index a787e96380..b7cea4f5ed 100644 --- a/Kernel/Net/NetworkAdapter.h +++ b/Kernel/Net/NetworkAdapter.h @@ -64,6 +64,7 @@ public: 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_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); |