|  | @@ -38,8 +38,10 @@ FILE_LICENCE ( GPL2_OR_LATER );
 | 
		
	
		
			
			| 38 | 38 |   *
 | 
		
	
		
			
			| 39 | 39 |   */
 | 
		
	
		
			
			| 40 | 40 |  
 | 
		
	
		
			
			| 41 |  | -static int ipv6conf_rx_router_advertisement ( struct net_device *netdev,
 | 
		
	
		
			
			| 42 |  | -					      unsigned int flags );
 | 
		
	
		
			
			|  | 41 | +static int
 | 
		
	
		
			
			|  | 42 | +ipv6conf_rx_router_advertisement ( struct net_device *netdev,
 | 
		
	
		
			
			|  | 43 | +				   struct ndp_router_advertisement_header *radv,
 | 
		
	
		
			
			|  | 44 | +				   size_t len );
 | 
		
	
		
			
			| 43 | 45 |  
 | 
		
	
		
			
			| 44 | 46 |  /**
 | 
		
	
		
			
			| 45 | 47 |   * Transmit NDP packet with link-layer address option
 | 
		
	
	
		
			
			|  | @@ -585,8 +587,8 @@ ndp_rx_router_advertisement ( struct io_buffer *iobuf,
 | 
		
	
		
			
			| 585 | 587 |  		goto err_options;
 | 
		
	
		
			
			| 586 | 588 |  
 | 
		
	
		
			
			| 587 | 589 |  	/* Pass to IPv6 autoconfiguration */
 | 
		
	
		
			
			| 588 |  | -	if ( ( rc = ipv6conf_rx_router_advertisement ( netdev,
 | 
		
	
		
			
			| 589 |  | -						       radv->flags ) ) != 0 )
 | 
		
	
		
			
			|  | 590 | +	if ( ( rc = ipv6conf_rx_router_advertisement ( netdev, radv,
 | 
		
	
		
			
			|  | 591 | +						       len ) ) != 0 )
 | 
		
	
		
			
			| 590 | 592 |  		goto err_ipv6conf;
 | 
		
	
		
			
			| 591 | 593 |  
 | 
		
	
		
			
			| 592 | 594 |   err_ipv6conf:
 | 
		
	
	
		
			
			|  | @@ -611,6 +613,179 @@ struct icmpv6_handler ndp_handlers[] __icmpv6_handler = {
 | 
		
	
		
			
			| 611 | 613 |  	},
 | 
		
	
		
			
			| 612 | 614 |  };
 | 
		
	
		
			
			| 613 | 615 |  
 | 
		
	
		
			
			|  | 616 | +/****************************************************************************
 | 
		
	
		
			
			|  | 617 | + *
 | 
		
	
		
			
			|  | 618 | + * NDP settings
 | 
		
	
		
			
			|  | 619 | + *
 | 
		
	
		
			
			|  | 620 | + */
 | 
		
	
		
			
			|  | 621 | +
 | 
		
	
		
			
			|  | 622 | +/** An NDP settings block */
 | 
		
	
		
			
			|  | 623 | +struct ndp_settings {
 | 
		
	
		
			
			|  | 624 | +	/** Reference counter */
 | 
		
	
		
			
			|  | 625 | +	struct refcnt refcnt;
 | 
		
	
		
			
			|  | 626 | +	/** Settings interface */
 | 
		
	
		
			
			|  | 627 | +	struct settings settings;
 | 
		
	
		
			
			|  | 628 | +	/** Length of NDP options */
 | 
		
	
		
			
			|  | 629 | +	size_t len;
 | 
		
	
		
			
			|  | 630 | +	/** NDP options */
 | 
		
	
		
			
			|  | 631 | +	union ndp_option option[0];
 | 
		
	
		
			
			|  | 632 | +};
 | 
		
	
		
			
			|  | 633 | +
 | 
		
	
		
			
			|  | 634 | +/** NDP settings scope */
 | 
		
	
		
			
			|  | 635 | +static const struct settings_scope ndp_settings_scope;
 | 
		
	
		
			
			|  | 636 | +
 | 
		
	
		
			
			|  | 637 | +/**
 | 
		
	
		
			
			|  | 638 | + * Construct NDP tag
 | 
		
	
		
			
			|  | 639 | + *
 | 
		
	
		
			
			|  | 640 | + * @v type		NDP option type
 | 
		
	
		
			
			|  | 641 | + * @v offset		Starting offset of data
 | 
		
	
		
			
			|  | 642 | + * @ret tag		NDP tag
 | 
		
	
		
			
			|  | 643 | + */
 | 
		
	
		
			
			|  | 644 | +#define NDP_TAG( type, offset )	( ( (offset) << 8 ) | (type) )
 | 
		
	
		
			
			|  | 645 | +
 | 
		
	
		
			
			|  | 646 | +/**
 | 
		
	
		
			
			|  | 647 | + * Extract NDP tag type
 | 
		
	
		
			
			|  | 648 | + *
 | 
		
	
		
			
			|  | 649 | + * @v tag		NDP tag
 | 
		
	
		
			
			|  | 650 | + * @ret type		NDP option type
 | 
		
	
		
			
			|  | 651 | + */
 | 
		
	
		
			
			|  | 652 | +#define NDP_TAG_TYPE( tag ) ( (tag) & 0xff )
 | 
		
	
		
			
			|  | 653 | +
 | 
		
	
		
			
			|  | 654 | +/**
 | 
		
	
		
			
			|  | 655 | + * Extract NDP tag offset
 | 
		
	
		
			
			|  | 656 | + *
 | 
		
	
		
			
			|  | 657 | + * @v tag		NDP tag
 | 
		
	
		
			
			|  | 658 | + * @ret offset		Starting offset of data
 | 
		
	
		
			
			|  | 659 | + */
 | 
		
	
		
			
			|  | 660 | +#define NDP_TAG_OFFSET( tag ) ( (tag) >> 8 )
 | 
		
	
		
			
			|  | 661 | +
 | 
		
	
		
			
			|  | 662 | +/**
 | 
		
	
		
			
			|  | 663 | + * Check applicability of NDP setting
 | 
		
	
		
			
			|  | 664 | + *
 | 
		
	
		
			
			|  | 665 | + * @v settings		Settings block
 | 
		
	
		
			
			|  | 666 | + * @v setting		Setting to fetch
 | 
		
	
		
			
			|  | 667 | + * @ret applies		Setting applies within this settings block
 | 
		
	
		
			
			|  | 668 | + */
 | 
		
	
		
			
			|  | 669 | +static int ndp_applies ( struct settings *settings __unused,
 | 
		
	
		
			
			|  | 670 | +			 const struct setting *setting ) {
 | 
		
	
		
			
			|  | 671 | +
 | 
		
	
		
			
			|  | 672 | +	return ( setting->scope == &ndp_settings_scope );
 | 
		
	
		
			
			|  | 673 | +}
 | 
		
	
		
			
			|  | 674 | +
 | 
		
	
		
			
			|  | 675 | +/**
 | 
		
	
		
			
			|  | 676 | + * Fetch value of NDP setting
 | 
		
	
		
			
			|  | 677 | + *
 | 
		
	
		
			
			|  | 678 | + * @v settings		Settings block
 | 
		
	
		
			
			|  | 679 | + * @v setting		Setting to fetch
 | 
		
	
		
			
			|  | 680 | + * @v data		Buffer to fill with setting data
 | 
		
	
		
			
			|  | 681 | + * @v len		Length of buffer
 | 
		
	
		
			
			|  | 682 | + * @ret len		Length of setting data, or negative error
 | 
		
	
		
			
			|  | 683 | + */
 | 
		
	
		
			
			|  | 684 | +static int ndp_fetch ( struct settings *settings,
 | 
		
	
		
			
			|  | 685 | +		       struct setting *setting,
 | 
		
	
		
			
			|  | 686 | +		       void *data, size_t len ) {
 | 
		
	
		
			
			|  | 687 | +	struct ndp_settings *ndpset =
 | 
		
	
		
			
			|  | 688 | +		container_of ( settings, struct ndp_settings, settings );
 | 
		
	
		
			
			|  | 689 | +	struct net_device *netdev =
 | 
		
	
		
			
			|  | 690 | +		container_of ( settings->parent, struct net_device,
 | 
		
	
		
			
			|  | 691 | +			       settings.settings );
 | 
		
	
		
			
			|  | 692 | +	union ndp_option *option;
 | 
		
	
		
			
			|  | 693 | +	unsigned int type = NDP_TAG_TYPE ( setting->tag );
 | 
		
	
		
			
			|  | 694 | +	unsigned int offset = NDP_TAG_OFFSET ( setting->tag );
 | 
		
	
		
			
			|  | 695 | +	size_t remaining;
 | 
		
	
		
			
			|  | 696 | +	size_t option_len;
 | 
		
	
		
			
			|  | 697 | +	size_t payload_len;
 | 
		
	
		
			
			|  | 698 | +
 | 
		
	
		
			
			|  | 699 | +	/* Scan through NDP options for requested type.  We can assume
 | 
		
	
		
			
			|  | 700 | +	 * that the options are well-formed, otherwise they would have
 | 
		
	
		
			
			|  | 701 | +	 * been rejected prior to being stored.
 | 
		
	
		
			
			|  | 702 | +	 */
 | 
		
	
		
			
			|  | 703 | +	option = ndpset->option;
 | 
		
	
		
			
			|  | 704 | +	remaining = ndpset->len;
 | 
		
	
		
			
			|  | 705 | +	while ( remaining ) {
 | 
		
	
		
			
			|  | 706 | +
 | 
		
	
		
			
			|  | 707 | +		/* Calculate option length */
 | 
		
	
		
			
			|  | 708 | +		option_len = ( option->header.blocks * NDP_OPTION_BLKSZ );
 | 
		
	
		
			
			|  | 709 | +
 | 
		
	
		
			
			|  | 710 | +		/* If this is the requested option, return it */
 | 
		
	
		
			
			|  | 711 | +		if ( option->header.type == type ) {
 | 
		
	
		
			
			|  | 712 | +
 | 
		
	
		
			
			|  | 713 | +			/* Sanity check */
 | 
		
	
		
			
			|  | 714 | +			if ( offset > option_len ) {
 | 
		
	
		
			
			|  | 715 | +				DBGC ( netdev, "NDP %s option %d too short\n",
 | 
		
	
		
			
			|  | 716 | +				       netdev->name, type );
 | 
		
	
		
			
			|  | 717 | +				return -EINVAL;
 | 
		
	
		
			
			|  | 718 | +			}
 | 
		
	
		
			
			|  | 719 | +			payload_len = ( option_len - offset );
 | 
		
	
		
			
			|  | 720 | +
 | 
		
	
		
			
			|  | 721 | +			/* Copy data to output buffer */
 | 
		
	
		
			
			|  | 722 | +			if ( len > payload_len )
 | 
		
	
		
			
			|  | 723 | +				len = payload_len;
 | 
		
	
		
			
			|  | 724 | +			memcpy ( data, ( ( ( void * ) option ) + offset ), len);
 | 
		
	
		
			
			|  | 725 | +			return payload_len;
 | 
		
	
		
			
			|  | 726 | +		}
 | 
		
	
		
			
			|  | 727 | +
 | 
		
	
		
			
			|  | 728 | +		/* Move to next option */
 | 
		
	
		
			
			|  | 729 | +		option = ( ( ( void * ) option ) + option_len );
 | 
		
	
		
			
			|  | 730 | +		remaining -= option_len;
 | 
		
	
		
			
			|  | 731 | +	}
 | 
		
	
		
			
			|  | 732 | +
 | 
		
	
		
			
			|  | 733 | +	return -ENOENT;
 | 
		
	
		
			
			|  | 734 | +}
 | 
		
	
		
			
			|  | 735 | +
 | 
		
	
		
			
			|  | 736 | +/** NDP settings operations */
 | 
		
	
		
			
			|  | 737 | +static struct settings_operations ndp_settings_operations = {
 | 
		
	
		
			
			|  | 738 | +	.applies = ndp_applies,
 | 
		
	
		
			
			|  | 739 | +	.fetch = ndp_fetch,
 | 
		
	
		
			
			|  | 740 | +};
 | 
		
	
		
			
			|  | 741 | +
 | 
		
	
		
			
			|  | 742 | +/**
 | 
		
	
		
			
			|  | 743 | + * Register NDP settings
 | 
		
	
		
			
			|  | 744 | + *
 | 
		
	
		
			
			|  | 745 | + * @v netdev		Network device
 | 
		
	
		
			
			|  | 746 | + * @v option		NDP options
 | 
		
	
		
			
			|  | 747 | + * @v len		Length of options
 | 
		
	
		
			
			|  | 748 | + * @ret rc		Return status code
 | 
		
	
		
			
			|  | 749 | + */
 | 
		
	
		
			
			|  | 750 | +static int ndp_register_settings ( struct net_device *netdev,
 | 
		
	
		
			
			|  | 751 | +				   union ndp_option *option, size_t len ) {
 | 
		
	
		
			
			|  | 752 | +	struct settings *parent = netdev_settings ( netdev );
 | 
		
	
		
			
			|  | 753 | +	struct ndp_settings *ndpset;
 | 
		
	
		
			
			|  | 754 | +	int rc;
 | 
		
	
		
			
			|  | 755 | +
 | 
		
	
		
			
			|  | 756 | +	/* Allocate and initialise structure */
 | 
		
	
		
			
			|  | 757 | +	ndpset = zalloc ( sizeof ( *ndpset ) + len );
 | 
		
	
		
			
			|  | 758 | +	if ( ! ndpset ) {
 | 
		
	
		
			
			|  | 759 | +		rc = -ENOMEM;
 | 
		
	
		
			
			|  | 760 | +		goto err_alloc;
 | 
		
	
		
			
			|  | 761 | +	}
 | 
		
	
		
			
			|  | 762 | +	ref_init ( &ndpset->refcnt, NULL );
 | 
		
	
		
			
			|  | 763 | +	settings_init ( &ndpset->settings, &ndp_settings_operations,
 | 
		
	
		
			
			|  | 764 | +			&ndpset->refcnt, &ndp_settings_scope );
 | 
		
	
		
			
			|  | 765 | +	ndpset->len = len;
 | 
		
	
		
			
			|  | 766 | +	memcpy ( ndpset->option, option, len );
 | 
		
	
		
			
			|  | 767 | +
 | 
		
	
		
			
			|  | 768 | +	/* Register settings */
 | 
		
	
		
			
			|  | 769 | +	if ( ( rc = register_settings ( &ndpset->settings, parent,
 | 
		
	
		
			
			|  | 770 | +					NDP_SETTINGS_NAME ) ) != 0 )
 | 
		
	
		
			
			|  | 771 | +		goto err_register;
 | 
		
	
		
			
			|  | 772 | +
 | 
		
	
		
			
			|  | 773 | + err_register:
 | 
		
	
		
			
			|  | 774 | +	ref_put ( &ndpset->refcnt );
 | 
		
	
		
			
			|  | 775 | + err_alloc:
 | 
		
	
		
			
			|  | 776 | +	return rc;
 | 
		
	
		
			
			|  | 777 | +}
 | 
		
	
		
			
			|  | 778 | +
 | 
		
	
		
			
			|  | 779 | +/** DNS server setting */
 | 
		
	
		
			
			|  | 780 | +const struct setting ndp_dns6_setting __setting ( SETTING_IP_EXTRA, dns6 ) = {
 | 
		
	
		
			
			|  | 781 | +	.name = "dns6",
 | 
		
	
		
			
			|  | 782 | +	.description = "DNS server",
 | 
		
	
		
			
			|  | 783 | +	.tag = NDP_TAG ( NDP_OPT_RDNSS,
 | 
		
	
		
			
			|  | 784 | +			 offsetof ( struct ndp_rdnss_option, addresses ) ),
 | 
		
	
		
			
			|  | 785 | +	.type = &setting_type_ipv6,
 | 
		
	
		
			
			|  | 786 | +	.scope = &ndp_settings_scope,
 | 
		
	
		
			
			|  | 787 | +};
 | 
		
	
		
			
			|  | 788 | +
 | 
		
	
		
			
			| 614 | 789 |  /****************************************************************************
 | 
		
	
		
			
			| 615 | 790 |   *
 | 
		
	
		
			
			| 616 | 791 |   * IPv6 autoconfiguration
 | 
		
	
	
		
			
			|  | @@ -713,12 +888,19 @@ static void ipv6conf_expired ( struct retry_timer *timer, int fail ) {
 | 
		
	
		
			
			| 713 | 888 |   * Handle router advertisement during IPv6 autoconfiguration
 | 
		
	
		
			
			| 714 | 889 |   *
 | 
		
	
		
			
			| 715 | 890 |   * @v netdev		Network device
 | 
		
	
		
			
			| 716 |  | - * @v flags		Router flags
 | 
		
	
		
			
			|  | 891 | + * @v radv		Router advertisement
 | 
		
	
		
			
			|  | 892 | + * @v len		Length of router advertisement
 | 
		
	
		
			
			| 717 | 893 |   * @ret rc		Return status code
 | 
		
	
		
			
			|  | 894 | + *
 | 
		
	
		
			
			|  | 895 | + * This function assumes that the router advertisement is well-formed,
 | 
		
	
		
			
			|  | 896 | + * since it must have already passed through option processing.
 | 
		
	
		
			
			| 718 | 897 |   */
 | 
		
	
		
			
			| 719 |  | -static int ipv6conf_rx_router_advertisement ( struct net_device *netdev,
 | 
		
	
		
			
			| 720 |  | -					      unsigned int flags ) {
 | 
		
	
		
			
			|  | 898 | +static int
 | 
		
	
		
			
			|  | 899 | +ipv6conf_rx_router_advertisement ( struct net_device *netdev,
 | 
		
	
		
			
			|  | 900 | +				   struct ndp_router_advertisement_header *radv,
 | 
		
	
		
			
			|  | 901 | +				   size_t len ) {
 | 
		
	
		
			
			| 721 | 902 |  	struct ipv6conf *ipv6conf;
 | 
		
	
		
			
			|  | 903 | +	size_t option_len;
 | 
		
	
		
			
			| 722 | 904 |  	int stateful;
 | 
		
	
		
			
			| 723 | 905 |  	int rc;
 | 
		
	
		
			
			| 724 | 906 |  
 | 
		
	
	
		
			
			|  | @@ -739,9 +921,15 @@ static int ipv6conf_rx_router_advertisement ( struct net_device *netdev,
 | 
		
	
		
			
			| 739 | 921 |  	/* Stop router solicitation timer */
 | 
		
	
		
			
			| 740 | 922 |  	stop_timer ( &ipv6conf->timer );
 | 
		
	
		
			
			| 741 | 923 |  
 | 
		
	
		
			
			|  | 924 | +	/* Register NDP settings */
 | 
		
	
		
			
			|  | 925 | +	option_len = ( len - offsetof ( typeof ( *radv ), option ) );
 | 
		
	
		
			
			|  | 926 | +	if ( ( rc = ndp_register_settings ( netdev, radv->option,
 | 
		
	
		
			
			|  | 927 | +					    option_len ) ) != 0 )
 | 
		
	
		
			
			|  | 928 | +		return rc;
 | 
		
	
		
			
			|  | 929 | +
 | 
		
	
		
			
			| 742 | 930 |  	/* Start DHCPv6 if required */
 | 
		
	
		
			
			| 743 |  | -	if ( flags & ( NDP_ROUTER_MANAGED | NDP_ROUTER_OTHER ) ) {
 | 
		
	
		
			
			| 744 |  | -		stateful = ( flags & NDP_ROUTER_MANAGED );
 | 
		
	
		
			
			|  | 931 | +	if ( radv->flags & ( NDP_ROUTER_MANAGED | NDP_ROUTER_OTHER ) ) {
 | 
		
	
		
			
			|  | 932 | +		stateful = ( radv->flags & NDP_ROUTER_MANAGED );
 | 
		
	
		
			
			| 745 | 933 |  		if ( ( rc = start_dhcpv6 ( &ipv6conf->dhcp, netdev,
 | 
		
	
		
			
			| 746 | 934 |  					   stateful ) ) != 0 ) {
 | 
		
	
		
			
			| 747 | 935 |  			DBGC ( netdev, "NDP %s could not start state%s DHCPv6: "
 |