1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2010-2014 Intel Corporation 3 */ 4 #include <stdio.h> 5 #include <string.h> 6 #include <inttypes.h> 7 8 #include <rte_string_fns.h> 9 10 #include <cmdline_parse.h> 11 #include <cmdline_parse_ipaddr.h> 12 13 #include "test_cmdline.h" 14 15 #define IP4(a,b,c,d) {.s_addr = (uint32_t)(((a) & 0xff) | \ 16 (((b) & 0xff) << 8) | \ 17 (((c) & 0xff) << 16) | \ 18 ((d) & 0xff) << 24)} 19 20 #define IP6(a, b, c, d, e, f, g, h) .ipv6 = RTE_IPV6(a, b, c, d, e, f, g, h) 21 22 /** these are defined in netinet/in.h but not present in linux headers */ 23 #ifndef NIPQUAD 24 25 #define NIPQUAD_FMT "%u.%u.%u.%u" 26 #define NIPQUAD(addr) \ 27 (unsigned)((unsigned char *)&addr)[0], \ 28 (unsigned)((unsigned char *)&addr)[1], \ 29 (unsigned)((unsigned char *)&addr)[2], \ 30 (unsigned)((unsigned char *)&addr)[3] 31 #endif 32 33 struct ipaddr_str { 34 const char * str; 35 cmdline_ipaddr_t addr; 36 unsigned flags; 37 }; 38 39 const struct ipaddr_str ipaddr_valid_strs[] = { 40 {"0.0.0.0", {AF_INET, {IP4(0,0,0,0)}, 0}, 41 CMDLINE_IPADDR_V4}, 42 {"0.0.0.0/0", {AF_INET, {IP4(0,0,0,0)}, 0}, 43 CMDLINE_IPADDR_V4 | CMDLINE_IPADDR_NETWORK}, 44 {"0.0.0.0/24", {AF_INET, {IP4(0,0,0,0)}, 24}, 45 CMDLINE_IPADDR_V4 | CMDLINE_IPADDR_NETWORK}, 46 {"192.168.1.0/24", {AF_INET, {IP4(192,168,1,0)}, 24}, 47 CMDLINE_IPADDR_V4 | CMDLINE_IPADDR_NETWORK}, 48 {"34.56.78.90/1", {AF_INET, {IP4(34,56,78,90)}, 1}, 49 CMDLINE_IPADDR_V4 | CMDLINE_IPADDR_NETWORK}, 50 {"::", {AF_INET6, {IP6(0,0,0,0,0,0,0,0)}, 0}, 51 CMDLINE_IPADDR_V6}, 52 {"::1", {AF_INET6, {IP6(0,0,0,0,0,0,0,1)}, 0}, 53 CMDLINE_IPADDR_V6}, 54 {"::1/32", {AF_INET6, {IP6(0,0,0,0,0,0,0,1)}, 32}, 55 CMDLINE_IPADDR_V6 | CMDLINE_IPADDR_NETWORK}, 56 {"::/32", {AF_INET6, {IP6(0,0,0,0,0,0,0,0)}, 32}, 57 CMDLINE_IPADDR_V6 | CMDLINE_IPADDR_NETWORK}, 58 /* RFC5952 requests that only lowercase should be used */ 59 {"1234:5678:90ab:cdef:4321:8765:BA09:FEDC", {AF_INET6, 60 {IP6(0x1234,0x5678,0x90AB,0xCDEF,0x4321,0x8765,0xBA09,0xFEDC)}, 61 0}, 62 CMDLINE_IPADDR_V6}, 63 {"1234::1234/64", {AF_INET6, 64 {IP6(0x1234,0,0,0,0,0,0,0x1234)}, 65 64}, 66 CMDLINE_IPADDR_V6 | CMDLINE_IPADDR_NETWORK}, 67 {"1234::/64", {AF_INET6, 68 {IP6(0x1234,0,0,0,0,0,0,0)}, 69 64}, 70 CMDLINE_IPADDR_V6 | CMDLINE_IPADDR_NETWORK}, 71 {"1:1::1/32", {AF_INET6, 72 {IP6(1,1,0,0,0,0,0,1)}, 73 32}, 74 CMDLINE_IPADDR_V6 | CMDLINE_IPADDR_NETWORK}, 75 {"1:2:3:4::/64", {AF_INET6, 76 {IP6(1,2,3,4,0,0,0,0)}, 77 64}, 78 CMDLINE_IPADDR_V6 | CMDLINE_IPADDR_NETWORK}, 79 {"::ffff:192.168.1.0/64", {AF_INET6, 80 {IP6(0,0,0,0,0,0xFFFF,0xC0A8,0x100)}, 81 64}, 82 CMDLINE_IPADDR_V6 | CMDLINE_IPADDR_NETWORK}, 83 /* RFC5952 requests not using :: to skip one block of zeros*/ 84 {"1::2:3:4:5:6:7", {AF_INET6, 85 {IP6(1,0,2,3,4,5,6,7)}, 86 0}, 87 CMDLINE_IPADDR_V6}, 88 }; 89 90 const char * ipaddr_garbage_addr4_strs[] = { 91 /* IPv4 */ 92 "192.168.1.0 garbage", 93 "192.168.1.0\0garbage", 94 "192.168.1.0#garbage", 95 "192.168.1.0\tgarbage", 96 "192.168.1.0\rgarbage", 97 "192.168.1.0\ngarbage", 98 }; 99 #define IPv4_GARBAGE_ADDR IP4(192,168,1,0) 100 101 const char * ipaddr_garbage_addr6_strs[] = { 102 /* IPv6 */ 103 "1:2:3:4::8 garbage", 104 "1:2:3:4::8#garbage", 105 "1:2:3:4::8\0garbage", 106 "1:2:3:4::8\rgarbage", 107 "1:2:3:4::8\ngarbage", 108 "1:2:3:4::8\tgarbage", 109 }; 110 #define IPv6_GARBAGE_ADDR {IP6(1,2,3,4,0,0,0,8)} 111 112 const char * ipaddr_garbage_network4_strs[] = { 113 /* IPv4 */ 114 "192.168.1.0/24 garbage", 115 "192.168.1.0/24\0garbage", 116 "192.168.1.0/24#garbage", 117 "192.168.1.0/24\tgarbage", 118 "192.168.1.0/24\rgarbage", 119 "192.168.1.0/24\ngarbage", 120 }; 121 #define IPv4_GARBAGE_PREFIX 24 122 123 const char * ipaddr_garbage_network6_strs[] = { 124 /* IPv6 */ 125 "1:2:3:4::8/64 garbage", 126 "1:2:3:4::8/64#garbage", 127 "1:2:3:4::8/64\0garbage", 128 "1:2:3:4::8/64\rgarbage", 129 "1:2:3:4::8/64\ngarbage", 130 "1:2:3:4::8/64\tgarbage", 131 }; 132 #define IPv6_GARBAGE_PREFIX 64 133 134 const char * ipaddr_invalid_strs[] = { 135 /** IPv4 **/ 136 137 /* invalid numbers */ 138 "0.0.0.-1", 139 "0.0.-1.0", 140 "0.-1.0.0", 141 "-1.0.0.0", 142 "0.0.0.-1/24", 143 "256.123.123.123", 144 "255.256.123.123", 145 "255.255.256.123", 146 "255.255.255.256", 147 "256.123.123.123/24", 148 "255.256.123.123/24", 149 "255.255.256.123/24", 150 "255.255.255.256/24", 151 /* invalid network mask */ 152 "1.2.3.4/33", 153 "1.2.3.4/33231313", 154 "1.2.3.4/-1", 155 "1.2.3.4/24/33", 156 "1.2.3.4/24/-1", 157 "1.2.3.4/24/", 158 /* wrong format */ 159 "1/24" 160 "/24" 161 "123.123.123", 162 "123.123.123.", 163 "123.123.123.123.", 164 "123.123.123..123", 165 "123.123.123.123.123", 166 ".123.123.123", 167 ".123.123.123.123", 168 "123.123.123/24", 169 "123.123.123./24", 170 "123.123.123.123./24", 171 "123.123.123..123/24", 172 "123.123.123.123.123/24", 173 ".123.123.123/24", 174 ".123.123.123.123/24", 175 /* invalid characters */ 176 "123.123.123.12F", 177 "123.123.12F.123", 178 "123.12F.123.123", 179 "12F.123.123.123", 180 "12J.123.123.123", 181 "123,123,123,123", 182 "123!123!123!12F", 183 "123.123.123.123/4F", 184 185 /** IPv6 **/ 186 187 /* wrong format */ 188 "::fffff", 189 "ffff:", 190 "1:2:3:4:5:6:7:192.168.1.1", 191 "1234:192.168.1.1:ffff::", 192 "1:2:3:4:5:6:7:890ab", 193 "1:2:3:4:5:6:7890a:b", 194 "1:2:3:4:5:67890:a:b", 195 "1:2:3:4:56789:0:a:b", 196 "1:2:3:45678:9:0:a:b", 197 "1:2:34567:8:9:0:a:b", 198 "1:23456:7:8:9:0:a:b", 199 "12345:6:7:8:9:0:a:b", 200 "1:::2", 201 "1::::2", 202 "::fffff/64", 203 "1::2::3", 204 "1::2::3/64", 205 ":1:2", 206 ":1:2/64", 207 ":1::2", 208 ":1::2/64", 209 "1::2:3:4:5:6:7:8/64", 210 211 /* invalid network mask */ 212 "1:2:3:4:5:6:7:8/129", 213 "1:2:3:4:5:6:7:8/-1", 214 215 /* invalid characters */ 216 "a:b:c:d:e:f:g::", 217 218 /** misc **/ 219 220 /* too long */ 221 "1234:1234:1234:1234:1234:1234:1234:1234:1234:1234:1234", 222 "random invalid text", 223 "", 224 "\0", 225 " ", 226 }; 227 228 static void 229 dump_addr(cmdline_ipaddr_t addr) 230 { 231 switch (addr.family) { 232 case AF_INET: 233 { 234 printf(NIPQUAD_FMT " prefixlen=%u\n", 235 NIPQUAD(addr.addr.ipv4.s_addr), addr.prefixlen); 236 break; 237 } 238 case AF_INET6: 239 { 240 printf(RTE_IPV6_ADDR_FMT " prefixlen=%u\n", 241 RTE_IPV6_ADDR_SPLIT(&addr.addr.ipv6), addr.prefixlen); 242 break; 243 } 244 default: 245 printf("Can't dump: unknown address family.\n"); 246 return; 247 } 248 } 249 250 251 static int 252 is_addr_different(cmdline_ipaddr_t addr1, cmdline_ipaddr_t addr2) 253 { 254 if (addr1.family != addr2.family) 255 return 1; 256 257 if (addr1.prefixlen != addr2.prefixlen) 258 return 1; 259 260 switch (addr1.family) { 261 /* IPv4 */ 262 case AF_INET: 263 if (memcmp(&addr1.addr.ipv4, &addr2.addr.ipv4, 264 sizeof(struct in_addr)) != 0) 265 return 1; 266 break; 267 /* IPv6 */ 268 case AF_INET6: 269 { 270 if (!rte_ipv6_addr_eq(&addr1.addr.ipv6, &addr2.addr.ipv6)) 271 return 1; 272 break; 273 } 274 /* thing that should not be */ 275 default: 276 return -1; 277 } 278 return 0; 279 } 280 281 static int 282 can_parse_addr(unsigned addr_flags, unsigned test_flags) 283 { 284 if ((test_flags & addr_flags) == addr_flags) { 285 /* if we are not trying to parse network addresses */ 286 if (test_flags < CMDLINE_IPADDR_NETWORK) 287 return 1; 288 /* if this is a network address */ 289 else if (addr_flags & CMDLINE_IPADDR_NETWORK) 290 return 1; 291 } 292 return 0; 293 } 294 295 int 296 test_parse_ipaddr_valid(void) 297 { 298 cmdline_parse_token_ipaddr_t token; 299 char buf[CMDLINE_TEST_BUFSIZE]; 300 cmdline_ipaddr_t result; 301 unsigned i; 302 uint8_t flags; 303 int ret; 304 305 /* cover all cases in help */ 306 for (flags = 0x1; flags < 0x8; flags++) { 307 token.ipaddr_data.flags = flags; 308 309 memset(buf, 0, sizeof(buf)); 310 311 if (cmdline_get_help_ipaddr((cmdline_parse_token_hdr_t*)&token, 312 buf, sizeof(buf)) == -1) { 313 printf("Error: help rejected valid parameters!\n"); 314 return -1; 315 } 316 } 317 318 /* test valid strings */ 319 for (i = 0; i < RTE_DIM(ipaddr_valid_strs); i++) { 320 321 /* test each valid string against different flags */ 322 for (flags = 1; flags < 0x8; flags++) { 323 324 /* skip bad flag */ 325 if (flags == CMDLINE_IPADDR_NETWORK) 326 continue; 327 328 /* clear out everything */ 329 memset(buf, 0, sizeof(buf)); 330 memset(&result, 0, sizeof(result)); 331 memset(&token, 0, sizeof(token)); 332 333 token.ipaddr_data.flags = flags; 334 335 cmdline_get_help_ipaddr((cmdline_parse_token_hdr_t*)&token, 336 buf, sizeof(buf)); 337 338 ret = cmdline_parse_ipaddr((cmdline_parse_token_hdr_t*)&token, 339 ipaddr_valid_strs[i].str, (void*)&result, 340 sizeof(result)); 341 342 /* if should have passed, or should have failed */ 343 if ((ret < 0) == 344 (can_parse_addr(ipaddr_valid_strs[i].flags, flags))) { 345 printf("Error: unexpected behavior when parsing %s as %s!\n", 346 ipaddr_valid_strs[i].str, buf); 347 printf("Parsed result: "); 348 dump_addr(result); 349 printf("Expected result: "); 350 dump_addr(ipaddr_valid_strs[i].addr); 351 return -1; 352 } 353 if (ret != -1 && 354 is_addr_different(result, ipaddr_valid_strs[i].addr)) { 355 printf("Error: result mismatch when parsing %s as %s!\n", 356 ipaddr_valid_strs[i].str, buf); 357 printf("Parsed result: "); 358 dump_addr(result); 359 printf("Expected result: "); 360 dump_addr(ipaddr_valid_strs[i].addr); 361 return -1; 362 } 363 } 364 } 365 366 /* test garbage ipv4 address strings */ 367 for (i = 0; i < RTE_DIM(ipaddr_garbage_addr4_strs); i++) { 368 369 struct in_addr tmp = IPv4_GARBAGE_ADDR; 370 371 /* test each valid string against different flags */ 372 for (flags = 1; flags < 0x8; flags++) { 373 374 /* skip bad flag */ 375 if (flags == CMDLINE_IPADDR_NETWORK) 376 continue; 377 378 /* clear out everything */ 379 memset(buf, 0, sizeof(buf)); 380 memset(&result, 0, sizeof(result)); 381 memset(&token, 0, sizeof(token)); 382 383 token.ipaddr_data.flags = flags; 384 385 cmdline_get_help_ipaddr((cmdline_parse_token_hdr_t*)&token, 386 buf, sizeof(buf)); 387 388 ret = cmdline_parse_ipaddr((cmdline_parse_token_hdr_t*)&token, 389 ipaddr_garbage_addr4_strs[i], (void*)&result, 390 sizeof(result)); 391 392 /* if should have passed, or should have failed */ 393 if ((ret < 0) == 394 (can_parse_addr(CMDLINE_IPADDR_V4, flags))) { 395 printf("Error: unexpected behavior when parsing %s as %s!\n", 396 ipaddr_garbage_addr4_strs[i], buf); 397 return -1; 398 } 399 if (ret != -1 && 400 memcmp(&result.addr.ipv4, &tmp, sizeof(tmp))) { 401 printf("Error: result mismatch when parsing %s as %s!\n", 402 ipaddr_garbage_addr4_strs[i], buf); 403 return -1; 404 } 405 } 406 } 407 408 /* test garbage ipv6 address strings */ 409 for (i = 0; i < RTE_DIM(ipaddr_garbage_addr6_strs); i++) { 410 411 cmdline_ipaddr_t tmp = {.addr = IPv6_GARBAGE_ADDR}; 412 413 /* test each valid string against different flags */ 414 for (flags = 1; flags < 0x8; flags++) { 415 416 /* skip bad flag */ 417 if (flags == CMDLINE_IPADDR_NETWORK) 418 continue; 419 420 /* clear out everything */ 421 memset(buf, 0, sizeof(buf)); 422 memset(&result, 0, sizeof(result)); 423 memset(&token, 0, sizeof(token)); 424 425 token.ipaddr_data.flags = flags; 426 427 cmdline_get_help_ipaddr((cmdline_parse_token_hdr_t*)&token, 428 buf, sizeof(buf)); 429 430 ret = cmdline_parse_ipaddr((cmdline_parse_token_hdr_t*)&token, 431 ipaddr_garbage_addr6_strs[i], (void*)&result, 432 sizeof(result)); 433 434 /* if should have passed, or should have failed */ 435 if ((ret < 0) == 436 (can_parse_addr(CMDLINE_IPADDR_V6, flags))) { 437 printf("Error: unexpected behavior when parsing %s as %s!\n", 438 ipaddr_garbage_addr6_strs[i], buf); 439 return -1; 440 } 441 if (ret != -1 && 442 !rte_ipv6_addr_eq(&result.addr.ipv6, &tmp.addr.ipv6)) { 443 printf("Error: result mismatch when parsing %s as %s!\n", 444 ipaddr_garbage_addr6_strs[i], buf); 445 return -1; 446 } 447 } 448 } 449 450 451 /* test garbage ipv4 network strings */ 452 for (i = 0; i < RTE_DIM(ipaddr_garbage_network4_strs); i++) { 453 454 struct in_addr tmp = IPv4_GARBAGE_ADDR; 455 456 /* test each valid string against different flags */ 457 for (flags = 1; flags < 0x8; flags++) { 458 459 /* skip bad flag */ 460 if (flags == CMDLINE_IPADDR_NETWORK) 461 continue; 462 463 /* clear out everything */ 464 memset(buf, 0, sizeof(buf)); 465 memset(&result, 0, sizeof(result)); 466 memset(&token, 0, sizeof(token)); 467 468 token.ipaddr_data.flags = flags; 469 470 cmdline_get_help_ipaddr((cmdline_parse_token_hdr_t*)&token, 471 buf, sizeof(buf)); 472 473 ret = cmdline_parse_ipaddr((cmdline_parse_token_hdr_t*)&token, 474 ipaddr_garbage_network4_strs[i], (void*)&result, 475 sizeof(result)); 476 477 /* if should have passed, or should have failed */ 478 if ((ret < 0) == 479 (can_parse_addr(CMDLINE_IPADDR_V4 | CMDLINE_IPADDR_NETWORK, flags))) { 480 printf("Error: unexpected behavior when parsing %s as %s!\n", 481 ipaddr_garbage_network4_strs[i], buf); 482 return -1; 483 } 484 if (ret != -1 && 485 memcmp(&result.addr.ipv4, &tmp, sizeof(tmp))) { 486 printf("Error: result mismatch when parsing %s as %s!\n", 487 ipaddr_garbage_network4_strs[i], buf); 488 return -1; 489 } 490 } 491 } 492 493 /* test garbage ipv6 address strings */ 494 for (i = 0; i < RTE_DIM(ipaddr_garbage_network6_strs); i++) { 495 496 cmdline_ipaddr_t tmp = {.addr = IPv6_GARBAGE_ADDR}; 497 498 /* test each valid string against different flags */ 499 for (flags = 1; flags < 0x8; flags++) { 500 501 /* skip bad flag */ 502 if (flags == CMDLINE_IPADDR_NETWORK) 503 continue; 504 505 /* clear out everything */ 506 memset(buf, 0, sizeof(buf)); 507 memset(&result, 0, sizeof(result)); 508 memset(&token, 0, sizeof(token)); 509 510 token.ipaddr_data.flags = flags; 511 512 cmdline_get_help_ipaddr((cmdline_parse_token_hdr_t*)&token, 513 buf, sizeof(buf)); 514 515 ret = cmdline_parse_ipaddr((cmdline_parse_token_hdr_t*)&token, 516 ipaddr_garbage_network6_strs[i], (void*)&result, 517 sizeof(result)); 518 519 /* if should have passed, or should have failed */ 520 if ((ret < 0) == 521 (can_parse_addr(CMDLINE_IPADDR_V6 | CMDLINE_IPADDR_NETWORK, flags))) { 522 printf("Error: unexpected behavior when parsing %s as %s!\n", 523 ipaddr_garbage_network6_strs[i], buf); 524 return -1; 525 } 526 if (ret != -1 && 527 !rte_ipv6_addr_eq(&result.addr.ipv6, &tmp.addr.ipv6)) { 528 printf("Error: result mismatch when parsing %s as %s!\n", 529 ipaddr_garbage_network6_strs[i], buf); 530 return -1; 531 } 532 } 533 } 534 535 return 0; 536 } 537 538 int 539 test_parse_ipaddr_invalid_data(void) 540 { 541 cmdline_parse_token_ipaddr_t token; 542 char buf[CMDLINE_TEST_BUFSIZE]; 543 cmdline_ipaddr_t result; 544 unsigned i; 545 uint8_t flags; 546 int ret; 547 548 memset(&result, 0, sizeof(result)); 549 550 /* test invalid strings */ 551 for (i = 0; i < RTE_DIM(ipaddr_invalid_strs); i++) { 552 553 /* test each valid string against different flags */ 554 for (flags = 1; flags < 0x8; flags++) { 555 556 /* skip bad flag */ 557 if (flags == CMDLINE_IPADDR_NETWORK) 558 continue; 559 560 /* clear out everything */ 561 memset(buf, 0, sizeof(buf)); 562 memset(&token, 0, sizeof(token)); 563 564 token.ipaddr_data.flags = flags; 565 566 cmdline_get_help_ipaddr((cmdline_parse_token_hdr_t*)&token, 567 buf, sizeof(buf)); 568 569 ret = cmdline_parse_ipaddr((cmdline_parse_token_hdr_t*)&token, 570 ipaddr_invalid_strs[i], (void*)&result, 571 sizeof(result)); 572 573 if (ret != -1) { 574 printf("Error: parsing %s as %s succeeded!\n", 575 ipaddr_invalid_strs[i], buf); 576 printf("Parsed result: "); 577 dump_addr(result); 578 return -1; 579 } 580 } 581 } 582 583 return 0; 584 } 585 586 int 587 test_parse_ipaddr_invalid_param(void) 588 { 589 cmdline_parse_token_ipaddr_t token; 590 char buf[CMDLINE_TEST_BUFSIZE]; 591 cmdline_ipaddr_t result; 592 593 snprintf(buf, sizeof(buf), "1.2.3.4"); 594 token.ipaddr_data.flags = CMDLINE_IPADDR_V4; 595 596 /* null token */ 597 if (cmdline_parse_ipaddr(NULL, buf, (void*)&result, 598 sizeof(result)) != -1) { 599 printf("Error: parser accepted invalid parameters!\n"); 600 return -1; 601 } 602 /* null buffer */ 603 if (cmdline_parse_ipaddr((cmdline_parse_token_hdr_t*)&token, 604 NULL, (void*)&result, sizeof(result)) != -1) { 605 printf("Error: parser accepted invalid parameters!\n"); 606 return -1; 607 } 608 /* empty buffer */ 609 if (cmdline_parse_ipaddr((cmdline_parse_token_hdr_t*)&token, 610 "", (void*)&result, sizeof(result)) != -1) { 611 printf("Error: parser accepted invalid parameters!\n"); 612 return -1; 613 } 614 /* null result */ 615 if (cmdline_parse_ipaddr((cmdline_parse_token_hdr_t*)&token, 616 buf, NULL, 0) == -1) { 617 printf("Error: parser rejected null result!\n"); 618 return -1; 619 } 620 621 /* null token */ 622 if (cmdline_get_help_ipaddr(NULL, buf, 0) != -1) { 623 printf("Error: help accepted invalid parameters!\n"); 624 return -1; 625 } 626 /* null buffer */ 627 if (cmdline_get_help_ipaddr((cmdline_parse_token_hdr_t*)&token, 628 NULL, 0) != -1) { 629 printf("Error: help accepted invalid parameters!\n"); 630 return -1; 631 } 632 return 0; 633 } 634