/*
 * config.h - Agent configurations header
 *
 * Copyright (C) 2019 IOPSYS Software Solutions AB. All rights reserved.
 *
 * Author: anjan.chanda@iopsys.eu
 *
 */

#ifndef CONFIG_H
#define CONFIG_H

#include <libubox/list.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <uci.h>
#include <easy/utils.h>
#include <easy/if_utils.h>
#include <wifidefs.h>
#include <timer_impl.h>

struct tlv_default_8021q_settings;
struct tlv_traffic_sep_policy;
struct wps_credential;
struct netif_bk;
struct agent;


#if 0
enum steer_action {
	STEER_DISABLE,     /** steer disallowed */
	STEER_MANDATE,     /** steer immediately */
	STEER_OPPORTUNITY, /** steer at the next opportunity within a time-window */
};
#endif


#define STEER_BTM_RETRY			0	/* no retry */
#define STEER_BTM_RETRY_INT		180	/* secs */

#define ASSOC_CONTROL_INT		10	/* secs */

#define CONTROLLER_SELECT_LOCAL			0	/* external cntlr */
#define CONTROLLER_SELECT_AUTODETECT	1	/* discover cntlr ID */
#define CONTROLLER_SELECT_PROBE_INT		20	/* secs */
#define CONTROLLER_SELECT_RETRY_INT		3	/* attempts */
#define CONTROLLER_SELECT_AUTOSTART		0	/* don't start cntlr */
#define UCI_DEVICE     "wifi-device"

#ifndef IFNAMSIZ
#define IFNAMSIZ 16
#endif

struct wsc_ext;
struct wifi_radio_element;

struct steer_policy {
	char name[16];	/* XXX: maps to struct steer_rule.name in (all)?
			 * cases otherwise there is no way of knowing how
			 * a steer_policy will be applied.
			 */

	bool enabled;
	void *policy;	/** policy specific data */
	struct list_head list; /* link to next policy */
};

struct stax {
	uint8_t macaddr[6];

	struct list_head list;
};

void stax_add_entry(struct list_head *h, uint8_t *macaddr);
void stax_del_entry(struct list_head *h, uint8_t *macaddr);

enum agent_steer_policy {
	AGENT_STEER_DISALLOW,		/* agent shall not steer based on rcpi */
	AGENT_STEER_RCPI_MANDATE,	/* agent shall steer based on rcpi */
	AGENT_STEER_RCPI_ALLOW,		/* agent may steer based on rcpi */
};

#if (EASYMESH_VERSION >= 6)
enum mld_type {
	MLD_TYPE_UNKNOWN,
	MLD_TYPE_BACKHAUL_BSS,
	MLD_TYPE_FRONTHAUL_BSS,
	MLD_TYPE_COMBINED_BSS,
	MLD_TYPE_BACKHAUL_STA,
};

struct mld_credential {
	char ifname[16];
	uint8_t id;
	uint8_t key[65];
	uint8_t ssid[33];
	char encryption[32];
	uint16_t vlanid;
	enum mld_type type;
	bool enabled;
	enum wifi_band band; /* bitmap */
	struct list_head list;
};
#endif

/* per-bss interface config is good for now.
 * per-sta config is overkill, or maybe not ;)
 */
struct netif_apcfg {
	bool invalid;
	char name[16];
	char device[16];
	char ssid[33];
	char key[65];
	char encryption[32];
	enum wifi_band band;
	bool enabled;
	bool sta_steer_enabled;
	bool assoc_control;
#if (EASYMESH_VERSION > 2)
	bool advertise_cce;
#endif
#if (EASYMESH_VERSION >= 6)
	uint8_t mld_id;
#endif
	/* int steer_policy; */

	unsigned int steer_btm_retry;       /* num retries for btm requests */
	unsigned int steer_btm_retry_secs;  /* btm retry interval in secs */

	unsigned int assoc_control_time;    /* secs to restrict sta from associating */

	/** ordered list of policies effective on per-bss interface */
	struct list_head steer_policylist;

	enum agent_steer_policy policy;

	struct list_head list;  /* link to next netif_config */
	uint8_t multi_ap;	/* BSS type, 0x01: backhaul, 0x02: fronthaul, 0x03: combined */
	unsigned int vid;	/* traffic separation vlan id */
	uint8_t bsta_disallow;  /* this option 1 p1 2 p2 3 both disallow */
	bool disallow_assoc;	/* if true all associations disallowed */
};

struct netif_bkcfg {
	char name[16];
	enum wifi_band band;
	char device[16];
	char ssid[33];
	char key[65];
	char encryption[32];
	bool enabled;
	bool onboarded;
	uint8_t priority;
	uint8_t bssid[6];

#if (EASYMESH_VERSION >= 6)
	bool is_mld_netdev;
	int mld_id;
#endif
	struct list_head list;  /* link to next netif_bkcfg */
};

struct policy_cfg {
	uint8_t report_interval;
	uint16_t pvid;
	uint8_t pcp_default;
	bool report_scan;
	bool report_sta_assocfails;
	uint32_t report_sta_assocfails_rate;

	/** STAs excluded from steering; list of stax structs */
	struct list_head steer_excludelist;
	/** STAs excluded from BTM req steering; list of stax structs */
	struct list_head steer_btm_excludelist;
};

struct ctrl_select_cfg {
	bool local;			/* true - own MAP Controller treated as the main or primary. */
	bool auto_detect;	/* Set to true if alid of controller is not explicitly provided by config. */
	uint16_t probe_int;	/* Time interval in seconds between controller discovery by the MAP Agent. */
	uint8_t retry_int;	/* Num of discovery retries before taking next action. */
#ifdef AGENT_DYNAMIC_CNTRL
	bool autostart;		/* true - agent will try to start own controller after not finding one. */
#endif
	uint8_t alid[6];	/* 1905 ALID of the device that will have the MAP Controller service */
};

struct dyn_bh_cfg {
	uint16_t bh_miss_tmo; /* Time interval (sec) between bh lost and recovery actions. */
	uint16_t bh_reconf_tmo; /* Time interval (sec) between bh link is lost and fallback recovery actions. */
};

enum runfreq {
	AGENT_RUN_OFF,
	AGENT_RUN_HIGH,
	AGENT_RUN_MOD = 5,
	AGENT_RUN_AUTO = AGENT_RUN_MOD,
	AGENT_RUN_LOW = 10,
};

enum agent_config_diff {
	CONFIG_DIFF_NONE = 0,
	CONFIG_DIFF_DEBUG = 1 << 0,
};

struct agent_config_radio {
	char name[16];
	enum wifi_band band;
	bool dedicated_backhaul;
	uint16_t encryption;
	uint8_t steer_policy;
	uint8_t util_threshold;
	uint8_t rcpi_threshold;
	uint8_t report_rcpi_threshold;
	uint8_t rcpi_hysteresis_margin;
	uint8_t report_util_threshold;
	bool include_sta_stats;
	bool include_sta_metric;
#if (EASYMESH_VERSION > 2)
	bool include_wifi6_sta_status;
#endif
#if (EASYMESH_VERSION >= 6)
	bool mlo_capable;
	uint8_t punct_bitmap[2];
#endif
	char ifprefix[IFNAMSIZ];
	struct list_head list;
};

#if (EASYMESH_VERSION > 2)
struct dpp_chirp {
	char ifname[16];
	enum wifi_band band;
	char device[16];
	int num_chan;
	int channel[16];
	int chirp_int;
	struct list_head list;
};
#endif

#ifdef VENDOR_EXTENSION
struct agent_extension_config {
	char name[32];
	struct list_head list;
};
#endif

struct agent_config {
	bool enabled;
	int debug_level;
	enum runfreq runfreq;
	struct list_head radiolist;
	struct list_head aplist;  /* list of netif_apcfg */
	struct list_head bklist;  /* list of netif_bkcfg */
#if (EASYMESH_VERSION > 2)
	/* TODO: could be moved to a dpp context struct */
	struct list_head chirplist;
	bool persist_uri;
#endif
#if (EASYMESH_VERSION >= 6)
	int num_mld;
	struct list_head mldlist;
#endif

	uint8_t map_profile;
	bool brcm_setup;
	bool configured;
	bool dyn_cntlr_sync;
	int resend_num;
	uint8_t cntlr_almac[6];
	char al_bridge[16];
	char netdev[IFNAMSIZ];
	bool island_prevention;
	bool eth_onboards_wifi_bhs;
	bool ap_follow_sta_dfs;
	bool guest_isolation;
	bool scan_on_boot_only;
#ifdef OPER_CHAN_CHANGE_RELAY_MCAST
	bool chan_ch_relay_mcast;
#endif

#ifdef CHECK_PARTIAL_WIFI_RELOAD
	bool partial_wifi_reload;
#endif

#ifdef VENDOR_EXTENSION
	/* list of extension plugins */
	struct list_head extlist;
#endif
	bool hide_bbss;
	bool backhaul_override;
	bool off_channel_monitor;

	struct policy_cfg *pcfg;  /* policy section */
	struct ctrl_select_cfg *cscfg;  /* controller select section */
	struct dyn_bh_cfg dbhcfg;  /* dynamic backhaul section */
	char mld_prefix[IFNAMSIZ - 1]; /* prefix + %1d */
	atimer_t metric_report_timer;
};

#if 0
union config {
	struct agent_config agent;
	struct controller_config cntlr;
};
#endif

enum m2_process_status {
	M2_PROCESS_OK,
	M2_PROCESS_ERROR,
	M2_PROCESS_TEARDOWN
};

int set_value(struct uci_context *ctx, struct uci_package *pkg,
		struct uci_section *section, const char *key,
		const char *value, enum uci_option_type type);
int set_value_by_string(const char *package, const char *section,
		const char *key, const char *value, enum uci_option_type type);
char *agent_get_controller_enabled(struct agent *a, char *buf);
struct uci_section *config_get_iface_section(struct uci_context *ctx,
		struct uci_package *pkg, const char *type, const char *ifname);
bool uci_reload_services(char *services);
struct uci_package *uci_load_pkg(struct uci_context **ctx, const char *config);
int wifi_get_iface_bssid(char *ifname, uint8_t *bssid);
int wifi_set_iface_bssid(struct netif_bk *bk, uint8_t *bssid);

struct agent_config_radio *get_agent_config_radio(struct agent_config *c,
						  const char *device);
struct agent_config_radio *get_agent_config_radio_from_iface(struct agent_config *c,
							     const char *ifname);
char *agent_config_get_ethwan(char *ifname);

int agent_config_init(struct agent *a, struct agent_config *cfg);
int agent_config_load(struct agent_config *cfg);
int agent_config_clean(struct agent_config *cfg);
int agent_config_defaults(struct agent *a, struct agent_config *cfg);
void agent_config_dump(struct agent_config *cfg);

int config_update(const char *confname, struct agent_config *cfg,
		const char *section, const char *option, int add,
		void *value, int len);


int config_update2(const char *confname, struct agent_config *cfg,
		const char *section_type,
		const char *match_option,
		const char *match_option_value,
		const char *option, int add, const void *value, int len);

int wifi_apply_iface_cfg(const char *ifname, const char *encryption,
		const char *ssid, const char *key);
int config_del_iface(const char *config, const char *type, const char *ifname);
struct uci_section *config_add_section(struct uci_context *ctx,
		struct uci_package *pkg, const char *config, const char *type,
		const char *key, const char *value, const char *section_name);

bool uci_check_wifi_iface(const char *package_name, const char *ifname,
		const char *section);
int uci_set_wireless_interface_option(const char *package_name,
		const char *section_type, const char *search_key, const char *search_val,
		const char *option, const char *value);
bool uci_add_wireless_iface_sec(const char *package_name, const char *interface_name,
		const char *section_type, const char *section_name);
int agent_init_wsc_attributes(struct agent *a);
int wifi_get_section_option(const char *package, const char *sec_type,
			    const char *sec_key, const char *sec_value,
			    const char *get_key, char *buf, int len);
void clean_bk(struct netif_bkcfg *p);
int clean_all_bk(struct agent_config *cfg);
void clean_ap(struct netif_apcfg *p);
int clean_all_ap(struct agent_config *cfg);

#if (EASYMESH_VERSION > 2)
int clean_dpp_uri(struct dpp_chirp *uri);
int clean_all_dpp_uris(struct agent_config *cfg);
struct dpp_chirp *dpp_chirp_by_ifname(struct agent_config *c,
				      const char *ifname);
#endif

struct netif_apcfg *uci_apply_wps_credentials(struct agent_config *cfg, char *ifname,
				 char *device, struct wps_credential *out,
				 struct wsc_ext *exts);
int config_calc_ifname(struct agent_config *cfg, char *device,
		uint8_t index, char *ifname);
int uci_set_bridge(char *config, char *bridge, char *proto, char *ipaddress);
int uci_add_dhcp(char *interface);
int uci_add_fw(struct agent_config *cfg, char *interface);
void config_disable_bstas(struct agent_config *cfg);
int config_disable_bsta(struct netif_bkcfg *bk);
int config_enable_bsta(struct netif_bkcfg *bk);
int uci_apply_default_8021q_settings(struct tlv_default_8021q_settings *tlv);
int uci_clear_traffic_sep(struct agent_config *cfg);
void uci_apply_traffic_sep(struct tlv_traffic_sep_policy *tlv);
int wifi_set_opclass_preference(char *radio_name, uint32_t opclass_id,
	uint32_t preference, uint8_t *channel_list, int channel_num);
int agent_config_opclass(struct  wifi_radio_element *radio);
void uci_apply_bss_association_mode(const char *interface_name, bool disallow_assoc);
uint16_t get_encryption(char *sec);
bool get_encryption_value(uint16_t auth_type, uint16_t encryption_type,
		char *encrypt_val, size_t elen, int *mfp);
struct netif_apcfg *get_netif_apcfg_by_name(struct agent_config *c,
							const char *name);
#if (EASYMESH_VERSION >= 6)
struct netif_bkcfg *agent_config_bsta_by_band(struct agent_config *cfg,
					      enum wifi_band band);
int agent_config_mld_rem_bsta_all(struct agent_config *cfg);
struct netif_apcfg *agent_config_get_ap_by_ssid_on_band(struct agent_config *cfg,
					    enum wifi_band band,
					    char *ssid);
struct netif_apcfg *agent_config_get_ap_by_ssid(struct agent_config *cfg,
					    char *ssid);
struct mld_credential *agent_get_mld_credential_by_ifname(struct agent_config *cfg,
						      const char *ifname);

uint8_t agent_get_first_available_mld_id(struct agent_config *cfg);
struct mld_credential *agent_get_bsta_mld_credential(struct agent_config *cfg);
struct mld_credential *agent_get_ap_mld_credential_by_ssid(struct agent_config *cfg,
							     char *ssid);
struct mld_credential *agent_get_mld_credential_by_id(struct agent_config *cfg,
						      uint8_t id);
int uci_apply_agent_bsta_config(struct agent_config *cfg, uint8_t id, char *ssid,
			        char *key, enum wifi_band band);
int uci_apply_agent_mld_config(struct agent_config *cfg, uint8_t id, char *ssid,
			       char *key, enum mld_type type, uint16_t vlanid,
			       enum wifi_band band);
int uci_apply_wireless_mld_config(struct agent_config *cfg, uint8_t id,
			 char *ssid, char *key, const char *mode);
void clean_mld_credential(struct mld_credential *mld);
int clean_all_mlds(struct agent_config *cfg);
#endif
#endif
