1 /*
2  * Copyright (c) 2022 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/ztest.h>
8 #include <zephyr/net_buf.h>
9 #include <zephyr/mgmt/mcumgr/mgmt/mgmt.h>
10 #include <zephyr/mgmt/mcumgr/transport/smp_dummy.h>
11 #include <zephyr/mgmt/mcumgr/grp/os_mgmt/os_mgmt.h>
12 #include <zcbor_common.h>
13 #include <zcbor_decode.h>
14 #include <zcbor_encode.h>
15 #include <mgmt/mcumgr/util/zcbor_bulk.h>
16 #include <string.h>
17 #include <smp_internal.h>
18 #include "smp_test_util.h"
19 
20 #include <zephyr/logging/log.h>
21 LOG_MODULE_REGISTER(mcumgr_fs_grp, 4);
22 
23 enum {
24 	LEGACY_VERSION = 0,
25 	CURRENT_VERSION,
26 	FUTURE_VERSION,
27 };
28 
29 #define SMP_RESPONSE_WAIT_TIME 3
30 #define QUERY_BUFFER_SIZE 16
31 #define ZCBOR_BUFFER_SIZE 256
32 #define OUTPUT_BUFFER_SIZE 256
33 #define ZCBOR_HISTORY_ARRAY_SIZE 4
34 
35 #ifdef CONFIG_MCUMGR_SMP_SUPPORT_ORIGINAL_PROTOCOL
36 /* Response to legacy packet */
37 static const uint8_t response_old[] = {
38 	0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x01, 0x07,
39 	0xbf, 0x62, 0x72, 0x63, 0x03, 0xff
40 };
41 #else
42 /* Response if a legacy packet is sent and server does not support it */
43 static const uint8_t response_old[] = {
44 	0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x01, 0x07,
45 	0xbf, 0x62, 0x72, 0x63, 0x0c, 0xff
46 };
47 #endif
48 
49 /* Response to current packet */
50 static const uint8_t response_current[] = {
51 	0x09, 0x00, 0x00, 0x13, 0x00, 0x00, 0x01, 0x07,
52 	0xbf, 0x63, 0x65, 0x72, 0x72, 0xbf, 0x65, 0x67,
53 	0x72, 0x6f, 0x75, 0x70, 0x00, 0x62, 0x72, 0x63,
54 	0x02, 0xff, 0xff
55 };
56 
57 /* Response if an invalid (too high) version packet is sent and server does not support it */
58 static const uint8_t response_new[] = {
59 	0x09, 0x00, 0x00, 0x06, 0x00, 0x00, 0x01, 0x07,
60 	0xbf, 0x62, 0x72, 0x63, 0x0d, 0xff
61 };
62 
63 static struct net_buf *nb;
64 
65 const uint8_t query_fake[] = "8";
66 
67 struct group_error {
68 	uint16_t group;
69 	uint16_t rc;
70 	bool found;
71 };
72 
mcumgr_ret_decode(zcbor_state_t * state,struct group_error * result)73 static bool mcumgr_ret_decode(zcbor_state_t *state, struct group_error *result)
74 {
75 	bool ok;
76 	size_t decoded;
77 	uint32_t tmp_group;
78 	uint32_t tmp_rc;
79 
80 	struct zcbor_map_decode_key_val output_decode[] = {
81 		ZCBOR_MAP_DECODE_KEY_DECODER("group", zcbor_uint32_decode, &tmp_group),
82 		ZCBOR_MAP_DECODE_KEY_DECODER("rc", zcbor_uint32_decode, &tmp_rc),
83 	};
84 
85 	result->found = false;
86 
87 	ok = zcbor_map_decode_bulk(state, output_decode, ARRAY_SIZE(output_decode), &decoded) == 0;
88 
89 	if (ok &&
90 	    zcbor_map_decode_bulk_key_found(output_decode, ARRAY_SIZE(output_decode), "group") &&
91 	    zcbor_map_decode_bulk_key_found(output_decode, ARRAY_SIZE(output_decode), "rc")) {
92 		result->group = (uint16_t)tmp_group;
93 		result->rc = (uint16_t)tmp_rc;
94 		result->found = true;
95 	}
96 
97 	return ok;
98 }
99 
ZTEST(smp_version,test_legacy_command)100 ZTEST(smp_version, test_legacy_command)
101 {
102 	uint8_t buffer[ZCBOR_BUFFER_SIZE];
103 	uint8_t buffer_out[OUTPUT_BUFFER_SIZE];
104 	bool ok;
105 	uint16_t buffer_size;
106 	zcbor_state_t zse[ZCBOR_HISTORY_ARRAY_SIZE] = { 0 };
107 	zcbor_state_t zsd[ZCBOR_HISTORY_ARRAY_SIZE] = { 0 };
108 	bool received;
109 	struct zcbor_string output = { 0 };
110 	size_t decoded = 0;
111 	struct smp_hdr *smp_header;
112 	int32_t rc;
113 	struct group_error group_error;
114 
115 	struct zcbor_map_decode_key_val output_decode[] = {
116 		ZCBOR_MAP_DECODE_KEY_DECODER("output", zcbor_tstr_decode, &output),
117 		ZCBOR_MAP_DECODE_KEY_DECODER("rc", zcbor_int32_decode, &rc),
118 		ZCBOR_MAP_DECODE_KEY_DECODER("err", mcumgr_ret_decode, &group_error),
119 	};
120 
121 	memset(buffer, 0, sizeof(buffer));
122 	memset(buffer_out, 0, sizeof(buffer_out));
123 	buffer_size = 0;
124 	memset(zse, 0, sizeof(zse));
125 	memset(zsd, 0, sizeof(zsd));
126 
127 	zcbor_new_encode_state(zse, 2, buffer, ARRAY_SIZE(buffer), 0);
128 
129 	ok = create_mcumgr_format_packet(zse, query_fake, buffer, buffer_out, &buffer_size,
130 					 LEGACY_VERSION);
131 	zassert_true(ok, "Expected packet creation to be successful");
132 
133 	/* Enable dummy SMP backend and ready for usage */
134 	smp_dummy_enable();
135 	smp_dummy_clear_state();
136 
137 	/* Send query command to dummy SMP backend */
138 	(void)smp_dummy_tx_pkt(buffer_out, buffer_size);
139 	smp_dummy_add_data();
140 
141 	/* For a short duration to see if response has been received */
142 	received = smp_dummy_wait_for_data(SMP_RESPONSE_WAIT_TIME);
143 	zassert_true(received, "Expected to receive data but timed out");
144 
145 	/* Retrieve response buffer and ensure validity */
146 	nb = smp_dummy_get_outgoing();
147 	smp_dummy_disable();
148 
149 	/* Check that the received response matches the expected response */
150 	zassert_equal(sizeof(response_old), nb->len, "Expected received data length mismatch");
151 	zassert_mem_equal(response_old, nb->data, nb->len, "Expected received data mismatch");
152 
153 	/* Process received data by removing header */
154 	smp_header = net_buf_pull_mem(nb, sizeof(struct smp_hdr));
155 	zassert_equal(smp_header->nh_version, LEGACY_VERSION,
156 		      "Expected response header version mismatch");
157 
158 	zcbor_new_decode_state(zsd, 4, nb->data, nb->len, 1, NULL, 0);
159 	ok = zcbor_map_decode_bulk(zsd, output_decode, ARRAY_SIZE(output_decode), &decoded) == 0;
160 
161 	zassert_true(ok, "Expected decode to be successful");
162 	zassert_equal(decoded, 1, "Expected to receive 1 decoded zcbor element");
163 
164 	zassert_equal(
165 		zcbor_map_decode_bulk_key_found(output_decode, ARRAY_SIZE(output_decode),
166 						"output"), 0,
167 		"Did not expect to get output in response");
168 
169 	zassert_equal(
170 		zcbor_map_decode_bulk_key_found(output_decode, ARRAY_SIZE(output_decode),
171 						"rc"), 1,
172 		"Expected to get rc in response");
173 
174 	zassert_equal(
175 		zcbor_map_decode_bulk_key_found(output_decode, ARRAY_SIZE(output_decode),
176 						"err"), 0,
177 		"Did not expect to get ret in response");
178 
179 #ifdef CONFIG_MCUMGR_SMP_SUPPORT_ORIGINAL_PROTOCOL
180 	zassert_equal(rc, MGMT_ERR_EINVAL, "Expected to get MGMT_ERR_EINVAL error");
181 #else
182 	zassert_equal(rc, MGMT_ERR_UNSUPPORTED_TOO_OLD,
183 		      "Expected to get MGMT_ERR_UNSUPPORTED_TOO_OLD error");
184 #endif
185 }
186 
ZTEST(smp_version,test_current_command)187 ZTEST(smp_version, test_current_command)
188 {
189 	uint8_t buffer[ZCBOR_BUFFER_SIZE];
190 	uint8_t buffer_out[OUTPUT_BUFFER_SIZE];
191 	bool ok;
192 	uint16_t buffer_size;
193 	zcbor_state_t zse[ZCBOR_HISTORY_ARRAY_SIZE] = { 0 };
194 	zcbor_state_t zsd[ZCBOR_HISTORY_ARRAY_SIZE] = { 0 };
195 	bool received;
196 	struct zcbor_string output = { 0 };
197 	size_t decoded = 0;
198 	struct smp_hdr *smp_header;
199 	int32_t rc;
200 	struct group_error group_error;
201 
202 	struct zcbor_map_decode_key_val output_decode[] = {
203 		ZCBOR_MAP_DECODE_KEY_DECODER("output", zcbor_tstr_decode, &output),
204 		ZCBOR_MAP_DECODE_KEY_DECODER("rc", zcbor_int32_decode, &rc),
205 		ZCBOR_MAP_DECODE_KEY_DECODER("err", mcumgr_ret_decode, &group_error),
206 	};
207 
208 	memset(buffer, 0, sizeof(buffer));
209 	memset(buffer_out, 0, sizeof(buffer_out));
210 	buffer_size = 0;
211 	memset(zse, 0, sizeof(zse));
212 	memset(zsd, 0, sizeof(zsd));
213 
214 	zcbor_new_encode_state(zse, 2, buffer, ARRAY_SIZE(buffer), 0);
215 
216 	ok = create_mcumgr_format_packet(zse, query_fake, buffer, buffer_out, &buffer_size,
217 					 CURRENT_VERSION);
218 	zassert_true(ok, "Expected packet creation to be successful");
219 
220 	/* Enable dummy SMP backend and ready for usage */
221 	smp_dummy_enable();
222 	smp_dummy_clear_state();
223 
224 	/* Send query command to dummy SMP backend */
225 	(void)smp_dummy_tx_pkt(buffer_out, buffer_size);
226 	smp_dummy_add_data();
227 
228 	/* For a short duration to see if response has been received */
229 	received = smp_dummy_wait_for_data(SMP_RESPONSE_WAIT_TIME);
230 	zassert_true(received, "Expected to receive data but timed out");
231 
232 	/* Retrieve response buffer and ensure validity */
233 	nb = smp_dummy_get_outgoing();
234 	smp_dummy_disable();
235 
236 	/* Check that the received response matches the expected response */
237 	zassert_equal(sizeof(response_current), nb->len, "Expected received data length mismatch");
238 	zassert_mem_equal(response_current, nb->data, nb->len, "Expected received data mismatch");
239 
240 	/* Process received data by removing header */
241 	smp_header = net_buf_pull_mem(nb, sizeof(struct smp_hdr));
242 	zassert_equal(smp_header->nh_version, CURRENT_VERSION,
243 		      "Expected response header version mismatch");
244 
245 	zcbor_new_decode_state(zsd, 4, nb->data, nb->len, 1, NULL, 0);
246 	ok = zcbor_map_decode_bulk(zsd, output_decode, ARRAY_SIZE(output_decode), &decoded) == 0;
247 
248 	zassert_true(ok, "Expected decode to be successful");
249 	zassert_equal(decoded, 1, "Expected to receive 1 decoded zcbor element");
250 
251 	zassert_equal(
252 		zcbor_map_decode_bulk_key_found(output_decode, ARRAY_SIZE(output_decode),
253 						"output"), 0,
254 		"Did not expect to get output in response");
255 
256 	zassert_equal(
257 		zcbor_map_decode_bulk_key_found(output_decode, ARRAY_SIZE(output_decode),
258 						"rc"), 0,
259 		"Did not expect to get rc in response");
260 
261 	zassert_equal(
262 		zcbor_map_decode_bulk_key_found(output_decode, ARRAY_SIZE(output_decode),
263 						"err"), 1,
264 		"Expected to get ret in response");
265 
266 	zassert_true(group_error.found, "Expected both group and rc in ret to be found");
267 	zassert_equal(group_error.group, MGMT_GROUP_ID_OS,
268 		      "Expected to get MGMT_GROUP_ID_OS for ret group");
269 	zassert_equal(group_error.rc, OS_MGMT_ERR_INVALID_FORMAT,
270 		      "Expected to get OS_MGMT_ERR_INVALID_FORMAT for ret rc");
271 }
272 
ZTEST(smp_version,test_new_command)273 ZTEST(smp_version, test_new_command)
274 {
275 	uint8_t buffer[ZCBOR_BUFFER_SIZE];
276 	uint8_t buffer_out[OUTPUT_BUFFER_SIZE];
277 	bool ok;
278 	uint16_t buffer_size;
279 	zcbor_state_t zse[ZCBOR_HISTORY_ARRAY_SIZE] = { 0 };
280 	zcbor_state_t zsd[ZCBOR_HISTORY_ARRAY_SIZE] = { 0 };
281 	bool received;
282 	struct zcbor_string output = { 0 };
283 	size_t decoded = 0;
284 	struct smp_hdr *smp_header;
285 	int32_t rc;
286 	struct group_error group_error;
287 
288 	struct zcbor_map_decode_key_val output_decode[] = {
289 		ZCBOR_MAP_DECODE_KEY_DECODER("output", zcbor_tstr_decode, &output),
290 		ZCBOR_MAP_DECODE_KEY_DECODER("rc", zcbor_int32_decode, &rc),
291 		ZCBOR_MAP_DECODE_KEY_DECODER("err", mcumgr_ret_decode, &group_error),
292 	};
293 
294 	memset(buffer, 0, sizeof(buffer));
295 	memset(buffer_out, 0, sizeof(buffer_out));
296 	buffer_size = 0;
297 	memset(zse, 0, sizeof(zse));
298 	memset(zsd, 0, sizeof(zsd));
299 
300 	zcbor_new_encode_state(zse, 2, buffer, ARRAY_SIZE(buffer), 0);
301 
302 	ok = create_mcumgr_format_packet(zse, query_fake, buffer, buffer_out, &buffer_size,
303 					 FUTURE_VERSION);
304 	zassert_true(ok, "Expected packet creation to be successful");
305 
306 	/* Enable dummy SMP backend and ready for usage */
307 	smp_dummy_enable();
308 	smp_dummy_clear_state();
309 
310 	/* Send query command to dummy SMP backend */
311 	(void)smp_dummy_tx_pkt(buffer_out, buffer_size);
312 	smp_dummy_add_data();
313 
314 	/* For a short duration to see if response has been received */
315 	received = smp_dummy_wait_for_data(SMP_RESPONSE_WAIT_TIME);
316 	zassert_true(received, "Expected to receive data but timed out");
317 
318 	/* Retrieve response buffer and ensure validity */
319 	nb = smp_dummy_get_outgoing();
320 	smp_dummy_disable();
321 
322 	/* Check that the received response matches the expected response */
323 	zassert_equal(sizeof(response_new), nb->len, "Expected received data length mismatch");
324 	zassert_mem_equal(response_new, nb->data, nb->len, "Expected received data mismatch");
325 
326 	/* Process received data by removing header */
327 	smp_header = net_buf_pull_mem(nb, sizeof(struct smp_hdr));
328 	zassert_equal(smp_header->nh_version, CURRENT_VERSION,
329 		      "Expected response header version mismatch");
330 
331 	zcbor_new_decode_state(zsd, 4, nb->data, nb->len, 1, NULL, 0);
332 	ok = zcbor_map_decode_bulk(zsd, output_decode, ARRAY_SIZE(output_decode), &decoded) == 0;
333 
334 	zassert_true(ok, "Expected decode to be successful");
335 	zassert_equal(decoded, 1, "Expected to receive 1 decoded zcbor element");
336 
337 	zassert_equal(
338 		zcbor_map_decode_bulk_key_found(output_decode, ARRAY_SIZE(output_decode),
339 						"output"), 0,
340 		"Did not expect to get output in response");
341 
342 	zassert_equal(
343 		zcbor_map_decode_bulk_key_found(output_decode, ARRAY_SIZE(output_decode),
344 						"rc"), 1,
345 		"Expected to get rc in response");
346 
347 	zassert_equal(
348 		zcbor_map_decode_bulk_key_found(output_decode, ARRAY_SIZE(output_decode),
349 						"err"), 0,
350 		"Did not expect to get ret in response");
351 
352 	zassert_equal(rc, MGMT_ERR_UNSUPPORTED_TOO_NEW,
353 		      "Expected to get MGMT_ERR_UNSUPPORTED_TOO_NEW error");
354 }
355 
cleanup_test(void * p)356 static void cleanup_test(void *p)
357 {
358 	if (nb != NULL) {
359 		net_buf_unref(nb);
360 		nb = NULL;
361 	}
362 }
363 
364 /* Main test set */
365 ZTEST_SUITE(smp_version, NULL, NULL, NULL, cleanup_test, NULL);
366