diff options
author | Michael Brown | 2016-07-18 15:37:04 +0200 |
---|---|---|
committer | Michael Brown | 2016-07-19 01:13:00 +0200 |
commit | ee54ab5be6c46adae18fc215b66d629796328b04 (patch) | |
tree | 22f5c5b1ad140a201c22ca1608f4ac820ab352c1 /src/net | |
parent | [settings] Correctly mortalise autovivified child settings blocks (diff) | |
download | ipxe-ee54ab5be6c46adae18fc215b66d629796328b04.tar.gz ipxe-ee54ab5be6c46adae18fc215b66d629796328b04.tar.xz ipxe-ee54ab5be6c46adae18fc215b66d629796328b04.zip |
[ipv6] Allow settings to comprise arbitrary subsets of NDP options
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/net')
-rw-r--r-- | src/net/ndp.c | 102 |
1 files changed, 67 insertions, 35 deletions
diff --git a/src/net/ndp.c b/src/net/ndp.c index fb6f3231..0876efd9 100644 --- a/src/net/ndp.c +++ b/src/net/ndp.c @@ -636,7 +636,7 @@ struct ndp_settings { /** Length of NDP options */ size_t len; /** NDP options */ - union ndp_option option[0]; + union ndp_option options[0]; }; /** NDP settings scope */ @@ -647,9 +647,11 @@ static const struct settings_scope ndp_settings_scope; * * @v type NDP option type * @v offset Starting offset of data + * @v len Length of data (or 0 to use all remaining data) * @ret tag NDP tag */ -#define NDP_TAG( type, offset ) ( ( (offset) << 8 ) | (type) ) +#define NDP_TAG( type, offset, len ) \ + ( ( (len) << 16 ) | ( (offset) << 8 ) | (type) ) /** * Extract NDP tag type @@ -657,7 +659,7 @@ static const struct settings_scope ndp_settings_scope; * @v tag NDP tag * @ret type NDP option type */ -#define NDP_TAG_TYPE( tag ) ( (tag) & 0xff ) +#define NDP_TAG_TYPE( tag ) ( ( (tag) >> 0 ) & 0xff ) /** * Extract NDP tag offset @@ -665,7 +667,23 @@ static const struct settings_scope ndp_settings_scope; * @v tag NDP tag * @ret offset Starting offset of data */ -#define NDP_TAG_OFFSET( tag ) ( (tag) >> 8 ) +#define NDP_TAG_OFFSET( tag ) ( ( (tag) >> 8 ) & 0xff ) + +/** + * Extract NDP tag length + * + * @v tag NDP tag + * @ret len Length of data (or 0 to use all remaining data) + */ +#define NDP_TAG_LEN( tag ) ( ( (tag) >> 16 ) & 0xff ) + +/** + * Extract NDP tag instance + * + * @v tag NDP tag + * @ret instance Instance + */ +#define NDP_TAG_INSTANCE( tag ) ( ( (tag) >> 24 ) & 0xff ) /** * Check applicability of NDP setting @@ -698,44 +716,58 @@ static int ndp_fetch ( struct settings *settings, container_of ( settings->parent, struct net_device, settings.settings ); union ndp_option *option; - unsigned int type = NDP_TAG_TYPE ( setting->tag ); - unsigned int offset = NDP_TAG_OFFSET ( setting->tag ); - size_t remaining; + unsigned int tag_type; + unsigned int tag_offset; + unsigned int tag_len; + unsigned int tag_instance; + size_t offset; size_t option_len; - size_t payload_len; + void *option_data; + + /* Parse setting tag */ + tag_type = NDP_TAG_TYPE ( setting->tag ); + tag_offset = NDP_TAG_OFFSET ( setting->tag ); + tag_len = NDP_TAG_LEN ( setting->tag ); + tag_instance = NDP_TAG_INSTANCE ( setting->tag ); /* Scan through NDP options for requested type. We can assume * that the options are well-formed, otherwise they would have * been rejected prior to being stored. */ - option = ndpset->option; - remaining = ndpset->len; - while ( remaining ) { + for ( offset = 0 ; offset < ndpset->len ; offset += option_len ) { /* Calculate option length */ + option = ( ( ( void * ) ndpset->options ) + offset ); option_len = ( option->header.blocks * NDP_OPTION_BLKSZ ); - /* If this is the requested option, return it */ - if ( option->header.type == type ) { - - /* Sanity check */ - if ( offset > option_len ) { - DBGC ( netdev, "NDP %s option %d too short\n", - netdev->name, type ); - return -EINVAL; - } - payload_len = ( option_len - offset ); - - /* Copy data to output buffer */ - if ( len > payload_len ) - len = payload_len; - memcpy ( data, ( ( ( void * ) option ) + offset ), len); - return payload_len; + /* Skip options that do not match this tag */ + if ( option->header.type != tag_type ) + continue; + + /* Skip previous instances of this option */ + if ( tag_instance-- != 0 ) + continue; + + /* Sanity check */ + if ( ( tag_offset + tag_len ) > option_len ) { + DBGC ( netdev, "NDP %s option %d too short\n", + netdev->name, tag_type ); + return -EINVAL; } + if ( ! tag_len ) + tag_len = ( option_len - tag_offset ); + option_data = ( ( ( void * ) option ) + tag_offset ); - /* Move to next option */ - option = ( ( ( void * ) option ) + option_len ); - remaining -= option_len; + /* Copy data to output buffer */ + if ( len > tag_len ) + len = tag_len; + memcpy ( data, option_data, len ); + + /* Default to hex if no type is specified */ + if ( ! setting->type ) + setting->type = &setting_type_hex; + + return tag_len; } return -ENOENT; @@ -751,12 +783,12 @@ static struct settings_operations ndp_settings_operations = { * Register NDP settings * * @v netdev Network device - * @v option NDP options + * @v options NDP options * @v len Length of options * @ret rc Return status code */ static int ndp_register_settings ( struct net_device *netdev, - union ndp_option *option, size_t len ) { + union ndp_option *options, size_t len ) { struct settings *parent = netdev_settings ( netdev ); struct ndp_settings *ndpset; int rc; @@ -771,7 +803,7 @@ static int ndp_register_settings ( struct net_device *netdev, settings_init ( &ndpset->settings, &ndp_settings_operations, &ndpset->refcnt, &ndp_settings_scope ); ndpset->len = len; - memcpy ( ndpset->option, option, len ); + memcpy ( ndpset->options, options, len ); /* Register settings */ if ( ( rc = register_settings ( &ndpset->settings, parent, @@ -789,7 +821,7 @@ const struct setting ndp_dns6_setting __setting ( SETTING_IP6_EXTRA, dns6 ) = { .name = "dns6", .description = "DNS server", .tag = NDP_TAG ( NDP_OPT_RDNSS, - offsetof ( struct ndp_rdnss_option, addresses ) ), + offsetof ( struct ndp_rdnss_option, addresses ), 0 ), .type = &setting_type_ipv6, .scope = &ndp_settings_scope, }; @@ -799,7 +831,7 @@ const struct setting ndp_dnssl_setting __setting ( SETTING_IP_EXTRA, dnssl ) = { .name = "dnssl", .description = "DNS search list", .tag = NDP_TAG ( NDP_OPT_DNSSL, - offsetof ( struct ndp_dnssl_option, names ) ), + offsetof ( struct ndp_dnssl_option, names ), 0 ), .type = &setting_type_dnssl, .scope = &ndp_settings_scope, }; |