675 lines · c
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 17struct 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 synchronized33 * with configurations inside ioam6.sh and always reflect the same.34 */35 36static 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 50static 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 64static 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 78enum {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 128static 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 201static 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 369static 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 440static 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 514static 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 536static 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 558static 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 595int 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;629recv: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;669close:670 free(iov.iov_base);671 close(fd);672out:673 return ret;674}675