/* * Copyright (C) 2019 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 com.android.net.module.util.netlink; import static com.android.net.module.util.netlink.StructNdOptPref64.getScaledLifetimePlc; import static com.android.net.module.util.netlink.StructNdOptPref64.plcToPrefixLength; import static com.android.net.module.util.netlink.StructNdOptPref64.prefixLengthToPlc; import static com.android.testutils.MiscAsserts.assertThrows; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import android.annotation.SuppressLint; import android.net.IpPrefix; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import libcore.util.HexEncoding; import org.junit.Test; import org.junit.runner.RunWith; import java.net.InetAddress; import java.nio.ByteBuffer; @RunWith(AndroidJUnit4.class) @SmallTest public class StructNdOptPref64Test { private static final String PREFIX1 = "64:ff9b::"; private static final String PREFIX2 = "2001:db8:1:2:3:64::"; private static byte[] prefixBytes(String addrString) throws Exception { InetAddress addr = InetAddress.getByName(addrString); byte[] prefixBytes = new byte[12]; System.arraycopy(addr.getAddress(), 0, prefixBytes, 0, 12); return prefixBytes; } @SuppressLint("NewApi") private static IpPrefix prefix(String addrString, int prefixLength) throws Exception { return new IpPrefix(InetAddress.getByName(addrString), prefixLength); } private void assertPref64OptMatches(int lifetime, IpPrefix prefix, StructNdOptPref64 opt) { assertEquals(StructNdOptPref64.TYPE, opt.type); assertEquals(2, opt.length); assertEquals(lifetime, opt.lifetime); assertEquals(prefix, opt.prefix); } private void assertToByteBufferMatches(StructNdOptPref64 opt, String expected) { String actual = HexEncoding.encodeToString(opt.toByteBuffer().array()); assertEquals(expected, actual); } private ByteBuffer makeNdOptPref64(int lifetime, byte[] prefix, int prefixLengthCode) { if (prefix.length != 12) throw new IllegalArgumentException("Prefix must be 12 bytes"); ByteBuffer buf = ByteBuffer.allocate(16) .put((byte) StructNdOptPref64.TYPE) .put((byte) StructNdOptPref64.LENGTH) .putShort(getScaledLifetimePlc(lifetime, prefixLengthCode)) .put(prefix, 0, 12); buf.flip(); return buf; } @Test public void testParseCannedOption() throws Exception { String hexBytes = "2602" // type=38, len=2 (16 bytes) + "0088" // lifetime=136, PLC=0 (/96) + "20010DB80003000400050006"; // 2001:db8:3:4:5:6/96 byte[] rawBytes = HexEncoding.decode(hexBytes); StructNdOptPref64 opt = StructNdOptPref64.parse(ByteBuffer.wrap(rawBytes)); assertPref64OptMatches(136, prefix("2001:DB8:3:4:5:6::", 96), opt); assertToByteBufferMatches(opt, hexBytes); hexBytes = "2602" // type=38, len=2 (16 bytes) + "2752" // lifetime=10064, PLC=2 (/56) + "0064FF9B0000000000000000"; // 64:ff9b::/56 rawBytes = HexEncoding.decode(hexBytes); opt = StructNdOptPref64.parse(ByteBuffer.wrap(rawBytes)); assertPref64OptMatches(10064, prefix("64:FF9B::", 56), opt); assertToByteBufferMatches(opt, hexBytes); } @Test public void testParsing() throws Exception { // Valid. ByteBuffer buf = makeNdOptPref64(600, prefixBytes(PREFIX1), 0); StructNdOptPref64 opt = StructNdOptPref64.parse(buf); assertPref64OptMatches(600, prefix(PREFIX1, 96), opt); // Valid, zero lifetime, /64. buf = makeNdOptPref64(0, prefixBytes(PREFIX1), 1); opt = StructNdOptPref64.parse(buf); assertPref64OptMatches(0, prefix(PREFIX1, 64), opt); // Valid, low lifetime, /56. buf = makeNdOptPref64(8, prefixBytes(PREFIX2), 2); opt = StructNdOptPref64.parse(buf); assertPref64OptMatches(8, prefix(PREFIX2, 56), opt); assertEquals(new IpPrefix("2001:db8:1::/56"), opt.prefix); // Prefix is truncated. // Valid, maximum lifetime, /32. buf = makeNdOptPref64(65528, prefixBytes(PREFIX2), 5); opt = StructNdOptPref64.parse(buf); assertPref64OptMatches(65528, prefix(PREFIX2, 32), opt); assertEquals(new IpPrefix("2001:db8::/32"), opt.prefix); // Prefix is truncated. // Lifetime not divisible by 8. buf = makeNdOptPref64(300, prefixBytes(PREFIX2), 0); opt = StructNdOptPref64.parse(buf); assertPref64OptMatches(296, prefix(PREFIX2, 96), opt); // Invalid prefix length codes. buf = makeNdOptPref64(600, prefixBytes(PREFIX1), 6); assertNull(StructNdOptPref64.parse(buf)); buf = makeNdOptPref64(600, prefixBytes(PREFIX1), 7); assertNull(StructNdOptPref64.parse(buf)); // Truncated to varying lengths... buf = makeNdOptPref64(600, prefixBytes(PREFIX1), 3); final int len = buf.limit(); for (int i = 0; i < buf.limit() - 1; i++) { buf.flip(); buf.limit(i); assertNull("Option truncated to " + i + " bytes, should have returned null", StructNdOptPref64.parse(buf)); } buf.flip(); buf.limit(len); // ... but otherwise OK. opt = StructNdOptPref64.parse(buf); assertPref64OptMatches(600, prefix(PREFIX1, 48), opt); } @Test public void testToString() throws Exception { ByteBuffer buf = makeNdOptPref64(600, prefixBytes(PREFIX1), 4); StructNdOptPref64 opt = StructNdOptPref64.parse(buf); assertPref64OptMatches(600, prefix(PREFIX1, 40), opt); assertEquals("NdOptPref64(64:ff9b::/40, 600)", opt.toString()); } private void assertInvalidPlc(int plc) { assertThrows(IllegalArgumentException.class, () -> plcToPrefixLength(plc)); } @Test public void testPrefixLengthToPlc() { for (int i = 0; i < 6; i++) { assertEquals(i, prefixLengthToPlc(plcToPrefixLength(i))); } assertInvalidPlc(-1); assertInvalidPlc(6); assertInvalidPlc(7); assertEquals(0, prefixLengthToPlc(96)); } private void assertInvalidParameters(IpPrefix prefix, int lifetime) { assertThrows(IllegalArgumentException.class, () -> new StructNdOptPref64(prefix, lifetime)); } @Test public void testToByteBuffer() throws Exception { final IpPrefix prefix1 = prefix(PREFIX1, 56); final IpPrefix prefix2 = prefix(PREFIX2, 96); StructNdOptPref64 opt = new StructNdOptPref64(prefix1, 600); assertToByteBufferMatches(opt, "2602025A0064FF9B0000000000000000"); assertEquals(new IpPrefix("64:ff9b::/56"), opt.prefix); assertEquals(600, opt.lifetime); opt = new StructNdOptPref64(prefix2, 65519); assertToByteBufferMatches(opt, "2602FFE820010DB80001000200030064"); assertEquals(new IpPrefix("2001:db8:1:2:3:64::/96"), opt.prefix); assertEquals(65512, opt.lifetime); assertInvalidParameters(prefix1, 65535); assertInvalidParameters(prefix2, -1); assertInvalidParameters(prefix("1.2.3.4", 32), 600); } }