// SPDX-License-Identifier: GPL-2.0+ /* * Author: Justin Iurman (justin.iurman@uliege.be) * * IOAM tester for IPv6, see ioam6.sh for details on each test case. */ #include #include #include #include #include #include #include #include #include #include #include struct ioam_config { __u32 id; __u64 wide; __u16 ingr_id; __u16 egr_id; __u32 ingr_wide; __u32 egr_wide; __u32 ns_data; __u64 ns_wide; __u32 sc_id; __u8 hlim; char *sc_data; }; /* * Be careful if you modify structs below - everything MUST be kept synchronized * with configurations inside ioam6.sh and always reflect the same. */ static struct ioam_config node1 = { .id = 1, .wide = 11111111, .ingr_id = 0xffff, /* default value */ .egr_id = 101, .ingr_wide = 0xffffffff, /* default value */ .egr_wide = 101101, .ns_data = 0xdeadbeef, .ns_wide = 0xcafec0caf00dc0de, .sc_id = 777, .sc_data = "something that will be 4n-aligned", .hlim = 64, }; static struct ioam_config node2 = { .id = 2, .wide = 22222222, .ingr_id = 201, .egr_id = 202, .ingr_wide = 201201, .egr_wide = 202202, .ns_data = 0xffffffff, /* default value */ .ns_wide = 0xffffffffffffffff, /* default value */ .sc_id = 0xffffff, /* default value */ .sc_data = NULL, .hlim = 63, }; enum { /********** * OUTPUT * **********/ __TEST_OUT_MIN, TEST_OUT_UNDEF_NS, TEST_OUT_NO_ROOM, TEST_OUT_NO_ROOM_OSS, TEST_OUT_BIT0, TEST_OUT_BIT1, TEST_OUT_BIT2, TEST_OUT_BIT3, TEST_OUT_BIT4, TEST_OUT_BIT5, TEST_OUT_BIT6, TEST_OUT_BIT7, TEST_OUT_BIT8, TEST_OUT_BIT9, TEST_OUT_BIT10, TEST_OUT_BIT11, TEST_OUT_BIT22, TEST_OUT_SIZE4, TEST_OUT_SIZE8, TEST_OUT_SIZE12, TEST_OUT_SIZE16, TEST_OUT_SIZE20, TEST_OUT_SIZE24, TEST_OUT_SIZE28, TEST_OUT_SIZE32, TEST_OUT_SIZE36, TEST_OUT_SIZE40, TEST_OUT_SIZE44, TEST_OUT_SIZE48, TEST_OUT_SIZE52, TEST_OUT_SIZE56, TEST_OUT_SIZE60, TEST_OUT_SIZE64, TEST_OUT_SIZE68, TEST_OUT_SIZE72, TEST_OUT_SIZE76, TEST_OUT_SIZE80, TEST_OUT_SIZE84, TEST_OUT_SIZE88, TEST_OUT_SIZE92, TEST_OUT_SIZE96, TEST_OUT_SIZE100, TEST_OUT_SIZE104, TEST_OUT_SIZE108, TEST_OUT_SIZE112, TEST_OUT_SIZE116, TEST_OUT_SIZE120, TEST_OUT_SIZE124, TEST_OUT_SIZE128, TEST_OUT_SIZE132, TEST_OUT_SIZE136, TEST_OUT_SIZE140, TEST_OUT_SIZE144, TEST_OUT_SIZE148, TEST_OUT_SIZE152, TEST_OUT_SIZE156, TEST_OUT_SIZE160, TEST_OUT_SIZE164, TEST_OUT_SIZE168, TEST_OUT_SIZE172, TEST_OUT_SIZE176, TEST_OUT_SIZE180, TEST_OUT_SIZE184, TEST_OUT_SIZE188, TEST_OUT_SIZE192, TEST_OUT_SIZE196, TEST_OUT_SIZE200, TEST_OUT_SIZE204, TEST_OUT_SIZE208, TEST_OUT_SIZE212, TEST_OUT_SIZE216, TEST_OUT_SIZE220, TEST_OUT_SIZE224, TEST_OUT_SIZE228, TEST_OUT_SIZE232, TEST_OUT_SIZE236, TEST_OUT_SIZE240, TEST_OUT_SIZE244, TEST_OUT_FULL_SUPP_TRACE, __TEST_OUT_MAX, /********* * INPUT * *********/ __TEST_IN_MIN, TEST_IN_UNDEF_NS, TEST_IN_NO_ROOM, TEST_IN_NO_ROOM_OSS, TEST_IN_DISABLED, TEST_IN_OFLAG, TEST_IN_BIT0, TEST_IN_BIT1, TEST_IN_BIT2, TEST_IN_BIT3, TEST_IN_BIT4, TEST_IN_BIT5, TEST_IN_BIT6, TEST_IN_BIT7, TEST_IN_BIT8, TEST_IN_BIT9, TEST_IN_BIT10, TEST_IN_BIT11, TEST_IN_BIT22, TEST_IN_SIZE4, TEST_IN_SIZE8, TEST_IN_SIZE12, TEST_IN_SIZE16, TEST_IN_SIZE20, TEST_IN_SIZE24, TEST_IN_SIZE28, TEST_IN_SIZE32, TEST_IN_SIZE36, TEST_IN_SIZE40, TEST_IN_SIZE44, TEST_IN_SIZE48, TEST_IN_SIZE52, TEST_IN_SIZE56, TEST_IN_SIZE60, TEST_IN_SIZE64, TEST_IN_SIZE68, TEST_IN_SIZE72, TEST_IN_SIZE76, TEST_IN_SIZE80, TEST_IN_SIZE84, TEST_IN_SIZE88, TEST_IN_SIZE92, TEST_IN_SIZE96, TEST_IN_SIZE100, TEST_IN_SIZE104, TEST_IN_SIZE108, TEST_IN_SIZE112, TEST_IN_SIZE116, TEST_IN_SIZE120, TEST_IN_SIZE124, TEST_IN_SIZE128, TEST_IN_SIZE132, TEST_IN_SIZE136, TEST_IN_SIZE140, TEST_IN_SIZE144, TEST_IN_SIZE148, TEST_IN_SIZE152, TEST_IN_SIZE156, TEST_IN_SIZE160, TEST_IN_SIZE164, TEST_IN_SIZE168, TEST_IN_SIZE172, TEST_IN_SIZE176, TEST_IN_SIZE180, TEST_IN_SIZE184, TEST_IN_SIZE188, TEST_IN_SIZE192, TEST_IN_SIZE196, TEST_IN_SIZE200, TEST_IN_SIZE204, TEST_IN_SIZE208, TEST_IN_SIZE212, TEST_IN_SIZE216, TEST_IN_SIZE220, TEST_IN_SIZE224, TEST_IN_SIZE228, TEST_IN_SIZE232, TEST_IN_SIZE236, TEST_IN_SIZE240, TEST_IN_SIZE244, TEST_IN_FULL_SUPP_TRACE, __TEST_IN_MAX, __TEST_MAX, }; static int check_header(int tid, struct ioam6_trace_hdr *trace, __u32 trace_type, __u8 trace_size, __u16 ioam_ns) { if (__be16_to_cpu(trace->namespace_id) != ioam_ns || __be32_to_cpu(trace->type_be32) != (trace_type << 8)) return 1; switch (tid) { case TEST_OUT_UNDEF_NS: case TEST_IN_UNDEF_NS: case TEST_IN_DISABLED: return trace->overflow == 1 || trace->nodelen != 1 || trace->remlen != 1; case TEST_OUT_NO_ROOM: case TEST_IN_NO_ROOM: case TEST_IN_OFLAG: return trace->overflow == 0 || trace->nodelen != 2 || trace->remlen != 1; case TEST_OUT_NO_ROOM_OSS: return trace->overflow == 0 || trace->nodelen != 0 || trace->remlen != 1; case TEST_IN_NO_ROOM_OSS: case TEST_OUT_BIT22: case TEST_IN_BIT22: return trace->overflow == 1 || trace->nodelen != 0 || trace->remlen != 0; case TEST_OUT_BIT0: case TEST_IN_BIT0: case TEST_OUT_BIT1: case TEST_IN_BIT1: case TEST_OUT_BIT2: case TEST_IN_BIT2: case TEST_OUT_BIT3: case TEST_IN_BIT3: case TEST_OUT_BIT4: case TEST_IN_BIT4: case TEST_OUT_BIT5: case TEST_IN_BIT5: case TEST_OUT_BIT6: case TEST_IN_BIT6: case TEST_OUT_BIT7: case TEST_IN_BIT7: case TEST_OUT_BIT11: case TEST_IN_BIT11: return trace->overflow == 1 || trace->nodelen != 1 || trace->remlen != 0; case TEST_OUT_BIT8: case TEST_IN_BIT8: case TEST_OUT_BIT9: case TEST_IN_BIT9: case TEST_OUT_BIT10: case TEST_IN_BIT10: return trace->overflow == 1 || trace->nodelen != 2 || trace->remlen != 0; case TEST_OUT_SIZE4: case TEST_OUT_SIZE8: case TEST_OUT_SIZE12: case TEST_OUT_SIZE16: case TEST_OUT_SIZE20: case TEST_OUT_SIZE24: case TEST_OUT_SIZE28: case TEST_OUT_SIZE32: case TEST_OUT_SIZE36: case TEST_OUT_SIZE40: case TEST_OUT_SIZE44: case TEST_OUT_SIZE48: case TEST_OUT_SIZE52: case TEST_OUT_SIZE56: case TEST_OUT_SIZE60: case TEST_OUT_SIZE64: case TEST_OUT_SIZE68: case TEST_OUT_SIZE72: case TEST_OUT_SIZE76: case TEST_OUT_SIZE80: case TEST_OUT_SIZE84: case TEST_OUT_SIZE88: case TEST_OUT_SIZE92: case TEST_OUT_SIZE96: case TEST_OUT_SIZE100: case TEST_OUT_SIZE104: case TEST_OUT_SIZE108: case TEST_OUT_SIZE112: case TEST_OUT_SIZE116: case TEST_OUT_SIZE120: case TEST_OUT_SIZE124: case TEST_OUT_SIZE128: case TEST_OUT_SIZE132: case TEST_OUT_SIZE136: case TEST_OUT_SIZE140: case TEST_OUT_SIZE144: case TEST_OUT_SIZE148: case TEST_OUT_SIZE152: case TEST_OUT_SIZE156: case TEST_OUT_SIZE160: case TEST_OUT_SIZE164: case TEST_OUT_SIZE168: case TEST_OUT_SIZE172: case TEST_OUT_SIZE176: case TEST_OUT_SIZE180: case TEST_OUT_SIZE184: case TEST_OUT_SIZE188: case TEST_OUT_SIZE192: case TEST_OUT_SIZE196: case TEST_OUT_SIZE200: case TEST_OUT_SIZE204: case TEST_OUT_SIZE208: case TEST_OUT_SIZE212: case TEST_OUT_SIZE216: case TEST_OUT_SIZE220: case TEST_OUT_SIZE224: case TEST_OUT_SIZE228: case TEST_OUT_SIZE232: case TEST_OUT_SIZE236: case TEST_OUT_SIZE240: case TEST_OUT_SIZE244: return trace->overflow == 1 || trace->nodelen != 1 || trace->remlen != trace_size / 4; case TEST_IN_SIZE4: case TEST_IN_SIZE8: case TEST_IN_SIZE12: case TEST_IN_SIZE16: case TEST_IN_SIZE20: case TEST_IN_SIZE24: case TEST_IN_SIZE28: case TEST_IN_SIZE32: case TEST_IN_SIZE36: case TEST_IN_SIZE40: case TEST_IN_SIZE44: case TEST_IN_SIZE48: case TEST_IN_SIZE52: case TEST_IN_SIZE56: case TEST_IN_SIZE60: case TEST_IN_SIZE64: case TEST_IN_SIZE68: case TEST_IN_SIZE72: case TEST_IN_SIZE76: case TEST_IN_SIZE80: case TEST_IN_SIZE84: case TEST_IN_SIZE88: case TEST_IN_SIZE92: case TEST_IN_SIZE96: case TEST_IN_SIZE100: case TEST_IN_SIZE104: case TEST_IN_SIZE108: case TEST_IN_SIZE112: case TEST_IN_SIZE116: case TEST_IN_SIZE120: case TEST_IN_SIZE124: case TEST_IN_SIZE128: case TEST_IN_SIZE132: case TEST_IN_SIZE136: case TEST_IN_SIZE140: case TEST_IN_SIZE144: case TEST_IN_SIZE148: case TEST_IN_SIZE152: case TEST_IN_SIZE156: case TEST_IN_SIZE160: case TEST_IN_SIZE164: case TEST_IN_SIZE168: case TEST_IN_SIZE172: case TEST_IN_SIZE176: case TEST_IN_SIZE180: case TEST_IN_SIZE184: case TEST_IN_SIZE188: case TEST_IN_SIZE192: case TEST_IN_SIZE196: case TEST_IN_SIZE200: case TEST_IN_SIZE204: case TEST_IN_SIZE208: case TEST_IN_SIZE212: case TEST_IN_SIZE216: case TEST_IN_SIZE220: case TEST_IN_SIZE224: case TEST_IN_SIZE228: case TEST_IN_SIZE232: case TEST_IN_SIZE236: case TEST_IN_SIZE240: case TEST_IN_SIZE244: return trace->overflow == 1 || trace->nodelen != 1 || trace->remlen != (trace_size / 4) - trace->nodelen; case TEST_OUT_FULL_SUPP_TRACE: case TEST_IN_FULL_SUPP_TRACE: return trace->overflow == 1 || trace->nodelen != 15 || trace->remlen != 0; default: break; } return 1; } static int check_data(struct ioam6_trace_hdr *trace, __u8 trace_size, const struct ioam_config cnf, bool is_output) { unsigned int len, i; __u8 aligned; __u64 raw64; __u32 raw32; __u8 *p; if (trace->type.bit12 | trace->type.bit13 | trace->type.bit14 | trace->type.bit15 | trace->type.bit16 | trace->type.bit17 | trace->type.bit18 | trace->type.bit19 | trace->type.bit20 | trace->type.bit21 | trace->type.bit23) return 1; for (i = 0; i < trace->remlen * 4; i++) { if (trace->data[i] != 0) return 1; } if (trace->remlen * 4 == trace_size) return 0; p = trace->data + trace->remlen * 4; if (trace->type.bit0) { raw32 = __be32_to_cpu(*((__u32 *)p)); if (cnf.hlim != (raw32 >> 24) || cnf.id != (raw32 & 0xffffff)) return 1; p += sizeof(__u32); } if (trace->type.bit1) { raw32 = __be32_to_cpu(*((__u32 *)p)); if (cnf.ingr_id != (raw32 >> 16) || cnf.egr_id != (raw32 & 0xffff)) return 1; p += sizeof(__u32); } if (trace->type.bit2) { raw32 = __be32_to_cpu(*((__u32 *)p)); if ((is_output && raw32 != 0xffffffff) || (!is_output && (raw32 == 0 || raw32 == 0xffffffff))) return 1; p += sizeof(__u32); } if (trace->type.bit3) { raw32 = __be32_to_cpu(*((__u32 *)p)); if ((is_output && raw32 != 0xffffffff) || (!is_output && (raw32 == 0 || raw32 == 0xffffffff))) return 1; p += sizeof(__u32); } if (trace->type.bit4) { if (__be32_to_cpu(*((__u32 *)p)) != 0xffffffff) return 1; p += sizeof(__u32); } if (trace->type.bit5) { if (__be32_to_cpu(*((__u32 *)p)) != cnf.ns_data) return 1; p += sizeof(__u32); } if (trace->type.bit6) { if (__be32_to_cpu(*((__u32 *)p)) == 0xffffffff) return 1; p += sizeof(__u32); } if (trace->type.bit7) { if (__be32_to_cpu(*((__u32 *)p)) != 0xffffffff) return 1; p += sizeof(__u32); } if (trace->type.bit8) { raw64 = __be64_to_cpu(*((__u64 *)p)); if (cnf.hlim != (raw64 >> 56) || cnf.wide != (raw64 & 0xffffffffffffff)) return 1; p += sizeof(__u64); } if (trace->type.bit9) { if (__be32_to_cpu(*((__u32 *)p)) != cnf.ingr_wide) return 1; p += sizeof(__u32); if (__be32_to_cpu(*((__u32 *)p)) != cnf.egr_wide) return 1; p += sizeof(__u32); } if (trace->type.bit10) { if (__be64_to_cpu(*((__u64 *)p)) != cnf.ns_wide) return 1; p += sizeof(__u64); } if (trace->type.bit11) { if (__be32_to_cpu(*((__u32 *)p)) != 0xffffffff) return 1; p += sizeof(__u32); } if (trace->type.bit22) { len = cnf.sc_data ? strlen(cnf.sc_data) : 0; aligned = cnf.sc_data ? __ALIGN_KERNEL(len, 4) : 0; raw32 = __be32_to_cpu(*((__u32 *)p)); if (aligned != (raw32 >> 24) * 4 || cnf.sc_id != (raw32 & 0xffffff)) return 1; p += sizeof(__u32); if (cnf.sc_data) { if (strncmp((char *)p, cnf.sc_data, len)) return 1; p += len; aligned -= len; while (aligned--) { if (*p != '\0') return 1; p += sizeof(__u8); } } } return 0; } static int check_ioam_trace(int tid, struct ioam6_trace_hdr *trace, __u32 trace_type, __u8 trace_size, __u16 ioam_ns) { if (check_header(tid, trace, trace_type, trace_size, ioam_ns)) return 1; if (tid > __TEST_OUT_MIN && tid < __TEST_OUT_MAX) return check_data(trace, trace_size, node1, true); if (tid > __TEST_IN_MIN && tid < __TEST_IN_MAX) return check_data(trace, trace_size, node2, false); return 1; } static int str2id(const char *tname) { if (!strcmp("output_undef_ns", tname)) return TEST_OUT_UNDEF_NS; if (!strcmp("output_no_room", tname)) return TEST_OUT_NO_ROOM; if (!strcmp("output_no_room_oss", tname)) return TEST_OUT_NO_ROOM_OSS; if (!strcmp("output_bit0", tname)) return TEST_OUT_BIT0; if (!strcmp("output_bit1", tname)) return TEST_OUT_BIT1; if (!strcmp("output_bit2", tname)) return TEST_OUT_BIT2; if (!strcmp("output_bit3", tname)) return TEST_OUT_BIT3; if (!strcmp("output_bit4", tname)) return TEST_OUT_BIT4; if (!strcmp("output_bit5", tname)) return TEST_OUT_BIT5; if (!strcmp("output_bit6", tname)) return TEST_OUT_BIT6; if (!strcmp("output_bit7", tname)) return TEST_OUT_BIT7; if (!strcmp("output_bit8", tname)) return TEST_OUT_BIT8; if (!strcmp("output_bit9", tname)) return TEST_OUT_BIT9; if (!strcmp("output_bit10", tname)) return TEST_OUT_BIT10; if (!strcmp("output_bit11", tname)) return TEST_OUT_BIT11; if (!strcmp("output_bit22", tname)) return TEST_OUT_BIT22; if (!strcmp("output_size4", tname)) return TEST_OUT_SIZE4; if (!strcmp("output_size8", tname)) return TEST_OUT_SIZE8; if (!strcmp("output_size12", tname)) return TEST_OUT_SIZE12; if (!strcmp("output_size16", tname)) return TEST_OUT_SIZE16; if (!strcmp("output_size20", tname)) return TEST_OUT_SIZE20; if (!strcmp("output_size24", tname)) return TEST_OUT_SIZE24; if (!strcmp("output_size28", tname)) return TEST_OUT_SIZE28; if (!strcmp("output_size32", tname)) return TEST_OUT_SIZE32; if (!strcmp("output_size36", tname)) return TEST_OUT_SIZE36; if (!strcmp("output_size40", tname)) return TEST_OUT_SIZE40; if (!strcmp("output_size44", tname)) return TEST_OUT_SIZE44; if (!strcmp("output_size48", tname)) return TEST_OUT_SIZE48; if (!strcmp("output_size52", tname)) return TEST_OUT_SIZE52; if (!strcmp("output_size56", tname)) return TEST_OUT_SIZE56; if (!strcmp("output_size60", tname)) return TEST_OUT_SIZE60; if (!strcmp("output_size64", tname)) return TEST_OUT_SIZE64; if (!strcmp("output_size68", tname)) return TEST_OUT_SIZE68; if (!strcmp("output_size72", tname)) return TEST_OUT_SIZE72; if (!strcmp("output_size76", tname)) return TEST_OUT_SIZE76; if (!strcmp("output_size80", tname)) return TEST_OUT_SIZE80; if (!strcmp("output_size84", tname)) return TEST_OUT_SIZE84; if (!strcmp("output_size88", tname)) return TEST_OUT_SIZE88; if (!strcmp("output_size92", tname)) return TEST_OUT_SIZE92; if (!strcmp("output_size96", tname)) return TEST_OUT_SIZE96; if (!strcmp("output_size100", tname)) return TEST_OUT_SIZE100; if (!strcmp("output_size104", tname)) return TEST_OUT_SIZE104; if (!strcmp("output_size108", tname)) return TEST_OUT_SIZE108; if (!strcmp("output_size112", tname)) return TEST_OUT_SIZE112; if (!strcmp("output_size116", tname)) return TEST_OUT_SIZE116; if (!strcmp("output_size120", tname)) return TEST_OUT_SIZE120; if (!strcmp("output_size124", tname)) return TEST_OUT_SIZE124; if (!strcmp("output_size128", tname)) return TEST_OUT_SIZE128; if (!strcmp("output_size132", tname)) return TEST_OUT_SIZE132; if (!strcmp("output_size136", tname)) return TEST_OUT_SIZE136; if (!strcmp("output_size140", tname)) return TEST_OUT_SIZE140; if (!strcmp("output_size144", tname)) return TEST_OUT_SIZE144; if (!strcmp("output_size148", tname)) return TEST_OUT_SIZE148; if (!strcmp("output_size152", tname)) return TEST_OUT_SIZE152; if (!strcmp("output_size156", tname)) return TEST_OUT_SIZE156; if (!strcmp("output_size160", tname)) return TEST_OUT_SIZE160; if (!strcmp("output_size164", tname)) return TEST_OUT_SIZE164; if (!strcmp("output_size168", tname)) return TEST_OUT_SIZE168; if (!strcmp("output_size172", tname)) return TEST_OUT_SIZE172; if (!strcmp("output_size176", tname)) return TEST_OUT_SIZE176; if (!strcmp("output_size180", tname)) return TEST_OUT_SIZE180; if (!strcmp("output_size184", tname)) return TEST_OUT_SIZE184; if (!strcmp("output_size188", tname)) return TEST_OUT_SIZE188; if (!strcmp("output_size192", tname)) return TEST_OUT_SIZE192; if (!strcmp("output_size196", tname)) return TEST_OUT_SIZE196; if (!strcmp("output_size200", tname)) return TEST_OUT_SIZE200; if (!strcmp("output_size204", tname)) return TEST_OUT_SIZE204; if (!strcmp("output_size208", tname)) return TEST_OUT_SIZE208; if (!strcmp("output_size212", tname)) return TEST_OUT_SIZE212; if (!strcmp("output_size216", tname)) return TEST_OUT_SIZE216; if (!strcmp("output_size220", tname)) return TEST_OUT_SIZE220; if (!strcmp("output_size224", tname)) return TEST_OUT_SIZE224; if (!strcmp("output_size228", tname)) return TEST_OUT_SIZE228; if (!strcmp("output_size232", tname)) return TEST_OUT_SIZE232; if (!strcmp("output_size236", tname)) return TEST_OUT_SIZE236; if (!strcmp("output_size240", tname)) return TEST_OUT_SIZE240; if (!strcmp("output_size244", tname)) return TEST_OUT_SIZE244; if (!strcmp("output_full_supp_trace", tname)) return TEST_OUT_FULL_SUPP_TRACE; if (!strcmp("input_undef_ns", tname)) return TEST_IN_UNDEF_NS; if (!strcmp("input_no_room", tname)) return TEST_IN_NO_ROOM; if (!strcmp("input_no_room_oss", tname)) return TEST_IN_NO_ROOM_OSS; if (!strcmp("input_disabled", tname)) return TEST_IN_DISABLED; if (!strcmp("input_oflag", tname)) return TEST_IN_OFLAG; if (!strcmp("input_bit0", tname)) return TEST_IN_BIT0; if (!strcmp("input_bit1", tname)) return TEST_IN_BIT1; if (!strcmp("input_bit2", tname)) return TEST_IN_BIT2; if (!strcmp("input_bit3", tname)) return TEST_IN_BIT3; if (!strcmp("input_bit4", tname)) return TEST_IN_BIT4; if (!strcmp("input_bit5", tname)) return TEST_IN_BIT5; if (!strcmp("input_bit6", tname)) return TEST_IN_BIT6; if (!strcmp("input_bit7", tname)) return TEST_IN_BIT7; if (!strcmp("input_bit8", tname)) return TEST_IN_BIT8; if (!strcmp("input_bit9", tname)) return TEST_IN_BIT9; if (!strcmp("input_bit10", tname)) return TEST_IN_BIT10; if (!strcmp("input_bit11", tname)) return TEST_IN_BIT11; if (!strcmp("input_bit22", tname)) return TEST_IN_BIT22; if (!strcmp("input_size4", tname)) return TEST_IN_SIZE4; if (!strcmp("input_size8", tname)) return TEST_IN_SIZE8; if (!strcmp("input_size12", tname)) return TEST_IN_SIZE12; if (!strcmp("input_size16", tname)) return TEST_IN_SIZE16; if (!strcmp("input_size20", tname)) return TEST_IN_SIZE20; if (!strcmp("input_size24", tname)) return TEST_IN_SIZE24; if (!strcmp("input_size28", tname)) return TEST_IN_SIZE28; if (!strcmp("input_size32", tname)) return TEST_IN_SIZE32; if (!strcmp("input_size36", tname)) return TEST_IN_SIZE36; if (!strcmp("input_size40", tname)) return TEST_IN_SIZE40; if (!strcmp("input_size44", tname)) return TEST_IN_SIZE44; if (!strcmp("input_size48", tname)) return TEST_IN_SIZE48; if (!strcmp("input_size52", tname)) return TEST_IN_SIZE52; if (!strcmp("input_size56", tname)) return TEST_IN_SIZE56; if (!strcmp("input_size60", tname)) return TEST_IN_SIZE60; if (!strcmp("input_size64", tname)) return TEST_IN_SIZE64; if (!strcmp("input_size68", tname)) return TEST_IN_SIZE68; if (!strcmp("input_size72", tname)) return TEST_IN_SIZE72; if (!strcmp("input_size76", tname)) return TEST_IN_SIZE76; if (!strcmp("input_size80", tname)) return TEST_IN_SIZE80; if (!strcmp("input_size84", tname)) return TEST_IN_SIZE84; if (!strcmp("input_size88", tname)) return TEST_IN_SIZE88; if (!strcmp("input_size92", tname)) return TEST_IN_SIZE92; if (!strcmp("input_size96", tname)) return TEST_IN_SIZE96; if (!strcmp("input_size100", tname)) return TEST_IN_SIZE100; if (!strcmp("input_size104", tname)) return TEST_IN_SIZE104; if (!strcmp("input_size108", tname)) return TEST_IN_SIZE108; if (!strcmp("input_size112", tname)) return TEST_IN_SIZE112; if (!strcmp("input_size116", tname)) return TEST_IN_SIZE116; if (!strcmp("input_size120", tname)) return TEST_IN_SIZE120; if (!strcmp("input_size124", tname)) return TEST_IN_SIZE124; if (!strcmp("input_size128", tname)) return TEST_IN_SIZE128; if (!strcmp("input_size132", tname)) return TEST_IN_SIZE132; if (!strcmp("input_size136", tname)) return TEST_IN_SIZE136; if (!strcmp("input_size140", tname)) return TEST_IN_SIZE140; if (!strcmp("input_size144", tname)) return TEST_IN_SIZE144; if (!strcmp("input_size148", tname)) return TEST_IN_SIZE148; if (!strcmp("input_size152", tname)) return TEST_IN_SIZE152; if (!strcmp("input_size156", tname)) return TEST_IN_SIZE156; if (!strcmp("input_size160", tname)) return TEST_IN_SIZE160; if (!strcmp("input_size164", tname)) return TEST_IN_SIZE164; if (!strcmp("input_size168", tname)) return TEST_IN_SIZE168; if (!strcmp("input_size172", tname)) return TEST_IN_SIZE172; if (!strcmp("input_size176", tname)) return TEST_IN_SIZE176; if (!strcmp("input_size180", tname)) return TEST_IN_SIZE180; if (!strcmp("input_size184", tname)) return TEST_IN_SIZE184; if (!strcmp("input_size188", tname)) return TEST_IN_SIZE188; if (!strcmp("input_size192", tname)) return TEST_IN_SIZE192; if (!strcmp("input_size196", tname)) return TEST_IN_SIZE196; if (!strcmp("input_size200", tname)) return TEST_IN_SIZE200; if (!strcmp("input_size204", tname)) return TEST_IN_SIZE204; if (!strcmp("input_size208", tname)) return TEST_IN_SIZE208; if (!strcmp("input_size212", tname)) return TEST_IN_SIZE212; if (!strcmp("input_size216", tname)) return TEST_IN_SIZE216; if (!strcmp("input_size220", tname)) return TEST_IN_SIZE220; if (!strcmp("input_size224", tname)) return TEST_IN_SIZE224; if (!strcmp("input_size228", tname)) return TEST_IN_SIZE228; if (!strcmp("input_size232", tname)) return TEST_IN_SIZE232; if (!strcmp("input_size236", tname)) return TEST_IN_SIZE236; if (!strcmp("input_size240", tname)) return TEST_IN_SIZE240; if (!strcmp("input_size244", tname)) return TEST_IN_SIZE244; if (!strcmp("input_full_supp_trace", tname)) return TEST_IN_FULL_SUPP_TRACE; return -1; } static int ipv6_addr_equal(const struct in6_addr *a1, const struct in6_addr *a2) { return ((a1->s6_addr32[0] ^ a2->s6_addr32[0]) | (a1->s6_addr32[1] ^ a2->s6_addr32[1]) | (a1->s6_addr32[2] ^ a2->s6_addr32[2]) | (a1->s6_addr32[3] ^ a2->s6_addr32[3])) == 0; } static int get_u32(__u32 *val, const char *arg, int base) { unsigned long res; char *ptr; if (!arg || !*arg) return -1; res = strtoul(arg, &ptr, base); if (!ptr || ptr == arg || *ptr) return -1; if (res == ULONG_MAX && errno == ERANGE) return -1; if (res > 0xFFFFFFFFUL) return -1; *val = res; return 0; } static int get_u16(__u16 *val, const char *arg, int base) { unsigned long res; char *ptr; if (!arg || !*arg) return -1; res = strtoul(arg, &ptr, base); if (!ptr || ptr == arg || *ptr) return -1; if (res == ULONG_MAX && errno == ERANGE) return -1; if (res > 0xFFFFUL) return -1; *val = res; return 0; } static int get_u8(__u8 *val, const char *arg, int base) { unsigned long res; char *ptr; if (!arg || !*arg) return -1; res = strtoul(arg, &ptr, base); if (!ptr || ptr == arg || *ptr) return -1; if (res == ULONG_MAX && errno == ERANGE) return -1; if (res > 0xFFUL) return -1; *val = res; return 0; } int main(int argc, char **argv) { __u8 buffer[512], *ptr, nexthdr, tr_size; struct ioam6_trace_hdr *trace; unsigned int hoplen, ret = 1; struct ipv6_hopopt_hdr *hbh; int fd, size, testname_id; struct in6_addr src, dst; struct ioam6_hdr *ioam6; struct timeval timeout; struct ipv6hdr *ipv6; __u32 tr_type; __u16 ioam_ns; if (argc != 9) goto out; testname_id = str2id(argv[2]); if (testname_id < 0 || inet_pton(AF_INET6, argv[3], &src) != 1 || inet_pton(AF_INET6, argv[4], &dst) != 1 || get_u32(&tr_type, argv[5], 16) || get_u8(&tr_size, argv[6], 0) || get_u16(&ioam_ns, argv[7], 0)) goto out; nexthdr = (!strcmp(argv[8], "encap") ? IPPROTO_IPV6 : IPPROTO_ICMPV6); hoplen = sizeof(*hbh); hoplen += 2; // 2-byte padding for alignment hoplen += sizeof(*ioam6); // IOAM option header hoplen += sizeof(*trace); // IOAM trace header hoplen += tr_size; // IOAM trace size hoplen += (tr_size % 8); // optional padding fd = socket(AF_PACKET, SOCK_DGRAM, __cpu_to_be16(ETH_P_IPV6)); if (fd < 0) goto out; if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, argv[1], strlen(argv[1]))) goto close; timeout.tv_sec = 1; timeout.tv_usec = 0; if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const char *)&timeout, sizeof(timeout))) goto close; recv: size = recv(fd, buffer, sizeof(buffer), 0); if (size <= 0) goto close; ipv6 = (struct ipv6hdr *)buffer; /* Skip packets that do not have the expected src/dst address or that * do not have a Hop-by-hop. */ if (!ipv6_addr_equal(&ipv6->saddr, &src) || !ipv6_addr_equal(&ipv6->daddr, &dst) || ipv6->nexthdr != IPPROTO_HOPOPTS) goto recv; /* Check Hbh's Next Header and Size. */ hbh = (struct ipv6_hopopt_hdr *)(buffer + sizeof(*ipv6)); if (hbh->nexthdr != nexthdr || hbh->hdrlen != (hoplen >> 3) - 1) goto close; /* Check we have a 2-byte padding for alignment. */ ptr = (__u8 *)hbh + sizeof(*hbh); if (ptr[0] != IPV6_TLV_PADN && ptr[1] != 0) goto close; /* Check we now have the IOAM option. */ ptr += 2; if (ptr[0] != IPV6_TLV_IOAM) goto close; /* Check its size and the IOAM option type. */ ioam6 = (struct ioam6_hdr *)ptr; if (ioam6->opt_len != sizeof(*ioam6) - 2 + sizeof(*trace) + tr_size || ioam6->type != IOAM6_TYPE_PREALLOC) goto close; trace = (struct ioam6_trace_hdr *)(ptr + sizeof(*ioam6)); /* Check the trailing 4-byte padding (potentially). */ ptr = (__u8 *)trace + sizeof(*trace) + tr_size; if (tr_size % 8 && ptr[0] != IPV6_TLV_PADN && ptr[1] != 2 && ptr[2] != 0 && ptr[3] != 0) goto close; /* Check the IOAM header and data. */ ret = check_ioam_trace(testname_id, trace, tr_type, tr_size, ioam_ns); close: close(fd); out: return ret; }