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