1/*
2 * Copyright (C) 2013 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
17package android.net;
18
19import java.net.Inet4Address;
20import java.net.Inet6Address;
21import java.net.InetAddress;
22import java.net.InterfaceAddress;
23import java.net.NetworkInterface;
24import java.net.SocketException;
25import java.util.Arrays;
26import java.util.Collections;
27import java.util.Comparator;
28import java.util.List;
29
30import android.net.LinkAddress;
31import android.os.Parcel;
32import android.test.AndroidTestCase;
33import static android.test.MoreAsserts.assertNotEqual;
34import android.test.suitebuilder.annotation.SmallTest;
35
36import static android.system.OsConstants.IFA_F_DEPRECATED;
37import static android.system.OsConstants.IFA_F_PERMANENT;
38import static android.system.OsConstants.IFA_F_TENTATIVE;
39import static android.system.OsConstants.RT_SCOPE_HOST;
40import static android.system.OsConstants.RT_SCOPE_LINK;
41import static android.system.OsConstants.RT_SCOPE_SITE;
42import static android.system.OsConstants.RT_SCOPE_UNIVERSE;
43
44/**
45 * Tests for {@link LinkAddress}.
46 */
47public class LinkAddressTest extends AndroidTestCase {
48
49    private static final String V4 = "192.0.2.1";
50    private static final String V6 = "2001:db8::1";
51    private static final InetAddress V4_ADDRESS = NetworkUtils.numericToInetAddress(V4);
52    private static final InetAddress V6_ADDRESS = NetworkUtils.numericToInetAddress(V6);
53
54    public void testConstants() {
55        // RT_SCOPE_UNIVERSE = 0, but all the other constants should be nonzero.
56        assertNotEqual(0, RT_SCOPE_HOST);
57        assertNotEqual(0, RT_SCOPE_LINK);
58        assertNotEqual(0, RT_SCOPE_SITE);
59
60        assertNotEqual(0, IFA_F_DEPRECATED);
61        assertNotEqual(0, IFA_F_PERMANENT);
62        assertNotEqual(0, IFA_F_TENTATIVE);
63    }
64
65    public void testConstructors() throws SocketException {
66        LinkAddress address;
67
68        // Valid addresses work as expected.
69        address = new LinkAddress(V4_ADDRESS, 25);
70        assertEquals(V4_ADDRESS, address.getAddress());
71        assertEquals(25, address.getPrefixLength());
72        assertEquals(0, address.getFlags());
73        assertEquals(RT_SCOPE_UNIVERSE, address.getScope());
74
75        address = new LinkAddress(V6_ADDRESS, 127);
76        assertEquals(V6_ADDRESS, address.getAddress());
77        assertEquals(127, address.getPrefixLength());
78        assertEquals(0, address.getFlags());
79        assertEquals(RT_SCOPE_UNIVERSE, address.getScope());
80
81        // Nonsensical flags/scopes or combinations thereof are acceptable.
82        address = new LinkAddress(V6 + "/64", IFA_F_DEPRECATED | IFA_F_PERMANENT, RT_SCOPE_LINK);
83        assertEquals(V6_ADDRESS, address.getAddress());
84        assertEquals(64, address.getPrefixLength());
85        assertEquals(IFA_F_DEPRECATED | IFA_F_PERMANENT, address.getFlags());
86        assertEquals(RT_SCOPE_LINK, address.getScope());
87
88        address = new LinkAddress(V4 + "/23", 123, 456);
89        assertEquals(V4_ADDRESS, address.getAddress());
90        assertEquals(23, address.getPrefixLength());
91        assertEquals(123, address.getFlags());
92        assertEquals(456, address.getScope());
93
94        // InterfaceAddress doesn't have a constructor. Fetch some from an interface.
95        List<InterfaceAddress> addrs = NetworkInterface.getByName("lo").getInterfaceAddresses();
96
97        // We expect to find 127.0.0.1/8 and ::1/128, in any order.
98        LinkAddress ipv4Loopback, ipv6Loopback;
99        assertEquals(2, addrs.size());
100        if (addrs.get(0).getAddress() instanceof Inet4Address) {
101            ipv4Loopback = new LinkAddress(addrs.get(0));
102            ipv6Loopback = new LinkAddress(addrs.get(1));
103        } else {
104            ipv4Loopback = new LinkAddress(addrs.get(1));
105            ipv6Loopback = new LinkAddress(addrs.get(0));
106        }
107
108        assertEquals(NetworkUtils.numericToInetAddress("127.0.0.1"), ipv4Loopback.getAddress());
109        assertEquals(8, ipv4Loopback.getPrefixLength());
110
111        assertEquals(NetworkUtils.numericToInetAddress("::1"), ipv6Loopback.getAddress());
112        assertEquals(128, ipv6Loopback.getPrefixLength());
113
114        // Null addresses are rejected.
115        try {
116            address = new LinkAddress(null, 24);
117            fail("Null InetAddress should cause IllegalArgumentException");
118        } catch(IllegalArgumentException expected) {}
119
120        try {
121            address = new LinkAddress((String) null, IFA_F_PERMANENT, RT_SCOPE_UNIVERSE);
122            fail("Null string should cause IllegalArgumentException");
123        } catch(IllegalArgumentException expected) {}
124
125        try {
126            address = new LinkAddress((InterfaceAddress) null);
127            fail("Null string should cause NullPointerException");
128        } catch(NullPointerException expected) {}
129
130        // Invalid prefix lengths are rejected.
131        try {
132            address = new LinkAddress(V4_ADDRESS, -1);
133            fail("Negative IPv4 prefix length should cause IllegalArgumentException");
134        } catch(IllegalArgumentException expected) {}
135
136        try {
137            address = new LinkAddress(V6_ADDRESS, -1);
138            fail("Negative IPv6 prefix length should cause IllegalArgumentException");
139        } catch(IllegalArgumentException expected) {}
140
141        try {
142            address = new LinkAddress(V4_ADDRESS, 33);
143            fail("/33 IPv4 prefix length should cause IllegalArgumentException");
144        } catch(IllegalArgumentException expected) {}
145
146        try {
147            address = new LinkAddress(V4 + "/33", IFA_F_PERMANENT, RT_SCOPE_UNIVERSE);
148            fail("/33 IPv4 prefix length should cause IllegalArgumentException");
149        } catch(IllegalArgumentException expected) {}
150
151
152        try {
153            address = new LinkAddress(V6_ADDRESS, 129, IFA_F_PERMANENT, RT_SCOPE_UNIVERSE);
154            fail("/129 IPv6 prefix length should cause IllegalArgumentException");
155        } catch(IllegalArgumentException expected) {}
156
157        try {
158            address = new LinkAddress(V6 + "/129", IFA_F_PERMANENT, RT_SCOPE_UNIVERSE);
159            fail("/129 IPv6 prefix length should cause IllegalArgumentException");
160        } catch(IllegalArgumentException expected) {}
161
162        // Multicast addresses are rejected.
163        try {
164            address = new LinkAddress("224.0.0.2/32");
165            fail("IPv4 multicast address should cause IllegalArgumentException");
166        } catch(IllegalArgumentException expected) {}
167
168        try {
169            address = new LinkAddress("ff02::1/128");
170            fail("IPv6 multicast address should cause IllegalArgumentException");
171        } catch(IllegalArgumentException expected) {}
172    }
173
174    public void testAddressScopes() {
175        assertEquals(RT_SCOPE_HOST, new LinkAddress("::/128").getScope());
176        assertEquals(RT_SCOPE_HOST, new LinkAddress("0.0.0.0/32").getScope());
177
178        assertEquals(RT_SCOPE_LINK, new LinkAddress("::1/128").getScope());
179        assertEquals(RT_SCOPE_LINK, new LinkAddress("127.0.0.5/8").getScope());
180        assertEquals(RT_SCOPE_LINK, new LinkAddress("fe80::ace:d00d/64").getScope());
181        assertEquals(RT_SCOPE_LINK, new LinkAddress("169.254.5.12/16").getScope());
182
183        assertEquals(RT_SCOPE_SITE, new LinkAddress("fec0::dead/64").getScope());
184
185        assertEquals(RT_SCOPE_UNIVERSE, new LinkAddress("10.1.2.3/21").getScope());
186        assertEquals(RT_SCOPE_UNIVERSE, new LinkAddress("192.0.2.1/25").getScope());
187        assertEquals(RT_SCOPE_UNIVERSE, new LinkAddress("2001:db8::/64").getScope());
188        assertEquals(RT_SCOPE_UNIVERSE, new LinkAddress("5000::/127").getScope());
189    }
190
191    private void assertIsSameAddressAs(LinkAddress l1, LinkAddress l2) {
192        assertTrue(l1 + " unexpectedly does not have same address as " + l2,
193                l1.isSameAddressAs(l2));
194        assertTrue(l2 + " unexpectedly does not have same address as " + l1,
195                l2.isSameAddressAs(l1));
196    }
197
198    private void assertIsNotSameAddressAs(LinkAddress l1, LinkAddress l2) {
199        assertFalse(l1 + " unexpectedly has same address as " + l2,
200                l1.isSameAddressAs(l2));
201        assertFalse(l2 + " unexpectedly has same address as " + l1,
202                l1.isSameAddressAs(l2));
203    }
204
205    private void assertLinkAddressesEqual(LinkAddress l1, LinkAddress l2) {
206        assertTrue(l1 + " unexpectedly not equal to " + l2, l1.equals(l2));
207        assertTrue(l2 + " unexpectedly not equal to " + l1, l2.equals(l1));
208        assertEquals(l1.hashCode(), l2.hashCode());
209    }
210
211    private void assertLinkAddressesNotEqual(LinkAddress l1, LinkAddress l2) {
212        assertFalse(l1 + " unexpectedly equal to " + l2, l1.equals(l2));
213        assertFalse(l2 + " unexpectedly equal to " + l1, l2.equals(l1));
214    }
215
216    public void testEqualsAndSameAddressAs() {
217        LinkAddress l1, l2, l3;
218
219        l1 = new LinkAddress("2001:db8::1/64");
220        l2 = new LinkAddress("2001:db8::1/64");
221        assertLinkAddressesEqual(l1, l2);
222        assertIsSameAddressAs(l1, l2);
223
224        l2 = new LinkAddress("2001:db8::1/65");
225        assertLinkAddressesNotEqual(l1, l2);
226        assertIsNotSameAddressAs(l1, l2);
227
228        l2 = new LinkAddress("2001:db8::2/64");
229        assertLinkAddressesNotEqual(l1, l2);
230        assertIsNotSameAddressAs(l1, l2);
231
232
233        l1 = new LinkAddress("192.0.2.1/24");
234        l2 = new LinkAddress("192.0.2.1/24");
235        assertLinkAddressesEqual(l1, l2);
236        assertIsSameAddressAs(l1, l2);
237
238        l2 = new LinkAddress("192.0.2.1/23");
239        assertLinkAddressesNotEqual(l1, l2);
240        assertIsNotSameAddressAs(l1, l2);
241
242        l2 = new LinkAddress("192.0.2.2/24");
243        assertLinkAddressesNotEqual(l1, l2);
244        assertIsNotSameAddressAs(l1, l2);
245
246
247        // Check equals() and isSameAddressAs() on identical addresses with different flags.
248        l1 = new LinkAddress(V6_ADDRESS, 64);
249        l2 = new LinkAddress(V6_ADDRESS, 64, 0, RT_SCOPE_UNIVERSE);
250        assertLinkAddressesEqual(l1, l2);
251        assertIsSameAddressAs(l1, l2);
252
253        l2 = new LinkAddress(V6_ADDRESS, 64, IFA_F_DEPRECATED, RT_SCOPE_UNIVERSE);
254        assertLinkAddressesNotEqual(l1, l2);
255        assertIsSameAddressAs(l1, l2);
256
257        // Check equals() and isSameAddressAs() on identical addresses with different scope.
258        l1 = new LinkAddress(V4_ADDRESS, 24);
259        l2 = new LinkAddress(V4_ADDRESS, 24, 0, RT_SCOPE_UNIVERSE);
260        assertLinkAddressesEqual(l1, l2);
261        assertIsSameAddressAs(l1, l2);
262
263        l2 = new LinkAddress(V4_ADDRESS, 24, 0, RT_SCOPE_HOST);
264        assertLinkAddressesNotEqual(l1, l2);
265        assertIsSameAddressAs(l1, l2);
266
267        // Addresses with the same start or end bytes aren't equal between families.
268        l1 = new LinkAddress("32.1.13.184/24");
269        l2 = new LinkAddress("2001:db8::1/24");
270        l3 = new LinkAddress("::2001:db8/24");
271
272        byte[] ipv4Bytes = l1.getAddress().getAddress();
273        byte[] l2FirstIPv6Bytes = Arrays.copyOf(l2.getAddress().getAddress(), 4);
274        byte[] l3LastIPv6Bytes = Arrays.copyOfRange(l3.getAddress().getAddress(), 12, 16);
275        assertTrue(Arrays.equals(ipv4Bytes, l2FirstIPv6Bytes));
276        assertTrue(Arrays.equals(ipv4Bytes, l3LastIPv6Bytes));
277
278        assertLinkAddressesNotEqual(l1, l2);
279        assertIsNotSameAddressAs(l1, l2);
280
281        assertLinkAddressesNotEqual(l1, l3);
282        assertIsNotSameAddressAs(l1, l3);
283
284        // Because we use InetAddress, an IPv4 address is equal to its IPv4-mapped address.
285        // TODO: Investigate fixing this.
286        String addressString = V4 + "/24";
287        l1 = new LinkAddress(addressString);
288        l2 = new LinkAddress("::ffff:" + addressString);
289        assertLinkAddressesEqual(l1, l2);
290        assertIsSameAddressAs(l1, l2);
291    }
292
293    public void testHashCode() {
294        LinkAddress l;
295
296        l = new LinkAddress(V4_ADDRESS, 23);
297        assertEquals(-982787, l.hashCode());
298
299        l = new LinkAddress(V4_ADDRESS, 23, 0, RT_SCOPE_HOST);
300        assertEquals(-971865, l.hashCode());
301
302        l = new LinkAddress(V4_ADDRESS, 27);
303        assertEquals(-982743, l.hashCode());
304
305        l = new LinkAddress(V6_ADDRESS, 64);
306        assertEquals(1076522926, l.hashCode());
307
308        l = new LinkAddress(V6_ADDRESS, 128);
309        assertEquals(1076523630, l.hashCode());
310
311        l = new LinkAddress(V6_ADDRESS, 128, IFA_F_TENTATIVE, RT_SCOPE_UNIVERSE);
312        assertEquals(1076524846, l.hashCode());
313    }
314
315    private LinkAddress passThroughParcel(LinkAddress l) {
316        Parcel p = Parcel.obtain();
317        LinkAddress l2 = null;
318        try {
319            l.writeToParcel(p, 0);
320            p.setDataPosition(0);
321            l2 = LinkAddress.CREATOR.createFromParcel(p);
322        } finally {
323            p.recycle();
324        }
325        assertNotNull(l2);
326        return l2;
327    }
328
329    private void assertParcelingIsLossless(LinkAddress l) {
330      LinkAddress l2 = passThroughParcel(l);
331      assertEquals(l, l2);
332    }
333
334    public void testParceling() {
335        LinkAddress l;
336
337        l = new LinkAddress(V6_ADDRESS, 64, 123, 456);
338        assertParcelingIsLossless(l);
339
340        l = new LinkAddress(V4 + "/28", IFA_F_PERMANENT, RT_SCOPE_LINK);
341        assertParcelingIsLossless(l);
342    }
343}
344