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

#ifndef NEIGH_H
#define NEIGH_H

#include <easy/easy.h>


#define NEIGH_ENTRIES_MAX		128
#define NEIGH_AGEOUT_DEFAULT		60000	/* ms */
#define NEIGH_AGEOUT_QUICK		2000	/* ms */
#define NEIGH_AGEOUT_PROBE		4000	/* ms */
#define MAX_PROBE_COUNT			2

#ifndef MAC_ADDR_HASH
#define MAC_ADDR_HASH(a)	(a[0] ^ a[1] ^ a[2] ^ a[3] ^ a[4] ^ a[5])
#endif

#define neigh_hash(o)	(MAC_ADDR_HASH(o) & (NEIGH_ENTRIES_MAX - 1))

struct ip_address_entry {
	struct ip_address ip;
	struct list_head list;
};

enum neigh_state {
	NEIGH_STATE_UNKNOWN    = 0x00,
	NEIGH_STATE_INCOMPLETE = 0x01,
	NEIGH_STATE_REACHABLE  = 0x02,
	NEIGH_STATE_STALE      = 0x04,
	NEIGH_STATE_PROBING    = 0x10,
	NEIGH_STATE_FAILED     = 0x20,
};

enum neigh_type {
	NEIGH_TYPE_UNKNOWN,
	NEIGH_TYPE_ETH,
	NEIGH_TYPE_WIFI,
	NEIGH_TYPE_GHN,
	NEIGH_TYPE_HPNA,
	NEIGH_TYPE_HOMEPLUG,
	NEIGH_TYPE_MOCA,
	NEIGH_TYPE_UPA,
	NEIGH_TYPE_OTHER
};

struct neigh_wanstats {
	uint64_t ul_packets;
	uint64_t ul_bytes;
	uint64_t dl_packets;
	uint64_t dl_bytes;
};

struct neigh_entry {
	uint8_t macaddr[6];
	uint16_t state;
	char ifname[16];
	uint16_t brport;		/* valid when 'ifname' is bridge type */
	enum neigh_type type;
	uint8_t is_stamld;
	uint8_t is_affiliated_sta;
	uint8_t isremote;		/* true when connected behind extender */
	uint8_t is1905;
	uint8_t is1905_slave;		/* member interface of 1905 device */
	uint8_t is1905_link;		/* 1905 interface forming link with nbr */
	uint8_t aladdr[6];		/* AL-address of neigh if 1905 device */
#define HOST_MAX_LINKS 16
	int num_links;
	uint8_t linkaddr[HOST_MAX_LINKS][6];		/* interface(s) of neigh through which it is connected */
	uint8_t stamldaddr[6];
	void *cookie;
	char hostname[256];
	struct ip_address ipv4;		/* from dhcp-lease table or neigh cache */
	int ipv4_type_dhcp;		/* set 1 when dhcp assigned ipv4 address */
	int ipv4_type_static;		/* set 1 on statically assigned ipv4 address */
	unsigned long leasetime;	/* lease time() end if dhcp addresses */
	struct list_head iflist;	/* list of struct node_interface */
	struct list_head iplist;	/* list of struct ip_address_entry */
	struct hlist_node hlist;
	int probing;			/* when probing entry through arping etc. */
	atimer_t probing_timer;

	int unreachable;		/* mark entry nonreachable */
	time_t lastchange;		/* last time when reachable state toggled */
	int delete_pending;
	atimer_t delete_timer;
	uint32_t ageing_time;           /* in msecs */
	struct timeval ageing_tmo;
	int probe_cnt;
	int event_pending;

	uint32_t num_tcp;
	uint32_t num_udp;
	struct neigh_wanstats ws;
	void *priv;
};

#define NEIGH_HISTORY_AGEOUT_DEFAULT		604800	/* 7 days in secs */

struct neigh_history_entry {
	time_t createtime;
	time_t lastseen;
	time_t lastchange;
	uint16_t state;
	bool alive;
	uint8_t is_stamld;
	uint8_t is_affiliated_sta;
	uint8_t is1905;
	uint8_t is1905_slave;
	uint8_t is1905_link;
	uint8_t aladdr[6];
	//uint8_t linkaddr[6];
	uint8_t macaddr[6];
	char hostname[256];
	char ifname[16];
	struct ip_address ip;
	int ipv4_type_dhcp;
	struct list_head iplist;	/* list of struct ip_address_entry */
	enum neigh_type type;
	struct neigh_wanstats ws;
	uint32_t timeout;		 /* in secs */
	atimer_t delete_timer;
	struct hlist_node hlist;
	void *priv;
};

extern void neigh_history_free(void *nq);
extern struct neigh_history_entry *neigh_history_lookup(void *nq, uint8_t *macaddr);

int neigh_history_entry_set_1905(void *nq, uint8_t *macaddr, uint8_t val);
int neigh_history_entry_set_1905_slave(void *nq, uint8_t *macaddr,
				       uint8_t *aladdr, uint8_t val);

int neigh_history_entry_set_1905_link(void *nq, uint8_t *macaddr,
				      uint8_t *aladdr, uint8_t val);

struct neigh_queue {
	struct hlist_head table[NEIGH_ENTRIES_MAX];	/* hashtable of struct neigh_entry */
	struct hlist_head history[NEIGH_ENTRIES_MAX];	/* hashtable of struct neigh_entry */
	uint32_t num_history;
	int pending_cnt;
	atimer_t ageing_timer;
	struct timeval next_tmo;
};

struct neigh_ip_entry {
	struct ip_address ip;
	uint32_t num_tcp;
	uint32_t num_udp;
	struct neigh_wanstats ws;
	struct hlist_node iphlist;
	uint8_t macaddr[6];
	void *neigh;
};

struct neigh_flow_entry {
	uint8_t macaddr[6];
	struct ip_address sip;
	struct ip_address dip;
	uint16_t sport;
	uint16_t dport;
	uint32_t l4proto;
	struct neigh_ip_entry *ipneigh;
	struct hlist_node hlist;
};


#define NEIGH_FLOW_ENTRIES_MAX		1024
#define NEIGH_IP_ENTRIES_MAX		256

struct neigh_flow_table {
	uint32_t num;
	struct hlist_head table[NEIGH_FLOW_ENTRIES_MAX];	/* hashtable of struct neigh_flow_entry */
	struct hlist_head iptable[NEIGH_IP_ENTRIES_MAX];	/* hashtable of struct neigh_ip_entry */
};

uint32_t ipaddr_hash(struct ip_address *ip);


extern int neigh_queue_init(void *nq);
extern void neigh_queue_free(void *nq);
extern void neigh_queue_flush(void *nq);
struct neigh_entry *neigh_queue_print(void *nq);

void neigh_set_unreachable(void *nq, const char *ifname);
void neigh_probe_unreachable(void *nq, const char *ifname);
void neigh_mark_reachable(void *nq, uint8_t *macaddr, const char *ifname);

int neigh_set_type(void *q, uint8_t *macaddr, enum neigh_type type);
int neigh_set_1905_remote(void *nq, uint8_t *aladdr, uint8_t val);
uint16_t neigh_get_brport(void *q, uint8_t *macaddr);
int neigh_set_1905(void *q, uint8_t *macaddr, uint8_t val);
int neigh_set_1905_slave(void *nq, uint8_t *macaddr, uint8_t *aladdr, uint8_t val);
int neigh_set_1905_link(void *nq, uint8_t *macaddr, uint8_t *aladdr, uint8_t val);
int neigh_set_1905_linkaddr(void *nq, uint8_t *aladdr, uint8_t *linkaddr);
bool is_neigh_1905(void *q, uint8_t *macaddr);
bool is_neigh_1905_slave(void *q, uint8_t *macaddr);

bool neigh_is_ip_known(struct neigh_entry *e, struct ip_address *ip);
int neigh_is_wifi_type(void *priv, uint8_t *macaddr);

struct neigh_entry *neigh_lookup(void *nq, uint8_t *macaddr);
struct neigh_entry *neigh_lookup_by_ipaddr(void *priv, struct ip_address *ip);
struct neigh_entry *neigh_lookup_by_aladdr(void *priv, uint8_t *aladdr);
struct neigh_entry *neigh_lookup_stamld_by_aladdr(void *priv, uint8_t *aladdr);
struct neigh_ip_entry *neigh_ip_entry_lookup(void *priv, struct ip_address *ip);
struct neigh_ip_entry *neigh_ip_entry_lookup2(void *priv, struct ip_address *ip);

int neigh_dequeue(void *nq, uint8_t *macaddr, void **cookie);
struct neigh_entry *neigh_enqueue(void *nq, uint8_t *macaddr, uint16_t state,
				  const char *ifname, enum neigh_type type,
				  struct ip_address *ip, uint32_t timeout,
				  void *cookie);

int neigh_update_ip_entry_stats(void *priv, struct ip_address *ip,
				struct neigh_entry *n);

int neigh_flush_ip_entry_stats(void *priv, struct ip_address *ip);

void neigh_unlink_flowtable(void *priv, struct ip_address *ip4);

struct node_interface *neigh_interface_lookup(struct neigh_entry *e,
						   uint8_t *macaddr);
struct node_interface *niegh_add_interface(struct neigh_entry *e,
					   uint8_t *macaddr);
#endif /* NEIGH_H */
