/* SPDX-License-Identifier: LGPL-2.1-only */
/*
 * hostapd_ctrl.c - CLI to hostapd
 *
 * Copyright (C) 2020-2024 Iopsys Software Solutions AB. All rights reserved.
 */
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include <sys/types.h>
#include <fcntl.h>
#include <net/if.h>
#include <time.h>
#include <syslog.h>
#include <unistd.h>
#include <stdlib.h>
#include <endian.h>
#include <dirent.h>

#include <easy/easy.h>
#include "wifiutils.h"
#include "wifi.h"
#include "debug.h"
#include "hostapd_ctrl.h"
#include "nlwifi.h"
#include "wpactrl.h"

static int hostapd_cli_get_sta_list(char *buf, uint8_t *stas, int *num_stas)
{
	const char *line;
	char *p;
	uint8_t tmp_sta[6];
	int i = 0;
	int ret;

	if (strlen(buf) == 0) {
		*num_stas = 0;
		return 0;
	}

	p = buf;
	while ((line = strsep(&p, "\n"))) {
		ret = sscanf(line, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
				&tmp_sta[0],
				&tmp_sta[1],
				&tmp_sta[2],
				&tmp_sta[3],
				&tmp_sta[4],
				&tmp_sta[5]);

		if (WARN_ON(ret != 6))
			continue;

		if(i >= *num_stas) {
			libwifi_err("%s Number of connected stations %d exceeds expected %d!\n",
				__func__, (i + 1), *num_stas);
			return -EINVAL;
		}

		memcpy(&stas[i*6], tmp_sta, 6);
		i++;
	}
	*num_stas = i;
	return 0;
}

#ifdef LIBWIFI_USE_CTRL_IFACE
static int hostapd_ctrl_get_stas(const char *ifname, char *ctrl_buf, size_t ctrl_buf_size)
{
	char buf[2048] = { 0 };
	char sta_mac[32] = { 0 };
	char cmd[64] = { 0 };
	char *pos;
	int ret, n;

	pos = ctrl_buf;
	ret = wpa_ctrl_iface_get(ifname, "STA-FIRST", buf, sizeof(buf), true);
	if (WARN_ON(ret))
		return ret;

	if (!strlen(buf)) {
		libwifi_dbg("[%s] %s: None associated devices\n", ifname, __func__);
		return 0;
	}

	if (ctrl_buf_size < strlen(buf)) {
		libwifi_err("[%s] %s: Output buffer too small %i < %i\n", ifname, __func__, ctrl_buf_size, strlen(buf));
		return -1;
	}

	do {
		if (!strlen(buf))
			break;
		if (strstr(buf, "FAIL")) {
			libwifi_warn("[%s] %s:Failed to get STA from hostapd\n", ifname, __func__);
			return -1;
		}

		if (WARN_ON(!strchr(buf, '\n')))
			return -1;

		n = (int)(strchr(buf, '\n') - buf);
		strncpy(sta_mac, buf, n);
		if ((pos + n + 1 - ctrl_buf) < ctrl_buf_size) {
			strncpy(pos, buf, n + 1);
			pos += n + 1;
		} else {
			libwifi_warn("[%s] %s Collected sta mac's exceed output buffer size %i > %i\n",
			             ifname, __func__, (int)(pos + n +1 - ctrl_buf), ctrl_buf_size);
			break;
		}
		snprintf(cmd, sizeof(cmd), "STA-NEXT %s" , sta_mac);
		memset(buf, 0, 1024);
		ret = wpa_ctrl_iface_get(ifname, cmd, buf, sizeof(buf), true);
		if (WARN_ON(ret))
			return ret;
	} while (true);
	*(pos - 1) = '\0';
	return 0;
}

int hostapd_iface_get_assoclist(const char *ifname, uint8_t *stas, int *num_stas)
{
	char ctrl_buf[4096] = { 0 };

	libwifi_dbg("[%s] %s\n", ifname, __func__);

	if (!ifname || !strlen(ifname)) {
		libwifi_err("[%s] %s Invalid args!\n", ifname, __func__);
		return -EINVAL;
	}
	if (!stas || *num_stas <= 0) {
		libwifi_err("[%s] %s Invalid args!\n", ifname, __func__);
		return -EINVAL;
	}

	memset(stas, 0, *num_stas * 6);
	if (hostapd_ctrl_get_stas(ifname, ctrl_buf, sizeof(ctrl_buf)))
		return -1;

	if (hostapd_cli_get_sta_list(ctrl_buf, stas, num_stas))
		return -1;

	return 0;
}

int hostapd_get_blocked_stas(const char *ifname, uint8_t *stas, int *num_stas)
{
	char buf[2048] = {};
	int ret = -1;

	memset(stas, 0, *num_stas * 6);
	ret = wpa_ctrl_iface_get(ifname, "raw DENY_ACL SHOW", buf, sizeof(buf), true);
	if (WARN_ON(ret))
		return ret;

	return hostapd_cli_get_sta_list(buf, stas, num_stas);
}

#else
int hostapd_iface_get_assoclist(const char *ifname, uint8_t *stas, int *num_stas)
{
	char buf[1024];

	if (!ifname || !strlen(ifname)) {
		libwifi_err("[%s] %s Invalid args!\n", ifname, __func__);
		return -EINVAL;
	}
	if (!stas || *num_stas <= 0) {
		libwifi_err("[%s] %s Invalid args!\n", ifname, __func__);
		return -EINVAL;
	}

	memset(stas, 0, *num_stas * 6);
	chrCmd(buf, sizeof(buf), "timeout %d hostapd_cli -i %s list_sta", WPACTRL_TIMEOUT, ifname);

	if (hostapd_cli_get_sta_list(buf, stas, num_stas))
		return -1;

	return 0;
}

int hostapd_get_blocked_stas(const char *ifname, uint8_t *stas, int *num_stas)
{
	char buf[2048] = {};
	int ret = -1;

	memset(stas, 0, *num_stas * 6);
	ret = wpa_cli_get(ifname, "raw DENY_ACL SHOW", buf, sizeof(buf));
	if (WARN_ON(ret))
		return ret;

	return hostapd_cli_get_sta_list(buf, stas, num_stas);
}

#endif

static int hostapd_cli_iface_get_sta_listen_interval(const char *buf, uint16_t *listen_int)
{
	char val[256] = { 0 };

	if (!listen_int)
		return -EINVAL;

	if (wpa_ctrl_get_param(buf, "listen_interval", val, sizeof(val))) {
		libwifi_warn("%s flags parameter not found in buffer [%s]!\n",
			__func__, buf);
		return -1;
	}

	*listen_int = strtoul(val, NULL, 10);
	return 0;
}

static int hostapd_cli_iface_get_sta_flags(const char *buf, char *flags, size_t flags_max)
{
	char val[256] = { 0 };
	size_t flags_len;

	if (!flags || flags_max == 0)
		return -EINVAL;

	if (wpa_ctrl_get_param(buf, "flags", val, sizeof(val))) {
		libwifi_warn("%s flags parameter not found in buffer [%s]!\n",
			__func__, buf);
		return -1;
	}

	flags_len = strlen(val);
	if (WARN_ON(flags_len >= flags_max))
		return -EINVAL;

	snprintf(flags, flags_max, "%s", val);

	return 0;
}

static int hostapd_cli_hexstr_to_bin(char *hex_buf, uint8_t *bin_buf, size_t *bin_buf_len)
{
	char *ptr;
	uint8_t octet = 0;
	int i = 0;
	int hex_buf_len;

	if (WARN_ON(!hex_buf) || WARN_ON(!bin_buf) || WARN_ON(!bin_buf_len) || WARN_ON(*bin_buf_len == 0))
		return -EINVAL;

	hex_buf_len = strlen(hex_buf);
	if (hex_buf_len == 0 || hex_buf_len % 2 != 0) {
		libwifi_err("Invalid hex string [%s] len[%ld]\n", hex_buf, hex_buf_len);
		return -1;
	}

	ptr = hex_buf;
	while (*ptr != '\0' && i < *bin_buf_len) {
		sscanf(ptr, "%2hhx", &octet);
		bin_buf[i] = octet;
		ptr += 2;
		i++;
	}
	*bin_buf_len = i;

	return 0;
}

int hostapd_cli_iface_send_action_frame(const char *ifname, struct wifi_frame_arg *arg,
					uint8_t *frame, size_t len, uint64_t *cookie)
{
	const uint8_t empty_mac[] = {
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00
	};
	enum wifi_mode mode;
	char wait[32] = { 0 };
	char src[32] = { 0 };
	char freq[32] = { 0 };
	char dst[32] = { 0 };
	char *data, *cmd;
	size_t buflen = 0;
	int ret = -1;

	ret = wifi_get_mode(ifname, &mode);
	if(ret != 0)
		return ret;

	/* Calc worst case length we should alloc */
	buflen = strlen("send_action") + strlen("data=") + sizeof(wait) + sizeof(src) +
		 sizeof(freq) + sizeof(dst) + 2 * len + 64;

	/* dst=<val> data=<val> [src=<val>] [wait=<val>] [freq=<val>] */
	cmd = calloc(buflen, sizeof(char));
	if (!cmd)
		return -1;
	data = calloc(2*len, sizeof(char));
	if (!data) {
		free(cmd);
		return -1;
	}

	btostr(frame, len, data);

	if (arg->duration)
		snprintf(wait, sizeof(wait), "wait=%u", arg->duration);
	if (memcmp(arg->src, empty_mac, 6))
		snprintf(src, sizeof(src), "src=" MACSTR, MAC2STR(arg->src)); /* Flawfinder: ignore */
	if (arg->freq)
		snprintf(freq, sizeof(freq), "freq=%u", arg->freq);

	snprintf(dst, sizeof(dst), "dst=" MACSTR, MAC2STR(arg->dst)); /* Flawfinder: ignore */

	snprintf(cmd, buflen, "send_action %s %s %s %s data=%s",
		 dst, src, freq, wait, data);

	switch (mode) {
	case WIFI_MODE_AP:
		ret = hostapd_cli_set(ifname, cmd, true);
		break;
	case WIFI_MODE_STA:
		ret = wpa_cli_set(ifname, cmd, true);
		break;
	default:
		break;
	}

	free(data);
	free(cmd);
	return ret;
}

int hostapd_cli_iface_dpp_listen(const char *ifname, uint32_t freq)
{
	char cmd[128] = {0};
	enum wifi_mode mode;
	int ret = -1;

	ret = wifi_get_mode(ifname, &mode);
	if(ret != 0)
		return ret;

	/* [freq=<val>] */
	snprintf(cmd, sizeof(cmd), "dpp_listen %u", freq);

	switch (mode) {
	case WIFI_MODE_AP:
		ret = hostapd_cli_set(ifname, cmd, true);
		break;
	case WIFI_MODE_STA:
		ret = wpa_cli_set(ifname, cmd, true);
		break;
	default:
		break;
	}

	return ret;
}

int hostapd_cli_iface_dpp_stop_listen(const char *ifname)
{
	const char *cmd = "dpp_stop_listen";
	enum wifi_mode mode;
	int ret = -1;

	ret = wifi_get_mode(ifname, &mode);
	if(ret != 0)
		return ret;

	switch (mode) {
	case WIFI_MODE_AP:
		ret = hostapd_cli_set(ifname, cmd, true);
		break;
	case WIFI_MODE_STA:
		ret = wpa_cli_set(ifname, cmd, true);
		break;
	default:
		break;
	}

	return ret;
}

int hostapd_cli_get_beacon_int(const char *ifname, uint32_t *beacon_int)
{
	uint8_t beacon[4096] = { 0 };
	size_t beacon_len = sizeof(beacon);
	struct beacon_frame *bf;
	int ret;

	/* get beacon */
	ret = hostapd_cli_get_beacon(ifname, beacon, &beacon_len);
	if (ret)
		return ret;

	bf = (struct beacon_frame *) beacon;
	*beacon_int = bf->beacon_int;

	return 0;
}

int hostapd_cli_get_ccfs0_ccfs1(const char *ifname, uint32_t *ccfs0, uint32_t *ccfs1)
{
	uint8_t beacon_ies[4096] = { 0 };
	size_t ies_len = sizeof(beacon_ies);
	uint8_t *ie_ptr;
	int ret;

	ret = hostapd_cli_get_beacon_ies(ifname, beacon_ies, &ies_len);
	if (WARN_ON(ret)) {
		return ret;
	}

	ie_ptr = wifi_find_ie(beacon_ies, ies_len, IE_VHT_OPER);
	if (ie_ptr && ie_ptr[1] == 5) {
		*ccfs0 = ie_ptr[3];
		*ccfs1 = ie_ptr[4];
	}

	return 0;
}

int hostapd_cli_radio_info(const char *ifname, enum wifi_band band, struct wifi_radio *radio)
{
	uint8_t beacon[4096] = { 0 };
	size_t beacon_len = sizeof(beacon);
	struct beacon_frame *bf;
	uint8_t *beacon_ies;
	uint8_t *ie_ptr;
	size_t ies_len;
	int ret;

	/* dtim */
	hostapd_cli_get_dtim(ifname, &radio->dtim_period);

	/* get beacon */
	ret = hostapd_cli_get_beacon(ifname, beacon, &beacon_len);
	if (ret)
		return ret;

	bf = (struct beacon_frame *) beacon;
	ies_len = beacon_len - ((uint8_t *)&bf->cap_info - beacon + 2);
	beacon_ies = bf->var;

	/* beacon interval */
	radio->beacon_int = bf->beacon_int;

	/* oper stds */
	ret = wifi_oper_stds_set_from_ie(beacon_ies, ies_len, &radio->oper_std);
	if (WARN_ON(ret)) {
		return ret;
	}
	correct_oper_std_by_band(band, &radio->oper_std);

	/* ccfs0/ccfs1 */
	ie_ptr = wifi_find_ie(beacon_ies, ies_len, IE_VHT_OPER);
	if (ie_ptr && ie_ptr[1] == 5) {
		radio->ccfs0 = ie_ptr[3];
		radio->ccfs1 = ie_ptr[4];
	}

	return 0;
}

int hostapd_cli_iface_ap_info(const char *ifname, struct wifi_ap *ap)
{
	char buf[1024];
	char val[256] = { 0 };
	int ret = 0;
	uint8_t beacon[4096] = { 0 };
	uint8_t *beacon_ies;
	uint8_t *ie_ptr;
	size_t beacon_len = sizeof(beacon);
	size_t ies_len;
	struct beacon_frame *bf;

	/* get beacon */
	ret = hostapd_cli_get_beacon(ifname, beacon, &beacon_len);
	if (WARN_ON(ret)) {
		ap->enabled = false;
		return ret;
	}

	ap->enabled = true;
	bf = (struct beacon_frame *)beacon;
	ies_len = beacon_len - ((uint8_t *)&bf->cap_info - beacon + 2);
	beacon_ies = bf->var;

	WARN_ON(wifi_oper_stds_set_from_ie(beacon_ies, ies_len, &ap->bss.oper_std));
	WARN_ON(wifi_ssid_advertised_set_from_ie(beacon_ies, ies_len, &ap->ssid_advertised));
	WARN_ON(wifi_get_bss_security_from_ies(&ap->bss, beacon_ies, ies_len));
	WARN_ON(wifi_apload_set_from_ie(beacon_ies, ies_len, &ap->bss.load));
	WARN_ON(wifi_dtim_set_from_ie(beacon_ies, ies_len, &ap->bss.dtim_period));
	ap->bss.beacon_int = bf->beacon_int;

	if (!ap->bss.dtim_period)
		hostapd_cli_get_dtim(ifname, &ap->bss.dtim_period);

	/* capabilities information */
	ie_ptr = (uint8_t *)&bf->cap_info;
	ap->bss.caps.valid |= WIFI_CAP_BASIC_VALID;
	memcpy(&ap->bss.caps.basic, ie_ptr, sizeof(bf->cap_info));
	wifi_cap_set_from_ie(ap->bss.cbitmap, ie_ptr, sizeof(bf->cap_info));

	/* ht capabilities */
	ie_ptr = wifi_find_ie(beacon_ies, ies_len, IE_HT_CAP);
	if (ie_ptr) {
		ap->bss.caps.valid |= WIFI_CAP_HT_VALID;
		memcpy(&ap->bss.caps.ht, &ie_ptr[2], ie_ptr[1]);
		wifi_cap_set_from_ie(ap->bss.cbitmap, ie_ptr, ie_ptr[1] + 2);
	}

	/* vht capabilities */
	ie_ptr = wifi_find_ie(beacon_ies, ies_len, IE_VHT_CAP);
	if (ie_ptr) {
		ap->bss.caps.valid |= WIFI_CAP_VHT_VALID;
		memcpy(&ap->bss.caps.vht, &ie_ptr[2], ie_ptr[1]);
		wifi_cap_set_from_ie(ap->bss.cbitmap, ie_ptr, ie_ptr[1] + 2);
	}

	/* extended capabilities */
	ie_ptr = wifi_find_ie(beacon_ies, ies_len, IE_EXT_CAP);
	if (ie_ptr) {
		ap->bss.caps.valid |= WIFI_CAP_EXT_VALID;
		memcpy(&ap->bss.caps.ext, &ie_ptr[2], ie_ptr[1]);
		wifi_cap_set_from_ie(ap->bss.cbitmap, ie_ptr, ie_ptr[1] + 2);
	}

	/* RM enabled capabilities */
	ie_ptr = wifi_find_ie(beacon_ies, ies_len, IE_RRM);
	if (ie_ptr && ie_ptr[1] == 5) {
		ap->bss.caps.valid |= WIFI_CAP_RM_VALID;
		memcpy(&ap->bss.caps.rrm, &ie_ptr[2], ie_ptr[1]);
		wifi_cap_set_from_ie(ap->bss.cbitmap, ie_ptr, ie_ptr[1] + 2);
	}

	/* mobility domain element */
	ie_ptr = wifi_find_ie(beacon_ies, ies_len, IE_MDE);
	if (ie_ptr) {
		wifi_cap_set_from_ie(ap->bss.cbitmap, ie_ptr, ie_ptr[1] + 2);
	}

	/* HE capabilities */
	ie_ptr = wifi_find_ie_ext(beacon_ies, ies_len, IE_EXT_HE_CAP);
	if (ie_ptr) {
		wifi_cap_from_ext_ie(&ie_ptr[2], ie_ptr[1], &ap->bss.caps);
		wifi_cap_set_from_ie(ap->bss.cbitmap, ie_ptr, ie_ptr[1] + 2);
	}

	/* HE Operations */
	ie_ptr = wifi_find_ie_ext(beacon_ies, ies_len, IE_EXT_HE_OPER);
	if (ie_ptr && ie_ptr[1] >= 7) {
		uint8_t *params = &ie_ptr[3];  /* 3-byte HE Operation Parameters */

		ap->bss.bss_color = ie_ptr[6] & 0x3f;
		ap->bss.bss_color_disabled = ie_ptr[6] & 0x80;

		/* VHT Operation Information Present: B14 in HE Operation parameters */
		if (!!(params[1] & 0x40) && ie_ptr[1] >= 10) {
			uint8_t *vht_oper_info = &ie_ptr[9];  /* Chan Width, CCFS0, CCFS1 */

			ap->bss.ccfs0 = vht_oper_info[1];
			ap->bss.ccfs1 = vht_oper_info[2];
		}
	}

	/* EHT capabilities */
	ie_ptr = wifi_find_ie_ext(beacon_ies, ies_len, IE_EXT_EHT_CAP);
	if (ie_ptr) {
		wifi_cap_from_ext_ie(&ie_ptr[2], ie_ptr[1], &ap->bss.caps);
		wifi_cap_set_from_ie(ap->bss.cbitmap, ie_ptr, ie_ptr[1] + 2);
	}

	/* EHT Operations */
	ie_ptr = wifi_find_ie_ext(beacon_ies, ies_len, IE_EXT_EHT_OPER);
	if (ie_ptr && ie_ptr[1] >= 6) {
		uint8_t eht_oper_param = ie_ptr[3];
		bool eht_oper_info_present = !!(eht_oper_param & 0x1);
		bool eht_puncture_bmp_present = !!(eht_oper_param & 0x2);

		if (eht_oper_info_present && ie_ptr[1] >= 9) {
			uint8_t *eht_oper_info = ie_ptr + 8;

			ap->bss.ccfs0 = eht_oper_info[1];
			ap->bss.ccfs1 = eht_oper_info[2];

			if (eht_puncture_bmp_present && ie_ptr[1] >= 11)
				memcpy(&ap->bss.puncture, &eht_oper_info[3], 2);
		}
	}

	/* VHT Operations */
	ie_ptr = wifi_find_ie(beacon_ies, ies_len, IE_VHT_OPER);
	if (ie_ptr && ie_ptr[1] == 5) {
		ap->bss.ccfs0 = ie_ptr[3];
		ap->bss.ccfs1 = ie_ptr[4];
	}

	/* WMM */
	ie_ptr = wifi_find_vsie(beacon_ies, ies_len, microsoft_oui, 2, 1);
	if (ie_ptr) {
		wifi_wmm_set_from_ie(ap->ac, ie_ptr, ie_ptr[1] + 2);
		wifi_cap_set_from_ie(ap->bss.cbitmap, ie_ptr, ie_ptr[1] + 2);
	}

	/* get ap info from STATUS-DRIVER */
	ret = hostapd_cli_get(ifname, "raw STATUS-DRIVER", buf, sizeof(buf));
	if (WARN_ON(ret))
		return ret;

	if (wpa_ctrl_get_param(buf, "capa.max_stations", val, sizeof(val))) {
		libwifi_warn("%s capa.max_stations parameter not found in buffer [%s]!\n",
			__func__, buf);
	}

	if (WARN_ON(sscanf(val, "%d", &ap->assoclist_max) != 1)) {
		libwifi_warn("Failed to parse capa.max_stations parameter\n");
	}

	return ret;
}

static int hostapd_cli_get_sta_capability_information(const char *sta_buf, uint8_t *ie, size_t *ie_len) {
	char val[256] = { 0 };
	uint16_t capa = 0;
	uint8_t *ptr;

	if (WARN_ON(!ie) || WARN_ON(*ie_len < 2))
		return -EINVAL;

	if (wpa_ctrl_get_param(sta_buf, "capability", val, sizeof(val))) {
		libwifi_warn("%s capability parameter not found in buffer [%s]!\n",
		__func__, sta_buf);
		return -1;
	}

	if (sscanf(val, "%hx", &capa) != 1)
		return -1;

	ptr = (uint8_t *)&capa;
	ie[0] = ptr[0];
	ie[1] = ptr[1];
	*ie_len = 2;

	return 0;
}

static int hostapd_cli_get_sta_ht_capabilities_info(const char *sta_buf, uint8_t *ie, size_t *ie_len) {
	char val[256] = { 0 };
	uint16_t capa = 0;
	uint8_t *ptr;

	if (WARN_ON(!ie) || WARN_ON(*ie_len < 2))
		return -EINVAL;

	if (wpa_ctrl_get_param(sta_buf, "ht_caps_info", val, sizeof(val))) {
		libwifi_warn("%s ht_caps_info parameter not found in buffer [%s]!\n",
		__func__, sta_buf);
		return -1;
	}

	if (sscanf(val, "%hx", &capa) != 1)
		return -1;

	ptr = (uint8_t *)&capa;
	ie[0] = ptr[0];
	ie[1] = ptr[1];
	*ie_len = 2;

	return 0;
}

static int hostapd_cli_get_sta_ht_mcs_bitmask(const char *sta_buf, uint8_t *ie, size_t *ie_len) {
	char val[256] = { 0 };
	int len;

	if (WARN_ON(!ie))
		return -EINVAL;

	if (wpa_ctrl_get_param(sta_buf, "ht_mcs_bitmask", val, sizeof(val))) {
		libwifi_warn("%s ht_msc_bitmask parameter not found in buffer [%s]!\n",
		__func__, sta_buf);
		return -1;
	}

	len = strlen(val);
	if (len % 2)
		return -1;

	if (len > *ie_len * 2)
		return -1;

	strtob(val, len, ie);
	*ie_len = len/2;

	return 0;
}

static int hostapd_cli_get_sta_vht_capabilities_info(const char *sta_buf, uint8_t *ie, size_t *ie_len) {
	char val[256] = { 0 };
	uint32_t capa = 0;
	uint8_t *ptr;

	if (WARN_ON(!ie) || WARN_ON(*ie_len < 4))
		return -EINVAL;

	if (wpa_ctrl_get_param(sta_buf, "vht_caps_info", val, sizeof(val))) {
		libwifi_warn("%s vht_caps_info parameter not found in buffer [%s]!\n",
		__func__, sta_buf);
		return -1;
	}

	if (sscanf(val, "%x", &capa) != 1)
		return -1;
	ptr = (uint8_t *)&capa;
	ie[0] = ptr[0];
	ie[1] = ptr[1];
	ie[2] = ptr[2];
	ie[3] = ptr[3];
	*ie_len = 4;

	return 0;
}

static int hostapd_cli_get_sta_vht_capabilities_info_full(const char *sta_buf, uint8_t *ie, size_t *ie_len) {
	char val[256] = { 0 };
	int len;

	if (WARN_ON(!ie))
		return -EINVAL;

	if (wpa_ctrl_get_param(sta_buf, "vht_capab", val, sizeof(val))) {
		libwifi_dbg("%s vht_capab parameter not found in buffer [%s]!\n",
		__func__, sta_buf);
		return -1;
	}

	len = strlen(val);
	if (len % 2)
		return -1;

	if (len > *ie_len * 2)
		return -1;

	strtob(val, len, ie);
	*ie_len = len/2;

	return 0;
}

static int hostapd_cli_get_sta_he_capabilities_info(const char *sta_buf, uint8_t *ie, size_t *ie_len) {
	char val[256] = { 0 };
	int len;

	if (WARN_ON(!ie))
		return -EINVAL;

	if (wpa_ctrl_get_param(sta_buf, "he_capab", val, sizeof(val))) {
		libwifi_dbg("%s he_capab parameter not found in buffer [%s]!\n",
		__func__, sta_buf);
		return -1;
	}

	len = strlen(val);
	if (len % 2)
		return -1;

	if (len > *ie_len * 2)
		return -1;

	strtob(val, len, ie);
	*ie_len = len/2;

	return 0;
}

static int hostapd_cli_get_sta_eht_capabilities_info(const char *sta_buf, uint8_t *ie, size_t *ie_len) {
	char val[256] = { 0 };
	int len;

	if (WARN_ON(!ie))
		return -EINVAL;

	if (wpa_ctrl_get_param(sta_buf, "eht_capab", val, sizeof(val))) {
		libwifi_dbg("%s eht_capab parameter not found in buffer [%s]!\n",
		__func__, sta_buf);
		return -1;
	}

	len = strlen(val);
	if (len % 2)
		return -1;

	if (len > *ie_len * 2)
		return -1;

	strtob(val, len, ie);
	*ie_len = len/2;

	return 0;
}

static int hostapd_cli_get_sta_rrm_capabilities_info(const char *sta_buf, uint8_t *ie, size_t *ie_len) {
	char val[256] = { 0 };
	int len;

	if (WARN_ON(!ie))
		return -EINVAL;

	if (wpa_ctrl_get_param(sta_buf, "rrm_capab", val, sizeof(val))) {
		libwifi_dbg("%s rrm_capab parameter not found in buffer [%s]!\n",
		__func__, sta_buf);
		return -1;
	}

	len = strlen(val);
	if (len % 2)
		return -1;

	if (len > *ie_len * 2)
		return -1;

	strtob(val, len, ie);
	*ie_len = len/2;

	return 0;
}

static int hostapd_cli_get_extended_capabilities(const char *sta_buf, uint8_t *ie, size_t *ie_len)
{
	int i = 0;
	size_t ext_len;
	char val[256] = { 0 };
	uint8_t octet = 0;
	char *ptr;

	if (WARN_ON(!ie) || WARN_ON(*ie_len < 8))
		return -EINVAL;

	if (wpa_ctrl_get_param(sta_buf, "ext_capab", val, sizeof(val))) {
		libwifi_warn("%s ext_capab parameter not found in buffer [%s]!\n",
		__func__, sta_buf);
		return -1;
	}
	ext_len = strlen(val);

	if (ext_len == 0 || ext_len % 2 != 0) {
		libwifi_err("Invalid format of ext_capab[%s] len[%ld]\n", val, ext_len);
		return -1;
	}

	ptr = val;
	while (*ptr != '\0' && i < *ie_len) {
		sscanf(ptr, "%2hhx", &octet);
		ie[i] = octet;
		ptr += 2;
		i++;
	}
	*ie_len = i;

	return 0;
}

static int hostapd_cli_get_max_nss(const char *sta_buf, uint8_t *max_nss)
{
	char val[256] = { 0 };

	if (wpa_ctrl_get_param(sta_buf, "max_nss", val, sizeof(val))) {
		libwifi_warn("%s max_nss parameter not found in buffer [%s]!\n",
		__func__, sta_buf);
		return -1;
	}

	if (WARN_ON(sscanf(val, "%hhu", max_nss) != 1)) {
		libwifi_warn("Failed to parse max_nss parameter\n");
		return -1;
	}

	return 0;
}

static int hostapd_cli_get_sta_mlo(const char *sta_buf, struct wifi_sta *info)
{
	char val[256] = {};

	if (!wpa_ctrl_get_param(sta_buf, "mlo_link_id", val, sizeof(val)))
		info->mlo_link_id = atoi(val);
	else
		info->mlo_link_id = -1;
	if (!wpa_ctrl_get_param(sta_buf, "bssid", val, sizeof(val)))
		hwaddr_aton(val, info->bssid);
	if (!wpa_ctrl_get_param(sta_buf, "mlo_bssid", val, sizeof(val)))
		hwaddr_aton(val, info->mld_bssid);
	if (!wpa_ctrl_get_param(sta_buf, "mlo_sta_link_addr", val, sizeof(val))) {
		memcpy(info->mld_macaddr, info->macaddr, 6);
		hwaddr_aton(val, info->macaddr);
	}

	return 0;
}

int hostapd_cli_iface_get_sta_info(const char *ifname, uint8_t *addr, struct wifi_sta *info)
{
	char sta_flags[128] = { 0 };
	char cmd[64] = { 0 };
	char buf[2048] = { 0 };
	uint8_t ie[128] = { 0 };
	uint8_t rrm[5] = { 0 };
	int ret = 0;
	size_t ie_max = sizeof(ie);
	size_t ie_len;

	libwifi_dbg("%s %s \n", ifname, __func__);

	snprintf(cmd, sizeof(cmd), "sta " MACSTR, MAC2STR(addr));	/* Flawfinder: ignore */

	if (WARN_ON(hostapd_cli_get(ifname, cmd, buf, sizeof(buf))))
		return -1;

	if (strlen(buf) < 17)
		return -1;

	if (!strcmp(buf, "FAIL"))
		return -1;

	if (hwaddr_aton(buf, info->macaddr) == NULL)
		return -1;

	if (memcmp(addr, info->macaddr, 6))
		return -1;

	/* Make sure we set back addr */
	memcpy(info->macaddr, addr, sizeof(info->macaddr));

	ie_len = ie_max;
	ret = hostapd_cli_get_sta_capability_information(buf, ie, &ie_len);

	if (WARN_ON(ret))
		wifi_cap_set_from_capability_information(info->cbitmap, ie, ie_len);

	hostapd_cli_iface_get_sta_listen_interval(buf, &info->listen_int);
	hostapd_cli_iface_get_sta_flags(buf, sta_flags, sizeof(sta_flags));

	if (strstr(sta_flags, "[HT]")) {
		size_t mcs_len = sizeof(info->caps.ht.supp_mcs);
		info->caps.valid |= WIFI_CAP_HT_VALID;
		ie_len = ie_max;
		ret = hostapd_cli_get_sta_ht_capabilities_info(buf, ie, &ie_len);
		if (!ret)
			wifi_cap_set_from_ht_capabilities_info(info->cbitmap, ie, ie_len);
		hostapd_cli_get_sta_ht_mcs_bitmask(buf, info->caps.ht.supp_mcs, &mcs_len);
	}

	if (strstr(sta_flags, "[VHT]")) {
		ie_len = ie_max;
		ret = hostapd_cli_get_sta_vht_capabilities_info_full(buf, ie, &ie_len);
		if (ret) {
			ie_len = ie_max;
			ret = hostapd_cli_get_sta_vht_capabilities_info(buf, ie, &ie_len);
		}

		if (!ret) {
			wifi_cap_set_from_vht_capabilities_info(info->cbitmap, ie, ie_len);
			memcpy(&info->caps.vht, ie, ie_len < sizeof(info->caps.vht) ? ie_len : sizeof(info->caps.vht));
			info->caps.valid |= WIFI_CAP_VHT_VALID;
		}
	}

	if (strstr(sta_flags, "[HE]")) {
		ie_len = ie_max;
		ret = hostapd_cli_get_sta_he_capabilities_info(buf, ie, &ie_len);
		if (!ret) {
			uint8_t ext_ie[1 + 128];

			wifi_cap_set_from_he(info->cbitmap, ie, ie_len);
			ext_ie[0] = IE_EXT_HE_CAP;
			memcpy(&ext_ie[1], ie, ie_len);
			wifi_cap_from_ext_ie_mode(ext_ie, ie_len + 1, &info->caps, WIFI_MODE_STA);
		}
	}

	if (strstr(sta_flags, "[EHT]")) {
		ie_len = ie_max;
		ret = hostapd_cli_get_sta_eht_capabilities_info(buf, ie, &ie_len);
		if (!ret) {
			uint8_t ext_ie[1 + 128];

			wifi_cap_set_from_eht(info->cbitmap, ie, ie_len);
			ext_ie[0] = IE_EXT_EHT_CAP;
			memcpy(&ext_ie[1], ie, ie_len);
			wifi_cap_from_ext_ie_mode(ext_ie, ie_len + 1, &info->caps, WIFI_MODE_STA);
		}
	}

	ie_len = ie_max;
	ret = hostapd_cli_get_extended_capabilities(buf, ie, &ie_len);
	if (!ret) {
		info->caps.valid |= WIFI_CAP_EXT_VALID;
		wifi_cap_set_from_extended_capabilities(info->cbitmap, ie, ie_len);
	}

	ie_len = ie_max;
	ret = hostapd_cli_get_sta_rrm_capabilities_info(buf, ie, &ie_len);
	if (!ret && ie_len == 5 && memcmp(ie, rrm, sizeof(rrm))) {
		info->caps.valid |= WIFI_CAP_RM_VALID;
		memcpy(&info->caps.rrm, ie, ie_len);
		wifi_cap_set_from_rm_enabled_capabilities(info->cbitmap, ie, ie_len);
	}

	hostapd_cli_get_max_nss(buf, &info->rate.m.nss);
	info->nss = info->rate.m.nss;
	hostapd_cli_get_sta_mlo(buf, info);
	info->mlo_capable = (info->mlo_link_id != WIFI_MLO_INVALID_LINK_ID);

	return 0;
}

int hostapd_cli_get_ssid(const char *ifname, char *ssid, size_t ssid_size)
{
	char buf[1024];
	int ret;

	if (!ssid || ssid_size == 0)
		return -EINVAL;

	memset(ssid, 0, ssid_size);

	ret = hostapd_cli_get(ifname, "get_config", buf, sizeof(buf));
	if (WARN_ON(ret))
		return ret;

	ret = wpa_ctrl_get_param(buf, "ssid", ssid, ssid_size);
	if (ret) {
		libwifi_warn("%s ssid parameter not found in buffer [%s]!\n",
				__func__, buf);
		return ret;
	}

	return 0;
}

int hostapd_cli_get_dtim(const char *ifname, uint32_t *dtim)
{
	char buf[1024];
	char val[256] = { 0 };
	int ret;

	if (!dtim)
		return -EINVAL;

	ret = hostapd_cli_get(ifname, "status", buf, sizeof(buf));
	if (WARN_ON(ret))
		return ret;

	ret = wpa_ctrl_get_param(buf, "dtim_period", val, sizeof(val));
	if (ret) {
		libwifi_warn("%s dtim_period parameter not found in buffer [%s]!\n",
				__func__, buf);
		return ret;
	}

	*dtim = atoi(val);
	return 0;
}

int wpa_cli_get_ssid(const char *ifname, char *ssid, size_t ssid_size)
{
	char buf[1024];
	int ret;

	if (!ssid || ssid_size == 0)
		return -EINVAL;

	memset(ssid, 0, ssid_size);

	ret = wpa_cli_get(ifname, "status", buf, sizeof(buf));
	if (WARN_ON(ret))
		return ret;

	ret = wpa_ctrl_get_param(buf, "ssid", ssid, ssid_size);
	if (ret) {
		libwifi_warn("%s ssid parameter not found in buffer [%s]!\n",
				__func__, buf);
		return ret;
	}

	return 0;
}

int hostapd_cli_iface_add_neighbor(const char *ifname, struct nbr *nbr, size_t nbr_size)
{
	char bssid[18] = { 0 };
	char nr[256] = { 0 };
	char cmd[1024] = { 0 };
	char ssid[128];

	if (hostapd_cli_get_ssid(ifname, ssid, sizeof(ssid)))
		return -1;

	snprintf(bssid, sizeof(bssid), MACSTR, MAC2STR(nbr->bssid));	/* Flawfinder: ignore */
	btostr((uint8_t *) nbr, nbr_size, nr);

	snprintf(cmd, sizeof(cmd), "set_neighbor " MACSTR " ssid=\\\"%s\\\" nr=%s",
		 MAC2STR(nbr->bssid), ssid, nr);

	return hostapd_cli_set(ifname, cmd, true);
}

int hostapd_cli_iface_del_neighbor(const char *ifname, unsigned char *bssid)
{
	char cmd[1024] = { 0 };
	char ssid[128];

	if (hostapd_cli_get_ssid(ifname, ssid, sizeof(ssid)))
		return -1;

	snprintf(cmd, sizeof(cmd), "remove_neighbor " MACSTR " ssid=\\\"%s\\\"",
		 MAC2STR(bssid), ssid);

	return hostapd_cli_set(ifname, cmd, true);
}

int hostapd_cli_iface_start_wps(const char *ifname, struct wps_param wps)
{
	struct wifi_mlo_link links[WIFI_NUM_LINK_PER_MLD] = {0};
	int num_links = WIFI_NUM_LINK_PER_MLD;
	char cmd[1024] = { 0 };
	enum wifi_mode mode;
	int ret = 0;

	if (wps.method == WPS_METHOD_PBC){
		snprintf(cmd, sizeof(cmd),"wps_pbc");
	} else if (wps.method == WPS_METHOD_PIN) {
		snprintf(cmd, sizeof(cmd),"wps_pin %s %08lu", "any", wps.pin);
	}

	ret = wifi_get_mode(ifname, &mode);
	if (WARN_ON(ret))
		return ret;

	if (WARN_ON(mode != WIFI_MODE_AP && mode != WIFI_MODE_STA))
		return -1;

	ret = wifi_get_mlo_links(ifname, BAND_ANY, links, &num_links);
	if (WARN_ON(ret || num_links == 0)) {
		/* non-MLD legacy case */
		if (mode == WIFI_MODE_AP) {
			ret = hostapd_cli_set(ifname, cmd, true);
		} else if (mode == WIFI_MODE_STA) {
			if (wps.role & WPS_ENROLLEE_BSTA)
				snprintf(cmd + strlen(cmd), sizeof(cmd) - strlen(cmd), " multi_ap=1");

			/* Do not check 'OK'. wpa_cli wps_pin returns pin. */
			ret = wpa_cli_set(ifname, cmd, false);
		}

		return ret;
	}

	for (int j = 0; j < num_links; j++) {
		char *ctrl_iface = NULL;

		ret = wifi_get_ctrl_interface_band(ifname, links[j].band, &ctrl_iface);
		if (WARN_ON(ret))
			return ret;

		if (links[j].band != BAND_2 && links[j].band != BAND_5) {
			free(ctrl_iface);
			continue;
		}

		if (mode == WIFI_MODE_AP) {
			ret = hostapd_cli_set(ctrl_iface, cmd, true);
		} else if (mode == WIFI_MODE_STA) {
			if (wps.role & WPS_ENROLLEE_BSTA)
				snprintf(cmd + strlen(cmd), sizeof(cmd) - strlen(cmd), " multi_ap=1");

			/* Do not check 'OK'. wpa_cli wps_pin returns pin. */
			ret = wpa_cli_set(ctrl_iface, cmd, false);
		}

		free(ctrl_iface);
	}

	return ret;
}

int hostapd_cli_iface_stop_wps(const char *ifname)
{
	enum wifi_mode mode;
	int ret = 0;

	ret = wifi_get_mode(ifname, &mode);

	if(ret != 0)
		return ret;

	switch (mode) {
	case WIFI_MODE_AP:
	case WIFI_MODE_AP_VLAN:
		ret = hostapd_cli_set(ifname, "wps_cancel", true);
		break;
	case WIFI_MODE_STA:
		ret = wpa_cli_set(ifname, "wps_cancel", true);
		break;
	default:
		WARN_ON(1);
		ret = -1;
		break;
	}

	return ret;
}

int hostapd_cli_iface_get_wps_status(const char *ifname, enum wps_status *s)
{
	char buf[256] = {0};
	int ret;

	ret = hostapd_cli_get(ifname, "wps_get_status", buf, sizeof(buf));
	if (WARN_ON(ret))
		return ret;

	const char *f1 = "PBC Status: ";
	const char *f2 = "Last WPS result: ";
	char *pos1, *pos2;

	pos1 = strstr(buf, f1);
	pos2 = strstr(buf, f2);

	if (!pos1 || !pos2)
		return -1;

	pos1 += strlen(f1);
	if (strstr(pos1, "Disabled"))
		*s = WPS_STATUS_INIT;
	else if (strstr(pos1, "Active"))
		*s = WPS_STATUS_PROCESSING;
	else if (strstr(pos1, "Timed-out"))
		*s = WPS_STATUS_TIMEOUT;
	else if (strstr(pos1, "Overlap"))
		*s = WPS_STATUS_FAIL;
	else
		*s = WPS_STATUS_UNKNOWN;

	if (strstr(pos2, "Success"))
		*s = WPS_STATUS_SUCCESS;

	libwifi_dbg("%s %s status %d \n", ifname, __func__,*s);
	return 0;
}

int hostapd_cli_iface_get_wps_ap_pin(const char *ifname, unsigned long *pin)
{
	char buf[256] = {0};
	int ret = 0;

	ret = hostapd_cli_get(ifname, "wps_ap_pin get", buf, sizeof(buf));
	if (WARN_ON(ret))
		return ret;

	if (WARN_ON(1 != sscanf(buf,"%lu",pin)))
		ret = -1;

	return ret;
}

int hostapd_cli_iface_set_wps_ap_pin(const char *ifname, unsigned long pin)
{
	char cmd[1024] = {0};
	char buf[256] = {0};
	char pinbuf[9] = {0};
	int ret = 0;

	snprintf(pinbuf, 9, "%08lu", pin);
	snprintf(cmd, sizeof(cmd),"wps_ap_pin set %08lu", pin);

	ret = hostapd_cli_set(ifname, cmd, false);
	if (WARN_ON(ret))
		return ret;

	ret = hostapd_cli_get(ifname, "wps_ap_pin get", buf, sizeof(buf));
	if (WARN_ON(ret))
		return ret;

	if (WARN_ON(!strstr(buf, pinbuf)))
		ret = -1;

	return ret;
}

int hostapd_cli_iface_get_neighbor_list(const char *ifname, struct nbr *nbrs, int *nr)
{
	struct nbr *nbr;
	char *p, *k, *l;
	char *line;
	char *word;
	char *buf;
	int count;
	int len;
	int ret;

	/* make sure we have place for show_neighbor output */
	buf = calloc(*nr, 256);
	WARN_ON(!buf);
	if (!buf)
		return -1;

	ret = hostapd_cli_get(ifname, "show_neighbor", buf, *nr * 256);
	if (WARN_ON(ret)) {
		free(buf);
		return ret;
	}

	nbr = nbrs;
	count = 0;

	p = buf;
	while ((line = strsep(&p, "\n"))) {
		k = line;
		l = NULL;

		if (WARN_ON(count >= *nr))
			break;

		while ((word = strsep(&k, " "))) {
			if (!(l = strstr(word, "nr=")))
				continue;

			len = strlen(l) - 3;
			if (len > 2 * sizeof(*nbr))
				len = 2 * sizeof(*nbr);

			strtob(l + 3, len, (uint8_t *) nbr);
			break;
		}

		if (!l)
			continue;

		libwifi_dbg("nbr[%d] " MACSTR " bssid_info 0x%x reg %u chan %u phy %u\n",
			count, MAC2STR(nbr->bssid), nbr->bssid_info, nbr->reg,
			nbr->channel, nbr->phy);

		count++;
		nbr = &nbrs[count];
	}

	free(buf);
	*nr = count;
	return ret;
}

int hostapd_cli_iface_req_beacon(const char *ifname, unsigned char *sta,
				 struct wifi_beacon_req *req,
				 size_t req_size)
{
	char cmd[1024] = { 0 };
	char req_hex[256] = { 0 };
	size_t size;
	struct wifi_opclass opclass;
	uint8_t mcast_bssid[6] = "\xff\xff\xff\xff\xff\xff";
	uint8_t empty_bssid[6] = "\x00\x00\x00\x00\x00\x00";
	enum wifi_bw bw = BW_UNKNOWN;
	bool is_dfs = true;

	libwifi_dbg("[%s] %s called\n", ifname, __func__);

	size = req_size;
	if (WARN_ON(req_size * 2 >= sizeof(req_hex)))
		size = sizeof(req_hex) / 2;

	/* opclass must be set */
	if (!req->oper_class) {
		if (WARN_ON(wifi_get_opclass_ht20(ifname, &opclass)))
			return -1;
		req->oper_class = (uint8_t)opclass.g_opclass;
	}

	/* use multicast if bssid is not set */
	if (!memcmp(req->bssid, empty_bssid, 6))
		memcpy(req->bssid, mcast_bssid, sizeof(mcast_bssid));

	/* use most appropriate mode if not set explicitly */
	if (req->mode == WIFI_BCNREQ_MODE_UNSET) {
		if (req->channel) {
			wifi_get_bandwidth(ifname, &bw);

			if (WARN_ON(bw < BW20 || bw > BW160))
				return -1;

			is_dfs = wifi_is_dfs_channel(ifname, req->channel, (20 << bw));
		}

		if (!is_dfs)
			req->mode = WIFI_BCNREQ_MODE_ACTIVE;
		else
			req->mode = WIFI_BCNREQ_MODE_PASSIVE;
	}

	/* adjust duration in case not set explicitly */
	if (!req->duration) {
		if (req->mode == WIFI_BCNREQ_MODE_PASSIVE)
			req->duration = 100;
		else if (req->mode == WIFI_BCNREQ_MODE_ACTIVE)
			req->duration = 20;
	}

	/* convert to LE */
	req->rand_interval = htole16(req->rand_interval);
	req->duration = htole16(req->duration);

	btostr((uint8_t *) req, size, req_hex);
	snprintf(cmd, sizeof(cmd), "req_beacon " MACSTR " %s",
		 MAC2STR(sta), req_hex);

	/* For some reason req_beacon don't reply with OK */
	hostapd_cli_set(ifname, cmd, false);
	return 0;
}

int hostapd_cli_iface_req_beacon_default(const char *ifname, unsigned char *sta)
{
	uint8_t bssid[6] = "\xff\xff\xff\xff\xff\xff";
	struct wifi_beacon_req *req_beacon;
	char req[sizeof(*req_beacon) + SSID_MAX_LEN + 1 + 2] = { 0 };
	struct wifi_opclass opclass;
	uint8_t ssid[SSID_MAX_LEN + 1] = { 0 };
	size_t ssidlen;

	libwifi_dbg("[%s] %s called\n", ifname, __func__);

	if (WARN_ON(hostapd_cli_get_ssid(ifname, (char *)ssid, sizeof(ssid))))
		return -1;

	ssidlen = strlen((char *)ssid);

	if (WARN_ON(wifi_get_opclass_ht20(ifname, &opclass)))
		return -1;

	req_beacon = (struct wifi_beacon_req *) req;

	/* Minimal configuration - passive, all channels, all bssid and our SSID */
	req_beacon->oper_class = (uint8_t)opclass.g_opclass;
	req_beacon->mode = WIFI_BCNREQ_MODE_PASSIVE;
	req_beacon->duration = 100;
	memcpy(req_beacon->bssid, bssid, sizeof(bssid));

	/* Add SSID */
	req_beacon->variable[0] = WIFI_BCNREQ_SSID;
	req_beacon->variable[1] = ssidlen;
	memcpy(&req_beacon->variable[2], ssid, ssidlen);

	return hostapd_cli_iface_req_beacon(ifname, sta, req_beacon,
				sizeof(*req_beacon) + 2 + ssidlen);
}

int hostapd_cli_disconnect_sta(const char *ifname, uint8_t *sta, uint16_t reason)
{
	char cmd[1024] = { 0 };

	snprintf(cmd, sizeof(cmd), "deauthenticate " MACSTR, MAC2STR(sta));	/* Flawfinder: ignore */

	if (reason > 0)
		snprintf(cmd + strlen(cmd), sizeof(cmd), " reason=%d", reason);

	return hostapd_cli_set(ifname, cmd, true);
}

int hostapd_iface_block_sta(const char *ifname, uint8_t *sta, int block)
{
	char cmd[1024] = { 0 };

	if (block) {
		snprintf(cmd, sizeof(cmd), "raw DENY_ACL ADD_MAC " MACSTR, MAC2STR(sta));	/* Flawfinder: ignore */
	} else {
		snprintf(cmd, sizeof(cmd), "raw DENY_ACL DEL_MAC " MACSTR, MAC2STR(sta));	/* Flawfinder: ignore */
	}

	return hostapd_cli_set(ifname, cmd, true);
}

int hostapd_cli_sta_disconnect_ap(const char *ifname, uint32_t reason)
{
	/* reason parameter is skiped because wpa_supplicant command
	 * disconnect does not allow setup this parameter,
	 * it is hardcoded to 3.
	 */
	return wpa_cli_set(ifname, "disconnect", true);
}

static int get_neighbor_data(const char *ifname, uint8_t *bssid, struct nbr *nbr)
{
	struct nbr *nbrs;
	int ret = -1;
	int nr = 50;
	int i;

	nbrs = calloc(nr, sizeof(*nbr));
	WARN_ON(!nbrs);
	if (!nbrs)
		return -1;

	if (wifi_get_neighbor_list(ifname, nbrs, &nr)) {
		ret = -1;
		goto free;
	}

	if (WARN_ON(!nr)) {
		ret = -1;
		goto free;
	}

	for (i = 0; i < nr; i++) {
		if (!memcmp(bssid, nbrs[i].bssid, 6)) {
			*nbr = nbrs[i];
			ret = 0;
			break;
		}
	}

free:
	free(nbrs);
	return ret;
}

int hostapd_cli_iface_req_bss_transition(const char *ifname, unsigned char *sta,
					 int bsss_nr, struct nbr *bsss,
					 struct bss_transition_params *params)
{
	size_t buflen;
	size_t len;
	char *buf;
	int i;
	int ret;

	buflen = 256 + bsss_nr * 256;

	buf = malloc(buflen);
	WARN_ON(!buf);
	if (!buf)
		return -1;

	len = 0;
	len += snprintf(buf + len, buflen - len, "bss_tm_req " MACSTR " ", MAC2STR(sta));

	for (i = 0 ; i < bsss_nr; i++) {
		struct nbr nbr = {};

		/* Lookup nbr on neighbor list to get missing params */
		if (params->pref && get_neighbor_data(ifname, bsss[i].bssid, &nbr))
			libwifi_warn("[%s] Preferred BSSID " MACSTR " not on neighbor list!\n",
			             ifname, MAC2STR(bsss[i].bssid));

		if (!bsss[i].bssid_info && !nbr.bssid_info)
			libwifi_dbg("[%s] Missing bssid_info for BSSID " MACSTR "!\n",
			            ifname, MAC2STR(bsss[i].bssid));

		if (!bsss[i].reg && !nbr.reg)
			libwifi_dbg("[%s] Missing reg for BSSID " MACSTR "!\n",
			            ifname, MAC2STR(bsss[i].bssid));

		if (!bsss[i].channel && !nbr.channel)
			libwifi_dbg("[%s] Missing channel for BSSID " MACSTR "!\n",
			            ifname, MAC2STR(bsss[i].bssid));

		if (!bsss[i].phy && !nbr.phy)
			libwifi_dbg("[%s] Missing phy for BSSID " MACSTR "!\n",
			            ifname, MAC2STR(bsss[i].bssid));

		len += snprintf(buf + len, buflen - len,
				"neighbor=" MACSTR ",%u,%hhu,%hhu,%hhu ",
				MAC2STR(bsss[i].bssid),
				bsss[i].bssid_info ? bsss[i].bssid_info : nbr.bssid_info,
				bsss[i].reg ? bsss[i].reg : nbr.reg,
				bsss[i].channel ? bsss[i].channel : nbr.channel,
				bsss[i].phy ? bsss[i].phy : nbr.phy);
	}

	len += snprintf(buf + len, buflen - len,
			"pref=%hhu disassoc_imminent=%hhu ",
			params->pref, params->disassoc_imminent);

	if (params->valid_int)
		len += snprintf(buf + len, buflen - len,
				"valid_int=%hhu ", params->valid_int);

	if (params->dialog_token)
		len += snprintf(buf + len, buflen - len,
				"dialog_token=%u ", params->dialog_token);

	if (params->bss_term)
		len += snprintf(buf + len, buflen - len,
				"bss_term=0,%hu ", params->bss_term);
	if (params->disassoc_timer)
		len += snprintf(buf + len, buflen - len,
				"disassoc_timer=%hu ", params->disassoc_timer);
	if (params->abridged)
		len += snprintf(buf + len, buflen - len,
				"abridged=%hhu ", params->abridged);
	if (params->ess_term)
		len += snprintf(buf + len, buflen - len,
				"url=%s ", params->url);

	if (params->mbo.valid) {
		len += snprintf(buf + len, buflen - len,
				"mbo=%u:%u:%u", params->mbo.reason,
				params->mbo.reassoc_delay,
				params->mbo.cell_pref);
	}

	ret = hostapd_cli_set(ifname, buf, false);

	free(buf);

	return ret;
}

int hostapd_cli_mbo_disallow_assoc(const char *ifname, int reason)
{
	char cmd[1024] = { 0 };

	snprintf(cmd, sizeof(cmd), "set mbo_assoc_disallow %d", reason);

	return hostapd_cli_set(ifname, cmd, true);
}

int hostapd_cli_ap_get_state(const char *ifname, char *state, int state_size)
{
	char buf[1024] = { 0 };

	/* Get current state */
	if (WARN_ON(hostapd_cli_get(ifname, "status", buf, sizeof(buf))))
		return -1;
	return wpa_ctrl_get_param(buf, "state", state, state_size);
}

int hostapd_cli_ap_set_state(const char *ifname, bool up)
{
	char buf[1024] = { 0 };
	char state[128] = { 0 };
	int ret = 0;

	/* Get current state */
	if (WARN_ON(hostapd_cli_get(ifname, "status", buf, sizeof(buf))))
		return -1;
	if (WARN_ON(wpa_ctrl_get_param(buf, "state", state, sizeof(state))))
		return -1;

	if (up) {
		if (strcmp(state, "ENABLED") && strcmp(state, "DFS"))
			ret = hostapd_cli_set(ifname, "enable", true);
		if (strcmp(state, "DFS"))
			/* Enable beaconing */
			WARN_ON(hostapd_cli_set(ifname, "update_beacon", true));
	} else {
		if (strcmp(state, "DISABLED"))
			/* Disable beaconing */
			ret = hostapd_cli_set(ifname, "raw STOP_AP", true);
	}

	return ret;
}

int hostapd_cli_ap_set_qos_map(const char *ifname,
	struct dscp_pcp_map *map,
	struct dscp_exception *exception,
	int num_exceptions)
{
	char cmd[1024] = { 0 };
	int i;
	int ret;

	if (num_exceptions > 21) {
		libwifi_dbg("[%s] Number of DSCP exceptions exceeds the maximum\n",
		            ifname);
		return -1;
	}

	strcat(cmd, "set_qos_map_set ");
	for (i = 0; i < num_exceptions; ++i) {
		char buf[50];

		ret = snprintf(buf, sizeof(buf), "%s%d,%d",
			(i != 0) ? "," : "",
			exception->dscp, exception->output_pcp);
		if (ret <= 0 || ret >= sizeof(buf)) {
			libwifi_dbg("[%s] Failed to build up temporary buffer for "
			            "exceptions\n", ifname);
			return -1;
		}

		if (strlen(cmd) + strlen(buf) > sizeof(cmd)) {
			libwifi_dbg("[%s] Failed to build up command buffer for "
			            "for primary map\n", ifname);
			return -1;
		}

		strcat(cmd, buf); /* Flawfinder: ignore */
	}

	for (i = 0; i <= 7; ++i) {
		char buf[50];

		ret = snprintf(buf, sizeof(buf), "%s%d,%d",
		    (i != 0 || num_exceptions != 0) ? "," : "",
		    map->dscp_to_up[i].dscp_min,
		    map->dscp_to_up[i].dscp_max);
		if (ret <= 0 || ret >= sizeof(buf)) {
			libwifi_dbg("[%s] Failed to build up temporary buffer for "
			            "primary map\n", ifname);
			return -1;
		}

		if (strlen(cmd) + strlen(buf) > sizeof(cmd)) {
			libwifi_dbg("[%s] Failed to build up command buffer for "
			            "for primary map\n", ifname);
			return -1;
		}

		strcat(cmd, buf); /* Flawfinder: ignore */
	}

	return hostapd_cli_set(ifname, cmd, true);
}

int hostapd_cli_ap_send_qos_map_conf(const char *ifname,
	uint8_t *sta_mac)
{
	char cmd[128] = { 0 };

	snprintf(cmd, sizeof(cmd), "send_qos_map_conf " MACSTR, MAC2STR(sta_mac));	/* Flawfinder: ignore */
	return hostapd_cli_set(ifname, cmd, true);
}

int hostapd_cli_iface_add_vendor_ie(const char *ifname, struct vendor_iereq *req)
{
	char cmd[1024] = { 0 };
	char ie[512] = { 0 };
	size_t size;

	if (WARN_ON(!req->mgmt_subtype))
		return 0;

	size = req->ie.ie_hdr.len + sizeof(req->ie.ie_hdr);
	if (WARN_ON(size * 2 >= sizeof(ie)))
		return -1;

	btostr((uint8_t *) &req->ie, size, ie);
	snprintf(cmd, sizeof(cmd), "set vendor_elements %s", ie);

	return hostapd_cli_set(ifname, cmd, true);
}

int hostapd_cli_iface_del_vendor_ie(const char *ifname, struct vendor_iereq *req)
{
	char cmd[1024] = { 0 };

	snprintf(cmd, sizeof(cmd), "set vendor_elements \"\"");
	return hostapd_cli_set(ifname, cmd, true);
}

int hostapd_cli_is_wds_sta(const char *ifname, uint8_t *sta_mac, char *ifname_wds, size_t ifname_max)
{
	char cmd[64] = { 0 };
	char val[256] = { 0 };
	char buf[2048] = { 0 };
	int is_wds = 0;

	snprintf(cmd, sizeof(cmd), "sta " MACSTR, MAC2STR(sta_mac));	/* Flawfinder: ignore */
	if (hostapd_cli_get(ifname, cmd, buf, sizeof(buf)))
		return 0;

	if (hostapd_cli_iface_get_sta_flags(buf, val, sizeof(val)))
		return 0;

	if (strstr(val, "[WDS]") || strstr(val, "[MULTI_AP]"))
		is_wds = 1;

	if (!ifname_wds)
		return is_wds;

	if (wpa_ctrl_get_param(buf, "ifname_wds", val, sizeof(val))) {
		libwifi_warn("[%s] %s ifname_wds parameter not found in buffer [%s]!\n",
			ifname, __func__, buf);
		return is_wds;
	}

	if (WARN_ON((strlen(val) + 1) > ifname_max))
		return is_wds;

	snprintf(ifname_wds, ifname_max, "%s", val);

	return is_wds;
}

int hostapd_cli_get_oper_stds(const char *ifname, uint8_t *std)
{
	uint8_t beacon_ies[4096] = { 0 };
	size_t ies_len = sizeof(beacon_ies);
	int ret;

	if (WARN_ON(!std))
		return -EINVAL;

	ret = hostapd_cli_get_beacon_ies(ifname, beacon_ies, &ies_len);
	if (WARN_ON(ret)) {
		return ret;
	}

	ret = wifi_oper_stds_set_from_ie(beacon_ies, ies_len, std);
	if (WARN_ON(ret)) {
		return ret;
	}

	return 0;
}

int hostapd_cli_get_beacon(const char *ifname, uint8_t *beacon, size_t *beacon_len)
{
	char buf[4096] = { 0 };

	if (WARN_ON(hostapd_cli_get(ifname, "get beacon", buf, sizeof(buf))))
		return -1;

	return hostapd_cli_hexstr_to_bin(buf, beacon, beacon_len);
}

int hostapd_cli_get_beacon_ies(const char *ifname, uint8_t *beacon_ies, size_t *beacon_ies_len)
{
	char buf[4096] = { 0 };

	if (WARN_ON(hostapd_cli_get(ifname, "get_beacon ies", buf, sizeof(buf))))
		return -1;

	return hostapd_cli_hexstr_to_bin(buf, beacon_ies, beacon_ies_len);
}

int hostapd_ubus_iface_monitor_sta(const char *ifname, uint8_t *sta, struct wifi_monsta_config *cfg)
{
	char buf[256];

	libwifi_dbg("[%s] hostapd monitor sta " MACSTR " %s\n", ifname, MAC2STR(sta),
			cfg->enable ? "enable" : "disable");

	/* Today we enable probe-req monitor in hostapd - even STA isn't connected */
	chrCmd(buf, sizeof(buf), "ubus call hostapd.%s %s '{\"addr\":\"" MACSTR "\"}'",
			ifname, cfg->enable ? "sta_monitor_add" : "sta_monitor_del", MAC2STR(sta));

	if (cfg->enable)
		chrCmd(buf, sizeof(buf),
				"ubus call hostapd.%s event_mask_add '{\"event\":\"probe-req-filtered\"}'",
				ifname);

	return 0;
}

int hostapd_cli_probe_sta(const char *ifname, uint8_t *sta)
{
	char cmd[512] = { 0 };

	if (WARN_ON(!sta))
		return -1;

	snprintf(cmd, sizeof(cmd), "poll_sta " MACSTR, MAC2STR(sta));	/* Flawfinder: ignore */

	return hostapd_cli_set(ifname, cmd, true);
}

int hostapd_ubus_iface_get_monitor_sta(const char *ifname, uint8_t *sta, struct wifi_monsta *mon)
{
	char buf[1024];
	char format[1024];
	int rssi;
	int age;

	libwifi_dbg("[%s] get monitor sta " MACSTR "\n", ifname, MAC2STR(sta));

	chrCmd(buf, sizeof(buf), "ubus -S call hostapd.%s sta_monitor_get '{\"addr\":\"" MACSTR "\"}'",
			ifname, MAC2STR(sta));

	snprintf(format, sizeof(format), "{\"sta\":{\"macaddr\":\"" MACSTR "\",\"rssi\":%%d,\"age\":%%d}}", MAC2STR(sta));
	/* Format: {"sta":{"macaddr":"30:10:b3:6d:8d:ba","rssi":-29,"age":782}} */
	if (WARN_ON(sscanf(buf, format, &rssi, &age) != 2))	/* Flawfinder: ignore */
		return -1;

	memcpy(mon->macaddr, sta, 6);
	mon->rssi_avg = (int8_t)rssi;
	mon->last_seen = age;

	return 0;
}

int hostapd_ubus_iface_get_monitor_stas(const char *ifname, struct wifi_monsta *stas, int *num)
{
	char buf[1024];
	struct wifi_monsta *sta;
	char *line;
	char *key;
	char *p;
	int i;

	chrCmd(buf, sizeof(buf), "ubus call hostapd.%s sta_monitor_get", ifname);

	p = buf;
	i = 0;

	while ((line = strsep(&p, "\n"))) {
		if (WARN_ON(i >= *num))
			break;

		key = strstr(line, "\"macaddr\":");
		if (!key)
			continue;

		sta = &stas[i];
		if (sscanf(key, "\"macaddr\": \"%hhx:%hhx:%hhx:%hhx:%hhx:%hhx\"",
			   &sta->macaddr[0],
			   &sta->macaddr[1],
			   &sta->macaddr[2],
			   &sta->macaddr[3],
			   &sta->macaddr[4],
			   &sta->macaddr[5]) != 6)
			continue;
		i++;
	}

	*num = i;
	return 0;
}

static int
hostapd_ubus_iface_subscribe_unsubscribe_frame(const char *ifname, uint8_t type, uint8_t stype, int sub)
{
	char buf[1024];
	char evt[64];
	char *res;

	if (type != WIFI_FRAME_MGMT)
		return -ENOTSUP;

	switch (stype) {
	case WIFI_FRAME_ASSOC_REQ:
		snprintf(evt, sizeof(evt), "raw-assoc");
		break;
	case WIFI_FRAME_REASSOC_REQ:
		snprintf(evt, sizeof(evt), "raw-reassoc");
		break;
	case WIFI_FRAME_PROBE_REQ:
		snprintf(evt, sizeof(evt), "raw-probe-req");
		break;
	case WIFI_FRAME_DISASSOC:
		snprintf(evt, sizeof(evt), "raw-disassoc");
		break;
	case WIFI_FRAME_AUTH:
		snprintf(evt, sizeof(evt), "raw-auth");
		break;
	case WIFI_FRAME_DEAUTH:
		snprintf(evt, sizeof(evt), "raw-deauth");
		break;
	case WIFI_FRAME_ACTION:
		snprintf(evt, sizeof(evt), "raw-action");
		break;
	default:
		return -ENOTSUP;
	}

	chrCmd(buf, sizeof(buf), "ubus call hostapd.%s %s '{\"event\":\"%s\"}'",
		ifname, sub ? "event_mask_add" : "event_mask_del", evt);

	chrCmd(buf, sizeof(buf), "ubus -S call hostapd.%s event_mask_get", ifname);
	res = strstr(buf, evt);

	if (sub && !res)
		return -1;

	if (!sub && res)
		return -1;

	return 0;
}

int hostapd_ubus_iface_subscribe_frame(const char *ifname, uint8_t type, uint8_t stype)
{
	return hostapd_ubus_iface_subscribe_unsubscribe_frame(ifname, type, stype, 1);
}

int hostapd_ubus_iface_unsubscribe_frame(const char *ifname, uint8_t type, uint8_t stype)
{
	return hostapd_ubus_iface_subscribe_unsubscribe_frame(ifname, type, stype, 0);
}

int hostapd_cli_iface_chan_switch(const char *ifname, struct chan_switch_param *param)
{
	char cmd[1024] = { 0 };

	/* <cs_count> <freq> [sec_channel_offset=] [center_freq1=] [center_freq2=] [bandwidth=] [blocktx] [ht|vht] */
	snprintf(cmd, sizeof(cmd), "chan_switch %d %d", param->count, param->freq);
	if (param->sec_chan_offset)
		snprintf(cmd + strlen(cmd), sizeof(cmd) - strlen(cmd), " sec_channel_offset=%d", param->sec_chan_offset);
	if (param->cf1)
		snprintf(cmd + strlen(cmd), sizeof(cmd) - strlen(cmd), " center_freq1=%d", param->cf1);
	if (param->cf2)
		snprintf(cmd + strlen(cmd), sizeof(cmd) - strlen(cmd), " center_freq2=%d", param->cf2);
	if (param->bandwidth)
		snprintf(cmd + strlen(cmd), sizeof(cmd) - strlen(cmd), " bandwidth=%d", param->bandwidth);
	if (param->blocktx)
		snprintf(cmd + strlen(cmd), sizeof(cmd) - strlen(cmd), " blocktx");
	if (param->ht)
		snprintf(cmd + strlen(cmd), sizeof(cmd) - strlen(cmd), " ht");
	if (param->vht)
		snprintf(cmd + strlen(cmd), sizeof(cmd) - strlen(cmd), " vht");
	if (param->he)
		snprintf(cmd + strlen(cmd), sizeof(cmd) - strlen(cmd), " he");
	if (param->eht)
		snprintf(cmd + strlen(cmd), sizeof(cmd) - strlen(cmd), " eht");
	if (param->auto_ht)
		snprintf(cmd + strlen(cmd), sizeof(cmd) - strlen(cmd), " auto-ht");

	return hostapd_cli_set(ifname, cmd, true);
}

int hostapd_cli_get_security_cap(const char *name, uint32_t *sec) {
	char buf[4096] = { 0 };
	char val[256] = { 0 };
	int wep = 0, sae = 0;
	uint32_t kf = 0;
	uint32_t cf = 0;
	int buildin_ft = 0;

	if (WARN_ON(!sec))
		return -EINVAL;

	*sec = 0;
	*sec |= BIT(WIFI_SECURITY_NONE);

	if (Cmd(buf, sizeof(buf), "hostapd -v11r") == 0)
		buildin_ft = 1;

	wep = Cmd(buf, sizeof(buf), "hostapd -vwep");

	if (wep == 0)
		*sec |= BIT(WIFI_SECURITY_WEP128);

	sae = Cmd(buf, sizeof(buf), "hostapd -vsae");
	if (sae == 0) {
		*sec |= BIT(WIFI_SECURITY_WPA3);
		*sec |= BIT(WIFI_SECURITY_WPA3PSK);
		if (buildin_ft)
			*sec |= BIT(WIFI_SECURITY_FT_WPA3PSK);
	}

	if (WARN_ON(hostapd_cli_get(name, "raw STATUS-DRIVER", buf, sizeof(buf))))
		return -1;

	if (wpa_ctrl_get_param(buf, "capa.key_mgmt", val, sizeof(val))) {
		libwifi_warn("%s capa.key_mgmt parameter not found in buffer [%s]!\n",
			__func__, buf);
		return -1;
	}

	if (sscanf(val, "%x", &kf) != 1) {
		libwifi_warn("Failed to parse capa.key_mgmt parameter\n");
		return -1;
	}

	if (wpa_ctrl_get_param(buf, "capa.enc", val, sizeof(val))) {
		libwifi_warn("%s capa.enc parameter not found in buffer [%s]!\n",
			__func__, buf);
		return -1;
	}

	if (sscanf(val, "%x", &cf) != 1) {
		libwifi_warn("Failed to parse capa.enc parameter\n");
		return -1;
	}

	if (wep == 0 && (cf & ENC_WEP40))
		*sec |= BIT(WIFI_SECURITY_WEP64);

	if (kf & KEY_MGMT_WPA)
		*sec |= BIT(WIFI_SECURITY_WPA);

	if (kf & KEY_MGMT_WPA2)
		*sec |= BIT(WIFI_SECURITY_WPA2);

	if (kf & KEY_MGMT_WPA_PSK)
		*sec |= BIT(WIFI_SECURITY_WPAPSK);

	if (kf & KEY_MGMT_WPA2_PSK) {
		*sec |= BIT(WIFI_SECURITY_WPA2PSK);
		if (buildin_ft)
			*sec |= BIT(WIFI_SECURITY_FT_WPA2PSK);
	}

	if (kf & KEY_MGMT_WPA_NONE)
		*sec |= BIT(WIFI_SECURITY_NONE);

	if (kf & KEY_MGMT_FT_PSK)
		*sec |= BIT(WIFI_SECURITY_FT_WPA2PSK);

	return 0;
}

static int parse_mac_address(char* buf, char *mac, size_t size)
{
	char *start, *pos;
	size_t len = 0;

	start = pos = buf;
	memset(mac, 0, size);

	while (*pos != '\0' && *pos != '\n') {
		pos++;
		len++;
	}

	*pos = '\0';

	if (len && len < size - 1)
		strncpy(mac, start, len);

	return 0;
}

static int hostapd_cli_get_wds_params(const char* ifname, char* parent, uint8_t *oper_std) {
	char buf[2048] = { 0 };
	char cmd[64] = { 0 };
	char path[256] = { 0 };
	char phy[32] = { 0 };
	int ret = -1;
	struct dirent *p;
	char ifname_wds[16] = { 0 };
	char sta_mac[32] = { 0 };
	DIR *d;
	int i = 0;

	if (WARN_ON(nlwifi_get_phyname(ifname, phy)))
		return ret;

	snprintf(path, sizeof(path), "/sys/class/ieee80211/%s/device/net", phy);

	if (WARN_ON(!(d = opendir(path))))
		return ret;

	while ((p = readdir(d)) && (i < WIFI_IFACE_MAX_NUM)) {
		char *ctrl_iface = NULL;

		if (!strcmp(p->d_name, "."))
			continue;
		if (!strcmp(p->d_name, ".."))
			continue;

                ret = wifi_get_ctrl_interface(p->d_name, &ctrl_iface);
                if (ret)
                        continue;

		memset(buf, 0, sizeof(buf));
		ret = hostapd_cli_get(ctrl_iface, "raw STA-FIRST", buf, sizeof(buf));

		if (ret || strlen(buf) == 0 || strstr(buf, "FAIL")) {
			free(ctrl_iface);
			continue;
		}

		memset(ifname_wds, 0, sizeof(ifname_wds));
		ret = wpa_ctrl_get_param(buf, "ifname_wds", ifname_wds, sizeof(ifname_wds));
		if (!ret) {
			if (strcmp(ifname_wds, ifname) == 0) {
				if (parent) {
					memset(parent, 0, 16);
					memcpy(parent, p->d_name, 15);
				}

				if (oper_std) {
					char flags[256] = { 0 };
					*oper_std = 0;

					if (!hostapd_cli_iface_get_sta_flags(buf, flags, sizeof(flags))) {
						if (strstr(flags, "[HT]"))
							*oper_std |= WIFI_N;
						if (strstr(flags, "[VHT]"))
							*oper_std |= WIFI_AC;
						if (strstr(flags, "[HE]"))
							*oper_std |= WIFI_AX;
						if (strstr(flags, "[EHT]"))
							*oper_std |= WIFI_BE;
					}
				}

				closedir(d);
				free(ctrl_iface);
				return 0;
			}
		}

		parse_mac_address(buf, sta_mac, sizeof(sta_mac));
		snprintf(cmd, sizeof(cmd), "raw STA-NEXT %s", sta_mac);

		memset(buf, 0, sizeof(buf));
		while (hostapd_cli_get(ctrl_iface, cmd, buf, sizeof(buf)) == 0) {
			if (strlen(buf) == 0 || strstr(buf, "FAIL"))
				break;

			memset(ifname_wds, 0, 16);
			ret = wpa_ctrl_get_param(buf, "ifname_wds", ifname_wds, sizeof(ifname_wds));
			if (!ret) {
				if (strcmp(ifname_wds, ifname) == 0) {
					if (parent) {
						memset(parent, 0, 16);
						memcpy(parent, p->d_name, 15);
					}

					if (oper_std) {
						char flags[256] = { 0 };
						*oper_std = 0;

						if (!hostapd_cli_iface_get_sta_flags(buf, flags, sizeof(flags))) {
							if (strstr(flags, "[HT]"))
								*oper_std |= WIFI_N;
							if (strstr(flags, "[VHT]"))
								*oper_std |= WIFI_AC;
							if (strstr(flags, "[HE]"))
								*oper_std |= WIFI_AX;
							if (strstr(flags, "[EHT]"))
								*oper_std |= WIFI_BE;
						}
					}

					closedir(d);
					free(ctrl_iface);
					return 0;
				}
			}

			parse_mac_address(buf, sta_mac, sizeof(sta_mac));
			snprintf(cmd, sizeof(cmd), "raw STA-NEXT %s", sta_mac);
			memset(buf, 0, sizeof(buf));
		}

		free(ctrl_iface);
		i++;
	}
	closedir(d);
	return -1;
}

int hostapd_cli_get_4addr_parent(const char* ifname, char* parent) {
	return hostapd_cli_get_wds_params(ifname, parent, NULL);
}

int hostapd_cli_get_wds_oper_stds(const char *ifname_wds, uint8_t *std)
{
	return hostapd_cli_get_wds_params(ifname_wds, NULL, std);
}

int hostapd_cli_rrm_lm_req(const char *ifname, uint8_t *sta) {
	char cmd[64] = { 0 };

	snprintf(cmd, sizeof(cmd), "RRM_LM_REQ " MACSTR, MAC2STR(sta)); /* Flawfinder: ignore */

	return hostapd_cli_set(ifname, cmd, false);
}

