// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Zebra VxLAN (EVPN) Data structures and definitions
 * These are "internal" to this function.
 * Copyright (C) 2016, 2017 Cumulus Networks, Inc.
 */

#ifndef _ZEBRA_VXLAN_PRIVATE_H
#define _ZEBRA_VXLAN_PRIVATE_H

#include <zebra.h>

#include "if.h"
#include "linklist.h"
#include "zebra_vxlan.h"
#include "zebra_vxlan_if.h"
#include "zebra_evpn.h"
#include "zebra_evpn_mac.h"

#ifdef __cplusplus
extern "C" {
#endif

#define ERR_STR_SZ 256

/* L3 VNI hash table */
struct zebra_l3vni {

	/* VNI key */
	vni_t vni;

	/* vrf_id */
	vrf_id_t vrf_id;

	uint32_t filter;
#define PREFIX_ROUTES_ONLY	(1 << 0) /* l3-vni used for prefix routes only */

	/* Corresponding Bridge information */
	vlanid_t vid;
	struct interface *bridge_if;

	/* Local IP */
	struct in_addr local_vtep_ip;

	/* kernel interface for l3vni */
	struct interface *vxlan_if;

	/* SVI interface corresponding to the l3vni */
	struct interface *svi_if;

	struct interface *mac_vlan_if;

	/* list of L2 VNIs associated with the L3 VNI */
	struct list *l2vnis;

	/* list of remote router-macs */
	struct hash *rmac_table;

	/* list of remote vtep-ip neigh */
	struct hash *nh_table;
};

#define IS_ZL3VNI_SVD_BACKED(zl3vni)                                           \
	(zl3vni->vxlan_if && zl3vni->vxlan_if->info &&                         \
	 IS_ZEBRA_VXLAN_IF_SVD((struct zebra_if *)zl3vni->vxlan_if->info))

/* get the vx-intf name for l3vni */
static inline const char *zl3vni_vxlan_if_name(struct zebra_l3vni *zl3vni)
{
	return zl3vni->vxlan_if ? zl3vni->vxlan_if->name : "None";
}

/* get the svi intf name for l3vni */
static inline const char *zl3vni_svi_if_name(struct zebra_l3vni *zl3vni)
{
	return zl3vni->svi_if ? zl3vni->svi_if->name : "None";
}

/* get the vrf name for l3vni */
static inline const char *zl3vni_vrf_name(struct zebra_l3vni *zl3vni)
{
	return vrf_id_to_name(zl3vni->vrf_id);
}

/* get the rmac string */
static inline const char *zl3vni_rmac2str(struct zebra_l3vni *zl3vni, char *buf,
					  int size)
{
	char *ptr;

	if (!buf)
		ptr = XMALLOC(MTYPE_TMP, ETHER_ADDR_STRLEN * sizeof(char));
	else {
		assert(size >= ETHER_ADDR_STRLEN);
		ptr = buf;
	}

	if (zl3vni->mac_vlan_if)
		snprintf(ptr, (ETHER_ADDR_STRLEN),
			 "%02x:%02x:%02x:%02x:%02x:%02x",
			 (uint8_t)zl3vni->mac_vlan_if->hw_addr[0],
			 (uint8_t)zl3vni->mac_vlan_if->hw_addr[1],
			 (uint8_t)zl3vni->mac_vlan_if->hw_addr[2],
			 (uint8_t)zl3vni->mac_vlan_if->hw_addr[3],
			 (uint8_t)zl3vni->mac_vlan_if->hw_addr[4],
			 (uint8_t)zl3vni->mac_vlan_if->hw_addr[5]);
	else if (zl3vni->svi_if)
		snprintf(ptr, (ETHER_ADDR_STRLEN),
			 "%02x:%02x:%02x:%02x:%02x:%02x",
			 (uint8_t)zl3vni->svi_if->hw_addr[0],
			 (uint8_t)zl3vni->svi_if->hw_addr[1],
			 (uint8_t)zl3vni->svi_if->hw_addr[2],
			 (uint8_t)zl3vni->svi_if->hw_addr[3],
			 (uint8_t)zl3vni->svi_if->hw_addr[4],
			 (uint8_t)zl3vni->svi_if->hw_addr[5]);
	else
		snprintf(ptr, ETHER_ADDR_STRLEN, "None");

	return ptr;
}

/* get the sys mac string */
static inline const char *zl3vni_sysmac2str(struct zebra_l3vni *zl3vni,
					    char *buf, int size)
{
	char *ptr;

	if (!buf)
		ptr = XMALLOC(MTYPE_TMP, ETHER_ADDR_STRLEN * sizeof(char));
	else {
		assert(size >= ETHER_ADDR_STRLEN);
		ptr = buf;
	}

	if (zl3vni->svi_if)
		snprintf(ptr, (ETHER_ADDR_STRLEN),
			 "%02x:%02x:%02x:%02x:%02x:%02x",
			 (uint8_t)zl3vni->svi_if->hw_addr[0],
			 (uint8_t)zl3vni->svi_if->hw_addr[1],
			 (uint8_t)zl3vni->svi_if->hw_addr[2],
			 (uint8_t)zl3vni->svi_if->hw_addr[3],
			 (uint8_t)zl3vni->svi_if->hw_addr[4],
			 (uint8_t)zl3vni->svi_if->hw_addr[5]);
	else
		snprintf(ptr, ETHER_ADDR_STRLEN, "None");

	return ptr;
}

/*
 * l3-vni is oper up when:
 * 0. if EVPN is enabled (advertise-all-vni cfged)
 * 1. it is associated to a vxlan-intf
 * 2. Associated vxlan-intf is oper up
 * 3. it is associated to an SVI
 * 4. associated SVI is oper up
 */
static inline int is_l3vni_oper_up(struct zebra_l3vni *zl3vni)
{
	return (is_evpn_enabled() && zl3vni && (zl3vni->vrf_id != VRF_UNKNOWN)
		&& zl3vni->vxlan_if && if_is_operative(zl3vni->vxlan_if)
		&& zl3vni->svi_if && if_is_operative(zl3vni->svi_if));
}

static inline const char *zl3vni_state2str(struct zebra_l3vni *zl3vni)
{
	if (!zl3vni)
		return NULL;

	if (is_l3vni_oper_up(zl3vni))
		return "Up";
	else
		return "Down";

	return NULL;
}

static inline vrf_id_t zl3vni_vrf_id(struct zebra_l3vni *zl3vni)
{
	return zl3vni->vrf_id;
}

static inline void zl3vni_get_svi_rmac(struct zebra_l3vni *zl3vni,
				       struct ethaddr *rmac)
{
	if (!zl3vni)
		return;

	if (!is_l3vni_oper_up(zl3vni))
		return;

	if (zl3vni->svi_if && if_is_operative(zl3vni->svi_if))
		memcpy(rmac->octet, zl3vni->svi_if->hw_addr, ETH_ALEN);
}


/* context for neigh hash walk - update l3vni and rmac */
struct neigh_l3info_walk_ctx {

	struct zebra_evpn *zevpn;
	struct zebra_l3vni *zl3vni;
	int add;
};

struct nh_walk_ctx {

	struct vty *vty;
	struct json_object *json;
};

extern struct zebra_l3vni *zl3vni_from_vrf(vrf_id_t vrf_id);
extern struct interface *zl3vni_map_to_vxlan_if(struct zebra_l3vni *zl3vni);
extern struct interface *zl3vni_map_to_svi_if(struct zebra_l3vni *zl3vni);
extern struct interface *zl3vni_map_to_mac_vlan_if(struct zebra_l3vni *zl3vni);
extern struct zebra_l3vni *zl3vni_lookup(vni_t vni);
extern vni_t vni_id_from_svi(struct interface *ifp, struct interface *br_if);

DECLARE_HOOK(zebra_rmac_update,
	     (struct zebra_mac * rmac, struct zebra_l3vni *zl3vni, bool delete,
	      const char *reason),
	     (rmac, zl3vni, delete, reason));


#ifdef __cplusplus
}
#endif

/*
 * Multicast hash table.
 *
 * This table contains -
 * 1. The (S, G) entries used for encapsulating and forwarding BUM traffic.
 *    S is the local VTEP-IP and G is a BUM mcast group address.
 * 2. The (X, G) entries used for terminating a BUM flow.
 * Multiple L2-VNIs can share the same MDT hence the need to maintain
 * an aggregated table that pimd can consume without much
 * re-interpretation.
 */
struct zebra_vxlan_sg {
	struct zebra_vrf *zvrf;

	struct prefix_sg sg;
	char sg_str[PREFIX_SG_STR_LEN];

	/* For SG - num of L2 VNIs using this entry for sending BUM traffic */
	/* For XG - num of SG using this as parent */
	uint32_t ref_cnt;
};

extern struct zebra_evpn *zevpn_lookup(vni_t vni);
extern void zebra_vxlan_sync_mac_dp_install(struct zebra_mac *mac,
					    bool set_inactive,
					    bool force_clear_static,
					    const char *caller);
extern bool zebra_evpn_do_dup_addr_detect(struct zebra_vrf *zvrf);
extern void zebra_vxlan_sg_ref(struct in_addr local_vtep_ip,
			       struct in_addr mcast_grp);
extern void zebra_vxlan_sg_deref(struct in_addr local_vtep_ip,
				 struct in_addr mcast_grp);
extern void zebra_vxlan_process_l3vni_oper_up(struct zebra_l3vni *zl3vni);
extern void zebra_vxlan_process_l3vni_oper_down(struct zebra_l3vni *zl3vni);
extern int zebra_evpn_vxlan_del(struct zebra_evpn *zevpn);

#endif /* _ZEBRA_VXLAN_PRIVATE_H */