1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2020 Red Hat, Inc. 3 */ 4 5 #include "test.h" 6 7 #include <time.h> 8 9 #include <rte_common.h> 10 #include <rte_cycles.h> 11 #include <rte_hexdump.h> 12 #include <rte_ip.h> 13 #include <rte_ip_frag.h> 14 #include <rte_mbuf.h> 15 #include <rte_random.h> 16 17 #define NUM_MBUFS 128 18 #define BURST 32 19 20 uint8_t expected_first_frag_ipv4_opts_copied[] = { 21 0x07, 0x0b, 0x04, 0x00, 22 0x00, 0x00, 0x00, 0x00, 23 0x00, 0x00, 0x00, 0x83, 24 0x07, 0x04, 0xc0, 0xa8, 25 0xe3, 0x96, 0x00, 0x00, 26 }; 27 28 uint8_t expected_sub_frag_ipv4_opts_copied[] = { 29 0x83, 0x07, 0x04, 0xc0, 30 0xa8, 0xe3, 0x96, 0x00, 31 }; 32 33 uint8_t expected_first_frag_ipv4_opts_nocopied[] = { 34 0x07, 0x0b, 0x04, 0x00, 35 0x00, 0x00, 0x00, 0x00, 36 0x00, 0x00, 0x00, 0x00, 37 }; 38 39 uint8_t expected_sub_frag_ipv4_opts_nocopied[0]; 40 41 struct test_opt_data { 42 bool is_first_frag; /**< offset is 0 */ 43 bool opt_copied; /**< ip option copied flag */ 44 uint16_t len; /**< option data len */ 45 uint8_t data[RTE_IPV4_HDR_OPT_MAX_LEN]; /**< option data */ 46 }; 47 48 static struct rte_mempool *pkt_pool, 49 *direct_pool, 50 *indirect_pool; 51 52 static inline void 53 hex_to_str(uint8_t *hex, uint16_t len, char *str) 54 { 55 int i; 56 57 for (i = 0; i < len; i++) { 58 sprintf(str, "%02x", hex[i]); 59 str += 2; 60 } 61 *str = 0; 62 } 63 64 static int 65 setup_buf_pool(void) 66 { 67 pkt_pool = rte_pktmbuf_pool_create("FRAG_MBUF_POOL", 68 NUM_MBUFS, BURST, 0, 69 RTE_MBUF_DEFAULT_BUF_SIZE, 70 SOCKET_ID_ANY); 71 if (pkt_pool == NULL) { 72 printf("%s: Error creating pkt mempool\n", __func__); 73 goto bad_setup; 74 } 75 76 direct_pool = rte_pktmbuf_pool_create("FRAG_D_MBUF_POOL", 77 NUM_MBUFS, BURST, 0, 78 RTE_MBUF_DEFAULT_BUF_SIZE, 79 SOCKET_ID_ANY); 80 if (direct_pool == NULL) { 81 printf("%s: Error creating direct mempool\n", __func__); 82 goto bad_setup; 83 } 84 85 indirect_pool = rte_pktmbuf_pool_create("FRAG_I_MBUF_POOL", 86 NUM_MBUFS, BURST, 0, 87 0, SOCKET_ID_ANY); 88 if (indirect_pool == NULL) { 89 printf("%s: Error creating indirect mempool\n", __func__); 90 goto bad_setup; 91 } 92 93 return TEST_SUCCESS; 94 95 bad_setup: 96 rte_mempool_free(pkt_pool); 97 pkt_pool = NULL; 98 99 rte_mempool_free(direct_pool); 100 direct_pool = NULL; 101 102 return TEST_FAILED; 103 } 104 105 static int testsuite_setup(void) 106 { 107 return setup_buf_pool(); 108 } 109 110 static void testsuite_teardown(void) 111 { 112 rte_mempool_free(pkt_pool); 113 rte_mempool_free(direct_pool); 114 rte_mempool_free(indirect_pool); 115 116 pkt_pool = NULL; 117 direct_pool = NULL; 118 indirect_pool = NULL; 119 } 120 121 static int ut_setup(void) 122 { 123 return TEST_SUCCESS; 124 } 125 126 static void ut_teardown(void) 127 { 128 } 129 130 static inline void 131 test_get_ipv4_opt(bool is_first_frag, bool opt_copied, 132 struct test_opt_data *expected_opt) 133 { 134 if (is_first_frag) { 135 if (opt_copied) { 136 expected_opt->len = 137 sizeof(expected_first_frag_ipv4_opts_copied); 138 memcpy(expected_opt->data, 139 expected_first_frag_ipv4_opts_copied, 140 sizeof(expected_first_frag_ipv4_opts_copied)); 141 } else { 142 expected_opt->len = 143 sizeof(expected_first_frag_ipv4_opts_nocopied); 144 memcpy(expected_opt->data, 145 expected_first_frag_ipv4_opts_nocopied, 146 sizeof(expected_first_frag_ipv4_opts_nocopied)); 147 } 148 } else { 149 if (opt_copied) { 150 expected_opt->len = 151 sizeof(expected_sub_frag_ipv4_opts_copied); 152 memcpy(expected_opt->data, 153 expected_sub_frag_ipv4_opts_copied, 154 sizeof(expected_sub_frag_ipv4_opts_copied)); 155 } else { 156 expected_opt->len = 157 sizeof(expected_sub_frag_ipv4_opts_nocopied); 158 memcpy(expected_opt->data, 159 expected_sub_frag_ipv4_opts_nocopied, 160 sizeof(expected_sub_frag_ipv4_opts_nocopied)); 161 } 162 } 163 } 164 165 static void 166 v4_allocate_packet_of(struct rte_mbuf *b, int fill, size_t s, 167 int df, uint8_t mf, uint16_t off, uint8_t ttl, uint8_t proto, 168 uint16_t pktid, bool have_opt, bool is_first_frag, bool opt_copied) 169 { 170 /* Create a packet, 2k bytes long */ 171 b->data_off = 0; 172 char *data = rte_pktmbuf_mtod(b, char *); 173 rte_be16_t fragment_offset = 0; /* fragmentation offset */ 174 uint16_t iph_len; 175 struct test_opt_data opt; 176 177 opt.len = 0; 178 179 if (have_opt) 180 test_get_ipv4_opt(is_first_frag, opt_copied, &opt); 181 182 iph_len = sizeof(struct rte_ipv4_hdr) + opt.len; 183 memset(data, fill, iph_len + s); 184 185 struct rte_ipv4_hdr *hdr = (struct rte_ipv4_hdr *)data; 186 187 hdr->version_ihl = 0x40; /* ipv4 */ 188 hdr->version_ihl += (iph_len / 4); 189 hdr->type_of_service = 0; 190 b->pkt_len = s + iph_len; 191 b->data_len = b->pkt_len; 192 hdr->total_length = rte_cpu_to_be_16(b->pkt_len); 193 hdr->packet_id = rte_cpu_to_be_16(pktid); 194 195 if (df) 196 fragment_offset |= 0x4000; 197 198 if (mf) 199 fragment_offset |= 0x2000; 200 201 if (off) 202 fragment_offset |= off; 203 204 hdr->fragment_offset = rte_cpu_to_be_16(fragment_offset); 205 206 if (!ttl) 207 ttl = 64; /* default to 64 */ 208 209 if (!proto) 210 proto = 1; /* icmp */ 211 212 hdr->time_to_live = ttl; 213 hdr->next_proto_id = proto; 214 hdr->hdr_checksum = 0; 215 hdr->src_addr = rte_cpu_to_be_32(0x8080808); 216 hdr->dst_addr = rte_cpu_to_be_32(0x8080404); 217 218 memcpy(hdr + 1, opt.data, opt.len); 219 } 220 221 static void 222 v6_allocate_packet_of(struct rte_mbuf *b, int fill, size_t s, uint8_t ttl, 223 uint8_t proto, uint16_t pktid) 224 { 225 /* Create a packet, 2k bytes long */ 226 b->data_off = 0; 227 char *data = rte_pktmbuf_mtod(b, char *); 228 229 memset(data, fill, sizeof(struct rte_ipv6_hdr) + s); 230 231 struct rte_ipv6_hdr *hdr = (struct rte_ipv6_hdr *)data; 232 b->pkt_len = s + sizeof(struct rte_ipv6_hdr); 233 b->data_len = b->pkt_len; 234 235 /* basic v6 header */ 236 hdr->vtc_flow = rte_cpu_to_be_32(0x60 << 24 | pktid); 237 hdr->payload_len = rte_cpu_to_be_16(b->pkt_len); 238 hdr->proto = proto; 239 hdr->hop_limits = ttl; 240 241 memset(&hdr->src_addr, 0x08, sizeof(hdr->src_addr)); 242 memset(&hdr->dst_addr, 0x04, sizeof(hdr->src_addr)); 243 } 244 245 static inline void 246 test_free_fragments(struct rte_mbuf *mb[], uint32_t num) 247 { 248 uint32_t i; 249 for (i = 0; i < num; i++) 250 rte_pktmbuf_free(mb[i]); 251 } 252 253 static inline void 254 test_get_offset(struct rte_mbuf **mb, int32_t len, 255 uint16_t *offset, int ipv) 256 { 257 int32_t i; 258 259 for (i = 0; i < len; i++) { 260 if (ipv == 4) { 261 struct rte_ipv4_hdr *iph = 262 rte_pktmbuf_mtod(mb[i], struct rte_ipv4_hdr *); 263 offset[i] = iph->fragment_offset; 264 } else if (ipv == 6) { 265 struct ipv6_extension_fragment *fh = 266 rte_pktmbuf_mtod_offset( 267 mb[i], 268 struct ipv6_extension_fragment *, 269 sizeof(struct rte_ipv6_hdr)); 270 offset[i] = fh->frag_data; 271 } 272 } 273 } 274 275 static inline void 276 test_get_frag_opt(struct rte_mbuf **mb, int32_t num, 277 struct test_opt_data *opt, int ipv, bool opt_copied) 278 { 279 int32_t i; 280 281 for (i = 0; i < num; i++) { 282 if (ipv == 4) { 283 struct rte_ipv4_hdr *iph = 284 rte_pktmbuf_mtod(mb[i], struct rte_ipv4_hdr *); 285 uint16_t header_len = (iph->version_ihl & 286 RTE_IPV4_HDR_IHL_MASK) * 287 RTE_IPV4_IHL_MULTIPLIER; 288 uint16_t opt_len = header_len - 289 sizeof(struct rte_ipv4_hdr); 290 291 opt->opt_copied = opt_copied; 292 293 if ((rte_be_to_cpu_16(iph->fragment_offset) & 294 RTE_IPV4_HDR_OFFSET_MASK) == 0) 295 opt->is_first_frag = true; 296 else 297 opt->is_first_frag = false; 298 299 if (likely(opt_len <= RTE_IPV4_HDR_OPT_MAX_LEN)) { 300 char *iph_opt = rte_pktmbuf_mtod_offset(mb[i], 301 char *, sizeof(struct rte_ipv4_hdr)); 302 opt->len = opt_len; 303 memcpy(opt->data, iph_opt, opt_len); 304 } else { 305 opt->len = RTE_IPV4_HDR_OPT_MAX_LEN; 306 memset(opt->data, RTE_IPV4_HDR_OPT_EOL, 307 sizeof(opt->data)); 308 } 309 opt++; 310 } 311 } 312 } 313 314 static int 315 test_ip_frag(void) 316 { 317 static const uint16_t RND_ID = UINT16_MAX; 318 int result = TEST_SUCCESS; 319 size_t i, j; 320 321 struct test_ip_frags { 322 int ipv; 323 size_t mtu_size; 324 size_t pkt_size; 325 int set_df; 326 uint8_t set_mf; 327 uint16_t set_of; 328 uint8_t ttl; 329 uint8_t proto; 330 uint16_t pkt_id; 331 int expected_frags; 332 uint16_t expected_fragment_offset[BURST]; 333 bool have_opt; 334 bool is_first_frag; 335 bool opt_copied; 336 } tests[] = { 337 {4, 1280, 1400, 0, 0, 0, 64, IPPROTO_ICMP, RND_ID, 2, 338 {0x2000, 0x009D}, false}, 339 {4, 1280, 1400, 0, 0, 0, 64, IPPROTO_ICMP, 0, 2, 340 {0x2000, 0x009D}, false}, 341 {4, 600, 1400, 0, 0, 0, 64, IPPROTO_ICMP, RND_ID, 3, 342 {0x2000, 0x2048, 0x0090}, false}, 343 {4, 4, 1400, 0, 0, 0, 64, IPPROTO_ICMP, RND_ID, -EINVAL}, 344 {4, 600, 1400, 1, 0, 0, 64, IPPROTO_ICMP, RND_ID, -ENOTSUP}, 345 {4, 600, 1400, 0, 0, 0, 0, IPPROTO_ICMP, RND_ID, 3, 346 {0x2000, 0x2046, 0x008C}, true, true, true}, 347 /* The first fragment */ 348 {4, 68, 104, 0, 1, 0, 0, IPPROTO_ICMP, RND_ID, 5, 349 {0x2000, 0x2003, 0x2006, 0x2009, 0x200C}, true, true, true}, 350 /* The middle fragment */ 351 {4, 68, 104, 0, 1, 13, 0, IPPROTO_ICMP, RND_ID, 3, 352 {0x200D, 0x2012, 0x2017}, true, false, true}, 353 /* The last fragment */ 354 {4, 68, 104, 0, 0, 26, 0, IPPROTO_ICMP, RND_ID, 3, 355 {0x201A, 0x201F, 0x0024}, true, false, true}, 356 /* The first fragment */ 357 {4, 68, 104, 0, 1, 0, 0, IPPROTO_ICMP, RND_ID, 4, 358 {0x2000, 0x2004, 0x2008, 0x200C}, true, true, false}, 359 /* The middle fragment */ 360 {4, 68, 104, 0, 1, 13, 0, IPPROTO_ICMP, RND_ID, 3, 361 {0x200D, 0x2013, 0x2019}, true, false, false}, 362 /* The last fragment */ 363 {4, 68, 104, 0, 0, 26, 0, IPPROTO_ICMP, RND_ID, 3, 364 {0x201A, 0x2020, 0x0026}, true, false, false}, 365 {6, 1280, 1400, 0, 0, 0, 64, IPPROTO_ICMP, RND_ID, 2, 366 {0x0001, 0x04D0}, false}, 367 {6, 1300, 1400, 0, 0, 0, 64, IPPROTO_ICMP, RND_ID, 2, 368 {0x0001, 0x04E0}, false}, 369 {6, 4, 1400, 0, 0, 0, 64, IPPROTO_ICMP, RND_ID, -EINVAL}, 370 {6, 1300, 1400, 0, 0, 0, 0, IPPROTO_ICMP, RND_ID, 2, 371 {0x0001, 0x04E0}, false}, 372 }; 373 374 for (i = 0; i < RTE_DIM(tests); i++) { 375 int32_t len = 0; 376 uint16_t fragment_offset[BURST]; 377 struct test_opt_data opt_res[BURST]; 378 struct test_opt_data opt_exp; 379 uint16_t pktid = tests[i].pkt_id; 380 struct rte_mbuf *pkts_out[BURST]; 381 struct rte_mbuf *b = rte_pktmbuf_alloc(pkt_pool); 382 383 RTE_TEST_ASSERT_NOT_EQUAL(b, NULL, 384 "Failed to allocate pkt."); 385 386 if (tests[i].pkt_id == RND_ID) 387 pktid = rte_rand_max(UINT16_MAX); 388 389 if (tests[i].ipv == 4) { 390 v4_allocate_packet_of(b, 0x41414141, 391 tests[i].pkt_size, 392 tests[i].set_df, 393 tests[i].set_mf, 394 tests[i].set_of, 395 tests[i].ttl, 396 tests[i].proto, 397 pktid, 398 tests[i].have_opt, 399 tests[i].is_first_frag, 400 tests[i].opt_copied); 401 } else if (tests[i].ipv == 6) { 402 v6_allocate_packet_of(b, 0x41414141, 403 tests[i].pkt_size, 404 tests[i].ttl, 405 tests[i].proto, 406 pktid); 407 } 408 409 if (tests[i].ipv == 4) 410 if (i % 2) 411 len = rte_ipv4_fragment_packet(b, pkts_out, BURST, 412 tests[i].mtu_size, 413 direct_pool, 414 indirect_pool); 415 else 416 len = rte_ipv4_fragment_copy_nonseg_packet(b, 417 pkts_out, 418 BURST, 419 tests[i].mtu_size, 420 direct_pool); 421 else if (tests[i].ipv == 6) 422 len = rte_ipv6_fragment_packet(b, pkts_out, BURST, 423 tests[i].mtu_size, 424 direct_pool, 425 indirect_pool); 426 427 rte_pktmbuf_free(b); 428 429 if (len > 0) { 430 test_get_offset(pkts_out, len, 431 fragment_offset, tests[i].ipv); 432 if (tests[i].have_opt) 433 test_get_frag_opt(pkts_out, len, opt_res, 434 tests[i].ipv, tests[i].opt_copied); 435 test_free_fragments(pkts_out, len); 436 } 437 438 printf("[check frag number]%zd: checking %d with %d\n", i, len, 439 tests[i].expected_frags); 440 RTE_TEST_ASSERT_EQUAL(len, tests[i].expected_frags, 441 "Failed case %zd.\n", i); 442 443 if (len > 0) { 444 for (j = 0; j < (size_t)len; j++) { 445 printf("[check offset]%zd-%zd: checking %d with %d\n", 446 i, j, fragment_offset[j], 447 rte_cpu_to_be_16( 448 tests[i].expected_fragment_offset[j])); 449 RTE_TEST_ASSERT_EQUAL(fragment_offset[j], 450 rte_cpu_to_be_16( 451 tests[i].expected_fragment_offset[j]), 452 "Failed case %zd.\n", i); 453 } 454 455 if (tests[i].have_opt && (tests[i].ipv == 4)) { 456 for (j = 0; j < (size_t)len; j++) { 457 char opt_res_str[2 * 458 RTE_IPV4_HDR_OPT_MAX_LEN + 1]; 459 char opt_exp_str[2 * 460 RTE_IPV4_HDR_OPT_MAX_LEN + 1]; 461 462 test_get_ipv4_opt( 463 opt_res[j].is_first_frag, 464 opt_res[j].opt_copied, 465 &opt_exp); 466 hex_to_str(opt_res[j].data, 467 opt_res[j].len, 468 opt_res_str); 469 hex_to_str(opt_exp.data, 470 opt_exp.len, 471 opt_exp_str); 472 473 printf( 474 "[check ipv4 option]%zd-%zd: checking (len:%u)%s with (len:%u)%s\n", 475 i, j, 476 opt_res[j].len, opt_res_str, 477 opt_exp.len, opt_exp_str); 478 RTE_TEST_ASSERT_SUCCESS( 479 strcmp(opt_res_str, 480 opt_exp_str), 481 "Failed case %zd.\n", i); 482 } 483 } 484 } 485 486 } 487 488 return result; 489 } 490 491 static struct unit_test_suite ipfrag_testsuite = { 492 .suite_name = "IP Frag Unit Test Suite", 493 .setup = testsuite_setup, 494 .teardown = testsuite_teardown, 495 .unit_test_cases = { 496 TEST_CASE_ST(ut_setup, ut_teardown, 497 test_ip_frag), 498 499 TEST_CASES_END() /**< NULL terminate unit test array */ 500 } 501 }; 502 503 static int 504 test_ipfrag(void) 505 { 506 rte_log_set_global_level(RTE_LOG_DEBUG); 507 rte_log_set_level(RTE_LOGTYPE_EAL, RTE_LOG_DEBUG); 508 509 return unit_test_suite_runner(&ipfrag_testsuite); 510 } 511 512 513 REGISTER_FAST_TEST(ipfrag_autotest, false, true, test_ipfrag); 514