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