@@ -5,6 +5,7 @@ use core::fmt;
5
5
6
6
use super :: { Error , Result } ;
7
7
use crate :: wire:: ip:: pretty_print_ip_payload;
8
+ use crate :: wire:: HardwareAddress ;
8
9
9
10
pub use super :: IpProtocol as Protocol ;
10
11
@@ -83,6 +84,12 @@ pub(crate) trait AddressExt {
83
84
/// The function panics if `data` is not sixteen octets long.
84
85
fn from_bytes ( data : & [ u8 ] ) -> Address ;
85
86
87
+ /// Create an IPv6 address based on the provided prefix and hardware identifier.
88
+ fn from_link_prefix (
89
+ link_prefix : & Cidr ,
90
+ interface_identifier : HardwareAddress ,
91
+ ) -> Option < Address > ;
92
+
86
93
/// Query whether the IPv6 address is an [unicast address].
87
94
///
88
95
/// [unicast address]: https://tools.ietf.org/html/rfc4291#section-2.5
@@ -142,6 +149,23 @@ impl AddressExt for Address {
142
149
Address :: from ( bytes)
143
150
}
144
151
152
+ fn from_link_prefix (
153
+ link_prefix : & Cidr ,
154
+ interface_identifier : HardwareAddress ,
155
+ ) -> Option < Address > {
156
+ if let Some ( eui64) = interface_identifier. as_eui_64 ( ) {
157
+ if link_prefix. prefix_len ( ) != 64 {
158
+ return None ;
159
+ }
160
+ let mut bytes = [ 0 ; 16 ] ;
161
+ bytes[ 0 ..8 ] . copy_from_slice ( & link_prefix. address ( ) . octets ( ) [ 0 ..8 ] ) ;
162
+ bytes[ 8 ..16 ] . copy_from_slice ( & eui64) ;
163
+ Some ( Address :: from_bytes ( & bytes) )
164
+ } else {
165
+ None
166
+ }
167
+ }
168
+
145
169
fn x_is_unicast ( & self ) -> bool {
146
170
!( self . is_multicast ( ) || self . is_unspecified ( ) )
147
171
}
@@ -259,6 +283,15 @@ impl Cidr {
259
283
}
260
284
}
261
285
286
+ /// Create an IPv6 CIDR based on the provided prefix and hardware identifier.
287
+ pub fn from_link_prefix (
288
+ link_prefix : & Cidr ,
289
+ interface_identifier : HardwareAddress ,
290
+ ) -> Option < Self > {
291
+ Address :: from_link_prefix ( link_prefix, interface_identifier)
292
+ . map ( |address| Self :: new ( address, link_prefix. prefix_len ( ) ) )
293
+ }
294
+
262
295
/// Return the address of this IPv6 CIDR block.
263
296
pub const fn address ( & self ) -> Address {
264
297
self . address
@@ -930,6 +963,45 @@ pub(crate) mod test {
930
963
assert ! ( cidr_without_prefix. contains_addr( & Address :: LOCALHOST ) ) ;
931
964
}
932
965
966
+ #[ test]
967
+ fn test_from_eui_64 ( ) {
968
+ #[ cfg( feature = "medium-ethernet" ) ]
969
+ use crate :: wire:: EthernetAddress ;
970
+ #[ cfg( feature = "medium-ieee802154" ) ]
971
+ use crate :: wire:: Ieee802154Address ;
972
+ let tests: std:: vec:: Vec < ( HardwareAddress , Address ) > = vec ! [
973
+ #[ cfg( feature = "medium-ethernet" ) ]
974
+ (
975
+ HardwareAddress :: Ethernet ( EthernetAddress :: from_bytes( & [
976
+ 0xaa , 0xbb , 0xcc , 0xdd , 0xee , 0xff ,
977
+ ] ) ) ,
978
+ Address :: new( 0x2001 , 0xdb8 , 0x3 , 0x0 , 0xa8bb , 0xccff , 0xfedd , 0xeeff ) ,
979
+ ) ,
980
+ #[ cfg( feature = "medium-ieee802154" ) ]
981
+ (
982
+ HardwareAddress :: Ieee802154 ( Ieee802154Address :: from_bytes( & [
983
+ 0x00 , 0x11 , 0x22 , 0x33 , 0x44 , 0x55 , 0x66 , 0x77 ,
984
+ ] ) ) ,
985
+ Address :: new( 0x2001 , 0xdb8 , 0x3 , 0x0 , 0x0211 , 0x2233 , 0x4455 , 0x6677 ) ,
986
+ ) ,
987
+ ] ;
988
+
989
+ let prefix = Cidr :: new ( GLOBAL_UNICAST_ADDR . mask ( 64 ) . into ( ) , 64 ) ;
990
+ let wrong_prefix = Cidr :: new ( GLOBAL_UNICAST_ADDR . mask ( 72 ) . into ( ) , 72 ) ;
991
+
992
+ for ( hardware, result) in tests {
993
+ let generated = Address :: from_link_prefix ( & prefix, hardware) . unwrap ( ) ;
994
+ assert ! ( prefix. contains_addr( & generated) ) ;
995
+ assert_eq ! ( generated, result) ;
996
+ assert ! ( Address :: from_link_prefix( & wrong_prefix, hardware) . is_none( ) ) ;
997
+
998
+ let generated = Cidr :: from_link_prefix ( & prefix, hardware) . unwrap ( ) ;
999
+ assert ! ( prefix. contains_subnet( & generated) ) ;
1000
+ assert_eq ! ( generated. address( ) , result) ;
1001
+ assert ! ( Cidr :: from_link_prefix( & wrong_prefix, hardware) . is_none( ) ) ;
1002
+ }
1003
+ }
1004
+
933
1005
#[ test]
934
1006
#[ should_panic( expected = "length" ) ]
935
1007
fn test_from_bytes_too_long ( ) {
0 commit comments