From c34d1518eb446d596087ed2b9dda33a513f7e980 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 19 Jul 2016 17:49:50 +0100 Subject: [PATCH] [ipv6] Create routing table based on IPv6 settings Use the IPv6 settings to construct the routing table, in a matter analogous to the construction of the IPv4 routing table. This allows for manual assignment of IPv6 addresses via e.g. set net0/ip6 2001:ba8:0:1d4::6950:5845 set net0/len6 64 set net0/gateway6 fe80::226:bff:fedd:d3c0 The prefix length ("len6") may be omitted, in which case a default prefix length of 64 will be assumed. Multiple IPv6 addresses may be assigned manually by implicitly creating child settings blocks. For example: set net0/ip6 2001:ba8:0:1d4::6950:5845 set net0.ula/ip6 fda4:2496:e992::6950:5845 Signed-off-by: Michael Brown --- src/include/ipxe/ipv6.h | 10 +- src/net/ipv6.c | 255 +++++++++++++++++++++------------------- src/net/ndp.c | 55 +-------- src/net/udp/dhcpv6.c | 17 +-- 4 files changed, 142 insertions(+), 195 deletions(-) diff --git a/src/include/ipxe/ipv6.h b/src/include/ipxe/ipv6.h index 4dd03f05..0e5292fb 100644 --- a/src/include/ipxe/ipv6.h +++ b/src/include/ipxe/ipv6.h @@ -25,6 +25,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** IPv6 maximum hop limit */ #define IPV6_HOP_LIMIT 0xff +/** IPv6 default prefix length */ +#define IPV6_DEFAULT_PREFIX_LEN 64 + +/** IPv6 maximum prefix length */ +#define IPV6_MAX_PREFIX_LEN 128 + /** IPv6 header */ struct ipv6_header { /** Version (4 bits), Traffic class (8 bits), Flow label (20 bits) */ @@ -258,10 +264,6 @@ 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_set_prefix ( struct net_device *netdev, struct in6_addr *prefix, - unsigned int prefix_len, struct in6_addr *router ); -extern int ipv6_set_address ( struct net_device *netdev, - struct in6_addr *address ); 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 04ba3d8b..d2a17312 100644 --- a/src/net/ipv6.c +++ b/src/net/ipv6.c @@ -164,107 +164,85 @@ static struct ipv6_miniroute * ipv6_miniroute ( struct net_device *netdev, * @v netdev Network device * @v address IPv6 address (or prefix) * @v prefix_len Prefix length - * @v flags Flags - * @ret miniroute Routing table entry, or NULL on failure + * @v router Router address (if any) + * @ret rc Return status code */ -static struct ipv6_miniroute * ipv6_add_miniroute ( struct net_device *netdev, - struct in6_addr *address, - unsigned int prefix_len, - unsigned int flags ) { +static 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; - - /* Create routing table entry */ - miniroute = zalloc ( sizeof ( *miniroute ) ); - if ( ! miniroute ) - return NULL; - miniroute->netdev = netdev_get ( netdev ); - memcpy ( &miniroute->address, address, sizeof ( miniroute->address ) ); - miniroute->prefix_len = prefix_len; - assert ( prefix_len <= ( 8 * sizeof ( miniroute->prefix_mask ) ) ); - for ( prefix_mask = miniroute->prefix_mask.s6_addr ; prefix_len >= 8 ; - prefix_mask++, prefix_len -= 8 ) { - *prefix_mask = 0xff; - } - if ( prefix_len ) - *prefix_mask <<= ( 8 - prefix_len ); - miniroute->flags = flags; - list_add ( &miniroute->list, &ipv6_miniroutes ); - ipv6_dump_miniroute ( miniroute ); - - return miniroute; -} - -/** - * Define IPv6 on-link prefix - * - * @v netdev Network device - * @v prefix IPv6 address prefix - * @v prefix_len Prefix length - * @v router Router address (or NULL) - * @ret rc Return status code - */ -int ipv6_set_prefix ( struct net_device *netdev, struct in6_addr *prefix, - unsigned int prefix_len, struct in6_addr *router ) { - struct ipv6_miniroute *miniroute; - int changed; + unsigned int remaining; + unsigned int i; /* Find or create routing table entry */ - miniroute = ipv6_miniroute ( netdev, prefix ); - if ( ! miniroute ) - miniroute = ipv6_add_miniroute ( netdev, prefix, prefix_len, 0); - if ( ! miniroute ) - return -ENOMEM; + miniroute = ipv6_miniroute ( netdev, address ); + if ( ! miniroute ) { - /* Record router and add to start or end of list as appropriate */ - list_del ( &miniroute->list ); - if ( router ) { - changed = ( ( ! ( miniroute->flags & IPV6_HAS_ROUTER ) ) || - ( memcmp ( &miniroute->router, router, - sizeof ( miniroute->router ) ) != 0 ) ); - miniroute->flags |= IPV6_HAS_ROUTER; - memcpy ( &miniroute->router, router, - sizeof ( miniroute->router ) ); - list_add_tail ( &miniroute->list, &ipv6_miniroutes ); - } else { - changed = ( miniroute->flags & IPV6_HAS_ROUTER ); - miniroute->flags &= ~IPV6_HAS_ROUTER; + /* Create new routing table entry */ + miniroute = zalloc ( sizeof ( *miniroute ) ); + if ( ! miniroute ) + return -ENOMEM; + miniroute->netdev = netdev_get ( netdev ); + memcpy ( &miniroute->address, address, + sizeof ( miniroute->address ) ); + + /* Default to prefix length of 64 if none specified */ + if ( ! prefix_len ) + prefix_len = IPV6_DEFAULT_PREFIX_LEN; + miniroute->prefix_len = prefix_len; + assert ( prefix_len <= IPV6_MAX_PREFIX_LEN ); + + /* Construct prefix mask */ + remaining = prefix_len; + for ( prefix_mask = miniroute->prefix_mask.s6_addr ; + remaining >= 8 ; prefix_mask++, remaining -= 8 ) { + *prefix_mask = 0xff; + } + if ( remaining ) + *prefix_mask <<= ( 8 - remaining ); + + /* Add to list of routes */ list_add ( &miniroute->list, &ipv6_miniroutes ); } - if ( changed ) - ipv6_dump_miniroute ( miniroute ); + /* Set or update address, if applicable */ + for ( i = 0 ; i < ( sizeof ( address->s6_addr32 ) / + sizeof ( address->s6_addr32[0] ) ) ; i++ ) { + if ( ( address->s6_addr32[i] & + ~miniroute->prefix_mask.s6_addr32[i] ) != 0 ) { + memcpy ( &miniroute->address, address, + sizeof ( miniroute->address ) ); + miniroute->flags |= IPV6_HAS_ADDRESS; + } + } + if ( miniroute->prefix_len == IPV6_MAX_PREFIX_LEN ) + miniroute->flags |= IPV6_HAS_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 ); return 0; } /** - * Add IPv6 on-link address + * Delete IPv6 minirouting table entry * - * @v netdev Network device - * @v address IPv6 address - * @ret rc Return status code - * - * An on-link prefix for the address must already exist. + * @v miniroute Routing table entry */ -int ipv6_set_address ( struct net_device *netdev, struct in6_addr *address ) { - struct ipv6_miniroute *miniroute; - int changed; +static void ipv6_del_miniroute ( struct ipv6_miniroute *miniroute ) { - /* Find routing table entry */ - miniroute = ipv6_miniroute ( netdev, address ); - if ( ! miniroute ) - return -EADDRNOTAVAIL; - - /* Record address */ - changed = ( ( ! ( miniroute->flags & IPV6_HAS_ADDRESS ) ) || - ( memcmp ( &miniroute->address, address, - sizeof ( miniroute->address ) ) != 0 ) ); - memcpy ( &miniroute->address, address, sizeof ( miniroute->address ) ); - miniroute->flags |= IPV6_HAS_ADDRESS; - if ( changed ) - ipv6_dump_miniroute ( miniroute ); - - return 0; + netdev_put ( miniroute->netdev ); + list_del ( &miniroute->list ); + free ( miniroute ); } /** @@ -1198,65 +1176,98 @@ static int ipv6_register_settings ( struct net_device *netdev ) { return rc; } +/** IPv6 network device driver */ +struct net_driver ipv6_driver __net_driver = { + .name = "IPv6", + .probe = ipv6_register_settings, +}; + /** - * Create IPv6 network device + * Create IPv6 routing table based on configured settings * * @v netdev Network device + * @v settings Settings block * @ret rc Return status code */ -static int ipv6_probe ( struct net_device *netdev ) { - struct ipv6_miniroute *miniroute; - struct in6_addr address; - int prefix_len; +static int ipv6_create_routes ( struct net_device *netdev, + struct settings *settings ) { + struct settings *child; + struct settings *origin; + struct in6_addr ip6_buf; + struct in6_addr gateway6_buf; + struct in6_addr *ip6 = &ip6_buf; + struct in6_addr *gateway6 = &gateway6_buf; + uint8_t len6; + size_t len; int rc; - /* Construct link-local address from EUI-64 as per RFC 2464 */ - memset ( &address, 0, sizeof ( address ) ); - prefix_len = ipv6_link_local ( &address, netdev ); - if ( prefix_len < 0 ) { - rc = prefix_len; - DBGC ( netdev, "IPv6 %s could not construct link-local " - "address: %s\n", netdev->name, strerror ( rc ) ); + /* First, create routing table for any child settings. We do + * this depth-first and in reverse order so that the end + * result reflects the relative priorities of the settings + * blocks. + */ + list_for_each_entry_reverse ( child, &settings->children, siblings ) + ipv6_create_routes ( netdev, child ); + + /* Fetch IPv6 address, if any */ + len = fetch_setting ( settings, &ip6_setting, &origin, NULL, + ip6, sizeof ( *ip6 ) ); + if ( ( len != sizeof ( *ip6 ) ) || ( origin != settings ) ) + return 0; + + /* Fetch prefix length, if defined */ + len = fetch_setting ( settings, &len6_setting, &origin, NULL, + &len6, sizeof ( len6 ) ); + if ( ( len != sizeof ( len6 ) ) || ( origin != settings ) ) + len6 = 0; + if ( len6 > IPV6_MAX_PREFIX_LEN ) + len6 = IPV6_MAX_PREFIX_LEN; + + /* Fetch gateway, if defined */ + len = fetch_setting ( settings, &gateway6_setting, &origin, NULL, + gateway6, sizeof ( *gateway6 ) ); + if ( ( len != sizeof ( *gateway6 ) ) || ( origin != settings ) ) + gateway6 = NULL; + + /* Create or update route */ + if ( ( rc = ipv6_add_miniroute ( netdev, ip6, len6, gateway6 ) ) != 0){ + DBGC ( netdev, "IPv6 %s could not add route: %s\n", + netdev->name, strerror ( rc ) ); return rc; } - /* Create link-local address for this network device */ - miniroute = ipv6_add_miniroute ( netdev, &address, prefix_len, - IPV6_HAS_ADDRESS ); - if ( ! miniroute ) - return -ENOMEM; - - /* Register link-local address settings */ - if ( ( rc = ipv6_register_settings ( netdev ) ) != 0 ) - return rc; - return 0; } /** - * Destroy IPv6 network device + * Create IPv6 routing table based on configured settings * - * @v netdev Network device + * @ret rc Return status code */ -static void ipv6_remove ( struct net_device *netdev ) { +static int ipv6_create_all_routes ( void ) { struct ipv6_miniroute *miniroute; struct ipv6_miniroute *tmp; + struct net_device *netdev; + struct settings *settings; + int rc; - /* Delete all miniroutes for this network device */ - list_for_each_entry_safe ( miniroute, tmp, &ipv6_miniroutes, list ) { - if ( miniroute->netdev == netdev ) { - netdev_put ( miniroute->netdev ); - list_del ( &miniroute->list ); - free ( miniroute ); - } + /* Delete all existing routes */ + list_for_each_entry_safe ( miniroute, tmp, &ipv6_miniroutes, list ) + ipv6_del_miniroute ( miniroute ); + + /* Create routes for each configured network device */ + for_each_netdev ( netdev ) { + settings = netdev_settings ( netdev ); + if ( ( rc = ipv6_create_routes ( netdev, settings ) ) != 0 ) + return rc; } + + return 0; } -/** IPv6 network device driver */ -struct net_driver ipv6_driver __net_driver = { - .name = "IPv6", - .probe = ipv6_probe, - .remove = ipv6_remove, +/** IPv6 settings applicator */ +struct settings_applicator ipv6_settings_applicator __settings_applicator = { + .apply = ipv6_create_all_routes, }; /* Drag in objects via ipv6_protocol */ diff --git a/src/net/ndp.c b/src/net/ndp.c index a35a1219..21bbd999 100644 --- a/src/net/ndp.c +++ b/src/net/ndp.c @@ -342,11 +342,6 @@ ndp_rx_router_advertisement_prefix ( struct net_device *netdev, union ndp_option *option, size_t len ) { struct ndp_router_advertisement_header *radv = &ndp->radv; struct ndp_prefix_information_option *prefix_opt = &option->prefix; - struct in6_addr *router = &sin6_src->sin6_addr; - struct in6_addr address; - struct ipv6conf *ipv6conf; - int prefix_len; - int rc; /* Sanity check */ if ( sizeof ( *prefix_opt ) > len ) { @@ -355,59 +350,13 @@ ndp_rx_router_advertisement_prefix ( struct net_device *netdev, return -EINVAL; } - /* Identify IPv6 configurator, if any */ - ipv6conf = ipv6conf_demux ( netdev ); DBGC ( netdev, "NDP %s found %sdefault router %s ", netdev->name, ( radv->lifetime ? "" : "non-" ), inet6_ntoa ( &sin6_src->sin6_addr ) ); - DBGC ( netdev, "for %s-link %sautonomous prefix %s/%d%s\n", + DBGC ( netdev, "for %s-link %sautonomous prefix %s/%d\n", ( ( prefix_opt->flags & NDP_PREFIX_ON_LINK ) ? "on" : "off" ), ( ( prefix_opt->flags & NDP_PREFIX_AUTONOMOUS ) ? "" : "non-" ), - inet6_ntoa ( &prefix_opt->prefix ), - prefix_opt->prefix_len, ( ipv6conf ? "" : " (ignored)" ) ); - - /* Do nothing unless IPv6 autoconfiguration is in progress */ - if ( ! ipv6conf ) - return 0; - - /* Ignore off-link prefixes */ - if ( ! ( prefix_opt->flags & NDP_PREFIX_ON_LINK ) ) - return 0; - - /* Define prefix */ - if ( ( rc = ipv6_set_prefix ( netdev, &prefix_opt->prefix, - prefix_opt->prefix_len, - ( radv->lifetime ? - router : NULL ) ) ) != 0 ) { - DBGC ( netdev, "NDP %s could not define prefix %s/%d: %s\n", - netdev->name, inet6_ntoa ( &prefix_opt->prefix ), - prefix_opt->prefix_len, strerror ( rc ) ); - return rc; - } - - /* Perform stateless address autoconfiguration, if applicable */ - if ( prefix_opt->flags & NDP_PREFIX_AUTONOMOUS ) { - memcpy ( &address, &prefix_opt->prefix, sizeof ( address ) ); - prefix_len = ipv6_eui64 ( &address, netdev ); - if ( prefix_len < 0 ) { - rc = prefix_len; - DBGC ( netdev, "NDP %s could not construct SLAAC " - "address: %s\n", netdev->name, strerror ( rc ) ); - return rc; - } - if ( prefix_len != prefix_opt->prefix_len ) { - DBGC ( netdev, "NDP %s incorrect SLAAC prefix length " - "%d (expected %d)\n", netdev->name, - prefix_opt->prefix_len, prefix_len ); - return -EINVAL; - } - if ( ( rc = ipv6_set_address ( netdev, &address ) ) != 0 ) { - DBGC ( netdev, "NDP %s could not set address %s: %s\n", - netdev->name, inet6_ntoa ( &address ), - strerror ( rc ) ); - return rc; - } - } + inet6_ntoa ( &prefix_opt->prefix ), prefix_opt->prefix_len ); return 0; } diff --git a/src/net/udp/dhcpv6.c b/src/net/udp/dhcpv6.c index a2c69aaa..253032e4 100644 --- a/src/net/udp/dhcpv6.c +++ b/src/net/udp/dhcpv6.c @@ -462,8 +462,6 @@ enum dhcpv6_session_state_flags { DHCPV6_RX_RECORD_SERVER_ID = 0x04, /** Record received IPv6 address */ DHCPV6_RX_RECORD_IAADDR = 0x08, - /** Apply received IPv6 address */ - DHCPV6_RX_APPLY_IAADDR = 0x10, }; /** DHCPv6 request state */ @@ -471,7 +469,7 @@ static struct dhcpv6_session_state dhcpv6_request = { .tx_type = DHCPV6_REQUEST, .rx_type = DHCPV6_REPLY, .flags = ( DHCPV6_TX_IA_NA | DHCPV6_TX_IAADDR | - DHCPV6_RX_RECORD_IAADDR | DHCPV6_RX_APPLY_IAADDR ), + DHCPV6_RX_RECORD_IAADDR ), .next = NULL, }; @@ -870,19 +868,6 @@ static int dhcpv6_rx ( struct dhcpv6_session *dhcpv6, dhcpv6->server_duid_len ); } - /* Apply identity association address, if applicable */ - if ( dhcpv6->state->flags & DHCPV6_RX_APPLY_IAADDR ) { - if ( ( rc = ipv6_set_address ( dhcpv6->netdev, - &dhcpv6->lease ) ) != 0 ) { - DBGC ( dhcpv6, "DHCPv6 %s could not apply %s: %s\n", - dhcpv6->netdev->name, - inet6_ntoa ( &dhcpv6->lease ), strerror ( rc ) ); - /* This is plausibly the error we want to return */ - dhcpv6->rc = rc; - goto done; - } - } - /* Transition to next state, if applicable */ if ( dhcpv6->state->next ) { dhcpv6_set_state ( dhcpv6, dhcpv6->state->next );