1 /* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */ 2 3 #define _GNU_SOURCE 4 5 #ifdef NDEBUG 6 #undef NDEBUG 7 #endif 8 9 #if HAVE_CONFIG_H 10 #include "config.h" 11 #endif 12 13 #include <assert.h> 14 #include <fcntl.h> 15 #include <stdbool.h> 16 #include <stdint.h> 17 #include <stdio.h> 18 #include <stdlib.h> 19 #include <string.h> 20 #include <unistd.h> 21 22 #include "libmctp-alloc.h" 23 #include "libmctp-log.h" 24 #include "range.h" 25 #include "test-utils.h" 26 27 #define TEST_DEST_EID 9 28 #define TEST_SRC_EID 10 29 30 #ifndef ARRAY_SIZE 31 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) 32 #endif 33 34 #define __unused __attribute__((unused)) 35 36 #define MAX_PAYLOAD_SIZE 50000 37 38 struct pktbuf { 39 struct mctp_hdr hdr; 40 uint8_t *payload; 41 }; 42 43 struct test_params { 44 bool seen; 45 size_t message_size; 46 }; 47 48 static void rx_message(uint8_t eid __unused, void *data, void *msg __unused, 49 size_t len) 50 { 51 struct test_params *param = (struct test_params *)data; 52 53 mctp_prdebug("MCTP message received: len %zd", len); 54 55 param->seen = true; 56 param->message_size = len; 57 } 58 59 static uint8_t get_sequence() 60 { 61 static uint8_t pkt_seq = 0; 62 63 return (pkt_seq++ % 4); 64 } 65 66 static uint8_t get_tag() 67 { 68 static uint8_t tag = 0; 69 70 return (tag++ % 8); 71 } 72 73 /* 74 * receive_pktbuf bypasses all bindings and directly invokes mctp_bus_rx. 75 * This is necessary in order invoke test cases on the core functionality. 76 * The memory allocated for the mctp packet is capped at MCTP_BTU 77 * size, however, the mimiced rx pkt still retains the len parameter. 78 * This allows to mimic packets larger than a sane memory allocator can 79 * provide. 80 */ 81 static void receive_ptkbuf(struct mctp_binding_test *binding, 82 const struct pktbuf *pktbuf, size_t len) 83 { 84 size_t alloc_size = MIN((size_t)MCTP_BTU, len); 85 struct mctp_pktbuf *rx_pkt; 86 87 rx_pkt = __mctp_alloc(sizeof(*rx_pkt) + MCTP_PACKET_SIZE(alloc_size)); 88 assert(rx_pkt); 89 90 /* Preserve passed len parameter */ 91 rx_pkt->size = MCTP_PACKET_SIZE(len); 92 rx_pkt->start = 0; 93 rx_pkt->end = MCTP_PACKET_SIZE(len); 94 rx_pkt->mctp_hdr_off = 0; 95 rx_pkt->next = NULL; 96 memcpy(rx_pkt->data, &pktbuf->hdr, sizeof(pktbuf->hdr)); 97 memcpy(rx_pkt->data + sizeof(pktbuf->hdr), pktbuf->payload, alloc_size); 98 99 mctp_bus_rx((struct mctp_binding *)binding, rx_pkt); 100 } 101 102 static void receive_one_fragment(struct mctp_binding_test *binding, 103 uint8_t *payload, size_t fragment_size, 104 uint8_t flags_seq_tag, struct pktbuf *pktbuf) 105 { 106 pktbuf->hdr.flags_seq_tag = flags_seq_tag; 107 pktbuf->payload = payload; 108 receive_ptkbuf(binding, pktbuf, fragment_size); 109 } 110 111 static void receive_two_fragment_message(struct mctp_binding_test *binding, 112 uint8_t *payload, 113 size_t fragment1_size, 114 size_t fragment2_size, 115 struct pktbuf *pktbuf) 116 { 117 uint8_t tag = MCTP_HDR_FLAG_TO | get_tag(); 118 uint8_t flags_seq_tag; 119 120 flags_seq_tag = MCTP_HDR_FLAG_SOM | 121 (get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag; 122 receive_one_fragment(binding, payload, fragment1_size, flags_seq_tag, 123 pktbuf); 124 125 flags_seq_tag = MCTP_HDR_FLAG_EOM | 126 (get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag; 127 receive_one_fragment(binding, payload + fragment1_size, fragment2_size, 128 flags_seq_tag, pktbuf); 129 } 130 131 static void mctp_core_test_simple_rx() 132 { 133 struct mctp *mctp = NULL; 134 struct mctp_binding_test *binding = NULL; 135 struct test_params test_param; 136 uint8_t test_payload[2 * MCTP_BTU]; 137 struct pktbuf pktbuf; 138 139 memset(test_payload, 0, sizeof(test_payload)); 140 test_param.seen = false; 141 test_param.message_size = 0; 142 mctp_test_stack_init(&mctp, &binding, TEST_DEST_EID); 143 mctp_set_rx_all(mctp, rx_message, &test_param); 144 memset(&pktbuf, 0, sizeof(pktbuf)); 145 pktbuf.hdr.dest = TEST_DEST_EID; 146 pktbuf.hdr.src = TEST_SRC_EID; 147 148 /* Receive 2 fragments of equal size */ 149 receive_two_fragment_message(binding, test_payload, MCTP_BTU, MCTP_BTU, 150 &pktbuf); 151 152 assert(test_param.seen); 153 assert(test_param.message_size == 2 * MCTP_BTU); 154 155 mctp_binding_test_destroy(binding); 156 mctp_destroy(mctp); 157 } 158 159 static void mctp_core_test_receive_equal_length_fragments() 160 { 161 struct mctp *mctp = NULL; 162 struct mctp_binding_test *binding = NULL; 163 struct test_params test_param; 164 static uint8_t test_payload[MAX_PAYLOAD_SIZE]; 165 uint8_t tag = MCTP_HDR_FLAG_TO | get_tag(); 166 struct pktbuf pktbuf; 167 uint8_t flags_seq_tag; 168 169 memset(test_payload, 0, sizeof(test_payload)); 170 test_param.seen = false; 171 test_param.message_size = 0; 172 mctp_test_stack_init(&mctp, &binding, TEST_DEST_EID); 173 mctp_set_rx_all(mctp, rx_message, &test_param); 174 memset(&pktbuf, 0, sizeof(pktbuf)); 175 pktbuf.hdr.dest = TEST_DEST_EID; 176 pktbuf.hdr.src = TEST_SRC_EID; 177 178 /* Receive 3 fragments, each of size MCTP_BTU */ 179 flags_seq_tag = MCTP_HDR_FLAG_SOM | 180 (get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag; 181 receive_one_fragment(binding, test_payload, MCTP_BTU, flags_seq_tag, 182 &pktbuf); 183 184 flags_seq_tag = (get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag; 185 receive_one_fragment(binding, test_payload + MCTP_BTU, MCTP_BTU, 186 flags_seq_tag, &pktbuf); 187 188 flags_seq_tag = MCTP_HDR_FLAG_EOM | 189 (get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag; 190 receive_one_fragment(binding, test_payload + (2 * MCTP_BTU), MCTP_BTU, 191 flags_seq_tag, &pktbuf); 192 193 assert(test_param.seen); 194 assert(test_param.message_size == 3 * MCTP_BTU); 195 196 mctp_binding_test_destroy(binding); 197 mctp_destroy(mctp); 198 } 199 200 static void mctp_core_test_receive_unexpected_smaller_middle_fragment() 201 { 202 struct mctp *mctp = NULL; 203 struct mctp_binding_test *binding = NULL; 204 struct test_params test_param; 205 static uint8_t test_payload[MAX_PAYLOAD_SIZE]; 206 uint8_t tag = MCTP_HDR_FLAG_TO | get_tag(); 207 struct pktbuf pktbuf; 208 uint8_t flags_seq_tag; 209 210 memset(test_payload, 0, sizeof(test_payload)); 211 test_param.seen = false; 212 test_param.message_size = 0; 213 mctp_test_stack_init(&mctp, &binding, TEST_DEST_EID); 214 mctp_set_rx_all(mctp, rx_message, &test_param); 215 memset(&pktbuf, 0, sizeof(pktbuf)); 216 pktbuf.hdr.dest = TEST_DEST_EID; 217 pktbuf.hdr.src = TEST_SRC_EID; 218 219 /* Middle fragment with size MCTP_BTU - 1 */ 220 flags_seq_tag = MCTP_HDR_FLAG_SOM | 221 (get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag; 222 receive_one_fragment(binding, test_payload, MCTP_BTU, flags_seq_tag, 223 &pktbuf); 224 225 flags_seq_tag = (get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag; 226 receive_one_fragment(binding, test_payload + MCTP_BTU, MCTP_BTU - 1, 227 flags_seq_tag, &pktbuf); 228 229 flags_seq_tag = MCTP_HDR_FLAG_EOM | 230 (get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag; 231 receive_one_fragment(binding, test_payload + (2 * MCTP_BTU), MCTP_BTU, 232 flags_seq_tag, &pktbuf); 233 234 assert(!test_param.seen); 235 236 mctp_binding_test_destroy(binding); 237 mctp_destroy(mctp); 238 } 239 240 static void mctp_core_test_receive_unexpected_bigger_middle_fragment() 241 { 242 struct mctp *mctp = NULL; 243 struct mctp_binding_test *binding = NULL; 244 struct test_params test_param; 245 static uint8_t test_payload[MAX_PAYLOAD_SIZE]; 246 uint8_t tag = MCTP_HDR_FLAG_TO | get_tag(); 247 struct pktbuf pktbuf; 248 uint8_t flags_seq_tag; 249 250 memset(test_payload, 0, sizeof(test_payload)); 251 test_param.seen = false; 252 test_param.message_size = 0; 253 mctp_test_stack_init(&mctp, &binding, TEST_DEST_EID); 254 mctp_set_rx_all(mctp, rx_message, &test_param); 255 memset(&pktbuf, 0, sizeof(pktbuf)); 256 pktbuf.hdr.dest = TEST_DEST_EID; 257 pktbuf.hdr.src = TEST_SRC_EID; 258 259 /* Middle fragment with size MCTP_BTU + 1 */ 260 flags_seq_tag = MCTP_HDR_FLAG_SOM | 261 (get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag; 262 receive_one_fragment(binding, test_payload, MCTP_BTU, flags_seq_tag, 263 &pktbuf); 264 265 flags_seq_tag = (get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag; 266 receive_one_fragment(binding, test_payload + MCTP_BTU, MCTP_BTU + 1, 267 flags_seq_tag, &pktbuf); 268 269 flags_seq_tag = MCTP_HDR_FLAG_EOM | 270 (get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag; 271 receive_one_fragment(binding, test_payload + (2 * MCTP_BTU), MCTP_BTU, 272 flags_seq_tag, &pktbuf); 273 274 assert(!test_param.seen); 275 276 mctp_binding_test_destroy(binding); 277 mctp_destroy(mctp); 278 } 279 280 static void mctp_core_test_receive_smaller_end_fragment() 281 { 282 struct mctp *mctp = NULL; 283 struct mctp_binding_test *binding = NULL; 284 struct test_params test_param; 285 static uint8_t test_payload[MAX_PAYLOAD_SIZE]; 286 uint8_t tag = MCTP_HDR_FLAG_TO | get_tag(); 287 uint8_t end_frag_size = MCTP_BTU - 10; 288 struct pktbuf pktbuf; 289 uint8_t flags_seq_tag; 290 291 memset(test_payload, 0, sizeof(test_payload)); 292 test_param.seen = false; 293 test_param.message_size = 0; 294 mctp_test_stack_init(&mctp, &binding, TEST_DEST_EID); 295 mctp_set_rx_all(mctp, rx_message, &test_param); 296 memset(&pktbuf, 0, sizeof(pktbuf)); 297 pktbuf.hdr.dest = TEST_DEST_EID; 298 pktbuf.hdr.src = TEST_SRC_EID; 299 300 flags_seq_tag = MCTP_HDR_FLAG_SOM | 301 (get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag; 302 receive_one_fragment(binding, test_payload, MCTP_BTU, flags_seq_tag, 303 &pktbuf); 304 305 flags_seq_tag = (get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag; 306 receive_one_fragment(binding, test_payload + MCTP_BTU, MCTP_BTU, 307 flags_seq_tag, &pktbuf); 308 309 flags_seq_tag = MCTP_HDR_FLAG_EOM | 310 (get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag; 311 receive_one_fragment(binding, test_payload + (2 * MCTP_BTU), 312 end_frag_size, flags_seq_tag, &pktbuf); 313 314 assert(test_param.seen); 315 assert(test_param.message_size == 316 (size_t)(2 * MCTP_BTU + end_frag_size)); 317 318 mctp_binding_test_destroy(binding); 319 mctp_destroy(mctp); 320 } 321 322 static void mctp_core_test_receive_bigger_end_fragment() 323 { 324 struct mctp *mctp = NULL; 325 struct mctp_binding_test *binding = NULL; 326 struct test_params test_param; 327 static uint8_t test_payload[MAX_PAYLOAD_SIZE]; 328 uint8_t tag = MCTP_HDR_FLAG_TO | get_tag(); 329 uint8_t end_frag_size = MCTP_BTU + 10; 330 struct pktbuf pktbuf; 331 uint8_t flags_seq_tag; 332 333 memset(test_payload, 0, sizeof(test_payload)); 334 test_param.seen = false; 335 test_param.message_size = 0; 336 mctp_test_stack_init(&mctp, &binding, TEST_DEST_EID); 337 mctp_set_rx_all(mctp, rx_message, &test_param); 338 memset(&pktbuf, 0, sizeof(pktbuf)); 339 pktbuf.hdr.dest = TEST_DEST_EID; 340 pktbuf.hdr.src = TEST_SRC_EID; 341 342 flags_seq_tag = MCTP_HDR_FLAG_SOM | 343 (get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag; 344 receive_one_fragment(binding, test_payload, MCTP_BTU, flags_seq_tag, 345 &pktbuf); 346 347 flags_seq_tag = (get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag; 348 receive_one_fragment(binding, test_payload + MCTP_BTU, MCTP_BTU, 349 flags_seq_tag, &pktbuf); 350 351 flags_seq_tag = MCTP_HDR_FLAG_EOM | 352 (get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag; 353 receive_one_fragment(binding, test_payload + (2 * MCTP_BTU), 354 end_frag_size, flags_seq_tag, &pktbuf); 355 356 assert(!test_param.seen); 357 358 mctp_binding_test_destroy(binding); 359 mctp_destroy(mctp); 360 } 361 362 static void mctp_core_test_drop_large_fragments() 363 { 364 struct mctp *mctp = NULL; 365 struct mctp_binding_test *binding = NULL; 366 struct test_params test_param; 367 static uint8_t test_payload[MAX_PAYLOAD_SIZE]; 368 struct pktbuf pktbuf; 369 370 memset(test_payload, 0, sizeof(test_payload)); 371 test_param.seen = false; 372 test_param.message_size = 0; 373 mctp_test_stack_init(&mctp, &binding, TEST_DEST_EID); 374 mctp_set_rx_all(mctp, rx_message, &test_param); 375 memset(&pktbuf, 0, sizeof(pktbuf)); 376 pktbuf.hdr.dest = TEST_DEST_EID; 377 pktbuf.hdr.src = TEST_SRC_EID; 378 379 /* Receive a large payload - first fragment with MCTP_BTU bytes, 380 * 2nd fragment of SIZE_MAX */ 381 382 receive_two_fragment_message(binding, test_payload, MCTP_BTU, 383 SIZE_MAX - sizeof(struct mctp_hdr), &pktbuf); 384 385 assert(!test_param.seen); 386 387 mctp_binding_test_destroy(binding); 388 mctp_destroy(mctp); 389 } 390 391 static void mctp_core_test_exhaust_context_buffers() 392 { 393 struct mctp *mctp = NULL; 394 struct mctp_binding_test *binding = NULL; 395 struct test_params test_param; 396 static uint8_t test_payload[MAX_PAYLOAD_SIZE]; 397 uint8_t tag = MCTP_HDR_FLAG_TO | get_tag(); 398 uint8_t i = 0; 399 const uint8_t max_context_buffers = 16; 400 struct pktbuf pktbuf; 401 uint8_t flags_seq_tag; 402 403 memset(test_payload, 0, sizeof(test_payload)); 404 test_param.seen = false; 405 test_param.message_size = 0; 406 mctp_test_stack_init(&mctp, &binding, TEST_DEST_EID); 407 mctp_set_rx_all(mctp, rx_message, &test_param); 408 memset(&pktbuf, 0, sizeof(pktbuf)); 409 pktbuf.hdr.dest = TEST_DEST_EID; 410 pktbuf.hdr.src = TEST_SRC_EID; 411 412 /* Exhaust all 16 context buffers*/ 413 for (i = 0; i < max_context_buffers; i++) { 414 flags_seq_tag = MCTP_HDR_FLAG_SOM | 415 (get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag; 416 receive_one_fragment(binding, test_payload, MCTP_BTU, 417 flags_seq_tag, &pktbuf); 418 419 /* Change source EID so that different contexts are created */ 420 pktbuf.hdr.src++; 421 } 422 423 /* Send a full message from a different EID */ 424 pktbuf.hdr.src++; 425 receive_two_fragment_message(binding, test_payload, MCTP_BTU, MCTP_BTU, 426 &pktbuf); 427 428 /* Message assembly should fail */ 429 assert(!test_param.seen); 430 431 /* Complete message assembly for one of the messages */ 432 pktbuf.hdr.src -= max_context_buffers; 433 flags_seq_tag = MCTP_HDR_FLAG_EOM | 434 (get_sequence() << MCTP_HDR_SEQ_SHIFT) | tag; 435 receive_one_fragment(binding, test_payload, MCTP_BTU, 436 flags_seq_tag, &pktbuf); 437 438 assert(test_param.seen); 439 assert(test_param.message_size == (2 * MCTP_BTU)); 440 441 mctp_binding_test_destroy(binding); 442 mctp_destroy(mctp); 443 } 444 445 /* clang-format off */ 446 #define TEST_CASE(test) { #test, test } 447 static const struct { 448 const char *name; 449 void (*test)(void); 450 } mctp_core_tests[] = { 451 TEST_CASE(mctp_core_test_simple_rx), 452 TEST_CASE(mctp_core_test_receive_equal_length_fragments), 453 TEST_CASE(mctp_core_test_receive_unexpected_smaller_middle_fragment), 454 TEST_CASE(mctp_core_test_receive_unexpected_bigger_middle_fragment), 455 TEST_CASE(mctp_core_test_receive_smaller_end_fragment), 456 TEST_CASE(mctp_core_test_receive_bigger_end_fragment), 457 TEST_CASE(mctp_core_test_drop_large_fragments), 458 TEST_CASE(mctp_core_test_exhaust_context_buffers), 459 }; 460 /* clang-format on */ 461 462 #ifndef BUILD_ASSERT 463 #define BUILD_ASSERT(x) \ 464 do { \ 465 (void)sizeof(char[0 - (!(x))]); \ 466 } while (0) 467 #endif 468 469 int main(void) 470 { 471 uint8_t i; 472 473 mctp_set_log_stdio(MCTP_LOG_DEBUG); 474 475 BUILD_ASSERT(ARRAY_SIZE(mctp_core_tests) < SIZE_MAX); 476 for (i = 0; i < ARRAY_SIZE(mctp_core_tests); i++) { 477 mctp_prlog(MCTP_LOG_DEBUG, "begin: %s", 478 mctp_core_tests[i].name); 479 mctp_core_tests[i].test(); 480 mctp_prlog(MCTP_LOG_DEBUG, "end: %s\n", 481 mctp_core_tests[i].name); 482 } 483 484 return 0; 485 } 486