/*
 * hostmngr.h - header file for hostmngr and hosts information.
 *
 * Copyright (C) 2023 IOPSYS Software Solutions AB. All rights reserved.
 *
 * See LICENSE file for license related information.
 *
 */

#ifndef HOSTMNGR_H
#define HOSTMNGR_H


struct arp_sock {
	struct uloop_fd uloop;
	void *priv;
};

int hostmngr_register_arp_sock(const char *ifname, struct arp_sock *arpsk, void *priv);
void hostmngr_unregister_arp_sock(struct arp_sock *arpsk);


struct i1905_metric {
	bool br_present;
	uint32_t tx_errors;
	uint32_t rx_errors;
	uint32_t tx_packets;
	uint32_t rx_packets;
	uint32_t available;		/* in percentage */
	uint32_t max_rate;		/* max throughput at MAC layer */
	uint32_t max_phyrate;
	uint8_t rssi;			/* rcpi (0..255) */
};

/**
 * @brief Defines non-1905 neighbors reported by a 1905 device in Topology Response.
 */
struct i1905_net_non1905_neighbor {
	//uint8_t local_macaddr[6];
	uint8_t macaddr[6];		/**< macaddress of the non-1905 device */
	struct list_head list;
};

struct i1905_genphy {
	uint8_t oui[3];
	uint8_t variant;
	char *url;
};

enum i1905_mediatype {
	I1905_802_3U_FAST_ETHERNET       = (0x0000),
	I1905_802_3AB_GIGABIT_ETHERNET   = (0x0001),
	I1905_802_11B_2_4_GHZ            = (0x0100),
	I1905_802_11G_2_4_GHZ            = (0x0101),
	I1905_802_11A_5_GHZ              = (0x0102),
	I1905_802_11N_2_4_GHZ            = (0x0103),
	I1905_802_11N_5_GHZ              = (0x0104),
	I1905_802_11AC_5_GHZ             = (0x0105),
	I1905_802_11AD_60_GHZ            = (0x0106),
	I1905_802_11AF_GHZ               = (0x0107),
#ifdef WIFI_EASYMESH
	I1905_802_11AX                   = (0x0108),  /* 11ax */
	I1905_802_11BE                   = (0x0109),  /* 11be */
#endif
	I1905_1901_WAVELET               = (0x0200),
	I1905_1901_FFT                   = (0x0201),
	I1905_MOCA_V1_1                  = (0x0300),
	I1905_MEDIA_UNKNOWN              = (0xFFFF),
};

#define IS_MEDIA_1901(m)	\
	((m) == I1905_1901_WAVELET || (m) == I1905_1901_FFT)


#define IS_MEDIA_WIFI(m)	\
	((m) >= I1905_802_11B_2_4_GHZ && (m) < I1905_1901_WAVELET)


#define IS_MEDIA_WIFI_5GHZ(m)			\
	((m) == I1905_802_11A_5_GHZ ||		\
	 (m) == I1905_802_11N_5_GHZ ||		\
	 (m) == I1905_802_11AC_5_GHZ)

#define IS_MEDIA_WIFI_2GHZ(m)			\
	((m) == I1905_802_11B_2_4_GHZ ||	\
	 (m) == I1905_802_11G_2_4_GHZ ||	\
	 (m) == I1905_802_11N_2_4_GHZ)



enum ip4addr_type {
	IP4_TYPE_UNKNOWN,
	IP4_TYPE_DHCP,
	IP4_TYPE_STATIC,
	IP4_TYPE_AUTOIP,
};

struct node_ipv4 {
	uint8_t macaddr[6];
	struct in_addr addr;
	enum ip4addr_type type;
	struct in_addr dhcpserver;
	struct list_head list;
};

enum ip6addr_type {
	IP6_TYPE_UNKNOWN,
	IP6_TYPE_LINKLOCAL,
	IP6_TYPE_DHCP,
	IP6_TYPE_STATIC,
	IP6_TYPE_SLAAC,
};

struct node_ipv6 {
	uint8_t macaddr[6];
	struct in6_addr addr;
	enum ip6addr_type type;
	struct in6_addr origin;
	struct list_head list;
};

struct non1905_neighbor {
	uint8_t macaddr[6];		/**< macaddress of the non-1905 neighbor */
	struct list_head list;
	bool invalid;
};

struct node_link {
	uint8_t macaddr[6];		/**< macaddress of the neighbor interface */
	uint8_t aladdr[6];		/**< ALID or AL-macaddress of the neighbor device */
	bool has_bridge;		/**< has atleast one L2 bridge in the link path */
	enum i1905_mediatype media;	/**< media information of the interface from neighbor's Topology response */
	struct i1905_genphy genphy;	/**< generic phy information of the interface, if available */
	struct i1905_metric metric;	/**< link metric of the link */
	//bool direct;			/**< whether direct link exists with this neighbor */
	//struct node_interface *iface;	/**< reference back to the node_interface this link connects to */
	bool invalid;
	struct list_head list;
};

struct node_neighbor {
	uint8_t macaddr[6];		/**< macaddress of the neighbor interface */
	struct list_head list;
	bool invalid;
};

struct node_interface {
	uint8_t macaddr[6];
	uint8_t ps;                    /**< power status */
	uint16_t mediatype;
	uint8_t sizeof_mediainfo;
	uint8_t *mediainfo;            /**< media specific data */
	uint8_t network_membership[6];
	struct list_head list;
	bool invalid;
};

#define DEFAULT_HISTORY_FILE            "/etc/hosts_history.json"
#define DEFAULT_HISTORY_TMPFS_FILE      "/tmp/hosts_history.json"
#define DEFAULT_HOSTS_FILE              "/etc/hostlist.json"
#define DEFAULT_HOSTS_TMPFS_FILE        "/tmp/hostlist.json"
#define DEFAULT_HISTORY_AGEOUT          604800		/* 7 days in seconds */
#define DEFAULT_HISTORY_MAX_NUM         512
#define DEFAULT_HISTORY_MAX_SIZE        0x100000	/* 1Mb */
#define DEFAULT_LAN_IFNAME              "br-lan"

#define HOST_EVENT			"host"
#define HOST_EVENT_CONNECT		"connected"
#define HOST_EVENT_DISCONNECT		"disconnected"

struct hostmngr_config {
	const char *objname;
	bool enabled;
	bool history;			/* keep history */
	bool history_persist;		/* keep history across reboot */
	bool stats;			/* gather stats */
	bool update_config;
	int num_ifnames;
	size_t ifnamelistlen;
	char *ifnamelist;
#define FILE_PATH_MAX	512
	char history_file[FILE_PATH_MAX];
	char hosts_file[FILE_PATH_MAX];
	uint32_t history_ageout;	/* ageout history entry in seconds */
	uint32_t history_maxnum;	/* max number of history entries */
	uint32_t history_maxsize;	/* max size of history entries (k/m) */
};

int hostmngr_config_defaults(struct hostmngr_config *cfg);
int hostmngr_dump_config(struct hostmngr_config *cfg);
int hostmngr_reconfig(struct hostmngr_config *cfg, const char *path, const char *file);
void hostmngr_config_free(struct hostmngr_config *cfg);
int hostmngr_config_add_zone(struct hostmngr_config *cfg, const char *ifname);

int ap_get_mld_ifname(char *ifname, char *mld_ifname);

struct local_interface {
	char ifname[16];
	int ifindex;
	uint8_t macaddr[6];
	char mld_ifname[16];
	bool is_affiliated_ap;
	uint32_t ifflags;
	uint8_t operstate;
	bool is_bridge;
	bool is_brif;
	bool is_ap;
	uint32_t brport;
	int br_ifindex;			/* master ifindex if enslaved to bridge */
	int nomaster;			/* is set to 1 when not enslaved */
	int carrier;			/* 1 = on, 0 = off */
	uint32_t num_ipaddrs;
	struct ip_address *ipaddrs;
	int exclude;
	char network[64];

	uint16_t  mediatype;
	uint8_t sizeof_mediainfo;
	uint8_t *mediainfo;            /**< media specific data */

	void *priv;
	struct arp_sock arpsk;
	struct list_head list;
};

struct node {
	void *priv;
	uint8_t macaddr[6];
	struct hlist_node hlist;
	struct list_head list;
	time_t last_updated;
	char ingress_ifr_name[256];
	atimer_t ageout;

	char friendly_name[65];           /**< friendly device name */
	char manufacturer_name[65];       /**< manufacturer name */
	char manufacturer_model[65];      /**< model name */
	char *control_url;                /**< control url of the device */

	char assoc_wifi_ref[1024];

	uint32_t num_interface;		  /**< number of interfaces */
	uint32_t num_ipv4;		  /**< number of IPv4 addresses this device has */
	uint32_t num_ipv6;		  /**< number of IPv6 addresses this device has */
	uint32_t num_brtuple;		  /**< number of L2 bridge tuples reported by it through Topology Response */
	uint32_t num_neighbor_non1905;	  /**< number of non-1905 neighbors reported by it through Topology response */
	uint32_t num_neighbor_1905;	  /**< number of 1905 neighbors reported by it through Topology response */
	uint32_t num_neighbor_l2;	  /**< number of L2 neighbors reported by it through Topology response */
	uint32_t num_links;		  /**< number of i1905_neighbor_interface to the neighbor 1905 device */

	struct list_head iflist;	  /**< list of struct node_interface */
	struct list_head ipv4list;	  /**< list of struct node_ipv4 */
	struct list_head ipv6list;	  /**< list of struct node_ipv6 */
	struct list_head brlist;	  /**< list of struct node_bridge_tuple */
	struct list_head nbrlist;         /**< list of struct neighbor */
	struct list_head n1905nbrlist;    /**< list of struct non1905_neighbor */
	struct list_head l2devlist;
	struct list_head linklist;	  /**< list of node_link */

	bool selfdevice;
	bool is1905;
	uint8_t ver1905;                  /**< if 1905 device, then which version */
	bool is_easymesh;
	bool invalid;
};

struct hostmngr_datamodel {
	uint32_t num_device;
	struct list_head devlist;
};

#define OBJECT_INVALID	((uint32_t)-1)

struct host_macaddr_entry {
	uint8_t macaddr[6];
	uint32_t reconnect_cnt;
	struct list_head list;
};

struct hostmngr_private {
	struct hostmngr_config cfg;
	atimer_t hbtimer;
	atimer_t refreshtimer;
	int num_local_interfaces;
	struct list_head iflist;	/* list of struct local_interface */
	struct neigh_queue neigh_q;

	struct neigh_flow_table nftable;

	struct hostmngr_datamodel net;

	bool use_ieee1905;
	uint32_t i1905;
	uint32_t i1905_topology;
	uint8_t ieee1905id[6];

	struct ubus_context *bus;
	struct ubus_object obj;
	struct ubus_event_handler evh;

	struct arp_sock arpsk;

	uint32_t num_hosts;
	struct list_head hostlist;
};


int hostmngr_init(void **priv, void *user_opts);
int hostmngr_exit(void *priv);
void hostmngr_run(void *priv);

struct local_interface *hostmngr_alloc_interface(const char *ifname);

void hostmngr_get_wifi_stations(struct hostmngr_private *priv,
				struct local_interface *iface);
int hostmngr_get_known_neighbors(struct hostmngr_private *priv, char *ifname);
int hostmngr_get_neigh_hostname(struct neigh_queue *q, uint8_t *macaddr);

int hostmngr_register_misc_events(struct hostmngr_private *priv);
int hostmngr_unregister_misc_events(struct hostmngr_private *priv);
int hostmngr_host_event(struct hostmngr_private *priv, const char *event,
			struct neigh_entry *e);

int hostmngr_register_nlevents(struct hostmngr_private *priv);
void hostmngr_unregister_nlevents(struct hostmngr_private *priv);


uint32_t lookup_object(void *bus, const char *objname);
int hostmngr_get_1905_topology(struct hostmngr_private *p);
int hostmngr_get_1905_aladdr(struct hostmngr_private *p);
int hostmngr_get_interface_network(struct hostmngr_private *priv);
int hostmngr_publish_object(struct hostmngr_private *p, const char *objname);
int hostmngr_remove_object(struct hostmngr_private *p);
int hostmngr_register_local_events(struct hostmngr_private *p);
int hostmngr_unregister_local_events(struct hostmngr_private *p);



static inline struct node *hostmngr_get_selfdevice(struct hostmngr_datamodel *dm)
{
	return list_first_entry(&dm->devlist, struct node, list);
}


int hostmngr_update_neigh_brport(struct hostmngr_private *priv, char *brname);
char *hostmngr_brport_to_ifname(struct hostmngr_private *priv, uint16_t brport);
struct local_interface *hostmngr_ifname_to_interface(struct hostmngr_private *priv, const char *ifname);
char *hostmngr_ifname_to_network(struct hostmngr_private *priv, const char *ifname);

int hostmngr_handle_ethport_carrier_off(struct hostmngr_private *priv, const char *ifname);
int hostmngr_handle_ethport_carrier_on(struct hostmngr_private *priv, const char *ifname);

struct node *alloc_node(uint8_t *macaddr);
void free_node(struct node *n);
int add_node(struct hostmngr_private *priv, struct list_head *list, uint8_t *macaddr, bool is1905);
int del_node(struct hostmngr_private *priv, struct list_head *list, uint8_t *macaddr);
struct node *lookup_node(struct hostmngr_private *priv, struct list_head *list, uint8_t *macaddr);


int nfct_get_entries_nolo(struct hostmngr_private *priv);
void neigh_flowtable_flush(void *priv);
void neigh_iptable_flush(void *priv);
void nfct_cleanup(void);

int neigh_history_entry_add(void *priv, struct neigh_history_entry *he);
int neigh_history_enqueue(void *priv, struct neigh_entry *e, uint32_t timeout);

int neigh_history_load_from_json_file(void *priv, const char *file);
int neigh_history_store_to_json_file(void *priv, const char *file);

int hostlist_add_entry(struct hostmngr_private *priv, uint8_t *macaddr, bool from_history);
int hostlist_del_entry(struct hostmngr_private *priv, uint8_t *macaddr);
int hostlist_flush(struct hostmngr_private *priv);

int hosts_load_from_json_file(void *priv, const char *file);
int hosts_store_to_json_file(void *priv, const char *file);

#endif /* HOSTMNGR_H */
