forked from Bananymous/banan-os
185 lines
4.5 KiB
C++
185 lines
4.5 KiB
C++
#include <kernel/Networking/ARPTable.h>
|
|
#include <kernel/Scheduler.h>
|
|
#include <kernel/Timer/Timer.h>
|
|
|
|
namespace Kernel
|
|
{
|
|
|
|
enum ARPOperation : uint16_t
|
|
{
|
|
Request = 1,
|
|
Reply = 2,
|
|
};
|
|
|
|
static constexpr BAN::IPv4Address s_broadcast_ipv4 { 0xFFFFFFFF };
|
|
static constexpr BAN::MACAddress s_broadcast_mac {{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }};
|
|
|
|
BAN::ErrorOr<BAN::UniqPtr<ARPTable>> 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()
|
|
{
|
|
if (m_process)
|
|
m_process->exit(0, SIGKILL);
|
|
m_process = nullptr;
|
|
}
|
|
|
|
BAN::ErrorOr<BAN::MACAddress> ARPTable::get_mac_from_ipv4(NetworkInterface& interface, BAN::IPv4Address ipv4_address)
|
|
{
|
|
if (ipv4_address == s_broadcast_ipv4)
|
|
return s_broadcast_mac;
|
|
|
|
if (interface.get_ipv4_address() == BAN::IPv4Address { 0 })
|
|
return BAN::Error::from_errno(EINVAL);
|
|
|
|
if (interface.get_ipv4_address().mask(interface.get_netmask()) != ipv4_address.mask(interface.get_netmask()))
|
|
ipv4_address = interface.get_gateway();
|
|
|
|
{
|
|
SpinLockGuard _(m_table_lock);
|
|
auto it = m_arp_table.find(ipv4_address);
|
|
if (it != m_arp_table.end())
|
|
return it->value;
|
|
}
|
|
|
|
ARPPacket arp_request;
|
|
arp_request.htype = 0x0001;
|
|
arp_request.ptype = EtherType::IPv4;
|
|
arp_request.hlen = 0x06;
|
|
arp_request.plen = 0x04;
|
|
arp_request.oper = ARPOperation::Request;
|
|
arp_request.sha = interface.get_mac_address();
|
|
arp_request.spa = interface.get_ipv4_address();
|
|
arp_request.tha = {{ 0, 0, 0, 0, 0, 0 }};
|
|
arp_request.tpa = ipv4_address;
|
|
|
|
TRY(interface.send_bytes(s_broadcast_mac, EtherType::ARP, BAN::ConstByteSpan::from(arp_request)));
|
|
|
|
uint64_t timeout = SystemTimer::get().ms_since_boot() + 1'000;
|
|
while (SystemTimer::get().ms_since_boot() < timeout)
|
|
{
|
|
{
|
|
SpinLockGuard _(m_table_lock);
|
|
auto it = m_arp_table.find(ipv4_address);
|
|
if (it != m_arp_table.end())
|
|
return it->value;
|
|
}
|
|
Scheduler::get().reschedule();
|
|
}
|
|
|
|
return BAN::Error::from_errno(ETIMEDOUT);
|
|
}
|
|
|
|
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())
|
|
{
|
|
ARPPacket arp_reply;
|
|
arp_reply.htype = 0x0001;
|
|
arp_reply.ptype = EtherType::IPv4;
|
|
arp_reply.hlen = 0x06;
|
|
arp_reply.plen = 0x04;
|
|
arp_reply.oper = ARPOperation::Reply;
|
|
arp_reply.sha = interface.get_mac_address();
|
|
arp_reply.spa = interface.get_ipv4_address();
|
|
arp_reply.tha = packet.sha;
|
|
arp_reply.tpa = packet.spa;
|
|
TRY(interface.send_bytes(packet.sha, EtherType::ARP, BAN::ConstByteSpan::from(arp_reply)));
|
|
}
|
|
break;
|
|
}
|
|
case ARPOperation::Reply:
|
|
{
|
|
SpinLockGuard _(m_table_lock);
|
|
auto it = m_arp_table.find(packet.spa);
|
|
|
|
if (it != m_arp_table.end())
|
|
{
|
|
if (it->value != packet.sha)
|
|
{
|
|
dprintln("Update IPv4 {} MAC to {}", packet.spa, packet.sha);
|
|
it->value = 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 (;;)
|
|
{
|
|
PendingArpPacket pending = ({
|
|
auto state = m_pending_lock.lock();
|
|
while (m_pending_packets.empty())
|
|
{
|
|
m_pending_lock.unlock(state);
|
|
m_pending_semaphore.block_indefinite();
|
|
state = m_pending_lock.lock();
|
|
}
|
|
auto packet = m_pending_packets.front();
|
|
m_pending_packets.pop();
|
|
m_pending_lock.unlock(state);
|
|
|
|
packet;
|
|
});
|
|
|
|
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>();
|
|
|
|
SpinLockGuard _(m_pending_lock);
|
|
|
|
if (m_pending_packets.full())
|
|
{
|
|
dprintln("arp packet queue full");
|
|
return;
|
|
}
|
|
|
|
m_pending_packets.push({ .interface = interface, .packet = arp_packet });
|
|
m_pending_semaphore.unblock();
|
|
}
|
|
|
|
}
|