diff options
Diffstat (limited to 'lib/routemap.c')
-rw-r--r-- | lib/routemap.c | 531 |
1 files changed, 484 insertions, 47 deletions
diff --git a/lib/routemap.c b/lib/routemap.c index 7302e231..1a1d318c 100644 --- a/lib/routemap.c +++ b/lib/routemap.c @@ -28,6 +28,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "command.h" #include "vty.h" #include "log.h" +#include "hash.h" /* Vector for route match rules. */ static vector route_match_vec; @@ -60,15 +61,47 @@ struct route_map_list void (*add_hook) (const char *); void (*delete_hook) (const char *); - void (*event_hook) (route_map_event_t, const char *); + void (*event_hook) (route_map_event_t, const char *); }; /* Master list of route map. */ -static struct route_map_list route_map_master = { NULL, NULL, NULL, NULL }; +static struct route_map_list route_map_master = { NULL, NULL, NULL, NULL, NULL }; + +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_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 */ +}; -static void -route_map_rule_delete (struct route_map_rule_list *, - struct route_map_rule *); +/* Hashes maintaining dependency between various sublists used by route maps */ +struct hash *route_map_dep_hash[ROUTE_MAP_DEP_MAX]; + +static unsigned int route_map_dep_hash_make_key (void *p); +static int route_map_dep_hash_cmp (const void *p1, const void *p2); +static void route_map_init_dep_hashes (void); +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 *); +static int rmap_debug = 0; static void route_map_index_delete (struct route_map_index *, int); @@ -105,45 +138,72 @@ route_map_add (const char *name) /* Execute hook. */ if (route_map_master.add_hook) - (*route_map_master.add_hook) (name); - + { + (*route_map_master.add_hook) (name); + route_map_notify_dependencies(name, RMAP_EVENT_CALL_ADDED); + } return map; } -/* Route map delete from list. */ +/* 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_delete (struct route_map *map) +route_map_free_map (struct route_map *map) { struct route_map_list *list; struct route_map_index *index; - char *name; - + while ((index = map->head) != NULL) route_map_index_delete (index, 0); - name = map->name; - list = &route_map_master; - if (map->next) - map->next->prev = map->prev; - else - list->tail = map->prev; + if (map != NULL) + { + 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; + if (map->prev) + map->prev->next = map->next; + else + list->head = map->next; - XFREE (MTYPE_ROUTE_MAP, map); + XFREE (MTYPE_ROUTE_MAP_NAME, map->name); + XFREE (MTYPE_ROUTE_MAP, map); + } +} +/* Route map delete from list. */ +static 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 = 1; /* Execute deletion hook. */ if (route_map_master.delete_hook) - (*route_map_master.delete_hook) (name); - - if (name) - XFREE (MTYPE_ROUTE_MAP_NAME, name); + { + (*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. */ @@ -152,12 +212,50 @@ route_map_lookup_by_name (const char *name) { struct route_map *map; + if (!name) + return NULL; + for (map = route_map_master.head; map; map = map->next) - if (strcmp (map->name, name) == 0) + if ((strcmp (map->name, name) == 0) && (!map->deleted)) return map; return NULL; } +int +route_map_mark_updated (const char *name, int del_later) +{ + struct route_map *map; + int ret = -1; + + /* We need to do this walk manually instead of calling lookup_by_name() + * because the lookup function doesn't return route maps marked as + * deleted. + */ + for (map = route_map_master.head; map; map = map->next) + if (strcmp (map->name, name) == 0) + { + map->to_be_processed = 1; + ret = 0; + } + + return(ret); +} + +int +route_map_clear_updated (struct route_map *map) +{ + int ret = -1; + + if (map) + { + map->to_be_processed = 0; + if (map->deleted) + route_map_free_map(map); + } + + return (ret); +} + /* Lookup route map. If there isn't route map create one and return it. */ static struct route_map * @@ -168,9 +266,31 @@ route_map_get (const char *name) map = route_map_lookup_by_name (name); if (map == NULL) map = route_map_add (name); + return map; } +void +route_map_walk_update_list (void *arg, + int (*route_map_update_fn) (void *arg, 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(arg, 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) @@ -271,7 +391,8 @@ vty_show_route_map (struct vty *vty, const char *name) else { for (map = route_map_master.head; map; map = map->next) - vty_show_route_map_entry (vty, map); + if (!map->deleted) + vty_show_route_map_entry (vty, map); } return CMD_SUCCESS; } @@ -320,9 +441,11 @@ route_map_index_delete (struct route_map_index *index, int notify) /* Execute event hook. */ if (route_map_master.event_hook && notify) - (*route_map_master.event_hook) (RMAP_EVENT_INDEX_DELETED, - index->map->name); - + { + (*route_map_master.event_hook) (RMAP_EVENT_INDEX_DELETED, + index->map->name); + route_map_notify_dependencies(index->map->name, RMAP_EVENT_CALL_ADDED); + } XFREE (MTYPE_ROUTE_MAP_INDEX, index); } @@ -386,9 +509,11 @@ route_map_index_add (struct route_map *map, enum route_map_type type, /* Execute event hook. */ if (route_map_master.event_hook) - (*route_map_master.event_hook) (RMAP_EVENT_INDEX_ADDED, - map->name); - + { + (*route_map_master.event_hook) (RMAP_EVENT_INDEX_ADDED, + map->name); + route_map_notify_dependencies (map->name, RMAP_EVENT_CALL_ADDED); + } return index; } @@ -521,6 +646,28 @@ rulecmp (const char *dst, const char *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; + 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); +} + /* Add match statement to route map. */ int route_map_add_match (struct route_map_index *index, const char *match_name, @@ -572,10 +719,13 @@ route_map_add_match (struct route_map_index *index, const char *match_name, /* Execute event hook. */ if (route_map_master.event_hook) - (*route_map_master.event_hook) (replaced ? - RMAP_EVENT_MATCH_REPLACED: - RMAP_EVENT_MATCH_ADDED, - index->map->name); + { + (*route_map_master.event_hook) (replaced ? + RMAP_EVENT_MATCH_REPLACED: + RMAP_EVENT_MATCH_ADDED, + index->map->name); + route_map_notify_dependencies(index->map->name, RMAP_EVENT_CALL_ADDED); + } return 0; } @@ -599,8 +749,11 @@ route_map_delete_match (struct route_map_index *index, const char *match_name, route_map_rule_delete (&index->match_list, rule); /* Execute event hook. */ if (route_map_master.event_hook) - (*route_map_master.event_hook) (RMAP_EVENT_MATCH_DELETED, - index->map->name); + { + (*route_map_master.event_hook) (RMAP_EVENT_MATCH_DELETED, + index->map->name); + route_map_notify_dependencies(index->map->name, RMAP_EVENT_CALL_ADDED); + } return 0; } /* Can't find matched rule. */ @@ -659,10 +812,13 @@ route_map_add_set (struct route_map_index *index, const char *set_name, /* Execute event hook. */ if (route_map_master.event_hook) - (*route_map_master.event_hook) (replaced ? - RMAP_EVENT_SET_REPLACED: - RMAP_EVENT_SET_ADDED, - index->map->name); + { + (*route_map_master.event_hook) (replaced ? + RMAP_EVENT_SET_REPLACED: + RMAP_EVENT_SET_ADDED, + index->map->name); + route_map_notify_dependencies(index->map->name, RMAP_EVENT_CALL_ADDED); + } return 0; } @@ -685,8 +841,11 @@ route_map_delete_set (struct route_map_index *index, const char *set_name, route_map_rule_delete (&index->set_list, rule); /* Execute event hook. */ if (route_map_master.event_hook) - (*route_map_master.event_hook) (RMAP_EVENT_SET_DELETED, - index->map->name); + { + (*route_map_master.event_hook) (RMAP_EVENT_SET_DELETED, + index->map->name); + route_map_notify_dependencies(index->map->name, RMAP_EVENT_CALL_ADDED); + } return 0; } /* Can't find matched rule. */ @@ -902,6 +1061,259 @@ route_map_finish (void) route_map_delete (route_map_master.head); } +/* Routines for route map dependency lists and dependency processing */ +static int +route_map_rmap_hash_cmp (const void *p1, const void *p2) +{ + return (strcmp((char *)p1, (char *)p2) == 0); +} + +static int +route_map_dep_hash_cmp (const void *p1, const void *p2) +{ + + return (strcmp (((struct route_map_dep *)p1)->dep_name, (char *)p2) == 0); +} + +static void +route_map_clear_reference(struct hash_backet *backet, void *arg) +{ + struct route_map_dep *dep = (struct route_map_dep *)backet->data; + char *rmap_name; + + if (dep && arg) + { + rmap_name = (char *)hash_release(dep->dep_rmap_hash, (void *)arg); + if (rmap_name) + { + XFREE(MTYPE_ROUTE_MAP_NAME, rmap_name); + } + 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; + + for (i = 1; i < ROUTE_MAP_DEP_MAX; i++) + { + hash_iterate(route_map_dep_hash[i], route_map_clear_reference, + (void *)rmap_name); + } +} + +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(route_map_dep_hash_make_key, + route_map_rmap_hash_cmp); + dep_entry->this_hash = NULL; + + return((void *)dep_entry); +} + +static void * +route_map_name_hash_alloc(void *p) +{ + return((void *)XSTRDUP(MTYPE_ROUTE_MAP_NAME, (char *)p)); +} + +static unsigned int +route_map_dep_hash_make_key (void *p) +{ + return (string_hash_make((char *)p)); +} + +static void +route_map_print_dependency (struct hash_backet *backet, void *data) +{ + char *rmap_name = (char *)backet->data; + char *dep_name = (char *)data; + + if (rmap_name) + zlog_debug("%s: Dependency for %s: %s", __FUNCTION__, 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 *ret_map_name; + char *dname, *rname; + int ret = 0; + + 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_CALL_ADDED: + case RMAP_EVENT_FILTER_ADDED: + if (rmap_debug) + zlog_debug("%s: Adding dependency for %s in %s", __FUNCTION__, + 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; + + hash_get(dep->dep_rmap_hash, rname, route_map_name_hash_alloc); + break; + case RMAP_EVENT_PLIST_DELETED: + case RMAP_EVENT_CLIST_DELETED: + case RMAP_EVENT_ECLIST_DELETED: + case RMAP_EVENT_ASLIST_DELETED: + case RMAP_EVENT_CALL_DELETED: + case RMAP_EVENT_FILTER_DELETED: + if (rmap_debug) + zlog_debug("%s: Deleting dependency for %s in %s", __FUNCTION__, + dep_name, rmap_name); + dep = (struct route_map_dep *) hash_get (dephash, dname, NULL); + if (!dep) { + goto out; + } + ret_map_name = (char *)hash_release(dep->dep_rmap_hash, rname); + if (ret_map_name) + XFREE(MTYPE_ROUTE_MAP_NAME, ret_map_name); + + if (!dep->dep_rmap_hash->count) + { + dep = hash_release(dephash, (void *)dep_name); + hash_free(dep->dep_rmap_hash); + XFREE(MTYPE_ROUTE_MAP_NAME, dep->dep_name); + XFREE(MTYPE_ROUTE_MAP_DEP, dep); + dep = NULL; + } + break; + default: + break; + } + + if (dep) + { + if (rmap_debug) + hash_iterate (dep->dep_rmap_hash, route_map_print_dependency, (void *)dep_name); + } + + 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_CALL_ADDED: + case RMAP_EVENT_CALL_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; + default: + upd8_hash = NULL; + break; + } + return (upd8_hash); +} + +static void +route_map_process_dependency (struct hash_backet *backet, void *data) +{ + char *rmap_name; + route_map_event_t type = (route_map_event_t )data; + + rmap_name = (char *)backet->data; + + if (rmap_name) + { + if (rmap_debug) + zlog_debug("%s: Notifying %s of dependency", __FUNCTION__, + rmap_name); + if (route_map_master.event_hook) + (*route_map_master.event_hook) (type, 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); +} + +void +route_map_notify_dependencies (const char *affected_name, route_map_event_t event) +{ + struct route_map_dep *dep; + struct hash *upd8_hash; + + if (!affected_name) + return; + + if ((upd8_hash = route_map_get_dep_hash(event)) == NULL) + 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; + + hash_iterate (dep->dep_rmap_hash, route_map_process_dependency, (void *)event); + } +} + /* VTY related functions. */ DEFUN (route_map, route_map_cmd, @@ -1183,9 +1595,19 @@ DEFUN (rmap_call, if (index) { if (index->nextrm) - XFREE (MTYPE_ROUTE_MAP_NAME, index->nextrm); + { + route_map_upd8_dependency (RMAP_EVENT_CALL_DELETED, + index->nextrm, + index->map->name); + XFREE (MTYPE_ROUTE_MAP_NAME, index->nextrm); + } index->nextrm = XSTRDUP (MTYPE_ROUTE_MAP_NAME, argv[0]); } + + /* Execute event hook. */ + route_map_upd8_dependency (RMAP_EVENT_CALL_ADDED, + index->nextrm, + index->map->name); return CMD_SUCCESS; } @@ -1201,6 +1623,9 @@ DEFUN (no_rmap_call, if (index->nextrm) { + route_map_upd8_dependency (RMAP_EVENT_CALL_DELETED, + index->nextrm, + index->map->name); XFREE (MTYPE_ROUTE_MAP_NAME, index->nextrm); index->nextrm = NULL; } @@ -1299,10 +1724,22 @@ static struct cmd_node rmap_node = 1 }; +static void +route_map_init_dep_hashes (void) +{ + int i; + + for (i = 1; i < ROUTE_MAP_DEP_MAX; i++) + route_map_dep_hash[i] = hash_create(route_map_dep_hash_make_key, + route_map_dep_hash_cmp); +} + /* Initialization of route map vector. */ void route_map_init_vty (void) { + route_map_init_dep_hashes(); + /* Install route map top node. */ install_node (&rmap_node, route_map_config_write); |