/*
 *
 * Copyright 2009-2016, LabN Consulting, L.L.C.
 *
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; see the file COPYING; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifndef QUAGGA_HGP_RFAPI_MONITOR_H
#define QUAGGA_HGP_RFAPI_MONITOR_H

#include "lib/zebra.h"
#include "lib/prefix.h"
#include "lib/table.h"

/*
 * These get attached to the nodes in an import table (using "aggregate" ptr)
 * to indicate which nves are interested in a prefix/target
 */
struct rfapi_monitor_vpn {
	struct rfapi_monitor_vpn *next; /* chain from struct agg_node */
	struct rfapi_descriptor *rfd;   /* which NVE requested the route */
	struct prefix p;		/* constant: pfx in original request */
	struct agg_node *node;		/* node we're currently attached to */
	uint32_t flags;
#define RFAPI_MON_FLAG_NEEDCALLBACK	0x00000001      /* deferred callback */

	// int				dcount;	/* debugging counter */
	struct thread *timer;
};

struct rfapi_monitor_encap {
	struct rfapi_monitor_encap *next;
	struct rfapi_monitor_encap *prev;
	struct agg_node *node;   /* VPN node */
	struct bgp_path_info *bpi; /* VPN bpi */
	struct agg_node *rn;     /* parent node */
};

struct rfapi_monitor_eth {
	struct rfapi_monitor_eth *next; /* for use in vpn0_queries list */
	struct rfapi_descriptor *rfd;   /* which NVE requested the route */
	struct ethaddr macaddr;
	uint32_t logical_net_id;
	struct thread *timer;
};

/*
 * This is referenced by the "aggregate" field of a route node
 * in an RFAPI import table.
 *
 * node lock/unlock:
 *	- one lock increment for this structure itself
 *	- one lock per chained struct rfapi_monitor_vpn
 *	- one lock for the mon_eth skiplist itself
 *	- one lock per mon_eth skiplist entry
 *	- one lock for the ext skiplist itself
 *	- one lock for each ext skiplist entry
 *		remember to free skiplist when freeing rfapi_it_extra
 *	- one lock per chained struct rfapi_monitor_encap
 *
 */
struct rfapi_it_extra {
	union {
		struct {
			struct rfapi_monitor_vpn *v;
			struct skiplist *idx_rd;  /* RD index */
			struct skiplist *mon_eth; /* ether queries */
			struct {
				/* routes with UN addrs, either cached encap or
				 * Encap TLV */
				int valid_interior_count;

				/* unicast exterior routes, key=bpi,
				 * val=allocated prefix */
				struct skiplist *source;
			} e;
		} vpn;
		struct {
			struct rfapi_monitor_encap *e;
		} encap;
	} u;
};

#define RFAPI_IT_EXTRA_GET(rn)                                                 \
	((struct rfapi_it_extra                                                \
		  *)((rn)->aggregate                                           \
			     ? (rn)->aggregate                                 \
			     : (agg_lock_node(rn),                             \
				(rn)->aggregate = XCALLOC(                     \
					MTYPE_RFAPI_IT_EXTRA,                  \
					sizeof(struct rfapi_it_extra)))))

#define RFAPI_RDINDEX(rn)                                                      \
	((rn)->aggregate ? RFAPI_IT_EXTRA_GET(rn)->u.vpn.idx_rd : NULL)

#define RFAPI_RDINDEX_W_ALLOC(rn) (RFAPI_IT_EXTRA_GET(rn)->u.vpn.idx_rd)

#define RFAPI_MONITOR_ETH(rn)                                                  \
	((rn)->aggregate ? RFAPI_IT_EXTRA_GET(rn)->u.vpn.mon_eth : NULL)

#define RFAPI_MONITOR_ETH_W_ALLOC(rn) (RFAPI_IT_EXTRA_GET(rn)->u.vpn.mon_eth)

#define RFAPI_MONITOR_VPN(rn)                                                  \
	((rn)->aggregate ? RFAPI_IT_EXTRA_GET(rn)->u.vpn.v : NULL)

#define RFAPI_MONITOR_VPN_W_ALLOC(rn) (RFAPI_IT_EXTRA_GET(rn)->u.vpn.v)

#define RFAPI_MONITOR_ENCAP(rn)                                                \
	((rn)->aggregate ? RFAPI_IT_EXTRA_GET(rn)->u.encap.e : NULL)

#define RFAPI_MONITOR_ENCAP_W_ALLOC(rn) (RFAPI_IT_EXTRA_GET(rn)->u.encap.e)

#define RFAPI_MONITOR_EXTERIOR(rn) (&(RFAPI_IT_EXTRA_GET(rn)->u.vpn.e))

#define RFAPI_HAS_MONITOR_EXTERIOR(rn)                                         \
	(rn && rn->aggregate                                                   \
	 && ((struct rfapi_it_extra *)(rn->aggregate))->u.vpn.e.source         \
	 && !skiplist_first(((struct rfapi_it_extra *)(rn->aggregate))         \
				    ->u.vpn.e.source,                          \
			    NULL, NULL))

extern void rfapiMonitorLoopCheck(struct rfapi_monitor_vpn *mchain);

extern void rfapiMonitorCleanCheck(struct bgp *bgp);

extern void rfapiMonitorCheckAttachAllowed(void);

extern void rfapiMonitorExtraFlush(safi_t safi, struct agg_node *rn);

extern struct agg_node *rfapiMonitorGetAttachNode(struct rfapi_descriptor *rfd,
						  struct prefix *p);

extern void rfapiMonitorAttachImportHd(struct rfapi_descriptor *rfd);

extern struct agg_node *rfapiMonitorAdd(struct bgp *bgp,
					struct rfapi_descriptor *rfd,
					struct prefix *p);

extern void rfapiMonitorDetachImportHd(struct rfapi_descriptor *rfd);

extern void rfapiMonitorDel(struct bgp *bgp, struct rfapi_descriptor *rfd,
			    struct prefix *p);

extern int rfapiMonitorDelHd(struct rfapi_descriptor *rfd);

extern void rfapiMonitorCallbacksOff(struct bgp *bgp);

extern void rfapiMonitorCallbacksOn(struct bgp *bgp);

extern void rfapiMonitorResponseRemovalOff(struct bgp *bgp);

extern void rfapiMonitorResponseRemovalOn(struct bgp *bgp);

extern void rfapiMonitorExtraPrune(safi_t safi, struct agg_node *rn);

extern void rfapiMonitorTimersRestart(struct rfapi_descriptor *rfd,
				      const struct prefix *p);

extern void rfapiMonitorItNodeChanged(struct rfapi_import_table *import_table,
				      struct agg_node *it_node,
				      struct rfapi_monitor_vpn *monitor_list);

extern void rfapiMonitorMovedUp(struct rfapi_import_table *import_table,
				struct agg_node *old_node,
				struct agg_node *new_node,
				struct rfapi_monitor_vpn *monitor_list);

extern struct agg_node *rfapiMonitorEthAdd(struct bgp *bgp,
					   struct rfapi_descriptor *rfd,
					   struct ethaddr *macaddr,
					   uint32_t logical_net_id);

extern void rfapiMonitorEthDel(struct bgp *bgp, struct rfapi_descriptor *rfd,
			       struct ethaddr *macaddr,
			       uint32_t logical_net_id);

#endif /* QUAGGA_HGP_RFAPI_MONITOR_H */
