/* SPDX-License-Identifier: BSD-3-Clause */
/*
 * tlv_checker.c - check if EasyMesh TLV data is valid.
 *
 * Copyright (C) 2024 IOPSYS Software Solutions AB. All rights reserved.
 *
 * Author: anjan.chanda@iopsys.eu
 *
 * See LICENSE file for license related information.
 */
#include <stdint.h>
#include <stdio.h>
#include <string.h>

#include <json-c/json.h>
#include <easy/easy.h>

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

struct tlv_validator {
	uint8_t type;
	int (*validator)(const uint8_t *data, size_t datalen);
};

#define DEFINE_VALIDATOR(t)                           \
	{                                             \
		.type = t, .validator = validate_##t, \
	}

#define FUNC(t) validate_##t


int FUNC(TLV_TYPE_WSC)(const uint8_t *data, size_t datalen)
{
	const uint8_t *mx_end;
	const uint8_t *pos;
	int ret = -1;

	if (!data || !datalen)
		return -1;

	pos = data;
	mx_end = data + datalen;

	while ((pos - data) < datalen - 4) {
		uint16_t attr_type;
		uint16_t attr_len;

		attr_type = buf_get_be16(pos);
		pos += 2;
		attr_len = buf_get_be16(pos);
		pos += 2;

		if (pos + attr_len > mx_end) {
			ret = -1;
			break;
		}

		if (attr_type == ATTR_MSG_TYPE) {
			if (attr_len != 1) {
				ret = -1;
				break;
			}

			if (*pos == WPS_M1 || *pos == WPS_M2)
				ret = 0;
		}

		pos += attr_len;
	}

	return ret;
}

int FUNC(TLV_TYPE_VENDOR_SPECIFIC)(const uint8_t *data, size_t datalen)
{
	if (datalen < 3)	/* atleast oui size */
		return -1;

	return 0;
}

int FUNC(MAP_TLV_SUPPORTED_SERVICE)(const uint8_t *data, size_t datalen)
{
	uint8_t num_services;

	if (datalen < sizeof(uint8_t))
		return -1;

	num_services = data[0];

	if (datalen != sizeof(uint8_t) + num_services)
		return -1;

	return 0;
}

int FUNC(MAP_TLV_SEARCHED_SERVICE)(const uint8_t *data, size_t datalen)
{
	uint8_t num_services;

	if (datalen < sizeof(uint8_t))
		return -1;

	num_services = data[0];

	if (datalen != sizeof(uint8_t) + num_services)
		return -1;

	return 0;
}

int FUNC(MAP_TLV_AP_RADIO_IDENTIFIER)(const uint8_t *data, size_t datalen)
{
	/* Radio identifier should be exactly 6 bytes */
	if (datalen != 6)
		return -1;

	return 0;
}

int FUNC(MAP_TLV_AP_CAPABILITY)(const uint8_t *data, size_t datalen)
{
	/* AP capability should be exactly 1 byte */
	if (datalen != 1)
		return -1;

	return 0;
}

int FUNC(MAP_TLV_AP_RADIO_BASIC_CAPABILITIES)(const uint8_t *data,
					      size_t datalen)
{
	const uint8_t *ptr;
	size_t remaining_len;
	uint8_t num_opclass;
	uint8_t num_nonop_channel;
	uint8_t i;

	/* Minimum size includes 6-byte radio, 1-byte max_bssnum, and 1-byte num_opclass */
	if (datalen < 8)
		return -1;

	ptr = data;
	remaining_len = datalen;

	/* Extract and validate the radio identifier (6 bytes) */
	ptr += 6;
	remaining_len -= 6;

	/* Extract and validate max_bssnum (1 byte) */
	ptr += 1;
	remaining_len -= 1;

	/* Extract num_opclass */
	num_opclass = *ptr;
	ptr += 1;
	remaining_len -= 1;

	/* Validate each operating class */
	for (i = 0; i < num_opclass; i++) {
		/* At least 3 bytes required for classid, max_txpower, and num_nonop_channel */
		if (remaining_len < 3) {
			return -1;
		}

		/* Extract classid, max_txpower, and num_nonop_channel */
		num_nonop_channel = ptr[2];
		ptr += 3;
		remaining_len -= 3;

		/* Not enough data for nonop_channel array */
		if (remaining_len < num_nonop_channel) {
			return -1;
		}

		/* Move pointer past the nonop_channel array */
		ptr += num_nonop_channel;
		remaining_len -= num_nonop_channel;
	}

	/* If we processed exactly all data, validation is successful */
	return (remaining_len == 0) ? 0 : -1;
}

int FUNC(MAP_TLV_AP_OPERATIONAL_BSS)(const uint8_t *buf, size_t buflen)
{
	const uint8_t *buf_ptr;
	uint8_t num_radio;
	uint8_t num_bss;
	uint8_t ssidlen;
	size_t offset;
	uint8_t i;
	uint8_t j;

	/* Ensure the buf is large enough to hold at least num_radio */
	if (buflen < sizeof(uint8_t))
		return -1;

	buf_ptr = (const uint8_t *)buf;
	offset = 0;

	/* Extract num_radio */
	num_radio = buf_ptr[offset];
	offset += sizeof(uint8_t);

	/* Iterate over each radio in the radiolist */
	for (i = 0; i < num_radio; i++) {
		/* Ensure buf has space for radio[6] and num_bss */
		if (offset + 6 + sizeof(uint8_t) > buflen)
			return -1;

		/* Skip the radio[6] */
		offset += 6;

		/* Extract num_bss */
		num_bss = buf_ptr[offset];
		offset += sizeof(uint8_t);

		/* Iterate over each BSS in the bss list */
		for (j = 0; j < num_bss; j++) {
			/* Ensure buf has space for bssid[6] and ssidlen */
			if (offset + 6 + sizeof(uint8_t) > buflen)
				return -1;

			/* Skip the bssid[6] */
			offset += 6;

			/* Extract ssidlen */
			ssidlen = buf_ptr[offset];
			offset += sizeof(uint8_t);

			/* Ensure buf has space for the SSID */
			if (offset + ssidlen > buflen)
				return -1;

			/* Skip the SSID */
			offset += ssidlen;
		}
	}

	return (offset == buflen) ? 0 : -1;
}

int FUNC(MAP_TLV_ASSOCIATED_CLIENTS)(const uint8_t *buf, size_t buflen)
{
	const uint8_t *buf_ptr;
	uint16_t num_client;
	uint8_t num_bss;
	size_t offset;
	uint8_t i;
	uint8_t j;

	/* Ensure the buffer is large enough to hold at least num_bss */
	if (buflen < sizeof(uint8_t))
		return -1;

	buf_ptr = (const uint8_t *)buf;
	offset = 0;

	/* Extract num_bss */
	num_bss = buf_ptr[offset];
	offset += sizeof(uint8_t);

	/* Iterate over each BSS in the bss list */
	for (i = 0; i < num_bss; i++) {
		/* Ensure buffer has space for bssid[6] and num_client */
		if (offset + 6 + sizeof(uint16_t) > buflen)
			return -1;

		/* Skip the bssid[6] */
		offset += 6;

		/* Extract num_client */
		num_client = BUF_GET_BE16(buf_ptr[offset]);
		offset += sizeof(uint16_t);

		/* Iterate over each STA in the sta list */
		for (j = 0; j < num_client; j++) {
			/* Ensure buffer has space for macaddr[6] and conntime */
			if (offset + 6 + sizeof(uint16_t) > buflen)
				return -1;

			/* Skip the macaddr[6] */
			offset += 6;

			/* Skip the conntime (uint16_t) */
			offset += sizeof(uint16_t);
		}
	}

	return (offset == buflen) ? 0 : -1;
}

#if (EASYMESH_VERSION >= 3)
int FUNC(MAP_TLV_BSS_CONFIGURATION_REPORT)(const uint8_t *buf, size_t buflen)
{
	const uint8_t *buf_ptr;
	size_t offset;
	uint8_t num_radio;
	uint8_t num_bss;
	uint8_t ssidlen;
	uint8_t i;
	uint8_t j;

	/* Ensure the buffer is large enough to hold at least num_radio */
	if (buflen < sizeof(uint8_t))
		return -1;

	buf_ptr = (const uint8_t *)buf;
	offset = 0;

	/* Extract num_radio */
	num_radio = buf_ptr[offset];
	offset += sizeof(uint8_t);

	/* Iterate over each radio in the radio list */
	for (i = 0; i < num_radio; i++) {
		/* Ensure buffer has space for ruid[6] and num_bss */
		if (offset + 6 + sizeof(uint8_t) > buflen) {
			return -1;
		}

		/* Skip the ruid[6] */
		offset += 6;

		num_bss = buf_ptr[offset];
		offset += sizeof(uint8_t);

		for (j = 0; j < num_bss; j++) {
			/* Ensure buffer has space for bssid[6], flag, rsvd, and ssidlen */
			if (offset + 6 + sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint8_t) > buflen)
				return -1;

			/* Skip the bssid[6] */
			offset += 6;

			/* Extract flag, rsvd, and ssidlen */
			offset += sizeof(uint8_t) + sizeof(uint8_t) +
				  sizeof(uint8_t);

			/* Extract ssidlen */
			ssidlen = buf_ptr[offset - sizeof(uint8_t)];

			/* Ensure buffer has space for the SSID */
			if (offset + ssidlen > buflen)
				return -1;

			offset += ssidlen;
		}
	}

	return (offset == buflen) ? 0 : -1;
}
#endif

int FUNC(MAP_TLV_ASSOCIATED_STA_EXT_LINK_METRICS)(const uint8_t *buf,
						  size_t buflen)
{
	const uint8_t *buf_ptr;
	size_t offset;
	uint8_t num_bss;
	uint8_t i;

	/* Ensure the buffer is large enough to hold at least macaddr[6] */
	if (buflen < sizeof(uint8_t) * 6)
		return -1;

	buf_ptr = (const uint8_t *)buf;
	offset = 0;

	/* Skip macaddr[6] */
	offset += 6;

	/* Ensure buffer is large enough to hold num_bss */
	if (offset + sizeof(uint8_t) > buflen) {
		return -1;
	}

	/* Extract num_bss */
	num_bss = buf_ptr[offset];
	offset += sizeof(uint8_t);

	/* Iterate over each BSS in the bss list */
	for (i = 0; i < num_bss; i++) {
		/* Ensure buffer has enough space for bssid[6] and the four uint32_t fields */
		if (offset + 6 + sizeof(uint32_t) * 4 > buflen)
			return -1;

		/* Skip the bssid[6] */
		offset += 6;

		/* Skip dl_rate, ul_rate, rx_util, and tx_util (each is uint32_t) */
		offset += sizeof(uint32_t) * 4;
	}

	return (offset == buflen) ? 0 : -1;
}

int FUNC(MAP_TLV_AP_HE_CAPABILITIES)(const uint8_t *buf, size_t buflen)
{
	uint8_t len_mcs;

	if (buflen < 9)
		return -1;

	len_mcs = buf[6];

	return buflen != (6 + 1 + len_mcs + 2) ? -1 : 0;
}

int FUNC(MAP_TLV_AP_VHT_CAPABILITIES)(const uint8_t *buf, size_t buflen)
{
	return buflen != 12 ? -1 : 0;
}

int FUNC(MAP_TLV_AP_HT_CAPABILITIES)(const uint8_t *buf, size_t buflen)
{
	return buflen != 7 ? -1 : 0;
}

int FUNC(MAP_TLV_STEERING_POLICY)(const uint8_t *data, size_t datalen)
{
	uint8_t num_macaddr_nobtmsteer;
	uint8_t num_macaddr_nosteer;
	size_t remaining_len;
	size_t expected_len;
	const uint8_t *ptr;
	uint8_t num_radio;
	uint8_t i;

	ptr = data;
	remaining_len = datalen;

	/* Validate nosteer sta_macaddr */
	if (remaining_len < 1)
		return -1;

	num_macaddr_nosteer = ptr[0];
	expected_len = 1 + (num_macaddr_nosteer * 6);

	if (remaining_len < expected_len)
		return -1;

	ptr += expected_len;
	remaining_len -= expected_len;

	/* Validate nobtmsteer sta_macaddr */
	if (remaining_len < 1)
		return -1; /* Not enough data for num field */

	num_macaddr_nobtmsteer = ptr[0];
	expected_len = 1 + (num_macaddr_nobtmsteer * 6);

	if (remaining_len < expected_len)
		return -1; /* Not enough data for nobtmsteer MAC addresses */

	ptr += expected_len;
	remaining_len -= expected_len;

	/* Validate num_radio field */
	if (remaining_len < 1)
		return -1; /* Not enough data for num_radio field */

	num_radio = ptr[0];
	ptr += 1;
	remaining_len -= 1;

	/* Validate each policy -
	 * Each policy is 6 bytes for radio +
	 * 1 byte for steer_policy +
	 * 1 byte for thresholds.
	 */
	expected_len = 8;
	for (i = 0; i < num_radio; i++) {
		if (remaining_len < expected_len)
			return -1;

		ptr += expected_len;
		remaining_len -= expected_len;
	}

	return remaining_len != 0 ? -1 : 0;
}

int FUNC(MAP_TLV_METRIC_REPORTING_POLICY)(const uint8_t *data, size_t datalen)
{
	size_t remaining_len;
	size_t expected_len;
	const uint8_t *ptr;
	uint8_t num_radio;
	uint8_t i;

	ptr = data;
	remaining_len = datalen;

	if (remaining_len < 2)
		return -1; /* Not enough data for interval and num_radio fields */

	ptr += 2;
	remaining_len -= 2;

	num_radio = data[1];

	/* Validate each policy -
	 * Each policy is 6 bytes for radio + 4 bytes for other fields.
	 */
	expected_len = 10;
	for (i = 0; i < num_radio; i++) {
		if (remaining_len < expected_len)
			return -1;

		ptr += expected_len;
		remaining_len -= expected_len;
	}

	return remaining_len != 0 ? -1 : 0;
}

int FUNC(MAP_TLV_CHANNEL_PREFERENCE)(const uint8_t *data, size_t datalen)
{
	size_t remaining_len;
	uint8_t num_opclass;
	uint8_t num_channel;
	size_t expected_len;
	const uint8_t *ptr;
	uint8_t i;

	ptr = data;
	remaining_len = datalen;

	/* 6 bytes for radio and 1 byte for num_opclass */
	if (remaining_len < 7)
		return -1;

	ptr += 6; /* Skip radio */
	remaining_len -= 6;

	num_opclass = *ptr;
	ptr += 1;
	remaining_len -= 1;

	/* Validate each operating class */
	for (i = 0; i < num_opclass; i++) {
		/* 1 byte for classid and 1 byte for num_channel */
		if (remaining_len < 2)
			return -1;

		ptr += 1; /* Skip classid */
		remaining_len -= 1;
		num_channel = *ptr;

		/* num_channel, num_channel for channels, preference */
		expected_len = 1 + num_channel + 1;

		if (remaining_len < expected_len)
			return -1;

		ptr += expected_len;
		remaining_len -= expected_len;
	}

	return remaining_len != 0 ? -1 : 0;
}

int FUNC(MAP_TLV_RADIO_OPERATION_RESTRICTION)(const uint8_t *data,
					      size_t datalen)
{
	const uint8_t *ptr;
	size_t remaining_len;
	uint8_t num_opclass;
	uint8_t num_channel;
	size_t expected_len;
	uint8_t i;

	ptr = data;
	remaining_len = datalen;

	/* Validate radio and num_opclass fields */
	if (remaining_len < 7)
		return -1;

	ptr += 7;
	remaining_len -= 7;
	num_opclass = data[6];

	/* Validate each operating class */
	for (i = 0; i < num_opclass; i++) {
		if (remaining_len < 2)
			return -1;

		num_channel = ptr[1];
		expected_len = 2 + (num_channel * 2);

		if (remaining_len < expected_len)
			return -1;

		ptr += expected_len;
		remaining_len -= expected_len;
	}

	return remaining_len != 0 ? -1 : 0;
}

int FUNC(MAP_TLV_TRANSMIT_POWER_LIMIT)(const uint8_t *data, size_t datalen)
{
	return datalen != 7 ? -1 : 0;
}

int FUNC(MAP_TLV_CHANNEL_SELECTION_RESPONSE)(const uint8_t *data,
					     size_t datalen)
{
	/* 6 bytes for radio + 1 byte for response */
	return datalen != 7 ? -1 : 0;
}

int FUNC(MAP_TLV_OPERATING_CHANNEL_REPORT)(const uint8_t *data, size_t datalen)
{
	size_t remaining_len;
	uint8_t num_opclass;
	size_t expected_len;
	const uint8_t *ptr;
	uint8_t i;

	ptr = data;
	remaining_len = datalen;

	/* Validate radio field */
	if (remaining_len < 7)
		return -1;

	ptr += 6;
	remaining_len -= 6;

	num_opclass = *ptr;
	ptr += 1;
	remaining_len -= 1;

	/* Validate each operating class */
	expected_len = 2;
	for (i = 0; i < num_opclass; i++) {
		if (remaining_len < expected_len)
			return -1;

		ptr += expected_len;
		remaining_len -= expected_len;
	}

	if (remaining_len != 1)
		return -1; /* Not enough data for curr_txpower */

	return 0;
}

int FUNC(MAP_TLV_CLIENT_INFO)(const uint8_t *data, size_t datalen)
{
	return datalen != 12 ? -1 : 0;
}

int FUNC(MAP_TLV_CLIENT_CAPABILITY_REPORT)(const uint8_t *data, size_t datalen)
{
	if (datalen < 1)
		return -1;

	return 0;
}

int FUNC(MAP_TLV_CLIENT_ASSOCIATION_EVENT)(const uint8_t *data, size_t datalen)
{
	return datalen != 13 ? -1 : 0;
}

int FUNC(MAP_TLV_AP_METRIC_QUERY)(const uint8_t *data, size_t datalen)
{
	size_t remaining_len;
	const uint8_t *ptr;
	uint8_t num_bss;

	ptr = data;
	remaining_len = datalen;

	/* Validate num_bss field */
	if (remaining_len < 1)
		return -1;

	num_bss = *ptr;
	remaining_len -= 1;

	return remaining_len != num_bss * 6 ? -1 : 0;
}

int FUNC(MAP_TLV_AP_METRICS)(const uint8_t *data, size_t datalen)
{
	size_t expected_len = 0;
	size_t remaining_len;
	const uint8_t *ptr;
	uint8_t esp_ac;

	ptr = data;
	remaining_len = datalen;

	/* 6 bytes for bssid + 1 byte for channel_utilization +
	 * 2 bytes for num_station + 1 byte for esp_ac + 3 bytes for esp_be
	 */
	if (remaining_len < 13)
		return -1;

	remaining_len -= 13;
	esp_ac = ptr[9];

	if (!(esp_ac & ESP_AC_BE))
		return -1;

	if (!!(esp_ac & ESP_AC_BK))
		expected_len += 3;

	if (!!(esp_ac & ESP_AC_VO))
		expected_len += 3;

	if (!!(esp_ac & ESP_AC_VI))
		expected_len += 3;

	return remaining_len != expected_len ? -1 : 0;
}

int FUNC(MAP_TLV_STA_MAC_ADDRESS)(const uint8_t *data, size_t datalen)
{
	return datalen != 6 ? -1 : 0;
}

int FUNC(MAP_TLV_ASSOCIATED_STA_LINK_METRICS)(const uint8_t *data,
					      size_t datalen)
{
	size_t expected_len_per_bss;
	size_t total_bss_len;
	size_t remaining_len;
	const uint8_t *ptr;
	uint8_t num_bss;

	ptr = data;
	remaining_len = datalen;

	/* Validate macaddr and num_bss fields */
	if (remaining_len < 7)
		return -1;

	ptr += 7;
	remaining_len -= 7;
	num_bss = data[6];

	/* Validate each BSS */
	expected_len_per_bss = 6 + 4 + 4 + 4 + 1;
	total_bss_len = num_bss * expected_len_per_bss;

	return remaining_len != total_bss_len ? -1 : 0;
}

int FUNC(MAP_TLV_UNASSOCIATED_STA_LINK_METRICS_QUERY)(const uint8_t *data,
						      size_t datalen)
{
	size_t expected_len_per_channel;
	size_t total_channel_len;
	size_t remaining_len;
	uint8_t num_channel;
	const uint8_t *ptr;
	uint8_t i;

	ptr = data;
	remaining_len = datalen;

	/* 1 byte for opclass + 1 byte for num_channel */
	if (remaining_len < 2)
		return -1;

	remaining_len -= 2;
	num_channel = ptr[1];
	ptr += 2; /* Skip opclass and num_channel */
	expected_len_per_channel =
		2; /* 1 byte for channel + 1 byte for num_sta */

	for (i = 0; i < num_channel; i++) {
		size_t expected_len_per_sta = 6;
		uint8_t num_sta;

		if (remaining_len < expected_len_per_channel)
			return -1;

		num_sta = ptr[1];
		total_channel_len = expected_len_per_channel +
				    (num_sta * expected_len_per_sta);
		if (remaining_len < total_channel_len)
			return -1;

		ptr += total_channel_len;
		remaining_len -= total_channel_len;
	}

	return remaining_len != 0 ? -1 : 0;
}

int FUNC(MAP_TLV_UNASSOCIATED_STA_LINK_METRICS_RESPONSE)(const uint8_t *data,
							 size_t datalen)
{
	size_t expected_len_per_sta;
	size_t total_sta_len;
	size_t remaining_len;
	const uint8_t *ptr;
	uint8_t num_sta;

	ptr = data;
	remaining_len = datalen;

	/* 1 byte for opclass + 1 byte for num_sta */
	if (remaining_len < 2)
		return -1;

	remaining_len -= 2;
	num_sta = ptr[1];
	ptr += 2;

	/* Validate each station entry */
	expected_len_per_sta = 6 + 1 + 4 + 1;
	total_sta_len = num_sta * expected_len_per_sta;
	if (remaining_len < total_sta_len)
		return -1;

	remaining_len -= total_sta_len;

	return remaining_len != 0 ? -1 : 0;
}

int FUNC(MAP_TLV_BEACON_METRICS_RESPONSE)(const uint8_t *data, size_t datalen)
{
	size_t remaining_len;
	uint8_t num_element;
	const uint8_t *ptr;

	ptr = data;
	remaining_len = datalen;

	/* 6 bytes for sta_macaddr + 1 byte for reserved + 1 byte for num_element */
	if (remaining_len < 8)
		return -1;

	num_element = ptr[7];
	remaining_len -= 8;

	return remaining_len != num_element ? -1 : 0;
}

int FUNC(MAP_TLV_BEACON_METRICS_QUERY)(const uint8_t *data, size_t datalen)
{
	const uint8_t *ptr;
	size_t remaining_len;
	uint8_t ssidlen;
	uint8_t num_report;
	uint8_t num_element;
	size_t i;
	size_t total_report_len;
	size_t expected_len_per_report;

	ptr = data;
	remaining_len = datalen;

	/* Validate sta_macaddr, opclass, channel, bssid, reporting_detail,
	 * ssidlen, num_report and num_element fields.
	 */
	if (remaining_len < 18)
		return -1;

	ptr += 15; /* skip reporting_detail */
	remaining_len -= 15;

	ssidlen = ptr[0];
	ptr += 1; /* Skip ssidlen */
	remaining_len -= 1;

	if (remaining_len < ssidlen)
		return -1;

	ptr += ssidlen; /* Skip ssid */
	remaining_len -= ssidlen;

	num_report = ptr[0];
	ptr += 1;
	remaining_len -= 1;

	/* Validate each ap_channel_report */
	expected_len_per_report = 2; /* 1 byte for len + 1 byte for opclass */
	total_report_len = 0;
	for (i = 0; i < num_report; i++) {
		uint8_t len;

		if (remaining_len < expected_len_per_report)
			return -1;

		len = ptr[0];
		if (remaining_len < expected_len_per_report + len)
			return -1;

		total_report_len += expected_len_per_report + len;
		ptr += expected_len_per_report + len;
		remaining_len -= expected_len_per_report + len;
	}

	/* Validate num_element field */
	if (remaining_len < 1)
		return -1;

	num_element = ptr[0];
	ptr += 1;
	remaining_len -= 1;

	return remaining_len != num_element ? -1 : 0;
}

int FUNC(MAP_TLV_STEERING_REQUEST)(const uint8_t *data, size_t datalen)
{
	size_t expected_len_target_bss;
	size_t expected_len_sta;
	uint8_t num_target_bss;
	size_t remaining_len;
	const uint8_t *ptr;
	uint8_t num_sta;

	ptr = data;
	remaining_len = datalen;

	/* Validate bssid, mode, op_window, and btm_disassoc_timer fields */
	if (remaining_len < 11)
		return -1;

	ptr += 11;
	remaining_len -= 11;

	/* Validate steer_sta_request.num and sta array */
	if (remaining_len < 1) {
		/* At least 1 byte needed for num_sta */
		return -1;
	}

	num_sta = ptr[0];
	ptr += 1;
	remaining_len -= 1;

	expected_len_sta = num_sta * 6;
	if (remaining_len < expected_len_sta)
		return -1;

	ptr += expected_len_sta;
	remaining_len -= expected_len_sta;

	/* Validate steer_target_bss.num and bss array */
	if (remaining_len < 1) {
		/* At least 1 byte needed for num_target_bss */
		return -1;
	}

	num_target_bss = ptr[0];
	ptr += 1;
	remaining_len -= 1;

	expected_len_target_bss = num_target_bss * (6 + 1 + 1);
	return remaining_len != expected_len_target_bss ? -1 : 0;
}

int FUNC(MAP_TLV_STEERING_BTM_REPORT)(const uint8_t *data, size_t datalen)
{
	size_t min_length;

	min_length = sizeof(struct tlv_steer_btm_report);

	/* target_bssid is either present or not */
	return (datalen != min_length || datalen != min_length + 6) ? -1 : 0;
}

int FUNC(MAP_TLV_CLIENT_ASSOCIATION_CONTROL_REQUEST)(const uint8_t *data,
						     size_t datalen)
{
	size_t expected_length;
	size_t min_length;

	min_length = sizeof(struct tlv_client_assoc_ctrl_request);

	if (datalen < min_length)
		return -1;

	expected_length = min_length + data[9] * 6;

	return datalen != expected_length ? -1 : 0;
}

int FUNC(MAP_TLV_BACKHAUL_STEERING_REQUEST)(const uint8_t *data, size_t datalen)
{
	return datalen != sizeof(struct tlv_backhaul_steer_request) ? -1 : 0;
}

int FUNC(MAP_TLV_BACKHAUL_STEERING_RESPONSE)(const uint8_t *data,
					     size_t datalen)
{
	return datalen != sizeof(struct tlv_backhaul_steer_resp) ? -1 : 0;
}

int FUNC(MAP_TLV_HIGHER_LAYER_DATA)(const uint8_t *data, size_t datalen)
{
	return datalen < sizeof(struct tlv_higher_layer_data) ? -1 : 0;
}

int FUNC(MAP_TLV_ASSOCIATED_STA_TRAFFIC_STATS)(const uint8_t *data,
					       size_t datalen)
{
	return datalen != sizeof(struct tlv_assoc_sta_traffic_stats) ? -1 : 0;
}

int FUNC(MAP_TLV_ERROR_CODE)(const uint8_t *data, size_t datalen)
{
	return datalen != sizeof(struct tlv_error_code) ? -1 : 0;
}

int FUNC(MAP_TLV_CHANNEL_SCAN_REPORTING_POLICY)(const uint8_t *data,
						size_t datalen)
{
	return datalen != sizeof(struct tlv_channel_scan_report_policy) ? -1 :
										0;
}

int FUNC(MAP_TLV_CHANNEL_SCAN_CAPABILITY)(const uint8_t *data, size_t datalen)
{
	uint8_t num_radio;
	size_t offset;
	int i, j;

	if (datalen < sizeof(struct tlv_channel_scan_capability))
		return -1;

	num_radio = data[0];
	offset = 1;

	for (i = 0; i < num_radio; i++) {
		uint8_t num_opclass;

		/* Each radio: 6 + 1 + 4 + 1 */
		if (datalen < offset + 12)
			return -1;

		offset += 12;
		num_opclass = data[offset - 1];

		for (j = 0; j < num_opclass; j++) {
			uint8_t num_channel;

			/* Each opclass: 1 + 1 + num_channel */
			if (datalen < offset + 2)
				return -1;

			num_channel = data[offset + 1];
			offset += 2 + num_channel;
		}
	}

	return datalen != offset ? -1 : 0;
}

int FUNC(MAP_TLV_CHANNEL_SCAN_REQ)(const uint8_t *data, size_t datalen)
{
	uint8_t num_radio;
	size_t offset;
	int i, j;

	if (datalen < sizeof(struct tlv_channel_scan_request))
		return -1;

	num_radio = data[1];
	offset = 2;

	for (i = 0; i < num_radio; i++) {
		uint8_t num_opclass;

		/* Each radio: 6 + 1 */
		if (datalen < offset + 7)
			return -1;

		offset += 6;
		num_opclass = data[offset];
		offset++;

		for (j = 0; j < num_opclass; j++) {
			uint8_t num_channel;

			/* Each opclass: 1 + 1 + num_channel */
			if (datalen < offset + 2)
				return -1;

			num_channel = data[offset + 1];
			offset += 2 + num_channel;
		}
	}

	return datalen != offset ? -1 : 0;
}

int FUNC(MAP_TLV_CHANNEL_SCAN_RES)(const uint8_t *data, size_t datalen)
{
	size_t consumed_length;
	uint8_t status;
	int i;

	if (datalen < sizeof(struct tlv_channel_scan_result))
		return -1;

	consumed_length = sizeof(struct tlv_channel_scan_result);
	status = data[8];

	if (status == CH_SCAN_STATUS_SUCCESS) {
		while (consumed_length < datalen) {
			uint16_t num_neighbor;

			/* scan_result_timestamp */
			if (consumed_length + 1 > datalen)
				return -1;

			consumed_length += 1 + data[consumed_length];

			/* utilization and noise */
			if (consumed_length + 2 > datalen)
				return -1;

			consumed_length += 2;

			/* validate num_neighbor */
			if (consumed_length + 2 > datalen)
				return -1;

			num_neighbor = BUF_GET_BE16(data[consumed_length]);
			consumed_length += 2;

			/* Validate each neighbor */
			for (i = 0; i < num_neighbor; i++) {
				uint8_t bssload_info = 0;

				/* BSSID */
				if (consumed_length + 6 > datalen)
					return -1;

				consumed_length += 6;

				/* SSID (len + ssid[]) */
				if (consumed_length + 1 > datalen)
					return -1;

				consumed_length += 1 + data[consumed_length];

				/* RCPI */
				if (consumed_length + 1 > datalen)
					return -1;

				consumed_length += 1;

				/* Bandwidth (len + bwstr[]) */
				if (consumed_length + 1 > datalen)
					return -1;

				consumed_length += 1 + data[consumed_length];

				/* Validate the BSS Load if 'info' indicates it is present */
				bssload_info = data[consumed_length];
				consumed_length += 1;

				if (bssload_info &
				    CH_SCAN_RESULT_BSSLOAD_PRESENT) {
					if (consumed_length + 3 > datalen)
						return -1;

					consumed_length +=
						3; /* ch_util + sta_count */
				}
			}

			/* total_scan_duration */
			if (consumed_length + 4 > datalen)
				return -1;

			consumed_length += 4;

			/* scan type */
			if (consumed_length + 1 > datalen)
				return -1;

			consumed_length += 1;
		}
	}

	return consumed_length != datalen ? -1 : 0;
}

int FUNC(MAP_TLV_TIMESTAMP)(const uint8_t *data, size_t datalen)
{
	if (datalen < sizeof(struct tlv_timestamp))
		return -1;

	return datalen != 1 + data[0] ? -1 : 0;
}

int FUNC(MAP_TLV_CAC_REQ)(const uint8_t *data, size_t datalen)
{
	size_t expected_length;
	uint8_t num_radio;

	if (datalen < sizeof(struct tlv_cac_request))
		return -1;

	num_radio = data[0];

	/* Each radio block is 6 + 1 + 1 + 1 bytes */
	expected_length = sizeof(struct tlv_cac_request) + num_radio * 9;

	return datalen != expected_length ? -1 : 0;
}

int FUNC(MAP_TLV_CAC_TERMINATION)(const uint8_t *data, size_t datalen)
{
	size_t expected_length;
	uint8_t num_radio;

	if (datalen < sizeof(struct tlv_cac_termination))
		return -1;

	num_radio = data[0];

	/* Each radio block is 6 (radio) + 1 (opclass) + 1 (channel) bytes */
	expected_length = sizeof(struct tlv_cac_termination) + num_radio * 8;

	return datalen != expected_length ? -1 : 0;
}

int FUNC(MAP_TLV_CAC_COMPLETION_REPORT)(const uint8_t *data, size_t datalen)
{
	size_t consumed_length;
	uint8_t num_radio;
	int i, j;

	if (datalen < sizeof(struct tlv_cac_complete_report))
		return -1;

	num_radio = data[0];
	consumed_length = 1;

	for (i = 0; i < num_radio; i++) {
		uint8_t num_pairs;

		if (datalen < consumed_length + 10)
			return -1;

		consumed_length += 9;
		num_pairs = data[consumed_length];
		consumed_length += 1;

		/* Validate each pair */
		for (j = 0; j < num_pairs; j++) {
			if (datalen < consumed_length + 2)
				return -1;

			consumed_length += 2;
		}
	}

	return datalen != consumed_length ? -1 : 0;
}

#if (EASYMESH_VERSION >= 3)
int FUNC(MAP_TLV_1905_SECURITY_CAPS)(const uint8_t *data, size_t datalen)
{
	return datalen != sizeof(struct tlv_1905_security_cap) ? -1 : 0;
}

int FUNC(MAP_TLV_AP_WIFI6_CAPS)(const uint8_t *data, size_t datalen)
{
	uint8_t num_roles;
	size_t offset;
	int i;

	if (datalen < sizeof(struct tlv_ap_wifi6_caps))
		return -1;

	offset = sizeof(struct tlv_ap_wifi6_caps);
	num_roles = data[6];

	for (i = 0; i < num_roles; i++) {
		size_t mcs_nss_length;

		if (datalen < offset + 1)
			return -1;

		mcs_nss_length = data[offset] & MCS_NSS_LEN_MASK;
		if (mcs_nss_length != 4 && mcs_nss_length != 8 &&
		    mcs_nss_length != 12)
			return -1;

		offset += 1 + mcs_nss_length;

		if (datalen <
		    offset + sizeof(struct wifi6_agent_role_other_caps))
			return -1;

		offset += sizeof(struct wifi6_agent_role_other_caps);
	}

	return datalen != offset ? -1 : 0;
}

int FUNC(MAP_TLV_MIC)(const uint8_t *data, size_t datalen)
{
	uint16_t mic_len = BUF_GET_BE16(data[13]);

	return datalen != sizeof(struct tlv_mic) + mic_len ? -1 : 0;
}

int FUNC(MAP_TLV_ENCRYPTED_PAYLOAD)(const uint8_t *data, size_t datalen)
{
	uint16_t enc_len = BUF_GET_BE16(data[18]);

	return datalen != sizeof(struct tlv_enc_payload) + enc_len ? -1 : 0;
}

int FUNC(MAP_TLV_1905_ENCAP_DPP)(const uint8_t *data, size_t datalen)
{
	uint16_t framelen;
	size_t offset;

	if (datalen < sizeof(struct tlv_1905_encap_dpp))
		return -1;

	offset = 1;
	if (!!(data[0] & ENCAP_DPP_ENROLLEE_MAC_PRESENT))
		offset += 6;

	if (offset + sizeof(struct encap_dpp_frame) > datalen)
		return -1;

	offset += 1;
	framelen = BUF_GET_BE16(data[offset]);
	offset += 2;

	return offset + framelen != datalen ? -1 : 0;
}

int FUNC(MAP_TLV_1905_ENCAP_EAPOL)(const uint8_t *data, size_t datalen)
{
	return datalen == 0 ? -1 : 0;
}
#endif

int FUNC(MAP_TLV_BACKHAUL_BSS_CONFIG)(const uint8_t *data, size_t datalen)
{
	return datalen == sizeof(struct tlv_bbss_config) ? -1 : 0;
}

#if (EASYMESH_VERSION >= 3)
int FUNC(MAP_TLV_DPP_BOOTSTRAP_URI_NOTIFICATION)(const uint8_t *data,
						 size_t datalen)
{
	return datalen < sizeof(struct tlv_dpp_uri_bootstrap) ? -1 : 0;
}

int FUNC(MAP_TLV_DPP_MESSAGE)(const uint8_t *data, size_t datalen)
{
	return datalen < sizeof(struct tlv_dpp_message) ? -1 : 0;
}

int FUNC(MAP_TLV_DPP_CCE_INDICATION)(const uint8_t *data, size_t datalen)
{
	return datalen != sizeof(struct tlv_dpp_cce) ? -1 : 0;
}

int FUNC(MAP_TLV_DPP_CHIRP_VALUE)(const uint8_t *data, size_t datalen)
{
	size_t expected_length;
	uint8_t hashlen;

	expected_length = sizeof(struct tlv_dpp_chirp);
	if (!!(data[0] & DPP_CHIRP_ENROLLEE_MAC_PRESENT)) {
		expected_length += 6;
		hashlen = data[7];
	} else {
		hashlen = data[1];
	}

	return datalen != expected_length + hashlen ? -1 : 0;
}

int FUNC(MAP_TLV_ASSOCIATED_WIFI6_STA_STATUS)(const uint8_t *data,
					      size_t datalen)
{
	size_t expected_length;
	uint8_t num_queue;

	expected_length = sizeof(struct tlv_assoc_wifi6_sta_status_report);
	if (datalen < expected_length)
		return -1;

	num_queue = data[6];
	expected_length += num_queue * 2;

	return datalen != expected_length ? -1 : 0;
}
#endif

int FUNC(MAP_TLV_CAC_STATUS_REPORT)(const uint8_t *data, size_t datalen)
{
	size_t expected_length;
	size_t offset = 0;
	uint8_t num_channel;

	if (datalen < sizeof(struct tlv_cac_status_report))
		return -1;

	/* Validate the available block */
	num_channel = data[offset];
	expected_length = 1 + num_channel * (1 + 1 + 2);
	if (offset + expected_length > datalen)
		return -1;

	offset += expected_length;

	/* Validate the noop block */
	num_channel = data[offset];
	expected_length = 1 + num_channel * (1 + 1 + 2);
	if (offset + expected_length > datalen)
		return -1;

	offset += expected_length;

	/* Validate the cac block */
	num_channel = data[offset];
	expected_length = 1 + num_channel * (1 + 1 + 2);
	if (offset + expected_length > datalen)
		return -1;

	offset += expected_length;

	return offset != datalen ? -1 : 0;
}

int FUNC(MAP_TLV_CAC_CAPABILITY)(const uint8_t *data, size_t datalen)
{
#define MAX_RADIOS 4
	uint8_t num_radio;
	uint16_t i, j, k;
	size_t offset = 0;

	if (datalen < sizeof(struct tlv_cac_cap))
		return -1;

	offset += 2;
	num_radio = data[offset];
	if (num_radio > MAX_RADIOS)
		return -1;

	offset += 1;
	/* Loop through each radio */
	for (i = 0; i < num_radio; i++) {
		uint8_t num_cac;

		if (offset + 6 + 1 > datalen)
			return -1;

		offset += 6;
		num_cac = data[offset];
		offset += 1;

		/* Loop through each CAC in the radio */
		for (j = 0; j < num_cac; j++) {
			uint8_t num_opclass;

			if (offset + 5 > datalen)
				return -1;

			offset += 4;
			num_opclass = data[offset];
			offset += 1;

			/* Loop through each opclass in the CAC */
			for (k = 0; k < num_opclass; k++) {
				uint8_t num_channel;

				if (offset + 2 > datalen)
					return -1;

				offset += 1;
				num_channel = data[offset];
				offset += 1;

				if (offset + num_channel > datalen)
					return -1;

				offset += num_channel;
			}
		}
	}

	return 0;
}

int FUNC(MAP_TLV_MULTIAP_PROFILE)(const uint8_t *data, size_t datalen)
{
	return datalen != sizeof(struct tlv_map_profile) ? -1 : 0;
}

int FUNC(MAP_TLV_PROFILE2_AP_CAPABILITY)(const uint8_t *data, size_t datalen)
{
	return datalen != sizeof(struct tlv_profile2_ap_cap) ? -1 : 0;
}

int FUNC(MAP_TLV_DEFAULT_8021Q_SETTINGS)(const uint8_t *data, size_t datalen)
{
	return datalen != sizeof(struct tlv_default_8021q_settings) ? -1 : 0;
}

int FUNC(MAP_TLV_TRAFFIC_SEPARATION_POLICY)(const uint8_t *data, size_t datalen)
{
#define MAX_SSID_LEN 32
	struct tlv_traffic_sep_policy *tlv;
	uint16_t offset = 1;
	uint16_t i;

	if (datalen < sizeof(struct tlv_traffic_sep_policy))
		return -1;

	tlv = (struct tlv_traffic_sep_policy *)data;

	for (i = 0; i < tlv->num_ssid; i++) {
		struct ssid_info *ssid_info;
		size_t expected_length;

		if (offset + sizeof(struct ssid_info) > datalen)
			return -1;

		ssid_info = (struct ssid_info *)(data + offset);
		if (ssid_info->len > MAX_SSID_LEN)
			return -1;

		/* Validate total length of SSID info */
		expected_length = sizeof(struct ssid_info) + ssid_info->len;
		if (offset + expected_length > datalen)
			return -1;

		offset += expected_length + sizeof(uint16_t);
	}

	return datalen != offset ? -1 : 0;
}

#if (EASYMESH_VERSION >= 3)
int FUNC(MAP_TLV_BSSID)(const uint8_t *data, size_t datalen)
{
	return datalen != sizeof(struct tlv_bssid) ? -1 : 0;
}

int FUNC(MAP_TLV_DEVICE_INVENTORY)(const uint8_t *data, size_t datalen)
{
	uint8_t lsn, lsv, lee, lcv;
	uint8_t num_radio;
	size_t offset = 0;
	int i;

	if (datalen < sizeof(struct tlv_device_inventory))
		return -1;

	lsn = data[0];
	offset += 1 + lsn;
	if (offset > datalen)
		return -1;

	lsv = data[offset];
	offset += 1 + lsv;
	if (offset > datalen)
		return -1;

	lee = data[offset];
	offset += 1 + lee;
	if (offset > datalen)
		return -1;

	num_radio = data[offset];
	offset += 1;

	for (i = 0; i < num_radio; i++) {
		if (offset + 6 + 1 > datalen)
			return -1;

		offset += 6;
		lcv = data[offset];
		offset += 1 + lcv;
		if (offset > datalen)
			return -1;
	}

	return datalen != offset ? -1 : 0;
}

int FUNC(MAP_TLV_AGENT_LIST)(const uint8_t *data, size_t datalen)
{
	uint8_t num_agent;

	if (datalen < sizeof(struct tlv_agent_list))
		return -1;

	num_agent = data[0];

	return datalen != 1 + num_agent * sizeof(struct agent_list_info) ? -1 :
										 0;
}
#endif

#if (EASYMESH_VERSION >= 4)
int FUNC(MAP_TLV_ANTICIPATED_CHANNEL_PREF)(const uint8_t *data, size_t datalen)
{
	/* TODO */
	return 0;
}

int FUNC(MAP_TLV_ANTICIPATED_CHANNEL_USAGE)(const uint8_t *data, size_t datalen)
{
	/* TODO */
	return 0;
}

int FUNC(MAP_TLV_SPATIAL_REUSE_REQUEST)(const uint8_t *data, size_t datalen)
{
	/* TODO */
	return 0;
}

int FUNC(MAP_TLV_SPATIAL_REUSE_REPORT)(const uint8_t *data, size_t datalen)
{
	/* TODO */
	return 0;
}

int FUNC(MAP_TLV_SPATIAL_REUSE_CONFIG_RESPONSE)(const uint8_t *data, size_t datalen)
{
	/* TODO */
	return 0;
}


int FUNC(MAP_TLV_CONTROLLER_CAPS)(const uint8_t *data, size_t datalen)
{
	return datalen < sizeof(struct tlv_controller_cap) ? -1 : 0;
}
#endif

int FUNC(MAP_TLV_STATUS_CODE)(const uint8_t *data, size_t datalen)
{
	return datalen != sizeof(struct tlv_status_code) ? -1 : 0;
}

int FUNC(MAP_TLV_REASON_CODE)(const uint8_t *data, size_t datalen)
{
	return datalen != sizeof(struct tlv_reason_code) ? -1 : 0;
}

int FUNC(MAP_TLV_BACKHAUL_STA_RADIO_CAPABILITY)(const uint8_t *data,
						size_t datalen)
{
	size_t expected_length;

	expected_length = sizeof(struct tlv_bsta_radio_cap);
	if (datalen < expected_length)
		return -1;

	if (!!(data[6] & BSTA_MACADDRESS_INCLUDED))
		expected_length += 6;

	return datalen != expected_length ? -1 : 0;
}

#if (EASYMESH_VERSION >= 3)
int FUNC(MAP_TLV_AKM_SUITE_CAPS)(const uint8_t *data, size_t datalen)
{
	uint8_t num_fbss, num_bbss;
	size_t offset = 0;

	if (datalen < sizeof(struct tlv_akm_suite_caps))
		return -1;

	num_bbss = data[offset];
	offset += 1;

	if (datalen < offset + num_bbss * sizeof(struct akm_suite))
		return -1;

	offset += num_bbss * sizeof(struct akm_suite);
	if (offset + 1 > datalen)
		return -1;

	num_fbss = data[offset];
	offset += 1;

	return datalen != offset + num_fbss * sizeof(struct akm_suite) ? -1 : 0;
}

int FUNC(MAP_TLV_SERVICE_PRIORITIZATION_RULE)(const uint8_t *data,
					      size_t datalen)
{
	return datalen != sizeof(struct tlv_spr) ? -1 : 0;
}

int FUNC(MAP_TLV_DSCP_MAPPING_TABLE)(const uint8_t *data, size_t datalen)
{
	return datalen != sizeof(struct tlv_dscp_pcp) ? -1 : 0;
}
#endif

#if (EASYMESH_VERSION >= 4)
int FUNC(MAP_TLV_QOS_MANAGEMENT_POLICY)(const uint8_t *data, size_t datalen)
{
	return datalen < sizeof(struct tlv_qos_mgmt_desc) ? -1 : 0;
}

int FUNC(MAP_TLV_QOS_MANAGEMENT_DESCRIPTOR)(const uint8_t *data, size_t datalen)
{
	return datalen < sizeof(struct tlv_qos_mgmt_desc) ? -1 : 0;
}
#endif

int FUNC(MAP_TLV_PROFILE2_ERR_CODE)(const uint8_t *data, size_t datalen)
{
	return datalen < sizeof(struct tlv_profile2_error_code) ? -1 : 0;
}

#if (EASYMESH_VERSION >= 3)
int FUNC(MAP_TLV_BSS_CONFIGURATION_REQUEST)(const uint8_t *data, size_t datalen)
{
	json_object *jsobj;
	json_tokener *tok;
	int res = 0;

	if (datalen < sizeof(struct tlv_bss_configuration))
		return -1;

	/* contains one or more JSON encoded DPP config objects */
	tok = json_tokener_new();
	if (!tok)
		return -1;

	jsobj = json_tokener_parse_ex(tok, (const char *)data, datalen);
	if (!jsobj || !json_object_is_type(jsobj, json_type_object))
		res = -1;

	json_tokener_free(tok);
	return res;
}

int FUNC(MAP_TLV_BSS_CONFIGURATION_RESPONSE)(const uint8_t *data,
					     size_t datalen)
{
	json_object *jsobj;
	json_tokener *tok;
	int res = 0;

	if (datalen < sizeof(struct tlv_bss_configuration))
		return -1;

	/* contains one or more JSON encoded DPP config objects */
	tok = json_tokener_new();
	if (!tok)
		return -1;

	jsobj = json_tokener_parse_ex(tok, (const char *)data, datalen);
	if (!jsobj || !json_object_is_type(jsobj, json_type_object))
		res = -1;

	json_tokener_free(tok);
	return res;
}
#endif

int FUNC(MAP_TLV_AP_RADIO_ADV_CAPABILITY)(const uint8_t *data, size_t datalen)
{
	return datalen != sizeof(struct tlv_ap_radio_adv_cap) ? -1 : 0;
}

int FUNC(MAP_TLV_ASSOCIATION_STATUS_NOTIF)(const uint8_t *data, size_t datalen)
{
	uint8_t num_bss;

	if (datalen < sizeof(struct tlv_assoc_status_notif))
		return -1;

	num_bss = data[0];
	return datalen != num_bss * (6 + 1) ? -1 : 0;
}

int FUNC(MAP_TLV_SOURCE_INFO)(const uint8_t *data, size_t datalen)
{
	return datalen != sizeof(struct tlv_source_info) ? -1 : 0;
}

int FUNC(MAP_TLV_TUNNELED_MSG_TYPE)(const uint8_t *data, size_t datalen)
{
	return datalen != sizeof(struct tlv_tunnel_msg_type) ? -1 : 0;
}

int FUNC(MAP_TLV_TUNNELED)(const uint8_t *data, size_t datalen)
{
	return 0; /* NOP */
}

int FUNC(MAP_TLV_PROFILE2_STEERING_REQ)(const uint8_t *data, size_t datalen)
{
	uint8_t num_target;
	uint8_t num_sta;
	size_t pos = 0;

	if (datalen < sizeof(struct tlv_profile2_steer_request))
		return -1;

	pos += 11;

	num_sta = data[pos];
	pos += 1;

	if (pos + num_sta * 6 > datalen)
		return -1;

	pos += num_sta * 6;
	if (pos + 1 > datalen)
		return -1;

	num_target = data[pos];

	return datalen != pos + num_target * (6 + 1 + 1 + 1) ? -1 : 0;
}

int FUNC(MAP_TLV_UNSUCCESS_ASSOCIATION_POLICY)(const uint8_t *data,
					       size_t datalen)
{
	return datalen != sizeof(struct tlv_unsuccess_assoc_policy) ? -1 : 0;
}

int FUNC(MAP_TLV_METRIC_COLLECTION_INTERVAL)(const uint8_t *data,
					     size_t datalen)
{
	return datalen != sizeof(struct tlv_metric_collection_int) ? -1 : 0;
}

int FUNC(MAP_TLV_RADIO_METRICS)(const uint8_t *data, size_t datalen)
{
	return datalen != sizeof(struct tlv_radio_metrics) ? -1 : 0;
}

int FUNC(MAP_TLV_AP_EXTENDED_METRICS)(const uint8_t *data, size_t datalen)
{
	return datalen != sizeof(struct tlv_ap_ext_metrics) ? -1 : 0;
}

#if (EASYMESH_VERSION >= 6)
int FUNC(MAP_TLV_WIFI7_AGENT_CAPABILITIES)(const uint8_t *data, size_t datalen)
{
	/* TODO */
	return 0;
}

int FUNC(MAP_TLV_AP_MLD_CONFIG)(const uint8_t *data, size_t datalen)
{
	/* TODO */
	return 0;
}

int FUNC(MAP_TLV_BACKHAUL_STA_MLD_CONFIG)(const uint8_t *data, size_t datalen)
{
	/* TODO */
	return 0;
}

int FUNC(MAP_TLV_STA_MLD_CONFIG)(const uint8_t *data, size_t datalen)
{
	/* TODO */
	return 0;
}

int FUNC(MAP_TLV_MLD_STRUCTURE)(const uint8_t *data, size_t datalen)
{
	/* TODO */
	return 0;
}

int FUNC(MAP_TLV_AFFILIATED_AP_METRICS)(const uint8_t *data, size_t datalen)
{
	/* TODO */
	return 0;
}

int FUNC(MAP_TLV_AFFILIATED_STA_METRICS)(const uint8_t *data, size_t datalen)
{
	/* TODO */
	return 0;
}

int FUNC(MAP_TLV_TID_TO_LINK_MAPPING_POLICY)(const uint8_t *data,
					     size_t datalen)
{
	/* TODO */
	return 0;
}

int FUNC(MAP_TLV_EHT_OPERATIONS)(const uint8_t *data, size_t datalen)
{
	/* TODO */
	return 0;
}

int FUNC(MAP_TLV_BSS_ADVANCED_CONFIG)(const uint8_t *data, size_t datalen)
{
	/* TODO */
	return 0;
}

int FUNC(MAP_TLV_RSN_PARAMETERS_CONFIG)(const uint8_t *data, size_t datalen)
{
	/* TODO */
	return 0;
}

int FUNC(MAP_TLV_SUPPORTED_CIPHER_SUITES)(const uint8_t *data, size_t datalen)
{
	uint8_t num;

	if (datalen < 1)
		return -1;

	num = data[0];
	return datalen != 1 + (size_t)num * 4 ? -1 : 0;
}
#endif

/* List of TLV validators */
struct tlv_validator tlv_validators[] = {
	DEFINE_VALIDATOR(TLV_TYPE_WSC),
	DEFINE_VALIDATOR(TLV_TYPE_VENDOR_SPECIFIC),
	DEFINE_VALIDATOR(MAP_TLV_SUPPORTED_SERVICE),
	DEFINE_VALIDATOR(MAP_TLV_SEARCHED_SERVICE),
	DEFINE_VALIDATOR(MAP_TLV_AP_RADIO_IDENTIFIER),
	DEFINE_VALIDATOR(MAP_TLV_AP_OPERATIONAL_BSS),
	DEFINE_VALIDATOR(MAP_TLV_ASSOCIATED_CLIENTS),
	DEFINE_VALIDATOR(MAP_TLV_AP_CAPABILITY),
	DEFINE_VALIDATOR(MAP_TLV_AP_RADIO_BASIC_CAPABILITIES),
	DEFINE_VALIDATOR(MAP_TLV_AP_HT_CAPABILITIES),
	DEFINE_VALIDATOR(MAP_TLV_AP_VHT_CAPABILITIES),
	DEFINE_VALIDATOR(MAP_TLV_AP_HE_CAPABILITIES),
	DEFINE_VALIDATOR(MAP_TLV_STEERING_POLICY),
	DEFINE_VALIDATOR(MAP_TLV_METRIC_REPORTING_POLICY),
	DEFINE_VALIDATOR(MAP_TLV_CHANNEL_PREFERENCE),
	DEFINE_VALIDATOR(MAP_TLV_RADIO_OPERATION_RESTRICTION),
	DEFINE_VALIDATOR(MAP_TLV_TRANSMIT_POWER_LIMIT),
	DEFINE_VALIDATOR(MAP_TLV_CHANNEL_SELECTION_RESPONSE),
	DEFINE_VALIDATOR(MAP_TLV_OPERATING_CHANNEL_REPORT),
	DEFINE_VALIDATOR(MAP_TLV_CLIENT_INFO),
	DEFINE_VALIDATOR(MAP_TLV_CLIENT_CAPABILITY_REPORT),
	DEFINE_VALIDATOR(MAP_TLV_CLIENT_ASSOCIATION_EVENT),
	DEFINE_VALIDATOR(MAP_TLV_AP_METRIC_QUERY),
	DEFINE_VALIDATOR(MAP_TLV_AP_METRICS),
	DEFINE_VALIDATOR(MAP_TLV_STA_MAC_ADDRESS),
	DEFINE_VALIDATOR(MAP_TLV_ASSOCIATED_STA_LINK_METRICS),
	DEFINE_VALIDATOR(MAP_TLV_UNASSOCIATED_STA_LINK_METRICS_QUERY),
	DEFINE_VALIDATOR(MAP_TLV_UNASSOCIATED_STA_LINK_METRICS_RESPONSE),
	DEFINE_VALIDATOR(MAP_TLV_BEACON_METRICS_QUERY),
	DEFINE_VALIDATOR(MAP_TLV_BEACON_METRICS_RESPONSE),
	DEFINE_VALIDATOR(MAP_TLV_STEERING_REQUEST),
	DEFINE_VALIDATOR(MAP_TLV_STEERING_BTM_REPORT),
	DEFINE_VALIDATOR(MAP_TLV_CLIENT_ASSOCIATION_CONTROL_REQUEST),
	DEFINE_VALIDATOR(MAP_TLV_BACKHAUL_STEERING_REQUEST),
	DEFINE_VALIDATOR(MAP_TLV_BACKHAUL_STEERING_RESPONSE),
	DEFINE_VALIDATOR(MAP_TLV_HIGHER_LAYER_DATA),
	DEFINE_VALIDATOR(MAP_TLV_ASSOCIATED_STA_TRAFFIC_STATS),
	DEFINE_VALIDATOR(MAP_TLV_ERROR_CODE),
	DEFINE_VALIDATOR(MAP_TLV_CHANNEL_SCAN_REPORTING_POLICY),
	DEFINE_VALIDATOR(MAP_TLV_CHANNEL_SCAN_CAPABILITY),
	DEFINE_VALIDATOR(MAP_TLV_CHANNEL_SCAN_REQ),
	DEFINE_VALIDATOR(MAP_TLV_CHANNEL_SCAN_RES),
	DEFINE_VALIDATOR(MAP_TLV_TIMESTAMP),
#if (EASYMESH_VERSION >= 3)
	DEFINE_VALIDATOR(MAP_TLV_1905_SECURITY_CAPS),
	DEFINE_VALIDATOR(MAP_TLV_AP_WIFI6_CAPS),
	DEFINE_VALIDATOR(MAP_TLV_MIC),
	DEFINE_VALIDATOR(MAP_TLV_ENCRYPTED_PAYLOAD),
#endif /* >= 3 */
	DEFINE_VALIDATOR(MAP_TLV_CAC_REQ),
	DEFINE_VALIDATOR(MAP_TLV_CAC_TERMINATION),
	DEFINE_VALIDATOR(MAP_TLV_CAC_COMPLETION_REPORT),
#if (EASYMESH_VERSION >= 3)
	DEFINE_VALIDATOR(MAP_TLV_ASSOCIATED_WIFI6_STA_STATUS),
#endif /* >= 3 */
	DEFINE_VALIDATOR(MAP_TLV_CAC_STATUS_REPORT),
	DEFINE_VALIDATOR(MAP_TLV_CAC_CAPABILITY),
	DEFINE_VALIDATOR(MAP_TLV_MULTIAP_PROFILE),
	DEFINE_VALIDATOR(MAP_TLV_PROFILE2_AP_CAPABILITY),
	DEFINE_VALIDATOR(MAP_TLV_DEFAULT_8021Q_SETTINGS),
	DEFINE_VALIDATOR(MAP_TLV_TRAFFIC_SEPARATION_POLICY),
#if (EASYMESH_VERSION >= 3)
	DEFINE_VALIDATOR(MAP_TLV_BSS_CONFIGURATION_REPORT),
	DEFINE_VALIDATOR(MAP_TLV_BSSID),
	DEFINE_VALIDATOR(MAP_TLV_SERVICE_PRIORITIZATION_RULE),
	DEFINE_VALIDATOR(MAP_TLV_DSCP_MAPPING_TABLE),
	DEFINE_VALIDATOR(MAP_TLV_BSS_CONFIGURATION_REQUEST),
#endif /* >= 3 */
	DEFINE_VALIDATOR(MAP_TLV_PROFILE2_ERR_CODE),
#if (EASYMESH_VERSION >= 3)
	DEFINE_VALIDATOR(MAP_TLV_BSS_CONFIGURATION_RESPONSE),
#endif /* >= 3 */
	DEFINE_VALIDATOR(MAP_TLV_AP_RADIO_ADV_CAPABILITY),
	DEFINE_VALIDATOR(MAP_TLV_ASSOCIATION_STATUS_NOTIF),
	DEFINE_VALIDATOR(MAP_TLV_SOURCE_INFO),
	DEFINE_VALIDATOR(MAP_TLV_TUNNELED_MSG_TYPE),
	DEFINE_VALIDATOR(MAP_TLV_TUNNELED),
	DEFINE_VALIDATOR(MAP_TLV_PROFILE2_STEERING_REQ),
	DEFINE_VALIDATOR(MAP_TLV_UNSUCCESS_ASSOCIATION_POLICY),
	DEFINE_VALIDATOR(MAP_TLV_METRIC_COLLECTION_INTERVAL),
	DEFINE_VALIDATOR(MAP_TLV_RADIO_METRICS),
	DEFINE_VALIDATOR(MAP_TLV_AP_EXTENDED_METRICS),
	DEFINE_VALIDATOR(MAP_TLV_ASSOCIATED_STA_EXT_LINK_METRICS),
	DEFINE_VALIDATOR(MAP_TLV_STATUS_CODE),
	DEFINE_VALIDATOR(MAP_TLV_REASON_CODE),
	DEFINE_VALIDATOR(MAP_TLV_BACKHAUL_STA_RADIO_CAPABILITY),
#if (EASYMESH_VERSION >= 3)
	DEFINE_VALIDATOR(MAP_TLV_AKM_SUITE_CAPS),
	DEFINE_VALIDATOR(MAP_TLV_1905_ENCAP_DPP),
	DEFINE_VALIDATOR(MAP_TLV_1905_ENCAP_EAPOL),
	DEFINE_VALIDATOR(MAP_TLV_DPP_BOOTSTRAP_URI_NOTIFICATION),
#endif /* >= 3 */
	DEFINE_VALIDATOR(MAP_TLV_BACKHAUL_BSS_CONFIG),
#if (EASYMESH_VERSION >= 3)
	DEFINE_VALIDATOR(MAP_TLV_DPP_MESSAGE),
	DEFINE_VALIDATOR(MAP_TLV_DPP_CCE_INDICATION),
	DEFINE_VALIDATOR(MAP_TLV_DPP_CHIRP_VALUE),
	DEFINE_VALIDATOR(MAP_TLV_DEVICE_INVENTORY),
	DEFINE_VALIDATOR(MAP_TLV_AGENT_LIST),
#endif /* >= 3 */
#if (EASYMESH_VERSION >= 4)
	DEFINE_VALIDATOR(MAP_TLV_ANTICIPATED_CHANNEL_PREF),
	DEFINE_VALIDATOR(MAP_TLV_ANTICIPATED_CHANNEL_USAGE),
	DEFINE_VALIDATOR(MAP_TLV_SPATIAL_REUSE_REQUEST),
	DEFINE_VALIDATOR(MAP_TLV_SPATIAL_REUSE_REPORT),
	DEFINE_VALIDATOR(MAP_TLV_SPATIAL_REUSE_CONFIG_RESPONSE),
	DEFINE_VALIDATOR(MAP_TLV_QOS_MANAGEMENT_POLICY),
	DEFINE_VALIDATOR(MAP_TLV_QOS_MANAGEMENT_DESCRIPTOR),
	DEFINE_VALIDATOR(MAP_TLV_CONTROLLER_CAPS),
#endif /* >= 4 */
#if (EASYMESH_VERSION >= 6)
	DEFINE_VALIDATOR(MAP_TLV_WIFI7_AGENT_CAPABILITIES),
	DEFINE_VALIDATOR(MAP_TLV_AP_MLD_CONFIG),
	DEFINE_VALIDATOR(MAP_TLV_BACKHAUL_STA_MLD_CONFIG),
	DEFINE_VALIDATOR(MAP_TLV_STA_MLD_CONFIG),
	DEFINE_VALIDATOR(MAP_TLV_MLD_STRUCTURE),
	DEFINE_VALIDATOR(MAP_TLV_AFFILIATED_STA_METRICS),
	DEFINE_VALIDATOR(MAP_TLV_AFFILIATED_AP_METRICS),
	DEFINE_VALIDATOR(MAP_TLV_TID_TO_LINK_MAPPING_POLICY),
	DEFINE_VALIDATOR(MAP_TLV_EHT_OPERATIONS),
	DEFINE_VALIDATOR(MAP_TLV_BSS_ADVANCED_CONFIG),
	DEFINE_VALIDATOR(MAP_TLV_RSN_PARAMETERS_CONFIG),
	DEFINE_VALIDATOR(MAP_TLV_SUPPORTED_CIPHER_SUITES),
#endif /* >= 6 */
};
