// SPDX-License-Identifier: GPL-2.0-or-later
/* Route map function.
 * Copyright (C) 1998, 1999 Kunihiro Ishiguro
 */

#include <zebra.h>

#include "linklist.h"
#include "memory.h"
#include "command.h"
#include "vector.h"
#include "prefix.h"
#include "vty.h"
#include "routemap.h"
#include "command.h"
#include "log.h"
#include "hash.h"
#include "libfrr.h"
#include "lib_errors.h"
#include "table.h"
#include "json.h"
#include "jhash.h"

#include "lib/routemap_clippy.c"

DEFINE_MTYPE_STATIC(LIB, ROUTE_MAP, "Route map");
DEFINE_MTYPE(LIB, ROUTE_MAP_NAME, "Route map name");
DEFINE_MTYPE_STATIC(LIB, ROUTE_MAP_INDEX, "Route map index");
DEFINE_MTYPE(LIB, ROUTE_MAP_RULE, "Route map rule");
DEFINE_MTYPE_STATIC(LIB, ROUTE_MAP_RULE_STR, "Route map rule str");
DEFINE_MTYPE(LIB, ROUTE_MAP_COMPILED, "Route map compiled");
DEFINE_MTYPE_STATIC(LIB, ROUTE_MAP_DEP, "Route map dependency");
DEFINE_MTYPE_STATIC(LIB, ROUTE_MAP_DEP_DATA, "Route map dependency data");

DEFINE_QOBJ_TYPE(route_map_index);
DEFINE_QOBJ_TYPE(route_map);

static int rmap_cmd_name_cmp(const struct route_map_rule_cmd_proxy *a,
			     const struct route_map_rule_cmd_proxy *b)
{
	return strcmp(a->cmd->str, b->cmd->str);
}

static uint32_t rmap_cmd_name_hash(const struct route_map_rule_cmd_proxy *item)
{
	return jhash(item->cmd->str, strlen(item->cmd->str), 0xbfd69320);
}

DECLARE_HASH(rmap_cmd_name, struct route_map_rule_cmd_proxy, itm,
	     rmap_cmd_name_cmp, rmap_cmd_name_hash);

static struct rmap_cmd_name_head rmap_match_cmds[1] = {
	INIT_HASH(rmap_match_cmds[0]),
};
static struct rmap_cmd_name_head rmap_set_cmds[1] = {
	INIT_HASH(rmap_set_cmds[0]),
};

#define IPv4_PREFIX_LIST "ip address prefix-list"
#define IPv6_PREFIX_LIST "ipv6 address prefix-list"

#define IS_RULE_IPv4_PREFIX_LIST(S)                                            \
	(strncmp(S, IPv4_PREFIX_LIST, strlen(IPv4_PREFIX_LIST)) == 0)
#define IS_RULE_IPv6_PREFIX_LIST(S)                                            \
	(strncmp(S, IPv6_PREFIX_LIST, strlen(IPv6_PREFIX_LIST)) == 0)

struct route_map_pentry_dep {
	struct prefix_list_entry *pentry;
	const char *plist_name;
	route_map_event_t event;
};

static void route_map_pfx_tbl_update(route_map_event_t event,
				     struct route_map_index *index, afi_t afi,
				     const char *plist_name);
static void route_map_pfx_table_add_default(afi_t afi,
					    struct route_map_index *index);
static void route_map_pfx_table_del_default(afi_t afi,
					    struct route_map_index *index);
static void route_map_add_plist_entries(afi_t afi,
					struct route_map_index *index,
					const char *plist_name,
					struct prefix_list_entry *entry);
static void route_map_del_plist_entries(afi_t afi,
					struct route_map_index *index,
					const char *plist_name,
					struct prefix_list_entry *entry);

static struct hash *route_map_get_dep_hash(route_map_event_t event);
static void route_map_free_map(struct route_map *map);

struct route_map_match_set_hooks rmap_match_set_hook;

/* match interface */
void route_map_match_interface_hook(int (*func)(
	struct route_map_index *index, const char *command,
	const char *arg, route_map_event_t type,
	char *errmsg, size_t errmsg_len))
{
	rmap_match_set_hook.match_interface = func;
}

/* no match interface */
void route_map_no_match_interface_hook(int (*func)(
	struct route_map_index *index, const char *command,
	const char *arg, route_map_event_t type,
	char *errmsg, size_t errmsg_len))
{
	rmap_match_set_hook.no_match_interface = func;
}

/* match ip address */
void route_map_match_ip_address_hook(int (*func)(
	struct route_map_index *index, const char *command,
	const char *arg, route_map_event_t type,
	char *errmsg, size_t errmsg_len))
{
	rmap_match_set_hook.match_ip_address = func;
}

/* no match ip address */
void route_map_no_match_ip_address_hook(int (*func)(
	struct route_map_index *index, const char *command,
	const char *arg, route_map_event_t type,
	char *errmsg, size_t errmsg_len))
{
	rmap_match_set_hook.no_match_ip_address = func;
}

/* match ip address prefix list */
void route_map_match_ip_address_prefix_list_hook(int (*func)(
	struct route_map_index *index, const char *command,
	const char *arg, route_map_event_t type,
	char *errmsg, size_t errmsg_len))
{
	rmap_match_set_hook.match_ip_address_prefix_list = func;
}

/* no match ip address prefix list */
void route_map_no_match_ip_address_prefix_list_hook(int (*func)(
	struct route_map_index *index, const char *command,
	const char *arg, route_map_event_t type,
	char *errmsg, size_t errmsg_len))
{
	rmap_match_set_hook.no_match_ip_address_prefix_list = func;
}

/* match ip next hop */
void route_map_match_ip_next_hop_hook(int (*func)(
	struct route_map_index *index, const char *command,
	const char *arg, route_map_event_t type,
	char *errmsg, size_t errmsg_len))
{
	rmap_match_set_hook.match_ip_next_hop = func;
}

/* no match ip next hop */
void route_map_no_match_ip_next_hop_hook(int (*func)(
	struct route_map_index *index, const char *command,
	const char *arg, route_map_event_t type,
	char *errmsg, size_t errmsg_len))
{
	rmap_match_set_hook.no_match_ip_next_hop = func;
}

/* match ipv6 next-hop */
void route_map_match_ipv6_next_hop_hook(int (*func)(
	struct route_map_index *index, const char *command, const char *arg,
	route_map_event_t type, char *errmsg, size_t errmsg_len))
{
	rmap_match_set_hook.match_ipv6_next_hop = func;
}

/* no match ipv6 next-hop */
void route_map_no_match_ipv6_next_hop_hook(int (*func)(
	struct route_map_index *index, const char *command, const char *arg,
	route_map_event_t type, char *errmsg, size_t errmsg_len))
{
	rmap_match_set_hook.no_match_ipv6_next_hop = func;
}

/* match ip next hop prefix list */
void route_map_match_ip_next_hop_prefix_list_hook(int (*func)(
	struct route_map_index *index, const char *command,
	const char *arg, route_map_event_t type,
	char *errmsg, size_t errmsg_len))
{
	rmap_match_set_hook.match_ip_next_hop_prefix_list = func;
}

/* no match ip next hop prefix list */
void route_map_no_match_ip_next_hop_prefix_list_hook(int (*func)(
	struct route_map_index *index, const char *command,
	const char *arg, route_map_event_t type,
	char *errmsg, size_t errmsg_len))
{
	rmap_match_set_hook.no_match_ip_next_hop_prefix_list = func;
}

/* match ip next-hop type */
void route_map_match_ip_next_hop_type_hook(int (*func)(
	struct route_map_index *index, const char *command,
	const char *arg, route_map_event_t type,
	char *errmsg, size_t errmsg_len))
{
	rmap_match_set_hook.match_ip_next_hop_type = func;
}

/* no match ip next-hop type */
void route_map_no_match_ip_next_hop_type_hook(int (*func)(
	struct route_map_index *index, const char *command,
	const char *arg, route_map_event_t type,
	char *errmsg, size_t errmsg_len))
{
	rmap_match_set_hook.no_match_ip_next_hop_type = func;
}

/* match ipv6 address */
void route_map_match_ipv6_address_hook(int (*func)(
	struct route_map_index *index, const char *command,
	const char *arg, route_map_event_t type,
	char *errmsg, size_t errmsg_len))
{
	rmap_match_set_hook.match_ipv6_address = func;
}

/* no match ipv6 address */
void route_map_no_match_ipv6_address_hook(int (*func)(
	struct route_map_index *index, const char *command,
	const char *arg, route_map_event_t type,
	char *errmsg, size_t errmsg_len))
{
	rmap_match_set_hook.no_match_ipv6_address = func;
}


/* match ipv6 address prefix list */
void route_map_match_ipv6_address_prefix_list_hook(int (*func)(
	struct route_map_index *index, const char *command,
	const char *arg, route_map_event_t type,
	char *errmsg, size_t errmsg_len))
{
	rmap_match_set_hook.match_ipv6_address_prefix_list = func;
}

/* no match ipv6 address prefix list */
void route_map_no_match_ipv6_address_prefix_list_hook(int (*func)(
	struct route_map_index *index, const char *command,
	const char *arg, route_map_event_t type,
	char *errmsg, size_t errmsg_len))
{
	rmap_match_set_hook.no_match_ipv6_address_prefix_list = func;
}

/* match ipv6 next-hop type */
void route_map_match_ipv6_next_hop_type_hook(int (*func)(
	struct route_map_index *index, const char *command,
	const char *arg, route_map_event_t type,
	char *errmsg, size_t errmsg_len))
{
	rmap_match_set_hook.match_ipv6_next_hop_type = func;
}

/* no match ipv6 next-hop type */
void route_map_no_match_ipv6_next_hop_type_hook(int (*func)(
	struct route_map_index *index, const char *command,
	const char *arg, route_map_event_t type,
	char *errmsg, size_t errmsg_len))
{
	rmap_match_set_hook.no_match_ipv6_next_hop_type = func;
}

/* match ipv6 next-hop prefix-list */
void route_map_match_ipv6_next_hop_prefix_list_hook(int (*func)(
	struct route_map_index *index, const char *command, const char *arg,
	route_map_event_t type, char *errmsg, size_t errmsg_len))
{
	rmap_match_set_hook.match_ipv6_next_hop_prefix_list = func;
}

/* no match ipv6 next-hop prefix-list */
void route_map_no_match_ipv6_next_hop_prefix_list_hook(int (*func)(
	struct route_map_index *index, const char *command, const char *arg,
	route_map_event_t type, char *errmsg, size_t errmsg_len))
{
	rmap_match_set_hook.no_match_ipv6_next_hop_prefix_list = func;
}

/* match metric */
void route_map_match_metric_hook(int (*func)(
	struct route_map_index *index, const char *command,
	const char *arg, route_map_event_t type,
	char *errmsg, size_t errmsg_len))
{
	rmap_match_set_hook.match_metric = func;
}

/* no match metric */
void route_map_no_match_metric_hook(int (*func)(
	struct route_map_index *index, const char *command,
	const char *arg, route_map_event_t type,
	char *errmsg, size_t errmsg_len))
{
	rmap_match_set_hook.no_match_metric = func;
}

/* match tag */
void route_map_match_tag_hook(int (*func)(struct route_map_index *index,
					  const char *command, const char *arg,
					  route_map_event_t type,
					  char *errmsg, size_t errmsg_len))
{
	rmap_match_set_hook.match_tag = func;
}

/* no match tag */
void route_map_no_match_tag_hook(int (*func)(
	struct route_map_index *index, const char *command,
	const char *arg, route_map_event_t type,
	char *errmsg, size_t errmsg_len))
{
	rmap_match_set_hook.no_match_tag = func;
}

/* set sr-te color */
void route_map_set_srte_color_hook(int (*func)(struct route_map_index *index,
					       const char *command,
					       const char *arg,
					       char *errmsg, size_t errmsg_len))
{
	rmap_match_set_hook.set_srte_color = func;
}

/* no set sr-te color */
void route_map_no_set_srte_color_hook(int (*func)(struct route_map_index *index,
						  const char *command,
						  const char *arg,
						  char *errmsg, size_t errmsg_len))
{
	rmap_match_set_hook.no_set_srte_color = func;
}

/* set ip nexthop */
void route_map_set_ip_nexthop_hook(int (*func)(struct route_map_index *index,
					       const char *command,
					       const char *arg,
					       char *errmsg, size_t errmsg_len))
{
	rmap_match_set_hook.set_ip_nexthop = func;
}

/* no set ip nexthop */
void route_map_no_set_ip_nexthop_hook(int (*func)(struct route_map_index *index,
						  const char *command,
						  const char *arg,
						  char *errmsg,
						  size_t errmsg_len))
{
	rmap_match_set_hook.no_set_ip_nexthop = func;
}

/* set ipv6 nexthop local */
void route_map_set_ipv6_nexthop_local_hook(
	int (*func)(struct route_map_index *index,
		    const char *command, const char *arg,
		    char *errmsg, size_t errmsg_len))
{
	rmap_match_set_hook.set_ipv6_nexthop_local = func;
}

/* no set ipv6 nexthop local */
void route_map_no_set_ipv6_nexthop_local_hook(
	int (*func)(struct route_map_index *index,
		    const char *command, const char *arg,
		    char *errmsg, size_t errmsg_len))
{
	rmap_match_set_hook.no_set_ipv6_nexthop_local = func;
}

/* set metric */
void route_map_set_metric_hook(int (*func)(struct route_map_index *index,
					   const char *command,
					   const char *arg,
					   char *errmsg, size_t errmsg_len))
{
	rmap_match_set_hook.set_metric = func;
}

/* no set metric */
void route_map_no_set_metric_hook(int (*func)(struct route_map_index *index,
					      const char *command,
					      const char *arg,
					      char *errmsg, size_t errmsg_len))
{
	rmap_match_set_hook.no_set_metric = func;
}
/* set min-metric */
void route_map_set_min_metric_hook(int (*func)(struct route_map_index *index,
					       const char *command,
					       const char *arg, char *errmsg,
					       size_t errmsg_len))
{
	rmap_match_set_hook.set_min_metric = func;
}

/* no set min-metric */
void route_map_no_set_min_metric_hook(int (*func)(struct route_map_index *index,
						  const char *command,
						  const char *arg, char *errmsg,
						  size_t errmsg_len))
{
	rmap_match_set_hook.no_set_min_metric = func;
}
/* set max-metric */
void route_map_set_max_metric_hook(int (*func)(struct route_map_index *index,
					       const char *command,
					       const char *arg, char *errmsg,
					       size_t errmsg_len))
{
	rmap_match_set_hook.set_max_metric = func;
}

/* no set max-metric */
void route_map_no_set_max_metric_hook(int (*func)(struct route_map_index *index,
						  const char *command,
						  const char *arg, char *errmsg,
						  size_t errmsg_len))
{
	rmap_match_set_hook.no_set_max_metric = func;
}

/* set tag */
void route_map_set_tag_hook(int (*func)(struct route_map_index *index,
					const char *command, const char *arg,
					char *errmsg, size_t errmsg_len))
{
	rmap_match_set_hook.set_tag = func;
}

/* no set tag */
void route_map_no_set_tag_hook(int (*func)(struct route_map_index *index,
					   const char *command,
					   const char *arg,
					   char *errmsg, size_t errmsg_len))
{
	rmap_match_set_hook.no_set_tag = func;
}

int generic_match_add(struct route_map_index *index,
		      const char *command, const char *arg,
		      route_map_event_t type,
		      char *errmsg, size_t errmsg_len)
{
	enum rmap_compile_rets ret;

	ret = route_map_add_match(index, command, arg, type);
	switch (ret) {
	case RMAP_RULE_MISSING:
		snprintf(errmsg, errmsg_len, "%% [%s] Can't find rule.",
			 frr_protonameinst);
		return CMD_WARNING_CONFIG_FAILED;
	case RMAP_COMPILE_ERROR:
		snprintf(errmsg, errmsg_len,
			 "%% [%s] Argument form is unsupported or malformed.",
			 frr_protonameinst);
		return CMD_WARNING_CONFIG_FAILED;
	case RMAP_COMPILE_SUCCESS:
		/*
		 * Nothing to do here move along
		 */
		break;
	}

	return CMD_SUCCESS;
}

int generic_match_delete(struct route_map_index *index,
			 const char *command, const char *arg,
			 route_map_event_t type,
			 char *errmsg, size_t errmsg_len)
{
	enum rmap_compile_rets ret;
	int retval = CMD_SUCCESS;
	char *dep_name = NULL;
	const char *tmpstr;
	char *rmap_name = NULL;

	if (type != RMAP_EVENT_MATCH_DELETED) {
		/* ignore the mundane, the types without any dependency */
		if (arg == NULL) {
			if ((tmpstr = route_map_get_match_arg(index, command))
			    != NULL)
				dep_name =
					XSTRDUP(MTYPE_ROUTE_MAP_RULE, tmpstr);
		} else {
			dep_name = XSTRDUP(MTYPE_ROUTE_MAP_RULE, arg);
		}
		rmap_name = XSTRDUP(MTYPE_ROUTE_MAP_NAME, index->map->name);
	}

	ret = route_map_delete_match(index, command, dep_name, type);
	switch (ret) {
	case RMAP_RULE_MISSING:
		snprintf(errmsg, errmsg_len, "%% [%s] Can't find rule.",
			 frr_protonameinst);
		retval = CMD_WARNING_CONFIG_FAILED;
		break;
	case RMAP_COMPILE_ERROR:
		snprintf(errmsg, errmsg_len,
			 "%% [%s] Argument form is unsupported or malformed.",
			 frr_protonameinst);
		retval = CMD_WARNING_CONFIG_FAILED;
		break;
	case RMAP_COMPILE_SUCCESS:
		/*
		 * Nothing to do here
		 */
		break;
	}

	XFREE(MTYPE_ROUTE_MAP_RULE, dep_name);
	XFREE(MTYPE_ROUTE_MAP_NAME, rmap_name);

	return retval;
}

int generic_set_add(struct route_map_index *index,
		    const char *command, const char *arg,
		    char *errmsg, size_t errmsg_len)
{
	enum rmap_compile_rets ret;

	ret = route_map_add_set(index, command, arg);
	switch (ret) {
	case RMAP_RULE_MISSING:
		snprintf(errmsg, errmsg_len,
			 "%% [%s] Can't find rule.", frr_protonameinst);
		return CMD_WARNING_CONFIG_FAILED;
	case RMAP_COMPILE_ERROR:
		snprintf(errmsg, errmsg_len,
			 "%% [%s] Argument form is unsupported or malformed.",
			frr_protonameinst);
		return CMD_WARNING_CONFIG_FAILED;
	case RMAP_COMPILE_SUCCESS:
		break;
	}

	return CMD_SUCCESS;
}

int generic_set_delete(struct route_map_index *index,
		       const char *command, const char *arg,
		       char *errmsg, size_t errmsg_len)
{
	enum rmap_compile_rets ret;

	ret = route_map_delete_set(index, command, arg);
	switch (ret) {
	case RMAP_RULE_MISSING:
			snprintf(errmsg, errmsg_len, "%% [%s] Can't find rule.",
				 frr_protonameinst);
		return CMD_WARNING_CONFIG_FAILED;
	case RMAP_COMPILE_ERROR:
			snprintf(errmsg, errmsg_len,
				 "%%  [%s] Argument form is unsupported or malformed.",
				 frr_protonameinst);
		return CMD_WARNING_CONFIG_FAILED;
	case RMAP_COMPILE_SUCCESS:
		break;
	}

	return CMD_SUCCESS;
}


/* Master list of route map. */
struct route_map_list route_map_master = {NULL, NULL, NULL, NULL, NULL};
struct hash *route_map_master_hash = NULL;

static unsigned int route_map_hash_key_make(const void *p)
{
	const struct route_map *map = p;
	return string_hash_make(map->name);
}

static bool route_map_hash_cmp(const void *p1, const void *p2)
{
	const struct route_map *map1 = p1;
	const struct route_map *map2 = p2;

	if (!strcmp(map1->name, map2->name))
		return true;

	return false;
}

enum route_map_upd8_type {
	ROUTE_MAP_ADD = 1,
	ROUTE_MAP_DEL,
};

/* all possible route-map dependency types */
enum route_map_dep_type {
	ROUTE_MAP_DEP_RMAP = 1,
	ROUTE_MAP_DEP_CLIST,
	ROUTE_MAP_DEP_ECLIST,
	ROUTE_MAP_DEP_LCLIST,
	ROUTE_MAP_DEP_PLIST,
	ROUTE_MAP_DEP_ASPATH,
	ROUTE_MAP_DEP_FILTER,
	ROUTE_MAP_DEP_MAX,
};

struct route_map_dep {
	char *dep_name;
	struct hash *dep_rmap_hash;
	struct hash *this_hash; /* ptr to the hash structure this is part of */
};

struct route_map_dep_data {
	/* Route-map name.
	 */
	char *rname;
	/* Count of number of sequences of this
	 * route-map that depend on the same entity.
	 */
	uint16_t  refcnt;
};

/* Hashes maintaining dependency between various sublists used by route maps */
static struct hash *route_map_dep_hash[ROUTE_MAP_DEP_MAX];

static unsigned int route_map_dep_hash_make_key(const void *p);
static void route_map_clear_all_references(char *rmap_name);
static void route_map_rule_delete(struct route_map_rule_list *,
				  struct route_map_rule *);

uint32_t rmap_debug;

/* New route map allocation. Please note route map's name must be
   specified. */
static struct route_map *route_map_new(const char *name)
{
	struct route_map *new;

	new = XCALLOC(MTYPE_ROUTE_MAP, sizeof(struct route_map));
	new->name = XSTRDUP(MTYPE_ROUTE_MAP_NAME, name);
	QOBJ_REG(new, route_map);
	return new;
}

/* Add new name to route_map. */
static struct route_map *route_map_add(const char *name)
{
	struct route_map *map, *exist;
	struct route_map_list *list;

	map = route_map_new(name);
	list = &route_map_master;

	/*
	 * Add map to the hash
	 *
	 * If the map already exists in the hash, then we know that
	 * FRR is now in a sequence of delete/create.
	 * All FRR needs to do here is set the to_be_processed
	 * bit (to inherit from the old one
	 */
	exist = hash_release(route_map_master_hash, map);
	if (exist) {
		map->to_be_processed = exist->to_be_processed;
		route_map_free_map(exist);
	}
	hash_get(route_map_master_hash, map, hash_alloc_intern);

	/* Add new entry to the head of the list to match how it is added in the
	 * hash table. This is to ensure that if the same route-map has been
	 * created more than once and then marked for deletion (which can happen
	 * if prior deletions haven't completed as BGP hasn't yet done the
	 * route-map processing), the order of the entities is the same in both
	 * the list and the hash table. Otherwise, since there is nothing to
	 * distinguish between the two entries, the wrong entry could get freed.
	 * TODO: This needs to be re-examined to handle it better - e.g., revive
	 * a deleted entry if the route-map is created again.
	 */
	map->prev = NULL;
	map->next = list->head;
	if (list->head)
		list->head->prev = map;
	list->head = map;
	if (!list->tail)
		list->tail = map;

	/* Execute hook. */
	if (route_map_master.add_hook) {
		(*route_map_master.add_hook)(name);
		route_map_notify_dependencies(name, RMAP_EVENT_CALL_ADDED);
	}

	if (!map->ipv4_prefix_table)
		map->ipv4_prefix_table = route_table_init();

	if (!map->ipv6_prefix_table)
		map->ipv6_prefix_table = route_table_init();

	if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)))
		zlog_debug("Add route-map %s", name);
	return map;
}

/* this is supposed to be called post processing by
 * the delete hook function. Don't invoke delete_hook
 * again in this routine.
 */
static void route_map_free_map(struct route_map *map)
{
	struct route_map_list *list;
	struct route_map_index *index;

	if (map == NULL)
		return;

	while ((index = map->head) != NULL)
		route_map_index_delete(index, 0);

	if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)))
		zlog_debug("Deleting route-map %s", map->name);

	list = &route_map_master;

	QOBJ_UNREG(map);

	if (map->next)
		map->next->prev = map->prev;
	else
		list->tail = map->prev;

	if (map->prev)
		map->prev->next = map->next;
	else
		list->head = map->next;

	route_table_finish(map->ipv4_prefix_table);
	route_table_finish(map->ipv6_prefix_table);

	hash_release(route_map_master_hash, map);
	XFREE(MTYPE_ROUTE_MAP_NAME, map->name);
	XFREE(MTYPE_ROUTE_MAP, map);
}

/* Route map delete from list. */
void route_map_delete(struct route_map *map)
{
	struct route_map_index *index;
	char *name;

	while ((index = map->head) != NULL)
		route_map_index_delete(index, 0);

	name = map->name;
	map->head = NULL;

	/* Clear all dependencies */
	route_map_clear_all_references(name);
	map->deleted = true;
	/* Execute deletion hook. */
	if (route_map_master.delete_hook) {
		(*route_map_master.delete_hook)(name);
		route_map_notify_dependencies(name, RMAP_EVENT_CALL_DELETED);
	}

	if (!map->to_be_processed) {
		route_map_free_map(map);
	}
}

/* Lookup route map by route map name string. */
struct route_map *route_map_lookup_by_name(const char *name)
{
	struct route_map *map;
	struct route_map tmp_map;

	if (!name)
		return NULL;

	// map.deleted is false via memset
	memset(&tmp_map, 0, sizeof(tmp_map));
	tmp_map.name = XSTRDUP(MTYPE_ROUTE_MAP_NAME, name);
	map = hash_lookup(route_map_master_hash, &tmp_map);
	XFREE(MTYPE_ROUTE_MAP_NAME, tmp_map.name);

	if (map && map->deleted)
		return NULL;

	return map;
}

/* Simple helper to warn if route-map does not exist. */
struct route_map *route_map_lookup_warn_noexist(struct vty *vty, const char *name)
{
	struct route_map *route_map = route_map_lookup_by_name(name);

	if (!route_map)
		if (vty_shell_serv(vty))
			vty_out(vty, "The route-map '%s' does not exist.\n", name);

	return route_map;
}

int route_map_mark_updated(const char *name)
{
	struct route_map *map;
	int ret = -1;
	struct route_map tmp_map;

	if (!name)
		return (ret);

	map = route_map_lookup_by_name(name);

	/* If we did not find the routemap with deleted=false try again
	 * with deleted=true
	 */
	if (!map) {
		memset(&tmp_map, 0, sizeof(tmp_map));
		tmp_map.name = XSTRDUP(MTYPE_ROUTE_MAP_NAME, name);
		tmp_map.deleted = true;
		map = hash_lookup(route_map_master_hash, &tmp_map);
		XFREE(MTYPE_ROUTE_MAP_NAME, tmp_map.name);
	}

	if (map) {
		map->to_be_processed = true;
		ret = 0;
	}

	return (ret);
}

static void route_map_clear_updated(struct route_map *map)
{
	if (map) {
		map->to_be_processed = false;
		if (map->deleted)
			route_map_free_map(map);
	}
}

/* Lookup route map.  If there isn't route map create one and return
   it. */
struct route_map *route_map_get(const char *name)
{
	struct route_map *map;

	map = route_map_lookup_by_name(name);
	if (map == NULL)
		map = route_map_add(name);

	return map;
}

void route_map_walk_update_list(void (*route_map_update_fn)(char *name))
{
	struct route_map *node;
	struct route_map *nnode = NULL;

	for (node = route_map_master.head; node; node = nnode) {
		if (node->to_be_processed) {
			/* DD: Should we add any thread yield code here */
			route_map_update_fn(node->name);
			nnode = node->next;
			route_map_clear_updated(node);
		} else
			nnode = node->next;
	}
}

static const char *route_map_action_reason2str(enum route_map_action_reason reason)
{
	switch (reason) {
	case route_map_action_none:
		return "none";
	case route_map_action_map_null:
		return "route-map is null";
	case route_map_action_no_index:
		return "no index";
	case route_map_action_next_deny:
		return "next statement is deny";
	case route_map_action_exit:
		return "exit policy";
	case route_map_action_goto_null:
		return "goto index is null";
	case route_map_action_index_deny:
		return "deny index";
	}

	return "Invalid reason";
}

/* Return route map's type string. */
static const char *route_map_type_str(enum route_map_type type)
{
	switch (type) {
	case RMAP_PERMIT:
		return "permit";
	case RMAP_DENY:
		return "deny";
	case RMAP_ANY:
		return "";
	}

	return "";
}

static const char *route_map_cmd_result_str(enum route_map_cmd_result_t res)
{
	switch (res) {
	case RMAP_MATCH:
		return "match";
	case RMAP_NOMATCH:
		return "no match";
	case RMAP_NOOP:
		return "noop";
	case RMAP_ERROR:
		return "error";
	case RMAP_OKAY:
		return "okay";
	}

	return "invalid";
}

static const char *route_map_result_str(route_map_result_t res)
{
	switch (res) {
	case RMAP_DENYMATCH:
		return "deny";
	case RMAP_PERMITMATCH:
		return "permit";
	}

	return "invalid";
}

/* show route-map */
static void vty_show_route_map_entry(struct vty *vty, struct route_map *map,
				     json_object *json)
{
	struct route_map_index *index;
	struct route_map_rule *rule;
	json_object *json_rmap = NULL;
	json_object *json_rules = NULL;

	if (json) {
		json_rmap = json_object_new_object();
		json_object_object_add(json, map->name, json_rmap);

		json_rules = json_object_new_array();
		json_object_int_add(json_rmap, "invoked",
				    map->applied - map->applied_clear);
		json_object_boolean_add(json_rmap, "disabledOptimization",
					map->optimization_disabled);
		json_object_boolean_add(json_rmap, "processedChange",
					map->to_be_processed);
		json_object_object_add(json_rmap, "rules", json_rules);
		json_object_int_add(json_rmap, "cpuTimeMS", map->cputime / 1000);
	} else {
		vty_out(vty,
			"route-map: %s Invoked: %" PRIu64
			" (%zu milliseconds total) Optimization: %s Processed Change: %s\n",
			map->name, map->applied - map->applied_clear, map->cputime / 1000,
			map->optimization_disabled ? "disabled" : "enabled",
			map->to_be_processed ? "true" : "false");
	}

	for (index = map->head; index; index = index->next) {
		if (json) {
			json_object *json_rule;
			json_object *json_matches;
			json_object *json_sets;
			char action[BUFSIZ] = {};

			json_rule = json_object_new_object();
			json_object_array_add(json_rules, json_rule);

			json_object_int_add(json_rule, "sequenceNumber",
					    index->pref);
			json_object_string_add(json_rule, "type",
					       route_map_type_str(index->type));
			json_object_int_add(json_rule, "invoked",
					    index->applied
						    - index->applied_clear);
			json_object_int_add(json_rule, "cpuTimeMS", index->cputime / 1000);

			/* Description */
			if (index->description)
				json_object_string_add(json_rule, "description",
						       index->description);

			/* Match clauses */
			json_matches = json_object_new_array();
			json_object_object_add(json_rule, "matchClauses",
					       json_matches);
			for (rule = index->match_list.head; rule;
			     rule = rule->next) {
				char buf[BUFSIZ];

				snprintf(buf, sizeof(buf), "%s %s",
					 rule->cmd->str, rule->rule_str);
				json_array_string_add(json_matches, buf);
			}

			/* Set clauses */
			json_sets = json_object_new_array();
			json_object_object_add(json_rule, "setClauses",
					       json_sets);
			for (rule = index->set_list.head; rule;
			     rule = rule->next) {
				char buf[BUFSIZ];

				snprintf(buf, sizeof(buf), "%s %s",
					 rule->cmd->str, rule->rule_str);
				json_array_string_add(json_sets, buf);
			}

			/* Call clause */
			if (index->nextrm)
				json_object_string_add(json_rule, "callClause",
						       index->nextrm);

			/* Exit Policy */
			if (index->exitpolicy == RMAP_GOTO)
				snprintf(action, sizeof(action), "Goto %d",
					 index->nextpref);
			else if (index->exitpolicy == RMAP_NEXT)
				snprintf(action, sizeof(action),
					 "Continue to next entry");
			else if (index->exitpolicy == RMAP_EXIT)
				snprintf(action, sizeof(action),
					 "Exit routemap");
			if (action[0] != '\0')
				json_object_string_add(json_rule, "action",
						       action);
		} else {
			vty_out(vty,
				" %s, sequence %d Invoked %" PRIu64 " (%zu milliseconds total)\n",
				route_map_type_str(index->type), index->pref,
				index->applied - index->applied_clear, index->cputime / 1000);

			/* Description */
			if (index->description)
				vty_out(vty, "  Description:\n    %s\n",
					index->description);

			/* Match clauses */
			vty_out(vty, "  Match clauses:\n");
			for (rule = index->match_list.head; rule;
			     rule = rule->next)
				vty_out(vty, "    %s %s\n", rule->cmd->str,
					rule->rule_str);

			/* Set clauses */
			vty_out(vty, "  Set clauses:\n");
			for (rule = index->set_list.head; rule;
			     rule = rule->next)
				vty_out(vty, "    %s %s\n", rule->cmd->str,
					rule->rule_str);

			/* Call clause */
			vty_out(vty, "  Call clause:\n");
			if (index->nextrm)
				vty_out(vty, "    Call %s\n", index->nextrm);

			/* Exit Policy */
			vty_out(vty, "  Action:\n");
			if (index->exitpolicy == RMAP_GOTO)
				vty_out(vty, "    Goto %d\n", index->nextpref);
			else if (index->exitpolicy == RMAP_NEXT)
				vty_out(vty, "    Continue to next entry\n");
			else if (index->exitpolicy == RMAP_EXIT)
				vty_out(vty, "    Exit routemap\n");
		}
	}
}

static int sort_route_map(const void **map1, const void **map2)
{
	const struct route_map *m1 = *map1;
	const struct route_map *m2 = *map2;

	return strcmp(m1->name, m2->name);
}

static int vty_show_route_map(struct vty *vty, const char *name, bool use_json)
{
	struct route_map *map;
	json_object *json = NULL;

	if (use_json)
		json = json_object_new_object();
	else
		vty_out(vty, "%s:\n", frr_protonameinst);

	if (name) {
		map = route_map_lookup_by_name(name);

		if (map) {
			vty_show_route_map_entry(vty, map, json);
		} else if (!use_json) {
			vty_out(vty, "%s: 'route-map %s' not found\n",
				frr_protonameinst, name);
		}
	} else {

		struct list *maplist = list_new();
		struct listnode *ln;

		for (map = route_map_master.head; map; map = map->next)
			listnode_add(maplist, map);

		list_sort(maplist, sort_route_map);

		for (ALL_LIST_ELEMENTS_RO(maplist, ln, map))
			vty_show_route_map_entry(vty, map, json);

		list_delete(&maplist);
	}

	return vty_json(vty, json);
}

/* Unused route map details */
static int vty_show_unused_route_map(struct vty *vty)
{
	struct list *maplist = list_new();
	struct listnode *ln;
	struct route_map *map;

	for (map = route_map_master.head; map; map = map->next) {
		/* If use_count is zero, No protocol is using this routemap.
		 * so adding to the list.
		 */
		if (!map->use_count)
			listnode_add(maplist, map);
	}

	if (maplist->count > 0) {
		vty_out(vty, "\n%s:\n", frr_protonameinst);
		list_sort(maplist, sort_route_map);

		for (ALL_LIST_ELEMENTS_RO(maplist, ln, map))
			vty_show_route_map_entry(vty, map, NULL);
	} else {
		vty_out(vty, "\n%s: None\n", frr_protonameinst);
	}

	list_delete(&maplist);
	return CMD_SUCCESS;
}

/* New route map allocation. Please note route map's name must be
   specified. */
static struct route_map_index *route_map_index_new(void)
{
	struct route_map_index *new;

	new = XCALLOC(MTYPE_ROUTE_MAP_INDEX, sizeof(struct route_map_index));
	new->exitpolicy = RMAP_EXIT; /* Default to Cisco-style */
	TAILQ_INIT(&new->rhclist);
	QOBJ_REG(new, route_map_index);
	return new;
}

/* Free route map index. */
void route_map_index_delete(struct route_map_index *index, int notify)
{
	struct routemap_hook_context *rhc;
	struct route_map_rule *rule;

	QOBJ_UNREG(index);

	if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)))
		zlog_debug("Deleting route-map %s sequence %d",
			   index->map->name, index->pref);

	/* Free route map entry description. */
	XFREE(MTYPE_TMP, index->description);

	/* Free route map northbound hook contexts. */
	while ((rhc = TAILQ_FIRST(&index->rhclist)) != NULL)
		routemap_hook_context_free(rhc);

	/* Free route match. */
	while ((rule = index->match_list.head) != NULL) {
		if (IS_RULE_IPv4_PREFIX_LIST(rule->cmd->str))
			route_map_pfx_tbl_update(RMAP_EVENT_PLIST_DELETED,
						 index, AFI_IP, rule->rule_str);
		else if (IS_RULE_IPv6_PREFIX_LIST(rule->cmd->str))
			route_map_pfx_tbl_update(RMAP_EVENT_PLIST_DELETED,
						 index, AFI_IP6,
						 rule->rule_str);

		route_map_rule_delete(&index->match_list, rule);
	}

	/* Free route set. */
	while ((rule = index->set_list.head) != NULL)
		route_map_rule_delete(&index->set_list, rule);

	/* Remove index from route map list. */
	if (index->next)
		index->next->prev = index->prev;
	else
		index->map->tail = index->prev;

	if (index->prev)
		index->prev->next = index->next;
	else
		index->map->head = index->next;

	/* Free 'char *nextrm' if not NULL */
	XFREE(MTYPE_ROUTE_MAP_NAME, index->nextrm);

	route_map_pfx_tbl_update(RMAP_EVENT_INDEX_DELETED, index, 0, NULL);

	/* Execute event hook. */
	if (route_map_master.event_hook && notify) {
		(*route_map_master.event_hook)(index->map->name);
		route_map_notify_dependencies(index->map->name,
					      RMAP_EVENT_CALL_ADDED);
	}
	XFREE(MTYPE_ROUTE_MAP_INDEX, index);
}

/* Lookup index from route map. */
static struct route_map_index *route_map_index_lookup(struct route_map *map,
						      enum route_map_type type,
						      int pref)
{
	struct route_map_index *index;

	for (index = map->head; index; index = index->next)
		if ((index->type == type || type == RMAP_ANY)
		    && index->pref == pref)
			return index;
	return NULL;
}

/* Add new index to route map. */
static struct route_map_index *
route_map_index_add(struct route_map *map, enum route_map_type type, int pref)
{
	struct route_map_index *index;
	struct route_map_index *point;

	/* Allocate new route map inex. */
	index = route_map_index_new();
	index->map = map;
	index->type = type;
	index->pref = pref;

	/* Compare preference. */
	for (point = map->head; point; point = point->next)
		if (point->pref >= pref)
			break;

	if (map->head == NULL) {
		map->head = map->tail = index;
	} else if (point == NULL) {
		index->prev = map->tail;
		map->tail->next = index;
		map->tail = index;
	} else if (point == map->head) {
		index->next = map->head;
		map->head->prev = index;
		map->head = index;
	} else {
		index->next = point;
		index->prev = point->prev;
		if (point->prev)
			point->prev->next = index;
		point->prev = index;
	}

	route_map_pfx_tbl_update(RMAP_EVENT_INDEX_ADDED, index, 0, NULL);

	/* Execute event hook. */
	if (route_map_master.event_hook) {
		(*route_map_master.event_hook)(map->name);
		route_map_notify_dependencies(map->name, RMAP_EVENT_CALL_ADDED);
	}

	if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)))
		zlog_debug("Route-map %s add sequence %d, type: %s",
			   map->name, pref, route_map_type_str(type));

	return index;
}

/* Get route map index. */
struct route_map_index *
route_map_index_get(struct route_map *map, enum route_map_type type, int pref)
{
	struct route_map_index *index;

	index = route_map_index_lookup(map, RMAP_ANY, pref);
	if (index && index->type != type) {
		/* Delete index from route map. */
		route_map_index_delete(index, 1);
		index = NULL;
	}
	if (index == NULL)
		index = route_map_index_add(map, type, pref);
	return index;
}

/* New route map rule */
static struct route_map_rule *route_map_rule_new(void)
{
	struct route_map_rule *new;

	new = XCALLOC(MTYPE_ROUTE_MAP_RULE, sizeof(struct route_map_rule));
	return new;
}

/* Install rule command to the match list. */
void _route_map_install_match(struct route_map_rule_cmd_proxy *proxy)
{
	rmap_cmd_name_add(rmap_match_cmds, proxy);
}

/* Install rule command to the set list. */
void _route_map_install_set(struct route_map_rule_cmd_proxy *proxy)
{
	rmap_cmd_name_add(rmap_set_cmds, proxy);
}

/* Lookup rule command from match list. */
static const struct route_map_rule_cmd *route_map_lookup_match(const char *name)
{
	struct route_map_rule_cmd refcmd = {.str = name};
	struct route_map_rule_cmd_proxy ref = {.cmd = &refcmd};
	struct route_map_rule_cmd_proxy *res;

	res = rmap_cmd_name_find(rmap_match_cmds, &ref);
	if (res)
		return res->cmd;
	return NULL;
}

/* Lookup rule command from set list. */
static const struct route_map_rule_cmd *route_map_lookup_set(const char *name)
{
	struct route_map_rule_cmd refcmd = {.str = name};
	struct route_map_rule_cmd_proxy ref = {.cmd = &refcmd};
	struct route_map_rule_cmd_proxy *res;

	res = rmap_cmd_name_find(rmap_set_cmds, &ref);
	if (res)
		return res->cmd;
	return NULL;
}

/* Add match and set rule to rule list. */
static void route_map_rule_add(struct route_map_rule_list *list,
			       struct route_map_rule *rule)
{
	rule->next = NULL;
	rule->prev = list->tail;
	if (list->tail)
		list->tail->next = rule;
	else
		list->head = rule;
	list->tail = rule;
}

/* Delete rule from rule list. */
static void route_map_rule_delete(struct route_map_rule_list *list,
				  struct route_map_rule *rule)
{
	if (rule->cmd->func_free)
		(*rule->cmd->func_free)(rule->value);

	XFREE(MTYPE_ROUTE_MAP_RULE_STR, rule->rule_str);

	if (rule->next)
		rule->next->prev = rule->prev;
	else
		list->tail = rule->prev;
	if (rule->prev)
		rule->prev->next = rule->next;
	else
		list->head = rule->next;

	XFREE(MTYPE_ROUTE_MAP_RULE, rule);
}

/* strcmp wrapper function which don't crush even argument is NULL. */
static int rulecmp(const char *dst, const char *src)
{
	if (dst == NULL) {
		if (src == NULL)
			return 0;
		else
			return 1;
	} else {
		if (src == NULL)
			return 1;
		else
			return strcmp(dst, src);
	}
	return 1;
}

/* Use this to return the already specified argument for this match. This is
 * useful to get the specified argument with a route map match rule when the
 * rule is being deleted and the argument is not provided.
 */
const char *route_map_get_match_arg(struct route_map_index *index,
				    const char *match_name)
{
	struct route_map_rule *rule;
	const struct route_map_rule_cmd *cmd;

	/* First lookup rule for add match statement. */
	cmd = route_map_lookup_match(match_name);
	if (cmd == NULL)
		return NULL;

	for (rule = index->match_list.head; rule; rule = rule->next)
		if (rule->cmd == cmd && rule->rule_str != NULL)
			return (rule->rule_str);

	return NULL;
}

static route_map_event_t get_route_map_delete_event(route_map_event_t type)
{
	switch (type) {
	case RMAP_EVENT_CALL_ADDED:
		return RMAP_EVENT_CALL_DELETED;
	case RMAP_EVENT_PLIST_ADDED:
		return RMAP_EVENT_PLIST_DELETED;
	case RMAP_EVENT_CLIST_ADDED:
		return RMAP_EVENT_CLIST_DELETED;
	case RMAP_EVENT_ECLIST_ADDED:
		return RMAP_EVENT_ECLIST_DELETED;
	case RMAP_EVENT_LLIST_ADDED:
		return RMAP_EVENT_LLIST_DELETED;
	case RMAP_EVENT_ASLIST_ADDED:
		return RMAP_EVENT_ASLIST_DELETED;
	case RMAP_EVENT_FILTER_ADDED:
		return RMAP_EVENT_FILTER_DELETED;
	case RMAP_EVENT_SET_ADDED:
	case RMAP_EVENT_SET_DELETED:
	case RMAP_EVENT_SET_REPLACED:
	case RMAP_EVENT_MATCH_ADDED:
	case RMAP_EVENT_MATCH_DELETED:
	case RMAP_EVENT_MATCH_REPLACED:
	case RMAP_EVENT_INDEX_ADDED:
	case RMAP_EVENT_INDEX_DELETED:
	case RMAP_EVENT_CALL_DELETED:
	case RMAP_EVENT_PLIST_DELETED:
	case RMAP_EVENT_CLIST_DELETED:
	case RMAP_EVENT_ECLIST_DELETED:
	case RMAP_EVENT_LLIST_DELETED:
	case RMAP_EVENT_ASLIST_DELETED:
	case RMAP_EVENT_FILTER_DELETED:
		/* This function returns the appropriate 'deleted' event type
		 * for every 'added' event type passed to this function.
		 * This is done only for named entities used in the
		 * route-map match commands.
		 * This function is not to be invoked for any of the other event
		 * types.
		 */
		assert(0);
	}

	assert(0);
	/*
	 * Return to make c happy but if we get here something has gone
	 * terribly terribly wrong, so yes this return makes no sense.
	 */
	return RMAP_EVENT_CALL_ADDED;
}

/* Add match statement to route map. */
enum rmap_compile_rets route_map_add_match(struct route_map_index *index,
					   const char *match_name,
					   const char *match_arg,
					   route_map_event_t type)
{
	struct route_map_rule *rule;
	struct route_map_rule *next;
	const struct route_map_rule_cmd *cmd;
	void *compile;
	int8_t delete_rmap_event_type = 0;
	const char *rule_key;

	/* First lookup rule for add match statement. */
	cmd = route_map_lookup_match(match_name);
	if (cmd == NULL)
		return RMAP_RULE_MISSING;

	/* Next call compile function for this match statement. */
	if (cmd->func_compile) {
		compile = (*cmd->func_compile)(match_arg);
		if (compile == NULL)
			return RMAP_COMPILE_ERROR;
	} else
		compile = NULL;
	/* use the compiled results if applicable */
	if (compile && cmd->func_get_rmap_rule_key)
		rule_key = (*cmd->func_get_rmap_rule_key)
			   (compile);
	else
		rule_key = match_arg;

	/* If argument is completely same ignore it. */
	for (rule = index->match_list.head; rule; rule = next) {
		next = rule->next;
		if (rule->cmd == cmd) {
			/* If the configured route-map match rule is exactly
			 * the same as the existing configuration then,
			 * ignore the duplicate configuration.
			 */
			if (rulecmp(match_arg, rule->rule_str) == 0) {
				if (cmd->func_free)
					(*cmd->func_free)(compile);

				return RMAP_COMPILE_SUCCESS;
			}

			/* If IPv4 or IPv6 prefix-list match criteria
			 * has been delete to the route-map index, update
			 * the route-map's prefix table.
			 */
			if (IS_RULE_IPv4_PREFIX_LIST(match_name))
				route_map_pfx_tbl_update(
					RMAP_EVENT_PLIST_DELETED, index, AFI_IP,
					rule->rule_str);
			else if (IS_RULE_IPv6_PREFIX_LIST(match_name))
				route_map_pfx_tbl_update(
					RMAP_EVENT_PLIST_DELETED, index,
					AFI_IP6, rule->rule_str);

			/* Remove the dependency of the route-map on the rule
			 * that is being replaced.
			 */
			if (type >= RMAP_EVENT_CALL_ADDED) {
				delete_rmap_event_type =
					get_route_map_delete_event(type);
				route_map_upd8_dependency(
							delete_rmap_event_type,
							rule->rule_str,
							index->map->name);
			}

			route_map_rule_delete(&index->match_list, rule);
		}
	}

	/* Add new route map match rule. */
	rule = route_map_rule_new();
	rule->cmd = cmd;
	rule->value = compile;
	if (match_arg)
		rule->rule_str = XSTRDUP(MTYPE_ROUTE_MAP_RULE_STR, match_arg);
	else
		rule->rule_str = NULL;

	/* Add new route match rule to linked list. */
	route_map_rule_add(&index->match_list, rule);

	/* If IPv4 or IPv6 prefix-list match criteria
	 * has been added to the route-map index, update
	 * the route-map's prefix table.
	 */
	if (IS_RULE_IPv4_PREFIX_LIST(match_name)) {
		route_map_pfx_tbl_update(RMAP_EVENT_PLIST_ADDED, index, AFI_IP,
					 match_arg);
	} else if (IS_RULE_IPv6_PREFIX_LIST(match_name)) {
		route_map_pfx_tbl_update(RMAP_EVENT_PLIST_ADDED, index, AFI_IP6,
					 match_arg);
	}

	/* Execute event hook. */
	if (route_map_master.event_hook) {
		(*route_map_master.event_hook)(index->map->name);
		route_map_notify_dependencies(index->map->name,
					      RMAP_EVENT_CALL_ADDED);
	}
	if (type != RMAP_EVENT_MATCH_ADDED)
		route_map_upd8_dependency(type, rule_key, index->map->name);

	return RMAP_COMPILE_SUCCESS;
}

/* Delete specified route match rule. */
enum rmap_compile_rets route_map_delete_match(struct route_map_index *index,
					      const char *match_name,
					      const char *match_arg,
					      route_map_event_t type)
{
	struct route_map_rule *rule;
	const struct route_map_rule_cmd *cmd;
	const char *rule_key;

	cmd = route_map_lookup_match(match_name);
	if (cmd == NULL)
		return RMAP_RULE_MISSING;

	for (rule = index->match_list.head; rule; rule = rule->next)
		if (rule->cmd == cmd && (rulecmp(rule->rule_str, match_arg) == 0
					 || match_arg == NULL)) {
			/* Execute event hook. */
			if (route_map_master.event_hook) {
				(*route_map_master.event_hook)(index->map->name);
				route_map_notify_dependencies(
					index->map->name,
					RMAP_EVENT_CALL_ADDED);
			}
			if (cmd->func_get_rmap_rule_key)
				rule_key = (*cmd->func_get_rmap_rule_key)
					   (rule->value);
			else
				rule_key = match_arg;

			if (type != RMAP_EVENT_MATCH_DELETED && rule_key)
				route_map_upd8_dependency(type, rule_key,
						index->map->name);

			route_map_rule_delete(&index->match_list, rule);

			/* If IPv4 or IPv6 prefix-list match criteria
			 * has been delete from the route-map index, update
			 * the route-map's prefix table.
			 */
			if (IS_RULE_IPv4_PREFIX_LIST(match_name)) {
				route_map_pfx_tbl_update(
					RMAP_EVENT_PLIST_DELETED, index, AFI_IP,
					match_arg);
			} else if (IS_RULE_IPv6_PREFIX_LIST(match_name)) {
				route_map_pfx_tbl_update(
					RMAP_EVENT_PLIST_DELETED, index,
					AFI_IP6, match_arg);
			}

			return RMAP_COMPILE_SUCCESS;
		}
	/* Can't find matched rule. */
	return RMAP_RULE_MISSING;
}

/* Add route-map set statement to the route map. */
enum rmap_compile_rets route_map_add_set(struct route_map_index *index,
					 const char *set_name,
					 const char *set_arg)
{
	struct route_map_rule *rule;
	struct route_map_rule *next;
	const struct route_map_rule_cmd *cmd;
	void *compile;

	cmd = route_map_lookup_set(set_name);
	if (cmd == NULL)
		return RMAP_RULE_MISSING;

	/* Next call compile function for this match statement. */
	if (cmd->func_compile) {
		compile = (*cmd->func_compile)(set_arg);
		if (compile == NULL)
			return RMAP_COMPILE_ERROR;
	} else
		compile = NULL;

	/* Add by WJL. if old set command of same kind exist, delete it first
	   to ensure only one set command of same kind exist under a
	   route_map_index. */
	for (rule = index->set_list.head; rule; rule = next) {
		next = rule->next;
		if (rule->cmd == cmd)
			route_map_rule_delete(&index->set_list, rule);
	}

	/* Add new route map match rule. */
	rule = route_map_rule_new();
	rule->cmd = cmd;
	rule->value = compile;
	if (set_arg)
		rule->rule_str = XSTRDUP(MTYPE_ROUTE_MAP_RULE_STR, set_arg);
	else
		rule->rule_str = NULL;

	/* Add new route match rule to linked list. */
	route_map_rule_add(&index->set_list, rule);

	/* Execute event hook. */
	if (route_map_master.event_hook) {
		(*route_map_master.event_hook)(index->map->name);
		route_map_notify_dependencies(index->map->name,
					      RMAP_EVENT_CALL_ADDED);
	}
	return RMAP_COMPILE_SUCCESS;
}

/* Delete route map set rule. */
enum rmap_compile_rets route_map_delete_set(struct route_map_index *index,
					    const char *set_name,
					    const char *set_arg)
{
	struct route_map_rule *rule;
	const struct route_map_rule_cmd *cmd;

	cmd = route_map_lookup_set(set_name);
	if (cmd == NULL)
		return RMAP_RULE_MISSING;

	for (rule = index->set_list.head; rule; rule = rule->next)
		if ((rule->cmd == cmd) && (rulecmp(rule->rule_str, set_arg) == 0
					   || set_arg == NULL)) {
			route_map_rule_delete(&index->set_list, rule);
			/* Execute event hook. */
			if (route_map_master.event_hook) {
				(*route_map_master.event_hook)(index->map->name);
				route_map_notify_dependencies(
					index->map->name,
					RMAP_EVENT_CALL_ADDED);
			}
			return RMAP_COMPILE_SUCCESS;
		}
	/* Can't find matched rule. */
	return RMAP_RULE_MISSING;
}

static enum route_map_cmd_result_t
route_map_apply_match(struct route_map_rule_list *match_list,
		      const struct prefix *prefix, void *object)
{
	enum route_map_cmd_result_t ret = RMAP_NOMATCH;
	struct route_map_rule *match;
	bool is_matched = false;


	/* Check all match rule and if there is no match rule, go to the
	   set statement. */
	if (!match_list->head)
		ret = RMAP_MATCH;
	else {
		for (match = match_list->head; match; match = match->next) {
			/*
			 * Try each match statement. If any match does not
			 * return RMAP_MATCH or RMAP_NOOP, return.
			 * Otherwise continue on to next match statement.
			 * All match statements must MATCH for
			 * end-result to be a match.
			 * (Exception:If match stmts result in a mix of
			 * MATCH/NOOP, then also end-result is a match)
			 * If all result in NOOP, end-result is NOOP.
			 */
			ret = (*match->cmd->func_apply)(match->value, prefix,
							object);

			/*
			 * If the consolidated result of func_apply is:
			 *   -----------------------------------------------
			 *   |  MATCH  | NOMATCH  |  NOOP   |  Final Result |
			 *   ------------------------------------------------
			 *   |   yes   |   yes    |  yes    |     NOMATCH   |
			 *   |   no    |   no     |  yes    |     NOOP      |
			 *   |   yes   |   no     |  yes    |     MATCH     |
			 *   |   no    |   yes    |  yes    |     NOMATCH   |
			 *   |-----------------------------------------------
			 *
			 *  Traditionally, all rules within route-map
			 *  should match for it to MATCH.
			 *  If there are noops within the route-map rules,
			 *  it follows the above matrix.
			 *
			 *   Eg: route-map rm1 permit 10
			 *         match rule1
			 *         match rule2
			 *         match rule3
			 *         ....
			 *       route-map rm1 permit 20
			 *         match ruleX
			 *         match ruleY
			 *         ...
			 */

			switch (ret) {
			case RMAP_MATCH:
				is_matched = true;
				break;

			case RMAP_NOMATCH:
				return ret;

			case RMAP_NOOP:
				if (is_matched)
					ret = RMAP_MATCH;
				break;

			case RMAP_OKAY:
			case RMAP_ERROR:
				break;
			}

		}
	}
	return ret;
}

static struct list *route_map_get_index_list(struct route_node **rn,
					     const struct prefix *prefix,
					     struct route_table *table)
{
	struct route_node *tmp_rn = NULL;

	if (!(*rn)) {
		*rn = route_node_match(table, prefix);

		if (!(*rn))
			return NULL;

		if ((*rn)->info)
			return (struct list *)((*rn)->info);

		/* If rn->info is NULL, get the parent.
		 * Store the rn in tmp_rn and unlock it later.
		 */
		tmp_rn = *rn;
	}

	do {
		*rn = (*rn)->parent;
		if (tmp_rn)
			route_unlock_node(tmp_rn);

		if (!(*rn))
			break;

		if ((*rn)->info) {
			route_lock_node(*rn);
			return (struct list *)((*rn)->info);
		}
	} while (!(*rn)->info);

	return NULL;
}

/*
 * This function returns the route-map index that best matches the prefix.
 */
static struct route_map_index *
route_map_get_index(struct route_map *map, const struct prefix *prefix,
		    void *object, enum route_map_cmd_result_t *match_ret)
{
	enum route_map_cmd_result_t ret = RMAP_NOMATCH;
	struct list *candidate_rmap_list = NULL;
	struct route_node *rn = NULL;
	struct listnode *ln = NULL, *nn = NULL;
	struct route_map_index *index = NULL, *best_index = NULL;
	struct route_map_index *head_index = NULL;
	struct route_table *table = NULL;

	/* Route-map optimization relies on LPM lookups of the prefix to reduce
	 * the amount of route-map clauses a given prefix needs to be processed
	 * against. These LPM trees are IPv4/IPv6-specific and prefix->family
	 * must be AF_INET or AF_INET6 in order for the lookup to succeed. So if
	 * the AF doesn't line up with the LPM trees, skip the optimization.
	 */
	if (map->optimization_disabled) {
		if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)))
			zlog_debug(
				"Skipping route-map optimization for route-map: %s, pfx: %pFX, family: %d",
				map->name, prefix, prefix->family);
		return map->head;
	}

	if (prefix->family == AF_INET)
		table = map->ipv4_prefix_table;
	else
		table = map->ipv6_prefix_table;

	do {
		candidate_rmap_list =
			route_map_get_index_list(&rn, prefix, table);
		if (!rn)
			break;

		/* If the index at the head of the list is of seq higher
		 * than that in best_index, ignore the list and get the
		 * parent node's list.
		 */
		head_index = (struct route_map_index *)(listgetdata(
			listhead(candidate_rmap_list)));
		if (best_index && head_index
		    && (best_index->pref < head_index->pref)) {
			route_unlock_node(rn);
			continue;
		}

		for (ALL_LIST_ELEMENTS(candidate_rmap_list, ln, nn, index)) {
			/* If the index is of seq higher than that in
			 * best_index, ignore the list and get the parent
			 * node's list.
			 */
			if (best_index && (best_index->pref < index->pref))
				break;

			ret = route_map_apply_match(&index->match_list, prefix,
						    object);

			if (ret == RMAP_MATCH) {
				*match_ret = ret;
				best_index = index;
				break;
			} else if (ret == RMAP_NOOP) {
				/*
				 * If match_ret is denymatch, even if we see
				 * more noops, we retain this return value and
				 * return this eventually if there are no
				 * matches.
				 * If a best match route-map index already
				 * exists, do not reset the match_ret.
				 */
				if (!best_index && (*match_ret != RMAP_NOMATCH))
					*match_ret = ret;
			} else {
				/*
				 * ret is RMAP_NOMATCH.
				 * If a best match route-map index already
				 * exists, do not reset the match_ret.
				 */
				if (!best_index)
					*match_ret = ret;
			}
		}

		route_unlock_node(rn);

	} while (rn);

	return best_index;
}

static int route_map_candidate_list_cmp(struct route_map_index *idx1,
					struct route_map_index *idx2)
{
	return idx1->pref - idx2->pref;
}

/*
 * This function adds the route-map index into the default route's
 * route-node in the route-map's IPv4/IPv6 prefix-table.
 */
static void route_map_pfx_table_add_default(afi_t afi,
					    struct route_map_index *index)
{
	struct route_node *rn = NULL;
	struct list *rmap_candidate_list = NULL;
	struct prefix p;
	bool updated_rn = false;
	struct route_table *table = NULL;

	memset(&p, 0, sizeof(p));
	p.family = afi2family(afi);
	p.prefixlen = 0;

	if (p.family == AF_INET)
		table = index->map->ipv4_prefix_table;
	else
		table = index->map->ipv6_prefix_table;

	/* Add default route to table */
	rn = route_node_get(table, &p);

	if (!rn)
		return;

	if (!rn->info) {
		rmap_candidate_list = list_new();
		rmap_candidate_list->cmp =
			(int (*)(void *, void *))route_map_candidate_list_cmp;
		rn->info = rmap_candidate_list;
	} else {
		rmap_candidate_list = (struct list *)rn->info;
		updated_rn = true;
	}

	listnode_add_sort_nodup(rmap_candidate_list, index);
	if (updated_rn)
		route_unlock_node(rn);
}

/*
 * This function removes the route-map index from the default route's
 * route-node in the route-map's IPv4/IPv6 prefix-table.
 */
static void route_map_pfx_table_del_default(afi_t afi,
					    struct route_map_index *index)
{
	struct route_node *rn = NULL;
	struct list *rmap_candidate_list = NULL;
	struct prefix p;
	struct route_table *table = NULL;

	memset(&p, 0, sizeof(p));
	p.family = afi2family(afi);
	p.prefixlen = 0;

	if (p.family == AF_INET)
		table = index->map->ipv4_prefix_table;
	else
		table = index->map->ipv6_prefix_table;

	/* Remove RMAP index from default route in table */
	rn = route_node_lookup(table, &p);
	if (!rn || !rn->info)
		return;

	rmap_candidate_list = (struct list *)rn->info;

	listnode_delete(rmap_candidate_list, index);

	if (listcount(rmap_candidate_list) == 0) {
		list_delete(&rmap_candidate_list);
		rn->info = NULL;
		route_unlock_node(rn);
	}
	route_unlock_node(rn);
}

/*
 * This function adds the route-map index to the route-node for
 * the prefix-entry in the route-map's IPv4/IPv6 prefix-table.
 */
static void route_map_pfx_table_add(struct route_table *table,
				    struct route_map_index *index,
				    struct prefix_list_entry *pentry)
{
	struct route_node *rn = NULL;
	struct list *rmap_candidate_list = NULL;
	bool updated_rn = false;

	rn = route_node_get(table, &pentry->prefix);
	if (!rn)
		return;

	if (!rn->info) {
		rmap_candidate_list = list_new();
		rmap_candidate_list->cmp =
			(int (*)(void *, void *))route_map_candidate_list_cmp;
		rn->info = rmap_candidate_list;
	} else {
		rmap_candidate_list = (struct list *)rn->info;
		updated_rn = true;
	}

	listnode_add_sort_nodup(rmap_candidate_list, index);
	if (updated_rn)
		route_unlock_node(rn);
}

/*
 * This function removes the route-map index from the route-node for
 * the prefix-entry in the route-map's IPv4/IPv6 prefix-table.
 */
static void route_map_pfx_table_del(struct route_table *table,
				    struct route_map_index *index,
				    struct prefix_list_entry *pentry)
{
	struct route_node *rn = NULL;
	struct list *rmap_candidate_list = NULL;

	rn = route_node_lookup(table, &pentry->prefix);
	if (!rn || !rn->info)
		return;

	rmap_candidate_list = (struct list *)rn->info;

	listnode_delete(rmap_candidate_list, index);

	if (listcount(rmap_candidate_list) == 0) {
		list_delete(&rmap_candidate_list);
		rn->info = NULL;
		route_unlock_node(rn);
	}
	route_unlock_node(rn);
}

/* This function checks for the presence of an IPv4 prefix-list
 * match rule in the given route-map index.
 */
static bool route_map_is_ip_pfx_list_rule_present(struct route_map_index *index)
{
	struct route_map_rule_list *match_list = NULL;
	struct route_map_rule *rule = NULL;

	match_list = &index->match_list;
	for (rule = match_list->head; rule; rule = rule->next)
		if (IS_RULE_IPv4_PREFIX_LIST(rule->cmd->str))
			return true;

	return false;
}

/* This function checks for the presence of an IPv6 prefix-list
 * match rule in the given route-map index.
 */
static bool
route_map_is_ipv6_pfx_list_rule_present(struct route_map_index *index)
{
	struct route_map_rule_list *match_list = NULL;
	struct route_map_rule *rule = NULL;

	match_list = &index->match_list;
	for (rule = match_list->head; rule; rule = rule->next)
		if (IS_RULE_IPv6_PREFIX_LIST(rule->cmd->str))
			return true;

	return false;
}

/* This function does the following:
 * 1) If plist_name is not present, search for a IPv4 or IPv6 prefix-list
 *    match clause (based on the afi passed to this foo) and get the
 *    prefix-list name.
 * 2) Look up the prefix-list using the name.
 * 3) If the prefix-list is not found then, add the index to the IPv4/IPv6
 *    default-route's node in the trie (based on the afi passed to this foo).
 * 4) If the prefix-list is found then, remove the index from the IPv4/IPv6
 *    default-route's node in the trie (based on the afi passed to this foo).
 * 5) If a prefix-entry is passed then, create a route-node for this entry and
 *    add this index to the route-node.
 * 6) If prefix-entry is not passed then, for every prefix-entry in the
 *    prefix-list, create a route-node for this entry and
 *    add this index to the route-node.
 */
static void route_map_add_plist_entries(afi_t afi,
					struct route_map_index *index,
					const char *plist_name,
					struct prefix_list_entry *entry)
{
	struct route_map_rule_list *match_list = NULL;
	struct route_map_rule *match = NULL;
	struct prefix_list *plist = NULL;
	struct prefix_list_entry *pentry = NULL;
	bool plist_rule_is_present = false;

	if (!plist_name) {
		match_list = &index->match_list;

		for (match = match_list->head; match; match = match->next) {
			if (afi == AFI_IP) {
				if (IS_RULE_IPv4_PREFIX_LIST(match->cmd->str)) {
					plist_rule_is_present = true;
					break;
				}
			} else {
				if (IS_RULE_IPv6_PREFIX_LIST(match->cmd->str)) {
					plist_rule_is_present = true;
					break;
				}
			}
		}

		if (plist_rule_is_present)
			plist = prefix_list_lookup(afi, match->rule_str);
	} else {
		plist = prefix_list_lookup(afi, plist_name);
	}

	if (!plist) {
		route_map_pfx_table_add_default(afi, index);
		return;
	}

	/* Default entry should be deleted only if the first entry of the
	 * prefix-list is created.
	 */
	if (entry) {
		if (plist->count == 1)
			route_map_pfx_table_del_default(afi, index);
	} else {
		route_map_pfx_table_del_default(afi, index);
	}

	if (entry) {
		if (afi == AFI_IP) {
			route_map_pfx_table_add(index->map->ipv4_prefix_table,
						index, entry);
		} else {
			route_map_pfx_table_add(index->map->ipv6_prefix_table,
						index, entry);
		}
	} else {
		for (pentry = plist->head; pentry; pentry = pentry->next) {
			if (afi == AFI_IP) {
				route_map_pfx_table_add(
					index->map->ipv4_prefix_table, index,
					pentry);
			} else {
				route_map_pfx_table_add(
					index->map->ipv6_prefix_table, index,
					pentry);
			}
		}
	}
}

/* This function does the following:
 * 1) If plist_name is not present, search for a IPv4 or IPv6 prefix-list
 *    match clause (based on the afi passed to this foo) and get the
 *    prefix-list name.
 * 2) Look up the prefix-list using the name.
 * 3) If the prefix-list is not found then, delete the index from the IPv4/IPv6
 *    default-route's node in the trie (based on the afi passed to this foo).
 * 4) If a prefix-entry is passed then, remove this index from the route-node
 *    for the prefix in this prefix-entry.
 * 5) If prefix-entry is not passed then, for every prefix-entry in the
 *    prefix-list, remove this index from the route-node
 *    for the prefix in this prefix-entry.
 */
static void route_map_del_plist_entries(afi_t afi,
					struct route_map_index *index,
					const char *plist_name,
					struct prefix_list_entry *entry)
{
	struct route_map_rule_list *match_list = NULL;
	struct route_map_rule *match = NULL;
	struct prefix_list *plist = NULL;
	struct prefix_list_entry *pentry = NULL;
	bool plist_rule_is_present = false;

	if (!plist_name) {
		match_list = &index->match_list;

		for (match = match_list->head; match; match = match->next) {
			if (afi == AFI_IP) {
				if (IS_RULE_IPv4_PREFIX_LIST(match->cmd->str)) {
					plist_rule_is_present = true;
					break;
				}
			} else {
				if (IS_RULE_IPv6_PREFIX_LIST(match->cmd->str)) {
					plist_rule_is_present = true;
					break;
				}
			}
		}

		if (plist_rule_is_present)
			plist = prefix_list_lookup(afi, match->rule_str);
	} else {
		plist = prefix_list_lookup(afi, plist_name);
	}

	if (!plist) {
		route_map_pfx_table_del_default(afi, index);
		return;
	}

	if (entry) {
		if (afi == AFI_IP) {
			route_map_pfx_table_del(index->map->ipv4_prefix_table,
						index, entry);
		} else {
			route_map_pfx_table_del(index->map->ipv6_prefix_table,
						index, entry);
		}
	} else {
		for (pentry = plist->head; pentry; pentry = pentry->next) {
			if (afi == AFI_IP) {
				route_map_pfx_table_del(
					index->map->ipv4_prefix_table, index,
					pentry);
			} else {
				route_map_pfx_table_del(
					index->map->ipv6_prefix_table, index,
					pentry);
			}
		}
	}
}

/*
 * This function handles the cases where a prefix-list is added/removed
 * as a match command from a particular route-map index.
 * It updates the prefix-table of the route-map accordingly.
 */
static void route_map_trie_update(afi_t afi, route_map_event_t event,
				  struct route_map_index *index,
				  const char *plist_name)
{
	if (event == RMAP_EVENT_PLIST_ADDED) {
		if (afi == AFI_IP) {
			if (!route_map_is_ipv6_pfx_list_rule_present(index)) {
				route_map_pfx_table_del_default(AFI_IP6, index);
				route_map_add_plist_entries(afi, index,
							    plist_name, NULL);
			} else {
				route_map_del_plist_entries(AFI_IP6, index,
							    NULL, NULL);
			}
		} else {
			if (!route_map_is_ip_pfx_list_rule_present(index)) {
				route_map_pfx_table_del_default(AFI_IP, index);
				route_map_add_plist_entries(afi, index,
							    plist_name, NULL);
			} else {
				route_map_del_plist_entries(AFI_IP, index, NULL,
							    NULL);
			}
		}
	} else if (event == RMAP_EVENT_PLIST_DELETED) {
		if (afi == AFI_IP) {
			route_map_del_plist_entries(afi, index, plist_name,
						    NULL);

			/* If IPv6 prefix-list match rule is not present,
			 * add this index to the IPv4 default route's trie
			 * node.
			 * Also, add this index to the trie nodes created
			 * for each of the prefix-entries within the IPv6
			 * prefix-list, if the IPv6 prefix-list match rule
			 * is present. Else, add this index to the IPv6
			 * default route's trie node.
			 */
			if (!route_map_is_ipv6_pfx_list_rule_present(index))
				route_map_pfx_table_add_default(afi, index);

			route_map_add_plist_entries(AFI_IP6, index, NULL, NULL);
		} else {
			route_map_del_plist_entries(afi, index, plist_name,
						    NULL);

			/* If IPv4 prefix-list match rule is not present,
			 * add this index to the IPv6 default route's trie
			 * node.
			 * Also, add this index to the trie nodes created
			 * for each of the prefix-entries within the IPv4
			 * prefix-list, if the IPv4 prefix-list match rule
			 * is present. Else, add this index to the IPv4
			 * default route's trie node.
			 */
			if (!route_map_is_ip_pfx_list_rule_present(index))
				route_map_pfx_table_add_default(afi, index);

			route_map_add_plist_entries(AFI_IP, index, NULL, NULL);
		}
	}
}

/*
 * This function handles the cases where a route-map index and
 * prefix-list is added/removed.
 * It updates the prefix-table of the route-map accordingly.
 */
static void route_map_pfx_tbl_update(route_map_event_t event,
				     struct route_map_index *index, afi_t afi,
				     const char *plist_name)
{
	if (!index)
		return;

	if (event == RMAP_EVENT_INDEX_ADDED) {
		route_map_pfx_table_add_default(AFI_IP, index);
		route_map_pfx_table_add_default(AFI_IP6, index);
		return;
	}

	if (event == RMAP_EVENT_INDEX_DELETED) {
		route_map_pfx_table_del_default(AFI_IP, index);
		route_map_pfx_table_del_default(AFI_IP6, index);

		return;
	}

	/* Handle prefix-list match rule addition/deletion.
	 */
	route_map_trie_update(afi, event, index, plist_name);
}

/*
 * This function handles the cases where a new prefix-entry is added to
 * a prefix-list or, an existing prefix-entry is removed from the prefix-list.
 * It updates the prefix-table of the route-map accordingly.
 */
static void route_map_pentry_update(route_map_event_t event,
				    const char *plist_name,
				    struct route_map_index *index,
				    struct prefix_list_entry *pentry)
{
	struct prefix_list *plist = NULL;
	afi_t afi;
	unsigned char family = pentry->prefix.family;

	if (family == AF_INET) {
		afi = AFI_IP;
		plist = prefix_list_lookup(AFI_IP, plist_name);
	} else {
		afi = AFI_IP6;
		plist = prefix_list_lookup(AFI_IP6, plist_name);
	}

	if (event == RMAP_EVENT_PLIST_ADDED) {
		if (afi == AFI_IP) {
			if (!route_map_is_ipv6_pfx_list_rule_present(index))
				route_map_add_plist_entries(afi, index,
							    plist_name, pentry);
		} else {
			if (!route_map_is_ip_pfx_list_rule_present(index))
				route_map_add_plist_entries(afi, index,
							    plist_name, pentry);
		}
	} else if (event == RMAP_EVENT_PLIST_DELETED) {
		route_map_del_plist_entries(afi, index, plist_name, pentry);

		if (plist->count == 1) {
			if (afi == AFI_IP) {
				if (!route_map_is_ipv6_pfx_list_rule_present(
					    index))
					route_map_pfx_table_add_default(afi,
									index);
			} else {
				if (!route_map_is_ip_pfx_list_rule_present(
					    index))
					route_map_pfx_table_add_default(afi,
									index);
			}
		}
	}
}

static void route_map_pentry_process_dependency(struct hash_bucket *bucket,
						void *data)
{
	char *rmap_name = NULL;
	struct route_map *rmap = NULL;
	struct route_map_index *index = NULL;
	struct route_map_rule_list *match_list = NULL;
	struct route_map_rule *match = NULL;
	struct route_map_dep_data *dep_data = NULL;
	struct route_map_pentry_dep *pentry_dep =
		(struct route_map_pentry_dep *)data;
	unsigned char family = pentry_dep->pentry->prefix.family;

	dep_data = (struct route_map_dep_data *)bucket->data;
	if (!dep_data)
		return;

	rmap_name = dep_data->rname;
	rmap = route_map_lookup_by_name(rmap_name);
	if (!rmap || !rmap->head)
		return;

	for (index = rmap->head; index; index = index->next) {
		match_list = &index->match_list;

		if (!match_list)
			continue;

		for (match = match_list->head; match; match = match->next) {
			if (strcmp(match->rule_str, pentry_dep->plist_name)
			    == 0) {
				if (IS_RULE_IPv4_PREFIX_LIST(match->cmd->str)
				    && family == AF_INET) {
					route_map_pentry_update(
						pentry_dep->event,
						pentry_dep->plist_name, index,
						pentry_dep->pentry);
				} else if (IS_RULE_IPv6_PREFIX_LIST(
						   match->cmd->str)
					   && family == AF_INET6) {
					route_map_pentry_update(
						pentry_dep->event,
						pentry_dep->plist_name, index,
						pentry_dep->pentry);
				}
			}
		}
	}
}

void route_map_notify_pentry_dependencies(const char *affected_name,
					  struct prefix_list_entry *pentry,
					  route_map_event_t event)
{
	struct route_map_dep *dep = NULL;
	struct hash *upd8_hash = NULL;
	struct route_map_pentry_dep pentry_dep;

	if (!affected_name || !pentry)
		return;

	upd8_hash = route_map_get_dep_hash(event);
	if (!upd8_hash)
		return;

	dep = (struct route_map_dep *)hash_get(upd8_hash, (void *)affected_name,
					       NULL);
	if (dep) {
		if (!dep->this_hash)
			dep->this_hash = upd8_hash;

		memset(&pentry_dep, 0, sizeof(pentry_dep));
		pentry_dep.pentry = pentry;
		pentry_dep.plist_name = affected_name;
		pentry_dep.event = event;

		hash_iterate(dep->dep_rmap_hash,
			     route_map_pentry_process_dependency,
			     (void *)&pentry_dep);
	}
}

/* Apply route map's each index to the object.

   The matrix for a route-map looks like this:
   (note, this includes the description for the "NEXT"
   and "GOTO" frobs now

	   |   Match   |   No Match   | No op
	   |-----------|--------------|-------
    permit |   action  |     cont     | cont.
	   |           | default:deny | default:permit
    -------------------+-----------------------
	   |   deny    |     cont     | cont.
    deny   |           | default:deny | default:permit
	   |-----------|--------------|--------

   action)
      -Apply Set statements, accept route
      -If Call statement is present jump to the specified route-map, if it
	 denies the route we finish.
      -If NEXT is specified, goto NEXT statement
      -If GOTO is specified, goto the first clause where pref > nextpref
      -If nothing is specified, do as Cisco and finish
   deny)
      -Route is denied by route-map.
   cont)
      -Goto Next index

   If we get no matches after we've processed all updates, then the route
   is dropped too.

   Some notes on the new "CALL", "NEXT" and "GOTO"
     call WORD        - If this clause is matched, then the set statements
			are executed and then we jump to route-map 'WORD'. If
			this route-map denies the route, we finish, in other
   case we
			do whatever the exit policy (EXIT, NEXT or GOTO) tells.
     on-match next    - If this clause is matched, then the set statements
			are executed and then we drop through to the next clause
     on-match goto n  - If this clause is matched, then the set statements
			are executed and then we goto the nth clause, or the
			first clause greater than this. In order to ensure
			route-maps *always* exit, you cannot jump backwards.
			Sorry ;)

   We need to make sure our route-map processing matches the above
*/
route_map_result_t route_map_apply_ext(struct route_map *map,
				       const struct prefix *prefix,
				       void *match_object, void *set_object,
				       int *pref)
{
	static int recursion = 0;
	enum route_map_cmd_result_t match_ret = RMAP_NOMATCH;
	route_map_result_t ret = RMAP_PERMITMATCH;
	struct route_map_index *index = NULL;
	struct route_map_rule *set = NULL;
	bool skip_match_clause = false;
	RUSAGE_T mbefore, mafter;
	RUSAGE_T ibefore, iafter;
	unsigned long cputime;
	enum route_map_action_reason reason = route_map_action_none;

	if (recursion > RMAP_RECURSION_LIMIT) {
		if (map)
			map->applied++;

		flog_warn(
			EC_LIB_RMAP_RECURSION_LIMIT,
			"route-map recursion limit (%d) reached, discarding route",
			RMAP_RECURSION_LIMIT);
		recursion = 0;
		return RMAP_DENYMATCH;
	}

	if (map == NULL || map->head == NULL) {
		if (map)
			map->applied++;
		ret = RMAP_DENYMATCH;
		reason = route_map_action_map_null;
		goto route_map_apply_end;
	}

	map->applied++;

	GETRUSAGE(&mbefore);
	ibefore = mbefore;

	if (prefix->family == AF_EVPN) {
		index = map->head;
	} else {
		skip_match_clause = true;
		index = route_map_get_index(map, prefix, match_object,
					    &match_ret);
	}

	if (index) {
		index->applied++;

		GETRUSAGE(&iafter);
		event_consumed_time(&iafter, &ibefore, &cputime);
		index->cputime += cputime;
		ibefore = iafter;

		if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)))
			zlog_debug(
				"Best match route-map: %s, sequence: %d for pfx: %pFX, result: %s",
				map->name, index->pref, prefix,
				route_map_cmd_result_str(match_ret));
	} else {
		if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)))
			zlog_debug(
				"No best match sequence for pfx: %pFX in route-map: %s, result: %s",
				prefix, map->name,
				route_map_cmd_result_str(match_ret));
		/*
		 * No index matches this prefix. Return deny unless,
		 * match_ret = RMAP_NOOP.
		 */
		if (match_ret == RMAP_NOOP)
			ret = RMAP_PERMITMATCH;
		else
			ret = RMAP_DENYMATCH;
		reason = route_map_action_no_index;
		goto route_map_apply_end;
	}

	for (; index; index = index->next) {
		if (!skip_match_clause) {
			index->applied++;
			/* Apply this index. */
			match_ret = route_map_apply_match(&index->match_list,
							  prefix, match_object);
			if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) {
				zlog_debug(
					"Route-map: %s, sequence: %d, prefix: %pFX, result: %s",
					map->name, index->pref, prefix,
					route_map_cmd_result_str(match_ret));
			}
		} else
			skip_match_clause = false;


		/* Now we apply the matrix from above */
		if (match_ret == RMAP_NOOP)
			/*
			 * Do not change the return value. Retain the previous
			 * return value. Previous values can be:
			 * 1)permitmatch (if a nomatch was never
			 * seen before in this route-map.)
			 * 2)denymatch (if a nomatch was seen earlier in one
			 * of the previous sequences)
			 */

			/*
			 * 'cont' from matrix - continue to next route-map
			 * sequence
			 */
			continue;
		else if (match_ret == RMAP_NOMATCH) {

			/*
			 * The return value is now changed to denymatch.
			 * So from here on out, even if we see more noops,
			 * we retain this return value and return this
			 * eventually if there are no matches.
			 */
			ret = RMAP_DENYMATCH;

			/*
			 * 'cont' from matrix - continue to next route-map
			 * sequence
			 */
			continue;
		} else if (match_ret == RMAP_MATCH) {
			if (index->type == RMAP_PERMIT)
			/* 'action' */
			{
				/* Match succeeded, rmap is of type permit */
				ret = RMAP_PERMITMATCH;

				/* permit+match must execute sets */
				for (set = index->set_list.head; set;
				     set = set->next)
					/*
					 * set cmds return RMAP_OKAY or
					 * RMAP_ERROR. We do not care if
					 * set succeeded or not. So, ignore
					 * return code.
					 */
					(void)(*set->cmd->func_apply)(
						set->value, prefix, set_object);

				/* Call another route-map if available */
				if (index->nextrm) {
					struct route_map *nextrm =
						route_map_lookup_by_name(
							index->nextrm);

					if (nextrm) /* Target route-map found,
						       jump to it */
					{
						recursion++;
						ret = route_map_apply_ext(
							nextrm, prefix,
							match_object,
							set_object, NULL);
						recursion--;
					}

					/* If nextrm returned 'deny', finish. */
					if (ret == RMAP_DENYMATCH) {
						reason = route_map_action_next_deny;
						goto route_map_apply_end;
					}
				}

				switch (index->exitpolicy) {
				case RMAP_EXIT:
					reason = route_map_action_exit;
					goto route_map_apply_end;
				case RMAP_NEXT:
					continue;
				case RMAP_GOTO: {
					/* Find the next clause to jump to */
					struct route_map_index *next =
						index->next;
					int nextpref = index->nextpref;

					while (next && next->pref < nextpref) {
						index = next;
						next = next->next;
					}
					if (next == NULL) {
						/* No clauses match! */
						reason = route_map_action_goto_null;
						goto route_map_apply_end;
					}
				}
				}
			} else if (index->type == RMAP_DENY)
			/* 'deny' */
			{
				ret = RMAP_DENYMATCH;
				reason = route_map_action_index_deny;
				goto route_map_apply_end;
			}
		}
		GETRUSAGE(&iafter);
		event_consumed_time(&iafter, &ibefore, &cputime);
		index->cputime += cputime;
		ibefore = iafter;
	}

route_map_apply_end:
	if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)))
		zlog_debug("Route-map: %s, prefix: %pFX, result: %s, reason: %s",
			   (map ? map->name : "null"), prefix, route_map_result_str(ret),
			   route_map_action_reason2str(reason));

	if (pref) {
		if (index != NULL && ret == RMAP_PERMITMATCH)
			*pref = index->pref;
		else
			*pref = 65536;
	}

	if (map) {
		GETRUSAGE(&mbefore);
		GETRUSAGE(&mafter);
		event_consumed_time(&mafter, &mbefore, &cputime);
		map->cputime += cputime;
	}

	return (ret);
}

void route_map_add_hook(void (*func)(const char *))
{
	route_map_master.add_hook = func;
}

void route_map_delete_hook(void (*func)(const char *))
{
	route_map_master.delete_hook = func;
}

void route_map_event_hook(void (*func)(const char *name))
{
	route_map_master.event_hook = func;
}

/* Routines for route map dependency lists and dependency processing */
static bool route_map_rmap_hash_cmp(const void *p1, const void *p2)
{
	return strcmp(((const struct route_map_dep_data *)p1)->rname,
		      ((const struct route_map_dep_data *)p2)->rname)
	       == 0;
}

static bool route_map_dep_hash_cmp(const void *p1, const void *p2)
{

	return (strcmp(((const struct route_map_dep *)p1)->dep_name,
		       (const char *)p2)
		== 0);
}

static void route_map_clear_reference(struct hash_bucket *bucket, void *arg)
{
	struct route_map_dep *dep = bucket->data;
	struct route_map_dep_data *dep_data = NULL, tmp_dep_data;

	memset(&tmp_dep_data, 0, sizeof(tmp_dep_data));
	tmp_dep_data.rname = arg;
	dep_data = hash_release(dep->dep_rmap_hash, &tmp_dep_data);
	if (dep_data) {
		if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)))
			zlog_debug("Clearing reference for %s to %s count: %d",
				   dep->dep_name, tmp_dep_data.rname,
				   dep_data->refcnt);

		XFREE(MTYPE_ROUTE_MAP_NAME, dep_data->rname);
		XFREE(MTYPE_ROUTE_MAP_DEP_DATA, dep_data);
	}
	if (!dep->dep_rmap_hash->count) {
		dep = hash_release(dep->this_hash, (void *)dep->dep_name);
		hash_free(dep->dep_rmap_hash);
		XFREE(MTYPE_ROUTE_MAP_NAME, dep->dep_name);
		XFREE(MTYPE_ROUTE_MAP_DEP, dep);
	}
}

static void route_map_clear_all_references(char *rmap_name)
{
	int i;

	if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)))
		zlog_debug("Clearing references for %s", rmap_name);

	for (i = 1; i < ROUTE_MAP_DEP_MAX; i++) {
		hash_iterate(route_map_dep_hash[i], route_map_clear_reference,
			     (void *)rmap_name);
	}
}

static unsigned int route_map_dep_data_hash_make_key(const void *p)
{
	const struct route_map_dep_data *dep_data = p;

	return string_hash_make(dep_data->rname);
}

static void *route_map_dep_hash_alloc(void *p)
{
	char *dep_name = (char *)p;
	struct route_map_dep *dep_entry;

	dep_entry = XCALLOC(MTYPE_ROUTE_MAP_DEP, sizeof(struct route_map_dep));
	dep_entry->dep_name = XSTRDUP(MTYPE_ROUTE_MAP_NAME, dep_name);
	dep_entry->dep_rmap_hash =
		hash_create_size(8, route_map_dep_data_hash_make_key,
				 route_map_rmap_hash_cmp, "Route Map Dep Hash");
	dep_entry->this_hash = NULL;

	return dep_entry;
}

static void *route_map_name_hash_alloc(void *p)
{
	struct route_map_dep_data *dep_data = NULL, *tmp_dep_data = NULL;

	dep_data = XCALLOC(MTYPE_ROUTE_MAP_DEP_DATA,
			   sizeof(struct route_map_dep_data));
	tmp_dep_data = p;
	dep_data->rname = XSTRDUP(MTYPE_ROUTE_MAP_NAME, tmp_dep_data->rname);
	return dep_data;
}

static unsigned int route_map_dep_hash_make_key(const void *p)
{
	return (string_hash_make((char *)p));
}

static void route_map_print_dependency(struct hash_bucket *bucket, void *data)
{
	struct route_map_dep_data *dep_data = bucket->data;
	char *rmap_name = dep_data->rname;
	char *dep_name = data;

	zlog_debug("%s: Dependency for %s: %s", __func__, dep_name, rmap_name);
}

static int route_map_dep_update(struct hash *dephash, const char *dep_name,
				const char *rmap_name, route_map_event_t type)
{
	struct route_map_dep *dep = NULL;
	char *dname, *rname;
	int ret = 0;
	struct route_map_dep_data *dep_data = NULL, *ret_dep_data = NULL;
	struct route_map_dep_data tmp_dep_data;

	dname = XSTRDUP(MTYPE_ROUTE_MAP_NAME, dep_name);
	rname = XSTRDUP(MTYPE_ROUTE_MAP_NAME, rmap_name);

	switch (type) {
	case RMAP_EVENT_PLIST_ADDED:
	case RMAP_EVENT_CLIST_ADDED:
	case RMAP_EVENT_ECLIST_ADDED:
	case RMAP_EVENT_ASLIST_ADDED:
	case RMAP_EVENT_LLIST_ADDED:
	case RMAP_EVENT_CALL_ADDED:
	case RMAP_EVENT_FILTER_ADDED:
		if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)))
			zlog_debug("Adding dependency for filter %s in route-map %s",
				   dep_name, rmap_name);
		dep = (struct route_map_dep *)hash_get(
			dephash, dname, route_map_dep_hash_alloc);
		if (!dep) {
			ret = -1;
			goto out;
		}

		if (!dep->this_hash)
			dep->this_hash = dephash;

		memset(&tmp_dep_data, 0, sizeof(tmp_dep_data));
		tmp_dep_data.rname = rname;
		dep_data = hash_lookup(dep->dep_rmap_hash, &tmp_dep_data);
		if (!dep_data)
			dep_data = hash_get(dep->dep_rmap_hash, &tmp_dep_data,
					    route_map_name_hash_alloc);

		dep_data->refcnt++;
		break;
	case RMAP_EVENT_PLIST_DELETED:
	case RMAP_EVENT_CLIST_DELETED:
	case RMAP_EVENT_ECLIST_DELETED:
	case RMAP_EVENT_ASLIST_DELETED:
	case RMAP_EVENT_LLIST_DELETED:
	case RMAP_EVENT_CALL_DELETED:
	case RMAP_EVENT_FILTER_DELETED:
		if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)))
			zlog_debug("Deleting dependency for filter %s in route-map %s",
				   dep_name, rmap_name);
		dep = (struct route_map_dep *)hash_get(dephash, dname, NULL);
		if (!dep) {
			goto out;
		}

		memset(&tmp_dep_data, 0, sizeof(tmp_dep_data));
		tmp_dep_data.rname = rname;
		dep_data = hash_lookup(dep->dep_rmap_hash, &tmp_dep_data);
		/*
		 * If dep_data is NULL then something has gone seriously
		 * wrong in route-map handling.  Note it and prevent
		 * the crash.
		 */
		if (!dep_data) {
			zlog_warn(
				"route-map dependency for route-map %s: %s is not correct",
				rmap_name, dep_name);
			goto out;
		}

		dep_data->refcnt--;

		if (!dep_data->refcnt) {
			ret_dep_data = hash_release(dep->dep_rmap_hash,
						    &tmp_dep_data);
			if (ret_dep_data) {
				XFREE(MTYPE_ROUTE_MAP_NAME,
				      ret_dep_data->rname);
				XFREE(MTYPE_ROUTE_MAP_DEP_DATA, ret_dep_data);
			}
		}

		if (!dep->dep_rmap_hash->count) {
			dep = hash_release(dephash, dname);
			hash_free(dep->dep_rmap_hash);
			XFREE(MTYPE_ROUTE_MAP_NAME, dep->dep_name);
			XFREE(MTYPE_ROUTE_MAP_DEP, dep);
		}
		break;
	case RMAP_EVENT_SET_ADDED:
	case RMAP_EVENT_SET_DELETED:
	case RMAP_EVENT_SET_REPLACED:
	case RMAP_EVENT_MATCH_ADDED:
	case RMAP_EVENT_MATCH_DELETED:
	case RMAP_EVENT_MATCH_REPLACED:
	case RMAP_EVENT_INDEX_ADDED:
	case RMAP_EVENT_INDEX_DELETED:
		break;
	}

	if (dep) {
		if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))
			hash_iterate(dep->dep_rmap_hash,
				     route_map_print_dependency, dname);
	}

out:
	XFREE(MTYPE_ROUTE_MAP_NAME, rname);
	XFREE(MTYPE_ROUTE_MAP_NAME, dname);
	return ret;
}

static struct hash *route_map_get_dep_hash(route_map_event_t event)
{
	struct hash *upd8_hash = NULL;

	switch (event) {
	case RMAP_EVENT_PLIST_ADDED:
	case RMAP_EVENT_PLIST_DELETED:
		upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_PLIST];
		break;
	case RMAP_EVENT_CLIST_ADDED:
	case RMAP_EVENT_CLIST_DELETED:
		upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_CLIST];
		break;
	case RMAP_EVENT_ECLIST_ADDED:
	case RMAP_EVENT_ECLIST_DELETED:
		upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_ECLIST];
		break;
	case RMAP_EVENT_ASLIST_ADDED:
	case RMAP_EVENT_ASLIST_DELETED:
		upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_ASPATH];
		break;
	case RMAP_EVENT_LLIST_ADDED:
	case RMAP_EVENT_LLIST_DELETED:
		upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_LCLIST];
		break;
	case RMAP_EVENT_CALL_ADDED:
	case RMAP_EVENT_CALL_DELETED:
	case RMAP_EVENT_MATCH_ADDED:
	case RMAP_EVENT_MATCH_DELETED:
		upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_RMAP];
		break;
	case RMAP_EVENT_FILTER_ADDED:
	case RMAP_EVENT_FILTER_DELETED:
		upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_FILTER];
		break;
	/*
	 * Should we actually be ignoring these?
	 * I am not sure but at this point in time, let
	 * us get them into this switch and we can peel
	 * them into the appropriate place in the future
	 */
	case RMAP_EVENT_SET_ADDED:
	case RMAP_EVENT_SET_DELETED:
	case RMAP_EVENT_SET_REPLACED:
	case RMAP_EVENT_MATCH_REPLACED:
	case RMAP_EVENT_INDEX_ADDED:
	case RMAP_EVENT_INDEX_DELETED:
		upd8_hash = NULL;
		break;
	}
	return (upd8_hash);
}

static void route_map_process_dependency(struct hash_bucket *bucket, void *data)
{
	struct route_map_dep_data *dep_data = NULL;
	char *rmap_name = NULL;

	dep_data = bucket->data;
	rmap_name = dep_data->rname;

	if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)))
		zlog_debug("Notifying %s of dependency", rmap_name);
	if (route_map_master.event_hook)
		(*route_map_master.event_hook)(rmap_name);
}

void route_map_upd8_dependency(route_map_event_t type, const char *arg,
			       const char *rmap_name)
{
	struct hash *upd8_hash = NULL;

	if ((upd8_hash = route_map_get_dep_hash(type))) {
		route_map_dep_update(upd8_hash, arg, rmap_name, type);

		if (type == RMAP_EVENT_CALL_ADDED) {
			/* Execute hook. */
			if (route_map_master.add_hook)
				(*route_map_master.add_hook)(rmap_name);
		} else if (type == RMAP_EVENT_CALL_DELETED) {
			/* Execute hook. */
			if (route_map_master.delete_hook)
				(*route_map_master.delete_hook)(rmap_name);
		}
	}
}

void route_map_notify_dependencies(const char *affected_name,
				   route_map_event_t event)
{
	struct route_map_dep *dep;
	struct hash *upd8_hash;
	char *name;

	if (!affected_name)
		return;

	name = XSTRDUP(MTYPE_ROUTE_MAP_NAME, affected_name);

	if ((upd8_hash = route_map_get_dep_hash(event)) == NULL) {
		XFREE(MTYPE_ROUTE_MAP_NAME, name);
		return;
	}

	dep = (struct route_map_dep *)hash_get(upd8_hash, name, NULL);
	if (dep) {
		if (!dep->this_hash)
			dep->this_hash = upd8_hash;

		if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)))
			zlog_debug("Filter %s updated", dep->dep_name);
		hash_iterate(dep->dep_rmap_hash, route_map_process_dependency,
			     (void *)event);
	}

	XFREE(MTYPE_ROUTE_MAP_NAME, name);
}

/* VTY related functions. */
static void clear_route_map_helper(struct route_map *map)
{
	struct route_map_index *index;

	map->applied_clear = map->applied;
	map->cputime = 0;
	for (index = map->head; index; index = index->next) {
		index->applied_clear = index->applied;
		index->cputime = 0;
	}
}

DEFPY (rmap_clear_counters,
       rmap_clear_counters_cmd,
       "clear route-map counters [RMAP_NAME$rmapname]",
       CLEAR_STR
       "route-map information\n"
       "counters associated with the specified route-map\n"
       "route-map name\n")
{
	struct route_map *map;

	if (rmapname) {
		map = route_map_lookup_by_name(rmapname);

		if (map)
			clear_route_map_helper(map);
		else {
			vty_out(vty, "%s: 'route-map %s' not found\n",
				frr_protonameinst, rmapname);
			return CMD_SUCCESS;
		}
	} else {
		for (map = route_map_master.head; map; map = map->next)
			clear_route_map_helper(map);
	}

	return CMD_SUCCESS;

}

DEFUN_NOSH (rmap_show_name,
            rmap_show_name_cmd,
            "show route-map [WORD] [json]",
            SHOW_STR
            "route-map information\n"
            "route-map name\n"
            JSON_STR)
{
	bool uj = use_json(argc, argv);
	int idx = 0;
	const char *name = NULL;

	if (argv_find(argv, argc, "WORD", &idx))
		name = argv[idx]->arg;

	return vty_show_route_map(vty, name, uj);
}

DEFUN (rmap_show_unused,
       rmap_show_unused_cmd,
       "show route-map-unused",
       SHOW_STR
       "unused route-map information\n")
{
	return vty_show_unused_route_map(vty);
}

DEFPY (debug_rmap,
       debug_rmap_cmd,
       "debug route-map [detail]$detail",
       DEBUG_STR
       "Debug option set for route-maps\n"
       "Detailed output\n")
{
	if (!detail)
		SET_FLAG(rmap_debug, DEBUG_ROUTEMAP);
	else
		SET_FLAG(rmap_debug, DEBUG_ROUTEMAP | DEBUG_ROUTEMAP_DETAIL);

	return CMD_SUCCESS;
}

DEFPY (no_debug_rmap,
       no_debug_rmap_cmd,
       "no debug route-map [detail]$detail",
       NO_STR
       DEBUG_STR
       "Debug option set for route-maps\n"
       "Detailed output\n")
{
	if (!detail)
		UNSET_FLAG(rmap_debug, DEBUG_ROUTEMAP);
	else
		UNSET_FLAG(rmap_debug, DEBUG_ROUTEMAP | DEBUG_ROUTEMAP_DETAIL);

	return CMD_SUCCESS;
}

/* Debug node. */
static int rmap_config_write_debug(struct vty *vty);
static struct cmd_node rmap_debug_node = {
	.name = "route-map debug",
	.node = RMAP_DEBUG_NODE,
	.prompt = "",
	.config_write = rmap_config_write_debug,
};

void route_map_show_debug(struct vty *vty)
{
	if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))
		vty_out(vty, "debug route-map\n");
}

/* Configuration write function. */
static int rmap_config_write_debug(struct vty *vty)
{
	int write = 0;

	if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)) {
		vty_out(vty, "debug route-map\n");
		write++;
	}

	return write;
}

/* Common route map rules */

void *route_map_rule_tag_compile(const char *arg)
{
	unsigned long int tmp;
	char *endptr;
	route_tag_t *tag;

	errno = 0;
	tmp = strtoul(arg, &endptr, 0);
	if (arg[0] == '\0' || *endptr != '\0' || errno || tmp > ROUTE_TAG_MAX)
		return NULL;

	tag = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(*tag));
	*tag = tmp;

	return tag;
}

void route_map_rule_tag_free(void *rule)
{
	XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
}

void route_map_finish(void)
{
	int i;
	struct route_map_rule_cmd_proxy *proxy;

	/* these 2 hash tables have INIT_HASH initializers, so the "default"
	 * state is "initialized & empty" => fini() followed by init() to
	 * return to that same state
	 */
	while ((proxy = rmap_cmd_name_pop(rmap_match_cmds)))
		(void)proxy;
	rmap_cmd_name_fini(rmap_match_cmds);
	rmap_cmd_name_init(rmap_match_cmds);

	while ((proxy = rmap_cmd_name_pop(rmap_set_cmds)))
		(void)proxy;
	rmap_cmd_name_fini(rmap_set_cmds);
	rmap_cmd_name_init(rmap_set_cmds);

	/*
	 * All protocols are setting these to NULL
	 * by default on shutdown( route_map_finish )
	 * Why are we making them do this work?
	 */
	route_map_master.add_hook = NULL;
	route_map_master.delete_hook = NULL;
	route_map_master.event_hook = NULL;

	/* cleanup route_map */
	while (route_map_master.head) {
		struct route_map *map = route_map_master.head;
		map->to_be_processed = false;
		route_map_delete(map);
	}

	for (i = 1; i < ROUTE_MAP_DEP_MAX; i++) {
		hash_free(route_map_dep_hash[i]);
		route_map_dep_hash[i] = NULL;
	}

	hash_free(route_map_master_hash);
	route_map_master_hash = NULL;
}

/* Increment the use_count counter while attaching the route map */
void route_map_counter_increment(struct route_map *map)
{
	if (map)
		map->use_count++;
}

/* Decrement the use_count counter while detaching the route map. */
void route_map_counter_decrement(struct route_map *map)
{
	if (map) {
		if (map->use_count <= 0)
			return;
		map->use_count--;
	}
}

DEFUN_HIDDEN(show_route_map_pfx_tbl, show_route_map_pfx_tbl_cmd,
	     "show route-map RMAP_NAME prefix-table",
	     SHOW_STR
	     "route-map\n"
	     "route-map name\n"
	     "internal prefix-table\n")
{
	const char *rmap_name = argv[2]->arg;
	struct route_map *rmap = NULL;
	struct route_table *rm_pfx_tbl4 = NULL;
	struct route_table *rm_pfx_tbl6 = NULL;
	struct route_node *rn = NULL, *prn = NULL;
	struct list *rmap_index_list = NULL;
	struct listnode *ln = NULL, *nln = NULL;
	struct route_map_index *index = NULL;
	uint8_t len = 54;

	vty_out(vty, "%s:\n", frr_protonameinst);
	rmap = route_map_lookup_by_name(rmap_name);
	if (rmap) {
		rm_pfx_tbl4 = rmap->ipv4_prefix_table;
		if (rm_pfx_tbl4) {
			vty_out(vty, "\n%s%43s%s\n", "IPv4 Prefix", "",
				"Route-map Index List");
			vty_out(vty, "%s%39s%s\n", "_______________", "",
				"____________________");
			for (rn = route_top(rm_pfx_tbl4); rn;
			     rn = route_next(rn)) {
				vty_out(vty, "    %pRN (%d)\n", rn,
					route_node_get_lock_count(rn));

				vty_out(vty, "(P) ");
				prn = rn->parent;
				if (prn) {
					vty_out(vty, "%pRN\n", prn);
				}

				vty_out(vty, "\n");
				rmap_index_list = (struct list *)rn->info;
				if (!rmap_index_list
				    || !listcount(rmap_index_list))
					vty_out(vty, "%*s%s\n", len, "", "-");
				else
					for (ALL_LIST_ELEMENTS(rmap_index_list,
							       ln, nln,
							       index)) {
						vty_out(vty, "%*s%s seq %d\n",
							len, "",
							index->map->name,
							index->pref);
					}
				vty_out(vty, "\n");
			}
		}

		rm_pfx_tbl6 = rmap->ipv6_prefix_table;
		if (rm_pfx_tbl6) {
			vty_out(vty, "\n%s%43s%s\n", "IPv6 Prefix", "",
				"Route-map Index List");
			vty_out(vty, "%s%39s%s\n", "_______________", "",
				"____________________");
			for (rn = route_top(rm_pfx_tbl6); rn;
			     rn = route_next(rn)) {
				vty_out(vty, "    %pRN (%d)\n", rn,
					route_node_get_lock_count(rn));

				vty_out(vty, "(P) ");
				prn = rn->parent;
				if (prn) {
					vty_out(vty, "%pRN\n", prn);
				}

				vty_out(vty, "\n");
				rmap_index_list = (struct list *)rn->info;
				if (!rmap_index_list
				    || !listcount(rmap_index_list))
					vty_out(vty, "%*s%s\n", len, "", "-");
				else
					for (ALL_LIST_ELEMENTS(rmap_index_list,
							       ln, nln,
							       index)) {
						vty_out(vty, "%*s%s seq %d\n",
							len, "",
							index->map->name,
							index->pref);
					}
				vty_out(vty, "\n");
			}
		}
	}

	vty_out(vty, "\n");
	return CMD_SUCCESS;
}

/* Initialization of route map vector. */
void route_map_init_new(bool in_backend)
{
	int i;

	route_map_master_hash =
		hash_create_size(8, route_map_hash_key_make, route_map_hash_cmp,
				 "Route Map Master Hash");

	for (i = 1; i < ROUTE_MAP_DEP_MAX; i++)
		route_map_dep_hash[i] = hash_create_size(
			8, route_map_dep_hash_make_key, route_map_dep_hash_cmp,
			"Route Map Dep Hash");

	UNSET_FLAG(rmap_debug, DEBUG_ROUTEMAP);

	if (!in_backend) {
		/* we do not want to handle config commands in the backend */
		route_map_cli_init();
	}

	/* Install route map top node. */
	install_node(&rmap_debug_node);

	/* Install route map commands. */
	install_element(CONFIG_NODE, &debug_rmap_cmd);
	install_element(CONFIG_NODE, &no_debug_rmap_cmd);

	/* Install show command */
	install_element(ENABLE_NODE, &rmap_clear_counters_cmd);

	install_element(ENABLE_NODE, &rmap_show_name_cmd);
	install_element(ENABLE_NODE, &rmap_show_unused_cmd);

	install_element(ENABLE_NODE, &debug_rmap_cmd);
	install_element(ENABLE_NODE, &no_debug_rmap_cmd);

	install_element(ENABLE_NODE, &show_route_map_pfx_tbl_cmd);
}

void route_map_init(void)
{
	route_map_init_new(false);
}
