// SPDX-License-Identifier: GPL-2.0-or-later /* zebra routemap. * Copyright (C) 2006 IBM Corporation */ #include #include "memory.h" #include "prefix.h" #include "rib.h" #include "vty.h" #include "routemap.h" #include "command.h" #include "filter.h" #include "plist.h" #include "nexthop.h" #include "lib/route_types.h" #include "vrf.h" #include "frrstr.h" #include "zebra/zebra_router.h" #include "zebra/redistribute.h" #include "zebra/debug.h" #include "zebra/zebra_rnh.h" #include "zebra/zebra_routemap.h" #include "zebra/zebra_vrf.h" #include "zebra/zebra_routemap_clippy.c" static uint32_t zebra_rmap_update_timer = ZEBRA_RMAP_DEFAULT_UPDATE_TIMER; static struct event *zebra_t_rmap_update = NULL; char *zebra_import_table_routemap[AFI_MAX][ZEBRA_KERNEL_TABLE_MAX]; struct zebra_rmap_obj { struct nexthop *nexthop; struct route_entry *re; }; /* 'match tag TAG' * Match function return 1 if match is success else return 0 */ static enum route_map_cmd_result_t route_match_tag(void *rule, const struct prefix *prefix, void *object) { route_tag_t *tag; struct zebra_rmap_obj *rm_data; tag = rule; rm_data = object; if (rm_data->re->tag == *tag) return RMAP_MATCH; return RMAP_NOMATCH; } /* Route map commands for tag matching */ static const struct route_map_rule_cmd route_match_tag_cmd = { "tag", route_match_tag, route_map_rule_tag_compile, route_map_rule_tag_free, }; /* `match interface IFNAME' */ /* Match function return 1 if match is success else return zero. */ static enum route_map_cmd_result_t route_match_interface(void *rule, const struct prefix *prefix, void *object) { struct zebra_rmap_obj *rm_data; char *ifname = rule; ifindex_t ifindex; if (strcasecmp(ifname, "any") == 0) return RMAP_MATCH; rm_data = object; if (!rm_data || !rm_data->nexthop) return RMAP_NOMATCH; ifindex = ifname2ifindex(ifname, rm_data->nexthop->vrf_id); if (ifindex == 0) return RMAP_NOMATCH; if (rm_data->nexthop->ifindex == ifindex) return RMAP_MATCH; return RMAP_NOMATCH; } /* Route map `match interface' match statement. `arg' is IFNAME value */ static void *route_match_interface_compile(const char *arg) { return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg); } /* Free route map's compiled `match interface' value. */ static void route_match_interface_free(void *rule) { XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); } static void show_vrf_proto_rm(struct vty *vty, struct zebra_vrf *zvrf, int af_type) { int i; vty_out(vty, "Protocol : route-map\n"); vty_out(vty, "-------------------------------------\n"); for (i = 0; i < ZEBRA_ROUTE_MAX; i++) { if (PROTO_RM_NAME(zvrf, af_type, i)) vty_out(vty, "%-24s : %-10s\n", zebra_route_string(i), PROTO_RM_NAME(zvrf, af_type, i)); else vty_out(vty, "%-24s : none\n", zebra_route_string(i)); } if (PROTO_RM_NAME(zvrf, af_type, i)) vty_out(vty, "%-24s : %-10s\n", "any", PROTO_RM_NAME(zvrf, af_type, i)); else vty_out(vty, "%-24s : none\n", "any"); } static void show_vrf_nht_rm(struct vty *vty, struct zebra_vrf *zvrf, int af_type, json_object *json) { int i; if (!json) { vty_out(vty, "Protocol : route-map\n"); vty_out(vty, "-------------------------------------\n"); } for (i = 0; i < ZEBRA_ROUTE_MAX; i++) { if (json) { if (NHT_RM_NAME(zvrf, af_type, i)) json_object_string_add( json, zebra_route_string(i), NHT_RM_NAME(zvrf, af_type, i)); else json_object_string_add( json, zebra_route_string(i), "none"); } else { if (NHT_RM_NAME(zvrf, af_type, i)) vty_out(vty, "%-24s : %-10s\n", zebra_route_string(i), NHT_RM_NAME(zvrf, af_type, i)); else vty_out(vty, "%-24s : none\n", zebra_route_string(i)); } } if (json) { if (NHT_RM_NAME(zvrf, af_type, i)) json_object_string_add(json, "any", NHT_RM_NAME(zvrf, af_type, i)); else json_object_string_add(json, "any", "none"); } else { if (NHT_RM_NAME(zvrf, af_type, i)) vty_out(vty, "%-24s : %-10s\n", "any", NHT_RM_NAME(zvrf, af_type, i)); else vty_out(vty, "%-24s : none\n", "any"); } } static int show_proto_rm(struct vty *vty, int af_type, const char *vrf_all, const char *vrf_name) { struct zebra_vrf *zvrf; if (vrf_all) { struct vrf *vrf; RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { zvrf = (struct zebra_vrf *)vrf->info; if (zvrf == NULL) continue; vty_out(vty, "VRF: %s\n", zvrf->vrf->name); show_vrf_proto_rm(vty, zvrf, af_type); } } else { vrf_id_t vrf_id = VRF_DEFAULT; if (vrf_name) VRF_GET_ID(vrf_id, vrf_name, false); zvrf = zebra_vrf_lookup_by_id(vrf_id); if (!zvrf) return CMD_SUCCESS; vty_out(vty, "VRF: %s\n", zvrf->vrf->name); show_vrf_proto_rm(vty, zvrf, af_type); } return CMD_SUCCESS; } static int show_nht_rm(struct vty *vty, int af_type, const char *vrf_all, const char *vrf_name, bool use_json) { struct zebra_vrf *zvrf; json_object *json = NULL; json_object *json_vrfs = NULL; if (use_json) { json = json_object_new_object(); json_vrfs = json_object_new_object(); json_object_string_add(json, "afi", (af_type == AFI_IP) ? "ipv4" : "ipv6"); } if (vrf_all) { struct vrf *vrf; if (use_json) json_object_object_add(json, "vrfs", json_vrfs); RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { zvrf = (struct zebra_vrf *)vrf->info; if (zvrf == NULL) continue; if (use_json) { json_object *json_proto = NULL; json_object *json_vrf = NULL; json_vrf = json_object_new_object(); json_object_object_add( json_vrfs, zvrf->vrf->name, json_vrf); json_proto = json_object_new_object(); json_object_object_add(json_vrf, "protocols", json_proto); show_vrf_nht_rm(vty, zvrf, af_type, json_proto); } else { vty_out(vty, "VRF: %s\n", zvrf->vrf->name); show_vrf_nht_rm(vty, zvrf, af_type, NULL); } } } else { json_object *json_proto = NULL; json_object *json_vrf = NULL; vrf_id_t vrf_id = VRF_DEFAULT; if (vrf_name) VRF_GET_ID(vrf_id, vrf_name, false); zvrf = zebra_vrf_lookup_by_id(vrf_id); if (!zvrf) { json_object_free(json); json_object_free(json_vrfs); return CMD_SUCCESS; } if (use_json) { json_object_object_add(json, "vrfs", json_vrfs); json_vrf = json_object_new_object(); json_object_object_add(json_vrfs, zvrf->vrf->name, json_vrf); json_proto = json_object_new_object(); json_object_object_add(json_vrf, "protocols", json_proto); show_vrf_nht_rm(vty, zvrf, af_type, json_proto); } else { vty_out(vty, "VRF: %s\n", zvrf->vrf->name); show_vrf_nht_rm(vty, zvrf, af_type, NULL); } } if (use_json) vty_json(vty, json); return CMD_SUCCESS; } /* Route map commands for interface matching */ static const struct route_map_rule_cmd route_match_interface_cmd = { "interface", route_match_interface, route_match_interface_compile, route_match_interface_free }; int ip_protocol_rm_add(struct zebra_vrf *zvrf, const char *rmap, int rtype, afi_t afi, safi_t safi) { struct route_table *table; if (PROTO_RM_NAME(zvrf, afi, rtype)) { if (strcmp(PROTO_RM_NAME(zvrf, afi, rtype), rmap) == 0) return CMD_SUCCESS; XFREE(MTYPE_ROUTE_MAP_NAME, PROTO_RM_NAME(zvrf, afi, rtype)); } route_map_counter_decrement(PROTO_RM_MAP(zvrf, afi, rtype)); PROTO_RM_NAME(zvrf, afi, rtype) = XSTRDUP(MTYPE_ROUTE_MAP_NAME, rmap); PROTO_RM_MAP(zvrf, afi, rtype) = route_map_lookup_by_name(PROTO_RM_NAME(zvrf, afi, rtype)); route_map_counter_increment(PROTO_RM_MAP(zvrf, afi, rtype)); if (PROTO_RM_NAME(zvrf, afi, rtype)) { if (IS_ZEBRA_DEBUG_RIB_DETAILED) zlog_debug( "%u: IPv4 Routemap config for protocol %d scheduling RIB processing", zvrf->vrf->vrf_id, rtype); /* Process routes of interested address-families. */ table = zebra_vrf_table(afi, safi, zvrf->vrf->vrf_id); if (table) rib_update_table(table, RIB_UPDATE_RMAP_CHANGE, rtype); } return CMD_SUCCESS; } int ip_protocol_rm_del(struct zebra_vrf *zvrf, const char *rmap, int rtype, afi_t afi, safi_t safi) { struct route_table *table; if (!PROTO_RM_NAME(zvrf, afi, rtype)) return CMD_SUCCESS; if (!rmap || strcmp(rmap, PROTO_RM_NAME(zvrf, afi, rtype)) == 0) { route_map_counter_decrement(PROTO_RM_MAP(zvrf, afi, rtype)); if (PROTO_RM_NAME(zvrf, afi, rtype)) { if (IS_ZEBRA_DEBUG_RIB_DETAILED) zlog_debug( "%u: IPv4 Routemap unconfig for protocol %d, scheduling RIB processing", zvrf->vrf->vrf_id, rtype); PROTO_RM_MAP(zvrf, afi, rtype) = NULL; /* Process routes of interested address-families. */ table = zebra_vrf_table(afi, safi, zvrf->vrf->vrf_id); if (table) rib_update_table(table, RIB_UPDATE_RMAP_CHANGE, rtype); } XFREE(MTYPE_ROUTE_MAP_NAME, PROTO_RM_NAME(zvrf, afi, rtype)); } return CMD_SUCCESS; } int ip_nht_rm_add(struct zebra_vrf *zvrf, const char *rmap, int rtype, int afi) { if (NHT_RM_NAME(zvrf, afi, rtype)) { if (strcmp(NHT_RM_NAME(zvrf, afi, rtype), rmap) == 0) return CMD_SUCCESS; XFREE(MTYPE_ROUTE_MAP_NAME, NHT_RM_NAME(zvrf, afi, rtype)); } route_map_counter_decrement(NHT_RM_MAP(zvrf, afi, rtype)); NHT_RM_NAME(zvrf, afi, rtype) = XSTRDUP(MTYPE_ROUTE_MAP_NAME, rmap); NHT_RM_MAP(zvrf, afi, rtype) = route_map_lookup_by_name(NHT_RM_NAME(zvrf, afi, rtype)); route_map_counter_increment(NHT_RM_MAP(zvrf, afi, rtype)); if (NHT_RM_MAP(zvrf, afi, rtype)) zebra_evaluate_rnh(zvrf, afi, 1, NULL, SAFI_UNICAST); return CMD_SUCCESS; } int ip_nht_rm_del(struct zebra_vrf *zvrf, const char *rmap, int rtype, int afi) { if (!NHT_RM_NAME(zvrf, afi, rtype)) return CMD_SUCCESS; if (!rmap || strcmp(rmap, NHT_RM_NAME(zvrf, afi, rtype)) == 0) { route_map_counter_decrement(NHT_RM_MAP(zvrf, afi, rtype)); if (NHT_RM_MAP(zvrf, afi, rtype)) { if (IS_ZEBRA_DEBUG_RIB_DETAILED) zlog_debug( "%u: IPv4 Routemap unconfig for protocol %d, scheduling RIB processing", zvrf->vrf->vrf_id, rtype); NHT_RM_MAP(zvrf, afi, rtype) = NULL; zebra_evaluate_rnh(zvrf, afi, 1, NULL, SAFI_UNICAST); } XFREE(MTYPE_ROUTE_MAP_NAME, NHT_RM_NAME(zvrf, afi, rtype)); } return CMD_SUCCESS; } DEFPY (show_ip_protocol, show_ip_protocol_cmd, "show ip protocol [vrf ]", SHOW_STR IP_STR "IP protocol filtering status\n" VRF_FULL_CMD_HELP_STR) { int ret = show_proto_rm(vty, AFI_IP, vrf_all, vrf_name); return ret; } DEFPY (show_ipv6_protocol, show_ipv6_protocol_cmd, "show ipv6 protocol [vrf ]", SHOW_STR IP6_STR "IPv6 protocol filtering status\n" VRF_FULL_CMD_HELP_STR) { int ret = show_proto_rm(vty, AFI_IP6, vrf_all, vrf_name); return ret; } DEFPY (show_ip_protocol_nht, show_ip_protocol_nht_cmd, "show ip nht route-map [vrf ] [json]", SHOW_STR IP_STR "IPv4 nexthop tracking table\n" "IPv4 Next Hop tracking filtering status\n" VRF_CMD_HELP_STR "All VRFs\n" JSON_STR) { int ret; bool uj = use_json(argc, argv); ret = show_nht_rm(vty, AFI_IP, vrf_all, vrf_name, uj); return ret; } DEFPY (show_ipv6_protocol_nht, show_ipv6_protocol_nht_cmd, "show ipv6 nht route-map [vrf ] [json]", SHOW_STR IP6_STR "IPv6 nexthop tracking table\n" "IPv6 Next Hop tracking filtering status\n" VRF_CMD_HELP_STR "All VRFs\n" JSON_STR) { int ret; bool uj = use_json(argc, argv); ret = show_nht_rm(vty, AFI_IP6, vrf_all, vrf_name, uj); return ret; } /*XXXXXXXXXXXXXXXXXXXXXXXXXXXX*/ /* `match ip next-hop IP_ACCESS_LIST' */ /* Match function return 1 if match is success else return zero. */ static enum route_map_cmd_result_t route_match_ip_next_hop(void *rule, const struct prefix *prefix, void *object) { struct access_list *alist; struct zebra_rmap_obj *rm_data; struct prefix_ipv4 p; rm_data = object; if (!rm_data) return RMAP_NOMATCH; switch (rm_data->nexthop->type) { case NEXTHOP_TYPE_IFINDEX: /* Interface routes can't match ip next-hop */ return RMAP_NOMATCH; case NEXTHOP_TYPE_IPV4_IFINDEX: case NEXTHOP_TYPE_IPV4: p.family = AF_INET; p.prefix = rm_data->nexthop->gate.ipv4; p.prefixlen = IPV4_MAX_BITLEN; break; case NEXTHOP_TYPE_IPV6: case NEXTHOP_TYPE_IPV6_IFINDEX: case NEXTHOP_TYPE_BLACKHOLE: return RMAP_NOMATCH; } alist = access_list_lookup(AFI_IP, (char *)rule); if (alist == NULL) { if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "%s: Access-List Specified: %s does not exist defaulting to NO_MATCH", __func__, (char *)rule); return RMAP_NOMATCH; } return (access_list_apply(alist, &p) == FILTER_DENY ? RMAP_NOMATCH : RMAP_MATCH); } /* Route map `ip next-hop' match statement. `arg' should be access-list name. */ static void *route_match_ip_next_hop_compile(const char *arg) { return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg); } /* Free route map's compiled `. */ static void route_match_ip_next_hop_free(void *rule) { XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); } /* Route map commands for ip next-hop matching. */ static const struct route_map_rule_cmd route_match_ip_next_hop_cmd = { "ip next-hop", route_match_ip_next_hop, route_match_ip_next_hop_compile, route_match_ip_next_hop_free }; /* `match ip next-hop prefix-list PREFIX_LIST' */ static enum route_map_cmd_result_t route_match_ip_next_hop_prefix_list(void *rule, const struct prefix *prefix, void *object) { struct prefix_list *plist; struct zebra_rmap_obj *rm_data; struct prefix_ipv4 p; rm_data = (struct zebra_rmap_obj *)object; if (!rm_data) return RMAP_NOMATCH; switch (rm_data->nexthop->type) { case NEXTHOP_TYPE_IFINDEX: /* Interface routes can't match ip next-hop */ return RMAP_NOMATCH; case NEXTHOP_TYPE_IPV4_IFINDEX: case NEXTHOP_TYPE_IPV4: p.family = AF_INET; p.prefix = rm_data->nexthop->gate.ipv4; p.prefixlen = IPV4_MAX_BITLEN; break; case NEXTHOP_TYPE_IPV6: case NEXTHOP_TYPE_IPV6_IFINDEX: case NEXTHOP_TYPE_BLACKHOLE: return RMAP_NOMATCH; } plist = prefix_list_lookup(AFI_IP, (char *)rule); if (plist == NULL) { if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "%s: Prefix List %s specified does not exist defaulting to NO_MATCH", __func__, (char *)rule); return RMAP_NOMATCH; } return (prefix_list_apply(plist, &p) == PREFIX_DENY ? RMAP_NOMATCH : RMAP_MATCH); } static void *route_match_ip_next_hop_prefix_list_compile(const char *arg) { return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg); } static void route_match_ip_next_hop_prefix_list_free(void *rule) { XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); } static const struct route_map_rule_cmd route_match_ip_next_hop_prefix_list_cmd = { "ip next-hop prefix-list", route_match_ip_next_hop_prefix_list, route_match_ip_next_hop_prefix_list_compile, route_match_ip_next_hop_prefix_list_free }; /* `match ip address IP_ACCESS_LIST' */ /* Match function should return 1 if match is success else return zero. */ static enum route_map_cmd_result_t route_match_address(afi_t afi, void *rule, const struct prefix *prefix, void *object) { struct access_list *alist; alist = access_list_lookup(afi, (char *)rule); if (alist == NULL) { if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "%s: Access-List Specified: %s does not exist defaulting to NO_MATCH", __func__, (char *)rule); return RMAP_NOMATCH; } return (access_list_apply(alist, prefix) == FILTER_DENY ? RMAP_NOMATCH : RMAP_MATCH); } static enum route_map_cmd_result_t route_match_ip_address(void *rule, const struct prefix *prefix, void *object) { return route_match_address(AFI_IP, rule, prefix, object); } static enum route_map_cmd_result_t route_match_ipv6_address(void *rule, const struct prefix *prefix, void *object) { return route_match_address(AFI_IP6, rule, prefix, object); } /* Route map `ip address' match statement. `arg' should be access-list name. */ static void *route_match_address_compile(const char *arg) { return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg); } /* Free route map's compiled `ip address' value. */ static void route_match_address_free(void *rule) { XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); } /* Route map commands for ip address matching. */ static const struct route_map_rule_cmd route_match_ip_address_cmd = { "ip address", route_match_ip_address, route_match_address_compile, route_match_address_free }; /* Route map commands for ipv6 address matching. */ static const struct route_map_rule_cmd route_match_ipv6_address_cmd = { "ipv6 address", route_match_ipv6_address, route_match_address_compile, route_match_address_free }; /* `match ip address prefix-list PREFIX_LIST' */ static enum route_map_cmd_result_t route_match_address_prefix_list(void *rule, const struct prefix *prefix, void *object, afi_t afi) { struct prefix_list *plist; plist = prefix_list_lookup(afi, (char *)rule); if (plist == NULL) { if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "%s: Prefix List %s specified does not exist defaulting to NO_MATCH", __func__, (char *)rule); return RMAP_NOMATCH; } return (prefix_list_apply(plist, prefix) == PREFIX_DENY ? RMAP_NOMATCH : RMAP_MATCH); } static enum route_map_cmd_result_t route_match_ip_address_prefix_list(void *rule, const struct prefix *prefix, void *object) { return (route_match_address_prefix_list(rule, prefix, object, AFI_IP)); } static void *route_match_address_prefix_list_compile(const char *arg) { return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg); } static void route_match_address_prefix_list_free(void *rule) { XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); } static const struct route_map_rule_cmd route_match_ip_address_prefix_list_cmd = { "ip address prefix-list", route_match_ip_address_prefix_list, route_match_address_prefix_list_compile, route_match_address_prefix_list_free }; static enum route_map_cmd_result_t route_match_ipv6_address_prefix_list(void *rule, const struct prefix *prefix, void *object) { return (route_match_address_prefix_list(rule, prefix, object, AFI_IP6)); } static const struct route_map_rule_cmd route_match_ipv6_address_prefix_list_cmd = { "ipv6 address prefix-list", route_match_ipv6_address_prefix_list, route_match_address_prefix_list_compile, route_match_address_prefix_list_free }; /* `match ipv6 next-hop type ' */ static enum route_map_cmd_result_t route_match_ipv6_next_hop_type(void *rule, const struct prefix *prefix, void *object) { struct zebra_rmap_obj *rm_data; if (prefix->family == AF_INET6) { rm_data = (struct zebra_rmap_obj *)object; if (!rm_data) return RMAP_NOMATCH; if (rm_data->nexthop->type == NEXTHOP_TYPE_BLACKHOLE) return RMAP_MATCH; } return RMAP_NOMATCH; } static void *route_match_ipv6_next_hop_type_compile(const char *arg) { return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg); } static void route_match_ipv6_next_hop_type_free(void *rule) { XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); } static const struct route_map_rule_cmd route_match_ipv6_next_hop_type_cmd = { "ipv6 next-hop type", route_match_ipv6_next_hop_type, route_match_ipv6_next_hop_type_compile, route_match_ipv6_next_hop_type_free }; /* `match ip address prefix-len PREFIXLEN' */ static enum route_map_cmd_result_t route_match_address_prefix_len(void *rule, const struct prefix *prefix, void *object) { uint32_t *prefixlen = (uint32_t *)rule; return ((prefix->prefixlen == *prefixlen) ? RMAP_MATCH : RMAP_NOMATCH); } static void *route_match_address_prefix_len_compile(const char *arg) { uint32_t *prefix_len; char *endptr = NULL; unsigned long tmpval; /* prefix len value shoud be integer. */ if (!all_digit(arg)) return NULL; errno = 0; tmpval = strtoul(arg, &endptr, 10); if (*endptr != '\0' || errno || tmpval > UINT32_MAX) return NULL; prefix_len = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(uint32_t)); *prefix_len = tmpval; return prefix_len; } static void route_match_address_prefix_len_free(void *rule) { XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); } static const struct route_map_rule_cmd route_match_ip_address_prefix_len_cmd = { "ip address prefix-len", route_match_address_prefix_len, route_match_address_prefix_len_compile, route_match_address_prefix_len_free }; static const struct route_map_rule_cmd route_match_ipv6_address_prefix_len_cmd = { "ipv6 address prefix-len", route_match_address_prefix_len, route_match_address_prefix_len_compile, route_match_address_prefix_len_free }; /* `match ip nexthop prefix-len PREFIXLEN' */ static enum route_map_cmd_result_t route_match_ip_nexthop_prefix_len(void *rule, const struct prefix *prefix, void *object) { uint32_t *prefixlen = (uint32_t *)rule; struct zebra_rmap_obj *rm_data; struct prefix_ipv4 p; rm_data = (struct zebra_rmap_obj *)object; if (!rm_data || !rm_data->nexthop) return RMAP_NOMATCH; switch (rm_data->nexthop->type) { case NEXTHOP_TYPE_IFINDEX: /* Interface routes can't match ip next-hop */ return RMAP_NOMATCH; case NEXTHOP_TYPE_IPV4_IFINDEX: case NEXTHOP_TYPE_IPV4: p.family = AF_INET; p.prefix = rm_data->nexthop->gate.ipv4; p.prefixlen = IPV4_MAX_BITLEN; break; case NEXTHOP_TYPE_IPV6: case NEXTHOP_TYPE_IPV6_IFINDEX: case NEXTHOP_TYPE_BLACKHOLE: return RMAP_NOMATCH; } return ((p.prefixlen == *prefixlen) ? RMAP_MATCH : RMAP_NOMATCH); } static const struct route_map_rule_cmd route_match_ip_nexthop_prefix_len_cmd = { "ip next-hop prefix-len", route_match_ip_nexthop_prefix_len, route_match_address_prefix_len_compile, /* reuse */ route_match_address_prefix_len_free /* reuse */ }; /* `match ip next-hop type ' */ static enum route_map_cmd_result_t route_match_ip_next_hop_type(void *rule, const struct prefix *prefix, void *object) { struct zebra_rmap_obj *rm_data; if (prefix->family == AF_INET) { rm_data = (struct zebra_rmap_obj *)object; if (!rm_data) return RMAP_NOMATCH; if (rm_data->nexthop->type == NEXTHOP_TYPE_BLACKHOLE) return RMAP_MATCH; } return RMAP_NOMATCH; } static void *route_match_ip_next_hop_type_compile(const char *arg) { return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg); } static void route_match_ip_next_hop_type_free(void *rule) { XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); } static const struct route_map_rule_cmd route_match_ip_next_hop_type_cmd = { "ip next-hop type", route_match_ip_next_hop_type, route_match_ip_next_hop_type_compile, route_match_ip_next_hop_type_free }; /* `match source-protocol PROTOCOL' */ static enum route_map_cmd_result_t route_match_source_protocol(void *rule, const struct prefix *p, void *object) { int32_t *rib_type = (int32_t *)rule; struct zebra_rmap_obj *rm_data; rm_data = (struct zebra_rmap_obj *)object; if (!rm_data) return RMAP_NOMATCH; return ((rm_data->re->type == *rib_type) ? RMAP_MATCH : RMAP_NOMATCH); } static void *route_match_source_protocol_compile(const char *arg) { uint32_t *rib_type; int i; i = proto_name2num(arg); rib_type = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(uint32_t)); *rib_type = i; return rib_type; } static void route_match_source_protocol_free(void *rule) { XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); } static const struct route_map_rule_cmd route_match_source_protocol_cmd = { "source-protocol", route_match_source_protocol, route_match_source_protocol_compile, route_match_source_protocol_free }; /* `source-instance` */ static enum route_map_cmd_result_t route_match_source_instance(void *rule, const struct prefix *p, void *object) { uint8_t *instance = (uint8_t *)rule; struct zebra_rmap_obj *rm_data; rm_data = (struct zebra_rmap_obj *)object; if (!rm_data) return RMAP_NOMATCH; return (rm_data->re->instance == *instance) ? RMAP_MATCH : RMAP_NOMATCH; } static void *route_match_source_instance_compile(const char *arg) { uint8_t *instance; int i; i = atoi(arg); instance = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(uint8_t)); *instance = i; return instance; } static void route_match_source_instance_free(void *rule) { XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); } static const struct route_map_rule_cmd route_match_source_instance_cmd = { "source-instance", route_match_source_instance, route_match_source_instance_compile, route_match_source_instance_free }; /* `set src A.B.C.D' */ /* Set src. */ static enum route_map_cmd_result_t route_set_src(void *rule, const struct prefix *prefix, void *object) { struct zebra_rmap_obj *rm_data; rm_data = (struct zebra_rmap_obj *)object; rm_data->nexthop->rmap_src = *(union g_addr *)rule; return RMAP_OKAY; } /* set src compilation. */ static void *route_set_src_compile(const char *arg) { union g_addr src, *psrc; if ((inet_pton(AF_INET6, arg, &src.ipv6) == 1) || (inet_pton(AF_INET, arg, &src.ipv4) == 1)) { psrc = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(union g_addr)); *psrc = src; return psrc; } return NULL; } /* Free route map's compiled `set src' value. */ static void route_set_src_free(void *rule) { XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); } /* Set src rule structure. */ static const struct route_map_rule_cmd route_set_src_cmd = { "src", route_set_src, route_set_src_compile, route_set_src_free, }; /* The function checks if the changed routemap specified by parameter rmap * matches the configured protocol routemaps in proto_rm table. If there is * a match then rib_update_table() to process the routes. */ static void zebra_rib_table_rm_update(const char *rmap) { int i = 0; struct route_table *table; struct vrf *vrf = NULL; struct zebra_vrf *zvrf = NULL; char *rmap_name; struct route_map *old = NULL; RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { zvrf = vrf->info; if (!zvrf) continue; for (i = 0; i <= ZEBRA_ROUTE_MAX; i++) { rmap_name = PROTO_RM_NAME(zvrf, AFI_IP, i); if (rmap_name && (strcmp(rmap_name, rmap) == 0)) { if (IS_ZEBRA_DEBUG_EVENT) zlog_debug( "%s : AFI_IP rmap %s, route type %s", __func__, rmap, zebra_route_string(i)); old = PROTO_RM_MAP(zvrf, AFI_IP, i); PROTO_RM_MAP(zvrf, AFI_IP, i) = route_map_lookup_by_name(rmap_name); /* old is NULL. i.e Route map creation event. * So update applied_counter. * If Old is not NULL, i.e It may be routemap * updation or deletion. * So no need to update the counter. */ if (!old) route_map_counter_increment( PROTO_RM_MAP(zvrf, AFI_IP, i)); /* There is single rib table for all protocols */ table = zvrf->table[AFI_IP][SAFI_UNICAST]; if (table) { rib_update_table( table, RIB_UPDATE_RMAP_CHANGE, i); } } rmap_name = PROTO_RM_NAME(zvrf, AFI_IP6, i); if (rmap_name && (strcmp(rmap_name, rmap) == 0)) { if (IS_ZEBRA_DEBUG_EVENT) zlog_debug( "%s : AFI_IP6 rmap %s, route type %s", __func__, rmap, zebra_route_string(i)); old = PROTO_RM_MAP(zvrf, AFI_IP6, i); PROTO_RM_MAP(zvrf, AFI_IP6, i) = route_map_lookup_by_name(rmap_name); if (!old) route_map_counter_increment( PROTO_RM_MAP(zvrf, AFI_IP6, i)); /* There is single rib table for all protocols */ table = zvrf->table[AFI_IP6][SAFI_UNICAST]; if (table) { rib_update_table( table, RIB_UPDATE_RMAP_CHANGE, i); } } } } } /* The function checks if the changed routemap specified by parameter rmap * matches the configured protocol routemaps in nht_rm table. If there is * a match then zebra_evaluate_rnh() to process the nexthops. */ static void zebra_nht_rm_update(const char *rmap) { int i = 0; struct route_table *table; struct vrf *vrf = NULL; struct zebra_vrf *zvrf = NULL; char *rmap_name; char afi_ip = 0; char afi_ipv6 = 0; struct route_map *old = NULL; RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { zvrf = vrf->info; if (!zvrf) continue; for (i = 0; i <= ZEBRA_ROUTE_MAX; i++) { rmap_name = NHT_RM_NAME(zvrf, AFI_IP, i); if (rmap_name && (strcmp(rmap_name, rmap) == 0)) { if (IS_ZEBRA_DEBUG_EVENT) zlog_debug( "%s : AFI_IP rmap %s, route type %s", __func__, rmap, zebra_route_string(i)); old = NHT_RM_MAP(zvrf, AFI_IP, i); NHT_RM_MAP(zvrf, AFI_IP, i) = route_map_lookup_by_name(rmap_name); if (!old) route_map_counter_increment( NHT_RM_MAP(zvrf, AFI_IP, i)); /* There is single rib table for all protocols */ if (afi_ip == 0) { table = zvrf->table[AFI_IP] [SAFI_UNICAST]; if (table) { afi_ip = 1; zebra_evaluate_rnh( zvrf, AFI_IP, 1, NULL, SAFI_UNICAST); } } } rmap_name = NHT_RM_NAME(zvrf, AFI_IP6, i); if (rmap_name && (strcmp(rmap_name, rmap) == 0)) { if (IS_ZEBRA_DEBUG_EVENT) zlog_debug( "%s : AFI_IP6 rmap %s, route type %s", __func__, rmap, zebra_route_string(i)); old = NHT_RM_MAP(zvrf, AFI_IP6, i); NHT_RM_MAP(zvrf, AFI_IP6, i) = route_map_lookup_by_name(rmap_name); if (!old) route_map_counter_increment( NHT_RM_MAP(zvrf, AFI_IP6, i)); /* There is single rib table for all protocols */ if (afi_ipv6 == 0) { table = zvrf->table[AFI_IP6] [SAFI_UNICAST]; if (table) { afi_ipv6 = 1; zebra_evaluate_rnh( zvrf, AFI_IP6, 1, NULL, SAFI_UNICAST); } } } } } } static void zebra_route_map_process_update_cb(char *rmap_name) { if (IS_ZEBRA_DEBUG_EVENT) zlog_debug("Event handler for route-map: %s", rmap_name); zebra_import_table_rm_update(rmap_name); zebra_rib_table_rm_update(rmap_name); zebra_nht_rm_update(rmap_name); } static void zebra_route_map_update_timer(struct event *thread) { if (IS_ZEBRA_DEBUG_EVENT) zlog_debug("Event driven route-map update triggered"); if (IS_ZEBRA_DEBUG_RIB_DETAILED) zlog_debug( "%u: Routemap update-timer fired, scheduling RIB processing", VRF_DEFAULT); route_map_walk_update_list(zebra_route_map_process_update_cb); /* * This code needs to be updated to be: * 1) VRF Aware * 2) Route-map aware */ } void zebra_route_map_set_delay_timer(uint32_t value) { zebra_rmap_update_timer = value; if (!value && zebra_t_rmap_update) { /* Event driven route map updates is being disabled */ /* But there's a pending timer. Fire it off now */ EVENT_OFF(zebra_t_rmap_update); zebra_route_map_update_timer(NULL); } } void zebra_routemap_finish(void) { /* Set zebra_rmap_update_timer to 0 so that it wont schedule again */ zebra_rmap_update_timer = 0; /* Thread off if any scheduled already */ EVENT_OFF(zebra_t_rmap_update); route_map_finish(); } route_map_result_t zebra_route_map_check(afi_t family, struct route_entry *re, const struct prefix *p, struct nexthop *nexthop, struct zebra_vrf *zvrf) { struct route_map *rmap = NULL; char *rm_name; route_map_result_t ret = RMAP_PERMITMATCH; struct zebra_rmap_obj rm_obj; rm_obj.nexthop = nexthop; rm_obj.re = re; if (re->type >= 0 && re->type < ZEBRA_ROUTE_MAX) { rm_name = PROTO_RM_NAME(zvrf, family, re->type); rmap = PROTO_RM_MAP(zvrf, family, re->type); if (rm_name && !rmap) return RMAP_DENYMATCH; } if (!rmap) { rm_name = PROTO_RM_NAME(zvrf, family, ZEBRA_ROUTE_MAX); rmap = PROTO_RM_MAP(zvrf, family, ZEBRA_ROUTE_MAX); if (rm_name && !rmap) return RMAP_DENYMATCH; } if (rmap) { ret = route_map_apply(rmap, p, &rm_obj); } return (ret); } char *zebra_get_import_table_route_map(afi_t afi, uint32_t table) { return zebra_import_table_routemap[afi][table]; } void zebra_add_import_table_route_map(afi_t afi, const char *rmap_name, uint32_t table) { zebra_import_table_routemap[afi][table] = XSTRDUP(MTYPE_ROUTE_MAP_NAME, rmap_name); } void zebra_del_import_table_route_map(afi_t afi, uint32_t table) { XFREE(MTYPE_ROUTE_MAP_NAME, zebra_import_table_routemap[afi][table]); } route_map_result_t zebra_import_table_route_map_check(int family, struct route_entry *re, const struct prefix *p, struct nexthop *nexthop, const char *rmap_name) { struct route_map *rmap = NULL; route_map_result_t ret = RMAP_DENYMATCH; struct zebra_rmap_obj rm_obj; rm_obj.nexthop = nexthop; rm_obj.re = re; if (re->type >= 0 && re->type < ZEBRA_ROUTE_MAX) rmap = route_map_lookup_by_name(rmap_name); if (rmap) { ret = route_map_apply(rmap, p, &rm_obj); } return (ret); } route_map_result_t zebra_nht_route_map_check(afi_t afi, int client_proto, const struct prefix *p, struct zebra_vrf *zvrf, struct route_entry *re, struct nexthop *nexthop) { struct route_map *rmap = NULL; route_map_result_t ret = RMAP_PERMITMATCH; struct zebra_rmap_obj rm_obj; rm_obj.nexthop = nexthop; rm_obj.re = re; if (client_proto >= 0 && client_proto < ZEBRA_ROUTE_MAX) rmap = NHT_RM_MAP(zvrf, afi, client_proto); if (!rmap && NHT_RM_MAP(zvrf, afi, ZEBRA_ROUTE_MAX)) rmap = NHT_RM_MAP(zvrf, afi, ZEBRA_ROUTE_MAX); if (rmap) ret = route_map_apply(rmap, p, &rm_obj); return ret; } static void zebra_route_map_mark_update(const char *rmap_name) { /* rmap_update_timer of 0 means don't do route updates */ if (zebra_rmap_update_timer) EVENT_OFF(zebra_t_rmap_update); event_add_timer(zrouter.master, zebra_route_map_update_timer, NULL, zebra_rmap_update_timer, &zebra_t_rmap_update); } static void zebra_route_map_add(const char *rmap_name) { if (route_map_mark_updated(rmap_name) == 0) zebra_route_map_mark_update(rmap_name); route_map_notify_dependencies(rmap_name, RMAP_EVENT_MATCH_ADDED); } static void zebra_route_map_delete(const char *rmap_name) { if (route_map_mark_updated(rmap_name) == 0) zebra_route_map_mark_update(rmap_name); route_map_notify_dependencies(rmap_name, RMAP_EVENT_MATCH_DELETED); } static void zebra_route_map_event(const char *rmap_name) { if (route_map_mark_updated(rmap_name) == 0) zebra_route_map_mark_update(rmap_name); route_map_notify_dependencies(rmap_name, RMAP_EVENT_MATCH_ADDED); } void zebra_routemap_vrf_delete(struct zebra_vrf *zvrf) { afi_t afi; uint8_t type; for (afi = AFI_IP; afi < AFI_MAX; afi++) { for (type = 0; type <= ZEBRA_ROUTE_MAX; type++) { if (PROTO_RM_NAME(zvrf, afi, type)) XFREE(MTYPE_ROUTE_MAP_NAME, PROTO_RM_NAME(zvrf, afi, type)); if (NHT_RM_NAME(zvrf, afi, type)) XFREE(MTYPE_ROUTE_MAP_NAME, NHT_RM_NAME(zvrf, afi, type)); } } } void zebra_route_map_init(void) { install_element(VIEW_NODE, &show_ip_protocol_cmd); install_element(VIEW_NODE, &show_ipv6_protocol_cmd); install_element(VIEW_NODE, &show_ip_protocol_nht_cmd); install_element(VIEW_NODE, &show_ipv6_protocol_nht_cmd); route_map_init_new(true); route_map_add_hook(zebra_route_map_add); route_map_delete_hook(zebra_route_map_delete); route_map_event_hook(zebra_route_map_event); route_map_match_interface_hook(generic_match_add); route_map_no_match_interface_hook(generic_match_delete); route_map_match_ip_address_hook(generic_match_add); route_map_no_match_ip_address_hook(generic_match_delete); route_map_match_ip_address_prefix_list_hook(generic_match_add); route_map_no_match_ip_address_prefix_list_hook(generic_match_delete); route_map_match_ip_next_hop_hook(generic_match_add); route_map_no_match_ip_next_hop_hook(generic_match_delete); route_map_match_ip_next_hop_prefix_list_hook(generic_match_add); route_map_no_match_ip_next_hop_prefix_list_hook(generic_match_delete); route_map_match_ip_next_hop_type_hook(generic_match_add); route_map_no_match_ip_next_hop_type_hook(generic_match_delete); route_map_match_tag_hook(generic_match_add); route_map_no_match_tag_hook(generic_match_delete); route_map_match_ipv6_address_hook(generic_match_add); route_map_no_match_ipv6_address_hook(generic_match_delete); route_map_match_ipv6_address_prefix_list_hook(generic_match_add); route_map_no_match_ipv6_address_prefix_list_hook(generic_match_delete); route_map_match_ipv6_next_hop_type_hook(generic_match_add); route_map_no_match_ipv6_next_hop_type_hook(generic_match_delete); route_map_install_match(&route_match_tag_cmd); route_map_install_match(&route_match_interface_cmd); route_map_install_match(&route_match_ip_next_hop_cmd); route_map_install_match(&route_match_ip_next_hop_prefix_list_cmd); route_map_install_match(&route_match_ip_address_cmd); route_map_install_match(&route_match_ipv6_address_cmd); route_map_install_match(&route_match_ip_address_prefix_list_cmd); route_map_install_match(&route_match_ipv6_address_prefix_list_cmd); route_map_install_match(&route_match_ip_address_prefix_len_cmd); route_map_install_match(&route_match_ipv6_address_prefix_len_cmd); route_map_install_match(&route_match_ip_nexthop_prefix_len_cmd); route_map_install_match(&route_match_ip_next_hop_type_cmd); route_map_install_match(&route_match_ipv6_next_hop_type_cmd); route_map_install_match(&route_match_source_protocol_cmd); route_map_install_match(&route_match_source_instance_cmd); /* */ route_map_install_set(&route_set_src_cmd); }