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