blob: 67bbc034fc77dc577ec39d60bd07e288417a371b [file] [log] [blame]
paul718e3742002-12-13 20:15:29 +00001/* 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#include <zebra.h>
22
23#include "linklist.h"
24#include "memory.h"
25#include "vector.h"
26#include "prefix.h"
27#include "routemap.h"
28#include "command.h"
paulfee0f4c2004-09-13 05:12:46 +000029#include "log.h"
paul718e3742002-12-13 20:15:29 +000030
31/* Vector for route match rules. */
32static vector route_match_vec;
33
34/* Vector for route set rules. */
35static vector route_set_vec;
36
37/* Route map rule. This rule has both `match' rule and `set' rule. */
38struct route_map_rule
39{
40 /* Rule type. */
41 struct route_map_rule_cmd *cmd;
42
43 /* For pretty printing. */
44 char *rule_str;
45
46 /* Pre-compiled match rule. */
47 void *value;
48
49 /* Linked list. */
50 struct route_map_rule *next;
51 struct route_map_rule *prev;
52};
53
54/* Making route map list. */
55struct route_map_list
56{
57 struct route_map *head;
58 struct route_map *tail;
59
60 void (*add_hook) (char *);
61 void (*delete_hook) (char *);
62 void (*event_hook) (route_map_event_t, char *);
63};
64
65/* Master list of route map. */
66static struct route_map_list route_map_master = { NULL, NULL, NULL, NULL };
67
68static void
69route_map_rule_delete (struct route_map_rule_list *,
70 struct route_map_rule *);
71
72static void
73route_map_index_delete (struct route_map_index *, int);
74
75/* New route map allocation. Please note route map's name must be
76 specified. */
77static struct route_map *
78route_map_new (char *name)
79{
80 struct route_map *new;
81
82 new = XCALLOC (MTYPE_ROUTE_MAP, sizeof (struct route_map));
83 new->name = XSTRDUP (MTYPE_ROUTE_MAP_NAME, name);
84 return new;
85}
86
87/* Add new name to route_map. */
88static struct route_map *
89route_map_add (char *name)
90{
91 struct route_map *map;
92 struct route_map_list *list;
93
94 map = route_map_new (name);
95 list = &route_map_master;
96
97 map->next = NULL;
98 map->prev = list->tail;
99 if (list->tail)
100 list->tail->next = map;
101 else
102 list->head = map;
103 list->tail = map;
104
105 /* Execute hook. */
106 if (route_map_master.add_hook)
107 (*route_map_master.add_hook) (name);
108
109 return map;
110}
111
112/* Route map delete from list. */
113static void
114route_map_delete (struct route_map *map)
115{
116 struct route_map_list *list;
117 struct route_map_index *index;
118 char *name;
119
120 while ((index = map->head) != NULL)
121 route_map_index_delete (index, 0);
122
123 name = map->name;
124
125 list = &route_map_master;
126
127 if (map->next)
128 map->next->prev = map->prev;
129 else
130 list->tail = map->prev;
131
132 if (map->prev)
133 map->prev->next = map->next;
134 else
135 list->head = map->next;
136
137 XFREE (MTYPE_ROUTE_MAP, map);
138
139 /* Execute deletion hook. */
140 if (route_map_master.delete_hook)
141 (*route_map_master.delete_hook) (name);
142
143 if (name)
144 XFREE (MTYPE_ROUTE_MAP_NAME, name);
145
146}
147
148/* Lookup route map by route map name string. */
149struct route_map *
150route_map_lookup_by_name (char *name)
151{
152 struct route_map *map;
153
154 for (map = route_map_master.head; map; map = map->next)
155 if (strcmp (map->name, name) == 0)
156 return map;
157 return NULL;
158}
159
160/* Lookup route map. If there isn't route map create one and return
161 it. */
162struct route_map *
163route_map_get (char *name)
164{
165 struct route_map *map;
166
167 map = route_map_lookup_by_name (name);
168 if (map == NULL)
169 map = route_map_add (name);
170 return map;
171}
172
173/* Return route map's type string. */
hasso8c328f12004-10-05 21:01:23 +0000174const static char *
paul718e3742002-12-13 20:15:29 +0000175route_map_type_str (enum route_map_type type)
176{
177 switch (type)
178 {
179 case RMAP_PERMIT:
180 return "permit";
181 break;
182 case RMAP_DENY:
183 return "deny";
184 break;
185 default:
186 return "";
187 break;
188 }
189}
190
191int
192route_map_empty (struct route_map *map)
193{
194 if (map->head == NULL && map->tail == NULL)
195 return 1;
196 else
197 return 0;
198}
199
paul5510e832004-07-09 14:00:01 +0000200/* show route-map */
201static void
202vty_show_route_map_entry (struct vty *vty, struct route_map *map)
paul718e3742002-12-13 20:15:29 +0000203{
paul718e3742002-12-13 20:15:29 +0000204 struct route_map_index *index;
205 struct route_map_rule *rule;
206
paul5510e832004-07-09 14:00:01 +0000207 for (index = map->head; index; index = index->next)
208 {
209 vty_out (vty, "route-map %s, %s, sequence %d%s",
210 map->name, route_map_type_str (index->type),
211 index->pref, VTY_NEWLINE);
212
213 /* Match clauses */
214 vty_out (vty, " Match clauses:%s", VTY_NEWLINE);
215 for (rule = index->match_list.head; rule; rule = rule->next)
216 vty_out (vty, " %s %s%s",
217 rule->cmd->str, rule->rule_str, VTY_NEWLINE);
218
219 vty_out (vty, " Set clauses:%s", VTY_NEWLINE);
220 for (rule = index->set_list.head; rule; rule = rule->next)
221 vty_out (vty, " %s %s%s",
222 rule->cmd->str, rule->rule_str, VTY_NEWLINE);
223
224 vty_out (vty, " Action:%s", VTY_NEWLINE);
paulfee0f4c2004-09-13 05:12:46 +0000225
226 if (index->nextrm)
227 vty_out (vty, " Call %s%s", index->nextrm, VTY_NEWLINE);
228 else if (index->exitpolicy == RMAP_GOTO)
paul5510e832004-07-09 14:00:01 +0000229 vty_out (vty, " Goto %d%s", index->nextpref, VTY_NEWLINE);
paul5510e832004-07-09 14:00:01 +0000230 else if (index->exitpolicy == RMAP_NEXT)
231 {
232 vty_out (vty, " Goto next, (entry ");
233 if (index->next)
234 vty_out (vty, " %d)%s", index->next->pref, VTY_NEWLINE);
235 else
236 vty_out (vty, " undefined)%s", VTY_NEWLINE);
237 }
238 else if (index->exitpolicy == RMAP_EXIT)
239 vty_out (vty, " Exit routemap%s", VTY_NEWLINE);
240 }
paul718e3742002-12-13 20:15:29 +0000241}
242
paul5510e832004-07-09 14:00:01 +0000243int
244vty_show_route_map (struct vty *vty, char *name)
245{
246 struct route_map *map;
247
248 if (name)
249 {
250 map = route_map_lookup_by_name (name);
251
252 if (map)
253 {
254 vty_show_route_map_entry (vty, map);
255 return CMD_SUCCESS;
256 }
257 else
258 {
259 vty_out (vty, "%%route-map %s not found%s", name, VTY_NEWLINE);
260 return CMD_WARNING;
261 }
262 }
263 return CMD_SUCCESS;
264}
265
266
paul718e3742002-12-13 20:15:29 +0000267/* New route map allocation. Please note route map's name must be
268 specified. */
269struct route_map_index *
270route_map_index_new ()
271{
272 struct route_map_index *new;
273
274 new = XCALLOC (MTYPE_ROUTE_MAP_INDEX, sizeof (struct route_map_index));
275 new->exitpolicy = RMAP_EXIT; /* Default to Cisco-style */
276 return new;
277}
278
279/* Free route map index. */
280static void
281route_map_index_delete (struct route_map_index *index, int notify)
282{
283 struct route_map_rule *rule;
284
285 /* Free route match. */
286 while ((rule = index->match_list.head) != NULL)
287 route_map_rule_delete (&index->match_list, rule);
288
289 /* Free route set. */
290 while ((rule = index->set_list.head) != NULL)
291 route_map_rule_delete (&index->set_list, rule);
292
293 /* Remove index from route map list. */
294 if (index->next)
295 index->next->prev = index->prev;
296 else
297 index->map->tail = index->prev;
298
299 if (index->prev)
300 index->prev->next = index->next;
301 else
302 index->map->head = index->next;
303
paulfee0f4c2004-09-13 05:12:46 +0000304 /* Free 'char *nextrm' if not NULL */
305 if (index->nextrm)
306 free (index->nextrm);
307
paul718e3742002-12-13 20:15:29 +0000308 /* Execute event hook. */
309 if (route_map_master.event_hook && notify)
310 (*route_map_master.event_hook) (RMAP_EVENT_INDEX_DELETED,
311 index->map->name);
312
313 XFREE (MTYPE_ROUTE_MAP_INDEX, index);
314}
315
316/* Lookup index from route map. */
317struct route_map_index *
318route_map_index_lookup (struct route_map *map, enum route_map_type type,
319 int pref)
320{
321 struct route_map_index *index;
322
323 for (index = map->head; index; index = index->next)
324 if ((index->type == type || type == RMAP_ANY)
325 && index->pref == pref)
326 return index;
327 return NULL;
328}
329
330/* Add new index to route map. */
331struct route_map_index *
332route_map_index_add (struct route_map *map, enum route_map_type type,
333 int pref)
334{
335 struct route_map_index *index;
336 struct route_map_index *point;
337
338 /* Allocate new route map inex. */
339 index = route_map_index_new ();
340 index->map = map;
341 index->type = type;
342 index->pref = pref;
343
344 /* Compare preference. */
345 for (point = map->head; point; point = point->next)
346 if (point->pref >= pref)
347 break;
348
349 if (map->head == NULL)
350 {
351 map->head = map->tail = index;
352 }
353 else if (point == NULL)
354 {
355 index->prev = map->tail;
356 map->tail->next = index;
357 map->tail = index;
358 }
359 else if (point == map->head)
360 {
361 index->next = map->head;
362 map->head->prev = index;
363 map->head = index;
364 }
365 else
366 {
367 index->next = point;
368 index->prev = point->prev;
369 if (point->prev)
370 point->prev->next = index;
371 point->prev = index;
372 }
373
374 /* Execute event hook. */
375 if (route_map_master.event_hook)
376 (*route_map_master.event_hook) (RMAP_EVENT_INDEX_ADDED,
377 map->name);
378
379 return index;
380}
381
382/* Get route map index. */
383struct route_map_index *
384route_map_index_get (struct route_map *map, enum route_map_type type,
385 int pref)
386{
387 struct route_map_index *index;
388
389 index = route_map_index_lookup (map, RMAP_ANY, pref);
390 if (index && index->type != type)
391 {
392 /* Delete index from route map. */
393 route_map_index_delete (index, 1);
394 index = NULL;
395 }
396 if (index == NULL)
397 index = route_map_index_add (map, type, pref);
398 return index;
399}
400
401/* New route map rule */
402struct route_map_rule *
403route_map_rule_new ()
404{
405 struct route_map_rule *new;
406
407 new = XCALLOC (MTYPE_ROUTE_MAP_RULE, sizeof (struct route_map_rule));
408 return new;
409}
410
411/* Install rule command to the match list. */
412void
413route_map_install_match (struct route_map_rule_cmd *cmd)
414{
415 vector_set (route_match_vec, cmd);
416}
417
418/* Install rule command to the set list. */
419void
420route_map_install_set (struct route_map_rule_cmd *cmd)
421{
422 vector_set (route_set_vec, cmd);
423}
424
425/* Lookup rule command from match list. */
426struct route_map_rule_cmd *
427route_map_lookup_match (char *name)
428{
hasso8c328f12004-10-05 21:01:23 +0000429 unsigned int i;
paul718e3742002-12-13 20:15:29 +0000430 struct route_map_rule_cmd *rule;
431
432 for (i = 0; i < vector_max (route_match_vec); i++)
433 if ((rule = vector_slot (route_match_vec, i)) != NULL)
434 if (strcmp (rule->str, name) == 0)
435 return rule;
436 return NULL;
437}
438
439/* Lookup rule command from set list. */
440struct route_map_rule_cmd *
441route_map_lookup_set (char *name)
442{
hasso8c328f12004-10-05 21:01:23 +0000443 unsigned int i;
paul718e3742002-12-13 20:15:29 +0000444 struct route_map_rule_cmd *rule;
445
446 for (i = 0; i < vector_max (route_set_vec); i++)
447 if ((rule = vector_slot (route_set_vec, i)) != NULL)
448 if (strcmp (rule->str, name) == 0)
449 return rule;
450 return NULL;
451}
452
453/* Add match and set rule to rule list. */
454static void
455route_map_rule_add (struct route_map_rule_list *list,
456 struct route_map_rule *rule)
457{
458 rule->next = NULL;
459 rule->prev = list->tail;
460 if (list->tail)
461 list->tail->next = rule;
462 else
463 list->head = rule;
464 list->tail = rule;
465}
466
467/* Delete rule from rule list. */
468static void
469route_map_rule_delete (struct route_map_rule_list *list,
470 struct route_map_rule *rule)
471{
472 if (rule->cmd->func_free)
473 (*rule->cmd->func_free) (rule->value);
474
475 if (rule->rule_str)
476 XFREE (MTYPE_ROUTE_MAP_RULE_STR, rule->rule_str);
477
478 if (rule->next)
479 rule->next->prev = rule->prev;
480 else
481 list->tail = rule->prev;
482 if (rule->prev)
483 rule->prev->next = rule->next;
484 else
485 list->head = rule->next;
486
487 XFREE (MTYPE_ROUTE_MAP_RULE, rule);
488}
489
490/* strcmp wrapper function which don't crush even argument is NULL. */
491int
492rulecmp (char *dst, char *src)
493{
494 if (dst == NULL)
495 {
496 if (src == NULL)
497 return 0;
498 else
499 return 1;
500 }
501 else
502 {
503 if (src == NULL)
504 return 1;
505 else
506 return strcmp (dst, src);
507 }
508 return 1;
509}
510
511/* Add match statement to route map. */
512int
513route_map_add_match (struct route_map_index *index, char *match_name,
514 char *match_arg)
515{
516 struct route_map_rule *rule;
517 struct route_map_rule *next;
518 struct route_map_rule_cmd *cmd;
519 void *compile;
520 int replaced = 0;
521
522 /* First lookup rule for add match statement. */
523 cmd = route_map_lookup_match (match_name);
524 if (cmd == NULL)
525 return RMAP_RULE_MISSING;
526
527 /* Next call compile function for this match statement. */
528 if (cmd->func_compile)
529 {
530 compile= (*cmd->func_compile)(match_arg);
531 if (compile == NULL)
532 return RMAP_COMPILE_ERROR;
533 }
534 else
535 compile = NULL;
536
537 /* If argument is completely same ignore it. */
538 for (rule = index->match_list.head; rule; rule = next)
539 {
540 next = rule->next;
541 if (rule->cmd == cmd)
542 {
543 route_map_rule_delete (&index->match_list, rule);
544 replaced = 1;
545 }
546 }
547
548 /* Add new route map match rule. */
549 rule = route_map_rule_new ();
550 rule->cmd = cmd;
551 rule->value = compile;
552 if (match_arg)
553 rule->rule_str = XSTRDUP (MTYPE_ROUTE_MAP_RULE_STR, match_arg);
554 else
555 rule->rule_str = NULL;
556
557 /* Add new route match rule to linked list. */
558 route_map_rule_add (&index->match_list, rule);
559
560 /* Execute event hook. */
561 if (route_map_master.event_hook)
562 (*route_map_master.event_hook) (replaced ?
563 RMAP_EVENT_MATCH_REPLACED:
564 RMAP_EVENT_MATCH_ADDED,
565 index->map->name);
566
567 return 0;
568}
569
570/* Delete specified route match rule. */
571int
572route_map_delete_match (struct route_map_index *index, char *match_name,
573 char *match_arg)
574{
575 struct route_map_rule *rule;
576 struct route_map_rule_cmd *cmd;
577
578 cmd = route_map_lookup_match (match_name);
579 if (cmd == NULL)
580 return 1;
581
582 for (rule = index->match_list.head; rule; rule = rule->next)
583 if (rule->cmd == cmd &&
584 (rulecmp (rule->rule_str, match_arg) == 0 || match_arg == NULL))
585 {
586 route_map_rule_delete (&index->match_list, rule);
587 /* Execute event hook. */
588 if (route_map_master.event_hook)
589 (*route_map_master.event_hook) (RMAP_EVENT_MATCH_DELETED,
590 index->map->name);
591 return 0;
592 }
593 /* Can't find matched rule. */
594 return 1;
595}
596
597/* Add route-map set statement to the route map. */
598int
599route_map_add_set (struct route_map_index *index, char *set_name,
600 char *set_arg)
601{
602 struct route_map_rule *rule;
603 struct route_map_rule *next;
604 struct route_map_rule_cmd *cmd;
605 void *compile;
606 int replaced = 0;
607
608 cmd = route_map_lookup_set (set_name);
609 if (cmd == NULL)
610 return RMAP_RULE_MISSING;
611
612 /* Next call compile function for this match statement. */
613 if (cmd->func_compile)
614 {
615 compile= (*cmd->func_compile)(set_arg);
616 if (compile == NULL)
617 return RMAP_COMPILE_ERROR;
618 }
619 else
620 compile = NULL;
621
622 /* Add by WJL. if old set command of same kind exist, delete it first
623 to ensure only one set command of same kind exist under a
624 route_map_index. */
625 for (rule = index->set_list.head; rule; rule = next)
626 {
627 next = rule->next;
628 if (rule->cmd == cmd)
629 {
630 route_map_rule_delete (&index->set_list, rule);
631 replaced = 1;
632 }
633 }
634
635 /* Add new route map match rule. */
636 rule = route_map_rule_new ();
637 rule->cmd = cmd;
638 rule->value = compile;
639 if (set_arg)
640 rule->rule_str = XSTRDUP (MTYPE_ROUTE_MAP_RULE_STR, set_arg);
641 else
642 rule->rule_str = NULL;
643
644 /* Add new route match rule to linked list. */
645 route_map_rule_add (&index->set_list, rule);
646
647 /* Execute event hook. */
648 if (route_map_master.event_hook)
649 (*route_map_master.event_hook) (replaced ?
650 RMAP_EVENT_SET_REPLACED:
651 RMAP_EVENT_SET_ADDED,
652 index->map->name);
653 return 0;
654}
655
656/* Delete route map set rule. */
657int
658route_map_delete_set (struct route_map_index *index, char *set_name,
659 char *set_arg)
660{
661 struct route_map_rule *rule;
662 struct route_map_rule_cmd *cmd;
663
664 cmd = route_map_lookup_set (set_name);
665 if (cmd == NULL)
666 return 1;
667
668 for (rule = index->set_list.head; rule; rule = rule->next)
669 if ((rule->cmd == cmd) &&
670 (rulecmp (rule->rule_str, set_arg) == 0 || set_arg == NULL))
671 {
672 route_map_rule_delete (&index->set_list, rule);
673 /* Execute event hook. */
674 if (route_map_master.event_hook)
675 (*route_map_master.event_hook) (RMAP_EVENT_SET_DELETED,
676 index->map->name);
677 return 0;
678 }
679 /* Can't find matched rule. */
680 return 1;
681}
682
paul3bf1c912003-10-29 06:30:19 +0000683/* Apply route map's each index to the object.
684
685 The matrix for a route-map looks like this:
686 (note, this includes the description for the "NEXT"
687 and "GOTO" frobs now
paul718e3742002-12-13 20:15:29 +0000688
paul3bf1c912003-10-29 06:30:19 +0000689 Match | No Match
690 |
691 permit action | cont
692 |
693 ------------------+---------------
694 |
695 deny deny | cont
696 |
697
paulfee0f4c2004-09-13 05:12:46 +0000698 action)
699 -Apply Set statements, accept route
700 -If Call statement is present jump to the specified route-map, if it
701 denies the route we finish.
702 -If NEXT is specified, goto NEXT statement
703 -If GOTO is specified, goto the first clause where pref > nextpref
704 -If nothing is specified, do as Cisco and finish
705 deny)
706 -Route is denied by route-map.
707 cont)
708 -Goto Next index
paul3bf1c912003-10-29 06:30:19 +0000709
710 If we get no matches after we've processed all updates, then the route
711 is dropped too.
712
paulfee0f4c2004-09-13 05:12:46 +0000713 Some notes on the new "CALL", "NEXT" and "GOTO"
714 call WORD - If this clause is matched, then the set statements
715 are executed and then we jump to route-map 'WORD'. If
716 this route-map denies the route, we finish, in other case we
717 do whatever the exit policy (EXIT, NEXT or GOTO) tells.
paul3bf1c912003-10-29 06:30:19 +0000718 on-match next - If this clause is matched, then the set statements
719 are executed and then we drop through to the next clause
720 on-match goto n - If this clause is matched, then the set statments
721 are executed and then we goto the nth clause, or the
722 first clause greater than this. In order to ensure
723 route-maps *always* exit, you cannot jump backwards.
724 Sorry ;)
725
726 We need to make sure our route-map processing matches the above
727*/
paul718e3742002-12-13 20:15:29 +0000728
paul3bf1c912003-10-29 06:30:19 +0000729route_map_result_t
730route_map_apply_match (struct route_map_rule_list *match_list,
731 struct prefix *prefix, route_map_object_t type,
732 void *object)
733{
734 route_map_result_t ret = RMAP_NOMATCH;
735 struct route_map_rule *match;
paul718e3742002-12-13 20:15:29 +0000736
paul3bf1c912003-10-29 06:30:19 +0000737
738 /* Check all match rule and if there is no match rule, go to the
739 set statement. */
740 if (!match_list->head)
741 ret = RMAP_MATCH;
742 else
paul718e3742002-12-13 20:15:29 +0000743 {
paul3bf1c912003-10-29 06:30:19 +0000744 for (match = match_list->head; match; match = match->next)
745 {
746 /* Try each match statement in turn, If any do not return
747 RMAP_MATCH, return, otherwise continue on to next match
748 statement. All match statements must match for end-result
749 to be a match. */
750 ret = (*match->cmd->func_apply) (match->value, prefix,
751 type, object);
752 if (ret != RMAP_MATCH)
753 return ret;
754 }
paul718e3742002-12-13 20:15:29 +0000755 }
paul3bf1c912003-10-29 06:30:19 +0000756 return ret;
paul718e3742002-12-13 20:15:29 +0000757}
758
759/* Apply route map to the object. */
760route_map_result_t
paul3bf1c912003-10-29 06:30:19 +0000761route_map_apply (struct route_map *map, struct prefix *prefix,
762 route_map_object_t type, void *object)
paul718e3742002-12-13 20:15:29 +0000763{
paulfee0f4c2004-09-13 05:12:46 +0000764 static int recursion = 0;
paul718e3742002-12-13 20:15:29 +0000765 int ret = 0;
766 struct route_map_index *index;
paul3bf1c912003-10-29 06:30:19 +0000767 struct route_map_rule *set;
paul718e3742002-12-13 20:15:29 +0000768
paulfee0f4c2004-09-13 05:12:46 +0000769 if (recursion > RMAP_RECURSION_LIMIT)
770 {
771 zlog (NULL, LOG_WARNING,
772 "route-map recursion limit (%d) reached, discarding route",
773 RMAP_RECURSION_LIMIT);
774 recursion = 0;
775 return RMAP_DENYMATCH;
776 }
777
paul718e3742002-12-13 20:15:29 +0000778 if (map == NULL)
779 return RMAP_DENYMATCH;
780
781 for (index = map->head; index; index = index->next)
782 {
paul3bf1c912003-10-29 06:30:19 +0000783 /* Apply this index. */
784 ret = route_map_apply_match (&index->match_list, prefix, type, object);
paul718e3742002-12-13 20:15:29 +0000785
paul3bf1c912003-10-29 06:30:19 +0000786 /* Now we apply the matrix from above */
787 if (ret == RMAP_NOMATCH)
788 /* 'cont' from matrix - continue to next route-map sequence */
789 continue;
790 else if (ret == RMAP_MATCH)
791 {
792 if (index->type == RMAP_PERMIT)
793 /* 'action' */
794 {
795 /* permit+match must execute sets */
796 for (set = index->set_list.head; set; set = set->next)
797 ret = (*set->cmd->func_apply) (set->value, prefix,
798 type, object);
paulfee0f4c2004-09-13 05:12:46 +0000799
800 /* Call another route-map if available */
801 if (index->nextrm)
802 {
803 struct route_map *nextrm =
804 route_map_lookup_by_name (index->nextrm);
805
806 if (nextrm) /* Target route-map found, jump to it */
807 {
808 recursion++;
809 ret = route_map_apply (nextrm, prefix, type, object);
810 recursion--;
811 }
812
813 /* If nextrm returned 'deny', finish. */
814 if (ret == RMAP_DENYMATCH)
815 return ret;
816 }
817
paul3bf1c912003-10-29 06:30:19 +0000818 switch (index->exitpolicy)
819 {
820 case RMAP_EXIT:
821 return ret;
822 case RMAP_NEXT:
823 continue;
824 case RMAP_GOTO:
825 {
826 /* Find the next clause to jump to */
827 struct route_map_index *next = index->next;
paulfee0f4c2004-09-13 05:12:46 +0000828 int nextpref = index->nextpref;
paul718e3742002-12-13 20:15:29 +0000829
paulfee0f4c2004-09-13 05:12:46 +0000830 while (next && next->pref < nextpref)
paul3bf1c912003-10-29 06:30:19 +0000831 {
832 index = next;
833 next = next->next;
834 }
835 if (next == NULL)
836 {
837 /* No clauses match! */
838 return ret;
839 }
840 }
841 }
842 }
843 else if (index->type == RMAP_DENY)
844 /* 'deny' */
845 {
paul3bf1c912003-10-29 06:30:19 +0000846 return RMAP_DENYMATCH;
847 }
848 }
paul718e3742002-12-13 20:15:29 +0000849 }
850 /* Finally route-map does not match at all. */
851 return RMAP_DENYMATCH;
852}
853
854void
855route_map_add_hook (void (*func) (char *))
856{
857 route_map_master.add_hook = func;
858}
859
860void
861route_map_delete_hook (void (*func) (char *))
862{
863 route_map_master.delete_hook = func;
864}
865
866void
867route_map_event_hook (void (*func) (route_map_event_t, char *))
868{
869 route_map_master.event_hook = func;
870}
871
872void
873route_map_init ()
874{
875 /* Make vector for match and set. */
876 route_match_vec = vector_init (1);
877 route_set_vec = vector_init (1);
878}
879
880/* VTY related functions. */
881DEFUN (route_map,
882 route_map_cmd,
883 "route-map WORD (deny|permit) <1-65535>",
884 "Create route-map or enter route-map command mode\n"
885 "Route map tag\n"
886 "Route map denies set operations\n"
887 "Route map permits set operations\n"
888 "Sequence to insert to/delete from existing route-map entry\n")
889{
890 int permit;
891 unsigned long pref;
892 struct route_map *map;
893 struct route_map_index *index;
894 char *endptr = NULL;
895
896 /* Permit check. */
897 if (strncmp (argv[1], "permit", strlen (argv[1])) == 0)
898 permit = RMAP_PERMIT;
899 else if (strncmp (argv[1], "deny", strlen (argv[1])) == 0)
900 permit = RMAP_DENY;
901 else
902 {
903 vty_out (vty, "the third field must be [permit|deny]%s", VTY_NEWLINE);
904 return CMD_WARNING;
905 }
906
907 /* Preference check. */
908 pref = strtoul (argv[2], &endptr, 10);
909 if (pref == ULONG_MAX || *endptr != '\0')
910 {
911 vty_out (vty, "the fourth field must be positive integer%s",
912 VTY_NEWLINE);
913 return CMD_WARNING;
914 }
915 if (pref == 0 || pref > 65535)
916 {
917 vty_out (vty, "the fourth field must be <1-65535>%s", VTY_NEWLINE);
918 return CMD_WARNING;
919 }
920
921 /* Get route map. */
922 map = route_map_get (argv[0]);
923 index = route_map_index_get (map, permit, pref);
924
925 vty->index = index;
926 vty->node = RMAP_NODE;
927 return CMD_SUCCESS;
928}
929
930DEFUN (no_route_map_all,
931 no_route_map_all_cmd,
932 "no route-map WORD",
933 NO_STR
934 "Create route-map or enter route-map command mode\n"
935 "Route map tag\n")
936{
937 struct route_map *map;
938
939 map = route_map_lookup_by_name (argv[0]);
940 if (map == NULL)
941 {
942 vty_out (vty, "%% Could not find route-map %s%s",
943 argv[0], VTY_NEWLINE);
944 return CMD_WARNING;
945 }
946
947 route_map_delete (map);
948
949 return CMD_SUCCESS;
950}
951
952DEFUN (no_route_map,
953 no_route_map_cmd,
954 "no route-map WORD (deny|permit) <1-65535>",
955 NO_STR
956 "Create route-map or enter route-map command mode\n"
957 "Route map tag\n"
958 "Route map denies set operations\n"
959 "Route map permits set operations\n"
960 "Sequence to insert to/delete from existing route-map entry\n")
961{
962 int permit;
963 unsigned long pref;
964 struct route_map *map;
965 struct route_map_index *index;
966 char *endptr = NULL;
967
968 /* Permit check. */
969 if (strncmp (argv[1], "permit", strlen (argv[1])) == 0)
970 permit = RMAP_PERMIT;
971 else if (strncmp (argv[1], "deny", strlen (argv[1])) == 0)
972 permit = RMAP_DENY;
973 else
974 {
975 vty_out (vty, "the third field must be [permit|deny]%s", VTY_NEWLINE);
976 return CMD_WARNING;
977 }
978
979 /* Preference. */
980 pref = strtoul (argv[2], &endptr, 10);
981 if (pref == ULONG_MAX || *endptr != '\0')
982 {
983 vty_out (vty, "the fourth field must be positive integer%s",
984 VTY_NEWLINE);
985 return CMD_WARNING;
986 }
987 if (pref == 0 || pref > 65535)
988 {
989 vty_out (vty, "the fourth field must be <1-65535>%s", VTY_NEWLINE);
990 return CMD_WARNING;
991 }
992
993 /* Existence check. */
994 map = route_map_lookup_by_name (argv[0]);
995 if (map == NULL)
996 {
997 vty_out (vty, "%% Could not find route-map %s%s",
998 argv[0], VTY_NEWLINE);
999 return CMD_WARNING;
1000 }
1001
1002 /* Lookup route map index. */
1003 index = route_map_index_lookup (map, permit, pref);
1004 if (index == NULL)
1005 {
1006 vty_out (vty, "%% Could not find route-map entry %s %s%s",
1007 argv[0], argv[2], VTY_NEWLINE);
1008 return CMD_WARNING;
1009 }
1010
1011 /* Delete index from route map. */
1012 route_map_index_delete (index, 1);
1013
1014 /* If this route rule is the last one, delete route map itself. */
1015 if (route_map_empty (map))
1016 route_map_delete (map);
1017
1018 return CMD_SUCCESS;
1019}
1020
1021DEFUN (rmap_onmatch_next,
1022 rmap_onmatch_next_cmd,
1023 "on-match next",
1024 "Exit policy on matches\n"
1025 "Next clause\n")
1026{
1027 struct route_map_index *index;
1028
1029 index = vty->index;
1030
1031 if (index)
1032 index->exitpolicy = RMAP_NEXT;
1033
1034 return CMD_SUCCESS;
1035}
1036
1037DEFUN (no_rmap_onmatch_next,
1038 no_rmap_onmatch_next_cmd,
1039 "no on-match next",
1040 NO_STR
1041 "Exit policy on matches\n"
1042 "Next clause\n")
1043{
1044 struct route_map_index *index;
1045
1046 index = vty->index;
1047
1048 if (index)
1049 index->exitpolicy = RMAP_EXIT;
1050
1051 return CMD_SUCCESS;
1052}
1053
1054DEFUN (rmap_onmatch_goto,
1055 rmap_onmatch_goto_cmd,
1056 "on-match goto <1-65535>",
1057 "Exit policy on matches\n"
1058 "Goto Clause number\n"
1059 "Number\n")
1060{
1061 struct route_map_index *index;
1062 int d = 0;
1063
1064 if (argv[0])
1065 d = atoi(argv[0]);
1066
1067 index = vty->index;
1068 if (index)
1069 {
1070 if (d <= index->pref)
1071 {
1072 /* Can't allow you to do that, Dave */
1073 vty_out (vty, "can't jump backwards in route-maps%s",
1074 VTY_NEWLINE);
1075 return CMD_WARNING;
1076 }
1077 else
1078 {
1079 index->exitpolicy = RMAP_GOTO;
1080 index->nextpref = d;
1081 }
1082 }
1083 return CMD_SUCCESS;
1084}
1085
1086DEFUN (no_rmap_onmatch_goto,
1087 no_rmap_onmatch_goto_cmd,
1088 "no on-match goto",
1089 NO_STR
1090 "Exit policy on matches\n"
paulfee0f4c2004-09-13 05:12:46 +00001091 "Goto Clause number\n")
paul718e3742002-12-13 20:15:29 +00001092{
1093 struct route_map_index *index;
1094
1095 index = vty->index;
1096
1097 if (index)
1098 index->exitpolicy = RMAP_EXIT;
1099
1100 return CMD_SUCCESS;
1101}
1102
paul5510e832004-07-09 14:00:01 +00001103/* Cisco/GNU Zebra compatible ALIASes for on-match next */
1104ALIAS (rmap_onmatch_goto,
1105 rmap_continue_cmd,
1106 "continue",
1107 "Continue on a different entry within the route-map\n")
1108
1109ALIAS (no_rmap_onmatch_goto,
1110 no_rmap_continue_cmd,
1111 "no continue",
1112 NO_STR
1113 "Continue on a different entry within the route-map\n")
1114
1115/* GNU Zebra compatible */
1116ALIAS (rmap_onmatch_goto,
1117 rmap_continue_seq_cmd,
1118 "continue <1-65535>",
1119 "Continue on a different entry within the route-map\n"
1120 "Route-map entry sequence number\n")
1121
1122ALIAS (no_rmap_onmatch_goto,
1123 no_rmap_continue_seq,
1124 "no continue <1-65535>",
1125 NO_STR
1126 "Continue on a different entry within the route-map\n"
1127 "Route-map entry sequence number\n")
1128
1129DEFUN (rmap_show,
1130 rmap_show_cmd,
1131 "show route-map",
1132 SHOW_STR
1133 "route-map information\n")
1134{
1135 return vty_show_route_map (vty, NULL);
1136}
1137
1138DEFUN (rmap_show_name,
1139 rmap_show_name_cmd,
1140 "show route-map WORD",
1141 SHOW_STR
1142 "route-map information\n"
1143 "route-map name\n")
1144{
1145 return vty_show_route_map (vty, argv[0]);
1146}
1147
paulfee0f4c2004-09-13 05:12:46 +00001148ALIAS (rmap_onmatch_goto,
1149 rmap_continue_index_cmd,
1150 "continue <1-65536>",
1151 "Exit policy on matches\n"
1152 "Goto Clause number\n")
1153
1154DEFUN (rmap_call,
1155 rmap_call_cmd,
1156 "call WORD",
1157 "Jump to another Route-Map after match+set\n"
1158 "Target route-map name\n")
1159{
1160 struct route_map_index *index;
1161
1162 index = vty->index;
1163 if (index)
1164 {
1165 if (index->nextrm)
1166 free (index->nextrm);
1167 index->nextrm = strdup (argv[0]);
1168 }
1169 return CMD_SUCCESS;
1170}
1171
1172DEFUN (no_rmap_call,
1173 no_rmap_call_cmd,
1174 "no call",
1175 NO_STR
1176 "Jump to another Route-Map after match+set\n")
1177{
1178 struct route_map_index *index;
1179
1180 index = vty->index;
1181
1182 if (index->nextrm)
1183 {
1184 free (index->nextrm);
1185 index->nextrm = NULL;
1186 }
1187
1188 return CMD_SUCCESS;
1189}
1190
paul718e3742002-12-13 20:15:29 +00001191/* Configuration write function. */
1192int
1193route_map_config_write (struct vty *vty)
1194{
1195 struct route_map *map;
1196 struct route_map_index *index;
1197 struct route_map_rule *rule;
1198 int first = 1;
1199 int write = 0;
1200
1201 for (map = route_map_master.head; map; map = map->next)
1202 for (index = map->head; index; index = index->next)
1203 {
1204 if (!first)
1205 vty_out (vty, "!%s", VTY_NEWLINE);
1206 else
1207 first = 0;
1208
1209 vty_out (vty, "route-map %s %s %d%s",
1210 map->name,
1211 route_map_type_str (index->type),
1212 index->pref, VTY_NEWLINE);
1213
1214 for (rule = index->match_list.head; rule; rule = rule->next)
1215 vty_out (vty, " match %s %s%s", rule->cmd->str,
1216 rule->rule_str ? rule->rule_str : "",
1217 VTY_NEWLINE);
1218
1219 for (rule = index->set_list.head; rule; rule = rule->next)
1220 vty_out (vty, " set %s %s%s", rule->cmd->str,
1221 rule->rule_str ? rule->rule_str : "",
1222 VTY_NEWLINE);
paulfee0f4c2004-09-13 05:12:46 +00001223 if (index->nextrm)
1224 vty_out (vty, " call %s%s", index->nextrm, VTY_NEWLINE);
paul718e3742002-12-13 20:15:29 +00001225 if (index->exitpolicy == RMAP_GOTO)
paulfee0f4c2004-09-13 05:12:46 +00001226 vty_out (vty, " on-match goto %d%s", index->nextpref, VTY_NEWLINE);
paul718e3742002-12-13 20:15:29 +00001227 if (index->exitpolicy == RMAP_NEXT)
1228 vty_out (vty," on-match next%s", VTY_NEWLINE);
1229
1230 write++;
1231 }
1232 return write;
1233}
1234
1235/* Route map node structure. */
1236struct cmd_node rmap_node =
1237{
1238 RMAP_NODE,
1239 "%s(config-route-map)# ",
1240 1
1241};
1242
1243/* Initialization of route map vector. */
1244void
1245route_map_init_vty ()
1246{
1247 /* Install route map top node. */
1248 install_node (&rmap_node, route_map_config_write);
1249
1250 /* Install route map commands. */
1251 install_default (RMAP_NODE);
1252 install_element (CONFIG_NODE, &route_map_cmd);
1253 install_element (CONFIG_NODE, &no_route_map_cmd);
1254 install_element (CONFIG_NODE, &no_route_map_all_cmd);
1255
1256 /* Install the on-match stuff */
1257 install_element (RMAP_NODE, &route_map_cmd);
1258 install_element (RMAP_NODE, &rmap_onmatch_next_cmd);
1259 install_element (RMAP_NODE, &no_rmap_onmatch_next_cmd);
1260 install_element (RMAP_NODE, &rmap_onmatch_goto_cmd);
1261 install_element (RMAP_NODE, &no_rmap_onmatch_goto_cmd);
paulfee0f4c2004-09-13 05:12:46 +00001262
1263 /* Install the continue stuff (ALIAS of on-match). */
1264 install_element (RMAP_NODE, &rmap_continue_cmd);
1265 install_element (RMAP_NODE, &no_rmap_continue_cmd);
1266 install_element (RMAP_NODE, &rmap_continue_index_cmd);
1267
1268 /* Install the call stuff. */
1269 install_element (RMAP_NODE, &rmap_call_cmd);
1270 install_element (RMAP_NODE, &no_rmap_call_cmd);
1271
paul5510e832004-07-09 14:00:01 +00001272 /* Install show command */
1273 install_element (ENABLE_NODE, &rmap_show_cmd);
1274 install_element (ENABLE_NODE, &rmap_show_name_cmd);
paul718e3742002-12-13 20:15:29 +00001275}