1 /*
2  * Copyright (c) 2023 Gardena GmbH
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include "lwm2m_engine.h"
8 #include "lwm2m_object.h"
9 
10 #include <zephyr/ztest.h>
11 
12 /* Declaration of 'private' function */
13 int prepare_msg_for_send(struct lwm2m_message *msg);
14 int build_msg_block_for_send(struct lwm2m_message *msg, uint16_t block_num,
15 			     enum coap_block_size block_size);
16 int request_output_block_ctx(struct coap_block_context **ctx);
17 void release_output_block_ctx(struct coap_block_context ** const ctx);
18 
19 BUILD_ASSERT(IS_ENABLED(CONFIG_LWM2M_COAP_BLOCK_TRANSFER),
20 	     "These tests expect to have block transfer enabled.");
21 
22 #define EXPECTED_LWM2M_COAP_FULL_BUFFER_SIZE 256
23 BUILD_ASSERT(CONFIG_LWM2M_COAP_ENCODE_BUFFER_SIZE == EXPECTED_LWM2M_COAP_FULL_BUFFER_SIZE,
24 	     "The expected max message size is wrong.");
25 
26 #define EXPECTED_NUM_OUTPUT_BLOCK_CONTEXT 3
27 BUILD_ASSERT(NUM_OUTPUT_BLOCK_CONTEXT == EXPECTED_NUM_OUTPUT_BLOCK_CONTEXT,
28 	     "The expected number of output block contexts is wrong.");
29 
30 #define EXPECTED_DEFAULT_HEADER_OFFSET 4
31 
32 struct net_block_transfer_fixture {
33 	uint8_t dummy_msg[CONFIG_LWM2M_COAP_ENCODE_BUFFER_SIZE];
34 	struct lwm2m_ctx ctx;
35 	struct lwm2m_message msg;
36 };
37 
net_block_transfer_setup(void)38 static void *net_block_transfer_setup(void)
39 {
40 	static struct net_block_transfer_fixture f;
41 
42 	for (uint_fast16_t i = 0; i < ARRAY_SIZE(f.dummy_msg); ++i) {
43 		f.dummy_msg[i] = (uint8_t)i;
44 	}
45 
46 	return &f;
47 }
48 
net_block_transfer_before(void * f)49 static void net_block_transfer_before(void *f)
50 {
51 	struct net_block_transfer_fixture *fixture = (struct net_block_transfer_fixture *)f;
52 
53 	memset(&fixture->ctx, 0, sizeof(struct lwm2m_ctx));
54 	memset(&fixture->msg, 0, sizeof(struct lwm2m_message));
55 	fixture->msg.ctx = &fixture->ctx;
56 }
57 
net_block_transfer_after(void * f)58 static void net_block_transfer_after(void *f)
59 {
60 	struct net_block_transfer_fixture *fixture = (struct net_block_transfer_fixture *)f;
61 
62 	lwm2m_reset_message(&fixture->msg, true);
63 }
64 
ZTEST_F(net_block_transfer,test_init_message_use_big_buffer)65 ZTEST_F(net_block_transfer, test_init_message_use_big_buffer)
66 {
67 	int ret;
68 	struct lwm2m_message *msg = &fixture->msg;
69 
70 
71 	ret = lwm2m_init_message(msg);
72 	zassert_ok(ret, "Failed to initialize lwm2m message");
73 
74 	zassert_not_equal(msg->msg_data, msg->cpkt.data,
75 			  "Default data buffer should not be used for writing body");
76 	zassert_equal(msg->cpkt.data, msg->body_encode_buffer.data,
77 			  "Full body buffer should be in use");
78 
79 	zassert_equal(EXPECTED_LWM2M_COAP_FULL_BUFFER_SIZE, msg->cpkt.max_len,
80 		      "Max length for the package is wrong");
81 
82 	zassert_equal(EXPECTED_DEFAULT_HEADER_OFFSET, msg->cpkt.offset);
83 
84 	/* write to buffer in a similar way as the writers */
85 	msg->out.out_cpkt = &msg->cpkt;
86 	ret = buf_append(CPKT_BUF_WRITE(msg->out.out_cpkt), fixture->dummy_msg,
87 			 EXPECTED_LWM2M_COAP_FULL_BUFFER_SIZE - EXPECTED_DEFAULT_HEADER_OFFSET);
88 	zassert_ok(ret, "Should be able to write to buffer");
89 	zassert_equal(msg->cpkt.max_len, msg->cpkt.offset, "Buffer should be full");
90 
91 	const uint8_t one_byte = 0xAB;
92 
93 	ret = buf_append(CPKT_BUF_WRITE(msg->out.out_cpkt), &one_byte, 1);
94 	zassert_equal(ret, -ENOMEM, "Should not be able to write to full buffer");
95 }
96 
97 #define EXPECTED_HEADERS_LEN 7
ZTEST_F(net_block_transfer,test_one_block_with_big_buffer)98 ZTEST_F(net_block_transfer, test_one_block_with_big_buffer)
99 {
100 	int ret;
101 	struct lwm2m_message *msg = &fixture->msg;
102 
103 	/*  Arrange */
104 	ret = lwm2m_init_message(msg);
105 	zassert_equal(0, ret, "Failed to initialize lwm2m message");
106 
107 	zassert_not_equal(msg->msg_data, msg->cpkt.data,
108 			  "Big body data buffer should be used for writing body");
109 	zassert_equal(msg->cpkt.data, msg->body_encode_buffer.data,
110 		      "Full body buffer should be in use");
111 
112 	ret = coap_append_option_int(&msg->cpkt, COAP_OPTION_CONTENT_FORMAT,
113 				     COAP_CONTENT_FORMAT_APP_LINK_FORMAT);
114 	zassert_equal(0, ret, "Not able to append option");
115 
116 	ret = coap_packet_append_payload_marker(&msg->cpkt);
117 	zassert_equal(0, ret, "Not able to append payload marker");
118 
119 	ret = buf_append(CPKT_BUF_WRITE(&msg->cpkt), fixture->dummy_msg,
120 			 CONFIG_LWM2M_COAP_BLOCK_SIZE);
121 	zassert_ok(ret, "Should be able to write to buffer");
122 
123 	uint16_t payload_len;
124 
125 	coap_packet_get_payload(&msg->cpkt, &payload_len);
126 	zassert_equal(payload_len, CONFIG_LWM2M_COAP_BLOCK_SIZE,
127 		      "Block was not filled as expected");
128 
129 	/*  Act */
130 	ret = prepare_msg_for_send(msg);
131 	zassert_equal(0, ret, "Preparing message for sending failed");
132 
133 	/*  Assert */
134 	zassert_equal(msg->msg_data, msg->cpkt.data,
135 		      "Default data buffer should be used for sending the block");
136 	zassert_is_null(msg->body_encode_buffer.data, "Complete body buffer should not be set");
137 
138 	const uint8_t *payload = coap_packet_get_payload(&msg->cpkt, &payload_len);
139 
140 	zassert_not_null(payload, "Payload expected");
141 	zassert_equal(payload_len, CONFIG_LWM2M_COAP_BLOCK_SIZE,
142 		      "Block was not filled as expected");
143 
144 	zassert_equal(EXPECTED_HEADERS_LEN, msg->cpkt.hdr_len + msg->cpkt.opt_len + 1,
145 		      "Headers length not as expected");
146 	/* payload should start after headers, options and payload marker (size: 1) */
147 	zassert_equal(payload, msg->cpkt.data + EXPECTED_HEADERS_LEN,
148 		      "Payload not starting at expected address");
149 
150 	const uint8_t expected_headers[EXPECTED_HEADERS_LEN] = {0x40, 0, 0, 0, 0xc1, 0x28, 0xff};
151 
152 	zassert_mem_equal(msg->cpkt.data, expected_headers, EXPECTED_HEADERS_LEN,
153 			  "Payload not starting at expected address");
154 
155 	/* check payload */
156 	for (uint_fast8_t i = 0; i < payload_len; ++i) {
157 		zassert_equal(payload[i], i, "Byte %i in payload is wrong", i);
158 	}
159 
160 	/* check first byte after payload */
161 	zassert_equal(payload[payload_len], 0x00, "Byte after payload is wrong");
162 }
163 
ZTEST_F(net_block_transfer,test_build_first_block_for_send)164 ZTEST_F(net_block_transfer, test_build_first_block_for_send)
165 {
166 	int ret;
167 	struct lwm2m_message *msg = &fixture->msg;
168 	uint16_t payload_len;
169 
170 	/*  Arrange */
171 	msg->code = COAP_METHOD_GET;
172 	ret = lwm2m_init_message(msg);
173 	zassert_equal(0, ret, "Failed to initialize lwm2m message");
174 
175 	ret = coap_append_option_int(&msg->cpkt, COAP_OPTION_CONTENT_FORMAT,
176 				     COAP_CONTENT_FORMAT_APP_LINK_FORMAT);
177 	zassert_equal(0, ret, "Not able to append option");
178 
179 	ret = coap_append_option_int(&msg->cpkt, COAP_OPTION_ACCEPT, COAP_CONTENT_FORMAT_APP_JSON);
180 	zassert_equal(0, ret, "Not able to append option");
181 
182 	const uint8_t expected_header_len = 4;
183 
184 	zassert_equal(msg->cpkt.hdr_len, expected_header_len, "Header length not as expected");
185 
186 	const uint8_t expected_options_len = 4;
187 
188 	zassert_equal(msg->cpkt.opt_len, expected_options_len, "Options length not as expected");
189 
190 	ret = coap_packet_append_payload_marker(&msg->cpkt);
191 	zassert_equal(0, ret, "Not able to append payload marker");
192 
193 	ret = buf_append(CPKT_BUF_WRITE(&msg->cpkt), fixture->dummy_msg,
194 			 2 * CONFIG_LWM2M_COAP_BLOCK_SIZE);
195 	zassert_ok(ret, "Should be able to write to buffer");
196 
197 	zassert_not_equal(msg->msg_data, msg->cpkt.data, "Buffer for block data is not yet in use");
198 
199 	/*  Act */
200 	ret = prepare_msg_for_send(msg);
201 	zassert_equal(ret, 0, "Could not create first block");
202 
203 	/*  Assert */
204 	zassert_equal(msg->msg_data, msg->cpkt.data, "Buffer for block data is not in use");
205 
206 	ret = coap_get_option_int(&msg->cpkt, COAP_OPTION_BLOCK1);
207 	zassert(ret > 0, "block 1 option not set");
208 
209 	const uint8_t *payload = coap_packet_get_payload(&msg->cpkt, &payload_len);
210 
211 	zassert_not_null(payload, "Payload expected");
212 	zassert_equal(payload_len, CONFIG_LWM2M_COAP_BLOCK_SIZE, "Wrong payload size");
213 	zassert_equal(0x00, payload[0], "First byte in payload wrong");
214 	zassert_equal(0x3f, payload[CONFIG_LWM2M_COAP_BLOCK_SIZE - 1],
215 		      "Last byte in payload wrong");
216 }
217 
ZTEST_F(net_block_transfer,test_build_blocks_for_send_exactly_2_blocks)218 ZTEST_F(net_block_transfer, test_build_blocks_for_send_exactly_2_blocks)
219 {
220 	int ret;
221 	struct lwm2m_message *msg = &fixture->msg;
222 	uint16_t payload_len;
223 	const uint8_t *payload;
224 
225 	/*  Arrange */
226 	msg->code = COAP_METHOD_PUT;
227 	ret = lwm2m_init_message(msg);
228 	zassert_equal(0, ret, "Failed to initialize lwm2m message");
229 
230 	const uint8_t *query = "query";
231 
232 	ret = coap_packet_append_option(&msg->cpkt, COAP_OPTION_URI_QUERY, query, strlen(query));
233 	zassert_equal(0, ret, "Not able to append option");
234 
235 	ret = coap_append_option_int(&msg->cpkt, COAP_OPTION_ACCEPT,
236 				     COAP_CONTENT_FORMAT_TEXT_PLAIN);
237 	zassert_equal(0, ret, "Not able to append option");
238 
239 	const uint8_t expected_header_len = 4;
240 
241 	zassert_equal(msg->cpkt.hdr_len, expected_header_len, "Header length not as expected");
242 
243 	const uint8_t expected_options_len = 8;
244 
245 	zassert_equal(msg->cpkt.opt_len, expected_options_len, "Options length not as expected");
246 
247 	ret = coap_packet_append_payload_marker(&msg->cpkt);
248 	zassert_equal(0, ret, "Not able to append payload marker");
249 
250 	ret = buf_append(CPKT_BUF_WRITE(&msg->cpkt), fixture->dummy_msg,
251 			 2 * CONFIG_LWM2M_COAP_BLOCK_SIZE);
252 	zassert_ok(ret, "Should be able to write to buffer");
253 
254 	zassert_not_equal(msg->msg_data, msg->cpkt.data, "Buffer for block data is not yet in use");
255 
256 	/*  block 0 */
257 	ret = prepare_msg_for_send(msg);
258 	zassert_equal(ret, 0, "Could not create first block");
259 
260 	zassert_equal(msg->msg_data, msg->cpkt.data, "Buffer for block data is not in use");
261 
262 	ret = coap_get_option_int(&msg->cpkt, COAP_OPTION_BLOCK1);
263 	zassert(ret > 0, "block 1 option not set");
264 
265 	payload = coap_packet_get_payload(&msg->cpkt, &payload_len);
266 
267 	zassert_not_null(payload, "Payload expected");
268 	zassert_equal(payload_len, CONFIG_LWM2M_COAP_BLOCK_SIZE, "Wrong payload size");
269 	zassert_equal(0x00, payload[0], "First byte in payload wrong");
270 	zassert_equal(0x3f, payload[CONFIG_LWM2M_COAP_BLOCK_SIZE - 1],
271 		      "Last byte in payload wrong");
272 
273 	/* block 1 */
274 	ret = build_msg_block_for_send(msg, 1, COAP_BLOCK_64);
275 	zassert_equal(ret, 0, "Could not create second block");
276 
277 	ret = coap_get_option_int(&msg->cpkt, COAP_OPTION_BLOCK1);
278 	zassert(ret > 0, "block 1 option not set");
279 
280 	payload = coap_packet_get_payload(&msg->cpkt, &payload_len);
281 
282 	zassert_not_null(payload, "Payload expected");
283 	zassert_equal(payload_len, CONFIG_LWM2M_COAP_BLOCK_SIZE, "Wrong payload size");
284 	zassert_equal(0x40, payload[0], "First byte in payload wrong");
285 	zassert_equal(0x7f, payload[CONFIG_LWM2M_COAP_BLOCK_SIZE - 1],
286 		      "Last byte in payload wrong");
287 
288 	/* block 2 doesn't exist */
289 	ret = build_msg_block_for_send(msg, 2, COAP_BLOCK_64);
290 	zassert_equal(ret, -EINVAL, "Could not create second block");
291 }
292 
ZTEST_F(net_block_transfer,test_build_blocks_for_send_more_than_2_blocks)293 ZTEST_F(net_block_transfer, test_build_blocks_for_send_more_than_2_blocks)
294 {
295 	int ret;
296 	struct lwm2m_message *msg = &fixture->msg;
297 	uint16_t payload_len;
298 	const uint8_t *payload;
299 
300 	/*  Arrange */
301 	msg->code = COAP_METHOD_DELETE;
302 	ret = lwm2m_init_message(msg);
303 	zassert_equal(0, ret, "Failed to initialize lwm2m message");
304 
305 	const uint8_t *proxy_scheme = "coap";
306 
307 	ret = coap_packet_append_option(&msg->cpkt, COAP_OPTION_PROXY_SCHEME, proxy_scheme,
308 					strlen(proxy_scheme));
309 	zassert_equal(0, ret, "Not able to append option");
310 
311 	ret = coap_append_option_int(&msg->cpkt, COAP_OPTION_CONTENT_FORMAT,
312 				     COAP_CONTENT_FORMAT_APP_JSON);
313 	zassert_equal(0, ret, "Not able to append option");
314 
315 	const uint8_t expected_header_len = 4;
316 
317 	zassert_equal(msg->cpkt.hdr_len, expected_header_len, "Header length not as expected");
318 
319 	const uint8_t expected_options_len = 8;
320 
321 	zassert_equal(msg->cpkt.opt_len, expected_options_len, "Options length not as expected");
322 
323 	ret = coap_packet_append_payload_marker(&msg->cpkt);
324 	zassert_equal(0, ret, "Not able to append payload marker");
325 
326 	ret = buf_append(CPKT_BUF_WRITE(&msg->cpkt), fixture->dummy_msg,
327 			 2 * CONFIG_LWM2M_COAP_BLOCK_SIZE + 1);
328 	zassert_ok(ret, "Should be able to write to buffer");
329 
330 	zassert_not_equal(msg->msg_data, msg->cpkt.data, "Buffer for block data is not yet in use");
331 
332 	/*  block 0 */
333 	ret = prepare_msg_for_send(msg);
334 	zassert_equal(ret, 0, "Could not create first block");
335 
336 	zassert_equal(msg->msg_data, msg->cpkt.data, "Buffer for block data is not in use");
337 
338 	ret = coap_get_option_int(&msg->cpkt, COAP_OPTION_BLOCK1);
339 	zassert(ret > 0, "block 1 option not set");
340 
341 	payload = coap_packet_get_payload(&msg->cpkt, &payload_len);
342 
343 	zassert_not_null(payload, "Payload expected");
344 	zassert_equal(payload_len, CONFIG_LWM2M_COAP_BLOCK_SIZE, "Wrong payload size");
345 	zassert_equal(0x00, payload[0], "First byte in payload wrong");
346 	zassert_equal(0x3f, payload[CONFIG_LWM2M_COAP_BLOCK_SIZE - 1],
347 		      "Last byte in payload wrong");
348 
349 	/* block 1 */
350 	ret = build_msg_block_for_send(msg, 1, COAP_BLOCK_64);
351 	zassert_equal(ret, 0, "Could not create second block");
352 
353 	ret = coap_get_option_int(&msg->cpkt, COAP_OPTION_BLOCK1);
354 	zassert(ret > 0, "block 1 option not set");
355 
356 	payload = coap_packet_get_payload(&msg->cpkt, &payload_len);
357 
358 	zassert_not_null(payload, "Payload expected");
359 	zassert_equal(payload_len, CONFIG_LWM2M_COAP_BLOCK_SIZE, "Wrong payload size");
360 	zassert_equal(0x40, payload[0], "First byte in payload wrong");
361 	zassert_equal(0x7f, payload[CONFIG_LWM2M_COAP_BLOCK_SIZE - 1],
362 		      "Last byte in payload wrong");
363 
364 	/* block 2 */
365 	ret = build_msg_block_for_send(msg, 2, COAP_BLOCK_64);
366 	zassert_equal(ret, 0, "Could not create second block");
367 
368 	ret = coap_get_option_int(&msg->cpkt, COAP_OPTION_BLOCK1);
369 	zassert(ret > 0, "block 1 option not set");
370 
371 	payload = coap_packet_get_payload(&msg->cpkt, &payload_len);
372 
373 	zassert_not_null(payload, "Payload expected");
374 	zassert_equal(payload_len, 1, "Wrong payload size");
375 	zassert_equal(0x80, payload[0], "First (and only) byte in payload wrong");
376 
377 	/* block 3 doesn't exist */
378 	ret = build_msg_block_for_send(msg, 3, COAP_BLOCK_64);
379 	zassert_equal(ret, -EINVAL, "Could not create second block");
380 }
381 
ZTEST_F(net_block_transfer,test_block_context)382 ZTEST_F(net_block_transfer, test_block_context)
383 {
384 	struct coap_block_context *ctx0, *ctx1, *ctx2, *ctx3, *ctx4;
385 	int ret;
386 
387 	zassert_equal(NUM_OUTPUT_BLOCK_CONTEXT, 3);
388 
389 	/* block context 0 */
390 	ret = request_output_block_ctx(&ctx0);
391 	zassert_ok(ret);
392 	zassert_not_null(ctx0);
393 	/* block context 1 */
394 	ret = request_output_block_ctx(&ctx1);
395 	zassert_ok(ret);
396 	zassert_not_null(ctx1);
397 	/* block context 2 */
398 	ret = request_output_block_ctx(&ctx2);
399 	zassert_ok(ret);
400 	zassert_not_null(ctx2);
401 
402 	/* Get one context more than available */
403 	ret = request_output_block_ctx(&ctx3);
404 	zassert_equal(ret, -ENOMEM);
405 	zassert_is_null(ctx3);
406 
407 	/* release one block context */
408 	release_output_block_ctx(&ctx2);
409 	zassert_is_null(ctx2);
410 
411 	/* get another block context */
412 	ret = request_output_block_ctx(&ctx4);
413 	zassert_ok(ret);
414 	zassert_not_null(ctx4);
415 
416 	/* release all block contexts */
417 	release_output_block_ctx(&ctx0);
418 	zassert_is_null(ctx0);
419 	release_output_block_ctx(&ctx1);
420 	zassert_is_null(ctx1);
421 	release_output_block_ctx(&ctx2);
422 	zassert_is_null(ctx2);
423 	release_output_block_ctx(&ctx3);
424 	zassert_is_null(ctx3);
425 	release_output_block_ctx(&ctx4);
426 	zassert_is_null(ctx4);
427 }
428 
429 ZTEST_SUITE(net_block_transfer, NULL, net_block_transfer_setup, net_block_transfer_before,
430 	    net_block_transfer_after, NULL);
431