2020-01-18 08:38:21 +00:00
/*
* Copyright ( c ) 2018 - 2020 , Andreas Kling < kling @ serenityos . org >
* All rights reserved .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions are met :
*
* 1. Redistributions of source code must retain the above copyright notice , this
* list of conditions and the following disclaimer .
*
* 2. Redistributions in binary form must reproduce the above copyright notice ,
* this list of conditions and the following disclaimer in the documentation
* and / or other materials provided with the distribution .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS " AS IS "
* AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL
* DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY ,
* OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*/
2020-02-16 00:33:41 +00:00
# include <AK/HashMap.h>
2019-12-27 23:59:52 +00:00
# include <Kernel/Net/LoopbackAdapter.h>
2019-06-07 09:43:58 +00:00
# include <Kernel/Net/Routing.h>
2019-08-28 11:58:01 +00:00
# include <Kernel/Thread.h>
2019-04-02 13:46:44 +00:00
2019-08-28 11:58:01 +00:00
//#define ROUTING_DEBUG
2020-02-16 00:27:42 +00:00
namespace Kernel {
2019-08-28 11:58:01 +00:00
Lockable < HashMap < IPv4Address , MACAddress > > & arp_table ( )
2019-04-02 13:46:44 +00:00
{
2020-08-22 15:53:34 +00:00
static Lockable < HashMap < IPv4Address , MACAddress > > * the ;
if ( ! the )
the = new Lockable < HashMap < IPv4Address , MACAddress > > ;
return * the ;
2019-08-28 11:58:01 +00:00
}
2019-08-29 01:18:38 +00:00
bool RoutingDecision : : is_zero ( ) const
{
return adapter . is_null ( ) | | next_hop . is_zero ( ) ;
}
2020-04-04 20:46:45 +00:00
RoutingDecision route_to ( const IPv4Address & target , const IPv4Address & source , const RefPtr < NetworkAdapter > through )
2019-08-28 11:58:01 +00:00
{
2020-04-04 20:46:45 +00: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 } ;
} ;
2019-12-27 23:59:52 +00:00
if ( target [ 0 ] = = 127 )
2020-04-04 20:46:45 +00:00
return if_matches ( LoopbackAdapter : : the ( ) , LoopbackAdapter : : the ( ) . mac_address ( ) ) ;
2019-12-27 23:59:52 +00:00
2019-08-28 11:58:01 +00:00
auto target_addr = target . to_u32 ( ) ;
auto source_addr = source . to_u32 ( ) ;
2020-02-07 23:19:46 +00:00
RefPtr < NetworkAdapter > local_adapter = nullptr ;
RefPtr < NetworkAdapter > gateway_adapter = nullptr ;
2019-08-28 11:58:01 +00:00
2020-04-04 20:46:45 +00:00
NetworkAdapter : : for_each ( [ source_addr , & target_addr , & local_adapter , & gateway_adapter , & matches ] ( auto & adapter ) {
2019-08-28 11:58:01 +00:00
auto adapter_addr = adapter . ipv4_address ( ) . to_u32 ( ) ;
auto adapter_mask = adapter . ipv4_netmask ( ) . to_u32 ( ) ;
if ( source_addr ! = 0 & & source_addr ! = adapter_addr )
return ;
2020-04-04 20:46:45 +00:00
if ( ( target_addr & adapter_mask ) = = ( adapter_addr & adapter_mask ) & & matches ( adapter ) )
2020-02-07 23:19:46 +00:00
local_adapter = adapter ;
2019-08-28 11:58:01 +00:00
2020-04-04 20:46:45 +00:00
if ( adapter . ipv4_gateway ( ) . to_u32 ( ) ! = 0 & & matches ( adapter ) )
2020-02-07 23:19:46 +00:00
gateway_adapter = adapter ;
2019-08-28 11:58:01 +00:00
} ) ;
2020-02-09 12:59:38 +00:00
if ( local_adapter & & target = = local_adapter - > ipv4_address ( ) )
return { local_adapter , local_adapter - > mac_address ( ) } ;
2019-08-28 11:58:01 +00:00
if ( ! local_adapter & & ! gateway_adapter ) {
# ifdef ROUTING_DEBUG
2020-03-01 19:45:39 +00:00
klog ( ) < < " Routing: Couldn't find a suitable adapter for route to " < < target . to_string ( ) . characters ( ) ;
2019-08-28 11:58:01 +00:00
# endif
return { nullptr , { } } ;
}
2020-02-07 23:19:46 +00:00
RefPtr < NetworkAdapter > adapter = nullptr ;
2019-08-28 11:58:01 +00:00
IPv4Address next_hop_ip ;
if ( local_adapter ) {
# ifdef ROUTING_DEBUG
2020-03-01 19:45:39 +00:00
klog ( ) < < " Routing: Got adapter for route (direct): " < < local_adapter - > name ( ) . characters ( ) < < " ( " < < local_adapter - > ipv4_address ( ) . to_string ( ) . characters ( ) < < " / " < < local_adapter - > ipv4_netmask ( ) . to_string ( ) . characters ( ) < < " ) for " < < target . to_string ( ) . characters ( ) ;
2019-08-28 11:58:01 +00:00
# endif
adapter = local_adapter ;
next_hop_ip = target ;
} else if ( gateway_adapter ) {
# ifdef ROUTING_DEBUG
2020-03-01 19:45:39 +00:00
klog ( ) < < " Routing: Got adapter for route (using gateway " < < gateway_adapter - > ipv4_gateway ( ) . to_string ( ) . characters ( ) < < " ): " < < gateway_adapter - > name ( ) . characters ( ) < < " ( " < < gateway_adapter - > ipv4_address ( ) . to_string ( ) . characters ( ) < < " / " < < gateway_adapter - > ipv4_netmask ( ) . to_string ( ) . characters ( ) < < " ) for " < < target . to_string ( ) . characters ( ) ;
2019-08-28 11:58:01 +00:00
# endif
adapter = gateway_adapter ;
next_hop_ip = gateway_adapter - > ipv4_gateway ( ) ;
} else {
return { nullptr , { } } ;
}
{
LOCKER ( arp_table ( ) . lock ( ) ) ;
auto addr = arp_table ( ) . resource ( ) . get ( next_hop_ip ) ;
if ( addr . has_value ( ) ) {
# ifdef ROUTING_DEBUG
2020-03-01 19:45:39 +00:00
klog ( ) < < " Routing: Using cached ARP entry for " < < next_hop_ip . to_string ( ) . characters ( ) < < " ( " < < addr . value ( ) . to_string ( ) . characters ( ) < < " ) " ;
2019-08-28 11:58:01 +00:00
# endif
return { adapter , addr . value ( ) } ;
}
}
# ifdef ROUTING_DEBUG
2020-03-01 19:45:39 +00:00
klog ( ) < < " Routing: Sending ARP request via adapter " < < adapter - > name ( ) . characters ( ) < < " for IPv4 address " < < next_hop_ip . to_string ( ) . characters ( ) ;
2019-08-28 11:58:01 +00:00
# endif
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 ) ;
2020-06-28 21:34:31 +00:00
( void ) Thread : : current ( ) - > block_until ( " Routing (ARP) " , [ next_hop_ip ] {
2019-08-28 11:58:01 +00:00
return arp_table ( ) . resource ( ) . get ( next_hop_ip ) . has_value ( ) ;
} ) ;
{
LOCKER ( arp_table ( ) . lock ( ) ) ;
auto addr = arp_table ( ) . resource ( ) . get ( next_hop_ip ) ;
if ( addr . has_value ( ) ) {
# ifdef ROUTING_DEBUG
2020-03-01 19:45:39 +00:00
klog ( ) < < " Routing: Got ARP response using adapter " < < adapter - > name ( ) . characters ( ) < < " for " < < next_hop_ip . to_string ( ) . characters ( ) < < " ( " < < addr . value ( ) . to_string ( ) . characters ( ) < < " ) " ;
2019-08-28 11:58:01 +00:00
# endif
return { adapter , addr . value ( ) } ;
}
}
# ifdef ROUTING_DEBUG
2020-03-01 19:45:39 +00:00
klog ( ) < < " Routing: Couldn't find route using adapter " < < adapter - > name ( ) . characters ( ) < < " for " < < target . to_string ( ) . characters ( ) ;
2019-08-28 11:58:01 +00:00
# endif
return { nullptr , { } } ;
2019-04-02 13:46:44 +00:00
}
2020-02-16 00:27:42 +00:00
}