2020-01-18 09:38:21 +01:00
/*
2021-03-09 21:57:21 +01:00
* Copyright ( c ) 2018 - 2021 , Andreas Kling < kling @ serenityos . org >
2020-01-18 09:38:21 +01:00
*
2021-04-22 01:24:48 -07:00
* SPDX - License - Identifier : BSD - 2 - Clause
2020-01-18 09:38:21 +01:00
*/
2020-02-16 01:33:41 +01:00
# include <AK/HashMap.h>
2020-08-24 19:35:19 -06:00
# include <AK/Singleton.h>
2021-01-25 16:07:10 +01:00
# include <Kernel/Debug.h>
2021-07-18 10:30:27 +02:00
# include <Kernel/Locking/ProtectedValue.h>
2019-12-28 10:59:52 +11:00
# include <Kernel/Net/LoopbackAdapter.h>
2021-04-30 19:18:23 +02:00
# include <Kernel/Net/NetworkTask.h>
2021-06-04 07:43:16 +03:00
# include <Kernel/Net/NetworkingManagement.h>
2019-06-07 11:43:58 +02:00
# include <Kernel/Net/Routing.h>
2019-08-28 21:58:01 +10:00
# include <Kernel/Thread.h>
2019-04-02 15:46:44 +02:00
2020-02-16 01:27:42 +01:00
namespace Kernel {
2021-08-07 21:34:11 +02:00
static Singleton < ProtectedValue < HashMap < IPv4Address , MACAddress > > > s_arp_table ;
2020-08-24 19:35:19 -06:00
2020-11-29 16:05:27 -07:00
class ARPTableBlocker : public Thread : : Blocker {
public :
ARPTableBlocker ( IPv4Address ip_addr , Optional < MACAddress > & addr ) ;
2021-08-05 20:48:14 +02:00
virtual StringView state_string ( ) const override { return " Routing (ARP) " sv ; }
2020-11-29 16:05:27 -07:00
virtual Type blocker_type ( ) const override { return Type : : Routing ; }
virtual bool should_block ( ) override { return m_should_block ; }
virtual void not_blocking ( bool ) override ;
bool unblock ( bool from_add_blocker , const IPv4Address & ip_addr , const MACAddress & addr )
{
if ( m_ip_addr ! = ip_addr )
return false ;
{
ScopedSpinLock lock ( m_lock ) ;
if ( m_did_unblock )
return false ;
m_did_unblock = true ;
m_addr = addr ;
}
if ( ! from_add_blocker )
unblock_from_blocker ( ) ;
return true ;
}
const IPv4Address & ip_addr ( ) const { return m_ip_addr ; }
private :
const IPv4Address m_ip_addr ;
Optional < MACAddress > & m_addr ;
bool m_did_unblock { false } ;
bool m_should_block { true } ;
} ;
class ARPTableBlockCondition : public Thread : : BlockCondition {
public :
void unblock ( const IPv4Address & ip_addr , const MACAddress & addr )
{
2020-12-22 11:17:56 -07:00
BlockCondition : : unblock ( [ & ] ( auto & b , void * , bool & ) {
2021-02-23 20:42:32 +01:00
VERIFY ( b . blocker_type ( ) = = Thread : : Blocker : : Type : : Routing ) ;
2020-11-29 16:05:27 -07:00
auto & blocker = static_cast < ARPTableBlocker & > ( b ) ;
return blocker . unblock ( false , ip_addr , addr ) ;
} ) ;
}
protected :
virtual bool should_add_blocker ( Thread : : Blocker & b , void * ) override
{
2021-02-23 20:42:32 +01:00
VERIFY ( b . blocker_type ( ) = = Thread : : Blocker : : Type : : Routing ) ;
2020-11-29 16:05:27 -07:00
auto & blocker = static_cast < ARPTableBlocker & > ( b ) ;
2021-07-18 10:30:27 +02:00
auto val = arp_table ( ) . with_shared ( [ & ] ( const auto & table ) - > auto {
return table . get ( blocker . ip_addr ( ) ) ;
} ) ;
2020-11-29 16:05:27 -07:00
if ( ! val . has_value ( ) )
return true ;
return blocker . unblock ( true , blocker . ip_addr ( ) , val . value ( ) ) ;
}
} ;
2021-08-07 21:34:11 +02:00
static Singleton < ARPTableBlockCondition > s_arp_table_block_condition ;
2020-11-29 16:05:27 -07:00
ARPTableBlocker : : ARPTableBlocker ( IPv4Address ip_addr , Optional < MACAddress > & addr )
: m_ip_addr ( ip_addr )
, m_addr ( addr )
{
if ( ! set_block_condition ( * s_arp_table_block_condition ) )
m_should_block = false ;
}
void ARPTableBlocker : : not_blocking ( bool timeout_in_past )
{
2021-02-23 20:42:32 +01:00
VERIFY ( timeout_in_past | | ! m_should_block ) ;
2021-07-18 10:30:27 +02:00
auto addr = arp_table ( ) . with_shared ( [ & ] ( const auto & table ) - > auto {
return table . get ( ip_addr ( ) ) ;
} ) ;
2020-11-29 16:05:27 -07:00
ScopedSpinLock lock ( m_lock ) ;
if ( ! m_did_unblock ) {
m_did_unblock = true ;
m_addr = move ( addr ) ;
}
}
2021-07-18 10:30:27 +02:00
ProtectedValue < HashMap < IPv4Address , MACAddress > > & arp_table ( )
2019-04-02 15:46:44 +02:00
{
2020-08-24 19:35:19 -06:00
return * s_arp_table ;
2019-08-28 21:58:01 +10:00
}
2021-07-24 19:37:54 -04:00
void update_arp_table ( const IPv4Address & ip_addr , const MACAddress & addr , UpdateArp update )
2020-11-29 16:05:27 -07:00
{
2021-07-18 10:30:27 +02:00
arp_table ( ) . with_exclusive ( [ & ] ( auto & table ) {
if ( update = = UpdateArp : : Set )
table . set ( ip_addr , addr ) ;
if ( update = = UpdateArp : : Delete )
table . remove ( ip_addr ) ;
} ) ;
2020-11-29 16:05:27 -07:00
s_arp_table_block_condition - > unblock ( ip_addr , addr ) ;
2021-04-27 00:42:15 +02:00
if constexpr ( ROUTING_DEBUG ) {
2021-07-18 10:30:27 +02:00
arp_table ( ) . with_shared ( [ & ] ( const auto & table ) {
dmesgln ( " ARP table ({} entries): " , table . size ( ) ) ;
for ( auto & it : table )
dmesgln ( " {} :: {} " , it . value . to_string ( ) , it . key . to_string ( ) ) ;
} ) ;
2020-11-29 16:05:27 -07:00
}
}
2019-08-29 11:18:38 +10:00
bool RoutingDecision : : is_zero ( ) const
{
return adapter . is_null ( ) | | next_hop . is_zero ( ) ;
}
2021-05-07 18:55:42 +02:00
static MACAddress multicast_ethernet_address ( IPv4Address const & address )
{
return MACAddress { 0x01 , 0x00 , 0x5e , ( u8 ) ( address [ 1 ] & 0x7f ) , address [ 2 ] , address [ 3 ] } ;
}
2020-04-05 01:16:45 +04:30
RoutingDecision route_to ( const IPv4Address & target , const IPv4Address & source , const RefPtr < NetworkAdapter > through )
2019-08-28 21:58:01 +10:00
{
2020-04-05 01:16:45 +04:30
auto matches = [ & ] ( auto & adapter ) {
if ( ! through )
return true ;
return through = = adapter ;
} ;
auto if_matches = [ & ] ( auto & adapter , const auto & mac ) - > RoutingDecision {
if ( ! matches ( adapter ) )
return { nullptr , { } } ;
return { adapter , mac } ;
} ;
2021-05-12 12:14:03 +02:00
if ( target [ 0 ] = = 0 & & target [ 1 ] = = 0 & & target [ 2 ] = = 0 & & target [ 3 ] = = 0 )
2021-06-04 07:43:16 +03:00
return if_matches ( * NetworkingManagement : : the ( ) . loopback_adapter ( ) , NetworkingManagement : : the ( ) . loopback_adapter ( ) - > mac_address ( ) ) ;
2019-12-28 10:59:52 +11:00
if ( target [ 0 ] = = 127 )
2021-06-04 07:43:16 +03:00
return if_matches ( * NetworkingManagement : : the ( ) . loopback_adapter ( ) , NetworkingManagement : : the ( ) . loopback_adapter ( ) - > mac_address ( ) ) ;
2019-12-28 10:59:52 +11:00
2019-08-28 21:58:01 +10:00
auto target_addr = target . to_u32 ( ) ;
auto source_addr = source . to_u32 ( ) ;
2020-02-08 00:19:46 +01:00
RefPtr < NetworkAdapter > local_adapter = nullptr ;
RefPtr < NetworkAdapter > gateway_adapter = nullptr ;
2019-08-28 21:58:01 +10:00
2021-06-04 07:43:16 +03:00
NetworkingManagement : : the ( ) . for_each ( [ source_addr , & target_addr , & local_adapter , & gateway_adapter , & matches , & through ] ( NetworkAdapter & adapter ) {
2019-08-28 21:58:01 +10:00
auto adapter_addr = adapter . ipv4_address ( ) . to_u32 ( ) ;
auto adapter_mask = adapter . ipv4_netmask ( ) . to_u32 ( ) ;
2021-05-12 14:43:14 +02:00
if ( target_addr = = adapter_addr ) {
2021-06-04 07:43:16 +03:00
local_adapter = NetworkingManagement : : the ( ) . loopback_adapter ( ) ;
2021-05-12 14:43:14 +02:00
return ;
}
2021-05-21 21:47:26 +02:00
if ( ! adapter . link_up ( ) | | ( adapter_addr = = 0 & & ! through ) )
return ;
2019-08-28 21:58:01 +10:00
if ( source_addr ! = 0 & & source_addr ! = adapter_addr )
return ;
2020-04-05 01:16:45 +04:30
if ( ( target_addr & adapter_mask ) = = ( adapter_addr & adapter_mask ) & & matches ( adapter ) )
2020-02-08 00:19:46 +01:00
local_adapter = adapter ;
2019-08-28 21:58:01 +10:00
2020-04-05 01:16:45 +04:30
if ( adapter . ipv4_gateway ( ) . to_u32 ( ) ! = 0 & & matches ( adapter ) )
2020-02-08 00:19:46 +01:00
gateway_adapter = adapter ;
2019-08-28 21:58:01 +10:00
} ) ;
2020-02-09 13:59:38 +01:00
if ( local_adapter & & target = = local_adapter - > ipv4_address ( ) )
return { local_adapter , local_adapter - > mac_address ( ) } ;
2019-08-28 21:58:01 +10:00
if ( ! local_adapter & & ! gateway_adapter ) {
2021-03-09 21:57:21 +01:00
dbgln_if ( ROUTING_DEBUG , " Routing: Couldn't find a suitable adapter for route to {} " , target ) ;
2019-08-28 21:58:01 +10:00
return { nullptr , { } } ;
}
2020-02-08 00:19:46 +01:00
RefPtr < NetworkAdapter > adapter = nullptr ;
2019-08-28 21:58:01 +10:00
IPv4Address next_hop_ip ;
if ( local_adapter ) {
2021-03-09 21:57:21 +01:00
dbgln_if ( ROUTING_DEBUG , " Routing: Got adapter for route (direct): {} ({}/{}) for {} " ,
local_adapter - > name ( ) ,
local_adapter - > ipv4_address ( ) ,
local_adapter - > ipv4_netmask ( ) ,
target ) ;
2019-08-28 21:58:01 +10:00
adapter = local_adapter ;
next_hop_ip = target ;
} else if ( gateway_adapter ) {
2021-03-09 21:57:21 +01:00
dbgln_if ( ROUTING_DEBUG , " Routing: Got adapter for route (using gateway {}): {} ({}/{}) for {} " ,
gateway_adapter - > ipv4_gateway ( ) ,
gateway_adapter - > name ( ) ,
gateway_adapter - > ipv4_address ( ) ,
gateway_adapter - > ipv4_netmask ( ) ,
target ) ;
2019-08-28 21:58:01 +10:00
adapter = gateway_adapter ;
next_hop_ip = gateway_adapter - > ipv4_gateway ( ) ;
} else {
return { nullptr , { } } ;
}
2021-02-15 21:20:15 +03:30
// If it's a broadcast, we already know everything we need to know.
// FIXME: We should also deal with the case where `target_addr` is
// a broadcast to a subnet rather than a full broadcast.
if ( target_addr = = 0xffffffff & & matches ( adapter ) )
return { adapter , { 0xff , 0xff , 0xff , 0xff , 0xff , 0xff } } ;
2021-06-04 07:43:16 +03:00
if ( adapter = = NetworkingManagement : : the ( ) . loopback_adapter ( ) )
2021-05-12 14:43:14 +02:00
return { adapter , adapter - > mac_address ( ) } ;
2021-05-07 18:55:42 +02:00
if ( ( target_addr & IPv4Address { 240 , 0 , 0 , 0 } . to_u32 ( ) ) = = IPv4Address { 224 , 0 , 0 , 0 } . to_u32 ( ) )
return { adapter , multicast_ethernet_address ( target ) } ;
2019-08-28 21:58:01 +10:00
{
2021-07-18 10:30:27 +02:00
auto addr = arp_table ( ) . with_shared ( [ & ] ( const auto & table ) - > auto {
return table . get ( next_hop_ip ) ;
} ) ;
2019-08-28 21:58:01 +10:00
if ( addr . has_value ( ) ) {
2021-03-09 21:57:21 +01:00
dbgln_if ( ROUTING_DEBUG , " Routing: Using cached ARP entry for {} ({}) " , next_hop_ip , addr . value ( ) . to_string ( ) ) ;
2019-08-28 21:58:01 +10:00
return { adapter , addr . value ( ) } ;
}
}
2021-03-09 21:57:21 +01:00
dbgln_if ( ROUTING_DEBUG , " Routing: Sending ARP request via adapter {} for IPv4 address {} " , adapter - > name ( ) , next_hop_ip ) ;
2019-08-28 21:58:01 +10:00
ARPPacket request ;
request . set_operation ( ARPOperation : : Request ) ;
request . set_target_hardware_address ( { 0xff , 0xff , 0xff , 0xff , 0xff , 0xff } ) ;
request . set_target_protocol_address ( next_hop_ip ) ;
request . set_sender_hardware_address ( adapter - > mac_address ( ) ) ;
request . set_sender_protocol_address ( adapter - > ipv4_address ( ) ) ;
adapter - > send ( { 0xff , 0xff , 0xff , 0xff , 0xff , 0xff } , request ) ;
2021-04-30 19:18:23 +02:00
if ( NetworkTask : : is_current ( ) ) {
// FIXME: Waiting for the ARP response from inside the NetworkTask would
// deadlock, so let's hope that whoever called route_to() tries again in a bit.
dbgln_if ( ROUTING_DEBUG , " Routing: Not waiting for ARP response from inside NetworkTask, sent ARP request using adapter {} for {} " , adapter - > name ( ) , target ) ;
return { nullptr , { } } ;
}
2020-11-29 16:05:27 -07:00
Optional < MACAddress > addr ;
2021-01-10 16:29:28 -07:00
if ( ! Thread : : current ( ) - > block < ARPTableBlocker > ( { } , next_hop_ip , addr ) . was_interrupted ( ) ) {
2019-08-28 21:58:01 +10:00
if ( addr . has_value ( ) ) {
2021-03-09 21:57:21 +01:00
dbgln_if ( ROUTING_DEBUG , " Routing: Got ARP response using adapter {} for {} ({}) " ,
adapter - > name ( ) ,
next_hop_ip ,
addr . value ( ) . to_string ( ) ) ;
2019-08-28 21:58:01 +10:00
return { adapter , addr . value ( ) } ;
}
}
2021-03-09 21:57:21 +01:00
dbgln_if ( ROUTING_DEBUG , " Routing: Couldn't find route using adapter {} for {} " , adapter - > name ( ) , target ) ;
2019-08-28 21:58:01 +10:00
return { nullptr , { } } ;
2019-04-02 15:46:44 +02:00
}
2020-02-16 01:27:42 +01:00
}