blob: 0a4eeb994e6e18eee5bf01f1f71a77ff19a9bec0 [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"
pauld4f09602005-05-23 12:43:34 +000029#include "vty.h"
paulfee0f4c2004-09-13 05:12:46 +000030#include "log.h"
paul718e3742002-12-13 20:15:29 +000031
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
paul9035efa2004-10-10 11:56:56 +000061 void (*add_hook) (const char *);
62 void (*delete_hook) (const char *);
63 void (*event_hook) (route_map_event_t, const char *);
paul718e3742002-12-13 20:15:29 +000064};
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 *
paul9035efa2004-10-10 11:56:56 +000079route_map_new (const char *name)
paul718e3742002-12-13 20:15:29 +000080{
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 *
paul9035efa2004-10-10 11:56:56 +000090route_map_add (const char *name)
paul718e3742002-12-13 20:15:29 +000091{
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 *
paul9035efa2004-10-10 11:56:56 +0000151route_map_lookup_by_name (const char *name)
paul718e3742002-12-13 20:15:29 +0000152{
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. */
paul8cc41982005-05-06 21:25:49 +0000163static struct route_map *
paul9035efa2004-10-10 11:56:56 +0000164route_map_get (const char *name)
paul718e3742002-12-13 20:15:29 +0000165{
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. */
paul8cc41982005-05-06 21:25:49 +0000175static const char *
paul718e3742002-12-13 20:15:29 +0000176route_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
paul8cc41982005-05-06 21:25:49 +0000192static int
paul718e3742002-12-13 20:15:29 +0000193route_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
paul5510e832004-07-09 14:00:01 +0000201/* show route-map */
202static void
203vty_show_route_map_entry (struct vty *vty, struct route_map *map)
paul718e3742002-12-13 20:15:29 +0000204{
paul718e3742002-12-13 20:15:29 +0000205 struct route_map_index *index;
206 struct route_map_rule *rule;
207
paul5510e832004-07-09 14:00:01 +0000208 for (index = map->head; index; index = index->next)
209 {
210 vty_out (vty, "route-map %s, %s, sequence %d%s",
211 map->name, route_map_type_str (index->type),
212 index->pref, VTY_NEWLINE);
hasso5bb4c192005-04-09 13:27:50 +0000213
214 /* Description */
215 if (index->description)
216 vty_out (vty, " Description:%s %s%s", VTY_NEWLINE,
217 index->description, VTY_NEWLINE);
paul5510e832004-07-09 14:00:01 +0000218
219 /* Match clauses */
220 vty_out (vty, " Match clauses:%s", VTY_NEWLINE);
221 for (rule = index->match_list.head; rule; rule = rule->next)
222 vty_out (vty, " %s %s%s",
223 rule->cmd->str, rule->rule_str, VTY_NEWLINE);
224
225 vty_out (vty, " Set clauses:%s", VTY_NEWLINE);
226 for (rule = index->set_list.head; rule; rule = rule->next)
227 vty_out (vty, " %s %s%s",
228 rule->cmd->str, rule->rule_str, VTY_NEWLINE);
229
230 vty_out (vty, " Action:%s", VTY_NEWLINE);
paulfee0f4c2004-09-13 05:12:46 +0000231
232 if (index->nextrm)
233 vty_out (vty, " Call %s%s", index->nextrm, VTY_NEWLINE);
234 else if (index->exitpolicy == RMAP_GOTO)
paul5510e832004-07-09 14:00:01 +0000235 vty_out (vty, " Goto %d%s", index->nextpref, VTY_NEWLINE);
paul5510e832004-07-09 14:00:01 +0000236 else if (index->exitpolicy == RMAP_NEXT)
237 {
238 vty_out (vty, " Goto next, (entry ");
239 if (index->next)
240 vty_out (vty, " %d)%s", index->next->pref, VTY_NEWLINE);
241 else
242 vty_out (vty, " undefined)%s", VTY_NEWLINE);
243 }
244 else if (index->exitpolicy == RMAP_EXIT)
245 vty_out (vty, " Exit routemap%s", VTY_NEWLINE);
246 }
paul718e3742002-12-13 20:15:29 +0000247}
248
paul8cc41982005-05-06 21:25:49 +0000249static int
paul9035efa2004-10-10 11:56:56 +0000250vty_show_route_map (struct vty *vty, const char *name)
paul5510e832004-07-09 14:00:01 +0000251{
252 struct route_map *map;
253
254 if (name)
255 {
256 map = route_map_lookup_by_name (name);
257
258 if (map)
259 {
260 vty_show_route_map_entry (vty, map);
261 return CMD_SUCCESS;
262 }
263 else
264 {
265 vty_out (vty, "%%route-map %s not found%s", name, VTY_NEWLINE);
266 return CMD_WARNING;
267 }
268 }
269 return CMD_SUCCESS;
270}
271
272
paul718e3742002-12-13 20:15:29 +0000273/* New route map allocation. Please note route map's name must be
274 specified. */
paul8cc41982005-05-06 21:25:49 +0000275static struct route_map_index *
276route_map_index_new (void)
paul718e3742002-12-13 20:15:29 +0000277{
278 struct route_map_index *new;
279
280 new = XCALLOC (MTYPE_ROUTE_MAP_INDEX, sizeof (struct route_map_index));
281 new->exitpolicy = RMAP_EXIT; /* Default to Cisco-style */
282 return new;
283}
284
285/* Free route map index. */
286static void
287route_map_index_delete (struct route_map_index *index, int notify)
288{
289 struct route_map_rule *rule;
290
291 /* Free route match. */
292 while ((rule = index->match_list.head) != NULL)
293 route_map_rule_delete (&index->match_list, rule);
294
295 /* Free route set. */
296 while ((rule = index->set_list.head) != NULL)
297 route_map_rule_delete (&index->set_list, rule);
298
299 /* Remove index from route map list. */
300 if (index->next)
301 index->next->prev = index->prev;
302 else
303 index->map->tail = index->prev;
304
305 if (index->prev)
306 index->prev->next = index->next;
307 else
308 index->map->head = index->next;
309
paulfee0f4c2004-09-13 05:12:46 +0000310 /* Free 'char *nextrm' if not NULL */
311 if (index->nextrm)
312 free (index->nextrm);
313
paul718e3742002-12-13 20:15:29 +0000314 /* Execute event hook. */
315 if (route_map_master.event_hook && notify)
316 (*route_map_master.event_hook) (RMAP_EVENT_INDEX_DELETED,
317 index->map->name);
318
319 XFREE (MTYPE_ROUTE_MAP_INDEX, index);
320}
321
322/* Lookup index from route map. */
paul8cc41982005-05-06 21:25:49 +0000323static struct route_map_index *
paul718e3742002-12-13 20:15:29 +0000324route_map_index_lookup (struct route_map *map, enum route_map_type type,
325 int pref)
326{
327 struct route_map_index *index;
328
329 for (index = map->head; index; index = index->next)
330 if ((index->type == type || type == RMAP_ANY)
331 && index->pref == pref)
332 return index;
333 return NULL;
334}
335
336/* Add new index to route map. */
paul8cc41982005-05-06 21:25:49 +0000337static struct route_map_index *
paul718e3742002-12-13 20:15:29 +0000338route_map_index_add (struct route_map *map, enum route_map_type type,
339 int pref)
340{
341 struct route_map_index *index;
342 struct route_map_index *point;
343
344 /* Allocate new route map inex. */
345 index = route_map_index_new ();
346 index->map = map;
347 index->type = type;
348 index->pref = pref;
349
350 /* Compare preference. */
351 for (point = map->head; point; point = point->next)
352 if (point->pref >= pref)
353 break;
354
355 if (map->head == NULL)
356 {
357 map->head = map->tail = index;
358 }
359 else if (point == NULL)
360 {
361 index->prev = map->tail;
362 map->tail->next = index;
363 map->tail = index;
364 }
365 else if (point == map->head)
366 {
367 index->next = map->head;
368 map->head->prev = index;
369 map->head = index;
370 }
371 else
372 {
373 index->next = point;
374 index->prev = point->prev;
375 if (point->prev)
376 point->prev->next = index;
377 point->prev = index;
378 }
379
380 /* Execute event hook. */
381 if (route_map_master.event_hook)
382 (*route_map_master.event_hook) (RMAP_EVENT_INDEX_ADDED,
383 map->name);
384
385 return index;
386}
387
388/* Get route map index. */
paul8cc41982005-05-06 21:25:49 +0000389static struct route_map_index *
paul718e3742002-12-13 20:15:29 +0000390route_map_index_get (struct route_map *map, enum route_map_type type,
391 int pref)
392{
393 struct route_map_index *index;
394
395 index = route_map_index_lookup (map, RMAP_ANY, pref);
396 if (index && index->type != type)
397 {
398 /* Delete index from route map. */
399 route_map_index_delete (index, 1);
400 index = NULL;
401 }
402 if (index == NULL)
403 index = route_map_index_add (map, type, pref);
404 return index;
405}
406
407/* New route map rule */
paul8cc41982005-05-06 21:25:49 +0000408static struct route_map_rule *
409route_map_rule_new (void)
paul718e3742002-12-13 20:15:29 +0000410{
411 struct route_map_rule *new;
412
413 new = XCALLOC (MTYPE_ROUTE_MAP_RULE, sizeof (struct route_map_rule));
414 return new;
415}
416
417/* Install rule command to the match list. */
418void
419route_map_install_match (struct route_map_rule_cmd *cmd)
420{
421 vector_set (route_match_vec, cmd);
422}
423
424/* Install rule command to the set list. */
425void
426route_map_install_set (struct route_map_rule_cmd *cmd)
427{
428 vector_set (route_set_vec, cmd);
429}
430
431/* Lookup rule command from match list. */
paul8cc41982005-05-06 21:25:49 +0000432static struct route_map_rule_cmd *
hasso27a43a82004-10-08 06:29:12 +0000433route_map_lookup_match (const char *name)
paul718e3742002-12-13 20:15:29 +0000434{
hasso8c328f12004-10-05 21:01:23 +0000435 unsigned int i;
paul718e3742002-12-13 20:15:29 +0000436 struct route_map_rule_cmd *rule;
437
paul55468c82005-03-14 20:19:01 +0000438 for (i = 0; i < vector_active (route_match_vec); i++)
paul718e3742002-12-13 20:15:29 +0000439 if ((rule = vector_slot (route_match_vec, i)) != NULL)
440 if (strcmp (rule->str, name) == 0)
441 return rule;
442 return NULL;
443}
444
445/* Lookup rule command from set list. */
paul8cc41982005-05-06 21:25:49 +0000446static struct route_map_rule_cmd *
hasso27a43a82004-10-08 06:29:12 +0000447route_map_lookup_set (const char *name)
paul718e3742002-12-13 20:15:29 +0000448{
hasso8c328f12004-10-05 21:01:23 +0000449 unsigned int i;
paul718e3742002-12-13 20:15:29 +0000450 struct route_map_rule_cmd *rule;
451
paul55468c82005-03-14 20:19:01 +0000452 for (i = 0; i < vector_active (route_set_vec); i++)
paul718e3742002-12-13 20:15:29 +0000453 if ((rule = vector_slot (route_set_vec, i)) != NULL)
454 if (strcmp (rule->str, name) == 0)
455 return rule;
456 return NULL;
457}
458
459/* Add match and set rule to rule list. */
460static void
461route_map_rule_add (struct route_map_rule_list *list,
462 struct route_map_rule *rule)
463{
464 rule->next = NULL;
465 rule->prev = list->tail;
466 if (list->tail)
467 list->tail->next = rule;
468 else
469 list->head = rule;
470 list->tail = rule;
471}
472
473/* Delete rule from rule list. */
474static void
475route_map_rule_delete (struct route_map_rule_list *list,
476 struct route_map_rule *rule)
477{
478 if (rule->cmd->func_free)
479 (*rule->cmd->func_free) (rule->value);
480
481 if (rule->rule_str)
482 XFREE (MTYPE_ROUTE_MAP_RULE_STR, rule->rule_str);
483
484 if (rule->next)
485 rule->next->prev = rule->prev;
486 else
487 list->tail = rule->prev;
488 if (rule->prev)
489 rule->prev->next = rule->next;
490 else
491 list->head = rule->next;
492
493 XFREE (MTYPE_ROUTE_MAP_RULE, rule);
494}
495
496/* strcmp wrapper function which don't crush even argument is NULL. */
paul8cc41982005-05-06 21:25:49 +0000497static int
paulc9eca012004-10-11 11:28:44 +0000498rulecmp (const char *dst, const char *src)
paul718e3742002-12-13 20:15:29 +0000499{
500 if (dst == NULL)
501 {
502 if (src == NULL)
503 return 0;
504 else
505 return 1;
506 }
507 else
508 {
509 if (src == NULL)
510 return 1;
511 else
512 return strcmp (dst, src);
513 }
514 return 1;
515}
516
517/* Add match statement to route map. */
518int
hasso27a43a82004-10-08 06:29:12 +0000519route_map_add_match (struct route_map_index *index, const char *match_name,
paulc9eca012004-10-11 11:28:44 +0000520 const char *match_arg)
paul718e3742002-12-13 20:15:29 +0000521{
522 struct route_map_rule *rule;
523 struct route_map_rule *next;
524 struct route_map_rule_cmd *cmd;
525 void *compile;
526 int replaced = 0;
527
528 /* First lookup rule for add match statement. */
529 cmd = route_map_lookup_match (match_name);
530 if (cmd == NULL)
531 return RMAP_RULE_MISSING;
532
533 /* Next call compile function for this match statement. */
534 if (cmd->func_compile)
535 {
536 compile= (*cmd->func_compile)(match_arg);
537 if (compile == NULL)
538 return RMAP_COMPILE_ERROR;
539 }
540 else
541 compile = NULL;
542
543 /* If argument is completely same ignore it. */
544 for (rule = index->match_list.head; rule; rule = next)
545 {
546 next = rule->next;
547 if (rule->cmd == cmd)
548 {
549 route_map_rule_delete (&index->match_list, rule);
550 replaced = 1;
551 }
552 }
553
554 /* Add new route map match rule. */
555 rule = route_map_rule_new ();
556 rule->cmd = cmd;
557 rule->value = compile;
558 if (match_arg)
559 rule->rule_str = XSTRDUP (MTYPE_ROUTE_MAP_RULE_STR, match_arg);
560 else
561 rule->rule_str = NULL;
562
563 /* Add new route match rule to linked list. */
564 route_map_rule_add (&index->match_list, rule);
565
566 /* Execute event hook. */
567 if (route_map_master.event_hook)
568 (*route_map_master.event_hook) (replaced ?
569 RMAP_EVENT_MATCH_REPLACED:
570 RMAP_EVENT_MATCH_ADDED,
571 index->map->name);
572
573 return 0;
574}
575
576/* Delete specified route match rule. */
577int
hasso27a43a82004-10-08 06:29:12 +0000578route_map_delete_match (struct route_map_index *index, const char *match_name,
paulc9eca012004-10-11 11:28:44 +0000579 const char *match_arg)
paul718e3742002-12-13 20:15:29 +0000580{
581 struct route_map_rule *rule;
582 struct route_map_rule_cmd *cmd;
583
584 cmd = route_map_lookup_match (match_name);
585 if (cmd == NULL)
586 return 1;
587
588 for (rule = index->match_list.head; rule; rule = rule->next)
589 if (rule->cmd == cmd &&
590 (rulecmp (rule->rule_str, match_arg) == 0 || match_arg == NULL))
591 {
592 route_map_rule_delete (&index->match_list, rule);
593 /* Execute event hook. */
594 if (route_map_master.event_hook)
595 (*route_map_master.event_hook) (RMAP_EVENT_MATCH_DELETED,
596 index->map->name);
597 return 0;
598 }
599 /* Can't find matched rule. */
600 return 1;
601}
602
603/* Add route-map set statement to the route map. */
604int
hasso27a43a82004-10-08 06:29:12 +0000605route_map_add_set (struct route_map_index *index, const char *set_name,
paulc9eca012004-10-11 11:28:44 +0000606 const char *set_arg)
paul718e3742002-12-13 20:15:29 +0000607{
608 struct route_map_rule *rule;
609 struct route_map_rule *next;
610 struct route_map_rule_cmd *cmd;
611 void *compile;
612 int replaced = 0;
613
614 cmd = route_map_lookup_set (set_name);
615 if (cmd == NULL)
616 return RMAP_RULE_MISSING;
617
618 /* Next call compile function for this match statement. */
619 if (cmd->func_compile)
620 {
621 compile= (*cmd->func_compile)(set_arg);
622 if (compile == NULL)
623 return RMAP_COMPILE_ERROR;
624 }
625 else
626 compile = NULL;
627
628 /* Add by WJL. if old set command of same kind exist, delete it first
629 to ensure only one set command of same kind exist under a
630 route_map_index. */
631 for (rule = index->set_list.head; rule; rule = next)
632 {
633 next = rule->next;
634 if (rule->cmd == cmd)
635 {
636 route_map_rule_delete (&index->set_list, rule);
637 replaced = 1;
638 }
639 }
640
641 /* Add new route map match rule. */
642 rule = route_map_rule_new ();
643 rule->cmd = cmd;
644 rule->value = compile;
645 if (set_arg)
646 rule->rule_str = XSTRDUP (MTYPE_ROUTE_MAP_RULE_STR, set_arg);
647 else
648 rule->rule_str = NULL;
649
650 /* Add new route match rule to linked list. */
651 route_map_rule_add (&index->set_list, rule);
652
653 /* Execute event hook. */
654 if (route_map_master.event_hook)
655 (*route_map_master.event_hook) (replaced ?
656 RMAP_EVENT_SET_REPLACED:
657 RMAP_EVENT_SET_ADDED,
658 index->map->name);
659 return 0;
660}
661
662/* Delete route map set rule. */
663int
hasso27a43a82004-10-08 06:29:12 +0000664route_map_delete_set (struct route_map_index *index, const char *set_name,
paulc9eca012004-10-11 11:28:44 +0000665 const char *set_arg)
paul718e3742002-12-13 20:15:29 +0000666{
667 struct route_map_rule *rule;
668 struct route_map_rule_cmd *cmd;
669
670 cmd = route_map_lookup_set (set_name);
671 if (cmd == NULL)
672 return 1;
673
674 for (rule = index->set_list.head; rule; rule = rule->next)
675 if ((rule->cmd == cmd) &&
676 (rulecmp (rule->rule_str, set_arg) == 0 || set_arg == NULL))
677 {
678 route_map_rule_delete (&index->set_list, rule);
679 /* Execute event hook. */
680 if (route_map_master.event_hook)
681 (*route_map_master.event_hook) (RMAP_EVENT_SET_DELETED,
682 index->map->name);
683 return 0;
684 }
685 /* Can't find matched rule. */
686 return 1;
687}
688
paul3bf1c912003-10-29 06:30:19 +0000689/* Apply route map's each index to the object.
690
691 The matrix for a route-map looks like this:
692 (note, this includes the description for the "NEXT"
693 and "GOTO" frobs now
paul718e3742002-12-13 20:15:29 +0000694
paul3bf1c912003-10-29 06:30:19 +0000695 Match | No Match
696 |
697 permit action | cont
698 |
699 ------------------+---------------
700 |
701 deny deny | cont
702 |
703
paulfee0f4c2004-09-13 05:12:46 +0000704 action)
705 -Apply Set statements, accept route
706 -If Call statement is present jump to the specified route-map, if it
707 denies the route we finish.
708 -If NEXT is specified, goto NEXT statement
709 -If GOTO is specified, goto the first clause where pref > nextpref
710 -If nothing is specified, do as Cisco and finish
711 deny)
712 -Route is denied by route-map.
713 cont)
714 -Goto Next index
paul3bf1c912003-10-29 06:30:19 +0000715
716 If we get no matches after we've processed all updates, then the route
717 is dropped too.
718
paulfee0f4c2004-09-13 05:12:46 +0000719 Some notes on the new "CALL", "NEXT" and "GOTO"
720 call WORD - If this clause is matched, then the set statements
721 are executed and then we jump to route-map 'WORD'. If
722 this route-map denies the route, we finish, in other case we
723 do whatever the exit policy (EXIT, NEXT or GOTO) tells.
paul3bf1c912003-10-29 06:30:19 +0000724 on-match next - If this clause is matched, then the set statements
725 are executed and then we drop through to the next clause
726 on-match goto n - If this clause is matched, then the set statments
727 are executed and then we goto the nth clause, or the
728 first clause greater than this. In order to ensure
729 route-maps *always* exit, you cannot jump backwards.
730 Sorry ;)
731
732 We need to make sure our route-map processing matches the above
733*/
paul718e3742002-12-13 20:15:29 +0000734
paul8cc41982005-05-06 21:25:49 +0000735static route_map_result_t
paul3bf1c912003-10-29 06:30:19 +0000736route_map_apply_match (struct route_map_rule_list *match_list,
737 struct prefix *prefix, route_map_object_t type,
738 void *object)
739{
740 route_map_result_t ret = RMAP_NOMATCH;
741 struct route_map_rule *match;
paul718e3742002-12-13 20:15:29 +0000742
paul3bf1c912003-10-29 06:30:19 +0000743
744 /* Check all match rule and if there is no match rule, go to the
745 set statement. */
746 if (!match_list->head)
747 ret = RMAP_MATCH;
748 else
paul718e3742002-12-13 20:15:29 +0000749 {
paul3bf1c912003-10-29 06:30:19 +0000750 for (match = match_list->head; match; match = match->next)
751 {
752 /* Try each match statement in turn, If any do not return
753 RMAP_MATCH, return, otherwise continue on to next match
754 statement. All match statements must match for end-result
755 to be a match. */
756 ret = (*match->cmd->func_apply) (match->value, prefix,
757 type, object);
758 if (ret != RMAP_MATCH)
759 return ret;
760 }
paul718e3742002-12-13 20:15:29 +0000761 }
paul3bf1c912003-10-29 06:30:19 +0000762 return ret;
paul718e3742002-12-13 20:15:29 +0000763}
764
765/* Apply route map to the object. */
766route_map_result_t
paul3bf1c912003-10-29 06:30:19 +0000767route_map_apply (struct route_map *map, struct prefix *prefix,
768 route_map_object_t type, void *object)
paul718e3742002-12-13 20:15:29 +0000769{
paulfee0f4c2004-09-13 05:12:46 +0000770 static int recursion = 0;
paul718e3742002-12-13 20:15:29 +0000771 int ret = 0;
772 struct route_map_index *index;
paul3bf1c912003-10-29 06:30:19 +0000773 struct route_map_rule *set;
paul718e3742002-12-13 20:15:29 +0000774
paulfee0f4c2004-09-13 05:12:46 +0000775 if (recursion > RMAP_RECURSION_LIMIT)
776 {
777 zlog (NULL, LOG_WARNING,
778 "route-map recursion limit (%d) reached, discarding route",
779 RMAP_RECURSION_LIMIT);
780 recursion = 0;
781 return RMAP_DENYMATCH;
782 }
783
paul718e3742002-12-13 20:15:29 +0000784 if (map == NULL)
785 return RMAP_DENYMATCH;
786
787 for (index = map->head; index; index = index->next)
788 {
paul3bf1c912003-10-29 06:30:19 +0000789 /* Apply this index. */
790 ret = route_map_apply_match (&index->match_list, prefix, type, object);
paul718e3742002-12-13 20:15:29 +0000791
paul3bf1c912003-10-29 06:30:19 +0000792 /* Now we apply the matrix from above */
793 if (ret == RMAP_NOMATCH)
794 /* 'cont' from matrix - continue to next route-map sequence */
795 continue;
796 else if (ret == RMAP_MATCH)
797 {
798 if (index->type == RMAP_PERMIT)
799 /* 'action' */
800 {
801 /* permit+match must execute sets */
802 for (set = index->set_list.head; set; set = set->next)
803 ret = (*set->cmd->func_apply) (set->value, prefix,
804 type, object);
paulfee0f4c2004-09-13 05:12:46 +0000805
806 /* Call another route-map if available */
807 if (index->nextrm)
808 {
809 struct route_map *nextrm =
810 route_map_lookup_by_name (index->nextrm);
811
812 if (nextrm) /* Target route-map found, jump to it */
813 {
814 recursion++;
815 ret = route_map_apply (nextrm, prefix, type, object);
816 recursion--;
817 }
818
819 /* If nextrm returned 'deny', finish. */
820 if (ret == RMAP_DENYMATCH)
821 return ret;
822 }
823
paul3bf1c912003-10-29 06:30:19 +0000824 switch (index->exitpolicy)
825 {
826 case RMAP_EXIT:
827 return ret;
828 case RMAP_NEXT:
829 continue;
830 case RMAP_GOTO:
831 {
832 /* Find the next clause to jump to */
833 struct route_map_index *next = index->next;
paulfee0f4c2004-09-13 05:12:46 +0000834 int nextpref = index->nextpref;
paul718e3742002-12-13 20:15:29 +0000835
paulfee0f4c2004-09-13 05:12:46 +0000836 while (next && next->pref < nextpref)
paul3bf1c912003-10-29 06:30:19 +0000837 {
838 index = next;
839 next = next->next;
840 }
841 if (next == NULL)
842 {
843 /* No clauses match! */
844 return ret;
845 }
846 }
847 }
848 }
849 else if (index->type == RMAP_DENY)
850 /* 'deny' */
851 {
paul3bf1c912003-10-29 06:30:19 +0000852 return RMAP_DENYMATCH;
853 }
854 }
paul718e3742002-12-13 20:15:29 +0000855 }
856 /* Finally route-map does not match at all. */
857 return RMAP_DENYMATCH;
858}
859
860void
paul9035efa2004-10-10 11:56:56 +0000861route_map_add_hook (void (*func) (const char *))
paul718e3742002-12-13 20:15:29 +0000862{
863 route_map_master.add_hook = func;
864}
865
866void
paul9035efa2004-10-10 11:56:56 +0000867route_map_delete_hook (void (*func) (const char *))
paul718e3742002-12-13 20:15:29 +0000868{
869 route_map_master.delete_hook = func;
870}
871
872void
paul9035efa2004-10-10 11:56:56 +0000873route_map_event_hook (void (*func) (route_map_event_t, const char *))
paul718e3742002-12-13 20:15:29 +0000874{
875 route_map_master.event_hook = func;
876}
877
878void
paul8cc41982005-05-06 21:25:49 +0000879route_map_init (void)
paul718e3742002-12-13 20:15:29 +0000880{
881 /* Make vector for match and set. */
882 route_match_vec = vector_init (1);
883 route_set_vec = vector_init (1);
884}
885
886/* VTY related functions. */
887DEFUN (route_map,
888 route_map_cmd,
889 "route-map WORD (deny|permit) <1-65535>",
890 "Create route-map or enter route-map command mode\n"
891 "Route map tag\n"
892 "Route map denies set operations\n"
893 "Route map permits set operations\n"
894 "Sequence to insert to/delete from existing route-map entry\n")
895{
896 int permit;
897 unsigned long pref;
898 struct route_map *map;
899 struct route_map_index *index;
900 char *endptr = NULL;
901
902 /* Permit check. */
903 if (strncmp (argv[1], "permit", strlen (argv[1])) == 0)
904 permit = RMAP_PERMIT;
905 else if (strncmp (argv[1], "deny", strlen (argv[1])) == 0)
906 permit = RMAP_DENY;
907 else
908 {
909 vty_out (vty, "the third field must be [permit|deny]%s", VTY_NEWLINE);
910 return CMD_WARNING;
911 }
912
913 /* Preference check. */
914 pref = strtoul (argv[2], &endptr, 10);
915 if (pref == ULONG_MAX || *endptr != '\0')
916 {
917 vty_out (vty, "the fourth field must be positive integer%s",
918 VTY_NEWLINE);
919 return CMD_WARNING;
920 }
921 if (pref == 0 || pref > 65535)
922 {
923 vty_out (vty, "the fourth field must be <1-65535>%s", VTY_NEWLINE);
924 return CMD_WARNING;
925 }
926
927 /* Get route map. */
928 map = route_map_get (argv[0]);
929 index = route_map_index_get (map, permit, pref);
930
931 vty->index = index;
932 vty->node = RMAP_NODE;
933 return CMD_SUCCESS;
934}
935
936DEFUN (no_route_map_all,
937 no_route_map_all_cmd,
938 "no route-map WORD",
939 NO_STR
940 "Create route-map or enter route-map command mode\n"
941 "Route map tag\n")
942{
943 struct route_map *map;
944
945 map = route_map_lookup_by_name (argv[0]);
946 if (map == NULL)
947 {
948 vty_out (vty, "%% Could not find route-map %s%s",
949 argv[0], VTY_NEWLINE);
950 return CMD_WARNING;
951 }
952
953 route_map_delete (map);
954
955 return CMD_SUCCESS;
956}
957
958DEFUN (no_route_map,
959 no_route_map_cmd,
960 "no route-map WORD (deny|permit) <1-65535>",
961 NO_STR
962 "Create route-map or enter route-map command mode\n"
963 "Route map tag\n"
964 "Route map denies set operations\n"
965 "Route map permits set operations\n"
966 "Sequence to insert to/delete from existing route-map entry\n")
967{
968 int permit;
969 unsigned long pref;
970 struct route_map *map;
971 struct route_map_index *index;
972 char *endptr = NULL;
973
974 /* Permit check. */
975 if (strncmp (argv[1], "permit", strlen (argv[1])) == 0)
976 permit = RMAP_PERMIT;
977 else if (strncmp (argv[1], "deny", strlen (argv[1])) == 0)
978 permit = RMAP_DENY;
979 else
980 {
981 vty_out (vty, "the third field must be [permit|deny]%s", VTY_NEWLINE);
982 return CMD_WARNING;
983 }
984
985 /* Preference. */
986 pref = strtoul (argv[2], &endptr, 10);
987 if (pref == ULONG_MAX || *endptr != '\0')
988 {
989 vty_out (vty, "the fourth field must be positive integer%s",
990 VTY_NEWLINE);
991 return CMD_WARNING;
992 }
993 if (pref == 0 || pref > 65535)
994 {
995 vty_out (vty, "the fourth field must be <1-65535>%s", VTY_NEWLINE);
996 return CMD_WARNING;
997 }
998
999 /* Existence check. */
1000 map = route_map_lookup_by_name (argv[0]);
1001 if (map == NULL)
1002 {
1003 vty_out (vty, "%% Could not find route-map %s%s",
1004 argv[0], VTY_NEWLINE);
1005 return CMD_WARNING;
1006 }
1007
1008 /* Lookup route map index. */
1009 index = route_map_index_lookup (map, permit, pref);
1010 if (index == NULL)
1011 {
1012 vty_out (vty, "%% Could not find route-map entry %s %s%s",
1013 argv[0], argv[2], VTY_NEWLINE);
1014 return CMD_WARNING;
1015 }
1016
1017 /* Delete index from route map. */
1018 route_map_index_delete (index, 1);
1019
1020 /* If this route rule is the last one, delete route map itself. */
1021 if (route_map_empty (map))
1022 route_map_delete (map);
1023
1024 return CMD_SUCCESS;
1025}
1026
1027DEFUN (rmap_onmatch_next,
1028 rmap_onmatch_next_cmd,
1029 "on-match next",
1030 "Exit policy on matches\n"
1031 "Next clause\n")
1032{
1033 struct route_map_index *index;
1034
1035 index = vty->index;
1036
1037 if (index)
1038 index->exitpolicy = RMAP_NEXT;
1039
1040 return CMD_SUCCESS;
1041}
1042
1043DEFUN (no_rmap_onmatch_next,
1044 no_rmap_onmatch_next_cmd,
1045 "no on-match next",
1046 NO_STR
1047 "Exit policy on matches\n"
1048 "Next clause\n")
1049{
1050 struct route_map_index *index;
1051
1052 index = vty->index;
1053
1054 if (index)
1055 index->exitpolicy = RMAP_EXIT;
1056
1057 return CMD_SUCCESS;
1058}
1059
1060DEFUN (rmap_onmatch_goto,
1061 rmap_onmatch_goto_cmd,
1062 "on-match goto <1-65535>",
1063 "Exit policy on matches\n"
1064 "Goto Clause number\n"
1065 "Number\n")
1066{
pauld4f09602005-05-23 12:43:34 +00001067 struct route_map_index *index = vty->index;
paul718e3742002-12-13 20:15:29 +00001068 int d = 0;
1069
paul718e3742002-12-13 20:15:29 +00001070 if (index)
1071 {
pauld4f09602005-05-23 12:43:34 +00001072 if (argc == 1 && argv[0])
1073 VTY_GET_INTEGER_RANGE("route-map index", d, argv[0], 1, 65536);
1074 else
1075 d = index->pref + 1;
1076
paul718e3742002-12-13 20:15:29 +00001077 if (d <= index->pref)
1078 {
1079 /* Can't allow you to do that, Dave */
1080 vty_out (vty, "can't jump backwards in route-maps%s",
1081 VTY_NEWLINE);
1082 return CMD_WARNING;
1083 }
1084 else
1085 {
1086 index->exitpolicy = RMAP_GOTO;
1087 index->nextpref = d;
1088 }
1089 }
1090 return CMD_SUCCESS;
1091}
1092
1093DEFUN (no_rmap_onmatch_goto,
1094 no_rmap_onmatch_goto_cmd,
1095 "no on-match goto",
1096 NO_STR
1097 "Exit policy on matches\n"
paulfee0f4c2004-09-13 05:12:46 +00001098 "Goto Clause number\n")
paul718e3742002-12-13 20:15:29 +00001099{
1100 struct route_map_index *index;
1101
1102 index = vty->index;
1103
1104 if (index)
1105 index->exitpolicy = RMAP_EXIT;
1106
1107 return CMD_SUCCESS;
1108}
1109
paul5510e832004-07-09 14:00:01 +00001110/* Cisco/GNU Zebra compatible ALIASes for on-match next */
1111ALIAS (rmap_onmatch_goto,
1112 rmap_continue_cmd,
1113 "continue",
1114 "Continue on a different entry within the route-map\n")
1115
1116ALIAS (no_rmap_onmatch_goto,
1117 no_rmap_continue_cmd,
1118 "no continue",
1119 NO_STR
1120 "Continue on a different entry within the route-map\n")
1121
1122/* GNU Zebra compatible */
1123ALIAS (rmap_onmatch_goto,
1124 rmap_continue_seq_cmd,
1125 "continue <1-65535>",
1126 "Continue on a different entry within the route-map\n"
1127 "Route-map entry sequence number\n")
1128
1129ALIAS (no_rmap_onmatch_goto,
1130 no_rmap_continue_seq,
1131 "no continue <1-65535>",
1132 NO_STR
1133 "Continue on a different entry within the route-map\n"
1134 "Route-map entry sequence number\n")
1135
1136DEFUN (rmap_show,
1137 rmap_show_cmd,
1138 "show route-map",
1139 SHOW_STR
1140 "route-map information\n")
1141{
1142 return vty_show_route_map (vty, NULL);
1143}
1144
1145DEFUN (rmap_show_name,
1146 rmap_show_name_cmd,
1147 "show route-map WORD",
1148 SHOW_STR
1149 "route-map information\n"
1150 "route-map name\n")
1151{
1152 return vty_show_route_map (vty, argv[0]);
1153}
1154
paulfee0f4c2004-09-13 05:12:46 +00001155ALIAS (rmap_onmatch_goto,
1156 rmap_continue_index_cmd,
1157 "continue <1-65536>",
1158 "Exit policy on matches\n"
1159 "Goto Clause number\n")
1160
1161DEFUN (rmap_call,
1162 rmap_call_cmd,
1163 "call WORD",
1164 "Jump to another Route-Map after match+set\n"
1165 "Target route-map name\n")
1166{
1167 struct route_map_index *index;
1168
1169 index = vty->index;
1170 if (index)
1171 {
1172 if (index->nextrm)
1173 free (index->nextrm);
1174 index->nextrm = strdup (argv[0]);
1175 }
1176 return CMD_SUCCESS;
1177}
1178
1179DEFUN (no_rmap_call,
1180 no_rmap_call_cmd,
1181 "no call",
1182 NO_STR
1183 "Jump to another Route-Map after match+set\n")
1184{
1185 struct route_map_index *index;
1186
1187 index = vty->index;
1188
1189 if (index->nextrm)
1190 {
1191 free (index->nextrm);
1192 index->nextrm = NULL;
1193 }
1194
1195 return CMD_SUCCESS;
1196}
1197
hasso4a8164e2005-04-08 14:20:18 +00001198DEFUN (rmap_description,
1199 rmap_description_cmd,
1200 "description .LINE",
1201 "Route-map comment\n"
1202 "Comment describing this route-map rule\n")
1203{
1204 struct route_map_index *index;
1205
1206 index = vty->index;
1207 if (index)
1208 {
1209 if (index->description)
1210 XFREE (MTYPE_TMP, index->description);
1211 index->description = argv_concat (argv, argc, 0);
1212 }
1213 return CMD_SUCCESS;
1214}
1215
1216DEFUN (no_rmap_description,
1217 no_rmap_description_cmd,
1218 "no description",
1219 NO_STR
1220 "Route-map comment\n")
1221{
1222 struct route_map_index *index;
1223
1224 index = vty->index;
1225 if (index)
1226 {
1227 if (index->description)
1228 XFREE (MTYPE_TMP, index->description);
1229 index->description = NULL;
1230 }
1231 return CMD_SUCCESS;
1232}
1233
paul718e3742002-12-13 20:15:29 +00001234/* Configuration write function. */
paul8cc41982005-05-06 21:25:49 +00001235static int
paul718e3742002-12-13 20:15:29 +00001236route_map_config_write (struct vty *vty)
1237{
1238 struct route_map *map;
1239 struct route_map_index *index;
1240 struct route_map_rule *rule;
1241 int first = 1;
1242 int write = 0;
1243
1244 for (map = route_map_master.head; map; map = map->next)
1245 for (index = map->head; index; index = index->next)
1246 {
1247 if (!first)
1248 vty_out (vty, "!%s", VTY_NEWLINE);
1249 else
1250 first = 0;
1251
1252 vty_out (vty, "route-map %s %s %d%s",
1253 map->name,
1254 route_map_type_str (index->type),
1255 index->pref, VTY_NEWLINE);
1256
hasso4a8164e2005-04-08 14:20:18 +00001257 if (index->description)
1258 vty_out (vty, " description %s%s", index->description, VTY_NEWLINE);
1259
paul718e3742002-12-13 20:15:29 +00001260 for (rule = index->match_list.head; rule; rule = rule->next)
1261 vty_out (vty, " match %s %s%s", rule->cmd->str,
1262 rule->rule_str ? rule->rule_str : "",
1263 VTY_NEWLINE);
1264
1265 for (rule = index->set_list.head; rule; rule = rule->next)
1266 vty_out (vty, " set %s %s%s", rule->cmd->str,
1267 rule->rule_str ? rule->rule_str : "",
1268 VTY_NEWLINE);
paulfee0f4c2004-09-13 05:12:46 +00001269 if (index->nextrm)
1270 vty_out (vty, " call %s%s", index->nextrm, VTY_NEWLINE);
paul718e3742002-12-13 20:15:29 +00001271 if (index->exitpolicy == RMAP_GOTO)
paulfee0f4c2004-09-13 05:12:46 +00001272 vty_out (vty, " on-match goto %d%s", index->nextpref, VTY_NEWLINE);
paul718e3742002-12-13 20:15:29 +00001273 if (index->exitpolicy == RMAP_NEXT)
1274 vty_out (vty," on-match next%s", VTY_NEWLINE);
1275
1276 write++;
1277 }
1278 return write;
1279}
1280
1281/* Route map node structure. */
1282struct cmd_node rmap_node =
1283{
1284 RMAP_NODE,
1285 "%s(config-route-map)# ",
1286 1
1287};
1288
1289/* Initialization of route map vector. */
1290void
paul8cc41982005-05-06 21:25:49 +00001291route_map_init_vty (void)
paul718e3742002-12-13 20:15:29 +00001292{
1293 /* Install route map top node. */
1294 install_node (&rmap_node, route_map_config_write);
1295
1296 /* Install route map commands. */
1297 install_default (RMAP_NODE);
1298 install_element (CONFIG_NODE, &route_map_cmd);
1299 install_element (CONFIG_NODE, &no_route_map_cmd);
1300 install_element (CONFIG_NODE, &no_route_map_all_cmd);
1301
1302 /* Install the on-match stuff */
1303 install_element (RMAP_NODE, &route_map_cmd);
1304 install_element (RMAP_NODE, &rmap_onmatch_next_cmd);
1305 install_element (RMAP_NODE, &no_rmap_onmatch_next_cmd);
1306 install_element (RMAP_NODE, &rmap_onmatch_goto_cmd);
1307 install_element (RMAP_NODE, &no_rmap_onmatch_goto_cmd);
paulfee0f4c2004-09-13 05:12:46 +00001308
1309 /* Install the continue stuff (ALIAS of on-match). */
1310 install_element (RMAP_NODE, &rmap_continue_cmd);
1311 install_element (RMAP_NODE, &no_rmap_continue_cmd);
1312 install_element (RMAP_NODE, &rmap_continue_index_cmd);
1313
1314 /* Install the call stuff. */
1315 install_element (RMAP_NODE, &rmap_call_cmd);
1316 install_element (RMAP_NODE, &no_rmap_call_cmd);
hasso4a8164e2005-04-08 14:20:18 +00001317
1318 /* Install description commands. */
1319 install_element (RMAP_NODE, &rmap_description_cmd);
1320 install_element (RMAP_NODE, &no_rmap_description_cmd);
paulfee0f4c2004-09-13 05:12:46 +00001321
paul5510e832004-07-09 14:00:01 +00001322 /* Install show command */
1323 install_element (ENABLE_NODE, &rmap_show_cmd);
1324 install_element (ENABLE_NODE, &rmap_show_name_cmd);
paul718e3742002-12-13 20:15:29 +00001325}