1 /*
2  * Copyright (C) 2019 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 static android.net.netlink.StructNdOptPref64.getScaledLifetimePlc;
20 import static android.net.netlink.StructNdOptPref64.plcToPrefixLength;
21 import static android.net.netlink.StructNdOptPref64.prefixLengthToPlc;
22 
23 import static com.android.testutils.MiscAsserts.assertThrows;
24 
25 import static org.junit.Assert.assertEquals;
26 import static org.junit.Assert.assertNull;
27 
28 import android.net.IpPrefix;
29 
30 import androidx.test.filters.SmallTest;
31 import androidx.test.runner.AndroidJUnit4;
32 
33 import libcore.util.HexEncoding;
34 
35 import org.junit.Test;
36 import org.junit.runner.RunWith;
37 
38 import java.net.InetAddress;
39 import java.nio.ByteBuffer;
40 
41 @RunWith(AndroidJUnit4.class)
42 @SmallTest
43 public class StructNdOptPref64Test {
44 
45     private static final String PREFIX1 = "64:ff9b::";
46     private static final String PREFIX2 = "2001:db8:1:2:3:64::";
47 
prefixBytes(String addrString)48     private static byte[] prefixBytes(String addrString) throws Exception {
49         InetAddress addr = InetAddress.getByName(addrString);
50         byte[] prefixBytes = new byte[12];
51         System.arraycopy(addr.getAddress(), 0, prefixBytes, 0, 12);
52         return prefixBytes;
53     }
54 
prefix(String addrString, int prefixLength)55     private static IpPrefix prefix(String addrString, int prefixLength) throws Exception {
56         return new IpPrefix(InetAddress.getByName(addrString), prefixLength);
57     }
58 
assertPref64OptMatches(int lifetime, IpPrefix prefix, StructNdOptPref64 opt)59     private void assertPref64OptMatches(int lifetime, IpPrefix prefix, StructNdOptPref64 opt) {
60         assertEquals(StructNdOptPref64.TYPE, opt.type);
61         assertEquals(2, opt.length);
62         assertEquals(lifetime, opt.lifetime);
63         assertEquals(prefix, opt.prefix);
64     }
65 
assertToByteBufferMatches(StructNdOptPref64 opt, String expected)66     private void assertToByteBufferMatches(StructNdOptPref64 opt, String expected) {
67         String actual = HexEncoding.encodeToString(opt.toByteBuffer().array());
68         assertEquals(expected, actual);
69     }
70 
makeNdOptPref64(int lifetime, byte[] prefix, int prefixLengthCode)71     private ByteBuffer makeNdOptPref64(int lifetime, byte[] prefix, int prefixLengthCode) {
72         if (prefix.length != 12) throw new IllegalArgumentException("Prefix must be 12 bytes");
73 
74         ByteBuffer buf = ByteBuffer.allocate(16)
75                 .put((byte) StructNdOptPref64.TYPE)
76                 .put((byte) StructNdOptPref64.LENGTH)
77                 .putShort(getScaledLifetimePlc(lifetime, prefixLengthCode))
78                 .put(prefix, 0, 12);
79 
80         buf.flip();
81         return buf;
82     }
83 
84     @Test
testParseCannedOption()85     public void testParseCannedOption() throws Exception {
86         String hexBytes = "2602"               // type=38, len=2 (16 bytes)
87                 + "0088"                       // lifetime=136, PLC=0 (/96)
88                 + "20010DB80003000400050006";  // 2001:db8:3:4:5:6/96
89         byte[] rawBytes = HexEncoding.decode(hexBytes);
90         StructNdOptPref64 opt = StructNdOptPref64.parse(ByteBuffer.wrap(rawBytes));
91         assertPref64OptMatches(136, prefix("2001:DB8:3:4:5:6::", 96), opt);
92         assertToByteBufferMatches(opt, hexBytes);
93 
94         hexBytes = "2602"                      // type=38, len=2 (16 bytes)
95                 + "2752"                       // lifetime=10064, PLC=2 (/56)
96                 + "0064FF9B0000000000000000";  // 64:ff9b::/56
97         rawBytes = HexEncoding.decode(hexBytes);
98         opt = StructNdOptPref64.parse(ByteBuffer.wrap(rawBytes));
99         assertPref64OptMatches(10064, prefix("64:FF9B::", 56), opt);
100         assertToByteBufferMatches(opt, hexBytes);
101     }
102 
103     @Test
testParsing()104     public void testParsing() throws Exception {
105         // Valid.
106         ByteBuffer buf = makeNdOptPref64(600, prefixBytes(PREFIX1), 0);
107         StructNdOptPref64 opt = StructNdOptPref64.parse(buf);
108         assertPref64OptMatches(600, prefix(PREFIX1, 96), opt);
109 
110         // Valid, zero lifetime, /64.
111         buf = makeNdOptPref64(0, prefixBytes(PREFIX1), 1);
112         opt = StructNdOptPref64.parse(buf);
113         assertPref64OptMatches(0, prefix(PREFIX1, 64), opt);
114 
115         // Valid, low lifetime, /56.
116         buf = makeNdOptPref64(8, prefixBytes(PREFIX2), 2);
117         opt = StructNdOptPref64.parse(buf);
118         assertPref64OptMatches(8, prefix(PREFIX2, 56), opt);
119         assertEquals(new IpPrefix("2001:db8:1::/56"), opt.prefix);  // Prefix is truncated.
120 
121         // Valid, maximum lifetime, /32.
122         buf = makeNdOptPref64(65528, prefixBytes(PREFIX2), 5);
123         opt = StructNdOptPref64.parse(buf);
124         assertPref64OptMatches(65528, prefix(PREFIX2, 32), opt);
125         assertEquals(new IpPrefix("2001:db8::/32"), opt.prefix);  // Prefix is truncated.
126 
127         // Lifetime not divisible by 8.
128         buf = makeNdOptPref64(300, prefixBytes(PREFIX2), 0);
129         opt = StructNdOptPref64.parse(buf);
130         assertPref64OptMatches(296, prefix(PREFIX2, 96), opt);
131 
132         // Invalid prefix length codes.
133         buf = makeNdOptPref64(600, prefixBytes(PREFIX1), 6);
134         assertNull(StructNdOptPref64.parse(buf));
135         buf = makeNdOptPref64(600, prefixBytes(PREFIX1), 7);
136         assertNull(StructNdOptPref64.parse(buf));
137 
138         // Truncated to varying lengths...
139         buf = makeNdOptPref64(600, prefixBytes(PREFIX1), 3);
140         final int len = buf.limit();
141         for (int i = 0; i < buf.limit() - 1; i++) {
142             buf.flip();
143             buf.limit(i);
144             assertNull("Option truncated to " + i + " bytes, should have returned null",
145                     StructNdOptPref64.parse(buf));
146         }
147         buf.flip();
148         buf.limit(len);
149         // ... but otherwise OK.
150         opt = StructNdOptPref64.parse(buf);
151         assertPref64OptMatches(600, prefix(PREFIX1, 48), opt);
152     }
153 
154     @Test
testToString()155     public void testToString() throws Exception {
156         ByteBuffer buf = makeNdOptPref64(600, prefixBytes(PREFIX1), 4);
157         StructNdOptPref64 opt = StructNdOptPref64.parse(buf);
158         assertPref64OptMatches(600, prefix(PREFIX1, 40), opt);
159         assertEquals("NdOptPref64(64:ff9b::/40, 600)", opt.toString());
160     }
161 
assertInvalidPlc(int plc)162     private void assertInvalidPlc(int plc) {
163         assertThrows(IllegalArgumentException.class, () -> plcToPrefixLength(plc));
164     }
165 
166     @Test
testPrefixLengthToPlc()167     public void testPrefixLengthToPlc() {
168         for (int i = 0; i < 6; i++) {
169             assertEquals(i, prefixLengthToPlc(plcToPrefixLength(i)));
170         }
171         assertInvalidPlc(-1);
172         assertInvalidPlc(6);
173         assertInvalidPlc(7);
174         assertEquals(0, prefixLengthToPlc(96));
175     }
176 
177 
assertInvalidParameters(IpPrefix prefix, int lifetime)178     private void assertInvalidParameters(IpPrefix prefix, int lifetime) {
179         assertThrows(IllegalArgumentException.class, () -> new StructNdOptPref64(prefix, lifetime));
180     }
181 
182     @Test
testToByteBuffer()183     public void testToByteBuffer() throws Exception {
184         final IpPrefix prefix1 = prefix(PREFIX1, 56);
185         final IpPrefix prefix2 = prefix(PREFIX2, 96);
186 
187         StructNdOptPref64 opt = new StructNdOptPref64(prefix1, 600);
188         assertToByteBufferMatches(opt, "2602025A0064FF9B0000000000000000");
189         assertEquals(new IpPrefix("64:ff9b::/56"), opt.prefix);
190         assertEquals(600, opt.lifetime);
191 
192         opt = new StructNdOptPref64(prefix2, 65519);
193         assertToByteBufferMatches(opt, "2602FFE820010DB80001000200030064");
194         assertEquals(new IpPrefix("2001:db8:1:2:3:64::/96"), opt.prefix);
195         assertEquals(65512, opt.lifetime);
196 
197         assertInvalidParameters(prefix1, 65535);
198         assertInvalidParameters(prefix2, -1);
199         assertInvalidParameters(prefix("1.2.3.4", 32), 600);
200     }
201 }
202