| // 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 <arpa/inet.h> | |
| #include <errno.h> | |
| #include <limits.h> | |
| #include <linux/const.h> | |
| #include <linux/if_ether.h> | |
| #include <linux/ioam6.h> | |
| #include <linux/ipv6.h> | |
| #include <stdbool.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <unistd.h> | |
| 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; | |
| } |