Loading common/netlinkclient/src/android/net/netlink/NdOption.java +6 −1 Original line number Diff line number Diff line Loading @@ -30,7 +30,7 @@ public class NdOption { public final int length; /** Constructs a new NdOption. */ public NdOption(byte type, int length) { NdOption(byte type, int length) { this.type = type; this.length = length; } Loading Loading @@ -69,6 +69,11 @@ public class NdOption { } } void writeToByteBuffer(ByteBuffer buf) { buf.put(type); buf.put((byte) length); } @Override public String toString() { return String.format("NdOption(%d, %d)", Byte.toUnsignedInt(type), length); Loading common/netlinkclient/src/android/net/netlink/StructNdOptPref64.java +56 −3 Original line number Diff line number Diff line Loading @@ -21,9 +21,11 @@ import android.util.Log; import androidx.annotation.NonNull; import java.net.Inet6Address; import java.net.InetAddress; import java.net.UnknownHostException; import java.nio.ByteBuffer; import java.util.Objects; /** * The PREF64 router advertisement option. RFC 8781. Loading @@ -44,6 +46,7 @@ import java.nio.ByteBuffer; public class StructNdOptPref64 extends NdOption { public static final int STRUCT_SIZE = 16; public static final int TYPE = 38; public static final byte LENGTH = 2; private static final String TAG = StructNdOptPref64.class.getSimpleName(); Loading @@ -55,7 +58,7 @@ public class StructNdOptPref64 extends NdOption { /** The NAT64 prefix. */ public final IpPrefix prefix; int plcToPrefixLength(int plc) { static int plcToPrefixLength(int plc) { switch (plc) { case 0: return 96; case 1: return 64; Loading @@ -68,10 +71,46 @@ public class StructNdOptPref64 extends NdOption { } } public StructNdOptPref64(@NonNull ByteBuffer buf) { static int prefixLengthToPlc(int prefixLength) { switch (prefixLength) { case 96: return 0; case 64: return 1; case 56: return 2; case 48: return 3; case 40: return 4; case 32: return 5; default: throw new IllegalArgumentException("Invalid prefix length " + prefixLength); } } /** * Returns the 2-byte "scaled lifetime and prefix length code" field: 13-bit lifetime, 3-bit PLC */ static short getScaledLifetimePlc(int lifetime, int prefixLengthCode) { return (short) ((lifetime & 0xfff8) | (prefixLengthCode & 0x7)); } public StructNdOptPref64(@NonNull IpPrefix prefix, int lifetime) { super((byte) TYPE, LENGTH); Objects.requireNonNull(prefix, "prefix must not be null"); if (!(prefix.getAddress() instanceof Inet6Address)) { throw new IllegalArgumentException("Must be an IPv6 prefix: " + prefix); } prefixLengthToPlc(prefix.getPrefixLength()); // Throw if the prefix length is invalid. this.prefix = prefix; if (lifetime < 0 || lifetime > 0xfff8) { throw new IllegalArgumentException("Invalid lifetime " + lifetime); } this.lifetime = lifetime & 0xfff8; } private StructNdOptPref64(@NonNull ByteBuffer buf) { super(buf.get(), Byte.toUnsignedInt(buf.get())); if (type != TYPE) throw new IllegalArgumentException("Invalid type " + type); if (length != 2) throw new IllegalArgumentException("Invalid length " + length); if (length != LENGTH) throw new IllegalArgumentException("Invalid length " + length); int scaledLifetimePlc = Short.toUnsignedInt(buf.getShort()); lifetime = scaledLifetimePlc & 0xfff8; Loading Loading @@ -108,6 +147,20 @@ public class StructNdOptPref64 extends NdOption { } } protected void writeToByteBuffer(ByteBuffer buf) { super.writeToByteBuffer(buf); buf.putShort(getScaledLifetimePlc(lifetime, prefixLengthToPlc(prefix.getPrefixLength()))); buf.put(prefix.getRawAddress(), 0, 12); } /** Outputs the wire format of the option to a new big-endian ByteBuffer. */ public ByteBuffer toByteBuffer() { ByteBuffer buf = ByteBuffer.allocate(STRUCT_SIZE); writeToByteBuffer(buf); buf.flip(); return buf; } @Override @NonNull public String toString() { Loading tests/integration/src/android/net/ip/IpClientIntegrationTest.java +7 −1 Original line number Diff line number Diff line Loading @@ -407,7 +407,13 @@ public class IpClientIntegrationTest { mPacketReaderThread.start(); mHandler = mPacketReaderThread.getThreadHandler(); mTapFd = iface.getFileDescriptor().getFileDescriptor(); // Detach the FileDescriptor from the ParcelFileDescriptor. // Otherwise, the garbage collector might call the ParcelFileDescriptor's finalizer, which // closes the FileDescriptor and destroys our tap interface. An alternative would be to // make the ParcelFileDescriptor or the TestNetworkInterface a class member so they never // go out of scope. mTapFd = new FileDescriptor(); mTapFd.setInt$(iface.getFileDescriptor().detachFd()); mPacketReader = new TapPacketReader(mHandler, mTapFd, DATA_BUFFER_LEN); mHandler.post(() -> mPacketReader.start()); } Loading tests/unit/src/android/net/netlink/StructNdOptPref64Test.java +53 −7 Original line number Diff line number Diff line Loading @@ -16,6 +16,12 @@ package android.net.netlink; import static android.net.netlink.StructNdOptPref64.getScaledLifetimePlc; import static android.net.netlink.StructNdOptPref64.plcToPrefixLength; import static android.net.netlink.StructNdOptPref64.prefixLengthToPlc; import static com.android.testutils.MiscAssertsKt.assertThrows; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; Loading Loading @@ -57,11 +63,9 @@ public class StructNdOptPref64Test { assertEquals(prefix, opt.prefix); } /** * Returns the 2-byte "scaled lifetime and prefix length code" field: 13-bit lifetime, 3-bit PLC */ private short getPref64ScaledLifetimePlc(int lifetime, int prefixLengthCode) { return (short) ((lifetime & 0xfff8) | (prefixLengthCode & 0x7)); private void assertToByteBufferMatches(StructNdOptPref64 opt, String expected) { String actual = HexEncoding.encodeToString(opt.toByteBuffer().array(), false /*upperCase*/); assertEquals(expected, actual); } private ByteBuffer makeNdOptPref64(int lifetime, byte[] prefix, int prefixLengthCode) { Loading @@ -69,8 +73,8 @@ public class StructNdOptPref64Test { ByteBuffer buf = ByteBuffer.allocate(16) .put((byte) StructNdOptPref64.TYPE) .put((byte) 2) // len=2 (16 bytes) .putShort(getPref64ScaledLifetimePlc(lifetime, prefixLengthCode)) .put((byte) StructNdOptPref64.LENGTH) .putShort(getScaledLifetimePlc(lifetime, prefixLengthCode)) .put(prefix, 0, 12); buf.flip(); Loading @@ -85,6 +89,7 @@ public class StructNdOptPref64Test { 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) Loading @@ -92,6 +97,7 @@ public class StructNdOptPref64Test { rawBytes = HexEncoding.decode(hexBytes); opt = StructNdOptPref64.parse(ByteBuffer.wrap(rawBytes)); assertPref64OptMatches(10064, prefix("64:ff9b::", 56), opt); assertToByteBufferMatches(opt, hexBytes); } @Test Loading Loading @@ -152,4 +158,44 @@ public class StructNdOptPref64Test { 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); } } Loading
common/netlinkclient/src/android/net/netlink/NdOption.java +6 −1 Original line number Diff line number Diff line Loading @@ -30,7 +30,7 @@ public class NdOption { public final int length; /** Constructs a new NdOption. */ public NdOption(byte type, int length) { NdOption(byte type, int length) { this.type = type; this.length = length; } Loading Loading @@ -69,6 +69,11 @@ public class NdOption { } } void writeToByteBuffer(ByteBuffer buf) { buf.put(type); buf.put((byte) length); } @Override public String toString() { return String.format("NdOption(%d, %d)", Byte.toUnsignedInt(type), length); Loading
common/netlinkclient/src/android/net/netlink/StructNdOptPref64.java +56 −3 Original line number Diff line number Diff line Loading @@ -21,9 +21,11 @@ import android.util.Log; import androidx.annotation.NonNull; import java.net.Inet6Address; import java.net.InetAddress; import java.net.UnknownHostException; import java.nio.ByteBuffer; import java.util.Objects; /** * The PREF64 router advertisement option. RFC 8781. Loading @@ -44,6 +46,7 @@ import java.nio.ByteBuffer; public class StructNdOptPref64 extends NdOption { public static final int STRUCT_SIZE = 16; public static final int TYPE = 38; public static final byte LENGTH = 2; private static final String TAG = StructNdOptPref64.class.getSimpleName(); Loading @@ -55,7 +58,7 @@ public class StructNdOptPref64 extends NdOption { /** The NAT64 prefix. */ public final IpPrefix prefix; int plcToPrefixLength(int plc) { static int plcToPrefixLength(int plc) { switch (plc) { case 0: return 96; case 1: return 64; Loading @@ -68,10 +71,46 @@ public class StructNdOptPref64 extends NdOption { } } public StructNdOptPref64(@NonNull ByteBuffer buf) { static int prefixLengthToPlc(int prefixLength) { switch (prefixLength) { case 96: return 0; case 64: return 1; case 56: return 2; case 48: return 3; case 40: return 4; case 32: return 5; default: throw new IllegalArgumentException("Invalid prefix length " + prefixLength); } } /** * Returns the 2-byte "scaled lifetime and prefix length code" field: 13-bit lifetime, 3-bit PLC */ static short getScaledLifetimePlc(int lifetime, int prefixLengthCode) { return (short) ((lifetime & 0xfff8) | (prefixLengthCode & 0x7)); } public StructNdOptPref64(@NonNull IpPrefix prefix, int lifetime) { super((byte) TYPE, LENGTH); Objects.requireNonNull(prefix, "prefix must not be null"); if (!(prefix.getAddress() instanceof Inet6Address)) { throw new IllegalArgumentException("Must be an IPv6 prefix: " + prefix); } prefixLengthToPlc(prefix.getPrefixLength()); // Throw if the prefix length is invalid. this.prefix = prefix; if (lifetime < 0 || lifetime > 0xfff8) { throw new IllegalArgumentException("Invalid lifetime " + lifetime); } this.lifetime = lifetime & 0xfff8; } private StructNdOptPref64(@NonNull ByteBuffer buf) { super(buf.get(), Byte.toUnsignedInt(buf.get())); if (type != TYPE) throw new IllegalArgumentException("Invalid type " + type); if (length != 2) throw new IllegalArgumentException("Invalid length " + length); if (length != LENGTH) throw new IllegalArgumentException("Invalid length " + length); int scaledLifetimePlc = Short.toUnsignedInt(buf.getShort()); lifetime = scaledLifetimePlc & 0xfff8; Loading Loading @@ -108,6 +147,20 @@ public class StructNdOptPref64 extends NdOption { } } protected void writeToByteBuffer(ByteBuffer buf) { super.writeToByteBuffer(buf); buf.putShort(getScaledLifetimePlc(lifetime, prefixLengthToPlc(prefix.getPrefixLength()))); buf.put(prefix.getRawAddress(), 0, 12); } /** Outputs the wire format of the option to a new big-endian ByteBuffer. */ public ByteBuffer toByteBuffer() { ByteBuffer buf = ByteBuffer.allocate(STRUCT_SIZE); writeToByteBuffer(buf); buf.flip(); return buf; } @Override @NonNull public String toString() { Loading
tests/integration/src/android/net/ip/IpClientIntegrationTest.java +7 −1 Original line number Diff line number Diff line Loading @@ -407,7 +407,13 @@ public class IpClientIntegrationTest { mPacketReaderThread.start(); mHandler = mPacketReaderThread.getThreadHandler(); mTapFd = iface.getFileDescriptor().getFileDescriptor(); // Detach the FileDescriptor from the ParcelFileDescriptor. // Otherwise, the garbage collector might call the ParcelFileDescriptor's finalizer, which // closes the FileDescriptor and destroys our tap interface. An alternative would be to // make the ParcelFileDescriptor or the TestNetworkInterface a class member so they never // go out of scope. mTapFd = new FileDescriptor(); mTapFd.setInt$(iface.getFileDescriptor().detachFd()); mPacketReader = new TapPacketReader(mHandler, mTapFd, DATA_BUFFER_LEN); mHandler.post(() -> mPacketReader.start()); } Loading
tests/unit/src/android/net/netlink/StructNdOptPref64Test.java +53 −7 Original line number Diff line number Diff line Loading @@ -16,6 +16,12 @@ package android.net.netlink; import static android.net.netlink.StructNdOptPref64.getScaledLifetimePlc; import static android.net.netlink.StructNdOptPref64.plcToPrefixLength; import static android.net.netlink.StructNdOptPref64.prefixLengthToPlc; import static com.android.testutils.MiscAssertsKt.assertThrows; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; Loading Loading @@ -57,11 +63,9 @@ public class StructNdOptPref64Test { assertEquals(prefix, opt.prefix); } /** * Returns the 2-byte "scaled lifetime and prefix length code" field: 13-bit lifetime, 3-bit PLC */ private short getPref64ScaledLifetimePlc(int lifetime, int prefixLengthCode) { return (short) ((lifetime & 0xfff8) | (prefixLengthCode & 0x7)); private void assertToByteBufferMatches(StructNdOptPref64 opt, String expected) { String actual = HexEncoding.encodeToString(opt.toByteBuffer().array(), false /*upperCase*/); assertEquals(expected, actual); } private ByteBuffer makeNdOptPref64(int lifetime, byte[] prefix, int prefixLengthCode) { Loading @@ -69,8 +73,8 @@ public class StructNdOptPref64Test { ByteBuffer buf = ByteBuffer.allocate(16) .put((byte) StructNdOptPref64.TYPE) .put((byte) 2) // len=2 (16 bytes) .putShort(getPref64ScaledLifetimePlc(lifetime, prefixLengthCode)) .put((byte) StructNdOptPref64.LENGTH) .putShort(getScaledLifetimePlc(lifetime, prefixLengthCode)) .put(prefix, 0, 12); buf.flip(); Loading @@ -85,6 +89,7 @@ public class StructNdOptPref64Test { 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) Loading @@ -92,6 +97,7 @@ public class StructNdOptPref64Test { rawBytes = HexEncoding.decode(hexBytes); opt = StructNdOptPref64.parse(ByteBuffer.wrap(rawBytes)); assertPref64OptMatches(10064, prefix("64:ff9b::", 56), opt); assertToByteBufferMatches(opt, hexBytes); } @Test Loading Loading @@ -152,4 +158,44 @@ public class StructNdOptPref64Test { 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); } }