|
@@ -20,6 +20,7 @@
|
20
|
20
|
FILE_LICENCE ( GPL2_OR_LATER );
|
21
|
21
|
|
22
|
22
|
#include <stdlib.h>
|
|
23
|
+#include <stdio.h>
|
23
|
24
|
#include <string.h>
|
24
|
25
|
#include <errno.h>
|
25
|
26
|
#include <byteswap.h>
|
|
@@ -41,6 +42,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
|
41
|
42
|
static struct ipv6conf * ipv6conf_demux ( struct net_device *netdev );
|
42
|
43
|
static int
|
43
|
44
|
ipv6conf_rx_router_advertisement ( struct net_device *netdev,
|
|
45
|
+ struct in6_addr *router,
|
44
|
46
|
struct ndp_router_advertisement_header *radv,
|
45
|
47
|
size_t len );
|
46
|
48
|
|
|
@@ -585,6 +587,7 @@ ndp_rx_router_advertisement ( struct io_buffer *iobuf,
|
585
|
587
|
struct sockaddr_in6 *sin6_dest __unused ) {
|
586
|
588
|
union ndp_header *ndp = iobuf->data;
|
587
|
589
|
struct ndp_router_advertisement_header *radv = &ndp->radv;
|
|
590
|
+ struct in6_addr *router = &sin6_src->sin6_addr;
|
588
|
591
|
size_t len = iob_len ( iobuf );
|
589
|
592
|
int rc;
|
590
|
593
|
|
|
@@ -595,8 +598,8 @@ ndp_rx_router_advertisement ( struct io_buffer *iobuf,
|
595
|
598
|
goto err_options;
|
596
|
599
|
|
597
|
600
|
/* Pass to IPv6 autoconfiguration */
|
598
|
|
- if ( ( rc = ipv6conf_rx_router_advertisement ( netdev, radv,
|
599
|
|
- len ) ) != 0 )
|
|
601
|
+ if ( ( rc = ipv6conf_rx_router_advertisement ( netdev, router,
|
|
602
|
+ radv, len ) ) != 0 )
|
600
|
603
|
goto err_ipv6conf;
|
601
|
604
|
|
602
|
605
|
err_ipv6conf:
|
|
@@ -627,12 +630,26 @@ struct icmpv6_handler ndp_handlers[] __icmpv6_handler = {
|
627
|
630
|
*
|
628
|
631
|
*/
|
629
|
632
|
|
|
633
|
+/** An NDP prefix settings block */
|
|
634
|
+struct ndp_prefix_settings {
|
|
635
|
+ /** Settings interface */
|
|
636
|
+ struct settings settings;
|
|
637
|
+ /** Name */
|
|
638
|
+ char name[4];
|
|
639
|
+ /** Prefix information option */
|
|
640
|
+ struct ndp_prefix_information_option *prefix;
|
|
641
|
+};
|
|
642
|
+
|
630
|
643
|
/** An NDP settings block */
|
631
|
644
|
struct ndp_settings {
|
632
|
645
|
/** Reference counter */
|
633
|
646
|
struct refcnt refcnt;
|
634
|
647
|
/** Settings interface */
|
635
|
648
|
struct settings settings;
|
|
649
|
+ /** Router address */
|
|
650
|
+ struct in6_addr router;
|
|
651
|
+ /** Router lifetime */
|
|
652
|
+ unsigned int lifetime;
|
636
|
653
|
/** Length of NDP options */
|
637
|
654
|
size_t len;
|
638
|
655
|
/** NDP options */
|
|
@@ -779,22 +796,207 @@ static struct settings_operations ndp_settings_operations = {
|
779
|
796
|
.fetch = ndp_fetch,
|
780
|
797
|
};
|
781
|
798
|
|
|
799
|
+/**
|
|
800
|
+ * Check applicability of NDP per-prefix setting
|
|
801
|
+ *
|
|
802
|
+ * @v settings Settings block
|
|
803
|
+ * @v setting Setting to fetch
|
|
804
|
+ * @ret applies Setting applies within this settings block
|
|
805
|
+ */
|
|
806
|
+static int ndp_prefix_applies ( struct settings *settings __unused,
|
|
807
|
+ const struct setting *setting ) {
|
|
808
|
+
|
|
809
|
+ return ( setting->scope == &ipv6_scope );
|
|
810
|
+}
|
|
811
|
+
|
|
812
|
+/**
|
|
813
|
+ * Fetch value of NDP IPv6 address setting
|
|
814
|
+ *
|
|
815
|
+ * @v settings Settings block
|
|
816
|
+ * @v data Buffer to fill with setting data
|
|
817
|
+ * @v len Length of buffer
|
|
818
|
+ * @ret len Length of setting data, or negative error
|
|
819
|
+ */
|
|
820
|
+static int ndp_prefix_fetch_ip6 ( struct settings *settings, void *data,
|
|
821
|
+ size_t len ) {
|
|
822
|
+ struct ndp_prefix_settings *prefset =
|
|
823
|
+ container_of ( settings, struct ndp_prefix_settings, settings );
|
|
824
|
+ struct ndp_settings *ndpset =
|
|
825
|
+ container_of ( settings->parent, struct ndp_settings, settings);
|
|
826
|
+ struct net_device *netdev =
|
|
827
|
+ container_of ( ndpset->settings.parent, struct net_device,
|
|
828
|
+ settings.settings );
|
|
829
|
+ struct ndp_prefix_information_option *prefix = prefset->prefix;
|
|
830
|
+ struct in6_addr ip6;
|
|
831
|
+ int prefix_len;
|
|
832
|
+
|
|
833
|
+ /* Skip dead prefixes */
|
|
834
|
+ if ( ! prefix->valid )
|
|
835
|
+ return -ENOENT;
|
|
836
|
+
|
|
837
|
+ /* Construct IPv6 address via SLAAC, if applicable */
|
|
838
|
+ memcpy ( &ip6, &prefix->prefix, sizeof ( ip6 ) );
|
|
839
|
+ if ( prefix->flags & NDP_PREFIX_AUTONOMOUS ) {
|
|
840
|
+ prefix_len = ipv6_eui64 ( &ip6, netdev );
|
|
841
|
+ if ( prefix_len < 0 )
|
|
842
|
+ return prefix_len;
|
|
843
|
+ if ( prefix_len != prefix->prefix_len )
|
|
844
|
+ return -EINVAL;
|
|
845
|
+ }
|
|
846
|
+
|
|
847
|
+ /* Fill in IPv6 address */
|
|
848
|
+ if ( len > sizeof ( ip6 ) )
|
|
849
|
+ len = sizeof ( ip6 );
|
|
850
|
+ memcpy ( data, &ip6, len );
|
|
851
|
+
|
|
852
|
+ return sizeof ( ip6 );
|
|
853
|
+}
|
|
854
|
+
|
|
855
|
+/**
|
|
856
|
+ * Fetch value of NDP prefix length setting
|
|
857
|
+ *
|
|
858
|
+ * @v settings Settings block
|
|
859
|
+ * @v data Buffer to fill with setting data
|
|
860
|
+ * @v len Length of buffer
|
|
861
|
+ * @ret len Length of setting data, or negative error
|
|
862
|
+ */
|
|
863
|
+static int ndp_prefix_fetch_len6 ( struct settings *settings, void *data,
|
|
864
|
+ size_t len ) {
|
|
865
|
+ struct ndp_prefix_settings *prefset =
|
|
866
|
+ container_of ( settings, struct ndp_prefix_settings, settings );
|
|
867
|
+ struct ndp_prefix_information_option *prefix = prefset->prefix;
|
|
868
|
+ uint8_t *len6;
|
|
869
|
+
|
|
870
|
+ /* Fill in prefix length */
|
|
871
|
+ if ( len >= sizeof ( *len6 ) ) {
|
|
872
|
+ /* We treat an off-link prefix as having a prefix
|
|
873
|
+ * length covering the entire IPv6 address.
|
|
874
|
+ */
|
|
875
|
+ len6 = data;
|
|
876
|
+ *len6 = ( ( prefix->flags & NDP_PREFIX_ON_LINK ) ?
|
|
877
|
+ prefix->prefix_len : -1UL );
|
|
878
|
+ }
|
|
879
|
+
|
|
880
|
+ return sizeof ( *len6 );
|
|
881
|
+}
|
|
882
|
+
|
|
883
|
+/**
|
|
884
|
+ * Fetch value of NDP router address setting
|
|
885
|
+ *
|
|
886
|
+ * @v settings Settings block
|
|
887
|
+ * @v data Buffer to fill with setting data
|
|
888
|
+ * @v len Length of buffer
|
|
889
|
+ * @ret len Length of setting data, or negative error
|
|
890
|
+ */
|
|
891
|
+static int ndp_prefix_fetch_gateway6 ( struct settings *settings,
|
|
892
|
+ void *data, size_t len ) {
|
|
893
|
+ struct ndp_settings *ndpset =
|
|
894
|
+ container_of ( settings->parent, struct ndp_settings, settings);
|
|
895
|
+
|
|
896
|
+ /* Treat non-routing router as non-existent */
|
|
897
|
+ if ( ! ndpset->lifetime )
|
|
898
|
+ return -ENOENT;
|
|
899
|
+
|
|
900
|
+ /* Fill in router address */
|
|
901
|
+ if ( len > sizeof ( ndpset->router ) )
|
|
902
|
+ len = sizeof ( ndpset->router );
|
|
903
|
+ memcpy ( data, &ndpset->router, len );
|
|
904
|
+
|
|
905
|
+ return sizeof ( ndpset->router );
|
|
906
|
+}
|
|
907
|
+
|
|
908
|
+/** An NDP per-prefix setting operation */
|
|
909
|
+struct ndp_prefix_operation {
|
|
910
|
+ /** Generic setting */
|
|
911
|
+ const struct setting *setting;
|
|
912
|
+ /**
|
|
913
|
+ * Fetch value of setting
|
|
914
|
+ *
|
|
915
|
+ * @v settings Settings block
|
|
916
|
+ * @v data Buffer to fill with setting data
|
|
917
|
+ * @v len Length of buffer
|
|
918
|
+ * @ret len Length of setting data, or negative error
|
|
919
|
+ */
|
|
920
|
+ int ( * fetch ) ( struct settings *settings, void *data, size_t len );
|
|
921
|
+};
|
|
922
|
+
|
|
923
|
+/** NDP per-prefix settings operations */
|
|
924
|
+static struct ndp_prefix_operation ndp_prefix_operations[] = {
|
|
925
|
+ { &ip6_setting, ndp_prefix_fetch_ip6 },
|
|
926
|
+ { &len6_setting, ndp_prefix_fetch_len6 },
|
|
927
|
+ { &gateway6_setting, ndp_prefix_fetch_gateway6 },
|
|
928
|
+};
|
|
929
|
+
|
|
930
|
+/**
|
|
931
|
+ * Fetch value of NDP pre-prefix setting
|
|
932
|
+ *
|
|
933
|
+ * @v settings Settings block
|
|
934
|
+ * @v setting Setting to fetch
|
|
935
|
+ * @v data Buffer to fill with setting data
|
|
936
|
+ * @v len Length of buffer
|
|
937
|
+ * @ret len Length of setting data, or negative error
|
|
938
|
+ */
|
|
939
|
+static int ndp_prefix_fetch ( struct settings *settings,
|
|
940
|
+ struct setting *setting,
|
|
941
|
+ void *data, size_t len ) {
|
|
942
|
+ struct ndp_prefix_operation *op;
|
|
943
|
+ unsigned int i;
|
|
944
|
+
|
|
945
|
+ /* Handle per-prefix settings */
|
|
946
|
+ for ( i = 0 ; i < ( sizeof ( ndp_prefix_operations ) /
|
|
947
|
+ sizeof ( ndp_prefix_operations[0] ) ) ; i++ ) {
|
|
948
|
+ op = &ndp_prefix_operations[i];
|
|
949
|
+ if ( setting_cmp ( setting, op->setting ) == 0 )
|
|
950
|
+ return op->fetch ( settings, data, len );
|
|
951
|
+ }
|
|
952
|
+
|
|
953
|
+ return -ENOENT;
|
|
954
|
+}
|
|
955
|
+
|
|
956
|
+/** NDP per-prefix settings operations */
|
|
957
|
+static struct settings_operations ndp_prefix_settings_operations = {
|
|
958
|
+ .applies = ndp_prefix_applies,
|
|
959
|
+ .fetch = ndp_prefix_fetch,
|
|
960
|
+};
|
|
961
|
+
|
782
|
962
|
/**
|
783
|
963
|
* Register NDP settings
|
784
|
964
|
*
|
785
|
965
|
* @v netdev Network device
|
|
966
|
+ * @v router Router address
|
|
967
|
+ * @v lifetime Router lifetime
|
786
|
968
|
* @v options NDP options
|
787
|
969
|
* @v len Length of options
|
788
|
970
|
* @ret rc Return status code
|
789
|
971
|
*/
|
790
|
972
|
static int ndp_register_settings ( struct net_device *netdev,
|
|
973
|
+ struct in6_addr *router,
|
|
974
|
+ unsigned int lifetime,
|
791
|
975
|
union ndp_option *options, size_t len ) {
|
792
|
976
|
struct settings *parent = netdev_settings ( netdev );
|
|
977
|
+ union ndp_option *option;
|
793
|
978
|
struct ndp_settings *ndpset;
|
|
979
|
+ struct ndp_prefix_settings *prefset;
|
|
980
|
+ size_t offset;
|
|
981
|
+ size_t option_len;
|
|
982
|
+ unsigned int prefixes;
|
|
983
|
+ unsigned int instance;
|
794
|
984
|
int rc;
|
795
|
985
|
|
|
986
|
+ /* Count number of prefix options. We can assume that the
|
|
987
|
+ * options are well-formed, otherwise they would have been
|
|
988
|
+ * rejected prior to being stored.
|
|
989
|
+ */
|
|
990
|
+ for ( prefixes = 0, offset = 0 ; offset < len ; offset += option_len ) {
|
|
991
|
+ option = ( ( ( void * ) options ) + offset );
|
|
992
|
+ option_len = ( option->header.blocks * NDP_OPTION_BLKSZ );
|
|
993
|
+ if ( option->header.type == NDP_OPT_PREFIX )
|
|
994
|
+ prefixes++;
|
|
995
|
+ }
|
|
996
|
+
|
796
|
997
|
/* Allocate and initialise structure */
|
797
|
|
- ndpset = zalloc ( sizeof ( *ndpset ) + len );
|
|
998
|
+ ndpset = zalloc ( sizeof ( *ndpset ) + len +
|
|
999
|
+ ( prefixes * sizeof ( *prefset ) ) );
|
798
|
1000
|
if ( ! ndpset ) {
|
799
|
1001
|
rc = -ENOMEM;
|
800
|
1002
|
goto err_alloc;
|
|
@@ -802,14 +1004,50 @@ static int ndp_register_settings ( struct net_device *netdev,
|
802
|
1004
|
ref_init ( &ndpset->refcnt, NULL );
|
803
|
1005
|
settings_init ( &ndpset->settings, &ndp_settings_operations,
|
804
|
1006
|
&ndpset->refcnt, &ndp_settings_scope );
|
|
1007
|
+ memcpy ( &ndpset->router, router, sizeof ( ndpset->router ) );
|
|
1008
|
+ ndpset->lifetime = lifetime;
|
805
|
1009
|
ndpset->len = len;
|
806
|
1010
|
memcpy ( ndpset->options, options, len );
|
|
1011
|
+ prefset = ( ( ( void * ) ndpset->options ) + len );
|
807
|
1012
|
|
808
|
1013
|
/* Register settings */
|
809
|
1014
|
if ( ( rc = register_settings ( &ndpset->settings, parent,
|
810
|
1015
|
NDP_SETTINGS_NAME ) ) != 0 )
|
811
|
1016
|
goto err_register;
|
812
|
1017
|
|
|
1018
|
+ /* Construct and register per-prefix settings */
|
|
1019
|
+ for ( instance = 0, offset = 0 ; offset < len ; offset += option_len ) {
|
|
1020
|
+
|
|
1021
|
+ /* Skip non-prefix options */
|
|
1022
|
+ option = ( ( ( void * ) ndpset->options ) + offset );
|
|
1023
|
+ option_len = ( option->header.blocks * NDP_OPTION_BLKSZ );
|
|
1024
|
+ if ( option->header.type != NDP_OPT_PREFIX )
|
|
1025
|
+ continue;
|
|
1026
|
+
|
|
1027
|
+ /* Initialise structure */
|
|
1028
|
+ settings_init ( &prefset->settings,
|
|
1029
|
+ &ndp_prefix_settings_operations,
|
|
1030
|
+ &ndpset->refcnt, &ndp_settings_scope );
|
|
1031
|
+ prefset->prefix = &option->prefix;
|
|
1032
|
+ snprintf ( prefset->name, sizeof ( prefset->name ), "%d",
|
|
1033
|
+ instance++ );
|
|
1034
|
+
|
|
1035
|
+ /* Register settings */
|
|
1036
|
+ if ( ( rc = register_settings ( &prefset->settings,
|
|
1037
|
+ &ndpset->settings,
|
|
1038
|
+ prefset->name ) ) != 0 )
|
|
1039
|
+ goto err_register_prefix;
|
|
1040
|
+
|
|
1041
|
+ /* Move to next per-prefix settings */
|
|
1042
|
+ prefset++;
|
|
1043
|
+ }
|
|
1044
|
+ assert ( instance == prefixes );
|
|
1045
|
+
|
|
1046
|
+ ref_put ( &ndpset->refcnt );
|
|
1047
|
+ return 0;
|
|
1048
|
+
|
|
1049
|
+ err_register_prefix:
|
|
1050
|
+ unregister_settings ( &ndpset->settings );
|
813
|
1051
|
err_register:
|
814
|
1052
|
ref_put ( &ndpset->refcnt );
|
815
|
1053
|
err_alloc:
|
|
@@ -938,6 +1176,7 @@ static void ipv6conf_expired ( struct retry_timer *timer, int fail ) {
|
938
|
1176
|
* Handle router advertisement during IPv6 autoconfiguration
|
939
|
1177
|
*
|
940
|
1178
|
* @v netdev Network device
|
|
1179
|
+ * @v router Router address
|
941
|
1180
|
* @v radv Router advertisement
|
942
|
1181
|
* @v len Length of router advertisement
|
943
|
1182
|
* @ret rc Return status code
|
|
@@ -947,6 +1186,7 @@ static void ipv6conf_expired ( struct retry_timer *timer, int fail ) {
|
947
|
1186
|
*/
|
948
|
1187
|
static int
|
949
|
1188
|
ipv6conf_rx_router_advertisement ( struct net_device *netdev,
|
|
1189
|
+ struct in6_addr *router,
|
950
|
1190
|
struct ndp_router_advertisement_header *radv,
|
951
|
1191
|
size_t len ) {
|
952
|
1192
|
struct ipv6conf *ipv6conf;
|
|
@@ -970,8 +1210,9 @@ ipv6conf_rx_router_advertisement ( struct net_device *netdev,
|
970
|
1210
|
|
971
|
1211
|
/* Register NDP settings */
|
972
|
1212
|
option_len = ( len - offsetof ( typeof ( *radv ), option ) );
|
973
|
|
- if ( ( rc = ndp_register_settings ( netdev, radv->option,
|
974
|
|
- option_len ) ) != 0 )
|
|
1213
|
+ if ( ( rc = ndp_register_settings ( netdev, router,
|
|
1214
|
+ ntohl ( radv->lifetime ),
|
|
1215
|
+ radv->option, option_len ) ) != 0 )
|
975
|
1216
|
return rc;
|
976
|
1217
|
|
977
|
1218
|
/* Start DHCPv6 if required */
|