blob: 8226346649abe1ae11c16e07da8a16ebbe0080db [file] [log] [blame]
/*
* Copyright (C) 2020 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 android.annotation.SuppressLint;
import android.net.IpPrefix;
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.
*
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Type | Length | Scaled Lifetime | PLC |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | |
* + +
* | Highest 96 bits of the Prefix |
* + +
* | |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
*/
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();
/**
* How many seconds the prefix is expected to remain valid.
* Valid values are from 0 to 65528 in multiples of 8.
*/
public final int lifetime;
/** The NAT64 prefix. */
@NonNull public final IpPrefix prefix;
static int plcToPrefixLength(int plc) {
switch (plc) {
case 0: return 96;
case 1: return 64;
case 2: return 56;
case 3: return 48;
case 4: return 40;
case 5: return 32;
default:
throw new IllegalArgumentException("Invalid prefix length code " + plc);
}
}
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;
}
@SuppressLint("NewApi")
private StructNdOptPref64(@NonNull ByteBuffer buf) {
super(buf.get(), Byte.toUnsignedInt(buf.get()));
if (type != TYPE) throw new IllegalArgumentException("Invalid type " + type);
if (length != LENGTH) throw new IllegalArgumentException("Invalid length " + length);
int scaledLifetimePlc = Short.toUnsignedInt(buf.getShort());
lifetime = scaledLifetimePlc & 0xfff8;
byte[] addressBytes = new byte[16];
buf.get(addressBytes, 0, 12);
InetAddress addr;
try {
addr = InetAddress.getByAddress(addressBytes);
} catch (UnknownHostException e) {
throw new AssertionError("16-byte array not valid InetAddress?");
}
prefix = new IpPrefix(addr, plcToPrefixLength(scaledLifetimePlc & 7));
}
/**
* Parses an option from a {@link ByteBuffer}.
*
* @param buf The buffer from which to parse the option. The buffer's byte order must be
* {@link java.nio.ByteOrder#BIG_ENDIAN}.
* @return the parsed option, or {@code null} if the option could not be parsed successfully
* (for example, if it was truncated, or if the prefix length code was wrong).
*/
public static StructNdOptPref64 parse(@NonNull ByteBuffer buf) {
if (buf.remaining() < STRUCT_SIZE) return null;
try {
return new StructNdOptPref64(buf);
} catch (IllegalArgumentException e) {
// Not great, but better than throwing an exception that might crash the caller.
// Convention in this package is that null indicates that the option was truncated, so
// callers must already handle it.
Log.d(TAG, "Invalid PREF64 option: " + e);
return null;
}
}
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() {
return String.format("NdOptPref64(%s, %d)", prefix, lifetime);
}
}