]> git.proxmox.com Git - mirror_frr.git/blob - lib/routemap_northbound.c
Merge pull request #5928 from xThaid/rt_link_cleanup
[mirror_frr.git] / lib / routemap_northbound.c
1 /*
2 * Route map northbound implementation.
3 *
4 * Copyright (C) 2019 Network Device Education Foundation, Inc. ("NetDEF")
5 * Rafael Zalamena
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 * 02110-1301 USA.
21 */
22
23 #include <zebra.h>
24
25 #include "lib/command.h"
26 #include "lib/log.h"
27 #include "lib/northbound.h"
28 #include "lib/routemap.h"
29
30 /*
31 * Auxiliary functions to avoid code duplication:
32 *
33 * lib_route_map_entry_set_destroy: unset `set` commands.
34 * lib_route_map_entry_match_destroy: unset `match` commands.
35 */
36 int lib_route_map_entry_match_destroy(enum nb_event event,
37 const struct lyd_node *dnode)
38 {
39 struct routemap_hook_context *rhc;
40 int rv;
41
42 if (event != NB_EV_APPLY)
43 return NB_OK;
44
45 rhc = nb_running_get_entry(dnode, NULL, true);
46 if (rhc->rhc_mhook == NULL)
47 return NB_OK;
48
49 rv = rhc->rhc_mhook(NULL, rhc->rhc_rmi, rhc->rhc_rule, NULL,
50 rhc->rhc_event);
51 if (rv != CMD_SUCCESS)
52 return NB_ERR_INCONSISTENCY;
53
54 return NB_OK;
55 }
56
57 int lib_route_map_entry_set_destroy(enum nb_event event,
58 const struct lyd_node *dnode)
59 {
60 struct routemap_hook_context *rhc;
61 int rv;
62
63 if (event != NB_EV_APPLY)
64 return NB_OK;
65
66 rhc = nb_running_get_entry(dnode, NULL, true);
67 if (rhc->rhc_shook == NULL)
68 return NB_OK;
69
70 rv = rhc->rhc_shook(NULL, rhc->rhc_rmi, rhc->rhc_rule, NULL);
71 if (rv != CMD_SUCCESS)
72 return NB_ERR_INCONSISTENCY;
73
74 return NB_OK;
75 }
76
77 /*
78 * Auxiliary hook context list manipulation functions.
79 */
80 struct routemap_hook_context *
81 routemap_hook_context_insert(struct route_map_index *rmi)
82 {
83 struct routemap_hook_context *rhc;
84
85 rhc = XCALLOC(MTYPE_TMP, sizeof(*rhc));
86 rhc->rhc_rmi = rmi;
87 TAILQ_INSERT_TAIL(&rmi->rhclist, rhc, rhc_entry);
88
89 return rhc;
90 }
91
92 void
93 routemap_hook_context_free(struct routemap_hook_context *rhc)
94 {
95 struct route_map_index *rmi = rhc->rhc_rmi;
96
97 TAILQ_REMOVE(&rmi->rhclist, rhc, rhc_entry);
98 XFREE(MTYPE_TMP, rhc);
99 }
100
101 /*
102 * XPath: /frr-route-map:lib/route-map
103 */
104 static int lib_route_map_create(enum nb_event event,
105 const struct lyd_node *dnode,
106 union nb_resource *resource)
107 {
108 struct route_map *rm;
109 const char *rm_name;
110
111 switch (event) {
112 case NB_EV_VALIDATE:
113 case NB_EV_PREPARE:
114 case NB_EV_ABORT:
115 /* NOTHING */
116 break;
117 case NB_EV_APPLY:
118 rm_name = yang_dnode_get_string(dnode, "./name");
119 rm = route_map_get(rm_name);
120 nb_running_set_entry(dnode, rm);
121 break;
122 }
123
124 return NB_OK;
125 }
126
127 static int lib_route_map_destroy(enum nb_event event,
128 const struct lyd_node *dnode)
129 {
130 struct route_map *rm;
131
132 switch (event) {
133 case NB_EV_VALIDATE:
134 case NB_EV_PREPARE:
135 case NB_EV_ABORT:
136 /* NOTHING */
137 break;
138 case NB_EV_APPLY:
139 rm = nb_running_unset_entry(dnode);
140 route_map_delete(rm);
141 break;
142 }
143
144 return NB_OK;
145 }
146
147 /*
148 * XPath: /frr-route-map:lib/route-map/entry
149 */
150 static int lib_route_map_entry_create(enum nb_event event,
151 const struct lyd_node *dnode,
152 union nb_resource *resource)
153 {
154 struct route_map_index *rmi;
155 struct route_map *rm;
156 uint16_t sequence;
157 int action;
158
159 switch (event) {
160 case NB_EV_VALIDATE:
161 case NB_EV_PREPARE:
162 case NB_EV_ABORT:
163 /* NOTHING */
164 break;
165 case NB_EV_APPLY:
166 sequence = yang_dnode_get_uint16(dnode, "./sequence");
167 action = yang_dnode_get_enum(dnode, "./action") == 0
168 ? RMAP_PERMIT
169 : RMAP_DENY;
170 rm = nb_running_get_entry(dnode, NULL, true);
171 rmi = route_map_index_get(rm, action, sequence);
172 nb_running_set_entry(dnode, rmi);
173 break;
174 }
175
176 return NB_OK;
177 }
178
179 static int lib_route_map_entry_destroy(enum nb_event event,
180 const struct lyd_node *dnode)
181 {
182 struct route_map_index *rmi;
183
184 switch (event) {
185 case NB_EV_VALIDATE:
186 case NB_EV_PREPARE:
187 case NB_EV_ABORT:
188 /* NOTHING */
189 break;
190 case NB_EV_APPLY:
191 rmi = nb_running_unset_entry(dnode);
192 route_map_index_delete(rmi, 1);
193 break;
194 }
195
196 return NB_OK;
197 }
198
199 /*
200 * XPath: /frr-route-map:lib/route-map/entry/description
201 */
202 static int lib_route_map_entry_description_modify(enum nb_event event,
203 const struct lyd_node *dnode,
204 union nb_resource *resource)
205 {
206 struct route_map_index *rmi;
207 const char *description;
208
209 switch (event) {
210 case NB_EV_VALIDATE:
211 /* NOTHING */
212 break;
213 case NB_EV_PREPARE:
214 description = yang_dnode_get_string(dnode, NULL);
215 resource->ptr = XSTRDUP(MTYPE_TMP, description);
216 if (resource->ptr == NULL)
217 return NB_ERR_RESOURCE;
218 break;
219 case NB_EV_ABORT:
220 XFREE(MTYPE_TMP, resource->ptr);
221 break;
222 case NB_EV_APPLY:
223 rmi = nb_running_get_entry(dnode, NULL, true);
224 XFREE(MTYPE_TMP, rmi->description);
225 rmi->description = resource->ptr;
226 break;
227 }
228
229 return NB_OK;
230 }
231
232 static int lib_route_map_entry_description_destroy(enum nb_event event,
233 const struct lyd_node *dnode)
234 {
235 struct route_map_index *rmi;
236
237 switch (event) {
238 case NB_EV_VALIDATE:
239 case NB_EV_PREPARE:
240 case NB_EV_ABORT:
241 /* NOTHING */
242 break;
243 case NB_EV_APPLY:
244 rmi = nb_running_get_entry(dnode, NULL, true);
245 XFREE(MTYPE_TMP, rmi->description);
246 break;
247 }
248
249 return NB_OK;
250 }
251
252 /*
253 * XPath: /frr-route-map:lib/route-map/entry/action
254 */
255 static int lib_route_map_entry_action_modify(enum nb_event event,
256 const struct lyd_node *dnode,
257 union nb_resource *resource)
258 {
259 struct route_map_index *rmi;
260
261 switch (event) {
262 case NB_EV_VALIDATE:
263 case NB_EV_PREPARE:
264 case NB_EV_ABORT:
265 /* NOTHING */
266 break;
267 case NB_EV_APPLY:
268 rmi = nb_running_get_entry(dnode, NULL, true);
269 rmi->type = yang_dnode_get_enum(dnode, NULL);
270 /* TODO: notify? */
271 break;
272 }
273
274 return NB_OK;
275 }
276
277 /*
278 * XPath: /frr-route-map:lib/route-map/entry/call
279 */
280 static int lib_route_map_entry_call_modify(enum nb_event event,
281 const struct lyd_node *dnode,
282 union nb_resource *resource)
283 {
284 struct route_map_index *rmi;
285 const char *rm_name, *rmn_name;
286
287 switch (event) {
288 case NB_EV_VALIDATE:
289 rm_name = yang_dnode_get_string(dnode, "../../name");
290 rmn_name = yang_dnode_get_string(dnode, NULL);
291 /* Don't allow to jump to the same route map instance. */
292 if (strcmp(rm_name, rmn_name) == 0)
293 return NB_ERR_VALIDATION;
294
295 /* TODO: detect circular route map sequences. */
296 break;
297 case NB_EV_PREPARE:
298 rmn_name = yang_dnode_get_string(dnode, NULL);
299 resource->ptr = XSTRDUP(MTYPE_ROUTE_MAP_NAME, rmn_name);
300 break;
301 case NB_EV_ABORT:
302 XFREE(MTYPE_ROUTE_MAP_NAME, resource->ptr);
303 break;
304 case NB_EV_APPLY:
305 rmi = nb_running_get_entry(dnode, NULL, true);
306 if (rmi->nextrm) {
307 route_map_upd8_dependency(RMAP_EVENT_CALL_DELETED,
308 rmi->nextrm, rmi->map->name);
309 XFREE(MTYPE_ROUTE_MAP_NAME, rmi->nextrm);
310 }
311 rmi->nextrm = resource->ptr;
312 route_map_upd8_dependency(RMAP_EVENT_CALL_ADDED, rmi->nextrm,
313 rmi->map->name);
314 break;
315 }
316
317 return NB_OK;
318 }
319
320 static int lib_route_map_entry_call_destroy(enum nb_event event,
321 const struct lyd_node *dnode)
322 {
323 struct route_map_index *rmi;
324
325 switch (event) {
326 case NB_EV_VALIDATE:
327 case NB_EV_PREPARE:
328 case NB_EV_ABORT:
329 /* NOTHING */
330 break;
331 case NB_EV_APPLY:
332 rmi = nb_running_get_entry(dnode, NULL, true);
333 route_map_upd8_dependency(RMAP_EVENT_CALL_DELETED, rmi->nextrm,
334 rmi->map->name);
335 XFREE(MTYPE_ROUTE_MAP_NAME, rmi->nextrm);
336 rmi->nextrm = NULL;
337 break;
338 }
339
340 return NB_OK;
341 }
342
343 /*
344 * XPath: /frr-route-map:lib/route-map/entry/exit-policy
345 */
346 static int lib_route_map_entry_exit_policy_modify(enum nb_event event,
347 const struct lyd_node *dnode,
348 union nb_resource *resource)
349 {
350 struct route_map_index *rmi;
351 int rm_action;
352 int policy;
353
354 switch (event) {
355 case NB_EV_VALIDATE:
356 policy = yang_dnode_get_enum(dnode, NULL);
357 switch (policy) {
358 case 0: /* permit-or-deny */
359 break;
360 case 1: /* next */
361 /* FALLTHROUGH */
362 case 2: /* goto */
363 rm_action = yang_dnode_get_enum(dnode, "../action");
364 if (rm_action == 1 /* deny */) {
365 /*
366 * On deny it is not possible to 'goto'
367 * anywhere.
368 */
369 return NB_ERR_VALIDATION;
370 }
371 break;
372 }
373 break;
374 case NB_EV_PREPARE:
375 case NB_EV_ABORT:
376 break;
377 case NB_EV_APPLY:
378 rmi = nb_running_get_entry(dnode, NULL, true);
379 policy = yang_dnode_get_enum(dnode, NULL);
380
381 switch (policy) {
382 case 0: /* permit-or-deny */
383 rmi->exitpolicy = RMAP_EXIT;
384 break;
385 case 1: /* next */
386 rmi->exitpolicy = RMAP_NEXT;
387 break;
388 case 2: /* goto */
389 rmi->exitpolicy = RMAP_GOTO;
390 break;
391 }
392 break;
393 }
394
395 return NB_OK;
396 }
397
398 /*
399 * XPath: /frr-route-map:lib/route-map/entry/goto-value
400 */
401 static int lib_route_map_entry_goto_value_modify(enum nb_event event,
402 const struct lyd_node *dnode,
403 union nb_resource *resource)
404 {
405 struct route_map_index *rmi;
406 uint16_t rmi_index;
407 uint16_t rmi_next;
408
409 switch (event) {
410 case NB_EV_VALIDATE:
411 rmi_index = yang_dnode_get_uint16(dnode, "../sequence");
412 rmi_next = yang_dnode_get_uint16(dnode, NULL);
413 if (rmi_next <= rmi_index) {
414 /* Can't jump backwards on a route map. */
415 return NB_ERR_VALIDATION;
416 }
417 break;
418 case NB_EV_PREPARE:
419 case NB_EV_ABORT:
420 /* NOTHING */
421 break;
422 case NB_EV_APPLY:
423 rmi = nb_running_get_entry(dnode, NULL, true);
424 rmi->nextpref = yang_dnode_get_uint16(dnode, NULL);
425 break;
426 }
427
428 return NB_OK;
429 }
430
431 static int lib_route_map_entry_goto_value_destroy(enum nb_event event,
432 const struct lyd_node *dnode)
433 {
434 struct route_map_index *rmi;
435
436 switch (event) {
437 case NB_EV_VALIDATE:
438 case NB_EV_PREPARE:
439 case NB_EV_ABORT:
440 /* NOTHING */
441 break;
442 case NB_EV_APPLY:
443 rmi = nb_running_get_entry(dnode, NULL, true);
444 rmi->nextpref = 0;
445 break;
446 }
447
448 return NB_OK;
449 }
450
451 /*
452 * XPath: /frr-route-map:lib/route-map/entry/match-condition
453 */
454 static int
455 lib_route_map_entry_match_condition_create(enum nb_event event,
456 const struct lyd_node *dnode,
457 union nb_resource *resource)
458 {
459 struct routemap_hook_context *rhc;
460 struct route_map_index *rmi;
461
462 switch (event) {
463 case NB_EV_VALIDATE:
464 case NB_EV_PREPARE:
465 case NB_EV_ABORT:
466 /* NOTHING */
467 break;
468 case NB_EV_APPLY:
469 rmi = nb_running_get_entry(dnode, NULL, true);
470 rhc = routemap_hook_context_insert(rmi);
471 nb_running_set_entry(dnode, rhc);
472 break;
473 }
474
475 return NB_OK;
476 }
477
478 static int
479 lib_route_map_entry_match_condition_destroy(enum nb_event event,
480 const struct lyd_node *dnode)
481 {
482 struct routemap_hook_context *rhc;
483 int rv;
484
485 if (event != NB_EV_APPLY)
486 return NB_OK;
487
488 rv = lib_route_map_entry_match_destroy(event, dnode);
489 rhc = nb_running_unset_entry(dnode);
490 routemap_hook_context_free(rhc);
491
492 return rv;
493 }
494
495 /*
496 * XPath: /frr-route-map:lib/route-map/entry/match-condition/interface
497 */
498 static int lib_route_map_entry_match_condition_interface_modify(
499 enum nb_event event, const struct lyd_node *dnode,
500 union nb_resource *resource)
501 {
502 struct routemap_hook_context *rhc;
503 const char *ifname;
504 int rv;
505
506 if (event != NB_EV_APPLY)
507 return NB_OK;
508
509 /* Check for hook function. */
510 if (rmap_match_set_hook.match_interface == NULL)
511 return NB_OK;
512
513 /* Add configuration. */
514 rhc = nb_running_get_entry(dnode, NULL, true);
515 ifname = yang_dnode_get_string(dnode, NULL);
516
517 /* Set destroy information. */
518 rhc->rhc_mhook = rmap_match_set_hook.no_match_interface;
519 rhc->rhc_rule = "interface";
520 rhc->rhc_event = RMAP_EVENT_MATCH_DELETED;
521
522 rv = rmap_match_set_hook.match_interface(NULL, rhc->rhc_rmi,
523 "interface", ifname,
524 RMAP_EVENT_MATCH_ADDED);
525 if (rv != CMD_SUCCESS) {
526 rhc->rhc_mhook = NULL;
527 return NB_ERR_INCONSISTENCY;
528 }
529
530 return NB_OK;
531 }
532
533 static int lib_route_map_entry_match_condition_interface_destroy(
534 enum nb_event event, const struct lyd_node *dnode)
535 {
536 return lib_route_map_entry_match_destroy(event, dnode);
537 }
538
539 /*
540 * XPath: /frr-route-map:lib/route-map/entry/match-condition/access-list-num
541 */
542 static int lib_route_map_entry_match_condition_access_list_num_modify(
543 enum nb_event event, const struct lyd_node *dnode,
544 union nb_resource *resource)
545 {
546 struct routemap_hook_context *rhc;
547 const char *acl;
548 int condition, rv;
549
550 if (event != NB_EV_APPLY)
551 return NB_OK;
552
553 /* Check for hook function. */
554 rv = CMD_SUCCESS;
555 acl = yang_dnode_get_string(dnode, NULL);
556 rhc = nb_running_get_entry(dnode, NULL, true);
557 condition = yang_dnode_get_enum(dnode, "../condition");
558 switch (condition) {
559 case 1: /* ipv4-address-list */
560 if (rmap_match_set_hook.match_ip_address == NULL)
561 break;
562 rhc->rhc_mhook = rmap_match_set_hook.no_match_ip_address;
563 rhc->rhc_rule = "ip address";
564 rhc->rhc_event = RMAP_EVENT_FILTER_DELETED;
565 rv = rmap_match_set_hook.match_ip_address(
566 NULL, rhc->rhc_rmi, "ip address", acl,
567 RMAP_EVENT_FILTER_ADDED);
568 break;
569 case 3: /* ipv4-next-hop-list */
570 if (rmap_match_set_hook.match_ip_next_hop == NULL)
571 break;
572 rhc->rhc_mhook = rmap_match_set_hook.no_match_ip_next_hop;
573 rhc->rhc_rule = "ip next-hop";
574 rhc->rhc_event = RMAP_EVENT_FILTER_DELETED;
575 rv = rmap_match_set_hook.match_ip_next_hop(
576 NULL, rhc->rhc_rmi, "ip next-hop", acl,
577 RMAP_EVENT_FILTER_ADDED);
578 break;
579 }
580 if (rv != CMD_SUCCESS) {
581 rhc->rhc_mhook = NULL;
582 return NB_ERR_INCONSISTENCY;
583 }
584
585 return NB_OK;
586 }
587
588 static int lib_route_map_entry_match_condition_access_list_num_destroy(
589 enum nb_event event, const struct lyd_node *dnode)
590 {
591 return lib_route_map_entry_match_destroy(event, dnode);
592 }
593
594 /*
595 * XPath:
596 * /frr-route-map:lib/route-map/entry/match-condition/access-list-num-extended
597 */
598 static int lib_route_map_entry_match_condition_access_list_num_extended_modify(
599 enum nb_event event, const struct lyd_node *dnode,
600 union nb_resource *resource)
601 {
602 return lib_route_map_entry_match_condition_access_list_num_modify(
603 event, dnode, resource);
604 }
605
606 static int lib_route_map_entry_match_condition_access_list_num_extended_destroy(
607 enum nb_event event, const struct lyd_node *dnode)
608 {
609 return lib_route_map_entry_match_condition_access_list_num_destroy(
610 event, dnode);
611 }
612
613 /*
614 * XPath: /frr-route-map:lib/route-map/entry/match-condition/list-name
615 */
616 static int lib_route_map_entry_match_condition_list_name_modify(
617 enum nb_event event, const struct lyd_node *dnode,
618 union nb_resource *resource)
619 {
620 struct routemap_hook_context *rhc;
621 const char *acl;
622 int condition;
623 int rv;
624
625 if (event != NB_EV_APPLY)
626 return NB_OK;
627
628 /* Check for hook installation, otherwise we can just stop. */
629 acl = yang_dnode_get_string(dnode, NULL);
630 rhc = nb_running_get_entry(dnode, NULL, true);
631 condition = yang_dnode_get_enum(dnode, "../condition");
632 switch (condition) {
633 case 1: /* ipv4-address-list */
634 if (rmap_match_set_hook.match_ip_address == NULL)
635 return NB_OK;
636 rhc->rhc_mhook = rmap_match_set_hook.no_match_ip_address;
637 rhc->rhc_rule = "ip address";
638 rhc->rhc_event = RMAP_EVENT_FILTER_DELETED;
639 rv = rmap_match_set_hook.match_ip_address(
640 NULL, rhc->rhc_rmi, "ip address", acl,
641 RMAP_EVENT_FILTER_ADDED);
642 break;
643 case 2: /* ipv4-prefix-list */
644 if (rmap_match_set_hook.match_ip_address_prefix_list == NULL)
645 return NB_OK;
646 rhc->rhc_mhook =
647 rmap_match_set_hook.no_match_ip_address_prefix_list;
648 rhc->rhc_rule = "ip address prefix-list";
649 rhc->rhc_event = RMAP_EVENT_PLIST_DELETED;
650 rv = rmap_match_set_hook.match_ip_address_prefix_list(
651 NULL, rhc->rhc_rmi, "ip address prefix-list", acl,
652 RMAP_EVENT_PLIST_ADDED);
653 break;
654 case 3: /* ipv4-next-hop-list */
655 if (rmap_match_set_hook.match_ip_next_hop == NULL)
656 return NB_OK;
657 rhc->rhc_mhook = rmap_match_set_hook.no_match_ip_next_hop;
658 rhc->rhc_rule = "ip next-hop";
659 rhc->rhc_event = RMAP_EVENT_FILTER_DELETED;
660 rv = rmap_match_set_hook.match_ip_next_hop(
661 NULL, rhc->rhc_rmi, "ip next-hop", acl,
662 RMAP_EVENT_FILTER_ADDED);
663 break;
664 case 4: /* ipv4-next-hop-prefix-list */
665 if (rmap_match_set_hook.match_ip_next_hop_prefix_list == NULL)
666 return NB_OK;
667 rhc->rhc_mhook =
668 rmap_match_set_hook.no_match_ip_next_hop_prefix_list;
669 rhc->rhc_rule = "ip next-hop prefix-list";
670 rhc->rhc_event = RMAP_EVENT_PLIST_DELETED;
671 rv = rmap_match_set_hook.match_ip_next_hop_prefix_list(
672 NULL, rhc->rhc_rmi, "ip next-hop prefix-list", acl,
673 RMAP_EVENT_PLIST_ADDED);
674 break;
675 case 6: /* ipv6-address-list */
676 if (rmap_match_set_hook.match_ipv6_address == NULL)
677 return NB_OK;
678 rhc->rhc_mhook = rmap_match_set_hook.no_match_ipv6_address;
679 rhc->rhc_rule = "ipv6 address";
680 rhc->rhc_event = RMAP_EVENT_FILTER_DELETED;
681 rv = rmap_match_set_hook.match_ipv6_address(
682 NULL, rhc->rhc_rmi, "ipv6 address", acl,
683 RMAP_EVENT_FILTER_ADDED);
684 break;
685 case 7: /* ipv6-prefix-list */
686 if (rmap_match_set_hook.match_ipv6_address_prefix_list == NULL)
687 return NB_OK;
688 rhc->rhc_mhook =
689 rmap_match_set_hook.no_match_ipv6_address_prefix_list;
690 rhc->rhc_rule = "ipv6 address prefix-list";
691 rhc->rhc_event = RMAP_EVENT_PLIST_DELETED;
692 rv = rmap_match_set_hook.match_ipv6_address_prefix_list(
693 NULL, rhc->rhc_rmi, "ipv6 address prefix-list", acl,
694 RMAP_EVENT_PLIST_ADDED);
695 break;
696 default:
697 rv = CMD_ERR_NO_MATCH;
698 break;
699 }
700 if (rv != CMD_SUCCESS) {
701 rhc->rhc_mhook = NULL;
702 return NB_ERR_INCONSISTENCY;
703 }
704
705 return NB_OK;
706 }
707
708 static int lib_route_map_entry_match_condition_list_name_destroy(
709 enum nb_event event, const struct lyd_node *dnode)
710 {
711 return lib_route_map_entry_match_destroy(event, dnode);
712 }
713
714 /*
715 * XPath: /frr-route-map:lib/route-map/entry/match-condition/ipv4-next-hop-type
716 */
717 static int lib_route_map_entry_match_condition_ipv4_next_hop_type_modify(
718 enum nb_event event, const struct lyd_node *dnode,
719 union nb_resource *resource)
720 {
721 struct routemap_hook_context *rhc;
722 const char *type;
723 int rv;
724
725 if (event != NB_EV_APPLY)
726 return NB_OK;
727
728 /* Check for hook function. */
729 if (rmap_match_set_hook.match_ip_next_hop_type == NULL)
730 return NB_OK;
731
732 /* Add configuration. */
733 rhc = nb_running_get_entry(dnode, NULL, true);
734 type = yang_dnode_get_string(dnode, NULL);
735
736 /* Set destroy information. */
737 rhc->rhc_mhook = rmap_match_set_hook.no_match_ip_next_hop_type;
738 rhc->rhc_rule = "ip next-hop type";
739 rhc->rhc_event = RMAP_EVENT_MATCH_DELETED;
740
741 rv = rmap_match_set_hook.match_ip_next_hop_type(
742 NULL, rhc->rhc_rmi, "ip next-hop type", type,
743 RMAP_EVENT_MATCH_ADDED);
744 if (rv != CMD_SUCCESS) {
745 rhc->rhc_mhook = NULL;
746 return NB_ERR_INCONSISTENCY;
747 }
748
749 return NB_OK;
750 }
751
752 static int lib_route_map_entry_match_condition_ipv4_next_hop_type_destroy(
753 enum nb_event event, const struct lyd_node *dnode)
754 {
755 return lib_route_map_entry_match_destroy(event, dnode);
756 }
757
758 /*
759 * XPath: /frr-route-map:lib/route-map/entry/match-condition/ipv6-next-hop-type
760 */
761 static int lib_route_map_entry_match_condition_ipv6_next_hop_type_modify(
762 enum nb_event event, const struct lyd_node *dnode,
763 union nb_resource *resource)
764 {
765 struct routemap_hook_context *rhc;
766 const char *type;
767 int rv;
768
769 if (event != NB_EV_APPLY)
770 return NB_OK;
771
772 /* Check for hook function. */
773 if (rmap_match_set_hook.match_ipv6_next_hop_type == NULL)
774 return NB_OK;
775
776 /* Add configuration. */
777 rhc = nb_running_get_entry(dnode, NULL, true);
778 type = yang_dnode_get_string(dnode, NULL);
779
780 /* Set destroy information. */
781 rhc->rhc_mhook = rmap_match_set_hook.no_match_ipv6_next_hop_type;
782 rhc->rhc_rule = "ipv6 next-hop type";
783 rhc->rhc_event = RMAP_EVENT_MATCH_DELETED;
784
785 rv = rmap_match_set_hook.match_ipv6_next_hop_type(
786 NULL, rhc->rhc_rmi, "ipv6 next-hop type", type,
787 RMAP_EVENT_MATCH_ADDED);
788 if (rv != CMD_SUCCESS) {
789 rhc->rhc_mhook = NULL;
790 return NB_ERR_INCONSISTENCY;
791 }
792
793 return NB_OK;
794 }
795
796 static int lib_route_map_entry_match_condition_ipv6_next_hop_type_destroy(
797 enum nb_event event, const struct lyd_node *dnode)
798 {
799 return lib_route_map_entry_match_destroy(event, dnode);
800 }
801
802 /*
803 * XPath: /frr-route-map:lib/route-map/entry/match-condition/metric
804 */
805 static int
806 lib_route_map_entry_match_condition_metric_modify(enum nb_event event,
807 const struct lyd_node *dnode,
808 union nb_resource *resource)
809 {
810 struct routemap_hook_context *rhc;
811 const char *type;
812 int rv;
813
814 if (event != NB_EV_APPLY)
815 return NB_OK;
816
817 /* Check for hook function. */
818 if (rmap_match_set_hook.match_metric == NULL)
819 return NB_OK;
820
821 /* Add configuration. */
822 rhc = nb_running_get_entry(dnode, NULL, true);
823 type = yang_dnode_get_string(dnode, NULL);
824
825 /* Set destroy information. */
826 rhc->rhc_mhook = rmap_match_set_hook.no_match_metric;
827 rhc->rhc_rule = "metric";
828 rhc->rhc_event = RMAP_EVENT_MATCH_DELETED;
829
830 rv = rmap_match_set_hook.match_metric(NULL, rhc->rhc_rmi, "metric",
831 type, RMAP_EVENT_MATCH_ADDED);
832 if (rv != CMD_SUCCESS) {
833 rhc->rhc_mhook = NULL;
834 return NB_ERR_INCONSISTENCY;
835 }
836
837 return NB_OK;
838 }
839
840 static int
841 lib_route_map_entry_match_condition_metric_destroy(enum nb_event event,
842 const struct lyd_node *dnode)
843 {
844 return lib_route_map_entry_match_destroy(event, dnode);
845 }
846
847 /*
848 * XPath: /frr-route-map:lib/route-map/entry/match-condition/tag
849 */
850 static int
851 lib_route_map_entry_match_condition_tag_modify(enum nb_event event,
852 const struct lyd_node *dnode,
853 union nb_resource *resource)
854 {
855 struct routemap_hook_context *rhc;
856 const char *tag;
857 int rv;
858
859 if (event != NB_EV_APPLY)
860 return NB_OK;
861
862 /* Check for hook function. */
863 if (rmap_match_set_hook.match_tag == NULL)
864 return NB_OK;
865
866 /* Add configuration. */
867 rhc = nb_running_get_entry(dnode, NULL, true);
868 tag = yang_dnode_get_string(dnode, NULL);
869
870 /* Set destroy information. */
871 rhc->rhc_mhook = rmap_match_set_hook.no_match_tag;
872 rhc->rhc_rule = "tag";
873 rhc->rhc_event = RMAP_EVENT_MATCH_DELETED;
874
875 rv = rmap_match_set_hook.match_tag(NULL, rhc->rhc_rmi, "tag", tag,
876 RMAP_EVENT_MATCH_ADDED);
877 if (rv != CMD_SUCCESS) {
878 rhc->rhc_mhook = NULL;
879 return NB_ERR_INCONSISTENCY;
880 }
881
882 return NB_OK;
883 }
884
885 static int
886 lib_route_map_entry_match_condition_tag_destroy(enum nb_event event,
887 const struct lyd_node *dnode)
888 {
889 return lib_route_map_entry_match_destroy(event, dnode);
890 }
891
892 /*
893 * XPath: /frr-route-map:lib/route-map/entry/set-action
894 */
895 static int lib_route_map_entry_set_action_create(enum nb_event event,
896 const struct lyd_node *dnode,
897 union nb_resource *resource)
898 {
899 return lib_route_map_entry_match_condition_create(event, dnode,
900 resource);
901 }
902
903 static int lib_route_map_entry_set_action_destroy(enum nb_event event,
904 const struct lyd_node *dnode)
905 {
906 struct routemap_hook_context *rhc;
907 int rv;
908
909 if (event != NB_EV_APPLY)
910 return NB_OK;
911
912 rv = lib_route_map_entry_set_destroy(event, dnode);
913 rhc = nb_running_unset_entry(dnode);
914 routemap_hook_context_free(rhc);
915
916 return rv;
917 }
918
919 /*
920 * XPath: /frr-route-map:lib/route-map/entry/set-action/ipv4-address
921 */
922 static int
923 lib_route_map_entry_set_action_ipv4_address_modify(enum nb_event event,
924 const struct lyd_node *dnode,
925 union nb_resource *resource)
926 {
927 struct routemap_hook_context *rhc;
928 const char *address;
929 struct in_addr ia;
930 int rv;
931
932 switch (event) {
933 case NB_EV_VALIDATE:
934 /*
935 * NOTE: validate if 'action' is 'ipv4-next-hop',
936 * currently it is not necessary because this is the
937 * only implemented action.
938 */
939 yang_dnode_get_ipv4(&ia, dnode, NULL);
940 if (ia.s_addr == INADDR_ANY || IPV4_CLASS_DE(ntohl(ia.s_addr)))
941 return NB_ERR_VALIDATION;
942 /* FALLTHROUGH */
943 case NB_EV_PREPARE:
944 case NB_EV_ABORT:
945 return NB_OK;
946 case NB_EV_APPLY:
947 break;
948 }
949
950 /* Check for hook function. */
951 if (rmap_match_set_hook.set_ip_nexthop == NULL)
952 return NB_OK;
953
954 /* Add configuration. */
955 rhc = nb_running_get_entry(dnode, NULL, true);
956 address = yang_dnode_get_string(dnode, NULL);
957
958 /* Set destroy information. */
959 rhc->rhc_shook = rmap_match_set_hook.no_set_ip_nexthop;
960 rhc->rhc_rule = "ip next-hop";
961
962 rv = rmap_match_set_hook.set_ip_nexthop(NULL, rhc->rhc_rmi,
963 "ip next-hop", address);
964 if (rv != CMD_SUCCESS) {
965 rhc->rhc_shook = NULL;
966 return NB_ERR_INCONSISTENCY;
967 }
968
969 return NB_OK;
970 }
971
972 static int lib_route_map_entry_set_action_ipv4_address_destroy(
973 enum nb_event event, const struct lyd_node *dnode)
974 {
975 return lib_route_map_entry_set_destroy(event, dnode);
976 }
977
978 /*
979 * XPath: /frr-route-map:lib/route-map/entry/set-action/ipv6-address
980 */
981 static int
982 lib_route_map_entry_set_action_ipv6_address_modify(enum nb_event event,
983 const struct lyd_node *dnode,
984 union nb_resource *resource)
985 {
986 struct routemap_hook_context *rhc;
987 const char *address;
988 struct in6_addr i6a;
989 int rv;
990
991 switch (event) {
992 case NB_EV_VALIDATE:
993 /*
994 * NOTE: validate if 'action' is 'ipv6-next-hop',
995 * currently it is not necessary because this is the
996 * only implemented action. Other actions might have
997 * different validations.
998 */
999 yang_dnode_get_ipv6(&i6a, dnode, NULL);
1000 if (!IN6_IS_ADDR_LINKLOCAL(&i6a))
1001 return NB_ERR_VALIDATION;
1002 /* FALLTHROUGH */
1003 case NB_EV_PREPARE:
1004 case NB_EV_ABORT:
1005 return NB_OK;
1006 case NB_EV_APPLY:
1007 break;
1008 }
1009
1010 /* Check for hook function. */
1011 if (rmap_match_set_hook.set_ipv6_nexthop_local == NULL)
1012 return NB_OK;
1013
1014 /* Add configuration. */
1015 rhc = nb_running_get_entry(dnode, NULL, true);
1016 address = yang_dnode_get_string(dnode, NULL);
1017
1018 /* Set destroy information. */
1019 rhc->rhc_shook = rmap_match_set_hook.no_set_ipv6_nexthop_local;
1020 rhc->rhc_rule = "ipv6 next-hop local";
1021
1022 rv = rmap_match_set_hook.set_ipv6_nexthop_local(
1023 NULL, rhc->rhc_rmi, "ipv6 next-hop local", address);
1024 if (rv != CMD_SUCCESS) {
1025 rhc->rhc_shook = NULL;
1026 return NB_ERR_INCONSISTENCY;
1027 }
1028
1029 return NB_OK;
1030 }
1031
1032 static int lib_route_map_entry_set_action_ipv6_address_destroy(
1033 enum nb_event event, const struct lyd_node *dnode)
1034 {
1035 return lib_route_map_entry_set_destroy(event, dnode);
1036 }
1037
1038 /*
1039 * XPath: /frr-route-map:lib/route-map/entry/set-action/value
1040 */
1041 static int set_action_modify(enum nb_event event, const struct lyd_node *dnode,
1042 union nb_resource *resource, const char *value)
1043 {
1044 struct routemap_hook_context *rhc;
1045 int rv;
1046
1047 /*
1048 * NOTE: validate if 'action' is 'metric', currently it is not
1049 * necessary because this is the only implemented action. Other
1050 * actions might have different validations.
1051 */
1052 if (event != NB_EV_APPLY)
1053 return NB_OK;
1054
1055 /* Check for hook function. */
1056 if (rmap_match_set_hook.set_metric == NULL)
1057 return NB_OK;
1058
1059 /* Add configuration. */
1060 rhc = nb_running_get_entry(dnode, NULL, true);
1061
1062 /* Set destroy information. */
1063 rhc->rhc_shook = rmap_match_set_hook.no_set_metric;
1064 rhc->rhc_rule = "metric";
1065
1066 rv = rmap_match_set_hook.set_metric(NULL, rhc->rhc_rmi, "metric",
1067 value);
1068 if (rv != CMD_SUCCESS) {
1069 rhc->rhc_shook = NULL;
1070 return NB_ERR_INCONSISTENCY;
1071 }
1072
1073 return NB_OK;
1074 }
1075
1076 static int
1077 lib_route_map_entry_set_action_value_modify(enum nb_event event,
1078 const struct lyd_node *dnode,
1079 union nb_resource *resource)
1080 {
1081 const char *metric = yang_dnode_get_string(dnode, NULL);
1082
1083 return set_action_modify(event, dnode, resource, metric);
1084 }
1085
1086 static int
1087 lib_route_map_entry_set_action_value_destroy(enum nb_event event,
1088 const struct lyd_node *dnode)
1089 {
1090 return lib_route_map_entry_set_destroy(event, dnode);
1091 }
1092
1093 /*
1094 * XPath: /frr-route-map:lib/route-map/entry/set-action/add-metric
1095 */
1096 static int
1097 lib_route_map_entry_set_action_add_metric_modify(enum nb_event event,
1098 const struct lyd_node *dnode,
1099 union nb_resource *resource)
1100 {
1101 return set_action_modify(event, dnode, resource, "+metric");
1102 }
1103
1104 static int
1105 lib_route_map_entry_set_action_add_metric_destroy(enum nb_event event,
1106 const struct lyd_node *dnode)
1107 {
1108 return lib_route_map_entry_set_action_value_destroy(event, dnode);
1109 }
1110
1111 /*
1112 * XPath: /frr-route-map:lib/route-map/entry/set-action/subtract-metric
1113 */
1114 static int lib_route_map_entry_set_action_subtract_metric_modify(
1115 enum nb_event event, const struct lyd_node *dnode,
1116 union nb_resource *resource)
1117 {
1118 return set_action_modify(event, dnode, resource, "-metric");
1119 }
1120
1121 static int lib_route_map_entry_set_action_subtract_metric_destroy(
1122 enum nb_event event, const struct lyd_node *dnode)
1123 {
1124 return lib_route_map_entry_set_action_value_destroy(event, dnode);
1125 }
1126
1127 /*
1128 * XPath: /frr-route-map:lib/route-map/entry/set-action/use-round-trip-time
1129 */
1130 static int lib_route_map_entry_set_action_use_round_trip_time_modify(
1131 enum nb_event event, const struct lyd_node *dnode,
1132 union nb_resource *resource)
1133 {
1134 return set_action_modify(event, dnode, resource, "rtt");
1135 }
1136
1137 static int lib_route_map_entry_set_action_use_round_trip_time_destroy(
1138 enum nb_event event, const struct lyd_node *dnode)
1139 {
1140 return lib_route_map_entry_set_action_value_destroy(event, dnode);
1141 }
1142
1143 /*
1144 * XPath: /frr-route-map:lib/route-map/entry/set-action/add-round-trip-time
1145 */
1146 static int lib_route_map_entry_set_action_add_round_trip_time_modify(
1147 enum nb_event event, const struct lyd_node *dnode,
1148 union nb_resource *resource)
1149 {
1150 return set_action_modify(event, dnode, resource, "+rtt");
1151 }
1152
1153 static int lib_route_map_entry_set_action_add_round_trip_time_destroy(
1154 enum nb_event event, const struct lyd_node *dnode)
1155 {
1156 return lib_route_map_entry_set_action_value_destroy(event, dnode);
1157 }
1158
1159 /*
1160 * XPath: /frr-route-map:lib/route-map/entry/set-action/subtract-round-trip-time
1161 */
1162 static int lib_route_map_entry_set_action_subtract_round_trip_time_modify(
1163 enum nb_event event, const struct lyd_node *dnode,
1164 union nb_resource *resource)
1165 {
1166 return set_action_modify(event, dnode, resource, "-rtt");
1167 }
1168
1169 static int lib_route_map_entry_set_action_subtract_round_trip_time_destroy(
1170 enum nb_event event, const struct lyd_node *dnode)
1171 {
1172 return lib_route_map_entry_set_action_value_destroy(event, dnode);
1173 }
1174
1175 /*
1176 * XPath: /frr-route-map:lib/route-map/entry/set-action/tag
1177 */
1178 static int
1179 lib_route_map_entry_set_action_tag_modify(enum nb_event event,
1180 const struct lyd_node *dnode,
1181 union nb_resource *resource)
1182 {
1183 struct routemap_hook_context *rhc;
1184 const char *tag;
1185 int rv;
1186
1187 /*
1188 * NOTE: validate if 'action' is 'tag', currently it is not
1189 * necessary because this is the only implemented action. Other
1190 * actions might have different validations.
1191 */
1192 if (event != NB_EV_APPLY)
1193 return NB_OK;
1194
1195 /* Check for hook function. */
1196 if (rmap_match_set_hook.set_tag == NULL)
1197 return NB_OK;
1198
1199 /* Add configuration. */
1200 rhc = nb_running_get_entry(dnode, NULL, true);
1201 tag = yang_dnode_get_string(dnode, NULL);
1202
1203 /* Set destroy information. */
1204 rhc->rhc_shook = rmap_match_set_hook.no_set_tag;
1205 rhc->rhc_rule = "tag";
1206
1207 rv = rmap_match_set_hook.set_tag(NULL, rhc->rhc_rmi, "tag", tag);
1208 if (rv != CMD_SUCCESS) {
1209 rhc->rhc_shook = NULL;
1210 return NB_ERR_INCONSISTENCY;
1211 }
1212
1213 return NB_OK;
1214 }
1215
1216 static int
1217 lib_route_map_entry_set_action_tag_destroy(enum nb_event event,
1218 const struct lyd_node *dnode)
1219 {
1220 return lib_route_map_entry_set_destroy(event, dnode);
1221 }
1222
1223 /* clang-format off */
1224 const struct frr_yang_module_info frr_route_map_info = {
1225 .name = "frr-route-map",
1226 .nodes = {
1227 {
1228 .xpath = "/frr-route-map:lib/route-map",
1229 .cbs = {
1230 .create = lib_route_map_create,
1231 .destroy = lib_route_map_destroy,
1232 }
1233 },
1234 {
1235 .xpath = "/frr-route-map:lib/route-map/entry",
1236 .cbs = {
1237 .create = lib_route_map_entry_create,
1238 .destroy = lib_route_map_entry_destroy,
1239 .cli_show = route_map_instance_show,
1240 .cli_show_end = route_map_instance_show_end,
1241 }
1242 },
1243 {
1244 .xpath = "/frr-route-map:lib/route-map/entry/description",
1245 .cbs = {
1246 .modify = lib_route_map_entry_description_modify,
1247 .destroy = lib_route_map_entry_description_destroy,
1248 .cli_show = route_map_description_show,
1249 }
1250 },
1251 {
1252 .xpath = "/frr-route-map:lib/route-map/entry/action",
1253 .cbs = {
1254 .modify = lib_route_map_entry_action_modify,
1255 }
1256 },
1257 {
1258 .xpath = "/frr-route-map:lib/route-map/entry/call",
1259 .cbs = {
1260 .modify = lib_route_map_entry_call_modify,
1261 .destroy = lib_route_map_entry_call_destroy,
1262 .cli_show = route_map_call_show,
1263 }
1264 },
1265 {
1266 .xpath = "/frr-route-map:lib/route-map/entry/exit-policy",
1267 .cbs = {
1268 .modify = lib_route_map_entry_exit_policy_modify,
1269 .cli_show = route_map_exit_policy_show,
1270 }
1271 },
1272 {
1273 .xpath = "/frr-route-map:lib/route-map/entry/goto-value",
1274 .cbs = {
1275 .modify = lib_route_map_entry_goto_value_modify,
1276 .destroy = lib_route_map_entry_goto_value_destroy,
1277 }
1278 },
1279 {
1280 .xpath = "/frr-route-map:lib/route-map/entry/match-condition",
1281 .cbs = {
1282 .create = lib_route_map_entry_match_condition_create,
1283 .destroy = lib_route_map_entry_match_condition_destroy,
1284 .cli_show = route_map_condition_show,
1285 }
1286 },
1287 {
1288 .xpath = "/frr-route-map:lib/route-map/entry/match-condition/interface",
1289 .cbs = {
1290 .modify = lib_route_map_entry_match_condition_interface_modify,
1291 .destroy = lib_route_map_entry_match_condition_interface_destroy,
1292 }
1293 },
1294 {
1295 .xpath = "/frr-route-map:lib/route-map/entry/match-condition/access-list-num",
1296 .cbs = {
1297 .modify = lib_route_map_entry_match_condition_access_list_num_modify,
1298 .destroy = lib_route_map_entry_match_condition_access_list_num_destroy,
1299 }
1300 },
1301 {
1302 .xpath = "/frr-route-map:lib/route-map/entry/match-condition/access-list-num-extended",
1303 .cbs = {
1304 .modify = lib_route_map_entry_match_condition_access_list_num_extended_modify,
1305 .destroy = lib_route_map_entry_match_condition_access_list_num_extended_destroy,
1306 }
1307 },
1308 {
1309 .xpath = "/frr-route-map:lib/route-map/entry/match-condition/list-name",
1310 .cbs = {
1311 .modify = lib_route_map_entry_match_condition_list_name_modify,
1312 .destroy = lib_route_map_entry_match_condition_list_name_destroy,
1313 }
1314 },
1315 {
1316 .xpath = "/frr-route-map:lib/route-map/entry/match-condition/ipv4-next-hop-type",
1317 .cbs = {
1318 .modify = lib_route_map_entry_match_condition_ipv4_next_hop_type_modify,
1319 .destroy = lib_route_map_entry_match_condition_ipv4_next_hop_type_destroy,
1320 }
1321 },
1322 {
1323 .xpath = "/frr-route-map:lib/route-map/entry/match-condition/ipv6-next-hop-type",
1324 .cbs = {
1325 .modify = lib_route_map_entry_match_condition_ipv6_next_hop_type_modify,
1326 .destroy = lib_route_map_entry_match_condition_ipv6_next_hop_type_destroy,
1327 }
1328 },
1329 {
1330 .xpath = "/frr-route-map:lib/route-map/entry/match-condition/metric",
1331 .cbs = {
1332 .modify = lib_route_map_entry_match_condition_metric_modify,
1333 .destroy = lib_route_map_entry_match_condition_metric_destroy,
1334 }
1335 },
1336 {
1337 .xpath = "/frr-route-map:lib/route-map/entry/match-condition/tag",
1338 .cbs = {
1339 .modify = lib_route_map_entry_match_condition_tag_modify,
1340 .destroy = lib_route_map_entry_match_condition_tag_destroy,
1341 }
1342 },
1343 {
1344 .xpath = "/frr-route-map:lib/route-map/entry/set-action",
1345 .cbs = {
1346 .create = lib_route_map_entry_set_action_create,
1347 .destroy = lib_route_map_entry_set_action_destroy,
1348 .cli_show = route_map_action_show,
1349 }
1350 },
1351 {
1352 .xpath = "/frr-route-map:lib/route-map/entry/set-action/ipv4-address",
1353 .cbs = {
1354 .modify = lib_route_map_entry_set_action_ipv4_address_modify,
1355 .destroy = lib_route_map_entry_set_action_ipv4_address_destroy,
1356 }
1357 },
1358 {
1359 .xpath = "/frr-route-map:lib/route-map/entry/set-action/ipv6-address",
1360 .cbs = {
1361 .modify = lib_route_map_entry_set_action_ipv6_address_modify,
1362 .destroy = lib_route_map_entry_set_action_ipv6_address_destroy,
1363 }
1364 },
1365 {
1366 .xpath = "/frr-route-map:lib/route-map/entry/set-action/value",
1367 .cbs = {
1368 .modify = lib_route_map_entry_set_action_value_modify,
1369 .destroy = lib_route_map_entry_set_action_value_destroy,
1370 }
1371 },
1372 {
1373 .xpath = "/frr-route-map:lib/route-map/entry/set-action/add-metric",
1374 .cbs = {
1375 .modify = lib_route_map_entry_set_action_add_metric_modify,
1376 .destroy = lib_route_map_entry_set_action_add_metric_destroy,
1377 }
1378 },
1379 {
1380 .xpath = "/frr-route-map:lib/route-map/entry/set-action/subtract-metric",
1381 .cbs = {
1382 .modify = lib_route_map_entry_set_action_subtract_metric_modify,
1383 .destroy = lib_route_map_entry_set_action_subtract_metric_destroy,
1384 }
1385 },
1386 {
1387 .xpath = "/frr-route-map:lib/route-map/entry/set-action/use-round-trip-time",
1388 .cbs = {
1389 .modify = lib_route_map_entry_set_action_use_round_trip_time_modify,
1390 .destroy = lib_route_map_entry_set_action_use_round_trip_time_destroy,
1391 }
1392 },
1393 {
1394 .xpath = "/frr-route-map:lib/route-map/entry/set-action/add-round-trip-time",
1395 .cbs = {
1396 .modify = lib_route_map_entry_set_action_add_round_trip_time_modify,
1397 .destroy = lib_route_map_entry_set_action_add_round_trip_time_destroy,
1398 }
1399 },
1400 {
1401 .xpath = "/frr-route-map:lib/route-map/entry/set-action/subtract-round-trip-time",
1402 .cbs = {
1403 .modify = lib_route_map_entry_set_action_subtract_round_trip_time_modify,
1404 .destroy = lib_route_map_entry_set_action_subtract_round_trip_time_destroy,
1405 }
1406 },
1407 {
1408 .xpath = "/frr-route-map:lib/route-map/entry/set-action/tag",
1409 .cbs = {
1410 .modify = lib_route_map_entry_set_action_tag_modify,
1411 .destroy = lib_route_map_entry_set_action_tag_destroy,
1412 }
1413 },
1414 {
1415 .xpath = NULL,
1416 },
1417 }
1418 };