/*
 * IFLA_INET6_* protocol-specific netlink attribute decoding check common code.
 *
 * Copyright (c) 2018-2021 The strace developers.
 * All rights reserved.
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

#ifndef STRACE_TESTS_NLATTR_IFLA_AF_INET6
# define STRACE_TESTS_NLATTR_IFLA_AF_INET6

# include "tests.h"

static void
print_arr_val(uint32_t *val, size_t idx, const char *idx_str)
{
	if (idx_str)
		printf("[%s] = ", idx_str);
	else
		printf("[%zu] = ", idx);

	printf("%d", *val);
}

static void
print_arr_uval(uint64_t *val, size_t idx, const char *idx_str)
{
	if (idx_str)
		printf("[%s] = ", idx_str);
	else
		printf("[%zu] = ", idx);

	printf("%" PRIu64, *val);
}

static void
print_inet6_conf_val(uint32_t *val, size_t idx)
{
	static const char * const strs[] = {
		XLAT_KNOWN(0, "DEVCONF_FORWARDING"),
		XLAT_KNOWN(1, "DEVCONF_HOPLIMIT"),
	};

	print_arr_val(val, idx, idx < ARRAY_SIZE(strs) ? strs[idx] : NULL);
}

static void
print_inet6_stats_val(uint64_t *val, size_t idx)
{
	static const char * const strs[] = {
		XLAT_KNOWN(0, "IPSTATS_MIB_NUM"),
		XLAT_KNOWN(1, "IPSTATS_MIB_INPKTS"),
	};

	print_arr_uval(val, idx, idx < ARRAY_SIZE(strs) ? strs[idx] : NULL);
}

static void
print_icmp6_stats_val(uint64_t *val, size_t idx)
{
	static const char * const strs[] = {
		XLAT_KNOWN(0, "ICMP6_MIB_NUM"),
		XLAT_KNOWN(1, "ICMP6_MIB_INMSGS"),
		XLAT_KNOWN(2, "ICMP6_MIB_INERRORS"),
		XLAT_KNOWN(3, "ICMP6_MIB_OUTMSGS"),
		XLAT_KNOWN(4, "ICMP6_MIB_OUTERRORS"),
		XLAT_KNOWN(5, "ICMP6_MIB_CSUMERRORS"),
		XLAT_UNKNOWN(6, "ICMP6_MIB_???"),
	};

	print_arr_uval(val, idx, idx < ARRAY_SIZE(strs) ? strs[idx] : NULL);
}

static void
check_ifla_af_inet6(int fd, void *nlh0, size_t hdrlen,
		    void (*init_msg)(struct nlmsghdr *, unsigned int),
		    void (*print_msg)(unsigned int),
		    void *pattern, size_t depth)
{
	static const uint8_t unknown_msg[] = { 0xab, 0xac, 0xdb, 0xcd };

	/* Undecoded, unknown */
	TEST_NESTED_NLATTR_OBJECT_EX_(fd, nlh0, hdrlen, init_msg, print_msg,
				      0, XLAT_KNOWN(0, "IFLA_INET6_UNSPEC"),
				      pattern, unknown_msg, print_quoted_hex,
				      depth,
				      printf("\"\\xab\\xac\\xdb\\xcd\""));
	TEST_NESTED_NLATTR_OBJECT_EX_(fd, nlh0, hdrlen, init_msg, print_msg,
				      10, "0xa" NRAW(" /* IFLA_INET6_??? */"),
				      pattern, unknown_msg, print_quoted_hex,
				      depth,
				      printf("\"\\xab\\xac\\xdb\\xcd\""));


	/* IFLA_INET6_FLAGS */
	static const struct {
		uint32_t flags;
		const char *str;
	} inet6_flags[] = {
		{ 0xf, "0xf" NRAW(" /* IF_??? */") },
		{ ARG_XLAT_KNOWN(0x10, "IF_RS_SENT") },
		{ ARG_XLAT_KNOWN(0xc0, "IF_RA_MANAGED|IF_RA_OTHERCONF") },
		{ ARG_XLAT_KNOWN(0xdeadc0de, "IF_RS_SENT|IF_RA_MANAGED"
					     "|IF_RA_OTHERCONF|IF_READY"
					     "|0x5eadc00e") },
	};

	for (size_t i = 0; i < ARRAY_SIZE(inet6_flags); i++) {
		TEST_NESTED_NLATTR_OBJECT_EX_(fd, nlh0, hdrlen,
					      init_msg, print_msg,
					      1, XLAT_KNOWN(0x1,
							    "IFLA_INET6_FLAGS"),
					      pattern, inet6_flags[i].flags,
					      print_quoted_hex, depth,
					      printf("%s", inet6_flags[i].str));
	}


	/* IFLA_INET6_CONF */
	uint32_t inet6_conf_vals[] = { 0xdeadc0de, 0xda7aface };
	TEST_NESTED_NLATTR_ARRAY_EX_(fd, nlh0, hdrlen, init_msg, print_msg,
				     IFLA_INET6_CONF,
				     XLAT_KNOWN(0x2, "IFLA_INET6_CONF"),
				     pattern, inet6_conf_vals, depth,
				     print_inet6_conf_val);


	/* IFLA_INET6_STATS */
	uint64_t inet6_stats_vals[] = { 0xdeadc0deda7aface, 0xdec0deedbadc0ded };
	TEST_NESTED_NLATTR_ARRAY_EX_(fd, nlh0, hdrlen, init_msg, print_msg,
				    IFLA_INET6_STATS,
				    XLAT_KNOWN(0x3, "IFLA_INET6_STATS"),
				    pattern, inet6_stats_vals, depth,
				    print_inet6_stats_val);


	/* IFLA_INET6_MCAST */
	TEST_NESTED_NLATTR_OBJECT_EX_(fd, nlh0, hdrlen, init_msg, print_msg,
				      4, XLAT_KNOWN(0x4, "IFLA_INET6_MCAST"),
				      pattern, unknown_msg, print_quoted_hex,
				      depth,
				      printf("\"\\xab\\xac\\xdb\\xcd\""));


	/* IFLA_INET6_CACHEINFO */
	static const struct ifla_cacheinfo ci = {
		0xbadc0ded, 0xfacebeef, 0xdecafeed, 0xdeadfeed,
	};
	TEST_NESTED_NLATTR_OBJECT_EX_(fd, nlh0, hdrlen, init_msg, print_msg, 5,
				      XLAT_KNOWN(0x5, "IFLA_INET6_CACHEINFO"),
				      pattern, ci, print_quoted_hex, depth,
				      printf("{");
				      PRINT_FIELD_U(ci, max_reasm_len);
				      printf(", ");
				      PRINT_FIELD_U(ci, tstamp);
				      printf(", ");
				      PRINT_FIELD_U(ci, reachable_time);
				      printf(", ");
				      PRINT_FIELD_U(ci, retrans_time);
				      printf("}"));


	/* IFLA_INET6_ICMP6STATS */
	uint64_t icmp6_stats_vals[] = {
		0xdeadc0deda7aface, 0xdec0deedbadc0ded, 0xfacebeefdeadfeed,
		0xdeadc0deda7afacd, 0xdec0deedbadc0dee, 0xfacebeefdeadfeef,
		0xdeadc0deda7afacc
	};
	TEST_NESTED_NLATTR_ARRAY_EX_(fd, nlh0, hdrlen, init_msg, print_msg,
				     IFLA_INET6_ICMP6STATS,
				     XLAT_KNOWN(0x6, "IFLA_INET6_ICMP6STATS"),
				     pattern, icmp6_stats_vals, depth,
				     print_icmp6_stats_val);


	/* IFLA_INET6_TOKEN */
	uint8_t inet6_addr[16] = {
		0xba, 0xdc, 0x0d, 0xed, 0xfa, 0xce, 0xbe, 0xef,
		0xde, 0xca, 0xfe, 0xed, 0xde, 0xad, 0xfe, 0xed,
	};
	TEST_NESTED_NLATTR_OBJECT_EX_(fd, nlh0, hdrlen, init_msg, print_msg,
				      7, XLAT_KNOWN(0x7, "IFLA_INET6_TOKEN"),
				      pattern, inet6_addr, print_quoted_hex,
				      depth,
				      printf(XLAT_KNOWN_FMT("%s", "%s"),
					     XLAT_SEL("\"\\xba\\xdc\\x0d\\xed"
					     "\\xfa\\xce\\xbe\\xef"
					     "\\xde\\xca\\xfe\\xed"
					     "\\xde\\xad\\xfe\\xed\"",
					     "inet_pton(AF_INET6"
					     ", \"badc:ded:face:beef"
					     ":deca:feed:dead:feed\")")));


	/* IFLA_INET6_ADDR_GEN_MODE */
	static const struct {
		uint8_t flags;
		const char *str;
	} agms[] = {
		{ ARG_XLAT_KNOWN(0, "IN6_ADDR_GEN_MODE_EUI64") },
		{ ARG_XLAT_KNOWN(0x3, "IN6_ADDR_GEN_MODE_RANDOM") },
		{ ARG_XLAT_UNKNOWN(0x4, "IN6_ADDR_GEN_MODE_???") },
		{ ARG_XLAT_UNKNOWN(0xff, "IN6_ADDR_GEN_MODE_???") },
	};

	for (size_t i = 0; i < ARRAY_SIZE(agms); i++) {
		TEST_NESTED_NLATTR_OBJECT_EX_(fd, nlh0, hdrlen,
					      init_msg, print_msg, 8,
					      XLAT_KNOWN(0x8, "IFLA_INET6_ADDR_GEN_MODE"),
					      pattern, agms[i].flags,
					      print_quoted_hex, depth,
					      printf("%s", agms[i].str));
	}


	/* IFLA_INET6_RA_MTU */
	static const uint32_t ra_mtu = 0xdeadc0de;
	TEST_NESTED_NLATTR_OBJECT_EX_(fd, nlh0, hdrlen, init_msg, print_msg,
				      9, XLAT_KNOWN(0x9, "IFLA_INET6_RA_MTU"),
				      pattern, ra_mtu, print_quoted_hex, depth,
				      printf("3735929054"));
}

#endif /* STRACE_TESTS_NLATTR_IFLA_AF_INET6 */