/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.net; import static com.android.testutils.MiscAsserts.assertEqualBothWays; import static com.android.testutils.MiscAsserts.assertNotEqualEitherWay; import static com.android.testutils.ParcelUtils.assertParcelingIsLossless; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.testutils.ConnectivityModuleTest; import org.junit.Test; import org.junit.runner.RunWith; import java.net.InetAddress; import java.util.Random; @RunWith(AndroidJUnit4.class) @SmallTest @ConnectivityModuleTest public class IpPrefixTest { private static InetAddress address(String addr) { return InetAddress.parseNumericAddress(addr); } // Explicitly cast everything to byte because "error: possible loss of precision". private static final byte[] IPV4_BYTES = { (byte) 192, (byte) 0, (byte) 2, (byte) 4}; private static final byte[] IPV6_BYTES = { (byte) 0x20, (byte) 0x01, (byte) 0x0d, (byte) 0xb8, (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef, (byte) 0x0f, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xa0 }; @Test public void testConstructor() { IpPrefix p; try { p = new IpPrefix((byte[]) null, 9); fail("Expected NullPointerException: null byte array"); } catch (RuntimeException expected) { } try { p = new IpPrefix((InetAddress) null, 10); fail("Expected NullPointerException: null InetAddress"); } catch (RuntimeException expected) { } try { p = new IpPrefix((String) null); fail("Expected NullPointerException: null String"); } catch (RuntimeException expected) { } try { byte[] b2 = {1, 2, 3, 4, 5}; p = new IpPrefix(b2, 29); fail("Expected IllegalArgumentException: invalid array length"); } catch (IllegalArgumentException expected) { } try { p = new IpPrefix("1.2.3.4"); fail("Expected IllegalArgumentException: no prefix length"); } catch (IllegalArgumentException expected) { } try { p = new IpPrefix("1.2.3.4/"); fail("Expected IllegalArgumentException: empty prefix length"); } catch (IllegalArgumentException expected) { } try { p = new IpPrefix("foo/32"); fail("Expected IllegalArgumentException: invalid address"); } catch (IllegalArgumentException expected) { } try { p = new IpPrefix("1/32"); fail("Expected IllegalArgumentException: deprecated IPv4 format"); } catch (IllegalArgumentException expected) { } try { p = new IpPrefix("1.2.3.256/32"); fail("Expected IllegalArgumentException: invalid IPv4 address"); } catch (IllegalArgumentException expected) { } try { p = new IpPrefix("foo/32"); fail("Expected IllegalArgumentException: non-address"); } catch (IllegalArgumentException expected) { } try { p = new IpPrefix("f00:::/32"); fail("Expected IllegalArgumentException: invalid IPv6 address"); } catch (IllegalArgumentException expected) { } p = new IpPrefix("/64"); assertEquals("::/64", p.toString()); p = new IpPrefix("/128"); assertEquals("::1/128", p.toString()); p = new IpPrefix("[2001:db8::123]/64"); assertEquals("2001:db8::/64", p.toString()); p = new IpPrefix(InetAddresses.parseNumericAddress("::128"), 64); assertEquals("::/64", p.toString()); } @Test public void testTruncation() { IpPrefix p; p = new IpPrefix(IPV4_BYTES, 32); assertEquals("192.0.2.4/32", p.toString()); p = new IpPrefix(IPV4_BYTES, 29); assertEquals("192.0.2.0/29", p.toString()); p = new IpPrefix(IPV4_BYTES, 8); assertEquals("192.0.0.0/8", p.toString()); p = new IpPrefix(IPV4_BYTES, 0); assertEquals("0.0.0.0/0", p.toString()); try { p = new IpPrefix(IPV4_BYTES, 33); fail("Expected IllegalArgumentException: invalid prefix length"); } catch (RuntimeException expected) { } try { p = new IpPrefix(IPV4_BYTES, 128); fail("Expected IllegalArgumentException: invalid prefix length"); } catch (RuntimeException expected) { } try { p = new IpPrefix(IPV4_BYTES, -1); fail("Expected IllegalArgumentException: negative prefix length"); } catch (RuntimeException expected) { } p = new IpPrefix(IPV6_BYTES, 128); assertEquals("2001:db8:dead:beef:f00::a0/128", p.toString()); p = new IpPrefix(IPV6_BYTES, 122); assertEquals("2001:db8:dead:beef:f00::80/122", p.toString()); p = new IpPrefix(IPV6_BYTES, 64); assertEquals("2001:db8:dead:beef::/64", p.toString()); p = new IpPrefix(IPV6_BYTES, 3); assertEquals("2000::/3", p.toString()); p = new IpPrefix(IPV6_BYTES, 0); assertEquals("::/0", p.toString()); try { p = new IpPrefix(IPV6_BYTES, -1); fail("Expected IllegalArgumentException: negative prefix length"); } catch (RuntimeException expected) { } try { p = new IpPrefix(IPV6_BYTES, 129); fail("Expected IllegalArgumentException: negative prefix length"); } catch (RuntimeException expected) { } } @Test public void testEquals() { IpPrefix p1, p2; p1 = new IpPrefix("192.0.2.251/23"); p2 = new IpPrefix(new byte[]{(byte) 192, (byte) 0, (byte) 2, (byte) 251}, 23); assertEqualBothWays(p1, p2); p1 = new IpPrefix("192.0.2.5/23"); assertEqualBothWays(p1, p2); p1 = new IpPrefix("192.0.2.5/24"); assertNotEqualEitherWay(p1, p2); p1 = new IpPrefix("192.0.4.5/23"); assertNotEqualEitherWay(p1, p2); p1 = new IpPrefix("2001:db8:dead:beef:f00::80/122"); p2 = new IpPrefix(IPV6_BYTES, 122); assertEquals("2001:db8:dead:beef:f00::80/122", p2.toString()); assertEqualBothWays(p1, p2); p1 = new IpPrefix("2001:db8:dead:beef:f00::bf/122"); assertEqualBothWays(p1, p2); p1 = new IpPrefix("2001:db8:dead:beef:f00::8:0/123"); assertNotEqualEitherWay(p1, p2); p1 = new IpPrefix("2001:db8:dead:beef::/122"); assertNotEqualEitherWay(p1, p2); // 192.0.2.4/32 != c000:0204::/32. byte[] ipv6bytes = new byte[16]; System.arraycopy(IPV4_BYTES, 0, ipv6bytes, 0, IPV4_BYTES.length); p1 = new IpPrefix(ipv6bytes, 32); assertEqualBothWays(p1, new IpPrefix("c000:0204::/32")); p2 = new IpPrefix(IPV4_BYTES, 32); assertNotEqualEitherWay(p1, p2); } @Test public void testContainsInetAddress() { IpPrefix p = new IpPrefix("2001:db8:f00::ace:d00d/127"); assertTrue(p.contains(address("2001:db8:f00::ace:d00c"))); assertTrue(p.contains(address("2001:db8:f00::ace:d00d"))); assertFalse(p.contains(address("2001:db8:f00::ace:d00e"))); assertFalse(p.contains(address("2001:db8:f00::bad:d00d"))); assertFalse(p.contains(address("2001:4868:4860::8888"))); assertFalse(p.contains(address("8.8.8.8"))); p = new IpPrefix("192.0.2.0/23"); assertTrue(p.contains(address("192.0.2.43"))); assertTrue(p.contains(address("192.0.3.21"))); assertFalse(p.contains(address("192.0.0.21"))); assertFalse(p.contains(address("8.8.8.8"))); assertFalse(p.contains(address("2001:4868:4860::8888"))); IpPrefix ipv6Default = new IpPrefix("::/0"); assertTrue(ipv6Default.contains(address("2001:db8::f00"))); assertFalse(ipv6Default.contains(address("192.0.2.1"))); IpPrefix ipv4Default = new IpPrefix("0.0.0.0/0"); assertTrue(ipv4Default.contains(address("255.255.255.255"))); assertTrue(ipv4Default.contains(address("192.0.2.1"))); assertFalse(ipv4Default.contains(address("2001:db8::f00"))); } @Test public void testContainsIpPrefix() { assertTrue(new IpPrefix("0.0.0.0/0").containsPrefix(new IpPrefix("0.0.0.0/0"))); assertTrue(new IpPrefix("0.0.0.0/0").containsPrefix(new IpPrefix("1.2.3.4/0"))); assertTrue(new IpPrefix("0.0.0.0/0").containsPrefix(new IpPrefix("1.2.3.4/8"))); assertTrue(new IpPrefix("0.0.0.0/0").containsPrefix(new IpPrefix("1.2.3.4/24"))); assertTrue(new IpPrefix("0.0.0.0/0").containsPrefix(new IpPrefix("1.2.3.4/23"))); assertTrue(new IpPrefix("1.2.3.4/8").containsPrefix(new IpPrefix("1.2.3.4/8"))); assertTrue(new IpPrefix("1.2.3.4/8").containsPrefix(new IpPrefix("1.254.12.9/8"))); assertTrue(new IpPrefix("1.2.3.4/21").containsPrefix(new IpPrefix("1.2.3.4/21"))); assertTrue(new IpPrefix("1.2.3.4/32").containsPrefix(new IpPrefix("1.2.3.4/32"))); assertTrue(new IpPrefix("1.2.3.4/20").containsPrefix(new IpPrefix("1.2.3.0/24"))); assertFalse(new IpPrefix("1.2.3.4/32").containsPrefix(new IpPrefix("1.2.3.5/32"))); assertFalse(new IpPrefix("1.2.3.4/8").containsPrefix(new IpPrefix("2.2.3.4/8"))); assertFalse(new IpPrefix("0.0.0.0/16").containsPrefix(new IpPrefix("0.0.0.0/15"))); assertFalse(new IpPrefix("100.0.0.0/8").containsPrefix(new IpPrefix("99.0.0.0/8"))); assertTrue(new IpPrefix("::/0").containsPrefix(new IpPrefix("::/0"))); assertTrue(new IpPrefix("::/0").containsPrefix(new IpPrefix("2001:db8::f00/1"))); assertTrue(new IpPrefix("::/0").containsPrefix(new IpPrefix("3d8a:661:a0::770/8"))); assertTrue(new IpPrefix("::/0").containsPrefix(new IpPrefix("2001:db8::f00/8"))); assertTrue(new IpPrefix("::/0").containsPrefix(new IpPrefix("2001:db8::f00/64"))); assertTrue(new IpPrefix("::/0").containsPrefix(new IpPrefix("2001:db8::f00/113"))); assertTrue(new IpPrefix("::/0").containsPrefix(new IpPrefix("2001:db8::f00/128"))); assertTrue(new IpPrefix("2001:db8:f00::ace:d00d/64").containsPrefix( new IpPrefix("2001:db8:f00::ace:d00d/64"))); assertTrue(new IpPrefix("2001:db8:f00::ace:d00d/64").containsPrefix( new IpPrefix("2001:db8:f00::ace:d00d/120"))); assertFalse(new IpPrefix("2001:db8:f00::ace:d00d/64").containsPrefix( new IpPrefix("2001:db8:f00::ace:d00d/32"))); assertFalse(new IpPrefix("2001:db8:f00::ace:d00d/64").containsPrefix( new IpPrefix("2006:db8:f00::ace:d00d/96"))); assertTrue(new IpPrefix("2001:db8:f00::ace:d00d/128").containsPrefix( new IpPrefix("2001:db8:f00::ace:d00d/128"))); assertTrue(new IpPrefix("2001:db8:f00::ace:d00d/100").containsPrefix( new IpPrefix("2001:db8:f00::ace:ccaf/110"))); assertFalse(new IpPrefix("2001:db8:f00::ace:d00d/128").containsPrefix( new IpPrefix("2001:db8:f00::ace:d00e/128"))); assertFalse(new IpPrefix("::/30").containsPrefix(new IpPrefix("::/29"))); } @Test public void testHashCode() { IpPrefix p = new IpPrefix(new byte[4], 0); Random random = new Random(); for (int i = 0; i < 100; i++) { final IpPrefix oldP = p; if (random.nextBoolean()) { // IPv4. byte[] b = new byte[4]; random.nextBytes(b); p = new IpPrefix(b, random.nextInt(33)); } else { // IPv6. byte[] b = new byte[16]; random.nextBytes(b); p = new IpPrefix(b, random.nextInt(129)); } if (p.equals(oldP)) { assertEquals(p.hashCode(), oldP.hashCode()); } if (p.hashCode() != oldP.hashCode()) { assertNotEquals(p, oldP); } } } @Test public void testHashCodeIsNotConstant() { IpPrefix[] prefixes = { new IpPrefix("2001:db8:f00::ace:d00d/127"), new IpPrefix("192.0.2.0/23"), new IpPrefix("::/0"), new IpPrefix("0.0.0.0/0"), }; for (int i = 0; i < prefixes.length; i++) { for (int j = i + 1; j < prefixes.length; j++) { assertNotEquals(prefixes[i].hashCode(), prefixes[j].hashCode()); } } } @Test public void testMappedAddressesAreBroken() { // 192.0.2.0/24 != ::ffff:c000:0204/120, but because we use InetAddress, // we are unable to comprehend that. byte[] ipv6bytes = { (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0xff, (byte) 0xff, (byte) 192, (byte) 0, (byte) 2, (byte) 0}; IpPrefix p = new IpPrefix(ipv6bytes, 120); assertEquals(16, p.getRawAddress().length); // Fine. assertArrayEquals(ipv6bytes, p.getRawAddress()); // Fine. // Broken. assertEquals("192.0.2.0/120", p.toString()); assertEquals(InetAddress.parseNumericAddress("192.0.2.0"), p.getAddress()); } @Test public void testParceling() { IpPrefix p; p = new IpPrefix("2001:4860:db8::/64"); assertParcelingIsLossless(p); assertTrue(p.isIPv6()); p = new IpPrefix("192.0.2.0/25"); assertParcelingIsLossless(p); assertTrue(p.isIPv4()); } }