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