1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Author: Justin Iurman (justin.iurman@uliege.be) 4 * 5 * IOAM tester for IPv6, see ioam6.sh for details on each test case. 6 */ 7 #include <arpa/inet.h> 8 #include <errno.h> 9 #include <limits.h> 10 #include <linux/const.h> 11 #include <linux/ioam6.h> 12 #include <linux/ipv6.h> 13 #include <stdlib.h> 14 #include <string.h> 15 #include <unistd.h> 16 17 struct ioam_config { 18 __u32 id; 19 __u64 wide; 20 __u16 ingr_id; 21 __u16 egr_id; 22 __u32 ingr_wide; 23 __u32 egr_wide; 24 __u32 ns_data; 25 __u64 ns_wide; 26 __u32 sc_id; 27 __u8 hlim; 28 char *sc_data; 29 }; 30 31 /* 32 * Be careful if you modify structs below - everything MUST be kept synchronized 33 * with configurations inside ioam6.sh and always reflect the same. 34 */ 35 36 static struct ioam_config node1 = { 37 .id = 1, 38 .wide = 11111111, 39 .ingr_id = 0xffff, /* default value */ 40 .egr_id = 101, 41 .ingr_wide = 0xffffffff, /* default value */ 42 .egr_wide = 101101, 43 .ns_data = 0xdeadbee0, 44 .ns_wide = 0xcafec0caf00dc0de, 45 .sc_id = 777, 46 .sc_data = "something that will be 4n-aligned", 47 .hlim = 64, 48 }; 49 50 static struct ioam_config node2 = { 51 .id = 2, 52 .wide = 22222222, 53 .ingr_id = 201, 54 .egr_id = 202, 55 .ingr_wide = 201201, 56 .egr_wide = 202202, 57 .ns_data = 0xdeadbee1, 58 .ns_wide = 0xcafec0caf11dc0de, 59 .sc_id = 666, 60 .sc_data = "Hello there -Obi", 61 .hlim = 63, 62 }; 63 64 static struct ioam_config node3 = { 65 .id = 3, 66 .wide = 33333333, 67 .ingr_id = 301, 68 .egr_id = 0xffff, /* default value */ 69 .ingr_wide = 301301, 70 .egr_wide = 0xffffffff, /* default value */ 71 .ns_data = 0xdeadbee2, 72 .ns_wide = 0xcafec0caf22dc0de, 73 .sc_id = 0xffffff, /* default value */ 74 .sc_data = NULL, 75 .hlim = 62, 76 }; 77 78 enum { 79 /********** 80 * OUTPUT * 81 **********/ 82 TEST_OUT_UNDEF_NS, 83 TEST_OUT_NO_ROOM, 84 TEST_OUT_BIT0, 85 TEST_OUT_BIT1, 86 TEST_OUT_BIT2, 87 TEST_OUT_BIT3, 88 TEST_OUT_BIT4, 89 TEST_OUT_BIT5, 90 TEST_OUT_BIT6, 91 TEST_OUT_BIT7, 92 TEST_OUT_BIT8, 93 TEST_OUT_BIT9, 94 TEST_OUT_BIT10, 95 TEST_OUT_BIT11, 96 TEST_OUT_BIT22, 97 TEST_OUT_FULL_SUPP_TRACE, 98 99 /********* 100 * INPUT * 101 *********/ 102 TEST_IN_UNDEF_NS, 103 TEST_IN_NO_ROOM, 104 TEST_IN_OFLAG, 105 TEST_IN_BIT0, 106 TEST_IN_BIT1, 107 TEST_IN_BIT2, 108 TEST_IN_BIT3, 109 TEST_IN_BIT4, 110 TEST_IN_BIT5, 111 TEST_IN_BIT6, 112 TEST_IN_BIT7, 113 TEST_IN_BIT8, 114 TEST_IN_BIT9, 115 TEST_IN_BIT10, 116 TEST_IN_BIT11, 117 TEST_IN_BIT22, 118 TEST_IN_FULL_SUPP_TRACE, 119 120 /********** 121 * GLOBAL * 122 **********/ 123 TEST_FWD_FULL_SUPP_TRACE, 124 125 __TEST_MAX, 126 }; 127 128 static int check_ioam_header(int tid, struct ioam6_trace_hdr *ioam6h, 129 __u32 trace_type, __u16 ioam_ns) 130 { 131 if (__be16_to_cpu(ioam6h->namespace_id) != ioam_ns || 132 __be32_to_cpu(ioam6h->type_be32) != (trace_type << 8)) 133 return 1; 134 135 switch (tid) { 136 case TEST_OUT_UNDEF_NS: 137 case TEST_IN_UNDEF_NS: 138 return ioam6h->overflow || 139 ioam6h->nodelen != 1 || 140 ioam6h->remlen != 1; 141 142 case TEST_OUT_NO_ROOM: 143 case TEST_IN_NO_ROOM: 144 case TEST_IN_OFLAG: 145 return !ioam6h->overflow || 146 ioam6h->nodelen != 2 || 147 ioam6h->remlen != 1; 148 149 case TEST_OUT_BIT0: 150 case TEST_IN_BIT0: 151 case TEST_OUT_BIT1: 152 case TEST_IN_BIT1: 153 case TEST_OUT_BIT2: 154 case TEST_IN_BIT2: 155 case TEST_OUT_BIT3: 156 case TEST_IN_BIT3: 157 case TEST_OUT_BIT4: 158 case TEST_IN_BIT4: 159 case TEST_OUT_BIT5: 160 case TEST_IN_BIT5: 161 case TEST_OUT_BIT6: 162 case TEST_IN_BIT6: 163 case TEST_OUT_BIT7: 164 case TEST_IN_BIT7: 165 case TEST_OUT_BIT11: 166 case TEST_IN_BIT11: 167 return ioam6h->overflow || 168 ioam6h->nodelen != 1 || 169 ioam6h->remlen; 170 171 case TEST_OUT_BIT8: 172 case TEST_IN_BIT8: 173 case TEST_OUT_BIT9: 174 case TEST_IN_BIT9: 175 case TEST_OUT_BIT10: 176 case TEST_IN_BIT10: 177 return ioam6h->overflow || 178 ioam6h->nodelen != 2 || 179 ioam6h->remlen; 180 181 case TEST_OUT_BIT22: 182 case TEST_IN_BIT22: 183 return ioam6h->overflow || 184 ioam6h->nodelen || 185 ioam6h->remlen; 186 187 case TEST_OUT_FULL_SUPP_TRACE: 188 case TEST_IN_FULL_SUPP_TRACE: 189 case TEST_FWD_FULL_SUPP_TRACE: 190 return ioam6h->overflow || 191 ioam6h->nodelen != 15 || 192 ioam6h->remlen; 193 194 default: 195 break; 196 } 197 198 return 1; 199 } 200 201 static int check_ioam6_data(__u8 **p, struct ioam6_trace_hdr *ioam6h, 202 const struct ioam_config cnf) 203 { 204 unsigned int len; 205 __u8 aligned; 206 __u64 raw64; 207 __u32 raw32; 208 209 if (ioam6h->type.bit0) { 210 raw32 = __be32_to_cpu(*((__u32 *)*p)); 211 if (cnf.hlim != (raw32 >> 24) || cnf.id != (raw32 & 0xffffff)) 212 return 1; 213 *p += sizeof(__u32); 214 } 215 216 if (ioam6h->type.bit1) { 217 raw32 = __be32_to_cpu(*((__u32 *)*p)); 218 if (cnf.ingr_id != (raw32 >> 16) || 219 cnf.egr_id != (raw32 & 0xffff)) 220 return 1; 221 *p += sizeof(__u32); 222 } 223 224 if (ioam6h->type.bit2) 225 *p += sizeof(__u32); 226 227 if (ioam6h->type.bit3) 228 *p += sizeof(__u32); 229 230 if (ioam6h->type.bit4) { 231 if (__be32_to_cpu(*((__u32 *)*p)) != 0xffffffff) 232 return 1; 233 *p += sizeof(__u32); 234 } 235 236 if (ioam6h->type.bit5) { 237 if (__be32_to_cpu(*((__u32 *)*p)) != cnf.ns_data) 238 return 1; 239 *p += sizeof(__u32); 240 } 241 242 if (ioam6h->type.bit6) 243 *p += sizeof(__u32); 244 245 if (ioam6h->type.bit7) { 246 if (__be32_to_cpu(*((__u32 *)*p)) != 0xffffffff) 247 return 1; 248 *p += sizeof(__u32); 249 } 250 251 if (ioam6h->type.bit8) { 252 raw64 = __be64_to_cpu(*((__u64 *)*p)); 253 if (cnf.hlim != (raw64 >> 56) || 254 cnf.wide != (raw64 & 0xffffffffffffff)) 255 return 1; 256 *p += sizeof(__u64); 257 } 258 259 if (ioam6h->type.bit9) { 260 if (__be32_to_cpu(*((__u32 *)*p)) != cnf.ingr_wide) 261 return 1; 262 *p += sizeof(__u32); 263 264 if (__be32_to_cpu(*((__u32 *)*p)) != cnf.egr_wide) 265 return 1; 266 *p += sizeof(__u32); 267 } 268 269 if (ioam6h->type.bit10) { 270 if (__be64_to_cpu(*((__u64 *)*p)) != cnf.ns_wide) 271 return 1; 272 *p += sizeof(__u64); 273 } 274 275 if (ioam6h->type.bit11) { 276 if (__be32_to_cpu(*((__u32 *)*p)) != 0xffffffff) 277 return 1; 278 *p += sizeof(__u32); 279 } 280 281 if (ioam6h->type.bit12) { 282 if (__be32_to_cpu(*((__u32 *)*p)) != 0xffffffff) 283 return 1; 284 *p += sizeof(__u32); 285 } 286 287 if (ioam6h->type.bit13) { 288 if (__be32_to_cpu(*((__u32 *)*p)) != 0xffffffff) 289 return 1; 290 *p += sizeof(__u32); 291 } 292 293 if (ioam6h->type.bit14) { 294 if (__be32_to_cpu(*((__u32 *)*p)) != 0xffffffff) 295 return 1; 296 *p += sizeof(__u32); 297 } 298 299 if (ioam6h->type.bit15) { 300 if (__be32_to_cpu(*((__u32 *)*p)) != 0xffffffff) 301 return 1; 302 *p += sizeof(__u32); 303 } 304 305 if (ioam6h->type.bit16) { 306 if (__be32_to_cpu(*((__u32 *)*p)) != 0xffffffff) 307 return 1; 308 *p += sizeof(__u32); 309 } 310 311 if (ioam6h->type.bit17) { 312 if (__be32_to_cpu(*((__u32 *)*p)) != 0xffffffff) 313 return 1; 314 *p += sizeof(__u32); 315 } 316 317 if (ioam6h->type.bit18) { 318 if (__be32_to_cpu(*((__u32 *)*p)) != 0xffffffff) 319 return 1; 320 *p += sizeof(__u32); 321 } 322 323 if (ioam6h->type.bit19) { 324 if (__be32_to_cpu(*((__u32 *)*p)) != 0xffffffff) 325 return 1; 326 *p += sizeof(__u32); 327 } 328 329 if (ioam6h->type.bit20) { 330 if (__be32_to_cpu(*((__u32 *)*p)) != 0xffffffff) 331 return 1; 332 *p += sizeof(__u32); 333 } 334 335 if (ioam6h->type.bit21) { 336 if (__be32_to_cpu(*((__u32 *)*p)) != 0xffffffff) 337 return 1; 338 *p += sizeof(__u32); 339 } 340 341 if (ioam6h->type.bit22) { 342 len = cnf.sc_data ? strlen(cnf.sc_data) : 0; 343 aligned = cnf.sc_data ? __ALIGN_KERNEL(len, 4) : 0; 344 345 raw32 = __be32_to_cpu(*((__u32 *)*p)); 346 if (aligned != (raw32 >> 24) * 4 || 347 cnf.sc_id != (raw32 & 0xffffff)) 348 return 1; 349 *p += sizeof(__u32); 350 351 if (cnf.sc_data) { 352 if (strncmp((char *)*p, cnf.sc_data, len)) 353 return 1; 354 355 *p += len; 356 aligned -= len; 357 358 while (aligned--) { 359 if (**p != '\0') 360 return 1; 361 *p += sizeof(__u8); 362 } 363 } 364 } 365 366 return 0; 367 } 368 369 static int check_ioam_header_and_data(int tid, struct ioam6_trace_hdr *ioam6h, 370 __u32 trace_type, __u16 ioam_ns) 371 { 372 __u8 *p; 373 374 if (check_ioam_header(tid, ioam6h, trace_type, ioam_ns)) 375 return 1; 376 377 p = ioam6h->data + ioam6h->remlen * 4; 378 379 switch (tid) { 380 case TEST_OUT_BIT0: 381 case TEST_OUT_BIT1: 382 case TEST_OUT_BIT2: 383 case TEST_OUT_BIT3: 384 case TEST_OUT_BIT4: 385 case TEST_OUT_BIT5: 386 case TEST_OUT_BIT6: 387 case TEST_OUT_BIT7: 388 case TEST_OUT_BIT8: 389 case TEST_OUT_BIT9: 390 case TEST_OUT_BIT10: 391 case TEST_OUT_BIT11: 392 case TEST_OUT_BIT22: 393 case TEST_OUT_FULL_SUPP_TRACE: 394 return check_ioam6_data(&p, ioam6h, node1); 395 396 case TEST_IN_BIT0: 397 case TEST_IN_BIT1: 398 case TEST_IN_BIT2: 399 case TEST_IN_BIT3: 400 case TEST_IN_BIT4: 401 case TEST_IN_BIT5: 402 case TEST_IN_BIT6: 403 case TEST_IN_BIT7: 404 case TEST_IN_BIT8: 405 case TEST_IN_BIT9: 406 case TEST_IN_BIT10: 407 case TEST_IN_BIT11: 408 case TEST_IN_BIT22: 409 case TEST_IN_FULL_SUPP_TRACE: 410 { 411 __u32 tmp32 = node2.egr_wide; 412 __u16 tmp16 = node2.egr_id; 413 int res; 414 415 node2.egr_id = 0xffff; 416 node2.egr_wide = 0xffffffff; 417 418 res = check_ioam6_data(&p, ioam6h, node2); 419 420 node2.egr_id = tmp16; 421 node2.egr_wide = tmp32; 422 423 return res; 424 } 425 426 case TEST_FWD_FULL_SUPP_TRACE: 427 if (check_ioam6_data(&p, ioam6h, node3)) 428 return 1; 429 if (check_ioam6_data(&p, ioam6h, node2)) 430 return 1; 431 return check_ioam6_data(&p, ioam6h, node1); 432 433 default: 434 break; 435 } 436 437 return 1; 438 } 439 440 static int str2id(const char *tname) 441 { 442 if (!strcmp("out_undef_ns", tname)) 443 return TEST_OUT_UNDEF_NS; 444 if (!strcmp("out_no_room", tname)) 445 return TEST_OUT_NO_ROOM; 446 if (!strcmp("out_bit0", tname)) 447 return TEST_OUT_BIT0; 448 if (!strcmp("out_bit1", tname)) 449 return TEST_OUT_BIT1; 450 if (!strcmp("out_bit2", tname)) 451 return TEST_OUT_BIT2; 452 if (!strcmp("out_bit3", tname)) 453 return TEST_OUT_BIT3; 454 if (!strcmp("out_bit4", tname)) 455 return TEST_OUT_BIT4; 456 if (!strcmp("out_bit5", tname)) 457 return TEST_OUT_BIT5; 458 if (!strcmp("out_bit6", tname)) 459 return TEST_OUT_BIT6; 460 if (!strcmp("out_bit7", tname)) 461 return TEST_OUT_BIT7; 462 if (!strcmp("out_bit8", tname)) 463 return TEST_OUT_BIT8; 464 if (!strcmp("out_bit9", tname)) 465 return TEST_OUT_BIT9; 466 if (!strcmp("out_bit10", tname)) 467 return TEST_OUT_BIT10; 468 if (!strcmp("out_bit11", tname)) 469 return TEST_OUT_BIT11; 470 if (!strcmp("out_bit22", tname)) 471 return TEST_OUT_BIT22; 472 if (!strcmp("out_full_supp_trace", tname)) 473 return TEST_OUT_FULL_SUPP_TRACE; 474 if (!strcmp("in_undef_ns", tname)) 475 return TEST_IN_UNDEF_NS; 476 if (!strcmp("in_no_room", tname)) 477 return TEST_IN_NO_ROOM; 478 if (!strcmp("in_oflag", tname)) 479 return TEST_IN_OFLAG; 480 if (!strcmp("in_bit0", tname)) 481 return TEST_IN_BIT0; 482 if (!strcmp("in_bit1", tname)) 483 return TEST_IN_BIT1; 484 if (!strcmp("in_bit2", tname)) 485 return TEST_IN_BIT2; 486 if (!strcmp("in_bit3", tname)) 487 return TEST_IN_BIT3; 488 if (!strcmp("in_bit4", tname)) 489 return TEST_IN_BIT4; 490 if (!strcmp("in_bit5", tname)) 491 return TEST_IN_BIT5; 492 if (!strcmp("in_bit6", tname)) 493 return TEST_IN_BIT6; 494 if (!strcmp("in_bit7", tname)) 495 return TEST_IN_BIT7; 496 if (!strcmp("in_bit8", tname)) 497 return TEST_IN_BIT8; 498 if (!strcmp("in_bit9", tname)) 499 return TEST_IN_BIT9; 500 if (!strcmp("in_bit10", tname)) 501 return TEST_IN_BIT10; 502 if (!strcmp("in_bit11", tname)) 503 return TEST_IN_BIT11; 504 if (!strcmp("in_bit22", tname)) 505 return TEST_IN_BIT22; 506 if (!strcmp("in_full_supp_trace", tname)) 507 return TEST_IN_FULL_SUPP_TRACE; 508 if (!strcmp("fwd_full_supp_trace", tname)) 509 return TEST_FWD_FULL_SUPP_TRACE; 510 511 return -1; 512 } 513 514 static int get_u32(__u32 *val, const char *arg, int base) 515 { 516 unsigned long res; 517 char *ptr; 518 519 if (!arg || !*arg) 520 return -1; 521 res = strtoul(arg, &ptr, base); 522 523 if (!ptr || ptr == arg || *ptr) 524 return -1; 525 526 if (res == ULONG_MAX && errno == ERANGE) 527 return -1; 528 529 if (res > 0xFFFFFFFFUL) 530 return -1; 531 532 *val = res; 533 return 0; 534 } 535 536 static int get_u16(__u16 *val, const char *arg, int base) 537 { 538 unsigned long res; 539 char *ptr; 540 541 if (!arg || !*arg) 542 return -1; 543 res = strtoul(arg, &ptr, base); 544 545 if (!ptr || ptr == arg || *ptr) 546 return -1; 547 548 if (res == ULONG_MAX && errno == ERANGE) 549 return -1; 550 551 if (res > 0xFFFFUL) 552 return -1; 553 554 *val = res; 555 return 0; 556 } 557 558 static int (*func[__TEST_MAX])(int, struct ioam6_trace_hdr *, __u32, __u16) = { 559 [TEST_OUT_UNDEF_NS] = check_ioam_header, 560 [TEST_OUT_NO_ROOM] = check_ioam_header, 561 [TEST_OUT_BIT0] = check_ioam_header_and_data, 562 [TEST_OUT_BIT1] = check_ioam_header_and_data, 563 [TEST_OUT_BIT2] = check_ioam_header_and_data, 564 [TEST_OUT_BIT3] = check_ioam_header_and_data, 565 [TEST_OUT_BIT4] = check_ioam_header_and_data, 566 [TEST_OUT_BIT5] = check_ioam_header_and_data, 567 [TEST_OUT_BIT6] = check_ioam_header_and_data, 568 [TEST_OUT_BIT7] = check_ioam_header_and_data, 569 [TEST_OUT_BIT8] = check_ioam_header_and_data, 570 [TEST_OUT_BIT9] = check_ioam_header_and_data, 571 [TEST_OUT_BIT10] = check_ioam_header_and_data, 572 [TEST_OUT_BIT11] = check_ioam_header_and_data, 573 [TEST_OUT_BIT22] = check_ioam_header_and_data, 574 [TEST_OUT_FULL_SUPP_TRACE] = check_ioam_header_and_data, 575 [TEST_IN_UNDEF_NS] = check_ioam_header, 576 [TEST_IN_NO_ROOM] = check_ioam_header, 577 [TEST_IN_OFLAG] = check_ioam_header, 578 [TEST_IN_BIT0] = check_ioam_header_and_data, 579 [TEST_IN_BIT1] = check_ioam_header_and_data, 580 [TEST_IN_BIT2] = check_ioam_header_and_data, 581 [TEST_IN_BIT3] = check_ioam_header_and_data, 582 [TEST_IN_BIT4] = check_ioam_header_and_data, 583 [TEST_IN_BIT5] = check_ioam_header_and_data, 584 [TEST_IN_BIT6] = check_ioam_header_and_data, 585 [TEST_IN_BIT7] = check_ioam_header_and_data, 586 [TEST_IN_BIT8] = check_ioam_header_and_data, 587 [TEST_IN_BIT9] = check_ioam_header_and_data, 588 [TEST_IN_BIT10] = check_ioam_header_and_data, 589 [TEST_IN_BIT11] = check_ioam_header_and_data, 590 [TEST_IN_BIT22] = check_ioam_header_and_data, 591 [TEST_IN_FULL_SUPP_TRACE] = check_ioam_header_and_data, 592 [TEST_FWD_FULL_SUPP_TRACE] = check_ioam_header_and_data, 593 }; 594 595 int main(int argc, char **argv) 596 { 597 int fd, size, hoplen, tid, ret = 1, on = 1; 598 struct ioam6_hdr *opt; 599 struct cmsghdr *cmsg; 600 struct msghdr msg; 601 struct iovec iov; 602 __u8 buffer[512]; 603 __u32 tr_type; 604 __u16 ioam_ns; 605 __u8 *ptr; 606 607 if (argc != 5) 608 goto out; 609 610 tid = str2id(argv[1]); 611 if (tid < 0 || !func[tid]) 612 goto out; 613 614 if (get_u32(&tr_type, argv[2], 16) || 615 get_u16(&ioam_ns, argv[3], 0)) 616 goto out; 617 618 fd = socket(PF_INET6, SOCK_RAW, 619 !strcmp(argv[4], "encap") ? IPPROTO_IPV6 : IPPROTO_ICMPV6); 620 if (fd < 0) 621 goto out; 622 623 setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPOPTS, &on, sizeof(on)); 624 625 iov.iov_len = 1; 626 iov.iov_base = malloc(CMSG_SPACE(sizeof(buffer))); 627 if (!iov.iov_base) 628 goto close; 629 recv: 630 memset(&msg, 0, sizeof(msg)); 631 msg.msg_iov = &iov; 632 msg.msg_iovlen = 1; 633 msg.msg_control = buffer; 634 msg.msg_controllen = CMSG_SPACE(sizeof(buffer)); 635 636 size = recvmsg(fd, &msg, 0); 637 if (size <= 0) 638 goto close; 639 640 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { 641 if (cmsg->cmsg_level != IPPROTO_IPV6 || 642 cmsg->cmsg_type != IPV6_HOPOPTS || 643 cmsg->cmsg_len < sizeof(struct ipv6_hopopt_hdr)) 644 continue; 645 646 ptr = (__u8 *)CMSG_DATA(cmsg); 647 648 hoplen = (ptr[1] + 1) << 3; 649 ptr += sizeof(struct ipv6_hopopt_hdr); 650 651 while (hoplen > 0) { 652 opt = (struct ioam6_hdr *)ptr; 653 654 if (opt->opt_type == IPV6_TLV_IOAM && 655 opt->type == IOAM6_TYPE_PREALLOC) { 656 ptr += sizeof(*opt); 657 ret = func[tid](tid, 658 (struct ioam6_trace_hdr *)ptr, 659 tr_type, ioam_ns); 660 goto close; 661 } 662 663 ptr += opt->opt_len + 2; 664 hoplen -= opt->opt_len + 2; 665 } 666 } 667 668 goto recv; 669 close: 670 free(iov.iov_base); 671 close(fd); 672 out: 673 return ret; 674 } 675