]> git.proxmox.com Git - mirror_frr.git/blob - lib/routemap_northbound.c
Merge pull request #6732 from opensourcerouting/printfrr-prep
[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 return set_action_modify(args->event, args->dnode, args->resource,
987 "+metric");
988 }
989
990 static int lib_route_map_entry_set_action_add_metric_destroy(
991 struct nb_cb_destroy_args *args)
992 {
993 return lib_route_map_entry_set_action_value_destroy(args);
994 }
995
996 /*
997 * XPath: /frr-route-map:lib/route-map/entry/set-action/subtract-metric
998 */
999 static int lib_route_map_entry_set_action_subtract_metric_modify(
1000 struct nb_cb_modify_args *args)
1001 {
1002 return set_action_modify(args->event, args->dnode, args->resource,
1003 "-metric");
1004 }
1005
1006 static int lib_route_map_entry_set_action_subtract_metric_destroy(
1007 struct nb_cb_destroy_args *args)
1008 {
1009 return lib_route_map_entry_set_action_value_destroy(args);
1010 }
1011
1012 /*
1013 * XPath: /frr-route-map:lib/route-map/entry/set-action/use-round-trip-time
1014 */
1015 static int lib_route_map_entry_set_action_use_round_trip_time_modify(
1016 struct nb_cb_modify_args *args)
1017 {
1018 return set_action_modify(args->event, args->dnode, args->resource,
1019 "rtt");
1020 }
1021
1022 static int lib_route_map_entry_set_action_use_round_trip_time_destroy(
1023 struct nb_cb_destroy_args *args)
1024 {
1025 return lib_route_map_entry_set_action_value_destroy(args);
1026 }
1027
1028 /*
1029 * XPath: /frr-route-map:lib/route-map/entry/set-action/add-round-trip-time
1030 */
1031 static int lib_route_map_entry_set_action_add_round_trip_time_modify(
1032 struct nb_cb_modify_args *args)
1033 {
1034 return set_action_modify(args->event, args->dnode, args->resource,
1035 "+rtt");
1036 }
1037
1038 static int lib_route_map_entry_set_action_add_round_trip_time_destroy(
1039 struct nb_cb_destroy_args *args)
1040 {
1041 return lib_route_map_entry_set_action_value_destroy(args);
1042 }
1043
1044 /*
1045 * XPath: /frr-route-map:lib/route-map/entry/set-action/subtract-round-trip-time
1046 */
1047 static int lib_route_map_entry_set_action_subtract_round_trip_time_modify(
1048 struct nb_cb_modify_args *args)
1049 {
1050 return set_action_modify(args->event, args->dnode, args->resource,
1051 "-rtt");
1052 }
1053
1054 static int lib_route_map_entry_set_action_subtract_round_trip_time_destroy(
1055 struct nb_cb_destroy_args *args)
1056 {
1057 return lib_route_map_entry_set_action_value_destroy(args);
1058 }
1059
1060 /*
1061 * XPath: /frr-route-map:lib/route-map/entry/set-action/tag
1062 */
1063 static int
1064 lib_route_map_entry_set_action_tag_modify(struct nb_cb_modify_args *args)
1065 {
1066 struct routemap_hook_context *rhc;
1067 const char *tag;
1068 int rv;
1069
1070 /*
1071 * NOTE: validate if 'action' is 'tag', currently it is not
1072 * necessary because this is the only implemented action. Other
1073 * actions might have different validations.
1074 */
1075 if (args->event != NB_EV_APPLY)
1076 return NB_OK;
1077
1078 /* Check for hook function. */
1079 if (rmap_match_set_hook.set_tag == NULL)
1080 return NB_OK;
1081
1082 /* Add configuration. */
1083 rhc = nb_running_get_entry(args->dnode, NULL, true);
1084 tag = yang_dnode_get_string(args->dnode, NULL);
1085
1086 /* Set destroy information. */
1087 rhc->rhc_shook = rmap_match_set_hook.no_set_tag;
1088 rhc->rhc_rule = "tag";
1089
1090 rv = rmap_match_set_hook.set_tag(NULL, rhc->rhc_rmi, "tag", tag);
1091 if (rv != CMD_SUCCESS) {
1092 rhc->rhc_shook = NULL;
1093 return NB_ERR_INCONSISTENCY;
1094 }
1095
1096 return NB_OK;
1097 }
1098
1099 static int
1100 lib_route_map_entry_set_action_tag_destroy(struct nb_cb_destroy_args *args)
1101 {
1102 return lib_route_map_entry_set_destroy(args);
1103 }
1104
1105 /* clang-format off */
1106 const struct frr_yang_module_info frr_route_map_info = {
1107 .name = "frr-route-map",
1108 .nodes = {
1109 {
1110 .xpath = "/frr-route-map:lib/route-map",
1111 .cbs = {
1112 .create = lib_route_map_create,
1113 .destroy = lib_route_map_destroy,
1114 }
1115 },
1116 {
1117 .xpath = "/frr-route-map:lib/route-map/entry",
1118 .cbs = {
1119 .create = lib_route_map_entry_create,
1120 .destroy = lib_route_map_entry_destroy,
1121 .cli_show = route_map_instance_show,
1122 .cli_show_end = route_map_instance_show_end,
1123 }
1124 },
1125 {
1126 .xpath = "/frr-route-map:lib/route-map/entry/description",
1127 .cbs = {
1128 .modify = lib_route_map_entry_description_modify,
1129 .destroy = lib_route_map_entry_description_destroy,
1130 .cli_show = route_map_description_show,
1131 }
1132 },
1133 {
1134 .xpath = "/frr-route-map:lib/route-map/entry/action",
1135 .cbs = {
1136 .modify = lib_route_map_entry_action_modify,
1137 }
1138 },
1139 {
1140 .xpath = "/frr-route-map:lib/route-map/entry/call",
1141 .cbs = {
1142 .modify = lib_route_map_entry_call_modify,
1143 .destroy = lib_route_map_entry_call_destroy,
1144 .cli_show = route_map_call_show,
1145 }
1146 },
1147 {
1148 .xpath = "/frr-route-map:lib/route-map/entry/exit-policy",
1149 .cbs = {
1150 .modify = lib_route_map_entry_exit_policy_modify,
1151 .cli_show = route_map_exit_policy_show,
1152 }
1153 },
1154 {
1155 .xpath = "/frr-route-map:lib/route-map/entry/goto-value",
1156 .cbs = {
1157 .modify = lib_route_map_entry_goto_value_modify,
1158 .destroy = lib_route_map_entry_goto_value_destroy,
1159 }
1160 },
1161 {
1162 .xpath = "/frr-route-map:lib/route-map/entry/match-condition",
1163 .cbs = {
1164 .create = lib_route_map_entry_match_condition_create,
1165 .destroy = lib_route_map_entry_match_condition_destroy,
1166 .cli_show = route_map_condition_show,
1167 }
1168 },
1169 {
1170 .xpath = "/frr-route-map:lib/route-map/entry/match-condition/interface",
1171 .cbs = {
1172 .modify = lib_route_map_entry_match_condition_interface_modify,
1173 .destroy = lib_route_map_entry_match_condition_interface_destroy,
1174 }
1175 },
1176 {
1177 .xpath = "/frr-route-map:lib/route-map/entry/match-condition/list-name",
1178 .cbs = {
1179 .modify = lib_route_map_entry_match_condition_list_name_modify,
1180 .destroy = lib_route_map_entry_match_condition_list_name_destroy,
1181 }
1182 },
1183 {
1184 .xpath = "/frr-route-map:lib/route-map/entry/match-condition/ipv4-next-hop-type",
1185 .cbs = {
1186 .modify = lib_route_map_entry_match_condition_ipv4_next_hop_type_modify,
1187 .destroy = lib_route_map_entry_match_condition_ipv4_next_hop_type_destroy,
1188 }
1189 },
1190 {
1191 .xpath = "/frr-route-map:lib/route-map/entry/match-condition/ipv6-next-hop-type",
1192 .cbs = {
1193 .modify = lib_route_map_entry_match_condition_ipv6_next_hop_type_modify,
1194 .destroy = lib_route_map_entry_match_condition_ipv6_next_hop_type_destroy,
1195 }
1196 },
1197 {
1198 .xpath = "/frr-route-map:lib/route-map/entry/match-condition/metric",
1199 .cbs = {
1200 .modify = lib_route_map_entry_match_condition_metric_modify,
1201 .destroy = lib_route_map_entry_match_condition_metric_destroy,
1202 }
1203 },
1204 {
1205 .xpath = "/frr-route-map:lib/route-map/entry/match-condition/tag",
1206 .cbs = {
1207 .modify = lib_route_map_entry_match_condition_tag_modify,
1208 .destroy = lib_route_map_entry_match_condition_tag_destroy,
1209 }
1210 },
1211 {
1212 .xpath = "/frr-route-map:lib/route-map/entry/set-action",
1213 .cbs = {
1214 .create = lib_route_map_entry_set_action_create,
1215 .destroy = lib_route_map_entry_set_action_destroy,
1216 .cli_show = route_map_action_show,
1217 }
1218 },
1219 {
1220 .xpath = "/frr-route-map:lib/route-map/entry/set-action/ipv4-address",
1221 .cbs = {
1222 .modify = lib_route_map_entry_set_action_ipv4_address_modify,
1223 .destroy = lib_route_map_entry_set_action_ipv4_address_destroy,
1224 }
1225 },
1226 {
1227 .xpath = "/frr-route-map:lib/route-map/entry/set-action/ipv6-address",
1228 .cbs = {
1229 .modify = lib_route_map_entry_set_action_ipv6_address_modify,
1230 .destroy = lib_route_map_entry_set_action_ipv6_address_destroy,
1231 }
1232 },
1233 {
1234 .xpath = "/frr-route-map:lib/route-map/entry/set-action/value",
1235 .cbs = {
1236 .modify = lib_route_map_entry_set_action_value_modify,
1237 .destroy = lib_route_map_entry_set_action_value_destroy,
1238 }
1239 },
1240 {
1241 .xpath = "/frr-route-map:lib/route-map/entry/set-action/add-metric",
1242 .cbs = {
1243 .modify = lib_route_map_entry_set_action_add_metric_modify,
1244 .destroy = lib_route_map_entry_set_action_add_metric_destroy,
1245 }
1246 },
1247 {
1248 .xpath = "/frr-route-map:lib/route-map/entry/set-action/subtract-metric",
1249 .cbs = {
1250 .modify = lib_route_map_entry_set_action_subtract_metric_modify,
1251 .destroy = lib_route_map_entry_set_action_subtract_metric_destroy,
1252 }
1253 },
1254 {
1255 .xpath = "/frr-route-map:lib/route-map/entry/set-action/use-round-trip-time",
1256 .cbs = {
1257 .modify = lib_route_map_entry_set_action_use_round_trip_time_modify,
1258 .destroy = lib_route_map_entry_set_action_use_round_trip_time_destroy,
1259 }
1260 },
1261 {
1262 .xpath = "/frr-route-map:lib/route-map/entry/set-action/add-round-trip-time",
1263 .cbs = {
1264 .modify = lib_route_map_entry_set_action_add_round_trip_time_modify,
1265 .destroy = lib_route_map_entry_set_action_add_round_trip_time_destroy,
1266 }
1267 },
1268 {
1269 .xpath = "/frr-route-map:lib/route-map/entry/set-action/subtract-round-trip-time",
1270 .cbs = {
1271 .modify = lib_route_map_entry_set_action_subtract_round_trip_time_modify,
1272 .destroy = lib_route_map_entry_set_action_subtract_round_trip_time_destroy,
1273 }
1274 },
1275 {
1276 .xpath = "/frr-route-map:lib/route-map/entry/set-action/tag",
1277 .cbs = {
1278 .modify = lib_route_map_entry_set_action_tag_modify,
1279 .destroy = lib_route_map_entry_set_action_tag_destroy,
1280 }
1281 },
1282 {
1283 .xpath = NULL,
1284 },
1285 }
1286 };