1 /*
2  *  Copyright (c) 2017, The OpenThread Authors.
3  *  All rights reserved.
4  *
5  *  Redistribution and use in source and binary forms, with or without
6  *  modification, are permitted provided that the following conditions are met:
7  *  1. Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  *  2. Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *  3. Neither the name of the copyright holder nor the
13  *     names of its contributors may be used to endorse or promote products
14  *     derived from this software without specific prior written permission.
15  *
16  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  *  POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include "common/code_utils.hpp"
30 #include "instance/instance.hpp"
31 #include "lib/spinel/spinel_encoder.hpp"
32 
33 #include "test_util.hpp"
34 
35 namespace ot {
36 namespace Spinel {
37 
38 enum
39 {
40     kTestBufferSize = 800,
41 };
42 
ReadFrame(Spinel::Buffer & aNcpBuffer,uint8_t * aFrame,uint16_t & aFrameLen)43 otError ReadFrame(Spinel::Buffer &aNcpBuffer, uint8_t *aFrame, uint16_t &aFrameLen)
44 {
45     otError error = OT_ERROR_NONE;
46 
47     SuccessOrExit(error = aNcpBuffer.OutFrameBegin());
48     aFrameLen = aNcpBuffer.OutFrameGetLength();
49     VerifyOrExit(aNcpBuffer.OutFrameRead(aFrameLen, aFrame) == aFrameLen, error = OT_ERROR_FAILED);
50     SuccessOrExit(error = aNcpBuffer.OutFrameRemove());
51 
52 exit:
53     return error;
54 }
55 
TestEncoder(void)56 void TestEncoder(void)
57 {
58     uint8_t         buffer[kTestBufferSize];
59     Spinel::Buffer  ncpBuffer(buffer, kTestBufferSize);
60     Spinel::Encoder encoder(ncpBuffer);
61 
62     uint8_t        frame[kTestBufferSize];
63     uint16_t       frameLen;
64     spinel_ssize_t parsedLen;
65 
66     const bool         kBool_1 = true;
67     const bool         kBool_2 = false;
68     const uint8_t      kUint8  = 0x42;
69     const int8_t       kInt8   = -73;
70     const uint16_t     kUint16 = 0xabcd;
71     const int16_t      kInt16  = -567;
72     const uint32_t     kUint32 = 0xdeadbeef;
73     const int32_t      kInt32  = -123455678L;
74     const uint64_t     kUint64 = 0xfe10dc32ba549876ULL;
75     const int64_t      kInt64  = -9197712039090021561LL;
76     const unsigned int kUint_1 = 9;
77     const unsigned int kUint_2 = 0xa3;
78     const unsigned int kUint_3 = 0x8765;
79     const unsigned int kUint_4 = SPINEL_MAX_UINT_PACKED - 1;
80 
81     const spinel_ipv6addr_t kIp6Addr = {
82         {0x6B, 0x41, 0x65, 0x73, 0x42, 0x68, 0x61, 0x76, 0x54, 0x61, 0x72, 0x7A, 0x49, 0x69, 0x61, 0x4E}};
83 
84     const spinel_eui48_t kEui48 = {
85         {4, 8, 15, 16, 23, 42} // "Lost" EUI48!
86     };
87 
88     const spinel_eui64_t kEui64 = {
89         {2, 3, 5, 7, 11, 13, 17, 19}, // "Prime" EUI64!
90     };
91 
92     const char kString_1[] = "OpenThread";
93     const char kString_2[] = "";
94 
95     const uint16_t kData[] = {10, 20, 3, 15, 1000, 60, 16}; // ... then comes 17,18,19,20  :)
96 
97     bool               b_1, b_2;
98     uint8_t            u8;
99     int8_t             i8;
100     uint16_t           u16;
101     int16_t            i16;
102     uint32_t           u32;
103     int32_t            i32;
104     uint64_t           u64;
105     int64_t            i64;
106     unsigned int       u_1, u_2, u_3, u_4;
107     spinel_ipv6addr_t *ip6Addr;
108     spinel_eui48_t    *eui48;
109     spinel_eui64_t    *eui64;
110     const char        *utf_1;
111     const char        *utf_2;
112     const uint8_t     *dataPtr;
113     spinel_size_t      dataLen;
114 
115     memset(buffer, 0, sizeof(buffer));
116 
117     printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
118     printf("\nTest 1: Encoding of simple types");
119 
120     SuccessOrQuit(encoder.BeginFrame(Spinel::Buffer::kPriorityLow));
121     SuccessOrQuit(encoder.WriteBool(kBool_1));
122     SuccessOrQuit(encoder.WriteBool(kBool_2));
123     SuccessOrQuit(encoder.WriteUint8(kUint8));
124     SuccessOrQuit(encoder.WriteInt8(kInt8));
125     SuccessOrQuit(encoder.WriteUint16(kUint16));
126     SuccessOrQuit(encoder.WriteInt16(kInt16));
127     SuccessOrQuit(encoder.WriteUint32(kUint32));
128     SuccessOrQuit(encoder.WriteInt32(kInt32));
129     SuccessOrQuit(encoder.WriteUint64(kUint64));
130     SuccessOrQuit(encoder.WriteInt64(kInt64));
131     SuccessOrQuit(encoder.WriteUintPacked(kUint_1));
132     SuccessOrQuit(encoder.WriteUintPacked(kUint_2));
133     SuccessOrQuit(encoder.WriteUintPacked(kUint_3));
134     SuccessOrQuit(encoder.WriteUintPacked(kUint_4));
135     SuccessOrQuit(encoder.WriteIp6Address(kIp6Addr));
136     SuccessOrQuit(encoder.WriteEui48(kEui48));
137     SuccessOrQuit(encoder.WriteEui64(kEui64));
138     SuccessOrQuit(encoder.WriteUtf8(kString_1));
139     SuccessOrQuit(encoder.WriteUtf8(kString_2));
140     SuccessOrQuit(encoder.WriteData((const uint8_t *)kData, sizeof(kData)));
141     SuccessOrQuit(encoder.EndFrame());
142 
143     DumpBuffer("Buffer", buffer, 256);
144     SuccessOrQuit(ReadFrame(ncpBuffer, frame, frameLen));
145     DumpBuffer("Frame", frame, frameLen);
146 
147     parsedLen = spinel_datatype_unpack(
148         frame, static_cast<spinel_size_t>(frameLen),
149         (SPINEL_DATATYPE_BOOL_S SPINEL_DATATYPE_BOOL_S SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_INT8_S
150              SPINEL_DATATYPE_UINT16_S SPINEL_DATATYPE_INT16_S SPINEL_DATATYPE_UINT32_S SPINEL_DATATYPE_INT32_S
151                  SPINEL_DATATYPE_UINT64_S SPINEL_DATATYPE_INT64_S SPINEL_DATATYPE_UINT_PACKED_S
152                      SPINEL_DATATYPE_UINT_PACKED_S SPINEL_DATATYPE_UINT_PACKED_S SPINEL_DATATYPE_UINT_PACKED_S
153                          SPINEL_DATATYPE_IPv6ADDR_S SPINEL_DATATYPE_EUI48_S SPINEL_DATATYPE_EUI64_S
154                              SPINEL_DATATYPE_UTF8_S SPINEL_DATATYPE_UTF8_S SPINEL_DATATYPE_DATA_S),
155         &b_1, &b_2, &u8, &i8, &u16, &i16, &u32, &i32, &u64, &i64, &u_1, &u_2, &u_3, &u_4, &ip6Addr, &eui48, &eui64,
156         &utf_1, &utf_2, &dataPtr, &dataLen);
157 
158     VerifyOrQuit(parsedLen == frameLen);
159     VerifyOrQuit(b_1 == kBool_1);
160     VerifyOrQuit(b_2 == kBool_2);
161     VerifyOrQuit(u8 == kUint8);
162     VerifyOrQuit(i8 == kInt8);
163     VerifyOrQuit(u16 == kUint16);
164     VerifyOrQuit(i16 == kInt16);
165     VerifyOrQuit(u32 == kUint32);
166     VerifyOrQuit(i32 == kInt32);
167     VerifyOrQuit(u64 == kUint64);
168     VerifyOrQuit(i64 == kInt64);
169     VerifyOrQuit(u_1 == kUint_1);
170     VerifyOrQuit(u_2 == kUint_2);
171     VerifyOrQuit(u_3 == kUint_3);
172     VerifyOrQuit(u_4 == kUint_4);
173     VerifyOrQuit(memcmp(ip6Addr, &kIp6Addr, sizeof(spinel_ipv6addr_t)) == 0);
174     VerifyOrQuit(memcmp(eui48, &kEui48, sizeof(spinel_eui48_t)) == 0);
175     VerifyOrQuit(memcmp(eui64, &kEui64, sizeof(spinel_eui64_t)) == 0);
176     VerifyOrQuit(memcmp(utf_1, kString_1, sizeof(kString_1)) == 0);
177     VerifyOrQuit(memcmp(utf_2, kString_2, sizeof(kString_2)) == 0);
178     VerifyOrQuit(dataLen == sizeof(kData));
179     VerifyOrQuit(memcmp(dataPtr, &kData, sizeof(kData)) == 0);
180 
181     printf(" -- PASS\n");
182 
183     printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
184     printf("\nTest 2: Test a single simple struct.");
185 
186     SuccessOrQuit(encoder.BeginFrame(Spinel::Buffer::kPriorityLow));
187     SuccessOrQuit(encoder.WriteUint8(kUint8));
188     SuccessOrQuit(encoder.OpenStruct());
189     {
190         SuccessOrQuit(encoder.WriteUint32(kUint32));
191         SuccessOrQuit(encoder.WriteEui48(kEui48));
192         SuccessOrQuit(encoder.WriteUintPacked(kUint_3));
193     }
194     SuccessOrQuit(encoder.CloseStruct());
195     SuccessOrQuit(encoder.WriteInt16(kInt16));
196     SuccessOrQuit(encoder.EndFrame());
197 
198     DumpBuffer("Buffer", buffer, 256);
199     SuccessOrQuit(ReadFrame(ncpBuffer, frame, frameLen));
200     DumpBuffer("Frame", frame, frameLen);
201 
202     parsedLen = spinel_datatype_unpack(
203         frame, static_cast<spinel_size_t>(frameLen),
204         (SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_STRUCT_S(
205             SPINEL_DATATYPE_UINT32_S SPINEL_DATATYPE_EUI48_S SPINEL_DATATYPE_UINT_PACKED_S) SPINEL_DATATYPE_INT16_S
206 
207          ),
208         &u8, &u32, &eui48, &u_3, &i16);
209 
210     VerifyOrQuit(parsedLen == frameLen);
211     VerifyOrQuit(u8 == kUint8);
212     VerifyOrQuit(i16 == kInt16);
213     VerifyOrQuit(u32 == kUint32);
214     VerifyOrQuit(u_3 == kUint_3);
215     VerifyOrQuit(memcmp(eui48, &kEui48, sizeof(spinel_eui48_t)) == 0);
216 
217     // Parse the struct as a "data with len".
218     parsedLen = spinel_datatype_unpack(frame, static_cast<spinel_size_t>(frameLen),
219                                        (SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_DATA_WLEN_S SPINEL_DATATYPE_INT16_S
220 
221                                         ),
222                                        &u8, &dataPtr, &dataLen, &i16);
223 
224     VerifyOrQuit(parsedLen == frameLen);
225     VerifyOrQuit(u8 == kUint8);
226     VerifyOrQuit(i16 == kInt16);
227 
228     printf(" -- PASS\n");
229 
230     printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
231     printf("\nTest 3: Test multiple structs and struct within struct.");
232 
233     SuccessOrQuit(encoder.BeginFrame(Spinel::Buffer::kPriorityLow));
234     SuccessOrQuit(encoder.OpenStruct());
235     {
236         SuccessOrQuit(encoder.WriteUint8(kUint8));
237         SuccessOrQuit(encoder.WriteUtf8(kString_1));
238         SuccessOrQuit(encoder.OpenStruct());
239         {
240             SuccessOrQuit(encoder.WriteBool(kBool_1));
241             SuccessOrQuit(encoder.WriteIp6Address(kIp6Addr));
242         }
243         SuccessOrQuit(encoder.CloseStruct());
244         SuccessOrQuit(encoder.WriteUint16(kUint16));
245     }
246     SuccessOrQuit(encoder.CloseStruct());
247     SuccessOrQuit(encoder.WriteEui48(kEui48));
248     SuccessOrQuit(encoder.OpenStruct());
249     {
250         SuccessOrQuit(encoder.WriteUint32(kUint32));
251     }
252     SuccessOrQuit(encoder.CloseStruct());
253     SuccessOrQuit(encoder.WriteInt32(kInt32));
254     SuccessOrQuit(encoder.EndFrame());
255 
256     DumpBuffer("Buffer", buffer, 256 + 100);
257 
258     SuccessOrQuit(ReadFrame(ncpBuffer, frame, frameLen));
259 
260     parsedLen = spinel_datatype_unpack(
261         frame, static_cast<spinel_size_t>(frameLen),
262         (SPINEL_DATATYPE_STRUCT_S(SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_UTF8_S SPINEL_DATATYPE_STRUCT_S(
263             SPINEL_DATATYPE_BOOL_S SPINEL_DATATYPE_IPv6ADDR_S) SPINEL_DATATYPE_UINT16_S)
264              SPINEL_DATATYPE_EUI48_S SPINEL_DATATYPE_STRUCT_S(SPINEL_DATATYPE_UINT32_S) SPINEL_DATATYPE_INT32_S),
265         &u8, &utf_1, &b_1, &ip6Addr, &u16, &eui48, &u32, &i32);
266 
267     VerifyOrQuit(parsedLen == frameLen);
268     VerifyOrQuit(b_1 == kBool_1);
269     VerifyOrQuit(u8 == kUint8);
270     VerifyOrQuit(u16 == kUint16);
271     VerifyOrQuit(u32 == kUint32);
272     VerifyOrQuit(i32 == kInt32);
273     VerifyOrQuit(memcmp(ip6Addr, &kIp6Addr, sizeof(spinel_ipv6addr_t)) == 0);
274     VerifyOrQuit(memcmp(eui48, &kEui48, sizeof(spinel_eui48_t)) == 0);
275     VerifyOrQuit(memcmp(utf_1, kString_1, sizeof(kString_1)) == 0);
276 
277     printf(" -- PASS\n");
278 
279     printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
280     printf("\nTest 4: Test unclosed struct.");
281 
282     SuccessOrQuit(encoder.BeginFrame(Spinel::Buffer::kPriorityLow));
283     SuccessOrQuit(encoder.WriteUint8(kUint8));
284     SuccessOrQuit(encoder.OpenStruct());
285     {
286         SuccessOrQuit(encoder.WriteUint32(kUint32));
287         SuccessOrQuit(encoder.OpenStruct());
288         {
289             SuccessOrQuit(encoder.WriteEui48(kEui48));
290             SuccessOrQuit(encoder.WriteUintPacked(kUint_3));
291             // Do not close the structs expecting `EndFrame()` to close them.
292         }
293     }
294     SuccessOrQuit(encoder.EndFrame());
295 
296     SuccessOrQuit(ReadFrame(ncpBuffer, frame, frameLen));
297 
298     parsedLen = spinel_datatype_unpack(
299         frame, static_cast<spinel_size_t>(frameLen),
300         (SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_STRUCT_S(
301             SPINEL_DATATYPE_UINT32_S SPINEL_DATATYPE_STRUCT_S(SPINEL_DATATYPE_EUI48_S SPINEL_DATATYPE_UINT_PACKED_S))),
302         &u8, &u32, &eui48, &u_3);
303 
304     VerifyOrQuit(parsedLen == frameLen);
305     VerifyOrQuit(u8 == kUint8);
306     VerifyOrQuit(u32 == kUint32);
307     VerifyOrQuit(u_3 == kUint_3);
308     VerifyOrQuit(memcmp(eui48, &kEui48, sizeof(spinel_eui48_t)) == 0);
309 
310     printf(" -- PASS\n");
311 
312     printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
313     printf("\nTest 5: Test saving position and resetting back to a saved position");
314 
315     SuccessOrQuit(encoder.BeginFrame(Spinel::Buffer::kPriorityLow));
316     SuccessOrQuit(encoder.WriteUint8(kUint8));
317     SuccessOrQuit(encoder.OpenStruct());
318     {
319         SuccessOrQuit(encoder.WriteUint32(kUint32));
320 
321         // Save position in middle a first open struct.
322         SuccessOrQuit(encoder.SavePosition());
323         SuccessOrQuit(encoder.OpenStruct());
324         {
325             SuccessOrQuit(encoder.WriteEui48(kEui48));
326             SuccessOrQuit(encoder.WriteUintPacked(kUint_3));
327         }
328 
329         // Reset to saved position in middle of the second open struct which should be discarded.
330 
331         SuccessOrQuit(encoder.ResetToSaved());
332 
333         SuccessOrQuit(encoder.WriteIp6Address(kIp6Addr));
334         SuccessOrQuit(encoder.WriteEui64(kEui64));
335     }
336     SuccessOrQuit(encoder.CloseStruct());
337     SuccessOrQuit(encoder.WriteUtf8(kString_1));
338     SuccessOrQuit(encoder.EndFrame());
339 
340     SuccessOrQuit(ReadFrame(ncpBuffer, frame, frameLen));
341 
342     parsedLen = spinel_datatype_unpack(
343         frame, static_cast<spinel_size_t>(frameLen),
344         (SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_STRUCT_S(
345             SPINEL_DATATYPE_UINT32_S SPINEL_DATATYPE_IPv6ADDR_S SPINEL_DATATYPE_EUI64_S) SPINEL_DATATYPE_UTF8_S),
346         &u8, &u32, &ip6Addr, &eui64, &utf_1);
347 
348     VerifyOrQuit(parsedLen == frameLen);
349 
350     VerifyOrQuit(u8 == kUint8);
351     VerifyOrQuit(u32 == kUint32);
352     VerifyOrQuit(i32 == kInt32);
353     VerifyOrQuit(memcmp(ip6Addr, &kIp6Addr, sizeof(spinel_ipv6addr_t)) == 0);
354     VerifyOrQuit(memcmp(eui64, &kEui64, sizeof(spinel_eui64_t)) == 0);
355     VerifyOrQuit(memcmp(utf_1, kString_1, sizeof(kString_1)) == 0);
356 
357     printf(" -- PASS\n");
358 }
359 
360 } // namespace Spinel
361 } // namespace ot
362 
main(void)363 int main(void)
364 {
365     ot::Spinel::TestEncoder();
366     printf("\nAll tests passed.\n");
367     return 0;
368 }
369