]>
Commit | Line | Data |
---|---|---|
718e3744 | 1 | /* Route map function. |
2 | Copyright (C) 1998, 1999 Kunihiro Ishiguro | |
3 | ||
4 | This file is part of GNU Zebra. | |
5 | ||
6 | GNU Zebra is free software; you can redistribute it and/or modify it | |
7 | under the terms of the GNU General Public License as published by the | |
8 | Free Software Foundation; either version 2, or (at your option) any | |
9 | later version. | |
10 | ||
11 | GNU Zebra is distributed in the hope that it will be useful, but | |
12 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | General Public License for more details. | |
15 | ||
16 | You should have received a copy of the GNU General Public License | |
17 | along with GNU Zebra; see the file COPYING. If not, write to the Free | |
18 | Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA | |
19 | 02111-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" | |
d4f0960c | 29 | #include "vty.h" |
fee0f4c6 | 30 | #include "log.h" |
518f0eb1 | 31 | #include "hash.h" |
6b0655a2 | 32 | |
718e3744 | 33 | /* Vector for route match rules. */ |
34 | static vector route_match_vec; | |
35 | ||
36 | /* Vector for route set rules. */ | |
37 | static vector route_set_vec; | |
38 | ||
39 | /* Route map rule. This rule has both `match' rule and `set' rule. */ | |
40 | struct route_map_rule | |
41 | { | |
42 | /* Rule type. */ | |
43 | struct route_map_rule_cmd *cmd; | |
44 | ||
45 | /* For pretty printing. */ | |
46 | char *rule_str; | |
47 | ||
48 | /* Pre-compiled match rule. */ | |
49 | void *value; | |
50 | ||
51 | /* Linked list. */ | |
52 | struct route_map_rule *next; | |
53 | struct route_map_rule *prev; | |
54 | }; | |
55 | ||
56 | /* Making route map list. */ | |
57 | struct route_map_list | |
58 | { | |
59 | struct route_map *head; | |
60 | struct route_map *tail; | |
61 | ||
9035efaa | 62 | void (*add_hook) (const char *); |
63 | void (*delete_hook) (const char *); | |
518f0eb1 | 64 | void (*event_hook) (route_map_event_t, const char *); |
718e3744 | 65 | }; |
66 | ||
67 | /* Master list of route map. */ | |
518f0eb1 DS |
68 | static struct route_map_list route_map_master = { NULL, NULL, NULL, NULL, NULL }; |
69 | ||
70 | enum route_map_upd8_type | |
71 | { | |
72 | ROUTE_MAP_ADD = 1, | |
73 | ROUTE_MAP_DEL, | |
74 | }; | |
75 | ||
76 | /* all possible route-map dependency types */ | |
77 | enum route_map_dep_type | |
78 | { | |
79 | ROUTE_MAP_DEP_RMAP = 1, | |
80 | ROUTE_MAP_DEP_CLIST, | |
81 | ROUTE_MAP_DEP_ECLIST, | |
82 | ROUTE_MAP_DEP_PLIST, | |
83 | ROUTE_MAP_DEP_ASPATH, | |
84 | ROUTE_MAP_DEP_FILTER, | |
85 | ROUTE_MAP_DEP_MAX, | |
86 | }; | |
87 | ||
88 | struct route_map_dep | |
89 | { | |
90 | char *dep_name; | |
91 | struct hash *dep_rmap_hash; | |
92 | struct hash *this_hash; /* ptr to the hash structure this is part of */ | |
93 | }; | |
718e3744 | 94 | |
518f0eb1 DS |
95 | /* Hashes maintaining dependency between various sublists used by route maps */ |
96 | struct hash *route_map_dep_hash[ROUTE_MAP_DEP_MAX]; | |
97 | ||
98 | static unsigned int route_map_dep_hash_make_key (void *p); | |
99 | static int route_map_dep_hash_cmp (const void *p1, const void *p2); | |
100 | static void route_map_init_dep_hashes (void); | |
101 | static void route_map_clear_all_references (char *rmap_name); | |
102 | static void route_map_rule_delete (struct route_map_rule_list *, | |
103 | struct route_map_rule *); | |
104 | static int rmap_debug = 0; | |
718e3744 | 105 | |
106 | static void | |
107 | route_map_index_delete (struct route_map_index *, int); | |
6b0655a2 | 108 | |
718e3744 | 109 | /* New route map allocation. Please note route map's name must be |
110 | specified. */ | |
111 | static struct route_map * | |
9035efaa | 112 | route_map_new (const char *name) |
718e3744 | 113 | { |
114 | struct route_map *new; | |
115 | ||
116 | new = XCALLOC (MTYPE_ROUTE_MAP, sizeof (struct route_map)); | |
117 | new->name = XSTRDUP (MTYPE_ROUTE_MAP_NAME, name); | |
118 | return new; | |
119 | } | |
120 | ||
121 | /* Add new name to route_map. */ | |
122 | static struct route_map * | |
9035efaa | 123 | route_map_add (const char *name) |
718e3744 | 124 | { |
125 | struct route_map *map; | |
126 | struct route_map_list *list; | |
127 | ||
128 | map = route_map_new (name); | |
129 | list = &route_map_master; | |
130 | ||
131 | map->next = NULL; | |
132 | map->prev = list->tail; | |
133 | if (list->tail) | |
134 | list->tail->next = map; | |
135 | else | |
136 | list->head = map; | |
137 | list->tail = map; | |
138 | ||
139 | /* Execute hook. */ | |
140 | if (route_map_master.add_hook) | |
518f0eb1 DS |
141 | { |
142 | (*route_map_master.add_hook) (name); | |
143 | route_map_notify_dependencies(name, RMAP_EVENT_CALL_ADDED); | |
144 | } | |
718e3744 | 145 | return map; |
146 | } | |
147 | ||
518f0eb1 DS |
148 | /* this is supposed to be called post processing by |
149 | * the delete hook function. Don't invoke delete_hook | |
150 | * again in this routine. | |
151 | */ | |
152 | void | |
153 | route_map_free_map (struct route_map *map) | |
718e3744 | 154 | { |
155 | struct route_map_list *list; | |
156 | struct route_map_index *index; | |
518f0eb1 | 157 | |
718e3744 | 158 | while ((index = map->head) != NULL) |
159 | route_map_index_delete (index, 0); | |
160 | ||
718e3744 | 161 | list = &route_map_master; |
162 | ||
518f0eb1 DS |
163 | if (map != NULL) |
164 | { | |
165 | if (map->next) | |
166 | map->next->prev = map->prev; | |
167 | else | |
168 | list->tail = map->prev; | |
718e3744 | 169 | |
518f0eb1 DS |
170 | if (map->prev) |
171 | map->prev->next = map->next; | |
172 | else | |
173 | list->head = map->next; | |
718e3744 | 174 | |
518f0eb1 DS |
175 | XFREE (MTYPE_ROUTE_MAP_NAME, map->name); |
176 | XFREE (MTYPE_ROUTE_MAP, map); | |
177 | } | |
178 | } | |
718e3744 | 179 | |
518f0eb1 DS |
180 | /* Route map delete from list. */ |
181 | static void | |
182 | route_map_delete (struct route_map *map) | |
183 | { | |
184 | struct route_map_index *index; | |
185 | char *name; | |
186 | ||
187 | while ((index = map->head) != NULL) | |
188 | route_map_index_delete (index, 0); | |
189 | ||
190 | name = map->name; | |
191 | map->head = NULL; | |
192 | ||
193 | /* Clear all dependencies */ | |
194 | route_map_clear_all_references(name); | |
195 | map->deleted = 1; | |
718e3744 | 196 | /* Execute deletion hook. */ |
197 | if (route_map_master.delete_hook) | |
518f0eb1 DS |
198 | { |
199 | (*route_map_master.delete_hook) (name); | |
200 | route_map_notify_dependencies(name, RMAP_EVENT_CALL_DELETED); | |
201 | } | |
718e3744 | 202 | |
518f0eb1 DS |
203 | if (!map->to_be_processed) |
204 | { | |
205 | route_map_free_map (map); | |
206 | } | |
718e3744 | 207 | } |
208 | ||
209 | /* Lookup route map by route map name string. */ | |
210 | struct route_map * | |
9035efaa | 211 | route_map_lookup_by_name (const char *name) |
718e3744 | 212 | { |
213 | struct route_map *map; | |
214 | ||
518f0eb1 DS |
215 | if (!name) |
216 | return NULL; | |
217 | ||
718e3744 | 218 | for (map = route_map_master.head; map; map = map->next) |
518f0eb1 | 219 | if ((strcmp (map->name, name) == 0) && (!map->deleted)) |
718e3744 | 220 | return map; |
221 | return NULL; | |
222 | } | |
223 | ||
518f0eb1 DS |
224 | int |
225 | route_map_mark_updated (const char *name, int del_later) | |
226 | { | |
227 | struct route_map *map; | |
228 | int ret = -1; | |
229 | ||
230 | /* We need to do this walk manually instead of calling lookup_by_name() | |
231 | * because the lookup function doesn't return route maps marked as | |
232 | * deleted. | |
233 | */ | |
234 | for (map = route_map_master.head; map; map = map->next) | |
235 | if (strcmp (map->name, name) == 0) | |
236 | { | |
237 | map->to_be_processed = 1; | |
238 | ret = 0; | |
239 | } | |
240 | ||
241 | return(ret); | |
242 | } | |
243 | ||
244 | int | |
245 | route_map_clear_updated (struct route_map *map) | |
246 | { | |
247 | int ret = -1; | |
248 | ||
249 | if (map) | |
250 | { | |
251 | map->to_be_processed = 0; | |
252 | if (map->deleted) | |
253 | route_map_free_map(map); | |
254 | } | |
255 | ||
256 | return (ret); | |
257 | } | |
258 | ||
718e3744 | 259 | /* Lookup route map. If there isn't route map create one and return |
260 | it. */ | |
8cc4198f | 261 | static struct route_map * |
9035efaa | 262 | route_map_get (const char *name) |
718e3744 | 263 | { |
264 | struct route_map *map; | |
265 | ||
266 | map = route_map_lookup_by_name (name); | |
267 | if (map == NULL) | |
268 | map = route_map_add (name); | |
518f0eb1 | 269 | |
718e3744 | 270 | return map; |
271 | } | |
272 | ||
518f0eb1 DS |
273 | void |
274 | route_map_walk_update_list (void *arg, | |
275 | int (*route_map_update_fn) (void *arg, char *name)) | |
276 | { | |
277 | struct route_map *node; | |
278 | struct route_map *nnode = NULL; | |
279 | ||
280 | for (node = route_map_master.head; node; node = nnode) | |
281 | { | |
282 | if (node->to_be_processed) | |
283 | { | |
284 | /* DD: Should we add any thread yield code here */ | |
285 | route_map_update_fn(arg, node->name); | |
286 | nnode = node->next; | |
287 | route_map_clear_updated(node); | |
288 | } | |
289 | else | |
290 | nnode = node->next; | |
291 | } | |
292 | } | |
293 | ||
718e3744 | 294 | /* Return route map's type string. */ |
8cc4198f | 295 | static const char * |
718e3744 | 296 | route_map_type_str (enum route_map_type type) |
297 | { | |
298 | switch (type) | |
299 | { | |
300 | case RMAP_PERMIT: | |
301 | return "permit"; | |
302 | break; | |
303 | case RMAP_DENY: | |
304 | return "deny"; | |
305 | break; | |
306 | default: | |
307 | return ""; | |
308 | break; | |
309 | } | |
310 | } | |
311 | ||
8cc4198f | 312 | static int |
718e3744 | 313 | route_map_empty (struct route_map *map) |
314 | { | |
315 | if (map->head == NULL && map->tail == NULL) | |
316 | return 1; | |
317 | else | |
318 | return 0; | |
319 | } | |
320 | ||
5510e83b | 321 | /* show route-map */ |
322 | static void | |
323 | vty_show_route_map_entry (struct vty *vty, struct route_map *map) | |
718e3744 | 324 | { |
718e3744 | 325 | struct route_map_index *index; |
326 | struct route_map_rule *rule; | |
327 | ||
fbf5d033 | 328 | /* Print the name of the protocol */ |
329 | if (zlog_default) | |
330 | vty_out (vty, "%s:%s", zlog_proto_names[zlog_default->protocol], | |
331 | VTY_NEWLINE); | |
332 | ||
5510e83b | 333 | for (index = map->head; index; index = index->next) |
334 | { | |
335 | vty_out (vty, "route-map %s, %s, sequence %d%s", | |
336 | map->name, route_map_type_str (index->type), | |
337 | index->pref, VTY_NEWLINE); | |
5bb4c198 | 338 | |
339 | /* Description */ | |
340 | if (index->description) | |
341 | vty_out (vty, " Description:%s %s%s", VTY_NEWLINE, | |
342 | index->description, VTY_NEWLINE); | |
5510e83b | 343 | |
344 | /* Match clauses */ | |
345 | vty_out (vty, " Match clauses:%s", VTY_NEWLINE); | |
346 | for (rule = index->match_list.head; rule; rule = rule->next) | |
347 | vty_out (vty, " %s %s%s", | |
348 | rule->cmd->str, rule->rule_str, VTY_NEWLINE); | |
349 | ||
350 | vty_out (vty, " Set clauses:%s", VTY_NEWLINE); | |
351 | for (rule = index->set_list.head; rule; rule = rule->next) | |
352 | vty_out (vty, " %s %s%s", | |
353 | rule->cmd->str, rule->rule_str, VTY_NEWLINE); | |
354 | ||
db29ae5f | 355 | /* Call clause */ |
356 | vty_out (vty, " Call clause:%s", VTY_NEWLINE); | |
fee0f4c6 | 357 | if (index->nextrm) |
358 | vty_out (vty, " Call %s%s", index->nextrm, VTY_NEWLINE); | |
db29ae5f | 359 | |
360 | /* Exit Policy */ | |
361 | vty_out (vty, " Action:%s", VTY_NEWLINE); | |
362 | if (index->exitpolicy == RMAP_GOTO) | |
5510e83b | 363 | vty_out (vty, " Goto %d%s", index->nextpref, VTY_NEWLINE); |
5510e83b | 364 | else if (index->exitpolicy == RMAP_NEXT) |
db29ae5f | 365 | vty_out (vty, " Continue to next entry%s", VTY_NEWLINE); |
5510e83b | 366 | else if (index->exitpolicy == RMAP_EXIT) |
367 | vty_out (vty, " Exit routemap%s", VTY_NEWLINE); | |
368 | } | |
718e3744 | 369 | } |
370 | ||
8cc4198f | 371 | static int |
9035efaa | 372 | vty_show_route_map (struct vty *vty, const char *name) |
5510e83b | 373 | { |
374 | struct route_map *map; | |
375 | ||
376 | if (name) | |
377 | { | |
378 | map = route_map_lookup_by_name (name); | |
379 | ||
380 | if (map) | |
381 | { | |
382 | vty_show_route_map_entry (vty, map); | |
383 | return CMD_SUCCESS; | |
384 | } | |
385 | else | |
386 | { | |
387 | vty_out (vty, "%%route-map %s not found%s", name, VTY_NEWLINE); | |
388 | return CMD_WARNING; | |
389 | } | |
390 | } | |
7514fb77 PJ |
391 | else |
392 | { | |
393 | for (map = route_map_master.head; map; map = map->next) | |
518f0eb1 DS |
394 | if (!map->deleted) |
395 | vty_show_route_map_entry (vty, map); | |
7514fb77 | 396 | } |
5510e83b | 397 | return CMD_SUCCESS; |
398 | } | |
399 | ||
400 | ||
718e3744 | 401 | /* New route map allocation. Please note route map's name must be |
402 | specified. */ | |
8cc4198f | 403 | static struct route_map_index * |
404 | route_map_index_new (void) | |
718e3744 | 405 | { |
406 | struct route_map_index *new; | |
407 | ||
408 | new = XCALLOC (MTYPE_ROUTE_MAP_INDEX, sizeof (struct route_map_index)); | |
409 | new->exitpolicy = RMAP_EXIT; /* Default to Cisco-style */ | |
410 | return new; | |
411 | } | |
412 | ||
413 | /* Free route map index. */ | |
414 | static void | |
415 | route_map_index_delete (struct route_map_index *index, int notify) | |
416 | { | |
417 | struct route_map_rule *rule; | |
418 | ||
419 | /* Free route match. */ | |
420 | while ((rule = index->match_list.head) != NULL) | |
421 | route_map_rule_delete (&index->match_list, rule); | |
422 | ||
423 | /* Free route set. */ | |
424 | while ((rule = index->set_list.head) != NULL) | |
425 | route_map_rule_delete (&index->set_list, rule); | |
426 | ||
427 | /* Remove index from route map list. */ | |
428 | if (index->next) | |
429 | index->next->prev = index->prev; | |
430 | else | |
431 | index->map->tail = index->prev; | |
432 | ||
433 | if (index->prev) | |
434 | index->prev->next = index->next; | |
435 | else | |
436 | index->map->head = index->next; | |
437 | ||
fee0f4c6 | 438 | /* Free 'char *nextrm' if not NULL */ |
439 | if (index->nextrm) | |
0241684e | 440 | XFREE (MTYPE_ROUTE_MAP_NAME, index->nextrm); |
fee0f4c6 | 441 | |
718e3744 | 442 | /* Execute event hook. */ |
443 | if (route_map_master.event_hook && notify) | |
518f0eb1 DS |
444 | { |
445 | (*route_map_master.event_hook) (RMAP_EVENT_INDEX_DELETED, | |
446 | index->map->name); | |
447 | route_map_notify_dependencies(index->map->name, RMAP_EVENT_CALL_ADDED); | |
448 | } | |
718e3744 | 449 | XFREE (MTYPE_ROUTE_MAP_INDEX, index); |
450 | } | |
451 | ||
452 | /* Lookup index from route map. */ | |
8cc4198f | 453 | static struct route_map_index * |
718e3744 | 454 | route_map_index_lookup (struct route_map *map, enum route_map_type type, |
455 | int pref) | |
456 | { | |
457 | struct route_map_index *index; | |
458 | ||
459 | for (index = map->head; index; index = index->next) | |
460 | if ((index->type == type || type == RMAP_ANY) | |
461 | && index->pref == pref) | |
462 | return index; | |
463 | return NULL; | |
464 | } | |
465 | ||
466 | /* Add new index to route map. */ | |
8cc4198f | 467 | static struct route_map_index * |
718e3744 | 468 | route_map_index_add (struct route_map *map, enum route_map_type type, |
469 | int pref) | |
470 | { | |
471 | struct route_map_index *index; | |
472 | struct route_map_index *point; | |
473 | ||
474 | /* Allocate new route map inex. */ | |
475 | index = route_map_index_new (); | |
476 | index->map = map; | |
477 | index->type = type; | |
478 | index->pref = pref; | |
479 | ||
480 | /* Compare preference. */ | |
481 | for (point = map->head; point; point = point->next) | |
482 | if (point->pref >= pref) | |
483 | break; | |
484 | ||
485 | if (map->head == NULL) | |
486 | { | |
487 | map->head = map->tail = index; | |
488 | } | |
489 | else if (point == NULL) | |
490 | { | |
491 | index->prev = map->tail; | |
492 | map->tail->next = index; | |
493 | map->tail = index; | |
494 | } | |
495 | else if (point == map->head) | |
496 | { | |
497 | index->next = map->head; | |
498 | map->head->prev = index; | |
499 | map->head = index; | |
500 | } | |
501 | else | |
502 | { | |
503 | index->next = point; | |
504 | index->prev = point->prev; | |
505 | if (point->prev) | |
506 | point->prev->next = index; | |
507 | point->prev = index; | |
508 | } | |
509 | ||
510 | /* Execute event hook. */ | |
511 | if (route_map_master.event_hook) | |
518f0eb1 DS |
512 | { |
513 | (*route_map_master.event_hook) (RMAP_EVENT_INDEX_ADDED, | |
514 | map->name); | |
515 | route_map_notify_dependencies (map->name, RMAP_EVENT_CALL_ADDED); | |
516 | } | |
718e3744 | 517 | return index; |
518 | } | |
519 | ||
520 | /* Get route map index. */ | |
8cc4198f | 521 | static struct route_map_index * |
718e3744 | 522 | route_map_index_get (struct route_map *map, enum route_map_type type, |
523 | int pref) | |
524 | { | |
525 | struct route_map_index *index; | |
526 | ||
527 | index = route_map_index_lookup (map, RMAP_ANY, pref); | |
528 | if (index && index->type != type) | |
529 | { | |
530 | /* Delete index from route map. */ | |
531 | route_map_index_delete (index, 1); | |
532 | index = NULL; | |
533 | } | |
534 | if (index == NULL) | |
535 | index = route_map_index_add (map, type, pref); | |
536 | return index; | |
537 | } | |
538 | ||
539 | /* New route map rule */ | |
8cc4198f | 540 | static struct route_map_rule * |
541 | route_map_rule_new (void) | |
718e3744 | 542 | { |
543 | struct route_map_rule *new; | |
544 | ||
545 | new = XCALLOC (MTYPE_ROUTE_MAP_RULE, sizeof (struct route_map_rule)); | |
546 | return new; | |
547 | } | |
6b0655a2 | 548 | |
718e3744 | 549 | /* Install rule command to the match list. */ |
550 | void | |
551 | route_map_install_match (struct route_map_rule_cmd *cmd) | |
552 | { | |
553 | vector_set (route_match_vec, cmd); | |
554 | } | |
555 | ||
556 | /* Install rule command to the set list. */ | |
557 | void | |
558 | route_map_install_set (struct route_map_rule_cmd *cmd) | |
559 | { | |
560 | vector_set (route_set_vec, cmd); | |
561 | } | |
562 | ||
563 | /* Lookup rule command from match list. */ | |
8cc4198f | 564 | static struct route_map_rule_cmd * |
27a43a81 | 565 | route_map_lookup_match (const char *name) |
718e3744 | 566 | { |
8c328f11 | 567 | unsigned int i; |
718e3744 | 568 | struct route_map_rule_cmd *rule; |
569 | ||
55468c86 | 570 | for (i = 0; i < vector_active (route_match_vec); i++) |
718e3744 | 571 | if ((rule = vector_slot (route_match_vec, i)) != NULL) |
572 | if (strcmp (rule->str, name) == 0) | |
573 | return rule; | |
574 | return NULL; | |
575 | } | |
576 | ||
577 | /* Lookup rule command from set list. */ | |
8cc4198f | 578 | static struct route_map_rule_cmd * |
27a43a81 | 579 | route_map_lookup_set (const char *name) |
718e3744 | 580 | { |
8c328f11 | 581 | unsigned int i; |
718e3744 | 582 | struct route_map_rule_cmd *rule; |
583 | ||
55468c86 | 584 | for (i = 0; i < vector_active (route_set_vec); i++) |
718e3744 | 585 | if ((rule = vector_slot (route_set_vec, i)) != NULL) |
586 | if (strcmp (rule->str, name) == 0) | |
587 | return rule; | |
588 | return NULL; | |
589 | } | |
590 | ||
591 | /* Add match and set rule to rule list. */ | |
592 | static void | |
593 | route_map_rule_add (struct route_map_rule_list *list, | |
594 | struct route_map_rule *rule) | |
595 | { | |
596 | rule->next = NULL; | |
597 | rule->prev = list->tail; | |
598 | if (list->tail) | |
599 | list->tail->next = rule; | |
600 | else | |
601 | list->head = rule; | |
602 | list->tail = rule; | |
603 | } | |
604 | ||
605 | /* Delete rule from rule list. */ | |
606 | static void | |
607 | route_map_rule_delete (struct route_map_rule_list *list, | |
608 | struct route_map_rule *rule) | |
609 | { | |
610 | if (rule->cmd->func_free) | |
611 | (*rule->cmd->func_free) (rule->value); | |
612 | ||
613 | if (rule->rule_str) | |
614 | XFREE (MTYPE_ROUTE_MAP_RULE_STR, rule->rule_str); | |
615 | ||
616 | if (rule->next) | |
617 | rule->next->prev = rule->prev; | |
618 | else | |
619 | list->tail = rule->prev; | |
620 | if (rule->prev) | |
621 | rule->prev->next = rule->next; | |
622 | else | |
623 | list->head = rule->next; | |
624 | ||
625 | XFREE (MTYPE_ROUTE_MAP_RULE, rule); | |
626 | } | |
627 | ||
628 | /* strcmp wrapper function which don't crush even argument is NULL. */ | |
8cc4198f | 629 | static int |
c9eca01b | 630 | rulecmp (const char *dst, const char *src) |
718e3744 | 631 | { |
632 | if (dst == NULL) | |
633 | { | |
634 | if (src == NULL) | |
635 | return 0; | |
636 | else | |
637 | return 1; | |
638 | } | |
639 | else | |
640 | { | |
641 | if (src == NULL) | |
642 | return 1; | |
643 | else | |
644 | return strcmp (dst, src); | |
645 | } | |
646 | return 1; | |
647 | } | |
648 | ||
518f0eb1 DS |
649 | /* Use this to return the already specified argument for this match. This is |
650 | * useful to get the specified argument with a route map match rule when the | |
651 | * rule is being deleted and the argument is not provided. | |
652 | */ | |
653 | const char * | |
654 | route_map_get_match_arg(struct route_map_index *index, const char *match_name) | |
655 | { | |
656 | struct route_map_rule *rule; | |
657 | struct route_map_rule_cmd *cmd; | |
658 | ||
659 | /* First lookup rule for add match statement. */ | |
660 | cmd = route_map_lookup_match (match_name); | |
661 | if (cmd == NULL) | |
662 | return NULL; | |
663 | ||
664 | for (rule = index->match_list.head; rule; rule = rule->next) | |
665 | if (rule->cmd == cmd && rule->rule_str != NULL) | |
666 | return (rule->rule_str); | |
667 | ||
668 | return (NULL); | |
669 | } | |
670 | ||
718e3744 | 671 | /* Add match statement to route map. */ |
672 | int | |
27a43a81 | 673 | route_map_add_match (struct route_map_index *index, const char *match_name, |
c9eca01b | 674 | const char *match_arg) |
718e3744 | 675 | { |
676 | struct route_map_rule *rule; | |
677 | struct route_map_rule *next; | |
678 | struct route_map_rule_cmd *cmd; | |
679 | void *compile; | |
680 | int replaced = 0; | |
681 | ||
682 | /* First lookup rule for add match statement. */ | |
683 | cmd = route_map_lookup_match (match_name); | |
684 | if (cmd == NULL) | |
685 | return RMAP_RULE_MISSING; | |
686 | ||
687 | /* Next call compile function for this match statement. */ | |
688 | if (cmd->func_compile) | |
689 | { | |
690 | compile= (*cmd->func_compile)(match_arg); | |
691 | if (compile == NULL) | |
692 | return RMAP_COMPILE_ERROR; | |
693 | } | |
694 | else | |
695 | compile = NULL; | |
696 | ||
697 | /* If argument is completely same ignore it. */ | |
698 | for (rule = index->match_list.head; rule; rule = next) | |
699 | { | |
700 | next = rule->next; | |
701 | if (rule->cmd == cmd) | |
702 | { | |
703 | route_map_rule_delete (&index->match_list, rule); | |
704 | replaced = 1; | |
705 | } | |
706 | } | |
707 | ||
708 | /* Add new route map match rule. */ | |
709 | rule = route_map_rule_new (); | |
710 | rule->cmd = cmd; | |
711 | rule->value = compile; | |
712 | if (match_arg) | |
713 | rule->rule_str = XSTRDUP (MTYPE_ROUTE_MAP_RULE_STR, match_arg); | |
714 | else | |
715 | rule->rule_str = NULL; | |
716 | ||
717 | /* Add new route match rule to linked list. */ | |
718 | route_map_rule_add (&index->match_list, rule); | |
719 | ||
720 | /* Execute event hook. */ | |
721 | if (route_map_master.event_hook) | |
518f0eb1 DS |
722 | { |
723 | (*route_map_master.event_hook) (replaced ? | |
724 | RMAP_EVENT_MATCH_REPLACED: | |
725 | RMAP_EVENT_MATCH_ADDED, | |
726 | index->map->name); | |
727 | route_map_notify_dependencies(index->map->name, RMAP_EVENT_CALL_ADDED); | |
728 | } | |
718e3744 | 729 | |
730 | return 0; | |
731 | } | |
732 | ||
733 | /* Delete specified route match rule. */ | |
734 | int | |
27a43a81 | 735 | route_map_delete_match (struct route_map_index *index, const char *match_name, |
c9eca01b | 736 | const char *match_arg) |
718e3744 | 737 | { |
738 | struct route_map_rule *rule; | |
739 | struct route_map_rule_cmd *cmd; | |
740 | ||
741 | cmd = route_map_lookup_match (match_name); | |
742 | if (cmd == NULL) | |
743 | return 1; | |
744 | ||
745 | for (rule = index->match_list.head; rule; rule = rule->next) | |
746 | if (rule->cmd == cmd && | |
747 | (rulecmp (rule->rule_str, match_arg) == 0 || match_arg == NULL)) | |
748 | { | |
749 | route_map_rule_delete (&index->match_list, rule); | |
750 | /* Execute event hook. */ | |
751 | if (route_map_master.event_hook) | |
518f0eb1 DS |
752 | { |
753 | (*route_map_master.event_hook) (RMAP_EVENT_MATCH_DELETED, | |
754 | index->map->name); | |
755 | route_map_notify_dependencies(index->map->name, RMAP_EVENT_CALL_ADDED); | |
756 | } | |
718e3744 | 757 | return 0; |
758 | } | |
759 | /* Can't find matched rule. */ | |
760 | return 1; | |
761 | } | |
762 | ||
763 | /* Add route-map set statement to the route map. */ | |
764 | int | |
27a43a81 | 765 | route_map_add_set (struct route_map_index *index, const char *set_name, |
c9eca01b | 766 | const char *set_arg) |
718e3744 | 767 | { |
768 | struct route_map_rule *rule; | |
769 | struct route_map_rule *next; | |
770 | struct route_map_rule_cmd *cmd; | |
771 | void *compile; | |
772 | int replaced = 0; | |
773 | ||
774 | cmd = route_map_lookup_set (set_name); | |
775 | if (cmd == NULL) | |
776 | return RMAP_RULE_MISSING; | |
777 | ||
778 | /* Next call compile function for this match statement. */ | |
779 | if (cmd->func_compile) | |
780 | { | |
781 | compile= (*cmd->func_compile)(set_arg); | |
782 | if (compile == NULL) | |
783 | return RMAP_COMPILE_ERROR; | |
784 | } | |
785 | else | |
786 | compile = NULL; | |
787 | ||
788 | /* Add by WJL. if old set command of same kind exist, delete it first | |
789 | to ensure only one set command of same kind exist under a | |
790 | route_map_index. */ | |
791 | for (rule = index->set_list.head; rule; rule = next) | |
792 | { | |
793 | next = rule->next; | |
794 | if (rule->cmd == cmd) | |
795 | { | |
796 | route_map_rule_delete (&index->set_list, rule); | |
797 | replaced = 1; | |
798 | } | |
799 | } | |
800 | ||
801 | /* Add new route map match rule. */ | |
802 | rule = route_map_rule_new (); | |
803 | rule->cmd = cmd; | |
804 | rule->value = compile; | |
805 | if (set_arg) | |
806 | rule->rule_str = XSTRDUP (MTYPE_ROUTE_MAP_RULE_STR, set_arg); | |
807 | else | |
808 | rule->rule_str = NULL; | |
809 | ||
810 | /* Add new route match rule to linked list. */ | |
811 | route_map_rule_add (&index->set_list, rule); | |
812 | ||
813 | /* Execute event hook. */ | |
814 | if (route_map_master.event_hook) | |
518f0eb1 DS |
815 | { |
816 | (*route_map_master.event_hook) (replaced ? | |
817 | RMAP_EVENT_SET_REPLACED: | |
818 | RMAP_EVENT_SET_ADDED, | |
819 | index->map->name); | |
820 | route_map_notify_dependencies(index->map->name, RMAP_EVENT_CALL_ADDED); | |
821 | } | |
718e3744 | 822 | return 0; |
823 | } | |
824 | ||
825 | /* Delete route map set rule. */ | |
826 | int | |
27a43a81 | 827 | route_map_delete_set (struct route_map_index *index, const char *set_name, |
c9eca01b | 828 | const char *set_arg) |
718e3744 | 829 | { |
830 | struct route_map_rule *rule; | |
831 | struct route_map_rule_cmd *cmd; | |
832 | ||
833 | cmd = route_map_lookup_set (set_name); | |
834 | if (cmd == NULL) | |
835 | return 1; | |
836 | ||
837 | for (rule = index->set_list.head; rule; rule = rule->next) | |
838 | if ((rule->cmd == cmd) && | |
839 | (rulecmp (rule->rule_str, set_arg) == 0 || set_arg == NULL)) | |
840 | { | |
841 | route_map_rule_delete (&index->set_list, rule); | |
842 | /* Execute event hook. */ | |
843 | if (route_map_master.event_hook) | |
518f0eb1 DS |
844 | { |
845 | (*route_map_master.event_hook) (RMAP_EVENT_SET_DELETED, | |
846 | index->map->name); | |
847 | route_map_notify_dependencies(index->map->name, RMAP_EVENT_CALL_ADDED); | |
848 | } | |
718e3744 | 849 | return 0; |
850 | } | |
851 | /* Can't find matched rule. */ | |
852 | return 1; | |
853 | } | |
854 | ||
3bf1c917 | 855 | /* Apply route map's each index to the object. |
856 | ||
857 | The matrix for a route-map looks like this: | |
858 | (note, this includes the description for the "NEXT" | |
859 | and "GOTO" frobs now | |
860 | ||
861 | Match | No Match | |
862 | | | |
863 | permit action | cont | |
864 | | | |
865 | ------------------+--------------- | |
866 | | | |
867 | deny deny | cont | |
868 | | | |
869 | ||
fee0f4c6 | 870 | action) |
871 | -Apply Set statements, accept route | |
872 | -If Call statement is present jump to the specified route-map, if it | |
873 | denies the route we finish. | |
874 | -If NEXT is specified, goto NEXT statement | |
875 | -If GOTO is specified, goto the first clause where pref > nextpref | |
876 | -If nothing is specified, do as Cisco and finish | |
877 | deny) | |
878 | -Route is denied by route-map. | |
879 | cont) | |
880 | -Goto Next index | |
3bf1c917 | 881 | |
882 | If we get no matches after we've processed all updates, then the route | |
883 | is dropped too. | |
884 | ||
fee0f4c6 | 885 | Some notes on the new "CALL", "NEXT" and "GOTO" |
886 | call WORD - If this clause is matched, then the set statements | |
887 | are executed and then we jump to route-map 'WORD'. If | |
888 | this route-map denies the route, we finish, in other case we | |
889 | do whatever the exit policy (EXIT, NEXT or GOTO) tells. | |
3bf1c917 | 890 | on-match next - If this clause is matched, then the set statements |
891 | are executed and then we drop through to the next clause | |
892 | on-match goto n - If this clause is matched, then the set statments | |
893 | are executed and then we goto the nth clause, or the | |
894 | first clause greater than this. In order to ensure | |
895 | route-maps *always* exit, you cannot jump backwards. | |
896 | Sorry ;) | |
897 | ||
898 | We need to make sure our route-map processing matches the above | |
718e3744 | 899 | */ |
3bf1c917 | 900 | |
8cc4198f | 901 | static route_map_result_t |
3bf1c917 | 902 | route_map_apply_match (struct route_map_rule_list *match_list, |
903 | struct prefix *prefix, route_map_object_t type, | |
904 | void *object) | |
718e3744 | 905 | { |
3bf1c917 | 906 | route_map_result_t ret = RMAP_NOMATCH; |
718e3744 | 907 | struct route_map_rule *match; |
718e3744 | 908 | |
718e3744 | 909 | |
3bf1c917 | 910 | /* Check all match rule and if there is no match rule, go to the |
911 | set statement. */ | |
912 | if (!match_list->head) | |
913 | ret = RMAP_MATCH; | |
914 | else | |
718e3744 | 915 | { |
3bf1c917 | 916 | for (match = match_list->head; match; match = match->next) |
917 | { | |
918 | /* Try each match statement in turn, If any do not return | |
919 | RMAP_MATCH, return, otherwise continue on to next match | |
920 | statement. All match statements must match for end-result | |
921 | to be a match. */ | |
922 | ret = (*match->cmd->func_apply) (match->value, prefix, | |
923 | type, object); | |
924 | if (ret != RMAP_MATCH) | |
925 | return ret; | |
926 | } | |
718e3744 | 927 | } |
3bf1c917 | 928 | return ret; |
718e3744 | 929 | } |
930 | ||
931 | /* Apply route map to the object. */ | |
932 | route_map_result_t | |
3bf1c917 | 933 | route_map_apply (struct route_map *map, struct prefix *prefix, |
934 | route_map_object_t type, void *object) | |
718e3744 | 935 | { |
fee0f4c6 | 936 | static int recursion = 0; |
718e3744 | 937 | int ret = 0; |
938 | struct route_map_index *index; | |
3bf1c917 | 939 | struct route_map_rule *set; |
718e3744 | 940 | |
fee0f4c6 | 941 | if (recursion > RMAP_RECURSION_LIMIT) |
942 | { | |
943 | zlog (NULL, LOG_WARNING, | |
944 | "route-map recursion limit (%d) reached, discarding route", | |
945 | RMAP_RECURSION_LIMIT); | |
946 | recursion = 0; | |
947 | return RMAP_DENYMATCH; | |
948 | } | |
949 | ||
718e3744 | 950 | if (map == NULL) |
951 | return RMAP_DENYMATCH; | |
952 | ||
953 | for (index = map->head; index; index = index->next) | |
954 | { | |
3bf1c917 | 955 | /* Apply this index. */ |
956 | ret = route_map_apply_match (&index->match_list, prefix, type, object); | |
957 | ||
958 | /* Now we apply the matrix from above */ | |
959 | if (ret == RMAP_NOMATCH) | |
960 | /* 'cont' from matrix - continue to next route-map sequence */ | |
961 | continue; | |
962 | else if (ret == RMAP_MATCH) | |
963 | { | |
964 | if (index->type == RMAP_PERMIT) | |
965 | /* 'action' */ | |
966 | { | |
967 | /* permit+match must execute sets */ | |
968 | for (set = index->set_list.head; set; set = set->next) | |
969 | ret = (*set->cmd->func_apply) (set->value, prefix, | |
970 | type, object); | |
fee0f4c6 | 971 | |
972 | /* Call another route-map if available */ | |
973 | if (index->nextrm) | |
974 | { | |
975 | struct route_map *nextrm = | |
976 | route_map_lookup_by_name (index->nextrm); | |
977 | ||
978 | if (nextrm) /* Target route-map found, jump to it */ | |
979 | { | |
980 | recursion++; | |
981 | ret = route_map_apply (nextrm, prefix, type, object); | |
982 | recursion--; | |
983 | } | |
984 | ||
985 | /* If nextrm returned 'deny', finish. */ | |
986 | if (ret == RMAP_DENYMATCH) | |
987 | return ret; | |
988 | } | |
989 | ||
3bf1c917 | 990 | switch (index->exitpolicy) |
991 | { | |
992 | case RMAP_EXIT: | |
993 | return ret; | |
994 | case RMAP_NEXT: | |
995 | continue; | |
996 | case RMAP_GOTO: | |
997 | { | |
998 | /* Find the next clause to jump to */ | |
999 | struct route_map_index *next = index->next; | |
fee0f4c6 | 1000 | int nextpref = index->nextpref; |
3bf1c917 | 1001 | |
fee0f4c6 | 1002 | while (next && next->pref < nextpref) |
3bf1c917 | 1003 | { |
1004 | index = next; | |
1005 | next = next->next; | |
1006 | } | |
1007 | if (next == NULL) | |
1008 | { | |
1009 | /* No clauses match! */ | |
1010 | return ret; | |
1011 | } | |
1012 | } | |
1013 | } | |
1014 | } | |
1015 | else if (index->type == RMAP_DENY) | |
1016 | /* 'deny' */ | |
1017 | { | |
3bf1c917 | 1018 | return RMAP_DENYMATCH; |
1019 | } | |
1020 | } | |
718e3744 | 1021 | } |
1022 | /* Finally route-map does not match at all. */ | |
1023 | return RMAP_DENYMATCH; | |
1024 | } | |
1025 | ||
1026 | void | |
9035efaa | 1027 | route_map_add_hook (void (*func) (const char *)) |
718e3744 | 1028 | { |
1029 | route_map_master.add_hook = func; | |
1030 | } | |
1031 | ||
1032 | void | |
9035efaa | 1033 | route_map_delete_hook (void (*func) (const char *)) |
718e3744 | 1034 | { |
1035 | route_map_master.delete_hook = func; | |
1036 | } | |
1037 | ||
1038 | void | |
9035efaa | 1039 | route_map_event_hook (void (*func) (route_map_event_t, const char *)) |
718e3744 | 1040 | { |
1041 | route_map_master.event_hook = func; | |
1042 | } | |
1043 | ||
1044 | void | |
8cc4198f | 1045 | route_map_init (void) |
718e3744 | 1046 | { |
1047 | /* Make vector for match and set. */ | |
1048 | route_match_vec = vector_init (1); | |
1049 | route_set_vec = vector_init (1); | |
1050 | } | |
228da428 CC |
1051 | |
1052 | void | |
1053 | route_map_finish (void) | |
1054 | { | |
1055 | vector_free (route_match_vec); | |
1056 | route_match_vec = NULL; | |
1057 | vector_free (route_set_vec); | |
1058 | route_set_vec = NULL; | |
1059 | } | |
6b0655a2 | 1060 | |
518f0eb1 DS |
1061 | /* Routines for route map dependency lists and dependency processing */ |
1062 | static int | |
1063 | route_map_rmap_hash_cmp (const void *p1, const void *p2) | |
1064 | { | |
1065 | return (strcmp((char *)p1, (char *)p2) == 0); | |
1066 | } | |
1067 | ||
1068 | static int | |
1069 | route_map_dep_hash_cmp (const void *p1, const void *p2) | |
1070 | { | |
1071 | ||
1072 | return (strcmp (((struct route_map_dep *)p1)->dep_name, (char *)p2) == 0); | |
1073 | } | |
1074 | ||
1075 | static void | |
1076 | route_map_rmap_free(struct hash_backet *backet, void *arg) | |
1077 | { | |
1078 | char *rmap_name = (char *)backet->data; | |
1079 | ||
1080 | if (rmap_name) | |
1081 | XFREE(MTYPE_ROUTE_MAP_NAME, rmap_name); | |
1082 | } | |
1083 | ||
1084 | static void | |
1085 | route_map_dep_hash_free (struct hash_backet *backet, void *arg) | |
1086 | { | |
1087 | struct route_map_dep *dep = (struct route_map_dep *)backet->data; | |
1088 | ||
1089 | if (!dep) | |
1090 | return; | |
1091 | ||
1092 | if (dep->dep_rmap_hash) | |
1093 | hash_iterate (dep->dep_rmap_hash, route_map_rmap_free, (void *)NULL); | |
1094 | ||
1095 | hash_free(dep->dep_rmap_hash); | |
1096 | XFREE(MTYPE_ROUTE_MAP_NAME, dep->dep_name); | |
1097 | XFREE(MTYPE_ROUTE_MAP_DEP, dep); | |
1098 | } | |
1099 | ||
1100 | static void | |
1101 | route_map_clear_reference(struct hash_backet *backet, void *arg) | |
1102 | { | |
1103 | struct route_map_dep *dep = (struct route_map_dep *)backet->data; | |
1104 | char *rmap_name; | |
1105 | ||
1106 | if (dep && arg) | |
1107 | { | |
1108 | rmap_name = (char *)hash_release(dep->dep_rmap_hash, (void *)arg); | |
1109 | if (rmap_name) | |
1110 | { | |
1111 | XFREE(MTYPE_ROUTE_MAP_NAME, rmap_name); | |
1112 | } | |
1113 | if (!dep->dep_rmap_hash->count) | |
1114 | { | |
1115 | dep = hash_release(dep->this_hash, (void *)dep->dep_name); | |
1116 | hash_free(dep->dep_rmap_hash); | |
1117 | XFREE(MTYPE_ROUTE_MAP_NAME, dep->dep_name); | |
1118 | XFREE(MTYPE_ROUTE_MAP_DEP, dep); | |
1119 | } | |
1120 | } | |
1121 | } | |
1122 | ||
1123 | static void | |
1124 | route_map_clear_all_references (char *rmap_name) | |
1125 | { | |
1126 | int i; | |
1127 | ||
1128 | for (i = 1; i < ROUTE_MAP_DEP_MAX; i++) | |
1129 | { | |
1130 | hash_iterate(route_map_dep_hash[i], route_map_clear_reference, | |
1131 | (void *)rmap_name); | |
1132 | } | |
1133 | } | |
1134 | ||
1135 | static void * | |
1136 | route_map_dep_hash_alloc(void *p) | |
1137 | { | |
1138 | char *dep_name = (char *)p; | |
1139 | struct route_map_dep *dep_entry; | |
1140 | ||
1141 | dep_entry = XCALLOC(MTYPE_ROUTE_MAP_DEP, sizeof(struct route_map_dep)); | |
1142 | dep_entry->dep_name = XSTRDUP(MTYPE_ROUTE_MAP_NAME, dep_name); | |
1143 | dep_entry->dep_rmap_hash = hash_create(route_map_dep_hash_make_key, | |
1144 | route_map_rmap_hash_cmp); | |
1145 | dep_entry->this_hash = NULL; | |
1146 | ||
1147 | return((void *)dep_entry); | |
1148 | } | |
1149 | ||
1150 | static void * | |
1151 | route_map_name_hash_alloc(void *p) | |
1152 | { | |
1153 | return((void *)XSTRDUP(MTYPE_ROUTE_MAP_NAME, (char *)p)); | |
1154 | } | |
1155 | ||
1156 | static unsigned int | |
1157 | route_map_dep_hash_make_key (void *p) | |
1158 | { | |
1159 | return (string_hash_make((char *)p)); | |
1160 | } | |
1161 | ||
1162 | static void | |
1163 | route_map_print_dependency (struct hash_backet *backet, void *data) | |
1164 | { | |
1165 | char *rmap_name = (char *)backet->data; | |
1166 | char *dep_name = (char *)data; | |
1167 | ||
1168 | if (rmap_name) | |
1169 | zlog_debug("%s: Dependency for %s: %s", __FUNCTION__, dep_name, rmap_name); | |
1170 | } | |
1171 | ||
1172 | static int | |
1173 | route_map_dep_update (struct hash *dephash, const char *dep_name, | |
1174 | const char *rmap_name, | |
1175 | route_map_event_t type) | |
1176 | { | |
1177 | struct route_map_dep *dep; | |
1178 | char *ret_map_name; | |
1179 | ||
1180 | switch (type) | |
1181 | { | |
1182 | case RMAP_EVENT_PLIST_ADDED: | |
1183 | case RMAP_EVENT_CLIST_ADDED: | |
1184 | case RMAP_EVENT_ECLIST_ADDED: | |
1185 | case RMAP_EVENT_ASLIST_ADDED: | |
1186 | case RMAP_EVENT_CALL_ADDED: | |
1187 | case RMAP_EVENT_FILTER_ADDED: | |
1188 | if (rmap_debug) | |
1189 | zlog_debug("%s: Adding dependency for %s in %s", __FUNCTION__, | |
1190 | dep_name, rmap_name); | |
1191 | dep = (struct route_map_dep *) hash_get (dephash, (void *)dep_name, | |
1192 | route_map_dep_hash_alloc); | |
1193 | if (!dep) | |
1194 | return -1; | |
1195 | ||
1196 | if (!dep->this_hash) | |
1197 | dep->this_hash = dephash; | |
1198 | ||
1199 | hash_get(dep->dep_rmap_hash, rmap_name, route_map_name_hash_alloc); | |
1200 | break; | |
1201 | case RMAP_EVENT_PLIST_DELETED: | |
1202 | case RMAP_EVENT_CLIST_DELETED: | |
1203 | case RMAP_EVENT_ECLIST_DELETED: | |
1204 | case RMAP_EVENT_ASLIST_DELETED: | |
1205 | case RMAP_EVENT_CALL_DELETED: | |
1206 | case RMAP_EVENT_FILTER_DELETED: | |
1207 | if (rmap_debug) | |
1208 | zlog_debug("%s: Deleting dependency for %s in %s", __FUNCTION__, | |
1209 | dep_name, rmap_name); | |
1210 | dep = (struct route_map_dep *) hash_get (dephash, (void *)dep_name, | |
1211 | NULL); | |
1212 | if (!dep) | |
1213 | return 0; | |
1214 | ret_map_name = (char *)hash_release(dep->dep_rmap_hash, (void *)rmap_name); | |
1215 | if (ret_map_name) | |
1216 | XFREE(MTYPE_ROUTE_MAP_NAME, ret_map_name); | |
1217 | ||
1218 | if (!dep->dep_rmap_hash->count) | |
1219 | { | |
1220 | dep = hash_release(dephash, (void *)dep_name); | |
1221 | hash_free(dep->dep_rmap_hash); | |
1222 | XFREE(MTYPE_ROUTE_MAP_NAME, dep->dep_name); | |
1223 | XFREE(MTYPE_ROUTE_MAP_DEP, dep); | |
1224 | dep = NULL; | |
1225 | } | |
1226 | break; | |
1227 | default: | |
1228 | break; | |
1229 | } | |
1230 | ||
1231 | if (dep) | |
1232 | { | |
1233 | if (rmap_debug) | |
1234 | hash_iterate (dep->dep_rmap_hash, route_map_print_dependency, (void *)dep_name); | |
1235 | } | |
1236 | return 0; | |
1237 | } | |
1238 | ||
1239 | static struct hash * | |
1240 | route_map_get_dep_hash (route_map_event_t event) | |
1241 | { | |
1242 | struct hash *upd8_hash = NULL; | |
1243 | ||
1244 | switch (event) | |
1245 | { | |
1246 | case RMAP_EVENT_PLIST_ADDED: | |
1247 | case RMAP_EVENT_PLIST_DELETED: | |
1248 | upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_PLIST]; | |
1249 | break; | |
1250 | case RMAP_EVENT_CLIST_ADDED: | |
1251 | case RMAP_EVENT_CLIST_DELETED: | |
1252 | upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_CLIST]; | |
1253 | break; | |
1254 | case RMAP_EVENT_ECLIST_ADDED: | |
1255 | case RMAP_EVENT_ECLIST_DELETED: | |
1256 | upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_ECLIST]; | |
1257 | break; | |
1258 | case RMAP_EVENT_ASLIST_ADDED: | |
1259 | case RMAP_EVENT_ASLIST_DELETED: | |
1260 | upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_ASPATH]; | |
1261 | break; | |
1262 | case RMAP_EVENT_CALL_ADDED: | |
1263 | case RMAP_EVENT_CALL_DELETED: | |
1264 | upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_RMAP]; | |
1265 | break; | |
1266 | case RMAP_EVENT_FILTER_ADDED: | |
1267 | case RMAP_EVENT_FILTER_DELETED: | |
1268 | upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_FILTER]; | |
1269 | break; | |
1270 | default: | |
1271 | upd8_hash = NULL; | |
1272 | break; | |
1273 | } | |
1274 | return (upd8_hash); | |
1275 | } | |
1276 | ||
1277 | static void | |
1278 | route_map_process_dependency (struct hash_backet *backet, void *data) | |
1279 | { | |
1280 | char *rmap_name; | |
1281 | route_map_event_t type = (route_map_event_t )data; | |
1282 | ||
1283 | rmap_name = (char *)backet->data; | |
1284 | ||
1285 | if (rmap_name) | |
1286 | { | |
1287 | if (rmap_debug) | |
1288 | zlog_debug("%s: Notifying %s of dependency", __FUNCTION__, | |
1289 | rmap_name); | |
1290 | if (route_map_master.event_hook) | |
1291 | (*route_map_master.event_hook) (type, rmap_name); | |
1292 | } | |
1293 | } | |
1294 | ||
1295 | void | |
1296 | route_map_upd8_dependency (route_map_event_t type, const char *arg, | |
1297 | const char *rmap_name) | |
1298 | { | |
1299 | struct hash *upd8_hash = NULL; | |
1300 | ||
1301 | if ((upd8_hash = route_map_get_dep_hash(type))) | |
1302 | route_map_dep_update (upd8_hash, arg, rmap_name, type); | |
1303 | } | |
1304 | ||
1305 | void | |
1306 | route_map_notify_dependencies (const char *affected_name, route_map_event_t event) | |
1307 | { | |
1308 | struct route_map_dep *dep; | |
1309 | struct hash *upd8_hash; | |
1310 | ||
1311 | if (!affected_name) | |
1312 | return; | |
1313 | ||
1314 | if ((upd8_hash = route_map_get_dep_hash(event)) == NULL) | |
1315 | return; | |
1316 | ||
1317 | dep = (struct route_map_dep *)hash_get (upd8_hash, (void *)affected_name, | |
1318 | NULL); | |
1319 | if (dep) | |
1320 | { | |
1321 | if (!dep->this_hash) | |
1322 | dep->this_hash = upd8_hash; | |
1323 | ||
1324 | hash_iterate (dep->dep_rmap_hash, route_map_process_dependency, (void *)event); | |
1325 | } | |
1326 | } | |
1327 | ||
718e3744 | 1328 | /* VTY related functions. */ |
1329 | DEFUN (route_map, | |
1330 | route_map_cmd, | |
1331 | "route-map WORD (deny|permit) <1-65535>", | |
1332 | "Create route-map or enter route-map command mode\n" | |
1333 | "Route map tag\n" | |
1334 | "Route map denies set operations\n" | |
1335 | "Route map permits set operations\n" | |
1336 | "Sequence to insert to/delete from existing route-map entry\n") | |
1337 | { | |
1338 | int permit; | |
1339 | unsigned long pref; | |
1340 | struct route_map *map; | |
1341 | struct route_map_index *index; | |
1342 | char *endptr = NULL; | |
1343 | ||
1344 | /* Permit check. */ | |
1345 | if (strncmp (argv[1], "permit", strlen (argv[1])) == 0) | |
1346 | permit = RMAP_PERMIT; | |
1347 | else if (strncmp (argv[1], "deny", strlen (argv[1])) == 0) | |
1348 | permit = RMAP_DENY; | |
1349 | else | |
1350 | { | |
1351 | vty_out (vty, "the third field must be [permit|deny]%s", VTY_NEWLINE); | |
1352 | return CMD_WARNING; | |
1353 | } | |
1354 | ||
1355 | /* Preference check. */ | |
1356 | pref = strtoul (argv[2], &endptr, 10); | |
1357 | if (pref == ULONG_MAX || *endptr != '\0') | |
1358 | { | |
1359 | vty_out (vty, "the fourth field must be positive integer%s", | |
1360 | VTY_NEWLINE); | |
1361 | return CMD_WARNING; | |
1362 | } | |
1363 | if (pref == 0 || pref > 65535) | |
1364 | { | |
1365 | vty_out (vty, "the fourth field must be <1-65535>%s", VTY_NEWLINE); | |
1366 | return CMD_WARNING; | |
1367 | } | |
1368 | ||
1369 | /* Get route map. */ | |
1370 | map = route_map_get (argv[0]); | |
1371 | index = route_map_index_get (map, permit, pref); | |
1372 | ||
1373 | vty->index = index; | |
1374 | vty->node = RMAP_NODE; | |
1375 | return CMD_SUCCESS; | |
1376 | } | |
1377 | ||
1378 | DEFUN (no_route_map_all, | |
1379 | no_route_map_all_cmd, | |
1380 | "no route-map WORD", | |
1381 | NO_STR | |
1382 | "Create route-map or enter route-map command mode\n" | |
1383 | "Route map tag\n") | |
1384 | { | |
1385 | struct route_map *map; | |
1386 | ||
1387 | map = route_map_lookup_by_name (argv[0]); | |
1388 | if (map == NULL) | |
1389 | { | |
1390 | vty_out (vty, "%% Could not find route-map %s%s", | |
1391 | argv[0], VTY_NEWLINE); | |
1392 | return CMD_WARNING; | |
1393 | } | |
1394 | ||
1395 | route_map_delete (map); | |
1396 | ||
1397 | return CMD_SUCCESS; | |
1398 | } | |
1399 | ||
1400 | DEFUN (no_route_map, | |
1401 | no_route_map_cmd, | |
1402 | "no route-map WORD (deny|permit) <1-65535>", | |
1403 | NO_STR | |
1404 | "Create route-map or enter route-map command mode\n" | |
1405 | "Route map tag\n" | |
1406 | "Route map denies set operations\n" | |
1407 | "Route map permits set operations\n" | |
1408 | "Sequence to insert to/delete from existing route-map entry\n") | |
1409 | { | |
1410 | int permit; | |
1411 | unsigned long pref; | |
1412 | struct route_map *map; | |
1413 | struct route_map_index *index; | |
1414 | char *endptr = NULL; | |
1415 | ||
1416 | /* Permit check. */ | |
1417 | if (strncmp (argv[1], "permit", strlen (argv[1])) == 0) | |
1418 | permit = RMAP_PERMIT; | |
1419 | else if (strncmp (argv[1], "deny", strlen (argv[1])) == 0) | |
1420 | permit = RMAP_DENY; | |
1421 | else | |
1422 | { | |
1423 | vty_out (vty, "the third field must be [permit|deny]%s", VTY_NEWLINE); | |
1424 | return CMD_WARNING; | |
1425 | } | |
1426 | ||
1427 | /* Preference. */ | |
1428 | pref = strtoul (argv[2], &endptr, 10); | |
1429 | if (pref == ULONG_MAX || *endptr != '\0') | |
1430 | { | |
1431 | vty_out (vty, "the fourth field must be positive integer%s", | |
1432 | VTY_NEWLINE); | |
1433 | return CMD_WARNING; | |
1434 | } | |
1435 | if (pref == 0 || pref > 65535) | |
1436 | { | |
1437 | vty_out (vty, "the fourth field must be <1-65535>%s", VTY_NEWLINE); | |
1438 | return CMD_WARNING; | |
1439 | } | |
1440 | ||
1441 | /* Existence check. */ | |
1442 | map = route_map_lookup_by_name (argv[0]); | |
1443 | if (map == NULL) | |
1444 | { | |
1445 | vty_out (vty, "%% Could not find route-map %s%s", | |
1446 | argv[0], VTY_NEWLINE); | |
1447 | return CMD_WARNING; | |
1448 | } | |
1449 | ||
1450 | /* Lookup route map index. */ | |
1451 | index = route_map_index_lookup (map, permit, pref); | |
1452 | if (index == NULL) | |
1453 | { | |
1454 | vty_out (vty, "%% Could not find route-map entry %s %s%s", | |
1455 | argv[0], argv[2], VTY_NEWLINE); | |
1456 | return CMD_WARNING; | |
1457 | } | |
1458 | ||
1459 | /* Delete index from route map. */ | |
1460 | route_map_index_delete (index, 1); | |
1461 | ||
1462 | /* If this route rule is the last one, delete route map itself. */ | |
1463 | if (route_map_empty (map)) | |
1464 | route_map_delete (map); | |
1465 | ||
1466 | return CMD_SUCCESS; | |
1467 | } | |
1468 | ||
1469 | DEFUN (rmap_onmatch_next, | |
1470 | rmap_onmatch_next_cmd, | |
1471 | "on-match next", | |
1472 | "Exit policy on matches\n" | |
1473 | "Next clause\n") | |
1474 | { | |
1475 | struct route_map_index *index; | |
1476 | ||
1477 | index = vty->index; | |
1478 | ||
1479 | if (index) | |
1480 | index->exitpolicy = RMAP_NEXT; | |
1481 | ||
1482 | return CMD_SUCCESS; | |
1483 | } | |
1484 | ||
1485 | DEFUN (no_rmap_onmatch_next, | |
1486 | no_rmap_onmatch_next_cmd, | |
1487 | "no on-match next", | |
1488 | NO_STR | |
1489 | "Exit policy on matches\n" | |
1490 | "Next clause\n") | |
1491 | { | |
1492 | struct route_map_index *index; | |
1493 | ||
1494 | index = vty->index; | |
1495 | ||
1496 | if (index) | |
1497 | index->exitpolicy = RMAP_EXIT; | |
1498 | ||
1499 | return CMD_SUCCESS; | |
1500 | } | |
1501 | ||
1502 | DEFUN (rmap_onmatch_goto, | |
1503 | rmap_onmatch_goto_cmd, | |
1504 | "on-match goto <1-65535>", | |
1505 | "Exit policy on matches\n" | |
1506 | "Goto Clause number\n" | |
1507 | "Number\n") | |
1508 | { | |
d4f0960c | 1509 | struct route_map_index *index = vty->index; |
718e3744 | 1510 | int d = 0; |
1511 | ||
718e3744 | 1512 | if (index) |
1513 | { | |
d4f0960c | 1514 | if (argc == 1 && argv[0]) |
1515 | VTY_GET_INTEGER_RANGE("route-map index", d, argv[0], 1, 65536); | |
1516 | else | |
1517 | d = index->pref + 1; | |
1518 | ||
718e3744 | 1519 | if (d <= index->pref) |
1520 | { | |
1521 | /* Can't allow you to do that, Dave */ | |
1522 | vty_out (vty, "can't jump backwards in route-maps%s", | |
1523 | VTY_NEWLINE); | |
1524 | return CMD_WARNING; | |
1525 | } | |
1526 | else | |
1527 | { | |
1528 | index->exitpolicy = RMAP_GOTO; | |
1529 | index->nextpref = d; | |
1530 | } | |
1531 | } | |
1532 | return CMD_SUCCESS; | |
1533 | } | |
1534 | ||
1535 | DEFUN (no_rmap_onmatch_goto, | |
1536 | no_rmap_onmatch_goto_cmd, | |
1537 | "no on-match goto", | |
1538 | NO_STR | |
1539 | "Exit policy on matches\n" | |
fee0f4c6 | 1540 | "Goto Clause number\n") |
718e3744 | 1541 | { |
1542 | struct route_map_index *index; | |
1543 | ||
1544 | index = vty->index; | |
1545 | ||
1546 | if (index) | |
1547 | index->exitpolicy = RMAP_EXIT; | |
1548 | ||
1549 | return CMD_SUCCESS; | |
1550 | } | |
1551 | ||
5510e83b | 1552 | /* Cisco/GNU Zebra compatible ALIASes for on-match next */ |
1553 | ALIAS (rmap_onmatch_goto, | |
1554 | rmap_continue_cmd, | |
1555 | "continue", | |
1556 | "Continue on a different entry within the route-map\n") | |
1557 | ||
1558 | ALIAS (no_rmap_onmatch_goto, | |
1559 | no_rmap_continue_cmd, | |
1560 | "no continue", | |
1561 | NO_STR | |
1562 | "Continue on a different entry within the route-map\n") | |
1563 | ||
1564 | /* GNU Zebra compatible */ | |
1565 | ALIAS (rmap_onmatch_goto, | |
1566 | rmap_continue_seq_cmd, | |
1567 | "continue <1-65535>", | |
1568 | "Continue on a different entry within the route-map\n" | |
1569 | "Route-map entry sequence number\n") | |
1570 | ||
1571 | ALIAS (no_rmap_onmatch_goto, | |
1572 | no_rmap_continue_seq, | |
1573 | "no continue <1-65535>", | |
1574 | NO_STR | |
1575 | "Continue on a different entry within the route-map\n" | |
1576 | "Route-map entry sequence number\n") | |
1577 | ||
5510e83b | 1578 | DEFUN (rmap_show_name, |
1579 | rmap_show_name_cmd, | |
7514fb77 | 1580 | "show route-map [WORD]", |
5510e83b | 1581 | SHOW_STR |
1582 | "route-map information\n" | |
1583 | "route-map name\n") | |
1584 | { | |
7514fb77 PJ |
1585 | const char *name = NULL; |
1586 | if (argc) | |
1587 | name = argv[0]; | |
1588 | return vty_show_route_map (vty, name); | |
5510e83b | 1589 | } |
1590 | ||
fee0f4c6 | 1591 | ALIAS (rmap_onmatch_goto, |
1592 | rmap_continue_index_cmd, | |
1593 | "continue <1-65536>", | |
1594 | "Exit policy on matches\n" | |
1595 | "Goto Clause number\n") | |
1596 | ||
1597 | DEFUN (rmap_call, | |
1598 | rmap_call_cmd, | |
1599 | "call WORD", | |
1600 | "Jump to another Route-Map after match+set\n" | |
1601 | "Target route-map name\n") | |
1602 | { | |
1603 | struct route_map_index *index; | |
1604 | ||
1605 | index = vty->index; | |
1606 | if (index) | |
1607 | { | |
1608 | if (index->nextrm) | |
518f0eb1 DS |
1609 | { |
1610 | route_map_upd8_dependency (RMAP_EVENT_CALL_DELETED, | |
1611 | index->nextrm, | |
1612 | index->map->name); | |
1613 | XFREE (MTYPE_ROUTE_MAP_NAME, index->nextrm); | |
1614 | } | |
0241684e | 1615 | index->nextrm = XSTRDUP (MTYPE_ROUTE_MAP_NAME, argv[0]); |
fee0f4c6 | 1616 | } |
518f0eb1 DS |
1617 | |
1618 | /* Execute event hook. */ | |
1619 | route_map_upd8_dependency (RMAP_EVENT_CALL_ADDED, | |
1620 | index->nextrm, | |
1621 | index->map->name); | |
fee0f4c6 | 1622 | return CMD_SUCCESS; |
1623 | } | |
1624 | ||
1625 | DEFUN (no_rmap_call, | |
1626 | no_rmap_call_cmd, | |
1627 | "no call", | |
1628 | NO_STR | |
1629 | "Jump to another Route-Map after match+set\n") | |
1630 | { | |
1631 | struct route_map_index *index; | |
1632 | ||
1633 | index = vty->index; | |
1634 | ||
1635 | if (index->nextrm) | |
1636 | { | |
518f0eb1 DS |
1637 | route_map_upd8_dependency (RMAP_EVENT_CALL_DELETED, |
1638 | index->nextrm, | |
1639 | index->map->name); | |
0241684e | 1640 | XFREE (MTYPE_ROUTE_MAP_NAME, index->nextrm); |
fee0f4c6 | 1641 | index->nextrm = NULL; |
1642 | } | |
1643 | ||
1644 | return CMD_SUCCESS; | |
1645 | } | |
1646 | ||
4a8164e5 | 1647 | DEFUN (rmap_description, |
1648 | rmap_description_cmd, | |
1649 | "description .LINE", | |
1650 | "Route-map comment\n" | |
1651 | "Comment describing this route-map rule\n") | |
1652 | { | |
1653 | struct route_map_index *index; | |
1654 | ||
1655 | index = vty->index; | |
1656 | if (index) | |
1657 | { | |
1658 | if (index->description) | |
1659 | XFREE (MTYPE_TMP, index->description); | |
1660 | index->description = argv_concat (argv, argc, 0); | |
1661 | } | |
1662 | return CMD_SUCCESS; | |
1663 | } | |
1664 | ||
1665 | DEFUN (no_rmap_description, | |
1666 | no_rmap_description_cmd, | |
1667 | "no description", | |
1668 | NO_STR | |
1669 | "Route-map comment\n") | |
1670 | { | |
1671 | struct route_map_index *index; | |
1672 | ||
1673 | index = vty->index; | |
1674 | if (index) | |
1675 | { | |
1676 | if (index->description) | |
1677 | XFREE (MTYPE_TMP, index->description); | |
1678 | index->description = NULL; | |
1679 | } | |
1680 | return CMD_SUCCESS; | |
1681 | } | |
1682 | ||
718e3744 | 1683 | /* Configuration write function. */ |
8cc4198f | 1684 | static int |
718e3744 | 1685 | route_map_config_write (struct vty *vty) |
1686 | { | |
1687 | struct route_map *map; | |
1688 | struct route_map_index *index; | |
1689 | struct route_map_rule *rule; | |
1690 | int first = 1; | |
1691 | int write = 0; | |
1692 | ||
1693 | for (map = route_map_master.head; map; map = map->next) | |
1694 | for (index = map->head; index; index = index->next) | |
1695 | { | |
1696 | if (!first) | |
1697 | vty_out (vty, "!%s", VTY_NEWLINE); | |
1698 | else | |
1699 | first = 0; | |
1700 | ||
1701 | vty_out (vty, "route-map %s %s %d%s", | |
1702 | map->name, | |
1703 | route_map_type_str (index->type), | |
1704 | index->pref, VTY_NEWLINE); | |
1705 | ||
4a8164e5 | 1706 | if (index->description) |
1707 | vty_out (vty, " description %s%s", index->description, VTY_NEWLINE); | |
1708 | ||
718e3744 | 1709 | for (rule = index->match_list.head; rule; rule = rule->next) |
1710 | vty_out (vty, " match %s %s%s", rule->cmd->str, | |
1711 | rule->rule_str ? rule->rule_str : "", | |
1712 | VTY_NEWLINE); | |
1713 | ||
1714 | for (rule = index->set_list.head; rule; rule = rule->next) | |
1715 | vty_out (vty, " set %s %s%s", rule->cmd->str, | |
1716 | rule->rule_str ? rule->rule_str : "", | |
1717 | VTY_NEWLINE); | |
fee0f4c6 | 1718 | if (index->nextrm) |
1719 | vty_out (vty, " call %s%s", index->nextrm, VTY_NEWLINE); | |
718e3744 | 1720 | if (index->exitpolicy == RMAP_GOTO) |
fee0f4c6 | 1721 | vty_out (vty, " on-match goto %d%s", index->nextpref, VTY_NEWLINE); |
718e3744 | 1722 | if (index->exitpolicy == RMAP_NEXT) |
1723 | vty_out (vty," on-match next%s", VTY_NEWLINE); | |
1724 | ||
1725 | write++; | |
1726 | } | |
1727 | return write; | |
1728 | } | |
1729 | ||
1730 | /* Route map node structure. */ | |
7fc626de | 1731 | static struct cmd_node rmap_node = |
718e3744 | 1732 | { |
1733 | RMAP_NODE, | |
1734 | "%s(config-route-map)# ", | |
1735 | 1 | |
1736 | }; | |
1737 | ||
518f0eb1 DS |
1738 | static void |
1739 | route_map_init_dep_hashes (void) | |
1740 | { | |
1741 | int i; | |
1742 | ||
1743 | for (i = 1; i < ROUTE_MAP_DEP_MAX; i++) | |
1744 | route_map_dep_hash[i] = hash_create(route_map_dep_hash_make_key, | |
1745 | route_map_dep_hash_cmp); | |
1746 | } | |
1747 | ||
718e3744 | 1748 | /* Initialization of route map vector. */ |
1749 | void | |
8cc4198f | 1750 | route_map_init_vty (void) |
718e3744 | 1751 | { |
518f0eb1 DS |
1752 | route_map_init_dep_hashes(); |
1753 | ||
718e3744 | 1754 | /* Install route map top node. */ |
1755 | install_node (&rmap_node, route_map_config_write); | |
1756 | ||
1757 | /* Install route map commands. */ | |
1758 | install_default (RMAP_NODE); | |
1759 | install_element (CONFIG_NODE, &route_map_cmd); | |
1760 | install_element (CONFIG_NODE, &no_route_map_cmd); | |
1761 | install_element (CONFIG_NODE, &no_route_map_all_cmd); | |
1762 | ||
1763 | /* Install the on-match stuff */ | |
1764 | install_element (RMAP_NODE, &route_map_cmd); | |
1765 | install_element (RMAP_NODE, &rmap_onmatch_next_cmd); | |
1766 | install_element (RMAP_NODE, &no_rmap_onmatch_next_cmd); | |
1767 | install_element (RMAP_NODE, &rmap_onmatch_goto_cmd); | |
1768 | install_element (RMAP_NODE, &no_rmap_onmatch_goto_cmd); | |
fee0f4c6 | 1769 | |
1770 | /* Install the continue stuff (ALIAS of on-match). */ | |
1771 | install_element (RMAP_NODE, &rmap_continue_cmd); | |
1772 | install_element (RMAP_NODE, &no_rmap_continue_cmd); | |
1773 | install_element (RMAP_NODE, &rmap_continue_index_cmd); | |
1774 | ||
1775 | /* Install the call stuff. */ | |
1776 | install_element (RMAP_NODE, &rmap_call_cmd); | |
1777 | install_element (RMAP_NODE, &no_rmap_call_cmd); | |
4a8164e5 | 1778 | |
1779 | /* Install description commands. */ | |
1780 | install_element (RMAP_NODE, &rmap_description_cmd); | |
1781 | install_element (RMAP_NODE, &no_rmap_description_cmd); | |
fee0f4c6 | 1782 | |
5510e83b | 1783 | /* Install show command */ |
5510e83b | 1784 | install_element (ENABLE_NODE, &rmap_show_name_cmd); |
718e3744 | 1785 | } |