/* SPDX-License-Identifier: BSD-3-Clause */
/*
 * Functions for the syntactic validation of EasyMesh CMDU payloads.
 *
 * Copyright (C) 2021-2024 IOPSYS Software Solutions AB. All rights reserved.
 *
 * See LICENSE file for license related information.
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>

#include <stdio.h>
#include <stdint.h>
#include <stdarg.h>

#include <libubox/blobmsg.h>
#include <libubox/blobmsg_json.h>
#include <libubox/uloop.h>
#include <libubox/ustream.h>
#include <libubox/utils.h>
#include <json-c/json.h>
#include <libubus.h>

#include <easy/easy.h>

#ifndef BIT
#define BIT(n)	(1 << (n))
#endif

#include "1905_tlvs.h"
#include "i1905_wsc.h"
#include "cmdu.h"
#include "easymesh.h"
#include "map_module.h"
#include "tlv_checker.h"

#ifdef EASYMESH_DEBUG
#define err(fmt, args...)		fprintf(stderr, fmt, ##args)	/* Flawfinder: ignore */
#define warn(fmt, args...)		fprintf(stderr, fmt, ##args_)	/* Flawfinder: ignore */
#define info(fmt, args...)		fprintf(stderr, fmt, ##args)	/* Flawfinder: ignore */
#define dbg(fmt, args...)		fprintf(stderr, fmt, ##args)	/* Flawfinder: ignore */
#define trace(fmt, args...)		fprintf(stderr, "%s:" fmt, __func__, ##args)	/* Flawfinder: ignore */
#else
#define err(fmt, args...)
#define warn(fmt, args...)
#define info(fmt, args...)
#define dbg(fmt, args...)
#define trace(fmt, args...)
#endif

#define tlv_validate(t, d)	validate_ ## t((d)->data, tlv_length(d))

#define tlv_validate_multi(iter, t, d)				\
({								\
	int ret = 0;						\
	iter = 0;						\
	while (iter < TLV_MAXNUM && d) {			\
		if (validate_ ## t((d)->data, tlv_length(d))) {	\
			ret = -1;				\
			break;					\
		}						\
		iter++;						\
	}							\
	ret;							\
})

#ifdef EASYMESH_VENDOR_EXT
static int validate_topology_response_vext(struct tlv *tv[TLV_MAXNUM])
{
	const uint8_t vendor_oui[4] = {0};
	uint32_t oui = 0;
	int num = 0;


	BUF_PUT_BE24(vendor_oui, EASYMESH_VENDOR_EXT_OUI_DEFAULT);
#ifdef EASYMESH_VENDOR_EXT_OUI
	oui = EASYMESH_VENDOR_EXT_OUI;
	BUF_PUT_BE24(vendor_oui, oui);
#endif

	while (num < TLV_MAXNUM && tv[num]) {
		struct tlv_vendor_bbss *tlv;
		uint16_t tlv_len = 0;
		int i, offset = 0;
		uint8_t oui2[3];  /* TODO: use the same vendor oui-type */

		tlv_len = tlv_length(tv[num]);
		if (!tlv_len)
			return -1;

		tlv = (struct tlv_vendor_bbss *)tv[num]->data;
		if (!tlv)
			return -1;

		/* oui (3 bytes) */
		if (offset + 3 > tlv_len)
			return -1;

		memcpy(oui2, vendor_oui, 3);
		oui2[2]++;
		if (memcmp(tlv->oui, oui2, 3)) {
			num++;
			continue;
		}

		offset += 3;

		/* num_radios (1 byte) */
		if (offset + 1 > tlv_len)
			return -1;

		offset += 1;

		for (i = 0; i < tlv->num_radios; i++) {
			uint8_t num_bss = 0;

			/* macaddr (6 bytes) */
			if (offset + 6 > tlv_len)
				return -1;

			offset += 6;

			/* num_bss (1 byte) */
			if (offset + 1 > tlv_len)
				return -1;

			memcpy(&num_bss, &tv[num]->data[offset], 1);

			offset += 1;

			/* bss macaddrs (num_bss * 6 bytes) */
			if (offset + (num_bss * 6) > tlv_len)
				return -1;

			offset += num_bss * 6;
		}

		num++;
	}

	return 0;
}
#endif	/* EASYMESH_VENDOR_EXT */

/////////////////////////////////////////////////////////////////////////

typedef bool (*map_cmdu_validate_func_t)(struct cmdu_buff *cmdu,
					 struct tlv *tv[][TLV_MAXNUM],
					 size_t num_tv,
					 uint8_t profile);

struct map_cmdu_validator {
	uint16_t type;
	map_cmdu_validate_func_t func;
};


#define FUNC(t)	{ .type = t, .func = map_cmdu_validate_ ## t }
#define DEFINE_FUNC(t)	map_cmdu_validate_ ## t

#define NUM_MAP_CMDUS	(CMDU_TYPE_1905_END - CMDU_TYPE_1905_START) + \
			(CMDU_TYPE_MAP_END - CMDU_TYPE_MAP_START) + 2


bool DEFINE_FUNC(CMDU_TYPE_TOPOLOGY_NOTIFICATION)(struct cmdu_buff *cmdu,
						  struct tlv *tv[][TLV_MAXNUM],
						  size_t num_tv,
						  uint8_t profile)
{
	return true;
}

bool DEFINE_FUNC(CMDU_TYPE_TOPOLOGY_RESPONSE)(struct cmdu_buff *cmdu,
					      struct tlv *tv[][TLV_MAXNUM],
					      size_t num_tv,
					      uint8_t profile)
{
#ifdef EASYMESH_VENDOR_EXT	//FIXME
	struct tlv_policy vendor_ext_policy[] = {
			{ .type = TLV_TYPE_VENDOR_SPECIFIC,
					.present = TLV_PRESENT_OPTIONAL_MORE,
					.minlen = 3, /* tlv_vendor_bbss:oui+num_radios */
			},
	};
	int ret = 0;

	ret = cmdu_parse_tlvs(cmdu, &tv[11], vendor_ext_policy, 1) << 1;
	if (ret) {
		dbg("%s: cmdu_parse_tlvs( profile=%d) failed, err = (%d) '%s', %d\n",
		    __func__, profile, map_error, map_strerror(map_error), ret);
		return false;
	}
#endif
	trace("SA = " MACFMT ", profile = %d\n", MAC2STR(cmdu->origin), profile);

	if (tv[6][0] && tlv_validate(MAP_TLV_SUPPORTED_SERVICE, tv[6][0]))
		return false;

	if (tlv_validate(MAP_TLV_AP_OPERATIONAL_BSS, tv[7][0]))
		return false;

	if (tv[8][0] && tlv_validate(MAP_TLV_ASSOCIATED_CLIENTS, tv[8][0]))
		return false;

	if (tlv_validate(MAP_TLV_MULTIAP_PROFILE, tv[9][0]))
		return false;

#ifdef EASYMESH_VENDOR_EXT
	/* TLV_TYPE_VENDOR_SPECIFIC */
	if (validate_topology_response_vext(tv[11]))
		return false;
#endif

#if (EASYMESH_VERSION > 2)
	if (profile > MULTIAP_PROFILE_2) {
		if (tlv_validate(MAP_TLV_BSS_CONFIGURATION_REPORT, tv[10][0]))
			return false;
	}
#endif

	return true;
}

bool DEFINE_FUNC(CMDU_TYPE_VENDOR_SPECIFIC)(struct cmdu_buff *cmdu,
					    struct tlv *tv[][TLV_MAXNUM],
					    size_t num_tv,
					    uint8_t profile)
{
	trace("SA = " MACFMT ", profile = %d\n", MAC2STR(cmdu->origin), profile);

	if (tlv_validate(TLV_TYPE_VENDOR_SPECIFIC, tv[0][0]))
		return false;

	return true;
}

enum CMDU_TYPE_AP_AUTOCONFIGURATION_SEARCH_ATTRS {
	AP_CONFIG_SEARCH_ATTR_1905_AL_MACADDR,
	AP_CONFIG_SEARCH_ATTR_SEARCHED_ROLE,
	AP_CONFIG_SEARCH_ATTR_FREQ_BAND,
	AP_CONFIG_SEARCH_ATTR_SUPPORTED_SERVICE,
	AP_CONFIG_SEARCH_ATTR_SEARCHED_SERVICE,
	AP_CONFIG_SEARCH_ATTR_MAP_PROFILE,
	AP_CONFIG_SEARCH_ATTR_DPP_CHIRP,
	NUM_ATTRS_AP_CONFIG_SEARCH,
};

bool DEFINE_FUNC(CMDU_TYPE_AP_AUTOCONFIGURATION_SEARCH)(struct cmdu_buff *cmdu,
							struct tlv *tv[][TLV_MAXNUM],
							size_t num_tv,
							uint8_t profile)
{
	trace("SA = " MACFMT ", profile = %d\n", MAC2STR(cmdu->origin), profile);

	if (tv[AP_CONFIG_SEARCH_ATTR_SUPPORTED_SERVICE][0] &&
	    tlv_validate(MAP_TLV_SUPPORTED_SERVICE, tv[AP_CONFIG_SEARCH_ATTR_SUPPORTED_SERVICE][0]))
		return false;

	if (tv[AP_CONFIG_SEARCH_ATTR_SEARCHED_SERVICE][0] &&
	    tlv_validate(MAP_TLV_SEARCHED_SERVICE, tv[AP_CONFIG_SEARCH_ATTR_SEARCHED_SERVICE][0]))
		return false;

	return true;
}


enum CMDU_TYPE_AP_AUTOCONFIGURATION_RESPONSE_ATTRS {
	AP_CONFIG_RESP_ATTR_SUPPORTED_ROLE,
	AP_CONFIG_RESP_ATTR_FREQ_BAND,
	AP_CONFIG_RESP_ATTR_SUPPORTED_SERVICE,
	AP_CONFIG_RESP_ATTR_MAP_PROFILE,
	AP_CONFIG_RESP_ATTR_1905_SECURITY,
	AP_CONFIG_RESP_ATTR_DPP_CHIRP,
	AP_CONFIG_RESP_ATTR_CNTLR_CAPS,
	NUM_ATTRS_AP_CONFIG_RESP,
};

bool DEFINE_FUNC(CMDU_TYPE_AP_AUTOCONFIGURATION_RESPONSE)(struct cmdu_buff *cmdu,
							  struct tlv *tv[][TLV_MAXNUM],
							  size_t num_tv,
							  uint8_t profile)
{
	trace("SA = " MACFMT ", profile = %d\n", MAC2STR(cmdu->origin), profile);

	if (tv[AP_CONFIG_RESP_ATTR_SUPPORTED_SERVICE][0] &&
	    tlv_validate(MAP_TLV_SUPPORTED_SERVICE, tv[AP_CONFIG_RESP_ATTR_SUPPORTED_SERVICE][0]))
		return false;

	return true;
}

bool DEFINE_FUNC(CMDU_TYPE_AP_AUTOCONFIGURATION_WSC)(struct cmdu_buff *cmdu,
						     struct tlv *tv[][TLV_MAXNUM],
						     size_t num_tv,
						     uint8_t profile)
{
	uint8_t wsc_mtype = 0;
	struct tlv *t = NULL;

	trace("SA = " MACFMT ", profile = %d\n", MAC2STR(cmdu->origin), profile);
	t = cmdu_peek_tlv(cmdu, TLV_TYPE_WSC);
	if (!t) {
		map_error = MAP_STATUS_ERR_TLV_NUM_LESS;
		dbg("%s: WSC TLV missing,  err = (%d) '%s'\n", __func__,
		    map_error, map_strerror(map_error));

		return false;
	}

	wsc_mtype = wsc_get_message_type(t->data, tlv_length(t));
	if (wsc_mtype != WPS_M1 && wsc_mtype != WPS_M2) {
		map_error = MAP_STATUS_ERR_TLV_MALFORMED;
		dbg("%s: WSC msg not M1 or M2 ,  err = (%d) '%s'\n", __func__,
		    map_error, map_strerror(map_error));

		return false;
	}

	if (wsc_mtype == WPS_M1) {
		if (num_tv > 0 && tlv_validate(MAP_TLV_AP_RADIO_BASIC_CAPABILITIES, tv[0][0]))
			return false;

		/* validate one WSC TLV (containing M1) */
		if (num_tv > 1 && tlv_validate(TLV_TYPE_WSC, tv[1][0]))
			return false;

		if (num_tv > 2 && tlv_validate(MAP_TLV_PROFILE2_AP_CAPABILITY, tv[2][0]))
			return false;

		if (num_tv > 3 && tlv_validate(MAP_TLV_AP_RADIO_ADV_CAPABILITY, tv[3][0]))
			return false;
	} else {
		int i;

		if (num_tv > 0 && tlv_validate(MAP_TLV_AP_RADIO_IDENTIFIER, tv[0][0]))
			return false;

		/* validate one-or-more WSC TLV (containing M2) */
		if (num_tv > 1 && tlv_validate_multi(i, TLV_TYPE_WSC, tv[1][i]))
			return false;

		if (num_tv > 2 && tv[2][0] && tlv_validate(MAP_TLV_DEFAULT_8021Q_SETTINGS, tv[2][0]))
			return false;

		if (num_tv > 3 && tv[3][0] && tlv_validate(MAP_TLV_TRAFFIC_SEPARATION_POLICY, tv[3][0]))
			return false;

#if (EASYMESH_VERSION >= 6)
		if (num_tv > 4 && tv[4][0] && tlv_validate(MAP_TLV_AP_MLD_CONFIG, tv[4][0]))
			return false;

		if (num_tv > 5 && tv[5][0] && tlv_validate(MAP_TLV_BACKHAUL_STA_MLD_CONFIG, tv[5][0]))
			return false;

		if (num_tv > 6 && tv[6][0] && tlv_validate(MAP_TLV_BSS_ADVANCED_CONFIG, tv[6][0]))
			return false;

		if (num_tv > 7 && tv[7][0] && tlv_validate(MAP_TLV_RSN_PARAMETERS_CONFIG, tv[7][0]))
			return false;
#endif
	}

	return true;
}

bool DEFINE_FUNC(CMDU_1905_ACK)(struct cmdu_buff *cmdu,
				struct tlv *tv[][TLV_MAXNUM],
				size_t num_tv,
				uint8_t profile)
{
	int num = 0;

	/* Parse Error Code TLVs */
	if (num_tv > 0 && tv[0][0]) {
		if (tlv_validate_multi(num, MAP_TLV_ERROR_CODE, tv[0][num]))
			return false;
	}

	if (!num)
		dbg("%s: 1905 ACK without any ERROR CODE\n", __func__);

	return true;
}

bool DEFINE_FUNC(CMDU_AP_CAPABILITY_REPORT)(struct cmdu_buff *cmdu,
					    struct tlv *tv[][TLV_MAXNUM],
					    size_t num_tv,
					    uint8_t profile)
{
	int idx;

	trace("SA = " MACFMT ", profile = %d\n", MAC2STR(cmdu->origin), profile);

	if (num_tv > 0 && tlv_validate(MAP_TLV_AP_CAPABILITY, tv[0][0]))
		return false;

	if (num_tv > 1 && tlv_validate_multi(idx, MAP_TLV_AP_RADIO_BASIC_CAPABILITIES, tv[1][idx]))
		return false;

	if (num_tv > 2 && tv[2][0] && tlv_validate_multi(idx, MAP_TLV_AP_HT_CAPABILITIES, tv[2][idx]))
		return false;

	if (num_tv > 3 && tv[3][0] && tlv_validate_multi(idx, MAP_TLV_AP_VHT_CAPABILITIES, tv[3][idx]))
		return false;

	if (num_tv > 4 && tv[4][0] && tlv_validate_multi(idx, MAP_TLV_AP_HE_CAPABILITIES, tv[4][idx]))
		return false;

	if (profile > MULTIAP_PROFILE_1) {
		if (num_tv > 5 && tlv_validate(MAP_TLV_CHANNEL_SCAN_CAPABILITY, tv[5][0]))
			return false;

		if (num_tv > 6 && tlv_validate(MAP_TLV_CAC_CAPABILITY, tv[6][0]))
			return false;

		if (num_tv > 7 && tlv_validate(MAP_TLV_PROFILE2_AP_CAPABILITY, tv[7][0]))
			return false;

		if (num_tv > 8 && tlv_validate(MAP_TLV_METRIC_COLLECTION_INTERVAL, tv[8][0]))
			return false;
	}

	if (profile > MULTIAP_PROFILE_2) {
#if (EASYMESH_VERSION >= 3)
		if (num_tv > 9 && tlv_validate_multi(idx, MAP_TLV_AP_WIFI6_CAPS, tv[9][idx]))
			return false;

		if (num_tv > 10 && tlv_validate(MAP_TLV_1905_SECURITY_CAPS, tv[10][0]))
			return false;

		if (num_tv > 11 && tlv_validate(MAP_TLV_DEVICE_INVENTORY, tv[11][0]))
			return false;
#endif

#if (EASYMESH_VERSION >= 4)
		if (num_tv > 12 && tlv_validate_multi(idx, MAP_TLV_AP_RADIO_ADV_CAPABILITY, tv[12][idx]))
			return false;
#endif

#if (EASYMESH_VERSION >= 6)
		if (num_tv > 13 && tv[13][0] && tlv_validate(MAP_TLV_AKM_SUITE_CAPS, tv[13][0]))
			return false;

		if (num_tv > 14 && tlv_validate_multi(idx, MAP_TLV_WIFI7_AGENT_CAPABILITIES, tv[14][idx]))
			return false;

		if (num_tv > 15 && tlv_validate_multi(idx, MAP_TLV_EHT_OPERATIONS, tv[15][idx]))
			return false;
#endif
	}

	return true;
}

bool DEFINE_FUNC(CMDU_AP_METRICS_RESPONSE)(struct cmdu_buff *cmdu,
					   struct tlv *tv[][TLV_MAXNUM],
					   size_t num_tv,
					   uint8_t profile)
{
	int idx = 0;

	trace("SA = " MACFMT ", profile = %d\n", MAC2STR(cmdu->origin), profile);

	if (num_tv > 0 && tlv_validate_multi(idx, MAP_TLV_AP_METRICS, tv[0][idx]))
		return false;

	if (num_tv > 1 && tv[1][0] && tlv_validate_multi(idx, MAP_TLV_ASSOCIATED_STA_TRAFFIC_STATS, tv[1][idx]))
		return false;

	if (num_tv > 2 && tv[2][0] && tlv_validate_multi(idx, MAP_TLV_ASSOCIATED_STA_LINK_METRICS, tv[2][idx]))
		return false;

	if (num_tv > 3 && tlv_validate_multi(idx, MAP_TLV_AP_EXTENDED_METRICS, tv[3][idx]))
		return false;

	if (num_tv > 4 && tv[4][0] && tlv_validate_multi(idx, MAP_TLV_RADIO_METRICS, tv[4][idx]))
		return false;

	if (num_tv > 5 && tv[5][0] && tlv_validate_multi(idx, MAP_TLV_ASSOCIATED_STA_EXT_LINK_METRICS, tv[5][idx]))
		return false;

	return true;
}

#if (EASYMESH_VERSION >= 2)
bool DEFINE_FUNC(CMDU_CHANNEL_SCAN_REPORT)(struct cmdu_buff *cmdu,
					   struct tlv *tv[][TLV_MAXNUM],
					   size_t num_tv,
					   uint8_t profile)
{
#define MAX_NUM_SCANRES	256
	struct tlv_policy scanres_policy[] = {
		[0] = {
			.type = MAP_TLV_CHANNEL_SCAN_RES,
			.present = TLV_PRESENT_MORE,
			.minlen = 9
		}
	};
	struct tlv *tv_scan[MAX_NUM_SCANRES] = {0};
	int num = MAX_NUM_SCANRES;
	int ret = 0;
	int i;

	if (num_tv > 0 && tlv_validate(MAP_TLV_TIMESTAMP, tv[0][0]))
		return false;

	ret = cmdu_parse_tlv_single(cmdu, tv_scan, scanres_policy, &num);
	if (ret) {
		dbg("%s: cmdu_parse_tlv_single failed, err = (%d) '%s'\n",
		    __func__, map_error, map_strerror(map_error));
		return false;
	}

	if (!tv_scan[0]) {
		dbg("%s: No SCAN_RES TLV received!\n", __func__);
		return false;
	}

	if (tlv_validate_multi(i, MAP_TLV_CHANNEL_SCAN_RES, tv_scan[i]))
		return false;

#undef MAX_NUM_SCANRES
	return true;
}
#endif

#if (EASYMESH_VERSION > 2)
/**
 * @enum enum direct_encap_dpp_order
 * @brief specifies order of output TLVs and max. number of different TLVs
 *		for validate_direct_encap_dpp function.
 */
enum proxied_encap_dpp_order {
	PROXIED_ENCAP_1905_ENCAP_DPP_IDX,
	PROXIED_ENCAP_CHIRP_VALUE_IDX,
	PROXIED_ENCAP_DPP_MAX_NUMBER_OF_TLV_TYPES
};

bool DEFINE_FUNC(CMDU_PROXIED_ENCAP_DPP)(struct cmdu_buff *cmdu,
					 struct tlv *tv[][TLV_MAXNUM],
					 size_t num_tv,
					 uint8_t profile)
{
	if (num_tv > 0 && tlv_validate(MAP_TLV_1905_ENCAP_DPP, tv[PROXIED_ENCAP_1905_ENCAP_DPP_IDX][0]))
		return false;

	if (num_tv > 1 && tv[PROXIED_ENCAP_CHIRP_VALUE_IDX][0]) {
		if (tlv_validate(MAP_TLV_DPP_CHIRP_VALUE, tv[PROXIED_ENCAP_CHIRP_VALUE_IDX][0]))
			return false;
	}

	return true;
}

/**
 * @enum enum direct_encap_dpp_order
 * @brief specifies order of output TLVs and max. number of different TLVs
 *		for validate_direct_encap_dpp function.
 */
enum direct_encap_dpp_order {
	DIRECT_ENCAP_DPP_MESSAGE_IDX,
	DIRECT_ENCAP_DPP_MAX_NUMBER_OF_TLV_TYPES
};

bool DEFINE_FUNC(CMDU_DIRECT_ENCAP_DPP)(struct cmdu_buff *cmdu,
					struct tlv *tv[][TLV_MAXNUM],
					size_t num_tv,
					uint8_t profile)
{
	if (num_tv > 0 && tlv_validate(MAP_TLV_DPP_MESSAGE, tv[DIRECT_ENCAP_DPP_MESSAGE_IDX][0]))
		return false;

	return true;
}

/**
 * @enum enum bss_configuration_request_tlvs_order
 * @brief specifies order of output TLVs and max. number of different TLVs
 *		for validate_bss_configuration_request function.
 */
enum bss_configuration_request_tlvs_order {
	BSS_CFG_REQ_MULTIAP_PROFILE_IDX,
	BSS_CFG_REQ_SUPPORTED_SERVICE_IDX,
	BSS_CFG_REQ_AKM_SUITE_CAPS_IDX,
	BSS_CFG_REQ_AP_RADIO_BASIC_CAPS_IDX,
	BSS_CFG_REQ_BACKHAUL_STA_RADIO_CAPS_IDX,
	BSS_CFG_REQ_PROFILE2_AP_CAP_IDX,
	BSS_CFG_REQ_AP_RADIO_ADVANCED_CAPS_IDX,
	BSS_CFG_REQ_CONFIG_REQUEST_IDX,
	BSS_CFG_REQ_MAX_NUMBER_OF_TLV_TYPES
};

bool DEFINE_FUNC(CMDU_BSS_CONFIG_REQUEST)(struct cmdu_buff *cmdu,
					  struct tlv *tv[][TLV_MAXNUM],
					  size_t num_tv,
					  uint8_t profile)
{
	int i;

	if (num_tv > 0 && tlv_validate(MAP_TLV_MULTIAP_PROFILE, tv[0][0]))
		return false;

	if (num_tv > 1 && tlv_validate(MAP_TLV_SUPPORTED_SERVICE, tv[1][0]))
		return false;

	if (num_tv > 2 && tlv_validate(MAP_TLV_AKM_SUITE_CAPS, tv[2][0]))
		return false;

	if (num_tv > 3 && tlv_validate_multi(i, MAP_TLV_AP_RADIO_BASIC_CAPABILITIES, tv[3][i]))
		return false;

	if (num_tv > 4 && tv[4][0] && tlv_validate_multi(i, MAP_TLV_BACKHAUL_STA_RADIO_CAPABILITY, tv[4][i]))
		return false;

	if (num_tv > 5 && tlv_validate(MAP_TLV_PROFILE2_AP_CAPABILITY, tv[5][0]))
		return false;

	if (num_tv > 6 && tlv_validate_multi(i, MAP_TLV_AP_RADIO_ADV_CAPABILITY, tv[6][i]))
		return false;

	if (num_tv > 7 && tlv_validate(MAP_TLV_BSS_CONFIGURATION_REQUEST, tv[7][0]))
		return false;

	return true;
}

bool DEFINE_FUNC(CMDU_BSS_CONFIG_RESULT)(struct cmdu_buff *cmdu,
					 struct tlv *tv[][TLV_MAXNUM],
					 size_t num_tv,
					 uint8_t profile)
{
	if (num_tv > 0 && tlv_validate(MAP_TLV_BSS_CONFIGURATION_REPORT, tv[0][0]))
		return false;

	return true;
}

bool DEFINE_FUNC(CMDU_CHIRP_NOTIFICATION)(struct cmdu_buff *cmdu,
					  struct tlv *tv[][TLV_MAXNUM],
					  size_t num_tv,
					  uint8_t profile)
{
	return true;
}

bool DEFINE_FUNC(CMDU_DPP_BOOTSTRAPING_URI)(struct cmdu_buff *cmdu,
					    struct tlv *tv[][TLV_MAXNUM],
					    size_t num_tv,
					    uint8_t profile)
{
	return true;
}
#endif /* EASYMESH_VERSION > 2 */

#if (EASYMESH_VERSION > 5)
bool DEFINE_FUNC(CMDU_EARLY_AP_CAPABILITY_REPORT)(struct cmdu_buff *cmdu,
						  struct tlv *tv[][TLV_MAXNUM],
						  size_t num_tv,
						  uint8_t profile)
{
	int i;

	if (num_tv > 0 && tlv_validate(MAP_TLV_AP_CAPABILITY, tv[0][0]))
		return false;

	if (num_tv > 1 && tlv_validate_multi(i, MAP_TLV_AP_RADIO_BASIC_CAPABILITIES, tv[1][i]))
		return false;

	if (num_tv > 2 && tlv_validate(MAP_TLV_AKM_SUITE_CAPS, tv[2][0]))
		return false;

	if (num_tv > 3 && tv[3][0]) {
		if (tlv_validate_multi(i, MAP_TLV_AP_HT_CAPABILITIES, tv[3][i]))
			return false;
	}

	if (num_tv > 4 && tv[4][0]) {
		if (tlv_validate_multi(i, MAP_TLV_AP_VHT_CAPABILITIES, tv[4][i]))
			return false;
	}

	if (num_tv > 5 && tv[5][0]) {
		if (tlv_validate_multi(i, MAP_TLV_AP_HE_CAPABILITIES, tv[5][i]))
			return false;
	}

	if (num_tv > 6 && tv[6][0]) {
		if (tlv_validate_multi(i, MAP_TLV_AP_WIFI6_CAPS, tv[6][i]))
			return false;
	}

	if (num_tv > 7 && tv[7][0]) {
		if (tlv_validate(MAP_TLV_WIFI7_AGENT_CAPABILITIES, tv[7][0]))
			return false;
	}

	if (num_tv > 8 && tv[8][0]) {
		if (tlv_validate(MAP_TLV_EHT_OPERATIONS, tv[8][0]))
			return false;
	}

	if (num_tv > 9 && tv[9][0]) {
		if (tlv_validate(MAP_TLV_1905_SECURITY_CAPS, tv[9][0]))
			return false;
	}

	if (num_tv > 10 && tv[10][0]) {
		if (tlv_validate(MAP_TLV_CAC_CAPABILITY, tv[10][0]))
			return false;
	}

	if (num_tv > 11 && tv[11][0]) {
		if (tlv_validate(MAP_TLV_PROFILE2_AP_CAPABILITY, tv[11][0]))
			return false;
	}

	if (num_tv > 12 && tv[12][0]) {
		if (tlv_validate(MAP_TLV_METRIC_COLLECTION_INTERVAL, tv[12][0]))
			return false;
	}

	if (num_tv > 13 && tv[13][0]) {
		if (tlv_validate(MAP_TLV_DEVICE_INVENTORY, tv[13][0]))
			return false;
	}

	if (num_tv > 14 && tv[14][0]) {
		if (tlv_validate_multi(i, MAP_TLV_AP_RADIO_ADV_CAPABILITY, tv[14][i]))
			return false;
	}

	if (num_tv > 15 && tv[15][0]) {
		if (tlv_validate(MAP_TLV_SUPPORTED_CIPHER_SUITES, tv[15][0]))
			return false;
	}

	return true;
}
#endif /* EASYMESH_VERSION > 5 */



struct map_cmdu_validator map_cmdu_validate[] = {
	FUNC(CMDU_TYPE_TOPOLOGY_NOTIFICATION),
	FUNC(CMDU_TYPE_TOPOLOGY_RESPONSE),
	FUNC(CMDU_TYPE_VENDOR_SPECIFIC),
	FUNC(CMDU_TYPE_AP_AUTOCONFIGURATION_SEARCH),
	FUNC(CMDU_TYPE_AP_AUTOCONFIGURATION_RESPONSE),
	FUNC(CMDU_TYPE_AP_AUTOCONFIGURATION_WSC),
	FUNC(CMDU_1905_ACK),
	FUNC(CMDU_AP_CAPABILITY_REPORT),
	FUNC(CMDU_AP_METRICS_RESPONSE),
#if (EASYMESH_VERSION >= 2)
	FUNC(CMDU_CHANNEL_SCAN_REPORT),
#endif
#if (EASYMESH_VERSION > 2)
	FUNC(CMDU_CHIRP_NOTIFICATION),
	FUNC(CMDU_PROXIED_ENCAP_DPP),
	FUNC(CMDU_DIRECT_ENCAP_DPP),
	FUNC(CMDU_BSS_CONFIG_REQUEST),
	FUNC(CMDU_BSS_CONFIG_RESULT),
	FUNC(CMDU_DPP_BOOTSTRAPING_URI),
#endif
#if (EASYMESH_VERSION > 5)
	FUNC(CMDU_EARLY_AP_CAPABILITY_REPORT),
#endif
};


bool map_cmdu_validate_func(struct cmdu_buff *cmdu,
			    struct tlv *tv[][TLV_MAXNUM],
			    size_t num_tv,
			    uint8_t profile)
{
	return true;
}

bool map_cmdu_validate_parse(struct cmdu_buff *cmdu,
			     struct tlv *tv[][TLV_MAXNUM],
			     size_t num_tv,
			     uint8_t profile)
{
	uint16_t type;
	int ret;

	ret = map_cmdu_parse_tlvs(cmdu, tv, num_tv, profile);
	if (ret) {
		dbg("%s: failed, err = (%d) '%s'\n", __func__, map_error,
		    map_strerror(map_error));
		return false;
	}

	type = cmdu_get_type(cmdu);
	for (int i = 0; i < ARRAY_SIZE(map_cmdu_validate); i++) {
		if (type == map_cmdu_validate[i].type && map_cmdu_validate[i].func)
			return map_cmdu_validate[i].func(cmdu, tv, num_tv, profile);
	}

	return map_cmdu_validate_func(cmdu, tv, num_tv, profile);
}

#undef FUNC
#undef DEFINE_FUNC
