summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/include/ipxe/in.h8
-rw-r--r--src/include/ipxe/ipv6.h39
-rw-r--r--src/net/ipv6.c175
-rw-r--r--src/tests/ipv6_test.c281
4 files changed, 452 insertions, 51 deletions
diff --git a/src/include/ipxe/in.h b/src/include/ipxe/in.h
index 0ebf441c..3044d631 100644
--- a/src/include/ipxe/in.h
+++ b/src/include/ipxe/in.h
@@ -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
diff --git a/src/include/ipxe/ipv6.h b/src/include/ipxe/ipv6.h
index 0e5292fb..4dd43f16 100644
--- a/src/include/ipxe/ipv6.h
+++ b/src/include/ipxe/ipv6.h
@@ -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,
diff --git a/src/net/ipv6.c b/src/net/ipv6.c
index c4e1f708..4b2c33eb 100644
--- a/src/net/ipv6.c
+++ b/src/net/ipv6.c
@@ -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>
@@ -79,6 +80,40 @@ static uint32_t ipv6col ( struct in6_addr *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
*
* @v miniroute 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 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 on-link, then use this route */
+ if ( match_len >= miniroute->prefix_len )
+ 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
diff --git a/src/tests/ipv6_test.c b/src/tests/ipv6_test.c
index 42b72961..9b3a744d 100644
--- a/src/tests/ipv6_test.c
+++ b/src/tests/ipv6_test.c
@@ -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
*
@@ -134,6 +203,148 @@ static void inet6_aton_fail_okx ( const char *text, const char *file,
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 */