xref: /openbmc/libmctp/tests/test_core.c (revision 490e3873)
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