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