]> git.proxmox.com Git - mirror_frr.git/blob - lib/routemap.c
ccf733e8b82bb315602e18aeb228c6bc0a2b3ca4
[mirror_frr.git] / lib / routemap.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* Route map function.
3 * Copyright (C) 1998, 1999 Kunihiro Ishiguro
4 */
5
6 #include <zebra.h>
7
8 #include "linklist.h"
9 #include "memory.h"
10 #include "command.h"
11 #include "vector.h"
12 #include "prefix.h"
13 #include "vty.h"
14 #include "routemap.h"
15 #include "command.h"
16 #include "log.h"
17 #include "hash.h"
18 #include "libfrr.h"
19 #include "lib_errors.h"
20 #include "table.h"
21 #include "json.h"
22 #include "jhash.h"
23
24 #include "lib/routemap_clippy.c"
25
26 DEFINE_MTYPE_STATIC(LIB, ROUTE_MAP, "Route map");
27 DEFINE_MTYPE(LIB, ROUTE_MAP_NAME, "Route map name");
28 DEFINE_MTYPE_STATIC(LIB, ROUTE_MAP_INDEX, "Route map index");
29 DEFINE_MTYPE(LIB, ROUTE_MAP_RULE, "Route map rule");
30 DEFINE_MTYPE_STATIC(LIB, ROUTE_MAP_RULE_STR, "Route map rule str");
31 DEFINE_MTYPE(LIB, ROUTE_MAP_COMPILED, "Route map compiled");
32 DEFINE_MTYPE_STATIC(LIB, ROUTE_MAP_DEP, "Route map dependency");
33 DEFINE_MTYPE_STATIC(LIB, ROUTE_MAP_DEP_DATA, "Route map dependency data");
34
35 DEFINE_QOBJ_TYPE(route_map_index);
36 DEFINE_QOBJ_TYPE(route_map);
37
38 static int rmap_cmd_name_cmp(const struct route_map_rule_cmd_proxy *a,
39 const struct route_map_rule_cmd_proxy *b)
40 {
41 return strcmp(a->cmd->str, b->cmd->str);
42 }
43
44 static uint32_t rmap_cmd_name_hash(const struct route_map_rule_cmd_proxy *item)
45 {
46 return jhash(item->cmd->str, strlen(item->cmd->str), 0xbfd69320);
47 }
48
49 DECLARE_HASH(rmap_cmd_name, struct route_map_rule_cmd_proxy, itm,
50 rmap_cmd_name_cmp, rmap_cmd_name_hash);
51
52 static struct rmap_cmd_name_head rmap_match_cmds[1] = {
53 INIT_HASH(rmap_match_cmds[0]),
54 };
55 static struct rmap_cmd_name_head rmap_set_cmds[1] = {
56 INIT_HASH(rmap_set_cmds[0]),
57 };
58
59 #define IPv4_PREFIX_LIST "ip address prefix-list"
60 #define IPv6_PREFIX_LIST "ipv6 address prefix-list"
61
62 #define IS_RULE_IPv4_PREFIX_LIST(S) \
63 (strncmp(S, IPv4_PREFIX_LIST, strlen(IPv4_PREFIX_LIST)) == 0)
64 #define IS_RULE_IPv6_PREFIX_LIST(S) \
65 (strncmp(S, IPv6_PREFIX_LIST, strlen(IPv6_PREFIX_LIST)) == 0)
66
67 struct route_map_pentry_dep {
68 struct prefix_list_entry *pentry;
69 const char *plist_name;
70 route_map_event_t event;
71 };
72
73 static void route_map_pfx_tbl_update(route_map_event_t event,
74 struct route_map_index *index, afi_t afi,
75 const char *plist_name);
76 static void route_map_pfx_table_add_default(afi_t afi,
77 struct route_map_index *index);
78 static void route_map_pfx_table_del_default(afi_t afi,
79 struct route_map_index *index);
80 static void route_map_add_plist_entries(afi_t afi,
81 struct route_map_index *index,
82 const char *plist_name,
83 struct prefix_list_entry *entry);
84 static void route_map_del_plist_entries(afi_t afi,
85 struct route_map_index *index,
86 const char *plist_name,
87 struct prefix_list_entry *entry);
88
89 static struct hash *route_map_get_dep_hash(route_map_event_t event);
90 static void route_map_free_map(struct route_map *map);
91
92 struct route_map_match_set_hooks rmap_match_set_hook;
93
94 /* match interface */
95 void route_map_match_interface_hook(int (*func)(
96 struct route_map_index *index, const char *command,
97 const char *arg, route_map_event_t type,
98 char *errmsg, size_t errmsg_len))
99 {
100 rmap_match_set_hook.match_interface = func;
101 }
102
103 /* no match interface */
104 void route_map_no_match_interface_hook(int (*func)(
105 struct route_map_index *index, const char *command,
106 const char *arg, route_map_event_t type,
107 char *errmsg, size_t errmsg_len))
108 {
109 rmap_match_set_hook.no_match_interface = func;
110 }
111
112 /* match ip address */
113 void route_map_match_ip_address_hook(int (*func)(
114 struct route_map_index *index, const char *command,
115 const char *arg, route_map_event_t type,
116 char *errmsg, size_t errmsg_len))
117 {
118 rmap_match_set_hook.match_ip_address = func;
119 }
120
121 /* no match ip address */
122 void route_map_no_match_ip_address_hook(int (*func)(
123 struct route_map_index *index, const char *command,
124 const char *arg, route_map_event_t type,
125 char *errmsg, size_t errmsg_len))
126 {
127 rmap_match_set_hook.no_match_ip_address = func;
128 }
129
130 /* match ip address prefix list */
131 void route_map_match_ip_address_prefix_list_hook(int (*func)(
132 struct route_map_index *index, const char *command,
133 const char *arg, route_map_event_t type,
134 char *errmsg, size_t errmsg_len))
135 {
136 rmap_match_set_hook.match_ip_address_prefix_list = func;
137 }
138
139 /* no match ip address prefix list */
140 void route_map_no_match_ip_address_prefix_list_hook(int (*func)(
141 struct route_map_index *index, const char *command,
142 const char *arg, route_map_event_t type,
143 char *errmsg, size_t errmsg_len))
144 {
145 rmap_match_set_hook.no_match_ip_address_prefix_list = func;
146 }
147
148 /* match ip next hop */
149 void route_map_match_ip_next_hop_hook(int (*func)(
150 struct route_map_index *index, const char *command,
151 const char *arg, route_map_event_t type,
152 char *errmsg, size_t errmsg_len))
153 {
154 rmap_match_set_hook.match_ip_next_hop = func;
155 }
156
157 /* no match ip next hop */
158 void route_map_no_match_ip_next_hop_hook(int (*func)(
159 struct route_map_index *index, const char *command,
160 const char *arg, route_map_event_t type,
161 char *errmsg, size_t errmsg_len))
162 {
163 rmap_match_set_hook.no_match_ip_next_hop = func;
164 }
165
166 /* match ipv6 next-hop */
167 void route_map_match_ipv6_next_hop_hook(int (*func)(
168 struct route_map_index *index, const char *command, const char *arg,
169 route_map_event_t type, char *errmsg, size_t errmsg_len))
170 {
171 rmap_match_set_hook.match_ipv6_next_hop = func;
172 }
173
174 /* no match ipv6 next-hop */
175 void route_map_no_match_ipv6_next_hop_hook(int (*func)(
176 struct route_map_index *index, const char *command, const char *arg,
177 route_map_event_t type, char *errmsg, size_t errmsg_len))
178 {
179 rmap_match_set_hook.no_match_ipv6_next_hop = func;
180 }
181
182 /* match ip next hop prefix list */
183 void route_map_match_ip_next_hop_prefix_list_hook(int (*func)(
184 struct route_map_index *index, const char *command,
185 const char *arg, route_map_event_t type,
186 char *errmsg, size_t errmsg_len))
187 {
188 rmap_match_set_hook.match_ip_next_hop_prefix_list = func;
189 }
190
191 /* no match ip next hop prefix list */
192 void route_map_no_match_ip_next_hop_prefix_list_hook(int (*func)(
193 struct route_map_index *index, const char *command,
194 const char *arg, route_map_event_t type,
195 char *errmsg, size_t errmsg_len))
196 {
197 rmap_match_set_hook.no_match_ip_next_hop_prefix_list = func;
198 }
199
200 /* match ip next-hop type */
201 void route_map_match_ip_next_hop_type_hook(int (*func)(
202 struct route_map_index *index, const char *command,
203 const char *arg, route_map_event_t type,
204 char *errmsg, size_t errmsg_len))
205 {
206 rmap_match_set_hook.match_ip_next_hop_type = func;
207 }
208
209 /* no match ip next-hop type */
210 void route_map_no_match_ip_next_hop_type_hook(int (*func)(
211 struct route_map_index *index, const char *command,
212 const char *arg, route_map_event_t type,
213 char *errmsg, size_t errmsg_len))
214 {
215 rmap_match_set_hook.no_match_ip_next_hop_type = func;
216 }
217
218 /* match ipv6 address */
219 void route_map_match_ipv6_address_hook(int (*func)(
220 struct route_map_index *index, const char *command,
221 const char *arg, route_map_event_t type,
222 char *errmsg, size_t errmsg_len))
223 {
224 rmap_match_set_hook.match_ipv6_address = func;
225 }
226
227 /* no match ipv6 address */
228 void route_map_no_match_ipv6_address_hook(int (*func)(
229 struct route_map_index *index, const char *command,
230 const char *arg, route_map_event_t type,
231 char *errmsg, size_t errmsg_len))
232 {
233 rmap_match_set_hook.no_match_ipv6_address = func;
234 }
235
236
237 /* match ipv6 address prefix list */
238 void route_map_match_ipv6_address_prefix_list_hook(int (*func)(
239 struct route_map_index *index, const char *command,
240 const char *arg, route_map_event_t type,
241 char *errmsg, size_t errmsg_len))
242 {
243 rmap_match_set_hook.match_ipv6_address_prefix_list = func;
244 }
245
246 /* no match ipv6 address prefix list */
247 void route_map_no_match_ipv6_address_prefix_list_hook(int (*func)(
248 struct route_map_index *index, const char *command,
249 const char *arg, route_map_event_t type,
250 char *errmsg, size_t errmsg_len))
251 {
252 rmap_match_set_hook.no_match_ipv6_address_prefix_list = func;
253 }
254
255 /* match ipv6 next-hop type */
256 void route_map_match_ipv6_next_hop_type_hook(int (*func)(
257 struct route_map_index *index, const char *command,
258 const char *arg, route_map_event_t type,
259 char *errmsg, size_t errmsg_len))
260 {
261 rmap_match_set_hook.match_ipv6_next_hop_type = func;
262 }
263
264 /* no match ipv6 next-hop type */
265 void route_map_no_match_ipv6_next_hop_type_hook(int (*func)(
266 struct route_map_index *index, const char *command,
267 const char *arg, route_map_event_t type,
268 char *errmsg, size_t errmsg_len))
269 {
270 rmap_match_set_hook.no_match_ipv6_next_hop_type = func;
271 }
272
273 /* match ipv6 next-hop prefix-list */
274 void route_map_match_ipv6_next_hop_prefix_list_hook(int (*func)(
275 struct route_map_index *index, const char *command, const char *arg,
276 route_map_event_t type, char *errmsg, size_t errmsg_len))
277 {
278 rmap_match_set_hook.match_ipv6_next_hop_prefix_list = func;
279 }
280
281 /* no match ipv6 next-hop prefix-list */
282 void route_map_no_match_ipv6_next_hop_prefix_list_hook(int (*func)(
283 struct route_map_index *index, const char *command, const char *arg,
284 route_map_event_t type, char *errmsg, size_t errmsg_len))
285 {
286 rmap_match_set_hook.no_match_ipv6_next_hop_prefix_list = func;
287 }
288
289 /* match metric */
290 void route_map_match_metric_hook(int (*func)(
291 struct route_map_index *index, const char *command,
292 const char *arg, route_map_event_t type,
293 char *errmsg, size_t errmsg_len))
294 {
295 rmap_match_set_hook.match_metric = func;
296 }
297
298 /* no match metric */
299 void route_map_no_match_metric_hook(int (*func)(
300 struct route_map_index *index, const char *command,
301 const char *arg, route_map_event_t type,
302 char *errmsg, size_t errmsg_len))
303 {
304 rmap_match_set_hook.no_match_metric = func;
305 }
306
307 /* match tag */
308 void route_map_match_tag_hook(int (*func)(struct route_map_index *index,
309 const char *command, const char *arg,
310 route_map_event_t type,
311 char *errmsg, size_t errmsg_len))
312 {
313 rmap_match_set_hook.match_tag = func;
314 }
315
316 /* no match tag */
317 void route_map_no_match_tag_hook(int (*func)(
318 struct route_map_index *index, const char *command,
319 const char *arg, route_map_event_t type,
320 char *errmsg, size_t errmsg_len))
321 {
322 rmap_match_set_hook.no_match_tag = func;
323 }
324
325 /* set sr-te color */
326 void route_map_set_srte_color_hook(int (*func)(struct route_map_index *index,
327 const char *command,
328 const char *arg,
329 char *errmsg, size_t errmsg_len))
330 {
331 rmap_match_set_hook.set_srte_color = func;
332 }
333
334 /* no set sr-te color */
335 void route_map_no_set_srte_color_hook(int (*func)(struct route_map_index *index,
336 const char *command,
337 const char *arg,
338 char *errmsg, size_t errmsg_len))
339 {
340 rmap_match_set_hook.no_set_srte_color = func;
341 }
342
343 /* set ip nexthop */
344 void route_map_set_ip_nexthop_hook(int (*func)(struct route_map_index *index,
345 const char *command,
346 const char *arg,
347 char *errmsg, size_t errmsg_len))
348 {
349 rmap_match_set_hook.set_ip_nexthop = func;
350 }
351
352 /* no set ip nexthop */
353 void route_map_no_set_ip_nexthop_hook(int (*func)(struct route_map_index *index,
354 const char *command,
355 const char *arg,
356 char *errmsg,
357 size_t errmsg_len))
358 {
359 rmap_match_set_hook.no_set_ip_nexthop = func;
360 }
361
362 /* set ipv6 nexthop local */
363 void route_map_set_ipv6_nexthop_local_hook(
364 int (*func)(struct route_map_index *index,
365 const char *command, const char *arg,
366 char *errmsg, size_t errmsg_len))
367 {
368 rmap_match_set_hook.set_ipv6_nexthop_local = func;
369 }
370
371 /* no set ipv6 nexthop local */
372 void route_map_no_set_ipv6_nexthop_local_hook(
373 int (*func)(struct route_map_index *index,
374 const char *command, const char *arg,
375 char *errmsg, size_t errmsg_len))
376 {
377 rmap_match_set_hook.no_set_ipv6_nexthop_local = func;
378 }
379
380 /* set metric */
381 void route_map_set_metric_hook(int (*func)(struct route_map_index *index,
382 const char *command,
383 const char *arg,
384 char *errmsg, size_t errmsg_len))
385 {
386 rmap_match_set_hook.set_metric = func;
387 }
388
389 /* no set metric */
390 void route_map_no_set_metric_hook(int (*func)(struct route_map_index *index,
391 const char *command,
392 const char *arg,
393 char *errmsg, size_t errmsg_len))
394 {
395 rmap_match_set_hook.no_set_metric = func;
396 }
397
398 /* set tag */
399 void route_map_set_tag_hook(int (*func)(struct route_map_index *index,
400 const char *command, const char *arg,
401 char *errmsg, size_t errmsg_len))
402 {
403 rmap_match_set_hook.set_tag = func;
404 }
405
406 /* no set tag */
407 void route_map_no_set_tag_hook(int (*func)(struct route_map_index *index,
408 const char *command,
409 const char *arg,
410 char *errmsg, size_t errmsg_len))
411 {
412 rmap_match_set_hook.no_set_tag = func;
413 }
414
415 int generic_match_add(struct route_map_index *index,
416 const char *command, const char *arg,
417 route_map_event_t type,
418 char *errmsg, size_t errmsg_len)
419 {
420 enum rmap_compile_rets ret;
421
422 ret = route_map_add_match(index, command, arg, type);
423 switch (ret) {
424 case RMAP_RULE_MISSING:
425 snprintf(errmsg, errmsg_len, "%% [%s] Can't find rule.",
426 frr_protonameinst);
427 return CMD_WARNING_CONFIG_FAILED;
428 case RMAP_COMPILE_ERROR:
429 snprintf(errmsg, errmsg_len,
430 "%% [%s] Argument form is unsupported or malformed.",
431 frr_protonameinst);
432 return CMD_WARNING_CONFIG_FAILED;
433 case RMAP_COMPILE_SUCCESS:
434 /*
435 * Nothing to do here move along
436 */
437 break;
438 }
439
440 return CMD_SUCCESS;
441 }
442
443 int generic_match_delete(struct route_map_index *index,
444 const char *command, const char *arg,
445 route_map_event_t type,
446 char *errmsg, size_t errmsg_len)
447 {
448 enum rmap_compile_rets ret;
449 int retval = CMD_SUCCESS;
450 char *dep_name = NULL;
451 const char *tmpstr;
452 char *rmap_name = NULL;
453
454 if (type != RMAP_EVENT_MATCH_DELETED) {
455 /* ignore the mundane, the types without any dependency */
456 if (arg == NULL) {
457 if ((tmpstr = route_map_get_match_arg(index, command))
458 != NULL)
459 dep_name =
460 XSTRDUP(MTYPE_ROUTE_MAP_RULE, tmpstr);
461 } else {
462 dep_name = XSTRDUP(MTYPE_ROUTE_MAP_RULE, arg);
463 }
464 rmap_name = XSTRDUP(MTYPE_ROUTE_MAP_NAME, index->map->name);
465 }
466
467 ret = route_map_delete_match(index, command, dep_name, type);
468 switch (ret) {
469 case RMAP_RULE_MISSING:
470 snprintf(errmsg, errmsg_len, "%% [%s] Can't find rule.",
471 frr_protonameinst);
472 retval = CMD_WARNING_CONFIG_FAILED;
473 break;
474 case RMAP_COMPILE_ERROR:
475 snprintf(errmsg, errmsg_len,
476 "%% [%s] Argument form is unsupported or malformed.",
477 frr_protonameinst);
478 retval = CMD_WARNING_CONFIG_FAILED;
479 break;
480 case RMAP_COMPILE_SUCCESS:
481 /*
482 * Nothing to do here
483 */
484 break;
485 }
486
487 XFREE(MTYPE_ROUTE_MAP_RULE, dep_name);
488 XFREE(MTYPE_ROUTE_MAP_NAME, rmap_name);
489
490 return retval;
491 }
492
493 int generic_set_add(struct route_map_index *index,
494 const char *command, const char *arg,
495 char *errmsg, size_t errmsg_len)
496 {
497 enum rmap_compile_rets ret;
498
499 ret = route_map_add_set(index, command, arg);
500 switch (ret) {
501 case RMAP_RULE_MISSING:
502 snprintf(errmsg, errmsg_len,
503 "%% [%s] Can't find rule.", frr_protonameinst);
504 return CMD_WARNING_CONFIG_FAILED;
505 case RMAP_COMPILE_ERROR:
506 snprintf(errmsg, errmsg_len,
507 "%% [%s] Argument form is unsupported or malformed.",
508 frr_protonameinst);
509 return CMD_WARNING_CONFIG_FAILED;
510 case RMAP_COMPILE_SUCCESS:
511 break;
512 }
513
514 return CMD_SUCCESS;
515 }
516
517 int generic_set_delete(struct route_map_index *index,
518 const char *command, const char *arg,
519 char *errmsg, size_t errmsg_len)
520 {
521 enum rmap_compile_rets ret;
522
523 ret = route_map_delete_set(index, command, arg);
524 switch (ret) {
525 case RMAP_RULE_MISSING:
526 snprintf(errmsg, errmsg_len, "%% [%s] Can't find rule.",
527 frr_protonameinst);
528 return CMD_WARNING_CONFIG_FAILED;
529 case RMAP_COMPILE_ERROR:
530 snprintf(errmsg, errmsg_len,
531 "%% [%s] Argument form is unsupported or malformed.",
532 frr_protonameinst);
533 return CMD_WARNING_CONFIG_FAILED;
534 case RMAP_COMPILE_SUCCESS:
535 break;
536 }
537
538 return CMD_SUCCESS;
539 }
540
541
542 /* Master list of route map. */
543 struct route_map_list route_map_master = {NULL, NULL, NULL, NULL, NULL};
544 struct hash *route_map_master_hash = NULL;
545
546 static unsigned int route_map_hash_key_make(const void *p)
547 {
548 const struct route_map *map = p;
549 return string_hash_make(map->name);
550 }
551
552 static bool route_map_hash_cmp(const void *p1, const void *p2)
553 {
554 const struct route_map *map1 = p1;
555 const struct route_map *map2 = p2;
556
557 if (!strcmp(map1->name, map2->name))
558 return true;
559
560 return false;
561 }
562
563 enum route_map_upd8_type {
564 ROUTE_MAP_ADD = 1,
565 ROUTE_MAP_DEL,
566 };
567
568 /* all possible route-map dependency types */
569 enum route_map_dep_type {
570 ROUTE_MAP_DEP_RMAP = 1,
571 ROUTE_MAP_DEP_CLIST,
572 ROUTE_MAP_DEP_ECLIST,
573 ROUTE_MAP_DEP_LCLIST,
574 ROUTE_MAP_DEP_PLIST,
575 ROUTE_MAP_DEP_ASPATH,
576 ROUTE_MAP_DEP_FILTER,
577 ROUTE_MAP_DEP_MAX,
578 };
579
580 struct route_map_dep {
581 char *dep_name;
582 struct hash *dep_rmap_hash;
583 struct hash *this_hash; /* ptr to the hash structure this is part of */
584 };
585
586 struct route_map_dep_data {
587 /* Route-map name.
588 */
589 char *rname;
590 /* Count of number of sequences of this
591 * route-map that depend on the same entity.
592 */
593 uint16_t refcnt;
594 };
595
596 /* Hashes maintaining dependency between various sublists used by route maps */
597 static struct hash *route_map_dep_hash[ROUTE_MAP_DEP_MAX];
598
599 static unsigned int route_map_dep_hash_make_key(const void *p);
600 static void route_map_clear_all_references(char *rmap_name);
601 static void route_map_rule_delete(struct route_map_rule_list *,
602 struct route_map_rule *);
603
604 uint32_t rmap_debug;
605
606 /* New route map allocation. Please note route map's name must be
607 specified. */
608 static struct route_map *route_map_new(const char *name)
609 {
610 struct route_map *new;
611
612 new = XCALLOC(MTYPE_ROUTE_MAP, sizeof(struct route_map));
613 new->name = XSTRDUP(MTYPE_ROUTE_MAP_NAME, name);
614 QOBJ_REG(new, route_map);
615 return new;
616 }
617
618 /* Add new name to route_map. */
619 static struct route_map *route_map_add(const char *name)
620 {
621 struct route_map *map, *exist;
622 struct route_map_list *list;
623
624 map = route_map_new(name);
625 list = &route_map_master;
626
627 /*
628 * Add map to the hash
629 *
630 * If the map already exists in the hash, then we know that
631 * FRR is now in a sequence of delete/create.
632 * All FRR needs to do here is set the to_be_processed
633 * bit (to inherit from the old one
634 */
635 exist = hash_release(route_map_master_hash, map);
636 if (exist) {
637 map->to_be_processed = exist->to_be_processed;
638 route_map_free_map(exist);
639 }
640 hash_get(route_map_master_hash, map, hash_alloc_intern);
641
642 /* Add new entry to the head of the list to match how it is added in the
643 * hash table. This is to ensure that if the same route-map has been
644 * created more than once and then marked for deletion (which can happen
645 * if prior deletions haven't completed as BGP hasn't yet done the
646 * route-map processing), the order of the entities is the same in both
647 * the list and the hash table. Otherwise, since there is nothing to
648 * distinguish between the two entries, the wrong entry could get freed.
649 * TODO: This needs to be re-examined to handle it better - e.g., revive
650 * a deleted entry if the route-map is created again.
651 */
652 map->prev = NULL;
653 map->next = list->head;
654 if (list->head)
655 list->head->prev = map;
656 list->head = map;
657 if (!list->tail)
658 list->tail = map;
659
660 /* Execute hook. */
661 if (route_map_master.add_hook) {
662 (*route_map_master.add_hook)(name);
663 route_map_notify_dependencies(name, RMAP_EVENT_CALL_ADDED);
664 }
665
666 if (!map->ipv4_prefix_table)
667 map->ipv4_prefix_table = route_table_init();
668
669 if (!map->ipv6_prefix_table)
670 map->ipv6_prefix_table = route_table_init();
671
672 if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))
673 zlog_debug("Add route-map %s", name);
674 return map;
675 }
676
677 /* this is supposed to be called post processing by
678 * the delete hook function. Don't invoke delete_hook
679 * again in this routine.
680 */
681 static void route_map_free_map(struct route_map *map)
682 {
683 struct route_map_list *list;
684 struct route_map_index *index;
685
686 if (map == NULL)
687 return;
688
689 while ((index = map->head) != NULL)
690 route_map_index_delete(index, 0);
691
692 if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))
693 zlog_debug("Deleting route-map %s", map->name);
694
695 list = &route_map_master;
696
697 QOBJ_UNREG(map);
698
699 if (map->next)
700 map->next->prev = map->prev;
701 else
702 list->tail = map->prev;
703
704 if (map->prev)
705 map->prev->next = map->next;
706 else
707 list->head = map->next;
708
709 hash_release(route_map_master_hash, map);
710 XFREE(MTYPE_ROUTE_MAP_NAME, map->name);
711 XFREE(MTYPE_ROUTE_MAP, map);
712 }
713
714 /* Route map delete from list. */
715 void route_map_delete(struct route_map *map)
716 {
717 struct route_map_index *index;
718 char *name;
719
720 while ((index = map->head) != NULL)
721 route_map_index_delete(index, 0);
722
723 name = map->name;
724 map->head = NULL;
725
726 /* Clear all dependencies */
727 route_map_clear_all_references(name);
728 map->deleted = true;
729 /* Execute deletion hook. */
730 if (route_map_master.delete_hook) {
731 (*route_map_master.delete_hook)(name);
732 route_map_notify_dependencies(name, RMAP_EVENT_CALL_DELETED);
733 }
734
735 if (!map->to_be_processed) {
736 route_map_free_map(map);
737 }
738 }
739
740 /* Lookup route map by route map name string. */
741 struct route_map *route_map_lookup_by_name(const char *name)
742 {
743 struct route_map *map;
744 struct route_map tmp_map;
745
746 if (!name)
747 return NULL;
748
749 // map.deleted is false via memset
750 memset(&tmp_map, 0, sizeof(tmp_map));
751 tmp_map.name = XSTRDUP(MTYPE_ROUTE_MAP_NAME, name);
752 map = hash_lookup(route_map_master_hash, &tmp_map);
753 XFREE(MTYPE_ROUTE_MAP_NAME, tmp_map.name);
754
755 if (map && map->deleted)
756 return NULL;
757
758 return map;
759 }
760
761 /* Simple helper to warn if route-map does not exist. */
762 struct route_map *route_map_lookup_warn_noexist(struct vty *vty, const char *name)
763 {
764 struct route_map *route_map = route_map_lookup_by_name(name);
765
766 if (!route_map)
767 if (vty_shell_serv(vty))
768 vty_out(vty, "The route-map '%s' does not exist.\n", name);
769
770 return route_map;
771 }
772
773 int route_map_mark_updated(const char *name)
774 {
775 struct route_map *map;
776 int ret = -1;
777 struct route_map tmp_map;
778
779 if (!name)
780 return (ret);
781
782 map = route_map_lookup_by_name(name);
783
784 /* If we did not find the routemap with deleted=false try again
785 * with deleted=true
786 */
787 if (!map) {
788 memset(&tmp_map, 0, sizeof(tmp_map));
789 tmp_map.name = XSTRDUP(MTYPE_ROUTE_MAP_NAME, name);
790 tmp_map.deleted = true;
791 map = hash_lookup(route_map_master_hash, &tmp_map);
792 XFREE(MTYPE_ROUTE_MAP_NAME, tmp_map.name);
793 }
794
795 if (map) {
796 map->to_be_processed = true;
797 ret = 0;
798 }
799
800 return (ret);
801 }
802
803 static void route_map_clear_updated(struct route_map *map)
804 {
805 if (map) {
806 map->to_be_processed = false;
807 if (map->deleted)
808 route_map_free_map(map);
809 }
810 }
811
812 /* Lookup route map. If there isn't route map create one and return
813 it. */
814 struct route_map *route_map_get(const char *name)
815 {
816 struct route_map *map;
817
818 map = route_map_lookup_by_name(name);
819 if (map == NULL)
820 map = route_map_add(name);
821
822 return map;
823 }
824
825 void route_map_walk_update_list(void (*route_map_update_fn)(char *name))
826 {
827 struct route_map *node;
828 struct route_map *nnode = NULL;
829
830 for (node = route_map_master.head; node; node = nnode) {
831 if (node->to_be_processed) {
832 /* DD: Should we add any thread yield code here */
833 route_map_update_fn(node->name);
834 nnode = node->next;
835 route_map_clear_updated(node);
836 } else
837 nnode = node->next;
838 }
839 }
840
841 /* Return route map's type string. */
842 static const char *route_map_type_str(enum route_map_type type)
843 {
844 switch (type) {
845 case RMAP_PERMIT:
846 return "permit";
847 case RMAP_DENY:
848 return "deny";
849 case RMAP_ANY:
850 return "";
851 }
852
853 return "";
854 }
855
856 static const char *route_map_cmd_result_str(enum route_map_cmd_result_t res)
857 {
858 switch (res) {
859 case RMAP_MATCH:
860 return "match";
861 case RMAP_NOMATCH:
862 return "no match";
863 case RMAP_NOOP:
864 return "noop";
865 case RMAP_ERROR:
866 return "error";
867 case RMAP_OKAY:
868 return "okay";
869 }
870
871 return "invalid";
872 }
873
874 static const char *route_map_result_str(route_map_result_t res)
875 {
876 switch (res) {
877 case RMAP_DENYMATCH:
878 return "deny";
879 case RMAP_PERMITMATCH:
880 return "permit";
881 }
882
883 return "invalid";
884 }
885
886 /* show route-map */
887 static void vty_show_route_map_entry(struct vty *vty, struct route_map *map,
888 json_object *json)
889 {
890 struct route_map_index *index;
891 struct route_map_rule *rule;
892 json_object *json_rmap = NULL;
893 json_object *json_rules = NULL;
894
895 if (json) {
896 json_rmap = json_object_new_object();
897 json_object_object_add(json, map->name, json_rmap);
898
899 json_rules = json_object_new_array();
900 json_object_int_add(json_rmap, "invoked",
901 map->applied - map->applied_clear);
902 json_object_boolean_add(json_rmap, "disabledOptimization",
903 map->optimization_disabled);
904 json_object_boolean_add(json_rmap, "processedChange",
905 map->to_be_processed);
906 json_object_object_add(json_rmap, "rules", json_rules);
907 } else {
908 vty_out(vty,
909 "route-map: %s Invoked: %" PRIu64
910 " Optimization: %s Processed Change: %s\n",
911 map->name, map->applied - map->applied_clear,
912 map->optimization_disabled ? "disabled" : "enabled",
913 map->to_be_processed ? "true" : "false");
914 }
915
916 for (index = map->head; index; index = index->next) {
917 if (json) {
918 json_object *json_rule;
919 json_object *json_matches;
920 json_object *json_sets;
921 char action[BUFSIZ] = {};
922
923 json_rule = json_object_new_object();
924 json_object_array_add(json_rules, json_rule);
925
926 json_object_int_add(json_rule, "sequenceNumber",
927 index->pref);
928 json_object_string_add(json_rule, "type",
929 route_map_type_str(index->type));
930 json_object_int_add(json_rule, "invoked",
931 index->applied
932 - index->applied_clear);
933
934 /* Description */
935 if (index->description)
936 json_object_string_add(json_rule, "description",
937 index->description);
938
939 /* Match clauses */
940 json_matches = json_object_new_array();
941 json_object_object_add(json_rule, "matchClauses",
942 json_matches);
943 for (rule = index->match_list.head; rule;
944 rule = rule->next) {
945 char buf[BUFSIZ];
946
947 snprintf(buf, sizeof(buf), "%s %s",
948 rule->cmd->str, rule->rule_str);
949 json_array_string_add(json_matches, buf);
950 }
951
952 /* Set clauses */
953 json_sets = json_object_new_array();
954 json_object_object_add(json_rule, "setClauses",
955 json_sets);
956 for (rule = index->set_list.head; rule;
957 rule = rule->next) {
958 char buf[BUFSIZ];
959
960 snprintf(buf, sizeof(buf), "%s %s",
961 rule->cmd->str, rule->rule_str);
962 json_array_string_add(json_sets, buf);
963 }
964
965 /* Call clause */
966 if (index->nextrm)
967 json_object_string_add(json_rule, "callClause",
968 index->nextrm);
969
970 /* Exit Policy */
971 if (index->exitpolicy == RMAP_GOTO)
972 snprintf(action, sizeof(action), "Goto %d",
973 index->nextpref);
974 else if (index->exitpolicy == RMAP_NEXT)
975 snprintf(action, sizeof(action),
976 "Continue to next entry");
977 else if (index->exitpolicy == RMAP_EXIT)
978 snprintf(action, sizeof(action),
979 "Exit routemap");
980 if (action[0] != '\0')
981 json_object_string_add(json_rule, "action",
982 action);
983 } else {
984 vty_out(vty, " %s, sequence %d Invoked %" PRIu64 "\n",
985 route_map_type_str(index->type), index->pref,
986 index->applied - index->applied_clear);
987
988 /* Description */
989 if (index->description)
990 vty_out(vty, " Description:\n %s\n",
991 index->description);
992
993 /* Match clauses */
994 vty_out(vty, " Match clauses:\n");
995 for (rule = index->match_list.head; rule;
996 rule = rule->next)
997 vty_out(vty, " %s %s\n", rule->cmd->str,
998 rule->rule_str);
999
1000 /* Set clauses */
1001 vty_out(vty, " Set clauses:\n");
1002 for (rule = index->set_list.head; rule;
1003 rule = rule->next)
1004 vty_out(vty, " %s %s\n", rule->cmd->str,
1005 rule->rule_str);
1006
1007 /* Call clause */
1008 vty_out(vty, " Call clause:\n");
1009 if (index->nextrm)
1010 vty_out(vty, " Call %s\n", index->nextrm);
1011
1012 /* Exit Policy */
1013 vty_out(vty, " Action:\n");
1014 if (index->exitpolicy == RMAP_GOTO)
1015 vty_out(vty, " Goto %d\n", index->nextpref);
1016 else if (index->exitpolicy == RMAP_NEXT)
1017 vty_out(vty, " Continue to next entry\n");
1018 else if (index->exitpolicy == RMAP_EXIT)
1019 vty_out(vty, " Exit routemap\n");
1020 }
1021 }
1022 }
1023
1024 static int sort_route_map(const void **map1, const void **map2)
1025 {
1026 const struct route_map *m1 = *map1;
1027 const struct route_map *m2 = *map2;
1028
1029 return strcmp(m1->name, m2->name);
1030 }
1031
1032 static int vty_show_route_map(struct vty *vty, const char *name, bool use_json)
1033 {
1034 struct route_map *map;
1035 json_object *json = NULL;
1036 json_object *json_proto = NULL;
1037
1038 if (use_json) {
1039 json = json_object_new_object();
1040 json_proto = json_object_new_object();
1041 json_object_object_add(json, frr_protonameinst, json_proto);
1042 } else
1043 vty_out(vty, "%s:\n", frr_protonameinst);
1044
1045 if (name) {
1046 map = route_map_lookup_by_name(name);
1047
1048 if (map) {
1049 vty_show_route_map_entry(vty, map, json_proto);
1050 } else if (!use_json) {
1051 vty_out(vty, "%s: 'route-map %s' not found\n",
1052 frr_protonameinst, name);
1053 }
1054 } else {
1055
1056 struct list *maplist = list_new();
1057 struct listnode *ln;
1058
1059 for (map = route_map_master.head; map; map = map->next)
1060 listnode_add(maplist, map);
1061
1062 list_sort(maplist, sort_route_map);
1063
1064 for (ALL_LIST_ELEMENTS_RO(maplist, ln, map))
1065 vty_show_route_map_entry(vty, map, json_proto);
1066
1067 list_delete(&maplist);
1068 }
1069
1070 return vty_json(vty, json);
1071 }
1072
1073 /* Unused route map details */
1074 static int vty_show_unused_route_map(struct vty *vty)
1075 {
1076 struct list *maplist = list_new();
1077 struct listnode *ln;
1078 struct route_map *map;
1079
1080 for (map = route_map_master.head; map; map = map->next) {
1081 /* If use_count is zero, No protocol is using this routemap.
1082 * so adding to the list.
1083 */
1084 if (!map->use_count)
1085 listnode_add(maplist, map);
1086 }
1087
1088 if (maplist->count > 0) {
1089 vty_out(vty, "\n%s:\n", frr_protonameinst);
1090 list_sort(maplist, sort_route_map);
1091
1092 for (ALL_LIST_ELEMENTS_RO(maplist, ln, map))
1093 vty_show_route_map_entry(vty, map, NULL);
1094 } else {
1095 vty_out(vty, "\n%s: None\n", frr_protonameinst);
1096 }
1097
1098 list_delete(&maplist);
1099 return CMD_SUCCESS;
1100 }
1101
1102 /* New route map allocation. Please note route map's name must be
1103 specified. */
1104 static struct route_map_index *route_map_index_new(void)
1105 {
1106 struct route_map_index *new;
1107
1108 new = XCALLOC(MTYPE_ROUTE_MAP_INDEX, sizeof(struct route_map_index));
1109 new->exitpolicy = RMAP_EXIT; /* Default to Cisco-style */
1110 TAILQ_INIT(&new->rhclist);
1111 QOBJ_REG(new, route_map_index);
1112 return new;
1113 }
1114
1115 /* Free route map index. */
1116 void route_map_index_delete(struct route_map_index *index, int notify)
1117 {
1118 struct routemap_hook_context *rhc;
1119 struct route_map_rule *rule;
1120
1121 QOBJ_UNREG(index);
1122
1123 if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))
1124 zlog_debug("Deleting route-map %s sequence %d",
1125 index->map->name, index->pref);
1126
1127 /* Free route map entry description. */
1128 XFREE(MTYPE_TMP, index->description);
1129
1130 /* Free route map northbound hook contexts. */
1131 while ((rhc = TAILQ_FIRST(&index->rhclist)) != NULL)
1132 routemap_hook_context_free(rhc);
1133
1134 /* Free route match. */
1135 while ((rule = index->match_list.head) != NULL) {
1136 if (IS_RULE_IPv4_PREFIX_LIST(rule->cmd->str))
1137 route_map_pfx_tbl_update(RMAP_EVENT_PLIST_DELETED,
1138 index, AFI_IP, rule->rule_str);
1139 else if (IS_RULE_IPv6_PREFIX_LIST(rule->cmd->str))
1140 route_map_pfx_tbl_update(RMAP_EVENT_PLIST_DELETED,
1141 index, AFI_IP6,
1142 rule->rule_str);
1143
1144 route_map_rule_delete(&index->match_list, rule);
1145 }
1146
1147 /* Free route set. */
1148 while ((rule = index->set_list.head) != NULL)
1149 route_map_rule_delete(&index->set_list, rule);
1150
1151 /* Remove index from route map list. */
1152 if (index->next)
1153 index->next->prev = index->prev;
1154 else
1155 index->map->tail = index->prev;
1156
1157 if (index->prev)
1158 index->prev->next = index->next;
1159 else
1160 index->map->head = index->next;
1161
1162 /* Free 'char *nextrm' if not NULL */
1163 XFREE(MTYPE_ROUTE_MAP_NAME, index->nextrm);
1164
1165 route_map_pfx_tbl_update(RMAP_EVENT_INDEX_DELETED, index, 0, NULL);
1166
1167 /* Execute event hook. */
1168 if (route_map_master.event_hook && notify) {
1169 (*route_map_master.event_hook)(index->map->name);
1170 route_map_notify_dependencies(index->map->name,
1171 RMAP_EVENT_CALL_ADDED);
1172 }
1173 XFREE(MTYPE_ROUTE_MAP_INDEX, index);
1174 }
1175
1176 /* Lookup index from route map. */
1177 static struct route_map_index *route_map_index_lookup(struct route_map *map,
1178 enum route_map_type type,
1179 int pref)
1180 {
1181 struct route_map_index *index;
1182
1183 for (index = map->head; index; index = index->next)
1184 if ((index->type == type || type == RMAP_ANY)
1185 && index->pref == pref)
1186 return index;
1187 return NULL;
1188 }
1189
1190 /* Add new index to route map. */
1191 static struct route_map_index *
1192 route_map_index_add(struct route_map *map, enum route_map_type type, int pref)
1193 {
1194 struct route_map_index *index;
1195 struct route_map_index *point;
1196
1197 /* Allocate new route map inex. */
1198 index = route_map_index_new();
1199 index->map = map;
1200 index->type = type;
1201 index->pref = pref;
1202
1203 /* Compare preference. */
1204 for (point = map->head; point; point = point->next)
1205 if (point->pref >= pref)
1206 break;
1207
1208 if (map->head == NULL) {
1209 map->head = map->tail = index;
1210 } else if (point == NULL) {
1211 index->prev = map->tail;
1212 map->tail->next = index;
1213 map->tail = index;
1214 } else if (point == map->head) {
1215 index->next = map->head;
1216 map->head->prev = index;
1217 map->head = index;
1218 } else {
1219 index->next = point;
1220 index->prev = point->prev;
1221 if (point->prev)
1222 point->prev->next = index;
1223 point->prev = index;
1224 }
1225
1226 route_map_pfx_tbl_update(RMAP_EVENT_INDEX_ADDED, index, 0, NULL);
1227
1228 /* Execute event hook. */
1229 if (route_map_master.event_hook) {
1230 (*route_map_master.event_hook)(map->name);
1231 route_map_notify_dependencies(map->name, RMAP_EVENT_CALL_ADDED);
1232 }
1233
1234 if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))
1235 zlog_debug("Route-map %s add sequence %d, type: %s",
1236 map->name, pref, route_map_type_str(type));
1237
1238 return index;
1239 }
1240
1241 /* Get route map index. */
1242 struct route_map_index *
1243 route_map_index_get(struct route_map *map, enum route_map_type type, int pref)
1244 {
1245 struct route_map_index *index;
1246
1247 index = route_map_index_lookup(map, RMAP_ANY, pref);
1248 if (index && index->type != type) {
1249 /* Delete index from route map. */
1250 route_map_index_delete(index, 1);
1251 index = NULL;
1252 }
1253 if (index == NULL)
1254 index = route_map_index_add(map, type, pref);
1255 return index;
1256 }
1257
1258 /* New route map rule */
1259 static struct route_map_rule *route_map_rule_new(void)
1260 {
1261 struct route_map_rule *new;
1262
1263 new = XCALLOC(MTYPE_ROUTE_MAP_RULE, sizeof(struct route_map_rule));
1264 return new;
1265 }
1266
1267 /* Install rule command to the match list. */
1268 void _route_map_install_match(struct route_map_rule_cmd_proxy *proxy)
1269 {
1270 rmap_cmd_name_add(rmap_match_cmds, proxy);
1271 }
1272
1273 /* Install rule command to the set list. */
1274 void _route_map_install_set(struct route_map_rule_cmd_proxy *proxy)
1275 {
1276 rmap_cmd_name_add(rmap_set_cmds, proxy);
1277 }
1278
1279 /* Lookup rule command from match list. */
1280 static const struct route_map_rule_cmd *route_map_lookup_match(const char *name)
1281 {
1282 struct route_map_rule_cmd refcmd = {.str = name};
1283 struct route_map_rule_cmd_proxy ref = {.cmd = &refcmd};
1284 struct route_map_rule_cmd_proxy *res;
1285
1286 res = rmap_cmd_name_find(rmap_match_cmds, &ref);
1287 if (res)
1288 return res->cmd;
1289 return NULL;
1290 }
1291
1292 /* Lookup rule command from set list. */
1293 static const struct route_map_rule_cmd *route_map_lookup_set(const char *name)
1294 {
1295 struct route_map_rule_cmd refcmd = {.str = name};
1296 struct route_map_rule_cmd_proxy ref = {.cmd = &refcmd};
1297 struct route_map_rule_cmd_proxy *res;
1298
1299 res = rmap_cmd_name_find(rmap_set_cmds, &ref);
1300 if (res)
1301 return res->cmd;
1302 return NULL;
1303 }
1304
1305 /* Add match and set rule to rule list. */
1306 static void route_map_rule_add(struct route_map_rule_list *list,
1307 struct route_map_rule *rule)
1308 {
1309 rule->next = NULL;
1310 rule->prev = list->tail;
1311 if (list->tail)
1312 list->tail->next = rule;
1313 else
1314 list->head = rule;
1315 list->tail = rule;
1316 }
1317
1318 /* Delete rule from rule list. */
1319 static void route_map_rule_delete(struct route_map_rule_list *list,
1320 struct route_map_rule *rule)
1321 {
1322 if (rule->cmd->func_free)
1323 (*rule->cmd->func_free)(rule->value);
1324
1325 XFREE(MTYPE_ROUTE_MAP_RULE_STR, rule->rule_str);
1326
1327 if (rule->next)
1328 rule->next->prev = rule->prev;
1329 else
1330 list->tail = rule->prev;
1331 if (rule->prev)
1332 rule->prev->next = rule->next;
1333 else
1334 list->head = rule->next;
1335
1336 XFREE(MTYPE_ROUTE_MAP_RULE, rule);
1337 }
1338
1339 /* strcmp wrapper function which don't crush even argument is NULL. */
1340 static int rulecmp(const char *dst, const char *src)
1341 {
1342 if (dst == NULL) {
1343 if (src == NULL)
1344 return 0;
1345 else
1346 return 1;
1347 } else {
1348 if (src == NULL)
1349 return 1;
1350 else
1351 return strcmp(dst, src);
1352 }
1353 return 1;
1354 }
1355
1356 /* Use this to return the already specified argument for this match. This is
1357 * useful to get the specified argument with a route map match rule when the
1358 * rule is being deleted and the argument is not provided.
1359 */
1360 const char *route_map_get_match_arg(struct route_map_index *index,
1361 const char *match_name)
1362 {
1363 struct route_map_rule *rule;
1364 const struct route_map_rule_cmd *cmd;
1365
1366 /* First lookup rule for add match statement. */
1367 cmd = route_map_lookup_match(match_name);
1368 if (cmd == NULL)
1369 return NULL;
1370
1371 for (rule = index->match_list.head; rule; rule = rule->next)
1372 if (rule->cmd == cmd && rule->rule_str != NULL)
1373 return (rule->rule_str);
1374
1375 return NULL;
1376 }
1377
1378 static route_map_event_t get_route_map_delete_event(route_map_event_t type)
1379 {
1380 switch (type) {
1381 case RMAP_EVENT_CALL_ADDED:
1382 return RMAP_EVENT_CALL_DELETED;
1383 case RMAP_EVENT_PLIST_ADDED:
1384 return RMAP_EVENT_PLIST_DELETED;
1385 case RMAP_EVENT_CLIST_ADDED:
1386 return RMAP_EVENT_CLIST_DELETED;
1387 case RMAP_EVENT_ECLIST_ADDED:
1388 return RMAP_EVENT_ECLIST_DELETED;
1389 case RMAP_EVENT_LLIST_ADDED:
1390 return RMAP_EVENT_LLIST_DELETED;
1391 case RMAP_EVENT_ASLIST_ADDED:
1392 return RMAP_EVENT_ASLIST_DELETED;
1393 case RMAP_EVENT_FILTER_ADDED:
1394 return RMAP_EVENT_FILTER_DELETED;
1395 case RMAP_EVENT_SET_ADDED:
1396 case RMAP_EVENT_SET_DELETED:
1397 case RMAP_EVENT_SET_REPLACED:
1398 case RMAP_EVENT_MATCH_ADDED:
1399 case RMAP_EVENT_MATCH_DELETED:
1400 case RMAP_EVENT_MATCH_REPLACED:
1401 case RMAP_EVENT_INDEX_ADDED:
1402 case RMAP_EVENT_INDEX_DELETED:
1403 case RMAP_EVENT_CALL_DELETED:
1404 case RMAP_EVENT_PLIST_DELETED:
1405 case RMAP_EVENT_CLIST_DELETED:
1406 case RMAP_EVENT_ECLIST_DELETED:
1407 case RMAP_EVENT_LLIST_DELETED:
1408 case RMAP_EVENT_ASLIST_DELETED:
1409 case RMAP_EVENT_FILTER_DELETED:
1410 /* This function returns the appropriate 'deleted' event type
1411 * for every 'added' event type passed to this function.
1412 * This is done only for named entities used in the
1413 * route-map match commands.
1414 * This function is not to be invoked for any of the other event
1415 * types.
1416 */
1417 assert(0);
1418 }
1419
1420 assert(0);
1421 /*
1422 * Return to make c happy but if we get here something has gone
1423 * terribly terribly wrong, so yes this return makes no sense.
1424 */
1425 return RMAP_EVENT_CALL_ADDED;
1426 }
1427
1428 /* Add match statement to route map. */
1429 enum rmap_compile_rets route_map_add_match(struct route_map_index *index,
1430 const char *match_name,
1431 const char *match_arg,
1432 route_map_event_t type)
1433 {
1434 struct route_map_rule *rule;
1435 struct route_map_rule *next;
1436 const struct route_map_rule_cmd *cmd;
1437 void *compile;
1438 int8_t delete_rmap_event_type = 0;
1439 const char *rule_key;
1440
1441 /* First lookup rule for add match statement. */
1442 cmd = route_map_lookup_match(match_name);
1443 if (cmd == NULL)
1444 return RMAP_RULE_MISSING;
1445
1446 /* Next call compile function for this match statement. */
1447 if (cmd->func_compile) {
1448 compile = (*cmd->func_compile)(match_arg);
1449 if (compile == NULL)
1450 return RMAP_COMPILE_ERROR;
1451 } else
1452 compile = NULL;
1453 /* use the compiled results if applicable */
1454 if (compile && cmd->func_get_rmap_rule_key)
1455 rule_key = (*cmd->func_get_rmap_rule_key)
1456 (compile);
1457 else
1458 rule_key = match_arg;
1459
1460 /* If argument is completely same ignore it. */
1461 for (rule = index->match_list.head; rule; rule = next) {
1462 next = rule->next;
1463 if (rule->cmd == cmd) {
1464 /* If the configured route-map match rule is exactly
1465 * the same as the existing configuration then,
1466 * ignore the duplicate configuration.
1467 */
1468 if (rulecmp(match_arg, rule->rule_str) == 0) {
1469 if (cmd->func_free)
1470 (*cmd->func_free)(compile);
1471
1472 return RMAP_COMPILE_SUCCESS;
1473 }
1474
1475 /* If IPv4 or IPv6 prefix-list match criteria
1476 * has been delete to the route-map index, update
1477 * the route-map's prefix table.
1478 */
1479 if (IS_RULE_IPv4_PREFIX_LIST(match_name))
1480 route_map_pfx_tbl_update(
1481 RMAP_EVENT_PLIST_DELETED, index, AFI_IP,
1482 rule->rule_str);
1483 else if (IS_RULE_IPv6_PREFIX_LIST(match_name))
1484 route_map_pfx_tbl_update(
1485 RMAP_EVENT_PLIST_DELETED, index,
1486 AFI_IP6, rule->rule_str);
1487
1488 /* Remove the dependency of the route-map on the rule
1489 * that is being replaced.
1490 */
1491 if (type >= RMAP_EVENT_CALL_ADDED) {
1492 delete_rmap_event_type =
1493 get_route_map_delete_event(type);
1494 route_map_upd8_dependency(
1495 delete_rmap_event_type,
1496 rule->rule_str,
1497 index->map->name);
1498 }
1499
1500 route_map_rule_delete(&index->match_list, rule);
1501 }
1502 }
1503
1504 /* Add new route map match rule. */
1505 rule = route_map_rule_new();
1506 rule->cmd = cmd;
1507 rule->value = compile;
1508 if (match_arg)
1509 rule->rule_str = XSTRDUP(MTYPE_ROUTE_MAP_RULE_STR, match_arg);
1510 else
1511 rule->rule_str = NULL;
1512
1513 /* Add new route match rule to linked list. */
1514 route_map_rule_add(&index->match_list, rule);
1515
1516 /* If IPv4 or IPv6 prefix-list match criteria
1517 * has been added to the route-map index, update
1518 * the route-map's prefix table.
1519 */
1520 if (IS_RULE_IPv4_PREFIX_LIST(match_name)) {
1521 route_map_pfx_tbl_update(RMAP_EVENT_PLIST_ADDED, index, AFI_IP,
1522 match_arg);
1523 } else if (IS_RULE_IPv6_PREFIX_LIST(match_name)) {
1524 route_map_pfx_tbl_update(RMAP_EVENT_PLIST_ADDED, index, AFI_IP6,
1525 match_arg);
1526 }
1527
1528 /* Execute event hook. */
1529 if (route_map_master.event_hook) {
1530 (*route_map_master.event_hook)(index->map->name);
1531 route_map_notify_dependencies(index->map->name,
1532 RMAP_EVENT_CALL_ADDED);
1533 }
1534 if (type != RMAP_EVENT_MATCH_ADDED)
1535 route_map_upd8_dependency(type, rule_key, index->map->name);
1536
1537 return RMAP_COMPILE_SUCCESS;
1538 }
1539
1540 /* Delete specified route match rule. */
1541 enum rmap_compile_rets route_map_delete_match(struct route_map_index *index,
1542 const char *match_name,
1543 const char *match_arg,
1544 route_map_event_t type)
1545 {
1546 struct route_map_rule *rule;
1547 const struct route_map_rule_cmd *cmd;
1548 const char *rule_key;
1549
1550 cmd = route_map_lookup_match(match_name);
1551 if (cmd == NULL)
1552 return RMAP_RULE_MISSING;
1553
1554 for (rule = index->match_list.head; rule; rule = rule->next)
1555 if (rule->cmd == cmd && (rulecmp(rule->rule_str, match_arg) == 0
1556 || match_arg == NULL)) {
1557 /* Execute event hook. */
1558 if (route_map_master.event_hook) {
1559 (*route_map_master.event_hook)(index->map->name);
1560 route_map_notify_dependencies(
1561 index->map->name,
1562 RMAP_EVENT_CALL_ADDED);
1563 }
1564 if (cmd->func_get_rmap_rule_key)
1565 rule_key = (*cmd->func_get_rmap_rule_key)
1566 (rule->value);
1567 else
1568 rule_key = match_arg;
1569
1570 if (type != RMAP_EVENT_MATCH_DELETED && rule_key)
1571 route_map_upd8_dependency(type, rule_key,
1572 index->map->name);
1573
1574 route_map_rule_delete(&index->match_list, rule);
1575
1576 /* If IPv4 or IPv6 prefix-list match criteria
1577 * has been delete from the route-map index, update
1578 * the route-map's prefix table.
1579 */
1580 if (IS_RULE_IPv4_PREFIX_LIST(match_name)) {
1581 route_map_pfx_tbl_update(
1582 RMAP_EVENT_PLIST_DELETED, index, AFI_IP,
1583 match_arg);
1584 } else if (IS_RULE_IPv6_PREFIX_LIST(match_name)) {
1585 route_map_pfx_tbl_update(
1586 RMAP_EVENT_PLIST_DELETED, index,
1587 AFI_IP6, match_arg);
1588 }
1589
1590 return RMAP_COMPILE_SUCCESS;
1591 }
1592 /* Can't find matched rule. */
1593 return RMAP_RULE_MISSING;
1594 }
1595
1596 /* Add route-map set statement to the route map. */
1597 enum rmap_compile_rets route_map_add_set(struct route_map_index *index,
1598 const char *set_name,
1599 const char *set_arg)
1600 {
1601 struct route_map_rule *rule;
1602 struct route_map_rule *next;
1603 const struct route_map_rule_cmd *cmd;
1604 void *compile;
1605
1606 cmd = route_map_lookup_set(set_name);
1607 if (cmd == NULL)
1608 return RMAP_RULE_MISSING;
1609
1610 /* Next call compile function for this match statement. */
1611 if (cmd->func_compile) {
1612 compile = (*cmd->func_compile)(set_arg);
1613 if (compile == NULL)
1614 return RMAP_COMPILE_ERROR;
1615 } else
1616 compile = NULL;
1617
1618 /* Add by WJL. if old set command of same kind exist, delete it first
1619 to ensure only one set command of same kind exist under a
1620 route_map_index. */
1621 for (rule = index->set_list.head; rule; rule = next) {
1622 next = rule->next;
1623 if (rule->cmd == cmd)
1624 route_map_rule_delete(&index->set_list, rule);
1625 }
1626
1627 /* Add new route map match rule. */
1628 rule = route_map_rule_new();
1629 rule->cmd = cmd;
1630 rule->value = compile;
1631 if (set_arg)
1632 rule->rule_str = XSTRDUP(MTYPE_ROUTE_MAP_RULE_STR, set_arg);
1633 else
1634 rule->rule_str = NULL;
1635
1636 /* Add new route match rule to linked list. */
1637 route_map_rule_add(&index->set_list, rule);
1638
1639 /* Execute event hook. */
1640 if (route_map_master.event_hook) {
1641 (*route_map_master.event_hook)(index->map->name);
1642 route_map_notify_dependencies(index->map->name,
1643 RMAP_EVENT_CALL_ADDED);
1644 }
1645 return RMAP_COMPILE_SUCCESS;
1646 }
1647
1648 /* Delete route map set rule. */
1649 enum rmap_compile_rets route_map_delete_set(struct route_map_index *index,
1650 const char *set_name,
1651 const char *set_arg)
1652 {
1653 struct route_map_rule *rule;
1654 const struct route_map_rule_cmd *cmd;
1655
1656 cmd = route_map_lookup_set(set_name);
1657 if (cmd == NULL)
1658 return RMAP_RULE_MISSING;
1659
1660 for (rule = index->set_list.head; rule; rule = rule->next)
1661 if ((rule->cmd == cmd) && (rulecmp(rule->rule_str, set_arg) == 0
1662 || set_arg == NULL)) {
1663 route_map_rule_delete(&index->set_list, rule);
1664 /* Execute event hook. */
1665 if (route_map_master.event_hook) {
1666 (*route_map_master.event_hook)(index->map->name);
1667 route_map_notify_dependencies(
1668 index->map->name,
1669 RMAP_EVENT_CALL_ADDED);
1670 }
1671 return RMAP_COMPILE_SUCCESS;
1672 }
1673 /* Can't find matched rule. */
1674 return RMAP_RULE_MISSING;
1675 }
1676
1677 static enum route_map_cmd_result_t
1678 route_map_apply_match(struct route_map_rule_list *match_list,
1679 const struct prefix *prefix, void *object)
1680 {
1681 enum route_map_cmd_result_t ret = RMAP_NOMATCH;
1682 struct route_map_rule *match;
1683 bool is_matched = false;
1684
1685
1686 /* Check all match rule and if there is no match rule, go to the
1687 set statement. */
1688 if (!match_list->head)
1689 ret = RMAP_MATCH;
1690 else {
1691 for (match = match_list->head; match; match = match->next) {
1692 /*
1693 * Try each match statement. If any match does not
1694 * return RMAP_MATCH or RMAP_NOOP, return.
1695 * Otherwise continue on to next match statement.
1696 * All match statements must MATCH for
1697 * end-result to be a match.
1698 * (Exception:If match stmts result in a mix of
1699 * MATCH/NOOP, then also end-result is a match)
1700 * If all result in NOOP, end-result is NOOP.
1701 */
1702 ret = (*match->cmd->func_apply)(match->value, prefix,
1703 object);
1704
1705 /*
1706 * If the consolidated result of func_apply is:
1707 * -----------------------------------------------
1708 * | MATCH | NOMATCH | NOOP | Final Result |
1709 * ------------------------------------------------
1710 * | yes | yes | yes | NOMATCH |
1711 * | no | no | yes | NOOP |
1712 * | yes | no | yes | MATCH |
1713 * | no | yes | yes | NOMATCH |
1714 * |-----------------------------------------------
1715 *
1716 * Traditionally, all rules within route-map
1717 * should match for it to MATCH.
1718 * If there are noops within the route-map rules,
1719 * it follows the above matrix.
1720 *
1721 * Eg: route-map rm1 permit 10
1722 * match rule1
1723 * match rule2
1724 * match rule3
1725 * ....
1726 * route-map rm1 permit 20
1727 * match ruleX
1728 * match ruleY
1729 * ...
1730 */
1731
1732 switch (ret) {
1733 case RMAP_MATCH:
1734 is_matched = true;
1735 break;
1736
1737 case RMAP_NOMATCH:
1738 return ret;
1739
1740 case RMAP_NOOP:
1741 if (is_matched)
1742 ret = RMAP_MATCH;
1743 break;
1744
1745 case RMAP_OKAY:
1746 case RMAP_ERROR:
1747 break;
1748 }
1749
1750 }
1751 }
1752 return ret;
1753 }
1754
1755 static struct list *route_map_get_index_list(struct route_node **rn,
1756 const struct prefix *prefix,
1757 struct route_table *table)
1758 {
1759 struct route_node *tmp_rn = NULL;
1760
1761 if (!(*rn)) {
1762 *rn = route_node_match(table, prefix);
1763
1764 if (!(*rn))
1765 return NULL;
1766
1767 if ((*rn)->info)
1768 return (struct list *)((*rn)->info);
1769
1770 /* If rn->info is NULL, get the parent.
1771 * Store the rn in tmp_rn and unlock it later.
1772 */
1773 tmp_rn = *rn;
1774 }
1775
1776 do {
1777 *rn = (*rn)->parent;
1778 if (tmp_rn)
1779 route_unlock_node(tmp_rn);
1780
1781 if (!(*rn))
1782 break;
1783
1784 if ((*rn)->info) {
1785 route_lock_node(*rn);
1786 return (struct list *)((*rn)->info);
1787 }
1788 } while (!(*rn)->info);
1789
1790 return NULL;
1791 }
1792
1793 /*
1794 * This function returns the route-map index that best matches the prefix.
1795 */
1796 static struct route_map_index *
1797 route_map_get_index(struct route_map *map, const struct prefix *prefix,
1798 void *object, enum route_map_cmd_result_t *match_ret)
1799 {
1800 enum route_map_cmd_result_t ret = RMAP_NOMATCH;
1801 struct list *candidate_rmap_list = NULL;
1802 struct route_node *rn = NULL;
1803 struct listnode *ln = NULL, *nn = NULL;
1804 struct route_map_index *index = NULL, *best_index = NULL;
1805 struct route_map_index *head_index = NULL;
1806 struct route_table *table = NULL;
1807
1808 /* Route-map optimization relies on LPM lookups of the prefix to reduce
1809 * the amount of route-map clauses a given prefix needs to be processed
1810 * against. These LPM trees are IPv4/IPv6-specific and prefix->family
1811 * must be AF_INET or AF_INET6 in order for the lookup to succeed. So if
1812 * the AF doesn't line up with the LPM trees, skip the optimization.
1813 */
1814 if (map->optimization_disabled ||
1815 (prefix->family == AF_INET && !map->ipv4_prefix_table) ||
1816 (prefix->family == AF_INET6 && !map->ipv6_prefix_table)) {
1817 if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))
1818 zlog_debug(
1819 "Skipping route-map optimization for route-map: %s, pfx: %pFX, family: %d",
1820 map->name, prefix, prefix->family);
1821 return map->head;
1822 }
1823
1824 if (prefix->family == AF_INET)
1825 table = map->ipv4_prefix_table;
1826 else
1827 table = map->ipv6_prefix_table;
1828
1829 if (!table)
1830 return NULL;
1831
1832 do {
1833 candidate_rmap_list =
1834 route_map_get_index_list(&rn, prefix, table);
1835 if (!rn)
1836 break;
1837
1838 /* If the index at the head of the list is of seq higher
1839 * than that in best_index, ignore the list and get the
1840 * parent node's list.
1841 */
1842 head_index = (struct route_map_index *)(listgetdata(
1843 listhead(candidate_rmap_list)));
1844 if (best_index && head_index
1845 && (best_index->pref < head_index->pref)) {
1846 route_unlock_node(rn);
1847 continue;
1848 }
1849
1850 for (ALL_LIST_ELEMENTS(candidate_rmap_list, ln, nn, index)) {
1851 /* If the index is of seq higher than that in
1852 * best_index, ignore the list and get the parent
1853 * node's list.
1854 */
1855 if (best_index && (best_index->pref < index->pref))
1856 break;
1857
1858 ret = route_map_apply_match(&index->match_list, prefix,
1859 object);
1860
1861 if (ret == RMAP_MATCH) {
1862 *match_ret = ret;
1863 best_index = index;
1864 break;
1865 } else if (ret == RMAP_NOOP) {
1866 /*
1867 * If match_ret is denymatch, even if we see
1868 * more noops, we retain this return value and
1869 * return this eventually if there are no
1870 * matches.
1871 * If a best match route-map index already
1872 * exists, do not reset the match_ret.
1873 */
1874 if (!best_index && (*match_ret != RMAP_NOMATCH))
1875 *match_ret = ret;
1876 } else {
1877 /*
1878 * ret is RMAP_NOMATCH.
1879 * If a best match route-map index already
1880 * exists, do not reset the match_ret.
1881 */
1882 if (!best_index)
1883 *match_ret = ret;
1884 }
1885 }
1886
1887 route_unlock_node(rn);
1888
1889 } while (rn);
1890
1891 return best_index;
1892 }
1893
1894 static int route_map_candidate_list_cmp(struct route_map_index *idx1,
1895 struct route_map_index *idx2)
1896 {
1897 return idx1->pref - idx2->pref;
1898 }
1899
1900 /*
1901 * This function adds the route-map index into the default route's
1902 * route-node in the route-map's IPv4/IPv6 prefix-table.
1903 */
1904 static void route_map_pfx_table_add_default(afi_t afi,
1905 struct route_map_index *index)
1906 {
1907 struct route_node *rn = NULL;
1908 struct list *rmap_candidate_list = NULL;
1909 struct prefix p;
1910 bool updated_rn = false;
1911 struct route_table *table = NULL;
1912
1913 memset(&p, 0, sizeof(p));
1914 p.family = afi2family(afi);
1915 p.prefixlen = 0;
1916
1917 if (p.family == AF_INET) {
1918 table = index->map->ipv4_prefix_table;
1919 if (!table)
1920 index->map->ipv4_prefix_table = route_table_init();
1921
1922 table = index->map->ipv4_prefix_table;
1923 } else {
1924 table = index->map->ipv6_prefix_table;
1925 if (!table)
1926 index->map->ipv6_prefix_table = route_table_init();
1927
1928 table = index->map->ipv6_prefix_table;
1929 }
1930
1931 /* Add default route to table */
1932 rn = route_node_get(table, &p);
1933
1934 if (!rn)
1935 return;
1936
1937 if (!rn->info) {
1938 rmap_candidate_list = list_new();
1939 rmap_candidate_list->cmp =
1940 (int (*)(void *, void *))route_map_candidate_list_cmp;
1941 rn->info = rmap_candidate_list;
1942 } else {
1943 rmap_candidate_list = (struct list *)rn->info;
1944 updated_rn = true;
1945 }
1946
1947 listnode_add_sort_nodup(rmap_candidate_list, index);
1948 if (updated_rn)
1949 route_unlock_node(rn);
1950 }
1951
1952 /*
1953 * This function removes the route-map index from the default route's
1954 * route-node in the route-map's IPv4/IPv6 prefix-table.
1955 */
1956 static void route_map_pfx_table_del_default(afi_t afi,
1957 struct route_map_index *index)
1958 {
1959 struct route_node *rn = NULL;
1960 struct list *rmap_candidate_list = NULL;
1961 struct prefix p;
1962 struct route_table *table = NULL;
1963
1964 memset(&p, 0, sizeof(p));
1965 p.family = afi2family(afi);
1966 p.prefixlen = 0;
1967
1968 if (p.family == AF_INET)
1969 table = index->map->ipv4_prefix_table;
1970 else
1971 table = index->map->ipv6_prefix_table;
1972
1973 /* Remove RMAP index from default route in table */
1974 rn = route_node_lookup(table, &p);
1975 if (!rn || !rn->info)
1976 return;
1977
1978 rmap_candidate_list = (struct list *)rn->info;
1979
1980 listnode_delete(rmap_candidate_list, index);
1981
1982 if (listcount(rmap_candidate_list) == 0) {
1983 list_delete(&rmap_candidate_list);
1984 rn->info = NULL;
1985 route_unlock_node(rn);
1986 }
1987 route_unlock_node(rn);
1988 }
1989
1990 /*
1991 * This function adds the route-map index to the route-node for
1992 * the prefix-entry in the route-map's IPv4/IPv6 prefix-table.
1993 */
1994 static void route_map_pfx_table_add(struct route_table *table,
1995 struct route_map_index *index,
1996 struct prefix_list_entry *pentry)
1997 {
1998 struct route_node *rn = NULL;
1999 struct list *rmap_candidate_list = NULL;
2000 bool updated_rn = false;
2001
2002 rn = route_node_get(table, &pentry->prefix);
2003 if (!rn)
2004 return;
2005
2006 if (!rn->info) {
2007 rmap_candidate_list = list_new();
2008 rmap_candidate_list->cmp =
2009 (int (*)(void *, void *))route_map_candidate_list_cmp;
2010 rn->info = rmap_candidate_list;
2011 } else {
2012 rmap_candidate_list = (struct list *)rn->info;
2013 updated_rn = true;
2014 }
2015
2016 listnode_add_sort_nodup(rmap_candidate_list, index);
2017 if (updated_rn)
2018 route_unlock_node(rn);
2019 }
2020
2021 /*
2022 * This function removes the route-map index from the route-node for
2023 * the prefix-entry in the route-map's IPv4/IPv6 prefix-table.
2024 */
2025 static void route_map_pfx_table_del(struct route_table *table,
2026 struct route_map_index *index,
2027 struct prefix_list_entry *pentry)
2028 {
2029 struct route_node *rn = NULL;
2030 struct list *rmap_candidate_list = NULL;
2031
2032 rn = route_node_lookup(table, &pentry->prefix);
2033 if (!rn || !rn->info)
2034 return;
2035
2036 rmap_candidate_list = (struct list *)rn->info;
2037
2038 listnode_delete(rmap_candidate_list, index);
2039
2040 if (listcount(rmap_candidate_list) == 0) {
2041 list_delete(&rmap_candidate_list);
2042 rn->info = NULL;
2043 route_unlock_node(rn);
2044 }
2045 route_unlock_node(rn);
2046 }
2047
2048 /* This function checks for the presence of an IPv4 prefix-list
2049 * match rule in the given route-map index.
2050 */
2051 static bool route_map_is_ip_pfx_list_rule_present(struct route_map_index *index)
2052 {
2053 struct route_map_rule_list *match_list = NULL;
2054 struct route_map_rule *rule = NULL;
2055
2056 match_list = &index->match_list;
2057 for (rule = match_list->head; rule; rule = rule->next)
2058 if (IS_RULE_IPv4_PREFIX_LIST(rule->cmd->str))
2059 return true;
2060
2061 return false;
2062 }
2063
2064 /* This function checks for the presence of an IPv6 prefix-list
2065 * match rule in the given route-map index.
2066 */
2067 static bool
2068 route_map_is_ipv6_pfx_list_rule_present(struct route_map_index *index)
2069 {
2070 struct route_map_rule_list *match_list = NULL;
2071 struct route_map_rule *rule = NULL;
2072
2073 match_list = &index->match_list;
2074 for (rule = match_list->head; rule; rule = rule->next)
2075 if (IS_RULE_IPv6_PREFIX_LIST(rule->cmd->str))
2076 return true;
2077
2078 return false;
2079 }
2080
2081 /* This function does the following:
2082 * 1) If plist_name is not present, search for a IPv4 or IPv6 prefix-list
2083 * match clause (based on the afi passed to this foo) and get the
2084 * prefix-list name.
2085 * 2) Look up the prefix-list using the name.
2086 * 3) If the prefix-list is not found then, add the index to the IPv4/IPv6
2087 * default-route's node in the trie (based on the afi passed to this foo).
2088 * 4) If the prefix-list is found then, remove the index from the IPv4/IPv6
2089 * default-route's node in the trie (based on the afi passed to this foo).
2090 * 5) If a prefix-entry is passed then, create a route-node for this entry and
2091 * add this index to the route-node.
2092 * 6) If prefix-entry is not passed then, for every prefix-entry in the
2093 * prefix-list, create a route-node for this entry and
2094 * add this index to the route-node.
2095 */
2096 static void route_map_add_plist_entries(afi_t afi,
2097 struct route_map_index *index,
2098 const char *plist_name,
2099 struct prefix_list_entry *entry)
2100 {
2101 struct route_map_rule_list *match_list = NULL;
2102 struct route_map_rule *match = NULL;
2103 struct prefix_list *plist = NULL;
2104 struct prefix_list_entry *pentry = NULL;
2105 bool plist_rule_is_present = false;
2106
2107 if (!plist_name) {
2108 match_list = &index->match_list;
2109
2110 for (match = match_list->head; match; match = match->next) {
2111 if (afi == AFI_IP) {
2112 if (IS_RULE_IPv4_PREFIX_LIST(match->cmd->str)) {
2113 plist_rule_is_present = true;
2114 break;
2115 }
2116 } else {
2117 if (IS_RULE_IPv6_PREFIX_LIST(match->cmd->str)) {
2118 plist_rule_is_present = true;
2119 break;
2120 }
2121 }
2122 }
2123
2124 if (plist_rule_is_present)
2125 plist = prefix_list_lookup(afi, match->rule_str);
2126 } else {
2127 plist = prefix_list_lookup(afi, plist_name);
2128 }
2129
2130 if (!plist) {
2131 route_map_pfx_table_add_default(afi, index);
2132 return;
2133 }
2134
2135 /* Default entry should be deleted only if the first entry of the
2136 * prefix-list is created.
2137 */
2138 if (entry) {
2139 if (plist->count == 1)
2140 route_map_pfx_table_del_default(afi, index);
2141 } else {
2142 route_map_pfx_table_del_default(afi, index);
2143 }
2144
2145 if (entry) {
2146 if (afi == AFI_IP) {
2147 route_map_pfx_table_add(index->map->ipv4_prefix_table,
2148 index, entry);
2149 } else {
2150 route_map_pfx_table_add(index->map->ipv6_prefix_table,
2151 index, entry);
2152 }
2153 } else {
2154 for (pentry = plist->head; pentry; pentry = pentry->next) {
2155 if (afi == AFI_IP) {
2156 route_map_pfx_table_add(
2157 index->map->ipv4_prefix_table, index,
2158 pentry);
2159 } else {
2160 route_map_pfx_table_add(
2161 index->map->ipv6_prefix_table, index,
2162 pentry);
2163 }
2164 }
2165 }
2166 }
2167
2168 /* This function does the following:
2169 * 1) If plist_name is not present, search for a IPv4 or IPv6 prefix-list
2170 * match clause (based on the afi passed to this foo) and get the
2171 * prefix-list name.
2172 * 2) Look up the prefix-list using the name.
2173 * 3) If the prefix-list is not found then, delete the index from the IPv4/IPv6
2174 * default-route's node in the trie (based on the afi passed to this foo).
2175 * 4) If a prefix-entry is passed then, remove this index from the route-node
2176 * for the prefix in this prefix-entry.
2177 * 5) If prefix-entry is not passed then, for every prefix-entry in the
2178 * prefix-list, remove this index from the route-node
2179 * for the prefix in this prefix-entry.
2180 */
2181 static void route_map_del_plist_entries(afi_t afi,
2182 struct route_map_index *index,
2183 const char *plist_name,
2184 struct prefix_list_entry *entry)
2185 {
2186 struct route_map_rule_list *match_list = NULL;
2187 struct route_map_rule *match = NULL;
2188 struct prefix_list *plist = NULL;
2189 struct prefix_list_entry *pentry = NULL;
2190 bool plist_rule_is_present = false;
2191
2192 if (!plist_name) {
2193 match_list = &index->match_list;
2194
2195 for (match = match_list->head; match; match = match->next) {
2196 if (afi == AFI_IP) {
2197 if (IS_RULE_IPv4_PREFIX_LIST(match->cmd->str)) {
2198 plist_rule_is_present = true;
2199 break;
2200 }
2201 } else {
2202 if (IS_RULE_IPv6_PREFIX_LIST(match->cmd->str)) {
2203 plist_rule_is_present = true;
2204 break;
2205 }
2206 }
2207 }
2208
2209 if (plist_rule_is_present)
2210 plist = prefix_list_lookup(afi, match->rule_str);
2211 } else {
2212 plist = prefix_list_lookup(afi, plist_name);
2213 }
2214
2215 if (!plist) {
2216 route_map_pfx_table_del_default(afi, index);
2217 return;
2218 }
2219
2220 if (entry) {
2221 if (afi == AFI_IP) {
2222 route_map_pfx_table_del(index->map->ipv4_prefix_table,
2223 index, entry);
2224 } else {
2225 route_map_pfx_table_del(index->map->ipv6_prefix_table,
2226 index, entry);
2227 }
2228 } else {
2229 for (pentry = plist->head; pentry; pentry = pentry->next) {
2230 if (afi == AFI_IP) {
2231 route_map_pfx_table_del(
2232 index->map->ipv4_prefix_table, index,
2233 pentry);
2234 } else {
2235 route_map_pfx_table_del(
2236 index->map->ipv6_prefix_table, index,
2237 pentry);
2238 }
2239 }
2240 }
2241 }
2242
2243 /*
2244 * This function handles the cases where a prefix-list is added/removed
2245 * as a match command from a particular route-map index.
2246 * It updates the prefix-table of the route-map accordingly.
2247 */
2248 static void route_map_trie_update(afi_t afi, route_map_event_t event,
2249 struct route_map_index *index,
2250 const char *plist_name)
2251 {
2252 if (event == RMAP_EVENT_PLIST_ADDED) {
2253 if (afi == AFI_IP) {
2254 if (!route_map_is_ipv6_pfx_list_rule_present(index)) {
2255 route_map_pfx_table_del_default(AFI_IP6, index);
2256 route_map_add_plist_entries(afi, index,
2257 plist_name, NULL);
2258 } else {
2259 route_map_del_plist_entries(AFI_IP6, index,
2260 NULL, NULL);
2261 }
2262 } else {
2263 if (!route_map_is_ip_pfx_list_rule_present(index)) {
2264 route_map_pfx_table_del_default(AFI_IP, index);
2265 route_map_add_plist_entries(afi, index,
2266 plist_name, NULL);
2267 } else {
2268 route_map_del_plist_entries(AFI_IP, index, NULL,
2269 NULL);
2270 }
2271 }
2272 } else if (event == RMAP_EVENT_PLIST_DELETED) {
2273 if (afi == AFI_IP) {
2274 route_map_del_plist_entries(afi, index, plist_name,
2275 NULL);
2276
2277 /* If IPv6 prefix-list match rule is not present,
2278 * add this index to the IPv4 default route's trie
2279 * node.
2280 * Also, add this index to the trie nodes created
2281 * for each of the prefix-entries within the IPv6
2282 * prefix-list, if the IPv6 prefix-list match rule
2283 * is present. Else, add this index to the IPv6
2284 * default route's trie node.
2285 */
2286 if (!route_map_is_ipv6_pfx_list_rule_present(index))
2287 route_map_pfx_table_add_default(afi, index);
2288
2289 route_map_add_plist_entries(AFI_IP6, index, NULL, NULL);
2290 } else {
2291 route_map_del_plist_entries(afi, index, plist_name,
2292 NULL);
2293
2294 /* If IPv4 prefix-list match rule is not present,
2295 * add this index to the IPv6 default route's trie
2296 * node.
2297 * Also, add this index to the trie nodes created
2298 * for each of the prefix-entries within the IPv4
2299 * prefix-list, if the IPv4 prefix-list match rule
2300 * is present. Else, add this index to the IPv4
2301 * default route's trie node.
2302 */
2303 if (!route_map_is_ip_pfx_list_rule_present(index))
2304 route_map_pfx_table_add_default(afi, index);
2305
2306 route_map_add_plist_entries(AFI_IP, index, NULL, NULL);
2307 }
2308 }
2309 }
2310
2311 /*
2312 * This function handles the cases where a route-map index and
2313 * prefix-list is added/removed.
2314 * It updates the prefix-table of the route-map accordingly.
2315 */
2316 static void route_map_pfx_tbl_update(route_map_event_t event,
2317 struct route_map_index *index, afi_t afi,
2318 const char *plist_name)
2319 {
2320 struct route_map *rmap = NULL;
2321
2322 if (!index)
2323 return;
2324
2325 if (event == RMAP_EVENT_INDEX_ADDED) {
2326 route_map_pfx_table_add_default(AFI_IP, index);
2327 route_map_pfx_table_add_default(AFI_IP6, index);
2328 return;
2329 }
2330
2331 if (event == RMAP_EVENT_INDEX_DELETED) {
2332 route_map_pfx_table_del_default(AFI_IP, index);
2333 route_map_pfx_table_del_default(AFI_IP6, index);
2334
2335 if ((index->map->head == NULL) && (index->map->tail == NULL)) {
2336 rmap = index->map;
2337
2338 if (rmap->ipv4_prefix_table) {
2339 route_table_finish(rmap->ipv4_prefix_table);
2340 rmap->ipv4_prefix_table = NULL;
2341 }
2342
2343 if (rmap->ipv6_prefix_table) {
2344 route_table_finish(rmap->ipv6_prefix_table);
2345 rmap->ipv6_prefix_table = NULL;
2346 }
2347 }
2348 return;
2349 }
2350
2351 /* Handle prefix-list match rule addition/deletion.
2352 */
2353 route_map_trie_update(afi, event, index, plist_name);
2354 }
2355
2356 /*
2357 * This function handles the cases where a new prefix-entry is added to
2358 * a prefix-list or, an existing prefix-entry is removed from the prefix-list.
2359 * It updates the prefix-table of the route-map accordingly.
2360 */
2361 static void route_map_pentry_update(route_map_event_t event,
2362 const char *plist_name,
2363 struct route_map_index *index,
2364 struct prefix_list_entry *pentry)
2365 {
2366 struct prefix_list *plist = NULL;
2367 afi_t afi;
2368 unsigned char family = pentry->prefix.family;
2369
2370 if (family == AF_INET) {
2371 afi = AFI_IP;
2372 plist = prefix_list_lookup(AFI_IP, plist_name);
2373 } else {
2374 afi = AFI_IP6;
2375 plist = prefix_list_lookup(AFI_IP6, plist_name);
2376 }
2377
2378 if (event == RMAP_EVENT_PLIST_ADDED) {
2379 if (afi == AFI_IP) {
2380 if (!route_map_is_ipv6_pfx_list_rule_present(index))
2381 route_map_add_plist_entries(afi, index,
2382 plist_name, pentry);
2383 } else {
2384 if (!route_map_is_ip_pfx_list_rule_present(index))
2385 route_map_add_plist_entries(afi, index,
2386 plist_name, pentry);
2387 }
2388 } else if (event == RMAP_EVENT_PLIST_DELETED) {
2389 route_map_del_plist_entries(afi, index, plist_name, pentry);
2390
2391 if (plist->count == 1) {
2392 if (afi == AFI_IP) {
2393 if (!route_map_is_ipv6_pfx_list_rule_present(
2394 index))
2395 route_map_pfx_table_add_default(afi,
2396 index);
2397 } else {
2398 if (!route_map_is_ip_pfx_list_rule_present(
2399 index))
2400 route_map_pfx_table_add_default(afi,
2401 index);
2402 }
2403 }
2404 }
2405 }
2406
2407 static void route_map_pentry_process_dependency(struct hash_bucket *bucket,
2408 void *data)
2409 {
2410 char *rmap_name = NULL;
2411 struct route_map *rmap = NULL;
2412 struct route_map_index *index = NULL;
2413 struct route_map_rule_list *match_list = NULL;
2414 struct route_map_rule *match = NULL;
2415 struct route_map_dep_data *dep_data = NULL;
2416 struct route_map_pentry_dep *pentry_dep =
2417 (struct route_map_pentry_dep *)data;
2418 unsigned char family = pentry_dep->pentry->prefix.family;
2419
2420 dep_data = (struct route_map_dep_data *)bucket->data;
2421 if (!dep_data)
2422 return;
2423
2424 rmap_name = dep_data->rname;
2425 rmap = route_map_lookup_by_name(rmap_name);
2426 if (!rmap || !rmap->head)
2427 return;
2428
2429 for (index = rmap->head; index; index = index->next) {
2430 match_list = &index->match_list;
2431
2432 if (!match_list)
2433 continue;
2434
2435 for (match = match_list->head; match; match = match->next) {
2436 if (strcmp(match->rule_str, pentry_dep->plist_name)
2437 == 0) {
2438 if (IS_RULE_IPv4_PREFIX_LIST(match->cmd->str)
2439 && family == AF_INET) {
2440 route_map_pentry_update(
2441 pentry_dep->event,
2442 pentry_dep->plist_name, index,
2443 pentry_dep->pentry);
2444 } else if (IS_RULE_IPv6_PREFIX_LIST(
2445 match->cmd->str)
2446 && family == AF_INET6) {
2447 route_map_pentry_update(
2448 pentry_dep->event,
2449 pentry_dep->plist_name, index,
2450 pentry_dep->pentry);
2451 }
2452 }
2453 }
2454 }
2455 }
2456
2457 void route_map_notify_pentry_dependencies(const char *affected_name,
2458 struct prefix_list_entry *pentry,
2459 route_map_event_t event)
2460 {
2461 struct route_map_dep *dep = NULL;
2462 struct hash *upd8_hash = NULL;
2463 struct route_map_pentry_dep pentry_dep;
2464
2465 if (!affected_name || !pentry)
2466 return;
2467
2468 upd8_hash = route_map_get_dep_hash(event);
2469 if (!upd8_hash)
2470 return;
2471
2472 dep = (struct route_map_dep *)hash_get(upd8_hash, (void *)affected_name,
2473 NULL);
2474 if (dep) {
2475 if (!dep->this_hash)
2476 dep->this_hash = upd8_hash;
2477
2478 memset(&pentry_dep, 0, sizeof(pentry_dep));
2479 pentry_dep.pentry = pentry;
2480 pentry_dep.plist_name = affected_name;
2481 pentry_dep.event = event;
2482
2483 hash_iterate(dep->dep_rmap_hash,
2484 route_map_pentry_process_dependency,
2485 (void *)&pentry_dep);
2486 }
2487 }
2488
2489 /* Apply route map's each index to the object.
2490
2491 The matrix for a route-map looks like this:
2492 (note, this includes the description for the "NEXT"
2493 and "GOTO" frobs now
2494
2495 | Match | No Match | No op
2496 |-----------|--------------|-------
2497 permit | action | cont | cont.
2498 | | default:deny | default:permit
2499 -------------------+-----------------------
2500 | deny | cont | cont.
2501 deny | | default:deny | default:permit
2502 |-----------|--------------|--------
2503
2504 action)
2505 -Apply Set statements, accept route
2506 -If Call statement is present jump to the specified route-map, if it
2507 denies the route we finish.
2508 -If NEXT is specified, goto NEXT statement
2509 -If GOTO is specified, goto the first clause where pref > nextpref
2510 -If nothing is specified, do as Cisco and finish
2511 deny)
2512 -Route is denied by route-map.
2513 cont)
2514 -Goto Next index
2515
2516 If we get no matches after we've processed all updates, then the route
2517 is dropped too.
2518
2519 Some notes on the new "CALL", "NEXT" and "GOTO"
2520 call WORD - If this clause is matched, then the set statements
2521 are executed and then we jump to route-map 'WORD'. If
2522 this route-map denies the route, we finish, in other
2523 case we
2524 do whatever the exit policy (EXIT, NEXT or GOTO) tells.
2525 on-match next - If this clause is matched, then the set statements
2526 are executed and then we drop through to the next clause
2527 on-match goto n - If this clause is matched, then the set statements
2528 are executed and then we goto the nth clause, or the
2529 first clause greater than this. In order to ensure
2530 route-maps *always* exit, you cannot jump backwards.
2531 Sorry ;)
2532
2533 We need to make sure our route-map processing matches the above
2534 */
2535 route_map_result_t route_map_apply_ext(struct route_map *map,
2536 const struct prefix *prefix,
2537 void *match_object, void *set_object,
2538 int *pref)
2539 {
2540 static int recursion = 0;
2541 enum route_map_cmd_result_t match_ret = RMAP_NOMATCH;
2542 route_map_result_t ret = RMAP_PERMITMATCH;
2543 struct route_map_index *index = NULL;
2544 struct route_map_rule *set = NULL;
2545 bool skip_match_clause = false;
2546 struct prefix conv;
2547
2548 if (recursion > RMAP_RECURSION_LIMIT) {
2549 flog_warn(
2550 EC_LIB_RMAP_RECURSION_LIMIT,
2551 "route-map recursion limit (%d) reached, discarding route",
2552 RMAP_RECURSION_LIMIT);
2553 recursion = 0;
2554 return RMAP_DENYMATCH;
2555 }
2556
2557 if (map == NULL || map->head == NULL) {
2558 ret = RMAP_DENYMATCH;
2559 goto route_map_apply_end;
2560 }
2561
2562 map->applied++;
2563
2564 /*
2565 * Handling for matching evpn_routes in the prefix table.
2566 *
2567 * We convert type2/5 prefix to ipv4/6 prefix to do longest
2568 * prefix matching on.
2569 */
2570 if (prefix->family == AF_EVPN) {
2571 if (evpn_prefix2prefix(prefix, &conv) != 0) {
2572 zlog_debug(
2573 "Unable to convert EVPN prefix %pFX into IPv4/IPv6 prefix. Falling back to non-optimized route-map lookup",
2574 prefix);
2575 } else {
2576 zlog_debug(
2577 "Converted EVPN prefix %pFX into %pFX for optimized route-map lookup",
2578 prefix, &conv);
2579
2580 prefix = &conv;
2581 }
2582 }
2583
2584 index = route_map_get_index(map, prefix, match_object, &match_ret);
2585 if (index) {
2586 index->applied++;
2587 if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))
2588 zlog_debug(
2589 "Best match route-map: %s, sequence: %d for pfx: %pFX, result: %s",
2590 map->name, index->pref, prefix,
2591 route_map_cmd_result_str(match_ret));
2592 } else {
2593 if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))
2594 zlog_debug(
2595 "No best match sequence for pfx: %pFX in route-map: %s, result: %s",
2596 prefix, map->name,
2597 route_map_cmd_result_str(match_ret));
2598 /*
2599 * No index matches this prefix. Return deny unless,
2600 * match_ret = RMAP_NOOP.
2601 */
2602 if (match_ret == RMAP_NOOP)
2603 ret = RMAP_PERMITMATCH;
2604 else
2605 ret = RMAP_DENYMATCH;
2606 goto route_map_apply_end;
2607 }
2608 skip_match_clause = true;
2609
2610 for (; index; index = index->next) {
2611 if (!skip_match_clause) {
2612 index->applied++;
2613 /* Apply this index. */
2614 match_ret = route_map_apply_match(&index->match_list,
2615 prefix, match_object);
2616 if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)) {
2617 zlog_debug(
2618 "Route-map: %s, sequence: %d, prefix: %pFX, result: %s",
2619 map->name, index->pref, prefix,
2620 route_map_cmd_result_str(match_ret));
2621 }
2622 } else
2623 skip_match_clause = false;
2624
2625
2626 /* Now we apply the matrix from above */
2627 if (match_ret == RMAP_NOOP)
2628 /*
2629 * Do not change the return value. Retain the previous
2630 * return value. Previous values can be:
2631 * 1)permitmatch (if a nomatch was never
2632 * seen before in this route-map.)
2633 * 2)denymatch (if a nomatch was seen earlier in one
2634 * of the previous sequences)
2635 */
2636
2637 /*
2638 * 'cont' from matrix - continue to next route-map
2639 * sequence
2640 */
2641 continue;
2642 else if (match_ret == RMAP_NOMATCH) {
2643
2644 /*
2645 * The return value is now changed to denymatch.
2646 * So from here on out, even if we see more noops,
2647 * we retain this return value and return this
2648 * eventually if there are no matches.
2649 */
2650 ret = RMAP_DENYMATCH;
2651
2652 /*
2653 * 'cont' from matrix - continue to next route-map
2654 * sequence
2655 */
2656 continue;
2657 } else if (match_ret == RMAP_MATCH) {
2658 if (index->type == RMAP_PERMIT)
2659 /* 'action' */
2660 {
2661 /* Match succeeded, rmap is of type permit */
2662 ret = RMAP_PERMITMATCH;
2663
2664 /* permit+match must execute sets */
2665 for (set = index->set_list.head; set;
2666 set = set->next)
2667 /*
2668 * set cmds return RMAP_OKAY or
2669 * RMAP_ERROR. We do not care if
2670 * set succeeded or not. So, ignore
2671 * return code.
2672 */
2673 (void)(*set->cmd->func_apply)(
2674 set->value, prefix, set_object);
2675
2676 /* Call another route-map if available */
2677 if (index->nextrm) {
2678 struct route_map *nextrm =
2679 route_map_lookup_by_name(
2680 index->nextrm);
2681
2682 if (nextrm) /* Target route-map found,
2683 jump to it */
2684 {
2685 recursion++;
2686 ret = route_map_apply_ext(
2687 nextrm, prefix,
2688 match_object,
2689 set_object, NULL);
2690 recursion--;
2691 }
2692
2693 /* If nextrm returned 'deny', finish. */
2694 if (ret == RMAP_DENYMATCH)
2695 goto route_map_apply_end;
2696 }
2697
2698 switch (index->exitpolicy) {
2699 case RMAP_EXIT:
2700 goto route_map_apply_end;
2701 case RMAP_NEXT:
2702 continue;
2703 case RMAP_GOTO: {
2704 /* Find the next clause to jump to */
2705 struct route_map_index *next =
2706 index->next;
2707 int nextpref = index->nextpref;
2708
2709 while (next && next->pref < nextpref) {
2710 index = next;
2711 next = next->next;
2712 }
2713 if (next == NULL) {
2714 /* No clauses match! */
2715 goto route_map_apply_end;
2716 }
2717 }
2718 }
2719 } else if (index->type == RMAP_DENY)
2720 /* 'deny' */
2721 {
2722 ret = RMAP_DENYMATCH;
2723 goto route_map_apply_end;
2724 }
2725 }
2726 }
2727
2728 route_map_apply_end:
2729 if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))
2730 zlog_debug("Route-map: %s, prefix: %pFX, result: %s",
2731 (map ? map->name : "null"), prefix,
2732 route_map_result_str(ret));
2733
2734 if (pref) {
2735 if (index != NULL && ret == RMAP_PERMITMATCH)
2736 *pref = index->pref;
2737 else
2738 *pref = 65536;
2739 }
2740
2741 return (ret);
2742 }
2743
2744 void route_map_add_hook(void (*func)(const char *))
2745 {
2746 route_map_master.add_hook = func;
2747 }
2748
2749 void route_map_delete_hook(void (*func)(const char *))
2750 {
2751 route_map_master.delete_hook = func;
2752 }
2753
2754 void route_map_event_hook(void (*func)(const char *name))
2755 {
2756 route_map_master.event_hook = func;
2757 }
2758
2759 /* Routines for route map dependency lists and dependency processing */
2760 static bool route_map_rmap_hash_cmp(const void *p1, const void *p2)
2761 {
2762 return strcmp(((const struct route_map_dep_data *)p1)->rname,
2763 ((const struct route_map_dep_data *)p2)->rname)
2764 == 0;
2765 }
2766
2767 static bool route_map_dep_hash_cmp(const void *p1, const void *p2)
2768 {
2769
2770 return (strcmp(((const struct route_map_dep *)p1)->dep_name,
2771 (const char *)p2)
2772 == 0);
2773 }
2774
2775 static void route_map_clear_reference(struct hash_bucket *bucket, void *arg)
2776 {
2777 struct route_map_dep *dep = bucket->data;
2778 struct route_map_dep_data *dep_data = NULL, tmp_dep_data;
2779
2780 memset(&tmp_dep_data, 0, sizeof(tmp_dep_data));
2781 tmp_dep_data.rname = arg;
2782 dep_data = hash_release(dep->dep_rmap_hash, &tmp_dep_data);
2783 if (dep_data) {
2784 if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))
2785 zlog_debug("Clearing reference for %s to %s count: %d",
2786 dep->dep_name, tmp_dep_data.rname,
2787 dep_data->refcnt);
2788
2789 XFREE(MTYPE_ROUTE_MAP_NAME, dep_data->rname);
2790 XFREE(MTYPE_ROUTE_MAP_DEP_DATA, dep_data);
2791 }
2792 if (!dep->dep_rmap_hash->count) {
2793 dep = hash_release(dep->this_hash, (void *)dep->dep_name);
2794 hash_free(dep->dep_rmap_hash);
2795 XFREE(MTYPE_ROUTE_MAP_NAME, dep->dep_name);
2796 XFREE(MTYPE_ROUTE_MAP_DEP, dep);
2797 }
2798 }
2799
2800 static void route_map_clear_all_references(char *rmap_name)
2801 {
2802 int i;
2803
2804 if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))
2805 zlog_debug("Clearing references for %s", rmap_name);
2806
2807 for (i = 1; i < ROUTE_MAP_DEP_MAX; i++) {
2808 hash_iterate(route_map_dep_hash[i], route_map_clear_reference,
2809 (void *)rmap_name);
2810 }
2811 }
2812
2813 static unsigned int route_map_dep_data_hash_make_key(const void *p)
2814 {
2815 const struct route_map_dep_data *dep_data = p;
2816
2817 return string_hash_make(dep_data->rname);
2818 }
2819
2820 static void *route_map_dep_hash_alloc(void *p)
2821 {
2822 char *dep_name = (char *)p;
2823 struct route_map_dep *dep_entry;
2824
2825 dep_entry = XCALLOC(MTYPE_ROUTE_MAP_DEP, sizeof(struct route_map_dep));
2826 dep_entry->dep_name = XSTRDUP(MTYPE_ROUTE_MAP_NAME, dep_name);
2827 dep_entry->dep_rmap_hash =
2828 hash_create_size(8, route_map_dep_data_hash_make_key,
2829 route_map_rmap_hash_cmp, "Route Map Dep Hash");
2830 dep_entry->this_hash = NULL;
2831
2832 return dep_entry;
2833 }
2834
2835 static void *route_map_name_hash_alloc(void *p)
2836 {
2837 struct route_map_dep_data *dep_data = NULL, *tmp_dep_data = NULL;
2838
2839 dep_data = XCALLOC(MTYPE_ROUTE_MAP_DEP_DATA,
2840 sizeof(struct route_map_dep_data));
2841 tmp_dep_data = p;
2842 dep_data->rname = XSTRDUP(MTYPE_ROUTE_MAP_NAME, tmp_dep_data->rname);
2843 return dep_data;
2844 }
2845
2846 static unsigned int route_map_dep_hash_make_key(const void *p)
2847 {
2848 return (string_hash_make((char *)p));
2849 }
2850
2851 static void route_map_print_dependency(struct hash_bucket *bucket, void *data)
2852 {
2853 struct route_map_dep_data *dep_data = bucket->data;
2854 char *rmap_name = dep_data->rname;
2855 char *dep_name = data;
2856
2857 zlog_debug("%s: Dependency for %s: %s", __func__, dep_name, rmap_name);
2858 }
2859
2860 static int route_map_dep_update(struct hash *dephash, const char *dep_name,
2861 const char *rmap_name, route_map_event_t type)
2862 {
2863 struct route_map_dep *dep = NULL;
2864 char *dname, *rname;
2865 int ret = 0;
2866 struct route_map_dep_data *dep_data = NULL, *ret_dep_data = NULL;
2867 struct route_map_dep_data tmp_dep_data;
2868
2869 dname = XSTRDUP(MTYPE_ROUTE_MAP_NAME, dep_name);
2870 rname = XSTRDUP(MTYPE_ROUTE_MAP_NAME, rmap_name);
2871
2872 switch (type) {
2873 case RMAP_EVENT_PLIST_ADDED:
2874 case RMAP_EVENT_CLIST_ADDED:
2875 case RMAP_EVENT_ECLIST_ADDED:
2876 case RMAP_EVENT_ASLIST_ADDED:
2877 case RMAP_EVENT_LLIST_ADDED:
2878 case RMAP_EVENT_CALL_ADDED:
2879 case RMAP_EVENT_FILTER_ADDED:
2880 if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))
2881 zlog_debug("Adding dependency for filter %s in route-map %s",
2882 dep_name, rmap_name);
2883 dep = (struct route_map_dep *)hash_get(
2884 dephash, dname, route_map_dep_hash_alloc);
2885 if (!dep) {
2886 ret = -1;
2887 goto out;
2888 }
2889
2890 if (!dep->this_hash)
2891 dep->this_hash = dephash;
2892
2893 memset(&tmp_dep_data, 0, sizeof(tmp_dep_data));
2894 tmp_dep_data.rname = rname;
2895 dep_data = hash_lookup(dep->dep_rmap_hash, &tmp_dep_data);
2896 if (!dep_data)
2897 dep_data = hash_get(dep->dep_rmap_hash, &tmp_dep_data,
2898 route_map_name_hash_alloc);
2899
2900 dep_data->refcnt++;
2901 break;
2902 case RMAP_EVENT_PLIST_DELETED:
2903 case RMAP_EVENT_CLIST_DELETED:
2904 case RMAP_EVENT_ECLIST_DELETED:
2905 case RMAP_EVENT_ASLIST_DELETED:
2906 case RMAP_EVENT_LLIST_DELETED:
2907 case RMAP_EVENT_CALL_DELETED:
2908 case RMAP_EVENT_FILTER_DELETED:
2909 if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))
2910 zlog_debug("Deleting dependency for filter %s in route-map %s",
2911 dep_name, rmap_name);
2912 dep = (struct route_map_dep *)hash_get(dephash, dname, NULL);
2913 if (!dep) {
2914 goto out;
2915 }
2916
2917 memset(&tmp_dep_data, 0, sizeof(tmp_dep_data));
2918 tmp_dep_data.rname = rname;
2919 dep_data = hash_lookup(dep->dep_rmap_hash, &tmp_dep_data);
2920 /*
2921 * If dep_data is NULL then something has gone seriously
2922 * wrong in route-map handling. Note it and prevent
2923 * the crash.
2924 */
2925 if (!dep_data) {
2926 zlog_warn(
2927 "route-map dependency for route-map %s: %s is not correct",
2928 rmap_name, dep_name);
2929 goto out;
2930 }
2931
2932 dep_data->refcnt--;
2933
2934 if (!dep_data->refcnt) {
2935 ret_dep_data = hash_release(dep->dep_rmap_hash,
2936 &tmp_dep_data);
2937 if (ret_dep_data) {
2938 XFREE(MTYPE_ROUTE_MAP_NAME,
2939 ret_dep_data->rname);
2940 XFREE(MTYPE_ROUTE_MAP_DEP_DATA, ret_dep_data);
2941 }
2942 }
2943
2944 if (!dep->dep_rmap_hash->count) {
2945 dep = hash_release(dephash, dname);
2946 hash_free(dep->dep_rmap_hash);
2947 XFREE(MTYPE_ROUTE_MAP_NAME, dep->dep_name);
2948 XFREE(MTYPE_ROUTE_MAP_DEP, dep);
2949 }
2950 break;
2951 case RMAP_EVENT_SET_ADDED:
2952 case RMAP_EVENT_SET_DELETED:
2953 case RMAP_EVENT_SET_REPLACED:
2954 case RMAP_EVENT_MATCH_ADDED:
2955 case RMAP_EVENT_MATCH_DELETED:
2956 case RMAP_EVENT_MATCH_REPLACED:
2957 case RMAP_EVENT_INDEX_ADDED:
2958 case RMAP_EVENT_INDEX_DELETED:
2959 break;
2960 }
2961
2962 if (dep) {
2963 if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))
2964 hash_iterate(dep->dep_rmap_hash,
2965 route_map_print_dependency, dname);
2966 }
2967
2968 out:
2969 XFREE(MTYPE_ROUTE_MAP_NAME, rname);
2970 XFREE(MTYPE_ROUTE_MAP_NAME, dname);
2971 return ret;
2972 }
2973
2974 static struct hash *route_map_get_dep_hash(route_map_event_t event)
2975 {
2976 struct hash *upd8_hash = NULL;
2977
2978 switch (event) {
2979 case RMAP_EVENT_PLIST_ADDED:
2980 case RMAP_EVENT_PLIST_DELETED:
2981 upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_PLIST];
2982 break;
2983 case RMAP_EVENT_CLIST_ADDED:
2984 case RMAP_EVENT_CLIST_DELETED:
2985 upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_CLIST];
2986 break;
2987 case RMAP_EVENT_ECLIST_ADDED:
2988 case RMAP_EVENT_ECLIST_DELETED:
2989 upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_ECLIST];
2990 break;
2991 case RMAP_EVENT_ASLIST_ADDED:
2992 case RMAP_EVENT_ASLIST_DELETED:
2993 upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_ASPATH];
2994 break;
2995 case RMAP_EVENT_LLIST_ADDED:
2996 case RMAP_EVENT_LLIST_DELETED:
2997 upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_LCLIST];
2998 break;
2999 case RMAP_EVENT_CALL_ADDED:
3000 case RMAP_EVENT_CALL_DELETED:
3001 case RMAP_EVENT_MATCH_ADDED:
3002 case RMAP_EVENT_MATCH_DELETED:
3003 upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_RMAP];
3004 break;
3005 case RMAP_EVENT_FILTER_ADDED:
3006 case RMAP_EVENT_FILTER_DELETED:
3007 upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_FILTER];
3008 break;
3009 /*
3010 * Should we actually be ignoring these?
3011 * I am not sure but at this point in time, let
3012 * us get them into this switch and we can peel
3013 * them into the appropriate place in the future
3014 */
3015 case RMAP_EVENT_SET_ADDED:
3016 case RMAP_EVENT_SET_DELETED:
3017 case RMAP_EVENT_SET_REPLACED:
3018 case RMAP_EVENT_MATCH_REPLACED:
3019 case RMAP_EVENT_INDEX_ADDED:
3020 case RMAP_EVENT_INDEX_DELETED:
3021 upd8_hash = NULL;
3022 break;
3023 }
3024 return (upd8_hash);
3025 }
3026
3027 static void route_map_process_dependency(struct hash_bucket *bucket, void *data)
3028 {
3029 struct route_map_dep_data *dep_data = NULL;
3030 char *rmap_name = NULL;
3031
3032 dep_data = bucket->data;
3033 rmap_name = dep_data->rname;
3034
3035 if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))
3036 zlog_debug("Notifying %s of dependency", rmap_name);
3037 if (route_map_master.event_hook)
3038 (*route_map_master.event_hook)(rmap_name);
3039 }
3040
3041 void route_map_upd8_dependency(route_map_event_t type, const char *arg,
3042 const char *rmap_name)
3043 {
3044 struct hash *upd8_hash = NULL;
3045
3046 if ((upd8_hash = route_map_get_dep_hash(type))) {
3047 route_map_dep_update(upd8_hash, arg, rmap_name, type);
3048
3049 if (type == RMAP_EVENT_CALL_ADDED) {
3050 /* Execute hook. */
3051 if (route_map_master.add_hook)
3052 (*route_map_master.add_hook)(rmap_name);
3053 } else if (type == RMAP_EVENT_CALL_DELETED) {
3054 /* Execute hook. */
3055 if (route_map_master.delete_hook)
3056 (*route_map_master.delete_hook)(rmap_name);
3057 }
3058 }
3059 }
3060
3061 void route_map_notify_dependencies(const char *affected_name,
3062 route_map_event_t event)
3063 {
3064 struct route_map_dep *dep;
3065 struct hash *upd8_hash;
3066 char *name;
3067
3068 if (!affected_name)
3069 return;
3070
3071 name = XSTRDUP(MTYPE_ROUTE_MAP_NAME, affected_name);
3072
3073 if ((upd8_hash = route_map_get_dep_hash(event)) == NULL) {
3074 XFREE(MTYPE_ROUTE_MAP_NAME, name);
3075 return;
3076 }
3077
3078 dep = (struct route_map_dep *)hash_get(upd8_hash, name, NULL);
3079 if (dep) {
3080 if (!dep->this_hash)
3081 dep->this_hash = upd8_hash;
3082
3083 if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))
3084 zlog_debug("Filter %s updated", dep->dep_name);
3085 hash_iterate(dep->dep_rmap_hash, route_map_process_dependency,
3086 (void *)event);
3087 }
3088
3089 XFREE(MTYPE_ROUTE_MAP_NAME, name);
3090 }
3091
3092 /* VTY related functions. */
3093 static void clear_route_map_helper(struct route_map *map)
3094 {
3095 struct route_map_index *index;
3096
3097 map->applied_clear = map->applied;
3098 for (index = map->head; index; index = index->next)
3099 index->applied_clear = index->applied;
3100 }
3101
3102 DEFUN (rmap_clear_counters,
3103 rmap_clear_counters_cmd,
3104 "clear route-map counters [WORD]",
3105 CLEAR_STR
3106 "route-map information\n"
3107 "counters associated with the specified route-map\n"
3108 "route-map name\n")
3109 {
3110 int idx_word = 2;
3111 struct route_map *map;
3112
3113 const char *name = (argc == 3 ) ? argv[idx_word]->arg : NULL;
3114
3115 if (name) {
3116 map = route_map_lookup_by_name(name);
3117
3118 if (map)
3119 clear_route_map_helper(map);
3120 else {
3121 vty_out(vty, "%s: 'route-map %s' not found\n",
3122 frr_protonameinst, name);
3123 return CMD_SUCCESS;
3124 }
3125 } else {
3126 for (map = route_map_master.head; map; map = map->next)
3127 clear_route_map_helper(map);
3128 }
3129
3130 return CMD_SUCCESS;
3131
3132 }
3133
3134 DEFUN (rmap_show_name,
3135 rmap_show_name_cmd,
3136 "show route-map [WORD] [json]",
3137 SHOW_STR
3138 "route-map information\n"
3139 "route-map name\n"
3140 JSON_STR)
3141 {
3142 bool uj = use_json(argc, argv);
3143 int idx = 0;
3144 const char *name = NULL;
3145
3146 if (argv_find(argv, argc, "WORD", &idx))
3147 name = argv[idx]->arg;
3148
3149 return vty_show_route_map(vty, name, uj);
3150 }
3151
3152 DEFUN (rmap_show_unused,
3153 rmap_show_unused_cmd,
3154 "show route-map-unused",
3155 SHOW_STR
3156 "unused route-map information\n")
3157 {
3158 return vty_show_unused_route_map(vty);
3159 }
3160
3161 DEFPY (debug_rmap,
3162 debug_rmap_cmd,
3163 "debug route-map [detail]$detail",
3164 DEBUG_STR
3165 "Debug option set for route-maps\n"
3166 "Detailed output\n")
3167 {
3168 if (!detail)
3169 SET_FLAG(rmap_debug, DEBUG_ROUTEMAP);
3170 else
3171 SET_FLAG(rmap_debug, DEBUG_ROUTEMAP | DEBUG_ROUTEMAP_DETAIL);
3172
3173 return CMD_SUCCESS;
3174 }
3175
3176 DEFPY (no_debug_rmap,
3177 no_debug_rmap_cmd,
3178 "no debug route-map [detail]$detail",
3179 NO_STR
3180 DEBUG_STR
3181 "Debug option set for route-maps\n"
3182 "Detailed output\n")
3183 {
3184 if (!detail)
3185 UNSET_FLAG(rmap_debug, DEBUG_ROUTEMAP);
3186 else
3187 UNSET_FLAG(rmap_debug, DEBUG_ROUTEMAP | DEBUG_ROUTEMAP_DETAIL);
3188
3189 return CMD_SUCCESS;
3190 }
3191
3192 /* Debug node. */
3193 static int rmap_config_write_debug(struct vty *vty);
3194 static struct cmd_node rmap_debug_node = {
3195 .name = "route-map debug",
3196 .node = RMAP_DEBUG_NODE,
3197 .prompt = "",
3198 .config_write = rmap_config_write_debug,
3199 };
3200
3201 void route_map_show_debug(struct vty *vty)
3202 {
3203 if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))
3204 vty_out(vty, "debug route-map\n");
3205 }
3206
3207 /* Configuration write function. */
3208 static int rmap_config_write_debug(struct vty *vty)
3209 {
3210 int write = 0;
3211
3212 if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)) {
3213 vty_out(vty, "debug route-map\n");
3214 write++;
3215 }
3216
3217 return write;
3218 }
3219
3220 /* Common route map rules */
3221
3222 void *route_map_rule_tag_compile(const char *arg)
3223 {
3224 unsigned long int tmp;
3225 char *endptr;
3226 route_tag_t *tag;
3227
3228 errno = 0;
3229 tmp = strtoul(arg, &endptr, 0);
3230 if (arg[0] == '\0' || *endptr != '\0' || errno || tmp > ROUTE_TAG_MAX)
3231 return NULL;
3232
3233 tag = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(*tag));
3234 *tag = tmp;
3235
3236 return tag;
3237 }
3238
3239 void route_map_rule_tag_free(void *rule)
3240 {
3241 XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
3242 }
3243
3244 void route_map_finish(void)
3245 {
3246 int i;
3247 struct route_map_rule_cmd_proxy *proxy;
3248
3249 /* these 2 hash tables have INIT_HASH initializers, so the "default"
3250 * state is "initialized & empty" => fini() followed by init() to
3251 * return to that same state
3252 */
3253 while ((proxy = rmap_cmd_name_pop(rmap_match_cmds)))
3254 (void)proxy;
3255 rmap_cmd_name_fini(rmap_match_cmds);
3256 rmap_cmd_name_init(rmap_match_cmds);
3257
3258 while ((proxy = rmap_cmd_name_pop(rmap_set_cmds)))
3259 (void)proxy;
3260 rmap_cmd_name_fini(rmap_set_cmds);
3261 rmap_cmd_name_init(rmap_set_cmds);
3262
3263 /*
3264 * All protocols are setting these to NULL
3265 * by default on shutdown( route_map_finish )
3266 * Why are we making them do this work?
3267 */
3268 route_map_master.add_hook = NULL;
3269 route_map_master.delete_hook = NULL;
3270 route_map_master.event_hook = NULL;
3271
3272 /* cleanup route_map */
3273 while (route_map_master.head) {
3274 struct route_map *map = route_map_master.head;
3275 map->to_be_processed = false;
3276 route_map_delete(map);
3277 }
3278
3279 for (i = 1; i < ROUTE_MAP_DEP_MAX; i++) {
3280 hash_free(route_map_dep_hash[i]);
3281 route_map_dep_hash[i] = NULL;
3282 }
3283
3284 hash_free(route_map_master_hash);
3285 route_map_master_hash = NULL;
3286 }
3287
3288 /* Increment the use_count counter while attaching the route map */
3289 void route_map_counter_increment(struct route_map *map)
3290 {
3291 if (map)
3292 map->use_count++;
3293 }
3294
3295 /* Decrement the use_count counter while detaching the route map. */
3296 void route_map_counter_decrement(struct route_map *map)
3297 {
3298 if (map) {
3299 if (map->use_count <= 0)
3300 return;
3301 map->use_count--;
3302 }
3303 }
3304
3305 DEFUN_HIDDEN(show_route_map_pfx_tbl, show_route_map_pfx_tbl_cmd,
3306 "show route-map RMAP_NAME prefix-table",
3307 SHOW_STR
3308 "route-map\n"
3309 "route-map name\n"
3310 "internal prefix-table\n")
3311 {
3312 const char *rmap_name = argv[2]->arg;
3313 struct route_map *rmap = NULL;
3314 struct route_table *rm_pfx_tbl4 = NULL;
3315 struct route_table *rm_pfx_tbl6 = NULL;
3316 struct route_node *rn = NULL, *prn = NULL;
3317 struct list *rmap_index_list = NULL;
3318 struct listnode *ln = NULL, *nln = NULL;
3319 struct route_map_index *index = NULL;
3320 uint8_t len = 54;
3321
3322 vty_out(vty, "%s:\n", frr_protonameinst);
3323 rmap = route_map_lookup_by_name(rmap_name);
3324 if (rmap) {
3325 rm_pfx_tbl4 = rmap->ipv4_prefix_table;
3326 if (rm_pfx_tbl4) {
3327 vty_out(vty, "\n%s%43s%s\n", "IPv4 Prefix", "",
3328 "Route-map Index List");
3329 vty_out(vty, "%s%39s%s\n", "_______________", "",
3330 "____________________");
3331 for (rn = route_top(rm_pfx_tbl4); rn;
3332 rn = route_next(rn)) {
3333 vty_out(vty, " %pRN (%d)\n", rn,
3334 route_node_get_lock_count(rn));
3335
3336 vty_out(vty, "(P) ");
3337 prn = rn->parent;
3338 if (prn) {
3339 vty_out(vty, "%pRN\n", prn);
3340 }
3341
3342 vty_out(vty, "\n");
3343 rmap_index_list = (struct list *)rn->info;
3344 if (!rmap_index_list
3345 || !listcount(rmap_index_list))
3346 vty_out(vty, "%*s%s\n", len, "", "-");
3347 else
3348 for (ALL_LIST_ELEMENTS(rmap_index_list,
3349 ln, nln,
3350 index)) {
3351 vty_out(vty, "%*s%s seq %d\n",
3352 len, "",
3353 index->map->name,
3354 index->pref);
3355 }
3356 vty_out(vty, "\n");
3357 }
3358 }
3359
3360 rm_pfx_tbl6 = rmap->ipv6_prefix_table;
3361 if (rm_pfx_tbl6) {
3362 vty_out(vty, "\n%s%43s%s\n", "IPv6 Prefix", "",
3363 "Route-map Index List");
3364 vty_out(vty, "%s%39s%s\n", "_______________", "",
3365 "____________________");
3366 for (rn = route_top(rm_pfx_tbl6); rn;
3367 rn = route_next(rn)) {
3368 vty_out(vty, " %pRN (%d)\n", rn,
3369 route_node_get_lock_count(rn));
3370
3371 vty_out(vty, "(P) ");
3372 prn = rn->parent;
3373 if (prn) {
3374 vty_out(vty, "%pRN\n", prn);
3375 }
3376
3377 vty_out(vty, "\n");
3378 rmap_index_list = (struct list *)rn->info;
3379 if (!rmap_index_list
3380 || !listcount(rmap_index_list))
3381 vty_out(vty, "%*s%s\n", len, "", "-");
3382 else
3383 for (ALL_LIST_ELEMENTS(rmap_index_list,
3384 ln, nln,
3385 index)) {
3386 vty_out(vty, "%*s%s seq %d\n",
3387 len, "",
3388 index->map->name,
3389 index->pref);
3390 }
3391 vty_out(vty, "\n");
3392 }
3393 }
3394 }
3395
3396 vty_out(vty, "\n");
3397 return CMD_SUCCESS;
3398 }
3399
3400 /* Initialization of route map vector. */
3401 void route_map_init(void)
3402 {
3403 int i;
3404
3405 route_map_master_hash =
3406 hash_create_size(8, route_map_hash_key_make, route_map_hash_cmp,
3407 "Route Map Master Hash");
3408
3409 for (i = 1; i < ROUTE_MAP_DEP_MAX; i++)
3410 route_map_dep_hash[i] = hash_create_size(
3411 8, route_map_dep_hash_make_key, route_map_dep_hash_cmp,
3412 "Route Map Dep Hash");
3413
3414 UNSET_FLAG(rmap_debug, DEBUG_ROUTEMAP);
3415
3416 route_map_cli_init();
3417
3418 /* Install route map top node. */
3419 install_node(&rmap_debug_node);
3420
3421 /* Install route map commands. */
3422 install_element(CONFIG_NODE, &debug_rmap_cmd);
3423 install_element(CONFIG_NODE, &no_debug_rmap_cmd);
3424
3425 /* Install show command */
3426 install_element(ENABLE_NODE, &rmap_clear_counters_cmd);
3427
3428 install_element(ENABLE_NODE, &rmap_show_name_cmd);
3429 install_element(ENABLE_NODE, &rmap_show_unused_cmd);
3430
3431 install_element(ENABLE_NODE, &debug_rmap_cmd);
3432 install_element(ENABLE_NODE, &no_debug_rmap_cmd);
3433
3434 install_element(ENABLE_NODE, &show_route_map_pfx_tbl_cmd);
3435 }