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 #include #include +#include #include #include #include @@ -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 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 * @@ -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 */