mirror of
				https://git.proxmox.com/git/mirror_frr
				synced 2025-11-01 05:25:20 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			3445 lines
		
	
	
		
			91 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			3445 lines
		
	
	
		
			91 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // 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;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /* 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);
 | |
| 	} else {
 | |
| 		vty_out(vty,
 | |
| 			"route-map: %s Invoked: %" PRIu64
 | |
| 			" Optimization: %s Processed Change: %s\n",
 | |
| 			map->name, map->applied - map->applied_clear,
 | |
| 			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);
 | |
| 
 | |
| 			/* 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 "\n",
 | |
| 				route_map_type_str(index->type), index->pref,
 | |
| 				index->applied - index->applied_clear);
 | |
| 
 | |
| 			/* 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;
 | |
| 	json_object *json_proto = NULL;
 | |
| 
 | |
| 	if (use_json) {
 | |
| 		json = json_object_new_object();
 | |
| 		json_proto = json_object_new_object();
 | |
| 		json_object_object_add(json, frr_protonameinst, json_proto);
 | |
| 	} 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_proto);
 | |
| 		} 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_proto);
 | |
| 
 | |
| 		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;
 | |
| 	struct prefix conv;
 | |
| 
 | |
| 	if (recursion > RMAP_RECURSION_LIMIT) {
 | |
| 		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) {
 | |
| 		ret = RMAP_DENYMATCH;
 | |
| 		goto route_map_apply_end;
 | |
| 	}
 | |
| 
 | |
| 	map->applied++;
 | |
| 
 | |
| 	/*
 | |
| 	 * Handling for matching evpn_routes in the prefix table.
 | |
| 	 *
 | |
| 	 * We convert type2/5 prefix to ipv4/6 prefix to do longest
 | |
| 	 * prefix matching on.
 | |
| 	 */
 | |
| 	if (prefix->family == AF_EVPN) {
 | |
| 		if (evpn_prefix2prefix(prefix, &conv) != 0) {
 | |
| 			if (unlikely(CHECK_FLAG(rmap_debug,
 | |
| 						DEBUG_ROUTEMAP_DETAIL)))
 | |
| 				zlog_debug(
 | |
| 					"Unable to convert EVPN prefix %pFX into IPv4/IPv6 prefix. Falling back to non-optimized route-map lookup",
 | |
| 					prefix);
 | |
| 		} else {
 | |
| 			if (unlikely(CHECK_FLAG(rmap_debug,
 | |
| 						DEBUG_ROUTEMAP_DETAIL)))
 | |
| 				zlog_debug(
 | |
| 					"Converted EVPN prefix %pFX into %pFX for optimized route-map lookup",
 | |
| 					prefix, &conv);
 | |
| 
 | |
| 			prefix = &conv;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	index = route_map_get_index(map, prefix, match_object, &match_ret);
 | |
| 	if (index) {
 | |
| 		index->applied++;
 | |
| 		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;
 | |
| 		goto route_map_apply_end;
 | |
| 	}
 | |
| 	skip_match_clause = true;
 | |
| 
 | |
| 	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)
 | |
| 						goto route_map_apply_end;
 | |
| 				}
 | |
| 
 | |
| 				switch (index->exitpolicy) {
 | |
| 				case RMAP_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! */
 | |
| 						goto route_map_apply_end;
 | |
| 					}
 | |
| 				}
 | |
| 				}
 | |
| 			} else if (index->type == RMAP_DENY)
 | |
| 			/* 'deny' */
 | |
| 			{
 | |
| 				ret = RMAP_DENYMATCH;
 | |
| 				goto route_map_apply_end;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| route_map_apply_end:
 | |
| 	if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)))
 | |
| 		zlog_debug("Route-map: %s, prefix: %pFX, result: %s",
 | |
| 			   (map ? map->name : "null"), prefix,
 | |
| 			   route_map_result_str(ret));
 | |
| 
 | |
| 	if (pref) {
 | |
| 		if (index != NULL && ret == RMAP_PERMITMATCH)
 | |
| 			*pref = index->pref;
 | |
| 		else
 | |
| 			*pref = 65536;
 | |
| 	}
 | |
| 
 | |
| 	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;
 | |
| 	for (index = map->head; index; index = index->next)
 | |
| 		index->applied_clear = index->applied;
 | |
| }
 | |
| 
 | |
| 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 (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(void)
 | |
| {
 | |
| 	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);
 | |
| 
 | |
| 	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);
 | |
| }
 | 
