diff --git a/kernel/include/kernel/Networking/ARPTable.h b/kernel/include/kernel/Networking/ARPTable.h index 0bb7c209f4..63f81fff63 100644 --- a/kernel/include/kernel/Networking/ARPTable.h +++ b/kernel/include/kernel/Networking/ARPTable.h @@ -1,9 +1,8 @@ #pragma once #include -#include -#include #include +#include namespace Kernel { @@ -16,14 +15,28 @@ namespace Kernel public: static BAN::ErrorOr> create(); - BAN::ErrorOr get_mac_from_ipv4(BAN::IPv4Address); + BAN::ErrorOr get_mac_from_ipv4(NetworkInterface&, BAN::IPv4Address); + + void handle_arp_packet(BAN::ConstByteSpan); private: ARPTable(); private: + struct ARPReply + { + BAN::IPv4Address ipv4_address { 0 }; + BAN::MACAddress mac_address; + }; + + private: + SpinLock m_lock; + BAN::HashMap m_arp_table; + BAN::Atomic m_has_got_reply; + ARPReply m_reply; + friend class BAN::UniqPtr; }; diff --git a/kernel/include/kernel/Networking/NetworkInterface.h b/kernel/include/kernel/Networking/NetworkInterface.h index 83efcf28dd..b23c3c1402 100644 --- a/kernel/include/kernel/Networking/NetworkInterface.h +++ b/kernel/include/kernel/Networking/NetworkInterface.h @@ -9,6 +9,20 @@ namespace Kernel { + struct EthernetHeader + { + BAN::MACAddress dst_mac; + BAN::MACAddress src_mac; + BAN::NetworkEndian ether_type; + }; + static_assert(sizeof(EthernetHeader) == 14); + + enum EtherType : uint16_t + { + IPv4 = 0x0800, + ARP = 0x0806, + }; + class NetworkInterface : public CharacterDevice { BAN_NON_COPYABLE(NetworkInterface); diff --git a/kernel/include/kernel/Networking/NetworkSocket.h b/kernel/include/kernel/Networking/NetworkSocket.h index 2c89e9f459..5171a936d8 100644 --- a/kernel/include/kernel/Networking/NetworkSocket.h +++ b/kernel/include/kernel/Networking/NetworkSocket.h @@ -9,6 +9,11 @@ namespace Kernel { + enum NetworkProtocol : uint8_t + { + UDP = 0x11, + }; + class NetworkSocket : public TmpInode, public BAN::Weakable { BAN_NON_COPYABLE(NetworkSocket); @@ -23,7 +28,7 @@ namespace Kernel virtual size_t protocol_header_size() const = 0; virtual void add_protocol_header(BAN::ByteSpan packet, uint16_t src_port, uint16_t dst_port) = 0; - virtual uint8_t protocol() const = 0; + virtual NetworkProtocol protocol() const = 0; virtual void add_packet(BAN::ConstByteSpan, BAN::IPv4Address sender_address, uint16_t sender_port) = 0; diff --git a/kernel/include/kernel/Networking/UDPSocket.h b/kernel/include/kernel/Networking/UDPSocket.h index 785660ce63..9b9fffb7d6 100644 --- a/kernel/include/kernel/Networking/UDPSocket.h +++ b/kernel/include/kernel/Networking/UDPSocket.h @@ -26,7 +26,7 @@ namespace Kernel virtual size_t protocol_header_size() const override { return sizeof(UDPHeader); } virtual void add_protocol_header(BAN::ByteSpan packet, uint16_t src_port, uint16_t dst_port) override; - virtual uint8_t protocol() const override { return 0x11; } + virtual NetworkProtocol protocol() const override { return NetworkProtocol::UDP; } protected: virtual void add_packet(BAN::ConstByteSpan, BAN::IPv4Address sender_addr, uint16_t sender_port) override; diff --git a/kernel/kernel/Networking/ARPTable.cpp b/kernel/kernel/Networking/ARPTable.cpp index d7efdb1bfc..8d6b47ba1c 100644 --- a/kernel/kernel/Networking/ARPTable.cpp +++ b/kernel/kernel/Networking/ARPTable.cpp @@ -1,8 +1,30 @@ +#include #include +#include namespace Kernel { + struct ARPPacket + { + BAN::NetworkEndian htype; + BAN::NetworkEndian ptype; + BAN::NetworkEndian hlen; + BAN::NetworkEndian plen; + BAN::NetworkEndian oper; + BAN::MACAddress sha; + BAN::IPv4Address spa; + BAN::MACAddress tha; + BAN::IPv4Address tpa; + }; + static_assert(sizeof(ARPPacket) == 28); + + 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 }}; @@ -16,12 +38,71 @@ namespace Kernel } - BAN::ErrorOr ARPTable::get_mac_from_ipv4(BAN::IPv4Address ipv4_address) + BAN::ErrorOr ARPTable::get_mac_from_ipv4(NetworkInterface& interface, BAN::IPv4Address ipv4_address) { + LockGuard _(m_lock); + if (ipv4_address == s_broadcast_ipv4) return s_broadcast_mac; - dprintln("No MAC for IPv4 {}", ipv4_address); - return BAN::Error::from_errno(ENOTSUP); + if (m_arp_table.contains(ipv4_address)) + return m_arp_table[ipv4_address]; + + BAN::Vector 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(); + ethernet_header.dst_mac = s_broadcast_mac; + ethernet_header.src_mac = interface.get_mac_address(); + ethernet_header.ether_type = EtherType::ARP; + + auto& arp_request = full_packet.slice(sizeof(EthernetHeader)).as(); + 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_raw_bytes(full_packet)); + + uint64_t timeout = SystemTimer::get().ms_since_boot() + 5'000; + while (!m_has_got_reply) + if (SystemTimer::get().ms_since_boot() >= timeout) + return BAN::Error::from_errno(ETIMEDOUT); + ASSERT_EQ(m_reply.ipv4_address, ipv4_address); + + BAN::MACAddress mac_address = m_reply.mac_address; + (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) + { + auto& arp_packet = buffer.as(); + if (arp_packet.oper != ARPOperation::Reply) + { + dprintln("Unhandled non-rely ARP packet (operation {2H})", (uint16_t)arp_packet.oper); + return; + } + + if (arp_packet.ptype != EtherType::IPv4) + { + 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; } } diff --git a/kernel/kernel/Networking/NetworkInterface.cpp b/kernel/kernel/Networking/NetworkInterface.cpp index fa0af961c9..0656f6d945 100644 --- a/kernel/kernel/Networking/NetworkInterface.cpp +++ b/kernel/kernel/Networking/NetworkInterface.cpp @@ -8,14 +8,6 @@ namespace Kernel { - struct EthernetHeader - { - BAN::MACAddress dst_mac; - BAN::MACAddress src_mac; - BAN::NetworkEndian ether_type; - }; - static_assert(sizeof(EthernetHeader) == 14); - static dev_t get_network_rdev_major() { static dev_t major = DevFileSystem::get().get_next_dev(); diff --git a/kernel/kernel/Networking/NetworkManager.cpp b/kernel/kernel/Networking/NetworkManager.cpp index d58d0ea754..c081666e0d 100644 --- a/kernel/kernel/Networking/NetworkManager.cpp +++ b/kernel/kernel/Networking/NetworkManager.cpp @@ -111,25 +111,49 @@ namespace Kernel void NetworkManager::on_receive(BAN::ConstByteSpan packet) { - // FIXME: properly handle packet parsing + auto ethernet_header = packet.as(); - auto ipv4 = packet.slice(14); - auto& ipv4_header = ipv4.as(); - auto src_ipv4 = ipv4_header.src_address; - - auto udp = ipv4.slice(20); - auto& udp_header = udp.as(); - uint16_t src_port = udp_header.src_port; - uint16_t dst_port = udp_header.dst_port; - - if (!m_bound_sockets.contains(dst_port)) + switch (ethernet_header.ether_type) { - dprintln("no one is listening on port {}", dst_port); - return; - } + case EtherType::ARP: + { + m_arp_table->handle_arp_packet(packet.slice(sizeof(EthernetHeader))); + break; + } + case EtherType::IPv4: + { + auto ipv4 = packet.slice(sizeof(EthernetHeader)); + auto& ipv4_header = ipv4.as(); + auto src_ipv4 = ipv4_header.src_address; + switch (ipv4_header.protocol) + { + case NetworkProtocol::UDP: + { + auto udp = ipv4.slice(sizeof(IPv4Header)); + auto& udp_header = udp.as(); + uint16_t src_port = udp_header.src_port; + uint16_t dst_port = udp_header.dst_port; - auto raw = udp.slice(8); - m_bound_sockets[dst_port].lock()->add_packet(raw, src_ipv4, src_port); + if (!m_bound_sockets.contains(dst_port)) + { + dprintln("no one is listening on port {}", dst_port); + return; + } + + auto raw = udp.slice(8); + m_bound_sockets[dst_port].lock()->add_packet(raw, src_ipv4, src_port); + break; + } + default: + dprintln("Unknown network protocol 0x{2H}", ipv4_header.protocol); + break; + } + break; + } + default: + dprintln("Unknown EtherType 0x{4H}", (uint16_t)ethernet_header.ether_type); + break; + } } } diff --git a/kernel/kernel/Networking/NetworkSocket.cpp b/kernel/kernel/Networking/NetworkSocket.cpp index 63e411956a..e39b530ed7 100644 --- a/kernel/kernel/Networking/NetworkSocket.cpp +++ b/kernel/kernel/Networking/NetworkSocket.cpp @@ -63,7 +63,7 @@ namespace Kernel return BAN::Error::from_errno(EINVAL); auto dst_addr = BAN::IPv4Address(destination->sin_addr.s_addr); - auto dst_mac = TRY(NetworkManager::get().arp_table().get_mac_from_ipv4(dst_addr)); + auto dst_mac = TRY(NetworkManager::get().arp_table().get_mac_from_ipv4(*m_interface, dst_addr)); const size_t interface_header_offset = 0; const size_t interface_header_size = m_interface->interface_header_size();