1 /* 2 * Copyright (C) 2020 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 17 package android.net.netlink; 18 19 import android.net.IpPrefix; 20 import android.util.Log; 21 22 import androidx.annotation.NonNull; 23 24 import java.net.Inet6Address; 25 import java.net.InetAddress; 26 import java.net.UnknownHostException; 27 import java.nio.ByteBuffer; 28 import java.util.Objects; 29 30 /** 31 * The PREF64 router advertisement option. RFC 8781. 32 * 33 * 0 1 2 3 34 * 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 35 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 36 * | Type | Length | Scaled Lifetime | PLC | 37 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 38 * | | 39 * + + 40 * | Highest 96 bits of the Prefix | 41 * + + 42 * | | 43 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 44 * 45 */ 46 public class StructNdOptPref64 extends NdOption { 47 public static final int STRUCT_SIZE = 16; 48 public static final int TYPE = 38; 49 public static final byte LENGTH = 2; 50 51 private static final String TAG = StructNdOptPref64.class.getSimpleName(); 52 53 /** 54 * How many seconds the prefix is expected to remain valid. 55 * Valid values are from 0 to 65528 in multiples of 8. 56 */ 57 public final int lifetime; 58 /** The NAT64 prefix. */ 59 @NonNull public final IpPrefix prefix; 60 plcToPrefixLength(int plc)61 static int plcToPrefixLength(int plc) { 62 switch (plc) { 63 case 0: return 96; 64 case 1: return 64; 65 case 2: return 56; 66 case 3: return 48; 67 case 4: return 40; 68 case 5: return 32; 69 default: 70 throw new IllegalArgumentException("Invalid prefix length code " + plc); 71 } 72 } 73 prefixLengthToPlc(int prefixLength)74 static int prefixLengthToPlc(int prefixLength) { 75 switch (prefixLength) { 76 case 96: return 0; 77 case 64: return 1; 78 case 56: return 2; 79 case 48: return 3; 80 case 40: return 4; 81 case 32: return 5; 82 default: 83 throw new IllegalArgumentException("Invalid prefix length " + prefixLength); 84 } 85 } 86 87 /** 88 * Returns the 2-byte "scaled lifetime and prefix length code" field: 13-bit lifetime, 3-bit PLC 89 */ getScaledLifetimePlc(int lifetime, int prefixLengthCode)90 static short getScaledLifetimePlc(int lifetime, int prefixLengthCode) { 91 return (short) ((lifetime & 0xfff8) | (prefixLengthCode & 0x7)); 92 } 93 StructNdOptPref64(@onNull IpPrefix prefix, int lifetime)94 public StructNdOptPref64(@NonNull IpPrefix prefix, int lifetime) { 95 super((byte) TYPE, LENGTH); 96 97 Objects.requireNonNull(prefix, "prefix must not be null"); 98 if (!(prefix.getAddress() instanceof Inet6Address)) { 99 throw new IllegalArgumentException("Must be an IPv6 prefix: " + prefix); 100 } 101 prefixLengthToPlc(prefix.getPrefixLength()); // Throw if the prefix length is invalid. 102 this.prefix = prefix; 103 104 if (lifetime < 0 || lifetime > 0xfff8) { 105 throw new IllegalArgumentException("Invalid lifetime " + lifetime); 106 } 107 this.lifetime = lifetime & 0xfff8; 108 } 109 StructNdOptPref64(@onNull ByteBuffer buf)110 private StructNdOptPref64(@NonNull ByteBuffer buf) { 111 super(buf.get(), Byte.toUnsignedInt(buf.get())); 112 if (type != TYPE) throw new IllegalArgumentException("Invalid type " + type); 113 if (length != LENGTH) throw new IllegalArgumentException("Invalid length " + length); 114 115 int scaledLifetimePlc = Short.toUnsignedInt(buf.getShort()); 116 lifetime = scaledLifetimePlc & 0xfff8; 117 118 byte[] addressBytes = new byte[16]; 119 buf.get(addressBytes, 0, 12); 120 InetAddress addr; 121 try { 122 addr = InetAddress.getByAddress(addressBytes); 123 } catch (UnknownHostException e) { 124 throw new AssertionError("16-byte array not valid InetAddress?"); 125 } 126 prefix = new IpPrefix(addr, plcToPrefixLength(scaledLifetimePlc & 7)); 127 } 128 129 /** 130 * Parses an option from a {@link ByteBuffer}. 131 * 132 * @param buf The buffer from which to parse the option. The buffer's byte order must be 133 * {@link java.nio.ByteOrder#BIG_ENDIAN}. 134 * @return the parsed option, or {@code null} if the option could not be parsed successfully 135 * (for example, if it was truncated, or if the prefix length code was wrong). 136 */ parse(@onNull ByteBuffer buf)137 public static StructNdOptPref64 parse(@NonNull ByteBuffer buf) { 138 if (buf == null || buf.remaining() < STRUCT_SIZE) return null; 139 try { 140 return new StructNdOptPref64(buf); 141 } catch (IllegalArgumentException e) { 142 // Not great, but better than throwing an exception that might crash the caller. 143 // Convention in this package is that null indicates that the option was truncated, so 144 // callers must already handle it. 145 Log.d(TAG, "Invalid PREF64 option: " + e); 146 return null; 147 } 148 } 149 writeToByteBuffer(ByteBuffer buf)150 protected void writeToByteBuffer(ByteBuffer buf) { 151 super.writeToByteBuffer(buf); 152 buf.putShort(getScaledLifetimePlc(lifetime, prefixLengthToPlc(prefix.getPrefixLength()))); 153 buf.put(prefix.getRawAddress(), 0, 12); 154 } 155 156 /** Outputs the wire format of the option to a new big-endian ByteBuffer. */ toByteBuffer()157 public ByteBuffer toByteBuffer() { 158 ByteBuffer buf = ByteBuffer.allocate(STRUCT_SIZE); 159 writeToByteBuffer(buf); 160 buf.flip(); 161 return buf; 162 } 163 164 @Override 165 @NonNull toString()166 public String toString() { 167 return String.format("NdOptPref64(%s, %d)", prefix, lifetime); 168 } 169 } 170