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