|
@@ -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: "
|