diff options
author | Donald Sharp <sharpd@cumulusnetworks.com> | 2018-01-23 19:11:36 +0100 |
---|---|---|
committer | Donald Sharp <sharpd@cumulusnetworks.com> | 2018-04-06 19:22:43 +0200 |
commit | e5c83d9b314cb513e78707de5d29ec655dbdca7e (patch) | |
tree | 0ede3af459164c589f9892e7f6c93e82f08ad208 /pbrd/pbr_vty.c | |
parent | Merge pull request #2029 from cdwertmann/patch-1 (diff) | |
download | frr-e5c83d9b314cb513e78707de5d29ec655dbdca7e.tar.xz frr-e5c83d9b314cb513e78707de5d29ec655dbdca7e.zip |
pbrd: Add PBR to FRR
This is an implementation of PBR for FRR.
This implemenation uses a combination of rules and
tables to determine how packets will flow.
PBR introduces a new concept of 'nexthop-groups' to
specify a group of nexthops that will be used for
ecmp. Nexthop-groups are specified on the cli via:
nexthop-group DONNA
nexthop 192.168.208.1
nexthop 192.168.209.1
nexthop 192.168.210.1
!
PBR sees the nexthop-group and installs these as a default
route with these nexthops starting at table 10000
robot# show pbr nexthop-groups
Nexthop-Group: DONNA Table: 10001 Valid: 1 Installed: 1
Valid: 1 nexthop 192.168.209.1
Valid: 1 nexthop 192.168.210.1
Valid: 1 nexthop 192.168.208.1
I have also introduced the ability to specify a table
in a 'show ip route table XXX' to see the specified tables.
robot# show ip route table 10001
Codes: K - kernel route, C - connected, S - static, R - RIP,
O - OSPF, I - IS-IS, B - BGP, P - PIM, E - EIGRP, N - NHRP,
T - Table, v - VNC, V - VNC-Direct, A - Babel, D - SHARP,
F - PBR,
> - selected route, * - FIB route
F>* 0.0.0.0/0 [0/0] via 192.168.208.1, enp0s8, 00:14:25
* via 192.168.209.1, enp0s9, 00:14:25
* via 192.168.210.1, enp0s10, 00:14:25
PBR tracks PBR-MAPS via the pbr-map command:
!
pbr-map EVA seq 10
match src-ip 4.3.4.0/24
set nexthop-group DONNA
!
pbr-map EVA seq 20
match dst-ip 4.3.5.0/24
set nexthop-group DONNA
!
pbr-maps can have 'match src-ip <prefix>' and 'match dst-ip <prefix>'
to affect decisions about incoming packets. Additionally if you
only have one nexthop to use for a pbr-map you do not need
to setup a nexthop-group and can specify 'set nexthop XXXX'.
To apply the pbr-map to an incoming interface you do this:
interface enp0s10
pbr-policy EVA
!
When a pbr-map is applied to interfaces it can be installed
into the kernel as a rule:
[sharpd@robot frr1]$ ip rule show
0: from all lookup local
309: from 4.3.4.0/24 iif enp0s10 lookup 10001
319: from all to 4.3.5.0/24 iif enp0s10 lookup 10001
1000: from all lookup [l3mdev-table]
32766: from all lookup main
32767: from all lookup default
[sharpd@robot frr1]$ ip route show table 10001
default proto pbr metric 20
nexthop via 192.168.208.1 dev enp0s8 weight 1
nexthop via 192.168.209.1 dev enp0s9 weight 1
nexthop via 192.168.210.1 dev enp0s10 weight 1
The linux kernel now will use the rules and tables to properly
apply these policies.
Signed-off-by: Donald Sharp <sharpd@cumulusnetworks.com>
Signed-off-by: Don Slice <dslice@cumulusnetworks.com>
Signed-off-by: Quentin Young <qlyoung@cumulusnetworks.com>
Diffstat (limited to 'pbrd/pbr_vty.c')
-rw-r--r-- | pbrd/pbr_vty.c | 631 |
1 files changed, 631 insertions, 0 deletions
diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c new file mode 100644 index 000000000..730cfd40f --- /dev/null +++ b/pbrd/pbr_vty.c @@ -0,0 +1,631 @@ +/* + * PBR - vty code + * Copyright (C) 2018 Cumulus Networks, Inc. + * Donald Sharp + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include <zebra.h> + +#include "vty.h" +#include "command.h" +#include "prefix.h" +#include "vrf.h" +#include "nexthop.h" +#include "nexthop_group.h" +#include "log.h" +#include "json.h" +#include "debug.h" + +#include "pbrd/pbr_nht.h" +#include "pbrd/pbr_map.h" +#include "pbrd/pbr_zebra.h" +#include "pbrd/pbr_vty.h" +#include "pbrd/pbr_event.h" +#include "pbrd/pbr_debug.h" +#ifndef VTYSH_EXTRACT_PL +#include "pbrd/pbr_vty_clippy.c" +#endif + +DEFUN_NOSH(pbr_map, pbr_map_cmd, "pbr-map WORD seq (1-1000)", + "Create pbr-map or enter pbr-map command mode\n" + "The name of the PBR MAP\n" + "Sequence to insert in existing pbr-map entry\n" + "Sequence number\n") +{ + const char *pbrm_name = argv[1]->arg; + uint32_t seqno = atoi(argv[3]->arg); + struct pbr_map_sequence *pbrms; + + pbrms = pbrms_get(pbrm_name, seqno); + VTY_PUSH_CONTEXT(PBRMAP_NODE, pbrms); + + return CMD_SUCCESS; +} + +DEFUN_NOSH(no_pbr_map, no_pbr_map_cmd, "no pbr-map WORD [seq (1-65535)]", + NO_STR + "Delete pbr-map\n" + "The name of the PBR MAP\n" + "Sequence to delete from existing pbr-map entry\n" + "Sequence number\n") +{ + const char *pbrm_name = argv[2]->arg; + uint32_t seqno = 0; + struct pbr_map *pbrm = pbrm_find(pbrm_name); + struct pbr_event *pbre; + struct pbr_map_sequence *pbrms; + struct listnode *node, *next_node; + + if (argc > 3) + seqno = atoi(argv[4]->arg); + + if (!pbrm) { + vty_out(vty, "pbr-map %s not found\n", pbrm_name); + return CMD_SUCCESS; + } + + if (seqno) { + pbrms = pbrms_get(pbrm->name, seqno); + pbrms->reason |= PBR_MAP_DEL_SEQUENCE_NUMBER; + } else { + for (ALL_LIST_ELEMENTS(pbrm->seqnumbers, node, next_node, + pbrms)) { + if (pbrms) + pbrms->reason |= PBR_MAP_DEL_SEQUENCE_NUMBER; + } + } + + pbre = pbr_event_new(PBR_MAP_DELETE, pbrm_name); + pbre->seqno = seqno; + pbr_event_enqueue(pbre); + + return CMD_SUCCESS; +} + +DEFPY(pbr_map_match_src, pbr_map_match_src_cmd, + "[no] match src-ip <A.B.C.D/M|X:X::X:X/M>$prefix", + NO_STR + "Match the rest of the command\n" + "Choose the src ip or ipv6 prefix to use\n" + "v4 Prefix\n" + "v6 Prefix\n") +{ + struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); + struct pbr_event *pbre; + + if (!no) { + if (!pbrms->src) + pbrms->src = prefix_new(); + prefix_copy(pbrms->src, prefix); + } else { + prefix_free(pbrms->src); + pbrms->src = 0; + } + + pbre = pbr_event_new(PBR_MAP_MODIFY, pbrms->parent->name); + pbre->seqno = pbrms->seqno; + pbr_event_enqueue(pbre); + + return CMD_SUCCESS; +} + +DEFPY(pbr_map_match_dst, pbr_map_match_dst_cmd, + "[no] match dst-ip <A.B.C.D/M|X:X::X:X/M>$prefix", + NO_STR + "Match the rest of the command\n" + "Choose the src ip or ipv6 prefix to use\n" + "v4 Prefix\n" + "v6 Prefix\n") +{ + struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); + struct pbr_event *pbre; + + if (!no) { + if (!pbrms->dst) + pbrms->dst = prefix_new(); + prefix_copy(pbrms->dst, prefix); + } else { + prefix_free(pbrms->dst); + pbrms->dst = 0; + } + + pbre = pbr_event_new(PBR_MAP_MODIFY, pbrms->parent->name); + pbre->seqno = pbrms->seqno; + pbr_event_enqueue(pbre); + + return CMD_SUCCESS; +} + +DEFPY(pbr_map_nexthop_group, pbr_map_nexthop_group_cmd, + "[no] set nexthop-group NAME$name", + NO_STR + "Set for the PBR-MAP\n" + "nexthop-group to use\n" + "The name of the nexthop-group\n") +{ + struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); + struct nexthop_group_cmd *nhgc; + struct pbr_event *pbre; + + nhgc = nhgc_find(name); + if (!nhgc) { + vty_out(vty, "Specified nexthop-group %s does not exist\n", + name); + vty_out(vty, "PBR-MAP will not be applied until it is created\n"); + } + + if (no) { + if (pbrms->nhgrp_name && strcmp(name, pbrms->nhgrp_name) == 0) + pbre = pbr_event_new(PBR_MAP_NHG_DELETE, + pbrms->parent->name); + else { + vty_out(vty, + "Nexthop Group specified: %s does not exist to remove", + name); + return CMD_WARNING; + } + } else { + if (pbrms->nhgrp_name) { + if (strcmp(name, pbrms->nhgrp_name) != 0) { + vty_out(vty, + "Please delete current nexthop group before modifying current one"); + return CMD_WARNING; + } + + return CMD_SUCCESS; + } + pbrms->nhgrp_name = XSTRDUP(MTYPE_TMP, name); + pbre = pbr_event_new(PBR_MAP_NHG_ADD, pbrms->parent->name); + } + + pbre->seqno = pbrms->seqno; + pbr_event_enqueue(pbre); + + return CMD_SUCCESS; +} + +DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd, + "[no] set nexthop <A.B.C.D|X:X::X:X>$addr [INTERFACE]$intf [nexthop-vrf NAME$name]", + NO_STR + "Set for the PBR-MAP\n" + "Specify one of the nexthops in this map\n" + "v4 Address\n" + "v6 Address\n" + "Interface to use\n" + "If the nexthop is in a different vrf tell us\n" + "The nexthop-vrf Name\n") +{ + struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); + struct vrf *vrf; + struct nexthop nhop; + struct nexthop *nh; + struct pbr_event *pbre; + + if (pbrms->nhgrp_name) { + vty_out(vty, + "Please unconfigure the nexthop group before adding an individual nexthop"); + return CMD_WARNING; + } + + if (name) + vrf = vrf_lookup_by_name(name); + else + vrf = vrf_lookup_by_id(VRF_DEFAULT); + + if (!vrf) { + vty_out(vty, "Specified: %s is non-existent\n", name); + return CMD_WARNING; + } + + memset(&nhop, 0, sizeof(nhop)); + nhop.vrf_id = vrf->vrf_id; + + if (addr->sa.sa_family == AF_INET) { + nhop.gate.ipv4.s_addr = addr->sin.sin_addr.s_addr; + if (intf) { + nhop.type = NEXTHOP_TYPE_IPV4_IFINDEX; + nhop.ifindex = ifname2ifindex(intf, vrf->vrf_id); + if (nhop.ifindex == IFINDEX_INTERNAL) { + vty_out(vty, + "Specified Intf %s does not exist in vrf: %s\n", + intf, vrf->name); + return CMD_WARNING; + } + } else + nhop.type = NEXTHOP_TYPE_IPV4; + } else { + memcpy(&nhop.gate.ipv6, &addr->sin6.sin6_addr, 16); + if (intf) { + nhop.type = NEXTHOP_TYPE_IPV6_IFINDEX; + nhop.ifindex = ifname2ifindex(intf, vrf->vrf_id); + if (nhop.ifindex == IFINDEX_INTERNAL) { + vty_out(vty, + "Specified Intf %s does not exist in vrf: %s\n", + intf, vrf->name); + return CMD_WARNING; + } + } else + nhop.type = NEXTHOP_TYPE_IPV6; + } + + if (pbrms->nhg) + nh = nexthop_exists(pbrms->nhg, &nhop); + else { + char buf[100]; + + if (no) { + vty_out(vty, "No nexthops to delete"); + return CMD_WARNING; + } + + pbrms->nhg = nexthop_group_new(); + pbrms->internal_nhg_name = + XSTRDUP(MTYPE_TMP, + pbr_nht_nexthop_make_name(pbrms->parent->name, + PBR_MAP_NAMELEN, + pbrms->seqno, + buf)); + nh = NULL; + } + + if (no) { + if (nh) { + // nexthop_del(pbrms->nhg, nh); + // nexthop_free(nh); + pbre = pbr_event_new(PBR_MAP_NEXTHOP_DELETE, + pbrms->parent->name); + pbre->seqno = pbrms->seqno; + pbr_event_enqueue(pbre); + } + } else if (!nh) { + + if (pbrms->nhg->nexthop) { + vty_out(vty, + "If you would like more than one nexthop please use nexthop-groups"); + return CMD_WARNING; + } + + /* must be adding new nexthop since !no and !nexthop_exists */ + nh = nexthop_new(); + + memcpy(nh, &nhop, sizeof(nhop)); + nexthop_add(&pbrms->nhg->nexthop, nh); + + pbre = pbr_event_new(PBR_MAP_NEXTHOP_ADD, pbrms->parent->name); + pbre->seqno = pbrms->seqno; + pbr_event_enqueue(pbre); + } + + return CMD_SUCCESS; +} + +DEFPY (pbr_table_range, + pbr_table_range_cmd, + "[no]$no pbr table range (10000-65535)$start (11000-65535)$end", + NO_STR + "Policy based routing\n" + "Policy based routing table\n" + "Table range\n" + "Initial value of range\n" + "Final value of range\n") +{ + if (no) + pbr_nht_set_tableid_range(PBR_NHT_DEFAULT_LOW_TABLEID, + PBR_NHT_DEFAULT_HIGH_TABLEID); + else + pbr_nht_set_tableid_range(start, end); + + return CMD_SUCCESS; +} + +DEFPY (pbr_rule_range, + pbr_rule_range_cmd, + "[no] pbr rule range (300-1300)$start (400-1400)$end", + NO_STR + "Policy based routing\n" + "Policy based routing rule\n" + "Rule range\n" + "Initial value of range\n" + "Final value of range\n") +{ + if (no) + pbr_nht_set_rule_range(PBR_NHT_DEFAULT_LOW_RULE, + PBR_NHT_DEFAULT_HIGH_RULE); + else + pbr_nht_set_rule_range(start, end); + + return CMD_SUCCESS; +} + +DEFPY (pbr_policy, + pbr_policy_cmd, + "[no] pbr-policy NAME$mapname", + NO_STR + "Policy to use\n" + "Name of the pbr-map to apply\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct pbr_map *pbrm, *old_pbrm; + struct pbr_interface *pbr_ifp = ifp->info; + + pbrm = pbrm_find(mapname); + + if (no) { + if (strcmp(pbr_ifp->mapname, mapname) == 0) { + strcpy(pbr_ifp->mapname, ""); + + if (pbrm) + pbr_map_interface_delete(pbrm, ifp); + } + } else { + if (strcmp(pbr_ifp->mapname, "") == 0) { + strcpy(pbr_ifp->mapname, mapname); + + if (pbrm) + pbr_map_add_interface(pbrm, ifp); + } else { + if (!(strcmp(pbr_ifp->mapname, mapname) == 0)) { + old_pbrm = pbrm_find(pbr_ifp->mapname); + if (old_pbrm) + pbr_map_interface_delete(old_pbrm, ifp); + strcpy(pbr_ifp->mapname, mapname); + if (pbrm) + pbr_map_add_interface(pbrm, ifp); + } + } + } + + return CMD_SUCCESS; +} + +DEFPY (show_pbr, + show_pbr_cmd, + "show pbr [json$json]", + SHOW_STR + "Policy Based Routing\n" + JSON_STR) +{ + pbr_nht_write_table_range(vty); + pbr_nht_write_rule_range(vty); + + return CMD_SUCCESS; +} + +DEFPY (show_pbr_map, + show_pbr_map_cmd, + "show pbr map [NAME$name] [detail$detail] [json$json]", + SHOW_STR + "Policy Based Routing\n" + "PBR Map\n" + "PBR Map Name\n" + "Detailed information\n" + JSON_STR) +{ + struct pbr_map_sequence *pbrms; + struct pbr_map *pbrm; + struct listnode *node; + char buf[PREFIX_STRLEN]; + char rbuf[64]; + + RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) { + if (name && strcmp(name, pbrm->name) != 0) + continue; + + vty_out(vty, " pbr-map %s valid: %d\n", pbrm->name, + pbrm->valid); + + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) { + if (pbrms->reason) + pbr_map_reason_string(pbrms->reason, rbuf, + sizeof(rbuf)); + vty_out(vty, + " Seq: %u rule: %u Installed: %d(%u) Reason: %s\n", + pbrms->seqno, pbrms->ruleno, pbrms->installed, + pbrms->unique, pbrms->reason ? rbuf : "Valid"); + + if (pbrms->src) + vty_out(vty, "\tSRC Match: %s\n", + prefix2str(pbrms->src, buf, + sizeof(buf))); + if (pbrms->dst) + vty_out(vty, "\tDST Match: %s\n", + prefix2str(pbrms->dst, buf, + sizeof(buf))); + + if (pbrms->nhgrp_name) { + vty_out(vty, + "\tNexthop-Group: %s(%u) Installed: %u(%d)\n", + pbrms->nhgrp_name, + pbr_nht_get_table(pbrms->nhgrp_name), + pbrms->nhs_installed, + pbr_nht_get_installed( + pbrms->nhgrp_name)); + } else if (pbrms->nhg) { + vty_out(vty, " "); + nexthop_group_write_nexthop( + vty, pbrms->nhg->nexthop); + vty_out(vty, + "\tInstalled: %u(%d) Tableid: %d\n", + pbrms->nhs_installed, + pbr_nht_get_installed( + pbrms->internal_nhg_name), + pbr_nht_get_table( + pbrms->internal_nhg_name)); + } else { + vty_out(vty, + "\tNexthop-Group: Unknown Installed: 0(0)\n"); + } + } + } + return CMD_SUCCESS; +} + +DEFPY(show_pbr_nexthop_group, + show_pbr_nexthop_group_cmd, + "show pbr nexthop-groups [WORD$word]", + SHOW_STR + "Policy Based Routing\n" + "Nexthop Groups\n" + "Optional Name of the nexthop group\n") +{ + pbr_nht_show_nexthop_group(vty, word); + + return CMD_SUCCESS; +} + +DEFPY (show_pbr_interface, + show_pbr_interface_cmd, + "show pbr interface [NAME$name] [json$json]", + SHOW_STR + "Policy Based Routing\n" + "PBR Interface\n" + "PBR Interface Name\n" + JSON_STR) +{ + struct interface *ifp; + struct vrf *vrf; + struct pbr_interface *pbr_ifp; + + RB_FOREACH(vrf, vrf_name_head, &vrfs_by_name) { + FOR_ALL_INTERFACES(vrf, ifp) { + struct pbr_map *pbrm; + + if (name && strcmp(ifp->name, name) != 0) + continue; + + pbr_ifp = ifp->info; + + if (strcmp(pbr_ifp->mapname, "") == 0) + continue; + + pbrm = pbrm_find(pbr_ifp->mapname); + vty_out(vty, " %s(%d) with pbr-policy %s", ifp->name, + ifp->ifindex, pbr_ifp->mapname); + if (!pbrm) + vty_out(vty, " (map doesn't exist)"); + vty_out(vty, "\n"); + } + } + + return CMD_SUCCESS; +} + +static struct cmd_node interface_node = { + INTERFACE_NODE, "%s(config-if)# ", 1 /* vtysh ? yes */ +}; + +static int pbr_interface_config_write(struct vty *vty) +{ + struct interface *ifp; + struct vrf *vrf; + + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { + FOR_ALL_INTERFACES (vrf, ifp) { + if (vrf->vrf_id == VRF_DEFAULT) + vty_frame(vty, "interface %s\n", ifp->name); + else + vty_frame(vty, "interface %s vrf %s\n", + ifp->name, vrf->name); + + pbr_map_write_interfaces(vty, ifp); + + vty_endframe(vty, "!\n"); + } + } + + return 1; +} + +/* PBR map node structure. */ +static struct cmd_node pbr_map_node = {PBRMAP_NODE, "%s(config-pbr-map)# ", 1}; + +static int pbr_vty_map_config_write_sequence(struct vty *vty, + struct pbr_map *pbrm, + struct pbr_map_sequence *pbrms) +{ + char buff[PREFIX_STRLEN]; + + vty_out (vty, "pbr-map %s seq %u\n", + pbrm->name, pbrms->seqno); + + if (pbrms->src) + vty_out(vty, " match src-ip %s\n", + prefix2str(pbrms->src, buff, sizeof buff)); + + if (pbrms->dst) + vty_out(vty, " match dst-ip %s\n", + prefix2str(pbrms->dst, buff, sizeof buff)); + + if (pbrms->nhgrp_name) + vty_out(vty, " set nexthop-group %s\n", pbrms->nhgrp_name); + + if (pbrms->nhg) { + vty_out(vty, " set"); + nexthop_group_write_nexthop(vty, pbrms->nhg->nexthop); + } + + vty_out (vty, "!\n"); + return 1; +} + +static int pbr_vty_map_config_write(struct vty *vty) +{ + struct pbr_map *pbrm; + + pbr_nht_write_table_range(vty); + pbr_nht_write_rule_range(vty); + + RB_FOREACH(pbrm, pbr_map_entry_head, &pbr_maps) { + struct pbr_map_sequence *pbrms; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) { + pbr_vty_map_config_write_sequence(vty, pbrm, pbrms); + } + } + + return 1; +} + +void pbr_vty_init(void) +{ + install_node(&interface_node, + pbr_interface_config_write); + if_cmd_init(); + + install_node(&pbr_map_node, + pbr_vty_map_config_write); + + install_default(PBRMAP_NODE); + + install_element(CONFIG_NODE, &pbr_map_cmd); + install_element(CONFIG_NODE, &no_pbr_map_cmd); + install_element(INTERFACE_NODE, &pbr_policy_cmd); + install_element(CONFIG_NODE, &pbr_table_range_cmd); + install_element(CONFIG_NODE, &pbr_rule_range_cmd); + install_element(PBRMAP_NODE, &pbr_map_match_src_cmd); + install_element(PBRMAP_NODE, &pbr_map_match_dst_cmd); + install_element(PBRMAP_NODE, &pbr_map_nexthop_group_cmd); + install_element(PBRMAP_NODE, &pbr_map_nexthop_cmd); + install_element(VIEW_NODE, &show_pbr_cmd); + install_element(VIEW_NODE, &show_pbr_map_cmd); + install_element(VIEW_NODE, &show_pbr_interface_cmd); + install_element(VIEW_NODE, &show_pbr_nexthop_group_cmd); + + pbr_debug_init_vty(); + + return; +} |