/* * Copyright (c) 2021 Eugene Syromyatnikov * All rights reserved. * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "tests.h" #include #include #include #include #include #include #include #include #include #include "test_nlattr.h" #include "xlat.h" #define XLAT_MACROS_ONLY # include "xlat/rtnl_nexthop_attrs.h" # include "xlat/rtnl_nha_res_group_attrs.h" # include "xlat/rtnl_nha_res_bucket_attrs.h" #undef XLAT_MACROS_ONLY #define DEF_NLATTR_NHMSG_FUNCS(sfx_, af_) \ static void \ init_##sfx_(struct nlmsghdr *const nlh, const unsigned int msg_len) \ { \ SET_STRUCT(struct nlmsghdr, nlh, \ .nlmsg_len = msg_len, \ .nlmsg_type = RTM_GETNEXTHOP, \ .nlmsg_flags = NLM_F_DUMP, \ ); \ \ struct nhmsg *const msg = NLMSG_DATA(nlh); \ SET_STRUCT(struct nhmsg, msg, \ .nh_family = (af_), \ .nh_scope = RT_SCOPE_NOWHERE, \ .nh_protocol = RTPROT_UNSPEC, \ .nh_flags = 0x22, \ ); \ } \ \ static void \ print_##sfx_(const unsigned int msg_len) \ { \ printf("{nlmsg_len=%u, nlmsg_type=" XLAT_FMT \ ", nlmsg_flags=" XLAT_FMT ", nlmsg_seq=0" \ ", nlmsg_pid=0}, {nh_family=" XLAT_FMT \ ", nh_scope=" XLAT_FMT ", nh_protocol=" XLAT_FMT \ ", nh_flags=" XLAT_FMT "}", \ msg_len, XLAT_ARGS(RTM_GETNEXTHOP), \ XLAT_ARGS(NLM_F_DUMP), XLAT_SEL(af_, #af_), \ XLAT_ARGS(RT_SCOPE_NOWHERE), \ XLAT_ARGS(RTPROT_UNSPEC), \ XLAT_ARGS(RTNH_F_PERVASIVE|RTNH_F_UNRESOLVED)); \ } \ /* End of DEF_NLATTR_NHMSG_FUNCS */ #define DEF_NLATTR_NHMSG_NESTED_FUNCS(sfx_, attr_) \ static void \ init_nhmsg_##sfx_(struct nlmsghdr *const nlh, \ const unsigned int msg_len) \ { \ init_nhmsg(nlh, msg_len); \ struct nlattr *nla = NLMSG_ATTR(nlh, sizeof(struct nhmsg)); \ SET_STRUCT(struct nlattr, nla, \ .nla_len = msg_len \ - NLMSG_SPACE(sizeof(struct nhmsg)), \ .nla_type = attr_, \ ); \ } \ \ static void \ print_nhmsg_##sfx_(const unsigned int msg_len) \ { \ print_nhmsg(msg_len); \ printf(", [{nla_len=%u, nla_type=" XLAT_FMT "}", \ (unsigned int) (msg_len - NLMSG_HDRLEN \ - NLMSG_ALIGN(sizeof(struct nhmsg))), \ XLAT_SEL(attr_, #attr_)); \ } \ /* End of DEF_NLATTR_NHMSG_NESTED_FUNCS */ DEF_NLATTR_NHMSG_FUNCS(nhmsg, AF_UNIX) DEF_NLATTR_NHMSG_FUNCS(nhmsg_inet, AF_INET) DEF_NLATTR_NHMSG_FUNCS(nhmsg_inet6, AF_INET6) DEF_NLATTR_NHMSG_NESTED_FUNCS(res_grp, NHA_RES_GROUP) DEF_NLATTR_NHMSG_NESTED_FUNCS(res_bkt, NHA_RES_BUCKET) static void print_nh_grp(const struct nexthop_grp *const elem, size_t idx) { switch (idx) { case 0: printf("{id=3735928559, weight=0}"); break; case 1: printf("{id=0, weight=218, resvd2=0xdead}"); break; case 2: printf("{id=4207869677, weight=190, resvd1=0xec}"); break; case 3: printf("{id=0, weight=0, resvd1=0xca, resvd2=0xbeef}"); break; default: error_msg_and_fail("Unexpected grp index: %zu", idx); } } int main(void) { skip_if_unavailable("/proc/self/fd/"); const int fd = create_nl_socket(NETLINK_ROUTE); const unsigned int hdrlen = sizeof(struct nhmsg); char nla_type_str[256]; void *nlh0 = midtail_alloc(NLMSG_SPACE(hdrlen), NLA_HDRLEN + 42); static char pattern[4096]; fill_memory_ex(pattern, sizeof(pattern), 'a', 'z' - 'a' + 1); /* Unknown attrs */ static const uint16_t unk_types[] = { 14, 0xffff & NLA_TYPE_MASK }; for (size_t i = 0; i < ARRAY_SIZE(unk_types); i++) { sprintf(nla_type_str, "%#x" NRAW(" /* NHA_??? */"), unk_types[i]); TEST_NLATTR_(fd, nlh0, hdrlen, init_nhmsg, print_nhmsg, unk_types[i], nla_type_str, 4, pattern, 4, print_quoted_hex(pattern, 4)); } /* unimplemented, no semantics: NHA_UNSPEC, NHA_ENCAP */ static const struct strval32 unimp_types[] = { { ARG_XLAT_KNOWN(0, "NHA_UNSPEC") }, { ARG_XLAT_KNOWN(0x8, "NHA_ENCAP") } }; for (size_t i = 0; i < ARRAY_SIZE(unimp_types); i++) { TEST_NLATTR_(fd, nlh0, hdrlen, init_nhmsg, print_nhmsg, unimp_types[i].val, unimp_types[i].str, 42, pattern, 42, print_quoted_hex(pattern, 32); printf("...")); } /* u32 attrs: NHA_ID, NHA_BLACKHOLE, NHA_GROUPS, NHA_FDB */ static const struct strval32 u32_attrs[] = { { ARG_XLAT_KNOWN(0x1, "NHA_ID") }, { ARG_XLAT_KNOWN(0x4, "NHA_BLACKHOLE") }, { ARG_XLAT_KNOWN(0x9, "NHA_GROUPS") }, { ARG_XLAT_KNOWN(0xb, "NHA_FDB") }, }; for (size_t i = 0; i < ARRAY_SIZE(u32_attrs); i++) { check_u32_nlattr(fd, nlh0, hdrlen, init_nhmsg, print_nhmsg, u32_attrs[i].val, u32_attrs[i].str, pattern, 0); } /* NHA_GROUP */ static const struct nexthop_grp grps[] = { { .id = 0xdeadbeef, .weight = 0, .resvd1 = 0, .resvd2 = 0 }, { .id = 0, .weight = 218, .resvd1 = 0, .resvd2 = 0xdead }, { .id = 0xfacefeed, .weight = 190, .resvd1 = 236, .resvd2 = 0 }, { .id = 0, .weight = 0, .resvd1 = 202, .resvd2 = 0xbeef }, }; TEST_NLATTR_ARRAY_(fd, nlh0, hdrlen, init_nhmsg, print_nhmsg, NHA_GROUP, XLAT_KNOWN(0x2, "NHA_GROUP"), pattern, grps, print_nh_grp); /* NHA_GROUP_TYPE */ static const struct strval16 grp_types[] = { { ARG_XLAT_KNOWN(0, "NEXTHOP_GRP_TYPE_MPATH") }, { ARG_XLAT_KNOWN(0x1, "NEXTHOP_GRP_TYPE_RES") }, { ARG_XLAT_UNKNOWN(0x2, "NEXTHOP_GRP_TYPE_???") }, { ARG_XLAT_UNKNOWN(0xbeef, "NEXTHOP_GRP_TYPE_???") }, }; for (size_t i = 0; i < ARRAY_SIZE(grp_types); i++) { TEST_NLATTR_OBJECT_(fd, nlh0, hdrlen, init_nhmsg, print_nhmsg, NHA_GROUP_TYPE, XLAT_KNOWN(0x3, "NHA_GROUP_TYPE"), pattern, grp_types[i].val, printf("%s", grp_types[i].str)); TEST_NLATTR_(fd, nlh0, hdrlen, init_nhmsg, print_nhmsg, NHA_GROUP_TYPE, XLAT_KNOWN(0x3, "NHA_GROUP_TYPE"), sizeof(grp_types[i].val) + 4, &grp_types[i].val, sizeof(grp_types[i].val), printf("%s", grp_types[i].str)); } /* ifindex: NHA_OIF, NHA_MASTER */ static const struct strval32 if_attrs[] = { { ARG_XLAT_KNOWN(0x5, "NHA_OIF") }, { ARG_XLAT_KNOWN(0xa, "NHA_MASTER") }, }; const uint32_t ifindex = ifindex_lo(); for (size_t i = 0; i < ARRAY_SIZE(if_attrs); i++) { static const uint32_t bogus = 0xdeadc0de; TEST_NLATTR_OBJECT_(fd, nlh0, hdrlen, init_nhmsg, print_nhmsg, if_attrs[i].val, if_attrs[i].str, pattern, bogus, printf("3735929054")); TEST_NLATTR_(fd, nlh0, hdrlen, init_nhmsg, print_nhmsg, if_attrs[i].val, if_attrs[i].str, sizeof(bogus) + 4, &bogus, sizeof(bogus), printf("3735929054")); TEST_NLATTR_OBJECT_(fd, nlh0, hdrlen, init_nhmsg, print_nhmsg, if_attrs[i].val, if_attrs[i].str, pattern, ifindex, printf(XLAT_FMT_U, XLAT_SEL(ifindex, IFINDEX_LO_STR))); TEST_NLATTR_(fd, nlh0, hdrlen, init_nhmsg, print_nhmsg, if_attrs[i].val, if_attrs[i].str, sizeof(ifindex) + 4, &ifindex, sizeof(ifindex), printf(XLAT_FMT_U, XLAT_SEL(ifindex, IFINDEX_LO_STR))); } /* NHA_GATEWAY */ static const struct { uint8_t af; uint8_t addr[16]; const char *str; void (* init_fn)(struct nlmsghdr *, unsigned int); void (* print_fn)(unsigned int); uint32_t len; } addrs[] = { { AF_UNIX, { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, "\"\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09\"", init_nhmsg, print_nhmsg, 10 }, { AF_INET, { 0xde, 0xca, 0xff, 0xed }, "inet_addr(\"222.202.255.237\")", init_nhmsg_inet, print_nhmsg_inet, 4 }, { AF_INET6, { 0xfa, 0xce, 0xbe, 0xef, [15] = 0xda }, "inet_pton(AF_INET6, \"face:beef::da\")", init_nhmsg_inet6, print_nhmsg_inet6, 16 }, }; static const struct strval32 addr_attrs[] = { { ARG_XLAT_KNOWN(0x6, "NHA_GATEWAY") }, }; for (size_t i = 0; i < ARRAY_SIZE(addrs); i++) { for (size_t j = 0; j < ARRAY_SIZE(addr_attrs); j++) { TEST_NLATTR_(fd, nlh0, hdrlen, addrs[i].init_fn, addrs[i].print_fn, addr_attrs[j].val, addr_attrs[j].str, addrs[i].len - 1, addrs[i].addr, addrs[i].len - 1, print_quoted_hex(addrs[i].addr, addrs[i].len - 1) ); TEST_NLATTR_(fd, nlh0, hdrlen, addrs[i].init_fn, addrs[i].print_fn, addr_attrs[j].val, addr_attrs[j].str, addrs[i].len, addrs[i].addr, addrs[i].len, #if XLAT_RAW || XLAT_VERBOSE print_quoted_hex(addrs[i].addr, addrs[i].len); #endif #if !XLAT_RAW if (!(XLAT_VERBOSE && addrs[i].af == AF_UNIX)) printf(VERB(" /* ") "%s" VERB(" */"), addrs[i].str); #endif ); } } /* NHA_ENCAP_TYPE */ static const struct strval16 enc_types[] = { { ARG_XLAT_KNOWN(0, "LWTUNNEL_ENCAP_NONE") }, { ARG_XLAT_KNOWN(0x8, "LWTUNNEL_ENCAP_RPL") }, { ARG_XLAT_UNKNOWN(0x9, "LWTUNNEL_ENCAP_???") }, { ARG_XLAT_UNKNOWN(0xbeef, "LWTUNNEL_ENCAP_???") }, }; for (size_t i = 0; i < ARRAY_SIZE(enc_types); i++) { TEST_NLATTR_OBJECT_(fd, nlh0, hdrlen, init_nhmsg, print_nhmsg, NHA_ENCAP_TYPE, XLAT_KNOWN(0x7, "NHA_ENCAP_TYPE"), pattern, enc_types[i].val, printf("%s", enc_types[i].str)); TEST_NLATTR_(fd, nlh0, hdrlen, init_nhmsg, print_nhmsg, NHA_ENCAP_TYPE, XLAT_KNOWN(0x7, "NHA_ENCAP_TYPE"), sizeof(enc_types[i].val) + 4, &enc_types[i].val, sizeof(enc_types[i].val), printf("%s", enc_types[i].str)); } /* NHA_RES_GROUP */ static const unsigned int res_grp_hdrlen = sizeof(struct nhmsg) + sizeof(struct nlattr); void *nlh1 = midtail_alloc(NLMSG_SPACE(res_grp_hdrlen), NLA_HDRLEN + 16); TEST_NLATTR_(fd, nlh0, hdrlen, init_nhmsg, print_nhmsg, NHA_RES_GROUP, XLAT_KNOWN(0xc, "NHA_RES_GROUP"), 3, pattern, 3, print_quoted_hex(pattern, 3)); /* unknown NHA_RES_GROUP_* attr */ static const uint16_t unk_res_grp_types[] = { 5, 0xffff & NLA_TYPE_MASK, }; for (size_t i = 0; i < ARRAY_SIZE(unk_res_grp_types); i++) { sprintf(nla_type_str, "%#x" NRAW(" /* NHA_RES_GROUP_??? */"), unk_res_grp_types[i]); TEST_NLATTR_(fd, nlh1, res_grp_hdrlen, init_nhmsg_res_grp, print_nhmsg_res_grp, unk_res_grp_types[i], nla_type_str, 16, pattern, 16, print_quoted_hex(pattern, 16); printf("]")); } /* not decoded: NHA_RES_GROUP_UNSPEC/NHA_RES_GROUP_PAD */ TEST_NLATTR_(fd, nlh1, res_grp_hdrlen, init_nhmsg_res_grp, print_nhmsg_res_grp, NHA_RES_GROUP_PAD, XLAT_KNOWN(0, "NHA_RES_GROUP_PAD"), 8, pattern, 8, print_quoted_hex(pattern, 8); printf("]")); /* u16: NHA_RES_GROUP_BUCKETS */ check_u16_nlattr(fd, nlh0, hdrlen, init_nhmsg_res_grp, print_nhmsg_res_grp, ARG_XLAT_KNOWN(0x1, "NHA_RES_GROUP_BUCKETS"), pattern, 1); /* clock_t: NHA_RES_GROUP_IDLE_TIMER, NHA_RES_GROUP_UNBALANCED_TIMER, * NHA_RES_GROUP_UNBALANCED_TIME */ static const struct strval32 res_grp_clk_attrs[] = { { ARG_XLAT_KNOWN(0x2, "NHA_RES_GROUP_IDLE_TIMER") }, { ARG_XLAT_KNOWN(0x3, "NHA_RES_GROUP_UNBALANCED_TIMER") }, { ARG_XLAT_KNOWN(0x4, "NHA_RES_GROUP_UNBALANCED_TIME") }, }; for (size_t i = 0; i < ARRAY_SIZE(res_grp_clk_attrs); i++) { check_clock_t_nlattr(fd, nlh0, hdrlen, init_nhmsg_res_grp, print_nhmsg_res_grp, res_grp_clk_attrs[i].val, res_grp_clk_attrs[i].str, 1); } /* NHA_RES_BUCKET */ static const unsigned int res_bkt_hdrlen = sizeof(struct nhmsg) + sizeof(struct nlattr); TEST_NLATTR_(fd, nlh0, hdrlen, init_nhmsg, print_nhmsg, NHA_RES_BUCKET, XLAT_KNOWN(0xd, "NHA_RES_BUCKET"), 3, pattern, 3, print_quoted_hex(pattern, 3)); /* unknown NHA_RES_GROUP_* attr */ static const uint16_t unk_res_bkt_types[] = { 4, 0xffff & NLA_TYPE_MASK, }; for (size_t i = 0; i < ARRAY_SIZE(unk_res_bkt_types); i++) { sprintf(nla_type_str, "%#x" NRAW(" /* NHA_RES_BUCKET_??? */"), unk_res_bkt_types[i]); TEST_NLATTR_(fd, nlh1, res_bkt_hdrlen, init_nhmsg_res_bkt, print_nhmsg_res_bkt, unk_res_bkt_types[i], nla_type_str, 16, pattern, 16, print_quoted_hex(pattern, 16); printf("]")); } /* not decoded: NHA_RES_BUCKET_UNSPEC/NHA_RES_BUCKET_PAD */ TEST_NLATTR_(fd, nlh1, res_bkt_hdrlen, init_nhmsg_res_bkt, print_nhmsg_res_bkt, NHA_RES_BUCKET_PAD, XLAT_KNOWN(0, "NHA_RES_BUCKET_PAD"), 8, pattern, 8, print_quoted_hex(pattern, 8); printf("]")); /* u16: NHA_RES_BUCKET_INDEX */ check_u16_nlattr(fd, nlh0, hdrlen, init_nhmsg_res_bkt, print_nhmsg_res_bkt, ARG_XLAT_KNOWN(0x1, "NHA_RES_BUCKET_INDEX"), pattern, 1); /* clock_t: NHA_RES_BUCKET_IDLE_TIME */ static const struct strval32 res_bkt_clk_attrs[] = { { ARG_XLAT_KNOWN(0x2, "NHA_RES_BUCKET_IDLE_TIME") }, }; for (size_t i = 0; i < ARRAY_SIZE(res_bkt_clk_attrs); i++) { check_clock_t_nlattr(fd, nlh0, hdrlen, init_nhmsg_res_bkt, print_nhmsg_res_bkt, res_bkt_clk_attrs[i].val, res_bkt_clk_attrs[i].str, 1); } /* u32: NHA_RES_BUCKET_NH_ID */ check_u32_nlattr(fd, nlh0, hdrlen, init_nhmsg_res_bkt, print_nhmsg_res_bkt, ARG_XLAT_KNOWN(0x3, "NHA_RES_BUCKET_NH_ID"), pattern, 1); puts("+++ exited with 0 +++"); return 0; }