/* BGP routing table
 * Copyright (C) 1998, 2001 Kunihiro Ishiguro
 *
 * This file is part of GNU Zebra.
 *
 * GNU Zebra 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, or (at your option) any
 * later version.
 *
 * GNU Zebra 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_BGP_TABLE_H
#define _QUAGGA_BGP_TABLE_H

/* XXX BEGIN TEMPORARY COMPAT */
#define bgp_dest bgp_node
/* XXX END TEMPORARY COMPAT */

#include "mpls.h"
#include "table.h"
#include "queue.h"
#include "linklist.h"
#include "bgpd.h"
#include "bgp_advertise.h"

struct bgp_table {
	/* table belongs to this instance */
	struct bgp *bgp;

	/* afi/safi of this table */
	afi_t afi;
	safi_t safi;

	int lock;

	struct route_table *route_table;
	uint64_t version;
};

enum bgp_path_selection_reason {
	bgp_path_selection_none,
	bgp_path_selection_first,
	bgp_path_selection_evpn_sticky_mac,
	bgp_path_selection_evpn_seq,
	bgp_path_selection_evpn_local_path,
	bgp_path_selection_evpn_non_proxy,
	bgp_path_selection_evpn_lower_ip,
	bgp_path_selection_weight,
	bgp_path_selection_local_pref,
	bgp_path_selection_local_route,
	bgp_path_selection_confed_as_path,
	bgp_path_selection_as_path,
	bgp_path_selection_origin,
	bgp_path_selection_med,
	bgp_path_selection_peer,
	bgp_path_selection_confed,
	bgp_path_selection_igp_metric,
	bgp_path_selection_older,
	bgp_path_selection_router_id,
	bgp_path_selection_cluster_length,
	bgp_path_selection_stale,
	bgp_path_selection_local_configured,
	bgp_path_selection_neighbor_ip,
	bgp_path_selection_default,
};

struct bgp_node {
	/*
	 * CAUTION
	 *
	 * These fields must be the very first fields in this structure.
	 *
	 * @see bgp_node_to_rnode
	 * @see bgp_node_from_rnode
	 */
	ROUTE_NODE_FIELDS

	struct bgp_adj_out_rb adj_out;

	struct bgp_adj_in *adj_in;

	struct bgp_dest *pdest;

	STAILQ_ENTRY(bgp_dest) pq;

	uint64_t version;

	mpls_label_t local_label;

	uint8_t flags;
#define BGP_NODE_PROCESS_SCHEDULED	(1 << 0)
#define BGP_NODE_USER_CLEAR             (1 << 1)
#define BGP_NODE_LABEL_CHANGED          (1 << 2)
#define BGP_NODE_REGISTERED_FOR_LABEL   (1 << 3)
#define BGP_NODE_SELECT_DEFER           (1 << 4)
	/* list node pointer */
	struct listnode *rt_node;
	struct bgp_addpath_node_data tx_addpath;

	enum bgp_path_selection_reason reason;
};

extern void bgp_delete_listnode(struct bgp_dest *dest);
/*
 * bgp_table_iter_t
 *
 * Structure that holds state for iterating over a bgp table.
 */
typedef struct bgp_table_iter_t_ {
	struct bgp_table *table;
	route_table_iter_t rt_iter;
} bgp_table_iter_t;

extern struct bgp_table *bgp_table_init(struct bgp *bgp, afi_t, safi_t);
extern void bgp_table_lock(struct bgp_table *);
extern void bgp_table_unlock(struct bgp_table *);
extern void bgp_table_finish(struct bgp_table **);


/*
 * bgp_dest_from_rnode
 *
 * Returns the bgp_dest structure corresponding to a route_node.
 */
static inline struct bgp_dest *bgp_dest_from_rnode(struct route_node *rnode)
{
	return (struct bgp_dest *)rnode;
}

/*
 * bgp_dest_to_rnode
 *
 * Returns the route_node structure corresponding to a bgp_dest.
 */
static inline struct route_node *bgp_dest_to_rnode(const struct bgp_dest *dest)
{
	return (struct route_node *)dest;
}

/*
 * bgp_dest_table
 *
 * Returns the bgp_table that the given dest is in.
 */
static inline struct bgp_table *bgp_dest_table(struct bgp_dest *dest)
{
	return route_table_get_info(bgp_dest_to_rnode(dest)->table);
}

/*
 * bgp_dest_parent_nolock
 *
 * Gets the parent dest of the given node without locking it.
 */
static inline struct bgp_dest *bgp_dest_parent_nolock(struct bgp_dest *dest)
{
	struct route_node *rn = bgp_dest_to_rnode(dest)->parent;

	return bgp_dest_from_rnode(rn);
}

/*
 * bgp_dest_unlock_node
 */
static inline void bgp_dest_unlock_node(struct bgp_dest *dest)
{
	bgp_delete_listnode(dest);
	route_unlock_node(bgp_dest_to_rnode(dest));
}

/*
 * bgp_table_top_nolock
 *
 * Gets the top dest in the table without locking it.
 *
 * @see bgp_table_top
 */
static inline struct bgp_dest *
bgp_table_top_nolock(const struct bgp_table *const table)
{
	return bgp_dest_from_rnode(table->route_table->top);
}

/*
 * bgp_table_top
 */
static inline struct bgp_dest *
bgp_table_top(const struct bgp_table *const table)
{
	return bgp_dest_from_rnode(route_top(table->route_table));
}

/*
 * bgp_route_next
 */
static inline struct bgp_dest *bgp_route_next(struct bgp_dest *dest)
{
	return bgp_dest_from_rnode(route_next(bgp_dest_to_rnode(dest)));
}

/*
 * bgp_route_next_until
 */
static inline struct bgp_dest *bgp_route_next_until(struct bgp_dest *dest,
						    struct bgp_dest *limit)
{
	struct route_node *rnode;

	rnode = route_next_until(bgp_dest_to_rnode(dest),
			bgp_dest_to_rnode(limit));

	return bgp_dest_from_rnode(rnode);
}

/*
 * bgp_node_get
 */
static inline struct bgp_dest *bgp_node_get(struct bgp_table *const table,
					    const struct prefix *p)
{
	return bgp_dest_from_rnode(route_node_get(table->route_table, p));
}

/*
 * bgp_node_lookup
 */
static inline struct bgp_dest *
bgp_node_lookup(const struct bgp_table *const table, const struct prefix *p)
{
	struct route_node *rn = route_node_lookup(table->route_table, p);

	return bgp_dest_from_rnode(rn);
}

/*
 * bgp_dest_lock_node
 */
static inline struct bgp_dest *bgp_dest_lock_node(struct bgp_dest *dest)
{
	struct route_node *rn = route_lock_node(bgp_dest_to_rnode(dest));

	return bgp_dest_from_rnode(rn);
}

/*
 * bgp_node_match
 */
static inline struct bgp_dest *bgp_node_match(const struct bgp_table *table,
					      const struct prefix *p)
{
	struct route_node *rn = route_node_match(table->route_table, p);

	return bgp_dest_from_rnode(rn);
}

/*
 * bgp_node_match_ipv4
 */
static inline struct bgp_dest *
bgp_node_match_ipv4(const struct bgp_table *table, struct in_addr *addr)
{
	struct route_node *rn = route_node_match_ipv4(table->route_table, addr);

	return bgp_dest_from_rnode(rn);
}

/*
 * bgp_node_match_ipv6
 */
static inline struct bgp_dest *
bgp_node_match_ipv6(const struct bgp_table *table, struct in6_addr *addr)
{
	struct route_node *rn = route_node_match_ipv6(table->route_table, addr);

	return bgp_dest_from_rnode(rn);
}

static inline unsigned long bgp_table_count(const struct bgp_table *const table)
{
	return route_table_count(table->route_table);
}

/*
 * bgp_table_get_next
 */
static inline struct bgp_dest *bgp_table_get_next(const struct bgp_table *table,
						  const struct prefix *p)
{
	return bgp_dest_from_rnode(route_table_get_next(table->route_table, p));
}

/*
 * bgp_table_iter_init
 */
static inline void bgp_table_iter_init(bgp_table_iter_t *iter,
				       struct bgp_table *table)
{
	bgp_table_lock(table);
	iter->table = table;
	route_table_iter_init(&iter->rt_iter, table->route_table);
}

/*
 * bgp_table_iter_next
 */
static inline struct bgp_dest *bgp_table_iter_next(bgp_table_iter_t *iter)
{
	return bgp_dest_from_rnode(route_table_iter_next(&iter->rt_iter));
}

/*
 * bgp_table_iter_cleanup
 */
static inline void bgp_table_iter_cleanup(bgp_table_iter_t *iter)
{
	route_table_iter_cleanup(&iter->rt_iter);
	bgp_table_unlock(iter->table);
	iter->table = NULL;
}

/*
 * bgp_table_iter_pause
 */
static inline void bgp_table_iter_pause(bgp_table_iter_t *iter)
{
	route_table_iter_pause(&iter->rt_iter);
}

/*
 * bgp_table_iter_is_done
 */
static inline int bgp_table_iter_is_done(bgp_table_iter_t *iter)
{
	return route_table_iter_is_done(&iter->rt_iter);
}

/*
 * bgp_table_iter_started
 */
static inline int bgp_table_iter_started(bgp_table_iter_t *iter)
{
	return route_table_iter_started(&iter->rt_iter);
}

/* This would benefit from a real atomic operation...
 * until then. */
static inline uint64_t bgp_table_next_version(struct bgp_table *table)
{
	return ++table->version;
}

static inline uint64_t bgp_table_version(struct bgp_table *table)
{
	return table->version;
}

/* Find the subtree of the prefix p
 *
 * This will return the first node that belongs the the subtree of p. Including
 * p itself, if it is in the tree.
 *
 * If the subtree is not present in the table, NULL is returned.
 */
struct bgp_dest *bgp_table_subtree_lookup(const struct bgp_table *table,
					  const struct prefix *p);

static inline struct bgp_aggregate *
bgp_dest_get_bgp_aggregate_info(struct bgp_dest *dest)
{
	return dest ? dest->info : NULL;
}

static inline void
bgp_dest_set_bgp_aggregate_info(struct bgp_dest *dest,
				struct bgp_aggregate *aggregate)
{
	dest->info = aggregate;
}

static inline struct bgp_distance *
bgp_dest_get_bgp_distance_info(struct bgp_dest *dest)
{
	return dest ? dest->info : NULL;
}

static inline void bgp_dest_set_bgp_distance_info(struct bgp_dest *dest,
						  struct bgp_distance *distance)
{
	dest->info = distance;
}

static inline struct bgp_static *
bgp_dest_get_bgp_static_info(struct bgp_dest *dest)
{
	return dest ? dest->info : NULL;
}

static inline void bgp_dest_set_bgp_static_info(struct bgp_dest *dest,
						struct bgp_static *bgp_static)
{
	dest->info = bgp_static;
}

static inline struct bgp_connected_ref *
bgp_dest_get_bgp_connected_ref_info(struct bgp_dest *dest)
{
	return dest ? dest->info : NULL;
}

static inline void
bgp_dest_set_bgp_connected_ref_info(struct bgp_dest *dest,
				    struct bgp_connected_ref *bc)
{
	dest->info = bc;
}

static inline struct bgp_nexthop_cache *
bgp_dest_get_bgp_nexthop_info(struct bgp_dest *dest)
{
	return dest ? dest->info : NULL;
}

static inline void bgp_dest_set_bgp_nexthop_info(struct bgp_dest *dest,
						 struct bgp_nexthop_cache *bnc)
{
	dest->info = bnc;
}

static inline struct bgp_path_info *
bgp_dest_get_bgp_path_info(struct bgp_dest *dest)
{
	return dest ? dest->info : NULL;
}

static inline void bgp_dest_set_bgp_path_info(struct bgp_dest *dest,
					      struct bgp_path_info *bi)
{
	dest->info = bi;
}

static inline struct bgp_table *
bgp_dest_get_bgp_table_info(struct bgp_dest *dest)
{
	return dest->info;
}

static inline void bgp_dest_set_bgp_table_info(struct bgp_dest *dest,
					       struct bgp_table *table)
{
	dest->info = table;
}

static inline bool bgp_dest_has_bgp_path_info_data(struct bgp_dest *dest)
{
	return !!dest->info;
}

static inline const struct prefix *bgp_dest_get_prefix(const struct bgp_dest *dest)
{
	return &dest->p;
}

#ifdef _FRR_ATTRIBUTE_PRINTFRR
#pragma FRR printfrr_ext "%pRN"  (struct bgp_node *)
#endif

#endif /* _QUAGGA_BGP_TABLE_H */
