2020-01-18 11:38:21 +03:00
/*
2021-03-09 23:57:21 +03:00
* Copyright ( c ) 2018 - 2021 , Andreas Kling < kling @ serenityos . org >
2020-01-18 11:38:21 +03:00
*
2021-04-22 11:24:48 +03:00
* SPDX - License - Identifier : BSD - 2 - Clause
2020-01-18 11:38:21 +03:00
*/
2020-02-16 03:33:41 +03:00
# include <AK/HashMap.h>
2020-08-25 04:35:19 +03:00
# include <AK/Singleton.h>
2021-01-25 18:07:10 +03:00
# include <Kernel/Debug.h>
2019-12-28 02:59:52 +03:00
# include <Kernel/Net/LoopbackAdapter.h>
2021-04-30 20:18:23 +03:00
# include <Kernel/Net/NetworkTask.h>
2021-06-04 07:43:16 +03:00
# include <Kernel/Net/NetworkingManagement.h>
2019-06-07 12:43:58 +03:00
# include <Kernel/Net/Routing.h>
2019-08-28 14:58:01 +03:00
# include <Kernel/Thread.h>
2019-04-02 16:46:44 +03:00
2020-02-16 03:27:42 +03:00
namespace Kernel {
2020-08-25 04:35:19 +03:00
static AK : : Singleton < Lockable < HashMap < IPv4Address , MACAddress > > > s_arp_table ;
2020-11-30 02:05:27 +03:00
class ARPTableBlocker : public Thread : : Blocker {
public :
ARPTableBlocker ( IPv4Address ip_addr , Optional < MACAddress > & addr ) ;
virtual const char * state_string ( ) const override { return " Routing (ARP) " ; }
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 21:17:56 +03:00
BlockCondition : : unblock ( [ & ] ( auto & b , void * , bool & ) {
2021-02-23 22:42:32 +03:00
VERIFY ( b . blocker_type ( ) = = Thread : : Blocker : : Type : : Routing ) ;
2020-11-30 02:05:27 +03: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 22:42:32 +03:00
VERIFY ( b . blocker_type ( ) = = Thread : : Blocker : : Type : : Routing ) ;
2020-11-30 02:05:27 +03:00
auto & blocker = static_cast < ARPTableBlocker & > ( b ) ;
auto val = s_arp_table - > resource ( ) . get ( blocker . ip_addr ( ) ) ;
if ( ! val . has_value ( ) )
return true ;
return blocker . unblock ( true , blocker . ip_addr ( ) , val . value ( ) ) ;
}
} ;
static AK : : Singleton < ARPTableBlockCondition > s_arp_table_block_condition ;
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 22:42:32 +03:00
VERIFY ( timeout_in_past | | ! m_should_block ) ;
2020-11-30 02:05:27 +03:00
auto addr = s_arp_table - > resource ( ) . get ( ip_addr ( ) ) ;
ScopedSpinLock lock ( m_lock ) ;
if ( ! m_did_unblock ) {
m_did_unblock = true ;
m_addr = move ( addr ) ;
}
}
2019-08-28 14:58:01 +03:00
Lockable < HashMap < IPv4Address , MACAddress > > & arp_table ( )
2019-04-02 16:46:44 +03:00
{
2020-08-25 04:35:19 +03:00
return * s_arp_table ;
2019-08-28 14:58:01 +03:00
}
2020-11-30 02:05:27 +03:00
void update_arp_table ( const IPv4Address & ip_addr , const MACAddress & addr )
{
2021-04-25 01:27:32 +03:00
Locker locker ( arp_table ( ) . lock ( ) ) ;
2020-11-30 02:05:27 +03:00
arp_table ( ) . resource ( ) . set ( ip_addr , addr ) ;
s_arp_table_block_condition - > unblock ( ip_addr , addr ) ;
2021-04-27 01:42:15 +03:00
if constexpr ( ROUTING_DEBUG ) {
dmesgln ( " ARP table ({} entries): " , arp_table ( ) . resource ( ) . size ( ) ) ;
for ( auto & it : arp_table ( ) . resource ( ) ) {
dmesgln ( " {} :: {} " , it . value . to_string ( ) , it . key . to_string ( ) ) ;
}
2020-11-30 02:05:27 +03:00
}
}
2019-08-29 04:18:38 +03:00
bool RoutingDecision : : is_zero ( ) const
{
return adapter . is_null ( ) | | next_hop . is_zero ( ) ;
}
2021-05-07 19:55:42 +03:00
static MACAddress multicast_ethernet_address ( IPv4Address const & address )
{
return MACAddress { 0x01 , 0x00 , 0x5e , ( u8 ) ( address [ 1 ] & 0x7f ) , address [ 2 ] , address [ 3 ] } ;
}
2020-04-04 23:46:45 +03:00
RoutingDecision route_to ( const IPv4Address & target , const IPv4Address & source , const RefPtr < NetworkAdapter > through )
2019-08-28 14:58:01 +03:00
{
2020-04-04 23:46:45 +03:00
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 13:14:03 +03: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 02:59:52 +03: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 02:59:52 +03:00
2019-08-28 14:58:01 +03:00
auto target_addr = target . to_u32 ( ) ;
auto source_addr = source . to_u32 ( ) ;
2020-02-08 02:19:46 +03:00
RefPtr < NetworkAdapter > local_adapter = nullptr ;
RefPtr < NetworkAdapter > gateway_adapter = nullptr ;
2019-08-28 14:58:01 +03: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 14:58:01 +03:00
auto adapter_addr = adapter . ipv4_address ( ) . to_u32 ( ) ;
auto adapter_mask = adapter . ipv4_netmask ( ) . to_u32 ( ) ;
2021-05-12 15:43:14 +03:00
if ( target_addr = = adapter_addr ) {
2021-06-04 07:43:16 +03:00
local_adapter = NetworkingManagement : : the ( ) . loopback_adapter ( ) ;
2021-05-12 15:43:14 +03:00
return ;
}
2021-05-21 22:47:26 +03:00
if ( ! adapter . link_up ( ) | | ( adapter_addr = = 0 & & ! through ) )
return ;
2019-08-28 14:58:01 +03:00
if ( source_addr ! = 0 & & source_addr ! = adapter_addr )
return ;
2020-04-04 23:46:45 +03:00
if ( ( target_addr & adapter_mask ) = = ( adapter_addr & adapter_mask ) & & matches ( adapter ) )
2020-02-08 02:19:46 +03:00
local_adapter = adapter ;
2019-08-28 14:58:01 +03:00
2020-04-04 23:46:45 +03:00
if ( adapter . ipv4_gateway ( ) . to_u32 ( ) ! = 0 & & matches ( adapter ) )
2020-02-08 02:19:46 +03:00
gateway_adapter = adapter ;
2019-08-28 14:58:01 +03:00
} ) ;
2020-02-09 15:59:38 +03:00
if ( local_adapter & & target = = local_adapter - > ipv4_address ( ) )
return { local_adapter , local_adapter - > mac_address ( ) } ;
2019-08-28 14:58:01 +03:00
if ( ! local_adapter & & ! gateway_adapter ) {
2021-03-09 23:57:21 +03:00
dbgln_if ( ROUTING_DEBUG , " Routing: Couldn't find a suitable adapter for route to {} " , target ) ;
2019-08-28 14:58:01 +03:00
return { nullptr , { } } ;
}
2020-02-08 02:19:46 +03:00
RefPtr < NetworkAdapter > adapter = nullptr ;
2019-08-28 14:58:01 +03:00
IPv4Address next_hop_ip ;
if ( local_adapter ) {
2021-03-09 23:57:21 +03: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 14:58:01 +03:00
adapter = local_adapter ;
next_hop_ip = target ;
} else if ( gateway_adapter ) {
2021-03-09 23:57:21 +03: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 14:58:01 +03:00
adapter = gateway_adapter ;
next_hop_ip = gateway_adapter - > ipv4_gateway ( ) ;
} else {
return { nullptr , { } } ;
}
2021-02-15 20:50:15 +03:00
// 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 15:43:14 +03:00
return { adapter , adapter - > mac_address ( ) } ;
2021-05-07 19:55:42 +03: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 14:58:01 +03:00
{
2021-04-25 01:27:32 +03:00
Locker locker ( arp_table ( ) . lock ( ) ) ;
2019-08-28 14:58:01 +03:00
auto addr = arp_table ( ) . resource ( ) . get ( next_hop_ip ) ;
if ( addr . has_value ( ) ) {
2021-03-09 23:57:21 +03:00
dbgln_if ( ROUTING_DEBUG , " Routing: Using cached ARP entry for {} ({}) " , next_hop_ip , addr . value ( ) . to_string ( ) ) ;
2019-08-28 14:58:01 +03:00
return { adapter , addr . value ( ) } ;
}
}
2021-03-09 23:57:21 +03:00
dbgln_if ( ROUTING_DEBUG , " Routing: Sending ARP request via adapter {} for IPv4 address {} " , adapter - > name ( ) , next_hop_ip ) ;
2019-08-28 14:58:01 +03: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 20:18:23 +03: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-30 02:05:27 +03:00
Optional < MACAddress > addr ;
2021-01-11 02:29:28 +03:00
if ( ! Thread : : current ( ) - > block < ARPTableBlocker > ( { } , next_hop_ip , addr ) . was_interrupted ( ) ) {
2019-08-28 14:58:01 +03:00
if ( addr . has_value ( ) ) {
2021-03-09 23:57:21 +03:00
dbgln_if ( ROUTING_DEBUG , " Routing: Got ARP response using adapter {} for {} ({}) " ,
adapter - > name ( ) ,
next_hop_ip ,
addr . value ( ) . to_string ( ) ) ;
2019-08-28 14:58:01 +03:00
return { adapter , addr . value ( ) } ;
}
}
2021-03-09 23:57:21 +03:00
dbgln_if ( ROUTING_DEBUG , " Routing: Couldn't find route using adapter {} for {} " , adapter - > name ( ) , target ) ;
2019-08-28 14:58:01 +03:00
return { nullptr , { } } ;
2019-04-02 16:46:44 +03:00
}
2020-02-16 03:27:42 +03:00
}