summaryrefslogtreecommitdiffstats
path: root/lib/routemap.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-09 13:16:35 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-09 13:16:35 +0000
commite2bbf175a2184bd76f6c54ccf8456babeb1a46fc (patch)
treef0b76550d6e6f500ada964a3a4ee933a45e5a6f1 /lib/routemap.c
parentInitial commit. (diff)
downloadfrr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.tar.xz
frr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.zip
Adding upstream version 9.1.upstream/9.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'lib/routemap.c')
-rw-r--r--lib/routemap.c3449
1 files changed, 3449 insertions, 0 deletions
diff --git a/lib/routemap.c b/lib/routemap.c
new file mode 100644
index 0000000..e0b0eb7
--- /dev/null
+++ b/lib/routemap.c
@@ -0,0 +1,3449 @@
+// 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) {
+ if (map)
+ map->applied++;
+
+ flog_warn(
+ EC_LIB_RMAP_RECURSION_LIMIT,
+ "route-map recursion limit (%d) reached, discarding route",
+ RMAP_RECURSION_LIMIT);
+ recursion = 0;
+ return RMAP_DENYMATCH;
+ }
+
+ if (map == NULL || map->head == NULL) {
+ if (map)
+ map->applied++;
+ ret = RMAP_DENYMATCH;
+ 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);
+}