mirror of
				https://git.proxmox.com/git/mirror_frr
				synced 2025-11-04 08:04:27 +00:00 
			
		
		
		
	* lib/routemap.c: (route_map_apply_index) renamed to
          route_map_apply_match.
          (route_map_apply_match) Take rule list as argument rather than
          index. concentrate solely on match logic.
          (route_map_apply) Properly implement the route map statement
          logic, according to the table, especially the on-match exit
          policy.
		
	
			
		
			
				
	
	
		
			1093 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1093 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* 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    action  |     cont
 | 
						||
                      |
 | 
						||
    ------------------+---------------
 | 
						||
                      |
 | 
						||
    deny      deny    |     cont
 | 
						||
                      |
 | 
						||
  
 | 
						||
   action) 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
 | 
						||
   deny)   If NEXT is specified, goto NEXT statement
 | 
						||
      If nothing is specified, finally will be 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 "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_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)
 | 
						||
{
 | 
						||
  int ret = 0;
 | 
						||
  struct route_map_index *index;
 | 
						||
  struct route_map_rule *set;
 | 
						||
 | 
						||
  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);
 | 
						||
              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;
 | 
						||
 | 
						||
                      while (next && next->pref < index->nextpref)
 | 
						||
                        {
 | 
						||
                          index = next;
 | 
						||
                          next = next->next;
 | 
						||
                        }
 | 
						||
                      if (next == NULL)
 | 
						||
                        {
 | 
						||
                          /* No clauses match! */
 | 
						||
                          return ret;
 | 
						||
                        }
 | 
						||
                    }
 | 
						||
                }
 | 
						||
            }
 | 
						||
          else if (index->type == RMAP_DENY)
 | 
						||
            /* 'deny' */
 | 
						||
            {
 | 
						||
              if (index->exitpolicy == RMAP_NEXT)
 | 
						||
                continue;
 | 
						||
              else
 | 
						||
                return RMAP_DENYMATCH;
 | 
						||
            }
 | 
						||
        }
 | 
						||
    }
 | 
						||
  /* 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);
 | 
						||
}
 |