11// Licensed to the .NET Foundation under one or more agreements.
22// The .NET Foundation licenses this file to you under the MIT license.
33
4+ using System . Buffers . Binary ;
45using System . Collections ;
56using System . Collections . Generic ;
7+ using System . Diagnostics ;
68using System . Linq ;
9+ using System . Net . Sockets ;
10+ using System . Runtime . InteropServices ;
711using System . Text ;
812using Xunit ;
913
@@ -26,38 +30,64 @@ public class IPNetworkTest
2630 {
2731 { "127.0.0.1/33" } , // PrefixLength max is 32 for IPv4
2832 { "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/129" } , // PrefixLength max is 128 for IPv6
29- { "127.0.0.1/31" } , // Bits exceed the prefix length of 31 (32nd bit is on)
30- { "198.51.255.0/23" } , // Bits exceed the prefix length of 23
31- { "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/127" } , // Bits exceed the prefix length of 31
32- { "2a01:110:8012::/45" } , // Bits exceed the prefix length of 45 (47th bit is on)
3333 } ;
3434
3535 public static TheoryData < string > ValidIPNetworkData = new TheoryData < string > ( )
3636 {
3737 { "0.0.0.0/32" } , // the whole IPv4 space
3838 { "0.0.0.0/0" } ,
39+ { "192.168.0.10/0" } ,
3940 { "128.0.0.0/1" } ,
41+ { "127.0.0.1/8" } ,
42+ { "127.0.0.1/31" } ,
43+ { "198.51.255.0/23" } ,
4044 { "::/128" } , // the whole IPv6 space
4145 { "255.255.255.255/32" } ,
4246 { "198.51.254.0/23" } ,
4347 { "42.42.128.0/17" } ,
4448 { "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128" } ,
4549 { "2a01:110:8012::/47" } ,
4650 { "2a01:110:8012::/100" } ,
51+ { "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/127" } ,
52+ { "2a01:110:8012::/45" } ,
4753 } ;
4854
55+ private uint GetMask32 ( int prefix )
56+ {
57+ Debug . Assert ( prefix != 0 ) ;
58+
59+ uint mask = uint . MaxValue << ( 32 - prefix ) ;
60+ return BitConverter . IsLittleEndian ? BinaryPrimitives . ReverseEndianness ( mask ) : mask ;
61+ }
62+ private UInt128 GetMask128 ( int prefix )
63+ {
64+ Debug . Assert ( prefix != 0 ) ;
65+
66+ UInt128 mask = UInt128 . MaxValue << ( 128 - prefix ) ;
67+ return BitConverter . IsLittleEndian ? BinaryPrimitives . ReverseEndianness ( mask ) : mask ;
68+ }
69+ private IPAddress GetBaseAddress ( IPAddress address , int prefix )
70+ => ( address . AddressFamily , prefix ) switch
71+ {
72+ ( AddressFamily . InterNetwork , 0 ) => new IPAddress ( [ 0 , 0 , 0 , 0 ] ) ,
73+ ( AddressFamily . InterNetwork , _ ) => new IPAddress ( MemoryMarshal . Read < uint > ( address . GetAddressBytes ( ) ) & GetMask32 ( prefix ) ) ,
74+ ( AddressFamily . InterNetworkV6 , 0 ) => new IPAddress ( [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ) ,
75+ ( AddressFamily . InterNetworkV6 , _ ) => new IPAddress ( MemoryMarshal . AsBytes ( [ MemoryMarshal . Read < UInt128 > ( address . GetAddressBytes ( ) ) & GetMask128 ( prefix ) ] ) ) ,
76+ _ => throw new ArgumentOutOfRangeException ( $ "Unexpected address family { address . AddressFamily } of { address } .")
77+ } ;
78+
4979 [ Theory ]
5080 [ MemberData ( nameof ( ValidIPNetworkData ) ) ]
5181 public void Constructor_Valid_Succeeds ( string input )
5282 {
5383 string [ ] splitInput = input . Split ( '/' ) ;
5484 IPAddress address = IPAddress . Parse ( splitInput [ 0 ] ) ;
55- int prefixLegth = int . Parse ( splitInput [ 1 ] ) ;
85+ int prefixLength = int . Parse ( splitInput [ 1 ] ) ;
5686
57- IPNetwork network = new IPNetwork ( address , prefixLegth ) ;
87+ IPNetwork network = new IPNetwork ( address , prefixLength ) ;
5888
59- Assert . Equal ( address , network . BaseAddress ) ;
60- Assert . Equal ( prefixLegth , network . PrefixLength ) ;
89+ Assert . Equal ( GetBaseAddress ( address , prefixLength ) , network . BaseAddress ) ;
90+ Assert . Equal ( prefixLength , network . PrefixLength ) ;
6191 }
6292
6393 [ Fact ]
@@ -77,17 +107,6 @@ public void Constructor_PrefixLenghtOutOfRange_ThrowsArgumentOutOfRangeException
77107 Assert . Throws < ArgumentOutOfRangeException > ( ( ) => new IPNetwork ( address , prefixLength ) ) ;
78108 }
79109
80- [ Theory ]
81- [ InlineData ( "192.168.0.1" , 31 ) ]
82- [ InlineData ( "42.42.192.0" , 17 ) ]
83- [ InlineData ( "128.0.0.0" , 0 ) ]
84- [ InlineData ( "2a01:110:8012::" , 46 ) ]
85- public void Constructor_NonZeroBitsAfterNetworkPrefix_ThrowsArgumentException ( string ipStr , int prefixLength )
86- {
87- IPAddress address = IPAddress . Parse ( ipStr ) ;
88- Assert . Throws < ArgumentException > ( ( ) => new IPNetwork ( address , prefixLength ) ) ;
89- }
90-
91110 [ Theory ]
92111 [ MemberData ( nameof ( IncorrectFormatData ) ) ]
93112 public void Parse_IncorrectFormat_ThrowsFormatException ( string input )
0 commit comments