1/* Route map function.
2   Copyright (C) 1998, 1999 Kunihiro Ishiguro
3
4This file is part of GNU Zebra.
5
6GNU Zebra is free software; you can redistribute it and/or modify it
7under the terms of the GNU General Public License as published by the
8Free Software Foundation; either version 2, or (at your option) any
9later version.
10
11GNU Zebra is distributed in the hope that it will be useful, but
12WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GNU Zebra; see the file COPYING.  If not, write to the Free
18Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
1902111-1307, USA.  */
20
21#ifdef FOX_LIST_SUPPORT
22
23#include <zebra.h>
24
25#include "linklist.h"
26#include "memory.h"
27#include "vector.h"
28#include "prefix.h"
29#include "routemap.h"
30#include "command.h"
31
32/* Vector for route match rules. */
33static vector route_match_vec;
34
35/* Vector for route set rules. */
36static vector route_set_vec;
37
38/* Route map rule. This rule has both `match' rule and `set' rule. */
39struct route_map_rule
40{
41  /* Rule type. */
42  struct route_map_rule_cmd *cmd;
43
44  /* For pretty printing. */
45  char *rule_str;
46
47  /* Pre-compiled match rule. */
48  void *value;
49
50  /* Linked list. */
51  struct route_map_rule *next;
52  struct route_map_rule *prev;
53};
54
55/* Making route map list. */
56struct route_map_list
57{
58  struct route_map *head;
59  struct route_map *tail;
60
61  void (*add_hook) (char *);
62  void (*delete_hook) (char *);
63  void (*event_hook) (route_map_event_t, char *);
64};
65
66/* Master list of route map. */
67static struct route_map_list route_map_master = { NULL, NULL, NULL, NULL };
68
69static void
70route_map_rule_delete (struct route_map_rule_list *,
71		       struct route_map_rule *);
72
73static void
74route_map_index_delete (struct route_map_index *, int);
75
76/* New route map allocation. Please note route map's name must be
77   specified. */
78static struct route_map *
79route_map_new (char *name)
80{
81  struct route_map *new;
82
83  new =  XCALLOC (MTYPE_ROUTE_MAP, sizeof (struct route_map));
84  new->name = XSTRDUP (MTYPE_ROUTE_MAP_NAME, name);
85  return new;
86}
87
88/* Add new name to route_map. */
89static struct route_map *
90route_map_add (char *name)
91{
92  struct route_map *map;
93  struct route_map_list *list;
94
95  map = route_map_new (name);
96  list = &route_map_master;
97
98  map->next = NULL;
99  map->prev = list->tail;
100  if (list->tail)
101    list->tail->next = map;
102  else
103    list->head = map;
104  list->tail = map;
105
106  /* Execute hook. */
107  if (route_map_master.add_hook)
108    (*route_map_master.add_hook) (name);
109
110  return map;
111}
112
113/* Route map delete from list. */
114static void
115route_map_delete (struct route_map *map)
116{
117  struct route_map_list *list;
118  struct route_map_index *index;
119  char *name;
120
121  while ((index = map->head) != NULL)
122    route_map_index_delete (index, 0);
123
124  name = map->name;
125
126  list = &route_map_master;
127
128  if (map->next)
129    map->next->prev = map->prev;
130  else
131    list->tail = map->prev;
132
133  if (map->prev)
134    map->prev->next = map->next;
135  else
136    list->head = map->next;
137
138  XFREE (MTYPE_ROUTE_MAP, map);
139
140  /* Execute deletion hook. */
141  if (route_map_master.delete_hook)
142    (*route_map_master.delete_hook) (name);
143
144  if (name)
145    XFREE (MTYPE_ROUTE_MAP_NAME, name);
146
147}
148
149/* Lookup route map by route map name string. */
150struct route_map *
151route_map_lookup_by_name (char *name)
152{
153  struct route_map *map;
154
155  for (map = route_map_master.head; map; map = map->next)
156    if (strcmp (map->name, name) == 0)
157      return map;
158  return NULL;
159}
160
161/* Lookup route map.  If there isn't route map create one and return
162   it. */
163struct route_map *
164route_map_get (char *name)
165{
166  struct route_map *map;
167
168  map = route_map_lookup_by_name (name);
169  if (map == NULL)
170    map = route_map_add (name);
171  return map;
172}
173
174/* Return route map's type string. */
175static char *
176route_map_type_str (enum route_map_type type)
177{
178  switch (type)
179    {
180    case RMAP_PERMIT:
181      return "permit";
182      break;
183    case RMAP_DENY:
184      return "deny";
185      break;
186    default:
187      return "";
188      break;
189    }
190}
191
192int
193route_map_empty (struct route_map *map)
194{
195  if (map->head == NULL && map->tail == NULL)
196    return 1;
197  else
198    return 0;
199}
200
201/* For debug. */
202void
203route_map_print ()
204{
205  struct route_map *map;
206  struct route_map_index *index;
207  struct route_map_rule *rule;
208
209  for (map = route_map_master.head; map; map = map->next)
210    for (index = map->head; index; index = index->next)
211      {
212	printf ("route-map %s %s %d\n",
213		map->name,
214		route_map_type_str (index->type),
215		index->pref);
216	for (rule = index->match_list.head; rule; rule = rule->next)
217	  printf (" match %s %s\n", rule->cmd->str, rule->rule_str);
218	for (rule = index->set_list.head; rule; rule = rule->next)
219	  printf (" set %s %s\n", rule->cmd->str, rule->rule_str);
220	if (index->exitpolicy == RMAP_GOTO)
221	  printf (" on-match goto %d\n", index->nextpref);
222	if (index->exitpolicy == RMAP_NEXT)
223	  printf (" on-match next\n");
224      }
225}
226
227/* New route map allocation. Please note route map's name must be
228   specified. */
229struct route_map_index *
230route_map_index_new ()
231{
232  struct route_map_index *new;
233
234  new =  XCALLOC (MTYPE_ROUTE_MAP_INDEX, sizeof (struct route_map_index));
235  new->exitpolicy = RMAP_EXIT; /* Default to Cisco-style */
236  return new;
237}
238
239/* Free route map index. */
240static void
241route_map_index_delete (struct route_map_index *index, int notify)
242{
243  struct route_map_rule *rule;
244
245  /* Free route match. */
246  while ((rule = index->match_list.head) != NULL)
247    route_map_rule_delete (&index->match_list, rule);
248
249  /* Free route set. */
250  while ((rule = index->set_list.head) != NULL)
251    route_map_rule_delete (&index->set_list, rule);
252
253  /* Remove index from route map list. */
254  if (index->next)
255    index->next->prev = index->prev;
256  else
257    index->map->tail = index->prev;
258
259  if (index->prev)
260    index->prev->next = index->next;
261  else
262    index->map->head = index->next;
263
264    /* Execute event hook. */
265  if (route_map_master.event_hook && notify)
266    (*route_map_master.event_hook) (RMAP_EVENT_INDEX_DELETED,
267				    index->map->name);
268
269  XFREE (MTYPE_ROUTE_MAP_INDEX, index);
270}
271
272/* Lookup index from route map. */
273struct route_map_index *
274route_map_index_lookup (struct route_map *map, enum route_map_type type,
275			int pref)
276{
277  struct route_map_index *index;
278
279  for (index = map->head; index; index = index->next)
280    if ((index->type == type || type == RMAP_ANY)
281	&& index->pref == pref)
282      return index;
283  return NULL;
284}
285
286/* Add new index to route map. */
287struct route_map_index *
288route_map_index_add (struct route_map *map, enum route_map_type type,
289		     int pref)
290{
291  struct route_map_index *index;
292  struct route_map_index *point;
293
294  /* Allocate new route map inex. */
295  index = route_map_index_new ();
296  index->map = map;
297  index->type = type;
298  index->pref = pref;
299
300  /* Compare preference. */
301  for (point = map->head; point; point = point->next)
302    if (point->pref >= pref)
303      break;
304
305  if (map->head == NULL)
306    {
307      map->head = map->tail = index;
308    }
309  else if (point == NULL)
310    {
311      index->prev = map->tail;
312      map->tail->next = index;
313      map->tail = index;
314    }
315  else if (point == map->head)
316    {
317      index->next = map->head;
318      map->head->prev = index;
319      map->head = index;
320    }
321  else
322    {
323      index->next = point;
324      index->prev = point->prev;
325      if (point->prev)
326	point->prev->next = index;
327      point->prev = index;
328    }
329
330  /* Execute event hook. */
331  if (route_map_master.event_hook)
332    (*route_map_master.event_hook) (RMAP_EVENT_INDEX_ADDED,
333				    map->name);
334
335  return index;
336}
337
338/* Get route map index. */
339struct route_map_index *
340route_map_index_get (struct route_map *map, enum route_map_type type,
341		     int pref)
342{
343  struct route_map_index *index;
344
345  index = route_map_index_lookup (map, RMAP_ANY, pref);
346  if (index && index->type != type)
347    {
348      /* Delete index from route map. */
349      route_map_index_delete (index, 1);
350      index = NULL;
351    }
352  if (index == NULL)
353    index = route_map_index_add (map, type, pref);
354  return index;
355}
356
357/* New route map rule */
358struct route_map_rule *
359route_map_rule_new ()
360{
361  struct route_map_rule *new;
362
363  new = XCALLOC (MTYPE_ROUTE_MAP_RULE, sizeof (struct route_map_rule));
364  return new;
365}
366
367/* Install rule command to the match list. */
368void
369route_map_install_match (struct route_map_rule_cmd *cmd)
370{
371  vector_set (route_match_vec, cmd);
372}
373
374/* Install rule command to the set list. */
375void
376route_map_install_set (struct route_map_rule_cmd *cmd)
377{
378  vector_set (route_set_vec, cmd);
379}
380
381/* Lookup rule command from match list. */
382struct route_map_rule_cmd *
383route_map_lookup_match (char *name)
384{
385  int i;
386  struct route_map_rule_cmd *rule;
387
388  for (i = 0; i < vector_max (route_match_vec); i++)
389    if ((rule = vector_slot (route_match_vec, i)) != NULL)
390      if (strcmp (rule->str, name) == 0)
391	return rule;
392  return NULL;
393}
394
395/* Lookup rule command from set list. */
396struct route_map_rule_cmd *
397route_map_lookup_set (char *name)
398{
399  int i;
400  struct route_map_rule_cmd *rule;
401
402  for (i = 0; i < vector_max (route_set_vec); i++)
403    if ((rule = vector_slot (route_set_vec, i)) != NULL)
404      if (strcmp (rule->str, name) == 0)
405	return rule;
406  return NULL;
407}
408
409/* Add match and set rule to rule list. */
410static void
411route_map_rule_add (struct route_map_rule_list *list,
412		    struct route_map_rule *rule)
413{
414  rule->next = NULL;
415  rule->prev = list->tail;
416  if (list->tail)
417    list->tail->next = rule;
418  else
419    list->head = rule;
420  list->tail = rule;
421}
422
423/* Delete rule from rule list. */
424static void
425route_map_rule_delete (struct route_map_rule_list *list,
426		       struct route_map_rule *rule)
427{
428  if (rule->cmd->func_free)
429    (*rule->cmd->func_free) (rule->value);
430
431  if (rule->rule_str)
432    XFREE (MTYPE_ROUTE_MAP_RULE_STR, rule->rule_str);
433
434  if (rule->next)
435    rule->next->prev = rule->prev;
436  else
437    list->tail = rule->prev;
438  if (rule->prev)
439    rule->prev->next = rule->next;
440  else
441    list->head = rule->next;
442
443  XFREE (MTYPE_ROUTE_MAP_RULE, rule);
444}
445
446/* strcmp wrapper function which don't crush even argument is NULL. */
447int
448rulecmp (char *dst, char *src)
449{
450  if (dst == NULL)
451    {
452      if (src ==  NULL)
453	return 0;
454      else
455	return 1;
456    }
457  else
458    {
459      if (src == NULL)
460	return 1;
461      else
462	return strcmp (dst, src);
463    }
464  return 1;
465}
466
467/* Add match statement to route map. */
468int
469route_map_add_match (struct route_map_index *index, char *match_name,
470		     char *match_arg)
471{
472  struct route_map_rule *rule;
473  struct route_map_rule *next;
474  struct route_map_rule_cmd *cmd;
475  void *compile;
476  int replaced = 0;
477
478  /* First lookup rule for add match statement. */
479  cmd = route_map_lookup_match (match_name);
480  if (cmd == NULL)
481    return RMAP_RULE_MISSING;
482
483  /* Next call compile function for this match statement. */
484  if (cmd->func_compile)
485    {
486      compile= (*cmd->func_compile)(match_arg);
487      if (compile == NULL)
488	return RMAP_COMPILE_ERROR;
489    }
490  else
491    compile = NULL;
492
493  /* If argument is completely same ignore it. */
494  for (rule = index->match_list.head; rule; rule = next)
495    {
496      next = rule->next;
497      if (rule->cmd == cmd)
498	{
499	  route_map_rule_delete (&index->match_list, rule);
500	  replaced = 1;
501	}
502    }
503
504  /* Add new route map match rule. */
505  rule = route_map_rule_new ();
506  rule->cmd = cmd;
507  rule->value = compile;
508  if (match_arg)
509    rule->rule_str = XSTRDUP (MTYPE_ROUTE_MAP_RULE_STR, match_arg);
510  else
511    rule->rule_str = NULL;
512
513  /* Add new route match rule to linked list. */
514  route_map_rule_add (&index->match_list, rule);
515
516  /* Execute event hook. */
517  if (route_map_master.event_hook)
518    (*route_map_master.event_hook) (replaced ?
519				    RMAP_EVENT_MATCH_REPLACED:
520				    RMAP_EVENT_MATCH_ADDED,
521				    index->map->name);
522
523  return 0;
524}
525
526/* Delete specified route match rule. */
527int
528route_map_delete_match (struct route_map_index *index, char *match_name,
529			char *match_arg)
530{
531  struct route_map_rule *rule;
532  struct route_map_rule_cmd *cmd;
533
534  cmd = route_map_lookup_match (match_name);
535  if (cmd == NULL)
536    return 1;
537
538  for (rule = index->match_list.head; rule; rule = rule->next)
539    if (rule->cmd == cmd &&
540	(rulecmp (rule->rule_str, match_arg) == 0 || match_arg == NULL))
541      {
542	route_map_rule_delete (&index->match_list, rule);
543	/* Execute event hook. */
544	if (route_map_master.event_hook)
545	  (*route_map_master.event_hook) (RMAP_EVENT_MATCH_DELETED,
546					  index->map->name);
547	return 0;
548      }
549  /* Can't find matched rule. */
550  return 1;
551}
552
553/* Add route-map set statement to the route map. */
554int
555route_map_add_set (struct route_map_index *index, char *set_name,
556		   char *set_arg)
557{
558  struct route_map_rule *rule;
559  struct route_map_rule *next;
560  struct route_map_rule_cmd *cmd;
561  void *compile;
562  int replaced = 0;
563
564  cmd = route_map_lookup_set (set_name);
565  if (cmd == NULL)
566    return RMAP_RULE_MISSING;
567
568  /* Next call compile function for this match statement. */
569  if (cmd->func_compile)
570    {
571      compile= (*cmd->func_compile)(set_arg);
572      if (compile == NULL)
573	return RMAP_COMPILE_ERROR;
574    }
575  else
576    compile = NULL;
577
578 /* Add by WJL. if old set command of same kind exist, delete it first
579    to ensure only one set command of same kind exist under a
580    route_map_index. */
581  for (rule = index->set_list.head; rule; rule = next)
582    {
583      next = rule->next;
584      if (rule->cmd == cmd)
585	{
586	  route_map_rule_delete (&index->set_list, rule);
587	  replaced = 1;
588	}
589    }
590
591  /* Add new route map match rule. */
592  rule = route_map_rule_new ();
593  rule->cmd = cmd;
594  rule->value = compile;
595  if (set_arg)
596    rule->rule_str = XSTRDUP (MTYPE_ROUTE_MAP_RULE_STR, set_arg);
597  else
598    rule->rule_str = NULL;
599
600  /* Add new route match rule to linked list. */
601  route_map_rule_add (&index->set_list, rule);
602
603  /* Execute event hook. */
604  if (route_map_master.event_hook)
605    (*route_map_master.event_hook) (replaced ?
606				    RMAP_EVENT_SET_REPLACED:
607				    RMAP_EVENT_SET_ADDED,
608				    index->map->name);
609  return 0;
610}
611
612/* Delete route map set rule. */
613int
614route_map_delete_set (struct route_map_index *index, char *set_name,
615			char *set_arg)
616{
617  struct route_map_rule *rule;
618  struct route_map_rule_cmd *cmd;
619
620  cmd = route_map_lookup_set (set_name);
621  if (cmd == NULL)
622    return 1;
623
624  for (rule = index->set_list.head; rule; rule = rule->next)
625    if ((rule->cmd == cmd) &&
626         (rulecmp (rule->rule_str, set_arg) == 0 || set_arg == NULL))
627      {
628        route_map_rule_delete (&index->set_list, rule);
629	/* Execute event hook. */
630	if (route_map_master.event_hook)
631	  (*route_map_master.event_hook) (RMAP_EVENT_SET_DELETED,
632					  index->map->name);
633        return 0;
634      }
635  /* Can't find matched rule. */
636  return 1;
637}
638
639/* Apply route map's each index to the object. */
640/*
641** The matrix for a route-map looks like this:
642** (note, this includes the description for the "NEXT"
643** and "GOTO" frobs now
644**
645**            Match   |   No Match
646**                    |
647**  permit      a     |      c
648**                    |
649**  ------------------+---------------
650**                    |
651**  deny        b     |      d
652**                    |
653**
654** a)   Apply Set statements, accept route
655**      If NEXT is specified, goto NEXT statement
656**      If GOTO is specified, goto the first clause where pref > nextpref
657**      If nothing is specified, do as Cisco and finish
658** b)   Finish route-map processing, and deny route
659** c) & d)   Goto Next index
660**
661** If we get no matches after we've processed all updates, then the route
662** is dropped too.
663**
664** Some notes on the new "NEXT" and "GOTO"
665**   on-match next    - If this clause is matched, then the set statements
666**                      are executed and then we drop through to the next clause
667**   on-match goto n  - If this clause is matched, then the set statments
668**                      are executed and then we goto the nth clause, or the
669**                      first clause greater than this. In order to ensure
670**                      route-maps *always* exit, you cannot jump backwards.
671**                      Sorry ;)
672**
673** We need to make sure our route-map processing matches the above
674*/
675route_map_result_t
676route_map_apply_index (struct route_map_index *index, struct prefix *prefix,
677                       route_map_object_t type, void *object)
678{
679  int ret;
680  struct route_map_rule *match;
681  struct route_map_rule *set;
682
683  /* Check all match rule and if there is no match rule return 0. */
684  for (match = index->match_list.head; match; match = match->next)
685    {
686      /* Try each match statement in turn. If any return something
687       other than RM_MATCH then we don't need to check anymore and can
688       return */
689      ret = (*match->cmd->func_apply)(match->value, prefix, type, object);
690      if (ret != RMAP_MATCH)
691	return ret;
692    }
693
694  /* We get here if all match statements matched From the matrix
695   above, if this is PERMIT we go on and apply the SET functions.  If
696   we're deny, we return indicating we matched a deny */
697
698  /* Apply set statement to the object. */
699  if (index->type == RMAP_PERMIT)
700    {
701      for (set = index->set_list.head; set; set = set->next)
702	{
703	  ret = (*set->cmd->func_apply)(set->value, prefix, type, object);
704	}
705      return RMAP_MATCH;
706    }
707  else
708    {
709      return RMAP_DENYMATCH;
710    }
711  /* Should not get here! */
712  return RMAP_MATCH;
713}
714
715/* Apply route map to the object. */
716route_map_result_t
717route_map_apply (struct route_map *map, struct prefix *prefix,
718		 route_map_object_t type, void *object)
719{
720  int ret = 0;
721  struct route_map_index *index;
722
723  if (map == NULL)
724    return RMAP_DENYMATCH;
725
726  for (index = map->head; index; index = index->next)
727    {
728      /* Apply this index. End here if we get a RM_NOMATCH */
729      ret = route_map_apply_index (index, prefix, type, object);
730
731      if (ret != RMAP_NOMATCH)
732	{
733	  /* We now have to handle the NEXT and GOTO clauses */
734	  if(index->exitpolicy == RMAP_EXIT)
735	    return ret;
736	  if(index->exitpolicy == RMAP_GOTO)
737	    {
738	      /* Find the next clause to jump to */
739	      struct route_map_index *next;
740
741	      next = index->next;
742	      while (next && next->pref < index->nextpref)
743		{
744		  index = next;
745		  next = next->next;
746		}
747	      if (next == NULL)
748		{
749		  /* No clauses match! */
750		  return ret;
751		}
752	    }
753	  /* Otherwise, we fall through as it was a NEXT */
754	}
755    }
756  /* Finally route-map does not match at all. */
757  return RMAP_DENYMATCH;
758}
759
760void
761route_map_add_hook (void (*func) (char *))
762{
763  route_map_master.add_hook = func;
764}
765
766void
767route_map_delete_hook (void (*func) (char *))
768{
769  route_map_master.delete_hook = func;
770}
771
772void
773route_map_event_hook (void (*func) (route_map_event_t, char *))
774{
775  route_map_master.event_hook = func;
776}
777
778void
779route_map_init ()
780{
781  /* Make vector for match and set. */
782  route_match_vec = vector_init (1);
783  route_set_vec = vector_init (1);
784}
785
786/* VTY related functions. */
787DEFUN (route_map,
788       route_map_cmd,
789       "route-map WORD (deny|permit) <1-65535>",
790       "Create route-map or enter route-map command mode\n"
791       "Route map tag\n"
792       "Route map denies set operations\n"
793       "Route map permits set operations\n"
794       "Sequence to insert to/delete from existing route-map entry\n")
795{
796  int permit;
797  unsigned long pref;
798  struct route_map *map;
799  struct route_map_index *index;
800  char *endptr = NULL;
801
802  /* Permit check. */
803  if (strncmp (argv[1], "permit", strlen (argv[1])) == 0)
804    permit = RMAP_PERMIT;
805  else if (strncmp (argv[1], "deny", strlen (argv[1])) == 0)
806    permit = RMAP_DENY;
807  else
808    {
809      vty_out (vty, "the third field must be [permit|deny]%s", VTY_NEWLINE);
810      return CMD_WARNING;
811    }
812
813  /* Preference check. */
814  pref = strtoul (argv[2], &endptr, 10);
815  if (pref == ULONG_MAX || *endptr != '\0')
816    {
817      vty_out (vty, "the fourth field must be positive integer%s",
818	       VTY_NEWLINE);
819      return CMD_WARNING;
820    }
821  if (pref == 0 || pref > 65535)
822    {
823      vty_out (vty, "the fourth field must be <1-65535>%s", VTY_NEWLINE);
824      return CMD_WARNING;
825    }
826
827  /* Get route map. */
828  map = route_map_get (argv[0]);
829  index = route_map_index_get (map, permit, pref);
830
831  vty->index = index;
832  vty->node = RMAP_NODE;
833  return CMD_SUCCESS;
834}
835
836DEFUN (no_route_map_all,
837       no_route_map_all_cmd,
838       "no route-map WORD",
839       NO_STR
840       "Create route-map or enter route-map command mode\n"
841       "Route map tag\n")
842{
843  struct route_map *map;
844
845  map = route_map_lookup_by_name (argv[0]);
846  if (map == NULL)
847    {
848      vty_out (vty, "%% Could not find route-map %s%s",
849	       argv[0], VTY_NEWLINE);
850      return CMD_WARNING;
851    }
852
853  route_map_delete (map);
854
855  return CMD_SUCCESS;
856}
857
858DEFUN (no_route_map,
859       no_route_map_cmd,
860       "no route-map WORD (deny|permit) <1-65535>",
861       NO_STR
862       "Create route-map or enter route-map command mode\n"
863       "Route map tag\n"
864       "Route map denies set operations\n"
865       "Route map permits set operations\n"
866       "Sequence to insert to/delete from existing route-map entry\n")
867{
868  int permit;
869  unsigned long pref;
870  struct route_map *map;
871  struct route_map_index *index;
872  char *endptr = NULL;
873
874  /* Permit check. */
875  if (strncmp (argv[1], "permit", strlen (argv[1])) == 0)
876    permit = RMAP_PERMIT;
877  else if (strncmp (argv[1], "deny", strlen (argv[1])) == 0)
878    permit = RMAP_DENY;
879  else
880    {
881      vty_out (vty, "the third field must be [permit|deny]%s", VTY_NEWLINE);
882      return CMD_WARNING;
883    }
884
885  /* Preference. */
886  pref = strtoul (argv[2], &endptr, 10);
887  if (pref == ULONG_MAX || *endptr != '\0')
888    {
889      vty_out (vty, "the fourth field must be positive integer%s",
890	       VTY_NEWLINE);
891      return CMD_WARNING;
892    }
893  if (pref == 0 || pref > 65535)
894    {
895      vty_out (vty, "the fourth field must be <1-65535>%s", VTY_NEWLINE);
896      return CMD_WARNING;
897    }
898
899  /* Existence check. */
900  map = route_map_lookup_by_name (argv[0]);
901  if (map == NULL)
902    {
903      vty_out (vty, "%% Could not find route-map %s%s",
904	       argv[0], VTY_NEWLINE);
905      return CMD_WARNING;
906    }
907
908  /* Lookup route map index. */
909  index = route_map_index_lookup (map, permit, pref);
910  if (index == NULL)
911    {
912      vty_out (vty, "%% Could not find route-map entry %s %s%s",
913	       argv[0], argv[2], VTY_NEWLINE);
914      return CMD_WARNING;
915    }
916
917  /* Delete index from route map. */
918  route_map_index_delete (index, 1);
919
920  /* If this route rule is the last one, delete route map itself. */
921  if (route_map_empty (map))
922    route_map_delete (map);
923
924  return CMD_SUCCESS;
925}
926
927DEFUN (rmap_onmatch_next,
928       rmap_onmatch_next_cmd,
929       "on-match next",
930       "Exit policy on matches\n"
931       "Next clause\n")
932{
933  struct route_map_index *index;
934
935  index = vty->index;
936
937  if (index)
938    index->exitpolicy = RMAP_NEXT;
939
940  return CMD_SUCCESS;
941}
942
943DEFUN (no_rmap_onmatch_next,
944       no_rmap_onmatch_next_cmd,
945       "no on-match next",
946       NO_STR
947       "Exit policy on matches\n"
948       "Next clause\n")
949{
950  struct route_map_index *index;
951
952  index = vty->index;
953
954  if (index)
955    index->exitpolicy = RMAP_EXIT;
956
957  return CMD_SUCCESS;
958}
959
960DEFUN (rmap_onmatch_goto,
961       rmap_onmatch_goto_cmd,
962       "on-match goto <1-65535>",
963       "Exit policy on matches\n"
964       "Goto Clause number\n"
965       "Number\n")
966{
967  struct route_map_index *index;
968  int d = 0;
969
970  if (argv[0])
971    d = atoi(argv[0]);
972
973  index = vty->index;
974  if (index)
975    {
976      if (d <= index->pref)
977	{
978	  /* Can't allow you to do that, Dave */
979	  vty_out (vty, "can't jump backwards in route-maps%s",
980		   VTY_NEWLINE);
981	  return CMD_WARNING;
982	}
983      else
984	{
985	  index->exitpolicy = RMAP_GOTO;
986	  index->nextpref = d;
987	}
988    }
989  return CMD_SUCCESS;
990}
991
992DEFUN (no_rmap_onmatch_goto,
993       no_rmap_onmatch_goto_cmd,
994       "no on-match goto",
995       NO_STR
996       "Exit policy on matches\n"
997       "Next clause\n")
998{
999  struct route_map_index *index;
1000
1001  index = vty->index;
1002
1003  if (index)
1004    index->exitpolicy = RMAP_EXIT;
1005
1006  return CMD_SUCCESS;
1007}
1008
1009/* Configuration write function. */
1010int
1011route_map_config_write (struct vty *vty)
1012{
1013  struct route_map *map;
1014  struct route_map_index *index;
1015  struct route_map_rule *rule;
1016  int first = 1;
1017  int write = 0;
1018
1019  for (map = route_map_master.head; map; map = map->next)
1020    for (index = map->head; index; index = index->next)
1021      {
1022	if (!first)
1023	  vty_out (vty, "!%s", VTY_NEWLINE);
1024	else
1025	  first = 0;
1026
1027	vty_out (vty, "route-map %s %s %d%s",
1028		 map->name,
1029		 route_map_type_str (index->type),
1030		 index->pref, VTY_NEWLINE);
1031
1032	for (rule = index->match_list.head; rule; rule = rule->next)
1033	  vty_out (vty, " match %s %s%s", rule->cmd->str,
1034		   rule->rule_str ? rule->rule_str : "",
1035		   VTY_NEWLINE);
1036
1037	for (rule = index->set_list.head; rule; rule = rule->next)
1038	  vty_out (vty, " set %s %s%s", rule->cmd->str,
1039		   rule->rule_str ? rule->rule_str : "",
1040		   VTY_NEWLINE);
1041	if (index->exitpolicy == RMAP_GOTO)
1042	  vty_out (vty, " on-match goto %d%s", index->nextpref,
1043		   VTY_NEWLINE);
1044	if (index->exitpolicy == RMAP_NEXT)
1045	  vty_out (vty," on-match next%s", VTY_NEWLINE);
1046
1047	write++;
1048      }
1049  return write;
1050}
1051
1052/* Route map node structure. */
1053struct cmd_node rmap_node =
1054{
1055  RMAP_NODE,
1056  "%s(config-route-map)# ",
1057  1
1058};
1059
1060/* Initialization of route map vector. */
1061void
1062route_map_init_vty ()
1063{
1064  /* Install route map top node. */
1065  install_node (&rmap_node, route_map_config_write);
1066
1067  /* Install route map commands. */
1068  install_default (RMAP_NODE);
1069  install_element (CONFIG_NODE, &route_map_cmd);
1070  install_element (CONFIG_NODE, &no_route_map_cmd);
1071  install_element (CONFIG_NODE, &no_route_map_all_cmd);
1072
1073  /* Install the on-match stuff */
1074  install_element (RMAP_NODE, &route_map_cmd);
1075  install_element (RMAP_NODE, &rmap_onmatch_next_cmd);
1076  install_element (RMAP_NODE, &no_rmap_onmatch_next_cmd);
1077  install_element (RMAP_NODE, &rmap_onmatch_goto_cmd);
1078  install_element (RMAP_NODE, &no_rmap_onmatch_goto_cmd);
1079}
1080#endif /* FOX_LIST_SUPPORT */
1081