/* Route map function. Copyright (C) 1998, 1999 Kunihiro Ishiguro This file is part of GNU Zebra. GNU Zebra is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GNU Zebra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Zebra; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include "linklist.h" #include "memory.h" #include "vector.h" #include "prefix.h" #include "routemap.h" #include "command.h" #include "vty.h" #include "log.h" /* Vector for route match rules. */ static vector route_match_vec; /* Vector for route set rules. */ static vector route_set_vec; /* Route map rule. This rule has both `match' rule and `set' rule. */ struct route_map_rule { /* Rule type. */ struct route_map_rule_cmd *cmd; /* For pretty printing. */ char *rule_str; /* Pre-compiled match rule. */ void *value; /* Linked list. */ struct route_map_rule *next; struct route_map_rule *prev; }; /* Making route map list. */ struct route_map_list { struct route_map *head; struct route_map *tail; void (*add_hook) (const char *); void (*delete_hook) (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 void route_map_rule_delete (struct route_map_rule_list *, struct route_map_rule *); static void route_map_index_delete (struct route_map_index *, int); /* 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); return new; } /* Add new name to route_map. */ static struct route_map * route_map_add (const char *name) { struct route_map *map; struct route_map_list *list; map = route_map_new (name); list = &route_map_master; map->next = NULL; map->prev = list->tail; if (list->tail) list->tail->next = map; else list->head = map; list->tail = map; /* Execute hook. */ if (route_map_master.add_hook) (*route_map_master.add_hook) (name); return map; } /* Route map delete from list. */ static void route_map_delete (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->prev) map->prev->next = map->next; else list->head = map->next; XFREE (MTYPE_ROUTE_MAP, map); /* Execute deletion hook. */ if (route_map_master.delete_hook) (*route_map_master.delete_hook) (name); if (name) XFREE (MTYPE_ROUTE_MAP_NAME, name); } /* Lookup route map by route map name string. */ struct route_map * route_map_lookup_by_name (const char *name) { struct route_map *map; for (map = route_map_master.head; map; map = map->next) if (strcmp (map->name, name) == 0) return map; return NULL; } /* Lookup route map. If there isn't route map create one and return it. */ static 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; } /* 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"; break; case RMAP_DENY: return "deny"; break; default: return ""; break; } } static int route_map_empty (struct route_map *map) { if (map->head == NULL && map->tail == NULL) return 1; else return 0; } /* show route-map */ static void vty_show_route_map_entry (struct vty *vty, struct route_map *map) { struct route_map_index *index; struct route_map_rule *rule; /* Print the name of the protocol */ if (zlog_default) vty_out (vty, "%s:%s", zlog_proto_names[zlog_default->protocol], VTY_NEWLINE); for (index = map->head; index; index = index->next) { vty_out (vty, "route-map %s, %s, sequence %d%s", map->name, route_map_type_str (index->type), index->pref, VTY_NEWLINE); /* Description */ if (index->description) vty_out (vty, " Description:%s %s%s", VTY_NEWLINE, index->description, VTY_NEWLINE); /* Match clauses */ vty_out (vty, " Match clauses:%s", VTY_NEWLINE); for (rule = index->match_list.head; rule; rule = rule->next) vty_out (vty, " %s %s%s", rule->cmd->str, rule->rule_str, VTY_NEWLINE); vty_out (vty, " Set clauses:%s", VTY_NEWLINE); for (rule = index->set_list.head; rule; rule = rule->next) vty_out (vty, " %s %s%s", rule->cmd->str, rule->rule_str, VTY_NEWLINE); /* Call clause */ vty_out (vty, " Call clause:%s", VTY_NEWLINE); if (index->nextrm) vty_out (vty, " Call %s%s", index->nextrm, VTY_NEWLINE); /* Exit Policy */ vty_out (vty, " Action:%s", VTY_NEWLINE); if (index->exitpolicy == RMAP_GOTO) vty_out (vty, " Goto %d%s", index->nextpref, VTY_NEWLINE); else if (index->exitpolicy == RMAP_NEXT) vty_out (vty, " Continue to next entry%s", VTY_NEWLINE); else if (index->exitpolicy == RMAP_EXIT) vty_out (vty, " Exit routemap%s", VTY_NEWLINE); } } static int vty_show_route_map (struct vty *vty, const char *name) { struct route_map *map; if (name) { map = route_map_lookup_by_name (name); if (map) { vty_show_route_map_entry (vty, map); return CMD_SUCCESS; } else { vty_out (vty, "%%route-map %s not found%s", name, VTY_NEWLINE); return CMD_WARNING; } } else { for (map = route_map_master.head; map; map = map->next) vty_show_route_map_entry (vty, map); } 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 */ return new; } /* Free route map index. */ static void route_map_index_delete (struct route_map_index *index, int notify) { struct route_map_rule *rule; /* Free route match. */ while ((rule = index->match_list.head) != NULL) 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 */ if (index->nextrm) XFREE (MTYPE_ROUTE_MAP_NAME, index->nextrm); /* Execute event hook. */ if (route_map_master.event_hook && notify) (*route_map_master.event_hook) (RMAP_EVENT_INDEX_DELETED, index->map->name); 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; } /* Execute event hook. */ if (route_map_master.event_hook) (*route_map_master.event_hook) (RMAP_EVENT_INDEX_ADDED, map->name); return index; } /* Get route map index. */ static 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 *cmd) { vector_set (route_match_vec, cmd); } /* Install rule command to the set list. */ void route_map_install_set (struct route_map_rule_cmd *cmd) { vector_set (route_set_vec, cmd); } /* Lookup rule command from match list. */ static struct route_map_rule_cmd * route_map_lookup_match (const char *name) { unsigned int i; struct route_map_rule_cmd *rule; for (i = 0; i < vector_active (route_match_vec); i++) if ((rule = vector_slot (route_match_vec, i)) != NULL) if (strcmp (rule->str, name) == 0) return rule; return NULL; } /* Lookup rule command from set list. */ static struct route_map_rule_cmd * route_map_lookup_set (const char *name) { unsigned int i; struct route_map_rule_cmd *rule; for (i = 0; i < vector_active (route_set_vec); i++) if ((rule = vector_slot (route_set_vec, i)) != NULL) if (strcmp (rule->str, name) == 0) return rule; 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); if (rule->rule_str) 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; } /* Add match statement to route map. */ int route_map_add_match (struct route_map_index *index, const char *match_name, const char *match_arg) { struct route_map_rule *rule; struct route_map_rule *next; struct route_map_rule_cmd *cmd; void *compile; int replaced = 0; /* 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; /* If argument is completely same ignore it. */ for (rule = index->match_list.head; rule; rule = next) { next = rule->next; if (rule->cmd == cmd) { route_map_rule_delete (&index->match_list, rule); replaced = 1; } } /* 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); /* 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); return 0; } /* Delete specified route match rule. */ int route_map_delete_match (struct route_map_index *index, const char *match_name, const char *match_arg) { struct route_map_rule *rule; struct route_map_rule_cmd *cmd; cmd = route_map_lookup_match (match_name); if (cmd == NULL) return 1; for (rule = index->match_list.head; rule; rule = rule->next) if (rule->cmd == cmd && (rulecmp (rule->rule_str, match_arg) == 0 || match_arg == NULL)) { 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); return 0; } /* Can't find matched rule. */ return 1; } /* Add route-map set statement to the route map. */ int 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; struct route_map_rule_cmd *cmd; void *compile; int replaced = 0; 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); replaced = 1; } } /* 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) (replaced ? RMAP_EVENT_SET_REPLACED: RMAP_EVENT_SET_ADDED, index->map->name); return 0; } /* Delete route map set rule. */ int route_map_delete_set (struct route_map_index *index, const char *set_name, const char *set_arg) { struct route_map_rule *rule; struct route_map_rule_cmd *cmd; cmd = route_map_lookup_set (set_name); if (cmd == NULL) return 1; 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) (RMAP_EVENT_SET_DELETED, index->map->name); return 0; } /* Can't find matched rule. */ return 1; } /* 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 | permit action | cont | ------------------+--------------- | deny deny | cont | 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 statments 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 */ static route_map_result_t route_map_apply_match (struct route_map_rule_list *match_list, struct prefix *prefix, route_map_object_t type, void *object) { route_map_result_t ret = RMAP_NOMATCH; struct route_map_rule *match; /* 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 in turn, If any do not return RMAP_MATCH, return, otherwise continue on to next match statement. All match statements must match for end-result to be a match. */ ret = (*match->cmd->func_apply) (match->value, prefix, type, object); if (ret != RMAP_MATCH) return ret; } } return ret; } /* Apply route map to the object. */ route_map_result_t route_map_apply (struct route_map *map, struct prefix *prefix, route_map_object_t type, void *object) { static int recursion = 0; int ret = 0; struct route_map_index *index; struct route_map_rule *set; if (recursion > RMAP_RECURSION_LIMIT) { zlog (NULL, LOG_WARNING, "route-map recursion limit (%d) reached, discarding route", RMAP_RECURSION_LIMIT); recursion = 0; return RMAP_DENYMATCH; } if (map == NULL) return RMAP_DENYMATCH; for (index = map->head; index; index = index->next) { /* Apply this index. */ ret = route_map_apply_match (&index->match_list, prefix, type, object); /* Now we apply the matrix from above */ if (ret == RMAP_NOMATCH) /* 'cont' from matrix - continue to next route-map sequence */ continue; else if (ret == RMAP_MATCH) { if (index->type == RMAP_PERMIT) /* 'action' */ { /* permit+match must execute sets */ for (set = index->set_list.head; set; set = set->next) ret = (*set->cmd->func_apply) (set->value, prefix, type, 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 (nextrm, prefix, type, object); recursion--; } /* If nextrm returned 'deny', finish. */ if (ret == RMAP_DENYMATCH) return ret; } switch (index->exitpolicy) { case RMAP_EXIT: return ret; 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! */ return ret; } } } } else if (index->type == RMAP_DENY) /* 'deny' */ { return RMAP_DENYMATCH; } } } /* Finally route-map does not match at all. */ return RMAP_DENYMATCH; } 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) (route_map_event_t, const char *)) { route_map_master.event_hook = func; } void route_map_init (void) { /* Make vector for match and set. */ route_match_vec = vector_init (1); route_set_vec = vector_init (1); } void route_map_finish (void) { vector_free (route_match_vec); route_match_vec = NULL; vector_free (route_set_vec); route_set_vec = NULL; } /* VTY related functions. */ DEFUN (route_map, route_map_cmd, "route-map WORD (deny|permit) <1-65535>", "Create route-map or enter route-map command mode\n" "Route map tag\n" "Route map denies set operations\n" "Route map permits set operations\n" "Sequence to insert to/delete from existing route-map entry\n") { int permit; unsigned long pref; struct route_map *map; struct route_map_index *index; char *endptr = NULL; /* Permit check. */ if (strncmp (argv[1], "permit", strlen (argv[1])) == 0) permit = RMAP_PERMIT; else if (strncmp (argv[1], "deny", strlen (argv[1])) == 0) permit = RMAP_DENY; else { vty_out (vty, "the third field must be [permit|deny]%s", VTY_NEWLINE); return CMD_WARNING; } /* Preference check. */ pref = strtoul (argv[2], &endptr, 10); if (pref == ULONG_MAX || *endptr != '\0') { vty_out (vty, "the fourth field must be positive integer%s", VTY_NEWLINE); return CMD_WARNING; } if (pref == 0 || pref > 65535) { vty_out (vty, "the fourth field must be <1-65535>%s", VTY_NEWLINE); return CMD_WARNING; } /* Get route map. */ map = route_map_get (argv[0]); index = route_map_index_get (map, permit, pref); vty->index = index; vty->node = RMAP_NODE; return CMD_SUCCESS; } DEFUN (no_route_map_all, no_route_map_all_cmd, "no route-map WORD", NO_STR "Create route-map or enter route-map command mode\n" "Route map tag\n") { struct route_map *map; map = route_map_lookup_by_name (argv[0]); if (map == NULL) { vty_out (vty, "%% Could not find route-map %s%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } route_map_delete (map); return CMD_SUCCESS; } DEFUN (no_route_map, no_route_map_cmd, "no route-map WORD (deny|permit) <1-65535>", NO_STR "Create route-map or enter route-map command mode\n" "Route map tag\n" "Route map denies set operations\n" "Route map permits set operations\n" "Sequence to insert to/delete from existing route-map entry\n") { int permit; unsigned long pref; struct route_map *map; struct route_map_index *index; char *endptr = NULL; /* Permit check. */ if (strncmp (argv[1], "permit", strlen (argv[1])) == 0) permit = RMAP_PERMIT; else if (strncmp (argv[1], "deny", strlen (argv[1])) == 0) permit = RMAP_DENY; else { vty_out (vty, "the third field must be [permit|deny]%s", VTY_NEWLINE); return CMD_WARNING; } /* Preference. */ pref = strtoul (argv[2], &endptr, 10); if (pref == ULONG_MAX || *endptr != '\0') { vty_out (vty, "the fourth field must be positive integer%s", VTY_NEWLINE); return CMD_WARNING; } if (pref == 0 || pref > 65535) { vty_out (vty, "the fourth field must be <1-65535>%s", VTY_NEWLINE); return CMD_WARNING; } /* Existence check. */ map = route_map_lookup_by_name (argv[0]); if (map == NULL) { vty_out (vty, "%% Could not find route-map %s%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } /* Lookup route map index. */ index = route_map_index_lookup (map, permit, pref); if (index == NULL) { vty_out (vty, "%% Could not find route-map entry %s %s%s", argv[0], argv[2], VTY_NEWLINE); return CMD_WARNING; } /* Delete index from route map. */ route_map_index_delete (index, 1); /* If this route rule is the last one, delete route map itself. */ if (route_map_empty (map)) route_map_delete (map); return CMD_SUCCESS; } DEFUN (rmap_onmatch_next, rmap_onmatch_next_cmd, "on-match next", "Exit policy on matches\n" "Next clause\n") { struct route_map_index *index; index = vty->index; if (index) index->exitpolicy = RMAP_NEXT; return CMD_SUCCESS; } DEFUN (no_rmap_onmatch_next, no_rmap_onmatch_next_cmd, "no on-match next", NO_STR "Exit policy on matches\n" "Next clause\n") { struct route_map_index *index; index = vty->index; if (index) index->exitpolicy = RMAP_EXIT; return CMD_SUCCESS; } DEFUN (rmap_onmatch_goto, rmap_onmatch_goto_cmd, "on-match goto <1-65535>", "Exit policy on matches\n" "Goto Clause number\n" "Number\n") { struct route_map_index *index = vty->index; int d = 0; if (index) { if (argc == 1 && argv[0]) VTY_GET_INTEGER_RANGE("route-map index", d, argv[0], 1, 65536); else d = index->pref + 1; if (d <= index->pref) { /* Can't allow you to do that, Dave */ vty_out (vty, "can't jump backwards in route-maps%s", VTY_NEWLINE); return CMD_WARNING; } else { index->exitpolicy = RMAP_GOTO; index->nextpref = d; } } return CMD_SUCCESS; } DEFUN (no_rmap_onmatch_goto, no_rmap_onmatch_goto_cmd, "no on-match goto", NO_STR "Exit policy on matches\n" "Goto Clause number\n") { struct route_map_index *index; index = vty->index; if (index) index->exitpolicy = RMAP_EXIT; return CMD_SUCCESS; } /* Cisco/GNU Zebra compatible ALIASes for on-match next */ ALIAS (rmap_onmatch_goto, rmap_continue_cmd, "continue", "Continue on a different entry within the route-map\n") ALIAS (no_rmap_onmatch_goto, no_rmap_continue_cmd, "no continue", NO_STR "Continue on a different entry within the route-map\n") /* GNU Zebra compatible */ ALIAS (rmap_onmatch_goto, rmap_continue_seq_cmd, "continue <1-65535>", "Continue on a different entry within the route-map\n" "Route-map entry sequence number\n") ALIAS (no_rmap_onmatch_goto, no_rmap_continue_seq, "no continue <1-65535>", NO_STR "Continue on a different entry within the route-map\n" "Route-map entry sequence number\n") DEFUN (rmap_show_name, rmap_show_name_cmd, "show route-map [WORD]", SHOW_STR "route-map information\n" "route-map name\n") { const char *name = NULL; if (argc) name = argv[0]; return vty_show_route_map (vty, name); } ALIAS (rmap_onmatch_goto, rmap_continue_index_cmd, "continue <1-65536>", "Exit policy on matches\n" "Goto Clause number\n") DEFUN (rmap_call, rmap_call_cmd, "call WORD", "Jump to another Route-Map after match+set\n" "Target route-map name\n") { struct route_map_index *index; index = vty->index; if (index) { if (index->nextrm) XFREE (MTYPE_ROUTE_MAP_NAME, index->nextrm); index->nextrm = XSTRDUP (MTYPE_ROUTE_MAP_NAME, argv[0]); } return CMD_SUCCESS; } DEFUN (no_rmap_call, no_rmap_call_cmd, "no call", NO_STR "Jump to another Route-Map after match+set\n") { struct route_map_index *index; index = vty->index; if (index->nextrm) { XFREE (MTYPE_ROUTE_MAP_NAME, index->nextrm); index->nextrm = NULL; } return CMD_SUCCESS; } DEFUN (rmap_description, rmap_description_cmd, "description .LINE", "Route-map comment\n" "Comment describing this route-map rule\n") { struct route_map_index *index; index = vty->index; if (index) { if (index->description) XFREE (MTYPE_TMP, index->description); index->description = argv_concat (argv, argc, 0); } return CMD_SUCCESS; } DEFUN (no_rmap_description, no_rmap_description_cmd, "no description", NO_STR "Route-map comment\n") { struct route_map_index *index; index = vty->index; if (index) { if (index->description) XFREE (MTYPE_TMP, index->description); index->description = NULL; } return CMD_SUCCESS; } /* Configuration write function. */ static int route_map_config_write (struct vty *vty) { struct route_map *map; struct route_map_index *index; struct route_map_rule *rule; int first = 1; int write = 0; for (map = route_map_master.head; map; map = map->next) for (index = map->head; index; index = index->next) { if (!first) vty_out (vty, "!%s", VTY_NEWLINE); else first = 0; vty_out (vty, "route-map %s %s %d%s", map->name, route_map_type_str (index->type), index->pref, VTY_NEWLINE); if (index->description) vty_out (vty, " description %s%s", index->description, VTY_NEWLINE); for (rule = index->match_list.head; rule; rule = rule->next) vty_out (vty, " match %s %s%s", rule->cmd->str, rule->rule_str ? rule->rule_str : "", VTY_NEWLINE); for (rule = index->set_list.head; rule; rule = rule->next) vty_out (vty, " set %s %s%s", rule->cmd->str, rule->rule_str ? rule->rule_str : "", VTY_NEWLINE); if (index->nextrm) vty_out (vty, " call %s%s", index->nextrm, VTY_NEWLINE); if (index->exitpolicy == RMAP_GOTO) vty_out (vty, " on-match goto %d%s", index->nextpref, VTY_NEWLINE); if (index->exitpolicy == RMAP_NEXT) vty_out (vty," on-match next%s", VTY_NEWLINE); write++; } return write; } /* Route map node structure. */ static struct cmd_node rmap_node = { RMAP_NODE, "%s(config-route-map)# ", 1 }; /* Initialization of route map vector. */ void route_map_init_vty (void) { /* Install route map top node. */ install_node (&rmap_node, route_map_config_write); /* Install route map commands. */ install_default (RMAP_NODE); install_element (CONFIG_NODE, &route_map_cmd); install_element (CONFIG_NODE, &no_route_map_cmd); install_element (CONFIG_NODE, &no_route_map_all_cmd); /* Install the on-match stuff */ install_element (RMAP_NODE, &route_map_cmd); install_element (RMAP_NODE, &rmap_onmatch_next_cmd); install_element (RMAP_NODE, &no_rmap_onmatch_next_cmd); install_element (RMAP_NODE, &rmap_onmatch_goto_cmd); install_element (RMAP_NODE, &no_rmap_onmatch_goto_cmd); /* Install the continue stuff (ALIAS of on-match). */ install_element (RMAP_NODE, &rmap_continue_cmd); install_element (RMAP_NODE, &no_rmap_continue_cmd); install_element (RMAP_NODE, &rmap_continue_index_cmd); /* Install the call stuff. */ install_element (RMAP_NODE, &rmap_call_cmd); install_element (RMAP_NODE, &no_rmap_call_cmd); /* Install description commands. */ install_element (RMAP_NODE, &rmap_description_cmd); install_element (RMAP_NODE, &no_rmap_description_cmd); /* Install show command */ install_element (ENABLE_NODE, &rmap_show_name_cmd); }