2000-10-23 13:52:54 +00:00
/*
route . c - - routing
2006-04-26 13:52:58 +00:00
Copyright ( C ) 2000 - 2005 Ivo Timmermans ,
2000 - 2006 Guus Sliepen < guus @ tinc - vpn . org >
2000-10-23 13:52:54 +00:00
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 2 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
2004-03-21 14:21:22 +00:00
$ Id $
2000-10-23 13:52:54 +00:00
*/
2003-07-17 15:06:27 +00:00
# include "system.h"
2000-10-23 13:52:54 +00:00
2003-07-17 15:06:27 +00:00
# include "avl_tree.h"
2000-11-20 19:12:17 +00:00
# include "connection.h"
2003-07-18 12:16:24 +00:00
# include "ethernet.h"
# include "ipv4.h"
# include "ipv6.h"
2003-07-06 22:11:37 +00:00
# include "logger.h"
2003-07-17 15:06:27 +00:00
# include "net.h"
# include "protocol.h"
# include "route.h"
# include "subnet.h"
# include "utils.h"
2000-10-23 13:52:54 +00:00
2003-07-22 20:55:21 +00:00
rmode_t routing_mode = RMODE_ROUTER ;
bool priorityinheritance = false ;
2002-03-01 14:09:31 +00:00
int macexpire = 600 ;
2003-07-22 20:55:21 +00:00
bool overwrite_mac = false ;
2003-07-15 16:26:18 +00:00
mac_t mymac = { { 0xFE , 0xFD , 0 , 0 , 0 , 0 } } ;
2000-10-23 13:52:54 +00:00
2003-10-06 13:49:57 +00:00
/* Sizes of various headers */
2003-10-06 13:57:12 +00:00
static const size_t ether_size = sizeof ( struct ether_header ) ;
static const size_t arp_size = sizeof ( struct ether_arp ) ;
static const size_t ip_size = sizeof ( struct ip ) ;
static const size_t icmp_size = sizeof ( struct icmp ) - sizeof ( struct ip ) ;
static const size_t ip6_size = sizeof ( struct ip6_hdr ) ;
static const size_t icmp6_size = sizeof ( struct icmp6_hdr ) ;
static const size_t ns_size = sizeof ( struct nd_neighbor_solicit ) ;
static const size_t opt_size = sizeof ( struct nd_opt_hdr ) ;
2003-10-06 13:49:57 +00:00
2003-03-29 21:51:21 +00:00
/* RFC 1071 */
2004-11-10 19:36:02 +00:00
static uint16_t inet_checksum ( void * data , int len , uint16_t prevsum )
2003-03-29 21:51:21 +00:00
{
uint16_t * p = data ;
uint32_t checksum = prevsum ^ 0xFFFF ;
2003-03-29 22:11:22 +00:00
while ( len > = 2 ) {
2003-03-29 21:51:21 +00:00
checksum + = * p + + ;
2003-03-29 22:11:22 +00:00
len - = 2 ;
}
if ( len )
2003-12-12 19:52:25 +00:00
checksum + = * ( uint8_t * ) p ;
2003-03-29 21:51:21 +00:00
while ( checksum > > 16 )
checksum = ( checksum & 0xFFFF ) + ( checksum > > 16 ) ;
return ~ checksum ;
}
2004-11-10 19:36:02 +00:00
static bool ratelimit ( int frequency ) {
2003-03-29 21:51:21 +00:00
static time_t lasttime = 0 ;
2003-10-06 13:49:57 +00:00
static int count = 0 ;
2003-03-29 21:51:21 +00:00
2003-10-06 13:49:57 +00:00
if ( lasttime = = now ) {
if ( + + count > frequency )
return true ;
} else {
lasttime = now ;
count = 0 ;
}
2003-03-29 21:51:21 +00:00
2003-07-22 20:55:21 +00:00
return false ;
2003-03-29 21:51:21 +00:00
}
2003-12-12 19:52:25 +00:00
2004-11-10 19:36:02 +00:00
static bool checklength ( node_t * source , vpn_packet_t * packet , length_t length ) {
2003-12-12 19:52:25 +00:00
if ( packet - > len < length ) {
ifdebug ( TRAFFIC ) logger ( LOG_WARNING , _ ( " Got too short packet from %s (%s) " ) , source - > name , source - > hostname ) ;
return false ;
} else
return true ;
}
2003-03-29 21:51:21 +00:00
2004-11-10 19:36:02 +00:00
static void learn_mac ( mac_t * address )
2000-10-23 13:52:54 +00:00
{
2002-09-09 21:25:28 +00:00
subnet_t * subnet ;
avl_node_t * node ;
connection_t * c ;
cp ( ) ;
subnet = lookup_subnet_mac ( address ) ;
/* If we don't know this MAC address yet, store it */
2003-12-12 19:52:25 +00:00
if ( ! subnet ) {
2003-07-12 17:41:48 +00:00
ifdebug ( TRAFFIC ) logger ( LOG_INFO , _ ( " Learned new MAC address %hx:%hx:%hx:%hx:%hx:%hx " ) ,
2002-09-09 21:25:28 +00:00
address - > x [ 0 ] , address - > x [ 1 ] , address - > x [ 2 ] , address - > x [ 3 ] ,
address - > x [ 4 ] , address - > x [ 5 ] ) ;
subnet = new_subnet ( ) ;
subnet - > type = SUBNET_MAC ;
2003-12-12 19:52:25 +00:00
subnet - > expires = now + macexpire ;
subnet - > net . mac . address = * address ;
2002-09-09 21:25:28 +00:00
subnet_add ( myself , subnet ) ;
/* And tell all other tinc daemons it's our MAC */
for ( node = connection_tree - > head ; node ; node = node - > next ) {
2003-08-28 21:05:11 +00:00
c = node - > data ;
2002-09-09 21:25:28 +00:00
if ( c - > status . active )
send_add_subnet ( c , subnet ) ;
}
}
2003-12-12 19:52:25 +00:00
if ( subnet - > expires )
subnet - > expires = now + macexpire ;
2002-03-01 14:09:31 +00:00
}
2003-12-12 19:52:25 +00:00
void age_subnets ( void )
2002-03-01 14:09:31 +00:00
{
2002-09-09 21:25:28 +00:00
subnet_t * s ;
connection_t * c ;
avl_node_t * node , * next , * node2 ;
cp ( ) ;
for ( node = myself - > subnet_tree - > head ; node ; node = next ) {
next = node - > next ;
2003-08-28 21:05:11 +00:00
s = node - > data ;
2003-12-12 19:52:25 +00:00
if ( s - > expires & & s - > expires < now ) {
ifdebug ( TRAFFIC ) {
char netstr [ MAXNETSTR ] ;
if ( net2str ( netstr , sizeof netstr , s ) )
logger ( LOG_INFO , _ ( " Subnet %s expired " ) , netstr ) ;
}
2002-09-09 21:25:28 +00:00
for ( node2 = connection_tree - > head ; node2 ; node2 = node2 - > next ) {
2003-08-28 21:05:11 +00:00
c = node2 - > data ;
2002-09-09 21:25:28 +00:00
if ( c - > status . active )
send_del_subnet ( c , s ) ;
}
subnet_del ( myself , s ) ;
}
2002-03-01 14:09:31 +00:00
}
2001-01-07 15:25:49 +00:00
}
2004-11-10 19:36:02 +00:00
static void route_mac ( node_t * source , vpn_packet_t * packet )
2001-01-07 15:25:49 +00:00
{
2002-09-09 21:25:28 +00:00
subnet_t * subnet ;
2006-08-09 22:31:10 +00:00
mac_t dest ;
2002-09-09 21:25:28 +00:00
cp ( ) ;
2006-08-09 22:31:10 +00:00
2002-09-09 21:25:28 +00:00
/* Learn source address */
2006-08-09 22:31:10 +00:00
if ( source = = myself ) {
mac_t src ;
memcpy ( & src , & packet - > data [ 6 ] , sizeof src ) ;
learn_mac ( & src ) ;
}
2002-09-09 21:25:28 +00:00
/* Lookup destination address */
2006-08-09 22:31:10 +00:00
memcpy ( & dest , & packet - > data [ 0 ] , sizeof dest ) ;
subnet = lookup_subnet_mac ( & dest ) ;
2002-09-09 21:25:28 +00:00
2003-12-12 19:52:25 +00:00
if ( ! subnet ) {
broadcast_packet ( source , packet ) ;
return ;
}
if ( subnet - > owner = = source ) {
ifdebug ( TRAFFIC ) logger ( LOG_WARNING , _ ( " Packet looping back to %s (%s)! " ) , source - > name , source - > hostname ) ;
return ;
}
send_packet ( subnet - > owner , packet ) ;
2000-11-04 22:57:33 +00:00
}
2003-03-29 21:51:21 +00:00
/* RFC 792 */
2003-12-20 19:47:53 +00:00
static void route_ipv4_unreachable ( node_t * source , vpn_packet_t * packet , uint8_t type , uint8_t code )
2003-03-29 21:51:21 +00:00
{
2003-12-12 19:52:25 +00:00
struct ip ip = { 0 } ;
struct icmp icmp = { 0 } ;
2003-03-29 21:51:21 +00:00
struct in_addr ip_src ;
struct in_addr ip_dst ;
uint32_t oldlen ;
2003-10-06 13:49:57 +00:00
if ( ratelimit ( 3 ) )
2003-03-29 21:51:21 +00:00
return ;
cp ( ) ;
2003-10-06 13:49:57 +00:00
/* Copy headers from packet into properly aligned structs on the stack */
memcpy ( & ip , packet - > data + ether_size , ip_size ) ;
2003-03-29 21:51:21 +00:00
/* Remember original source and destination */
2003-12-12 19:52:25 +00:00
ip_src = ip . ip_src ;
ip_dst = ip . ip_dst ;
2003-10-06 13:49:57 +00:00
oldlen = packet - > len - ether_size ;
2003-12-12 19:52:25 +00:00
2003-12-20 19:47:53 +00:00
if ( type = = ICMP_DEST_UNREACH & & code = = ICMP_FRAG_NEEDED )
icmp . icmp_nextmtu = htons ( packet - > len - ether_size ) ;
2003-10-06 13:49:57 +00:00
if ( oldlen > = IP_MSS - ip_size - icmp_size )
oldlen = IP_MSS - ip_size - icmp_size ;
2003-03-29 21:51:21 +00:00
/* Copy first part of original contents to ICMP message */
2003-10-06 13:49:57 +00:00
memmove ( packet - > data + ether_size + ip_size + icmp_size , packet - > data + ether_size , oldlen ) ;
2003-03-29 21:51:21 +00:00
/* Fill in IPv4 header */
2003-10-06 13:49:57 +00:00
ip . ip_v = 4 ;
ip . ip_hl = ip_size / 4 ;
ip . ip_tos = 0 ;
ip . ip_len = htons ( ip_size + icmp_size + oldlen ) ;
ip . ip_id = 0 ;
ip . ip_off = 0 ;
ip . ip_ttl = 255 ;
ip . ip_p = IPPROTO_ICMP ;
ip . ip_sum = 0 ;
2003-12-12 19:52:25 +00:00
ip . ip_src = ip_dst ;
ip . ip_dst = ip_src ;
2003-10-06 13:49:57 +00:00
ip . ip_sum = inet_checksum ( & ip , ip_size , ~ 0 ) ;
2003-03-29 21:51:21 +00:00
/* Fill in ICMP header */
2003-12-20 19:47:53 +00:00
icmp . icmp_type = type ;
2003-10-06 13:49:57 +00:00
icmp . icmp_code = code ;
icmp . icmp_cksum = 0 ;
2003-03-29 21:51:21 +00:00
2003-10-06 13:49:57 +00:00
icmp . icmp_cksum = inet_checksum ( & icmp , icmp_size , ~ 0 ) ;
icmp . icmp_cksum = inet_checksum ( packet - > data + ether_size + ip_size + icmp_size , oldlen , icmp . icmp_cksum ) ;
/* Copy structs on stack back to packet */
memcpy ( packet - > data + ether_size , & ip , ip_size ) ;
memcpy ( packet - > data + ether_size + ip_size , & icmp , icmp_size ) ;
2003-03-29 21:51:21 +00:00
2003-10-06 13:49:57 +00:00
packet - > len = ether_size + ip_size + icmp_size + oldlen ;
2003-12-20 19:47:53 +00:00
2003-12-12 19:52:25 +00:00
send_packet ( source , packet ) ;
2003-03-29 21:51:21 +00:00
}
2003-12-22 11:04:17 +00:00
/* RFC 791 */
2004-11-10 19:36:02 +00:00
static void fragment_ipv4_packet ( node_t * dest , vpn_packet_t * packet ) {
2003-12-22 11:04:17 +00:00
struct ip ip ;
vpn_packet_t fragment ;
int len , maxlen , todo ;
uint8_t * offset ;
uint16_t ip_off , origf ;
cp ( ) ;
memcpy ( & ip , packet - > data + ether_size , ip_size ) ;
fragment . priority = packet - > priority ;
if ( ip . ip_hl ! = ip_size / 4 )
return ;
todo = ntohs ( ip . ip_len ) - ip_size ;
if ( ether_size + ip_size + todo ! = packet - > len ) {
2006-08-08 13:29:17 +00:00
ifdebug ( TRAFFIC ) logger ( LOG_WARNING , _ ( " Length of packet (%d) doesn't match length in IPv4 header (%zd) " ) , packet - > len , ether_size + ip_size + todo ) ;
2003-12-22 11:04:17 +00:00
return ;
}
ifdebug ( TRAFFIC ) logger ( LOG_INFO , _ ( " Fragmenting packet of %d bytes to %s (%s) " ) , packet - > len , dest - > name , dest - > hostname ) ;
offset = packet - > data + ether_size + ip_size ;
maxlen = ( dest - > mtu - ether_size - ip_size ) & ~ 0x7 ;
ip_off = ntohs ( ip . ip_off ) ;
origf = ip_off & ~ IP_OFFMASK ;
ip_off & = IP_OFFMASK ;
while ( todo ) {
len = todo > maxlen ? maxlen : todo ;
memcpy ( fragment . data + ether_size + ip_size , offset , len ) ;
todo - = len ;
offset + = len ;
ip . ip_len = htons ( ip_size + len ) ;
ip . ip_off = htons ( ip_off | origf | ( todo ? IP_MF : 0 ) ) ;
ip . ip_sum = 0 ;
ip . ip_sum = inet_checksum ( & ip , ip_size , ~ 0 ) ;
memcpy ( fragment . data , packet - > data , ether_size ) ;
memcpy ( fragment . data + ether_size , & ip , ip_size ) ;
fragment . len = ether_size + ip_size + len ;
send_packet ( dest , & fragment ) ;
ip_off + = len / 8 ;
}
}
2004-11-10 19:36:02 +00:00
static void route_ipv4_unicast ( node_t * source , vpn_packet_t * packet )
2002-02-10 21:57:54 +00:00
{
2002-09-09 21:25:28 +00:00
subnet_t * subnet ;
2003-12-24 10:48:15 +00:00
node_t * via ;
2006-08-09 22:31:10 +00:00
ipv4_t dest ;
2002-09-09 21:25:28 +00:00
cp ( ) ;
2006-08-09 22:31:10 +00:00
memcpy ( & dest , & packet - > data [ 30 ] , sizeof dest ) ;
subnet = lookup_subnet_ipv4 ( & dest ) ;
2002-09-09 21:25:28 +00:00
if ( ! subnet ) {
2003-12-12 19:52:25 +00:00
ifdebug ( TRAFFIC ) logger ( LOG_WARNING , _ ( " Cannot route packet from %s (%s): unknown IPv4 destination address %d.%d.%d.%d " ) ,
source - > name , source - > hostname ,
2006-08-09 22:31:10 +00:00
dest . x [ 0 ] ,
dest . x [ 1 ] ,
dest . x [ 2 ] ,
dest . x [ 3 ] ) ;
2003-12-12 19:52:25 +00:00
2003-12-20 19:47:53 +00:00
route_ipv4_unreachable ( source , packet , ICMP_DEST_UNREACH , ICMP_NET_UNKNOWN ) ;
2003-12-12 19:52:25 +00:00
return ;
2002-09-09 21:25:28 +00:00
}
2003-03-29 21:51:21 +00:00
2003-12-12 19:52:25 +00:00
if ( subnet - > owner = = source ) {
ifdebug ( TRAFFIC ) logger ( LOG_WARNING , _ ( " Packet looping back to %s (%s)! " ) , source - > name , source - > hostname ) ;
return ;
}
2003-03-29 21:51:21 +00:00
if ( ! subnet - > owner - > status . reachable )
2003-12-20 19:47:53 +00:00
route_ipv4_unreachable ( source , packet , ICMP_DEST_UNREACH , ICMP_NET_UNREACH ) ;
2003-12-22 11:04:17 +00:00
if ( priorityinheritance )
packet - > priority = packet - > data [ 15 ] ;
2003-12-24 10:48:15 +00:00
via = ( subnet - > owner - > via = = myself ) ? subnet - > owner - > nexthop : subnet - > owner - > via ;
2004-11-10 18:10:59 +00:00
if ( via & & packet - > len > via - > mtu & & via ! = myself ) {
2003-12-24 10:48:15 +00:00
ifdebug ( TRAFFIC ) logger ( LOG_INFO , _ ( " Packet for %s (%s) length %d larger than MTU %d " ) , subnet - > owner - > name , subnet - > owner - > hostname , packet - > len , via - > mtu ) ;
2003-12-22 11:04:17 +00:00
if ( packet - > data [ 20 ] & 0x40 ) {
2003-12-24 10:48:15 +00:00
packet - > len = via - > mtu ;
2003-12-22 11:04:17 +00:00
route_ipv4_unreachable ( source , packet , ICMP_DEST_UNREACH , ICMP_FRAG_NEEDED ) ;
} else {
2003-12-24 10:48:15 +00:00
fragment_ipv4_packet ( via , packet ) ;
2003-12-22 11:04:17 +00:00
}
2003-12-20 19:47:53 +00:00
return ;
}
2003-12-12 19:52:25 +00:00
send_packet ( subnet - > owner , packet ) ;
}
2004-11-10 19:36:02 +00:00
static void route_ipv4 ( node_t * source , vpn_packet_t * packet )
2003-12-12 19:52:25 +00:00
{
cp ( ) ;
if ( ! checklength ( source , packet , ether_size + ip_size ) )
return ;
2008-12-23 22:31:38 +00:00
if ( packet - > data [ 30 ] & 0xf0 = = 0xe0 | |
packet - > data [ 30 ] = = 255 & &
packet - > data [ 31 ] = = 255 & &
packet - > data [ 32 ] = = 255 & &
packet - > data [ 33 ] = = 255 )
broadcast_packet ( source , packet ) ;
else
route_ipv4_unicast ( source , packet ) ;
2000-10-23 13:52:54 +00:00
}
2003-03-29 21:51:21 +00:00
/* RFC 2463 */
2003-12-20 19:47:53 +00:00
static void route_ipv6_unreachable ( node_t * source , vpn_packet_t * packet , uint8_t type , uint8_t code )
2003-03-29 21:51:21 +00:00
{
2003-10-06 13:49:57 +00:00
struct ip6_hdr ip6 ;
2003-12-12 19:52:25 +00:00
struct icmp6_hdr icmp6 = { 0 } ;
2003-03-29 21:51:21 +00:00
uint16_t checksum ;
struct {
struct in6_addr ip6_src ; /* source address */
struct in6_addr ip6_dst ; /* destination address */
uint32_t length ;
uint32_t next ;
} pseudo ;
2003-10-06 13:49:57 +00:00
if ( ratelimit ( 3 ) )
2003-03-29 21:51:21 +00:00
return ;
cp ( ) ;
2003-10-06 13:49:57 +00:00
/* Copy headers from packet to structs on the stack */
memcpy ( & ip6 , packet - > data + ether_size , ip6_size ) ;
2003-03-29 21:51:21 +00:00
/* Remember original source and destination */
2003-12-12 19:52:25 +00:00
pseudo . ip6_src = ip6 . ip6_dst ;
pseudo . ip6_dst = ip6 . ip6_src ;
2003-10-06 13:49:57 +00:00
2003-12-12 19:52:25 +00:00
pseudo . length = packet - > len - ether_size ;
2003-12-20 19:47:53 +00:00
if ( type = = ICMP6_PACKET_TOO_BIG )
icmp6 . icmp6_mtu = htonl ( pseudo . length ) ;
2003-03-29 21:51:21 +00:00
2003-10-06 13:49:57 +00:00
if ( pseudo . length > = IP_MSS - ip6_size - icmp6_size )
pseudo . length = IP_MSS - ip6_size - icmp6_size ;
2003-03-29 21:51:21 +00:00
/* Copy first part of original contents to ICMP message */
2003-10-06 13:49:57 +00:00
memmove ( packet - > data + ether_size + ip6_size + icmp6_size , packet - > data + ether_size , pseudo . length ) ;
2003-03-29 21:51:21 +00:00
/* Fill in IPv6 header */
2003-10-06 13:49:57 +00:00
ip6 . ip6_flow = htonl ( 0x60000000UL ) ;
ip6 . ip6_plen = htons ( icmp6_size + pseudo . length ) ;
ip6 . ip6_nxt = IPPROTO_ICMPV6 ;
ip6 . ip6_hlim = 255 ;
2003-12-12 19:52:25 +00:00
ip6 . ip6_src = pseudo . ip6_src ;
ip6 . ip6_dst = pseudo . ip6_dst ;
2003-03-29 21:51:21 +00:00
/* Fill in ICMP header */
2003-12-20 19:47:53 +00:00
icmp6 . icmp6_type = type ;
2003-10-06 13:49:57 +00:00
icmp6 . icmp6_code = code ;
icmp6 . icmp6_cksum = 0 ;
2003-03-29 21:51:21 +00:00
/* Create pseudo header */
2003-10-06 13:49:57 +00:00
pseudo . length = htonl ( icmp6_size + pseudo . length ) ;
2003-03-29 21:51:21 +00:00
pseudo . next = htonl ( IPPROTO_ICMPV6 ) ;
/* Generate checksum */
checksum = inet_checksum ( & pseudo , sizeof ( pseudo ) , ~ 0 ) ;
2003-10-06 13:49:57 +00:00
checksum = inet_checksum ( & icmp6 , icmp6_size , checksum ) ;
checksum = inet_checksum ( packet - > data + ether_size + ip6_size + icmp6_size , ntohl ( pseudo . length ) - icmp6_size , checksum ) ;
2003-03-29 21:51:21 +00:00
2003-10-06 13:49:57 +00:00
icmp6 . icmp6_cksum = checksum ;
/* Copy structs on stack back to packet */
memcpy ( packet - > data + ether_size , & ip6 , ip6_size ) ;
memcpy ( packet - > data + ether_size + ip6_size , & icmp6 , icmp6_size ) ;
2003-03-29 21:51:21 +00:00
2003-10-06 13:49:57 +00:00
packet - > len = ether_size + ip6_size + ntohl ( pseudo . length ) ;
2003-03-29 21:51:21 +00:00
2003-12-12 19:52:25 +00:00
send_packet ( source , packet ) ;
2003-03-29 21:51:21 +00:00
}
2004-11-10 19:36:02 +00:00
static void route_ipv6_unicast ( node_t * source , vpn_packet_t * packet )
2000-10-23 13:52:54 +00:00
{
2002-09-09 21:25:28 +00:00
subnet_t * subnet ;
2003-12-24 10:48:15 +00:00
node_t * via ;
2006-08-09 22:31:10 +00:00
ipv6_t dest ;
2002-09-09 21:25:28 +00:00
cp ( ) ;
2006-08-09 22:31:10 +00:00
memcpy ( & dest , & packet - > data [ 38 ] , sizeof dest ) ;
subnet = lookup_subnet_ipv6 ( & dest ) ;
2002-09-09 21:25:28 +00:00
if ( ! subnet ) {
2003-12-12 19:52:25 +00:00
ifdebug ( TRAFFIC ) logger ( LOG_WARNING , _ ( " Cannot route packet from %s (%s): unknown IPv6 destination address %hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx " ) ,
source - > name , source - > hostname ,
2006-08-09 22:31:10 +00:00
ntohs ( dest . x [ 0 ] ) ,
ntohs ( dest . x [ 1 ] ) ,
ntohs ( dest . x [ 2 ] ) ,
ntohs ( dest . x [ 3 ] ) ,
ntohs ( dest . x [ 4 ] ) ,
ntohs ( dest . x [ 5 ] ) ,
ntohs ( dest . x [ 6 ] ) ,
ntohs ( dest . x [ 7 ] ) ) ;
2003-12-12 19:52:25 +00:00
2003-12-20 19:47:53 +00:00
route_ipv6_unreachable ( source , packet , ICMP6_DST_UNREACH , ICMP6_DST_UNREACH_ADDR ) ;
2003-12-12 19:52:25 +00:00
return ;
}
if ( subnet - > owner = = source ) {
ifdebug ( TRAFFIC ) logger ( LOG_WARNING , _ ( " Packet looping back to %s (%s)! " ) , source - > name , source - > hostname ) ;
return ;
2002-09-09 21:25:28 +00:00
}
2003-03-29 21:51:21 +00:00
if ( ! subnet - > owner - > status . reachable )
2003-12-20 19:47:53 +00:00
route_ipv6_unreachable ( source , packet , ICMP6_DST_UNREACH , ICMP6_DST_UNREACH_NOROUTE ) ;
2003-12-24 10:48:15 +00:00
via = ( subnet - > owner - > via = = myself ) ? subnet - > owner - > nexthop : subnet - > owner - > via ;
2003-07-06 17:49:49 +00:00
2004-11-10 18:10:59 +00:00
if ( via & & packet - > len > via - > mtu & & via ! = myself ) {
2003-12-24 10:48:15 +00:00
ifdebug ( TRAFFIC ) logger ( LOG_INFO , _ ( " Packet for %s (%s) length %d larger than MTU %d " ) , subnet - > owner - > name , subnet - > owner - > hostname , packet - > len , via - > mtu ) ;
packet - > len = via - > mtu ;
2003-12-20 19:47:53 +00:00
route_ipv6_unreachable ( source , packet , ICMP6_PACKET_TOO_BIG , 0 ) ;
return ;
}
2003-12-12 19:52:25 +00:00
send_packet ( subnet - > owner , packet ) ;
}
2003-03-29 21:51:21 +00:00
/* RFC 2461 */
2002-03-12 14:25:04 +00:00
2003-12-12 19:52:25 +00:00
static void route_neighborsol ( node_t * source , vpn_packet_t * packet )
2002-03-11 13:14:53 +00:00
{
2003-10-06 13:49:57 +00:00
struct ip6_hdr ip6 ;
struct nd_neighbor_solicit ns ;
struct nd_opt_hdr opt ;
2002-09-09 21:25:28 +00:00
subnet_t * subnet ;
uint16_t checksum ;
struct {
struct in6_addr ip6_src ; /* source address */
struct in6_addr ip6_dst ; /* destination address */
uint32_t length ;
2003-03-29 21:51:21 +00:00
uint32_t next ;
2002-09-09 21:25:28 +00:00
} pseudo ;
cp ( ) ;
2003-12-12 19:52:25 +00:00
if ( ! checklength ( source , packet , ether_size + ip6_size + ns_size + opt_size + ETH_ALEN ) )
return ;
if ( source ! = myself ) {
ifdebug ( TRAFFIC ) logger ( LOG_WARNING , _ ( " Got neighbor solicitation request from %s (%s) while in router mode! " ) , source - > name , source - > hostname ) ;
return ;
}
2003-10-06 13:49:57 +00:00
/* Copy headers from packet to structs on the stack */
memcpy ( & ip6 , packet - > data + ether_size , ip6_size ) ;
memcpy ( & ns , packet - > data + ether_size + ip6_size , ns_size ) ;
memcpy ( & opt , packet - > data + ether_size + ip6_size + ns_size , opt_size ) ;
2002-09-09 21:25:28 +00:00
/* First, snatch the source address from the neighbor solicitation packet */
2003-06-11 19:28:38 +00:00
if ( overwrite_mac )
2003-10-06 13:49:57 +00:00
memcpy ( mymac . x , packet - > data + ETH_ALEN , ETH_ALEN ) ;
2002-09-09 21:25:28 +00:00
/* Check if this is a valid neighbor solicitation request */
2003-10-06 13:49:57 +00:00
if ( ns . nd_ns_hdr . icmp6_type ! = ND_NEIGHBOR_SOLICIT | |
opt . nd_opt_type ! = ND_OPT_SOURCE_LINKADDR ) {
2003-07-12 17:41:48 +00:00
ifdebug ( TRAFFIC ) logger ( LOG_WARNING , _ ( " Cannot route packet: received unknown type neighbor solicitation request " ) ) ;
2002-09-09 21:25:28 +00:00
return ;
}
/* Create pseudo header */
2003-12-12 19:52:25 +00:00
pseudo . ip6_src = ip6 . ip6_src ;
pseudo . ip6_dst = ip6 . ip6_dst ;
2003-10-06 13:49:57 +00:00
pseudo . length = htonl ( ns_size + opt_size + ETH_ALEN ) ;
2003-03-29 21:51:21 +00:00
pseudo . next = htonl ( IPPROTO_ICMPV6 ) ;
2002-09-09 21:25:28 +00:00
/* Generate checksum */
2003-03-29 21:51:21 +00:00
checksum = inet_checksum ( & pseudo , sizeof ( pseudo ) , ~ 0 ) ;
2003-10-06 13:49:57 +00:00
checksum = inet_checksum ( & ns , ns_size , checksum ) ;
checksum = inet_checksum ( & opt , opt_size , checksum ) ;
2003-12-08 12:00:40 +00:00
checksum = inet_checksum ( packet - > data + ether_size + ip6_size + ns_size + opt_size , ETH_ALEN , checksum ) ;
2002-09-09 21:25:28 +00:00
if ( checksum ) {
2003-07-12 17:41:48 +00:00
ifdebug ( TRAFFIC ) logger ( LOG_WARNING , _ ( " Cannot route packet: checksum error for neighbor solicitation request " ) ) ;
2002-09-09 21:25:28 +00:00
return ;
}
/* Check if the IPv6 address exists on the VPN */
2003-10-06 13:49:57 +00:00
subnet = lookup_subnet_ipv6 ( ( ipv6_t * ) & ns . nd_ns_target ) ;
2002-09-09 21:25:28 +00:00
if ( ! subnet ) {
2003-07-12 17:41:48 +00:00
ifdebug ( TRAFFIC ) logger ( LOG_WARNING , _ ( " Cannot route packet: neighbor solicitation request for unknown address %hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx " ) ,
2003-10-06 13:49:57 +00:00
ntohs ( ( ( uint16_t * ) & ns . nd_ns_target ) [ 0 ] ) ,
ntohs ( ( ( uint16_t * ) & ns . nd_ns_target ) [ 1 ] ) ,
ntohs ( ( ( uint16_t * ) & ns . nd_ns_target ) [ 2 ] ) ,
ntohs ( ( ( uint16_t * ) & ns . nd_ns_target ) [ 3 ] ) ,
ntohs ( ( ( uint16_t * ) & ns . nd_ns_target ) [ 4 ] ) ,
ntohs ( ( ( uint16_t * ) & ns . nd_ns_target ) [ 5 ] ) ,
ntohs ( ( ( uint16_t * ) & ns . nd_ns_target ) [ 6 ] ) ,
ntohs ( ( ( uint16_t * ) & ns . nd_ns_target ) [ 7 ] ) ) ;
2002-09-09 21:25:28 +00:00
return ;
}
/* Check if it is for our own subnet */
if ( subnet - > owner = = myself )
return ; /* silently ignore */
/* Create neighbor advertation reply */
2003-10-06 13:49:57 +00:00
memcpy ( packet - > data , packet - > data + ETH_ALEN , ETH_ALEN ) ; /* copy destination address */
packet - > data [ ETH_ALEN * 2 - 1 ] ^ = 0xFF ; /* mangle source address so it looks like it's not from us */
2002-09-09 21:25:28 +00:00
2003-12-12 19:52:25 +00:00
ip6 . ip6_dst = ip6 . ip6_src ; /* swap destination and source protocoll address */
ip6 . ip6_src = ns . nd_ns_target ;
2002-09-09 21:25:28 +00:00
2003-12-08 12:00:40 +00:00
memcpy ( packet - > data + ether_size + ip6_size + ns_size + opt_size , packet - > data + ETH_ALEN , ETH_ALEN ) ; /* add fake source hard addr */
2002-09-09 21:25:28 +00:00
2003-10-06 13:49:57 +00:00
ns . nd_ns_cksum = 0 ;
ns . nd_ns_type = ND_NEIGHBOR_ADVERT ;
ns . nd_ns_reserved = htonl ( 0x40000000UL ) ; /* Set solicited flag */
opt . nd_opt_type = ND_OPT_TARGET_LINKADDR ;
2002-09-09 21:25:28 +00:00
/* Create pseudo header */
2003-12-12 19:52:25 +00:00
pseudo . ip6_src = ip6 . ip6_src ;
pseudo . ip6_dst = ip6 . ip6_dst ;
2003-10-06 13:49:57 +00:00
pseudo . length = htonl ( ns_size + opt_size + ETH_ALEN ) ;
2003-03-29 21:51:21 +00:00
pseudo . next = htonl ( IPPROTO_ICMPV6 ) ;
2002-09-09 21:25:28 +00:00
/* Generate checksum */
2003-03-29 21:51:21 +00:00
checksum = inet_checksum ( & pseudo , sizeof ( pseudo ) , ~ 0 ) ;
2003-10-06 13:49:57 +00:00
checksum = inet_checksum ( & ns , ns_size , checksum ) ;
checksum = inet_checksum ( & opt , opt_size , checksum ) ;
2003-12-08 12:00:40 +00:00
checksum = inet_checksum ( packet - > data + ether_size + ip6_size + ns_size + opt_size , ETH_ALEN , checksum ) ;
2003-10-06 13:49:57 +00:00
ns . nd_ns_hdr . icmp6_cksum = checksum ;
2002-09-09 21:25:28 +00:00
2003-10-06 13:49:57 +00:00
/* Copy structs on stack back to packet */
memcpy ( packet - > data + ether_size , & ip6 , ip6_size ) ;
memcpy ( packet - > data + ether_size + ip6_size , & ns , ns_size ) ;
memcpy ( packet - > data + ether_size + ip6_size + ns_size , & opt , opt_size ) ;
2002-09-09 21:25:28 +00:00
2003-12-12 19:52:25 +00:00
send_packet ( source , packet ) ;
}
2004-11-10 19:36:02 +00:00
static void route_ipv6 ( node_t * source , vpn_packet_t * packet )
2003-12-12 19:52:25 +00:00
{
cp ( ) ;
if ( ! checklength ( source , packet , ether_size + ip6_size ) )
return ;
if ( packet - > data [ 20 ] = = IPPROTO_ICMPV6 & & checklength ( source , packet , ether_size + ip6_size + icmp6_size ) & & packet - > data [ 54 ] = = ND_NEIGHBOR_SOLICIT ) {
route_neighborsol ( source , packet ) ;
return ;
}
2008-12-23 22:31:38 +00:00
if ( packet - > data [ 38 ] = = 255 )
broadcast_packet ( source , packet ) ;
else
route_ipv6_unicast ( source , packet ) ;
2002-03-11 13:14:53 +00:00
}
2003-03-29 21:51:21 +00:00
/* RFC 826 */
2003-12-12 19:52:25 +00:00
static void route_arp ( node_t * source , vpn_packet_t * packet )
2001-06-04 11:14:35 +00:00
{
2003-10-06 13:49:57 +00:00
struct ether_arp arp ;
2002-09-09 21:25:28 +00:00
subnet_t * subnet ;
2003-10-06 13:49:57 +00:00
struct in_addr addr ;
2002-09-09 21:25:28 +00:00
cp ( ) ;
2003-12-12 19:52:25 +00:00
if ( ! checklength ( source , packet , ether_size + arp_size ) )
return ;
if ( source ! = myself ) {
ifdebug ( TRAFFIC ) logger ( LOG_WARNING , _ ( " Got ARP request from %s (%s) while in router mode! " ) , source - > name , source - > hostname ) ;
return ;
}
2002-09-09 21:25:28 +00:00
/* First, snatch the source address from the ARP packet */
2003-06-11 19:28:38 +00:00
if ( overwrite_mac )
2003-10-06 13:49:57 +00:00
memcpy ( mymac . x , packet - > data + ETH_ALEN , ETH_ALEN ) ;
2002-09-09 21:25:28 +00:00
2003-10-06 13:49:57 +00:00
/* Copy headers from packet to structs on the stack */
2002-09-09 21:25:28 +00:00
2003-10-06 13:49:57 +00:00
memcpy ( & arp , packet - > data + ether_size , arp_size ) ;
2002-09-09 21:25:28 +00:00
/* Check if this is a valid ARP request */
2003-10-06 13:49:57 +00:00
if ( ntohs ( arp . arp_hrd ) ! = ARPHRD_ETHER | | ntohs ( arp . arp_pro ) ! = ETH_P_IP | |
arp . arp_hln ! = ETH_ALEN | | arp . arp_pln ! = sizeof ( addr ) | | ntohs ( arp . arp_op ) ! = ARPOP_REQUEST ) {
2003-07-12 17:41:48 +00:00
ifdebug ( TRAFFIC ) logger ( LOG_WARNING , _ ( " Cannot route packet: received unknown type ARP request " ) ) ;
2002-09-09 21:25:28 +00:00
return ;
}
/* Check if the IPv4 address exists on the VPN */
2003-10-06 13:49:57 +00:00
subnet = lookup_subnet_ipv4 ( ( ipv4_t * ) & arp . arp_tpa ) ;
2002-09-09 21:25:28 +00:00
if ( ! subnet ) {
2003-07-12 17:41:48 +00:00
ifdebug ( TRAFFIC ) logger ( LOG_WARNING , _ ( " Cannot route packet: ARP request for unknown address %d.%d.%d.%d " ) ,
2003-10-06 13:49:57 +00:00
arp . arp_tpa [ 0 ] , arp . arp_tpa [ 1 ] , arp . arp_tpa [ 2 ] ,
arp . arp_tpa [ 3 ] ) ;
2002-09-09 21:25:28 +00:00
return ;
}
/* Check if it is for our own subnet */
if ( subnet - > owner = = myself )
return ; /* silently ignore */
2003-10-06 13:49:57 +00:00
memcpy ( packet - > data , packet - > data + ETH_ALEN , ETH_ALEN ) ; /* copy destination address */
packet - > data [ ETH_ALEN * 2 - 1 ] ^ = 0xFF ; /* mangle source address so it looks like it's not from us */
memcpy ( & addr , arp . arp_tpa , sizeof ( addr ) ) ; /* save protocol addr */
memcpy ( arp . arp_tpa , arp . arp_spa , sizeof ( addr ) ) ; /* swap destination and source protocol address */
memcpy ( arp . arp_spa , & addr , sizeof ( addr ) ) ; /* ... */
2002-09-09 21:25:28 +00:00
2003-10-06 13:49:57 +00:00
memcpy ( arp . arp_tha , arp . arp_sha , ETH_ALEN ) ; /* set target hard/proto addr */
memcpy ( arp . arp_sha , packet - > data + ETH_ALEN , ETH_ALEN ) ; /* add fake source hard addr */
arp . arp_op = htons ( ARPOP_REPLY ) ;
2002-09-09 21:25:28 +00:00
2003-10-06 13:49:57 +00:00
/* Copy structs on stack back to packet */
memcpy ( packet - > data + ether_size , & arp , arp_size ) ;
2002-09-09 21:25:28 +00:00
2003-12-12 19:52:25 +00:00
send_packet ( source , packet ) ;
2001-06-04 11:14:35 +00:00
}
2003-12-12 19:52:25 +00:00
void route ( node_t * source , vpn_packet_t * packet )
2001-01-05 23:53:53 +00:00
{
2002-09-09 21:25:28 +00:00
cp ( ) ;
2003-12-12 19:52:25 +00:00
if ( ! checklength ( source , packet , ether_size ) )
2003-09-23 20:59:01 +00:00
return ;
2002-09-09 21:25:28 +00:00
switch ( routing_mode ) {
case RMODE_ROUTER :
{
uint16_t type ;
2002-09-09 22:33:31 +00:00
type = ntohs ( * ( ( uint16_t * ) ( & packet - > data [ 12 ] ) ) ) ;
2002-09-09 21:25:28 +00:00
switch ( type ) {
2003-12-12 19:52:25 +00:00
case ETH_P_ARP :
route_arp ( source , packet ) ;
break ;
2003-10-01 09:14:01 +00:00
2003-12-12 19:52:25 +00:00
case ETH_P_IP :
route_ipv4 ( source , packet ) ;
2002-09-09 21:25:28 +00:00
break ;
2003-10-06 13:49:57 +00:00
case ETH_P_IPV6 :
2003-12-12 19:52:25 +00:00
route_ipv6 ( source , packet ) ;
2002-09-09 21:25:28 +00:00
break ;
default :
2003-12-12 19:52:25 +00:00
ifdebug ( TRAFFIC ) logger ( LOG_WARNING , _ ( " Cannot route packet from %s (%s): unknown type %hx " ) , source - > name , source - > hostname , type ) ;
2002-09-09 21:25:28 +00:00
break ;
}
}
break ;
case RMODE_SWITCH :
2003-12-12 19:52:25 +00:00
route_mac ( source , packet ) ;
2002-09-09 21:25:28 +00:00
break ;
case RMODE_HUB :
2003-12-12 19:52:25 +00:00
broadcast_packet ( source , packet ) ;
2002-09-09 21:25:28 +00:00
break ;
}
2001-01-07 15:25:49 +00:00
}