From 4ad3c73b3099cfe3b7f1c79ddfe9061809e4ac6d Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 19 Jul 2016 16:57:32 +0100 Subject: [PATCH] [ipv6] Match user expectations for IPv6 settings priorities A reasonable user expectation is that ${net0/ip6} should show the "highest-priority" of the IPv6 addresses, even when multiple IPv6 addresses are active. The expected order of priority is likely to be manually-assigned addresses first, then stateful DHCPv6 addresses, then SLAAC addresses, and lastly link-local addresses. Using ${priority} to enforce an ordering is undesirable since that would affect the priority assigned to each of the net blocks as a whole, so use the sibling ordering capability instead. Signed-off-by: Michael Brown --- src/include/ipxe/ipv6.h | 12 ++++++++++++ src/net/ipv6.c | 1 + src/net/ndp.c | 19 +++++++++++++++++-- src/net/udp/dhcpv6.c | 1 + 4 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/include/ipxe/ipv6.h b/src/include/ipxe/ipv6.h index 86c8ff92..4dd03f05 100644 --- a/src/include/ipxe/ipv6.h +++ b/src/include/ipxe/ipv6.h @@ -238,6 +238,18 @@ static inline void ipv6_all_routers ( struct in6_addr *addr ) { addr->s6_addr[15] = 2; } +/** IPv6 settings sibling order */ +enum ipv6_settings_order { + /** No address */ + IPV6_ORDER_PREFIX_ONLY = -4, + /** Link-local address */ + IPV6_ORDER_LINK_LOCAL = -3, + /** Address assigned via SLAAC */ + IPV6_ORDER_SLAAC = -2, + /** Address assigned via DHCPv6 */ + IPV6_ORDER_DHCPV6 = -1, +}; + /** IPv6 link-local address settings block name */ #define IPV6_SETTINGS_NAME "link" diff --git a/src/net/ipv6.c b/src/net/ipv6.c index cdd6640c..04ba3d8b 100644 --- a/src/net/ipv6.c +++ b/src/net/ipv6.c @@ -1185,6 +1185,7 @@ static int ipv6_register_settings ( struct net_device *netdev ) { ref_init ( &ipv6set->refcnt, NULL ); settings_init ( &ipv6set->settings, &ipv6_settings_operations, &ipv6set->refcnt, &ipv6_scope ); + ipv6set->settings.order = IPV6_ORDER_LINK_LOCAL; /* Register settings */ if ( ( rc = register_settings ( &ipv6set->settings, parent, diff --git a/src/net/ndp.c b/src/net/ndp.c index c488acc7..a35a1219 100644 --- a/src/net/ndp.c +++ b/src/net/ndp.c @@ -981,17 +981,28 @@ static int ndp_register_settings ( struct net_device *netdev, size_t option_len; unsigned int prefixes; unsigned int instance; + int order; int rc; /* Count number of prefix options. We can assume that the * options are well-formed, otherwise they would have been * rejected prior to being stored. */ + order = IPV6_ORDER_PREFIX_ONLY; for ( prefixes = 0, offset = 0 ; offset < len ; offset += option_len ) { + + /* Skip non-prefix options */ option = ( ( ( void * ) options ) + offset ); option_len = ( option->header.blocks * NDP_OPTION_BLKSZ ); - if ( option->header.type == NDP_OPT_PREFIX ) - prefixes++; + if ( option->header.type != NDP_OPT_PREFIX ) + continue; + + /* Count number of prefixes */ + prefixes++; + + /* Increase overall order if we have SLAAC addresses */ + if ( option->prefix.flags & NDP_PREFIX_AUTONOMOUS ) + order = IPV6_ORDER_SLAAC; } /* Allocate and initialise structure */ @@ -1004,6 +1015,7 @@ static int ndp_register_settings ( struct net_device *netdev, ref_init ( &ndpset->refcnt, NULL ); settings_init ( &ndpset->settings, &ndp_settings_operations, &ndpset->refcnt, &ndp_settings_scope ); + ndpset->settings.order = order; memcpy ( &ndpset->router, router, sizeof ( ndpset->router ) ); ndpset->lifetime = lifetime; ndpset->len = len; @@ -1028,6 +1040,9 @@ static int ndp_register_settings ( struct net_device *netdev, settings_init ( &prefset->settings, &ndp_prefix_settings_operations, &ndpset->refcnt, &ndp_settings_scope ); + prefset->settings.order = + ( ( option->prefix.flags & NDP_PREFIX_AUTONOMOUS ) ? + IPV6_ORDER_SLAAC : IPV6_ORDER_PREFIX_ONLY ); prefset->prefix = &option->prefix; snprintf ( prefset->name, sizeof ( prefset->name ), "%d", instance++ ); diff --git a/src/net/udp/dhcpv6.c b/src/net/udp/dhcpv6.c index ac701042..a2c69aaa 100644 --- a/src/net/udp/dhcpv6.c +++ b/src/net/udp/dhcpv6.c @@ -375,6 +375,7 @@ static int dhcpv6_register ( struct in6_addr *lease, ref_init ( &dhcpv6set->refcnt, NULL ); settings_init ( &dhcpv6set->settings, &dhcpv6_settings_operations, &dhcpv6set->refcnt, &dhcpv6_scope ); + dhcpv6set->settings.order = IPV6_ORDER_DHCPV6; data = ( ( ( void * ) dhcpv6set ) + sizeof ( *dhcpv6set ) ); len = options->len; memcpy ( data, options->data, len );