Files
ipxe/src/tests/ipv6_test.c
Michael Brown 47af48012e [netdevice] Separate concept of scope ID from network device name index
The network device index currently serves two purposes: acting as a
sequential index for network device names ("net0", "net1", etc), and
acting as an opaque unique integer identifier used in socket address
scope IDs.

There is no particular need for these usages to be linked, and it can
lead to situations in which devices are named unexpectedly.  For
example: if a system has two network devices "net0" and "net1", a VLAN
is created as "net1-42", and then a USB NIC is connected, then the USB
NIC will be named "net3" rather than the expected "net2" since the
VLAN device "net1-42" will have consumed an index.

Separate the usages: rename the "index" field to "scope_id" (matching
its one and only use case), and assign the name without reference to
the scope ID by finding the first unused name.  For consistency,
assign the scope ID by similarly finding the first unused scope ID.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-01-14 00:09:20 +00:00

596 lines
19 KiB
C

/*
* Copyright (C) 2013 Michael Brown <mbrown@fensystems.co.uk>.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** @file
*
* IPv6 tests
*
*/
/* Forcibly enable assertions */
#undef NDEBUG
#include <stdint.h>
#include <string.h>
#include <byteswap.h>
#include <ipxe/ipv6.h>
#include <ipxe/test.h>
/** Define inline IPv6 address */
#define IPV6(...) { __VA_ARGS__ }
/** An IPv6 test prefix */
struct ipv6_test_prefix {
/** Prefix length */
unsigned int len;
/** Prefix mask */
const char *mask;
};
/** An IPv6 test routing table entry */
struct ipv6_test_route {
/** Local address */
const char *address;
/** Prefix */
const struct ipv6_test_prefix *prefix;
/** 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 prefix */
#define PREFIX( name, LEN, MASK ) \
static const struct ipv6_test_prefix name = { \
.len = LEN, \
.mask = MASK, \
};
/** 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,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ),
};
/** A sample link-local IPv6 address */
static const struct in6_addr sample_link_local = {
.s6_addr = IPV6 ( 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
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,
0x00, 0x00, 0x00, 0x00, 0x69, 0x50, 0x58, 0x45 ),
};
/** A sample multicast IPv6 address */
static const struct in6_addr sample_multicast = {
.s6_addr = IPV6 ( 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
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 ),
.scope_id = 42,
.state = NETDEV_OPEN,
};
/** /48 prefix */
PREFIX ( prefix48, 48, "ffff:ffff:ffff::" );
/** /64 prefix */
PREFIX ( prefix64, 64, "ffff:ffff:ffff:ffff::" );
/** /126 prefix */
PREFIX ( prefix126, 126, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffc" );
/** /127 prefix */
PREFIX ( prefix127, 127, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe" );
/** /128 prefix */
PREFIX ( prefix128, 128, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" );
/** Routing table with only a link-local address */
TABLE ( table_link_local,
{ "fe80::69ff:fe50:5845", &prefix64, NULL } );
/** Routing table with a global address */
TABLE ( table_normal,
{ "fe80::69ff:fe50:5845", &prefix64, NULL },
{ "2001:db8:3::1", &prefix64, "fe80::1" } );
/** Routing table with multiple addresses and routers */
TABLE ( table_multi,
{ "fe80::69ff:fe50:5845", &prefix64, NULL },
{ "2001:db8:3::1", &prefix64, "fe80::1" },
{ "2001:db8:5::1", &prefix64, NULL },
{ "2001:db8:42::1", &prefix64, "fe80::2" },
{ "fd44:9112:6442::69ff:fe50:5845", &prefix64, "fe80::1" },
{ "fd70:6ba9:50ae::69ff:fe50:5845", &prefix64, "fe80::3" } );
/** Routing table with unusual prefix lengths */
TABLE ( table_unusual,
{ "2001:db8:1::1", &prefix48, "fe80::1" },
{ "2001:db8:2::1", &prefix126, NULL },
{ "2001:db8:3::1", &prefix127, NULL },
{ "2001:db8:4::1", &prefix128, NULL } );
/**
* Report an inet6_ntoa() test result
*
* @v addr IPv6 address
* @v text Expected textual representation
* @v file Test code file
* @v line Test code line
*/
static void inet6_ntoa_okx ( const struct in6_addr *addr, const char *text,
const char *file, unsigned int line ) {
char *actual;
actual = inet6_ntoa ( addr );
DBG ( "inet6_ntoa ( %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ) "
"= %s\n", ntohs ( addr->s6_addr16[0] ),
ntohs ( addr->s6_addr16[1] ), ntohs ( addr->s6_addr16[2] ),
ntohs ( addr->s6_addr16[3] ), ntohs ( addr->s6_addr16[4] ),
ntohs ( addr->s6_addr16[5] ), ntohs ( addr->s6_addr16[6] ),
ntohs ( addr->s6_addr16[7] ), actual );
okx ( strcmp ( actual, text ) == 0, file, line );
}
#define inet6_ntoa_ok( addr, text ) do { \
static const struct in6_addr in = { \
.s6_addr = addr, \
}; \
inet6_ntoa_okx ( &in, text, __FILE__, __LINE__ ); \
} while ( 0 )
/**
* Report an inet6_aton() test result
*
* @v text Textual representation
* @v addr Expected IPv6 address
* @v file Test code file
* @v line Test code line
*/
static void inet6_aton_okx ( const char *text, const struct in6_addr *addr,
const char *file, unsigned int line ) {
struct in6_addr actual;
okx ( inet6_aton ( text, &actual ) == 0, file, line );
DBG ( "inet6_aton ( \"%s\" ) = %s\n", text, inet6_ntoa ( &actual ) );
okx ( memcmp ( &actual, addr, sizeof ( actual ) ) == 0,
file, line );
}
#define inet6_aton_ok( text, addr ) do { \
static const struct in6_addr in = { \
.s6_addr = addr, \
}; \
inet6_aton_okx ( text, &in, __FILE__, __LINE__ ); \
} while ( 0 )
/**
* Report an inet6_aton() failure test result
*
* @v text Textual representation
* @v file Test code file
* @v line Test code line
*/
static void inet6_aton_fail_okx ( const char *text, const char *file,
unsigned int line ) {
struct in6_addr dummy;
okx ( inet6_aton ( text, &dummy ) != 0, file, line );
}
#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 ipv6_miniroute *miniroute;
struct in6_addr address;
struct in6_addr router;
struct in6_addr mask;
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 );
}
okx ( inet6_aton ( route->prefix->mask, &mask ) == 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 );
/* Check routing table entry */
miniroute = list_first_entry ( &ipv6_miniroutes,
struct ipv6_miniroute, list );
okx ( miniroute != NULL, file, line );
okx ( memcmp ( &miniroute->address, &address,
sizeof ( address ) ) == 0, file, line );
okx ( miniroute->prefix_len == route->prefix->len,
file, line );
okx ( memcmp ( &miniroute->prefix_mask, &mask,
sizeof ( mask ) ) == 0, file, line );
if ( route->router ) {
okx ( memcmp ( &miniroute->router, &router,
sizeof ( router ) ) == 0, file, line );
} else {
okx ( IN6_IS_ADDR_UNSPECIFIED ( &miniroute->router ),
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.scope_id, &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
*
*/
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,
0x00, 0x00, 0x00, 0x00, 0x69, 0x50, 0x58, 0x45 ),
"2001:ba8:0:1d4::6950:5845" );
/* No zeros */
inet6_ntoa_ok ( IPV6 ( 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x01,
0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01 ),
"2001:db8:1:1:1:1:1:1" );
/* Run of zeros */
inet6_ntoa_ok ( IPV6 ( 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 ),
"2001:db8::1" );
/* No "::" for single zero */
inet6_ntoa_ok ( IPV6 ( 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x01,
0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01 ),
"2001:db8:0:1:1:1:1:1" );
/* Use "::" for longest run of zeros */
inet6_ntoa_ok ( IPV6 ( 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 ),
"2001:0:0:1::1" );
/* Use "::" for leftmost equal-length run of zeros */
inet6_ntoa_ok ( IPV6 ( 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 ),
"2001:db8::1:0:0:1" );
/* Trailing run of zeros */
inet6_ntoa_ok ( IPV6 ( 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ),
"fe80::" );
/* Leading run of zeros */
inet6_ntoa_ok ( IPV6 ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 ),
"::1" );
/* All zeros */
inet6_ntoa_ok ( IPV6 ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ),
"::" );
/* Maximum length */
inet6_ntoa_ok ( IPV6 ( 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff ),
"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" );
/* inet6_aton() tests */
inet6_aton_ok ( "2001:ba8:0:1d4::6950:5845",
IPV6 ( 0x20, 0x01, 0x0b, 0xa8, 0x00, 0x00, 0x01, 0xd4,
0x00, 0x00, 0x00, 0x00, 0x69, 0x50, 0x58, 0x45));
/* No zeros */
inet6_aton_ok ( "2001:db8:1:1:1:1:1:1",
IPV6 ( 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x01,
0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01));
/* All intervening zeros */
inet6_aton_ok ( "fe80::1",
IPV6 ( 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01));
/* Trailing run of zeros */
inet6_aton_ok ( "fe80::",
IPV6 ( 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00));
/* Leading run of zeros */
inet6_aton_ok ( "::1",
IPV6 ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01));
/* All zeros */
inet6_aton_ok ( "::",
IPV6 ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00));
/* inet6_aton() failure tests */
inet6_aton_fail_ok ( "20012:ba8:0:1d4::6950:5845" );
inet6_aton_fail_ok ( "200z:ba8:0:1d4::6950:5845" );
inet6_aton_fail_ok ( "2001.ba8:0:1d4::6950:5845" );
inet6_aton_fail_ok ( "2001:db8:1:1:1:1:1" );
inet6_aton_fail_ok ( "2001:db8:1:1:1:1:1:1:2" );
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 );
ipv6_table_ok ( &table_unusual );
/* 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 );
/* Routing table with unusual prefix lengths */
ipv6_route_ok ( &table_unusual, "2001:db8:2::1",
"2001:db8:2::1", NULL );
ipv6_route_ok ( &table_unusual, "2001:db8:2::3",
"2001:db8:2::1", NULL );
ipv6_route_ok ( &table_unusual, "2001:db8:3::1",
"2001:db8:3::1", NULL );
ipv6_route_ok ( &table_unusual, "2001:db8:3::2",
"2001:db8:1::1", "fe80::1" );
ipv6_route_ok ( &table_unusual, "2001:db8:4::1",
"2001:db8:4::1", NULL );
ipv6_route_ok ( &table_unusual, "2001:db8:4::0",
"2001:db8:1::1", "fe80::1" );
ipv6_route_ok ( &table_unusual, "2001:db8:4::2",
"2001:db8:1::1", "fe80::1" );
/* Destroy test routing tables */
ipv6_table_del ( &table_link_local );
ipv6_table_del ( &table_normal );
ipv6_table_del ( &table_multi );
ipv6_table_del ( &table_unusual );
}
/** IPv6 self-test */
struct self_test ipv6_test __self_test = {
.name = "ipv6",
.exec = ipv6_test_exec,
};