1078 lines
25 KiB
C
Executable File
1078 lines
25 KiB
C
Executable File
/* 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 <zebra.h>
|
||
|
||
#include "linklist.h"
|
||
#include "memory.h"
|
||
#include "vector.h"
|
||
#include "prefix.h"
|
||
#include "routemap.h"
|
||
#include "command.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) (char *);
|
||
void (*delete_hook) (char *);
|
||
void (*event_hook) (route_map_event_t, 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 (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 (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 (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. */
|
||
struct route_map *
|
||
route_map_get (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 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;
|
||
}
|
||
}
|
||
|
||
int
|
||
route_map_empty (struct route_map *map)
|
||
{
|
||
if (map->head == NULL && map->tail == NULL)
|
||
return 1;
|
||
else
|
||
return 0;
|
||
}
|
||
|
||
/* For debug. */
|
||
void
|
||
route_map_print ()
|
||
{
|
||
struct route_map *map;
|
||
struct route_map_index *index;
|
||
struct route_map_rule *rule;
|
||
|
||
for (map = route_map_master.head; map; map = map->next)
|
||
for (index = map->head; index; index = index->next)
|
||
{
|
||
printf ("route-map %s %s %d\n",
|
||
map->name,
|
||
route_map_type_str (index->type),
|
||
index->pref);
|
||
for (rule = index->match_list.head; rule; rule = rule->next)
|
||
printf (" match %s %s\n", rule->cmd->str, rule->rule_str);
|
||
for (rule = index->set_list.head; rule; rule = rule->next)
|
||
printf (" set %s %s\n", rule->cmd->str, rule->rule_str);
|
||
if (index->exitpolicy == RMAP_GOTO)
|
||
printf (" on-match goto %d\n", index->nextpref);
|
||
if (index->exitpolicy == RMAP_NEXT)
|
||
printf (" on-match next\n");
|
||
}
|
||
}
|
||
|
||
/* New route map allocation. Please note route map's name must be
|
||
specified. */
|
||
struct route_map_index *
|
||
route_map_index_new ()
|
||
{
|
||
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;
|
||
|
||
/* 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. */
|
||
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. */
|
||
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. */
|
||
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 */
|
||
struct route_map_rule *
|
||
route_map_rule_new ()
|
||
{
|
||
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. */
|
||
struct route_map_rule_cmd *
|
||
route_map_lookup_match (char *name)
|
||
{
|
||
int i;
|
||
struct route_map_rule_cmd *rule;
|
||
|
||
for (i = 0; i < vector_max (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. */
|
||
struct route_map_rule_cmd *
|
||
route_map_lookup_set (char *name)
|
||
{
|
||
int i;
|
||
struct route_map_rule_cmd *rule;
|
||
|
||
for (i = 0; i < vector_max (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. */
|
||
int
|
||
rulecmp (char *dst, 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, char *match_name,
|
||
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, char *match_name,
|
||
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, char *set_name,
|
||
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, char *set_name,
|
||
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 a | c
|
||
** |
|
||
** ------------------+---------------
|
||
** |
|
||
** deny b | d
|
||
** |
|
||
**
|
||
** a) Apply Set statements, accept route
|
||
** 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
|
||
** b) Finish route-map processing, and deny route
|
||
** c) & d) 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 "NEXT" and "GOTO"
|
||
** 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
|
||
*/
|
||
route_map_result_t
|
||
route_map_apply_index (struct route_map_index *index, struct prefix *prefix,
|
||
route_map_object_t type, void *object)
|
||
{
|
||
int ret;
|
||
struct route_map_rule *match;
|
||
struct route_map_rule *set;
|
||
|
||
/* Check all match rule and if there is no match rule return 0. */
|
||
for (match = index->match_list.head; match; match = match->next)
|
||
{
|
||
/* Try each match statement in turn. If any return something
|
||
other than RM_MATCH then we don't need to check anymore and can
|
||
return */
|
||
ret = (*match->cmd->func_apply)(match->value, prefix, type, object);
|
||
if (ret != RMAP_MATCH)
|
||
return ret;
|
||
}
|
||
|
||
/* We get here if all match statements matched From the matrix
|
||
above, if this is PERMIT we go on and apply the SET functions. If
|
||
we're deny, we return indicating we matched a deny */
|
||
|
||
/* Apply set statement to the object. */
|
||
if (index->type == RMAP_PERMIT)
|
||
{
|
||
for (set = index->set_list.head; set; set = set->next)
|
||
{
|
||
ret = (*set->cmd->func_apply)(set->value, prefix, type, object);
|
||
}
|
||
return RMAP_MATCH;
|
||
}
|
||
else
|
||
{
|
||
return RMAP_DENYMATCH;
|
||
}
|
||
/* Should not get here! */
|
||
return RMAP_MATCH;
|
||
}
|
||
|
||
/* 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)
|
||
{
|
||
int ret = 0;
|
||
struct route_map_index *index;
|
||
|
||
if (map == NULL)
|
||
return RMAP_DENYMATCH;
|
||
|
||
for (index = map->head; index; index = index->next)
|
||
{
|
||
/* Apply this index. End here if we get a RM_NOMATCH */
|
||
ret = route_map_apply_index (index, prefix, type, object);
|
||
|
||
if (ret != RMAP_NOMATCH)
|
||
{
|
||
/* We now have to handle the NEXT and GOTO clauses */
|
||
if(index->exitpolicy == RMAP_EXIT)
|
||
return ret;
|
||
if(index->exitpolicy == RMAP_GOTO)
|
||
{
|
||
/* Find the next clause to jump to */
|
||
struct route_map_index *next;
|
||
|
||
next = index->next;
|
||
while (next && next->pref < index->nextpref)
|
||
{
|
||
index = next;
|
||
next = next->next;
|
||
}
|
||
if (next == NULL)
|
||
{
|
||
/* No clauses match! */
|
||
return ret;
|
||
}
|
||
}
|
||
/* Otherwise, we fall through as it was a NEXT */
|
||
}
|
||
}
|
||
/* Finally route-map does not match at all. */
|
||
return RMAP_DENYMATCH;
|
||
}
|
||
|
||
void
|
||
route_map_add_hook (void (*func) (char *))
|
||
{
|
||
route_map_master.add_hook = func;
|
||
}
|
||
|
||
void
|
||
route_map_delete_hook (void (*func) (char *))
|
||
{
|
||
route_map_master.delete_hook = func;
|
||
}
|
||
|
||
void
|
||
route_map_event_hook (void (*func) (route_map_event_t, char *))
|
||
{
|
||
route_map_master.event_hook = func;
|
||
}
|
||
|
||
void
|
||
route_map_init ()
|
||
{
|
||
/* Make vector for match and set. */
|
||
route_match_vec = vector_init (1);
|
||
route_set_vec = vector_init (1);
|
||
}
|
||
|
||
/* 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;
|
||
int d = 0;
|
||
|
||
if (argv[0])
|
||
d = atoi(argv[0]);
|
||
|
||
index = vty->index;
|
||
if (index)
|
||
{
|
||
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"
|
||
"Next clause\n")
|
||
{
|
||
struct route_map_index *index;
|
||
|
||
index = vty->index;
|
||
|
||
if (index)
|
||
index->exitpolicy = RMAP_EXIT;
|
||
|
||
return CMD_SUCCESS;
|
||
}
|
||
|
||
/* Configuration write function. */
|
||
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);
|
||
|
||
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->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. */
|
||
struct cmd_node rmap_node =
|
||
{
|
||
RMAP_NODE,
|
||
"%s(config-route-map)# ",
|
||
1
|
||
};
|
||
|
||
/* Initialization of route map vector. */
|
||
void
|
||
route_map_init_vty ()
|
||
{
|
||
/* 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);
|
||
}
|