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