@@ -138,6 +138,28 @@ impl IpNet {
138
138
}
139
139
}
140
140
141
+ /// Return `true` iff this subnet is in a multicast address range with
142
+ /// administrative scope (admin-local, site-local or organization-local) as
143
+ /// defined in [RFC 7346] and [RFC 4291].
144
+ ///
145
+ /// [RFC 7346]: https://tools.ietf.org/html/rfc7346
146
+ /// [RFC 4291]: https://tools.ietf.org/html/rfc4291
147
+ pub const fn is_admin_scoped_multicast ( & self ) -> bool {
148
+ match self {
149
+ IpNet :: V4 ( _inner) => false , // IPv4 does not support ULA
150
+ IpNet :: V6 ( inner) => inner. is_admin_scoped_multicast ( ) ,
151
+ }
152
+ }
153
+
154
+ /// Return `true` iff this subnet is in a Unique Local Address range.
155
+ /// This is only valid for IPv6 addresses.
156
+ pub const fn is_unique_local ( & self ) -> bool {
157
+ match self {
158
+ IpNet :: V4 ( _inner) => false , // IPv4 does not support ULA
159
+ IpNet :: V6 ( inner) => inner. is_unique_local ( ) ,
160
+ }
161
+ }
162
+
141
163
/// Return `true` iff this subnet is in a loopback address range.
142
164
pub const fn is_loopback ( & self ) -> bool {
143
165
match self {
@@ -606,6 +628,26 @@ impl Ipv6Net {
606
628
self . addr . is_multicast ( )
607
629
}
608
630
631
+ /// Return `true` if this address is a multicast address with
632
+ /// administrative scope (admin-local, site-local or organization-local) as
633
+ /// defined in [RFC 7346] and [RFC 4291].
634
+ ///
635
+ /// [RFC 7346]: https://tools.ietf.org/html/rfc7346
636
+ /// [RFC 4291]: https://tools.ietf.org/html/rfc4291
637
+ pub const fn is_admin_scoped_multicast ( & self ) -> bool {
638
+ if !self . addr . is_multicast ( ) {
639
+ return false ;
640
+ }
641
+
642
+ // Extract the scope field (bits 4-7 of the second byte)
643
+ let segments = self . addr . segments ( ) ;
644
+ let scope = ( segments[ 0 ] & 0x000F ) as u8 ;
645
+
646
+ // RFC 4291/7346: Scope values 4 (admin-local), 5 (site-local) and
647
+ // 8 (organization-local)
648
+ matches ! ( scope, 0x4 | 0x5 | 0x8 )
649
+ }
650
+
609
651
/// Return `true` iff this subnet is in a loopback address range.
610
652
pub const fn is_loopback ( & self ) -> bool {
611
653
self . addr . is_loopback ( )
@@ -624,10 +666,11 @@ impl Ipv6Net {
624
666
}
625
667
626
668
/// Return `true` if this subnetwork is in the IPv6 Unique Local Address
627
- /// range defined in RFC 4193, e.g., `fd00:/8`
628
- pub fn is_unique_local ( & self ) -> bool {
629
- // TODO: Delegate to `Ipv6Addr::is_unique_local()` when stabilized.
630
- self . first_addr ( ) . octets ( ) [ 0 ] == 0xfd
669
+ /// range defined in [RFC 4193], e.g., `fd00:/8`.
670
+ ///
671
+ /// [RFC 4193]: https://tools.ietf.org/html/rfc4193
672
+ pub const fn is_unique_local ( & self ) -> bool {
673
+ self . addr . is_unique_local ( )
631
674
}
632
675
633
676
/// Return the first address within this subnet.
@@ -1076,4 +1119,37 @@ mod tests {
1076
1119
let unspec: IpNet = "0.0.0.0/0" . parse ( ) . unwrap ( ) ;
1077
1120
assert ! ( unspec. is_network_address( ) ) ;
1078
1121
}
1122
+
1123
+ #[ test]
1124
+ fn test_is_multicast_with_scopes ( ) {
1125
+ // IPv4 multicast tests (224.0.0.0/4 is the IPv4 multicast range)
1126
+ let v4_mcast: IpNet = "224.0.0.1/32" . parse ( ) . unwrap ( ) ;
1127
+ let v4_not_mcast: IpNet = "192.168.1.1/24" . parse ( ) . unwrap ( ) ;
1128
+
1129
+ assert ! ( v4_mcast. is_multicast( ) ) ;
1130
+ assert ! ( !v4_not_mcast. is_multicast( ) ) ;
1131
+
1132
+ // IPv6 multicast tests (ff00::/8 is the IPv6 multicast range)
1133
+ let v6_mcast: IpNet = "ff02::1/128" . parse ( ) . unwrap ( ) ;
1134
+ let v6_not_mcast: IpNet = "2001:db8::1/64" . parse ( ) . unwrap ( ) ;
1135
+
1136
+ assert ! ( v6_mcast. is_multicast( ) ) ;
1137
+ assert ! ( !v6_not_mcast. is_multicast( ) ) ;
1138
+
1139
+ // Test for multicast_admin_scoped (site-local)
1140
+ let v6_site_scoped_mcast: IpNet = "ff05::1/128" . parse ( ) . unwrap ( ) ;
1141
+ // Test for multicast_admin_scoped (organization-local)
1142
+ let v6_org_scoped_mcast: IpNet = "ff08::1/128" . parse ( ) . unwrap ( ) ;
1143
+ //Test for multicast_admin_scoped (admin-local)
1144
+ let v6_admin_scoped_mcast: IpNet = "ff04::1/128" . parse ( ) . unwrap ( ) ;
1145
+ // Test for a multicast address that is not admin scoped
1146
+ let v6_not_admin_scoped_mcast: IpNet = "ff02::1/128" . parse ( ) . unwrap ( ) ;
1147
+
1148
+ assert ! ( v6_site_scoped_mcast. is_admin_scoped_multicast( ) ) ;
1149
+ assert ! ( v6_org_scoped_mcast. is_admin_scoped_multicast( ) ) ;
1150
+ assert ! ( v6_admin_scoped_mcast. is_admin_scoped_multicast( ) ) ;
1151
+ assert ! ( !v6_not_admin_scoped_mcast. is_admin_scoped_multicast( ) ) ;
1152
+ // Always false for IPv4
1153
+ assert ! ( !v4_mcast. is_admin_scoped_multicast( ) ) ;
1154
+ }
1079
1155
}
0 commit comments