Kernel: ARP now replies to requests
This commit is contained in:
parent
3a6d31d3fa
commit
649e9f4500
|
@ -1,12 +1,29 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/CircularQueue.h>
|
||||||
#include <BAN/HashMap.h>
|
#include <BAN/HashMap.h>
|
||||||
#include <BAN/UniqPtr.h>
|
#include <BAN/UniqPtr.h>
|
||||||
#include <kernel/Networking/NetworkInterface.h>
|
#include <kernel/Networking/NetworkInterface.h>
|
||||||
|
#include <kernel/Process.h>
|
||||||
|
#include <kernel/Semaphore.h>
|
||||||
|
|
||||||
namespace Kernel
|
namespace Kernel
|
||||||
{
|
{
|
||||||
|
|
||||||
|
struct ARPPacket
|
||||||
|
{
|
||||||
|
BAN::NetworkEndian<uint16_t> htype { 0 };
|
||||||
|
BAN::NetworkEndian<uint16_t> ptype { 0 };
|
||||||
|
BAN::NetworkEndian<uint8_t> hlen { 0 };
|
||||||
|
BAN::NetworkEndian<uint8_t> plen { 0 };
|
||||||
|
BAN::NetworkEndian<uint16_t> oper { 0 };
|
||||||
|
BAN::MACAddress sha { 0, 0, 0, 0, 0, 0 };
|
||||||
|
BAN::IPv4Address spa { 0 };
|
||||||
|
BAN::MACAddress tha { 0, 0, 0, 0, 0, 0 };
|
||||||
|
BAN::IPv4Address tpa { 0 };
|
||||||
|
};
|
||||||
|
static_assert(sizeof(ARPPacket) == 28);
|
||||||
|
|
||||||
class ARPTable
|
class ARPTable
|
||||||
{
|
{
|
||||||
BAN_NON_COPYABLE(ARPTable);
|
BAN_NON_COPYABLE(ARPTable);
|
||||||
|
@ -17,16 +34,19 @@ namespace Kernel
|
||||||
|
|
||||||
BAN::ErrorOr<BAN::MACAddress> get_mac_from_ipv4(NetworkInterface&, BAN::IPv4Address);
|
BAN::ErrorOr<BAN::MACAddress> get_mac_from_ipv4(NetworkInterface&, BAN::IPv4Address);
|
||||||
|
|
||||||
void handle_arp_packet(BAN::ConstByteSpan);
|
void add_arp_packet(NetworkInterface&, BAN::ConstByteSpan);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ARPTable();
|
ARPTable();
|
||||||
|
|
||||||
|
void packet_handle_task();
|
||||||
|
BAN::ErrorOr<void> handle_arp_packet(NetworkInterface&, const ARPPacket&);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct ARPReply
|
struct PendingArpPacket
|
||||||
{
|
{
|
||||||
BAN::IPv4Address ipv4_address { 0 };
|
NetworkInterface& interface;
|
||||||
BAN::MACAddress mac_address;
|
ARPPacket packet;
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -34,8 +54,9 @@ namespace Kernel
|
||||||
|
|
||||||
BAN::HashMap<BAN::IPv4Address, BAN::MACAddress> m_arp_table;
|
BAN::HashMap<BAN::IPv4Address, BAN::MACAddress> m_arp_table;
|
||||||
|
|
||||||
BAN::Atomic<bool> m_has_got_reply;
|
Process* m_process = nullptr;
|
||||||
ARPReply m_reply;
|
BAN::CircularQueue<PendingArpPacket, 128> m_pending_packets;
|
||||||
|
Semaphore m_pending_semaphore;
|
||||||
|
|
||||||
friend class BAN::UniqPtr<ARPTable>;
|
friend class BAN::UniqPtr<ARPTable>;
|
||||||
};
|
};
|
||||||
|
|
|
@ -38,7 +38,7 @@ namespace Kernel
|
||||||
|
|
||||||
BAN::ErrorOr<BAN::RefPtr<NetworkSocket>> create_socket(SocketType, mode_t, uid_t, gid_t);
|
BAN::ErrorOr<BAN::RefPtr<NetworkSocket>> create_socket(SocketType, mode_t, uid_t, gid_t);
|
||||||
|
|
||||||
void on_receive(BAN::ConstByteSpan);
|
void on_receive(NetworkInterface&, BAN::ConstByteSpan);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
NetworkManager();
|
NetworkManager();
|
||||||
|
|
|
@ -5,20 +5,6 @@
|
||||||
namespace Kernel
|
namespace Kernel
|
||||||
{
|
{
|
||||||
|
|
||||||
struct ARPPacket
|
|
||||||
{
|
|
||||||
BAN::NetworkEndian<uint16_t> htype;
|
|
||||||
BAN::NetworkEndian<uint16_t> ptype;
|
|
||||||
BAN::NetworkEndian<uint8_t> hlen;
|
|
||||||
BAN::NetworkEndian<uint8_t> plen;
|
|
||||||
BAN::NetworkEndian<uint16_t> oper;
|
|
||||||
BAN::MACAddress sha;
|
|
||||||
BAN::IPv4Address spa;
|
|
||||||
BAN::MACAddress tha;
|
|
||||||
BAN::IPv4Address tpa;
|
|
||||||
};
|
|
||||||
static_assert(sizeof(ARPPacket) == 28);
|
|
||||||
|
|
||||||
enum ARPOperation : uint16_t
|
enum ARPOperation : uint16_t
|
||||||
{
|
{
|
||||||
Request = 1,
|
Request = 1,
|
||||||
|
@ -30,22 +16,31 @@ namespace Kernel
|
||||||
|
|
||||||
BAN::ErrorOr<BAN::UniqPtr<ARPTable>> ARPTable::create()
|
BAN::ErrorOr<BAN::UniqPtr<ARPTable>> ARPTable::create()
|
||||||
{
|
{
|
||||||
return TRY(BAN::UniqPtr<ARPTable>::create());
|
auto arp_table = TRY(BAN::UniqPtr<ARPTable>::create());
|
||||||
|
arp_table->m_process = Process::create_kernel(
|
||||||
|
[](void* arp_table_ptr)
|
||||||
|
{
|
||||||
|
auto& arp_table = *reinterpret_cast<ARPTable*>(arp_table_ptr);
|
||||||
|
arp_table.packet_handle_task();
|
||||||
|
}, arp_table.ptr()
|
||||||
|
);
|
||||||
|
ASSERT(arp_table->m_process);
|
||||||
|
return arp_table;
|
||||||
}
|
}
|
||||||
|
|
||||||
ARPTable::ARPTable()
|
ARPTable::ARPTable()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BAN::ErrorOr<BAN::MACAddress> ARPTable::get_mac_from_ipv4(NetworkInterface& interface, BAN::IPv4Address ipv4_address)
|
BAN::ErrorOr<BAN::MACAddress> ARPTable::get_mac_from_ipv4(NetworkInterface& interface, BAN::IPv4Address ipv4_address)
|
||||||
{
|
{
|
||||||
LockGuard _(m_lock);
|
{
|
||||||
|
LockGuard _(m_lock);
|
||||||
if (ipv4_address == s_broadcast_ipv4)
|
if (ipv4_address == s_broadcast_ipv4)
|
||||||
return s_broadcast_mac;
|
return s_broadcast_mac;
|
||||||
if (m_arp_table.contains(ipv4_address))
|
if (m_arp_table.contains(ipv4_address))
|
||||||
return m_arp_table[ipv4_address];
|
return m_arp_table[ipv4_address];
|
||||||
|
}
|
||||||
|
|
||||||
BAN::Vector<uint8_t> full_packet_buffer;
|
BAN::Vector<uint8_t> full_packet_buffer;
|
||||||
TRY(full_packet_buffer.resize(sizeof(ARPPacket) + sizeof(EthernetHeader)));
|
TRY(full_packet_buffer.resize(sizeof(ARPPacket) + sizeof(EthernetHeader)));
|
||||||
|
@ -70,39 +65,121 @@ namespace Kernel
|
||||||
TRY(interface.send_raw_bytes(full_packet));
|
TRY(interface.send_raw_bytes(full_packet));
|
||||||
|
|
||||||
uint64_t timeout = SystemTimer::get().ms_since_boot() + 5'000;
|
uint64_t timeout = SystemTimer::get().ms_since_boot() + 5'000;
|
||||||
while (!m_has_got_reply)
|
while (SystemTimer::get().ms_since_boot() < timeout)
|
||||||
if (SystemTimer::get().ms_since_boot() >= timeout)
|
{
|
||||||
return BAN::Error::from_errno(ETIMEDOUT);
|
{
|
||||||
ASSERT_EQ(m_reply.ipv4_address, ipv4_address);
|
LockGuard _(m_lock);
|
||||||
|
if (m_arp_table.contains(ipv4_address))
|
||||||
|
return m_arp_table[ipv4_address];
|
||||||
|
}
|
||||||
|
TRY(Thread::current().block_or_eintr(m_pending_semaphore));
|
||||||
|
}
|
||||||
|
|
||||||
BAN::MACAddress mac_address = m_reply.mac_address;
|
return BAN::Error::from_errno(EINVAL);
|
||||||
(void)m_arp_table.insert(ipv4_address, m_reply.mac_address);
|
|
||||||
m_has_got_reply = false;
|
|
||||||
|
|
||||||
dprintln("IPv4 {} resolved to MAC {}", ipv4_address, mac_address);
|
|
||||||
|
|
||||||
return mac_address;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ARPTable::handle_arp_packet(BAN::ConstByteSpan buffer)
|
BAN::ErrorOr<void> ARPTable::handle_arp_packet(NetworkInterface& interface, const ARPPacket& packet)
|
||||||
|
{
|
||||||
|
if (packet.ptype != EtherType::IPv4)
|
||||||
|
{
|
||||||
|
dprintln("Non IPv4 arp packet?");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (packet.oper)
|
||||||
|
{
|
||||||
|
case ARPOperation::Request:
|
||||||
|
{
|
||||||
|
if (packet.tpa == interface.get_ipv4_address())
|
||||||
|
{
|
||||||
|
BAN::Vector<uint8_t> full_packet_buffer;
|
||||||
|
TRY(full_packet_buffer.resize(sizeof(ARPPacket) + sizeof(EthernetHeader)));
|
||||||
|
auto full_packet = BAN::ByteSpan { full_packet_buffer.span() };
|
||||||
|
|
||||||
|
auto& ethernet_header = full_packet.as<EthernetHeader>();
|
||||||
|
ethernet_header.dst_mac = packet.sha;
|
||||||
|
ethernet_header.src_mac = interface.get_mac_address();
|
||||||
|
ethernet_header.ether_type = EtherType::ARP;
|
||||||
|
|
||||||
|
auto& arp_request = full_packet.slice(sizeof(EthernetHeader)).as<ARPPacket>();
|
||||||
|
arp_request.htype = 0x0001;
|
||||||
|
arp_request.ptype = EtherType::IPv4;
|
||||||
|
arp_request.hlen = 0x06;
|
||||||
|
arp_request.plen = 0x04;
|
||||||
|
arp_request.oper = ARPOperation::Reply;
|
||||||
|
arp_request.sha = interface.get_mac_address();
|
||||||
|
arp_request.spa = interface.get_ipv4_address();
|
||||||
|
arp_request.tha = packet.sha;
|
||||||
|
arp_request.tpa = packet.spa;
|
||||||
|
|
||||||
|
TRY(interface.send_raw_bytes(full_packet));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ARPOperation::Reply:
|
||||||
|
{
|
||||||
|
LockGuard _(m_lock);
|
||||||
|
if (m_arp_table.contains(packet.spa))
|
||||||
|
{
|
||||||
|
if (m_arp_table[packet.spa] != packet.sha)
|
||||||
|
{
|
||||||
|
dprintln("Update IPv4 {} MAC to {}", packet.spa, packet.sha);
|
||||||
|
m_arp_table[packet.spa] = packet.sha;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TRY(m_arp_table.insert(packet.spa, packet.sha));
|
||||||
|
dprintln("Assign IPv4 {} MAC to {}", packet.spa, packet.sha);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
dprintln("Unhandled ARP packet (oper {4H})", (uint16_t)packet.oper);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void ARPTable::packet_handle_task()
|
||||||
|
{
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
BAN::Optional<PendingArpPacket> pending;
|
||||||
|
|
||||||
|
{
|
||||||
|
CriticalScope _;
|
||||||
|
if (!m_pending_packets.empty())
|
||||||
|
{
|
||||||
|
pending = m_pending_packets.front();
|
||||||
|
m_pending_packets.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pending.has_value())
|
||||||
|
{
|
||||||
|
m_pending_semaphore.block();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto ret = handle_arp_packet(pending->interface, pending->packet); ret.is_error())
|
||||||
|
dwarnln("{}", ret.error());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ARPTable::add_arp_packet(NetworkInterface& interface, BAN::ConstByteSpan buffer)
|
||||||
{
|
{
|
||||||
auto& arp_packet = buffer.as<const ARPPacket>();
|
auto& arp_packet = buffer.as<const ARPPacket>();
|
||||||
if (arp_packet.oper != ARPOperation::Reply)
|
|
||||||
|
if (m_pending_packets.full())
|
||||||
{
|
{
|
||||||
dprintln("Unhandled non-rely ARP packet (operation {2H})", (uint16_t)arp_packet.oper);
|
dprintln("arp packet queue full");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (arp_packet.ptype != EtherType::IPv4)
|
m_pending_packets.push({ .interface = interface, .packet = arp_packet });
|
||||||
{
|
m_pending_semaphore.unblock();
|
||||||
dprintln("Unhandled non-ipv4 ARP packet (ptype {2H})", (uint16_t)arp_packet.ptype);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ASSERT(!m_has_got_reply);
|
|
||||||
m_has_got_reply = true;
|
|
||||||
m_reply.ipv4_address = arp_packet.spa;
|
|
||||||
m_reply.mac_address = arp_packet.sha;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -292,7 +292,7 @@ namespace Kernel
|
||||||
break;
|
break;
|
||||||
ASSERT_LTE((uint16_t)descriptor.length, E1000_RX_BUFFER_SIZE);
|
ASSERT_LTE((uint16_t)descriptor.length, E1000_RX_BUFFER_SIZE);
|
||||||
|
|
||||||
NetworkManager::get().on_receive(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
|
||||||
});
|
});
|
||||||
|
|
|
@ -109,7 +109,7 @@ namespace Kernel
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkManager::on_receive(BAN::ConstByteSpan packet)
|
void NetworkManager::on_receive(NetworkInterface& interface, BAN::ConstByteSpan packet)
|
||||||
{
|
{
|
||||||
auto ethernet_header = packet.as<const EthernetHeader>();
|
auto ethernet_header = packet.as<const EthernetHeader>();
|
||||||
|
|
||||||
|
@ -117,7 +117,7 @@ namespace Kernel
|
||||||
{
|
{
|
||||||
case EtherType::ARP:
|
case EtherType::ARP:
|
||||||
{
|
{
|
||||||
m_arp_table->handle_arp_packet(packet.slice(sizeof(EthernetHeader)));
|
m_arp_table->add_arp_packet(interface, packet.slice(sizeof(EthernetHeader)));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EtherType::IPv4:
|
case EtherType::IPv4:
|
||||||
|
|
Loading…
Reference in New Issue