1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.net;
18 
19 import static com.android.testutils.MiscAsserts.assertEqualBothWays;
20 import static com.android.testutils.MiscAsserts.assertNotEqualEitherWay;
21 import static com.android.testutils.ParcelUtils.assertParcelingIsLossless;
22 
23 import static org.junit.Assert.assertArrayEquals;
24 import static org.junit.Assert.assertEquals;
25 import static org.junit.Assert.assertFalse;
26 import static org.junit.Assert.assertNotEquals;
27 import static org.junit.Assert.assertTrue;
28 import static org.junit.Assert.fail;
29 
30 import androidx.test.filters.SmallTest;
31 import androidx.test.runner.AndroidJUnit4;
32 
33 import org.junit.Test;
34 import org.junit.runner.RunWith;
35 
36 import java.net.InetAddress;
37 import java.util.Random;
38 
39 @RunWith(AndroidJUnit4.class)
40 @SmallTest
41 public class IpPrefixTest {
42 
address(String addr)43     private static InetAddress address(String addr) {
44         return InetAddress.parseNumericAddress(addr);
45     }
46 
47     // Explicitly cast everything to byte because "error: possible loss of precision".
48     private static final byte[] IPV4_BYTES = { (byte) 192, (byte) 0, (byte) 2, (byte) 4};
49     private static final byte[] IPV6_BYTES = {
50         (byte) 0x20, (byte) 0x01, (byte) 0x0d, (byte) 0xb8,
51         (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef,
52         (byte) 0x0f, (byte) 0x00, (byte) 0x00, (byte) 0x00,
53         (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xa0
54     };
55 
56     @Test
testConstructor()57     public void testConstructor() {
58         IpPrefix p;
59         try {
60             p = new IpPrefix((byte[]) null, 9);
61             fail("Expected NullPointerException: null byte array");
62         } catch (RuntimeException expected) { }
63 
64         try {
65             p = new IpPrefix((InetAddress) null, 10);
66             fail("Expected NullPointerException: null InetAddress");
67         } catch (RuntimeException expected) { }
68 
69         try {
70             p = new IpPrefix((String) null);
71             fail("Expected NullPointerException: null String");
72         } catch (RuntimeException expected) { }
73 
74 
75         try {
76             byte[] b2 = {1, 2, 3, 4, 5};
77             p = new IpPrefix(b2, 29);
78             fail("Expected IllegalArgumentException: invalid array length");
79         } catch (IllegalArgumentException expected) { }
80 
81         try {
82             p = new IpPrefix("1.2.3.4");
83             fail("Expected IllegalArgumentException: no prefix length");
84         } catch (IllegalArgumentException expected) { }
85 
86         try {
87             p = new IpPrefix("1.2.3.4/");
88             fail("Expected IllegalArgumentException: empty prefix length");
89         } catch (IllegalArgumentException expected) { }
90 
91         try {
92             p = new IpPrefix("foo/32");
93             fail("Expected IllegalArgumentException: invalid address");
94         } catch (IllegalArgumentException expected) { }
95 
96         try {
97             p = new IpPrefix("1/32");
98             fail("Expected IllegalArgumentException: deprecated IPv4 format");
99         } catch (IllegalArgumentException expected) { }
100 
101         try {
102             p = new IpPrefix("1.2.3.256/32");
103             fail("Expected IllegalArgumentException: invalid IPv4 address");
104         } catch (IllegalArgumentException expected) { }
105 
106         try {
107             p = new IpPrefix("foo/32");
108             fail("Expected IllegalArgumentException: non-address");
109         } catch (IllegalArgumentException expected) { }
110 
111         try {
112             p = new IpPrefix("f00:::/32");
113             fail("Expected IllegalArgumentException: invalid IPv6 address");
114         } catch (IllegalArgumentException expected) { }
115 
116         p = new IpPrefix("/64");
117         assertEquals("::/64", p.toString());
118 
119         p = new IpPrefix("/128");
120         assertEquals("::1/128", p.toString());
121 
122         p = new IpPrefix("[2001:db8::123]/64");
123         assertEquals("2001:db8::/64", p.toString());
124     }
125 
126     @Test
testTruncation()127     public void testTruncation() {
128         IpPrefix p;
129 
130         p = new IpPrefix(IPV4_BYTES, 32);
131         assertEquals("192.0.2.4/32", p.toString());
132 
133         p = new IpPrefix(IPV4_BYTES, 29);
134         assertEquals("192.0.2.0/29", p.toString());
135 
136         p = new IpPrefix(IPV4_BYTES, 8);
137         assertEquals("192.0.0.0/8", p.toString());
138 
139         p = new IpPrefix(IPV4_BYTES, 0);
140         assertEquals("0.0.0.0/0", p.toString());
141 
142         try {
143             p = new IpPrefix(IPV4_BYTES, 33);
144             fail("Expected IllegalArgumentException: invalid prefix length");
145         } catch (RuntimeException expected) { }
146 
147         try {
148             p = new IpPrefix(IPV4_BYTES, 128);
149             fail("Expected IllegalArgumentException: invalid prefix length");
150         } catch (RuntimeException expected) { }
151 
152         try {
153             p = new IpPrefix(IPV4_BYTES, -1);
154             fail("Expected IllegalArgumentException: negative prefix length");
155         } catch (RuntimeException expected) { }
156 
157         p = new IpPrefix(IPV6_BYTES, 128);
158         assertEquals("2001:db8:dead:beef:f00::a0/128", p.toString());
159 
160         p = new IpPrefix(IPV6_BYTES, 122);
161         assertEquals("2001:db8:dead:beef:f00::80/122", p.toString());
162 
163         p = new IpPrefix(IPV6_BYTES, 64);
164         assertEquals("2001:db8:dead:beef::/64", p.toString());
165 
166         p = new IpPrefix(IPV6_BYTES, 3);
167         assertEquals("2000::/3", p.toString());
168 
169         p = new IpPrefix(IPV6_BYTES, 0);
170         assertEquals("::/0", p.toString());
171 
172         try {
173             p = new IpPrefix(IPV6_BYTES, -1);
174             fail("Expected IllegalArgumentException: negative prefix length");
175         } catch (RuntimeException expected) { }
176 
177         try {
178             p = new IpPrefix(IPV6_BYTES, 129);
179             fail("Expected IllegalArgumentException: negative prefix length");
180         } catch (RuntimeException expected) { }
181 
182     }
183 
184     @Test
testEquals()185     public void testEquals() {
186         IpPrefix p1, p2;
187 
188         p1 = new IpPrefix("192.0.2.251/23");
189         p2 = new IpPrefix(new byte[]{(byte) 192, (byte) 0, (byte) 2, (byte) 251}, 23);
190         assertEqualBothWays(p1, p2);
191 
192         p1 = new IpPrefix("192.0.2.5/23");
193         assertEqualBothWays(p1, p2);
194 
195         p1 = new IpPrefix("192.0.2.5/24");
196         assertNotEqualEitherWay(p1, p2);
197 
198         p1 = new IpPrefix("192.0.4.5/23");
199         assertNotEqualEitherWay(p1, p2);
200 
201 
202         p1 = new IpPrefix("2001:db8:dead:beef:f00::80/122");
203         p2 = new IpPrefix(IPV6_BYTES, 122);
204         assertEquals("2001:db8:dead:beef:f00::80/122", p2.toString());
205         assertEqualBothWays(p1, p2);
206 
207         p1 = new IpPrefix("2001:db8:dead:beef:f00::bf/122");
208         assertEqualBothWays(p1, p2);
209 
210         p1 = new IpPrefix("2001:db8:dead:beef:f00::8:0/123");
211         assertNotEqualEitherWay(p1, p2);
212 
213         p1 = new IpPrefix("2001:db8:dead:beef::/122");
214         assertNotEqualEitherWay(p1, p2);
215 
216         // 192.0.2.4/32 != c000:0204::/32.
217         byte[] ipv6bytes = new byte[16];
218         System.arraycopy(IPV4_BYTES, 0, ipv6bytes, 0, IPV4_BYTES.length);
219         p1 = new IpPrefix(ipv6bytes, 32);
220         assertEqualBothWays(p1, new IpPrefix("c000:0204::/32"));
221 
222         p2 = new IpPrefix(IPV4_BYTES, 32);
223         assertNotEqualEitherWay(p1, p2);
224     }
225 
226     @Test
testContainsInetAddress()227     public void testContainsInetAddress() {
228         IpPrefix p = new IpPrefix("2001:db8:f00::ace:d00d/127");
229         assertTrue(p.contains(address("2001:db8:f00::ace:d00c")));
230         assertTrue(p.contains(address("2001:db8:f00::ace:d00d")));
231         assertFalse(p.contains(address("2001:db8:f00::ace:d00e")));
232         assertFalse(p.contains(address("2001:db8:f00::bad:d00d")));
233         assertFalse(p.contains(address("2001:4868:4860::8888")));
234         assertFalse(p.contains(address("8.8.8.8")));
235 
236         p = new IpPrefix("192.0.2.0/23");
237         assertTrue(p.contains(address("192.0.2.43")));
238         assertTrue(p.contains(address("192.0.3.21")));
239         assertFalse(p.contains(address("192.0.0.21")));
240         assertFalse(p.contains(address("8.8.8.8")));
241         assertFalse(p.contains(address("2001:4868:4860::8888")));
242 
243         IpPrefix ipv6Default = new IpPrefix("::/0");
244         assertTrue(ipv6Default.contains(address("2001:db8::f00")));
245         assertFalse(ipv6Default.contains(address("192.0.2.1")));
246 
247         IpPrefix ipv4Default = new IpPrefix("0.0.0.0/0");
248         assertTrue(ipv4Default.contains(address("255.255.255.255")));
249         assertTrue(ipv4Default.contains(address("192.0.2.1")));
250         assertFalse(ipv4Default.contains(address("2001:db8::f00")));
251     }
252 
253     @Test
testContainsIpPrefix()254     public void testContainsIpPrefix() {
255         assertTrue(new IpPrefix("0.0.0.0/0").containsPrefix(new IpPrefix("0.0.0.0/0")));
256         assertTrue(new IpPrefix("0.0.0.0/0").containsPrefix(new IpPrefix("1.2.3.4/0")));
257         assertTrue(new IpPrefix("0.0.0.0/0").containsPrefix(new IpPrefix("1.2.3.4/8")));
258         assertTrue(new IpPrefix("0.0.0.0/0").containsPrefix(new IpPrefix("1.2.3.4/24")));
259         assertTrue(new IpPrefix("0.0.0.0/0").containsPrefix(new IpPrefix("1.2.3.4/23")));
260 
261         assertTrue(new IpPrefix("1.2.3.4/8").containsPrefix(new IpPrefix("1.2.3.4/8")));
262         assertTrue(new IpPrefix("1.2.3.4/8").containsPrefix(new IpPrefix("1.254.12.9/8")));
263         assertTrue(new IpPrefix("1.2.3.4/21").containsPrefix(new IpPrefix("1.2.3.4/21")));
264         assertTrue(new IpPrefix("1.2.3.4/32").containsPrefix(new IpPrefix("1.2.3.4/32")));
265 
266         assertTrue(new IpPrefix("1.2.3.4/20").containsPrefix(new IpPrefix("1.2.3.0/24")));
267 
268         assertFalse(new IpPrefix("1.2.3.4/32").containsPrefix(new IpPrefix("1.2.3.5/32")));
269         assertFalse(new IpPrefix("1.2.3.4/8").containsPrefix(new IpPrefix("2.2.3.4/8")));
270         assertFalse(new IpPrefix("0.0.0.0/16").containsPrefix(new IpPrefix("0.0.0.0/15")));
271         assertFalse(new IpPrefix("100.0.0.0/8").containsPrefix(new IpPrefix("99.0.0.0/8")));
272 
273         assertTrue(new IpPrefix("::/0").containsPrefix(new IpPrefix("::/0")));
274         assertTrue(new IpPrefix("::/0").containsPrefix(new IpPrefix("2001:db8::f00/1")));
275         assertTrue(new IpPrefix("::/0").containsPrefix(new IpPrefix("3d8a:661:a0::770/8")));
276         assertTrue(new IpPrefix("::/0").containsPrefix(new IpPrefix("2001:db8::f00/8")));
277         assertTrue(new IpPrefix("::/0").containsPrefix(new IpPrefix("2001:db8::f00/64")));
278         assertTrue(new IpPrefix("::/0").containsPrefix(new IpPrefix("2001:db8::f00/113")));
279         assertTrue(new IpPrefix("::/0").containsPrefix(new IpPrefix("2001:db8::f00/128")));
280 
281         assertTrue(new IpPrefix("2001:db8:f00::ace:d00d/64").containsPrefix(
282                 new IpPrefix("2001:db8:f00::ace:d00d/64")));
283         assertTrue(new IpPrefix("2001:db8:f00::ace:d00d/64").containsPrefix(
284                 new IpPrefix("2001:db8:f00::ace:d00d/120")));
285         assertFalse(new IpPrefix("2001:db8:f00::ace:d00d/64").containsPrefix(
286                 new IpPrefix("2001:db8:f00::ace:d00d/32")));
287         assertFalse(new IpPrefix("2001:db8:f00::ace:d00d/64").containsPrefix(
288                 new IpPrefix("2006:db8:f00::ace:d00d/96")));
289 
290         assertTrue(new IpPrefix("2001:db8:f00::ace:d00d/128").containsPrefix(
291                 new IpPrefix("2001:db8:f00::ace:d00d/128")));
292         assertTrue(new IpPrefix("2001:db8:f00::ace:d00d/100").containsPrefix(
293                 new IpPrefix("2001:db8:f00::ace:ccaf/110")));
294 
295         assertFalse(new IpPrefix("2001:db8:f00::ace:d00d/128").containsPrefix(
296                 new IpPrefix("2001:db8:f00::ace:d00e/128")));
297         assertFalse(new IpPrefix("::/30").containsPrefix(new IpPrefix("::/29")));
298     }
299 
300     @Test
testHashCode()301     public void testHashCode() {
302         IpPrefix p = new IpPrefix(new byte[4], 0);
303         Random random = new Random();
304         for (int i = 0; i < 100; i++) {
305             final IpPrefix oldP = p;
306             if (random.nextBoolean()) {
307                 // IPv4.
308                 byte[] b = new byte[4];
309                 random.nextBytes(b);
310                 p = new IpPrefix(b, random.nextInt(33));
311             } else {
312                 // IPv6.
313                 byte[] b = new byte[16];
314                 random.nextBytes(b);
315                 p = new IpPrefix(b, random.nextInt(129));
316             }
317             if (p.equals(oldP)) {
318                 assertEquals(p.hashCode(), oldP.hashCode());
319             }
320             if (p.hashCode() != oldP.hashCode()) {
321                 assertNotEquals(p, oldP);
322             }
323         }
324     }
325 
326     @Test
testHashCodeIsNotConstant()327     public void testHashCodeIsNotConstant() {
328         IpPrefix[] prefixes = {
329             new IpPrefix("2001:db8:f00::ace:d00d/127"),
330             new IpPrefix("192.0.2.0/23"),
331             new IpPrefix("::/0"),
332             new IpPrefix("0.0.0.0/0"),
333         };
334         for (int i = 0; i < prefixes.length; i++) {
335             for (int j = i + 1; j < prefixes.length; j++) {
336                 assertNotEquals(prefixes[i].hashCode(), prefixes[j].hashCode());
337             }
338         }
339     }
340 
341     @Test
testMappedAddressesAreBroken()342     public void testMappedAddressesAreBroken() {
343         // 192.0.2.0/24 != ::ffff:c000:0204/120, but because we use InetAddress,
344         // we are unable to comprehend that.
345         byte[] ipv6bytes = {
346             (byte) 0, (byte) 0, (byte) 0, (byte) 0,
347             (byte) 0, (byte) 0, (byte) 0, (byte) 0,
348             (byte) 0, (byte) 0, (byte) 0xff, (byte) 0xff,
349             (byte) 192, (byte) 0, (byte) 2, (byte) 0};
350         IpPrefix p = new IpPrefix(ipv6bytes, 120);
351         assertEquals(16, p.getRawAddress().length);       // Fine.
352         assertArrayEquals(ipv6bytes, p.getRawAddress());  // Fine.
353 
354         // Broken.
355         assertEquals("192.0.2.0/120", p.toString());
356         assertEquals(InetAddress.parseNumericAddress("192.0.2.0"), p.getAddress());
357     }
358 
359     @Test
testParceling()360     public void testParceling() {
361         IpPrefix p;
362 
363         p = new IpPrefix("2001:4860:db8::/64");
364         assertParcelingIsLossless(p);
365         assertTrue(p.isIPv6());
366 
367         p = new IpPrefix("192.0.2.0/25");
368         assertParcelingIsLossless(p);
369         assertTrue(p.isIPv4());
370     }
371 }
372