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