/*
 * agent.h - wifiagent header
 *
 * Copyright (C) 2020 IOPSYS Software Solutions AB. All rights reserved.
 *
 * Author: anjan.chanda@iopsys.eu
 *
 */

#ifndef AGENT_H
#define AGENT_H

#include <timer_impl.h>
#include <stdint.h>
#include <time.h>
#include <cmdu_ackq.h>
#include <easy/hlist.h>
#include <easy/if_utils.h>
#include <i1905_wsc.h>
#include <easymesh.h>
#include <libubox/list.h>
#include <libubus.h>
#include <map_module.h>
#include <stdbool.h>
#include <wifidefs.h>


#include "utils/allmac.h"
#include "utils/debug.h"
#include "utils/libcrypto.h"
#include "autoconfig.h"
#include "config.h"
#include "dynbh/dynbh.h"
#include "nl.h"
#include "scan.h"
#include "wifi.h"
#include "wifi_opclass.h"
#include "wifi_scanresults.h"
#if (EASYMESH_VERSION >= 3)
#ifdef USE_LIBDPP
#ifdef ZEROTOUCH_DPP
#include "zt.h"
#endif
#endif
#endif

#if (EASYMESH_VERSION >= 6)
struct bsta_mld;
#endif
struct blob_attr;

#ifdef VENDOR_EXTENSION
struct registered_plugin {
	void *plugin;
	struct list_head list;
};

extern int load_extensions(int argc, char *argv[], struct list_head *plugins);
extern int unload_extensions(struct list_head *plugins);

int CMDU_HOOK(void *app, uint16_t type, uint16_t mid, uint8_t *src,
			   void *cmdu, size_t cmdulen, int hooktype);

#else

static inline int load_extensions(int argc, char *argv[], struct list_head *plugins)
{
	return 0;
}

static inline int unload_extensions(struct list_head *plugins)
{
	return 0;
}

static inline int CMDU_HOOK(void *app, uint16_t type, uint16_t mid, uint8_t *src,
			    void *cmdu, size_t cmdulen, int hooktype)
{
	return 0;
}
#endif	/* VENDOR_EXTENSION */


#define IFACE_TIMEOUT 1
#define MAPAGENT_OBJECT		"map.agent"
#define MAPAGENT_DBG_OBJECT	"map.agent.dbg"
#define MAP_UPLINK_PATH 	"/var/run/multiap/multiap.backhaul"

#define MAX_STA 32
#define MAX_AKM_SUITES	16

#define AGENT_RECONFIG_REASON_AP_AUTOCONF	0x01
#define AGENT_RECONFIG_REASON_VLAN_SETUP	0x02
#define AGENT_RECONFIG_REASON_VLAN_TEARDOWN	0x04
#define AGENT_RECONFIG_REASON_AP_CRED		0x08
#if (EASYMESH_VERSION >= 6)
#define AGENT_RECONFIG_REASON_PUNCT_BITMAP	0x10
#define AGENT_RECONFIG_REASON_MLD_ID		0x20
#endif

#define AGENT_AP_RECONFIG_SSID			0x01
#define AGENT_AP_RECONFIG_PSK			0x02

#define AGENT_MAX_CMDU 0x10000

/* defined in agent/main.c */
extern const char *ubus_socket;
extern const char *pidfile;

/* struct node - maps to a 1905 device */
struct node {
	uint8_t alid[6];
	uint8_t map_profile;		   /** profile info of node */

	struct agent *agent;
	struct list_head list;
};

/** TODO - refine - preferred neighbor candiate for a STA */
struct pref_neighbor {
	uint8_t bssid[6];
	int8_t rssi;
	int8_t rsni;
	uint32_t bssid_info;
	uint8_t reg;
	uint8_t channel;
	uint8_t phy;
	uint8_t reason;		/* MBO Transition Reason Code (Profile-2) */
	struct list_head list;
	/* to calculate preference */
	uint32_t max_bwrate;	/* max possible downlink phy rate */
	uint32_t est_bwrate;	/* estimated downlink phy rate */
	uint32_t ch_util;	/* channel utilization percent */
	int num_stas;           /* number of stas connected */
	uint32_t flags;         /* struct neighbor flags */
};

struct sta_neighbor {
	struct sta_nbr nbr;
	struct timespec tsp;
	struct list_head list;
};

struct sta_channel_report {
	uint8_t opclass;
	uint8_t num_channel;
	uint8_t channel[128];
};

#define MAX_UNASSOC_STAMACS 10
struct unassoc_sta_metric {
	uint8_t channel;
	uint8_t num_sta;
	struct {
		uint8_t macaddr[6];
	} sta[MAX_UNASSOC_STAMACS];
};

struct action_frame_container {
	uint8_t frame_control[2];
	uint8_t duration[2];
	uint8_t dst[6];
	uint8_t src[6];
	uint8_t bssid[6];
	uint8_t seq_num[2];
	uint8_t category_code;
	union {
		/* Category 05: Radio measurement*/
		struct {
			uint8_t action_code;
			uint8_t dialog_token;
			uint8_t tags[];
		} __attribute__((packed)) rrm;
	} u;
} __attribute__((packed));

struct sta_error_response {
	uint8_t sta_mac[6];
	uint8_t response;
};

struct wifi_assoc_frame {
	uint16_t len;
	uint8_t macaddr[6];
	uint8_t *frame;
	struct list_head list;
};

struct wifi_bss_element {
	timestamp_t tsp;
	uint8_t bssid[6];
	char ssid[33];
	bool enabled;
	uint32_t uptime;
	uint64_t tx_packets;
	uint64_t rx_packets;
	uint64_t tx_error_packets;
	uint64_t tx_ucast_bytes;
	uint64_t rx_ucast_bytes;
	uint64_t tx_mcast_bytes;
	uint64_t rx_mcast_bytes;
	uint64_t tx_bcast_bytes;
	uint64_t rx_bcast_bytes;
	uint8_t is_ac_be;
	uint8_t is_ac_bk;
	uint8_t is_ac_vo;
	uint8_t is_ac_vi;
	uint8_t est_wmm_be[3];
	uint8_t est_wmm_bk[3];
	uint8_t est_wmm_vi[3];
	uint8_t est_wmm_vo[3];
#if (EASYMESH_VERSION >= 6)
	uint8_t eht_oper_caps[10];
#endif
	uint32_t num_stations;
	uint8_t utilization;

	//struct wifi_sta_element *stalist; /* TODO */
};

#define STA_PERIODIC_RUN_INTERVAL       1000       /** 1 sec */
#define STA_NBR_REFRESH_CNT             120        /** x5 = 10 mins in auto */
#define STA_NBR_LIST_INTERVAL           2000  /** fetch bcn rpt after 2 secs of requesting*/
#define STA_NBR_AGEOUT_INTERVAL         10000   /** sta bcnreport is dynamic; expires quickly */
#define STA_STEERED_DEAUTH_INTERVAL     10   /** 10 sec after successful sterring sta will be cleaned up */

typedef struct cntlr_preflist {
	int num;
	struct pref_neighbor pref[];
} cntlr_preflist_t;

enum sta_steer_policy {
	STA_STEER_ALLOWED  = 1 << 0,    /** steer allowed by agent (default set) */
	STA_STEER_MANDATE  = 1 << 1,    /** steer immediately as instructed by cntlr */
	STA_STEER_OPPORTUNITY = 1 << 2, /** steer at the next opportunity within a time-window */
};

enum sta_disallowed {
	STA_DISALLOWED_NONE = 0,
	STA_DISALLOWED_BTM = 1 << 0,
	STA_DISALLOWED_LOCAL = 1 << 1,
};

#define sta_steer_allowed(p)	(!!((p) & STA_STEER_ALLOWED))

struct sta_bcn_req {
	struct netif_ap *ap;
	uint8_t sta_addr[6];
	uint8_t opclass;
	uint8_t channel;
	uint8_t bssid[6];
	uint8_t reporting_detail;
	uint8_t ssid_len;
	char ssid[33];
	uint8_t num_element;
	uint8_t element[128];
};


/* enum netif_type { IFTYPE_WIRED, IFTYPE_WIFI, IFTYPE_UNKNOWN }; */

#define BSS_REFRESH_INTERVAL		60000       /** 1 min */

#define NBR_REFRESH_INTERVAL		600000      /** 10 mins */
#define NBR_AGEOUT_INTERVAL		1800000U    /** not dynamic; so 30mins is good */
#define NBR_SCAN_CNT			60          /** 1 hr */

struct neighbor {
	struct nbr nbr;
	struct timespec tsp;	/* last seen tsp */
	uint8_t radio_mac[6];	/* Radio Unique Identifier */
	struct list_head list;
#define NBR_FLAG_DRV_UPDATED     0x1
	uint32_t flags;
};

struct restrict_sta_entry {
	uint8_t macaddr[6];
	uint8_t bssid[6];
	struct agent *agent;
	uint8_t mode;
	atimer_t restrict_timer;
	struct list_head list;
};

/* fronthaul wifi (ap) interface */
struct netif_ap {
	char ifname[16];
	int channel;
	int bandwidth;
	uint8_t bssid[6];
	uint32_t obj_id;
	char ssid[33];
	char standard[32];
	char radio_name[16];
	bool enabled;
	bool torndown;
	int bssload;
	struct netif_apcfg *cfg;
	int nbr_scan;
	enum wifi_band band;
	atimer_t nbr_timer; /** refresh neighbor list timer */
	atimer_t bss_timer; /** refresh own bss timer */

	/* Channel utilization threshold timer */
	atimer_t util_threshold_timer;

	struct list_head restrict_stalist;  /* list of restrict_sta_entry */
	struct list_head nbrlist;
	int nbr_nr;
	uint16_t max_sta; /* max number of stations: 0 - unlimited */
	uint16_t num_sta;
	bool sent_assocnotif;
	struct list_head stalist;
	struct agent *agent;

	/* AP TLV information, move out of netif_ap? */
	uint8_t rx_spatial_streams;
	uint8_t tx_spatial_streams;

	/* previous rcpi threshold value */
	uint8_t prev_rcpi;

	/* previous channel utilization threshold value */
	uint8_t prev_util;

	uint8_t reconfig_reason;
#if (EASYMESH_VERSION >= 6)
	bool is_affiliated_ap;
	char mld_ifname[16];
	uint32_t mlo_link_id;
	uint8_t mld_macaddr[6];
#endif
	struct wifi_bss_element bss;

	struct list_head list;
};

enum bk_steer_trigger {
	BK_STEER_MANDATE,
	BK_STEER_LOCAL
};

/* backhaul wifi (sta) interface */
struct netif_bk {
	char ifname[16];
	bool enabled;
	int channel;
	uint8_t macaddr[6];
	uint8_t bssid[6];
	char ssid[33];
	bool connected;
	bool wps_active;
	uint32_t obj_id;
	struct netif_bkcfg *cfg;

#if (EASYMESH_VERSION > 2)
	int dpp_ch_chirp_cnt;
	int dpp_ch_chirp_idx;
	struct dpp_chirp *dpp_cfg;
#endif
#if (EASYMESH_VERSION >= 6)
	bool is_affiliated_sta;
	char mld_ifname[16];
	uint32_t mlo_link_id;
	uint8_t mld_macaddr[6];
#endif
	/* enum netif_type iftype; */
	struct agent *agent;
	struct {
		uint8_t prev_bssid[6];
		uint8_t target_bssid[6];
		uint16_t mid;
		char ul_ifname[16];
		bool expired;
		enum bk_steer_trigger trigger;
		int reason;
	} bsta_steer;
	atimer_t steer_timeout;
	struct timespec cac_start;
	int cac_time;

	uint32_t total_connect_time; /* time used as uplink (seconds) */
	struct timespec connect_t;
	struct timespec disconnect_t;

#define BSTA_BLACKLIST_MAX_NUM 16
	int num_blacklist_bssids;
	uint8_t blacklist_bssid[16][6];
};


enum rcpi_state {
	RCPI_STATE_UNKNOWN,
	RCPI_STATE_LOW,
	RCPI_STATE_HIGH,
};

struct sta {
	uint8_t macaddr[6];
	uint8_t bssid[6];
#if (EASYMESH_VERSION >= 6)
	bool is_affiliated_sta;
	uint8_t mlo_link_id;
	uint8_t mld_macaddr[6];
	uint8_t ruid[6];
#endif
	uint32_t caps;                  /** capability bitmap */
	int rssi[WIFI_NUM_ANTENNA];     /** rssi of last received pkt */
#define MAX_RSSI_MEAS 2             /** must be >= 2: current and previous measurement alway required */
	int8_t rssi_avg[MAX_RSSI_MEAS]; /** average RSSI history FIFO buffer */
	uint8_t num_rssi;               /** number of avg RSSI samples stored in FIFO buffer */
#define NUM_RSSI_REP_TRIG 3         /** number of consecutive avg RSSI measurements below/above trigger needed for report */
	uint8_t num_rssi_rep;           /** number of consecutive avg RSSI measurements below/above trigger observed */
	enum rcpi_state rep_rcpi_state; /** toggled when STA Metrics Report sent due to crossing RCPI Threshold (low/high) */
	uint64_t connected_ms;          /** number of msecs connected */
	struct timespec last_update;
	uint32_t tx_rate;		/** tx-rate in kbps */
	uint32_t rx_rate;		/** rx-rate in kbps */
	uint64_t tx_bytes;
	uint64_t rx_bytes;
	uint64_t tx_pkts;
	uint64_t rx_pkts;
	uint32_t tx_fail_pkts;
	uint32_t rx_fail_pkts;
	uint32_t rtx_pkts;         /** total retransmitted packets */
	int tx_thput;              /** estimate based on tx-bytes per sec */
	int rx_thput;              /** rx-bytes per sec */
	int est_rx_thput;
	int est_tx_thput;
	int tx_airtime;		   /** tx airtime in ms */
	int rx_airtime;            /** rx airtime in ms */
	int stale_stats_cnt;       /** number of times same stats obtained */
#define STEER_BTM	0x10
	int steer_ready;                    /** steer when reached this state */
	uint32_t steer_policy;              /** steer policies, STA_STEER_* */
	int steer_btm_cnt;                  /** num times tried to BTM-steer STA */
	int steer_secs;                     /** num secs STA adjudged as steering candidate */
	struct list_head rulelist;          /** steering rule list */
	uint32_t steer_opportunity_tmo;     /** steer op timeout in msec */
	struct timespec steer_opportunity;  /** steer opportunity time window */
	atimer_t sta_steer_timer;           /** steer opportunity timer */
	atimer_t sta_steer_deauth_timer;    /** time to deauth sta left after successful ster */
	int ref;                            /** ref counting purpose */
	int inform_leaving;                 /** flag indicating leaving BSS */
	bool wait_for_cntlr_nbr;            /** awaiting pref nbr from cntlr */
	bool supports_bcnreport;            /** supports 11K beacon reporting */
	uint8_t rrm_mode;                   /** bitmap for ACTIVE/PASSIVE/TABLE */
	atimer_t sta_timer;                 /** periodic run */
	atimer_t sta_bcn_req_timer;         /** enqueue bcn requests */
	struct list_head sta_nbrlist;       /** neighbor BSSs as seen by this STA */
	struct list_head pref_nbrlist;      /** neighbors arranged by preference */
	cntlr_preflist_t *cntlr_preflist;   /** neighbors preferred by controller */
	int sta_nbr_nr;                     /** number of neighbor BSSs */
	int sta_bcn_req_nr;                 /** num of bcn requests in queue */
	struct sta_bcn_req bcn_req_queue[16]; /** beacon request queue */
	struct agent *agent;
	struct list_head list;              /** next STA in the list */
	struct wifi_assoc_frame *assoc_frame;
};


/* wds wifi (ap vlan) interface */
struct netif_wds {
	char ifname[16];
	struct list_head list;
};

#define WIFI_DEVICE_MAX_NUM	4
#define WIFI_IFACE_MAX_NUM	16

enum wifi_iface_mode {
	WIFI_IFACE_AP,
	WIFI_IFACE_BK,
#if (EASYMESH_VERSION >= 6)
	WIFI_IFACE_AFFILIATED_AP,
	WIFI_IFACE_AFFILIATED_BK,
	WIFI_IFACE_AFFILIATED_STA,
	WIFI_IFACE_AP_MLO,
	WIFI_IFACE_AP_BSTA
#endif
};

struct wifi_opclass_current_element {
	timestamp_t tsp;
	uint8_t id;
	uint8_t channel;
	int8_t txpower;
};

struct wifi_sta_element {
	timestamp_t tsp;
	uint8_t macaddr[6];
	uint32_t dl_rate;             /* latest data rate in Kbps: ap -> sta */
	uint32_t ul_rate;             /* latest data rate in Kbps: sta -> ap */
	unsigned long ul_utilization; /* time in msecs for receive from sta */
	unsigned long dl_utilization; /* time in msecs for transmit to sta */
	uint32_t dl_est_thput;        /* in Mbps */
	uint32_t ul_est_thput;        /* in Mbps */
	int8_t rssi;                  /* average; in dBm */
	uint32_t conn_time;           /* in secs since last associated */
	uint64_t tx_bytes;            /* transmit bytes count: ap -> sta */
	uint64_t rx_bytes;            /* receive bytes count: sta -> ap */
	uint32_t tx_pkts;
	uint32_t rx_pkts;
	uint32_t tx_errors;
	uint32_t rx_errors;
	uint32_t rtx_pkts;            /* total retransmitted packets */
	struct ip_address ipaddr;
	char hostname[128];
	uint8_t num_bcn_reports;
	uint8_t *bcn_reportlist;      /* list of beacon reports */
};

#define MIN_SCAN_ITV_SEC 3 /* 3 sec */

struct wifi_backhaul_element {
	uint8_t macaddr[6];
};

struct wsc_ext {
#if VENDOR_EXTENSION
	bool enabled;
	char bridge[16];
#endif
#define VEN_IES_MAX 16
	uint8_t num_ven_ies;
	struct wsc_vendor_ie ven_ies[VEN_IES_MAX];
};

struct wifi_cac_request {
	uint8_t channel;
	uint8_t opclass;
	enum wifi_bw bw;
	uint8_t ctrl_channel;
	enum wifi_cac_method method;
	uint8_t action;

	bool report_failed;
	uint8_t report_failed_channel;
	uint8_t report_failed_opclass;
	uint8_t report_failed_status;

	struct timespec time;
};

#if (EASYMESH_VERSION >= 6)
struct wifi7_radio_capabilities {
	uint8_t max_ap_links;
	uint8_t max_bsta_links;
	uint8_t ap_ttlm;
	uint8_t bsta_ttlm;

	bool sta_non_mlo_support;
	bool ap_str_support;
	bool ap_nstr_support;
	bool ap_emlsr_support;
	bool ap_emlmr_support;

	bool bsta_str_support;
	bool bsta_nstr_support;
	bool bsta_emlsr_support;
	bool bsta_emlmr_support;

	int ap_str_freqsep;
	int ap_emlsr_freqsep;
	int ap_emlmr_freqsep;

	int bsta_str_freqsep;
	int bsta_emlsr_freqsep;
	int bsta_emlmr_freqsep;

};
#endif

struct wsc_metadata {
	char manufacturer[65];          /* with terminating '\0' */
	char model_name[33];
	char device_name[33];
	char model_number[33];
	char serial_number[33];
	uint8_t device_type[8];         /* <category>0050F204<subcategory> */
};

#define SHA256_LENGTH 32
struct wifi_radio_element {
	char name[16];
	uint8_t macaddr[6];
	uint8_t country_code[2];
	uint32_t obj_id;
	enum wifi_band band;
	bool enabled;
	bool acs_enabled;
	int anpi;
	uint8_t total_utilization;   /** in %age, linearly scaled 0..255 */
	uint8_t tx_utilization;
	uint8_t rx_utilization;
	uint8_t other_utilization;
	uint8_t rx_streams;
	uint8_t tx_streams;

	uint8_t current_opclass;
	uint8_t current_channel;
	uint8_t current_txpower_percent;
	uint32_t current_bandwidth;
	bool cac_required;

	uint8_t transmit_power_limit; /* set in the channel selection message */
	uint8_t max_bss;

	uint32_t num_curr_opclass;
	//uint32_t num_scanresult; /* scanresults->entry_num */

	uint64_t tx_bytes;
	uint64_t tx_packets;
	uint64_t tx_error_packets;
	uint64_t tx_dropped_packets;
	uint64_t rx_bytes;
	uint64_t rx_packets;
	uint64_t rx_error_packets;
	uint64_t rx_dropped_packets;
	uint64_t rx_plcp_error_packets;
	uint64_t rx_fcs_error_packets;
	uint64_t rx_mac_error_packets;
	uint64_t rx_unknown_packets;

	struct wifi_backhaul_element bksta;

	/* Device reported opclass */
	struct wifi_radio_opclass opclass;

	/* Controller requested opclass */
	struct wifi_radio_opclass req_opclass;

	/* Scan results to be reported to the controller */
	struct wifi_scanres_element *scanlist;

	/* Device reported scanresults */
	struct wifi_scanresults scanresults;

	/* List used to keep track of unassociated STAs with
	 * their monitor status and current RCPI measurements.
	 */
	struct list_head unassocstalist; /* list of wifi_unassoc_sta_element */
#define MAX_UNASSOC_STA 32 /* max number of unassociated STAs for monitoring */
	uint8_t num_unassoc_sta;

	/** AP-Autoconfig */
	enum autocfg_state state;
	bool dedicated_backhaul;
	struct wsc_data autconfig;
	uint16_t mid;
	uint16_t wsc_mid;
	uint16_t renew_mid; /* debug purposes */

	/* radio scan state */
	enum wifi_scan_state scan_state;

	struct {
		bool opclass_preferences;
	} post_scan_action;

	/* wps metadata */
	uint8_t uuid[16];
	char vendor[65];

	bool report_oper_channel;
	bool local_acs_enabled;

	uint8_t reported_channel;
	uint32_t reported_bandwidth;

	struct agent *agent;
	uint32_t cac_methods;			/**< bitmap of wifi_cac_method */
	struct wifi_cac_request cac_request;
	atimer_t preference_report_timer;
	/* Unassociated station measurement timer */
	atimer_t unassoc_sta_meas_timer;
#if (EASYMESH_VERSION >= 6)
	struct wifi7_radio_capabilities wifi7_caps;
#endif

	struct agent_wifi_caps {
		struct wifi_caps wifi_caps;

		struct tlv_ap_ht_cap ht_cap;
		struct tlv_ap_vht_cap vht_cap;
		struct {
			uint8_t mcs_len;
			uint8_t mcs[12];
			uint8_t cap[2];
		} he_cap;

		struct {
			uint8_t caps;
			uint8_t mcs[12];
			uint8_t mcs_len;
			uint8_t beamform_caps;
			uint8_t max_mu_mimo_users;
			uint8_t max_dl_ofdma_users;
			uint8_t max_ul_ofdma_users;
			uint8_t other_caps;
		} wifi6_cap;
	} ap_caps;

	struct agent_wifi_caps sta_caps;

	uint8_t num_ap_akms;
	struct agent_wifi_akm {
		uint8_t oui[3];
		uint8_t type;
	} ap_akms[MAX_AKM_SUITES];

	uint8_t num_sta_akms;
	struct agent_wifi_akm sta_akms[MAX_AKM_SUITES];

	struct wifi_radio_diagnostic diag;

	/* Scan request data recorded for this device */
	struct wifi_scan_request_radio scan_req;
	/* 5 minutes available for scan */
	atimer_t available_scan_timer;
	/* Last scan request timestamp */
	struct timespec last_scan_tsp;

	struct list_head aplist;
	bool has_bsta;
	struct netif_bk bk;
	struct list_head list;
};

struct wifi_sta_steer_list {
	uint8_t sta_mac[6];
	uint8_t complete;
};

/** struct agent - wifi agent */
struct agent {
	struct log_options lopts;
	uint64_t uptime; /* uptime in seconds */
	uint8_t almac[6];
	uint8_t cntlr_almac[6];
	uint32_t i1905obj_id;

	atimer_t autocfg_dispatcher;
	atimer_t reload_scheduler;
	uint8_t reconfig_reason; /* bitmap: 1<<0 apconf, 1<<1 vlan teardown */
	atimer_t cntlr_scheduler;
	atimer_t onboarding_scheduler;
	atimer_t disable_unconnected_bstas_scheduler;
	atimer_t init_ifaces_scheduler;
	atimer_t enable_fhs_scheduler;
	atimer_t boot_scan_scheduler;
	atimer_t agent_periodic_run;
	/* refresh radio stats data */
	atimer_t radio_stats_scheduler;

	struct list_head pending_asynclist;
	uint32_t band_pending_mask; /* pending radio status API invokes */

	struct list_head framelist;
	struct list_head wdslist;

#if (EASYMESH_VERSION > 2)
#ifdef USE_LIBDPP
	void *dpp;
	struct dpp_peer *own_peer;
	uint8_t *pa_frame;
	size_t pa_framelen;
	struct allmac_htable peer_table;

	int dpp_pa_cnt;
	atimer_t dpp_periodic_pa;

	struct list_head conf_resplist;
	struct hlist_head fragtable[128]; /* NUM_FRAGMENTS */
#ifdef ZEROTOUCH_DPP
	struct zt_context zt;
#endif
#endif
#endif
	uint16_t pvid;

	int num_nodes;
	struct list_head nodelist;

	int num_radios;
	struct list_head radiolist;
	struct agent_config cfg;
	struct cmdu_ackq cmdu_ack_q;

	/* backhaul link info */
	struct {
		char ifname[16];
		uint8_t ul_almac[6];
		uint8_t ul_hwaddr[6];
	} ul_dev;

	/* controller selection configuration */
	struct {
		bool local;
		uint16_t probe_int;
		uint8_t retry_int;
#ifdef AGENT_DYNAMIC_CNTRL
		bool autostart;
#endif
		uint8_t alid[6];
	} cntlr_select;

	/* dynamic controller config sync */
	uint16_t sync_config_reqsize;
	uint8_t *sync_config_req;
	void *privkey;

	/* plugins */
#ifdef VENDOR_EXTENSION
	/* list of vendor extension plugins */
	struct list_head extlist;
#endif

	/* BTM steer request */
	uint8_t steer_dialog_token; /** steer dialog token */

	/* steering opportunity */
	bool is_sta_steer_start; /* To know whether STA steering is on going */
	atimer_t sta_steer_req_timer; /** steer opportunity timer */
	uint32_t sta_steerlist_count;
	struct wifi_sta_steer_list sta_steer_list[MAX_STA];

	/* timestamp of most recent occurence of controller in the network */
	struct timespec observed_time;
	/* number of autoconfig searches w/o reply from controller */
	int cntlr_miss_count;
	/* running controller available in network */
	bool active_cntlr;
	/* multiple controllers found in network */
	bool multiple_cntlr;
	/* current autoconfig search interval - depends on state */
	uint16_t autocfg_interval;

	/* channel scan */
	uint8_t scan_status_code;
	int boot_scan_tries;

	/* i1905 stack subscription */
	uint32_t map_oid;
	mapmodule_cmdu_mask_t cmdu_mask;
	void *subscriber;
	bool subscribed;

	/* ubus object and events */
	struct ubus_context *ubus_ctx;
	struct ubus_event_handler evh;
	struct ubus_event_handler ieee1905_evh;
	struct ubus_object obj;

	/* ubus debug object */
	struct ubus_object obj_dbg;

	atimer_t bh_lost_timer;
	atimer_t bh_reconf_timer;
#ifdef AGENT_ISLAND_PREVENTION
	atimer_t sta_disconnect_timer;
	atimer_t fh_disable_timer;
#endif /* AGENT_ISLAND_PREVENTION */
#if (EASYMESH_VERSION > 2)
	atimer_t dpp_legacy_timeout;
#endif
#ifdef DYNBH
	/* dynamic backhaul */
	struct dynbh_ctx dbh;
#endif
	atimer_t dynbh_scheduler;
	bool dynbh_upgrade_ongoing; /* when true upgrade attempt ongoing */
	int dynbh_attempts; /* number of upgrade attempts since connection */
	bool connected;
	struct timespec dynbh_last_start;
	struct timespec dynbh_last_end;
	uint32_t wifi_backhaul_time;
	struct timespec connect_t;
	struct timespec start_t;
	struct timespec disconnect_t;
	uint8_t backhaul_macaddr[2][6];
	struct timespec backhaul_change_t;
#define AGENT_MAX_ETHPORTS 16
	int num_ethports;
	char ethports[AGENT_MAX_ETHPORTS][16];
	struct timespec eth_connect_t;


	struct ts_context ts;

	/* Device Inventory */
	struct {
		char serial_number[65];
		char sw_version[65];
		char ex_env[65];
	} device_inventory;

	struct wsc_metadata wscdev;

	/* unsuccessful association */
	struct timespec last_unassoc;
	uint32_t unassoc_cnt;

#if (EASYMESH_VERSION > 2)
	/* QoS rules */
	struct list_head qos_rules;
#endif
#if (EASYMESH_VERSION >= 6)
	int num_ap_mld;
	struct list_head apmldlist;
	uint8_t ap_mld_cfg_sha256[SHA256_LENGTH];
	uint8_t bsta_mld_cfg_sha256[SHA256_LENGTH];
	struct bsta_mld *bstamld; /* not set if no bstamld configured */
#endif
};


/* todo: make static again after ubus api format is synced */
void parse_ap_stats(struct ubus_request *req, int type,
		struct blob_attr *msg);


int agent_subscribe_for_cmdus(struct agent *a);

struct netif_bk *agent_get_bsta_by_band(struct agent *a, enum wifi_band band);
struct netif_bk *agent_get_bsta(struct agent *a, uint8_t *bssid);
struct wsc_data *agent_free_wsc_data(struct wsc_data *wsc);
void agent_link_ap_to_cfg(struct agent *a);
uint32_t agent_config_reload(struct agent *a);

uint8_t agent_steer_next_dialog_token(struct agent *a);

extern void run_agent(void *opts);

int agent_exec_platform_scripts(const char *arg);
struct node *agent_find_node(struct agent *c, uint8_t *almac);
struct node *agent_alloc_node(struct agent *a, uint8_t *almac);
struct node *agent_add_node(struct agent *a, uint8_t *almac);

bool agent_bsta_steer_is_active(struct netif_bk *bk);
bool agent_bsta_steer_is_active_all(struct agent *a);
int agent_bsta_steer(struct agent *a, char *ifname, uint8_t *target_bssid,
		     bool force_disassoc);
void parse_i1905_info(struct ubus_request *req, int type,
		struct blob_attr *msg);
void agent_bsta_steer_clear(struct netif_bk *bk);
int agent_bsta_steer_handler(struct agent *a, struct netif_bk *bk,
		uint8_t *bssid);
bool wifi_radio_cac_required(struct agent *a,
		struct wifi_radio_element *re, int channel,
		int bandwidth, uint32_t *cac_time);

void wifi_bsta_check_cac_done(struct agent *a);
void wifi_bsta_connect(struct agent *a, struct netif_bk *bk,
			      uint8_t *bssid, uint32_t freq);

extern int wifiagent_steer_sta(struct ubus_context *ctx, char *ifname,
		unsigned char *sta,
		int pref_cnt, struct pref_neighbor *pref,
		int optime);

int agent_assoc_control_sta(uint8_t *bssid, uint8_t mode, int tmo,
		int num_sta, uint8_t stalist[][6]);

int wifiagent_toggle_fh(struct agent *a, bool isl_prev,
			const char *ifname, int enable);
bool agent_has_downstream(struct agent *a);

extern int wifiagent_get_status(struct ubus_context *ctx,
					struct ubus_request_data *req);
int wifiagent_get_nodes(struct ubus_context *ctx,
		       struct ubus_request_data *req);
extern int wifiagent_get_info(struct ubus_context *ctx,
					struct ubus_request_data *req);
int wifiagent_get_bk_info(struct ubus_context *ctx,
					struct ubus_request_data *req);
int wifiagent_initiate_wpspbc(struct ubus_context *ctx, struct ubus_object *obj,
					struct ubus_request_data *req);
#ifdef NOTIFY_EVENTS
extern void wifiagent_notify_event(struct agent *a, void *ev_type,
		void *ev_data);
#else
#define wifiagent_notify_event(a, e, d)
#endif
struct netif_ap *wifi_radio_to_ap(struct agent *a, struct wifi_radio_element *re);
struct wifi_radio_element *agent_get_radio_by_name(struct agent *a, const char *name);
struct netif_ap *agent_get_ap(struct agent *a, uint8_t *bssid);
struct netif_bk *agent_get_bsta_by_ifname(struct agent *a, const char *name);
struct sta *agent_get_sta(struct agent *a, uint8_t *sta);
struct netif_ap *agent_get_ap_with_sta(struct agent *a, uint8_t *sta);
struct wifi_radio_element *agent_get_radio_with_ifname(struct agent *a, char *ifname);
int prepare_tunneled_message(void *agent, const char *ifname,
		uint8_t protocol, const char *framestr);
bool agent_may_enable_fhs(struct agent *a);
int wifi_mod_bridge(struct agent *a, struct netif_bk *bk, bool add);
struct wifi_assoc_frame *wifi_get_frame(struct agent *a, uint8_t *macaddr);

int wifi_radio_scan_req(struct agent *a, struct wifi_radio_element *re,
			       const char *ssid, int num_opclass,
			       uint8_t *opclass, int num_channel,
			       uint8_t *channl);
int wifi_radio_scan_req_all(struct agent *a);

void list_stas(struct ubus_request *req, int type, struct blob_attr *msg);
void reschedule_nbrlist_update(struct netif_ap *ap);

void agent_reload_local_cntlr(struct agent *a, bool on);
void agent_disable_local_cntlr(struct agent *a);

void agent_schedule_fh_disable(struct agent *a);
void wifiagent_log_cntlrinfo(struct agent *a);

int wifi_clean_torndown_ifaces_on_radio(struct agent *a, struct wifi_radio_element *re);
int wifi_clean_torndown_ifaces(struct agent *a);

void agent_free_radios(struct agent *a);
void agent_free_cntlr_sync(struct agent *a);
void clear_sta(struct sta *s);
void clear_aplist_in_radio(struct wifi_radio_element *re);
void clear_aplist(struct agent *a);
void clear_bk(struct agent *a, struct netif_bk *bk);
void clear_bklist(struct agent *a);
void clear_wdslist(struct agent *a);

void parse_bk(struct ubus_request *req, int type,
		struct blob_attr *msg);

int agent_switch_according_to_pref(struct agent *a);
struct sta *agent_get_sta(struct agent *a, uint8_t *mac);

struct wifi_radio_element *agent_get_radio_by_band(struct agent *a,
						   enum wifi_band band);
struct wifi_radio_element *agent_get_radio(struct agent *a, uint8_t *macaddr);
struct wifi_radio_element *agent_get_radio_with_ap(struct agent *a, uint8_t *bssid);
struct wifi_radio_element *agent_get_radio_by_name(struct agent *a,
		const char *radio);

struct netif_ap *netif_alloc_ap(struct agent *a, const char *ifname);
struct netif_ap *agent_get_ap_by_ifname(struct agent *a, const char *ifname);
#if (EASYMESH_VERSION >= 6)
struct netif_ap *agent_get_ap_by_bss_index(struct wifi_radio_element *re,
		uint8_t bss_index);
#endif

struct netif_wds *netif_alloc_wds(struct agent *a, const char *ifname);
struct netif_wds *agent_get_wds_by_ifname(struct agent *a, const char *ifname);
void netif_free_wds(struct netif_wds *n);
void netif_free_wds_by_ifname(struct agent *a, const char *ifname);

void agent_set_post_scan_action_pref(struct agent *agent, struct wifi_radio_element *re,
				     bool opclass_preferences);
bool is_channel_supported_by_radio(struct wifi_radio_element *r,
				   uint8_t opclass,
				   uint8_t channel);
void agent_handle_bh_lost(struct agent *a, struct netif_bk *bk, bool immediate);
void agent_pref_neighbor_to_nbr(struct pref_neighbor *pref_nbr_src,
		struct nbr *wifi_nbr_dst);

struct netif_ap *agent_gen_netif_ap(struct agent *a, struct wifi_radio_element *re);
int agent_reset_stack(struct agent *a);
int wifiagent_ubus_reset_stack(struct ubus_context *ctx, struct ubus_object *obj,
		struct ubus_request_data *req);

struct sta *wifi_add_sta(struct agent *a, const char *ifname,
               unsigned char *macaddr);
int wifi_del_sta(struct agent *a, const char *ifname,
               uint8_t *macaddr);
int wifi_topology_notification(struct agent *a, struct sta *s,
			       struct netif_ap *ap, uint8_t assoc_event,
			       uint16_t reason);

void bk_toggle(struct agent *a, struct netif_bk *bk,
	       uint8_t *bssid, uint32_t freq);
#endif /* AGENT_H */
