/* * Copyright 2017 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 org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import android.os.Build; import androidx.test.filters.SmallTest; import com.android.net.module.util.MacAddressUtils; import com.android.testutils.DevSdkIgnoreRule; import com.android.testutils.DevSdkIgnoreRunner; import org.junit.Test; import org.junit.runner.RunWith; import java.net.Inet6Address; import java.util.Arrays; import java.util.Random; @SmallTest @RunWith(DevSdkIgnoreRunner.class) @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R) public class MacAddressTest { static class AddrTypeTestCase { byte[] addr; int expectedType; static AddrTypeTestCase of(int expectedType, int... addr) { AddrTypeTestCase t = new AddrTypeTestCase(); t.expectedType = expectedType; t.addr = toByteArray(addr); return t; } } @Test public void testMacAddrTypes() { AddrTypeTestCase[] testcases = { AddrTypeTestCase.of(MacAddress.TYPE_UNKNOWN), AddrTypeTestCase.of(MacAddress.TYPE_UNKNOWN, 0), AddrTypeTestCase.of(MacAddress.TYPE_UNKNOWN, 1, 2, 3, 4, 5), AddrTypeTestCase.of(MacAddress.TYPE_UNKNOWN, 1, 2, 3, 4, 5, 6, 7), AddrTypeTestCase.of(MacAddress.TYPE_UNICAST, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0), AddrTypeTestCase.of(MacAddress.TYPE_BROADCAST, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff), AddrTypeTestCase.of(MacAddress.TYPE_MULTICAST, 1, 2, 3, 4, 5, 6), AddrTypeTestCase.of(MacAddress.TYPE_MULTICAST, 11, 22, 33, 44, 55, 66), AddrTypeTestCase.of(MacAddress.TYPE_MULTICAST, 33, 33, 0xaa, 0xbb, 0xcc, 0xdd) }; for (AddrTypeTestCase t : testcases) { int got = MacAddress.macAddressType(t.addr); String msg = String.format("expected type of %s to be %s, but got %s", Arrays.toString(t.addr), t.expectedType, got); assertEquals(msg, t.expectedType, got); if (got != MacAddress.TYPE_UNKNOWN) { assertEquals(got, MacAddress.fromBytes(t.addr).getAddressType()); } } } @Test public void testToOuiString() { String[][] macs = { {"07:00:d3:56:8a:c4", "07:00:d3"}, {"33:33:aa:bb:cc:dd", "33:33:aa"}, {"06:00:00:00:00:00", "06:00:00"}, {"07:00:d3:56:8a:c4", "07:00:d3"} }; for (String[] pair : macs) { String mac = pair[0]; String expected = pair[1]; assertEquals(expected, MacAddress.fromString(mac).toOuiString()); } } @Test public void testHexPaddingWhenPrinting() { String[] macs = { "07:00:d3:56:8a:c4", "33:33:aa:bb:cc:dd", "06:00:00:00:00:00", "07:00:d3:56:8a:c4" }; for (String mac : macs) { assertEquals(mac, MacAddress.fromString(mac).toString()); assertEquals(mac, MacAddress.stringAddrFromByteAddr(MacAddress.byteAddrFromStringAddr(mac))); } } @Test public void testIsMulticastAddress() { MacAddress[] multicastAddresses = { MacAddress.BROADCAST_ADDRESS, MacAddress.fromString("07:00:d3:56:8a:c4"), MacAddress.fromString("33:33:aa:bb:cc:dd"), }; MacAddress[] unicastAddresses = { MacAddress.ALL_ZEROS_ADDRESS, MacAddress.fromString("00:01:44:55:66:77"), MacAddress.fromString("08:00:22:33:44:55"), MacAddress.fromString("06:00:00:00:00:00"), }; for (MacAddress mac : multicastAddresses) { String msg = mac.toString() + " expected to be a multicast address"; assertTrue(msg, MacAddressUtils.isMulticastAddress(mac)); } for (MacAddress mac : unicastAddresses) { String msg = mac.toString() + " expected not to be a multicast address"; assertFalse(msg, MacAddressUtils.isMulticastAddress(mac)); } } @Test public void testIsLocallyAssignedAddress() { MacAddress[] localAddresses = { MacAddress.fromString("06:00:00:00:00:00"), MacAddress.fromString("07:00:d3:56:8a:c4"), MacAddress.fromString("33:33:aa:bb:cc:dd"), }; MacAddress[] universalAddresses = { MacAddress.fromString("00:01:44:55:66:77"), MacAddress.fromString("08:00:22:33:44:55"), }; for (MacAddress mac : localAddresses) { String msg = mac.toString() + " expected to be a locally assigned address"; assertTrue(msg, mac.isLocallyAssigned()); } for (MacAddress mac : universalAddresses) { String msg = mac.toString() + " expected not to be globally unique address"; assertFalse(msg, mac.isLocallyAssigned()); } } @Test public void testMacAddressConversions() { final int iterations = 10000; for (int i = 0; i < iterations; i++) { MacAddress mac = MacAddressUtils.createRandomUnicastAddress(); String stringRepr = mac.toString(); byte[] bytesRepr = mac.toByteArray(); assertEquals(mac, MacAddress.fromString(stringRepr)); assertEquals(mac, MacAddress.fromBytes(bytesRepr)); assertEquals(mac, MacAddress.fromString(MacAddress.stringAddrFromByteAddr(bytesRepr))); assertEquals(mac, MacAddress.fromBytes(MacAddress.byteAddrFromStringAddr(stringRepr))); } } @Test public void testMacAddressRandomGeneration() { final int iterations = 1000; final String expectedAndroidOui = "da:a1:19"; for (int i = 0; i < iterations; i++) { MacAddress mac = MacAddress.createRandomUnicastAddressWithGoogleBase(); String stringRepr = mac.toString(); assertTrue(stringRepr + " expected to be a locally assigned address", mac.isLocallyAssigned()); assertTrue(stringRepr + " expected to begin with " + expectedAndroidOui, stringRepr.startsWith(expectedAndroidOui)); } final Random r = new Random(); final String anotherOui = "24:5f:78"; final String expectedLocalOui = "26:5f:78"; final MacAddress base = MacAddress.fromString(anotherOui + ":0:0:0"); for (int i = 0; i < iterations; i++) { MacAddress mac = MacAddressUtils.createRandomUnicastAddress(base, r); String stringRepr = mac.toString(); assertTrue(stringRepr + " expected to be a locally assigned address", mac.isLocallyAssigned()); assertEquals(MacAddress.TYPE_UNICAST, mac.getAddressType()); assertTrue(stringRepr + " expected to begin with " + expectedLocalOui, stringRepr.startsWith(expectedLocalOui)); } for (int i = 0; i < iterations; i++) { MacAddress mac = MacAddressUtils.createRandomUnicastAddress(); String stringRepr = mac.toString(); assertTrue(stringRepr + " expected to be a locally assigned address", mac.isLocallyAssigned()); assertEquals(MacAddress.TYPE_UNICAST, mac.getAddressType()); } } @Test public void testConstructorInputValidation() { String[] invalidStringAddresses = { "", "abcd", "1:2:3:4:5", "1:2:3:4:5:6:7", "10000:2:3:4:5:6", }; for (String s : invalidStringAddresses) { try { MacAddress mac = MacAddress.fromString(s); fail("MacAddress.fromString(" + s + ") should have failed, but returned " + mac); } catch (IllegalArgumentException excepted) { } } try { MacAddress mac = MacAddress.fromString(null); fail("MacAddress.fromString(null) should have failed, but returned " + mac); } catch (NullPointerException excepted) { } byte[][] invalidBytesAddresses = { {}, {1,2,3,4,5}, {1,2,3,4,5,6,7}, }; for (byte[] b : invalidBytesAddresses) { try { MacAddress mac = MacAddress.fromBytes(b); fail("MacAddress.fromBytes(" + Arrays.toString(b) + ") should have failed, but returned " + mac); } catch (IllegalArgumentException excepted) { } } try { MacAddress mac = MacAddress.fromBytes(null); fail("MacAddress.fromBytes(null) should have failed, but returned " + mac); } catch (NullPointerException excepted) { } } @Test public void testMatches() { // match 4 bytes prefix assertTrue(MacAddress.fromString("aa:bb:cc:dd:ee:11").matches( MacAddress.fromString("aa:bb:cc:dd:00:00"), MacAddress.fromString("ff:ff:ff:ff:00:00"))); // match bytes 0,1,2 and 5 assertTrue(MacAddress.fromString("aa:bb:cc:dd:ee:11").matches( MacAddress.fromString("aa:bb:cc:00:00:11"), MacAddress.fromString("ff:ff:ff:00:00:ff"))); // match 34 bit prefix assertTrue(MacAddress.fromString("aa:bb:cc:dd:ee:11").matches( MacAddress.fromString("aa:bb:cc:dd:c0:00"), MacAddress.fromString("ff:ff:ff:ff:c0:00"))); // fail to match 36 bit prefix assertFalse(MacAddress.fromString("aa:bb:cc:dd:ee:11").matches( MacAddress.fromString("aa:bb:cc:dd:40:00"), MacAddress.fromString("ff:ff:ff:ff:f0:00"))); // match all 6 bytes assertTrue(MacAddress.fromString("aa:bb:cc:dd:ee:11").matches( MacAddress.fromString("aa:bb:cc:dd:ee:11"), MacAddress.fromString("ff:ff:ff:ff:ff:ff"))); // match none of 6 bytes assertTrue(MacAddress.fromString("aa:bb:cc:dd:ee:11").matches( MacAddress.fromString("00:00:00:00:00:00"), MacAddress.fromString("00:00:00:00:00:00"))); } /** * Tests that link-local address generation from MAC is valid. */ @Test public void testLinkLocalFromMacGeneration() { MacAddress mac = MacAddress.fromString("52:74:f2:b1:a8:7f"); byte[] inet6ll = {(byte) 0xfe, (byte) 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x74, (byte) 0xf2, (byte) 0xff, (byte) 0xfe, (byte) 0xb1, (byte) 0xa8, 0x7f}; Inet6Address llv6 = mac.getLinkLocalIpv6FromEui48Mac(); assertTrue(llv6.isLinkLocalAddress()); assertArrayEquals(inet6ll, llv6.getAddress()); } static byte[] toByteArray(int... in) { byte[] out = new byte[in.length]; for (int i = 0; i < in.length; i++) { out[i] = (byte) in[i]; } return out; } }