]> git.proxmox.com Git - mirror_frr.git/blob - lib/routemap_cli.c
Merge pull request #5778 from ton31337/fix/add_doc_for_ebgp_connected_route_check
[mirror_frr.git] / lib / routemap_cli.c
1 /*
2 * Route map northbound CLI 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/northbound_cli.h"
27 #include "lib/routemap.h"
28
29 #ifndef VTYSH_EXTRACT_PL
30 #include "lib/routemap_cli_clippy.c"
31 #endif /* VTYSH_EXTRACT_PL */
32
33 #define ROUTE_MAP_CMD_STR \
34 "Create route-map or enter route-map command mode\n" \
35 "Route map tag\n"
36 #define ROUTE_MAP_OP_CMD_STR \
37 "Route map denies set operations\n" \
38 "Route map permits set operations\n"
39 #define ROUTE_MAP_SEQUENCE_CMD_STR \
40 "Sequence to insert to/delete from existing route-map entry\n"
41
42 DEFPY_NOSH(
43 route_map, route_map_cmd,
44 "route-map WORD$name <deny|permit>$action (1-65535)$sequence",
45 ROUTE_MAP_CMD_STR
46 ROUTE_MAP_OP_CMD_STR
47 ROUTE_MAP_SEQUENCE_CMD_STR)
48 {
49 struct route_map_index *rmi;
50 struct route_map *rm;
51 int action_type;
52 char xpath_action[XPATH_MAXLEN + 64];
53 char xpath_index[XPATH_MAXLEN + 32];
54 char xpath[XPATH_MAXLEN];
55 int rv;
56
57 snprintf(xpath, sizeof(xpath),
58 "/frr-route-map:lib/route-map[name='%s']", name);
59 nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
60
61 snprintf(xpath_index, sizeof(xpath_index), "%s/entry[sequence='%lu']",
62 xpath, sequence);
63 nb_cli_enqueue_change(vty, xpath_index, NB_OP_CREATE, NULL);
64
65 snprintf(xpath_action, sizeof(xpath_action), "%s/action", xpath_index);
66 nb_cli_enqueue_change(vty, xpath_action, NB_OP_MODIFY, action);
67
68 rv = nb_cli_apply_changes(vty, NULL);
69 if (rv == CMD_SUCCESS) {
70 VTY_PUSH_XPATH(RMAP_NODE, xpath_index);
71
72 /* Add support for non-migrated route map users. */
73 rm = route_map_get(name);
74 action_type = (action[0] == 'p') ? RMAP_PERMIT : RMAP_DENY;
75 rmi = route_map_index_get(rm, action_type, sequence);
76 VTY_PUSH_CONTEXT(RMAP_NODE, rmi);
77 }
78
79 return rv;
80 }
81
82 DEFPY(
83 no_route_map_all, no_route_map_all_cmd,
84 "no route-map WORD$name",
85 NO_STR
86 ROUTE_MAP_CMD_STR)
87 {
88 char xpath[XPATH_MAXLEN];
89
90 snprintf(xpath, sizeof(xpath),
91 "/frr-route-map:lib/route-map[name='%s']", name);
92 nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
93
94 return nb_cli_apply_changes(vty, NULL);
95 }
96
97 DEFPY(
98 no_route_map, no_route_map_cmd,
99 "no route-map WORD$name <deny|permit>$action (1-65535)$sequence",
100 NO_STR
101 ROUTE_MAP_CMD_STR
102 ROUTE_MAP_OP_CMD_STR
103 ROUTE_MAP_SEQUENCE_CMD_STR)
104 {
105 char xpath[XPATH_MAXLEN];
106
107 snprintf(xpath, sizeof(xpath),
108 "/frr-route-map:lib/route-map[name='%s']/entry[sequence='%lu']",
109 name, sequence);
110 nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
111
112 return nb_cli_apply_changes(vty, NULL);
113 }
114
115 void route_map_instance_show(struct vty *vty, struct lyd_node *dnode,
116 bool show_defaults)
117 {
118 const struct route_map_rule *rmr;
119 const struct route_map_index *rmi;
120 const char *name = yang_dnode_get_string(dnode, "../name");
121 const char *action = yang_dnode_get_string(dnode, "./action");
122 const char *sequence = yang_dnode_get_string(dnode, "./sequence");
123
124 vty_out(vty, "route-map %s %s %s\n", name, action, sequence);
125
126 rmi = nb_running_get_entry(dnode, NULL, false);
127 if (rmi == NULL) {
128 /*
129 * We can't have outdated rules if route map hasn't
130 * been created yet.
131 */
132 return;
133 }
134
135 #define SKIP_RULE(name) if (strcmp((name), rmr->cmd->str) == 0) continue
136
137 /* Print route map `match` for old CLI users. */
138 for (rmr = rmi->match_list.head; rmr; rmr = rmr->next) {
139 /* Skip all matches implemented by northbound. */
140 SKIP_RULE("interface");
141 SKIP_RULE("ip address");
142 SKIP_RULE("ip address prefix-list");
143 SKIP_RULE("ip next-hop");
144 SKIP_RULE("ip next-hop prefix-list");
145 SKIP_RULE("ip next-hop type");
146 SKIP_RULE("ipv6 address");
147 SKIP_RULE("ipv6 address prefix-list");
148 SKIP_RULE("ipv6 next-hop type");
149 SKIP_RULE("metric");
150 SKIP_RULE("tag");
151
152 vty_out(vty, " match %s %s\n", rmr->cmd->str,
153 rmr->rule_str ? rmr->rule_str : "");
154 }
155
156 /* Print route map `set` for old CLI users. */
157 for (rmr = rmi->set_list.head; rmr; rmr = rmr->next) {
158 /* Skip all sets implemented by northbound. */
159 SKIP_RULE("metric");
160 SKIP_RULE("tag");
161
162 vty_out(vty, " set %s %s\n", rmr->cmd->str,
163 rmr->rule_str ? rmr->rule_str : "");
164 }
165
166 #undef SKIP_RULE
167 }
168
169 void route_map_instance_show_end(struct vty *vty, struct lyd_node *dnode)
170 {
171 vty_out(vty, "!\n");
172 }
173
174 DEFPY(
175 match_interface, match_interface_cmd,
176 "match interface IFNAME",
177 MATCH_STR
178 "Match first hop interface of route\n"
179 INTERFACE_STR)
180 {
181 const char *xpath = "./match-condition[condition='interface']";
182 char xpath_value[XPATH_MAXLEN];
183
184 nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
185 snprintf(xpath_value, sizeof(xpath_value), "%s/interface", xpath);
186 nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, ifname);
187
188 return nb_cli_apply_changes(vty, NULL);
189 }
190
191 DEFPY(
192 no_match_interface, no_match_interface_cmd,
193 "no match interface [IFNAME]",
194 NO_STR
195 MATCH_STR
196 "Match first hop interface of route\n"
197 INTERFACE_STR)
198 {
199 const char *xpath = "./match-condition[condition='interface']";
200
201 nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
202
203 return nb_cli_apply_changes(vty, NULL);
204 }
205
206 DEFPY(
207 match_ip_address, match_ip_address_cmd,
208 "match ip address <(1-199)$acll|(1300-2699)$aclh|WORD$name>",
209 MATCH_STR
210 IP_STR
211 "Match address of route\n"
212 "IP access-list number\n"
213 "IP access-list number (expanded range)\n"
214 "IP Access-list name\n")
215 {
216 const char *xpath = "./match-condition[condition='ipv4-address-list']";
217 char xpath_value[XPATH_MAXLEN + 32];
218 int acln = acll ? acll : aclh;
219
220 nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
221 if (name) {
222 snprintf(xpath_value, sizeof(xpath_value), "%s/list-name",
223 xpath);
224 nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, name);
225 } else /* if (acll || aclh) */ {
226 if ((acln >= 1 && acln <= 99)
227 || (acln >= 1300 && acln <= 1999)) {
228 snprintf(xpath_value, sizeof(xpath_value),
229 "%s/access-list-num", xpath);
230 } else {
231 /*
232 * if ((acln >= 100 && acln <= 199)
233 * || (acln >= 2000 && acln <= 2699))
234 */
235 snprintf(xpath_value, sizeof(xpath_value),
236 "%s/access-list-num-extended", xpath);
237 }
238 nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY,
239 acll_str ? acll_str : aclh_str);
240 }
241
242 return nb_cli_apply_changes(vty, NULL);
243 }
244
245 DEFPY(
246 no_match_ip_address, no_match_ip_address_cmd,
247 "no match ip address [<(1-199)|(1300-2699)|WORD>]",
248 NO_STR
249 MATCH_STR
250 IP_STR
251 "Match address of route\n"
252 "IP access-list number\n"
253 "IP access-list number (expanded range)\n"
254 "IP Access-list name\n")
255 {
256 const char *xpath = "./match-condition[condition='ipv4-address-list']";
257
258 nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
259
260 return nb_cli_apply_changes(vty, NULL);
261 }
262
263 DEFPY(
264 match_ip_address_prefix_list,
265 match_ip_address_prefix_list_cmd,
266 "match ip address prefix-list WORD$name",
267 MATCH_STR
268 IP_STR
269 "Match address of route\n"
270 "Match entries of prefix-lists\n"
271 "IP prefix-list name\n")
272 {
273 const char *xpath = "./match-condition[condition='ipv4-prefix-list']";
274 char xpath_value[XPATH_MAXLEN];
275
276 nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
277 snprintf(xpath_value, sizeof(xpath_value), "%s/list-name", xpath);
278 nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, name);
279
280 return nb_cli_apply_changes(vty, NULL);
281 }
282
283 DEFPY(
284 no_match_ip_address_prefix_list, no_match_ip_address_prefix_list_cmd,
285 "no match ip address prefix-list [WORD]",
286 NO_STR
287 MATCH_STR
288 IP_STR
289 "Match address of route\n"
290 "Match entries of prefix-lists\n"
291 "IP prefix-list name\n")
292 {
293 const char *xpath = "./match-condition[condition='ipv4-prefix-list']";
294
295 nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
296
297 return nb_cli_apply_changes(vty, NULL);
298 }
299
300 DEFPY(
301 match_ip_next_hop, match_ip_next_hop_cmd,
302 "match ip next-hop <(1-199)$acll|(1300-2699)$aclh|WORD$name>",
303 MATCH_STR
304 IP_STR
305 "Match next-hop address of route\n"
306 "IP access-list number\n"
307 "IP access-list number (expanded range)\n"
308 "IP Access-list name\n")
309 {
310 const char *xpath = "./match-condition[condition='ipv4-next-hop-list']";
311 char xpath_value[XPATH_MAXLEN + 32];
312 int acln = acll ? acll : aclh;
313
314 nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
315 if (name) {
316 snprintf(xpath_value, sizeof(xpath_value), "%s/list-name",
317 xpath);
318 nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, name);
319 } else /* if (acll || aclh) */ {
320 if ((acln >= 1 && acln <= 99)
321 || (acln >= 1300 && acln <= 1999)) {
322 snprintf(xpath_value, sizeof(xpath_value),
323 "%s/access-list-num", xpath);
324 } else {
325 /*
326 * if ((acln >= 100 && acln <= 199)
327 * || (acln >= 2000 && acln <= 2699))
328 */
329 snprintf(xpath_value, sizeof(xpath_value),
330 "%s/access-list-num-extended", xpath);
331 }
332 nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY,
333 acll_str ? acll_str : aclh_str);
334 }
335
336 return nb_cli_apply_changes(vty, NULL);
337 }
338
339 DEFPY(
340 no_match_ip_next_hop, no_match_ip_next_hop_cmd,
341 "no match ip next-hop [<(1-199)|(1300-2699)|WORD>]",
342 NO_STR
343 MATCH_STR
344 IP_STR
345 "Match address of route\n"
346 "IP access-list number\n"
347 "IP access-list number (expanded range)\n"
348 "IP Access-list name\n")
349 {
350 const char *xpath = "./match-condition[condition='ipv4-next-hop-list']";
351
352 nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
353
354 return nb_cli_apply_changes(vty, NULL);
355 }
356
357 DEFPY(
358 match_ip_next_hop_prefix_list,
359 match_ip_next_hop_prefix_list_cmd,
360 "match ip next-hop prefix-list WORD$name",
361 MATCH_STR
362 IP_STR
363 "Match next-hop address of route\n"
364 "Match entries of prefix-lists\n"
365 "IP prefix-list name\n")
366 {
367 const char *xpath =
368 "./match-condition[condition='ipv4-next-hop-prefix-list']";
369 char xpath_value[XPATH_MAXLEN];
370
371 nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
372 snprintf(xpath_value, sizeof(xpath_value), "%s/list-name", xpath);
373 nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, name);
374
375 return nb_cli_apply_changes(vty, NULL);
376 }
377
378 DEFPY(
379 no_match_ip_next_hop_prefix_list,
380 no_match_ip_next_hop_prefix_list_cmd,
381 "no match ip next-hop prefix-list [WORD]",
382 NO_STR
383 MATCH_STR
384 IP_STR
385 "Match next-hop address of route\n"
386 "Match entries of prefix-lists\n"
387 "IP prefix-list name\n")
388 {
389 const char *xpath =
390 "./match-condition[condition='ipv4-next-hop-prefix-list']";
391
392 nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
393
394 return nb_cli_apply_changes(vty, NULL);
395 }
396
397 DEFPY(
398 match_ip_next_hop_type, match_ip_next_hop_type_cmd,
399 "match ip next-hop type <blackhole>$type",
400 MATCH_STR
401 IP_STR
402 "Match next-hop address of route\n"
403 "Match entries by type\n"
404 "Blackhole\n")
405 {
406 const char *xpath = "./match-condition[condition='ipv4-next-hop-type']";
407 char xpath_value[XPATH_MAXLEN];
408
409 nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
410 snprintf(xpath_value, sizeof(xpath_value), "%s/ipv4-next-hop-type",
411 xpath);
412 nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, type);
413
414 return nb_cli_apply_changes(vty, NULL);
415 }
416
417 DEFPY(
418 no_match_ip_next_hop_type, no_match_ip_next_hop_type_cmd,
419 "no match ip next-hop type [<blackhole>]",
420 NO_STR MATCH_STR IP_STR
421 "Match next-hop address of route\n"
422 "Match entries by type\n"
423 "Blackhole\n")
424 {
425 const char *xpath = "./match-condition[condition='ipv4-next-hop-type']";
426
427 nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
428
429 return nb_cli_apply_changes(vty, NULL);
430 }
431
432 DEFPY(
433 match_ipv6_address, match_ipv6_address_cmd,
434 "match ipv6 address WORD$name",
435 MATCH_STR
436 IPV6_STR
437 "Match IPv6 address of route\n"
438 "IPv6 access-list name\n")
439 {
440 const char *xpath = "./match-condition[condition='ipv6-address-list']";
441 char xpath_value[XPATH_MAXLEN];
442
443 nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
444 snprintf(xpath_value, sizeof(xpath_value), "%s/list-name", xpath);
445 nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, name);
446
447 return nb_cli_apply_changes(vty, NULL);
448 }
449
450 DEFPY(
451 no_match_ipv6_address, no_match_ipv6_address_cmd,
452 "no match ipv6 address [WORD]",
453 NO_STR
454 MATCH_STR
455 IPV6_STR
456 "Match IPv6 address of route\n"
457 "IPv6 access-list name\n")
458 {
459 const char *xpath = "./match-condition[condition='ipv6-address-list']";
460
461 nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
462
463 return nb_cli_apply_changes(vty, NULL);
464 }
465
466 DEFPY(
467 match_ipv6_address_prefix_list, match_ipv6_address_prefix_list_cmd,
468 "match ipv6 address prefix-list WORD$name",
469 MATCH_STR
470 IPV6_STR
471 "Match address of route\n"
472 "Match entries of prefix-lists\n"
473 "IP prefix-list name\n")
474 {
475 const char *xpath = "./match-condition[condition='ipv6-prefix-list']";
476 char xpath_value[XPATH_MAXLEN];
477
478 nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
479 snprintf(xpath_value, sizeof(xpath_value), "%s/list-name", xpath);
480 nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, name);
481
482 return nb_cli_apply_changes(vty, NULL);
483 }
484
485 DEFPY(
486 no_match_ipv6_address_prefix_list,
487 no_match_ipv6_address_prefix_list_cmd,
488 "no match ipv6 address prefix-list [WORD]",
489 NO_STR
490 MATCH_STR
491 IPV6_STR
492 "Match address of route\n"
493 "Match entries of prefix-lists\n"
494 "IP prefix-list name\n")
495 {
496 const char *xpath = "./match-condition[condition='ipv6-prefix-list']";
497
498 nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
499
500 return nb_cli_apply_changes(vty, NULL);
501 }
502
503 DEFPY(
504 match_ipv6_next_hop_type, match_ipv6_next_hop_type_cmd,
505 "match ipv6 next-hop type <blackhole>$type",
506 MATCH_STR IPV6_STR
507 "Match next-hop address of route\n"
508 "Match entries by type\n"
509 "Blackhole\n")
510 {
511 const char *xpath = "./match-condition[condition='ipv6-next-hop-type']";
512 char xpath_value[XPATH_MAXLEN];
513
514 nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
515 snprintf(xpath_value, sizeof(xpath_value), "%s/ipv6-next-hop-type",
516 xpath);
517 nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, type);
518
519 return nb_cli_apply_changes(vty, NULL);
520 }
521
522 DEFPY(
523 no_match_ipv6_next_hop_type, no_match_ipv6_next_hop_type_cmd,
524 "no match ipv6 next-hop type [<blackhole>]",
525 NO_STR MATCH_STR IPV6_STR
526 "Match address of route\n"
527 "Match entries by type\n"
528 "Blackhole\n")
529 {
530 const char *xpath = "./match-condition[condition='ipv6-next-hop-type']";
531
532 nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
533
534 return nb_cli_apply_changes(vty, NULL);
535 }
536
537 DEFPY(
538 match_metric, match_metric_cmd,
539 "match metric (0-4294967295)$metric",
540 MATCH_STR
541 "Match metric of route\n"
542 "Metric value\n")
543 {
544 const char *xpath = "./match-condition[condition='metric']";
545 char xpath_value[XPATH_MAXLEN];
546
547 nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
548 snprintf(xpath_value, sizeof(xpath_value), "%s/metric", xpath);
549 nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, metric_str);
550
551 return nb_cli_apply_changes(vty, NULL);
552 }
553
554 DEFPY(
555 no_match_metric, no_match_metric_cmd,
556 "no match metric [(0-4294967295)]",
557 NO_STR
558 MATCH_STR
559 "Match metric of route\n"
560 "Metric value\n")
561 {
562 const char *xpath = "./match-condition[condition='metric']";
563
564 nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
565
566 return nb_cli_apply_changes(vty, NULL);
567 }
568
569 DEFPY(
570 match_tag, match_tag_cmd,
571 "match tag (1-4294967295)$tag",
572 MATCH_STR
573 "Match tag of route\n"
574 "Tag value\n")
575 {
576 const char *xpath = "./match-condition[condition='tag']";
577 char xpath_value[XPATH_MAXLEN];
578
579 nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
580 snprintf(xpath_value, sizeof(xpath_value), "%s/tag", xpath);
581 nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, tag_str);
582
583 return nb_cli_apply_changes(vty, NULL);
584 }
585
586 DEFPY(
587 no_match_tag, no_match_tag_cmd,
588 "no match tag [(1-4294967295)]",
589 NO_STR
590 MATCH_STR
591 "Match tag of route\n"
592 "Tag value\n")
593 {
594 const char *xpath = "./match-condition[condition='tag']";
595
596 nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
597
598 return nb_cli_apply_changes(vty, NULL);
599 }
600
601 void route_map_condition_show(struct vty *vty, struct lyd_node *dnode,
602 bool show_defaults)
603 {
604 int condition = yang_dnode_get_enum(dnode, "./condition");
605 struct lyd_node *ln;
606 const char *acl;
607
608 switch (condition) {
609 case 0: /* interface */
610 vty_out(vty, " match interface %s\n",
611 yang_dnode_get_string(dnode, "./interface"));
612 break;
613 case 1: /* ipv4-address-list */
614 case 3: /* ipv4-next-hop-list */
615 acl = NULL;
616 if ((ln = yang_dnode_get(dnode, "./list-name")) != NULL)
617 acl = yang_dnode_get_string(ln, NULL);
618 else if ((ln = yang_dnode_get(dnode, "./access-list-num"))
619 != NULL)
620 acl = yang_dnode_get_string(ln, NULL);
621 else if ((ln = yang_dnode_get(dnode,
622 "./access-list-num-extended"))
623 != NULL)
624 acl = yang_dnode_get_string(ln, NULL);
625
626 assert(acl);
627
628 switch (condition) {
629 case 1:
630 vty_out(vty, " match ip address %s\n", acl);
631 break;
632 case 3:
633 vty_out(vty, " match ip next-hop %s\n", acl);
634 break;
635 }
636 break;
637 case 2: /* ipv4-prefix-list */
638 vty_out(vty, " match ip address prefix-list %s\n",
639 yang_dnode_get_string(dnode, "./list-name"));
640 break;
641 case 4: /* ipv4-next-hop-prefix-list */
642 vty_out(vty, " match ip next-hop prefix-list %s\n",
643 yang_dnode_get_string(dnode, "./list-name"));
644 break;
645 case 5: /* ipv4-next-hop-type */
646 vty_out(vty, " match ip next-hop type %s\n",
647 yang_dnode_get_string(dnode, "./ipv4-next-hop-type"));
648 break;
649 case 6: /* ipv6-address-list */
650 vty_out(vty, " match ipv6 address %s\n",
651 yang_dnode_get_string(dnode, "./list-name"));
652 break;
653 case 7: /* ipv6-prefix-list */
654 vty_out(vty, " match ipv6 address prefix-list %s\n",
655 yang_dnode_get_string(dnode, "./list-name"));
656 break;
657 case 8: /* ipv6-next-hop-type */
658 vty_out(vty, " match ipv6 next-hop type %s\n",
659 yang_dnode_get_string(dnode, "./ipv6-next-hop-type"));
660 break;
661 case 9: /* metric */
662 vty_out(vty, " match metric %s\n",
663 yang_dnode_get_string(dnode, "./metric"));
664 break;
665 case 10: /* tag */
666 vty_out(vty, " match tag %s\n",
667 yang_dnode_get_string(dnode, "./tag"));
668 break;
669 case 100:
670 /* NOTHING: custom field, should be handled by daemon. */
671 break;
672 }
673 }
674
675 DEFPY(
676 set_ip_nexthop, set_ip_nexthop_cmd,
677 "set ip next-hop A.B.C.D$addr",
678 SET_STR
679 IP_STR
680 "Next hop address\n"
681 "IP address of next hop\n")
682 {
683 const char *xpath = "./set-action[action='ipv4-next-hop']";
684 char xpath_value[XPATH_MAXLEN];
685
686 nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
687 snprintf(xpath_value, sizeof(xpath_value), "%s/ipv4-address", xpath);
688 nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, addr_str);
689
690 return nb_cli_apply_changes(vty, NULL);
691 }
692
693 DEFPY(
694 no_set_ip_nexthop, no_set_ip_nexthop_cmd,
695 "no set ip next-hop [A.B.C.D]",
696 NO_STR
697 SET_STR
698 IP_STR
699 "Next hop address\n"
700 "IP address of next hop\n")
701 {
702 const char *xpath = "./set-action[action='ipv4-next-hop']";
703
704 nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
705
706 return nb_cli_apply_changes(vty, NULL);
707 }
708
709 DEFPY(
710 set_ipv6_nexthop_local, set_ipv6_nexthop_local_cmd,
711 "set ipv6 next-hop local X:X::X:X$addr",
712 SET_STR
713 IPV6_STR
714 "IPv6 next-hop address\n"
715 "IPv6 local address\n"
716 "IPv6 address of next hop\n")
717 {
718 const char *xpath = "./set-action[action='ipv6-next-hop']";
719 char xpath_value[XPATH_MAXLEN];
720
721 nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
722 snprintf(xpath_value, sizeof(xpath_value), "%s/ipv6-address", xpath);
723 nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, addr_str);
724
725 return nb_cli_apply_changes(vty, NULL);
726 }
727
728 DEFPY(
729 no_set_ipv6_nexthop_local, no_set_ipv6_nexthop_local_cmd,
730 "no set ipv6 next-hop local [X:X::X:X]",
731 NO_STR
732 SET_STR
733 IPV6_STR
734 "IPv6 next-hop address\n"
735 "IPv6 local address\n"
736 "IPv6 address of next hop\n")
737 {
738 const char *xpath = "./set-action[action='ipv6-next-hop']";
739
740 nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
741
742 return nb_cli_apply_changes(vty, NULL);
743 }
744
745 DEFPY(
746 set_metric, set_metric_cmd,
747 "set metric <(0-4294967295)$metric|rtt$rtt|+rtt$artt|-rtt$srtt|+metric$ametric|-metric$smetric>",
748 SET_STR
749 "Metric value for destination routing protocol\n"
750 "Metric value\n"
751 "Assign round trip time\n"
752 "Add round trip time\n"
753 "Subtract round trip time\n"
754 "Add metric\n"
755 "Subtract metric\n")
756 {
757 const char *xpath = "./set-action[action='metric']";
758 char xpath_value[XPATH_MAXLEN];
759 char value[64];
760
761 nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
762 if (rtt) {
763 snprintf(xpath_value, sizeof(xpath_value),
764 "%s/use-round-trip-time", xpath);
765 snprintf(value, sizeof(value), "true");
766 } else if (artt) {
767 snprintf(xpath_value, sizeof(xpath_value),
768 "%s/add-round-trip-time", xpath);
769 snprintf(value, sizeof(value), "true");
770 } else if (srtt) {
771 snprintf(xpath_value, sizeof(xpath_value),
772 "%s/subtract-round-trip-time", xpath);
773 snprintf(value, sizeof(value), "true");
774 } else if (ametric) {
775 snprintf(xpath_value, sizeof(xpath_value), "%s/add-metric",
776 xpath);
777 snprintf(value, sizeof(value), "true");
778 } else if (smetric) {
779 snprintf(xpath_value, sizeof(xpath_value), "%s/subtract-metric",
780 xpath);
781 snprintf(value, sizeof(value), "true");
782 } else {
783 snprintf(xpath_value, sizeof(xpath_value), "%s/value", xpath);
784 snprintf(value, sizeof(value), "%lu", metric);
785 }
786 nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, value);
787
788 return nb_cli_apply_changes(vty, NULL);
789 }
790
791 DEFPY(
792 no_set_metric, no_set_metric_cmd,
793 "no set metric [(0-4294967295)]",
794 NO_STR
795 SET_STR
796 "Metric value for destination routing protocol\n"
797 "Metric value\n")
798 {
799 const char *xpath = "./set-action[action='metric']";
800
801 nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
802 return nb_cli_apply_changes(vty, NULL);
803 }
804
805 DEFPY(
806 set_tag, set_tag_cmd,
807 "set tag (1-4294967295)$tag",
808 SET_STR
809 "Tag value for routing protocol\n"
810 "Tag value\n")
811 {
812 const char *xpath = "./set-action[action='tag']";
813 char xpath_value[XPATH_MAXLEN];
814
815 nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
816 snprintf(xpath_value, sizeof(xpath_value), "%s/tag", xpath);
817 nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, tag_str);
818
819 return nb_cli_apply_changes(vty, NULL);
820 }
821
822 DEFPY(
823 no_set_tag, no_set_tag_cmd,
824 "no set tag [(1-4294967295)]",
825 NO_STR
826 SET_STR
827 "Tag value for routing protocol\n"
828 "Tag value\n")
829 {
830 const char *xpath = "./set-action[action='tag']";
831
832 nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
833
834 return nb_cli_apply_changes(vty, NULL);
835 }
836
837 void route_map_action_show(struct vty *vty, struct lyd_node *dnode,
838 bool show_defaults)
839 {
840 int action = yang_dnode_get_enum(dnode, "./action");
841
842 switch (action) {
843 case 0: /* ipv4-next-hop */
844 vty_out(vty, " set ip next-hop %s\n",
845 yang_dnode_get_string(dnode, "./ipv4-address"));
846 break;
847 case 1: /* ipv6-next-hop */
848 vty_out(vty, " set ipv6 next-hop local %s\n",
849 yang_dnode_get_string(dnode, "./ipv6-address"));
850 break;
851 case 2: /* metric */
852 if (yang_dnode_get(dnode, "./use-round-trip-time")) {
853 vty_out(vty, " set metric rtt\n");
854 } else if (yang_dnode_get(dnode, "./add-round-trip-time")) {
855 vty_out(vty, " set metric +rtt\n");
856 } else if (yang_dnode_get(dnode, "./subtract-round-trip-time")) {
857 vty_out(vty, " set metric -rtt\n");
858 } else if (yang_dnode_get(dnode, "./add-metric")) {
859 vty_out(vty, " set metric +metric\n");
860 } else if (yang_dnode_get(dnode, "./subtract-metric")) {
861 vty_out(vty, " set metric -metric\n");
862 } else {
863 vty_out(vty, " set metric %s\n",
864 yang_dnode_get_string(dnode, "./value"));
865 }
866 break;
867 case 3: /* tag */
868 vty_out(vty, " set tag %s\n",
869 yang_dnode_get_string(dnode, "./tag"));
870 break;
871 case 100:
872 /* NOTHING: custom field, should be handled by daemon. */
873 break;
874 }
875 }
876
877 DEFPY(
878 rmap_onmatch_next, rmap_onmatch_next_cmd,
879 "on-match next",
880 "Exit policy on matches\n"
881 "Next clause\n")
882 {
883 nb_cli_enqueue_change(vty, "./exit-policy", NB_OP_MODIFY, "next");
884
885 return nb_cli_apply_changes(vty, NULL);
886 }
887
888 DEFPY(
889 no_rmap_onmatch_next,
890 no_rmap_onmatch_next_cmd,
891 "no on-match next",
892 NO_STR
893 "Exit policy on matches\n"
894 "Next clause\n")
895 {
896 nb_cli_enqueue_change(vty, "./exit-policy", NB_OP_DESTROY, NULL);
897
898 return nb_cli_apply_changes(vty, NULL);
899 }
900
901 DEFPY(
902 rmap_onmatch_goto, rmap_onmatch_goto_cmd,
903 "on-match goto (1-65535)$rm_num",
904 "Exit policy on matches\n"
905 "Goto Clause number\n"
906 "Number\n")
907 {
908 nb_cli_enqueue_change(vty, "./exit-policy", NB_OP_MODIFY, "goto");
909 nb_cli_enqueue_change(vty, "./goto-value", NB_OP_MODIFY, rm_num_str);
910
911 return nb_cli_apply_changes(vty, NULL);
912 }
913
914 DEFPY(
915 no_rmap_onmatch_goto, no_rmap_onmatch_goto_cmd,
916 "no on-match goto",
917 NO_STR
918 "Exit policy on matches\n"
919 "Goto Clause number\n")
920 {
921 nb_cli_enqueue_change(vty, "./exit-policy", NB_OP_DESTROY, NULL);
922
923 return nb_cli_apply_changes(vty, NULL);
924 }
925
926 /* Cisco/GNU Zebra compatibility aliases */
927 ALIAS(
928 rmap_onmatch_goto, rmap_continue_cmd,
929 "continue (1-65535)$rm_num",
930 "Continue on a different entry within the route-map\n"
931 "Route-map entry sequence number\n")
932
933 ALIAS(
934 no_rmap_onmatch_goto, no_rmap_continue_cmd,
935 "no continue [(1-65535)]",
936 NO_STR
937 "Continue on a different entry within the route-map\n"
938 "Route-map entry sequence number\n")
939
940 void route_map_exit_policy_show(struct vty *vty, struct lyd_node *dnode,
941 bool show_defaults)
942 {
943 int exit_policy = yang_dnode_get_enum(dnode, NULL);
944
945 switch (exit_policy) {
946 case 0: /* permit-or-deny */
947 /* NOTHING: default option. */
948 break;
949 case 1: /* next */
950 vty_out(vty, " on-match next\n");
951 break;
952 case 2: /* goto */
953 vty_out(vty, " on-match goto %s\n",
954 yang_dnode_get_string(dnode, "../goto-value"));
955 break;
956 }
957 }
958
959 DEFPY(
960 rmap_call, rmap_call_cmd,
961 "call WORD$name",
962 "Jump to another Route-Map after match+set\n"
963 "Target route-map name\n")
964 {
965 nb_cli_enqueue_change(vty, "./call", NB_OP_MODIFY, name);
966
967 return nb_cli_apply_changes(vty, NULL);
968 }
969
970 DEFPY(
971 no_rmap_call, no_rmap_call_cmd,
972 "no call",
973 NO_STR
974 "Jump to another Route-Map after match+set\n")
975 {
976 nb_cli_enqueue_change(vty, "./call", NB_OP_DESTROY, NULL);
977
978 return nb_cli_apply_changes(vty, NULL);
979 }
980
981 void route_map_call_show(struct vty *vty, struct lyd_node *dnode,
982 bool show_defaults)
983 {
984 vty_out(vty, " call %s\n", yang_dnode_get_string(dnode, NULL));
985 }
986
987 DEFPY(
988 rmap_description, rmap_description_cmd,
989 "description LINE...",
990 "Route-map comment\n"
991 "Comment describing this route-map rule\n")
992 {
993 char *desc;
994 int rv;
995
996 desc = argv_concat(argv, argc, 1);
997 nb_cli_enqueue_change(vty, "./description", NB_OP_MODIFY, desc);
998 rv = nb_cli_apply_changes(vty, NULL);
999 XFREE(MTYPE_TMP, desc);
1000
1001 return rv;
1002 }
1003
1004 DEFUN (no_rmap_description,
1005 no_rmap_description_cmd,
1006 "no description",
1007 NO_STR
1008 "Route-map comment\n")
1009 {
1010 nb_cli_enqueue_change(vty, "./description", NB_OP_DESTROY, NULL);
1011
1012 return nb_cli_apply_changes(vty, NULL);
1013 }
1014
1015 void route_map_description_show(struct vty *vty, struct lyd_node *dnode,
1016 bool show_defaults)
1017 {
1018 vty_out(vty, " description %s\n", yang_dnode_get_string(dnode, NULL));
1019 }
1020
1021 static int route_map_config_write(struct vty *vty)
1022 {
1023 struct lyd_node *dnode;
1024 int written = 0;
1025
1026 dnode = yang_dnode_get(running_config->dnode,
1027 "/frr-route-map:lib");
1028 if (dnode) {
1029 nb_cli_show_dnode_cmds(vty, dnode, false);
1030 written = 1;
1031 }
1032
1033 return written;
1034 }
1035
1036 /* Route map node structure. */
1037 static struct cmd_node rmap_node = {RMAP_NODE, "%s(config-route-map)# ", 1};
1038
1039 static void rmap_autocomplete(vector comps, struct cmd_token *token)
1040 {
1041 struct route_map *map;
1042
1043 for (map = route_map_master.head; map; map = map->next)
1044 vector_set(comps, XSTRDUP(MTYPE_COMPLETION, map->name));
1045 }
1046
1047 static const struct cmd_variable_handler rmap_var_handlers[] = {
1048 {.varname = "route_map", .completions = rmap_autocomplete},
1049 {.tokenname = "ROUTEMAP_NAME", .completions = rmap_autocomplete},
1050 {.tokenname = "RMAP_NAME", .completions = rmap_autocomplete},
1051 {.completions = NULL}
1052 };
1053
1054 void route_map_cli_init(void)
1055 {
1056 /* Auto complete handler. */
1057 cmd_variable_handler_register(rmap_var_handlers);
1058
1059 /* CLI commands. */
1060 install_node(&rmap_node, route_map_config_write);
1061 install_default(RMAP_NODE);
1062 install_element(CONFIG_NODE, &route_map_cmd);
1063 install_element(CONFIG_NODE, &no_route_map_cmd);
1064 install_element(CONFIG_NODE, &no_route_map_all_cmd);
1065
1066 /* Install the on-match stuff */
1067 install_element(RMAP_NODE, &route_map_cmd);
1068 install_element(RMAP_NODE, &rmap_onmatch_next_cmd);
1069 install_element(RMAP_NODE, &no_rmap_onmatch_next_cmd);
1070 install_element(RMAP_NODE, &rmap_onmatch_goto_cmd);
1071 install_element(RMAP_NODE, &no_rmap_onmatch_goto_cmd);
1072 install_element(RMAP_NODE, &rmap_continue_cmd);
1073 install_element(RMAP_NODE, &no_rmap_continue_cmd);
1074
1075 /* Install the call stuff. */
1076 install_element(RMAP_NODE, &rmap_call_cmd);
1077 install_element(RMAP_NODE, &no_rmap_call_cmd);
1078
1079 /* Install description commands. */
1080 install_element(RMAP_NODE, &rmap_description_cmd);
1081 install_element(RMAP_NODE, &no_rmap_description_cmd);
1082
1083 /* Install 'match' commands. */
1084 install_element(RMAP_NODE, &match_interface_cmd);
1085 install_element(RMAP_NODE, &no_match_interface_cmd);
1086
1087 install_element(RMAP_NODE, &match_ip_address_cmd);
1088 install_element(RMAP_NODE, &no_match_ip_address_cmd);
1089
1090 install_element(RMAP_NODE, &match_ip_address_prefix_list_cmd);
1091 install_element(RMAP_NODE, &no_match_ip_address_prefix_list_cmd);
1092
1093 install_element(RMAP_NODE, &match_ip_next_hop_cmd);
1094 install_element(RMAP_NODE, &no_match_ip_next_hop_cmd);
1095
1096 install_element(RMAP_NODE, &match_ip_next_hop_prefix_list_cmd);
1097 install_element(RMAP_NODE, &no_match_ip_next_hop_prefix_list_cmd);
1098
1099 install_element(RMAP_NODE, &match_ip_next_hop_type_cmd);
1100 install_element(RMAP_NODE, &no_match_ip_next_hop_type_cmd);
1101
1102 install_element(RMAP_NODE, &match_ipv6_address_cmd);
1103 install_element(RMAP_NODE, &no_match_ipv6_address_cmd);
1104
1105 install_element(RMAP_NODE, &match_ipv6_address_prefix_list_cmd);
1106 install_element(RMAP_NODE, &no_match_ipv6_address_prefix_list_cmd);
1107
1108 install_element(RMAP_NODE, &match_ipv6_next_hop_type_cmd);
1109 install_element(RMAP_NODE, &no_match_ipv6_next_hop_type_cmd);
1110
1111 install_element(RMAP_NODE, &match_metric_cmd);
1112 install_element(RMAP_NODE, &no_match_metric_cmd);
1113
1114 install_element(RMAP_NODE, &match_tag_cmd);
1115 install_element(RMAP_NODE, &no_match_tag_cmd);
1116
1117 /* Install 'set' commands. */
1118 install_element(RMAP_NODE, &set_ip_nexthop_cmd);
1119 install_element(RMAP_NODE, &no_set_ip_nexthop_cmd);
1120
1121 install_element(RMAP_NODE, &set_ipv6_nexthop_local_cmd);
1122 install_element(RMAP_NODE, &no_set_ipv6_nexthop_local_cmd);
1123
1124 install_element(RMAP_NODE, &set_metric_cmd);
1125 install_element(RMAP_NODE, &no_set_metric_cmd);
1126
1127 install_element(RMAP_NODE, &set_tag_cmd);
1128 install_element(RMAP_NODE, &no_set_tag_cmd);
1129 }