david/ipxe
david
/
ipxe
Archived
1
0
Fork 0

[ipv6] Allow for multiple routers

Select the IPv6 source address and corresponding router (if any) using
a very simplified version of the algorithm from RFC6724:

- Ignore any source address that has a smaller scope than the
  destination address.  For example, do not use a link-local source
  address when sending to a global destination address.

- If we have a source address which is on the same link as the
  destination address, then use that source address.

- If we are left with multiple possible source addresses, then choose
  the address with the smallest scope.  For example, if we are sending
  to a site-local destination address and we have both a global source
  address and a site-local source address, then use the site-local
  source address.

- If we are still left with multiple possible source addresses, then
  choose the address with the longest matching prefix.

For the purposes of this algorithm, we treat RFC4193 Unique Local
Addresses as having organisation-local scope.  Since we use only
link-local scope for our multicast transmissions, this approximation
should remain valid in all practical situations.

Originally-implemented-by: Thomas Bächler <thomas@archlinux.org>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
Michael Brown 2016-07-25 15:20:22 +01:00
parent a454baaf11
commit a4c4f72297
4 changed files with 451 additions and 50 deletions

View File

@ -69,8 +69,12 @@ struct in6_addr {
( ( *( ( const uint16_t * ) (addr) ) & htons ( 0xffc0 ) ) == \
htons ( 0xfe80 ) )
#define IN6_IS_ADDR_NONGLOBAL( addr ) \
( IN6_IS_ADDR_LINKLOCAL (addr) || IN6_IS_ADDR_MULTICAST (addr) )
#define IN6_IS_ADDR_SITELOCAL( addr ) \
( ( *( ( const uint16_t * ) (addr) ) & htons ( 0xffc0 ) ) == \
htons ( 0xfec0 ) )
#define IN6_IS_ADDR_ULA( addr ) \
( ( *( ( const uint8_t * ) (addr) ) & 0xfe ) == 0xfc )
/**
* IPv4 socket address

View File

@ -158,6 +158,24 @@ struct ipv6_pseudo_header {
uint8_t next_header;
} __attribute__ (( packed ));
/** IPv6 address scopes */
enum ipv6_address_scope {
/** Interface-local address scope */
IPV6_SCOPE_INTERFACE_LOCAL = 0x1,
/** Link-local address scope */
IPV6_SCOPE_LINK_LOCAL = 0x2,
/** Admin-local address scope */
INV6_SCOPE_ADMIN_LOCAL = 0x4,
/** Site-local address scope */
IPV6_SCOPE_SITE_LOCAL = 0x5,
/** Organisation-local address scope */
IPV6_SCOPE_ORGANISATION_LOCAL = 0x8,
/** Global address scope */
IPV6_SCOPE_GLOBAL = 0xe,
/** Maximum scope */
IPV6_SCOPE_MAX = 0xf,
};
/** An IPv6 address/routing table entry */
struct ipv6_miniroute {
/** List of miniroutes */
@ -174,6 +192,8 @@ struct ipv6_miniroute {
struct in6_addr prefix_mask;
/** Router address */
struct in6_addr router;
/** Scope */
unsigned int scope;
/** Flags */
unsigned int flags;
};
@ -244,6 +264,18 @@ static inline void ipv6_all_routers ( struct in6_addr *addr ) {
addr->s6_addr[15] = 2;
}
/**
* Get multicast address scope
*
* @v addr Multicast address
* @ret scope Address scope
*/
static inline unsigned int
ipv6_multicast_scope ( const struct in6_addr *addr ) {
return ( addr->s6_addr[1] & 0x0f );
}
/** IPv6 settings sibling order */
enum ipv6_settings_order {
/** No address */
@ -264,6 +296,13 @@ extern struct list_head ipv6_miniroutes;
extern struct net_protocol ipv6_protocol __net_protocol;
extern int ipv6_has_addr ( struct net_device *netdev, struct in6_addr *addr );
extern int ipv6_add_miniroute ( struct net_device *netdev,
struct in6_addr *address,
unsigned int prefix_len,
struct in6_addr *router );
extern void ipv6_del_miniroute ( struct ipv6_miniroute *miniroute );
extern struct ipv6_miniroute * ipv6_route ( unsigned int scope_id,
struct in6_addr **dest );
extern int parse_ipv6_setting ( const struct setting_type *type,
const char *value, void *buf, size_t len );
extern int format_ipv6_setting ( const struct setting_type *type,

View File

@ -23,6 +23,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <assert.h>
#include <byteswap.h>
@ -78,6 +79,40 @@ static uint32_t ipv6col ( struct in6_addr *in ) {
return crc32_le ( 0, in, sizeof ( *in ) );
}
/**
* Determine IPv6 address scope
*
* @v addr IPv6 address
* @ret scope Address scope
*/
static unsigned int ipv6_scope ( const struct in6_addr *addr ) {
/* Multicast addresses directly include a scope field */
if ( IN6_IS_ADDR_MULTICAST ( addr ) )
return ipv6_multicast_scope ( addr );
/* Link-local addresses have link-local scope */
if ( IN6_IS_ADDR_LINKLOCAL ( addr ) )
return IPV6_SCOPE_LINK_LOCAL;
/* Site-local addresses have site-local scope */
if ( IN6_IS_ADDR_SITELOCAL ( addr ) )
return IPV6_SCOPE_SITE_LOCAL;
/* Unique local addresses do not directly map to a defined
* scope. They effectively have a scope which is wider than
* link-local but narrower than global. Since the only
* multicast packets that we transmit are link-local, we can
* simply choose an arbitrary scope between link-local and
* global.
*/
if ( IN6_IS_ADDR_ULA ( addr ) )
return IPV6_SCOPE_ORGANISATION_LOCAL;
/* All other addresses are assumed to be global */
return IPV6_SCOPE_GLOBAL;
}
/**
* Dump IPv6 routing table entry
*
@ -119,23 +154,32 @@ int ipv6_has_addr ( struct net_device *netdev, struct in6_addr *addr ) {
}
/**
* Check if IPv6 address is within a routing table entry's local network
* Count matching bits of an IPv6 routing table entry prefix
*
* @v miniroute Routing table entry
* @v address IPv6 address
* @ret is_on_link Address is within this entry's local network
* @ret match_len Number of matching prefix bits
*/
static int ipv6_is_on_link ( struct ipv6_miniroute *miniroute,
struct in6_addr *address ) {
static unsigned int ipv6_match_len ( struct ipv6_miniroute *miniroute,
struct in6_addr *address ) {
unsigned int match_len = 0;
unsigned int i;
uint32_t diff;
for ( i = 0 ; i < ( sizeof ( address->s6_addr32 ) /
sizeof ( address->s6_addr32[0] ) ) ; i++ ) {
if ( (( address->s6_addr32[i] ^ miniroute->address.s6_addr32[i])
& miniroute->prefix_mask.s6_addr32[i] ) != 0 )
return 0;
diff = ntohl ( ~( ( ~( address->s6_addr32[i] ^
miniroute->address.s6_addr32[i] ) )
& miniroute->prefix_mask.s6_addr32[i] ) );
match_len += 32;
if ( diff ) {
match_len -= flsl ( diff );
break;
}
}
return 1;
return match_len;
}
/**
@ -148,12 +192,15 @@ static int ipv6_is_on_link ( struct ipv6_miniroute *miniroute,
static struct ipv6_miniroute * ipv6_miniroute ( struct net_device *netdev,
struct in6_addr *address ) {
struct ipv6_miniroute *miniroute;
unsigned int match_len;
list_for_each_entry ( miniroute, &ipv6_miniroutes, list ) {
if ( ( miniroute->netdev == netdev ) &&
ipv6_is_on_link ( miniroute, address ) ) {
return miniroute;
}
if ( miniroute->netdev != netdev )
continue;
match_len = ipv6_match_len ( miniroute, address );
if ( match_len < miniroute->prefix_len )
continue;
return miniroute;
}
return NULL;
}
@ -167,10 +214,8 @@ static struct ipv6_miniroute * ipv6_miniroute ( struct net_device *netdev,
* @v router Router address (if any)
* @ret rc Return status code
*/
static int ipv6_add_miniroute ( struct net_device *netdev,
struct in6_addr *address,
unsigned int prefix_len,
struct in6_addr *router ) {
int ipv6_add_miniroute ( struct net_device *netdev, struct in6_addr *address,
unsigned int prefix_len, struct in6_addr *router ) {
struct ipv6_miniroute *miniroute;
uint8_t *prefix_mask;
unsigned int remaining;
@ -178,7 +223,12 @@ static int ipv6_add_miniroute ( struct net_device *netdev,
/* Find or create routing table entry */
miniroute = ipv6_miniroute ( netdev, address );
if ( ! miniroute ) {
if ( miniroute ) {
/* Remove from existing position in routing table */
list_del ( &miniroute->list );
} else {
/* Create new routing table entry */
miniroute = zalloc ( sizeof ( *miniroute ) );
@ -202,11 +252,11 @@ static int ipv6_add_miniroute ( struct net_device *netdev,
}
if ( remaining )
*prefix_mask <<= ( 8 - remaining );
/* Add to list of routes */
list_add ( &miniroute->list, &ipv6_miniroutes );
}
/* Add to start of routing table */
list_add ( &miniroute->list, &ipv6_miniroutes );
/* Set or update address, if applicable */
for ( i = 0 ; i < ( sizeof ( address->s6_addr32 ) /
sizeof ( address->s6_addr32[0] ) ) ; i++ ) {
@ -220,13 +270,14 @@ static int ipv6_add_miniroute ( struct net_device *netdev,
if ( miniroute->prefix_len == IPV6_MAX_PREFIX_LEN )
miniroute->flags |= IPV6_HAS_ADDRESS;
/* Update scope */
miniroute->scope = ipv6_scope ( &miniroute->address );
/* Set or update router, if applicable */
if ( router ) {
memcpy ( &miniroute->router, router,
sizeof ( miniroute->router ) );
miniroute->flags |= IPV6_HAS_ROUTER;
list_del ( &miniroute->list );
list_add_tail ( &miniroute->list, &ipv6_miniroutes );
}
ipv6_dump_miniroute ( miniroute );
@ -238,7 +289,7 @@ static int ipv6_add_miniroute ( struct net_device *netdev,
*
* @v miniroute Routing table entry
*/
static void ipv6_del_miniroute ( struct ipv6_miniroute *miniroute ) {
void ipv6_del_miniroute ( struct ipv6_miniroute *miniroute ) {
netdev_put ( miniroute->netdev );
list_del ( &miniroute->list );
@ -253,9 +304,17 @@ static void ipv6_del_miniroute ( struct ipv6_miniroute *miniroute ) {
* @ret dest Next hop destination address
* @ret miniroute Routing table entry to use, or NULL if no route
*/
static struct ipv6_miniroute * ipv6_route ( unsigned int scope_id,
struct in6_addr **dest ) {
struct ipv6_miniroute * ipv6_route ( unsigned int scope_id,
struct in6_addr **dest ) {
struct ipv6_miniroute *miniroute;
struct ipv6_miniroute *chosen = NULL;
unsigned int best = 0;
unsigned int match_len;
unsigned int score;
unsigned int scope;
/* Calculate destination address scope */
scope = ipv6_scope ( *dest );
/* Find first usable route in routing table */
list_for_each_entry ( miniroute, &ipv6_miniroutes, list ) {
@ -264,37 +323,54 @@ static struct ipv6_miniroute * ipv6_route ( unsigned int scope_id,
if ( ! netdev_is_open ( miniroute->netdev ) )
continue;
/* Skip routing table entries with no usable source address */
/* Skip entries with no usable source address */
if ( ! ( miniroute->flags & IPV6_HAS_ADDRESS ) )
continue;
if ( IN6_IS_ADDR_NONGLOBAL ( *dest ) ) {
/* Skip entries with a non-matching scope ID, if
* destination specifies a scope ID.
*/
if ( scope_id && ( miniroute->netdev->index != scope_id ) )
continue;
/* If destination is non-global, and the scope ID
* matches this network device, then use this route.
*/
if ( miniroute->netdev->index == scope_id )
return miniroute;
/* Skip entries that are out of scope */
if ( miniroute->scope < scope )
continue;
} else {
/* Calculate match length */
match_len = ipv6_match_len ( miniroute, *dest );
/* If destination is an on-link global
* address, then use this route.
*/
if ( ipv6_is_on_link ( miniroute, *dest ) )
return miniroute;
/* If destination is on-link, then use this route */
if ( match_len >= miniroute->prefix_len )
return miniroute;
/* If destination is an off-link global
* address, and we have a default gateway,
* then use this route.
*/
if ( miniroute->flags & IPV6_HAS_ROUTER ) {
*dest = &miniroute->router;
return miniroute;
}
/* If destination is unicast, then skip off-link
* entries with no router.
*/
if ( ! ( IN6_IS_ADDR_MULTICAST ( *dest ) ||
( miniroute->flags & IPV6_HAS_ROUTER ) ) )
continue;
/* Choose best route, defined as being the route with
* the smallest viable scope. If two routes both have
* the same scope, then prefer the route with the
* longest match length.
*/
score = ( ( ( IPV6_SCOPE_MAX + 1 - miniroute->scope ) << 8 )
+ match_len );
if ( score > best ) {
chosen = miniroute;
best = score;
}
}
/* Return chosen route, if any */
if ( chosen ) {
if ( ! IN6_IS_ADDR_MULTICAST ( *dest ) )
*dest = &chosen->router;
return chosen;
}
return NULL;
}
@ -880,7 +956,7 @@ static const char * ipv6_sock_ntoa ( struct sockaddr *sa ) {
const char *netdev_name;
/* Identify network device, if applicable */
if ( IN6_IS_ADDR_NONGLOBAL ( in ) ) {
if ( IN6_IS_ADDR_LINKLOCAL ( in ) || IN6_IS_ADDR_MULTICAST ( in ) ) {
netdev = find_netdev_by_index ( sin6->sin6_scope_id );
netdev_name = ( netdev ? netdev->name : "UNKNOWN" );
} else {
@ -946,7 +1022,8 @@ static int ipv6_sock_aton ( const char *string, struct sockaddr *sa ) {
}
sin6->sin6_scope_id = netdev->index;
} else if ( IN6_IS_ADDR_NONGLOBAL ( &in ) ) {
} else if ( IN6_IS_ADDR_LINKLOCAL ( &in ) ||
IN6_IS_ADDR_MULTICAST ( &in ) ) {
/* If no network device is explicitly specified for a
* link-local or multicast address, default to using

View File

@ -41,6 +41,38 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** Define inline IPv6 address */
#define IPV6(...) { __VA_ARGS__ }
/** An IPv6 test routing table entry */
struct ipv6_test_route {
/** Local address */
const char *address;
/** Prefix length */
unsigned int prefix_len;
/** Router address (if any) */
const char *router;
};
/** An IPv6 test routing table */
struct ipv6_test_table {
/** Test routing table entries */
const struct ipv6_test_route *routes;
/** Number of table entries */
unsigned int count;
/** Constructed routing table */
struct list_head list;
};
/** Define a test routing table */
#define TABLE( name, ... ) \
static const struct ipv6_test_route name ## _routes[] = { \
__VA_ARGS__ \
}; \
static struct ipv6_test_table name = { \
.routes = name ## _routes, \
.count = ( sizeof ( name ## _routes ) / \
sizeof ( name ## _routes[0] ) ), \
.list = LIST_HEAD_INIT ( name.list ), \
};
/** The unspecified IPv6 address */
static const struct in6_addr sample_unspecified = {
.s6_addr = IPV6 ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@ -53,6 +85,18 @@ static const struct in6_addr sample_link_local = {
0x00, 0x00, 0x69, 0xff, 0xfe, 0x50, 0x58, 0x45 ),
};
/** A sample site-local IPv6 address */
static const struct in6_addr sample_site_local = {
.s6_addr = IPV6 ( 0xfe, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 ),
};
/** A sample ULA IPv6 address */
static const struct in6_addr sample_ula = {
.s6_addr = IPV6 ( 0xfd, 0x44, 0x91, 0x12, 0x64, 0x42, 0x00, 0x00,
0x00, 0x00, 0x69, 0xff, 0xfe, 0x50, 0x58, 0x45 ),
};
/** A sample global IPv6 address */
static const struct in6_addr sample_global = {
.s6_addr = IPV6 ( 0x20, 0x01, 0x0b, 0xa8, 0x00, 0x00, 0x01, 0xd4,
@ -65,6 +109,31 @@ static const struct in6_addr sample_multicast = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 ),
};
/** Dummy network device used for routing tests */
static struct net_device ipv6_test_netdev = {
.refcnt = REF_INIT ( ref_no_free ),
.index = 42,
.state = NETDEV_OPEN,
};
/** Routing table with only a link-local address */
TABLE ( table_link_local,
{ "fe80::69ff:fe50:5845", 64, NULL } );
/** Routing table with a global address */
TABLE ( table_normal,
{ "fe80::69ff:fe50:5845", 64, NULL },
{ "2001:db8:3::1", 64, "fe80::1" } );
/** Routing table with multiple addresses and routers */
TABLE ( table_multi,
{ "fe80::69ff:fe50:5845", 64, NULL },
{ "2001:db8:3::1", 64, "fe80::1" },
{ "2001:db8:5::1", 64, NULL },
{ "2001:db8:42::1", 64, "fe80::2" },
{ "fd44:9112:6442::69ff:fe50:5845", 64, "fe80::1" },
{ "fd70:6ba9:50ae::69ff:fe50:5845", 64, "fe80::3" } );
/**
* Report an inet6_ntoa() test result
*
@ -133,6 +202,148 @@ static void inet6_aton_fail_okx ( const char *text, const char *file,
#define inet6_aton_fail_ok( text ) \
inet6_aton_fail_okx ( text, __FILE__, __LINE__ )
/**
* Create test routing table
*
* @v table Test routing table
* @v file Test code file
* @v line Test code line
*/
static void ipv6_table_okx ( struct ipv6_test_table *table, const char *file,
unsigned int line ) {
const struct ipv6_test_route *route;
struct in6_addr address;
struct in6_addr router;
struct list_head saved;
unsigned int i;
/* Sanity check */
okx ( list_empty ( &table->list ), file, line );
/* Save existing routing table */
INIT_LIST_HEAD ( &saved );
list_splice_init ( &ipv6_miniroutes, &saved );
/* Construct routing table */
for ( i = 0 ; i < table->count ; i++ ) {
/* Parse address and router (if applicable) */
route = &table->routes[i];
okx ( inet6_aton ( route->address, &address ) == 0,
file, line );
if ( route->router ) {
okx ( inet6_aton ( route->router, &router ) == 0,
file, line );
}
/* Add routing table entry */
okx ( ipv6_add_miniroute ( &ipv6_test_netdev, &address,
route->prefix_len,
( route->router ?
&router : NULL ) ) == 0,
file, line );
}
/* Save constructed routing table */
list_splice_init ( &ipv6_miniroutes, &table->list );
/* Restore original routing table */
list_splice ( &saved, &ipv6_miniroutes );
}
#define ipv6_table_ok( table ) \
ipv6_table_okx ( table, __FILE__, __LINE__ )
/**
* Report an ipv6_route() test result
*
* @v table Test routing table
* @v dest Destination address
* @v src Expected source address, or NULL to expect failure
* @v next Expected next hop address, or NULL to expect destination
* @v file Test code file
* @v line Test code line
*/
static void ipv6_route_okx ( struct ipv6_test_table *table, const char *dest,
const char *src, const char *next,
const char *file, unsigned int line ) {
struct in6_addr in_dest;
struct in6_addr in_src;
struct in6_addr in_next;
struct in6_addr *actual;
struct ipv6_miniroute *miniroute;
struct list_head saved;
/* Switch to test routing table */
INIT_LIST_HEAD ( &saved );
list_splice_init ( &ipv6_miniroutes, &saved );
list_splice_init ( &table->list, &ipv6_miniroutes );
/* Parse addresses */
okx ( inet6_aton ( dest, &in_dest ) == 0, file, line );
if ( src )
okx ( inet6_aton ( src, &in_src ) == 0, file, line );
if ( next ) {
okx ( inet6_aton ( next, &in_next ) == 0, file, line );
} else {
memcpy ( &in_next, &in_dest, sizeof ( in_next ) );
}
/* Perform routing */
actual = &in_dest;
miniroute = ipv6_route ( ipv6_test_netdev.index, &actual );
/* Validate result */
if ( src ) {
/* Check that a route was found */
okx ( miniroute != NULL, file, line );
DBG ( "ipv6_route ( %s ) = %s", dest, inet6_ntoa ( actual ) );
DBG ( " from %s\n", inet6_ntoa ( &miniroute->address ) );
/* Check that expected source address was used */
okx ( memcmp ( &miniroute->address, &in_src,
sizeof ( in_src ) ) == 0, file, line );
/* Check that expected next hop address was used */
okx ( memcmp ( actual, &in_next, sizeof ( *actual ) ) == 0,
file, line );
} else {
/* Routing is expected to fail */
okx ( miniroute == NULL, file, line );
}
/* Restore original routing table */
list_splice_init ( &ipv6_miniroutes, &table->list );
list_splice ( &saved, &ipv6_miniroutes );
}
#define ipv6_route_ok( table, dest, src, next ) \
ipv6_route_okx ( table, dest, src, next, __FILE__, __LINE__ )
/**
* Destroy test routing table
*
* @v table Test routing table
*/
static void ipv6_table_del ( struct ipv6_test_table *table ) {
struct ipv6_miniroute *miniroute;
struct ipv6_miniroute *tmp;
struct list_head saved;
/* Switch to test routing table */
INIT_LIST_HEAD ( &saved );
list_splice_init ( &ipv6_miniroutes, &saved );
list_splice_init ( &table->list, &ipv6_miniroutes );
/* Delete all existing routes */
list_for_each_entry_safe ( miniroute, tmp, &ipv6_miniroutes, list )
ipv6_del_miniroute ( miniroute );
/* Restore original routing table */
list_splice ( &saved, &ipv6_miniroutes );
}
/**
* Perform IPv6 self-tests
*
@ -142,16 +353,34 @@ static void ipv6_test_exec ( void ) {
/* Address testing macros */
ok ( IN6_IS_ADDR_UNSPECIFIED ( &sample_unspecified ) );
ok ( ! IN6_IS_ADDR_UNSPECIFIED ( &sample_link_local ) );
ok ( ! IN6_IS_ADDR_UNSPECIFIED ( &sample_site_local ) );
ok ( ! IN6_IS_ADDR_UNSPECIFIED ( &sample_ula ) );
ok ( ! IN6_IS_ADDR_UNSPECIFIED ( &sample_global ) );
ok ( ! IN6_IS_ADDR_UNSPECIFIED ( &sample_multicast ) );
ok ( ! IN6_IS_ADDR_MULTICAST ( &sample_unspecified ) );
ok ( ! IN6_IS_ADDR_MULTICAST ( &sample_link_local ) );
ok ( ! IN6_IS_ADDR_MULTICAST ( &sample_site_local ) );
ok ( ! IN6_IS_ADDR_MULTICAST ( &sample_ula ) );
ok ( ! IN6_IS_ADDR_MULTICAST ( &sample_global ) );
ok ( IN6_IS_ADDR_MULTICAST ( &sample_multicast ) );
ok ( ! IN6_IS_ADDR_LINKLOCAL ( &sample_unspecified ) );
ok ( IN6_IS_ADDR_LINKLOCAL ( &sample_link_local ) );
ok ( ! IN6_IS_ADDR_LINKLOCAL ( &sample_site_local ) );
ok ( ! IN6_IS_ADDR_LINKLOCAL ( &sample_ula ) );
ok ( ! IN6_IS_ADDR_LINKLOCAL ( &sample_global ) );
ok ( ! IN6_IS_ADDR_LINKLOCAL ( &sample_multicast ) );
ok ( ! IN6_IS_ADDR_SITELOCAL ( &sample_unspecified ) );
ok ( ! IN6_IS_ADDR_SITELOCAL ( &sample_link_local ) );
ok ( IN6_IS_ADDR_SITELOCAL ( &sample_site_local ) );
ok ( ! IN6_IS_ADDR_SITELOCAL ( &sample_ula ) );
ok ( ! IN6_IS_ADDR_SITELOCAL ( &sample_global ) );
ok ( ! IN6_IS_ADDR_SITELOCAL ( &sample_multicast ) );
ok ( ! IN6_IS_ADDR_ULA ( &sample_unspecified ) );
ok ( ! IN6_IS_ADDR_ULA ( &sample_link_local ) );
ok ( ! IN6_IS_ADDR_ULA ( &sample_site_local ) );
ok ( IN6_IS_ADDR_ULA ( &sample_ula ) );
ok ( ! IN6_IS_ADDR_ULA ( &sample_global ) );
ok ( ! IN6_IS_ADDR_ULA ( &sample_multicast ) );
/* inet6_ntoa() tests */
inet6_ntoa_ok ( IPV6 ( 0x20, 0x01, 0x0b, 0xa8, 0x00, 0x00, 0x01, 0xd4,
@ -228,6 +457,58 @@ static void ipv6_test_exec ( void ) {
inet6_aton_fail_ok ( "2001:db8::1::2" );
inet6_aton_fail_ok ( "2001:ba8:0:1d4:::6950:5845" );
inet6_aton_fail_ok ( ":::" );
/* Create test routing tables */
ipv6_table_ok ( &table_link_local );
ipv6_table_ok ( &table_normal );
ipv6_table_ok ( &table_multi );
/* Routing table with only a link-local address */
ipv6_route_ok ( &table_link_local, "fe80::1",
"fe80::69ff:fe50:5845", NULL );
ipv6_route_ok ( &table_link_local, "2001:db8:1::1",
NULL, NULL );
ipv6_route_ok ( &table_link_local, "ff02::1",
"fe80::69ff:fe50:5845", NULL );
/** Routing table with a global address */
ipv6_route_ok ( &table_normal, "fe80::1",
"fe80::69ff:fe50:5845", NULL );
ipv6_route_ok ( &table_normal, "2001:db8:3::42",
"2001:db8:3::1", NULL );
ipv6_route_ok ( &table_normal, "2001:ba8:0:1d4::6950:5845",
"2001:db8:3::1", "fe80::1" );
ipv6_route_ok ( &table_normal, "ff02::1",
"fe80::69ff:fe50:5845", NULL );
ipv6_route_ok ( &table_normal, "ff0e::1",
"2001:db8:3::1", NULL );
/** Routing table with multiple addresses and routers */
ipv6_route_ok ( &table_multi, "fe80::1",
"fe80::69ff:fe50:5845", NULL );
ipv6_route_ok ( &table_multi, "2001:db8:3::17",
"2001:db8:3::1", NULL );
ipv6_route_ok ( &table_multi, "2001:db8:5::92",
"2001:db8:5::1", NULL );
ipv6_route_ok ( &table_multi, "2001:db8:42::17",
"2001:db8:42::1", NULL );
ipv6_route_ok ( &table_multi, "2001:db8:5:1::17",
"2001:db8:3::1", "fe80::1" );
ipv6_route_ok ( &table_multi, "fd44:9112:6442::1",
"fd44:9112:6442::69ff:fe50:5845", NULL );
ipv6_route_ok ( &table_multi, "fd70:6ba9:50ae::1",
"fd70:6ba9:50ae::69ff:fe50:5845", NULL );
ipv6_route_ok ( &table_multi, "fd40::3",
"fd44:9112:6442::69ff:fe50:5845", "fe80::1" );
ipv6_route_ok ( &table_multi, "fd70::2",
"fd70:6ba9:50ae::69ff:fe50:5845", "fe80::3" );
ipv6_route_ok ( &table_multi, "ff02::1",
"fe80::69ff:fe50:5845", NULL );
/* Destroy test routing tables */
ipv6_table_del ( &table_link_local );
ipv6_table_del ( &table_normal );
ipv6_table_del ( &table_multi );
}
/** IPv6 self-test */