3 * Copyright (C) 2018 Cumulus Networks, Inc.
6 * FRR is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2, or (at your option) any
11 * FRR is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; see the file COPYING; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
27 #include "nexthop_group.h"
28 #include "nexthop_group_private.h"
33 #include "pbrd/pbr_nht.h"
34 #include "pbrd/pbr_map.h"
35 #include "pbrd/pbr_zebra.h"
36 #include "pbrd/pbr_vty.h"
37 #include "pbrd/pbr_debug.h"
38 #ifndef VTYSH_EXTRACT_PL
39 #include "pbrd/pbr_vty_clippy.c"
42 DEFUN_NOSH(pbr_map
, pbr_map_cmd
, "pbr-map PBRMAP seq (1-700)",
43 "Create pbr-map or enter pbr-map command mode\n"
44 "The name of the PBR MAP\n"
45 "Sequence to insert in existing pbr-map entry\n"
48 const char *pbrm_name
= argv
[1]->arg
;
49 uint32_t seqno
= atoi(argv
[3]->arg
);
50 struct pbr_map_sequence
*pbrms
;
52 pbrms
= pbrms_get(pbrm_name
, seqno
);
53 VTY_PUSH_CONTEXT(PBRMAP_NODE
, pbrms
);
58 DEFUN_NOSH(no_pbr_map
, no_pbr_map_cmd
, "no pbr-map PBRMAP [seq (1-700)]",
61 "The name of the PBR MAP\n"
62 "Sequence to delete from existing pbr-map entry\n"
65 const char *pbrm_name
= argv
[2]->arg
;
67 struct pbr_map
*pbrm
= pbrm_find(pbrm_name
);
68 struct pbr_map_sequence
*pbrms
;
69 struct listnode
*node
, *next_node
;
72 seqno
= atoi(argv
[4]->arg
);
75 vty_out(vty
, "pbr-map %s not found\n", pbrm_name
);
79 for (ALL_LIST_ELEMENTS(pbrm
->seqnumbers
, node
, next_node
, pbrms
)) {
80 if (seqno
&& pbrms
->seqno
!= seqno
)
83 pbr_map_delete(pbrms
);
89 DEFPY(pbr_set_table_range
,
90 pbr_set_table_range_cmd
,
91 "[no] pbr table range (10000-4294966272)$lb (10000-4294966272)$ub",
94 "Set table ID range\n"
95 "Set table ID range\n"
96 "Lower bound for table ID range\n"
97 "Upper bound for table ID range\n")
99 /* upper bound is 2^32 - 2^10 */
100 int ret
= CMD_WARNING
;
101 const int minrange
= 1000;
103 /* validate given bounds */
105 vty_out(vty
, "%% Lower bound must be less than upper bound\n");
106 else if (ub
- lb
< minrange
)
107 vty_out(vty
, "%% Range breadth must be at least %d\n", minrange
);
110 pbr_nht_set_tableid_range((uint32_t) lb
, (uint32_t) ub
);
117 DEFPY(pbr_map_match_src
, pbr_map_match_src_cmd
,
118 "[no] match src-ip <A.B.C.D/M|X:X::X:X/M>$prefix",
120 "Match the rest of the command\n"
121 "Choose the src ip or ipv6 prefix to use\n"
125 struct pbr_map_sequence
*pbrms
= VTY_GET_CONTEXT(pbr_map_sequence
);
127 pbrms
->family
= prefix
->family
;
131 if (prefix_same(pbrms
->src
, prefix
))
135 "A `match src-ip XX` command already exists, please remove that first\n");
136 return CMD_WARNING_CONFIG_FAILED
;
139 pbrms
->src
= prefix_new();
140 prefix_copy(pbrms
->src
, prefix
);
142 prefix_free(&pbrms
->src
);
144 pbr_map_check(pbrms
);
149 DEFPY(pbr_map_match_dst
, pbr_map_match_dst_cmd
,
150 "[no] match dst-ip <A.B.C.D/M|X:X::X:X/M>$prefix",
152 "Match the rest of the command\n"
153 "Choose the dst ip or ipv6 prefix to use\n"
157 struct pbr_map_sequence
*pbrms
= VTY_GET_CONTEXT(pbr_map_sequence
);
159 pbrms
->family
= prefix
->family
;
163 if (prefix_same(pbrms
->dst
, prefix
))
167 "A `match dst-ip XX` command already exists, please remove that first\n");
168 return CMD_WARNING_CONFIG_FAILED
;
171 pbrms
->dst
= prefix_new();
172 prefix_copy(pbrms
->dst
, prefix
);
174 prefix_free(&pbrms
->dst
);
176 pbr_map_check(pbrms
);
181 DEFPY(pbr_map_match_mark
, pbr_map_match_mark_cmd
,
182 "[no] match mark (1-4294967295)$mark",
184 "Match the rest of the command\n"
185 "Choose the mark value to use\n"
188 struct pbr_map_sequence
*pbrms
= VTY_GET_CONTEXT(pbr_map_sequence
);
191 vty_out(vty
, "pbr marks are not supported on this platform");
192 return CMD_WARNING_CONFIG_FAILED
;
197 if (pbrms
->mark
== (uint32_t)mark
)
201 "A `match mark XX` command already exists, please remove that first\n");
202 return CMD_WARNING_CONFIG_FAILED
;
205 pbrms
->mark
= (uint32_t)mark
;
209 pbr_map_check(pbrms
);
214 #define SET_VRF_EXISTS_STR \
215 "A `set vrf XX` command already exists, please remove that first\n"
217 DEFPY(pbr_map_nexthop_group
, pbr_map_nexthop_group_cmd
,
218 "[no] set nexthop-group NHGNAME$name",
220 "Set for the PBR-MAP\n"
221 "nexthop-group to use\n"
222 "The name of the nexthop-group\n")
224 struct pbr_map_sequence
*pbrms
= VTY_GET_CONTEXT(pbr_map_sequence
);
225 struct nexthop_group_cmd
*nhgc
;
229 "A `set nexthop XX` command already exists, please remove that first\n");
230 return CMD_WARNING_CONFIG_FAILED
;
233 if (pbrms
->vrf_lookup
|| pbrms
->vrf_unchanged
) {
234 vty_out(vty
, SET_VRF_EXISTS_STR
);
235 return CMD_WARNING_CONFIG_FAILED
;
238 nhgc
= nhgc_find(name
);
240 vty_out(vty
, "Specified nexthop-group %s does not exist\n",
243 "PBR-MAP will not be applied until it is created\n");
247 if (pbrms
->nhgrp_name
&& strcmp(name
, pbrms
->nhgrp_name
) == 0)
248 pbr_map_delete_nexthops(pbrms
);
251 "Nexthop Group specified: %s does not exist to remove\n",
253 return CMD_WARNING_CONFIG_FAILED
;
256 if (pbrms
->nhgrp_name
) {
257 if (strcmp(name
, pbrms
->nhgrp_name
) != 0) {
259 "Please delete current nexthop group before modifying current one\n");
260 return CMD_WARNING_CONFIG_FAILED
;
265 pbrms
->nhgrp_name
= XSTRDUP(MTYPE_TMP
, name
);
266 pbr_map_check(pbrms
);
272 DEFPY(pbr_map_nexthop
, pbr_map_nexthop_cmd
,
275 <A.B.C.D|X:X::X:X>$addr [INTERFACE$intf]\
278 [nexthop-vrf NAME$vrf_name]",
280 "Set for the PBR-MAP\n"
281 "Specify one of the nexthops in this map\n"
286 "If the nexthop is in a different vrf tell us\n"
287 "The nexthop-vrf Name\n")
289 struct pbr_map_sequence
*pbrms
= VTY_GET_CONTEXT(pbr_map_sequence
);
294 if (pbrms
->nhgrp_name
) {
296 "Please unconfigure the nexthop group before adding an individual nexthop\n");
297 return CMD_WARNING_CONFIG_FAILED
;
300 if (pbrms
->vrf_lookup
|| pbrms
->vrf_unchanged
) {
301 vty_out(vty
, SET_VRF_EXISTS_STR
);
302 return CMD_WARNING_CONFIG_FAILED
;
306 vrf
= vrf_lookup_by_name(vrf_name
);
308 vrf
= vrf_lookup_by_id(VRF_DEFAULT
);
311 vty_out(vty
, "Specified: %s is non-existent\n", vrf_name
);
312 return CMD_WARNING_CONFIG_FAILED
;
315 memset(&nhop
, 0, sizeof(nhop
));
316 nhop
.vrf_id
= vrf
->vrf_id
;
319 nhop
.ifindex
= ifname2ifindex(intf
, vrf
->vrf_id
);
320 if (nhop
.ifindex
== IFINDEX_INTERNAL
) {
322 "Specified Intf %s does not exist in vrf: %s\n",
324 return CMD_WARNING_CONFIG_FAILED
;
329 if (addr
->sa
.sa_family
== AF_INET
) {
330 nhop
.gate
.ipv4
.s_addr
= addr
->sin
.sin_addr
.s_addr
;
332 nhop
.type
= NEXTHOP_TYPE_IPV4_IFINDEX
;
334 nhop
.type
= NEXTHOP_TYPE_IPV4
;
336 nhop
.gate
.ipv6
= addr
->sin6
.sin6_addr
;
338 nhop
.type
= NEXTHOP_TYPE_IPV6_IFINDEX
;
340 if (IN6_IS_ADDR_LINKLOCAL(&nhop
.gate
.ipv6
)) {
342 "Specified a v6 LL with no interface, rejecting\n");
343 return CMD_WARNING_CONFIG_FAILED
;
345 nhop
.type
= NEXTHOP_TYPE_IPV6
;
349 nhop
.type
= NEXTHOP_TYPE_IFINDEX
;
352 nh
= nexthop_exists(pbrms
->nhg
, &nhop
);
354 char buf
[PBR_NHC_NAMELEN
];
357 vty_out(vty
, "No nexthops to delete\n");
358 return CMD_WARNING_CONFIG_FAILED
;
361 pbrms
->nhg
= nexthop_group_new();
362 pbrms
->internal_nhg_name
=
364 pbr_nht_nexthop_make_name(pbrms
->parent
->name
,
373 pbr_nht_delete_individual_nexthop(pbrms
);
376 if (pbrms
->nhg
->nexthop
) {
378 "If you would like more than one nexthop please use nexthop-groups\n");
379 return CMD_WARNING_CONFIG_FAILED
;
382 /* must be adding new nexthop since !no and !nexthop_exists */
385 memcpy(nh
, &nhop
, sizeof(nhop
));
386 _nexthop_add(&pbrms
->nhg
->nexthop
, nh
);
388 pbr_nht_add_individual_nexthop(pbrms
);
389 pbr_map_check(pbrms
);
392 if (nhop
.type
== NEXTHOP_TYPE_IFINDEX
393 || (nhop
.type
== NEXTHOP_TYPE_IPV6_IFINDEX
394 && IN6_IS_ADDR_LINKLOCAL(&nhop
.gate
.ipv6
))) {
395 struct interface
*ifp
;
397 ifp
= if_lookup_by_index(nhop
.ifindex
, nhop
.vrf_id
);
399 pbr_nht_nexthop_interface_update(ifp
);
405 DEFPY(pbr_map_vrf
, pbr_map_vrf_cmd
,
406 "[no] set vrf <NAME$vrf_name|unchanged>",
408 "Set for the PBR-MAP\n"
409 "Specify the VRF for this map\n"
411 "Use the interface's VRF for lookup\n")
413 struct pbr_map_sequence
*pbrms
= VTY_GET_CONTEXT(pbr_map_sequence
);
414 int ret
= CMD_SUCCESS
;
417 pbr_map_delete_vrf(pbrms
);
420 pbrms
->nhs_installed
= false;
421 pbrms
->vrf_name
[0] = '\0';
422 pbrms
->vrf_lookup
= false;
423 pbrms
->vrf_unchanged
= false;
428 if (pbrms
->nhgrp_name
|| pbrms
->nhg
) {
430 "A `set nexthop/nexthop-group XX` command already exits, please remove that first\n");
431 ret
= CMD_WARNING_CONFIG_FAILED
;
435 if (pbrms
->vrf_lookup
|| pbrms
->vrf_unchanged
) {
436 vty_out(vty
, SET_VRF_EXISTS_STR
);
437 ret
= CMD_WARNING_CONFIG_FAILED
;
442 if (!pbr_vrf_lookup_by_name(vrf_name
)) {
443 vty_out(vty
, "Specified: %s is non-existent\n",
445 ret
= CMD_WARNING_CONFIG_FAILED
;
449 pbrms
->vrf_lookup
= true;
450 strlcpy(pbrms
->vrf_name
, vrf_name
, sizeof(pbrms
->vrf_name
));
452 pbrms
->vrf_unchanged
= true;
454 pbr_map_check(pbrms
);
462 "[no] pbr-policy PBRMAP$mapname",
465 "Name of the pbr-map to apply\n")
467 VTY_DECLVAR_CONTEXT(interface
, ifp
);
468 struct pbr_map
*pbrm
, *old_pbrm
;
469 struct pbr_interface
*pbr_ifp
= ifp
->info
;
472 pbrm
= pbrm_find(mapname
);
475 /* we don't want one and we don't have one, so... */
479 /* Some one could have fat fingered the interface name */
480 pbr_ifp
= pbr_if_new(ifp
);
484 if (strcmp(pbr_ifp
->mapname
, mapname
) == 0) {
485 pbr_ifp
->mapname
[0] = '\0';
487 pbr_map_interface_delete(pbrm
, ifp
);
490 if (strcmp(pbr_ifp
->mapname
, "") != 0) {
491 old_pbrm
= pbrm_find(pbr_ifp
->mapname
);
494 * So if we have an old pbrm we should only
495 * delete it if we are actually deleting and
496 * moving to a new pbrm
498 if (old_pbrm
&& old_pbrm
!= pbrm
)
499 pbr_map_interface_delete(old_pbrm
, ifp
);
501 snprintf(pbr_ifp
->mapname
, sizeof(pbr_ifp
->mapname
),
505 * So only reinstall if the old_pbrm and this pbrm are
508 if (pbrm
&& pbrm
!= old_pbrm
)
509 pbr_map_add_interface(pbrm
, ifp
);
521 pbr_nht_write_table_range(vty
);
522 pbr_nht_write_rule_range(vty
);
529 "show pbr map [NAME$name] [detail$detail]",
534 "Detailed information\n")
536 struct pbr_map_sequence
*pbrms
;
537 struct pbr_map
*pbrm
;
538 struct listnode
*node
;
539 char buf
[PREFIX_STRLEN
];
542 RB_FOREACH (pbrm
, pbr_map_entry_head
, &pbr_maps
) {
543 if (name
&& strcmp(name
, pbrm
->name
) != 0)
546 vty_out(vty
, " pbr-map %s valid: %d\n", pbrm
->name
,
549 for (ALL_LIST_ELEMENTS_RO(pbrm
->seqnumbers
, node
, pbrms
)) {
551 pbr_map_reason_string(pbrms
->reason
, rbuf
,
554 " Seq: %u rule: %u Installed: %" PRIu64
"(%u) Reason: %s\n",
555 pbrms
->seqno
, pbrms
->ruleno
, pbrms
->installed
,
556 pbrms
->unique
, pbrms
->reason
? rbuf
: "Valid");
559 vty_out(vty
, "\tSRC Match: %s\n",
560 prefix2str(pbrms
->src
, buf
,
563 vty_out(vty
, "\tDST Match: %s\n",
564 prefix2str(pbrms
->dst
, buf
,
567 vty_out(vty
, "\tMARK Match: %u\n", pbrms
->mark
);
569 if (pbrms
->nhgrp_name
) {
571 "\tNexthop-Group: %s(%u) Installed: %u(%d)\n",
573 pbr_nht_get_table(pbrms
->nhgrp_name
),
574 pbrms
->nhs_installed
,
575 pbr_nht_get_installed(
577 } else if (pbrms
->nhg
) {
579 nexthop_group_write_nexthop(
580 vty
, pbrms
->nhg
->nexthop
);
582 "\tInstalled: %u(%d) Tableid: %d\n",
583 pbrms
->nhs_installed
,
584 pbr_nht_get_installed(
585 pbrms
->internal_nhg_name
),
587 pbrms
->internal_nhg_name
));
588 } else if (pbrms
->vrf_unchanged
) {
590 "\tVRF Unchanged (use interface vrf)\n");
591 } else if (pbrms
->vrf_lookup
) {
592 vty_out(vty
, "\tVRF Lookup: %s\n",
596 "\tNexthop-Group: Unknown Installed: 0(0)\n");
603 DEFPY(show_pbr_nexthop_group
,
604 show_pbr_nexthop_group_cmd
,
605 "show pbr nexthop-groups [WORD$word]",
609 "Optional Name of the nexthop group\n")
611 pbr_nht_show_nexthop_group(vty
, word
);
616 DEFPY (show_pbr_interface
,
617 show_pbr_interface_cmd
,
618 "show pbr interface [NAME$name]",
622 "PBR Interface Name\n")
624 struct interface
*ifp
;
626 struct pbr_interface
*pbr_ifp
;
628 RB_FOREACH(vrf
, vrf_name_head
, &vrfs_by_name
) {
629 FOR_ALL_INTERFACES(vrf
, ifp
) {
630 struct pbr_map
*pbrm
;
635 if (name
&& strcmp(ifp
->name
, name
) != 0)
640 if (strcmp(pbr_ifp
->mapname
, "") == 0)
643 pbrm
= pbrm_find(pbr_ifp
->mapname
);
644 vty_out(vty
, " %s(%d) with pbr-policy %s", ifp
->name
,
645 ifp
->ifindex
, pbr_ifp
->mapname
);
647 vty_out(vty
, " (map doesn't exist)");
655 /* PBR debugging CLI ------------------------------------------------------- */
657 static struct cmd_node debug_node
= {DEBUG_NODE
, "", 1};
661 "[no] debug pbr [{map$map|zebra$zebra|nht$nht|events$events}]",
666 "PBRD <-> Zebra communications\n"
670 uint32_t mode
= DEBUG_NODE2MODE(vty
->node
);
673 DEBUG_MODE_SET(&pbr_dbg_map
, mode
, !no
);
675 DEBUG_MODE_SET(&pbr_dbg_zebra
, mode
, !no
);
677 DEBUG_MODE_SET(&pbr_dbg_nht
, mode
, !no
);
679 DEBUG_MODE_SET(&pbr_dbg_event
, mode
, !no
);
681 /* no specific debug --> act on all of them */
682 if (strmatch(argv
[argc
- 1]->text
, "pbr"))
683 pbr_debug_set_all(mode
, !no
);
688 DEFUN_NOSH(show_debugging_pbr
,
689 show_debugging_pbr_cmd
,
690 "show debugging [pbr]",
695 vty_out(vty
, "PBR debugging status:\n");
697 pbr_debug_config_write_helper(vty
, false);
702 /* ------------------------------------------------------------------------- */
705 static struct cmd_node interface_node
= {
706 INTERFACE_NODE
, "%s(config-if)# ", 1 /* vtysh ? yes */
709 static int pbr_interface_config_write(struct vty
*vty
)
711 struct interface
*ifp
;
714 RB_FOREACH (vrf
, vrf_name_head
, &vrfs_by_name
) {
715 FOR_ALL_INTERFACES (vrf
, ifp
) {
716 if (vrf
->vrf_id
== VRF_DEFAULT
)
717 vty_frame(vty
, "interface %s\n", ifp
->name
);
719 vty_frame(vty
, "interface %s vrf %s\n",
720 ifp
->name
, vrf
->name
);
723 vty_out(vty
, " description %s\n", ifp
->desc
);
725 pbr_map_write_interfaces(vty
, ifp
);
727 vty_endframe(vty
, "!\n");
734 /* PBR map node structure. */
735 static struct cmd_node pbr_map_node
= {PBRMAP_NODE
, "%s(config-pbr-map)# ", 1};
737 static int pbr_vty_map_config_write_sequence(struct vty
*vty
,
738 struct pbr_map
*pbrm
,
739 struct pbr_map_sequence
*pbrms
)
741 char buff
[PREFIX_STRLEN
];
743 vty_out(vty
, "pbr-map %s seq %u\n", pbrm
->name
, pbrms
->seqno
);
746 vty_out(vty
, " match src-ip %s\n",
747 prefix2str(pbrms
->src
, buff
, sizeof(buff
)));
750 vty_out(vty
, " match dst-ip %s\n",
751 prefix2str(pbrms
->dst
, buff
, sizeof(buff
)));
754 vty_out(vty
, " match mark %u\n", pbrms
->mark
);
756 if (pbrms
->vrf_unchanged
)
757 vty_out(vty
, " set vrf unchanged\n");
759 if (pbrms
->vrf_lookup
)
760 vty_out(vty
, " set vrf %s\n", pbrms
->vrf_name
);
762 if (pbrms
->nhgrp_name
)
763 vty_out(vty
, " set nexthop-group %s\n", pbrms
->nhgrp_name
);
766 vty_out(vty
, " set ");
767 nexthop_group_write_nexthop(vty
, pbrms
->nhg
->nexthop
);
774 static int pbr_vty_map_config_write(struct vty
*vty
)
776 struct pbr_map
*pbrm
;
778 pbr_nht_write_table_range(vty
);
779 pbr_nht_write_rule_range(vty
);
781 RB_FOREACH(pbrm
, pbr_map_entry_head
, &pbr_maps
) {
782 struct pbr_map_sequence
*pbrms
;
783 struct listnode
*node
;
785 for (ALL_LIST_ELEMENTS_RO(pbrm
->seqnumbers
, node
, pbrms
))
786 pbr_vty_map_config_write_sequence(vty
, pbrm
, pbrms
);
792 static void pbr_map_completer(vector comps
, struct cmd_token
*token
)
794 struct pbr_map
*pbrm
;
796 RB_FOREACH (pbrm
, pbr_map_entry_head
, &pbr_maps
)
797 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
, pbrm
->name
));
800 static const struct cmd_variable_handler pbr_map_name
[] = {
802 .tokenname
= "PBRMAP", .completions
= pbr_map_completer
,
809 void pbr_vty_init(void)
811 cmd_variable_handler_register(pbr_map_name
);
813 install_node(&interface_node
,
814 pbr_interface_config_write
);
817 install_node(&pbr_map_node
,
818 pbr_vty_map_config_write
);
821 install_node(&debug_node
, pbr_debug_config_write
);
822 install_element(VIEW_NODE
, &debug_pbr_cmd
);
823 install_element(CONFIG_NODE
, &debug_pbr_cmd
);
824 install_element(VIEW_NODE
, &show_debugging_pbr_cmd
);
826 install_default(PBRMAP_NODE
);
828 install_element(CONFIG_NODE
, &pbr_map_cmd
);
829 install_element(CONFIG_NODE
, &no_pbr_map_cmd
);
830 install_element(CONFIG_NODE
, &pbr_set_table_range_cmd
);
831 install_element(INTERFACE_NODE
, &pbr_policy_cmd
);
832 install_element(PBRMAP_NODE
, &pbr_map_match_src_cmd
);
833 install_element(PBRMAP_NODE
, &pbr_map_match_dst_cmd
);
834 install_element(PBRMAP_NODE
, &pbr_map_match_mark_cmd
);
835 install_element(PBRMAP_NODE
, &pbr_map_nexthop_group_cmd
);
836 install_element(PBRMAP_NODE
, &pbr_map_nexthop_cmd
);
837 install_element(PBRMAP_NODE
, &pbr_map_vrf_cmd
);
838 install_element(VIEW_NODE
, &show_pbr_cmd
);
839 install_element(VIEW_NODE
, &show_pbr_map_cmd
);
840 install_element(VIEW_NODE
, &show_pbr_interface_cmd
);
841 install_element(VIEW_NODE
, &show_pbr_nexthop_group_cmd
);