diff options
author | Michael Brown | 2016-07-18 16:13:10 +0200 |
---|---|---|
committer | Michael Brown | 2016-07-19 01:13:00 +0200 |
commit | 3b783d7fd2be053438c4fa968359f01b3c7ece8b (patch) | |
tree | d62167fcb7bce84020a41fbc3c40e5168e2dbb47 /src/net | |
parent | [ipv6] Allow settings to comprise arbitrary subsets of NDP options (diff) | |
download | ipxe-3b783d7fd2be053438c4fa968359f01b3c7ece8b.tar.gz ipxe-3b783d7fd2be053438c4fa968359f01b3c7ece8b.tar.xz ipxe-3b783d7fd2be053438c4fa968359f01b3c7ece8b.zip |
[ipv6] Expose IPv6 settings acquired through NDP
Expose the IPv6 address (or prefix) as ${ip6}, the prefix length as
${len6}, and the router address as ${gateway6}.
Originally-implemented-by: Hannes Reinecke <hare@suse.de>
Originally-implemented-by: Marin Hannache <git@mareo.fr>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/net')
-rw-r--r-- | src/net/ipv6.c | 27 | ||||
-rw-r--r-- | src/net/ndp.c | 251 |
2 files changed, 273 insertions, 5 deletions
diff --git a/src/net/ipv6.c b/src/net/ipv6.c index bbc00d33..78d61369 100644 --- a/src/net/ipv6.c +++ b/src/net/ipv6.c @@ -1061,6 +1061,33 @@ int format_ipv6_setting ( const struct setting_type *type __unused, return snprintf ( buf, len, "%s", inet6_ntoa ( ipv6 ) ); } +/** IPv6 settings scope */ +const struct settings_scope ipv6_scope; + +/** IPv6 address setting */ +const struct setting ip6_setting __setting ( SETTING_IP6, ip6 ) = { + .name = "ip6", + .description = "IPv6 address", + .type = &setting_type_ipv6, + .scope = &ipv6_scope, +}; + +/** IPv6 prefix length setting */ +const struct setting len6_setting __setting ( SETTING_IP6, len6 ) = { + .name = "len6", + .description = "IPv6 prefix length", + .type = &setting_type_int8, + .scope = &ipv6_scope, +}; + +/** Default gateway setting */ +const struct setting gateway6_setting __setting ( SETTING_IP6, gateway6 ) = { + .name = "gateway6", + .description = "IPv6 gateway", + .type = &setting_type_ipv6, + .scope = &ipv6_scope, +}; + /** * Create IPv6 network device * diff --git a/src/net/ndp.c b/src/net/ndp.c index 0876efd9..c488acc7 100644 --- a/src/net/ndp.c +++ b/src/net/ndp.c @@ -20,6 +20,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <stdlib.h> +#include <stdio.h> #include <string.h> #include <errno.h> #include <byteswap.h> @@ -41,6 +42,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); static struct ipv6conf * ipv6conf_demux ( struct net_device *netdev ); static int ipv6conf_rx_router_advertisement ( struct net_device *netdev, + struct in6_addr *router, struct ndp_router_advertisement_header *radv, size_t len ); @@ -585,6 +587,7 @@ ndp_rx_router_advertisement ( struct io_buffer *iobuf, struct sockaddr_in6 *sin6_dest __unused ) { union ndp_header *ndp = iobuf->data; struct ndp_router_advertisement_header *radv = &ndp->radv; + struct in6_addr *router = &sin6_src->sin6_addr; size_t len = iob_len ( iobuf ); int rc; @@ -595,8 +598,8 @@ ndp_rx_router_advertisement ( struct io_buffer *iobuf, goto err_options; /* Pass to IPv6 autoconfiguration */ - if ( ( rc = ipv6conf_rx_router_advertisement ( netdev, radv, - len ) ) != 0 ) + if ( ( rc = ipv6conf_rx_router_advertisement ( netdev, router, + radv, len ) ) != 0 ) goto err_ipv6conf; err_ipv6conf: @@ -627,12 +630,26 @@ struct icmpv6_handler ndp_handlers[] __icmpv6_handler = { * */ +/** An NDP prefix settings block */ +struct ndp_prefix_settings { + /** Settings interface */ + struct settings settings; + /** Name */ + char name[4]; + /** Prefix information option */ + struct ndp_prefix_information_option *prefix; +}; + /** An NDP settings block */ struct ndp_settings { /** Reference counter */ struct refcnt refcnt; /** Settings interface */ struct settings settings; + /** Router address */ + struct in6_addr router; + /** Router lifetime */ + unsigned int lifetime; /** Length of NDP options */ size_t len; /** NDP options */ @@ -780,21 +797,206 @@ static struct settings_operations ndp_settings_operations = { }; /** + * Check applicability of NDP per-prefix setting + * + * @v settings Settings block + * @v setting Setting to fetch + * @ret applies Setting applies within this settings block + */ +static int ndp_prefix_applies ( struct settings *settings __unused, + const struct setting *setting ) { + + return ( setting->scope == &ipv6_scope ); +} + +/** + * Fetch value of NDP IPv6 address setting + * + * @v settings Settings block + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ +static int ndp_prefix_fetch_ip6 ( struct settings *settings, void *data, + size_t len ) { + struct ndp_prefix_settings *prefset = + container_of ( settings, struct ndp_prefix_settings, settings ); + struct ndp_settings *ndpset = + container_of ( settings->parent, struct ndp_settings, settings); + struct net_device *netdev = + container_of ( ndpset->settings.parent, struct net_device, + settings.settings ); + struct ndp_prefix_information_option *prefix = prefset->prefix; + struct in6_addr ip6; + int prefix_len; + + /* Skip dead prefixes */ + if ( ! prefix->valid ) + return -ENOENT; + + /* Construct IPv6 address via SLAAC, if applicable */ + memcpy ( &ip6, &prefix->prefix, sizeof ( ip6 ) ); + if ( prefix->flags & NDP_PREFIX_AUTONOMOUS ) { + prefix_len = ipv6_eui64 ( &ip6, netdev ); + if ( prefix_len < 0 ) + return prefix_len; + if ( prefix_len != prefix->prefix_len ) + return -EINVAL; + } + + /* Fill in IPv6 address */ + if ( len > sizeof ( ip6 ) ) + len = sizeof ( ip6 ); + memcpy ( data, &ip6, len ); + + return sizeof ( ip6 ); +} + +/** + * Fetch value of NDP prefix length setting + * + * @v settings Settings block + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ +static int ndp_prefix_fetch_len6 ( struct settings *settings, void *data, + size_t len ) { + struct ndp_prefix_settings *prefset = + container_of ( settings, struct ndp_prefix_settings, settings ); + struct ndp_prefix_information_option *prefix = prefset->prefix; + uint8_t *len6; + + /* Fill in prefix length */ + if ( len >= sizeof ( *len6 ) ) { + /* We treat an off-link prefix as having a prefix + * length covering the entire IPv6 address. + */ + len6 = data; + *len6 = ( ( prefix->flags & NDP_PREFIX_ON_LINK ) ? + prefix->prefix_len : -1UL ); + } + + return sizeof ( *len6 ); +} + +/** + * Fetch value of NDP router address setting + * + * @v settings Settings block + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ +static int ndp_prefix_fetch_gateway6 ( struct settings *settings, + void *data, size_t len ) { + struct ndp_settings *ndpset = + container_of ( settings->parent, struct ndp_settings, settings); + + /* Treat non-routing router as non-existent */ + if ( ! ndpset->lifetime ) + return -ENOENT; + + /* Fill in router address */ + if ( len > sizeof ( ndpset->router ) ) + len = sizeof ( ndpset->router ); + memcpy ( data, &ndpset->router, len ); + + return sizeof ( ndpset->router ); +} + +/** An NDP per-prefix setting operation */ +struct ndp_prefix_operation { + /** Generic setting */ + const struct setting *setting; + /** + * Fetch value of setting + * + * @v settings Settings block + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ + int ( * fetch ) ( struct settings *settings, void *data, size_t len ); +}; + +/** NDP per-prefix settings operations */ +static struct ndp_prefix_operation ndp_prefix_operations[] = { + { &ip6_setting, ndp_prefix_fetch_ip6 }, + { &len6_setting, ndp_prefix_fetch_len6 }, + { &gateway6_setting, ndp_prefix_fetch_gateway6 }, +}; + +/** + * Fetch value of NDP pre-prefix setting + * + * @v settings Settings block + * @v setting Setting to fetch + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ +static int ndp_prefix_fetch ( struct settings *settings, + struct setting *setting, + void *data, size_t len ) { + struct ndp_prefix_operation *op; + unsigned int i; + + /* Handle per-prefix settings */ + for ( i = 0 ; i < ( sizeof ( ndp_prefix_operations ) / + sizeof ( ndp_prefix_operations[0] ) ) ; i++ ) { + op = &ndp_prefix_operations[i]; + if ( setting_cmp ( setting, op->setting ) == 0 ) + return op->fetch ( settings, data, len ); + } + + return -ENOENT; +} + +/** NDP per-prefix settings operations */ +static struct settings_operations ndp_prefix_settings_operations = { + .applies = ndp_prefix_applies, + .fetch = ndp_prefix_fetch, +}; + +/** * Register NDP settings * * @v netdev Network device + * @v router Router address + * @v lifetime Router lifetime * @v options NDP options * @v len Length of options * @ret rc Return status code */ static int ndp_register_settings ( struct net_device *netdev, + struct in6_addr *router, + unsigned int lifetime, union ndp_option *options, size_t len ) { struct settings *parent = netdev_settings ( netdev ); + union ndp_option *option; struct ndp_settings *ndpset; + struct ndp_prefix_settings *prefset; + size_t offset; + size_t option_len; + unsigned int prefixes; + unsigned int instance; 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. + */ + for ( prefixes = 0, offset = 0 ; offset < len ; offset += option_len ) { + option = ( ( ( void * ) options ) + offset ); + option_len = ( option->header.blocks * NDP_OPTION_BLKSZ ); + if ( option->header.type == NDP_OPT_PREFIX ) + prefixes++; + } + /* Allocate and initialise structure */ - ndpset = zalloc ( sizeof ( *ndpset ) + len ); + ndpset = zalloc ( sizeof ( *ndpset ) + len + + ( prefixes * sizeof ( *prefset ) ) ); if ( ! ndpset ) { rc = -ENOMEM; goto err_alloc; @@ -802,14 +1004,50 @@ 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 ); + memcpy ( &ndpset->router, router, sizeof ( ndpset->router ) ); + ndpset->lifetime = lifetime; ndpset->len = len; memcpy ( ndpset->options, options, len ); + prefset = ( ( ( void * ) ndpset->options ) + len ); /* Register settings */ if ( ( rc = register_settings ( &ndpset->settings, parent, NDP_SETTINGS_NAME ) ) != 0 ) goto err_register; + /* Construct and register per-prefix settings */ + for ( instance = 0, offset = 0 ; offset < len ; offset += option_len ) { + + /* Skip non-prefix options */ + option = ( ( ( void * ) ndpset->options ) + offset ); + option_len = ( option->header.blocks * NDP_OPTION_BLKSZ ); + if ( option->header.type != NDP_OPT_PREFIX ) + continue; + + /* Initialise structure */ + settings_init ( &prefset->settings, + &ndp_prefix_settings_operations, + &ndpset->refcnt, &ndp_settings_scope ); + prefset->prefix = &option->prefix; + snprintf ( prefset->name, sizeof ( prefset->name ), "%d", + instance++ ); + + /* Register settings */ + if ( ( rc = register_settings ( &prefset->settings, + &ndpset->settings, + prefset->name ) ) != 0 ) + goto err_register_prefix; + + /* Move to next per-prefix settings */ + prefset++; + } + assert ( instance == prefixes ); + + ref_put ( &ndpset->refcnt ); + return 0; + + err_register_prefix: + unregister_settings ( &ndpset->settings ); err_register: ref_put ( &ndpset->refcnt ); err_alloc: @@ -938,6 +1176,7 @@ static void ipv6conf_expired ( struct retry_timer *timer, int fail ) { * Handle router advertisement during IPv6 autoconfiguration * * @v netdev Network device + * @v router Router address * @v radv Router advertisement * @v len Length of router advertisement * @ret rc Return status code @@ -947,6 +1186,7 @@ static void ipv6conf_expired ( struct retry_timer *timer, int fail ) { */ static int ipv6conf_rx_router_advertisement ( struct net_device *netdev, + struct in6_addr *router, struct ndp_router_advertisement_header *radv, size_t len ) { struct ipv6conf *ipv6conf; @@ -970,8 +1210,9 @@ ipv6conf_rx_router_advertisement ( struct net_device *netdev, /* Register NDP settings */ option_len = ( len - offsetof ( typeof ( *radv ), option ) ); - if ( ( rc = ndp_register_settings ( netdev, radv->option, - option_len ) ) != 0 ) + if ( ( rc = ndp_register_settings ( netdev, router, + ntohl ( radv->lifetime ), + radv->option, option_len ) ) != 0 ) return rc; /* Start DHCPv6 if required */ |