1 /*
2 * Copyright (c) 2022 René Beckmann
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/tc_util.h>
8 #include <mqtt_sn_msg.h>
9 #include <zephyr/net/mqtt_sn.h>
10 #include <zephyr/sys/util.h> /* for ARRAY_SIZE */
11 #include <zephyr/ztest.h>
12
13 #include <zephyr/logging/log.h>
14 LOG_MODULE_REGISTER(test);
15
16 #define CLIENTID MQTT_UTF8_LITERAL("zephyr")
17 #define BUFFER_SIZE 128
18
19 struct mqtt_sn_decode_test {
20 uint8_t *data;
21 size_t datasz;
22 char *name;
23 struct mqtt_sn_param expected;
24 };
25
26 #define MQTT_SN_DECODE_TEST(dataset) \
27 .data = dataset, .datasz = sizeof(dataset), .name = STRINGIFY(dataset)
28
29 /* advertise gw id 42, duration 0xDEAD */
30 static uint8_t advertise[] = { 5, 0x00, 42, 0xDE, 0xAD };
31 /* searchgw with radius 1 */
32 static uint8_t searchgw[] = { 3, 0x01, 1 };
33 /* gwinfo gw id 42, address 127.0.0.1 */
34 static uint8_t gwinfo[] = { 7, 0x02, 42, 127, 0, 0, 1 };
35 /* connect with flags [will, clean_session], duration 300, client id "zephyrclient" */
36 static uint8_t connect[] = { 18, 0x04, 0x0C, 0x01, 1, 44, 'z', 'e', 'p',
37 'h', 'y', 'r', 'c', 'l', 'i', 'e', 'n', 't' };
38 /* connack with return code accepted */
39 static uint8_t connack1[] = { 3, 0x05, 0x00 };
40 /* connack with extended length field and return code rejected - invalid topic id */
41 static uint8_t connack2[] = { 0x01, 0, 5, 0x05, 0x02 };
42 /* empty message */
43 static uint8_t willtopicreq[] = { 2, 0x06 };
44 /* willtopic with flags [qos 1, retain], topic "/zephyr" */
45 static uint8_t willtopic[] = { 10, 0x07, 0x30, '/', 'z', 'e', 'p', 'h', 'y', 'r' };
46 /* empty message */
47 static uint8_t willmsgreq[] = { 2, 0x08 };
48 /* willmsg with msg "mywill" */
49 static uint8_t willmsg[] = { 8, 0x09, 'm', 'y', 'w', 'i', 'l', 'l' };
50 /* registration with topic ID 0x1A1B, msg ID 0x1C1D, topic "/zephyr" */
51 static uint8_t reg[] = { 13, 0x0A, 0x1A, 0x1B, 0x1C, 0x1D, '/', 'z', 'e', 'p', 'h', 'y', 'r' };
52 /* registration with topic ID 0x0000, msg ID 0x1C1D, topic "/zephyr" */
53 static uint8_t reg_client[] = {
54 13, 0x0A, 0x00, 0x00, 0x1C, 0x1D, '/', 'z', 'e', 'p', 'h', 'y', 'r'
55 };
56 /* registration ack with topic ID 0x1A1B, msg ID 0x1C1D, return code accepted */
57 static uint8_t regack[] = { 7, 0x0B, 0x1A, 0x1B, 0x1C, 0x1D, 0 };
58 /* publish message with flags [DUP, QOS2, Retain, Short Topic Type], topic ID "RB", msg ID 0x1C1D,
59 * data "zephyr"
60 */
61 static uint8_t publish[] = { 13, 0x0C, 0xD2, 'R', 'B', 0x1C, 0x1D, 'z', 'e', 'p', 'h', 'y', 'r' };
62 /* publish ack with topic ID 0x1A1B, msg ID 0x1C1D, return code rejected: not supported */
63 static uint8_t puback[] = { 7, 0x0D, 0x1A, 0x1B, 0x1C, 0x1D, 0x03 };
64 /* pubrec */
65 static uint8_t pubrec[] = { 4, 0x0F, 0xBE, 0xEF };
66 /* pubrel */
67 static uint8_t pubrel[] = { 4, 0x10, 0xBE, 0xEF };
68 /* pubcomp */
69 static uint8_t pubcomp[] = { 4, 0x0E, 0xBE, 0xEF };
70 /* subscribe with flags [DUP, QOS0, topic name], message ID 0x1C1D, for topic "/zephyr" */
71 static uint8_t subscribe[] = { 12, 0x12, 0x80, 0x1C, 0x1D, '/', 'z', 'e', 'p', 'h', 'y', 'r' };
72 /* subscribe ack with flags [QOS-1], topic ID 0x1A1B, msg ID 0x1909,
73 * return code rejected - congested
74 */
75 static uint8_t suback[] = { 8, 0x13, 0x60, 0x1A, 0x1B, 0x19, 0x09, 0x01 };
76 /* unsubscribe with flags [predefined topic ID], message ID 0x1C1D, for topic 0x1234 */
77 static uint8_t unsubscribe[] = { 7, 0x14, 0x01, 0x1C, 0x1D, 0x12, 0x34 };
78 /* unsubscribe ack msg ID 0x1337 */
79 static uint8_t unsuback[] = { 4, 0x15, 0x13, 0x37 };
80 /* pingreq from client "zephyrclient" */
81 static uint8_t pingreq[] = { 14, 0x16, 'z', 'e', 'p', 'h', 'y', 'r', 'c', 'l', 'i', 'e', 'n', 't' };
82 /* pingreq - empty */
83 static uint8_t pingreq1[] = { 2, 0x16 };
84 /* pingresp */
85 static uint8_t pingresp[] = { 2, 0x17 };
86 /* disconnect by client with duration 10000 */
87 static uint8_t disconnect[] = { 4, 0x18, 0x27, 0x10 };
88 /* empty disconnect by GW */
89 static uint8_t disconnect_gw[] = { 2, 0x18 };
90 /* willtopicupd with flags [QOS0, retain], topic "/zephyr" */
91 static uint8_t willtopicupd[] = { 10, 0x1A, 0x10, '/', 'z', 'e', 'p', 'h', 'y', 'r' };
92 /* willmsgupd with message "mywill" */
93 static uint8_t willmsgupd[] = { 8, 0x1C, 'm', 'y', 'w', 'i', 'l', 'l' };
94 /* willtopicresp */
95 static uint8_t willtopicresp[] = { 3, 0x1B, 0 };
96 /* willmsgresp */
97 static uint8_t willmsgresp[] = { 3, 0x1D, 0 };
98
99 static struct mqtt_sn_decode_test decode_tests[] = {
100 {
101 MQTT_SN_DECODE_TEST(advertise),
102 .expected = (struct mqtt_sn_param){
103 .type = MQTT_SN_MSG_TYPE_ADVERTISE,
104 .params.advertise = {
105 .gw_id = 42,
106 .duration = 0xDEAD
107 }
108 }
109 },
110 {
111 MQTT_SN_DECODE_TEST(gwinfo),
112 .expected = (struct mqtt_sn_param){
113 .type = MQTT_SN_MSG_TYPE_GWINFO,
114 .params.gwinfo = {
115 .gw_id = 42,
116 .gw_add = {
117 .data = gwinfo+3,
118 .size = 4
119 }
120 }
121 }
122 },
123 {
124 MQTT_SN_DECODE_TEST(connack1),
125 .expected = (struct mqtt_sn_param){
126 .type = MQTT_SN_MSG_TYPE_CONNACK,
127 .params.connack = {
128 .ret_code = MQTT_SN_CODE_ACCEPTED
129 }
130 }
131 },
132 {
133 MQTT_SN_DECODE_TEST(connack2),
134 .expected = (struct mqtt_sn_param){
135 .type = MQTT_SN_MSG_TYPE_CONNACK,
136 .params.connack = {
137 .ret_code = MQTT_SN_CODE_REJECTED_TOPIC_ID
138 }
139 }
140 },
141 {
142 MQTT_SN_DECODE_TEST(willtopicreq),
143 .expected = (struct mqtt_sn_param){
144 .type = MQTT_SN_MSG_TYPE_WILLTOPICREQ
145 }
146 },
147 {
148 MQTT_SN_DECODE_TEST(willmsgreq),
149 .expected = (struct mqtt_sn_param){
150 .type = MQTT_SN_MSG_TYPE_WILLMSGREQ
151 }
152 },
153 {
154 MQTT_SN_DECODE_TEST(reg),
155 .expected = (struct mqtt_sn_param){
156 .type = MQTT_SN_MSG_TYPE_REGISTER,
157 .params.reg = {
158 .topic_id = 0x1A1B,
159 .msg_id = 0x1C1D,
160 .topic = {
161 .data = reg + 6,
162 .size = 7
163 }
164 }
165 }
166 },
167 {
168 MQTT_SN_DECODE_TEST(regack),
169 .expected = (struct mqtt_sn_param){
170 .type = MQTT_SN_MSG_TYPE_REGACK,
171 .params.regack = {
172 .topic_id = 0x1A1B,
173 .msg_id = 0x1C1D,
174 .ret_code = MQTT_SN_CODE_ACCEPTED
175 }
176 }
177 },
178 {
179 MQTT_SN_DECODE_TEST(publish),
180 .expected = (struct mqtt_sn_param){
181 .type = MQTT_SN_MSG_TYPE_PUBLISH,
182 .params.publish = {
183 .dup = true,
184 .qos = MQTT_SN_QOS_2,
185 .retain = true,
186 .topic_type = MQTT_SN_TOPIC_TYPE_SHORT,
187 .topic_id = 0x5242, /* "RB" in hex */
188 .msg_id = 0x1C1D,
189 .data = {
190 .data = publish + 7,
191 .size = 6
192 }
193 }
194 }
195 },
196 {
197 MQTT_SN_DECODE_TEST(puback),
198 .expected = (struct mqtt_sn_param){
199 .type = MQTT_SN_MSG_TYPE_PUBACK,
200 .params.puback = {
201 .topic_id = 0x1A1B,
202 .msg_id = 0x1C1D,
203 .ret_code = MQTT_SN_CODE_REJECTED_NOTSUP
204 }
205 }
206 },
207 {
208 MQTT_SN_DECODE_TEST(pubrec),
209 .expected = (struct mqtt_sn_param){
210 .type = MQTT_SN_MSG_TYPE_PUBREC,
211 .params.puback = {
212 .msg_id = 0xBEEF,
213 }
214 }
215 },
216 {
217 MQTT_SN_DECODE_TEST(pubrel),
218 .expected = (struct mqtt_sn_param){
219 .type = MQTT_SN_MSG_TYPE_PUBREL,
220 .params.pubrel = {
221 .msg_id = 0xBEEF,
222 }
223 }
224 },
225 {
226 MQTT_SN_DECODE_TEST(pubcomp),
227 .expected = (struct mqtt_sn_param){
228 .type = MQTT_SN_MSG_TYPE_PUBCOMP,
229 .params.pubcomp = {
230 .msg_id = 0xBEEF,
231 }
232 }
233 },
234 {
235 MQTT_SN_DECODE_TEST(suback),
236 .expected = (struct mqtt_sn_param){
237 .type = MQTT_SN_MSG_TYPE_SUBACK,
238 .params.suback = {
239 .topic_id = 0x1A1B,
240 .msg_id = 0x1909,
241 .qos = MQTT_SN_QOS_M1,
242 .ret_code = MQTT_SN_CODE_REJECTED_CONGESTION
243 }
244 }
245 },
246 {
247 MQTT_SN_DECODE_TEST(unsuback),
248 .expected = (struct mqtt_sn_param){
249 .type = MQTT_SN_MSG_TYPE_UNSUBACK,
250 .params.unsuback = {
251 .msg_id = 0x1337,
252 }
253 }
254 },
255 {
256 MQTT_SN_DECODE_TEST(pingreq1),
257 .expected = (struct mqtt_sn_param){
258 .type = MQTT_SN_MSG_TYPE_PINGREQ,
259 }
260 },
261 {
262 MQTT_SN_DECODE_TEST(pingresp),
263 .expected = (struct mqtt_sn_param){
264 .type = MQTT_SN_MSG_TYPE_PINGRESP,
265 }
266 },
267 {
268 MQTT_SN_DECODE_TEST(disconnect_gw),
269 .expected = (struct mqtt_sn_param){
270 .type = MQTT_SN_MSG_TYPE_DISCONNECT,
271 }
272 },
273 {
274 MQTT_SN_DECODE_TEST(willtopicresp),
275 .expected = (struct mqtt_sn_param){
276 .type = MQTT_SN_MSG_TYPE_WILLTOPICRESP,
277 }
278 },
279 {
280 MQTT_SN_DECODE_TEST(willmsgresp),
281 .expected = (struct mqtt_sn_param){
282 .type = MQTT_SN_MSG_TYPE_WILLMSGRESP,
283 }
284 },
285 };
286
ZTEST(mqtt_sn_packet,test_mqtt_packet_decode)287 static ZTEST(mqtt_sn_packet, test_mqtt_packet_decode)
288 {
289 struct net_buf_simple msg;
290 struct mqtt_sn_param param;
291 int err;
292
293 for (size_t i = 0; i < ARRAY_SIZE(decode_tests); i++) {
294 TC_PRINT("%s - test %zu: %s\n", __func__, i, decode_tests[i].name);
295 LOG_HEXDUMP_DBG(decode_tests[i].data, decode_tests[i].datasz, "Test data");
296 if (i == 4) {
297 LOG_HEXDUMP_INF(willtopicreq, sizeof(willtopicreq), "willtopicreq");
298 }
299 memset(¶m, 0, sizeof(param));
300 net_buf_simple_init_with_data(&msg, decode_tests[i].data, decode_tests[i].datasz);
301
302 err = mqtt_sn_decode_msg(&msg, ¶m);
303 zassert_equal(err, 0, "Unexpected error %d", err);
304
305 LOG_HEXDUMP_DBG(¶m, sizeof(param), "Decoded data");
306 LOG_HEXDUMP_DBG(&decode_tests[i].expected, sizeof(param), "Expected data");
307 zassert_mem_equal(¶m, &decode_tests[i].expected, sizeof(param), "in test %zu",
308 i);
309 }
310 }
311
312 struct mqtt_sn_encode_test {
313 const char *name;
314 const uint8_t *expected;
315 size_t expectedsz;
316 struct mqtt_sn_param p;
317 };
318
319 #define MQTT_SN_ENCODE_TEST(_name) \
320 .name = STRINGIFY(_name), .expected = _name, .expectedsz = sizeof(_name)
321
322 static struct mqtt_sn_encode_test encode_tests[] = {
323 {
324 MQTT_SN_ENCODE_TEST(searchgw),
325 .p = {
326 .type = MQTT_SN_MSG_TYPE_SEARCHGW,
327 .params.searchgw = {
328 .radius = 1
329 }
330 }
331 },
332 {
333 MQTT_SN_ENCODE_TEST(gwinfo),
334 .p = {
335 .type = MQTT_SN_MSG_TYPE_GWINFO,
336 .params.gwinfo = {
337 .gw_id = 42,
338 .gw_add = MQTT_SN_DATA_BYTES(127, 0, 0, 1)
339 }
340 }
341 },
342 {
343 MQTT_SN_ENCODE_TEST(connect),
344 .p = {
345 .type = MQTT_SN_MSG_TYPE_CONNECT,
346 .params.connect = {
347 .will = true,
348 .clean_session = true,
349 .client_id = MQTT_SN_DATA_STRING_LITERAL("zephyrclient"),
350 .duration = 300,
351 }
352 }
353 },
354 {
355 MQTT_SN_ENCODE_TEST(willtopic),
356 .p = {
357 .type = MQTT_SN_MSG_TYPE_WILLTOPIC,
358 .params.willtopic = {
359 .qos = MQTT_SN_QOS_1,
360 .retain = true,
361 .topic = MQTT_SN_DATA_STRING_LITERAL("/zephyr")
362 }
363 }
364 },
365 {
366 MQTT_SN_ENCODE_TEST(willmsg),
367 .p = {
368 .type = MQTT_SN_MSG_TYPE_WILLMSG,
369 .params.willmsg = {
370 .msg = MQTT_SN_DATA_STRING_LITERAL("mywill")
371 }
372 }
373 },
374 {
375 MQTT_SN_ENCODE_TEST(reg_client),
376 .p = {
377 .type = MQTT_SN_MSG_TYPE_REGISTER,
378 .params.reg = {
379 /*
380 * The client must not encode the topic ID -
381 * check this is followed
382 */
383 .topic_id = 0x1A1B,
384 .msg_id = 0x1C1D,
385 .topic = MQTT_SN_DATA_STRING_LITERAL("/zephyr")
386 }
387 }
388 },
389 {
390 MQTT_SN_ENCODE_TEST(regack),
391 .p = {
392 .type = MQTT_SN_MSG_TYPE_REGACK,
393 .params.regack = {
394 .topic_id = 0x1A1B,
395 .msg_id = 0x1C1D,
396 .ret_code = MQTT_SN_CODE_ACCEPTED
397 }
398 }
399 },
400 {
401 MQTT_SN_ENCODE_TEST(publish),
402 .p = {
403 .type = MQTT_SN_MSG_TYPE_PUBLISH,
404 .params.publish = {
405 .dup = true,
406 .qos = MQTT_SN_QOS_2,
407 .retain = true,
408
409 .topic_id = 0x5242,
410 .topic_type = MQTT_SN_TOPIC_TYPE_SHORT,
411 .msg_id = 0x1C1D,
412 .data = MQTT_SN_DATA_STRING_LITERAL("zephyr")
413 }
414 }
415 },
416 {
417 MQTT_SN_ENCODE_TEST(puback),
418 .p = {
419 .type = MQTT_SN_MSG_TYPE_PUBACK,
420 .params.puback = {
421 .topic_id = 0x1A1B,
422 .msg_id = 0x1C1D,
423 .ret_code = MQTT_SN_CODE_REJECTED_NOTSUP
424 }
425 }
426 },
427 {
428 MQTT_SN_ENCODE_TEST(pubrec),
429 .p = {
430 .type = MQTT_SN_MSG_TYPE_PUBREC,
431 .params.pubrec = {
432 .msg_id = 0xBEEF
433 }
434 }
435 },
436 {
437 MQTT_SN_ENCODE_TEST(pubrel),
438 .p = {
439 .type = MQTT_SN_MSG_TYPE_PUBREL,
440 .params.pubrel = {
441 .msg_id = 0xBEEF
442 }
443 }
444 },
445 {
446 MQTT_SN_ENCODE_TEST(pubcomp),
447 .p = {
448 .type = MQTT_SN_MSG_TYPE_PUBCOMP,
449 .params.pubcomp = {
450 .msg_id = 0xBEEF
451 }
452 }
453 },
454 {
455 MQTT_SN_ENCODE_TEST(subscribe),
456 .p = {
457 .type = MQTT_SN_MSG_TYPE_SUBSCRIBE,
458 .params.subscribe = {
459 .dup = true,
460 .qos = MQTT_SN_QOS_0,
461 .msg_id = 0x1C1D,
462 .topic_type = MQTT_SN_FLAGS_TOPICID_TYPE_NORMAL,
463 .topic.topic_name = MQTT_SN_DATA_STRING_LITERAL("/zephyr")
464 }
465 }
466 },
467 {
468 MQTT_SN_ENCODE_TEST(unsubscribe),
469 .p = {
470 .type = MQTT_SN_MSG_TYPE_UNSUBSCRIBE,
471 .params.unsubscribe = {
472 .msg_id = 0x1C1D,
473 .topic_type = MQTT_SN_FLAGS_TOPICID_TYPE_PREDEF,
474 .topic.topic_id = 0x1234
475 }
476 }
477 },
478 {
479 MQTT_SN_ENCODE_TEST(pingreq),
480 .p = {
481 .type = MQTT_SN_MSG_TYPE_PINGREQ,
482 .params.pingreq = {
483 .client_id = MQTT_SN_DATA_STRING_LITERAL("zephyrclient")
484 }
485 }
486 },
487 {
488 MQTT_SN_ENCODE_TEST(pingresp),
489 .p = {
490 .type = MQTT_SN_MSG_TYPE_PINGRESP
491 }
492 },
493 {
494 MQTT_SN_ENCODE_TEST(disconnect),
495 .p = {
496 .type = MQTT_SN_MSG_TYPE_DISCONNECT,
497 .params.disconnect = {
498 .duration = 10000
499 }
500 }
501 },
502 {
503 MQTT_SN_ENCODE_TEST(willtopicupd),
504 .p = {
505 .type = MQTT_SN_MSG_TYPE_WILLTOPICUPD,
506 .params.willtopicupd = {
507 .qos = MQTT_SN_QOS_0,
508 .retain = true,
509 .topic = MQTT_SN_DATA_STRING_LITERAL("/zephyr")
510 }
511 }
512 },
513 {
514 MQTT_SN_ENCODE_TEST(willmsgupd),
515 .p = {
516 .type = MQTT_SN_MSG_TYPE_WILLMSGUPD,
517 .params.willmsgupd = {
518 .msg = MQTT_SN_DATA_STRING_LITERAL("mywill")
519 }
520 }
521 }
522 };
523
ZTEST(mqtt_sn_packet,test_mqtt_packet_encode)524 static ZTEST(mqtt_sn_packet, test_mqtt_packet_encode)
525 {
526 NET_BUF_SIMPLE_DEFINE(msg, 255);
527 int err;
528
529 for (size_t i = 0; i < ARRAY_SIZE(encode_tests); i++) {
530 net_buf_simple_reset(&msg);
531 TC_PRINT("%s - test %zu: %s\n", __func__, i, encode_tests[i].name);
532
533 err = mqtt_sn_encode_msg(&msg, &encode_tests[i].p);
534 zassert_equal(err, 0, "Unexpected error");
535 LOG_HEXDUMP_DBG(encode_tests[i].expected, encode_tests[i].expectedsz,
536 "Expected data");
537 LOG_HEXDUMP_DBG(msg.data, msg.len, "Encoded data");
538 zassert_equal(msg.len, encode_tests[i].expectedsz,
539 "Unexpected data size %zu (%zu)", msg.len,
540 encode_tests[i].expectedsz);
541 zassert_mem_equal(encode_tests[i].expected, msg.data, msg.len,
542 "Bad encoded message");
543 }
544 }
545
546 ZTEST_SUITE(mqtt_sn_packet, NULL, NULL, NULL, NULL, NULL);
547