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