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