diff --git a/Makefile.am b/Makefile.am index 737b1acf6414..563300b8fd34 100644 --- a/Makefile.am +++ b/Makefile.am @@ -55,6 +55,7 @@ include babeld/subdir.am include eigrpd/subdir.am include sharpd/subdir.am include pimd/subdir.am +include pbrd/subdir.am SUBDIRS = . @LIBRFP@ @RFPTEST@ \ @BGPD@ \ diff --git a/configure.ac b/configure.ac index 24d6fa086720..34b37b05267a 100755 --- a/configure.ac +++ b/configure.ac @@ -1346,6 +1346,7 @@ AM_CONDITIONAL(BABELD, test "${enable_babeld}" != "no") AM_CONDITIONAL(OSPF6D, test "${enable_ospf6d}" != "no") AM_CONDITIONAL(ISISD, test "${enable_isisd}" != "no") AM_CONDITIONAL(PIMD, test "${enable_pimd}" != "no") +AM_CONDITIONAL(PBRD, test "${enable_pbrd}" != "no") if test "${enable_bgp_announce}" = "no";then AC_DEFINE(DISABLE_BGP_ANNOUNCE,1,Disable BGP installation to zebra) diff --git a/doc/manpages/common-options.rst b/doc/manpages/common-options.rst index 5b136f5e1a27..e433c720afb7 100644 --- a/doc/manpages/common-options.rst +++ b/doc/manpages/common-options.rst @@ -122,6 +122,7 @@ These following options control the daemon's VTY (interactive command line) inte pimd 2611 ldpd 2612 eigrpd 2613 + pbrd 2615 Port 2607 is used for ospfd's Opaque LSA API, while port 2600 is used for the (insecure) TCP-ZEBRA interface. diff --git a/doc/manpages/conf.py b/doc/manpages/conf.py index a78e1a2c3892..b5ea537faa8e 100644 --- a/doc/manpages/conf.py +++ b/doc/manpages/conf.py @@ -318,6 +318,7 @@ ('ldpd', 'ldpd', fwfrr.format("an LDP "), [], 8), ('nhrpd', 'nhrpd', fwfrr.format("a Next Hop Routing Protocol "), [], 8), ('pimd', 'pimd', fwfrr.format("a PIM "), [], 8), + ('pbrd', 'pbrd', fwfrr.format("a PBR "), [], 8), ('mtracebis', 'mtracebis', "a multicast trace client", [], 8), ('ripd', 'ripd', fwfrr.format("a RIP "), [], 8), ('ripngd', 'ripngd', fwfrr.format("a RIPNG "), [], 8), diff --git a/doc/manpages/defines.rst b/doc/manpages/defines.rst index 841abe300e49..21c3790afaeb 100644 --- a/doc/manpages/defines.rst +++ b/doc/manpages/defines.rst @@ -1,3 +1,3 @@ .. |synopsis-options| replace:: [-d|-t|-dt] [-C] [-f config-file] [-i pid-file] [-z zclient-path] [-u user] [-g group] [-A vty-addr] [-P vty-port] [-M module[:options]] [-N pathspace] [--vty_socket vty-path] [--moduledir module-path] .. |synopsis-options-hv| replace:: [-h] [-v] -.. |seealso-programs| replace:: zebra(8), vtysh(1), ripd(8), ripngd(8), ospfd(8), ospf6d(8), bgpd(8), isisd(8), babeld(8), nhrpd(8), pimd(8), ldpd(8), eigrpd(8), mtracebis(8) +.. |seealso-programs| replace:: zebra(8), vtysh(1), ripd(8), ripngd(8), ospfd(8), ospf6d(8), bgpd(8), isisd(8), babeld(8), nhrpd(8), pimd(8), pbrd(8), ldpd(8), eigrpd(8), mtracebis(8) diff --git a/doc/manpages/index.rst b/doc/manpages/index.rst index ae5c4ca3bd11..964cc07d7193 100644 --- a/doc/manpages/index.rst +++ b/doc/manpages/index.rst @@ -18,6 +18,7 @@ Welcome to FRR's documentation! ospfclient ospfd pimd + pbrd mtracebis ripd ripngd diff --git a/doc/manpages/pbrd.rst b/doc/manpages/pbrd.rst new file mode 100644 index 000000000000..38e1bc267b62 --- /dev/null +++ b/doc/manpages/pbrd.rst @@ -0,0 +1,39 @@ +**** +PBRD +**** + +.. include:: defines.rst +.. |DAEMON| replace:: pbrd + +SYNOPSIS +======== +|DAEMON| |synopsis-options-hv| + +|DAEMON| |synopsis-options| + +DESCRIPTION +=========== +|DAEMON| is a routing component that works with the FRRouting engine. + +OPTIONS +======= +OPTIONS available for the |DAEMON| command: + +.. include:: common-options.rst + +FILES +===== + +|INSTALL_PREFIX_SBIN|/|DAEMON| + The default location of the |DAEMON| binary. + +|INSTALL_PREFIX_ETC|/|DAEMON|.conf + The default location of the |DAEMON| config file. + +$(PWD)/|DAEMON|.log + If the |DAEMON| process is configured to output logs to a file, then you + will find this file in the directory where you started |DAEMON|. + +.. include:: epilogue.rst + + diff --git a/doc/user/index.rst b/doc/user/index.rst index 54ee8b12f884..e1a2c19ff6d2 100644 --- a/doc/user/index.rst +++ b/doc/user/index.rst @@ -23,6 +23,7 @@ Welcome to FRR's documentation! ospfd ospf6d pim + pbr ripd ripngd vnc diff --git a/doc/user/pbr.rst b/doc/user/pbr.rst new file mode 100644 index 000000000000..7d25fc794ef4 --- /dev/null +++ b/doc/user/pbr.rst @@ -0,0 +1,130 @@ +.. _pbr: + +*** +PBR +*** + +:abbr:`PBR` is Policy Based Routing. This implementation supports a very +simple interface to allow admins to influence routing on their router. At +this time you can only match on destination and source prefixes for +an incoming interface. At this point in time, this implementation will +only work on Linux. + +.. _starting-pbr: + +STARTING PBR +============ + +Default configuration file for *pbrd* is :file:`pbrd.conf`. The typical +location of :file:`pbrd.conf` is |INSTALL_PREFIX_ETC|/pbrd.conf. + +If the user is using integrated config, then :file:`pbrd.conf` need +not be present and the :file:`frr.conf` is read instead. + +.. program:: pbrd + +:abbr:`PBR` supports all the common FRR daemon start options which are +documented elsewhere. + +.. _nexthop-groups: + +NEXTHOP GROUPS +============== + +Nexthop groups are a way to encapsulate ECMP information together. It's a +listing of ECMP nexthops used to forward packets for when a pbr-map is +matched. + +.. index:: nexthop-group +.. clicmd:: nexthop-group NAME + + Create a nexthop-group with an associated NAME. This will put + you into a sub-mode where you can specify individual nexthops. + To exit this mode type exit or end as per normal conventions + for leaving a sub-mode. + +.. clicmd:: nexthop [A.B.C.D|X:X::X:XX] [interface] [nexthop-vrf NAME] + + Create a v4 or v6 nexthop. All normal rules for creating nexthops + that you are used to are allowed here. The syntax was intentionally + kept the same as creating nexthops as you would for static routes. + +.. _pbr-maps: + +PBR MAPS +======== + +PBR MAPS are a way to group policies that we would like to apply +to individual interfaces. These policies when applied are matched +against incoming packets. If matched the nexthop-group or nexthop +is used to forward the packets to the end destination + +.. index:: pbr-map +.. clicmd:: pbr-map NAME seq (1-1000) + + Create a pbr-map with NAME and sequence number specified. This + command puts you into a new submode for pbr-map specification. + To exit this mode type exit or end as per normal conventions + for leaving a sub-mode. + +.. index:: match +.. clicmd:: match src-ip + + When a incoming packet matches the source prefix specified, take the packet + and forward according to the nexthops specified. This command + accepts both v4 and v6 prefixes. This command is used in + conjunction of the 'match dst-ip ' command for matching. + +.. clicmd:: match dst-ip + + When a incoming packet matches the destination prefix specified, take + the packet and forward according to the nexthops specified. This + command accepts both v4 and v6 prefixes. This command is used in + conjuction of the 'match src-ip ' command for matching. + +.. clicmd:: set nexthop-group NAME + + Use the nexthop-group NAME as the place to forward packets when + the match commands have matched a packet. + +.. clicmd:: set nexthop [A.B.C.D|X:X::X:XX] [interface] [nexthop-vrf NAME] + + Use this individual nexthop as the place to forward packets when + the match commands have matched a packet. + +.. _pbr-policy: + +PBR POLICY +========== + +After you have specified a PBR MAP, in order for it to be turned on, +you must apply the PBR MAP to an interface. This policy application +to an interface causes the policy to be installed into the kernel. + +..index:: pbr-policy +.. clicmd:: pbr-policy NAME + + This command is available under interface sub-mode. This turns + on the PBR MAP NAME and allows it to work properly. + +.. _pbr-details: + +PBR DETAILS +=========== + +Under the covers a PBR MAP is translated into two separate constructs +in the linux kernel. + +..index:: Rules + + The PBR MAP specified creates a `ip rule ...` that is inserted into + the linux kernel that points to a table to use for forwarding once + the rule matches. + +..index:: Tables + + The creation of a nexthop or nexthop-group is translated to a default + route in a table with the nexthops specified as the nexthops for the + default route. + + diff --git a/lib/command.c b/lib/command.c index b289cdd7a3cb..47d96ac40f93 100644 --- a/lib/command.c +++ b/lib/command.c @@ -110,6 +110,7 @@ const char *node_names[] = { "as list", // AS_LIST_NODE, "community list", // COMMUNITY_LIST_NODE, "routemap", // RMAP_NODE, + "pbr-map", // PBRMAP_NODE, "smux", // SMUX_NODE, "dump", // DUMP_NODE, "forwarding", // FORWARDING_NODE, @@ -1314,6 +1315,7 @@ void cmd_exit(struct vty *vty) case KEYCHAIN_NODE: case MASC_NODE: case RMAP_NODE: + case PBRMAP_NODE: case VTY_NODE: vty->node = CONFIG_NODE; break; @@ -1411,6 +1413,7 @@ DEFUN (config_end, case BGP_EVPN_VNI_NODE: case BGP_IPV6L_NODE: case RMAP_NODE: + case PBRMAP_NODE: case OSPF_NODE: case OSPF6_NODE: case LDP_NODE: diff --git a/lib/command.h b/lib/command.h index 1e700aaa8f8c..bf525d92c75d 100644 --- a/lib/command.h +++ b/lib/command.h @@ -133,6 +133,7 @@ enum node_type { AS_LIST_NODE, /* AS list node. */ COMMUNITY_LIST_NODE, /* Community list node. */ RMAP_NODE, /* Route map node. */ + PBRMAP_NODE, /* PBR map node. */ SMUX_NODE, /* SNMP configuration node. */ DUMP_NODE, /* Packet dump node. */ FORWARDING_NODE, /* IP forwarding node. */ diff --git a/lib/nexthop.c b/lib/nexthop.c index fb7ccc169e70..24e52f78b607 100644 --- a/lib/nexthop.c +++ b/lib/nexthop.c @@ -31,6 +31,7 @@ #include "prefix.h" #include "nexthop.h" #include "mpls.h" +#include "jhash.h" DEFINE_MTYPE_STATIC(LIB, NEXTHOP, "Nexthop") DEFINE_MTYPE_STATIC(LIB, NH_LABEL, "Nexthop label") @@ -310,3 +311,15 @@ unsigned int nexthop_level(struct nexthop *nexthop) return rv; } + +uint32_t nexthop_hash(struct nexthop *nexthop) +{ + uint32_t key; + + key = jhash_1word(nexthop->vrf_id, 0x45afe398); + key = jhash_1word(nexthop->ifindex, key); + key = jhash_1word(nexthop->type, key); + key = jhash(&nexthop->gate, sizeof(union g_addr), key); + + return key; +} diff --git a/lib/nexthop.h b/lib/nexthop.h index 568243d3a9b7..9a556fb54c39 100644 --- a/lib/nexthop.h +++ b/lib/nexthop.h @@ -118,6 +118,23 @@ void nexthop_add_labels(struct nexthop *, enum lsp_types_t, u_int8_t, mpls_label_t *); void nexthop_del_labels(struct nexthop *); +/* + * Hash a nexthop. Suitable for use with hash tables. + * + * This function uses the following values when computing the hash: + * - vrf_id + * - ifindex + * - type + * - gate + * + * nexthop + * The nexthop to hash + * + * Returns: + * 32-bit hash of nexthop + */ +uint32_t nexthop_hash(struct nexthop *nexthop); + extern bool nexthop_same(const struct nexthop *nh1, const struct nexthop *nh2); extern const char *nexthop_type_to_str(enum nexthop_types_t nh_type); diff --git a/lib/route_types.txt b/lib/route_types.txt index 98cada8f8988..8eeca49fa6ad 100644 --- a/lib/route_types.txt +++ b/lib/route_types.txt @@ -79,6 +79,7 @@ ZEBRA_ROUTE_BGP_DIRECT_EXT, bgp-direct-to-nve-groups, NULL, 'e', 0, 0, "BGP2VNC" ZEBRA_ROUTE_BGP_VPN, vpn, NULL, 'c', 1, 1, "VPN", bgpd ZEBRA_ROUTE_BABEL, babel, babeld, 'A', 1, 1, "Babel" ZEBRA_ROUTE_SHARP, sharp, sharpd, 'D', 1, 1, "SHARP" +ZEBRA_ROUTE_PBR, pbr, pbrd, 'F', 1, 1, "PBR" ZEBRA_ROUTE_ALL, wildcard, none, '-', 0, 0, "-" @@ -105,3 +106,4 @@ ZEBRA_ROUTE_VNC_DIRECT, "VNC direct (not via zebra) routes" ZEBRA_ROUTE_BGP_VPN, "BGP VPN routes" ZEBRA_ROUTE_BABEL, "Babel routing protocol (Babel)" ZEBRA_ROUTE_SHARP, "Super Happy Advanced Routing Protocol (sharpd)" +ZEBRA_ROUTE_PBR, "Policy Based Routing (PBR)" diff --git a/lib/vty.c b/lib/vty.c index 4a703f12ba4d..dacbb02604d9 100644 --- a/lib/vty.c +++ b/lib/vty.c @@ -719,6 +719,7 @@ static void vty_end_config(struct vty *vty) case BGP_EVPN_NODE: case BGP_IPV6L_NODE: case RMAP_NODE: + case PBRMAP_NODE: case OSPF_NODE: case OSPF6_NODE: case LDP_NODE: @@ -1116,6 +1117,7 @@ static void vty_stop_input(struct vty *vty) case EIGRP_NODE: case BGP_NODE: case RMAP_NODE: + case PBRMAP_NODE: case OSPF_NODE: case OSPF6_NODE: case LDP_NODE: diff --git a/pbrd/.gitignore b/pbrd/.gitignore new file mode 100644 index 000000000000..f8142d776921 --- /dev/null +++ b/pbrd/.gitignore @@ -0,0 +1,16 @@ +!Makefile +Makefile.in +libpbr.a +pbrd +tags +TAGS +.deps +*.o +*.lo +*.la +*.libs +.arch-inventory +.arch-ids +*clippy.c +*~ +*.loT diff --git a/pbrd/Makefile b/pbrd/Makefile new file mode 100644 index 000000000000..e8999c3409ca --- /dev/null +++ b/pbrd/Makefile @@ -0,0 +1,10 @@ +all: ALWAYS + @$(MAKE) -s -C .. pbrd/pbrd +%: ALWAYS + @$(MAKE) -s -C .. pbrd/$@ + +Makefile: + #nothing +ALWAYS: +.PHONY: ALWAYS makefiles +.SUFFIXES: diff --git a/pbrd/pbr_debug.c b/pbrd/pbr_debug.c new file mode 100644 index 000000000000..1634595128ae --- /dev/null +++ b/pbrd/pbr_debug.c @@ -0,0 +1,171 @@ +/* + * PBR - debugging + * Copyright (C) 2018 Cumulus Networks, Inc. + * Quentin Young + * + * This program 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 of the License, or (at your option) + * any later version. + * + * This program 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 + +#include "debug.h" +#include "command.h" +#include "vector.h" + +#ifndef VTYSH_EXTRACT_PL +#include "pbrd/pbr_debug_clippy.c" +#endif +#include "pbrd/pbr_debug.h" + +struct debug pbr_dbg_map = {0, "PBR map"}; +struct debug pbr_dbg_zebra = {0, "PBR Zebra communications"}; +struct debug pbr_dbg_nht = {0, "PBR nexthop tracking"}; +struct debug pbr_dbg_event = {0, "PBR events"}; + +struct debug *pbr_debugs[] = {&pbr_dbg_map, &pbr_dbg_zebra, &pbr_dbg_nht, + &pbr_dbg_event}; + +const char *pbr_debugs_conflines[] = { + "debug pbr map", + "debug pbr zebra", + "debug pbr nht", + "debug pbr events", +}; + +/* + * Set or unset flags on all debugs for pbrd. + * + * flags + * The flags to set + * + * set + * Whether to set or unset the specified flags + */ +static void pbr_debug_set_all(uint32_t flags, bool set) +{ + for (unsigned int i = 0; i < array_size(pbr_debugs); i++) { + DEBUG_FLAGS_SET(pbr_debugs[i], flags, set); + + /* if all modes have been turned off, don't preserve options */ + if (!DEBUG_MODE_CHECK(pbr_debugs[i], DEBUG_MODE_ALL)) + DEBUG_CLEAR(pbr_debugs[i]); + } +} + +/* + * Check flags on all debugs for pbrd. + * + * flags + * The flags to set + * + * Returns: + * The subset of the given flags that were set in all pbrd debugs + */ +static uint32_t pbr_debug_check_all(uint32_t flags) +{ + uint32_t mode = DEBUG_MODE_ALL; + for (unsigned int i = 0; i < array_size(pbr_debugs); i++) + mode &= DEBUG_MODE_CHECK(pbr_debugs[i], flags); + return mode; +} + +static int pbr_debug_config_write_helper(struct vty *vty, bool config) +{ + uint32_t mode = DEBUG_MODE_ALL; + + if (config) + mode = DEBUG_MODE_CONF; + + if (pbr_debug_check_all(DEBUG_MODE_CONF) == mode) { + vty_out(vty, "debug pbr\n"); + return 0; + } + + for (unsigned int i = 0; i < array_size(pbr_debugs); i++) + if (DEBUG_MODE_CHECK(pbr_debugs[i], mode)) + vty_out(vty, "%s\n", pbr_debugs_conflines[i]); + return 0; +} + +int pbr_debug_config_write(struct vty *vty) +{ + return pbr_debug_config_write_helper(vty, true); +} + +/* PBR debugging CLI ------------------------------------------------------- */ +/* clang-format off */ + +DEFPY(debug_pbr, + debug_pbr_cmd, + "[no] debug pbr [{map$map|zebra$zebra|nht$nht|events$events}]", + NO_STR + DEBUG_STR + "Policy Based Routing\n" + "Policy maps\n" + "PBRD <-> Zebra communications\n" + "Nexthop tracking\n" + "Events\n") +{ + uint32_t mode = DEBUG_NODE2MODE(vty->node); + if (map) + DEBUG_MODE_SET(&pbr_dbg_map, mode, !no); + if (zebra) + DEBUG_MODE_SET(&pbr_dbg_zebra, mode, !no); + if (nht) + DEBUG_MODE_SET(&pbr_dbg_nht, mode, !no); + if (events) + DEBUG_MODE_SET(&pbr_dbg_event, mode, !no); + + /* no specific debug --> act on all of them */ + if (strmatch(argv[argc - 1]->text, "pbr")) + pbr_debug_set_all(mode, !no); + + return CMD_SUCCESS; +} + +DEFUN_NOSH(show_debugging_pbr, + show_debugging_pbr_cmd, + "show debugging [pbr]", + SHOW_STR + DEBUG_STR + "Policy Based Routing\n") +{ + vty_out(vty, "PBR debugging status:\n"); + + pbr_debug_config_write_helper(vty, false); + + return CMD_SUCCESS; +} + +/* clang-format on */ +/* ------------------------------------------------------------------------- */ + +static struct cmd_node debug_node = {DEBUG_NODE, "", 1}; + +struct debug_callbacks pbr_dbg_cbs = {.debug_set_all = pbr_debug_set_all}; + +void pbr_debug_init(void) +{ + debug_init(&pbr_dbg_cbs); +} + +void pbr_debug_init_vty(void) +{ + install_node(&debug_node, pbr_debug_config_write); + + install_element(VIEW_NODE, &debug_pbr_cmd); + install_element(CONFIG_NODE, &debug_pbr_cmd); + + install_element(VIEW_NODE, &show_debugging_pbr_cmd); +} diff --git a/pbrd/pbr_debug.h b/pbrd/pbr_debug.h new file mode 100644 index 000000000000..274472462913 --- /dev/null +++ b/pbrd/pbr_debug.h @@ -0,0 +1,53 @@ +/* + * PBR - debugging + * Copyright (C) 2018 Cumulus Networks, Inc. + * Quentin Young + * + * This program 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 of the License, or (at your option) + * any later version. + * + * This program 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 + */ +#ifndef __PBR_DEBUG_H__ +#define __PBR_DEBUG_H__ + +#include + +#include "debug.h" + +/* PBR debugging records */ +extern struct debug pbr_dbg_map; +extern struct debug pbr_dbg_zebra; +extern struct debug pbr_dbg_nht; +extern struct debug pbr_dbg_event; + +/* + * Initialize PBR debugging. + * + * Installs VTY commands and registers callbacks. + */ +void pbr_debug_init(void); + +/* + * Install PBR debugging VTY commands. + */ +void pbr_debug_init_vty(void); + +/* + * Print PBR debugging configuration. + * + * vty + * VTY to print debugging configuration to. + */ +int pbr_debug_config_write(struct vty *vty); + +#endif /* __PBR_DEBUG_H__ */ diff --git a/pbrd/pbr_event.c b/pbrd/pbr_event.c new file mode 100644 index 000000000000..72a59801b4f9 --- /dev/null +++ b/pbrd/pbr_event.c @@ -0,0 +1,219 @@ +/* + * PBR-event Code + * Copyright (C) 2018 Cumulus Networks, Inc. + * Donald Sharp + * + * This file is part of FRR. + * + * 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 + +#include +#include +#include +#include +#include + +#include "pbrd/pbr_event.h" +#include "pbrd/pbr_map.h" +#include "pbrd/pbr_nht.h" +#include "pbrd/pbr_memory.h" +#include "pbrd/pbr_debug.h" + +DEFINE_MTYPE_STATIC(PBRD, PBR_EVENT, "Event WorkQueue") + +struct work_queue *pbr_event_wq; + +static const char *pbr_event_wqentry2str(struct pbr_event *pbre, + char *buffer, size_t buflen) +{ + switch(pbre->event) { + case PBR_NHG_NEW: + snprintf(buffer, buflen, "Nexthop Group Added %s", + pbre->name); + break; + case PBR_NHG_ADD_NEXTHOP: + snprintf(buffer, buflen, "Nexthop Group Nexthop Added %s", + pbre->name); + break; + case PBR_NHG_DEL_NEXTHOP: + snprintf(buffer, buflen, "Nexthop Group Nexthop Deleted %s", + pbre->name); + break; + case PBR_NHG_DELETE: + snprintf(buffer, buflen, "Nexthop Group Deleted %s", + pbre->name); + break; + case PBR_MAP_NEXTHOP_ADD: + snprintf(buffer, buflen, "Nexthop Added to %s(%d)", pbre->name, + pbre->seqno); + break; + case PBR_MAP_NEXTHOP_DELETE: + snprintf(buffer, buflen, "Nexthop Deleted from %s(%d)", + pbre->name, pbre->seqno); + break; + case PBR_MAP_NHG_ADD: + snprintf(buffer, buflen, "Nexthop Group Added to %s(%d)", + pbre->name, pbre->seqno); + break; + case PBR_MAP_NHG_DELETE: + snprintf(buffer, buflen, "Nexthop Group Deleted from %s(%d)", + pbre->name, pbre->seqno); + break; + case PBR_MAP_ADD: + snprintf(buffer, buflen, "PBR-MAP %s Added", + pbre->name); + break; + case PBR_MAP_MODIFY: + snprintf(buffer, buflen, "PBR_MAP %s Modified", + pbre->name); + break; + case PBR_MAP_DELETE: + snprintf(buffer, buflen, "PBR_MAP %s Deleted", + pbre->name); + break; + case PBR_NH_CHANGED: + snprintf(buffer, buflen, "Nexthop Call back from Zebra"); + break; + case PBR_MAP_INSTALL: + snprintf(buffer, buflen, "PBR_MAP %s Installing into zapi", + pbre->name); + break; + case PBR_POLICY_CHANGED: + snprintf(buffer, buflen, + "PBR-Policy %s applied to an interface", pbre->name); + break; + case PBR_MAP_POLICY_INSTALL: + snprintf(buffer, buflen, "PBR-POLICY installation time for %s", + pbre->name); + break; + case PBR_POLICY_DELETED: + snprintf(buffer, buflen, "PBR-POLICY deleted from %s", + pbre->name); + break; + } + + return buffer; +} + +void pbr_event_free(struct pbr_event **pbre) +{ + XFREE(MTYPE_PBR_EVENT, *pbre); +} + +static void pbr_event_delete_wq(struct work_queue *wq, void *data) +{ + struct pbr_event *pbre = (struct pbr_event *)data; + + XFREE(MTYPE_PBR_EVENT, pbre); +} + +static wq_item_status pbr_event_process_wq(struct work_queue *wq, void *data) +{ + struct pbr_event *pbre = (struct pbr_event *)data; + char buffer[256]; + + DEBUGD(&pbr_dbg_event, "%s: Handling event %s", __PRETTY_FUNCTION__, + pbr_event_wqentry2str(pbre, buffer, sizeof(buffer))); + + switch (pbre->event) { + case PBR_NHG_NEW: + pbr_nht_add_group(pbre->name); + pbr_map_check_nh_group_change(pbre->name); + break; + case PBR_NHG_ADD_NEXTHOP: + pbr_nht_change_group(pbre->name); + pbr_map_check_nh_group_change(pbre->name); + break; + case PBR_NHG_DEL_NEXTHOP: + pbr_nht_change_group(pbre->name); + pbr_map_check_nh_group_change(pbre->name); + break; + case PBR_NHG_DELETE: + pbr_nht_delete_group(pbre->name); + pbr_map_check_nh_group_change(pbre->name); + break; + case PBR_MAP_NEXTHOP_ADD: + pbr_nht_add_individual_nexthop(pbre->name, pbre->seqno); + pbr_map_check(pbre->name, pbre->seqno); + break; + case PBR_MAP_NEXTHOP_DELETE: + pbr_nht_delete_individual_nexthop(pbre->name, pbre->seqno); + pbr_map_check(pbre->name, pbre->seqno); + break; + case PBR_MAP_NHG_ADD: + pbr_map_check(pbre->name, pbre->seqno); + break; + case PBR_MAP_NHG_DELETE: + pbr_map_check(pbre->name, pbre->seqno); + break; + case PBR_MAP_ADD: + pbr_map_add_interfaces(pbre->name); + break; + case PBR_MAP_MODIFY: + pbr_map_check(pbre->name, pbre->seqno); + break; + case PBR_MAP_DELETE: + pbr_map_delete(pbre->name, pbre->seqno); + break; + case PBR_NH_CHANGED: + pbr_map_check_nh_group_change(pbre->name); + break; + case PBR_MAP_INSTALL: + pbr_map_install(pbre->name); + break; + case PBR_POLICY_CHANGED: + pbr_map_check_policy_change(pbre->name); + break; + case PBR_MAP_POLICY_INSTALL: + pbr_map_policy_install(pbre->name); + break; + case PBR_POLICY_DELETED: + pbr_map_policy_delete(pbre->name); + break; + } + + return WQ_SUCCESS; +} + +void pbr_event_enqueue(struct pbr_event *pbre) +{ + work_queue_add(pbr_event_wq, pbre); +} + +struct pbr_event *pbr_event_new(enum pbr_events ev, const char *name) +{ + struct pbr_event *event; + event = XCALLOC(MTYPE_PBR_EVENT, sizeof(struct pbr_event)); + event->event = ev; + if (name) + strlcpy(event->name, name, sizeof(event->name)); + return event; +} + +extern struct thread_master *master; + +void pbr_event_init(void) +{ + pbr_event_wq = work_queue_new(master, "PBR Main Work Queue"); + pbr_event_wq->spec.workfunc = &pbr_event_process_wq; + pbr_event_wq->spec.del_item_data = &pbr_event_delete_wq; +} + +void pbr_event_stop(void) +{ + work_queue_free_and_null(&pbr_event_wq); +} diff --git a/pbrd/pbr_event.h b/pbrd/pbr_event.h new file mode 100644 index 000000000000..2d895d969413 --- /dev/null +++ b/pbrd/pbr_event.h @@ -0,0 +1,137 @@ +/* + * PBR-event Header + * Copyright (C) 2018 Cumulus Networks, Inc. + * Donald Sharp + * + * This file is part of FRR. + * + * 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 + */ +#ifndef __PBR_EVENT_H__ +#define __PBR_EVENT_H__ + +enum pbr_events { + /* + * A NHG has been added to the system, handle it + */ + PBR_NHG_NEW, + + /* + * A NHG has been modified( added a new nexthop ) + */ + PBR_NHG_ADD_NEXTHOP, + + /* + * A NHG has been modified( deleted a nexthop ) + */ + PBR_NHG_DEL_NEXTHOP, + + /* + * A NHG has been deleted from the system + */ + PBR_NHG_DELETE, + + /* + * A individual nexthop has been added + */ + PBR_MAP_NEXTHOP_ADD, + + /* + * A individual nexthop has been deleted + */ + PBR_MAP_NEXTHOP_DELETE, + + /* + * A nexthop group has been added to a pbr-map + */ + PBR_MAP_NHG_ADD, + + /* + * A nexthop group has been deleted from a pbr-map + */ + PBR_MAP_NHG_DELETE, + + /* + * A new pbr-map has been created + */ + PBR_MAP_ADD, + + /* + * The pbr-map has been modified in some fashion + */ + PBR_MAP_MODIFY, + + /* + * The pbr-map has been deleted from the system + */ + PBR_MAP_DELETE, + + /* + * Start the sequence of events to install/remove the policy + * from being installed + */ + PBR_MAP_INSTALL, + + /* + * We believe we have gotten enough information to actually + * install the rule portion, since the nexthops are installed + */ + PBR_MAP_POLICY_INSTALL, + + /* + * Callbacks for a Nexthop in a nexthop group has been + * changed in some fashion + */ + PBR_NH_CHANGED, + + /* + * Callback for when a policy has been applied to an interface + */ + PBR_POLICY_CHANGED, + + /* + * Callback for when a interface has been issued a no + * policy command + */ + PBR_POLICY_DELETED, +}; + +struct pbr_event { + enum pbr_events event; + + char name[100]; + union g_addr addr; + uint32_t seqno; +}; + +/* + * Return a event structure that can be filled in and enqueued. + * Assume this memory is owned by the event subsystem. + */ +extern struct pbr_event *pbr_event_new(enum pbr_events ev, const char *name); + +/* + * Free the associated pbr_event item + */ +extern void pbr_event_free(struct pbr_event **pbre); + +/* + * Enqueue an event for later processing + */ +void pbr_event_enqueue(struct pbr_event *pbre); + +extern void pbr_event_init(void); +extern void pbr_event_stop(void); +#endif diff --git a/pbrd/pbr_main.c b/pbrd/pbr_main.c new file mode 100644 index 000000000000..569540fd0366 --- /dev/null +++ b/pbrd/pbr_main.c @@ -0,0 +1,170 @@ +/* + * PBR - main code + * Copyright (C) Cumulus Networks, Inc. + * Donald Sharp + * + * This file is part of FRR. + * + * 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 + +#include +#include "getopt.h" +#include "thread.h" +#include "prefix.h" +#include "linklist.h" +#include "if.h" +#include "vector.h" +#include "vty.h" +#include "command.h" +#include "filter.h" +#include "plist.h" +#include "stream.h" +#include "log.h" +#include "memory.h" +#include "privs.h" +#include "sigevent.h" +#include "zclient.h" +#include "keychain.h" +#include "distribute.h" +#include "libfrr.h" +#include "routemap.h" +#include "nexthop.h" +#include "nexthop_group.h" + +#include "pbr_nht.h" +#include "pbr_map.h" +#include "pbr_zebra.h" +#include "pbr_event.h" +#include "pbr_vty.h" +#include "pbr_debug.h" + +zebra_capabilities_t _caps_p[] = { + ZCAP_NET_RAW, ZCAP_BIND, ZCAP_NET_ADMIN, +}; + +struct zebra_privs_t pbr_privs = { +#if defined(FRR_USER) && defined(FRR_GROUP) + .user = FRR_USER, + .group = FRR_GROUP, +#endif +#if defined(VTY_GROUP) + .vty_group = VTY_GROUP, +#endif + .caps_p = _caps_p, + .cap_num_p = array_size(_caps_p), + .cap_num_i = 0}; + +struct option longopts[] = {{0}}; + +/* Master of threads. */ +struct thread_master *master; + +/* SIGHUP handler. */ +static void sighup(void) +{ + zlog_info("SIGHUP received"); +} + +/* SIGINT / SIGTERM handler. */ +static void sigint(void) +{ + zlog_notice("Terminating on signal"); + + pbr_event_stop(); + exit(0); +} + +/* SIGUSR1 handler. */ +static void sigusr1(void) +{ + zlog_rotate(); +} + +struct quagga_signal_t pbr_signals[] = { + { + .signal = SIGHUP, + .handler = &sighup, + }, + { + .signal = SIGUSR1, + .handler = &sigusr1, + }, + { + .signal = SIGINT, + .handler = &sigint, + }, + { + .signal = SIGTERM, + .handler = &sigint, + }, +}; + +#define PBR_VTY_PORT 2615 + +FRR_DAEMON_INFO(pbrd, PBR, .vty_port = PBR_VTY_PORT, + + .proghelp = "Implementation of PBR.", + + .signals = pbr_signals, + .n_signals = array_size(pbr_signals), + + .privs = &pbr_privs, ) + +int main(int argc, char **argv, char **envp) +{ + frr_preinit(&pbrd_di, argc, argv); + frr_opt_add("", longopts, ""); + + while (1) { + int opt; + + opt = frr_getopt(argc, argv, NULL); + + if (opt == EOF) + break; + + switch (opt) { + case 0: + break; + default: + frr_help_exit(1); + break; + } + } + + master = frr_init(); + + pbr_debug_init(); + + vrf_init(NULL, NULL, NULL, NULL); + nexthop_group_init(pbr_nhgroup_add_cb, + pbr_nhgroup_add_nexthop_cb, + pbr_nhgroup_del_nexthop_cb, + pbr_nhgroup_delete_cb); + + pbr_event_init(); + pbr_nht_init(); + pbr_map_init(); + pbr_zebra_init(); + pbr_vty_init(); + + frr_config_fork(); + frr_run(master); + + /* Not reached. */ + return 0; +} diff --git a/pbrd/pbr_map.c b/pbrd/pbr_map.c new file mode 100644 index 000000000000..e1ce10807dd1 --- /dev/null +++ b/pbrd/pbr_map.c @@ -0,0 +1,632 @@ +/* + * PBR-map Code + * Copyright (C) 2018 Cumulus Networks, Inc. + * Donald Sharp + * + * This file is part of FRR. + * + * 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 + +#include "thread.h" +#include "linklist.h" +#include "prefix.h" +#include "table.h" +#include "vrf.h" +#include "nexthop.h" +#include "nexthop_group.h" +#include "memory.h" +#include "log.h" +#include "vty.h" + +#include "pbr_nht.h" +#include "pbr_map.h" +#include "pbr_event.h" +#include "pbr_zebra.h" +#include "pbr_memory.h" +#include "pbr_debug.h" + +DEFINE_MTYPE_STATIC(PBRD, PBR_MAP, "PBR Map") +DEFINE_MTYPE_STATIC(PBRD, PBR_MAP_SEQNO, "PBR Map Sequence") +DEFINE_MTYPE_STATIC(PBRD, PBR_MAP_INTERFACE, "PBR Map Interface") + +static uint32_t pbr_map_sequence_unique; + +static __inline int pbr_map_compare(const struct pbr_map *pbrmap1, + const struct pbr_map *pbrmap2); + +RB_GENERATE(pbr_map_entry_head, pbr_map, pbr_map_entry, pbr_map_compare) + +struct pbr_map_entry_head pbr_maps = RB_INITIALIZER(&pbr_maps); + +DEFINE_QOBJ_TYPE(pbr_map_sequence) + +static __inline int pbr_map_compare(const struct pbr_map *pbrmap1, + const struct pbr_map *pbrmap2) +{ + return strcmp(pbrmap1->name, pbrmap2->name); +} + +static int pbr_map_sequence_compare(const struct pbr_map_sequence *pbrms1, + const struct pbr_map_sequence *pbrms2) +{ + if (pbrms1->seqno == pbrms2->seqno) + return 0; + + if (pbrms1->seqno < pbrms2->seqno) + return -1; + + return 1; +} + +static void pbr_map_sequence_delete(struct pbr_map_sequence *pbrms) +{ + if (pbrms->internal_nhg_name) + XFREE(MTYPE_TMP, pbrms->internal_nhg_name); + + XFREE(MTYPE_PBR_MAP_SEQNO, pbrms); +} + +static int pbr_map_interface_compare(const struct pbr_map_interface *pmi1, + const struct pbr_map_interface *pmi2) +{ + return strcmp(pmi1->ifp->name, pmi2->ifp->name); +} + +static void pbr_map_interface_list_delete(const struct pbr_map_interface *pmi) +{ + pbr_map_policy_delete(pmi->ifp->name); +} + +static const char *pbr_map_reason_str[] = { + "Invalid NH-group", "Invalid NH", "No Nexthops", + "Both NH and NH-Group", "Invalid Src or Dst", "Deleting Sequence", +}; + +void pbr_map_reason_string(unsigned int reason, char *buf, int size) +{ + unsigned int bit; + int len = 0; + + if (!buf) + return; + + for (bit = 0; bit < array_size(pbr_map_reason_str); bit++) { + if ((reason & (1 << bit)) && (len < size)) { + len += snprintf((buf + len), (size - len), "%s%s", + (len > 0) ? ", " : "", + pbr_map_reason_str[bit]); + } + } +} + + +void pbr_map_interface_delete(struct pbr_map *pbrm, struct interface *ifp_del) +{ + + struct listnode *node; + struct pbr_map_interface *pmi; + struct pbr_event *pbre; + + for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, node, pmi)) { + if (ifp_del == pmi->ifp) + break; + } + + if (pmi) { + pmi->delete = true; + + pbre = pbr_event_new(PBR_POLICY_DELETED, pmi->ifp->name); + pbr_event_enqueue(pbre); + } +} + +void pbr_map_add_interface(struct pbr_map *pbrm, struct interface *ifp_add) +{ + struct listnode *node; + struct pbr_map_interface *pmi; + struct pbr_event *pbre; + + for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, node, pmi)) { + if (ifp_add == pmi->ifp) + return; + } + + pmi = XCALLOC(MTYPE_PBR_MAP_INTERFACE, sizeof(*pmi)); + pmi->ifp = ifp_add; + pmi->pbrm = pbrm; + listnode_add_sort(pbrm->incoming, pmi); + + pbre = pbr_event_new(PBR_POLICY_CHANGED, pbrm->name); + pbr_event_enqueue(pbre); +} + +void pbr_map_write_interfaces(struct vty *vty, struct interface *ifp) +{ + struct pbr_interface *pbr_ifp = ifp->info; + + if (!(strcmp(pbr_ifp->mapname, "") == 0)) + vty_out(vty, " pbr-policy %s\n", pbr_ifp->mapname); +} + +struct pbr_map *pbrm_find(const char *name) +{ + struct pbr_map pbrm; + + strlcpy(pbrm.name, name, sizeof(pbrm.name)); + + return RB_FIND(pbr_map_entry_head, &pbr_maps, &pbrm); +} + +extern void pbr_map_delete(const char *name, uint32_t seqno) +{ + struct pbr_map *pbrm; + struct pbr_map_sequence *pbrms; + struct listnode *node, *nnode; + bool uninstall = false; + + pbrm = pbrm_find(name); + + for (ALL_LIST_ELEMENTS(pbrm->seqnumbers, node, nnode, pbrms)) { + if (pbrms->reason & PBR_MAP_DEL_SEQUENCE_NUMBER) { + uninstall = true; + break; + } + } + + if (uninstall) + pbr_send_pbr_map(pbrm, 0); + + for (ALL_LIST_ELEMENTS(pbrm->seqnumbers, node, nnode, pbrms)) { + if (!(pbrms->reason & PBR_MAP_DEL_SEQUENCE_NUMBER)) + continue; + + if (pbrms->nhg) + pbr_nht_delete_individual_nexthop(pbrms->parent->name, + pbrms->seqno); + + listnode_delete(pbrm->seqnumbers, pbrms); + } + + if (pbrm->seqnumbers->count == 0) { + RB_REMOVE(pbr_map_entry_head, &pbr_maps, pbrm); + XFREE(MTYPE_PBR_MAP, pbrm); + } +} + +extern struct pbr_map_sequence *pbrms_lookup_unique(uint32_t unique, + ifindex_t ifindex) +{ + struct pbr_map_sequence *pbrms; + struct listnode *snode, *inode; + struct pbr_map_interface *pmi; + struct pbr_map *pbrm; + + RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) { + for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi)) { + if (pmi->ifp->ifindex != ifindex) + continue; + + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, snode, + pbrms)) { + DEBUGD(&pbr_dbg_map, "%s: Comparing %u to %u", + __PRETTY_FUNCTION__, pbrms->unique, + unique); + if (pbrms->unique == unique) + return pbrms; + } + } + } + + return NULL; +} + +extern struct pbr_map_sequence *pbrms_get(const char *name, uint32_t seqno) +{ + struct pbr_map *pbrm; + struct pbr_map_sequence *pbrms; + struct listnode *node; + struct pbr_event *pbre; + + pbrm = pbrm_find(name); + if (!pbrm) { + pbrm = XCALLOC(MTYPE_PBR_MAP, sizeof(*pbrm)); + strcpy(pbrm->name, name); + + pbrm->seqnumbers = list_new(); + pbrm->seqnumbers->cmp = + (int (*)(void *, void *))pbr_map_sequence_compare; + pbrm->seqnumbers->del = + (void (*)(void *))pbr_map_sequence_delete; + + pbrm->incoming = list_new(); + pbrm->incoming->cmp = + (int (*)(void *, void *))pbr_map_interface_compare; + pbrm->incoming->del = + (void (*)(void *))pbr_map_interface_list_delete; + + RB_INSERT(pbr_map_entry_head, &pbr_maps, pbrm); + + pbre = pbr_event_new(PBR_MAP_ADD, name); + } else + pbre = NULL; + + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) { + if (pbrms->seqno == seqno) + break; + + } + + if (!pbrms) { + pbrms = XCALLOC(MTYPE_PBR_MAP_SEQNO, sizeof(*pbrms)); + pbrms->unique = pbr_map_sequence_unique++; + pbrms->seqno = seqno; + pbrms->ruleno = pbr_nht_get_next_rule(seqno); + pbrms->parent = pbrm; + pbrms->reason = + PBR_MAP_INVALID_SRCDST | + PBR_MAP_INVALID_NO_NEXTHOPS; + + QOBJ_REG(pbrms, pbr_map_sequence); + listnode_add_sort(pbrm->seqnumbers, pbrms); + + pbrm->installed = false; + } + + if (pbre) + pbr_event_enqueue(pbre); + + return pbrms; +} + +static void +pbr_map_sequence_check_nexthops_valid(struct pbr_map_sequence *pbrms) +{ + /* + * Check validness of the nexthop or nexthop-group + */ + if (!pbrms->nhg && !pbrms->nhgrp_name) + pbrms->reason |= PBR_MAP_INVALID_NO_NEXTHOPS; + + if (pbrms->nhg && pbrms->nhgrp_name) + pbrms->reason |= PBR_MAP_INVALID_BOTH_NHANDGRP; + + if (pbrms->nhg && + !pbr_nht_nexthop_group_valid(pbrms->internal_nhg_name)) + pbrms->reason |= PBR_MAP_INVALID_NEXTHOP; + + if (pbrms->nhgrp_name) { + if (!pbr_nht_nexthop_group_valid(pbrms->nhgrp_name)) + pbrms->reason |= PBR_MAP_INVALID_NEXTHOP_GROUP; + else + pbrms->nhs_installed = true; + } +} + +static void pbr_map_sequence_check_src_dst_valid(struct pbr_map_sequence *pbrms) +{ + if (!pbrms->src && !pbrms->dst) + pbrms->reason |= PBR_MAP_INVALID_SRCDST; +} + +/* + * Checks to see if we think that the pbmrs is valid. If we think + * the config is valid return true. + */ +static void pbr_map_sequence_check_valid(struct pbr_map_sequence *pbrms) +{ + pbr_map_sequence_check_nexthops_valid(pbrms); + + pbr_map_sequence_check_src_dst_valid(pbrms); +} + +static bool pbr_map_check_valid_internal(struct pbr_map *pbrm) +{ + struct pbr_map_sequence *pbrms; + struct listnode *node; + + pbrm->valid = true; + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) { + pbrms->reason = 0; + pbr_map_sequence_check_valid(pbrms); + /* + * A pbr_map_sequence that is invalid causes + * the whole shebang to be invalid + */ + if (pbrms->reason != 0) + pbrm->valid = false; + } + + return pbrm->valid; +} + +/* + * For a given PBR-MAP check to see if we think it is a + * valid config or not. If so note that it is and return + * that we are valid. + */ +extern bool pbr_map_check_valid(const char *name) +{ + struct pbr_map *pbrm; + + pbrm = pbrm_find(name); + if (!pbrm) { + DEBUGD(&pbr_dbg_map, + "%s: Specified PBR-MAP(%s) does not exist?", + __PRETTY_FUNCTION__, name); + return false; + } + + pbr_map_check_valid_internal(pbrm); + return pbrm->valid; +} + +extern void pbr_map_schedule_policy_from_nhg(const char *nh_group) +{ + struct pbr_map_sequence *pbrms; + struct pbr_event *pbre; + struct pbr_map *pbrm; + struct listnode *node; + + RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) { + DEBUGD(&pbr_dbg_map, "%s: Looking at %s", __PRETTY_FUNCTION__, + pbrm->name); + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) { + DEBUGD(&pbr_dbg_map, "\tNH Grp name: %s", + pbrms->nhgrp_name ? pbrms->nhgrp_name : "NULL"); + + if (pbrms->nhgrp_name + && (strcmp(nh_group, pbrms->nhgrp_name) == 0)) { + pbrms->nhs_installed = true; + + pbre = pbr_event_new(PBR_MAP_MODIFY, + pbrm->name); + pbre->seqno = pbrms->seqno; + + pbr_event_enqueue(pbre); + } + + if (pbrms->nhg + && (strcmp(nh_group, pbrms->internal_nhg_name) + == 0)) { + pbrms->nhs_installed = true; + + pbre = pbr_event_new(PBR_MAP_MODIFY, + pbrm->name); + pbre->seqno = pbrms->seqno; + + pbr_event_enqueue(pbre); + } + } + } +} + +extern void pbr_map_policy_install(const char *name) +{ + struct pbr_map_sequence *pbrms; + struct pbr_map *pbrm; + struct listnode *node; + bool install; + + DEBUGD(&pbr_dbg_map, "%s: for %s", __PRETTY_FUNCTION__, name); + pbrm = pbrm_find(name); + if (!pbrm) + return; + + install = true; + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) { + DEBUGD(&pbr_dbg_map, + "%s: Looking at what to install %s(%u) %d %d", + __PRETTY_FUNCTION__, name, pbrms->seqno, pbrm->valid, + pbrms->nhs_installed); + if (!pbrm->valid || !pbrms->nhs_installed) + install = false; + } + + if (install && pbrm->incoming->count) { + DEBUGD(&pbr_dbg_map, "\tInstalling"); + pbr_send_pbr_map(pbrm, true); + } +} + +extern void pbr_map_policy_delete(const char *ifname) +{ + struct listnode *node, *nnode; + struct pbr_map_interface *pmi; + struct pbr_map *pbrm; + + RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) { + for (ALL_LIST_ELEMENTS(pbrm->incoming, node, nnode, pmi)) { + DEBUGD(&pbr_dbg_map, "Comparing %s to %s %d", + pmi->ifp->name, ifname, pmi->delete); + if (strcmp(ifname, pmi->ifp->name) != 0) + continue; + + pbr_send_pbr_map(pbrm, false); + listnode_delete(pbrm->incoming, pmi); + pmi->pbrm = NULL; + XFREE(MTYPE_PBR_MAP_INTERFACE, pmi); + } + } +} + +/* + * For a nexthop group specified, see if any of the pbr-maps + * are using it and if so, check to see that we are still + * valid for usage. If we are valid then schedule the installation/deletion + * of the pbr-policy. + */ +extern void pbr_map_check_nh_group_change(const char *nh_group) +{ + struct pbr_map_sequence *pbrms; + struct pbr_map *pbrm; + struct listnode *node; + bool found_name; + + zlog_warn("*** %s for %s ***", __func__, nh_group); + RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) { + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) { + found_name = false; + if (pbrms->nhgrp_name) + found_name = + !strcmp(nh_group, pbrms->nhgrp_name); + else if (pbrms->nhg) + found_name = !strcmp(nh_group, + pbrms->internal_nhg_name); + + if (found_name) { + bool original = pbrm->valid; + zlog_warn("*** %s pbrm->valid is %u ***", + __func__, pbrm->valid); + + pbr_map_check_valid_internal(pbrm); + + if (original != pbrm->valid) { + struct pbr_event *pbre; + + pbre = pbr_event_new(PBR_MAP_INSTALL, + pbrm->name); + pbr_event_enqueue(pbre); + } + break; + } + } + } +} + +extern void pbr_map_check(const char *name, uint32_t seqno) +{ + struct pbr_map_sequence *pbrms; + struct listnode *node; + struct pbr_map *pbrm; + + DEBUGD(&pbr_dbg_map, "%s: for %s(%u)", __PRETTY_FUNCTION__, name, + seqno); + if (pbr_map_check_valid(name)) + DEBUGD(&pbr_dbg_map, "We are totally valid %s\n", name); + + pbrm = pbrm_find(name); + if (!pbrm) + return; + + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) { + if (seqno != pbrms->seqno) + continue; + + DEBUGD(&pbr_dbg_map, "%s: Installing %s(%u) reason: %" PRIu64, + __PRETTY_FUNCTION__, name, seqno, pbrms->reason); + + if (pbrms->reason == PBR_MAP_VALID_SEQUENCE_NUMBER) { + struct pbr_event *pbre; + + DEBUGD(&pbr_dbg_map, + "%s: Installing %s(%u) reason: %" PRIu64, + __PRETTY_FUNCTION__, name, seqno, pbrms->reason); + DEBUGD(&pbr_dbg_map, + "\tSending PBR_MAP_POLICY_INSTALL event"); + + pbre = pbr_event_new(PBR_MAP_POLICY_INSTALL, + pbrm->name); + pbre->event = PBR_MAP_POLICY_INSTALL; + strcpy(pbre->name, pbrm->name); + + pbr_event_enqueue(pbre); + + break; + } else { + DEBUGD(&pbr_dbg_map, + "%s: Removing %s(%u) reason: %" PRIu64, + __PRETTY_FUNCTION__, name, seqno, pbrms->reason); + pbr_send_pbr_map(pbrm, false); + break; + } + } +} + +extern void pbr_map_install(const char *name) +{ + struct pbr_map *pbrm; + + pbrm = pbrm_find(name); + if (!pbrm) { + DEBUGD(&pbr_dbg_map, + "%s: Specified PBR-MAP(%s) does not exist?", + __PRETTY_FUNCTION__, name); + return; + } + + if (!pbrm->incoming->count) + return; + + pbr_send_pbr_map(pbrm, true); + pbrm->installed = true; +} + +extern void pbr_map_add_interfaces(const char *name) +{ + struct pbr_map *pbrm; + struct interface *ifp; + struct pbr_interface *pbr_ifp; + struct vrf *vrf; + + pbrm = pbrm_find(name); + if (!pbrm) { + DEBUGD(&pbr_dbg_map, + "%s: Specified PBR-MAP(%s) does not exist?", + __PRETTY_FUNCTION__, name); + return; + } + + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { + FOR_ALL_INTERFACES (vrf, ifp) { + if (ifp->info) { + pbr_ifp = ifp->info; + if (strcmp(name, pbr_ifp->mapname) == 0) + pbr_map_add_interface(pbrm, ifp); + } + } + } +} + + +extern void pbr_map_check_policy_change(const char *name) +{ + struct pbr_map *pbrm; + + pbrm = pbrm_find(name); + if (!pbrm) { + DEBUGD(&pbr_dbg_map, + "%s: Specified PBR-MAP(%s) does not exist?", + __PRETTY_FUNCTION__, name); + return; + } + + pbr_map_check_valid(name); + if (pbrm->valid && !pbrm->installed) { + struct pbr_event *pbre; + + pbre = pbr_event_new(PBR_MAP_INSTALL, name); + + pbr_event_enqueue(pbre); + } +} + +extern void pbr_map_init(void) +{ + RB_INIT(pbr_map_entry_head, &pbr_maps); + + pbr_map_sequence_unique = 1; +} diff --git a/pbrd/pbr_map.h b/pbrd/pbr_map.h new file mode 100644 index 000000000000..f6630432a3eb --- /dev/null +++ b/pbrd/pbr_map.h @@ -0,0 +1,155 @@ +/* + * PBR-map Header + * Copyright (C) 2018 Cumulus Networks, Inc. + * Donald Sharp + * + * This file is part of FRR. + * + * 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 + */ +#ifndef __PBR_MAP_H__ +#define __PBR_MAP_H__ + +struct pbr_map { + /* + * RB Tree of the pbr_maps + */ + RB_ENTRY(pbr_map) pbr_map_entry; + + /* + * The name of the PBR_MAP + */ + char name[100]; + + struct list *seqnumbers; + + /* + * The list of incoming interfaces that + * we will apply this policy map onto + */ + struct list *incoming; + + /* + * If valid is true we think the pbr_map is valid, + * If false, look in individual pbrms to see + * what we think is the invalid reason + */ + bool valid; + + bool installed; +}; + +RB_HEAD(pbr_map_entry_head, pbr_map); +RB_PROTOTYPE(pbr_map_entry_head, pbr_map, pbr_map_entry, pbr_map_compare) + +struct pbr_map_interface { + struct interface *ifp; + + struct pbr_map *pbrm; + + bool delete; +}; + +struct pbr_map_sequence { + struct pbr_map *parent; + + /* + * The Unique identifier of this specific pbrms + */ + uint32_t unique; + + /* + * The sequence of where we are for display + */ + uint32_t seqno; + + /* + * The rule number to install into + */ + uint32_t ruleno; + + /* + * Our policy Catchers + */ + struct prefix *src; + struct prefix *dst; + + /* + * The nexthop group we auto create + * for when the user specifies a individual + * nexthop + */ + struct nexthop_group *nhg; + char *internal_nhg_name; + + /* + * The name of the nexthop group + * configured in the pbr-map + */ + char *nhgrp_name; + + /* + * Do we think are nexthops are installed + */ + bool nhs_installed; + + bool installed; + /* + * A reason of 0 means we think the pbr_map_sequence is good to go + * We can accumuluate multiple failure states + */ +#define PBR_MAP_VALID_SEQUENCE_NUMBER 0 +#define PBR_MAP_INVALID_NEXTHOP_GROUP (1 << 0) +#define PBR_MAP_INVALID_NEXTHOP (1 << 1) +#define PBR_MAP_INVALID_NO_NEXTHOPS (1 << 2) +#define PBR_MAP_INVALID_BOTH_NHANDGRP (1 << 3) +#define PBR_MAP_INVALID_SRCDST (1 << 4) +#define PBR_MAP_DEL_SEQUENCE_NUMBER (1 << 5) + uint64_t reason; + + QOBJ_FIELDS +}; + +DECLARE_QOBJ_TYPE(pbr_map_sequence) + +extern struct pbr_map_entry_head pbr_maps; + +extern struct pbr_map_sequence *pbrms_get(const char *name, uint32_t seqno); +extern struct pbr_map_sequence *pbrms_lookup_unique(uint32_t unique, + ifindex_t ifindex); + +extern struct pbr_map *pbrm_find(const char *name); +extern void pbr_map_delete(const char *name, uint32_t seqno); +extern void pbr_map_add_interface(struct pbr_map *pbrm, struct interface *ifp); +extern void pbr_map_interface_delete(struct pbr_map *pbrm, + struct interface *ifp); +extern void pbr_map_write_interfaces(struct vty *vty, struct interface *ifp); +extern void pbr_map_init(void); + +extern bool pbr_map_check_valid(const char *name); + +extern void pbr_map_check(const char *name, uint32_t seqno); +extern void pbr_map_check_nh_group_change(const char *nh_group); +extern void pbr_map_check_policy_change(const char *name); +extern void pbr_map_reason_string(unsigned int reason, char *buf, int size); +extern void pbr_map_add_interfaces(const char *name); + +extern void pbr_map_schedule_policy_from_nhg(const char *nh_group); + +extern void pbr_map_install(const char *name); + +extern void pbr_map_policy_install(const char *name); +extern void pbr_map_policy_delete(const char *ifname); +#endif diff --git a/pbrd/pbr_memory.c b/pbrd/pbr_memory.c new file mode 100644 index 000000000000..6cd74647d99c --- /dev/null +++ b/pbrd/pbr_memory.c @@ -0,0 +1,29 @@ +/* + * PBR memory code. + * Copyright (C) Cumulus Networks, Inc. + * Donald Sharp + * + * This file is part of FRR. + * + * 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 + +#include + +#include "pbrd/pbr_memory.h" + + +DEFINE_MGROUP(PBRD, "pbrd") diff --git a/pbrd/pbr_memory.h b/pbrd/pbr_memory.h new file mode 100644 index 000000000000..babcbee2eb47 --- /dev/null +++ b/pbrd/pbr_memory.h @@ -0,0 +1,26 @@ +/* + * pbr memory code. + * Copyright (C) Cumulus Networks, Inc. + * Donald Sharp + * + * This file is part of FRR. + * + * 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 + */ +#ifndef __PBR_MEMORY_H__ + +DECLARE_MGROUP(PBRD) + +#endif diff --git a/pbrd/pbr_nht.c b/pbrd/pbr_nht.c new file mode 100644 index 000000000000..199642b46299 --- /dev/null +++ b/pbrd/pbr_nht.c @@ -0,0 +1,749 @@ +/* + * PBR-nht Code + * Copyright (C) 2018 Cumulus Networks, Inc. + * Donald Sharp + * + * This file is part of FRR. + * + * 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 + +#include +#include +#include +#include +#include +#include +#include + +#include "pbrd/pbr_nht.h" +#include "pbrd/pbr_map.h" +#include "pbrd/pbr_event.h" +#include "pbrd/pbr_zebra.h" +#include "pbrd/pbr_memory.h" +#include "pbrd/pbr_debug.h" + +DEFINE_MTYPE_STATIC(PBRD, PBR_NHG, "PBR Nexthop Groups") + +static struct hash *pbr_nhg_hash; +static struct hash *pbr_nhrc_hash; + +static uint32_t pbr_nhg_low_table; +static uint32_t pbr_nhg_high_table; +static uint32_t pbr_nhg_low_rule; +static uint32_t pbr_nhg_high_rule; +static bool nhg_tableid[65535]; + +/* + * Nexthop refcount. + */ +struct nhrc { + struct nexthop nexthop; + unsigned int refcount; +}; + +/* Hash functions for pbr_nhrc_hash ---------------------------------------- */ + +static void *pbr_nhrc_hash_alloc(void *p) +{ + struct nhrc *nhrc = XCALLOC(MTYPE_PBR_NHG, sizeof(struct nhrc)); + nhrc->nexthop = *(struct nexthop *)p; + return nhrc; +} + +static int pbr_nhrc_hash_equal(const void *arg1, const void *arg2) +{ + const struct nexthop *nh1, *nh2; + + nh1 = arg1; + nh2 = arg2; + + return nexthop_same(nh1, nh2); +} + +/* ------------------------------------------------------------------------- */ + +static void *pbr_nh_alloc(void *p) +{ + struct pbr_nexthop_cache *new; + struct pbr_nexthop_cache *pnhc = (struct pbr_nexthop_cache *)p; + struct nhrc *nhrc; + + new = XCALLOC(MTYPE_PBR_NHG, sizeof(*new)); + nhrc = hash_get(pbr_nhrc_hash, pnhc->nexthop, pbr_nhrc_hash_alloc); + new->nexthop = &nhrc->nexthop; + + /* Decremented again in pbr_nh_delete */ + ++nhrc->refcount; + + DEBUGD(&pbr_dbg_nht, "%s: Sending nexthop to Zebra", + __PRETTY_FUNCTION__); + + pbr_send_rnh(new->nexthop, true); + + new->valid = false; + return new; +} + +static void pbr_nh_delete(struct pbr_nexthop_cache **pnhc) +{ + struct nhrc *nhrc; + + nhrc = hash_lookup(pbr_nhrc_hash, (*pnhc)->nexthop); + + if (nhrc) + --nhrc->refcount; + if (!nhrc || nhrc->refcount == 0) { + DEBUGD(&pbr_dbg_nht, "%s: Removing nexthop from Zebra", + __PRETTY_FUNCTION__); + pbr_send_rnh((*pnhc)->nexthop, false); + } + if (nhrc && nhrc->refcount == 0) { + hash_release(pbr_nhrc_hash, nhrc); + XFREE(MTYPE_PBR_NHG, nhrc); + } + + XFREE(MTYPE_PBR_NHG, *pnhc); +} + +static uint32_t pbr_nh_hash_key(void *arg) +{ + uint32_t key; + struct pbr_nexthop_cache *pbrnc = (struct pbr_nexthop_cache *)arg; + + key = nexthop_hash(pbrnc->nexthop); + + return key; +} + +static int pbr_nh_hash_equal(const void *arg1, const void *arg2) +{ + const struct pbr_nexthop_cache *pbrnc1 = + (const struct pbr_nexthop_cache *)arg1; + const struct pbr_nexthop_cache *pbrnc2 = + (const struct pbr_nexthop_cache *)arg2; + + if (pbrnc1->nexthop->vrf_id != pbrnc2->nexthop->vrf_id) + return 0; + + if (pbrnc1->nexthop->ifindex != pbrnc2->nexthop->ifindex) + return 0; + + if (pbrnc1->nexthop->type != pbrnc2->nexthop->type) + return 0; + + switch (pbrnc1->nexthop->type) { + case NEXTHOP_TYPE_IFINDEX: + return 1; + case NEXTHOP_TYPE_IPV4_IFINDEX: + case NEXTHOP_TYPE_IPV4: + return pbrnc1->nexthop->gate.ipv4.s_addr + == pbrnc2->nexthop->gate.ipv4.s_addr; + case NEXTHOP_TYPE_IPV6_IFINDEX: + case NEXTHOP_TYPE_IPV6: + return !memcmp(&pbrnc1->nexthop->gate.ipv6, + &pbrnc2->nexthop->gate.ipv6, 16); + case NEXTHOP_TYPE_BLACKHOLE: + return pbrnc1->nexthop->bh_type == pbrnc2->nexthop->bh_type; + } + + /* + * We should not get here + */ + return 0; +} + +void pbr_nhgroup_add_cb(const char *name) +{ + struct pbr_event *pbre; + + pbre = pbr_event_new(PBR_NHG_NEW, name); + + pbr_event_enqueue(pbre); + DEBUGD(&pbr_dbg_nht, "%s: Received ADD cb for %s", __PRETTY_FUNCTION__, + name); +} + +void pbr_nhgroup_add_nexthop_cb(const struct nexthop_group_cmd *nhg, + const struct nexthop *nhop) +{ + struct pbr_event *pbre; + + pbre = pbr_event_new(PBR_NHG_ADD_NEXTHOP, nhg->name); + + pbr_event_enqueue(pbre); + DEBUGD(&pbr_dbg_nht, "%s: Received NEXTHOP_ADD cb for %s", + __PRETTY_FUNCTION__, nhg->name); +} + +void pbr_nhgroup_del_nexthop_cb(const struct nexthop_group_cmd *nhg, + const struct nexthop *nhop) +{ + struct pbr_event *pbre; + + pbre = pbr_event_new(PBR_NHG_DEL_NEXTHOP, nhg->name); + + pbr_event_enqueue(pbre); + DEBUGD(&pbr_dbg_nht, "%s: Received NEXTHOP_DEL cb for %s", + __PRETTY_FUNCTION__, nhg->name); +} + +void pbr_nhgroup_delete_cb(const char *name) +{ + struct pbr_event *pbre; + + pbre = pbr_event_new(PBR_NHG_DELETE, name); + + pbr_event_enqueue(pbre); + DEBUGD(&pbr_dbg_nht, "%s: Received DELETE cb for %s", + __PRETTY_FUNCTION__, name); +} + +#if 0 +static struct pbr_nexthop_cache *pbr_nht_lookup_nexthop(struct nexthop *nexthop) +{ + return NULL; +} +#endif + +static void pbr_nht_find_nhg_from_table_install(struct hash_backet *b, + void *data) +{ + struct pbr_nexthop_group_cache *pnhgc = + (struct pbr_nexthop_group_cache *)b->data; + uint32_t *table_id = (uint32_t *)data; + + if (pnhgc->table_id == *table_id) { + DEBUGD(&pbr_dbg_nht, "%s: Table ID (%u) matches %s", + __PRETTY_FUNCTION__, *table_id, pnhgc->name); + pnhgc->installed = true; + pbr_map_schedule_policy_from_nhg(pnhgc->name); + } +} + +void pbr_nht_route_installed_for_table(uint32_t table_id) +{ + hash_iterate(pbr_nhg_hash, pbr_nht_find_nhg_from_table_install, + &table_id); +} + +static void pbr_nht_find_nhg_from_table_remove(struct hash_backet *b, + void *data) +{ + ; +} + +void pbr_nht_route_removed_for_table(uint32_t table_id) +{ + hash_iterate(pbr_nhg_hash, pbr_nht_find_nhg_from_table_remove, + &table_id); +} + +/* + * Loop through all nexthops in a nexthop group to check that they are all the + * same. If they are not all the same, log this peculiarity. + * + * nhg + * The nexthop group to check + * + * Returns: + * - AFI of last nexthop in the group + * - AFI_MAX on error + */ +static afi_t pbr_nht_which_afi(struct nexthop_group nhg) +{ + struct nexthop *nexthop; + afi_t install_afi = AFI_MAX; + bool v6, v4, bh; + v6 = v4 = bh = false; + + for (ALL_NEXTHOPS(nhg, nexthop)) { + switch (nexthop->type) { + case NEXTHOP_TYPE_IFINDEX: + break; + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + v6 = true; + install_afi = AFI_IP; + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + v4 = true; + install_afi = AFI_IP6; + break; + case NEXTHOP_TYPE_BLACKHOLE: + bh = true; + install_afi = AFI_MAX; + break; + } + } + + if (!bh && v6 && v4) + DEBUGD(&pbr_dbg_nht, + "%s: Saw both V6 and V4 nexthops...using %s", + __PRETTY_FUNCTION__, afi2str(install_afi)); + if (bh && (v6 || v4)) + DEBUGD(&pbr_dbg_nht, + "%s: Saw blackhole nexthop(s) with %s%s%s nexthop(s), using AFI_MAX.", + __PRETTY_FUNCTION__, v4 ? "v4" : "", + (v4 && v6) ? " and " : "", v6 ? "v6" : ""); + + return install_afi; +} + +static void pbr_nht_install_nexthop_group(struct pbr_nexthop_group_cache *pnhgc, + struct nexthop_group nhg) +{ + afi_t install_afi; + + install_afi = pbr_nht_which_afi(nhg); + + pnhgc->installed = false; + route_add(pnhgc, nhg, install_afi); +} + +static void +pbr_nht_uninstall_nexthop_group(struct pbr_nexthop_group_cache *pnhgc, + struct nexthop_group nhg) +{ + afi_t install_afi; + + install_afi = pbr_nht_which_afi(nhg); + + pnhgc->installed = false; + pnhgc->valid = false; + route_delete(pnhgc, install_afi); +} + +void pbr_nht_change_group(const char *name) +{ + struct nexthop_group_cmd *nhgc; + struct pbr_nexthop_group_cache *pnhgc; + struct pbr_nexthop_group_cache find; + struct nexthop *nhop; + + nhgc = nhgc_find(name); + if (!nhgc) + return; + + memset(&find, 0, sizeof(find)); + strcpy(find.name, name); + pnhgc = hash_lookup(pbr_nhg_hash, &find); + + if (!pnhgc) { + DEBUGD(&pbr_dbg_nht, + "%s: Could not find nexthop-group cache w/ name '%s'", + __PRETTY_FUNCTION__, name); + return; + } + + for (ALL_NEXTHOPS(nhgc->nhg, nhop)) { + struct pbr_nexthop_cache lookup; + struct pbr_nexthop_cache *pnhc; + + lookup.nexthop = nhop; + pnhc = hash_lookup(pnhgc->nhh, &lookup); + if (!pnhc) { + pnhc = hash_get(pnhgc->nhh, &lookup, pbr_nh_alloc); + pnhc->parent = pnhgc; + } + } + pbr_nht_install_nexthop_group(pnhgc, nhgc->nhg); +} + +char *pbr_nht_nexthop_make_name(char *name, uint32_t seqno, char *buffer) +{ + sprintf(buffer, "%s%u", name, seqno); + return buffer; +} + +static void *pbr_nhgc_alloc(void *p) +{ + struct pbr_nexthop_group_cache *new; + struct pbr_nexthop_group_cache *pnhgc = + (struct pbr_nexthop_group_cache *)p; + + new = XCALLOC(MTYPE_PBR_NHG, sizeof(*new)); + + strcpy(new->name, pnhgc->name); + new->table_id = pbr_nht_get_next_tableid(); + + DEBUGD(&pbr_dbg_nht, "%s: NHT: %s assigned Table ID: %u", + __PRETTY_FUNCTION__, new->name, new->table_id); + + new->nhh = hash_create_size(8, pbr_nh_hash_key, pbr_nh_hash_equal, + "PBR NH Cache Hash"); + return new; +} + +void pbr_nht_add_individual_nexthop(const char *name, uint32_t seqno) +{ + struct pbr_nexthop_group_cache *pnhgc; + struct pbr_nexthop_group_cache find; + struct pbr_nexthop_cache *pnhc; + struct pbr_map_sequence *pbrms; + struct pbr_nexthop_cache lookup; + + pbrms = pbrms_get(name, seqno); + + memset(&find, 0, sizeof(find)); + pbr_nht_nexthop_make_name(pbrms->parent->name, pbrms->seqno, find.name); + if (!pbrms->internal_nhg_name) + pbrms->internal_nhg_name = XSTRDUP(MTYPE_TMP, find.name); + + pnhgc = hash_get(pbr_nhg_hash, &find, pbr_nhgc_alloc); + + lookup.nexthop = pbrms->nhg->nexthop; + pnhc = hash_get(pnhgc->nhh, &lookup, pbr_nh_alloc); + pnhc->parent = pnhgc; + pbr_nht_install_nexthop_group(pnhgc, *pbrms->nhg); +} + +void pbr_nht_delete_individual_nexthop(const char *name, uint32_t seqno) +{ + struct pbr_nexthop_group_cache *pnhgc; + struct pbr_nexthop_group_cache find; + struct pbr_nexthop_cache *pnhc; + struct pbr_nexthop_cache lup; + struct pbr_map_sequence *pbrms; + struct nexthop *nh; + + pbrms = pbrms_get(name, seqno); + + memset(&find, 0, sizeof(find)); + strcpy(&find.name[0], pbrms->internal_nhg_name); + pnhgc = hash_lookup(pbr_nhg_hash, &find); + + nh = pbrms->nhg->nexthop; + lup.nexthop = nh; + pnhc = hash_lookup(pnhgc->nhh, &lup); + pnhc->parent = NULL; + hash_release(pnhgc->nhh, pnhc); + pbr_nh_delete(&pnhc); + pbr_nht_uninstall_nexthop_group(pnhgc, *pbrms->nhg); + + hash_release(pbr_nhg_hash, pnhgc); + + nexthop_del(pbrms->nhg, nh); + nexthop_free(nh); + nexthop_group_delete(&pbrms->nhg); + XFREE(MTYPE_TMP, pbrms->internal_nhg_name); +} + +void pbr_nht_add_group(const char *name) +{ + struct nexthop *nhop; + struct nexthop_group_cmd *nhgc; + struct pbr_nexthop_group_cache *pnhgc; + struct pbr_nexthop_group_cache lookup; + + nhgc = nhgc_find(name); + + if (!nhgc) { + zlog_warn("%s: Could not find group %s to add", + __PRETTY_FUNCTION__, name); + return; + } + + strcpy(lookup.name, name); + pnhgc = hash_get(pbr_nhg_hash, &lookup, pbr_nhgc_alloc); + DEBUGD(&pbr_dbg_nht, "%s: Retrieved NHGC @ %p", __PRETTY_FUNCTION__, + pnhgc); + + for (ALL_NEXTHOPS(nhgc->nhg, nhop)) { + struct pbr_nexthop_cache lookup; + struct pbr_nexthop_cache *pnhc; + + lookup.nexthop = nhop; + pnhc = hash_lookup(pnhgc->nhh, &lookup); + if (!pnhc) { + pnhc = hash_get(pnhgc->nhh, &lookup, pbr_nh_alloc); + pnhc->parent = pnhgc; + } + } +} + +void pbr_nht_delete_group(const char *name) +{ + struct pbr_map_sequence *pbrms; + struct listnode *snode; + struct pbr_map *pbrm; + + RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) { + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, snode, pbrms)) { + if (pbrms->nhgrp_name + && strcmp(pbrms->nhgrp_name, name) == 0) { + pbrms->reason |= PBR_MAP_INVALID_NO_NEXTHOPS; + pbrm->valid = false; + } + } + } +} + +bool pbr_nht_nexthop_valid(struct nexthop_group *nhg) +{ + DEBUGD(&pbr_dbg_nht, "%s: %p", __PRETTY_FUNCTION__, nhg); + return true; +} + +bool pbr_nht_nexthop_group_valid(const char *name) +{ + struct pbr_nexthop_group_cache *pnhgc; + struct pbr_nexthop_group_cache lookup; + + DEBUGD(&pbr_dbg_nht, "%s: %s", __PRETTY_FUNCTION__, name); + + strcpy(lookup.name, name); + pnhgc = hash_get(pbr_nhg_hash, &lookup, NULL); + if (!pnhgc) + return false; + DEBUGD(&pbr_dbg_nht, "%s: \t%d %d", __PRETTY_FUNCTION__, pnhgc->valid, + pnhgc->installed); + if (pnhgc->valid && pnhgc->installed) + return true; + + return false; +} + +struct pbr_nht_individual { + struct zapi_route *nhr; + + uint32_t valid; +}; + +static void pbr_nht_individual_nexthop_update_lookup(struct hash_backet *b, + void *data) +{ + struct pbr_nexthop_cache *pnhc = b->data; + struct pbr_nht_individual *pnhi = data; + char buf[PREFIX_STRLEN]; + bool old_valid; + + old_valid = pnhc->valid; + + switch (pnhi->nhr->prefix.family) { + case AF_INET: + if (pnhc->nexthop->gate.ipv4.s_addr + == pnhi->nhr->prefix.u.prefix4.s_addr) + pnhc->valid = !!pnhi->nhr->nexthop_num; + break; + case AF_INET6: + if (memcmp(&pnhc->nexthop->gate.ipv6, + &pnhi->nhr->prefix.u.prefix6, 16) + == 0) + pnhc->valid = !!pnhi->nhr->nexthop_num; + break; + } + + DEBUGD(&pbr_dbg_nht, "\tFound %s: old: %d new: %d", + prefix2str(&pnhi->nhr->prefix, buf, sizeof(buf)), old_valid, + pnhc->valid); + + if (old_valid != pnhc->valid) { + struct pbr_event *pbre; + + pbre = pbr_event_new(PBR_NH_CHANGED, pnhc->parent->name); + + pbr_event_enqueue(pbre); + } + + if (pnhc->valid) + pnhi->valid += 1; +} + +static void pbr_nht_nexthop_update_lookup(struct hash_backet *b, void *data) +{ + struct pbr_nexthop_group_cache *pnhgc = b->data; + struct pbr_nht_individual pnhi; + + pnhi.nhr = (struct zapi_route *)data; + pnhi.valid = 0; + hash_iterate(pnhgc->nhh, pbr_nht_individual_nexthop_update_lookup, + &pnhi); + + /* + * If any of the specified nexthops are valid we are valid + */ + pnhgc->valid = !!pnhi.valid; +} + +void pbr_nht_nexthop_update(struct zapi_route *nhr) +{ + hash_iterate(pbr_nhg_hash, pbr_nht_nexthop_update_lookup, nhr); +} + +static uint32_t pbr_nhg_hash_key(void *arg) +{ + struct pbr_nexthop_group_cache *nhgc = + (struct pbr_nexthop_group_cache *)arg; + + return jhash(&nhgc->name, strlen(nhgc->name), 0x52c34a96); +} + +static int pbr_nhg_hash_equal(const void *arg1, const void *arg2) +{ + const struct pbr_nexthop_group_cache *nhgc1 = + (const struct pbr_nexthop_group_cache *)arg1; + const struct pbr_nexthop_group_cache *nhgc2 = + (const struct pbr_nexthop_group_cache *)arg2; + + return !strcmp(nhgc1->name, nhgc2->name); +} + + +uint32_t pbr_nht_get_next_tableid(void) +{ + uint32_t i; + bool found = false; + + for (i = pbr_nhg_low_table; i <= pbr_nhg_high_table; i++) { + if (nhg_tableid[i] == false) { + found = true; + break; + } + } + + if (found) { + nhg_tableid[i] = true; + return i; + } else + return 0; +} + +void pbr_nht_set_tableid_range(uint32_t low, uint32_t high) +{ + pbr_nhg_low_table = low; + pbr_nhg_high_table = high; +} + +void pbr_nht_write_table_range(struct vty *vty) +{ + if (pbr_nhg_low_table != PBR_NHT_DEFAULT_LOW_TABLEID + || pbr_nhg_high_table != PBR_NHT_DEFAULT_HIGH_TABLEID) { + vty_out(vty, "pbr table range %u %u\n", pbr_nhg_low_table, + pbr_nhg_high_table); + } +} + +uint32_t pbr_nht_get_next_rule(uint32_t seqno) +{ + return seqno + pbr_nhg_low_rule - 1; +} +void pbr_nht_set_rule_range(uint32_t low, uint32_t high) +{ + pbr_nhg_low_rule = low; + pbr_nhg_high_rule = high; +} + +void pbr_nht_write_rule_range(struct vty *vty) +{ + if (pbr_nhg_low_rule != PBR_NHT_DEFAULT_LOW_RULE + || pbr_nhg_high_rule != PBR_NHT_DEFAULT_HIGH_RULE) { + vty_out(vty, "pbr rule range %u %u\n", pbr_nhg_low_rule, + pbr_nhg_high_rule); + } +} + +uint32_t pbr_nht_get_table(const char *name) +{ + struct pbr_nexthop_group_cache find; + struct pbr_nexthop_group_cache *pnhgc; + + memset(&find, 0, sizeof(find)); + strcpy(find.name, name); + pnhgc = hash_lookup(pbr_nhg_hash, &find); + + if (!pnhgc) { + DEBUGD(&pbr_dbg_nht, + "%s: Could not find nexthop-group cache w/ name '%s'", + __PRETTY_FUNCTION__, name); + return 5000; + } + + return pnhgc->table_id; +} + +bool pbr_nht_get_installed(const char *name) +{ + struct pbr_nexthop_group_cache find; + struct pbr_nexthop_group_cache *pnhgc; + + memset(&find, 0, sizeof(find)); + strcpy(find.name, name); + + pnhgc = hash_lookup(pbr_nhg_hash, &find); + + if (!pnhgc) { + return false; + } + + return pnhgc->installed; +} + +static void pbr_nht_show_nhg_nexthops(struct hash_backet *b, void *data) +{ + struct pbr_nexthop_cache *pnhc = b->data; + struct vty *vty = data; + + vty_out(vty, "\tValid: %d", pnhc->valid); + nexthop_group_write_nexthop(vty, pnhc->nexthop); +} + +struct pbr_nht_show { + struct vty *vty; + const char *name; +}; + +static void pbr_nht_show_nhg(struct hash_backet *b, void *data) +{ + struct pbr_nexthop_group_cache *pnhgc = b->data; + struct pbr_nht_show *pns = data; + struct vty *vty; + + if (pns->name && strcmp(pns->name, pnhgc->name) != 0) + return; + + vty = pns->vty; + vty_out(vty, "Nexthop-Group: %s Table: %u Valid: %d Installed: %d\n", + pnhgc->name, pnhgc->table_id, pnhgc->valid, pnhgc->installed); + + hash_iterate(pnhgc->nhh, pbr_nht_show_nhg_nexthops, vty); +} + +void pbr_nht_show_nexthop_group(struct vty *vty, const char *name) +{ + struct pbr_nht_show pns; + + pns.vty = vty; + pns.name = name; + + hash_iterate(pbr_nhg_hash, pbr_nht_show_nhg, &pns); +} + +void pbr_nht_init(void) +{ + pbr_nhg_hash = hash_create_size( + 16, pbr_nhg_hash_key, pbr_nhg_hash_equal, "PBR NHG Cache Hash"); + pbr_nhrc_hash = + hash_create_size(16, (unsigned int (*)(void *))nexthop_hash, + pbr_nhrc_hash_equal, "PBR NH Hash"); + + pbr_nhg_low_table = PBR_NHT_DEFAULT_LOW_TABLEID; + pbr_nhg_high_table = PBR_NHT_DEFAULT_HIGH_TABLEID; + pbr_nhg_low_rule = PBR_NHT_DEFAULT_LOW_RULE; + pbr_nhg_high_rule = PBR_NHT_DEFAULT_HIGH_RULE; + memset(&nhg_tableid, 0, 65535 * sizeof(uint8_t)); +} diff --git a/pbrd/pbr_nht.h b/pbrd/pbr_nht.h new file mode 100644 index 000000000000..638dbf11da77 --- /dev/null +++ b/pbrd/pbr_nht.h @@ -0,0 +1,115 @@ +/* + * PBR-nht Header + * Copyright (C) 2018 Cumulus Networks, Inc. + * Donald Sharp + * + * This file is part of FRR. + * + * 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 + */ +#ifndef __PBR_NHT_H__ +#define __PBR_NHT_H__ + +#include +#include + +struct pbr_nexthop_group_cache { + char name[100]; + + uint32_t table_id; + + struct hash *nhh; + + /* + * If all nexthops are considered valid + */ + bool valid; + + bool installed; +}; + +struct pbr_nexthop_cache { + struct pbr_nexthop_group_cache *parent; + + struct nexthop *nexthop; + + bool valid; +}; + +extern void pbr_nht_write_table_range(struct vty *vty); +#define PBR_NHT_DEFAULT_LOW_TABLEID 10000 +#define PBR_NHT_DEFAULT_HIGH_TABLEID 11000 +extern void pbr_nht_set_tableid_range(uint32_t low, uint32_t high); + +/* + * Get the next tableid to use for installation + */ +extern uint32_t pbr_nht_get_next_tableid(void); +/* + * Get the next rule number to use for installation + */ +extern void pbr_nht_write_rule_range(struct vty *vty); + +#define PBR_NHT_DEFAULT_LOW_RULE 300 +#define PBR_NHT_DEFAULT_HIGH_RULE 1300 +extern void pbr_nht_set_rule_range(uint32_t low, uint32_t high); + +extern uint32_t pbr_nht_get_next_rule(uint32_t seqno); + +extern void pbr_nhgroup_add_cb(const char *name); +extern void pbr_nhgroup_add_nexthop_cb(const struct nexthop_group_cmd *nhg, + const struct nexthop *nhop); +extern void pbr_nhgroup_del_nexthop_cb(const struct nexthop_group_cmd *nhg, + const struct nexthop *nhop); +extern void pbr_nhgroup_delete_cb(const char *name); + +extern bool pbr_nht_nexthop_valid(struct nexthop_group *nhg); +extern bool pbr_nht_nexthop_group_valid(const char *name); + +extern void pbr_nht_add_group(const char *name); +extern void pbr_nht_change_group(const char *name); +extern void pbr_nht_delete_group(const char *name); + +extern void pbr_nht_add_individual_nexthop(const char *name, uint32_t seqno); +extern void pbr_nht_delete_individual_nexthop(const char *name, uint32_t seqno); +/* + * Given the tableid of the installed default + * route, find the nexthop-group associated with + * it, then find all pbr-maps that use it and + * install/delete them as well. + */ +extern void pbr_nht_route_installed_for_table(uint32_t table_id); +extern void pbr_nht_route_removed_for_table(uint32_t table_id); + +/* + * Given the nexthop group name, lookup the associated + * tableid with it + */ +extern uint32_t pbr_nht_get_table(const char *name); + +extern bool pbr_nht_get_installed(const char *name); + +extern char *pbr_nht_nexthop_make_name(char *name, uint32_t seqno, + char *buffer); + +extern void pbr_nht_show_nexthop_group(struct vty *vty, const char *name); + +/* + * When we get a callback from zebra about a nexthop changing + */ +extern void pbr_nht_nexthop_update(struct zapi_route *nhr); + +extern void pbr_nht_init(void); +#endif diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c new file mode 100644 index 000000000000..527430d72156 --- /dev/null +++ b/pbrd/pbr_vty.c @@ -0,0 +1,632 @@ +/* + * PBR - vty code + * Copyright (C) Cumulus Networks, Inc. + * Donald Sharp + * + * This file is part of FRR. + * + * 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 + +#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 $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 $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 $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, + 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; +} diff --git a/pbrd/pbr_vty.h b/pbrd/pbr_vty.h new file mode 100644 index 000000000000..cdd63abc9469 --- /dev/null +++ b/pbrd/pbr_vty.h @@ -0,0 +1,26 @@ +/* + * VTY library for PBR + * Copyright (C) Cumulus Networks, Inc. + * Donald Sharp + * + * This file is part of FRR. + * + * 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 + */ +#ifndef __PBR_VTY_H__ +#define __PBR_VTY_H__ + +extern void pbr_vty_init(void); +#endif diff --git a/pbrd/pbr_zebra.c b/pbrd/pbr_zebra.c new file mode 100644 index 000000000000..e7a3dc940f56 --- /dev/null +++ b/pbrd/pbr_zebra.c @@ -0,0 +1,581 @@ +/* + * Zebra connect code. + * Copyright (C) Cumulus Networks, Inc. + * Donald Sharp + * + * This file is part of FRR. + * + * 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 + +#include "thread.h" +#include "command.h" +#include "network.h" +#include "prefix.h" +#include "routemap.h" +#include "table.h" +#include "stream.h" +#include "memory.h" +#include "zclient.h" +#include "filter.h" +#include "plist.h" +#include "log.h" +#include "nexthop.h" +#include "nexthop_group.h" + +#include "pbr_nht.h" +#include "pbr_map.h" +#include "pbr_memory.h" +#include "pbr_zebra.h" +#include "pbr_debug.h" + +DEFINE_MTYPE_STATIC(PBRD, PBR_INTERFACE, "PBR Interface") + +/* Zebra structure to hold current status. */ +struct zclient *zclient = NULL; + +/* For registering threads. */ +extern struct thread_master *master; + +static struct interface *zebra_interface_if_lookup(struct stream *s) +{ + char ifname_tmp[INTERFACE_NAMSIZ]; + + /* Read interface name. */ + stream_get(ifname_tmp, s, INTERFACE_NAMSIZ); + + /* And look it up. */ + return if_lookup_by_name(ifname_tmp, VRF_DEFAULT); +} + +static struct pbr_interface *pbr_if_new(struct interface *ifp) +{ + struct pbr_interface *pbr_ifp; + + zassert(ifp); + zassert(!ifp->info); + + pbr_ifp = XCALLOC(MTYPE_PBR_INTERFACE, sizeof(*pbr_ifp)); + + if (!pbr_ifp) { + zlog_err("%s: PBR XCALLOC(%zu) failure", __PRETTY_FUNCTION__, + sizeof(*pbr_ifp)); + return 0; + } + + return (pbr_ifp); +} + +/* Inteface addition message from zebra. */ +static int interface_add(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct interface *ifp; + + ifp = zebra_interface_add_read(zclient->ibuf, vrf_id); + + if (!ifp) + return 0; + + if (!ifp->info) { + struct pbr_interface *pbr_ifp; + + pbr_ifp = pbr_if_new(ifp); + ifp->info = pbr_ifp; + } + + return 0; +} + +static int interface_delete(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct interface *ifp; + struct stream *s; + + s = zclient->ibuf; + /* zebra_interface_state_read () updates interface structure in iflist + */ + ifp = zebra_interface_state_read(s, vrf_id); + + if (ifp == NULL) + return 0; + + if_set_index(ifp, IFINDEX_INTERNAL); + + return 0; +} + +static int interface_address_add(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + zebra_interface_address_read(command, zclient->ibuf, vrf_id); + + return 0; +} + +static int interface_address_delete(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct connected *c; + + c = zebra_interface_address_read(command, zclient->ibuf, vrf_id); + + if (!c) + return 0; + + connected_free(c); + return 0; +} + +static int interface_state_up(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + + zebra_interface_if_lookup(zclient->ibuf); + + return 0; +} + +static int interface_state_down(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + + zebra_interface_state_read(zclient->ibuf, vrf_id); + + return 0; +} + +static int route_notify_owner(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct prefix p; + enum zapi_route_notify_owner note; + uint32_t table_id; + char buf[PREFIX_STRLEN]; + + prefix2str(&p, buf, sizeof(buf)); + + if (!zapi_route_notify_decode(zclient->ibuf, &p, &table_id, ¬e)) + return -1; + + switch (note) { + case ZAPI_ROUTE_FAIL_INSTALL: + DEBUGD(&pbr_dbg_zebra, + "%s: [%s] Route install failure for table: %u", + __PRETTY_FUNCTION__, buf, table_id); + break; + case ZAPI_ROUTE_BETTER_ADMIN_WON: + DEBUGD(&pbr_dbg_zebra, + "%s: [%s] Route better admin distance won for table: %u", + __PRETTY_FUNCTION__, buf, table_id); + break; + case ZAPI_ROUTE_INSTALLED: + DEBUGD(&pbr_dbg_zebra, + "%s: [%s] Route installed succeeded for table: %u", + __PRETTY_FUNCTION__, buf, table_id); + pbr_nht_route_installed_for_table(table_id); + break; + case ZAPI_ROUTE_REMOVED: + DEBUGD(&pbr_dbg_zebra, + "%s: [%s] Route Removed succeeded for table: %u", + __PRETTY_FUNCTION__, buf, table_id); + pbr_nht_route_removed_for_table(table_id); + break; + case ZAPI_ROUTE_REMOVE_FAIL: + DEBUGD(&pbr_dbg_zebra, + "%s: [%s] Route remove fail for table: %u", + __PRETTY_FUNCTION__, buf, table_id); + break; + } + + return 0; +} + +static int rule_notify_owner(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + uint32_t seqno, priority, unique; + enum zapi_rule_notify_owner note; + struct pbr_map_sequence *pbrms; + ifindex_t ifi; + + if (!zapi_rule_notify_decode(zclient->ibuf, &seqno, &priority, &unique, + &ifi, ¬e)) + return -1; + + pbrms = pbrms_lookup_unique(unique, ifi); + if (!pbrms) { + DEBUGD(&pbr_dbg_zebra, + "%s: Failure to lookup pbrms based upon %u", + __PRETTY_FUNCTION__, unique); + return 0; + } + + switch (note) { + case ZAPI_RULE_FAIL_INSTALL: + DEBUGD(&pbr_dbg_zebra, "%s: Recieved RULE_FAIL_INSTALL", + __PRETTY_FUNCTION__); + pbrms->installed = false; + break; + case ZAPI_RULE_INSTALLED: + pbrms->installed = true; + DEBUGD(&pbr_dbg_zebra, "%s: Recived RULE_INSTALLED", + __PRETTY_FUNCTION__); + break; + case ZAPI_RULE_REMOVED: + DEBUGD(&pbr_dbg_zebra, "%s: Received RULE REMOVED", + __PRETTY_FUNCTION__); + break; + } + + return 0; +} + +static void zebra_connected(struct zclient *zclient) +{ + zclient_send_reg_requests(zclient, VRF_DEFAULT); +} + +static void route_add_helper(struct zapi_route *api, struct nexthop_group nhg, + uint8_t install_afi) +{ + struct zapi_nexthop *api_nh; + struct nexthop *nhop; + int i; + + api->prefix.family = install_afi; + + i = 0; + for (ALL_NEXTHOPS(nhg, nhop)) { + api_nh = &api->nexthops[i]; + api_nh->vrf_id = nhop->vrf_id; + api_nh->type = nhop->type; + switch (nhop->type) { + case NEXTHOP_TYPE_IPV4: + api_nh->gate.ipv4 = nhop->gate.ipv4; + break; + case NEXTHOP_TYPE_IPV4_IFINDEX: + api_nh->gate.ipv4 = nhop->gate.ipv4; + api_nh->ifindex = nhop->ifindex; + break; + case NEXTHOP_TYPE_IFINDEX: + api_nh->ifindex = nhop->ifindex; + break; + case NEXTHOP_TYPE_IPV6: + memcpy(&api_nh->gate.ipv6, &nhop->gate.ipv6, 16); + break; + case NEXTHOP_TYPE_IPV6_IFINDEX: + api_nh->ifindex = nhop->ifindex; + memcpy(&api_nh->gate.ipv6, &nhop->gate.ipv6, 16); + break; + case NEXTHOP_TYPE_BLACKHOLE: + api_nh->bh_type = nhop->bh_type; + break; + } + i++; + } + api->nexthop_num = i; + + zclient_route_send(ZEBRA_ROUTE_ADD, zclient, api); +} + +/* + * This function assumes a default route is being + * installed into the appropriate tableid + */ +void route_add(struct pbr_nexthop_group_cache *pnhgc, struct nexthop_group nhg, + afi_t install_afi) +{ + struct zapi_route api; + + memset(&api, 0, sizeof(api)); + + api.vrf_id = VRF_DEFAULT; + api.type = ZEBRA_ROUTE_PBR; + api.safi = SAFI_UNICAST; + /* + * Sending a default route + */ + api.tableid = pnhgc->table_id; + SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION); + SET_FLAG(api.message, ZAPI_MESSAGE_TABLEID); + SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP); + switch (install_afi) { + case AFI_MAX: + route_add_helper(&api, nhg, AF_INET); + route_add_helper(&api, nhg, AF_INET6); + break; + case AFI_IP: + route_add_helper(&api, nhg, AF_INET); + break; + case AFI_IP6: + route_add_helper(&api, nhg, AF_INET6); + break; + case AFI_L2VPN: + DEBUGD(&pbr_dbg_zebra, + "%s: Asked to install unsupported route type: L2VPN", + __PRETTY_FUNCTION__); + break; + } +} + +/* + * This function assumes a default route is being + * removed from the appropriate tableid + */ +void route_delete(struct pbr_nexthop_group_cache *pnhgc, afi_t afi) +{ + struct zapi_route api; + + memset(&api, 0, sizeof(api)); + api.vrf_id = VRF_DEFAULT; + api.type = ZEBRA_ROUTE_PBR; + api.safi = SAFI_UNICAST; + + api.tableid = pnhgc->table_id; + SET_FLAG(api.message, ZAPI_MESSAGE_TABLEID); + + switch (afi) { + case AFI_IP: + api.prefix.family = AF_INET; + zclient_route_send(ZEBRA_ROUTE_DELETE, zclient, &api); + break; + case AFI_IP6: + api.prefix.family = AF_INET6; + zclient_route_send(ZEBRA_ROUTE_DELETE, zclient, &api); + break; + case AFI_MAX: + api.prefix.family = AF_INET; + zclient_route_send(ZEBRA_ROUTE_DELETE, zclient, &api); + api.prefix.family = AF_INET6; + zclient_route_send(ZEBRA_ROUTE_DELETE, zclient, &api); + break; + case AFI_L2VPN: + DEBUGD(&pbr_dbg_zebra, + "%s: Asked to delete unsupported route type: L2VPN", + __PRETTY_FUNCTION__); + break; + } + + return; +} + +static int pbr_zebra_nexthop_update(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct zapi_route nhr; + char buf[PREFIX2STR_BUFFER]; + uint32_t i; + + zapi_nexthop_update_decode(zclient->ibuf, &nhr); + + if (DEBUG_MODE_CHECK(&pbr_dbg_zebra, DEBUG_MODE_ALL)) { + + DEBUGD(&pbr_dbg_zebra, "%s: Received Nexthop update: %s", + __PRETTY_FUNCTION__, + prefix2str(&nhr.prefix, buf, sizeof(buf))); + + DEBUGD(&pbr_dbg_zebra, "%s: (\tNexthops(%u)", + __PRETTY_FUNCTION__, nhr.nexthop_num); + + for (i = 0; i < nhr.nexthop_num; i++) { + DEBUGD(&pbr_dbg_zebra, + "%s: \tType: %d: vrf: %d, ifindex: %d gate: %s", + __PRETTY_FUNCTION__, nhr.nexthops[i].type, + nhr.nexthops[i].vrf_id, nhr.nexthops[i].ifindex, + inet_ntoa(nhr.nexthops[i].gate.ipv4)); + } + } + + pbr_nht_nexthop_update(&nhr); + return 1; +} + +extern struct zebra_privs_t pbr_privs; + +void pbr_zebra_init(void) +{ + struct zclient_options opt = { .receive_notify = true }; + + zclient = zclient_new_notify(master, &opt); + + zclient_init(zclient, ZEBRA_ROUTE_PBR, 0, &pbr_privs); + zclient->zebra_connected = zebra_connected; + zclient->interface_add = interface_add; + zclient->interface_delete = interface_delete; + zclient->interface_up = interface_state_up; + zclient->interface_down = interface_state_down; + zclient->interface_address_add = interface_address_add; + zclient->interface_address_delete = interface_address_delete; + zclient->route_notify_owner = route_notify_owner; + zclient->rule_notify_owner = rule_notify_owner; + zclient->nexthop_update = pbr_zebra_nexthop_update; +} + +void pbr_send_rnh(struct nexthop *nhop, bool reg) +{ + uint32_t command; + struct prefix p; + + command = (reg) ? + ZEBRA_NEXTHOP_REGISTER : ZEBRA_NEXTHOP_UNREGISTER; + + memset(&p, 0, sizeof(p)); + switch(nhop->type) { + case NEXTHOP_TYPE_IFINDEX: + case NEXTHOP_TYPE_BLACKHOLE: + return; + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + p.family = AF_INET; + p.u.prefix4.s_addr = nhop->gate.ipv4.s_addr; + p.prefixlen = 32; + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + p.family = AF_INET6; + memcpy(&p.u.prefix6, &nhop->gate.ipv6, 16); + p.prefixlen = 128; + break; + } + + if (zclient_send_rnh(zclient, command, &p, + false, nhop->vrf_id) < 0) { + zlog_warn("%s: Failure to send nexthop to zebra", + __PRETTY_FUNCTION__); + } +} + +static void pbr_encode_pbr_map_sequence_prefix(struct stream *s, + struct prefix *p, + u_char family) +{ + struct prefix any; + + if (!p) { + memset(&any, 0, sizeof(any)); + any.family = family; + p = &any; + } + + stream_putc(s, p->family); + stream_putc(s, p->prefixlen); + stream_put(s, &p->u.prefix, prefix_blen(p)); +} + +static void pbr_encode_pbr_map_sequence(struct stream *s, + struct pbr_map_sequence *pbrms, + struct interface *ifp) +{ + u_char family; + + family = AF_INET; + if (pbrms->src) + family = pbrms->src->family; + + if (pbrms->dst) + family = pbrms->dst->family; + + stream_putl(s, pbrms->seqno); + stream_putl(s, pbrms->ruleno); + stream_putl(s, pbrms->unique); + pbr_encode_pbr_map_sequence_prefix(s, pbrms->src, family); + stream_putw(s, 0); /* src port */ + pbr_encode_pbr_map_sequence_prefix(s, pbrms->dst, family); + stream_putw(s, 0); /* dst port */ + if (pbrms->nhgrp_name) + stream_putl(s, pbr_nht_get_table(pbrms->nhgrp_name)); + else if (pbrms->nhg) + stream_putl(s, pbr_nht_get_table(pbrms->internal_nhg_name)); + stream_putl(s, ifp->ifindex); +} + +void pbr_send_pbr_map(struct pbr_map *pbrm, bool install) +{ + struct listnode *inode, *snode; + struct pbr_map_sequence *pbrms; + struct pbr_map_interface *pmi; + struct stream *s; + uint32_t total; + ssize_t tspot; + + DEBUGD(&pbr_dbg_zebra, "%s: for %s %d", __PRETTY_FUNCTION__, pbrm->name, + install); + + s = zclient->obuf; + stream_reset(s); + + zclient_create_header(s, + install ? ZEBRA_RULE_ADD : ZEBRA_RULE_DELETE, + VRF_DEFAULT); + + total = 0; + tspot = stream_get_endp(s); + stream_putl(s, 0); + for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi)) { + + DEBUGD(&pbr_dbg_zebra, "%s: \t%s %s %d %s %u", + __PRETTY_FUNCTION__, install ? "Installing" : "Deleting", + pbrm->name, install, pmi->ifp->name, pmi->delete); + + if (!install && pmi->delete) { + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, snode, + pbrms)) { + pbr_encode_pbr_map_sequence(s, + pbrms, pmi->ifp); + total++; + } + continue; + } + + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, snode, pbrms)) { + + DEBUGD(&pbr_dbg_zebra, "%s: \tSeqno: %u %ld valid %u", + __PRETTY_FUNCTION__, pbrms->seqno, pbrms->reason, + pbrm->valid); + + if (!install && + !(pbrms->reason & PBR_MAP_DEL_SEQUENCE_NUMBER)) + continue; + + if (!install && !pbrms->installed) + continue; + + if (install && pbrms->installed) + continue; + + DEBUGD(&pbr_dbg_zebra, "%s: \t Seq: %u ifp %s", + __PRETTY_FUNCTION__, pbrms->seqno, + pmi->ifp->name); + + pbr_encode_pbr_map_sequence(s, pbrms, pmi->ifp); + total++; + } + } + + DEBUGD(&pbr_dbg_zebra, "%s: Putting %u at %zu ", __PRETTY_FUNCTION__, + total, tspot); + + stream_putl_at(s, tspot, total); + stream_putw_at(s, 0, stream_get_endp(s)); + + if (!total) { + stream_reset(s); + return; + } + zclient_send_message(zclient); +} diff --git a/pbrd/pbr_zebra.h b/pbrd/pbr_zebra.h new file mode 100644 index 000000000000..f3071a59ae74 --- /dev/null +++ b/pbrd/pbr_zebra.h @@ -0,0 +1,39 @@ +/* + * Zebra connect library for PBR + * Copyright (C) Cumulus Networks, Inc. + * Donald Sharp + * + * This file is part of FRR. + * + * 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 + */ +#ifndef __PBR_ZEBRA_H__ +#define __PBR_ZEBRA_H__ + +struct pbr_interface { + char mapname[100]; +}; + +extern void pbr_zebra_init(void); + +extern void route_add(struct pbr_nexthop_group_cache *pnhgc, + struct nexthop_group nhg, afi_t install_afi); +extern void route_delete(struct pbr_nexthop_group_cache *pnhgc, + afi_t install_afi); + +extern void pbr_send_rnh(struct nexthop *nhop, bool reg); + +extern void pbr_send_pbr_map(struct pbr_map *pbrm, bool install); +#endif diff --git a/pbrd/pbrd.conf.sample b/pbrd/pbrd.conf.sample new file mode 100644 index 000000000000..bb1c2edca825 --- /dev/null +++ b/pbrd/pbrd.conf.sample @@ -0,0 +1,3 @@ +! +! +log stdout diff --git a/pbrd/subdir.am b/pbrd/subdir.am new file mode 100644 index 000000000000..361e6c1fdeb1 --- /dev/null +++ b/pbrd/subdir.am @@ -0,0 +1,39 @@ +# +# pbrd +# + +if PBRD +noinst_LIBRARIES += pbrd/libpbr.a +sbin_PROGRAMS += pbrd/pbrd +dist_examples_DATA += pbrd/pbrd.conf.sample +endif + +pbrd_libpbr_a_SOURCES = \ + pbrd/pbr_zebra.c \ + pbrd/pbr_vty.c \ + pbrd/pbr_map.c \ + pbrd/pbr_memory.c \ + pbrd/pbr_nht.c \ + pbrd/pbr_event.c \ + pbrd/pbr_debug.c \ + # end + +noinst_HEADERS += \ + pbrd/pbr_event.h \ + pbrd/pbr_map.h \ + pbrd/pbr_memory.h \ + pbrd/pbr_nht.h \ + pbrd/pbr_vty.h \ + pbrd/pbr_zebra.h \ + pbrd/pbr_debug.h \ + # end + +pbrd/pbr_vty_clippy.c: $(CLIPPY_DEPS) +pbrd/pbr_vty.$(OBJEXT): pbrd/pbr_vty_clippy.c + +pbrd/pbr_debug_clippy.c: $(CLIPPY_DEPS) +pbrd/pbr_debug.$(OBJEXT): pbrd/pbr_debug_clippy.c + +pbrd_pbrd_SOURCES = pbrd/pbr_main.c +pbrd_pbrd_LDADD = pbrd/libpbr.a lib/libfrr.la @LIBCAP@ + diff --git a/redhat/daemons b/redhat/daemons index 889e288e5766..1dd7c95d23b1 100644 --- a/redhat/daemons +++ b/redhat/daemons @@ -50,6 +50,7 @@ nhrpd=no eigrpd=no babeld=no sharpd=no +pbrd=no # # Command line options for the daemons # @@ -66,6 +67,7 @@ nhrpd_options=("-A 127.0.0.1") eigrpd_options=("-A 127.0.0.1") babeld_options=("-A 127.0.0.1") sharpd_options=("-A 127.0.0.1") +pbrd_options=("-A 127.0.0.1") # # If the vtysh_enable is yes, then the unified config is read diff --git a/redhat/frr.spec.in b/redhat/frr.spec.in index b4073f94f322..707735720978 100644 --- a/redhat/frr.spec.in +++ b/redhat/frr.spec.in @@ -85,7 +85,7 @@ %{!?frr_gid: %global frr_gid 92 } %{!?vty_gid: %global vty_gid 85 } -%define daemon_list zebra ripd ospfd bgpd isisd ripngd ospf6d +%define daemon_list zebra ripd ospfd bgpd isisd ripngd ospf6d pbrd %if %{with_ldpd} %define daemon_ldpd ldpd diff --git a/tools/etc/frr/daemons b/tools/etc/frr/daemons index ac17fed03a06..9a96c0490ae0 100644 --- a/tools/etc/frr/daemons +++ b/tools/etc/frr/daemons @@ -34,3 +34,4 @@ nhrpd=no eigrpd=no babeld=no sharpd=no +pbrd=no diff --git a/tools/etc/frr/daemons.conf b/tools/etc/frr/daemons.conf index 783d16731873..e6c0cde9686b 100644 --- a/tools/etc/frr/daemons.conf +++ b/tools/etc/frr/daemons.conf @@ -17,6 +17,7 @@ nhrpd_options=" --daemon -A 127.0.0.1" eigrpd_options=" --daemon -A 127.0.0.1" babeld_options=" --daemon -A 127.0.0.1" sharpd_options=" --daemon -A 127.0.0.1" +pbrd_options=" --daemon -A 127.0.0.1" # The list of daemons to watch is automatically generated by the init script. watchfrr_enable=yes diff --git a/tools/frr b/tools/frr index 0f6140d3ecf3..065be665ae4f 100755 --- a/tools/frr +++ b/tools/frr @@ -21,7 +21,7 @@ V_PATH=/var/run/frr # Local Daemon selection may be done by using /etc/frr/daemons. # See /usr/share/doc/frr/README.Debian.gz for further information. # Keep zebra first and do not list watchfrr! -DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd babeld pimd ldpd nhrpd eigrpd sharpd" +DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd babeld pimd ldpd nhrpd eigrpd sharpd pbrd" MAX_INSTANCES=5 RELOAD_SCRIPT=/usr/lib/frr/frr-reload.py diff --git a/vtysh/Makefile.am b/vtysh/Makefile.am index 33d34fc0ddf8..52641de72c3f 100644 --- a/vtysh/Makefile.am +++ b/vtysh/Makefile.am @@ -141,6 +141,11 @@ if SNMP vtysh_scan += $(top_srcdir)/lib/agentx.c endif +if PBRD +vtysh_scan += $(top_srcdir)/pbrd/pbr_vty.c +vtysh_scan += $(top_srcdir)/pbrd/pbr_debug.c +endif + vtysh_cmd_FILES = $(vtysh_scan) \ $(top_srcdir)/lib/keychain.c $(top_srcdir)/lib/routemap.c \ $(top_srcdir)/lib/filter.c $(top_srcdir)/lib/plist.c \ @@ -148,6 +153,7 @@ vtysh_cmd_FILES = $(vtysh_scan) \ $(top_srcdir)/lib/vrf.c \ $(top_srcdir)/lib/vty.c $(top_srcdir)/zebra/debug.c \ $(top_srcdir)/lib/logicalrouter.c \ + $(top_srcdir)/lib/nexthop_group.c \ $(top_srcdir)/zebra/interface.c \ $(top_srcdir)/zebra/irdp_interface.c \ $(top_srcdir)/zebra/rtadv.c $(top_srcdir)/zebra/zebra_vty.c \ diff --git a/vtysh/extract.pl.in b/vtysh/extract.pl.in index bdee0d47e29b..c0277b3d61b7 100755 --- a/vtysh/extract.pl.in +++ b/vtysh/extract.pl.in @@ -99,6 +99,9 @@ foreach (@ARGV) { elsif ($file =~ /lib\/ns\.c$/) { $protocol = "VTYSH_ZEBRA"; } + elsif ($file =~ /lib\/nexthop_group\.c$/) { + $protocol = "VTYSH_PBRD"; + } elsif ($file =~ /lib\/plist\.c$/) { if ($defun_array[1] =~ m/ipv6/) { $protocol = "VTYSH_RIPNGD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ZEBRA|VTYSH_BABELD|VTYSH_ISISD"; diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 556ce2722918..ae10d387cfe6 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -79,6 +79,7 @@ struct vtysh_client vtysh_client[] = { {.fd = -1, .name = "babeld", .flag = VTYSH_BABELD, .next = NULL}, {.fd = -1, .name = "sharpd", .flag = VTYSH_SHARPD, .next = NULL}, {.fd = -1, .name = "watchfrr", .flag = VTYSH_WATCHFRR, .next = NULL}, + {.fd = -1, .name = "pbrd", .flag = VTYSH_PBRD, .next = NULL}, }; enum vtysh_write_integrated vtysh_write_integrated = @@ -986,8 +987,15 @@ static struct cmd_node vrf_node = { VRF_NODE, "%s(config-vrf)# ", }; +static struct cmd_node nh_group_node = { + NH_GROUP_NODE, + "%s(config-nh-group)# ", +}; + static struct cmd_node rmap_node = {RMAP_NODE, "%s(config-route-map)# "}; +static struct cmd_node pbr_map_node = {PBRMAP_NODE, "%s(config-pbr-map)# "}; + static struct cmd_node zebra_node = {ZEBRA_NODE, "%s(config-router)# "}; static struct cmd_node bgp_vpnv4_node = {BGP_VPNV4_NODE, @@ -1487,6 +1495,24 @@ DEFUNSH(VTYSH_RMAP, vtysh_route_map, vtysh_route_map_cmd, return CMD_SUCCESS; } +DEFUNSH(VTYSH_PBRD, vtysh_pbr_map, vtysh_pbr_map_cmd, + "pbr-map NAME seq (1-1000)", + "Create pbr-map or enter pbr-map command mode\n" + "The name of the PBR MAP\n" + "Sequence to insert to/delete from existing pbr-map entry\n" + "Sequence number\n") +{ + vty->node = PBRMAP_NODE; + return CMD_SUCCESS; +} + +DEFSH(VTYSH_PBRD, vtysh_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") + DEFUNSH(VTYSH_ALL, vtysh_line_vty, vtysh_line_vty_cmd, "line vty", "Configure a terminal line\n" "Virtual terminal\n") @@ -1547,6 +1573,7 @@ static int vtysh_exit(struct vty *vty) case ISIS_NODE: case MASC_NODE: case RMAP_NODE: + case PBRMAP_NODE: case VTY_NODE: case KEYCHAIN_NODE: vtysh_execute("end"); @@ -1683,6 +1710,18 @@ DEFUNSH(VTYSH_RMAP, vtysh_quit_rmap, vtysh_quit_rmap_cmd, "quit", return vtysh_exit_rmap(self, vty, argc, argv); } +DEFUNSH(VTYSH_PBRD, vtysh_exit_pbr_map, vtysh_exit_pbr_map_cmd, "exit", + "Exit current mode and down to previous mode\n") +{ + return vtysh_exit(vty); +} + +DEFUNSH(VTYSH_PBRD, vtysh_quit_pbr_map, vtysh_quit_pbr_map_cmd, "quit", + "Exit current mode and down to previous mode\n") +{ + return vtysh_exit_rmap(self, vty, argc, argv); +} + DEFUNSH(VTYSH_BGPD, vtysh_exit_bgpd, vtysh_exit_bgpd_cmd, "exit", "Exit current mode and down to previous mode\n") { @@ -1826,6 +1865,20 @@ DEFSH(VTYSH_ZEBRA, vtysh_no_logicalrouter_cmd, "The Name Space\n" "The file name in " NS_RUN_DIR ", or a full pathname\n") +DEFUNSH(VTYSH_PBRD, vtysh_nexthop_group, vtysh_nexthop_group_cmd, + "nexthop-group NAME", + "Nexthop Group configuration\n" + "Name of the Nexthop Group\n") +{ + vty->node = NH_GROUP_NODE; + return CMD_SUCCESS; +} + +DEFSH(VTYSH_PBRD, vtysh_no_nexthop_group_cmd, "no nexthop-group NAME", + NO_STR + "Nexthop Group Configuration\n" + "Name of the Nexthop Group\n") + DEFUNSH(VTYSH_VRF, vtysh_vrf, vtysh_vrf_cmd, "vrf NAME", "Select a VRF to configure\n" "VRF's name\n") @@ -1864,6 +1917,18 @@ DEFUNSH(VTYSH_VRF, vtysh_quit_vrf, vtysh_quit_vrf_cmd, "quit", return vtysh_exit_vrf(self, vty, argc, argv); } +DEFUNSH(VTYSH_PBRD, vtysh_exit_nexthop_group, vtysh_exit_nexthop_group_cmd, + "exit", "Exit current mode and down to previous mode\n") +{ + return vtysh_exit(vty); +} + +DEFUNSH(VTYSH_VRF, vtysh_quit_nexthop_group, vtysh_quit_nexthop_group_cmd, + "quit", "Exit current mode and down to previous mode\n") +{ + return vtysh_exit_nexthop_group(self, vty, argc, argv); +} + /* TODO Implement interface description commands in ripngd, ospf6d * and isisd. */ DEFSH(VTYSH_ZEBRA | VTYSH_RIPD | VTYSH_OSPFD | VTYSH_EIGRPD, @@ -1939,7 +2004,7 @@ DEFUN (vtysh_show_work_queues, DEFUN (vtysh_show_work_queues_daemon, vtysh_show_work_queues_daemon_cmd, - "show work-queues ", + "show work-queues ", SHOW_STR "Work Queue information\n" "For the zebra daemon\n" @@ -1948,7 +2013,8 @@ DEFUN (vtysh_show_work_queues_daemon, "For the ospf daemon\n" "For the ospfv6 daemon\n" "For the bgp daemon\n" - "For the isis daemon\n") + "For the isis daemon\n" + "For the pbr daemon\n") { int idx_protocol = 2; unsigned int i; @@ -3099,7 +3165,9 @@ void vtysh_init_vty(void) install_node(&link_params_node, NULL); install_node(&logicalrouter_node, NULL); install_node(&vrf_node, NULL); + install_node(&nh_group_node, NULL); install_node(&rmap_node, NULL); + install_node(&pbr_map_node, NULL); install_node(&zebra_node, NULL); install_node(&bgp_vpnv4_node, NULL); install_node(&bgp_vpnv6_node, NULL); @@ -3228,6 +3296,8 @@ void vtysh_init_vty(void) install_element(KEYCHAIN_KEY_NODE, &vtysh_quit_ripd_cmd); install_element(RMAP_NODE, &vtysh_exit_rmap_cmd); install_element(RMAP_NODE, &vtysh_quit_rmap_cmd); + install_element(PBRMAP_NODE, &vtysh_exit_pbr_map_cmd); + install_element(PBRMAP_NODE, &vtysh_quit_pbr_map_cmd); install_element(VTY_NODE, &vtysh_exit_line_vty_cmd); install_element(VTY_NODE, &vtysh_quit_line_vty_cmd); @@ -3268,6 +3338,7 @@ void vtysh_init_vty(void) install_element(KEYCHAIN_NODE, &vtysh_end_all_cmd); install_element(KEYCHAIN_KEY_NODE, &vtysh_end_all_cmd); install_element(RMAP_NODE, &vtysh_end_all_cmd); + install_element(PBRMAP_NODE, &vtysh_end_all_cmd); install_element(VTY_NODE, &vtysh_end_all_cmd); install_element(INTERFACE_NODE, &vtysh_interface_desc_cmd); @@ -3290,6 +3361,11 @@ void vtysh_init_vty(void) install_element(LOGICALROUTER_NODE, &vtysh_exit_logicalrouter_cmd); install_element(LOGICALROUTER_NODE, &vtysh_quit_logicalrouter_cmd); + install_element(CONFIG_NODE, &vtysh_nexthop_group_cmd); + install_element(NH_GROUP_NODE, &vtysh_end_all_cmd); + install_element(NH_GROUP_NODE, &vtysh_exit_nexthop_group_cmd); + install_element(NH_GROUP_NODE, &vtysh_quit_nexthop_group_cmd); + install_element(VRF_NODE, &vtysh_end_all_cmd); install_element(VRF_NODE, &vtysh_exit_vrf_cmd); install_element(VRF_NODE, &vtysh_quit_vrf_cmd); @@ -3360,6 +3436,8 @@ void vtysh_init_vty(void) install_element(CONFIG_NODE, &key_chain_cmd); install_element(CONFIG_NODE, &vtysh_route_map_cmd); + install_element(CONFIG_NODE, &vtysh_pbr_map_cmd); + install_element(CONFIG_NODE, &vtysh_no_pbr_map_cmd); install_element(CONFIG_NODE, &vtysh_line_vty_cmd); install_element(KEYCHAIN_NODE, &key_cmd); install_element(KEYCHAIN_NODE, &key_chain_cmd); @@ -3374,6 +3452,7 @@ void vtysh_init_vty(void) install_element(CONFIG_NODE, &vtysh_vrf_cmd); install_element(CONFIG_NODE, &vtysh_no_vrf_cmd); + install_element(CONFIG_NODE, &vtysh_no_nexthop_group_cmd); /* "write terminal" command. */ install_element(ENABLE_NODE, &vtysh_write_terminal_cmd); diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h index 52a1a46105f1..f4f92bb21cf0 100644 --- a/vtysh/vtysh.h +++ b/vtysh/vtysh.h @@ -24,29 +24,30 @@ #include "memory.h" DECLARE_MGROUP(MVTYSH) -#define VTYSH_ZEBRA 0x01 -#define VTYSH_RIPD 0x02 -#define VTYSH_RIPNGD 0x04 -#define VTYSH_OSPFD 0x08 -#define VTYSH_OSPF6D 0x10 -#define VTYSH_BGPD 0x20 -#define VTYSH_ISISD 0x40 -#define VTYSH_PIMD 0x100 -#define VTYSH_LDPD 0x200 -#define VTYSH_WATCHFRR 0x400 -#define VTYSH_NHRPD 0x800 -#define VTYSH_EIGRPD 0x1000 -#define VTYSH_BABELD 0x2000 -#define VTYSH_SHARPD 0x4000 +#define VTYSH_ZEBRA 0x0001 +#define VTYSH_RIPD 0x0002 +#define VTYSH_RIPNGD 0x0004 +#define VTYSH_OSPFD 0x0008 +#define VTYSH_OSPF6D 0x0010 +#define VTYSH_BGPD 0x0020 +#define VTYSH_ISISD 0x0040 +#define VTYSH_PIMD 0x0080 +#define VTYSH_LDPD 0x0100 +#define VTYSH_WATCHFRR 0x0200 +#define VTYSH_NHRPD 0x0400 +#define VTYSH_EIGRPD 0x0800 +#define VTYSH_BABELD 0x1000 +#define VTYSH_SHARPD 0x2000 +#define VTYSH_PBRD 0x4000 /* commands in REALLYALL are crucial to correct vtysh operation */ #define VTYSH_REALLYALL ~0U /* watchfrr is not in ALL since library CLI functions should not be * run on it (logging & co. should stay in a fixed/frozen config, and * things like prefix lists are not even initialised) */ -#define VTYSH_ALL VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_LDPD|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_SHARPD +#define VTYSH_ALL VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_LDPD|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_SHARPD|VTYSH_PBRD #define VTYSH_RMAP VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_EIGRPD|VTYSH_SHARPD -#define VTYSH_INTERFACE VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD +#define VTYSH_INTERFACE VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_PBRD #define VTYSH_NS VTYSH_ZEBRA #define VTYSH_VRF VTYSH_ZEBRA|VTYSH_PIMD diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c index e3431fac59b9..f823e9a439f5 100644 --- a/vtysh/vtysh_config.c +++ b/vtysh/vtysh_config.c @@ -205,6 +205,9 @@ void vtysh_config_parse_line(void *arg, const char *line) config = config_get(LOGICALROUTER_NODE, line); else if (strncmp(line, "vrf", strlen("vrf")) == 0) config = config_get(VRF_NODE, line); + else if (strncmp(line, "nexthop-group", strlen("nexthop-group")) + == 0) + config = config_get(NH_GROUP_NODE, line); else if (strncmp(line, "router-id", strlen("router-id")) == 0) config = config_get(ZEBRA_NODE, line); else if (strncmp(line, "router rip", strlen("router rip")) == 0) @@ -235,6 +238,8 @@ void vtysh_config_parse_line(void *arg, const char *line) config = config_get(ISIS_NODE, line); else if (strncmp(line, "route-map", strlen("route-map")) == 0) config = config_get(RMAP_NODE, line); + else if (strncmp(line, "pbr-map", strlen("pbr-map")) == 0) + config = config_get(PBRMAP_NODE, line); else if (strncmp(line, "access-list", strlen("access-list")) == 0) config = config_get(ACCESS_NODE, line);