1 /*
2  * Copyright (C) 2023 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.dhcp6
18 
19 import androidx.test.filters.SmallTest
20 import androidx.test.runner.AndroidJUnit4
21 import com.android.net.module.util.HexDump
22 import com.android.testutils.assertThrows
23 import kotlin.test.assertEquals
24 import kotlin.test.assertTrue
25 import org.junit.Test
26 import org.junit.runner.RunWith
27 
28 @RunWith(AndroidJUnit4::class)
29 @SmallTest
30 class Dhcp6PacketTest {
31     @Test
testDecodeDhcp6SolicitPacketnull32     fun testDecodeDhcp6SolicitPacket() {
33         val solicitHex =
34                 // Solicit, Transaction ID
35                 "01000F51" +
36                 // client identifier option(option_len=12)
37                 "0001000C0003001B024CCBFFFE5F6EA9" +
38                 // elapsed time option(option_len=2)
39                 "000800020000" +
40                 // IA_PD option(option_len=41, including IA prefix option)
41                 "00190029DE3570F50000000000000000" +
42                 // IA prefix option(option_len=25)
43                 "001A001900000000000000004000000000000000000000000000000000"
44         val bytes = HexDump.hexStringToByteArray(solicitHex)
45         val packet = Dhcp6Packet.decode(bytes, bytes.size)
46         assertTrue(packet is Dhcp6SolicitPacket)
47     }
48 
49     @Test
testDecodeDhcp6SolicitPacket_incorrectOptionLengthnull50     fun testDecodeDhcp6SolicitPacket_incorrectOptionLength() {
51         val solicitHex =
52                 // Solicit, Transaction ID
53                 "01000F51" +
54                 // client identifier option(option_len=12)
55                 "0001000C0003001B024CCBFFFE5F6EA9" +
56                 // elapsed time option(wrong option_len=4)
57                 "000800040000" +
58                 // IA_PD option(option_len=41, including IA prefix option)
59                 "00190029DE3570F50000000000000000" +
60                 // IA prefix option(option_len=25)
61                 "001A001900000000000000004000000000000000000000000000000000"
62         val bytes = HexDump.hexStringToByteArray(solicitHex)
63         assertThrows(Dhcp6Packet.ParseException::class.java) {
64                 Dhcp6Packet.decode(bytes, bytes.size)
65         }
66     }
67 
68     @Test
testDecodeDhcp6SolicitPacket_lastTruncatedOptionnull69     fun testDecodeDhcp6SolicitPacket_lastTruncatedOption() {
70         val solicitHex =
71                 // Solicit, Transaction ID
72                 "01000F51" +
73                 // client identifier option(option_len=12)
74                 "0001000C0003001B024CCBFFFE5F6EA9" +
75                 // elapsed time option(option_len=2)
76                 "000800020000" +
77                 // IA_PD option(option_len=41, including IA prefix option)
78                 "00190029DE3570F50000000000000000" +
79                 // IA prefix option(option_len=25, missing one byte)
80                 "001A0019000000000000000040000000000000000000000000000000"
81         val bytes = HexDump.hexStringToByteArray(solicitHex)
82         assertThrows(Dhcp6Packet.ParseException::class.java) {
83                 Dhcp6Packet.decode(bytes, bytes.size)
84         }
85     }
86 
87     @Test
testDecodeDhcp6SolicitPacket_middleTruncatedOptionnull88     fun testDecodeDhcp6SolicitPacket_middleTruncatedOption() {
89         val solicitHex =
90                 // Solicit, Transaction ID
91                 "01000F51" +
92                 // client identifier option(option_len=12, missing one byte)
93                 "0001000C0003001B024CCBFFFE5F6E" +
94                 // elapsed time option(option_len=2)
95                 "000800020000" +
96                 // IA_PD option(option_len=41, including IA prefix option)
97                 "00190029DE3570F50000000000000000" +
98                 // IA prefix option(option_len=25)
99                 "001A001900000000000000004000000000000000000000000000000000"
100         val bytes = HexDump.hexStringToByteArray(solicitHex)
101         assertThrows(Dhcp6Packet.ParseException::class.java) {
102                 Dhcp6Packet.decode(bytes, bytes.size)
103         }
104     }
105 
106     @Test
testDecodeDhcp6AdvertisePacketnull107     fun testDecodeDhcp6AdvertisePacket() {
108         val advertiseHex =
109                 // Advertise, Transaction ID
110                 "0200078A" +
111                 // server identifier option(option_len=10)
112                 "0002000A0003000186C9B26AED4D" +
113                 // client identifier option(option_len=12)
114                 "0001000C0003001B024CCBFFFE5F6EA9" +
115                 // IA_PD option(option_len=70, including IA prefix option)
116                 "001900460CDDCA0C000000CF0000014C" +
117                 // IA prefix option(option_len=25, prefix="2401:fa00:49c:412::/64")
118                 "001A00190000019F0000064F402401FA00049C04810000000000000000" +
119                 // IA prefix option(option_len=25, prefix="fdfd:9ed6:7950:2::/64")
120                 "001A00190000019F0000A8C040FDFD9ED6795000010000000000000000"
121         val bytes = HexDump.hexStringToByteArray(advertiseHex)
122         val packet = Dhcp6Packet.decode(bytes, bytes.size)
123         assertTrue(packet is Dhcp6AdvertisePacket)
124     }
125 
126     @Test
testDecodeDhcp6SolicitPacket_unsupportedOptionnull127     fun testDecodeDhcp6SolicitPacket_unsupportedOption() {
128         val advertiseHex =
129                 // Advertise, Transaction ID
130                 "0200078A" +
131                 // server identifier option(option_len=10)
132                 "0002000A0003000186C9B26AED4D" +
133                 // client identifier option(option_len=12)
134                 "0001000C0003001B024CCBFFFE5F6EA9" +
135                 // SOL_MAX_RT (don't support this option yet)
136                 "005200040000003C" +
137                 // IA_PD option(option_len=70, including IA prefix option)
138                 "001900460CDDCA0C000000CF0000014C" +
139                 // IA prefix option(option_len=25, prefix="2401:fa00:49c:412::/64")
140                 "001A00190000019F0000064F402401FA00049C04810000000000000000" +
141                 // IA prefix option(option_len=25, prefix="fdfd:9ed6:7950:2::/64")
142                 "001A00190000019F0000A8C040FDFD9ED6795000010000000000000000"
143         val bytes = HexDump.hexStringToByteArray(advertiseHex)
144         // The unsupported option will be skipped normally and won't throw ParseException.
145         val packet = Dhcp6Packet.decode(bytes, bytes.size)
146         assertTrue(packet is Dhcp6AdvertisePacket)
147     }
148 
149     @Test
testDecodeDhcp6ReplyPacketnull150     fun testDecodeDhcp6ReplyPacket() {
151         val replyHex =
152             // Reply, Transaction ID
153             "07000A47" +
154             // server identifier option(option_len=10)
155             "0002000A0003000186C9B26AED4D" +
156             // client identifier option(option_len=12)
157             "0001000C0003001B02FBBAFFFEB7BC71" +
158             // SOL_MAX_RT (don't support this option yet)
159             "005200040000003c" +
160             // Rapid Commit
161             "000e0000" +
162             // DNS recursive server (don't support this opton yet)
163             "00170010fdfd9ed6795000000000000000000001" +
164             // IA_PD option(option_len=70, including IA prefix option)
165             "0019004629cc56c7000000d300000152" +
166             // IA prefix option(option_len=25, prefix="2401:fa00:49c:412::/64", preferred=400,
167             // valid=1623)
168             "001a00190000019000000657402401fa00049c04120000000000000000" +
169             // IA prefix option(option_len=25, prefix="fdfd:9ed6:7950:2::/64", preferred=423,
170             // valid=43200)
171             "001a0019000001a70000a8c040fdfd9ed6795000020000000000000000"
172         val bytes = HexDump.hexStringToByteArray(replyHex)
173         val packet = Dhcp6Packet.decode(bytes, bytes.size)
174         assertTrue(packet is Dhcp6ReplyPacket)
175         assertEquals(400, packet.prefixDelegation.minimalPreferredLifetime)
176         assertEquals(1623, packet.prefixDelegation.minimalValidLifetime)
177     }
178 
179     @Test
testGetMinimalPreferredValidLifetimenull180     fun testGetMinimalPreferredValidLifetime() {
181         val replyHex =
182             // Reply, Transaction ID
183             "07000A47" +
184             // server identifier option(option_len=10)
185             "0002000A0003000186C9B26AED4D" +
186             // client identifier option(option_len=12)
187             "0001000C0003001B02FBBAFFFEB7BC71" +
188             // SOL_MAX_RT (don't support this option yet)
189             "005200040000003c" +
190             // Rapid Commit
191             "000e0000" +
192             // DNS recursive server (don't support this opton yet)
193             "00170010fdfd9ed6795000000000000000000001" +
194             // IA_PD option(option_len=70, including IA prefix option)
195             "0019004629cc56c7000000d300000152" +
196             // IA prefix option(option_len=25, prefix="2401:fa00:49c:412::/64", preferred=0,
197             // valid=0)
198             "001a00190000000000000000402401fa00049c04120000000000000000" +
199             // IA prefix option(option_len=25, prefix="fdfd:9ed6:7950:2::/64", preferred=423,
200             // valid=43200)
201             "001a0019000001a70000a8c040fdfd9ed6795000020000000000000000"
202         val bytes = HexDump.hexStringToByteArray(replyHex)
203         val packet = Dhcp6Packet.decode(bytes, bytes.size)
204         assertTrue(packet is Dhcp6ReplyPacket)
205         assertEquals(423, packet.prefixDelegation.minimalPreferredLifetime)
206         assertEquals(43200, packet.prefixDelegation.minimalValidLifetime)
207     }
208 
209     @Test
testStatusCodeOptionWithStatusMessagenull210     fun testStatusCodeOptionWithStatusMessage() {
211         val replyHex =
212             // Reply, Transaction ID
213             "07000A47" +
214             // server identifier option(option_len=10)
215             "0002000A0003000186C9B26AED4D" +
216             // client identifier option(option_len=12)
217             "0001000C0003001B02FBBAFFFEB7BC71" +
218             // SOL_MAX_RT (don't support this option yet)
219             "005200040000003c" +
220             // Rapid Commit
221             "000e0000" +
222             // DNS recursive server (don't support this opton yet)
223             "00170010fdfd9ed6795000000000000000000001" +
224             // IA_PD option (t1=t2=0, empty prefix)
225             "0019000c000000000000000000000000" +
226             // Status code option: status code=NoPrefixAvail
227             "000d00150006" +
228             // Status code option: status message="no prefix available"
229             "6e6f2070726566697820617661696c61626c65"
230         val bytes = HexDump.hexStringToByteArray(replyHex)
231         val packet = Dhcp6Packet.decode(bytes, bytes.size)
232         assertTrue(packet is Dhcp6ReplyPacket)
233         assertEquals(0, packet.mPrefixDelegation.iaid)
234         assertEquals(0, packet.mPrefixDelegation.t1)
235         assertEquals(0, packet.mPrefixDelegation.t2)
236         assertEquals(Dhcp6Packet.STATUS_NO_PREFIX_AVAIL, packet.mStatusCode)
237     }
238 
239     @Test
testStatusCodeOptionWithoutStatusMessagenull240     fun testStatusCodeOptionWithoutStatusMessage() {
241         val replyHex =
242             // Reply, Transaction ID
243             "07000A47" +
244             // server identifier option(option_len=10)
245             "0002000A0003000186C9B26AED4D" +
246             // client identifier option(option_len=12)
247             "0001000C0003001B02FBBAFFFEB7BC71" +
248             // SOL_MAX_RT (don't support this option yet)
249             "005200040000003c" +
250             // Rapid Commit
251             "000e0000" +
252             // DNS recursive server (don't support this opton yet)
253             "00170010fdfd9ed6795000000000000000000001" +
254             // IA_PD option (t1=t2=0, empty prefix)
255             "0019000c000000000000000000000000" +
256             // Status code option: status code=NoPrefixAvail
257             "000d00020006"
258         val bytes = HexDump.hexStringToByteArray(replyHex)
259         val packet = Dhcp6Packet.decode(bytes, bytes.size)
260         assertTrue(packet is Dhcp6ReplyPacket)
261         assertEquals(0, packet.mPrefixDelegation.iaid)
262         assertEquals(0, packet.mPrefixDelegation.t1)
263         assertEquals(0, packet.mPrefixDelegation.t2)
264         assertEquals(Dhcp6Packet.STATUS_NO_PREFIX_AVAIL, packet.mStatusCode)
265     }
266 
267     @Test
testStatusCodeOptionInIaPdWithStatusMessagenull268     fun testStatusCodeOptionInIaPdWithStatusMessage() {
269         val replyHex =
270             // Reply, Transaction ID
271             "07000A47" +
272             // server identifier option(option_len=10)
273             "0002000A0003000186C9B26AED4D" +
274             // client identifier option(option_len=12)
275             "0001000C0003001B02FBBAFFFEB7BC71" +
276             // SOL_MAX_RT (don't support this option yet)
277             "005200040000003c" +
278             // Rapid Commit
279             "000e0000" +
280             // DNS recursive server (don't support this opton yet)
281             "00170010fdfd9ed6795000000000000000000001" +
282             // IA_PD option (t1=t2=0, status code=NoPrefixAvail,
283             // status message="no prefix available")
284             "00190025000000000000000000000000000d00150006" +
285             "6e6f2070726566697820617661696c61626c65"
286         val bytes = HexDump.hexStringToByteArray(replyHex)
287         val packet = Dhcp6Packet.decode(bytes, bytes.size)
288         assertTrue(packet is Dhcp6ReplyPacket)
289         assertEquals(0, packet.mPrefixDelegation.iaid)
290         assertEquals(0, packet.mPrefixDelegation.t1)
291         assertEquals(0, packet.mPrefixDelegation.t2)
292         assertEquals(Dhcp6Packet.STATUS_NO_PREFIX_AVAIL, packet.mPrefixDelegation.statusCode)
293     }
294 
295     @Test
testStatusCodeOptionInIaPdWithoutStatusMessagenull296     fun testStatusCodeOptionInIaPdWithoutStatusMessage() {
297         val replyHex =
298             // Reply, Transaction ID
299             "07000A47" +
300             // server identifier option(option_len=10)
301             "0002000A0003000186C9B26AED4D" +
302             // client identifier option(option_len=12)
303             "0001000C0003001B02FBBAFFFEB7BC71" +
304             // SOL_MAX_RT (don't support this option yet)
305             "005200040000003c" +
306             // Rapid Commit
307             "000e0000" +
308             // DNS recursive server (don't support this opton yet)
309             "00170010fdfd9ed6795000000000000000000001" +
310             // IA_PD option (t1=t2=0, status code=NoPrefixAvail)
311             "00190012000000000000000000000000000d00020006"
312         val bytes = HexDump.hexStringToByteArray(replyHex)
313         val packet = Dhcp6Packet.decode(bytes, bytes.size)
314         assertTrue(packet is Dhcp6ReplyPacket)
315         assertEquals(0, packet.mPrefixDelegation.iaid)
316         assertEquals(0, packet.mPrefixDelegation.t1)
317         assertEquals(0, packet.mPrefixDelegation.t2)
318         assertEquals(Dhcp6Packet.STATUS_NO_PREFIX_AVAIL, packet.mPrefixDelegation.statusCode)
319     }
320 
321     @Test
testStatusCodeOptionWithTruncatedStatusMessagenull322     fun testStatusCodeOptionWithTruncatedStatusMessage() {
323         val replyHex =
324             // Reply, Transaction ID
325             "07000A47" +
326             // server identifier option(option_len=10)
327             "0002000A0003000186C9B26AED4D" +
328             // client identifier option(option_len=12)
329             "0001000C0003001B02FBBAFFFEB7BC71" +
330             // SOL_MAX_RT (don't support this option yet)
331             "005200040000003c" +
332             // Rapid Commit
333             "000e0000" +
334             // DNS recursive server (don't support this opton yet)
335             "00170010fdfd9ed6795000000000000000000001" +
336             // Status code option: len=21, status code=NoPrefixAvail
337             "000d00150006" +
338             // Status code option: truncated status message="no prefix available"
339             "6e6f2070726566697820617661696c6162"
340         val bytes = HexDump.hexStringToByteArray(replyHex)
341         assertThrows(Dhcp6Packet.ParseException::class.java) {
342             Dhcp6Packet.decode(bytes, bytes.size)
343         }
344     }
345 
346     @Test
testStatusCodeOptionInIaPdWithTruncatedStatusMessagenull347     fun testStatusCodeOptionInIaPdWithTruncatedStatusMessage() {
348         val replyHex =
349             // Reply, Transaction ID
350             "07000A47" +
351             // server identifier option(option_len=10)
352             "0002000A0003000186C9B26AED4D" +
353             // client identifier option(option_len=12)
354             "0001000C0003001B02FBBAFFFEB7BC71" +
355             // SOL_MAX_RT (don't support this option yet)
356             "005200040000003c" +
357             // Rapid Commit
358             "000e0000" +
359             // DNS recursive server (don't support this opton yet)
360             "00170010fdfd9ed6795000000000000000000001" +
361             // IA_PD option (t1=t2=0, empty prefix)
362             "00190025000000000000000000000000" +
363             // Status code option: len=21, status code=NoPrefixAvail
364             "000d00150006" +
365             // truncated status message="no prefix available")
366             "6e6f2070726566697820617661696c6162"
367         val bytes = HexDump.hexStringToByteArray(replyHex)
368         assertThrows(Dhcp6Packet.ParseException::class.java) {
369             Dhcp6Packet.decode(bytes, bytes.size)
370         }
371     }
372 }
373