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