From e5c83d9b314cb513e78707de5d29ec655dbdca7e Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Tue, 23 Jan 2018 13:11:36 -0500 Subject: [PATCH 01/21] pbrd: Add PBR to FRR This is an implementation of PBR for FRR. This implemenation uses a combination of rules and tables to determine how packets will flow. PBR introduces a new concept of 'nexthop-groups' to specify a group of nexthops that will be used for ecmp. Nexthop-groups are specified on the cli via: nexthop-group DONNA nexthop 192.168.208.1 nexthop 192.168.209.1 nexthop 192.168.210.1 ! PBR sees the nexthop-group and installs these as a default route with these nexthops starting at table 10000 robot# show pbr nexthop-groups Nexthop-Group: DONNA Table: 10001 Valid: 1 Installed: 1 Valid: 1 nexthop 192.168.209.1 Valid: 1 nexthop 192.168.210.1 Valid: 1 nexthop 192.168.208.1 I have also introduced the ability to specify a table in a 'show ip route table XXX' to see the specified tables. robot# show ip route table 10001 Codes: K - kernel route, C - connected, S - static, R - RIP, O - OSPF, I - IS-IS, B - BGP, P - PIM, E - EIGRP, N - NHRP, T - Table, v - VNC, V - VNC-Direct, A - Babel, D - SHARP, F - PBR, > - selected route, * - FIB route F>* 0.0.0.0/0 [0/0] via 192.168.208.1, enp0s8, 00:14:25 * via 192.168.209.1, enp0s9, 00:14:25 * via 192.168.210.1, enp0s10, 00:14:25 PBR tracks PBR-MAPS via the pbr-map command: ! pbr-map EVA seq 10 match src-ip 4.3.4.0/24 set nexthop-group DONNA ! pbr-map EVA seq 20 match dst-ip 4.3.5.0/24 set nexthop-group DONNA ! pbr-maps can have 'match src-ip ' and 'match dst-ip ' to affect decisions about incoming packets. Additionally if you only have one nexthop to use for a pbr-map you do not need to setup a nexthop-group and can specify 'set nexthop XXXX'. To apply the pbr-map to an incoming interface you do this: interface enp0s10 pbr-policy EVA ! When a pbr-map is applied to interfaces it can be installed into the kernel as a rule: [sharpd@robot frr1]$ ip rule show 0: from all lookup local 309: from 4.3.4.0/24 iif enp0s10 lookup 10001 319: from all to 4.3.5.0/24 iif enp0s10 lookup 10001 1000: from all lookup [l3mdev-table] 32766: from all lookup main 32767: from all lookup default [sharpd@robot frr1]$ ip route show table 10001 default proto pbr metric 20 nexthop via 192.168.208.1 dev enp0s8 weight 1 nexthop via 192.168.209.1 dev enp0s9 weight 1 nexthop via 192.168.210.1 dev enp0s10 weight 1 The linux kernel now will use the rules and tables to properly apply these policies. Signed-off-by: Donald Sharp Signed-off-by: Don Slice Signed-off-by: Quentin Young --- Makefile.am | 1 + configure.ac | 1 + lib/command.c | 3 + lib/command.h | 1 + lib/route_types.txt | 2 + lib/vty.c | 2 + pbrd/.gitignore | 15 + pbrd/Makefile | 10 + pbrd/pbr_debug.c | 171 +++++++++ pbrd/pbr_debug.h | 53 +++ pbrd/pbr_event.c | 219 ++++++++++++ pbrd/pbr_event.h | 137 ++++++++ pbrd/pbr_main.c | 168 +++++++++ pbrd/pbr_map.c | 630 +++++++++++++++++++++++++++++++++ pbrd/pbr_map.h | 154 ++++++++ pbrd/pbr_memory.c | 27 ++ pbrd/pbr_memory.h | 24 ++ pbrd/pbr_nht.c | 699 +++++++++++++++++++++++++++++++++++++ pbrd/pbr_nht.h | 113 ++++++ pbrd/pbr_vty.c | 631 +++++++++++++++++++++++++++++++++ pbrd/pbr_vty.h | 24 ++ pbrd/pbr_zebra.c | 579 ++++++++++++++++++++++++++++++ pbrd/pbr_zebra.h | 37 ++ pbrd/pbrd.conf.sample | 3 + pbrd/subdir.am | 39 +++ redhat/daemons | 2 + redhat/frr.spec.in | 2 +- tools/etc/frr/daemons | 1 + tools/etc/frr/daemons.conf | 1 + tools/frr | 2 +- vtysh/Makefile.am | 6 + vtysh/extract.pl.in | 3 + vtysh/vtysh.c | 83 ++++- vtysh/vtysh.h | 33 +- vtysh/vtysh_config.c | 5 + 35 files changed, 3861 insertions(+), 20 deletions(-) create mode 100644 pbrd/.gitignore create mode 100644 pbrd/Makefile create mode 100644 pbrd/pbr_debug.c create mode 100644 pbrd/pbr_debug.h create mode 100644 pbrd/pbr_event.c create mode 100644 pbrd/pbr_event.h create mode 100644 pbrd/pbr_main.c create mode 100644 pbrd/pbr_map.c create mode 100644 pbrd/pbr_map.h create mode 100644 pbrd/pbr_memory.c create mode 100644 pbrd/pbr_memory.h create mode 100644 pbrd/pbr_nht.c create mode 100644 pbrd/pbr_nht.h create mode 100644 pbrd/pbr_vty.c create mode 100644 pbrd/pbr_vty.h create mode 100644 pbrd/pbr_zebra.c create mode 100644 pbrd/pbr_zebra.h create mode 100644 pbrd/pbrd.conf.sample create mode 100644 pbrd/subdir.am diff --git a/Makefile.am b/Makefile.am index 30150b594b..6cac1a7ba3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -56,6 +56,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 0b94115dba..53a80e790f 100755 --- a/configure.ac +++ b/configure.ac @@ -1377,6 +1377,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/lib/command.c b/lib/command.c index f244d67e86..2bff3b17a2 100644 --- a/lib/command.c +++ b/lib/command.c @@ -105,6 +105,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, @@ -1312,6 +1313,7 @@ void cmd_exit(struct vty *vty) case ISIS_NODE: case KEYCHAIN_NODE: case RMAP_NODE: + case PBRMAP_NODE: case VTY_NODE: vty->node = CONFIG_NODE; break; @@ -1409,6 +1411,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 8d88ea1902..4bb57c77a3 100644 --- a/lib/command.h +++ b/lib/command.h @@ -128,6 +128,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/route_types.txt b/lib/route_types.txt index 310a993c38..91eaf94d95 100644 --- a/lib/route_types.txt +++ b/lib/route_types.txt @@ -78,6 +78,7 @@ ZEBRA_ROUTE_BGP_DIRECT, bgp-direct, NULL, 'b', 0, 0, "BGP-Direct" ZEBRA_ROUTE_BGP_DIRECT_EXT, bgp-direct-to-nve-groups, NULL, 'e', 0, 0, "BGP2VNC" 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, "-" @@ -103,3 +104,4 @@ ZEBRA_ROUTE_LDP, "Label Distribution Protocol (LDP)" ZEBRA_ROUTE_VNC_DIRECT, "VNC direct (not via zebra) 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 9381c6e0fd..e8463da3d7 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: @@ -1115,6 +1116,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 0000000000..ff95d88527 --- /dev/null +++ b/pbrd/.gitignore @@ -0,0 +1,15 @@ +!Makefile +Makefile.in +libpbr.a +pbrd +tags +TAGS +.deps +*.o +*.lo +*.la +*.libs +.arch-inventory +.arch-ids +*~ +*.loT diff --git a/pbrd/Makefile b/pbrd/Makefile new file mode 100644 index 0000000000..e8999c3409 --- /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 0000000000..1634595128 --- /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 0000000000..2744724629 --- /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 0000000000..72a59801b4 --- /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 0000000000..2d895d9694 --- /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 0000000000..c8ee2f599f --- /dev/null +++ b/pbrd/pbr_main.c @@ -0,0 +1,168 @@ +/* + * PBR - main code + * Copyright (C) 2018 Cumulus Networks, Inc. + * Donald Sharp + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#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 0000000000..07607af505 --- /dev/null +++ b/pbrd/pbr_map.c @@ -0,0 +1,630 @@ +/* + * PBR-map Code + * Copyright (C) 2018 Cumulus Networks, Inc. + * Donald Sharp + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#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 0000000000..fca59951b5 --- /dev/null +++ b/pbrd/pbr_map.h @@ -0,0 +1,154 @@ +/* + * PBR-map Header + * Copyright (C) 2018 Cumulus Networks, Inc. + * Donald Sharp + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#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 + */ +#define PBR_MAP_NAMELEN 100 + char name[PBR_MAP_NAMELEN]; + + 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 0000000000..febe406ca7 --- /dev/null +++ b/pbrd/pbr_memory.c @@ -0,0 +1,27 @@ +/* + * PBR memory code. + * Copyright (C) 2018 Cumulus Networks, Inc. + * Donald Sharp + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#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 0000000000..a87d519099 --- /dev/null +++ b/pbrd/pbr_memory.h @@ -0,0 +1,24 @@ +/* + * pbr memory code. + * Copyright (C) 2018 Cumulus Networks, Inc. + * Donald Sharp + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#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 0000000000..817d8bb5e3 --- /dev/null +++ b/pbrd/pbr_nht.c @@ -0,0 +1,699 @@ +/* + * PBR-nht Code + * Copyright (C) 2018 Cumulus Networks, Inc. + * Donald Sharp + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#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 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]; + +static void *pbr_nh_alloc(void *p) +{ + struct pbr_nexthop_cache *new; + struct pbr_nexthop_cache *pnhc = (struct pbr_nexthop_cache *)p; + + new = XCALLOC(MTYPE_PBR_NHG, sizeof(*new)); + memcpy(&new->nexthop, &pnhc->nexthop, sizeof(struct nexthop)); + + 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) +{ + pbr_send_rnh(&(*pnhc)->nexthop, false); + + 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 = jhash_1word(pbrnc->nexthop.vrf_id, 0x45afe398); + key = jhash_1word(pbrnc->nexthop.ifindex, key); + key = jhash_1word(pbrnc->nexthop.type, key); + key = jhash(&pbrnc->nexthop.gate, sizeof(union g_addr), key); + + 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; + + memcpy(&lookup.nexthop, nhop, sizeof(*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, size_t l, + uint32_t seqno, char *buffer) +{ + snprintf(buffer, l, "%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, PBR_MAP_NAMELEN, + 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); + + memcpy(&lookup.nexthop, pbrms->nhg->nexthop, sizeof(struct 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; + memcpy(&lup.nexthop, nh, sizeof(struct nexthop)); + 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; + + memcpy(&lookup.nexthop, nhop, sizeof(*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_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 0000000000..2508f49e4b --- /dev/null +++ b/pbrd/pbr_nht.h @@ -0,0 +1,113 @@ +/* + * PBR-nht Header + * Copyright (C) 2018 Cumulus Networks, Inc. + * Donald Sharp + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef __PBR_NHT_H__ +#define __PBR_NHT_H__ + +#include +#include + +struct pbr_nexthop_group_cache { + char name[PBR_MAP_NAMELEN]; + + 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, size_t l, 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 0000000000..730cfd40f2 --- /dev/null +++ b/pbrd/pbr_vty.c @@ -0,0 +1,631 @@ +/* + * PBR - vty code + * Copyright (C) 2018 Cumulus Networks, Inc. + * Donald Sharp + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#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, + PBR_MAP_NAMELEN, + pbrms->seqno, + buf)); + nh = NULL; + } + + if (no) { + if (nh) { + // nexthop_del(pbrms->nhg, nh); + // nexthop_free(nh); + pbre = pbr_event_new(PBR_MAP_NEXTHOP_DELETE, + pbrms->parent->name); + pbre->seqno = pbrms->seqno; + pbr_event_enqueue(pbre); + } + } else if (!nh) { + + if (pbrms->nhg->nexthop) { + vty_out(vty, + "If you would like more than one nexthop please use nexthop-groups"); + return CMD_WARNING; + } + + /* must be adding new nexthop since !no and !nexthop_exists */ + nh = nexthop_new(); + + memcpy(nh, &nhop, sizeof(nhop)); + nexthop_add(&pbrms->nhg->nexthop, nh); + + pbre = pbr_event_new(PBR_MAP_NEXTHOP_ADD, pbrms->parent->name); + pbre->seqno = pbrms->seqno; + pbr_event_enqueue(pbre); + } + + return CMD_SUCCESS; +} + +DEFPY (pbr_table_range, + pbr_table_range_cmd, + "[no]$no pbr table range (10000-65535)$start (11000-65535)$end", + NO_STR + "Policy based routing\n" + "Policy based routing table\n" + "Table range\n" + "Initial value of range\n" + "Final value of range\n") +{ + if (no) + pbr_nht_set_tableid_range(PBR_NHT_DEFAULT_LOW_TABLEID, + PBR_NHT_DEFAULT_HIGH_TABLEID); + else + pbr_nht_set_tableid_range(start, end); + + return CMD_SUCCESS; +} + +DEFPY (pbr_rule_range, + pbr_rule_range_cmd, + "[no] pbr rule range (300-1300)$start (400-1400)$end", + NO_STR + "Policy based routing\n" + "Policy based routing rule\n" + "Rule range\n" + "Initial value of range\n" + "Final value of range\n") +{ + if (no) + pbr_nht_set_rule_range(PBR_NHT_DEFAULT_LOW_RULE, + PBR_NHT_DEFAULT_HIGH_RULE); + else + pbr_nht_set_rule_range(start, end); + + return CMD_SUCCESS; +} + +DEFPY (pbr_policy, + pbr_policy_cmd, + "[no] pbr-policy NAME$mapname", + NO_STR + "Policy to use\n" + "Name of the pbr-map to apply\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct pbr_map *pbrm, *old_pbrm; + struct pbr_interface *pbr_ifp = ifp->info; + + pbrm = pbrm_find(mapname); + + if (no) { + if (strcmp(pbr_ifp->mapname, mapname) == 0) { + strcpy(pbr_ifp->mapname, ""); + + if (pbrm) + pbr_map_interface_delete(pbrm, ifp); + } + } else { + if (strcmp(pbr_ifp->mapname, "") == 0) { + strcpy(pbr_ifp->mapname, mapname); + + if (pbrm) + pbr_map_add_interface(pbrm, ifp); + } else { + if (!(strcmp(pbr_ifp->mapname, mapname) == 0)) { + old_pbrm = pbrm_find(pbr_ifp->mapname); + if (old_pbrm) + pbr_map_interface_delete(old_pbrm, ifp); + strcpy(pbr_ifp->mapname, mapname); + if (pbrm) + pbr_map_add_interface(pbrm, ifp); + } + } + } + + return CMD_SUCCESS; +} + +DEFPY (show_pbr, + show_pbr_cmd, + "show pbr [json$json]", + SHOW_STR + "Policy Based Routing\n" + JSON_STR) +{ + pbr_nht_write_table_range(vty); + pbr_nht_write_rule_range(vty); + + return CMD_SUCCESS; +} + +DEFPY (show_pbr_map, + show_pbr_map_cmd, + "show pbr map [NAME$name] [detail$detail] [json$json]", + SHOW_STR + "Policy Based Routing\n" + "PBR Map\n" + "PBR Map Name\n" + "Detailed information\n" + JSON_STR) +{ + struct pbr_map_sequence *pbrms; + struct pbr_map *pbrm; + struct listnode *node; + char buf[PREFIX_STRLEN]; + char rbuf[64]; + + RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) { + if (name && strcmp(name, pbrm->name) != 0) + continue; + + vty_out(vty, " pbr-map %s valid: %d\n", pbrm->name, + pbrm->valid); + + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) { + if (pbrms->reason) + pbr_map_reason_string(pbrms->reason, rbuf, + sizeof(rbuf)); + vty_out(vty, + " Seq: %u rule: %u Installed: %d(%u) Reason: %s\n", + pbrms->seqno, pbrms->ruleno, pbrms->installed, + pbrms->unique, pbrms->reason ? rbuf : "Valid"); + + if (pbrms->src) + vty_out(vty, "\tSRC Match: %s\n", + prefix2str(pbrms->src, buf, + sizeof(buf))); + if (pbrms->dst) + vty_out(vty, "\tDST Match: %s\n", + prefix2str(pbrms->dst, buf, + sizeof(buf))); + + if (pbrms->nhgrp_name) { + vty_out(vty, + "\tNexthop-Group: %s(%u) Installed: %u(%d)\n", + pbrms->nhgrp_name, + pbr_nht_get_table(pbrms->nhgrp_name), + pbrms->nhs_installed, + pbr_nht_get_installed( + pbrms->nhgrp_name)); + } else if (pbrms->nhg) { + vty_out(vty, " "); + nexthop_group_write_nexthop( + vty, pbrms->nhg->nexthop); + vty_out(vty, + "\tInstalled: %u(%d) Tableid: %d\n", + pbrms->nhs_installed, + pbr_nht_get_installed( + pbrms->internal_nhg_name), + pbr_nht_get_table( + pbrms->internal_nhg_name)); + } else { + vty_out(vty, + "\tNexthop-Group: Unknown Installed: 0(0)\n"); + } + } + } + return CMD_SUCCESS; +} + +DEFPY(show_pbr_nexthop_group, + show_pbr_nexthop_group_cmd, + "show pbr nexthop-groups [WORD$word]", + SHOW_STR + "Policy Based Routing\n" + "Nexthop Groups\n" + "Optional Name of the nexthop group\n") +{ + pbr_nht_show_nexthop_group(vty, word); + + return CMD_SUCCESS; +} + +DEFPY (show_pbr_interface, + show_pbr_interface_cmd, + "show pbr interface [NAME$name] [json$json]", + SHOW_STR + "Policy Based Routing\n" + "PBR Interface\n" + "PBR Interface Name\n" + JSON_STR) +{ + struct interface *ifp; + struct vrf *vrf; + struct pbr_interface *pbr_ifp; + + RB_FOREACH(vrf, vrf_name_head, &vrfs_by_name) { + FOR_ALL_INTERFACES(vrf, ifp) { + struct pbr_map *pbrm; + + if (name && strcmp(ifp->name, name) != 0) + continue; + + pbr_ifp = ifp->info; + + if (strcmp(pbr_ifp->mapname, "") == 0) + continue; + + pbrm = pbrm_find(pbr_ifp->mapname); + vty_out(vty, " %s(%d) with pbr-policy %s", ifp->name, + ifp->ifindex, pbr_ifp->mapname); + if (!pbrm) + vty_out(vty, " (map doesn't exist)"); + vty_out(vty, "\n"); + } + } + + return CMD_SUCCESS; +} + +static struct cmd_node interface_node = { + INTERFACE_NODE, "%s(config-if)# ", 1 /* vtysh ? yes */ +}; + +static int pbr_interface_config_write(struct vty *vty) +{ + struct interface *ifp; + struct vrf *vrf; + + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { + FOR_ALL_INTERFACES (vrf, ifp) { + if (vrf->vrf_id == VRF_DEFAULT) + vty_frame(vty, "interface %s\n", ifp->name); + else + vty_frame(vty, "interface %s vrf %s\n", + ifp->name, vrf->name); + + pbr_map_write_interfaces(vty, ifp); + + vty_endframe(vty, "!\n"); + } + } + + return 1; +} + +/* PBR map node structure. */ +static struct cmd_node pbr_map_node = {PBRMAP_NODE, "%s(config-pbr-map)# ", 1}; + +static int pbr_vty_map_config_write_sequence(struct vty *vty, + struct pbr_map *pbrm, + struct pbr_map_sequence *pbrms) +{ + char buff[PREFIX_STRLEN]; + + vty_out (vty, "pbr-map %s seq %u\n", + pbrm->name, pbrms->seqno); + + if (pbrms->src) + vty_out(vty, " match src-ip %s\n", + prefix2str(pbrms->src, buff, sizeof buff)); + + if (pbrms->dst) + vty_out(vty, " match dst-ip %s\n", + prefix2str(pbrms->dst, buff, sizeof buff)); + + if (pbrms->nhgrp_name) + vty_out(vty, " set nexthop-group %s\n", pbrms->nhgrp_name); + + if (pbrms->nhg) { + vty_out(vty, " set"); + nexthop_group_write_nexthop(vty, pbrms->nhg->nexthop); + } + + vty_out (vty, "!\n"); + return 1; +} + +static int pbr_vty_map_config_write(struct vty *vty) +{ + struct pbr_map *pbrm; + + pbr_nht_write_table_range(vty); + pbr_nht_write_rule_range(vty); + + RB_FOREACH(pbrm, pbr_map_entry_head, &pbr_maps) { + struct pbr_map_sequence *pbrms; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) { + pbr_vty_map_config_write_sequence(vty, pbrm, pbrms); + } + } + + return 1; +} + +void pbr_vty_init(void) +{ + install_node(&interface_node, + pbr_interface_config_write); + if_cmd_init(); + + install_node(&pbr_map_node, + pbr_vty_map_config_write); + + install_default(PBRMAP_NODE); + + install_element(CONFIG_NODE, &pbr_map_cmd); + install_element(CONFIG_NODE, &no_pbr_map_cmd); + install_element(INTERFACE_NODE, &pbr_policy_cmd); + install_element(CONFIG_NODE, &pbr_table_range_cmd); + install_element(CONFIG_NODE, &pbr_rule_range_cmd); + install_element(PBRMAP_NODE, &pbr_map_match_src_cmd); + install_element(PBRMAP_NODE, &pbr_map_match_dst_cmd); + install_element(PBRMAP_NODE, &pbr_map_nexthop_group_cmd); + install_element(PBRMAP_NODE, &pbr_map_nexthop_cmd); + install_element(VIEW_NODE, &show_pbr_cmd); + install_element(VIEW_NODE, &show_pbr_map_cmd); + install_element(VIEW_NODE, &show_pbr_interface_cmd); + install_element(VIEW_NODE, &show_pbr_nexthop_group_cmd); + + pbr_debug_init_vty(); + + return; +} diff --git a/pbrd/pbr_vty.h b/pbrd/pbr_vty.h new file mode 100644 index 0000000000..6e345fd7e2 --- /dev/null +++ b/pbrd/pbr_vty.h @@ -0,0 +1,24 @@ +/* + * VTY library for PBR + * Copyright (C) 2018 Cumulus Networks, Inc. + * Donald Sharp + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#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 0000000000..8c2d470da1 --- /dev/null +++ b/pbrd/pbr_zebra.c @@ -0,0 +1,579 @@ +/* + * Zebra connect code. + * Copyright (C) 2018 Cumulus Networks, Inc. + * Donald Sharp + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#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 0000000000..2ee99ec837 --- /dev/null +++ b/pbrd/pbr_zebra.h @@ -0,0 +1,37 @@ +/* + * Zebra connect library for PBR + * Copyright (C) 2018 Cumulus Networks, Inc. + * Donald Sharp + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#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 0000000000..bb1c2edca8 --- /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 0000000000..361e6c1fde --- /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 889e288e57..1dd7c95d23 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 14fb910219..8832a6d403 100644 --- a/redhat/frr.spec.in +++ b/redhat/frr.spec.in @@ -86,7 +86,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 ac17fed03a..9a96c0490a 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 783d167318..e6c0cde968 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 6de19a4bde..09f1fa493f 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 33d34fc0dd..52641de72c 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 bdee0d47e2..c0277b3d61 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 14c7c2fed6..2cab5bdaef 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 = @@ -997,8 +998,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, @@ -1504,6 +1512,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") @@ -1563,6 +1589,7 @@ static int vtysh_exit(struct vty *vty) case LDP_L2VPN_NODE: case ISIS_NODE: case RMAP_NODE: + case PBRMAP_NODE: case VTY_NODE: case KEYCHAIN_NODE: vtysh_execute("end"); @@ -1709,6 +1736,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") { @@ -1852,6 +1891,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") @@ -1890,6 +1943,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, @@ -1965,7 +2030,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" @@ -1974,7 +2039,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; @@ -3130,7 +3196,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); @@ -3259,6 +3327,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); @@ -3299,6 +3369,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); @@ -3321,6 +3392,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); @@ -3394,6 +3470,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); @@ -3410,6 +3488,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 9b21c3376a..f3e58f309e 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 e94bd139e5..3748fef54c 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); From a6c93cb20fa11bfdc3dedc81099cf9ee39f19f64 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Thu, 15 Mar 2018 10:13:58 -0400 Subject: [PATCH 02/21] doc: Add documentation for PBRD Signed-off-by: Donald Sharp --- doc/Makefile.am | 6 ++ doc/manpages/common-options.rst | 1 + doc/manpages/conf.py | 1 + doc/manpages/defines.rst | 2 +- doc/manpages/index.rst | 1 + doc/manpages/pbrd.rst | 39 ++++++++++ doc/user/index.rst | 1 + doc/user/pbr.rst | 130 ++++++++++++++++++++++++++++++++ 8 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 doc/manpages/pbrd.rst create mode 100644 doc/user/pbr.rst diff --git a/doc/Makefile.am b/doc/Makefile.am index 6228683f94..a6e693c0c9 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -26,6 +26,10 @@ man_MANS += $(MANPAGE_BUILDDIR)/pimd.8 man_MANS += $(MANPAGE_BUILDDIR)/mtracebis.8 endif +if PBRD +man_MANS += $(MANPAGE_BUILDDIR)/pbrd.8 +endif + if BGPD man_MANS += $(MANPAGE_BUILDDIR)/bgpd.8 endif @@ -140,6 +144,7 @@ EXTRA_DIST = frr-sphinx.mk \ manpages/ospfd.rst \ manpages/pimd.rst \ manpages/ripd.rst \ + manpages/pbrd.rst \ manpages/ripngd.rst \ manpages/vtysh.rst \ manpages/watchfrr.rst \ @@ -202,6 +207,7 @@ EXTRA_DIST = frr-sphinx.mk \ user/overview.rst \ user/pim.rst \ user/ripd.rst \ + user/pbr.rst \ user/ripngd.rst \ user/routemap.rst \ user/routeserver.rst \ diff --git a/doc/manpages/common-options.rst b/doc/manpages/common-options.rst index 5b136f5e1a..e433c720af 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 a78e1a2c38..b5ea537faa 100644 --- a/doc/manpages/conf.py +++ b/doc/manpages/conf.py @@ -318,6 +318,7 @@ man_pages = [ ('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 841abe300e..21c3790afa 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 ae5c4ca3bd..964cc07d71 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 0000000000..38e1bc267b --- /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 e505042994..c264b4c83e 100644 --- a/doc/user/index.rst +++ b/doc/user/index.rst @@ -22,6 +22,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 0000000000..7d25fc794e --- /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. + + From d37653864b4af35e262294a09c756fedec4793b3 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Thu, 15 Mar 2018 11:56:07 -0400 Subject: [PATCH 03/21] pbrd: Cleanup CI warnings There are a bunch of CI warnings that need to be cleaned up. Signed-off-by: Donald Sharp --- pbrd/pbr_debug.c | 2 ++ pbrd/pbr_event.c | 3 ++- pbrd/pbr_main.c | 4 ++-- pbrd/pbr_map.c | 39 ++++++++++++++++++--------------------- pbrd/pbr_nht.c | 4 ++-- pbrd/pbr_vty.c | 17 +++++++---------- pbrd/pbr_zebra.c | 11 +++-------- pbrd/pbr_zebra.h | 2 ++ 8 files changed, 38 insertions(+), 44 deletions(-) diff --git a/pbrd/pbr_debug.c b/pbrd/pbr_debug.c index 1634595128..e9b4a52955 100644 --- a/pbrd/pbr_debug.c +++ b/pbrd/pbr_debug.c @@ -75,6 +75,7 @@ static void pbr_debug_set_all(uint32_t flags, bool set) 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; @@ -118,6 +119,7 @@ DEFPY(debug_pbr, "Events\n") { uint32_t mode = DEBUG_NODE2MODE(vty->node); + if (map) DEBUG_MODE_SET(&pbr_dbg_map, mode, !no); if (zebra) diff --git a/pbrd/pbr_event.c b/pbrd/pbr_event.c index 72a59801b4..4563d7d91c 100644 --- a/pbrd/pbr_event.c +++ b/pbrd/pbr_event.c @@ -40,7 +40,7 @@ struct work_queue *pbr_event_wq; static const char *pbr_event_wqentry2str(struct pbr_event *pbre, char *buffer, size_t buflen) { - switch(pbre->event) { + switch (pbre->event) { case PBR_NHG_NEW: snprintf(buffer, buflen, "Nexthop Group Added %s", pbre->name); @@ -197,6 +197,7 @@ void pbr_event_enqueue(struct pbr_event *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) diff --git a/pbrd/pbr_main.c b/pbrd/pbr_main.c index c8ee2f599f..83cac6daa2 100644 --- a/pbrd/pbr_main.c +++ b/pbrd/pbr_main.c @@ -66,7 +66,7 @@ struct zebra_privs_t pbr_privs = { .cap_num_p = array_size(_caps_p), .cap_num_i = 0}; -struct option longopts[] = {{0}}; +struct option longopts[] = { { 0 } }; /* Master of threads. */ struct thread_master *master; @@ -120,7 +120,7 @@ FRR_DAEMON_INFO(pbrd, PBR, .vty_port = PBR_VTY_PORT, .signals = pbr_signals, .n_signals = array_size(pbr_signals), - .privs = &pbr_privs, ) + .privs = &pbr_privs,) int main(int argc, char **argv, char **envp) { diff --git a/pbrd/pbr_map.c b/pbrd/pbr_map.c index 07607af505..3b058675d7 100644 --- a/pbrd/pbr_map.c +++ b/pbrd/pbr_map.c @@ -43,8 +43,8 @@ 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); +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) @@ -52,8 +52,8 @@ 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) +static inline int pbr_map_compare(const struct pbr_map *pbrmap1, + const struct pbr_map *pbrmap2) { return strcmp(pbrmap1->name, pbrmap2->name); } @@ -205,8 +205,8 @@ extern void pbr_map_delete(const char *name, uint32_t seqno) } } -extern struct pbr_map_sequence *pbrms_lookup_unique(uint32_t unique, - ifindex_t ifindex) +struct pbr_map_sequence *pbrms_lookup_unique(uint32_t unique, + ifindex_t ifindex) { struct pbr_map_sequence *pbrms; struct listnode *snode, *inode; @@ -232,7 +232,7 @@ extern struct pbr_map_sequence *pbrms_lookup_unique(uint32_t unique, return NULL; } -extern struct pbr_map_sequence *pbrms_get(const char *name, uint32_t seqno) +struct pbr_map_sequence *pbrms_get(const char *name, uint32_t seqno) { struct pbr_map *pbrm; struct pbr_map_sequence *pbrms; @@ -356,7 +356,7 @@ static bool pbr_map_check_valid_internal(struct pbr_map *pbrm) * 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) +bool pbr_map_check_valid(const char *name) { struct pbr_map *pbrm; @@ -372,7 +372,7 @@ extern bool pbr_map_check_valid(const char *name) return pbrm->valid; } -extern void pbr_map_schedule_policy_from_nhg(const char *nh_group) +void pbr_map_schedule_policy_from_nhg(const char *nh_group) { struct pbr_map_sequence *pbrms; struct pbr_event *pbre; @@ -412,7 +412,7 @@ extern void pbr_map_schedule_policy_from_nhg(const char *nh_group) } } -extern void pbr_map_policy_install(const char *name) +void pbr_map_policy_install(const char *name) { struct pbr_map_sequence *pbrms; struct pbr_map *pbrm; @@ -440,7 +440,7 @@ extern void pbr_map_policy_install(const char *name) } } -extern void pbr_map_policy_delete(const char *ifname) +void pbr_map_policy_delete(const char *ifname) { struct listnode *node, *nnode; struct pbr_map_interface *pmi; @@ -467,7 +467,7 @@ extern void pbr_map_policy_delete(const char *ifname) * 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) +void pbr_map_check_nh_group_change(const char *nh_group) { struct pbr_map_sequence *pbrms; struct pbr_map *pbrm; @@ -487,8 +487,6 @@ extern void pbr_map_check_nh_group_change(const char *nh_group) if (found_name) { bool original = pbrm->valid; - zlog_warn("*** %s pbrm->valid is %u ***", - __func__, pbrm->valid); pbr_map_check_valid_internal(pbrm); @@ -505,7 +503,7 @@ extern void pbr_map_check_nh_group_change(const char *nh_group) } } -extern void pbr_map_check(const char *name, uint32_t seqno) +void pbr_map_check(const char *name, uint32_t seqno) { struct pbr_map_sequence *pbrms; struct listnode *node; @@ -554,7 +552,7 @@ extern void pbr_map_check(const char *name, uint32_t seqno) } } -extern void pbr_map_install(const char *name) +void pbr_map_install(const char *name) { struct pbr_map *pbrm; @@ -573,7 +571,7 @@ extern void pbr_map_install(const char *name) pbrm->installed = true; } -extern void pbr_map_add_interfaces(const char *name) +void pbr_map_add_interfaces(const char *name) { struct pbr_map *pbrm; struct interface *ifp; @@ -589,7 +587,7 @@ extern void pbr_map_add_interfaces(const char *name) } RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { - FOR_ALL_INTERFACES (vrf, ifp) { + FOR_ALL_INTERFACES (vrf, ifp) { if (ifp->info) { pbr_ifp = ifp->info; if (strcmp(name, pbr_ifp->mapname) == 0) @@ -599,8 +597,7 @@ extern void pbr_map_add_interfaces(const char *name) } } - -extern void pbr_map_check_policy_change(const char *name) +void pbr_map_check_policy_change(const char *name) { struct pbr_map *pbrm; @@ -622,7 +619,7 @@ extern void pbr_map_check_policy_change(const char *name) } } -extern void pbr_map_init(void) +void pbr_map_init(void) { RB_INIT(pbr_map_entry_head, &pbr_maps); diff --git a/pbrd/pbr_nht.c b/pbrd/pbr_nht.c index 817d8bb5e3..63d509bf6b 100644 --- a/pbrd/pbr_nht.c +++ b/pbrd/pbr_nht.c @@ -220,6 +220,7 @@ 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)) { @@ -639,9 +640,8 @@ bool pbr_nht_get_installed(const char *name) pnhgc = hash_lookup(pbr_nhg_hash, &find); - if (!pnhgc) { + if (!pnhgc) return false; - } return pnhgc->installed; } diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c index 730cfd40f2..6787edbe07 100644 --- a/pbrd/pbr_vty.c +++ b/pbrd/pbr_vty.c @@ -495,7 +495,7 @@ DEFPY (show_pbr_interface, "PBR Interface Name\n" JSON_STR) { - struct interface *ifp; + struct interface *ifp; struct vrf *vrf; struct pbr_interface *pbr_ifp; @@ -558,16 +558,16 @@ static int pbr_vty_map_config_write_sequence(struct vty *vty, { char buff[PREFIX_STRLEN]; - vty_out (vty, "pbr-map %s seq %u\n", - pbrm->name, pbrms->seqno); + 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)); + prefix2str(pbrms->src, buff, sizeof(buff))); if (pbrms->dst) vty_out(vty, " match dst-ip %s\n", - prefix2str(pbrms->dst, buff, sizeof buff)); + prefix2str(pbrms->dst, buff, sizeof(buff))); if (pbrms->nhgrp_name) vty_out(vty, " set nexthop-group %s\n", pbrms->nhgrp_name); @@ -577,7 +577,7 @@ static int pbr_vty_map_config_write_sequence(struct vty *vty, nexthop_group_write_nexthop(vty, pbrms->nhg->nexthop); } - vty_out (vty, "!\n"); + vty_out(vty, "!\n"); return 1; } @@ -592,9 +592,8 @@ static int pbr_vty_map_config_write(struct vty *vty) struct pbr_map_sequence *pbrms; struct listnode *node; - for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) { + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) pbr_vty_map_config_write_sequence(vty, pbrm, pbrms); - } } return 1; @@ -626,6 +625,4 @@ void pbr_vty_init(void) install_element(VIEW_NODE, &show_pbr_nexthop_group_cmd); pbr_debug_init_vty(); - - return; } diff --git a/pbrd/pbr_zebra.c b/pbrd/pbr_zebra.c index 8c2d470da1..b066965249 100644 --- a/pbrd/pbr_zebra.c +++ b/pbrd/pbr_zebra.c @@ -43,10 +43,7 @@ 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; +struct zclient *zclient; static struct interface *zebra_interface_if_lookup(struct stream *s) { @@ -368,8 +365,6 @@ void route_delete(struct pbr_nexthop_group_cache *pnhgc, afi_t afi) __PRETTY_FUNCTION__); break; } - - return; } static int pbr_zebra_nexthop_update(int command, struct zclient *zclient, @@ -433,7 +428,7 @@ void pbr_send_rnh(struct nexthop *nhop, bool reg) ZEBRA_NEXTHOP_REGISTER : ZEBRA_NEXTHOP_UNREGISTER; memset(&p, 0, sizeof(p)); - switch(nhop->type) { + switch (nhop->type) { case NEXTHOP_TYPE_IFINDEX: case NEXTHOP_TYPE_BLACKHOLE: return; @@ -542,7 +537,7 @@ void pbr_send_pbr_map(struct pbr_map *pbrm, bool install) for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, snode, pbrms)) { - DEBUGD(&pbr_dbg_zebra, "%s: \tSeqno: %u %ld valid %u", + DEBUGD(&pbr_dbg_zebra, "%s: \tSeqno: %u %" PRIu64 " valid %u", __PRETTY_FUNCTION__, pbrms->seqno, pbrms->reason, pbrm->valid); diff --git a/pbrd/pbr_zebra.h b/pbrd/pbr_zebra.h index 2ee99ec837..6c1eca9d19 100644 --- a/pbrd/pbr_zebra.h +++ b/pbrd/pbr_zebra.h @@ -24,6 +24,8 @@ struct pbr_interface { char mapname[100]; }; +extern struct thread_master *master; + extern void pbr_zebra_init(void); extern void route_add(struct pbr_nexthop_group_cache *pnhgc, From 1abe1280a0903f3a096f06655e4ffb4906428443 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Thu, 15 Mar 2018 12:46:18 -0400 Subject: [PATCH 04/21] redhat: PBR modifications to allow it to build properly Add to the redhat build the PBR daemon and it's ancillary files. Signed-off-by: Donald Sharp --- redhat/frr.init | 2 +- redhat/frr.spec.in | 22 ++++++++++++++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/redhat/frr.init b/redhat/frr.init index a62647b258..19b282fe1a 100755 --- a/redhat/frr.init +++ b/redhat/frr.init @@ -33,7 +33,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 pimd ldpd nhrpd eigrpd babeld" +DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd pimd pbrd ldpd nhrpd eigrpd babeld" MAX_INSTANCES=5 RELOAD_SCRIPT=/usr/lib/frr/frr-reload.py diff --git a/redhat/frr.spec.in b/redhat/frr.spec.in index 8832a6d403..f83c08e611 100644 --- a/redhat/frr.spec.in +++ b/redhat/frr.spec.in @@ -27,6 +27,7 @@ %{!?with_bgp_vnc: %global with_bgp_vnc 0 } %{!?with_pimd: %global with_pimd 1 } %{!?with_rpki: %global with_rpki 0 } +%{!?with_pbrd: %global with_pbrd 1 } # path defines %define _sysconfdir /etc/frr @@ -100,6 +101,12 @@ %define daemon_pimd "" %endif +%if %{with_pbrd} +%define daemon_pbrd pbrd +%else +%define daemon_pbrd "" +%endif + %if %{with_nhrpd} %define daemon_nhrpd nhrpd %else @@ -124,7 +131,7 @@ %define daemon_watchfrr "" %endif -%define all_daemons %{daemon_list} %{daemon_ldpd} %{daemon_pimd} %{daemon_nhrpd} %{daemon_eigrpd} %{daemon_babeld} %{daemon_watchfrr} +%define all_daemons %{daemon_list} %{daemon_ldpd} %{daemon_pimd} %{daemon_nhrpd} %{daemon_eigrpd} %{daemon_babeld} %{daemon_watchfrr} %{daemon_pbrd} # allow build dir to be kept %{!?keep_build: %global keep_build 0 } @@ -182,7 +189,7 @@ protocol. It takes multi-server and multi-thread approach to resolve the current complexity of the Internet. FRRouting supports BGP4, OSPFv2, OSPFv3, ISIS, RIP, RIPng, PIM, LDP -NHRP, Babel and EIGRP. +NHRP, Babel, PBR and EIGRP. FRRouting is a fork of Quagga. @@ -266,6 +273,11 @@ developing OSPF-API and frr applications. %else --disable-pimd \ %endif +%if %{with_pbrd} + --enable-pbrd \ +%else + --disable-pbrd \ +%endif %if %{with_nhrpd} --enable-nhrpd \ %else @@ -414,6 +426,9 @@ zebra_spec_add_service nhrpd 2610/tcp "NHRPd vty" %if %{with_pimd} zebra_spec_add_service pimd 2611/tcp "PIMd vty" %endif +%if %{with_pbrd} +zebra_spec_add_service pbrd 2615/tcp "PBRd vty" +%endif %if %{with_ldpd} zebra_spec_add_service ldpd 2612/tcp "LDPd vty" %endif @@ -547,6 +562,9 @@ rm -rf %{buildroot} %if %{with_pimd} %{_sbindir}/pimd %endif +%if %{with_pbrd} + %{_sbindir}/pbrd +%endif %{_sbindir}/isisd %if %{with_ldpd} %{_sbindir}/ldpd From 6568993b1b624637db7999b7f00cc62b7501bd4f Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Fri, 16 Mar 2018 18:11:45 -0400 Subject: [PATCH 05/21] doc: bit of cleanup for pbr docs * Indent is 3 spaces * Use CLI cross-references * Wrap to 80 columns * Adjust section capitalization Signed-off-by: Quentin Young --- doc/manpages/pbrd.rst | 3 +- doc/user/pbr.rst | 111 ++++++++++++++++++++---------------------- 2 files changed, 53 insertions(+), 61 deletions(-) diff --git a/doc/manpages/pbrd.rst b/doc/manpages/pbrd.rst index 38e1bc267b..d9a80b1f84 100644 --- a/doc/manpages/pbrd.rst +++ b/doc/manpages/pbrd.rst @@ -31,8 +31,7 @@ FILES 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|. + 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/pbr.rst b/doc/user/pbr.rst index 7d25fc794e..eec575cf3f 100644 --- a/doc/user/pbr.rst +++ b/doc/user/pbr.rst @@ -4,22 +4,21 @@ 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. +: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 +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. +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 @@ -28,33 +27,31 @@ documented elsewhere. .. _nexthop-groups: -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. +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. + 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. - + 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 ======== -PBR MAPS are a way to group policies that we would like to apply +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 @@ -62,69 +59,65 @@ 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. + 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 +.. clicmd:: match src-ip PREFIX - 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. + 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 + :clicmd:`match dst-ip PREFIX` command for matching. -.. clicmd:: match dst-ip +.. clicmd:: match dst-ip PREFIX - 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. + 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 + :clicmd:`match src-ip PREFIX` 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. + 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. + Use this individual nexthop as the place to forward packets when the match + commands have matched a packet. .. _pbr-policy: -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. +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 +.. 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. + 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 +PBR Details =========== -Under the covers a PBR MAP is translated into two separate constructs -in the linux kernel. +Under the covers a PBR map is translated into two separate constructs in the +Linux kernel. -..index:: Rules +.. index:: PBR 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 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. - 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. +.. index:: PBR 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. - From d36d0d57711ec79bb7ff4360b62d80b1a9809f6f Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Mon, 19 Mar 2018 09:01:52 -0400 Subject: [PATCH 06/21] lib: Add hash and use const a bit more intelligently This commit adds code to notify the compiler that we will not be changing the arguments to nexthop2str and we expect thre return to be treated the same. Additionally we add some code to allow nexthops to be hashed to be used in a hash. Signed-off-by: Quentin Young --- lib/nexthop.c | 15 ++++++++++++++- lib/nexthop.h | 19 ++++++++++++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/lib/nexthop.c b/lib/nexthop.c index b1e9582b20..3603050f06 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") @@ -240,7 +241,7 @@ void nexthop_del_labels(struct nexthop *nexthop) } } -const char *nexthop2str(struct nexthop *nexthop, char *str, int size) +const char *nexthop2str(const struct nexthop *nexthop, char *str, int size) { switch (nexthop->type) { case NEXTHOP_TYPE_IFINDEX: @@ -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 036fc5b888..e4af405d5f 100644 --- a/lib/nexthop.h +++ b/lib/nexthop.h @@ -118,6 +118,23 @@ void nexthop_add_labels(struct nexthop *, enum lsp_types_t, uint8_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); @@ -126,7 +143,7 @@ extern int nexthop_same_no_recurse(const struct nexthop *next1, extern int nexthop_labels_match(struct nexthop *nh1, struct nexthop *nh2); extern int nexthop_same_firsthop(struct nexthop *next1, struct nexthop *next2); -extern const char *nexthop2str(struct nexthop *nexthop, char *str, int size); +extern const char *nexthop2str(const struct nexthop *nexthop, char *str, int size); extern struct nexthop *nexthop_next(struct nexthop *nexthop); extern unsigned int nexthop_level(struct nexthop *nexthop); #endif /*_LIB_NEXTHOP_H */ From b13e5ad68a740b66c921c5ab1314e0898f0ed310 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Mon, 19 Mar 2018 09:04:17 -0400 Subject: [PATCH 07/21] pbrd: Remove pbr_events The pbr_events.c file was a mistake in that it overly complicated the code and made it hard to think about what was happening. Remove all the events and just do the work where needed. Additionally rethink the sending of the pbr map to zebra and only send one notification at a time instead of having the sending function attempt to figure out what to do. Clean up some of the no form of commands to make them work properly. Signed-off-by: Donald Sharp Signed-off-by: Quentin Young Signed-off-by: Don Slice --- pbrd/pbr_event.c | 220 ---------------------------------- pbrd/pbr_event.h | 137 --------------------- pbrd/pbr_main.c | 3 - pbrd/pbr_map.c | 305 +++++++++++++++++++---------------------------- pbrd/pbr_map.h | 17 +-- pbrd/pbr_nht.c | 289 +++++++++++++++++++++++++++++++------------- pbrd/pbr_nht.h | 10 +- pbrd/pbr_vty.c | 65 ++++------ pbrd/pbr_zebra.c | 68 ++--------- pbrd/pbr_zebra.h | 5 +- pbrd/subdir.am | 2 - 11 files changed, 380 insertions(+), 741 deletions(-) delete mode 100644 pbrd/pbr_event.c delete mode 100644 pbrd/pbr_event.h diff --git a/pbrd/pbr_event.c b/pbrd/pbr_event.c deleted file mode 100644 index 4563d7d91c..0000000000 --- a/pbrd/pbr_event.c +++ /dev/null @@ -1,220 +0,0 @@ -/* - * 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 deleted file mode 100644 index 2d895d9694..0000000000 --- a/pbrd/pbr_event.h +++ /dev/null @@ -1,137 +0,0 @@ -/* - * 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 index 83cac6daa2..638e284a1a 100644 --- a/pbrd/pbr_main.c +++ b/pbrd/pbr_main.c @@ -46,7 +46,6 @@ #include "pbr_nht.h" #include "pbr_map.h" #include "pbr_zebra.h" -#include "pbr_event.h" #include "pbr_vty.h" #include "pbr_debug.h" @@ -82,7 +81,6 @@ static void sigint(void) { zlog_notice("Terminating on signal"); - pbr_event_stop(); exit(0); } @@ -154,7 +152,6 @@ int main(int argc, char **argv, char **envp) pbr_nhgroup_del_nexthop_cb, pbr_nhgroup_delete_cb); - pbr_event_init(); pbr_nht_init(); pbr_map_init(); pbr_zebra_init(); diff --git a/pbrd/pbr_map.c b/pbrd/pbr_map.c index 3b058675d7..8e4d52cbcf 100644 --- a/pbrd/pbr_map.c +++ b/pbrd/pbr_map.c @@ -32,7 +32,6 @@ #include "pbr_nht.h" #include "pbr_map.h" -#include "pbr_event.h" #include "pbr_zebra.h" #include "pbr_memory.h" #include "pbr_debug.h" @@ -84,9 +83,20 @@ static int pbr_map_interface_compare(const struct pbr_map_interface *pmi1, return strcmp(pmi1->ifp->name, pmi2->ifp->name); } -static void pbr_map_interface_list_delete(const struct pbr_map_interface *pmi) +static void pbr_map_interface_list_delete(struct pbr_map_interface *pmi) { - pbr_map_policy_delete(pmi->ifp->name); + struct pbr_map_interface *pmi_int; + struct listnode *node, *nnode; + struct pbr_map *pbrm; + + RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) { + for (ALL_LIST_ELEMENTS(pbrm->incoming, node, nnode, pmi_int)) { + if (pmi == pmi_int) { + pbr_map_policy_delete(pbrm, pmi); + return; + } + } + } } static const char *pbr_map_reason_str[] = { @@ -117,26 +127,20 @@ 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); - } + if (pmi) + pbr_map_policy_delete(pbrm, pmi); } 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) @@ -148,8 +152,9 @@ void pbr_map_add_interface(struct pbr_map *pbrm, struct interface *ifp_add) pmi->pbrm = pbrm; listnode_add_sort(pbrm->incoming, pmi); - pbre = pbr_event_new(PBR_POLICY_CHANGED, pbrm->name); - pbr_event_enqueue(pbre); + pbr_map_check_valid(pbrm->name); + if (pbrm->valid && !pbrm->installed) + pbr_map_install(pbrm); } void pbr_map_write_interfaces(struct vty *vty, struct interface *ifp) @@ -169,35 +174,21 @@ struct pbr_map *pbrm_find(const char *name) return RB_FIND(pbr_map_entry_head, &pbr_maps, &pbrm); } -extern void pbr_map_delete(const char *name, uint32_t seqno) +extern void pbr_map_delete(struct pbr_map_sequence *pbrms) { struct pbr_map *pbrm; - struct pbr_map_sequence *pbrms; - struct listnode *node, *nnode; - bool uninstall = false; + struct listnode *inode; + struct pbr_map_interface *pmi; - pbrm = pbrm_find(name); + pbrm = pbrms->parent; - for (ALL_LIST_ELEMENTS(pbrm->seqnumbers, node, nnode, pbrms)) { - if (pbrms->reason & PBR_MAP_DEL_SEQUENCE_NUMBER) { - uninstall = true; - break; - } - } + for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi)) + pbr_send_pbr_map(pbrms, pmi, false); - if (uninstall) - pbr_send_pbr_map(pbrm, 0); + if (pbrms->nhg) + pbr_nht_delete_individual_nexthop(pbrms); - 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); - } + listnode_delete(pbrm->seqnumbers, pbrms); if (pbrm->seqnumbers->count == 0) { RB_REMOVE(pbr_map_entry_head, &pbr_maps, pbrm); @@ -205,6 +196,24 @@ extern void pbr_map_delete(const char *name, uint32_t seqno) } } +void pbr_map_delete_nexthop_group(struct pbr_map_sequence *pbrms) +{ + struct pbr_map *pbrm = pbrms->parent; + struct listnode *node; + struct pbr_map_interface *pmi; + + if (pbrm->valid && pbrms->nhs_installed && pbrm->incoming->count) { + for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, node, pmi)) + pbr_send_pbr_map(pbrms, pmi, false); + } + + pbrm->valid = false; + pbrms->nhs_installed = false; + pbrms->installed = false; + pbrms->reason |= PBR_MAP_INVALID_NO_NEXTHOPS; + pbrms->nhgrp_name = NULL; +} + struct pbr_map_sequence *pbrms_lookup_unique(uint32_t unique, ifindex_t ifindex) { @@ -232,12 +241,28 @@ struct pbr_map_sequence *pbrms_lookup_unique(uint32_t unique, return NULL; } +static void pbr_map_add_interfaces(struct pbr_map *pbrm) +{ + struct interface *ifp; + struct pbr_interface *pbr_ifp; + struct vrf *vrf; + + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { + FOR_ALL_INTERFACES (vrf, ifp) { + if (ifp->info) { + pbr_ifp = ifp->info; + if (strcmp(pbrm->name, pbr_ifp->mapname) == 0) + pbr_map_add_interface(pbrm, ifp); + } + } + } +} + 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) { @@ -258,9 +283,8 @@ struct pbr_map_sequence *pbrms_get(const char *name, uint32_t seqno) RB_INSERT(pbr_map_entry_head, &pbr_maps, pbrm); - pbre = pbr_event_new(PBR_MAP_ADD, name); - } else - pbre = NULL; + pbr_map_add_interfaces(pbrm); + } for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) { if (pbrms->seqno == seqno) @@ -284,9 +308,6 @@ struct pbr_map_sequence *pbrms_get(const char *name, uint32_t seqno) pbrm->installed = false; } - if (pbre) - pbr_event_enqueue(pbre); - return pbrms; } @@ -375,7 +396,6 @@ bool pbr_map_check_valid(const char *name) 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; @@ -390,11 +410,7 @@ void pbr_map_schedule_policy_from_nhg(const char *nh_group) && (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); + pbr_map_check(pbrms); } if (pbrms->nhg @@ -402,11 +418,7 @@ void pbr_map_schedule_policy_from_nhg(const char *nh_group) == 0)) { pbrms->nhs_installed = true; - pbre = pbr_event_new(PBR_MAP_MODIFY, - pbrm->name); - pbre->seqno = pbrms->seqno; - - pbr_event_enqueue(pbre); + pbr_map_check(pbrms); } } } @@ -416,49 +428,41 @@ void pbr_map_policy_install(const char *name) { struct pbr_map_sequence *pbrms; struct pbr_map *pbrm; - struct listnode *node; - bool install; + struct listnode *node, *inode; + struct pbr_map_interface *pmi; 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); + if (pbrm->valid && pbrms->nhs_installed && pbrm->incoming->count) { + DEBUGD(&pbr_dbg_map, "\tInstalling %s %u", + pbrm->name, pbrms->seqno); + for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi)) + pbr_send_pbr_map(pbrms, pmi, true); + } } } -void pbr_map_policy_delete(const char *ifname) +void pbr_map_policy_delete(struct pbr_map *pbrm, struct pbr_map_interface *pmi) { - struct listnode *node, *nnode; - struct pbr_map_interface *pmi; - struct pbr_map *pbrm; + struct listnode *node; + struct pbr_map_sequence *pbrms; - 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 (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) + pbr_send_pbr_map(pbrms, pmi, false); + + listnode_delete(pbrm->incoming, pmi); + pmi->pbrm = NULL; + XFREE(MTYPE_PBR_MAP_INTERFACE, pmi); } /* @@ -490,135 +494,66 @@ void pbr_map_check_nh_group_change(const char *nh_group) 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); - } + if (original != pbrm->valid) + pbr_map_install(pbrm); break; } } } } -void pbr_map_check(const char *name, uint32_t seqno) +void pbr_map_check(struct pbr_map_sequence *pbrms) { - struct pbr_map_sequence *pbrms; - struct listnode *node; struct pbr_map *pbrm; + struct listnode *inode; + struct pbr_map_interface *pmi; + bool install; - 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 = pbrms->parent; + DEBUGD(&pbr_dbg_map, "%s: for %s(%u)", __PRETTY_FUNCTION__, + pbrm->name, pbrms->seqno); + if (pbr_map_check_valid(pbrm->name)) + DEBUGD(&pbr_dbg_map, "We are totally valid %s\n", + pbrm->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__, pbrm->name, pbrms->seqno, pbrms->reason); + if (pbrms->reason == PBR_MAP_VALID_SEQUENCE_NUMBER) { + install = true; 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; - } + __PRETTY_FUNCTION__, pbrm->name, pbrms->seqno, + pbrms->reason); + DEBUGD(&pbr_dbg_map, + "\tSending PBR_MAP_POLICY_INSTALL event"); + } else { + install = false; + DEBUGD(&pbr_dbg_map, + "%s: Removing %s(%u) reason: %" PRIu64, + __PRETTY_FUNCTION__, pbrm->name, + pbrms->seqno, pbrms->reason); } + + for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi)) + pbr_send_pbr_map(pbrms, pmi, install); } -void pbr_map_install(const char *name) +void pbr_map_install(struct pbr_map *pbrm) { - 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; - } + struct listnode *node, *inode; + struct pbr_map_sequence *pbrms; + struct pbr_map_interface *pmi; if (!pbrm->incoming->count) return; - pbr_send_pbr_map(pbrm, true); + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) + for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi)) + pbr_send_pbr_map(pbrms, pmi, true); + pbrm->installed = true; } -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); - } - } - } -} - -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); - } -} - void pbr_map_init(void) { RB_INIT(pbr_map_entry_head, &pbr_maps); diff --git a/pbrd/pbr_map.h b/pbrd/pbr_map.h index fca59951b5..12d49d98c0 100644 --- a/pbrd/pbr_map.h +++ b/pbrd/pbr_map.h @@ -104,7 +104,11 @@ struct pbr_map_sequence { */ bool nhs_installed; + /* + * Are we installed + */ bool installed; + /* * A reason of 0 means we think the pbr_map_sequence is good to go * We can accumuluate multiple failure states @@ -115,7 +119,6 @@ struct pbr_map_sequence { #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 @@ -130,7 +133,8 @@ 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_delete(struct pbr_map_sequence *pbrms); +extern void pbr_map_delete_nexthop_group(struct pbr_map_sequence *pbrms); 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); @@ -139,16 +143,15 @@ 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(struct pbr_map_sequence *pbrms); 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_install(struct pbr_map *pbrm); extern void pbr_map_policy_install(const char *name); -extern void pbr_map_policy_delete(const char *ifname); +extern void pbr_map_policy_delete(struct pbr_map *pbrm, + struct pbr_map_interface *pmi); #endif diff --git a/pbrd/pbr_nht.c b/pbrd/pbr_nht.c index 63d509bf6b..7586790c85 100644 --- a/pbrd/pbr_nht.c +++ b/pbrd/pbr_nht.c @@ -26,10 +26,10 @@ #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" @@ -37,6 +37,7 @@ 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; @@ -44,18 +45,55 @@ static uint32_t pbr_nhg_low_rule; static uint32_t pbr_nhg_high_rule; static bool nhg_tableid[65535]; +static void pbr_nht_install_nexthop_group(struct pbr_nexthop_group_cache *pnhgc, + struct nexthop_group nhg); + +/* + * 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)); - memcpy(&new->nexthop, &pnhc->nexthop, sizeof(struct nexthop)); + 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); + pbr_send_rnh(new->nexthop, true); new->valid = false; return new; @@ -63,20 +101,36 @@ static void *pbr_nh_alloc(void *p) static void pbr_nh_delete(struct pbr_nexthop_cache **pnhc) { - pbr_send_rnh(&(*pnhc)->nexthop, false); + 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 void pbr_nh_delete_iterate(struct hash_backet *b, void *p) +{ + pbr_nh_delete((struct pbr_nexthop_cache **)&b->data); +} + static uint32_t pbr_nh_hash_key(void *arg) { uint32_t key; struct pbr_nexthop_cache *pbrnc = (struct pbr_nexthop_cache *)arg; - key = jhash_1word(pbrnc->nexthop.vrf_id, 0x45afe398); - key = jhash_1word(pbrnc->nexthop.ifindex, key); - key = jhash_1word(pbrnc->nexthop.type, key); - key = jhash(&pbrnc->nexthop.gate, sizeof(union g_addr), key); + key = nexthop_hash(pbrnc->nexthop); return key; } @@ -88,28 +142,28 @@ static int pbr_nh_hash_equal(const void *arg1, const void *arg2) const struct pbr_nexthop_cache *pbrnc2 = (const struct pbr_nexthop_cache *)arg2; - if (pbrnc1->nexthop.vrf_id != pbrnc2->nexthop.vrf_id) + if (pbrnc1->nexthop->vrf_id != pbrnc2->nexthop->vrf_id) return 0; - if (pbrnc1->nexthop.ifindex != pbrnc2->nexthop.ifindex) + if (pbrnc1->nexthop->ifindex != pbrnc2->nexthop->ifindex) return 0; - if (pbrnc1->nexthop.type != pbrnc2->nexthop.type) + if (pbrnc1->nexthop->type != pbrnc2->nexthop->type) return 0; - switch (pbrnc1->nexthop.type) { + 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; + 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); + return !memcmp(&pbrnc1->nexthop->gate.ipv6, + &pbrnc2->nexthop->gate.ipv6, 16); case NEXTHOP_TYPE_BLACKHOLE: - return pbrnc1->nexthop.bh_type == pbrnc2->nexthop.bh_type; + return pbrnc1->nexthop->bh_type == pbrnc2->nexthop->bh_type; } /* @@ -118,50 +172,118 @@ static int pbr_nh_hash_equal(const void *arg1, const void *arg2) return 0; } +static void pbr_nhgc_delete(struct pbr_nexthop_group_cache *p) +{ + hash_iterate(p->nhh, pbr_nh_delete_iterate, NULL); + hash_free(p->nhh); + XFREE(MTYPE_PBR_NHG, p); +} + +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_nhgroup_add_cb(const char *name) { - struct pbr_event *pbre; + struct pbr_nexthop_group_cache *pnhgc; + struct nexthop_group_cmd *nhgc; - pbre = pbr_event_new(PBR_NHG_NEW, name); + nhgc = nhgc_find(name); + pnhgc = pbr_nht_add_group(name); - pbr_event_enqueue(pbre); - DEBUGD(&pbr_dbg_nht, "%s: Received ADD cb for %s", __PRETTY_FUNCTION__, + DEBUGD(&pbr_dbg_nht, "%s: Added nexthop-group %s", __PRETTY_FUNCTION__, name); + + pbr_nht_install_nexthop_group(pnhgc, nhgc->nhg); + pbr_map_check_nh_group_change(name); } -void pbr_nhgroup_add_nexthop_cb(const struct nexthop_group_cmd *nhg, +void pbr_nhgroup_add_nexthop_cb(const struct nexthop_group_cmd *nhgc, const struct nexthop *nhop) { - struct pbr_event *pbre; + char debugstr[256]; + struct pbr_nexthop_group_cache pnhgc_find = {}; + struct pbr_nexthop_group_cache *pnhgc; + struct pbr_nexthop_cache pnhc_find = {}; + struct pbr_nexthop_cache *pnhc; - pbre = pbr_event_new(PBR_NHG_ADD_NEXTHOP, nhg->name); + /* find pnhgc by name */ + strlcpy(pnhgc_find.name, nhgc->name, sizeof(pnhgc_find.name)); + pnhgc = hash_get(pbr_nhg_hash, &pnhgc_find, pbr_nhgc_alloc); - pbr_event_enqueue(pbre); - DEBUGD(&pbr_dbg_nht, "%s: Received NEXTHOP_ADD cb for %s", - __PRETTY_FUNCTION__, nhg->name); + /* create & insert new pnhc into pnhgc->nhh */ + pnhc_find.nexthop = (struct nexthop *)nhop; + pnhc = hash_get(pnhgc->nhh, &pnhc_find, pbr_nh_alloc); + pnhc_find.nexthop = NULL; + + /* set parent pnhgc */ + pnhc->parent = pnhgc; + + if (DEBUG_MODE_CHECK(&pbr_dbg_nht, DEBUG_MODE_ALL)) { + nexthop2str(nhop, debugstr, sizeof(debugstr)); + DEBUGD(&pbr_dbg_nht, "%s: Added %s to nexthop-group %s", + __PRETTY_FUNCTION__, debugstr, nhgc->name); + } + + pbr_nht_install_nexthop_group(pnhgc, nhgc->nhg); + pbr_map_check_nh_group_change(nhgc->name); } -void pbr_nhgroup_del_nexthop_cb(const struct nexthop_group_cmd *nhg, +void pbr_nhgroup_del_nexthop_cb(const struct nexthop_group_cmd *nhgc, const struct nexthop *nhop) { - struct pbr_event *pbre; + char debugstr[256]; + struct pbr_nexthop_group_cache pnhgc_find = {}; + struct pbr_nexthop_group_cache *pnhgc; + struct pbr_nexthop_cache pnhc_find = {}; + struct pbr_nexthop_cache *pnhc; - pbre = pbr_event_new(PBR_NHG_DEL_NEXTHOP, nhg->name); + /* find pnhgc by name */ + strlcpy(pnhgc_find.name, nhgc->name, sizeof(pnhgc_find.name)); + pnhgc = hash_get(pbr_nhg_hash, &pnhgc_find, pbr_nhgc_alloc); - pbr_event_enqueue(pbre); - DEBUGD(&pbr_dbg_nht, "%s: Received NEXTHOP_DEL cb for %s", - __PRETTY_FUNCTION__, nhg->name); + /* delete pnhc from pnhgc->nhh */ + pnhc_find.nexthop = (struct nexthop *)nhop; + pnhc = hash_release(pnhgc->nhh, &pnhc_find); + + /* delete pnhc */ + pbr_nh_delete(&pnhc); + + if (DEBUG_MODE_CHECK(&pbr_dbg_nht, DEBUG_MODE_ALL)) { + nexthop2str(nhop, debugstr, sizeof(debugstr)); + DEBUGD(&pbr_dbg_nht, "%s: Removed %s from nexthop-group %s", + __PRETTY_FUNCTION__, debugstr, nhgc->name); + } + + pbr_nht_install_nexthop_group(pnhgc, nhgc->nhg); + pbr_map_check_nh_group_change(nhgc->name); } void pbr_nhgroup_delete_cb(const char *name) { - struct pbr_event *pbre; + /* delete group from all pbrms's */ + pbr_nht_delete_group(name); - pbre = pbr_event_new(PBR_NHG_DELETE, name); - - pbr_event_enqueue(pbre); - DEBUGD(&pbr_dbg_nht, "%s: Received DELETE cb for %s", + DEBUGD(&pbr_dbg_nht, "%s: Removed nexthop-group %s", __PRETTY_FUNCTION__, name); + + pbr_map_check_nh_group_change(name); } #if 0 @@ -307,7 +429,7 @@ void pbr_nht_change_group(const char *name) struct pbr_nexthop_cache lookup; struct pbr_nexthop_cache *pnhc; - memcpy(&lookup.nexthop, nhop, sizeof(*nhop)); + lookup.nexthop = nhop; pnhc = hash_lookup(pnhgc->nhh, &lookup); if (!pnhc) { pnhc = hash_get(pnhgc->nhh, &lookup, pbr_nh_alloc); @@ -324,35 +446,13 @@ char *pbr_nht_nexthop_make_name(char *name, size_t l, 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) +void pbr_nht_add_individual_nexthop(struct pbr_map_sequence *pbrms) { 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, PBR_MAP_NAMELEN, pbrms->seqno, find.name); @@ -361,29 +461,39 @@ void pbr_nht_add_individual_nexthop(const char *name, uint32_t seqno) pnhgc = hash_get(pbr_nhg_hash, &find, pbr_nhgc_alloc); - memcpy(&lookup.nexthop, pbrms->nhg->nexthop, sizeof(struct nexthop)); + 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) +void pbr_nht_delete_individual_nexthop(struct pbr_map_sequence *pbrms) { 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 pbr_map *pbrm = pbrms->parent; + struct listnode *node; + struct pbr_map_interface *pmi; struct nexthop *nh; - pbrms = pbrms_get(name, seqno); + if (pbrm->valid && pbrms->nhs_installed && pbrm->incoming->count) { + for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, node, pmi)) + pbr_send_pbr_map(pbrms, pmi, false); + } + + pbrm->valid = false; + pbrms->nhs_installed = false; + pbrms->installed = false; + pbrms->reason |= PBR_MAP_INVALID_NO_NEXTHOPS; memset(&find, 0, sizeof(find)); strcpy(&find.name[0], pbrms->internal_nhg_name); pnhgc = hash_lookup(pbr_nhg_hash, &find); nh = pbrms->nhg->nexthop; - memcpy(&lup.nexthop, nh, sizeof(struct nexthop)); + lup.nexthop = nh; pnhc = hash_lookup(pnhgc->nhh, &lup); pnhc->parent = NULL; hash_release(pnhgc->nhh, pnhc); @@ -398,7 +508,7 @@ void pbr_nht_delete_individual_nexthop(const char *name, uint32_t seqno) XFREE(MTYPE_TMP, pbrms->internal_nhg_name); } -void pbr_nht_add_group(const char *name) +struct pbr_nexthop_group_cache *pbr_nht_add_group(const char *name) { struct nexthop *nhop; struct nexthop_group_cmd *nhgc; @@ -410,7 +520,7 @@ void pbr_nht_add_group(const char *name) if (!nhgc) { zlog_warn("%s: Could not find group %s to add", __PRETTY_FUNCTION__, name); - return; + return NULL; } strcpy(lookup.name, name); @@ -422,13 +532,15 @@ void pbr_nht_add_group(const char *name) struct pbr_nexthop_cache lookup; struct pbr_nexthop_cache *pnhc; - memcpy(&lookup.nexthop, nhop, sizeof(*nhop)); + lookup.nexthop = nhop; pnhc = hash_lookup(pnhgc->nhh, &lookup); if (!pnhc) { pnhc = hash_get(pnhgc->nhh, &lookup, pbr_nh_alloc); pnhc->parent = pnhgc; } } + + return pnhgc; } void pbr_nht_delete_group(const char *name) @@ -436,16 +548,25 @@ void pbr_nht_delete_group(const char *name) struct pbr_map_sequence *pbrms; struct listnode *snode; struct pbr_map *pbrm; + struct pbr_nexthop_group_cache pnhgc_find; + struct pbr_nexthop_group_cache *pnhgc; 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) { + && strmatch(pbrms->nhgrp_name, name)) { pbrms->reason |= PBR_MAP_INVALID_NO_NEXTHOPS; + nexthop_group_delete(&pbrms->nhg); + pbrms->nhg = NULL; + pbrms->internal_nhg_name = NULL; pbrm->valid = false; } } } + + strlcpy(pnhgc_find.name, name, sizeof(pnhgc_find.name)); + pnhgc = hash_release(pbr_nhg_hash, &pnhgc_find); + pbr_nhgc_delete(pnhgc); } bool pbr_nht_nexthop_valid(struct nexthop_group *nhg) @@ -491,13 +612,14 @@ static void pbr_nht_individual_nexthop_update_lookup(struct hash_backet *b, switch (pnhi->nhr->prefix.family) { case AF_INET: - if (pnhc->nexthop.gate.ipv4.s_addr + 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) + if (memcmp(&pnhc->nexthop->gate.ipv6, + &pnhi->nhr->prefix.u.prefix6, 16) + == 0) pnhc->valid = !!pnhi->nhr->nexthop_num; break; } @@ -506,14 +628,6 @@ static void pbr_nht_individual_nexthop_update_lookup(struct hash_backet *b, 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; } @@ -522,6 +636,9 @@ 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; + bool old_valid; + + old_valid = pnhgc->valid; pnhi.nhr = (struct zapi_route *)data; pnhi.valid = 0; @@ -532,6 +649,9 @@ static void pbr_nht_nexthop_update_lookup(struct hash_backet *b, void *data) * If any of the specified nexthops are valid we are valid */ pnhgc->valid = !!pnhi.valid; + + if (old_valid != pnhgc->valid) + pbr_map_check_nh_group_change(pnhgc->name); } void pbr_nht_nexthop_update(struct zapi_route *nhr) @@ -652,7 +772,7 @@ static void pbr_nht_show_nhg_nexthops(struct hash_backet *b, void *data) struct vty *vty = data; vty_out(vty, "\tValid: %d", pnhc->valid); - nexthop_group_write_nexthop(vty, &pnhc->nexthop); + nexthop_group_write_nexthop(vty, pnhc->nexthop); } struct pbr_nht_show { @@ -690,6 +810,9 @@ 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; diff --git a/pbrd/pbr_nht.h b/pbrd/pbr_nht.h index 2508f49e4b..2cfb076f00 100644 --- a/pbrd/pbr_nht.h +++ b/pbrd/pbr_nht.h @@ -23,6 +23,8 @@ #include #include +#include "pbr_map.h" + struct pbr_nexthop_group_cache { char name[PBR_MAP_NAMELEN]; @@ -41,7 +43,7 @@ struct pbr_nexthop_group_cache { struct pbr_nexthop_cache { struct pbr_nexthop_group_cache *parent; - struct nexthop nexthop; + struct nexthop *nexthop; bool valid; }; @@ -76,12 +78,12 @@ 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 struct pbr_nexthop_group_cache *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); +extern void pbr_nht_add_individual_nexthop(struct pbr_map_sequence *pbrms); +extern void pbr_nht_delete_individual_nexthop(struct pbr_map_sequence *pbrms); /* * Given the tableid of the installed default * route, find the nexthop-group associated with diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c index 6787edbe07..042d940646 100644 --- a/pbrd/pbr_vty.c +++ b/pbrd/pbr_vty.c @@ -33,7 +33,6 @@ #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" @@ -65,7 +64,6 @@ DEFUN_NOSH(no_pbr_map, no_pbr_map_cmd, "no pbr-map WORD [seq (1-65535)]", 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; @@ -77,20 +75,12 @@ DEFUN_NOSH(no_pbr_map, no_pbr_map_cmd, "no pbr-map WORD [seq (1-65535)]", 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; - } - } + for (ALL_LIST_ELEMENTS(pbrm->seqnumbers, node, next_node, pbrms)) { + if (seqno && pbrms->seqno != seqno) + continue; - pbre = pbr_event_new(PBR_MAP_DELETE, pbrm_name); - pbre->seqno = seqno; - pbr_event_enqueue(pbre); + pbr_map_delete(pbrms); + } return CMD_SUCCESS; } @@ -104,7 +94,6 @@ DEFPY(pbr_map_match_src, pbr_map_match_src_cmd, "v6 Prefix\n") { struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); - struct pbr_event *pbre; if (!no) { if (!pbrms->src) @@ -115,9 +104,7 @@ DEFPY(pbr_map_match_src, pbr_map_match_src_cmd, pbrms->src = 0; } - pbre = pbr_event_new(PBR_MAP_MODIFY, pbrms->parent->name); - pbre->seqno = pbrms->seqno; - pbr_event_enqueue(pbre); + pbr_map_check(pbrms); return CMD_SUCCESS; } @@ -131,7 +118,6 @@ DEFPY(pbr_map_match_dst, pbr_map_match_dst_cmd, "v6 Prefix\n") { struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); - struct pbr_event *pbre; if (!no) { if (!pbrms->dst) @@ -139,12 +125,10 @@ DEFPY(pbr_map_match_dst, pbr_map_match_dst_cmd, prefix_copy(pbrms->dst, prefix); } else { prefix_free(pbrms->dst); - pbrms->dst = 0; + pbrms->dst = NULL; } - pbre = pbr_event_new(PBR_MAP_MODIFY, pbrms->parent->name); - pbre->seqno = pbrms->seqno; - pbr_event_enqueue(pbre); + pbr_map_check(pbrms); return CMD_SUCCESS; } @@ -158,7 +142,6 @@ DEFPY(pbr_map_nexthop_group, pbr_map_nexthop_group_cmd, { 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) { @@ -169,8 +152,7 @@ DEFPY(pbr_map_nexthop_group, pbr_map_nexthop_group_cmd, if (no) { if (pbrms->nhgrp_name && strcmp(name, pbrms->nhgrp_name) == 0) - pbre = pbr_event_new(PBR_MAP_NHG_DELETE, - pbrms->parent->name); + pbr_map_delete_nexthop_group(pbrms); else { vty_out(vty, "Nexthop Group specified: %s does not exist to remove", @@ -188,12 +170,9 @@ DEFPY(pbr_map_nexthop_group, pbr_map_nexthop_group_cmd, return CMD_SUCCESS; } pbrms->nhgrp_name = XSTRDUP(MTYPE_TMP, name); - pbre = pbr_event_new(PBR_MAP_NHG_ADD, pbrms->parent->name); + pbr_map_check(pbrms); } - pbre->seqno = pbrms->seqno; - pbr_event_enqueue(pbre); - return CMD_SUCCESS; } @@ -212,7 +191,6 @@ DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd, struct vrf *vrf; struct nexthop nhop; struct nexthop *nh; - struct pbr_event *pbre; if (pbrms->nhgrp_name) { vty_out(vty, @@ -282,14 +260,8 @@ DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd, } 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); - } + if (nh) + pbr_nht_delete_individual_nexthop(pbrms); } else if (!nh) { if (pbrms->nhg->nexthop) { @@ -304,9 +276,8 @@ DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd, 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); + pbr_nht_add_individual_nexthop(pbrms); + pbr_map_check(pbrms); } return CMD_SUCCESS; @@ -363,6 +334,14 @@ DEFPY (pbr_policy, pbrm = pbrm_find(mapname); + if (!pbr_ifp) { + /* + * Some one could have fat fingered the interface + * name + */ + pbr_ifp = pbr_if_new(ifp); + } + if (no) { if (strcmp(pbr_ifp->mapname, mapname) == 0) { strcpy(pbr_ifp->mapname, ""); diff --git a/pbrd/pbr_zebra.c b/pbrd/pbr_zebra.c index b066965249..971fe65aaf 100644 --- a/pbrd/pbr_zebra.c +++ b/pbrd/pbr_zebra.c @@ -56,7 +56,7 @@ static struct interface *zebra_interface_if_lookup(struct stream *s) return if_lookup_by_name(ifname_tmp, VRF_DEFAULT); } -static struct pbr_interface *pbr_if_new(struct interface *ifp) +struct pbr_interface *pbr_if_new(struct interface *ifp) { struct pbr_interface *pbr_ifp; @@ -497,14 +497,11 @@ static void pbr_encode_pbr_map_sequence(struct stream *s, stream_putl(s, ifp->ifindex); } -void pbr_send_pbr_map(struct pbr_map *pbrm, bool install) +void pbr_send_pbr_map(struct pbr_map_sequence *pbrms, + struct pbr_map_interface *pmi, bool install) { - struct listnode *inode, *snode; - struct pbr_map_sequence *pbrms; - struct pbr_map_interface *pmi; + struct pbr_map *pbrm = pbrms->parent; struct stream *s; - uint32_t total; - ssize_t tspot; DEBUGD(&pbr_dbg_zebra, "%s: for %s %d", __PRETTY_FUNCTION__, pbrm->name, install); @@ -516,59 +513,18 @@ void pbr_send_pbr_map(struct pbr_map *pbrm, bool install) 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)) { + /* + * We are sending one item at a time at the moment + */ + stream_putl(s, 1); - DEBUGD(&pbr_dbg_zebra, "%s: \t%s %s %d %s %u", - __PRETTY_FUNCTION__, install ? "Installing" : "Deleting", - pbrm->name, install, pmi->ifp->name, pmi->delete); + 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; - } + pbr_encode_pbr_map_sequence(s, pbrms, pmi->ifp); - for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, snode, pbrms)) { - - DEBUGD(&pbr_dbg_zebra, "%s: \tSeqno: %u %" PRIu64 " 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 index 6c1eca9d19..4cbefe2636 100644 --- a/pbrd/pbr_zebra.h +++ b/pbrd/pbr_zebra.h @@ -35,5 +35,8 @@ extern void route_delete(struct pbr_nexthop_group_cache *pnhgc, extern void pbr_send_rnh(struct nexthop *nhop, bool reg); -extern void pbr_send_pbr_map(struct pbr_map *pbrm, bool install); +extern void pbr_send_pbr_map(struct pbr_map_sequence *pbrms, + struct pbr_map_interface *pmi, bool install); + +extern struct pbr_interface *pbr_if_new(struct interface *ifp); #endif diff --git a/pbrd/subdir.am b/pbrd/subdir.am index 361e6c1fde..42ab393218 100644 --- a/pbrd/subdir.am +++ b/pbrd/subdir.am @@ -14,12 +14,10 @@ pbrd_libpbr_a_SOURCES = \ 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 \ From e042a421d143b0e8fb360fa22c00718aea9f49a8 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Tue, 20 Mar 2018 14:05:44 -0400 Subject: [PATCH 08/21] pbrd: Only allow one nexthop group or nexthop at a time When we are entering 'set nexthop' and 'set nexthop-group' ensure that the cli only allows one of these to happen at a time. Ticket: CM-20125 Signed-off-by: Donald Sharp --- pbrd/pbr_vty.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c index 042d940646..77e4e3b6fa 100644 --- a/pbrd/pbr_vty.c +++ b/pbrd/pbr_vty.c @@ -143,6 +143,12 @@ DEFPY(pbr_map_nexthop_group, pbr_map_nexthop_group_cmd, struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); struct nexthop_group_cmd *nhgc; + if (pbrms->nhg) { + vty_out(vty, + "A `set nexthop XX` command already exists, please remove that first\n"); + return CMD_WARNING; + } + nhgc = nhgc_find(name); if (!nhgc) { vty_out(vty, "Specified nexthop-group %s does not exist\n", From f1c3fe1b44bb43ac8491f09f2b4f3af265a1829d Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Tue, 20 Mar 2018 16:19:26 -0400 Subject: [PATCH 09/21] pbrd: fix null pointer deref when showing ifaces If there are no PBR interfaces configured and we do a 'show run', pbrd crashes with a NPD when it tries to dereference ifp->info. Signed-off-by: Quentin Young --- pbrd/pbr_map.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pbrd/pbr_map.c b/pbrd/pbr_map.c index 8e4d52cbcf..f3ecc5882a 100644 --- a/pbrd/pbr_map.c +++ b/pbrd/pbr_map.c @@ -161,7 +161,8 @@ void pbr_map_write_interfaces(struct vty *vty, struct interface *ifp) { struct pbr_interface *pbr_ifp = ifp->info; - if (!(strcmp(pbr_ifp->mapname, "") == 0)) + if (pbr_ifp + && strncmp(pbr_ifp->mapname, "", sizeof(pbr_ifp->mapname)) != 0) vty_out(vty, " pbr-policy %s\n", pbr_ifp->mapname); } From 57cdafc411806760b230499eab82df560566d893 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Tue, 20 Mar 2018 16:43:54 -0400 Subject: [PATCH 10/21] lib, pbrd: rm extra space when displaying nexthop Signed-off-by: Quentin Young --- lib/nexthop_group.c | 2 +- pbrd/pbr_nht.c | 2 +- pbrd/pbr_vty.c | 7 +++---- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/nexthop_group.c b/lib/nexthop_group.c index e486247244..d7dbfd8a49 100644 --- a/lib/nexthop_group.c +++ b/lib/nexthop_group.c @@ -320,7 +320,7 @@ void nexthop_group_write_nexthop(struct vty *vty, struct nexthop *nh) char buf[100]; struct vrf *vrf; - vty_out(vty, " nexthop "); + vty_out(vty, "nexthop "); switch (nh->type) { case NEXTHOP_TYPE_IFINDEX: diff --git a/pbrd/pbr_nht.c b/pbrd/pbr_nht.c index 7586790c85..c8a58e46f5 100644 --- a/pbrd/pbr_nht.c +++ b/pbrd/pbr_nht.c @@ -771,7 +771,7 @@ 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); + vty_out(vty, "\tValid: %d ", pnhc->valid); nexthop_group_write_nexthop(vty, pnhc->nexthop); } diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c index 77e4e3b6fa..0e66c39706 100644 --- a/pbrd/pbr_vty.c +++ b/pbrd/pbr_vty.c @@ -543,8 +543,7 @@ static int pbr_vty_map_config_write_sequence(struct vty *vty, { char buff[PREFIX_STRLEN]; - vty_out(vty, "pbr-map %s seq %u\n", - pbrm->name, pbrms->seqno); + vty_frame(vty, "pbr-map %s seq %u\n", pbrm->name, pbrms->seqno); if (pbrms->src) vty_out(vty, " match src-ip %s\n", @@ -558,11 +557,11 @@ static int pbr_vty_map_config_write_sequence(struct vty *vty, vty_out(vty, " set nexthop-group %s\n", pbrms->nhgrp_name); if (pbrms->nhg) { - vty_out(vty, " set"); + vty_out(vty, " set "); nexthop_group_write_nexthop(vty, pbrms->nhg->nexthop); } - vty_out(vty, "!\n"); + vty_endframe(vty, "!\n"); return 1; } From 811f859f175f8912df47145699e5116a81320a1e Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Wed, 21 Mar 2018 12:07:20 -0400 Subject: [PATCH 11/21] lib: dont break display of nexthop-groups themselves Signed-off-by: Quentin Young --- lib/nexthop_group.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/nexthop_group.c b/lib/nexthop_group.c index d7dbfd8a49..1ec49c2a02 100644 --- a/lib/nexthop_group.c +++ b/lib/nexthop_group.c @@ -361,8 +361,10 @@ static int nexthop_group_write(struct vty *vty) RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) { vty_out(vty, "nexthop-group %s\n", nhgc->name); - for (nh = nhgc->nhg.nexthop; nh; nh = nh->next) + for (nh = nhgc->nhg.nexthop; nh; nh = nh->next) { + vty_out(vty, " "); nexthop_group_write_nexthop(vty, nh); + } vty_out(vty, "!\n"); } From ff9799c31ebff3360706eaaad96c5cd27e3eaef6 Mon Sep 17 00:00:00 2001 From: Don Slice Date: Wed, 21 Mar 2018 21:23:32 +0000 Subject: [PATCH 12/21] pbrd: add ability to delete routes and rules correctly Signed-off-by: Don Slice --- pbrd/pbr_map.c | 14 +++++++--- pbrd/pbr_nht.c | 72 ++++++++++++++++++++++++++++++++------------------ 2 files changed, 56 insertions(+), 30 deletions(-) diff --git a/pbrd/pbr_map.c b/pbrd/pbr_map.c index f3ecc5882a..ea79320a71 100644 --- a/pbrd/pbr_map.c +++ b/pbrd/pbr_map.c @@ -476,10 +476,10 @@ void pbr_map_check_nh_group_change(const char *nh_group) { struct pbr_map_sequence *pbrms; struct pbr_map *pbrm; - struct listnode *node; + struct listnode *node, *inode; + struct pbr_map_interface *pmi; 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; @@ -495,9 +495,15 @@ void pbr_map_check_nh_group_change(const char *nh_group) pbr_map_check_valid_internal(pbrm); - if (original != pbrm->valid) + if (pbrm->valid && (original != pbrm->valid)) pbr_map_install(pbrm); - break; + + if (pbrm->valid == false) + for (ALL_LIST_ELEMENTS_RO( + pbrm->incoming, inode, + pmi)) + pbr_send_pbr_map(pbrms, pmi, + false); } } } diff --git a/pbrd/pbr_nht.c b/pbrd/pbr_nht.c index c8a58e46f5..1ce8c2104d 100644 --- a/pbrd/pbr_nht.c +++ b/pbrd/pbr_nht.c @@ -47,6 +47,10 @@ static bool nhg_tableid[65535]; static void pbr_nht_install_nexthop_group(struct pbr_nexthop_group_cache *pnhgc, struct nexthop_group nhg); +static void +pbr_nht_uninstall_nexthop_group(struct pbr_nexthop_group_cache *pnhgc, + struct nexthop_group nhg, + enum nexthop_types_t nh_afi); /* * Nexthop refcount. @@ -253,6 +257,7 @@ void pbr_nhgroup_del_nexthop_cb(const struct nexthop_group_cmd *nhgc, struct pbr_nexthop_group_cache *pnhgc; struct pbr_nexthop_cache pnhc_find = {}; struct pbr_nexthop_cache *pnhc; + enum nexthop_types_t nh_afi = nhop->type; /* find pnhgc by name */ strlcpy(pnhgc_find.name, nhgc->name, sizeof(pnhgc_find.name)); @@ -271,18 +276,22 @@ void pbr_nhgroup_del_nexthop_cb(const struct nexthop_group_cmd *nhgc, __PRETTY_FUNCTION__, debugstr, nhgc->name); } - pbr_nht_install_nexthop_group(pnhgc, nhgc->nhg); + if (pnhgc->nhh->count) + pbr_nht_install_nexthop_group(pnhgc, nhgc->nhg); + else + pbr_nht_uninstall_nexthop_group(pnhgc, nhgc->nhg, nh_afi); + pbr_map_check_nh_group_change(nhgc->name); } void pbr_nhgroup_delete_cb(const char *name) { - /* delete group from all pbrms's */ - pbr_nht_delete_group(name); - DEBUGD(&pbr_dbg_nht, "%s: Removed nexthop-group %s", __PRETTY_FUNCTION__, name); + /* delete group from all pbrms's */ + pbr_nht_delete_group(name); + pbr_map_check_nh_group_change(name); } @@ -337,7 +346,8 @@ void pbr_nht_route_removed_for_table(uint32_t table_id) * - AFI of last nexthop in the group * - AFI_MAX on error */ -static afi_t pbr_nht_which_afi(struct nexthop_group nhg) +static afi_t pbr_nht_which_afi(struct nexthop_group nhg, + enum nexthop_types_t nh_afi) { struct nexthop *nexthop; afi_t install_afi = AFI_MAX; @@ -345,27 +355,32 @@ static afi_t pbr_nht_which_afi(struct nexthop_group nhg) 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; + if (!nh_afi) { + for (ALL_NEXTHOPS(nhg, nexthop)) { + nh_afi = nexthop->type; break; } } + switch (nh_afi) { + 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", @@ -383,20 +398,23 @@ static void pbr_nht_install_nexthop_group(struct pbr_nexthop_group_cache *pnhgc, struct nexthop_group nhg) { afi_t install_afi; + enum nexthop_types_t nh_afi = 0; - install_afi = pbr_nht_which_afi(nhg); + install_afi = pbr_nht_which_afi(nhg, nh_afi); 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) + struct nexthop_group nhg, + enum nexthop_types_t nh_afi) { afi_t install_afi; - install_afi = pbr_nht_which_afi(nhg); + install_afi = pbr_nht_which_afi(nhg, nh_afi); pnhgc->installed = false; pnhgc->valid = false; @@ -477,6 +495,7 @@ void pbr_nht_delete_individual_nexthop(struct pbr_map_sequence *pbrms) struct listnode *node; struct pbr_map_interface *pmi; struct nexthop *nh; + enum nexthop_types_t nh_afi = 0; if (pbrm->valid && pbrms->nhs_installed && pbrm->incoming->count) { for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, node, pmi)) @@ -493,12 +512,13 @@ void pbr_nht_delete_individual_nexthop(struct pbr_map_sequence *pbrms) pnhgc = hash_lookup(pbr_nhg_hash, &find); nh = pbrms->nhg->nexthop; + nh_afi = nh->type; 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); + pbr_nht_uninstall_nexthop_group(pnhgc, *pbrms->nhg, nh_afi); hash_release(pbr_nhg_hash, pnhgc); From 66fd4b64e5e0a6d6d5f524c48889ced674d7c6e0 Mon Sep 17 00:00:00 2001 From: Don Slice Date: Thu, 22 Mar 2018 13:56:56 +0000 Subject: [PATCH 13/21] pbrd: fix deletion of match or src of valid pbr-map When removing either the match dst or match src of a previously valid pbr map, we would just try to re-install the rule which was rejected. This fix deletes the old rule before we re-apply the new rule. Signed-off-by: Don Slice --- pbrd/pbr_map.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pbrd/pbr_map.c b/pbrd/pbr_map.c index ea79320a71..5962509e82 100644 --- a/pbrd/pbr_map.c +++ b/pbrd/pbr_map.c @@ -527,6 +527,13 @@ void pbr_map_check(struct pbr_map_sequence *pbrms) __PRETTY_FUNCTION__, pbrm->name, pbrms->seqno, pbrms->reason); if (pbrms->reason == PBR_MAP_VALID_SEQUENCE_NUMBER) { + if (pbrms->installed) { + install = false; + for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi)) { + pbr_send_pbr_map(pbrms, pmi, install); + } + install = true; + } install = true; DEBUGD(&pbr_dbg_map, "%s: Installing %s(%u) reason: %" PRIu64, __PRETTY_FUNCTION__, pbrm->name, pbrms->seqno, From b8eb036fb289df03190f0a88dbee2431d21c0d55 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Thu, 22 Mar 2018 13:25:20 -0400 Subject: [PATCH 14/21] pbrd: If we get the same prefix safely ignore If the match src-ip or dst-ip command entered has already been received and it's the same prefix, we are done and do not need to do anything more. Signed-off-by: Donald Sharp --- pbrd/pbr_vty.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c index 0e66c39706..c8c3ed3302 100644 --- a/pbrd/pbr_vty.c +++ b/pbrd/pbr_vty.c @@ -96,6 +96,9 @@ DEFPY(pbr_map_match_src, pbr_map_match_src_cmd, struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); if (!no) { + if (prefix_same(pbrms->src, prefix)) + return CMD_SUCCESS; + if (!pbrms->src) pbrms->src = prefix_new(); prefix_copy(pbrms->src, prefix); @@ -120,6 +123,9 @@ DEFPY(pbr_map_match_dst, pbr_map_match_dst_cmd, struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); if (!no) { + if (prefix_same(pbrms->dst, prefix)) + return CMD_SUCCESS; + if (!pbrms->dst) pbrms->dst = prefix_new(); prefix_copy(pbrms->dst, prefix); From 1c33fb1dbbaacd33fddea93a60eda539b8c53dc4 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Thu, 22 Mar 2018 17:12:13 -0400 Subject: [PATCH 15/21] pbrd: Prevent crash from partially configured interface If an interface is in the process of coming up or partially deconfigured, prevent pbrd from crashing Signed-off-by: Donald Sharp --- pbrd/pbr_vty.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c index c8c3ed3302..02902fd009 100644 --- a/pbrd/pbr_vty.c +++ b/pbrd/pbr_vty.c @@ -494,6 +494,9 @@ DEFPY (show_pbr_interface, FOR_ALL_INTERFACES(vrf, ifp) { struct pbr_map *pbrm; + if (!ifp->info) + continue; + if (name && strcmp(ifp->name, name) != 0) continue; From 8ee9738bbc436725b32c4b43cb4118f5fc3e1103 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Sun, 25 Mar 2018 21:00:19 -0400 Subject: [PATCH 16/21] pbrd: Reduce range so we cannot go over the vrf rule The linux kernel puts the vrf rule at 1000, since pbr rules need to be before this rule, don't allow us to go beyond. Ticket: CM-19946 Signed-off-by: Donald Sharp --- pbrd/pbr_vty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c index 02902fd009..f01286717c 100644 --- a/pbrd/pbr_vty.c +++ b/pbrd/pbr_vty.c @@ -316,7 +316,7 @@ DEFPY (pbr_table_range, DEFPY (pbr_rule_range, pbr_rule_range_cmd, - "[no] pbr rule range (300-1300)$start (400-1400)$end", + "[no] pbr rule range (300-900)$start (400-1000)$end", NO_STR "Policy based routing\n" "Policy based routing rule\n" From 5e44f18ff124df9338ffe0c28611736fa1ecadd7 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Sun, 25 Mar 2018 21:18:56 -0400 Subject: [PATCH 17/21] pbrd: Do not use vrf_frame for pbrms display When displaying a pbr map sequence for a show run do not use the vty_frame construct. We should display the config even if we do not have much to display. Ticket: CM-20196 Signed-off-by: Donald Sharp --- pbrd/pbr_vty.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c index f01286717c..e19bbcb633 100644 --- a/pbrd/pbr_vty.c +++ b/pbrd/pbr_vty.c @@ -552,7 +552,7 @@ static int pbr_vty_map_config_write_sequence(struct vty *vty, { char buff[PREFIX_STRLEN]; - vty_frame(vty, "pbr-map %s seq %u\n", pbrm->name, pbrms->seqno); + vty_out(vty, "pbr-map %s seq %u\n", pbrm->name, pbrms->seqno); if (pbrms->src) vty_out(vty, " match src-ip %s\n", @@ -570,7 +570,7 @@ static int pbr_vty_map_config_write_sequence(struct vty *vty, nexthop_group_write_nexthop(vty, pbrms->nhg->nexthop); } - vty_endframe(vty, "!\n"); + vty_out(vty, "!\n"); return 1; } From d61d5d888cc0e9f8526eff39de7e3aa4949fe322 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Mon, 26 Mar 2018 11:05:52 -0400 Subject: [PATCH 18/21] zebra: Add code to notice nexthop changes for pbr tables When we have a PBR installed as a table, we need to notice when a nexthop changes and rethink the routes for the pbr tables. Add code to nexthop tracking to notice the pbr watched nexthop has changed in some manner. If it is a pbr route that depends on the nexthop then just enqueue it for rethinking. This is a bit of a hammer, we know that only pbr routes are going to be installing routes in weird non-standard tables as such we need to only handle nexthop changes for nexthops that are actually changing that we care about and to only requeue for route nodes we have route entries for from PBR Signed-off-by: Donald Sharp --- zebra/zebra_rnh.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c index 65df4e15aa..879da092f0 100644 --- a/zebra/zebra_rnh.c +++ b/zebra/zebra_rnh.c @@ -514,6 +514,59 @@ static void zebra_rnh_notify_protocol_clients(vrf_id_t vrfid, int family, } } +static void zebra_rnh_process_pbr_tables(int family, + struct route_node *nrn, + struct rnh *rnh, + struct route_node *prn, + struct route_entry *re) +{ + struct zebra_ns_table *znst; + struct route_entry *o_re; + struct route_node *o_rn; + struct listnode *node; + struct zserv *client; + struct zebra_ns *zns; + afi_t afi = AFI_IP; + + if (family == AF_INET6) + afi = AFI_IP6; + + /* + * We are only concerned about nexthops that change for + * anyone using PBR + */ + for (ALL_LIST_ELEMENTS_RO(rnh->client_list, node, client)) { + if (client->proto == ZEBRA_ROUTE_PBR) + break; + } + + if (!client) + return; + + zns = zebra_ns_lookup(NS_DEFAULT); + RB_FOREACH (znst, zebra_ns_table_head, &zns->ns_tables) { + if (afi != znst->afi) + continue; + + for (o_rn = route_top(znst->table); + o_rn; o_rn = srcdest_route_next(o_rn)) { + RNODE_FOREACH_RE (o_rn, o_re) { + if (o_re->type == ZEBRA_ROUTE_PBR) + break; + + } + + /* + * If we have a PBR route and a nexthop changes + * just rethink it. Yes this is a hammer, but + * a small one + */ + if (o_re) + rib_queue_add(o_rn); + } + } +} + static void zebra_rnh_process_static_routes(vrf_id_t vrfid, int family, struct route_node *nrn, struct rnh *rnh, @@ -752,6 +805,9 @@ static void zebra_rnh_eval_nexthop_entry(vrf_id_t vrfid, int family, int force, zebra_rnh_process_static_routes(vrfid, family, nrn, rnh, prn, rnh->state); + zebra_rnh_process_pbr_tables(family, nrn, rnh, prn, + rnh->state); + /* Process pseudowires attached to this nexthop */ zebra_rnh_process_pseudowires(vrfid, rnh); } From 3a9210c22043112b325f3772a9db541ad6bb0792 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Mon, 26 Mar 2018 12:37:43 -0400 Subject: [PATCH 19/21] pbrd: Indicate command failure a bit better When a command is attempted to be configed and it fails to be installed, indicate via vtysh return codes that we did not accept the command Ticket: CM-20216 Signed-off-by: Donald Sharp --- pbrd/pbr_vty.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c index e19bbcb633..9800baf674 100644 --- a/pbrd/pbr_vty.c +++ b/pbrd/pbr_vty.c @@ -152,7 +152,7 @@ DEFPY(pbr_map_nexthop_group, pbr_map_nexthop_group_cmd, if (pbrms->nhg) { vty_out(vty, "A `set nexthop XX` command already exists, please remove that first\n"); - return CMD_WARNING; + return CMD_WARNING_CONFIG_FAILED; } nhgc = nhgc_find(name); @@ -169,14 +169,14 @@ DEFPY(pbr_map_nexthop_group, pbr_map_nexthop_group_cmd, vty_out(vty, "Nexthop Group specified: %s does not exist to remove", name); - return CMD_WARNING; + return CMD_WARNING_CONFIG_FAILED; } } 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_WARNING_CONFIG_FAILED; } return CMD_SUCCESS; @@ -207,7 +207,7 @@ DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd, if (pbrms->nhgrp_name) { vty_out(vty, "Please unconfigure the nexthop group before adding an individual nexthop"); - return CMD_WARNING; + return CMD_WARNING_CONFIG_FAILED; } if (name) @@ -217,7 +217,7 @@ DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd, if (!vrf) { vty_out(vty, "Specified: %s is non-existent\n", name); - return CMD_WARNING; + return CMD_WARNING_CONFIG_FAILED; } memset(&nhop, 0, sizeof(nhop)); @@ -232,7 +232,7 @@ DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd, vty_out(vty, "Specified Intf %s does not exist in vrf: %s\n", intf, vrf->name); - return CMD_WARNING; + return CMD_WARNING_CONFIG_FAILED; } } else nhop.type = NEXTHOP_TYPE_IPV4; @@ -245,7 +245,7 @@ DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd, vty_out(vty, "Specified Intf %s does not exist in vrf: %s\n", intf, vrf->name); - return CMD_WARNING; + return CMD_WARNING_CONFIG_FAILED; } } else nhop.type = NEXTHOP_TYPE_IPV6; @@ -258,7 +258,7 @@ DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd, if (no) { vty_out(vty, "No nexthops to delete"); - return CMD_WARNING; + return CMD_WARNING_CONFIG_FAILED; } pbrms->nhg = nexthop_group_new(); @@ -279,7 +279,7 @@ DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd, if (pbrms->nhg->nexthop) { vty_out(vty, "If you would like more than one nexthop please use nexthop-groups"); - return CMD_WARNING; + return CMD_WARNING_CONFIG_FAILED; } /* must be adding new nexthop since !no and !nexthop_exists */ From a1b7005bf472981df256f57f72bf16176727d021 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Mon, 26 Mar 2018 13:32:41 -0400 Subject: [PATCH 20/21] pbrd: Remove table and rule range commands Since PBR is meant to be for small deployments, allowing end users to arbitrarily change rule and table ranges without some more careful thought on what is going on and how to do it, sets us up for issues. At this time remove these knobs. Signed-off-by: Donald Sharp --- pbrd/pbr_vty.c | 40 ---------------------------------------- 1 file changed, 40 deletions(-) diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c index 9800baf674..a4f3c54646 100644 --- a/pbrd/pbr_vty.c +++ b/pbrd/pbr_vty.c @@ -295,44 +295,6 @@ DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd, 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-900)$start (400-1000)$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", @@ -606,8 +568,6 @@ void pbr_vty_init(void) 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); From 49027ce8568ca773b0fc441e4abbf71d0d605c2c Mon Sep 17 00:00:00 2001 From: Don Slice Date: Mon, 26 Mar 2018 19:16:09 +0000 Subject: [PATCH 21/21] pbrd: adjust/remove the rule correctly when dst and/or src removed When the last match criteria was removed (dst-ip or src-ip), we were not deleting the rule correctly for ipv6. This fix retains the needed src-ip/dst-ip during the pbr_send_pbr_map process so the appropriate information is available for the rule delete. Signed-off-by: Don Slice --- pbrd/pbr_map.c | 7 ------- pbrd/pbr_map.h | 5 +++++ pbrd/pbr_vty.c | 4 ++++ pbrd/pbr_zebra.c | 11 ++++------- zebra/rule_netlink.c | 3 --- 5 files changed, 13 insertions(+), 17 deletions(-) diff --git a/pbrd/pbr_map.c b/pbrd/pbr_map.c index 5962509e82..ea79320a71 100644 --- a/pbrd/pbr_map.c +++ b/pbrd/pbr_map.c @@ -527,13 +527,6 @@ void pbr_map_check(struct pbr_map_sequence *pbrms) __PRETTY_FUNCTION__, pbrm->name, pbrms->seqno, pbrms->reason); if (pbrms->reason == PBR_MAP_VALID_SEQUENCE_NUMBER) { - if (pbrms->installed) { - install = false; - for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi)) { - pbr_send_pbr_map(pbrms, pmi, install); - } - install = true; - } install = true; DEBUGD(&pbr_dbg_map, "%s: Installing %s(%u) reason: %" PRIu64, __PRETTY_FUNCTION__, pbrm->name, pbrms->seqno, diff --git a/pbrd/pbr_map.h b/pbrd/pbr_map.h index 12d49d98c0..5cb22d7429 100644 --- a/pbrd/pbr_map.h +++ b/pbrd/pbr_map.h @@ -85,6 +85,11 @@ struct pbr_map_sequence { struct prefix *src; struct prefix *dst; + /* + * Family of the src/dst. Needed when deleting since we clear them + */ + unsigned char family; + /* * The nexthop group we auto create * for when the user specifies a individual diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c index a4f3c54646..87ec3804a5 100644 --- a/pbrd/pbr_vty.c +++ b/pbrd/pbr_vty.c @@ -95,6 +95,8 @@ DEFPY(pbr_map_match_src, pbr_map_match_src_cmd, { struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); + pbrms->family = prefix->family; + if (!no) { if (prefix_same(pbrms->src, prefix)) return CMD_SUCCESS; @@ -122,6 +124,8 @@ DEFPY(pbr_map_match_dst, pbr_map_match_dst_cmd, { struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); + pbrms->family = prefix->family; + if (!no) { if (prefix_same(pbrms->dst, prefix)) return CMD_SUCCESS; diff --git a/pbrd/pbr_zebra.c b/pbrd/pbr_zebra.c index 971fe65aaf..a1a2d34ac1 100644 --- a/pbrd/pbr_zebra.c +++ b/pbrd/pbr_zebra.c @@ -455,7 +455,7 @@ void pbr_send_rnh(struct nexthop *nhop, bool reg) static void pbr_encode_pbr_map_sequence_prefix(struct stream *s, struct prefix *p, - u_char family) + unsigned char family) { struct prefix any; @@ -474,14 +474,11 @@ static void pbr_encode_pbr_map_sequence(struct stream *s, struct pbr_map_sequence *pbrms, struct interface *ifp) { - u_char family; + unsigned char family; family = AF_INET; - if (pbrms->src) - family = pbrms->src->family; - - if (pbrms->dst) - family = pbrms->dst->family; + if (pbrms->family) + family = pbrms->family; stream_putl(s, pbrms->seqno); stream_putl(s, pbrms->ruleno); diff --git a/zebra/rule_netlink.c b/zebra/rule_netlink.c index 2122f9f5fa..dc942204a4 100644 --- a/zebra/rule_netlink.c +++ b/zebra/rule_netlink.c @@ -77,9 +77,6 @@ static int netlink_rule_update(int cmd, struct zebra_pbr_rule *rule) req.frh.family = family; req.frh.action = FR_ACT_TO_TBL; - if (cmd == RTM_NEWRULE) - req.n.nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL; - /* rule's pref # */ addattr32(&req.n, sizeof(req), FRA_PRIORITY, rule->priority);