blob: 7302e23118439c407d1791df1fc637c7dd29b2af [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"
David Lamparter6b0655a2014-06-04 06:53:35 +020031
paul718e3742002-12-13 20:15:29 +000032/* 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);
David Lamparter6b0655a2014-06-04 06:53:35 +020075
paul718e3742002-12-13 20:15:29 +000076/* 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
vincentfbf5d032005-09-29 11:25:50 +0000208 /* Print the name of the protocol */
209 if (zlog_default)
210 vty_out (vty, "%s:%s", zlog_proto_names[zlog_default->protocol],
211 VTY_NEWLINE);
212
paul5510e832004-07-09 14:00:01 +0000213 for (index = map->head; index; index = index->next)
214 {
215 vty_out (vty, "route-map %s, %s, sequence %d%s",
216 map->name, route_map_type_str (index->type),
217 index->pref, VTY_NEWLINE);
hasso5bb4c192005-04-09 13:27:50 +0000218
219 /* Description */
220 if (index->description)
221 vty_out (vty, " Description:%s %s%s", VTY_NEWLINE,
222 index->description, VTY_NEWLINE);
paul5510e832004-07-09 14:00:01 +0000223
224 /* Match clauses */
225 vty_out (vty, " Match clauses:%s", VTY_NEWLINE);
226 for (rule = index->match_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, " Set clauses:%s", VTY_NEWLINE);
231 for (rule = index->set_list.head; rule; rule = rule->next)
232 vty_out (vty, " %s %s%s",
233 rule->cmd->str, rule->rule_str, VTY_NEWLINE);
234
pauldb29ae52005-11-12 22:36:41 +0000235 /* Call clause */
236 vty_out (vty, " Call clause:%s", VTY_NEWLINE);
paulfee0f4c2004-09-13 05:12:46 +0000237 if (index->nextrm)
238 vty_out (vty, " Call %s%s", index->nextrm, VTY_NEWLINE);
pauldb29ae52005-11-12 22:36:41 +0000239
240 /* Exit Policy */
241 vty_out (vty, " Action:%s", VTY_NEWLINE);
242 if (index->exitpolicy == RMAP_GOTO)
paul5510e832004-07-09 14:00:01 +0000243 vty_out (vty, " Goto %d%s", index->nextpref, VTY_NEWLINE);
paul5510e832004-07-09 14:00:01 +0000244 else if (index->exitpolicy == RMAP_NEXT)
pauldb29ae52005-11-12 22:36:41 +0000245 vty_out (vty, " Continue to next entry%s", VTY_NEWLINE);
paul5510e832004-07-09 14:00:01 +0000246 else if (index->exitpolicy == RMAP_EXIT)
247 vty_out (vty, " Exit routemap%s", VTY_NEWLINE);
248 }
paul718e3742002-12-13 20:15:29 +0000249}
250
paul8cc41982005-05-06 21:25:49 +0000251static int
paul9035efa2004-10-10 11:56:56 +0000252vty_show_route_map (struct vty *vty, const char *name)
paul5510e832004-07-09 14:00:01 +0000253{
254 struct route_map *map;
255
256 if (name)
257 {
258 map = route_map_lookup_by_name (name);
259
260 if (map)
261 {
262 vty_show_route_map_entry (vty, map);
263 return CMD_SUCCESS;
264 }
265 else
266 {
267 vty_out (vty, "%%route-map %s not found%s", name, VTY_NEWLINE);
268 return CMD_WARNING;
269 }
270 }
Paul Jakma7514fb72007-05-02 16:05:35 +0000271 else
272 {
273 for (map = route_map_master.head; map; map = map->next)
274 vty_show_route_map_entry (vty, map);
275 }
paul5510e832004-07-09 14:00:01 +0000276 return CMD_SUCCESS;
277}
278
279
paul718e3742002-12-13 20:15:29 +0000280/* New route map allocation. Please note route map's name must be
281 specified. */
paul8cc41982005-05-06 21:25:49 +0000282static struct route_map_index *
283route_map_index_new (void)
paul718e3742002-12-13 20:15:29 +0000284{
285 struct route_map_index *new;
286
287 new = XCALLOC (MTYPE_ROUTE_MAP_INDEX, sizeof (struct route_map_index));
288 new->exitpolicy = RMAP_EXIT; /* Default to Cisco-style */
289 return new;
290}
291
292/* Free route map index. */
293static void
294route_map_index_delete (struct route_map_index *index, int notify)
295{
296 struct route_map_rule *rule;
297
298 /* Free route match. */
299 while ((rule = index->match_list.head) != NULL)
300 route_map_rule_delete (&index->match_list, rule);
301
302 /* Free route set. */
303 while ((rule = index->set_list.head) != NULL)
304 route_map_rule_delete (&index->set_list, rule);
305
306 /* Remove index from route map list. */
307 if (index->next)
308 index->next->prev = index->prev;
309 else
310 index->map->tail = index->prev;
311
312 if (index->prev)
313 index->prev->next = index->next;
314 else
315 index->map->head = index->next;
316
paulfee0f4c2004-09-13 05:12:46 +0000317 /* Free 'char *nextrm' if not NULL */
318 if (index->nextrm)
paul02416842005-10-26 05:05:16 +0000319 XFREE (MTYPE_ROUTE_MAP_NAME, index->nextrm);
paulfee0f4c2004-09-13 05:12:46 +0000320
paul718e3742002-12-13 20:15:29 +0000321 /* Execute event hook. */
322 if (route_map_master.event_hook && notify)
323 (*route_map_master.event_hook) (RMAP_EVENT_INDEX_DELETED,
324 index->map->name);
325
326 XFREE (MTYPE_ROUTE_MAP_INDEX, index);
327}
328
329/* Lookup index from route map. */
paul8cc41982005-05-06 21:25:49 +0000330static struct route_map_index *
paul718e3742002-12-13 20:15:29 +0000331route_map_index_lookup (struct route_map *map, enum route_map_type type,
332 int pref)
333{
334 struct route_map_index *index;
335
336 for (index = map->head; index; index = index->next)
337 if ((index->type == type || type == RMAP_ANY)
338 && index->pref == pref)
339 return index;
340 return NULL;
341}
342
343/* Add new index to route map. */
paul8cc41982005-05-06 21:25:49 +0000344static struct route_map_index *
paul718e3742002-12-13 20:15:29 +0000345route_map_index_add (struct route_map *map, enum route_map_type type,
346 int pref)
347{
348 struct route_map_index *index;
349 struct route_map_index *point;
350
351 /* Allocate new route map inex. */
352 index = route_map_index_new ();
353 index->map = map;
354 index->type = type;
355 index->pref = pref;
356
357 /* Compare preference. */
358 for (point = map->head; point; point = point->next)
359 if (point->pref >= pref)
360 break;
361
362 if (map->head == NULL)
363 {
364 map->head = map->tail = index;
365 }
366 else if (point == NULL)
367 {
368 index->prev = map->tail;
369 map->tail->next = index;
370 map->tail = index;
371 }
372 else if (point == map->head)
373 {
374 index->next = map->head;
375 map->head->prev = index;
376 map->head = index;
377 }
378 else
379 {
380 index->next = point;
381 index->prev = point->prev;
382 if (point->prev)
383 point->prev->next = index;
384 point->prev = index;
385 }
386
387 /* Execute event hook. */
388 if (route_map_master.event_hook)
389 (*route_map_master.event_hook) (RMAP_EVENT_INDEX_ADDED,
390 map->name);
391
392 return index;
393}
394
395/* Get route map index. */
paul8cc41982005-05-06 21:25:49 +0000396static struct route_map_index *
paul718e3742002-12-13 20:15:29 +0000397route_map_index_get (struct route_map *map, enum route_map_type type,
398 int pref)
399{
400 struct route_map_index *index;
401
402 index = route_map_index_lookup (map, RMAP_ANY, pref);
403 if (index && index->type != type)
404 {
405 /* Delete index from route map. */
406 route_map_index_delete (index, 1);
407 index = NULL;
408 }
409 if (index == NULL)
410 index = route_map_index_add (map, type, pref);
411 return index;
412}
413
414/* New route map rule */
paul8cc41982005-05-06 21:25:49 +0000415static struct route_map_rule *
416route_map_rule_new (void)
paul718e3742002-12-13 20:15:29 +0000417{
418 struct route_map_rule *new;
419
420 new = XCALLOC (MTYPE_ROUTE_MAP_RULE, sizeof (struct route_map_rule));
421 return new;
422}
David Lamparter6b0655a2014-06-04 06:53:35 +0200423
paul718e3742002-12-13 20:15:29 +0000424/* Install rule command to the match list. */
425void
426route_map_install_match (struct route_map_rule_cmd *cmd)
427{
428 vector_set (route_match_vec, cmd);
429}
430
431/* Install rule command to the set list. */
432void
433route_map_install_set (struct route_map_rule_cmd *cmd)
434{
435 vector_set (route_set_vec, cmd);
436}
437
438/* Lookup rule command from match list. */
paul8cc41982005-05-06 21:25:49 +0000439static struct route_map_rule_cmd *
hasso27a43a82004-10-08 06:29:12 +0000440route_map_lookup_match (const char *name)
paul718e3742002-12-13 20:15:29 +0000441{
hasso8c328f12004-10-05 21:01:23 +0000442 unsigned int i;
paul718e3742002-12-13 20:15:29 +0000443 struct route_map_rule_cmd *rule;
444
paul55468c82005-03-14 20:19:01 +0000445 for (i = 0; i < vector_active (route_match_vec); i++)
paul718e3742002-12-13 20:15:29 +0000446 if ((rule = vector_slot (route_match_vec, i)) != NULL)
447 if (strcmp (rule->str, name) == 0)
448 return rule;
449 return NULL;
450}
451
452/* Lookup rule command from set list. */
paul8cc41982005-05-06 21:25:49 +0000453static struct route_map_rule_cmd *
hasso27a43a82004-10-08 06:29:12 +0000454route_map_lookup_set (const char *name)
paul718e3742002-12-13 20:15:29 +0000455{
hasso8c328f12004-10-05 21:01:23 +0000456 unsigned int i;
paul718e3742002-12-13 20:15:29 +0000457 struct route_map_rule_cmd *rule;
458
paul55468c82005-03-14 20:19:01 +0000459 for (i = 0; i < vector_active (route_set_vec); i++)
paul718e3742002-12-13 20:15:29 +0000460 if ((rule = vector_slot (route_set_vec, i)) != NULL)
461 if (strcmp (rule->str, name) == 0)
462 return rule;
463 return NULL;
464}
465
466/* Add match and set rule to rule list. */
467static void
468route_map_rule_add (struct route_map_rule_list *list,
469 struct route_map_rule *rule)
470{
471 rule->next = NULL;
472 rule->prev = list->tail;
473 if (list->tail)
474 list->tail->next = rule;
475 else
476 list->head = rule;
477 list->tail = rule;
478}
479
480/* Delete rule from rule list. */
481static void
482route_map_rule_delete (struct route_map_rule_list *list,
483 struct route_map_rule *rule)
484{
485 if (rule->cmd->func_free)
486 (*rule->cmd->func_free) (rule->value);
487
488 if (rule->rule_str)
489 XFREE (MTYPE_ROUTE_MAP_RULE_STR, rule->rule_str);
490
491 if (rule->next)
492 rule->next->prev = rule->prev;
493 else
494 list->tail = rule->prev;
495 if (rule->prev)
496 rule->prev->next = rule->next;
497 else
498 list->head = rule->next;
499
500 XFREE (MTYPE_ROUTE_MAP_RULE, rule);
501}
502
503/* strcmp wrapper function which don't crush even argument is NULL. */
paul8cc41982005-05-06 21:25:49 +0000504static int
paulc9eca012004-10-11 11:28:44 +0000505rulecmp (const char *dst, const char *src)
paul718e3742002-12-13 20:15:29 +0000506{
507 if (dst == NULL)
508 {
509 if (src == NULL)
510 return 0;
511 else
512 return 1;
513 }
514 else
515 {
516 if (src == NULL)
517 return 1;
518 else
519 return strcmp (dst, src);
520 }
521 return 1;
522}
523
524/* Add match statement to route map. */
525int
hasso27a43a82004-10-08 06:29:12 +0000526route_map_add_match (struct route_map_index *index, const char *match_name,
paulc9eca012004-10-11 11:28:44 +0000527 const char *match_arg)
paul718e3742002-12-13 20:15:29 +0000528{
529 struct route_map_rule *rule;
530 struct route_map_rule *next;
531 struct route_map_rule_cmd *cmd;
532 void *compile;
533 int replaced = 0;
534
535 /* First lookup rule for add match statement. */
536 cmd = route_map_lookup_match (match_name);
537 if (cmd == NULL)
538 return RMAP_RULE_MISSING;
539
540 /* Next call compile function for this match statement. */
541 if (cmd->func_compile)
542 {
543 compile= (*cmd->func_compile)(match_arg);
544 if (compile == NULL)
545 return RMAP_COMPILE_ERROR;
546 }
547 else
548 compile = NULL;
549
550 /* If argument is completely same ignore it. */
551 for (rule = index->match_list.head; rule; rule = next)
552 {
553 next = rule->next;
554 if (rule->cmd == cmd)
555 {
556 route_map_rule_delete (&index->match_list, rule);
557 replaced = 1;
558 }
559 }
560
561 /* Add new route map match rule. */
562 rule = route_map_rule_new ();
563 rule->cmd = cmd;
564 rule->value = compile;
565 if (match_arg)
566 rule->rule_str = XSTRDUP (MTYPE_ROUTE_MAP_RULE_STR, match_arg);
567 else
568 rule->rule_str = NULL;
569
570 /* Add new route match rule to linked list. */
571 route_map_rule_add (&index->match_list, rule);
572
573 /* Execute event hook. */
574 if (route_map_master.event_hook)
575 (*route_map_master.event_hook) (replaced ?
576 RMAP_EVENT_MATCH_REPLACED:
577 RMAP_EVENT_MATCH_ADDED,
578 index->map->name);
579
580 return 0;
581}
582
583/* Delete specified route match rule. */
584int
hasso27a43a82004-10-08 06:29:12 +0000585route_map_delete_match (struct route_map_index *index, const char *match_name,
paulc9eca012004-10-11 11:28:44 +0000586 const char *match_arg)
paul718e3742002-12-13 20:15:29 +0000587{
588 struct route_map_rule *rule;
589 struct route_map_rule_cmd *cmd;
590
591 cmd = route_map_lookup_match (match_name);
592 if (cmd == NULL)
593 return 1;
594
595 for (rule = index->match_list.head; rule; rule = rule->next)
596 if (rule->cmd == cmd &&
597 (rulecmp (rule->rule_str, match_arg) == 0 || match_arg == NULL))
598 {
599 route_map_rule_delete (&index->match_list, rule);
600 /* Execute event hook. */
601 if (route_map_master.event_hook)
602 (*route_map_master.event_hook) (RMAP_EVENT_MATCH_DELETED,
603 index->map->name);
604 return 0;
605 }
606 /* Can't find matched rule. */
607 return 1;
608}
609
610/* Add route-map set statement to the route map. */
611int
hasso27a43a82004-10-08 06:29:12 +0000612route_map_add_set (struct route_map_index *index, const char *set_name,
paulc9eca012004-10-11 11:28:44 +0000613 const char *set_arg)
paul718e3742002-12-13 20:15:29 +0000614{
615 struct route_map_rule *rule;
616 struct route_map_rule *next;
617 struct route_map_rule_cmd *cmd;
618 void *compile;
619 int replaced = 0;
620
621 cmd = route_map_lookup_set (set_name);
622 if (cmd == NULL)
623 return RMAP_RULE_MISSING;
624
625 /* Next call compile function for this match statement. */
626 if (cmd->func_compile)
627 {
628 compile= (*cmd->func_compile)(set_arg);
629 if (compile == NULL)
630 return RMAP_COMPILE_ERROR;
631 }
632 else
633 compile = NULL;
634
635 /* Add by WJL. if old set command of same kind exist, delete it first
636 to ensure only one set command of same kind exist under a
637 route_map_index. */
638 for (rule = index->set_list.head; rule; rule = next)
639 {
640 next = rule->next;
641 if (rule->cmd == cmd)
642 {
643 route_map_rule_delete (&index->set_list, rule);
644 replaced = 1;
645 }
646 }
647
648 /* Add new route map match rule. */
649 rule = route_map_rule_new ();
650 rule->cmd = cmd;
651 rule->value = compile;
652 if (set_arg)
653 rule->rule_str = XSTRDUP (MTYPE_ROUTE_MAP_RULE_STR, set_arg);
654 else
655 rule->rule_str = NULL;
656
657 /* Add new route match rule to linked list. */
658 route_map_rule_add (&index->set_list, rule);
659
660 /* Execute event hook. */
661 if (route_map_master.event_hook)
662 (*route_map_master.event_hook) (replaced ?
663 RMAP_EVENT_SET_REPLACED:
664 RMAP_EVENT_SET_ADDED,
665 index->map->name);
666 return 0;
667}
668
669/* Delete route map set rule. */
670int
hasso27a43a82004-10-08 06:29:12 +0000671route_map_delete_set (struct route_map_index *index, const char *set_name,
paulc9eca012004-10-11 11:28:44 +0000672 const char *set_arg)
paul718e3742002-12-13 20:15:29 +0000673{
674 struct route_map_rule *rule;
675 struct route_map_rule_cmd *cmd;
676
677 cmd = route_map_lookup_set (set_name);
678 if (cmd == NULL)
679 return 1;
680
681 for (rule = index->set_list.head; rule; rule = rule->next)
682 if ((rule->cmd == cmd) &&
683 (rulecmp (rule->rule_str, set_arg) == 0 || set_arg == NULL))
684 {
685 route_map_rule_delete (&index->set_list, rule);
686 /* Execute event hook. */
687 if (route_map_master.event_hook)
688 (*route_map_master.event_hook) (RMAP_EVENT_SET_DELETED,
689 index->map->name);
690 return 0;
691 }
692 /* Can't find matched rule. */
693 return 1;
694}
695
paul3bf1c912003-10-29 06:30:19 +0000696/* Apply route map's each index to the object.
697
698 The matrix for a route-map looks like this:
699 (note, this includes the description for the "NEXT"
700 and "GOTO" frobs now
paul718e3742002-12-13 20:15:29 +0000701
paul3bf1c912003-10-29 06:30:19 +0000702 Match | No Match
703 |
704 permit action | cont
705 |
706 ------------------+---------------
707 |
708 deny deny | cont
709 |
710
paulfee0f4c2004-09-13 05:12:46 +0000711 action)
712 -Apply Set statements, accept route
713 -If Call statement is present jump to the specified route-map, if it
714 denies the route we finish.
715 -If NEXT is specified, goto NEXT statement
716 -If GOTO is specified, goto the first clause where pref > nextpref
717 -If nothing is specified, do as Cisco and finish
718 deny)
719 -Route is denied by route-map.
720 cont)
721 -Goto Next index
paul3bf1c912003-10-29 06:30:19 +0000722
723 If we get no matches after we've processed all updates, then the route
724 is dropped too.
725
paulfee0f4c2004-09-13 05:12:46 +0000726 Some notes on the new "CALL", "NEXT" and "GOTO"
727 call WORD - If this clause is matched, then the set statements
728 are executed and then we jump to route-map 'WORD'. If
729 this route-map denies the route, we finish, in other case we
730 do whatever the exit policy (EXIT, NEXT or GOTO) tells.
paul3bf1c912003-10-29 06:30:19 +0000731 on-match next - If this clause is matched, then the set statements
732 are executed and then we drop through to the next clause
733 on-match goto n - If this clause is matched, then the set statments
734 are executed and then we goto the nth clause, or the
735 first clause greater than this. In order to ensure
736 route-maps *always* exit, you cannot jump backwards.
737 Sorry ;)
738
739 We need to make sure our route-map processing matches the above
740*/
paul718e3742002-12-13 20:15:29 +0000741
paul8cc41982005-05-06 21:25:49 +0000742static route_map_result_t
paul3bf1c912003-10-29 06:30:19 +0000743route_map_apply_match (struct route_map_rule_list *match_list,
744 struct prefix *prefix, route_map_object_t type,
745 void *object)
746{
747 route_map_result_t ret = RMAP_NOMATCH;
748 struct route_map_rule *match;
paul718e3742002-12-13 20:15:29 +0000749
paul3bf1c912003-10-29 06:30:19 +0000750
751 /* Check all match rule and if there is no match rule, go to the
752 set statement. */
753 if (!match_list->head)
754 ret = RMAP_MATCH;
755 else
paul718e3742002-12-13 20:15:29 +0000756 {
paul3bf1c912003-10-29 06:30:19 +0000757 for (match = match_list->head; match; match = match->next)
758 {
759 /* Try each match statement in turn, If any do not return
760 RMAP_MATCH, return, otherwise continue on to next match
761 statement. All match statements must match for end-result
762 to be a match. */
763 ret = (*match->cmd->func_apply) (match->value, prefix,
764 type, object);
765 if (ret != RMAP_MATCH)
766 return ret;
767 }
paul718e3742002-12-13 20:15:29 +0000768 }
paul3bf1c912003-10-29 06:30:19 +0000769 return ret;
paul718e3742002-12-13 20:15:29 +0000770}
771
772/* Apply route map to the object. */
773route_map_result_t
paul3bf1c912003-10-29 06:30:19 +0000774route_map_apply (struct route_map *map, struct prefix *prefix,
775 route_map_object_t type, void *object)
paul718e3742002-12-13 20:15:29 +0000776{
paulfee0f4c2004-09-13 05:12:46 +0000777 static int recursion = 0;
paul718e3742002-12-13 20:15:29 +0000778 int ret = 0;
779 struct route_map_index *index;
paul3bf1c912003-10-29 06:30:19 +0000780 struct route_map_rule *set;
paul718e3742002-12-13 20:15:29 +0000781
paulfee0f4c2004-09-13 05:12:46 +0000782 if (recursion > RMAP_RECURSION_LIMIT)
783 {
784 zlog (NULL, LOG_WARNING,
785 "route-map recursion limit (%d) reached, discarding route",
786 RMAP_RECURSION_LIMIT);
787 recursion = 0;
788 return RMAP_DENYMATCH;
789 }
790
paul718e3742002-12-13 20:15:29 +0000791 if (map == NULL)
792 return RMAP_DENYMATCH;
793
794 for (index = map->head; index; index = index->next)
795 {
paul3bf1c912003-10-29 06:30:19 +0000796 /* Apply this index. */
797 ret = route_map_apply_match (&index->match_list, prefix, type, object);
paul718e3742002-12-13 20:15:29 +0000798
paul3bf1c912003-10-29 06:30:19 +0000799 /* Now we apply the matrix from above */
800 if (ret == RMAP_NOMATCH)
801 /* 'cont' from matrix - continue to next route-map sequence */
802 continue;
803 else if (ret == RMAP_MATCH)
804 {
805 if (index->type == RMAP_PERMIT)
806 /* 'action' */
807 {
808 /* permit+match must execute sets */
809 for (set = index->set_list.head; set; set = set->next)
810 ret = (*set->cmd->func_apply) (set->value, prefix,
811 type, object);
paulfee0f4c2004-09-13 05:12:46 +0000812
813 /* Call another route-map if available */
814 if (index->nextrm)
815 {
816 struct route_map *nextrm =
817 route_map_lookup_by_name (index->nextrm);
818
819 if (nextrm) /* Target route-map found, jump to it */
820 {
821 recursion++;
822 ret = route_map_apply (nextrm, prefix, type, object);
823 recursion--;
824 }
825
826 /* If nextrm returned 'deny', finish. */
827 if (ret == RMAP_DENYMATCH)
828 return ret;
829 }
830
paul3bf1c912003-10-29 06:30:19 +0000831 switch (index->exitpolicy)
832 {
833 case RMAP_EXIT:
834 return ret;
835 case RMAP_NEXT:
836 continue;
837 case RMAP_GOTO:
838 {
839 /* Find the next clause to jump to */
840 struct route_map_index *next = index->next;
paulfee0f4c2004-09-13 05:12:46 +0000841 int nextpref = index->nextpref;
paul718e3742002-12-13 20:15:29 +0000842
paulfee0f4c2004-09-13 05:12:46 +0000843 while (next && next->pref < nextpref)
paul3bf1c912003-10-29 06:30:19 +0000844 {
845 index = next;
846 next = next->next;
847 }
848 if (next == NULL)
849 {
850 /* No clauses match! */
851 return ret;
852 }
853 }
854 }
855 }
856 else if (index->type == RMAP_DENY)
857 /* 'deny' */
858 {
paul3bf1c912003-10-29 06:30:19 +0000859 return RMAP_DENYMATCH;
860 }
861 }
paul718e3742002-12-13 20:15:29 +0000862 }
863 /* Finally route-map does not match at all. */
864 return RMAP_DENYMATCH;
865}
866
867void
paul9035efa2004-10-10 11:56:56 +0000868route_map_add_hook (void (*func) (const char *))
paul718e3742002-12-13 20:15:29 +0000869{
870 route_map_master.add_hook = func;
871}
872
873void
paul9035efa2004-10-10 11:56:56 +0000874route_map_delete_hook (void (*func) (const char *))
paul718e3742002-12-13 20:15:29 +0000875{
876 route_map_master.delete_hook = func;
877}
878
879void
paul9035efa2004-10-10 11:56:56 +0000880route_map_event_hook (void (*func) (route_map_event_t, const char *))
paul718e3742002-12-13 20:15:29 +0000881{
882 route_map_master.event_hook = func;
883}
884
885void
paul8cc41982005-05-06 21:25:49 +0000886route_map_init (void)
paul718e3742002-12-13 20:15:29 +0000887{
888 /* Make vector for match and set. */
889 route_match_vec = vector_init (1);
890 route_set_vec = vector_init (1);
891}
Chris Caputo228da422009-07-18 05:44:03 +0000892
893void
894route_map_finish (void)
895{
896 vector_free (route_match_vec);
897 route_match_vec = NULL;
898 vector_free (route_set_vec);
899 route_set_vec = NULL;
Lou Berger056f3762013-04-10 12:30:04 -0700900 /* cleanup route_map */
901 while (route_map_master.head)
902 route_map_delete (route_map_master.head);
Chris Caputo228da422009-07-18 05:44:03 +0000903}
David Lamparter6b0655a2014-06-04 06:53:35 +0200904
paul718e3742002-12-13 20:15:29 +0000905/* VTY related functions. */
906DEFUN (route_map,
907 route_map_cmd,
908 "route-map WORD (deny|permit) <1-65535>",
909 "Create route-map or enter route-map command mode\n"
910 "Route map tag\n"
911 "Route map denies set operations\n"
912 "Route map permits set operations\n"
913 "Sequence to insert to/delete from existing route-map entry\n")
914{
915 int permit;
916 unsigned long pref;
917 struct route_map *map;
918 struct route_map_index *index;
919 char *endptr = NULL;
920
921 /* Permit check. */
922 if (strncmp (argv[1], "permit", strlen (argv[1])) == 0)
923 permit = RMAP_PERMIT;
924 else if (strncmp (argv[1], "deny", strlen (argv[1])) == 0)
925 permit = RMAP_DENY;
926 else
927 {
928 vty_out (vty, "the third field must be [permit|deny]%s", VTY_NEWLINE);
929 return CMD_WARNING;
930 }
931
932 /* Preference check. */
933 pref = strtoul (argv[2], &endptr, 10);
934 if (pref == ULONG_MAX || *endptr != '\0')
935 {
936 vty_out (vty, "the fourth field must be positive integer%s",
937 VTY_NEWLINE);
938 return CMD_WARNING;
939 }
940 if (pref == 0 || pref > 65535)
941 {
942 vty_out (vty, "the fourth field must be <1-65535>%s", VTY_NEWLINE);
943 return CMD_WARNING;
944 }
945
946 /* Get route map. */
947 map = route_map_get (argv[0]);
948 index = route_map_index_get (map, permit, pref);
949
950 vty->index = index;
951 vty->node = RMAP_NODE;
952 return CMD_SUCCESS;
953}
954
955DEFUN (no_route_map_all,
956 no_route_map_all_cmd,
957 "no route-map WORD",
958 NO_STR
959 "Create route-map or enter route-map command mode\n"
960 "Route map tag\n")
961{
962 struct route_map *map;
963
964 map = route_map_lookup_by_name (argv[0]);
965 if (map == NULL)
966 {
967 vty_out (vty, "%% Could not find route-map %s%s",
968 argv[0], VTY_NEWLINE);
969 return CMD_WARNING;
970 }
971
972 route_map_delete (map);
973
974 return CMD_SUCCESS;
975}
976
977DEFUN (no_route_map,
978 no_route_map_cmd,
979 "no route-map WORD (deny|permit) <1-65535>",
980 NO_STR
981 "Create route-map or enter route-map command mode\n"
982 "Route map tag\n"
983 "Route map denies set operations\n"
984 "Route map permits set operations\n"
985 "Sequence to insert to/delete from existing route-map entry\n")
986{
987 int permit;
988 unsigned long pref;
989 struct route_map *map;
990 struct route_map_index *index;
991 char *endptr = NULL;
992
993 /* Permit check. */
994 if (strncmp (argv[1], "permit", strlen (argv[1])) == 0)
995 permit = RMAP_PERMIT;
996 else if (strncmp (argv[1], "deny", strlen (argv[1])) == 0)
997 permit = RMAP_DENY;
998 else
999 {
1000 vty_out (vty, "the third field must be [permit|deny]%s", VTY_NEWLINE);
1001 return CMD_WARNING;
1002 }
1003
1004 /* Preference. */
1005 pref = strtoul (argv[2], &endptr, 10);
1006 if (pref == ULONG_MAX || *endptr != '\0')
1007 {
1008 vty_out (vty, "the fourth field must be positive integer%s",
1009 VTY_NEWLINE);
1010 return CMD_WARNING;
1011 }
1012 if (pref == 0 || pref > 65535)
1013 {
1014 vty_out (vty, "the fourth field must be <1-65535>%s", VTY_NEWLINE);
1015 return CMD_WARNING;
1016 }
1017
1018 /* Existence check. */
1019 map = route_map_lookup_by_name (argv[0]);
1020 if (map == NULL)
1021 {
1022 vty_out (vty, "%% Could not find route-map %s%s",
1023 argv[0], VTY_NEWLINE);
1024 return CMD_WARNING;
1025 }
1026
1027 /* Lookup route map index. */
1028 index = route_map_index_lookup (map, permit, pref);
1029 if (index == NULL)
1030 {
1031 vty_out (vty, "%% Could not find route-map entry %s %s%s",
1032 argv[0], argv[2], VTY_NEWLINE);
1033 return CMD_WARNING;
1034 }
1035
1036 /* Delete index from route map. */
1037 route_map_index_delete (index, 1);
1038
1039 /* If this route rule is the last one, delete route map itself. */
1040 if (route_map_empty (map))
1041 route_map_delete (map);
1042
1043 return CMD_SUCCESS;
1044}
1045
1046DEFUN (rmap_onmatch_next,
1047 rmap_onmatch_next_cmd,
1048 "on-match next",
1049 "Exit policy on matches\n"
1050 "Next clause\n")
1051{
1052 struct route_map_index *index;
1053
1054 index = vty->index;
1055
1056 if (index)
1057 index->exitpolicy = RMAP_NEXT;
1058
1059 return CMD_SUCCESS;
1060}
1061
1062DEFUN (no_rmap_onmatch_next,
1063 no_rmap_onmatch_next_cmd,
1064 "no on-match next",
1065 NO_STR
1066 "Exit policy on matches\n"
1067 "Next clause\n")
1068{
1069 struct route_map_index *index;
1070
1071 index = vty->index;
1072
1073 if (index)
1074 index->exitpolicy = RMAP_EXIT;
1075
1076 return CMD_SUCCESS;
1077}
1078
1079DEFUN (rmap_onmatch_goto,
1080 rmap_onmatch_goto_cmd,
1081 "on-match goto <1-65535>",
1082 "Exit policy on matches\n"
1083 "Goto Clause number\n"
1084 "Number\n")
1085{
pauld4f09602005-05-23 12:43:34 +00001086 struct route_map_index *index = vty->index;
paul718e3742002-12-13 20:15:29 +00001087 int d = 0;
1088
paul718e3742002-12-13 20:15:29 +00001089 if (index)
1090 {
pauld4f09602005-05-23 12:43:34 +00001091 if (argc == 1 && argv[0])
1092 VTY_GET_INTEGER_RANGE("route-map index", d, argv[0], 1, 65536);
1093 else
1094 d = index->pref + 1;
1095
paul718e3742002-12-13 20:15:29 +00001096 if (d <= index->pref)
1097 {
1098 /* Can't allow you to do that, Dave */
1099 vty_out (vty, "can't jump backwards in route-maps%s",
1100 VTY_NEWLINE);
1101 return CMD_WARNING;
1102 }
1103 else
1104 {
1105 index->exitpolicy = RMAP_GOTO;
1106 index->nextpref = d;
1107 }
1108 }
1109 return CMD_SUCCESS;
1110}
1111
1112DEFUN (no_rmap_onmatch_goto,
1113 no_rmap_onmatch_goto_cmd,
1114 "no on-match goto",
1115 NO_STR
1116 "Exit policy on matches\n"
paulfee0f4c2004-09-13 05:12:46 +00001117 "Goto Clause number\n")
paul718e3742002-12-13 20:15:29 +00001118{
1119 struct route_map_index *index;
1120
1121 index = vty->index;
1122
1123 if (index)
1124 index->exitpolicy = RMAP_EXIT;
1125
1126 return CMD_SUCCESS;
1127}
1128
paul5510e832004-07-09 14:00:01 +00001129/* Cisco/GNU Zebra compatible ALIASes for on-match next */
1130ALIAS (rmap_onmatch_goto,
1131 rmap_continue_cmd,
1132 "continue",
1133 "Continue on a different entry within the route-map\n")
1134
1135ALIAS (no_rmap_onmatch_goto,
1136 no_rmap_continue_cmd,
1137 "no continue",
1138 NO_STR
1139 "Continue on a different entry within the route-map\n")
1140
1141/* GNU Zebra compatible */
1142ALIAS (rmap_onmatch_goto,
1143 rmap_continue_seq_cmd,
1144 "continue <1-65535>",
1145 "Continue on a different entry within the route-map\n"
1146 "Route-map entry sequence number\n")
1147
1148ALIAS (no_rmap_onmatch_goto,
1149 no_rmap_continue_seq,
1150 "no continue <1-65535>",
1151 NO_STR
1152 "Continue on a different entry within the route-map\n"
1153 "Route-map entry sequence number\n")
1154
paul5510e832004-07-09 14:00:01 +00001155DEFUN (rmap_show_name,
1156 rmap_show_name_cmd,
Paul Jakma7514fb72007-05-02 16:05:35 +00001157 "show route-map [WORD]",
paul5510e832004-07-09 14:00:01 +00001158 SHOW_STR
1159 "route-map information\n"
1160 "route-map name\n")
1161{
Paul Jakma7514fb72007-05-02 16:05:35 +00001162 const char *name = NULL;
1163 if (argc)
1164 name = argv[0];
1165 return vty_show_route_map (vty, name);
paul5510e832004-07-09 14:00:01 +00001166}
1167
paulfee0f4c2004-09-13 05:12:46 +00001168ALIAS (rmap_onmatch_goto,
1169 rmap_continue_index_cmd,
1170 "continue <1-65536>",
1171 "Exit policy on matches\n"
1172 "Goto Clause number\n")
1173
1174DEFUN (rmap_call,
1175 rmap_call_cmd,
1176 "call WORD",
1177 "Jump to another Route-Map after match+set\n"
1178 "Target route-map name\n")
1179{
1180 struct route_map_index *index;
1181
1182 index = vty->index;
1183 if (index)
1184 {
1185 if (index->nextrm)
paul02416842005-10-26 05:05:16 +00001186 XFREE (MTYPE_ROUTE_MAP_NAME, index->nextrm);
1187 index->nextrm = XSTRDUP (MTYPE_ROUTE_MAP_NAME, argv[0]);
paulfee0f4c2004-09-13 05:12:46 +00001188 }
1189 return CMD_SUCCESS;
1190}
1191
1192DEFUN (no_rmap_call,
1193 no_rmap_call_cmd,
1194 "no call",
1195 NO_STR
1196 "Jump to another Route-Map after match+set\n")
1197{
1198 struct route_map_index *index;
1199
1200 index = vty->index;
1201
1202 if (index->nextrm)
1203 {
paul02416842005-10-26 05:05:16 +00001204 XFREE (MTYPE_ROUTE_MAP_NAME, index->nextrm);
paulfee0f4c2004-09-13 05:12:46 +00001205 index->nextrm = NULL;
1206 }
1207
1208 return CMD_SUCCESS;
1209}
1210
hasso4a8164e2005-04-08 14:20:18 +00001211DEFUN (rmap_description,
1212 rmap_description_cmd,
1213 "description .LINE",
1214 "Route-map comment\n"
1215 "Comment describing this route-map rule\n")
1216{
1217 struct route_map_index *index;
1218
1219 index = vty->index;
1220 if (index)
1221 {
1222 if (index->description)
1223 XFREE (MTYPE_TMP, index->description);
1224 index->description = argv_concat (argv, argc, 0);
1225 }
1226 return CMD_SUCCESS;
1227}
1228
1229DEFUN (no_rmap_description,
1230 no_rmap_description_cmd,
1231 "no description",
1232 NO_STR
1233 "Route-map comment\n")
1234{
1235 struct route_map_index *index;
1236
1237 index = vty->index;
1238 if (index)
1239 {
1240 if (index->description)
1241 XFREE (MTYPE_TMP, index->description);
1242 index->description = NULL;
1243 }
1244 return CMD_SUCCESS;
1245}
1246
paul718e3742002-12-13 20:15:29 +00001247/* Configuration write function. */
paul8cc41982005-05-06 21:25:49 +00001248static int
paul718e3742002-12-13 20:15:29 +00001249route_map_config_write (struct vty *vty)
1250{
1251 struct route_map *map;
1252 struct route_map_index *index;
1253 struct route_map_rule *rule;
1254 int first = 1;
1255 int write = 0;
1256
1257 for (map = route_map_master.head; map; map = map->next)
1258 for (index = map->head; index; index = index->next)
1259 {
1260 if (!first)
1261 vty_out (vty, "!%s", VTY_NEWLINE);
1262 else
1263 first = 0;
1264
1265 vty_out (vty, "route-map %s %s %d%s",
1266 map->name,
1267 route_map_type_str (index->type),
1268 index->pref, VTY_NEWLINE);
1269
hasso4a8164e2005-04-08 14:20:18 +00001270 if (index->description)
1271 vty_out (vty, " description %s%s", index->description, VTY_NEWLINE);
1272
paul718e3742002-12-13 20:15:29 +00001273 for (rule = index->match_list.head; rule; rule = rule->next)
1274 vty_out (vty, " match %s %s%s", rule->cmd->str,
1275 rule->rule_str ? rule->rule_str : "",
1276 VTY_NEWLINE);
1277
1278 for (rule = index->set_list.head; rule; rule = rule->next)
1279 vty_out (vty, " set %s %s%s", rule->cmd->str,
1280 rule->rule_str ? rule->rule_str : "",
1281 VTY_NEWLINE);
paulfee0f4c2004-09-13 05:12:46 +00001282 if (index->nextrm)
1283 vty_out (vty, " call %s%s", index->nextrm, VTY_NEWLINE);
paul718e3742002-12-13 20:15:29 +00001284 if (index->exitpolicy == RMAP_GOTO)
paulfee0f4c2004-09-13 05:12:46 +00001285 vty_out (vty, " on-match goto %d%s", index->nextpref, VTY_NEWLINE);
paul718e3742002-12-13 20:15:29 +00001286 if (index->exitpolicy == RMAP_NEXT)
1287 vty_out (vty," on-match next%s", VTY_NEWLINE);
1288
1289 write++;
1290 }
1291 return write;
1292}
1293
1294/* Route map node structure. */
Stephen Hemminger7fc626d2008-12-01 11:10:34 -08001295static struct cmd_node rmap_node =
paul718e3742002-12-13 20:15:29 +00001296{
1297 RMAP_NODE,
1298 "%s(config-route-map)# ",
1299 1
1300};
1301
1302/* Initialization of route map vector. */
1303void
paul8cc41982005-05-06 21:25:49 +00001304route_map_init_vty (void)
paul718e3742002-12-13 20:15:29 +00001305{
1306 /* Install route map top node. */
1307 install_node (&rmap_node, route_map_config_write);
1308
1309 /* Install route map commands. */
1310 install_default (RMAP_NODE);
1311 install_element (CONFIG_NODE, &route_map_cmd);
1312 install_element (CONFIG_NODE, &no_route_map_cmd);
1313 install_element (CONFIG_NODE, &no_route_map_all_cmd);
1314
1315 /* Install the on-match stuff */
1316 install_element (RMAP_NODE, &route_map_cmd);
1317 install_element (RMAP_NODE, &rmap_onmatch_next_cmd);
1318 install_element (RMAP_NODE, &no_rmap_onmatch_next_cmd);
1319 install_element (RMAP_NODE, &rmap_onmatch_goto_cmd);
1320 install_element (RMAP_NODE, &no_rmap_onmatch_goto_cmd);
paulfee0f4c2004-09-13 05:12:46 +00001321
1322 /* Install the continue stuff (ALIAS of on-match). */
1323 install_element (RMAP_NODE, &rmap_continue_cmd);
1324 install_element (RMAP_NODE, &no_rmap_continue_cmd);
1325 install_element (RMAP_NODE, &rmap_continue_index_cmd);
1326
1327 /* Install the call stuff. */
1328 install_element (RMAP_NODE, &rmap_call_cmd);
1329 install_element (RMAP_NODE, &no_rmap_call_cmd);
hasso4a8164e2005-04-08 14:20:18 +00001330
1331 /* Install description commands. */
1332 install_element (RMAP_NODE, &rmap_description_cmd);
1333 install_element (RMAP_NODE, &no_rmap_description_cmd);
paulfee0f4c2004-09-13 05:12:46 +00001334
paul5510e832004-07-09 14:00:01 +00001335 /* Install show command */
paul5510e832004-07-09 14:00:01 +00001336 install_element (ENABLE_NODE, &rmap_show_name_cmd);
paul718e3742002-12-13 20:15:29 +00001337}