summaryrefslogtreecommitdiffstats
path: root/pbrd
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:53:30 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:53:30 +0000
commit2c7cac91ed6e7db0f6937923d2b57f97dbdbc337 (patch)
treec05dc0f8e6aa3accc84e3e5cffc933ed94941383 /pbrd
parentInitial commit. (diff)
downloadfrr-upstream.tar.xz
frr-upstream.zip
Adding upstream version 8.4.4.upstream/8.4.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'pbrd')
-rw-r--r--pbrd/.gitignore1
-rw-r--r--pbrd/Makefile10
-rw-r--r--pbrd/pbr_debug.c80
-rw-r--r--pbrd/pbr_debug.h73
-rw-r--r--pbrd/pbr_main.c186
-rw-r--r--pbrd/pbr_map.c924
-rw-r--r--pbrd/pbr_map.h238
-rw-r--r--pbrd/pbr_memory.c27
-rw-r--r--pbrd/pbr_memory.h24
-rw-r--r--pbrd/pbr_nht.c1488
-rw-r--r--pbrd/pbr_nht.h157
-rw-r--r--pbrd/pbr_vrf.c139
-rw-r--r--pbrd/pbr_vrf.h43
-rw-r--r--pbrd/pbr_vty.c1432
-rw-r--r--pbrd/pbr_vty.h24
-rw-r--r--pbrd/pbr_zebra.c616
-rw-r--r--pbrd/pbr_zebra.h52
-rw-r--r--pbrd/subdir.am43
18 files changed, 5557 insertions, 0 deletions
diff --git a/pbrd/.gitignore b/pbrd/.gitignore
new file mode 100644
index 0000000..86622ea
--- /dev/null
+++ b/pbrd/.gitignore
@@ -0,0 +1 @@
+pbrd
diff --git a/pbrd/Makefile b/pbrd/Makefile
new file mode 100644
index 0000000..e8999c3
--- /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 0000000..82f045c
--- /dev/null
+++ b/pbrd/pbr_debug.c
@@ -0,0 +1,80 @@
+/*
+ * 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 <zebra.h>
+
+#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",
+};
+
+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]);
+ }
+}
+
+int pbr_debug_config_write_helper(struct vty *vty, bool config)
+{
+ uint32_t mode = DEBUG_MODE_ALL;
+
+ if (config)
+ mode = DEBUG_MODE_CONF;
+
+ 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);
+}
+
+struct debug_callbacks pbr_dbg_cbs = {.debug_set_all = pbr_debug_set_all};
+
+void pbr_debug_init(void)
+{
+ debug_init(&pbr_dbg_cbs);
+}
diff --git a/pbrd/pbr_debug.h b/pbrd/pbr_debug.h
new file mode 100644
index 0000000..e72fb88
--- /dev/null
+++ b/pbrd/pbr_debug.h
@@ -0,0 +1,73 @@
+/*
+ * 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 <zebra.h>
+
+#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);
+
+/*
+ * Set or unset flags on all debugs for pbrd.
+ *
+ * flags
+ * The flags to set
+ *
+ * set
+ * Whether to set or unset the specified flags
+ */
+void pbr_debug_set_all(uint32_t flags, bool set);
+
+/*
+ * Config write helper.
+ *
+ * vty
+ * Vty to write to
+ *
+ * config
+ * Whether we are writing to show run or saving config file
+ *
+ * Returns:
+ * 0 for convenience
+ */
+int pbr_debug_config_write_helper(struct vty *vty, bool config);
+
+/*
+ * 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_main.c b/pbrd/pbr_main.c
new file mode 100644
index 0000000..59aa367
--- /dev/null
+++ b/pbrd/pbr_main.c
@@ -0,0 +1,186 @@
+/*
+ * 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 <zebra.h>
+
+#include <lib/version.h>
+#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_vty.h"
+#include "pbr_debug.h"
+#include "pbr_vrf.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_vrf_terminate();
+
+ frr_fini();
+
+ exit(0);
+}
+
+/* SIGUSR1 handler. */
+static void sigusr1(void)
+{
+ zlog_rotate();
+}
+
+struct frr_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
+
+static const struct frr_yang_module_info *const pbrd_yang_modules[] = {
+ &frr_filter_info,
+ &frr_interface_info,
+ &frr_vrf_info,
+};
+
+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,
+
+ .yang_modules = pbrd_yang_modules,
+ .n_yang_modules = array_size(pbrd_yang_modules),
+);
+
+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);
+ }
+ }
+
+ master = frr_init();
+
+ pbr_debug_init();
+
+ nexthop_group_init(pbr_nhgroup_add_cb,
+ pbr_nhgroup_add_nexthop_cb,
+ pbr_nhgroup_del_nexthop_cb,
+ pbr_nhgroup_delete_cb);
+
+ /*
+ * So we safely ignore these commands since
+ * we are getting them at this point in time
+ */
+ access_list_init();
+ pbr_nht_init();
+ pbr_map_init();
+ if_zapi_callbacks(pbr_ifp_create, pbr_ifp_up,
+ pbr_ifp_down, pbr_ifp_destroy);
+ pbr_zebra_init();
+ pbr_vrf_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 0000000..5daac55
--- /dev/null
+++ b/pbrd/pbr_map.c
@@ -0,0 +1,924 @@
+/*
+ * 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 <zebra.h>
+
+#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_zebra.h"
+#include "pbr_memory.h"
+#include "pbr_debug.h"
+#include "pbr_vrf.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 bool pbr_map_check_valid_internal(struct pbr_map *pbrm);
+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)
+{
+ XFREE(MTYPE_TMP, pbrms->internal_nhg_name);
+
+ QOBJ_UNREG(pbrms);
+ 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(struct pbr_map_interface *pmi)
+{
+ 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 bool pbrms_is_installed(const struct pbr_map_sequence *pbrms,
+ const struct pbr_map_interface *pmi)
+{
+ uint64_t is_installed = (uint64_t)1 << pmi->install_bit;
+
+ is_installed &= pbrms->installed;
+
+ if (is_installed)
+ return true;
+
+ return false;
+}
+
+/* If any sequence is installed on the interface, assume installed */
+static bool
+pbr_map_interface_is_installed(const struct pbr_map *pbrm,
+ const struct pbr_map_interface *check_pmi)
+{
+
+ struct pbr_map_sequence *pbrms;
+ struct pbr_map_interface *pmi;
+ struct listnode *node, *inode;
+
+ for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms))
+ for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi))
+ if (pmi == check_pmi && pbrms_is_installed(pbrms, pmi))
+ return true;
+
+ return false;
+}
+
+static bool pbr_map_interface_is_valid(const struct pbr_map_interface *pmi)
+{
+ /* Don't install rules without a real ifindex on the incoming interface.
+ *
+ * This can happen when we have config for an interface that does not
+ * exist or when an interface is changing vrfs.
+ */
+ if (pmi->ifp && pmi->ifp->ifindex != IFINDEX_INTERNAL)
+ return true;
+
+ return false;
+}
+
+static void pbr_map_pbrms_update_common(struct pbr_map_sequence *pbrms,
+ bool install, bool changed)
+{
+ struct pbr_map *pbrm;
+ struct listnode *node;
+ struct pbr_map_interface *pmi;
+
+ pbrm = pbrms->parent;
+
+ if (pbrms->nhs_installed && pbrm->incoming->count) {
+ for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, node, pmi)) {
+ if (!pmi->ifp)
+ continue;
+
+ if (install && !pbr_map_interface_is_valid(pmi))
+ continue;
+
+ pbr_send_pbr_map(pbrms, pmi, install, changed);
+ }
+ }
+}
+
+static void pbr_map_pbrms_install(struct pbr_map_sequence *pbrms, bool changed)
+{
+ pbr_map_pbrms_update_common(pbrms, true, changed);
+}
+
+static void pbr_map_pbrms_uninstall(struct pbr_map_sequence *pbrms)
+{
+ pbr_map_pbrms_update_common(pbrms, false, false);
+}
+
+static const char *const pbr_map_reason_str[] = {
+ "Invalid NH-group", "Invalid NH", "No Nexthops",
+ "Both NH and NH-Group", "Invalid Src or Dst", "Invalid VRF",
+ "Both VLAN Set and Strip", "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_final_interface_deletion(struct pbr_map *pbrm,
+ struct pbr_map_interface *pmi)
+{
+ if (pmi->delete && !pbr_map_interface_is_installed(pbrm, pmi)) {
+ listnode_delete(pbrm->incoming, pmi);
+ pmi->pbrm = NULL;
+
+ bf_release_index(pbrm->ifi_bitfield, pmi->install_bit);
+ XFREE(MTYPE_PBR_MAP_INTERFACE, pmi);
+ }
+}
+
+void pbr_map_interface_delete(struct pbr_map *pbrm, struct interface *ifp_del)
+{
+
+ struct listnode *node;
+ struct pbr_map_interface *pmi;
+
+ for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, node, pmi)) {
+ if (ifp_del == pmi->ifp)
+ break;
+ }
+
+ 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;
+
+ 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);
+
+ bf_assign_index(pbrm->ifi_bitfield, pmi->install_bit);
+ pbr_map_check_valid(pbrm->name);
+ if (pbrm->valid)
+ pbr_map_install(pbrm);
+}
+
+static int
+pbr_map_policy_interface_update_common(const struct interface *ifp,
+ struct pbr_interface **pbr_ifp,
+ struct pbr_map **pbrm)
+{
+ if (!ifp->info) {
+ DEBUGD(&pbr_dbg_map, "%s: %s has no pbr_interface info",
+ __func__, ifp->name);
+ return -1;
+ }
+
+ *pbr_ifp = ifp->info;
+
+ *pbrm = pbrm_find((*pbr_ifp)->mapname);
+
+ if (!*pbrm) {
+ DEBUGD(&pbr_dbg_map, "%s: applied PBR-MAP(%s) does not exist?",
+ __func__, (*pbr_ifp)->mapname);
+ return -1;
+ }
+
+ return 0;
+}
+
+void pbr_map_policy_interface_update(const struct interface *ifp, bool state_up)
+{
+ struct pbr_interface *pbr_ifp;
+ struct pbr_map_sequence *pbrms;
+ struct pbr_map *pbrm;
+ struct listnode *node, *inode;
+ struct pbr_map_interface *pmi;
+
+ if (pbr_map_policy_interface_update_common(ifp, &pbr_ifp, &pbrm))
+ return;
+
+ DEBUGD(&pbr_dbg_map, "%s: %s %s rules on interface %s", __func__,
+ pbr_ifp->mapname, (state_up ? "installing" : "removing"),
+ ifp->name);
+
+ /*
+ * Walk the list and install/remove maps on the interface.
+ */
+ for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms))
+ for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi))
+ if (pmi->ifp == ifp && pbr_map_interface_is_valid(pmi))
+ pbr_send_pbr_map(pbrms, pmi, state_up, true);
+}
+
+static void pbrms_vrf_update(struct pbr_map_sequence *pbrms,
+ const struct pbr_vrf *pbr_vrf)
+{
+ const char *vrf_name = pbr_vrf_name(pbr_vrf);
+
+ if (pbrms->vrf_lookup
+ && (strncmp(vrf_name, pbrms->vrf_name, sizeof(pbrms->vrf_name))
+ == 0)) {
+ DEBUGD(&pbr_dbg_map, " Seq %u uses vrf %s (%u), updating map",
+ pbrms->seqno, vrf_name, pbr_vrf_id(pbr_vrf));
+
+ pbr_map_check(pbrms, false);
+ }
+}
+
+/* Vrf enabled/disabled */
+void pbr_map_vrf_update(const struct pbr_vrf *pbr_vrf)
+{
+ struct pbr_map *pbrm;
+ struct pbr_map_sequence *pbrms;
+ struct listnode *node;
+
+ if (!pbr_vrf)
+ return;
+
+ bool enabled = pbr_vrf_is_enabled(pbr_vrf);
+
+ DEBUGD(&pbr_dbg_map, "%s: %s (%u) %s, updating pbr maps", __func__,
+ pbr_vrf_name(pbr_vrf), pbr_vrf_id(pbr_vrf),
+ enabled ? "enabled" : "disabled");
+
+ RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) {
+ DEBUGD(&pbr_dbg_map, "%s: Looking at %s", __func__, pbrm->name);
+ for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms))
+ pbrms_vrf_update(pbrms, pbr_vrf);
+ }
+}
+
+void pbr_map_write_interfaces(struct vty *vty, struct interface *ifp)
+{
+ struct pbr_interface *pbr_ifp = ifp->info;
+
+ if (pbr_ifp
+ && strncmp(pbr_ifp->mapname, "", sizeof(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(struct pbr_map_sequence *pbrms)
+{
+ struct pbr_map *pbrm;
+ struct listnode *inode;
+ struct pbr_map_interface *pmi;
+
+ pbrm = pbrms->parent;
+
+ for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi))
+ pbr_send_pbr_map(pbrms, pmi, false, false);
+
+ if (pbrms->nhg)
+ pbr_nht_delete_individual_nexthop(pbrms);
+
+ listnode_delete(pbrm->seqnumbers, pbrms);
+
+ if (pbrm->seqnumbers->count == 0) {
+ RB_REMOVE(pbr_map_entry_head, &pbr_maps, pbrm);
+
+ bf_free(pbrm->ifi_bitfield);
+ XFREE(MTYPE_PBR_MAP, pbrm);
+ }
+}
+
+static void pbr_map_delete_common(struct pbr_map_sequence *pbrms)
+{
+ struct pbr_map *pbrm = pbrms->parent;
+
+ pbr_map_pbrms_uninstall(pbrms);
+
+ pbrm->valid = false;
+ pbrms->nhs_installed = false;
+ pbrms->reason |= PBR_MAP_INVALID_NO_NEXTHOPS;
+ XFREE(MTYPE_TMP, pbrms->nhgrp_name);
+}
+
+void pbr_map_delete_nexthops(struct pbr_map_sequence *pbrms)
+{
+ pbr_map_delete_common(pbrms);
+}
+
+void pbr_map_delete_vrf(struct pbr_map_sequence *pbrms)
+{
+ pbr_map_delete_common(pbrms);
+}
+
+struct pbr_map_sequence *pbrms_lookup_unique(uint32_t unique, char *ifname,
+ struct pbr_map_interface **ppmi)
+{
+ 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 (strcmp(pmi->ifp->name, ifname) != 0)
+ continue;
+
+ if (ppmi)
+ *ppmi = pmi;
+
+ for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, snode,
+ pbrms)) {
+ DEBUGD(&pbr_dbg_map, "%s: Comparing %u to %u",
+ __func__, pbrms->unique, unique);
+ if (pbrms->unique == unique)
+ return pbrms;
+ }
+ }
+ }
+
+ 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);
+ }
+ }
+ }
+}
+
+/* Decodes a standardized DSCP into its representative value */
+uint8_t pbr_map_decode_dscp_enum(const char *name)
+{
+ /* Standard Differentiated Services Field Codepoints */
+ if (!strcmp(name, "cs0"))
+ return 0;
+ if (!strcmp(name, "cs1"))
+ return 8;
+ if (!strcmp(name, "cs2"))
+ return 16;
+ if (!strcmp(name, "cs3"))
+ return 24;
+ if (!strcmp(name, "cs4"))
+ return 32;
+ if (!strcmp(name, "cs5"))
+ return 40;
+ if (!strcmp(name, "cs6"))
+ return 48;
+ if (!strcmp(name, "cs7"))
+ return 56;
+ if (!strcmp(name, "af11"))
+ return 10;
+ if (!strcmp(name, "af12"))
+ return 12;
+ if (!strcmp(name, "af13"))
+ return 14;
+ if (!strcmp(name, "af21"))
+ return 18;
+ if (!strcmp(name, "af22"))
+ return 20;
+ if (!strcmp(name, "af23"))
+ return 22;
+ if (!strcmp(name, "af31"))
+ return 26;
+ if (!strcmp(name, "af32"))
+ return 28;
+ if (!strcmp(name, "af33"))
+ return 30;
+ if (!strcmp(name, "af41"))
+ return 34;
+ if (!strcmp(name, "af42"))
+ return 36;
+ if (!strcmp(name, "af43"))
+ return 38;
+ if (!strcmp(name, "ef"))
+ return 46;
+ if (!strcmp(name, "voice-admit"))
+ return 44;
+
+ /* No match? Error out */
+ return -1;
+}
+
+struct pbr_map_sequence *pbrms_get(const char *name, uint32_t seqno)
+{
+ struct pbr_map *pbrm;
+ struct pbr_map_sequence *pbrms;
+ struct listnode *node;
+
+ pbrm = pbrm_find(name);
+ if (!pbrm) {
+ pbrm = XCALLOC(MTYPE_PBR_MAP, sizeof(*pbrm));
+ snprintf(pbrm->name, sizeof(pbrm->name), "%s", 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);
+
+ bf_init(pbrm->ifi_bitfield, 64);
+ pbr_map_add_interfaces(pbrm);
+ }
+
+ 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->action_vlan_id = 0;
+ pbrms->action_vlan_flags = 0;
+ pbrms->action_pcp = 0;
+
+ pbrms->action_queue_id = PBR_MAP_UNDEFINED_QUEUE_ID;
+
+ pbrms->reason =
+ PBR_MAP_INVALID_EMPTY |
+ PBR_MAP_INVALID_NO_NEXTHOPS;
+ pbrms->vrf_name[0] = '\0';
+
+ QOBJ_REG(pbrms, pbr_map_sequence);
+ listnode_add_sort(pbrm->seqnumbers, pbrms);
+ }
+
+ return pbrms;
+}
+
+static void
+pbr_map_sequence_check_nexthops_valid(struct pbr_map_sequence *pbrms)
+{
+ /* Check if any are present first */
+ if (!pbrms->vrf_unchanged && !pbrms->vrf_lookup && !pbrms->nhg
+ && !pbrms->nhgrp_name) {
+ pbrms->reason |= PBR_MAP_INVALID_NO_NEXTHOPS;
+ return;
+ }
+
+ /*
+ * Check validness of vrf.
+ */
+
+ /* This one can be considered always valid */
+ if (pbrms->vrf_unchanged)
+ pbrms->nhs_installed = true;
+
+ if (pbrms->vrf_lookup) {
+ struct pbr_vrf *pbr_vrf =
+ pbr_vrf_lookup_by_name(pbrms->vrf_name);
+
+ if (pbr_vrf && pbr_vrf_is_valid(pbr_vrf))
+ pbrms->nhs_installed = true;
+ else
+ pbrms->reason |= PBR_MAP_INVALID_VRF;
+ }
+
+ /*
+ * Check validness of the nexthop or nexthop-group
+ */
+
+ /* Only nexthop or nexthop group allowed */
+ 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_not_empty(struct pbr_map_sequence *pbrms)
+{
+ if (!pbrms->src && !pbrms->dst && !pbrms->mark && !pbrms->dsfield
+ && !pbrms->action_vlan_id && !pbrms->action_vlan_flags
+ && !pbrms->action_pcp
+ && pbrms->action_queue_id == PBR_MAP_UNDEFINED_QUEUE_ID)
+ pbrms->reason |= PBR_MAP_INVALID_EMPTY;
+}
+
+static void pbr_map_sequence_check_vlan_actions(struct pbr_map_sequence *pbrms)
+{
+ /* The set vlan tag action does the following:
+ * 1. If the frame is untagged, it tags the frame with the
+ * configured VLAN ID.
+ * 2. If the frame is tagged, if replaces the tag.
+ *
+ * The strip vlan action removes any inner tag, so it is invalid to
+ * specify both a set and strip action.
+ */
+ if ((pbrms->action_vlan_id != 0) && (pbrms->action_vlan_flags != 0))
+ pbrms->reason |= PBR_MAP_INVALID_SET_STRIP_VLAN;
+}
+
+
+/*
+ * 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_vlan_actions(pbrms);
+ pbr_map_sequence_check_not_empty(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.
+ */
+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?", __func__,
+ name);
+ return false;
+ }
+
+ pbr_map_check_valid_internal(pbrm);
+ return pbrm->valid;
+}
+
+void pbr_map_schedule_policy_from_nhg(const char *nh_group, bool installed)
+{
+ struct pbr_map_sequence *pbrms;
+ struct pbr_map *pbrm;
+ struct listnode *node;
+
+ RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) {
+ DEBUGD(&pbr_dbg_map, "%s: Looking at %s", __func__, pbrm->name);
+ for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) {
+ DEBUGD(&pbr_dbg_map, " NH Grp name: %s",
+ pbrms->nhgrp_name ?
+ pbrms->nhgrp_name : pbrms->internal_nhg_name);
+
+ if (pbrms->nhgrp_name
+ && (strcmp(nh_group, pbrms->nhgrp_name) == 0)) {
+ pbrms->nhs_installed = installed;
+
+ pbr_map_check(pbrms, false);
+ }
+
+ if (pbrms->nhg
+ && (strcmp(nh_group, pbrms->internal_nhg_name)
+ == 0)) {
+ pbrms->nhs_installed = installed;
+
+ pbr_map_check(pbrms, false);
+ }
+ }
+ }
+}
+
+void pbr_map_policy_install(const char *name)
+{
+ struct pbr_map_sequence *pbrms;
+ struct pbr_map *pbrm;
+ struct listnode *node, *inode;
+ struct pbr_map_interface *pmi;
+
+ DEBUGD(&pbr_dbg_map, "%s: for %s", __func__, name);
+ pbrm = pbrm_find(name);
+ if (!pbrm)
+ return;
+
+ for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) {
+ DEBUGD(&pbr_dbg_map,
+ "%s: Looking at what to install %s(%u) %d %d", __func__,
+ name, pbrms->seqno, pbrm->valid, pbrms->nhs_installed);
+
+ if (pbrm->valid && pbrms->nhs_installed
+ && pbrm->incoming->count) {
+ DEBUGD(&pbr_dbg_map, " Installing %s %u", pbrm->name,
+ pbrms->seqno);
+ for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi))
+ if (pbr_map_interface_is_valid(pmi))
+ pbr_send_pbr_map(pbrms, pmi, true,
+ false);
+ }
+ }
+}
+
+void pbr_map_policy_delete(struct pbr_map *pbrm, struct pbr_map_interface *pmi)
+{
+ struct listnode *node;
+ struct pbr_map_sequence *pbrms;
+ bool sent = false;
+
+
+ for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms))
+ if (pbr_send_pbr_map(pbrms, pmi, false, true))
+ sent = true; /* rule removal sent to zebra */
+
+ pmi->delete = true;
+
+ /*
+ * If we actually sent something for deletion, wait on zapi callback
+ * before clearing data.
+ */
+ if (sent)
+ return;
+
+ pbr_map_final_interface_deletion(pbrm, 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.
+ */
+void pbr_map_check_nh_group_change(const char *nh_group)
+{
+ struct pbr_map_sequence *pbrms;
+ struct pbr_map *pbrm;
+ struct listnode *node, *inode;
+ struct pbr_map_interface *pmi;
+ bool found_name;
+
+ 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;
+
+ /* Set data we were waiting on */
+ if (pbrms->nhgrp_name)
+ pbr_nht_set_seq_nhg_data(
+ pbrms,
+ nhgc_find(pbrms->nhgrp_name));
+
+ pbr_map_check_valid_internal(pbrm);
+
+ if (pbrm->valid && (original != pbrm->valid))
+ pbr_map_install(pbrm);
+
+ if (pbrm->valid == false)
+ for (ALL_LIST_ELEMENTS_RO(
+ pbrm->incoming, inode,
+ pmi))
+ pbr_send_pbr_map(pbrms, pmi,
+ false, false);
+ }
+ }
+ }
+}
+
+void pbr_map_check_vrf_nh_group_change(const char *nh_group,
+ struct pbr_vrf *pbr_vrf,
+ uint32_t old_vrf_id)
+{
+ struct pbr_map *pbrm;
+ struct pbr_map_sequence *pbrms;
+ struct listnode *node;
+
+
+ RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) {
+ for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) {
+ if (pbrms->nhgrp_name)
+ continue;
+
+ if (pbrms->nhg == NULL)
+ continue;
+
+ if (strcmp(nh_group, pbrms->internal_nhg_name))
+ continue;
+
+ if (pbrms->nhg->nexthop == NULL)
+ continue;
+
+ if (pbrms->nhg->nexthop->vrf_id != old_vrf_id)
+ continue;
+
+ pbrms->nhg->nexthop->vrf_id = pbr_vrf_id(pbr_vrf);
+ }
+ }
+}
+
+void pbr_map_check_interface_nh_group_change(const char *nh_group,
+ struct interface *ifp,
+ ifindex_t oldifindex)
+{
+ struct pbr_map *pbrm;
+ struct pbr_map_sequence *pbrms;
+ struct listnode *node;
+
+ RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) {
+ for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) {
+ if (pbrms->nhgrp_name)
+ continue;
+
+ if (pbrms->nhg == NULL)
+ continue;
+
+ if (strcmp(nh_group, pbrms->internal_nhg_name))
+ continue;
+
+ if (pbrms->nhg->nexthop == NULL)
+ continue;
+
+ if (pbrms->nhg->nexthop->ifindex != oldifindex)
+ continue;
+
+ pbrms->nhg->nexthop->ifindex = ifp->ifindex;
+ }
+ }
+}
+
+void pbr_map_check(struct pbr_map_sequence *pbrms, bool changed)
+{
+ struct pbr_map *pbrm;
+ bool install;
+
+ pbrm = pbrms->parent;
+ DEBUGD(&pbr_dbg_map, "%s: for %s(%u)", __func__, pbrm->name,
+ pbrms->seqno);
+ if (pbr_map_check_valid(pbrm->name))
+ DEBUGD(&pbr_dbg_map, "We are totally valid %s",
+ pbrm->name);
+
+ if (pbrms->reason == PBR_MAP_VALID_SEQUENCE_NUMBER) {
+ install = true;
+ DEBUGD(&pbr_dbg_map, "%s: Installing %s(%u) reason: %" PRIu64,
+ __func__, pbrm->name, pbrms->seqno, pbrms->reason);
+ DEBUGD(&pbr_dbg_map,
+ " Sending PBR_MAP_POLICY_INSTALL event");
+ } else {
+ install = false;
+ DEBUGD(&pbr_dbg_map, "%s: Removing %s(%u) reason: %" PRIu64,
+ __func__, pbrm->name, pbrms->seqno, pbrms->reason);
+ }
+
+ if (install)
+ pbr_map_pbrms_install(pbrms, changed);
+ else
+ pbr_map_pbrms_uninstall(pbrms);
+}
+
+void pbr_map_install(struct pbr_map *pbrm)
+{
+ struct pbr_map_sequence *pbrms;
+ struct listnode *node;
+
+ if (!pbrm->incoming->count)
+ return;
+
+ for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms))
+ pbr_map_pbrms_install(pbrms, false);
+}
+
+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 0000000..3527523
--- /dev/null
+++ b/pbrd/pbr_map.h
@@ -0,0 +1,238 @@
+/*
+ * 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__
+
+#include <bitfield.h>
+
+#include "pbr_vrf.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;
+
+ bitfield_t ifi_bitfield;
+ /*
+ * 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;
+};
+
+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 {
+ uint32_t install_bit;
+
+ 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;
+
+ /*
+ * src and dst ports
+ */
+ uint16_t src_prt;
+ uint16_t dst_prt;
+
+ /*
+ * The ip protocol we want to match on
+ */
+ uint8_t ip_proto;
+
+ /*
+ * Our policy Catchers
+ */
+ struct prefix *src;
+ struct prefix *dst;
+ uint8_t dsfield;
+ uint32_t mark;
+
+ /*
+ * Actions
+ */
+ uint8_t action_pcp;
+ uint8_t action_vlan_id;
+#define PBR_MAP_STRIP_INNER_ANY (1 << 0)
+ uint8_t action_vlan_flags;
+
+#define PBR_MAP_UNDEFINED_QUEUE_ID 0
+ uint32_t action_queue_id;
+
+ /*
+ * Family of the src/dst. Needed when deleting since we clear them
+ */
+ unsigned char family;
+
+ /*
+ * Use interface's vrf.
+ */
+ bool vrf_unchanged;
+
+ /*
+ * The vrf to lookup in was directly configured.
+ */
+ bool vrf_lookup;
+
+ /*
+ * VRF to lookup.
+ */
+ char vrf_name[VRF_NAMSIZ + 1];
+
+ /*
+ * 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;
+
+ /*
+ * Are we installed
+ */
+ uint64_t 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_EMPTY (1 << 4)
+#define PBR_MAP_INVALID_VRF (1 << 5)
+#define PBR_MAP_INVALID_SET_STRIP_VLAN (1 << 6)
+ 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, char *ifname,
+ struct pbr_map_interface **ppmi);
+
+extern struct pbr_map *pbrm_find(const char *name);
+extern void pbr_map_delete(struct pbr_map_sequence *pbrms);
+extern void pbr_map_delete_nexthops(struct pbr_map_sequence *pbrms);
+extern void pbr_map_delete_vrf(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);
+
+extern uint8_t pbr_map_decode_dscp_enum(const char *name);
+
+/* Update maps installed on interface */
+extern void pbr_map_policy_interface_update(const struct interface *ifp,
+ bool state_up);
+
+extern void pbr_map_final_interface_deletion(struct pbr_map *pbrm,
+ struct pbr_map_interface *pmi);
+
+extern void pbr_map_vrf_update(const struct pbr_vrf *pbr_vrf);
+
+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);
+
+/**
+ * Re-check the pbr map for validity.
+ *
+ * Install if valid, remove if not.
+ *
+ * If changed is set, the config on the on the map has changed somewhere
+ * and the rules need to be replaced if valid.
+ */
+extern void pbr_map_check(struct pbr_map_sequence *pbrms, bool changed);
+extern void pbr_map_check_nh_group_change(const char *nh_group);
+extern void pbr_map_reason_string(unsigned int reason, char *buf, int size);
+
+extern void pbr_map_schedule_policy_from_nhg(const char *nh_group,
+ bool installed);
+
+extern void pbr_map_install(struct pbr_map *pbrm);
+
+extern void pbr_map_policy_install(const char *name);
+extern void pbr_map_policy_delete(struct pbr_map *pbrm,
+ struct pbr_map_interface *pmi);
+
+extern void pbr_map_check_vrf_nh_group_change(const char *nh_group,
+ struct pbr_vrf *pbr_vrf,
+ uint32_t old_vrf_id);
+extern void pbr_map_check_interface_nh_group_change(const char *nh_group,
+ struct interface *ifp,
+ ifindex_t oldifindex);
+#endif
diff --git a/pbrd/pbr_memory.c b/pbrd/pbr_memory.c
new file mode 100644
index 0000000..5531d41
--- /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 <zebra.h>
+
+#include <memory.h>
+
+#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 0000000..eb13d5d
--- /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 0000000..cbff483
--- /dev/null
+++ b/pbrd/pbr_nht.c
@@ -0,0 +1,1488 @@
+/*
+ * 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 <zebra.h>
+
+#include <log.h>
+#include <nexthop.h>
+#include "nexthop_group.h"
+#include "nexthop_group_private.h"
+#include <hash.h>
+#include <jhash.h>
+#include <vty.h>
+#include <zclient.h>
+#include <debug.h>
+
+#include "pbrd/pbr_nht.h"
+#include "pbrd/pbr_map.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");
+
+struct hash *pbr_nhg_hash;
+static struct hash *pbr_nhrc_hash;
+static struct hash *pbr_nhg_allocated_id_hash;
+
+static uint32_t pbr_nhg_low_table;
+static uint32_t pbr_nhg_high_table;
+static uint32_t pbr_next_unallocated_table_id;
+static uint32_t pbr_nhg_low_rule;
+static uint32_t pbr_nhg_high_rule;
+
+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_type);
+
+/*
+ * 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;
+ nhrc->nexthop.next = NULL;
+ nhrc->nexthop.prev = NULL;
+ return nhrc;
+}
+
+static bool pbr_nhrc_hash_equal(const void *arg1, const void *arg2)
+{
+ const struct nexthop *nh1, *nh2;
+
+ nh1 = arg1;
+ nh2 = arg2;
+
+ return nexthop_same(nh1, nh2);
+}
+
+/* ------------------------------------------------------------------------- */
+
+static void *pbr_nh_alloc(void *p)
+{
+ struct pbr_nexthop_cache *new;
+ struct pbr_nexthop_cache *pnhc = (struct pbr_nexthop_cache *)p;
+ struct nhrc *nhrc;
+
+ new = XCALLOC(MTYPE_PBR_NHG, sizeof(*new));
+ nhrc = hash_get(pbr_nhrc_hash, &pnhc->nexthop, pbr_nhrc_hash_alloc);
+ new->nexthop = nhrc->nexthop;
+
+ /* Decremented again in pbr_nh_delete */
+ ++nhrc->refcount;
+
+ DEBUGD(&pbr_dbg_nht, "%s: Sending nexthop to Zebra", __func__);
+
+ pbr_send_rnh(&new->nexthop, true);
+
+ new->valid = false;
+ return new;
+}
+
+static void pbr_nh_delete(struct pbr_nexthop_cache **pnhc)
+{
+ struct nhrc *nhrc;
+
+ nhrc = hash_lookup(pbr_nhrc_hash, &((*pnhc)->nexthop));
+
+ if (nhrc)
+ --nhrc->refcount;
+ if (!nhrc || nhrc->refcount == 0) {
+ DEBUGD(&pbr_dbg_nht, "%s: Removing nexthop from Zebra",
+ __func__);
+ 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_bucket *b, void *p)
+{
+ pbr_nh_delete((struct pbr_nexthop_cache **)&b->data);
+}
+
+static uint32_t pbr_nh_hash_key(const void *arg)
+{
+ uint32_t key;
+ const struct pbr_nexthop_cache *pbrnc = arg;
+
+ key = nexthop_hash(&pbrnc->nexthop);
+
+ return key;
+}
+
+static bool 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 false;
+
+ if (pbrnc1->nexthop.ifindex != pbrnc2->nexthop.ifindex)
+ return false;
+
+ if (pbrnc1->nexthop.type != pbrnc2->nexthop.type)
+ return false;
+
+ switch (pbrnc1->nexthop.type) {
+ case NEXTHOP_TYPE_IFINDEX:
+ return pbrnc1->nexthop.ifindex == pbrnc2->nexthop.ifindex;
+ 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 false;
+}
+
+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));
+
+ strlcpy(new->name, pnhgc->name, sizeof(pnhgc->name));
+ pbr_nht_reserve_next_table_id(new);
+
+ DEBUGD(&pbr_dbg_nht, "%s: NHT: %s assigned Table ID: %u", __func__,
+ 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_nexthop_group_cache *pnhgc;
+ struct nexthop_group_cmd *nhgc;
+
+ nhgc = nhgc_find(name);
+
+ if (!nhgc) {
+ DEBUGD(&pbr_dbg_nht, "%s: Could not find nhgc with name: %s",
+ __func__, name);
+ return;
+ }
+
+ pnhgc = pbr_nht_add_group(name);
+
+ if (!pnhgc)
+ return;
+
+ DEBUGD(&pbr_dbg_nht, "%s: Added nexthop-group %s", __func__, name);
+
+ pbr_map_check_nh_group_change(name);
+}
+
+void pbr_nhgroup_add_nexthop_cb(const struct nexthop_group_cmd *nhgc,
+ const struct nexthop *nhop)
+{
+ 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;
+
+ /* find pnhgc by name */
+ strlcpy(pnhgc_find.name, nhgc->name, sizeof(pnhgc_find.name));
+ pnhgc = hash_lookup(pbr_nhg_hash, &pnhgc_find);
+
+ if (!pnhgc) {
+ /* Check if configured table range is exhausted */
+ if (!pbr_nht_has_unallocated_table()) {
+ zlog_warn(
+ "%s: Exhausted all table identifiers; cannot create nexthop-group cache for nexthop-group '%s'",
+ __func__, nhgc->name);
+ return;
+ }
+
+ /* No nhgc but range not exhausted? Then alloc it */
+ pnhgc = hash_get(pbr_nhg_hash, &pnhgc_find, pbr_nhgc_alloc);
+ }
+
+ /* create & insert new pnhc into pnhgc->nhh */
+ pnhc_find.nexthop = *nhop;
+ pnhc = hash_get(pnhgc->nhh, &pnhc_find, pbr_nh_alloc);
+
+ /* 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",
+ __func__, debugstr, nhgc->name);
+ }
+
+ pbr_nht_install_nexthop_group(pnhgc, nhgc->nhg);
+ pbr_map_check_nh_group_change(nhgc->name);
+
+ if (nhop->type == NEXTHOP_TYPE_IFINDEX
+ || (nhop->type == NEXTHOP_TYPE_IPV6_IFINDEX
+ && IN6_IS_ADDR_LINKLOCAL(&nhop->gate.ipv6))) {
+ struct interface *ifp;
+
+ ifp = if_lookup_by_index(nhop->ifindex, nhop->vrf_id);
+ if (ifp)
+ pbr_nht_nexthop_interface_update(ifp);
+ }
+}
+
+void pbr_nhgroup_del_nexthop_cb(const struct nexthop_group_cmd *nhgc,
+ const struct nexthop *nhop)
+{
+ 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;
+ enum nexthop_types_t nh_type = nhop->type;
+
+ /* find pnhgc by name */
+ strlcpy(pnhgc_find.name, nhgc->name, sizeof(pnhgc_find.name));
+ pnhgc = hash_lookup(pbr_nhg_hash, &pnhgc_find);
+
+ /*
+ * Ignore deletions of nhg we did not / could not allocate nhgc for
+ * Occurs when PBR table range is full but new nhg keep coming in
+ */
+ if (!pnhgc)
+ return;
+
+ /* delete pnhc from pnhgc->nhh */
+ pnhc_find.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",
+ __func__, debugstr, nhgc->name);
+ }
+
+ if (pnhgc->nhh->count)
+ pbr_nht_install_nexthop_group(pnhgc, nhgc->nhg);
+ else
+ pbr_nht_uninstall_nexthop_group(pnhgc, nhgc->nhg, nh_type);
+
+ pbr_map_check_nh_group_change(nhgc->name);
+}
+
+void pbr_nhgroup_delete_cb(const char *name)
+{
+ DEBUGD(&pbr_dbg_nht, "%s: Removed nexthop-group %s", __func__, name);
+
+ /* delete group from all pbrms's */
+ pbr_nht_delete_group(name);
+
+ pbr_map_check_nh_group_change(name);
+}
+
+static void
+pbr_nht_find_nhg_from_table_update(struct pbr_nexthop_group_cache *pnhgc,
+ uint32_t table_id, bool installed)
+{
+ if (pnhgc->table_id == table_id) {
+ DEBUGD(&pbr_dbg_nht, "%s: %s: Table ID (%u) matches %s",
+ __func__, (installed ? "install" : "remove"), table_id,
+ pnhgc->name);
+
+ pnhgc->installed = installed;
+ pnhgc->valid = installed;
+ pbr_map_schedule_policy_from_nhg(pnhgc->name, pnhgc->installed);
+ }
+}
+
+static void pbr_nht_find_nhg_from_table_install(struct hash_bucket *b,
+ void *data)
+{
+ struct pbr_nexthop_group_cache *pnhgc =
+ (struct pbr_nexthop_group_cache *)b->data;
+ uint32_t table_id = *(uint32_t *)data;
+
+ pbr_nht_find_nhg_from_table_update(pnhgc, table_id, true);
+}
+
+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_bucket *b,
+ void *data)
+{
+ struct pbr_nexthop_group_cache *pnhgc =
+ (struct pbr_nexthop_group_cache *)b->data;
+ uint32_t table_id = *(uint32_t *)data;
+
+ pbr_nht_find_nhg_from_table_update(pnhgc, table_id, false);
+}
+
+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,
+ enum nexthop_types_t nh_type)
+{
+ struct nexthop *nexthop;
+ afi_t install_afi = AFI_MAX;
+ bool v6, v4, bh;
+
+ if (nh_type) {
+ switch (nh_type) {
+ case NEXTHOP_TYPE_IPV4:
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ return AFI_IP;
+ case NEXTHOP_TYPE_IPV6:
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ return AFI_IP6;
+ case NEXTHOP_TYPE_IFINDEX:
+ case NEXTHOP_TYPE_BLACKHOLE:
+ return AFI_MAX;
+ }
+ }
+
+ v6 = v4 = bh = false;
+
+ for (ALL_NEXTHOPS(nhg, nexthop)) {
+ nh_type = nexthop->type;
+
+ switch (nh_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;
+ break;
+ }
+ }
+
+ /* Interface and/or blackhole nexthops only. */
+ if (!v4 && !v6)
+ install_afi = AFI_MAX;
+
+ if (!bh && v6 && v4)
+ DEBUGD(&pbr_dbg_nht,
+ "%s: Saw both V6 and V4 nexthops...using %s", __func__,
+ 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.",
+ __func__, 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;
+ enum nexthop_types_t nh_type = 0;
+
+ install_afi = pbr_nht_which_afi(nhg, nh_type);
+
+ route_add(pnhgc, nhg, install_afi);
+}
+
+static void
+pbr_nht_uninstall_nexthop_group(struct pbr_nexthop_group_cache *pnhgc,
+ struct nexthop_group nhg,
+ enum nexthop_types_t nh_type)
+{
+ afi_t install_afi;
+
+ install_afi = pbr_nht_which_afi(nhg, nh_type);
+
+ 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));
+ snprintf(find.name, sizeof(find.name), "%s", name);
+ pnhgc = hash_lookup(pbr_nhg_hash, &find);
+
+ if (!pnhgc) {
+ DEBUGD(&pbr_dbg_nht,
+ "%s: Could not find nexthop-group cache w/ name '%s'",
+ __func__, name);
+ return;
+ }
+
+ for (ALL_NEXTHOPS(nhgc->nhg, nhop)) {
+ struct pbr_nexthop_cache lookup;
+ struct pbr_nexthop_cache *pnhc;
+
+ lookup.nexthop = *nhop;
+ pnhc = hash_lookup(pnhgc->nhh, &lookup);
+ if (!pnhc) {
+ pnhc = hash_get(pnhgc->nhh, &lookup, pbr_nh_alloc);
+ pnhc->parent = pnhgc;
+ }
+ }
+ pbr_nht_install_nexthop_group(pnhgc, nhgc->nhg);
+}
+
+char *pbr_nht_nexthop_make_name(char *name, size_t l,
+ uint32_t seqno, char *buffer)
+{
+ snprintf(buffer, l, "%s%u", name, seqno);
+ return buffer;
+}
+
+/* Set data derived from nhg in pbrms */
+void pbr_nht_set_seq_nhg_data(struct pbr_map_sequence *pbrms,
+ const struct nexthop_group_cmd *nhgc)
+{
+ const struct nexthop_group *nhg;
+
+ if (!nhgc)
+ return;
+
+ nhg = &nhgc->nhg;
+ if (!nhg->nexthop)
+ return;
+
+ switch (nhg->nexthop->type) {
+ case NEXTHOP_TYPE_IPV6:
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ pbrms->family = AF_INET6;
+ break;
+ case NEXTHOP_TYPE_IPV4:
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ pbrms->family = AF_INET;
+ default:
+ break;
+ }
+}
+
+/* Configure a routemap sequence to use a given nexthop group */
+void pbr_nht_set_seq_nhg(struct pbr_map_sequence *pbrms, const char *name)
+{
+ struct nexthop_group_cmd *nhgc;
+
+ if (!name)
+ return;
+
+ pbrms->nhgrp_name = XSTRDUP(MTYPE_TMP, name);
+
+ nhgc = nhgc_find(name);
+ if (!nhgc)
+ return;
+
+ pbr_nht_set_seq_nhg_data(pbrms, nhgc);
+}
+
+void pbr_nht_add_individual_nexthop(struct pbr_map_sequence *pbrms,
+ const struct nexthop *nhop)
+{
+ struct pbr_nexthop_group_cache *pnhgc;
+ struct pbr_nexthop_group_cache find;
+ struct pbr_nexthop_cache *pnhc;
+ struct pbr_nexthop_cache lookup;
+ struct nexthop *nh;
+ char buf[PBR_NHC_NAMELEN];
+
+ pbrms->nhg = nexthop_group_new();
+ pbrms->internal_nhg_name = XSTRDUP(
+ MTYPE_TMP,
+ pbr_nht_nexthop_make_name(pbrms->parent->name, PBR_NHC_NAMELEN,
+ pbrms->seqno, buf));
+
+ nh = nexthop_new();
+ memcpy(nh, nhop, sizeof(*nh));
+
+ nexthop_group_add_sorted(pbrms->nhg, nh);
+
+ memset(&find, 0, sizeof(find));
+ pbr_nht_nexthop_make_name(pbrms->parent->name, PBR_NHC_NAMELEN,
+ pbrms->seqno, find.name);
+
+ if (!pbr_nht_has_unallocated_table()) {
+ zlog_warn(
+ "%s: Exhausted all table identifiers; cannot create nexthop-group cache for nexthop-group '%s'",
+ __func__, find.name);
+ return;
+ }
+
+ if (!pbrms->internal_nhg_name)
+ pbrms->internal_nhg_name = XSTRDUP(MTYPE_TMP, find.name);
+
+ pnhgc = hash_get(pbr_nhg_hash, &find, pbr_nhgc_alloc);
+
+ lookup.nexthop = *pbrms->nhg->nexthop;
+ pnhc = hash_get(pnhgc->nhh, &lookup, pbr_nh_alloc);
+ pnhc->parent = pnhgc;
+ if (nhop->vrf_id != VRF_DEFAULT) {
+ struct vrf *vrf = vrf_lookup_by_id(nhop->vrf_id);
+
+ if (vrf)
+ strlcpy(pnhc->vrf_name, vrf->name,
+ sizeof(pnhc->vrf_name));
+ }
+
+ if (nhop->ifindex != 0) {
+ struct interface *ifp =
+ if_lookup_by_index(nhop->ifindex, nhop->vrf_id);
+
+ if (ifp)
+ strlcpy(pnhc->intf_name, ifp->name,
+ sizeof(pnhc->intf_name));
+ }
+ pbr_nht_install_nexthop_group(pnhgc, *pbrms->nhg);
+}
+
+static void pbr_nht_release_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 nexthop *nh;
+ enum nexthop_types_t nh_type = 0;
+
+ memset(&find, 0, sizeof(find));
+ snprintf(find.name, sizeof(find.name), "%s", pbrms->internal_nhg_name);
+ pnhgc = hash_lookup(pbr_nhg_hash, &find);
+
+ nh = pbrms->nhg->nexthop;
+ nh_type = 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, nh_type);
+
+ hash_release(pbr_nhg_hash, pnhgc);
+ pbr_nhgc_delete(pnhgc);
+
+ nexthop_group_delete(&pbrms->nhg);
+ XFREE(MTYPE_TMP, pbrms->internal_nhg_name);
+}
+
+void pbr_nht_delete_individual_nexthop(struct pbr_map_sequence *pbrms)
+{
+ pbr_map_delete_nexthops(pbrms);
+
+ pbr_nht_release_individual_nexthop(pbrms);
+}
+
+struct pbr_nexthop_group_cache *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;
+
+ if (!pbr_nht_has_unallocated_table()) {
+ zlog_warn(
+ "%s: Exhausted all table identifiers; cannot create nexthop-group cache for nexthop-group '%s'",
+ __func__, name);
+ return NULL;
+ }
+
+ nhgc = nhgc_find(name);
+
+ if (!nhgc) {
+ DEBUGD(&pbr_dbg_nht, "%s: Could not find nhgc with name: %s",
+ __func__, name);
+ return NULL;
+ }
+
+ snprintf(lookup.name, sizeof(lookup.name), "%s", name);
+ pnhgc = hash_get(pbr_nhg_hash, &lookup, pbr_nhgc_alloc);
+ DEBUGD(&pbr_dbg_nht, "%s: Retrieved NHGC @ %p", __func__, pnhgc);
+
+ for (ALL_NEXTHOPS(nhgc->nhg, nhop)) {
+ struct pbr_nexthop_cache lookupc;
+ struct pbr_nexthop_cache *pnhc;
+
+ lookupc.nexthop = *nhop;
+ pnhc = hash_lookup(pnhgc->nhh, &lookupc);
+ if (!pnhc) {
+ pnhc = hash_get(pnhgc->nhh, &lookupc, pbr_nh_alloc);
+ pnhc->parent = pnhgc;
+ }
+ }
+
+ return pnhgc;
+}
+
+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
+ && strmatch(pbrms->nhgrp_name, name)) {
+ pbrms->reason |= PBR_MAP_INVALID_NO_NEXTHOPS;
+ 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);
+
+ /*
+ * Ignore deletions of nh we did not / could not allocate nhgc for
+ * Occurs when PBR table range is full but new nhg keep coming in
+ */
+ if (!pnhgc)
+ return;
+
+ /* Remove and recalculate the next table id */
+ hash_release(pbr_nhg_allocated_id_hash, pnhgc);
+ pbr_nht_update_next_unallocated_table_id();
+
+ pbr_nhgc_delete(pnhgc);
+}
+
+bool pbr_nht_nexthop_valid(struct nexthop_group *nhg)
+{
+ DEBUGD(&pbr_dbg_nht, "%s: %p", __func__, 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", __func__, name);
+
+ snprintf(lookup.name, sizeof(lookup.name), "%s", name);
+ pnhgc = hash_get(pbr_nhg_hash, &lookup, NULL);
+ if (!pnhgc)
+ return false;
+ DEBUGD(&pbr_dbg_nht, "%s: %d %d", __func__, pnhgc->valid,
+ pnhgc->installed);
+ if (pnhgc->valid && pnhgc->installed)
+ return true;
+
+ return false;
+}
+
+struct pbr_nht_individual {
+ struct zapi_route *nhr;
+ struct interface *ifp;
+ struct pbr_vrf *pbr_vrf;
+ struct pbr_nexthop_cache *pnhc;
+ vrf_id_t old_vrf_id;
+
+ bool valid;
+
+ bool nhr_matched;
+};
+
+static bool
+pbr_nht_individual_nexthop_gw_update(struct pbr_nexthop_cache *pnhc,
+ struct pbr_nht_individual *pnhi)
+{
+ bool is_valid = pnhc->valid;
+
+ /*
+ * If we have an interface down event, let's note that
+ * it is happening and find all the nexthops that depend
+ * on that interface. As that if we have an interface
+ * flapping fast enough it means that zebra might turn
+ * those nexthop tracking events into a no-update
+ * So let's search and do the right thing on the
+ * interface event.
+ */
+ if (!pnhi->nhr) {
+ switch (pnhc->nexthop.type) {
+ case NEXTHOP_TYPE_BLACKHOLE:
+ case NEXTHOP_TYPE_IPV4:
+ case NEXTHOP_TYPE_IPV6:
+ goto done;
+ case NEXTHOP_TYPE_IFINDEX:
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ if (pnhc->nexthop.ifindex == pnhi->ifp->ifindex)
+ is_valid = if_is_up(pnhi->ifp);
+ goto done;
+ }
+
+ goto done;
+ }
+
+ switch (pnhi->nhr->prefix.family) {
+ case AF_INET:
+ if (pnhc->nexthop.gate.ipv4.s_addr
+ != pnhi->nhr->prefix.u.prefix4.s_addr)
+ goto done; /* Unrelated change */
+ break;
+ case AF_INET6:
+ if (memcmp(&pnhc->nexthop.gate.ipv6,
+ &pnhi->nhr->prefix.u.prefix6, 16)
+ != 0)
+ goto done; /* Unrelated change */
+ break;
+ }
+
+ pnhi->nhr_matched = true;
+ if (!pnhi->nhr->nexthop_num) {
+ is_valid = false;
+ goto done;
+ }
+
+ if (pnhc->nexthop.type == NEXTHOP_TYPE_IPV4_IFINDEX
+ || pnhc->nexthop.type == NEXTHOP_TYPE_IPV6_IFINDEX) {
+
+ /* GATEWAY_IFINDEX type shouldn't resolve to group */
+ if (pnhi->nhr->nexthop_num > 1) {
+ is_valid = false;
+ goto done;
+ }
+
+ /* If whatever we resolved to wasn't on the interface we
+ * specified. (i.e. not a connected route), its invalid.
+ */
+ if (pnhi->nhr->nexthops[0].ifindex != pnhc->nexthop.ifindex) {
+ is_valid = false;
+ goto done;
+ }
+ }
+
+ is_valid = true;
+
+done:
+ pnhc->valid = is_valid;
+
+ return pnhc->valid;
+}
+
+static bool
+pbr_nht_individual_nexthop_interface_update(struct pbr_nexthop_cache *pnhc,
+ struct pbr_nht_individual *pnhi)
+{
+ bool is_valid = pnhc->valid;
+
+ if (!pnhi->ifp) /* It doesn't care about non-interface updates */
+ goto done;
+
+ if (pnhc->nexthop.ifindex
+ != pnhi->ifp->ifindex) /* Un-related interface */
+ goto done;
+
+ pnhi->nhr_matched = true;
+ is_valid = !!if_is_up(pnhi->ifp);
+
+done:
+ pnhc->valid = is_valid;
+
+ return pnhc->valid;
+}
+
+/* Given this update either from interface or nexthop tracking, re-validate this
+ * nexthop.
+ *
+ * If the update is un-related, the subroutines shoud just return their cached
+ * valid state.
+ */
+static void pbr_nht_individual_nexthop_update(struct pbr_nexthop_cache *pnhc,
+ struct pbr_nht_individual *pnhi)
+{
+ assert(pnhi->nhr || pnhi->ifp); /* Either nexthop or interface update */
+
+ switch (pnhc->nexthop.type) {
+ case NEXTHOP_TYPE_IFINDEX:
+ pbr_nht_individual_nexthop_interface_update(pnhc, pnhi);
+ break;
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ if (IN6_IS_ADDR_LINKLOCAL(&pnhc->nexthop.gate.ipv6)) {
+ pbr_nht_individual_nexthop_interface_update(pnhc, pnhi);
+ break;
+ }
+ /* Intentional fall thru */
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ case NEXTHOP_TYPE_IPV4:
+ case NEXTHOP_TYPE_IPV6:
+ pbr_nht_individual_nexthop_gw_update(pnhc, pnhi);
+ break;
+ case NEXTHOP_TYPE_BLACKHOLE:
+ pnhc->valid = true;
+ break;
+ }
+}
+
+static void pbr_nht_individual_nexthop_update_lookup(struct hash_bucket *b,
+ void *data)
+{
+ struct pbr_nexthop_cache *pnhc = b->data;
+ struct pbr_nht_individual *pnhi = data;
+ bool old_valid;
+
+ old_valid = pnhc->valid;
+
+ pbr_nht_individual_nexthop_update(pnhc, pnhi);
+
+ DEBUGD(&pbr_dbg_nht, " Found %pFX: old: %d new: %d",
+ &pnhi->nhr->prefix, old_valid, pnhc->valid);
+
+ if (pnhc->valid)
+ pnhi->valid = true;
+}
+
+static void pbr_nexthop_group_cache_iterate_to_group(struct hash_bucket *b,
+ void *data)
+{
+ struct pbr_nexthop_cache *pnhc = b->data;
+ struct nexthop_group *nhg = data;
+ struct nexthop *nh = NULL;
+
+ copy_nexthops(&nh, &pnhc->nexthop, NULL);
+
+ _nexthop_add(&nhg->nexthop, nh);
+}
+
+static void
+pbr_nexthop_group_cache_to_nexthop_group(struct nexthop_group *nhg,
+ struct pbr_nexthop_group_cache *pnhgc)
+{
+ hash_iterate(pnhgc->nhh, pbr_nexthop_group_cache_iterate_to_group, nhg);
+}
+
+static void pbr_nht_nexthop_update_lookup(struct hash_bucket *b, void *data)
+{
+ struct pbr_nexthop_group_cache *pnhgc = b->data;
+ struct pbr_nht_individual pnhi = {};
+ struct nexthop_group nhg = {};
+ bool old_valid;
+
+ old_valid = pnhgc->valid;
+
+ pnhi.nhr = (struct zapi_route *)data;
+ pnhi.valid = false;
+ pnhi.nhr_matched = false;
+ hash_iterate(pnhgc->nhh, pbr_nht_individual_nexthop_update_lookup,
+ &pnhi);
+
+ if (!pnhi.nhr_matched)
+ return;
+
+ /*
+ * If any of the specified nexthops are valid we are valid
+ */
+ pnhgc->valid = !!pnhi.valid;
+
+ pbr_nexthop_group_cache_to_nexthop_group(&nhg, pnhgc);
+
+ if (pnhgc->valid)
+ pbr_nht_install_nexthop_group(pnhgc, nhg);
+ else
+ pbr_nht_uninstall_nexthop_group(pnhgc, nhg, 0);
+
+ /* Don't need copied nexthops anymore */
+ nexthops_free(nhg.nexthop);
+
+ if (old_valid != pnhgc->valid)
+ pbr_map_check_nh_group_change(pnhgc->name);
+}
+
+void pbr_nht_nexthop_update(struct zapi_route *nhr)
+{
+ hash_iterate(pbr_nhg_hash, pbr_nht_nexthop_update_lookup, nhr);
+}
+
+struct nhrc_vrf_info {
+ struct pbr_vrf *pbr_vrf;
+ uint32_t old_vrf_id;
+ struct nhrc *nhrc;
+};
+
+static int pbr_nht_nhrc_vrf_change(struct hash_bucket *b, void *data)
+{
+ struct nhrc *nhrc = b->data;
+ struct nhrc_vrf_info *nhrcvi = data;
+
+ if (nhrc->nexthop.vrf_id == nhrcvi->old_vrf_id) {
+ nhrcvi->nhrc = nhrc;
+ return HASHWALK_ABORT;
+ }
+
+ return HASHWALK_CONTINUE;
+}
+
+static int pbr_nht_individual_nexthop_vrf_handle(struct hash_bucket *b,
+ void *data)
+{
+ struct pbr_nexthop_cache *pnhc = b->data;
+ struct pbr_nht_individual *pnhi = data;
+
+ if (pnhc->looked_at == true)
+ return HASHWALK_CONTINUE;
+
+ if (pnhc->nexthop.vrf_id == VRF_DEFAULT)
+ return HASHWALK_CONTINUE;
+
+ if (strncmp(pnhc->vrf_name, pbr_vrf_name(pnhi->pbr_vrf),
+ sizeof(pnhc->vrf_name))
+ == 0) {
+ pnhi->pnhc = pnhc;
+
+ if (pnhc->nexthop.vrf_id != pbr_vrf_id(pnhi->pbr_vrf)) {
+ struct nhrc_vrf_info nhrcvi;
+
+ memset(&nhrcvi, 0, sizeof(nhrcvi));
+ nhrcvi.pbr_vrf = pnhi->pbr_vrf;
+ nhrcvi.old_vrf_id = pnhc->nexthop.vrf_id;
+
+ pnhi->nhr_matched = true;
+ pnhi->old_vrf_id = pnhc->nexthop.vrf_id;
+
+ do {
+ nhrcvi.nhrc = NULL;
+ hash_walk(pbr_nhrc_hash,
+ pbr_nht_nhrc_vrf_change, &nhrcvi);
+ if (nhrcvi.nhrc) {
+ hash_release(pbr_nhrc_hash,
+ nhrcvi.nhrc);
+ nhrcvi.nhrc->nexthop.vrf_id =
+ pbr_vrf_id(pnhi->pbr_vrf);
+ (void)hash_get(pbr_nhrc_hash,
+ nhrcvi.nhrc,
+ hash_alloc_intern);
+ pbr_send_rnh(&nhrcvi.nhrc->nexthop, true);
+ }
+ } while (nhrcvi.nhrc);
+ }
+
+ pnhc->looked_at = true;
+ return HASHWALK_ABORT;
+ }
+
+ return HASHWALK_CONTINUE;
+}
+
+static void pbr_nht_clear_looked_at(struct hash_bucket *b, void *data)
+{
+ struct pbr_nexthop_cache *pnhc = b->data;
+
+ pnhc->looked_at = false;
+}
+
+static void pbr_nht_nexthop_vrf_handle(struct hash_bucket *b, void *data)
+{
+ struct pbr_nexthop_group_cache *pnhgc = b->data;
+ struct pbr_vrf *pbr_vrf = data;
+ struct pbr_nht_individual pnhi = {};
+
+ hash_iterate(pnhgc->nhh, pbr_nht_clear_looked_at, NULL);
+ memset(&pnhi, 0, sizeof(pnhi));
+ pnhi.pbr_vrf = pbr_vrf;
+ do {
+ struct pbr_nexthop_cache *pnhc;
+
+ pnhi.pnhc = NULL;
+ hash_walk(pnhgc->nhh, pbr_nht_individual_nexthop_vrf_handle,
+ &pnhi);
+
+ if (!pnhi.pnhc)
+ continue;
+
+ pnhc = pnhi.pnhc;
+ pnhc->nexthop.vrf_id = pnhi.old_vrf_id;
+ pnhi.pnhc = hash_release(pnhgc->nhh, pnhi.pnhc);
+ if (pnhi.pnhc) {
+ pnhi.pnhc->nexthop.vrf_id = pbr_vrf_id(pbr_vrf);
+
+ (void)hash_get(pnhgc->nhh, pnhi.pnhc,
+ hash_alloc_intern);
+ } else
+ pnhc->nexthop.vrf_id = pbr_vrf_id(pbr_vrf);
+
+ pbr_map_check_vrf_nh_group_change(pnhgc->name, pbr_vrf,
+ pnhi.old_vrf_id);
+ } while (pnhi.pnhc);
+}
+
+void pbr_nht_vrf_update(struct pbr_vrf *pbr_vrf)
+{
+ hash_iterate(pbr_nhg_hash, pbr_nht_nexthop_vrf_handle, pbr_vrf);
+}
+
+static void pbr_nht_individual_nexthop_interface_handle(struct hash_bucket *b,
+ void *data)
+{
+ struct pbr_nexthop_cache *pnhc = b->data;
+ struct pbr_nht_individual *pnhi = data;
+
+ if (pnhc->nexthop.ifindex == 0)
+ return;
+
+ if ((strncmp(pnhc->intf_name, pnhi->ifp->name, sizeof(pnhc->intf_name))
+ == 0)
+ && pnhc->nexthop.ifindex != pnhi->ifp->ifindex)
+ pnhi->pnhc = pnhc;
+}
+
+static void pbr_nht_nexthop_interface_handle(struct hash_bucket *b, void *data)
+{
+ struct pbr_nexthop_group_cache *pnhgc = b->data;
+ struct interface *ifp = data;
+ struct pbr_nht_individual pnhi = {};
+ struct nhrc *nhrc;
+ uint32_t old_ifindex;
+
+ do {
+ memset(&pnhi, 0, sizeof(pnhi));
+ pnhi.ifp = ifp;
+ hash_iterate(pnhgc->nhh,
+ pbr_nht_individual_nexthop_interface_handle,
+ &pnhi);
+
+ if (!pnhi.pnhc)
+ continue;
+
+ pnhi.pnhc = hash_release(pnhgc->nhh, pnhi.pnhc);
+ old_ifindex = pnhi.pnhc->nexthop.ifindex;
+
+ nhrc = hash_lookup(pbr_nhrc_hash, &pnhi.pnhc->nexthop);
+ if (nhrc) {
+ hash_release(pbr_nhrc_hash, nhrc);
+ nhrc->nexthop.ifindex = ifp->ifindex;
+ (void)hash_get(pbr_nhrc_hash, nhrc, hash_alloc_intern);
+ }
+ pnhi.pnhc->nexthop.ifindex = ifp->ifindex;
+
+ (void)hash_get(pnhgc->nhh, pnhi.pnhc, hash_alloc_intern);
+
+ pbr_map_check_interface_nh_group_change(pnhgc->name, ifp,
+ old_ifindex);
+ } while (pnhi.pnhc);
+}
+
+void pbr_nht_interface_update(struct interface *ifp)
+{
+ hash_iterate(pbr_nhg_hash, pbr_nht_nexthop_interface_handle, ifp);
+}
+
+static void
+pbr_nht_individual_nexthop_interface_update_lookup(struct hash_bucket *b,
+ void *data)
+{
+ struct pbr_nexthop_cache *pnhc = b->data;
+ struct pbr_nht_individual *pnhi = data;
+ bool old_valid;
+
+ old_valid = pnhc->valid;
+
+ pbr_nht_individual_nexthop_update(pnhc, pnhi);
+
+ DEBUGD(&pbr_dbg_nht, " Found %s: old: %d new: %d", pnhi->ifp->name,
+ old_valid, pnhc->valid);
+
+ if (pnhc->valid)
+ pnhi->valid = true;
+}
+
+static void pbr_nht_nexthop_interface_update_lookup(struct hash_bucket *b,
+ void *data)
+{
+ struct pbr_nexthop_group_cache *pnhgc = b->data;
+ struct pbr_nht_individual pnhi = {};
+ struct nexthop_group nhg = {};
+ bool old_valid;
+
+ old_valid = pnhgc->valid;
+
+ pnhi.ifp = data;
+ pnhi.valid = false;
+ hash_iterate(pnhgc->nhh,
+ pbr_nht_individual_nexthop_interface_update_lookup, &pnhi);
+
+ /*
+ * If any of the specified nexthops are valid we are valid
+ */
+ pnhgc->valid = pnhi.valid;
+
+ pbr_nexthop_group_cache_to_nexthop_group(&nhg, pnhgc);
+
+ if (pnhgc->valid)
+ pbr_nht_install_nexthop_group(pnhgc, nhg);
+ else
+ pbr_nht_uninstall_nexthop_group(pnhgc, nhg, 0);
+
+ nexthops_free(nhg.nexthop);
+
+ if (old_valid != pnhgc->valid)
+ pbr_map_check_nh_group_change(pnhgc->name);
+}
+
+void pbr_nht_nexthop_interface_update(struct interface *ifp)
+{
+ hash_iterate(pbr_nhg_hash, pbr_nht_nexthop_interface_update_lookup,
+ ifp);
+}
+
+static bool pbr_nhg_allocated_id_hash_equal(const void *arg1, const void *arg2)
+{
+ const struct pbr_nexthop_group_cache *left, *right;
+
+ left = (const struct pbr_nexthop_group_cache *)arg1;
+ right = (const struct pbr_nexthop_group_cache *)arg2;
+
+ return left->table_id == right->table_id;
+}
+
+static uint32_t pbr_nhg_allocated_id_hash_key(const void *arg)
+{
+ const struct pbr_nexthop_group_cache *nhgc = arg;
+
+ /* table_id makes elements in this hash unique */
+ return nhgc->table_id;
+}
+
+static uint32_t pbr_nhg_hash_key(const void *arg)
+{
+ const struct pbr_nexthop_group_cache *nhgc = arg;
+
+ return jhash(&nhgc->name, strlen(nhgc->name), 0x52c34a96);
+}
+
+static bool 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_find_next_unallocated_table_id(void)
+{
+ struct pbr_nexthop_group_cache iter;
+
+ /*
+ * Find the smallest unallocated table id
+ * This can be non-trivial considering nhg removals / shifting upper &
+ * lower bounds, so start at the lowest in the range and continue until
+ * an unallocated space is found
+ */
+ for (iter.table_id = pbr_nhg_low_table;
+ iter.table_id < pbr_nhg_high_table; ++iter.table_id)
+ if (!hash_lookup(pbr_nhg_allocated_id_hash, &iter))
+ return iter.table_id;
+
+ /* Configured range is full, cannot install anywhere */
+ return 0;
+}
+
+bool pbr_nht_has_unallocated_table(void)
+{
+ return !!pbr_next_unallocated_table_id;
+}
+
+void pbr_nht_update_next_unallocated_table_id(void)
+{
+ pbr_next_unallocated_table_id =
+ pbr_nht_find_next_unallocated_table_id();
+}
+
+uint32_t pbr_nht_reserve_next_table_id(struct pbr_nexthop_group_cache *nhgc)
+{
+ /* Nothing to reserve if all tables in range already used */
+ if (!pbr_next_unallocated_table_id)
+ return 0;
+
+ /* Reserve this table id */
+ nhgc->table_id = pbr_next_unallocated_table_id;
+
+ /* Mark table id as allocated in id-indexed hash */
+ (void)hash_get(pbr_nhg_allocated_id_hash, nhgc, hash_alloc_intern);
+
+ /* Pre-compute the next unallocated table id */
+ pbr_nht_update_next_unallocated_table_id();
+
+ /* Present caller with reserved table id */
+ return nhgc->table_id;
+}
+
+void pbr_nht_set_tableid_range(uint32_t low, uint32_t high)
+{
+ pbr_nhg_low_table = low;
+ pbr_nhg_high_table = high;
+
+ /* Re-compute next unallocated id within new range */
+ pbr_nht_update_next_unallocated_table_id();
+}
+
+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));
+ snprintf(find.name, sizeof(find.name), "%s", name);
+ pnhgc = hash_lookup(pbr_nhg_hash, &find);
+
+ if (!pnhgc) {
+ DEBUGD(&pbr_dbg_nht,
+ "%s: Could not find nexthop-group cache w/ name '%s'",
+ __func__, 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));
+ snprintf(find.name, sizeof(find.name), "%s", name);
+
+ pnhgc = hash_lookup(pbr_nhg_hash, &find);
+
+ if (!pnhgc)
+ return false;
+
+ return pnhgc->installed;
+}
+
+static void pbr_nht_show_nhg_nexthops(struct hash_bucket *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);
+}
+
+static void pbr_nht_json_nhg_nexthops(struct hash_bucket *b, void *data)
+{
+ struct pbr_nexthop_cache *pnhc = b->data;
+ json_object *all_hops = data;
+ json_object *this_hop;
+
+ this_hop = json_object_new_object();
+ nexthop_group_json_nexthop(this_hop, &pnhc->nexthop);
+ json_object_boolean_add(this_hop, "valid", pnhc->valid);
+
+ json_object_array_add(all_hops, this_hop);
+}
+
+struct pbr_nht_show {
+ struct vty *vty;
+ json_object *json;
+ const char *name;
+};
+
+static void pbr_nht_show_nhg(struct hash_bucket *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);
+}
+
+static void pbr_nht_json_nhg(struct hash_bucket *b, void *data)
+{
+ struct pbr_nexthop_group_cache *pnhgc = b->data;
+ struct pbr_nht_show *pns = data;
+ json_object *j, *this_group, *group_hops;
+
+ if (pns->name && strcmp(pns->name, pnhgc->name) != 0)
+ return;
+
+ j = pns->json;
+ this_group = json_object_new_object();
+
+ if (!j || !this_group)
+ return;
+
+ json_object_int_add(this_group, "id", pnhgc->table_id);
+ json_object_string_add(this_group, "name", pnhgc->name);
+ json_object_boolean_add(this_group, "valid", pnhgc->valid);
+ json_object_boolean_add(this_group, "installed", pnhgc->installed);
+
+ group_hops = json_object_new_array();
+
+ if (group_hops) {
+ hash_iterate(pnhgc->nhh, pbr_nht_json_nhg_nexthops, group_hops);
+ json_object_object_add(this_group, "nexthops", group_hops);
+ }
+
+ json_object_array_add(j, this_group);
+}
+
+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_json_nexthop_group(json_object *j, const char *name)
+{
+ struct pbr_nht_show pns;
+
+ pns.name = name;
+ pns.json = j;
+
+ hash_iterate(pbr_nhg_hash, pbr_nht_json_nhg, &pns);
+}
+
+void pbr_nht_init(void)
+{
+ pbr_nhg_hash = hash_create_size(
+ 16, pbr_nhg_hash_key, pbr_nhg_hash_equal, "PBR NHG Cache Hash");
+ pbr_nhrc_hash =
+ hash_create_size(16, (unsigned int (*)(const void *))nexthop_hash,
+ pbr_nhrc_hash_equal, "PBR NH Hash");
+ pbr_nhg_allocated_id_hash = hash_create_size(
+ 16, pbr_nhg_allocated_id_hash_key,
+ pbr_nhg_allocated_id_hash_equal, "PBR Allocated Table 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;
+
+ /* First unallocated table is lowest in range on init */
+ pbr_next_unallocated_table_id = PBR_NHT_DEFAULT_LOW_TABLEID;
+}
diff --git a/pbrd/pbr_nht.h b/pbrd/pbr_nht.h
new file mode 100644
index 0000000..ecc92cc
--- /dev/null
+++ b/pbrd/pbr_nht.h
@@ -0,0 +1,157 @@
+/*
+ * 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 <lib/zclient.h>
+#include <lib/nexthop_group.h>
+
+#include "pbr_map.h"
+#include "json.h"
+
+#define PBR_NHC_NAMELEN PBR_MAP_NAMELEN + 10
+
+extern struct hash *pbr_nhg_hash;
+
+struct pbr_nexthop_group_cache {
+ char name[PBR_NHC_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;
+
+ char vrf_name[VRF_NAMSIZ + 1];
+ char intf_name[INTERFACE_NAMSIZ + 1];
+
+ struct nexthop nexthop;
+
+ bool looked_at;
+ bool valid;
+ bool nhr_matched;
+};
+
+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);
+
+/*
+ * Find and reserve the next available table for installation;
+ * Sequential calls to this function will reserve sequential table numbers
+ * until the configured range is exhausted; calls made after exhaustion always
+ * return 0
+ */
+extern uint32_t
+pbr_nht_reserve_next_table_id(struct pbr_nexthop_group_cache *nhgc);
+/*
+ * Get the next tableid to use for installation to kernel
+ */
+extern uint32_t pbr_nht_find_next_unallocated_table_id(void);
+/*
+ * Calculate where the next table representing a nhg will go in kernel
+ */
+extern void pbr_nht_update_next_unallocated_table_id(void);
+/*
+ * Indicate if there are free spots to install a table to kernel within the
+ * configured PBR table range
+ */
+extern bool pbr_nht_has_unallocated_table(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 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_set_seq_nhg_data(struct pbr_map_sequence *pbrms,
+ const struct nexthop_group_cmd *nhgc);
+extern void pbr_nht_set_seq_nhg(struct pbr_map_sequence *pbrms,
+ const char *name);
+
+extern void pbr_nht_add_individual_nexthop(struct pbr_map_sequence *pbrms,
+ const struct nexthop *nhop);
+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
+ * 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);
+extern void pbr_nht_json_nexthop_group(json_object *j, const char *name);
+
+/*
+ * When we get a callback from zebra about a nexthop changing
+ */
+extern void pbr_nht_nexthop_update(struct zapi_route *nhr);
+
+/*
+ * When we get a callback from zebra about an interface status update.
+ */
+extern void pbr_nht_nexthop_interface_update(struct interface *ifp);
+
+extern void pbr_nht_init(void);
+
+extern void pbr_nht_vrf_update(struct pbr_vrf *pbr_vrf);
+extern void pbr_nht_interface_update(struct interface *ifp);
+#endif
diff --git a/pbrd/pbr_vrf.c b/pbrd/pbr_vrf.c
new file mode 100644
index 0000000..277269a
--- /dev/null
+++ b/pbrd/pbr_vrf.c
@@ -0,0 +1,139 @@
+/*
+ * PBR - vrf code
+ * Copyright (C) 2019 Cumulus Networks, Inc.
+ * Stephen Worley
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <zebra.h>
+
+#include "vrf.h"
+
+#include "pbr_vrf.h"
+#include "pbr_memory.h"
+#include "pbr_map.h"
+#include "pbr_debug.h"
+#include "pbr_nht.h"
+#include "pbr_zebra.h"
+
+DEFINE_MTYPE_STATIC(PBRD, PBR_MAP_VRF, "PBR Map VRF");
+
+static struct pbr_vrf *pbr_vrf_alloc(void)
+{
+ struct pbr_vrf *pbr_vrf;
+
+ pbr_vrf = XCALLOC(MTYPE_PBR_MAP_VRF, sizeof(struct pbr_vrf));
+
+ return pbr_vrf;
+}
+
+static void pbr_vrf_free(struct pbr_vrf *pbr_vrf)
+{
+ XFREE(MTYPE_PBR_MAP_VRF, pbr_vrf);
+}
+
+static int pbr_vrf_new(struct vrf *vrf)
+{
+ struct pbr_vrf *pbr_vrf;
+
+ DEBUGD(&pbr_dbg_event, "%s: %u (%s)", __func__, vrf->vrf_id, vrf->name);
+
+ pbr_vrf = pbr_vrf_alloc();
+ vrf->info = pbr_vrf;
+ pbr_vrf->vrf = vrf;
+
+ return 0;
+}
+
+static int pbr_vrf_enable(struct vrf *vrf)
+{
+ DEBUGD(&pbr_dbg_event, "%s: %u (%s)", __func__, vrf->vrf_id, vrf->name);
+
+ pbr_nht_vrf_update(vrf->info);
+ pbr_map_vrf_update(vrf->info);
+
+ return 0;
+}
+
+static int pbr_vrf_disable(struct vrf *vrf)
+{
+ DEBUGD(&pbr_dbg_event, "%s: %u (%s)", __func__, vrf->vrf_id, vrf->name);
+
+ pbr_map_vrf_update(vrf->info);
+
+ return 0;
+}
+
+static int pbr_vrf_delete(struct vrf *vrf)
+{
+ DEBUGD(&pbr_dbg_event, "%s: %u (%s)", __func__, vrf->vrf_id, vrf->name);
+
+ /*
+ * Make sure vrf is always marked disabled first so we handle
+ * pbr rules using it.
+ */
+ assert(!vrf_is_enabled(vrf));
+
+ pbr_vrf_free(vrf->info);
+ vrf->info = NULL;
+
+ return 0;
+}
+
+struct pbr_vrf *pbr_vrf_lookup_by_name(const char *name)
+{
+ struct vrf *vrf;
+
+ if (!name)
+ name = VRF_DEFAULT_NAME;
+
+ vrf = vrf_lookup_by_name(name);
+ if (vrf)
+ return ((struct pbr_vrf *)vrf->info);
+
+ return NULL;
+}
+
+bool pbr_vrf_is_enabled(const struct pbr_vrf *pbr_vrf)
+{
+ return vrf_is_enabled(pbr_vrf->vrf) ? true : false;
+}
+
+bool pbr_vrf_is_valid(const struct pbr_vrf *pbr_vrf)
+{
+ if (vrf_is_backend_netns())
+ return false;
+
+ if (!pbr_vrf->vrf)
+ return false;
+
+ return pbr_vrf_is_enabled(pbr_vrf);
+}
+
+void pbr_vrf_init(void)
+{
+ vrf_init(pbr_vrf_new, pbr_vrf_enable, pbr_vrf_disable, pbr_vrf_delete);
+}
+
+void pbr_vrf_terminate(void)
+{
+ struct vrf *vrf;
+ struct interface *ifp;
+
+ RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
+ FOR_ALL_INTERFACES (vrf, ifp)
+ pbr_if_del(ifp);
+ }
+}
diff --git a/pbrd/pbr_vrf.h b/pbrd/pbr_vrf.h
new file mode 100644
index 0000000..e37bcd4
--- /dev/null
+++ b/pbrd/pbr_vrf.h
@@ -0,0 +1,43 @@
+/*
+ * VRF library for PBR
+ * Copyright (C) 2019 Cumulus Networks, Inc.
+ * Stephen Worley
+ *
+ * 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_VRF_H__
+#define __PBR_VRF_H__
+
+struct pbr_vrf {
+ struct vrf *vrf;
+};
+
+static inline const char *pbr_vrf_name(const struct pbr_vrf *pbr_vrf)
+{
+ return pbr_vrf->vrf->name;
+}
+
+static inline vrf_id_t pbr_vrf_id(const struct pbr_vrf *pbr_vrf)
+{
+ return pbr_vrf->vrf->vrf_id;
+}
+
+extern struct pbr_vrf *pbr_vrf_lookup_by_name(const char *name);
+extern bool pbr_vrf_is_valid(const struct pbr_vrf *pbr_vrf);
+extern bool pbr_vrf_is_enabled(const struct pbr_vrf *pbr_vrf);
+
+extern void pbr_vrf_init(void);
+extern void pbr_vrf_terminate(void);
+#endif
diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c
new file mode 100644
index 0000000..60e215e
--- /dev/null
+++ b/pbrd/pbr_vty.c
@@ -0,0 +1,1432 @@
+/*
+ * PBR - vty code
+ * Copyright (C) 2018 Cumulus Networks, Inc.
+ * Donald Sharp
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <zebra.h>
+
+#include "vty.h"
+#include "command.h"
+#include "prefix.h"
+#include "vrf.h"
+#include "nexthop.h"
+#include "nexthop_group.h"
+#include "nexthop_group_private.h"
+#include "log.h"
+#include "json.h"
+#include "debug.h"
+#include "pbr.h"
+
+#include "pbrd/pbr_nht.h"
+#include "pbrd/pbr_map.h"
+#include "pbrd/pbr_zebra.h"
+#include "pbrd/pbr_vty.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 PBRMAP seq (1-700)",
+ "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 PBRMAP [seq (1-700)]",
+ 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_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;
+ }
+
+ for (ALL_LIST_ELEMENTS(pbrm->seqnumbers, node, next_node, pbrms)) {
+ if (seqno && pbrms->seqno != seqno)
+ continue;
+
+ pbr_map_delete(pbrms);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(pbr_set_table_range,
+ pbr_set_table_range_cmd,
+ "pbr table range (10000-4294966272)$lb (10000-4294966272)$ub",
+ PBR_STR
+ "Set table ID range\n"
+ "Set table ID range\n"
+ "Lower bound for table ID range\n"
+ "Upper bound for table ID range\n")
+{
+ /* upper bound is 2^32 - 2^10 */
+ int ret = CMD_WARNING;
+ const int minrange = 1000;
+
+ /* validate given bounds */
+ if (lb > ub)
+ vty_out(vty, "%% Lower bound must be less than upper bound\n");
+ else if (ub - lb < minrange)
+ vty_out(vty, "%% Range breadth must be at least %d\n", minrange);
+ else {
+ ret = CMD_SUCCESS;
+ pbr_nht_set_tableid_range((uint32_t) lb, (uint32_t) ub);
+ }
+
+ return ret;
+}
+
+DEFPY(no_pbr_set_table_range, no_pbr_set_table_range_cmd,
+ "no pbr table range [(10000-4294966272)$lb (10000-4294966272)$ub]",
+ NO_STR
+ PBR_STR
+ "Set table ID range\n"
+ "Set table ID range\n"
+ "Lower bound for table ID range\n"
+ "Upper bound for table ID range\n")
+{
+ pbr_nht_set_tableid_range(PBR_NHT_DEFAULT_LOW_TABLEID,
+ PBR_NHT_DEFAULT_HIGH_TABLEID);
+ return CMD_SUCCESS;
+}
+
+DEFPY(pbr_map_match_src, pbr_map_match_src_cmd,
+ "[no] match src-ip <A.B.C.D/M|X:X::X:X/M>$prefix",
+ NO_STR
+ "Match the rest of the command\n"
+ "Choose the src ip or ipv6 prefix to use\n"
+ "v4 Prefix\n"
+ "v6 Prefix\n")
+{
+ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
+
+ if (!pbrms)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (pbrms->dst && prefix->family != pbrms->dst->family) {
+ vty_out(vty, "Cannot mismatch families within match src/dst\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ pbrms->family = prefix->family;
+
+ if (!no) {
+ if (pbrms->src) {
+ if (prefix_same(pbrms->src, prefix))
+ return CMD_SUCCESS;
+ } else
+ pbrms->src = prefix_new();
+
+ prefix_copy(pbrms->src, prefix);
+ } else
+ prefix_free(&pbrms->src);
+
+ pbr_map_check(pbrms, true);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(pbr_map_match_dst, pbr_map_match_dst_cmd,
+ "[no] match dst-ip <A.B.C.D/M|X:X::X:X/M>$prefix",
+ NO_STR
+ "Match the rest of the command\n"
+ "Choose the dst ip or ipv6 prefix to use\n"
+ "v4 Prefix\n"
+ "v6 Prefix\n")
+{
+ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
+
+ if (!pbrms)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (pbrms->src && prefix->family != pbrms->src->family) {
+ vty_out(vty, "Cannot mismatch families within match src/dst\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ pbrms->family = prefix->family;
+
+ if (!no) {
+ if (pbrms->dst) {
+ if (prefix_same(pbrms->dst, prefix))
+ return CMD_SUCCESS;
+ } else
+ pbrms->dst = prefix_new();
+
+ prefix_copy(pbrms->dst, prefix);
+ } else
+ prefix_free(&pbrms->dst);
+
+ pbr_map_check(pbrms, true);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(pbr_map_match_ip_proto, pbr_map_match_ip_proto_cmd,
+ "[no] match ip-protocol [tcp|udp]$ip_proto",
+ NO_STR
+ "Match the rest of the command\n"
+ "Choose an ip-protocol\n"
+ "Match on tcp flows\n"
+ "Match on udp flows\n")
+{
+ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
+ struct protoent *p;
+
+ if (!pbrms)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (!no) {
+ p = getprotobyname(ip_proto);
+ if (!p) {
+ vty_out(vty, "Unable to convert %s to proto id\n",
+ ip_proto);
+ return CMD_WARNING;
+ }
+
+ pbrms->ip_proto = p->p_proto;
+ } else
+ pbrms->ip_proto = 0;
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(pbr_map_match_src_port, pbr_map_match_src_port_cmd,
+ "[no] match src-port (1-65535)$port",
+ NO_STR
+ "Match the rest of the command\n"
+ "Choose the source port to use\n"
+ "The Source Port\n")
+{
+ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
+
+ if (!pbrms)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (!no) {
+ if (pbrms->src_prt == port)
+ return CMD_SUCCESS;
+ else
+ pbrms->src_prt = port;
+ } else
+ pbrms->src_prt = 0;
+
+ pbr_map_check(pbrms, true);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(pbr_map_match_dst_port, pbr_map_match_dst_port_cmd,
+ "[no] match dst-port (1-65535)$port",
+ NO_STR
+ "Match the rest of the command\n"
+ "Choose the destination port to use\n"
+ "The Destination Port\n")
+{
+ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
+
+ if (!pbrms)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (!no) {
+ if (pbrms->dst_prt == port)
+ return CMD_SUCCESS;
+ else
+ pbrms->dst_prt = port;
+ } else
+ pbrms->dst_prt = 0;
+
+ pbr_map_check(pbrms, true);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(pbr_map_match_dscp, pbr_map_match_dscp_cmd,
+ "[no] match dscp DSCP$dscp",
+ NO_STR
+ "Match the rest of the command\n"
+ "Match based on IP DSCP field\n"
+ "DSCP value (below 64) or standard codepoint name\n")
+{
+ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
+ char dscpname[100];
+ uint8_t rawDscp;
+
+ if (!pbrms)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ /* Discriminate dscp enums (cs0, cs1 etc.) and numbers */
+ bool isANumber = true;
+ for (int i = 0; i < (int)strlen(dscp); i++) {
+ /* Letters are not numbers */
+ if (!isdigit(dscp[i]))
+ isANumber = false;
+
+ /* Lowercase the dscp enum (if needed) */
+ if (isupper(dscp[i]))
+ dscpname[i] = tolower(dscp[i]);
+ else
+ dscpname[i] = dscp[i];
+ }
+ dscpname[strlen(dscp)] = '\0';
+
+ if (isANumber) {
+ /* dscp passed is a regular number */
+ long dscpAsNum = strtol(dscp, NULL, 0);
+
+ if (dscpAsNum > PBR_DSFIELD_DSCP >> 2) {
+ /* Refuse to install on overflow */
+ vty_out(vty, "dscp (%s) must be less than 64\n", dscp);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ rawDscp = dscpAsNum;
+ } else {
+ /* check dscp if it is an enum like cs0 */
+ rawDscp = pbr_map_decode_dscp_enum(dscpname);
+ if (rawDscp > PBR_DSFIELD_DSCP) {
+ vty_out(vty, "Invalid dscp value: %s\n", dscpname);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ }
+
+ if (!no) {
+ if (((pbrms->dsfield & PBR_DSFIELD_DSCP) >> 2) == rawDscp)
+ return CMD_SUCCESS;
+
+ /* Set the DSCP bits of the DSField */
+ pbrms->dsfield =
+ (pbrms->dsfield & ~PBR_DSFIELD_DSCP) | (rawDscp << 2);
+ } else {
+ pbrms->dsfield &= ~PBR_DSFIELD_DSCP;
+ }
+
+ pbr_map_check(pbrms, true);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(pbr_map_match_ecn, pbr_map_match_ecn_cmd,
+ "[no] match ecn (0-3)$ecn",
+ NO_STR
+ "Match the rest of the command\n"
+ "Match based on IP ECN field\n"
+ "Explicit Congestion Notification\n")
+{
+ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
+
+ if (!pbrms)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (!no) {
+ if ((pbrms->dsfield & PBR_DSFIELD_ECN) == ecn)
+ return CMD_SUCCESS;
+
+ /* Set the ECN bits of the DSField */
+ pbrms->dsfield = (pbrms->dsfield & ~PBR_DSFIELD_ECN) | ecn;
+ } else {
+ pbrms->dsfield &= ~PBR_DSFIELD_ECN;
+ }
+
+ pbr_map_check(pbrms, true);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(pbr_map_match_mark, pbr_map_match_mark_cmd,
+ "[no] match mark (1-4294967295)$mark",
+ NO_STR
+ "Match the rest of the command\n"
+ "Choose the mark value to use\n"
+ "mark\n")
+{
+ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
+
+ if (!pbrms)
+ return CMD_WARNING_CONFIG_FAILED;
+
+#ifndef GNU_LINUX
+ vty_out(vty, "pbr marks are not supported on this platform\n");
+ return CMD_WARNING_CONFIG_FAILED;
+#endif
+
+ if (!no) {
+ if (pbrms->mark)
+ if (pbrms->mark == (uint32_t)mark)
+ return CMD_SUCCESS;
+
+ pbrms->mark = (uint32_t)mark;
+ } else
+ pbrms->mark = 0;
+
+ pbr_map_check(pbrms, true);
+
+ return CMD_SUCCESS;
+}
+
+static void pbrms_clear_set_vrf_config(struct pbr_map_sequence *pbrms)
+{
+ if (pbrms->vrf_lookup || pbrms->vrf_unchanged) {
+ pbr_map_delete_vrf(pbrms);
+ pbrms->vrf_name[0] = '\0';
+ pbrms->vrf_lookup = false;
+ pbrms->vrf_unchanged = false;
+ }
+}
+
+static void pbrms_clear_set_nhg_config(struct pbr_map_sequence *pbrms)
+{
+ if (pbrms->nhgrp_name)
+ pbr_map_delete_nexthops(pbrms);
+}
+
+static void pbrms_clear_set_nexthop_config(struct pbr_map_sequence *pbrms)
+{
+ if (pbrms->nhg)
+ pbr_nht_delete_individual_nexthop(pbrms);
+}
+
+static void pbrms_clear_set_config(struct pbr_map_sequence *pbrms)
+{
+ pbrms_clear_set_vrf_config(pbrms);
+ pbrms_clear_set_nhg_config(pbrms);
+ pbrms_clear_set_nexthop_config(pbrms);
+
+ pbrms->nhs_installed = false;
+}
+
+
+DEFPY(pbr_map_action_queue_id, pbr_map_action_queue_id_cmd,
+ "[no] set queue-id <(1-65535)$queue_id>",
+ NO_STR
+ "Set the rest of the command\n"
+ "Set based on egress port queue id\n"
+ "A valid value in range 1..65535 \n")
+{
+ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
+
+ if (!pbrms)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (!no)
+ pbrms->action_queue_id = queue_id;
+ else if ((uint32_t)queue_id == pbrms->action_queue_id)
+ pbrms->action_queue_id = PBR_MAP_UNDEFINED_QUEUE_ID;
+
+ pbr_map_check(pbrms, true);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(pbr_map_action_pcp, pbr_map_action_pcp_cmd, "[no] set pcp <(0-7)$pcp>",
+ NO_STR
+ "Set the rest of the command\n"
+ "Set based on 802.1p Priority Code Point (PCP) value\n"
+ "A valid value in range 0..7\n")
+{
+ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
+
+ if (!pbrms)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (!no)
+ pbrms->action_pcp = pcp;
+ else if (pcp == pbrms->action_pcp)
+ pbrms->action_pcp = 0;
+
+ pbr_map_check(pbrms, true);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(pbr_map_action_vlan_id, pbr_map_action_vlan_id_cmd,
+ "[no] set vlan <(1-4094)$vlan_id>",
+ NO_STR
+ "Set the rest of the command\n"
+ "Set action for VLAN tagging\n"
+ "A valid value in range 1..4094\n")
+{
+ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
+
+ if (!pbrms)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (!no)
+ pbrms->action_vlan_id = vlan_id;
+ else if (pbrms->action_vlan_id == vlan_id)
+ pbrms->action_vlan_id = 0;
+
+ pbr_map_check(pbrms, true);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(pbr_map_action_strip_vlan, pbr_map_action_strip_vlan_cmd,
+ "[no] strip vlan",
+ NO_STR
+ "Strip the vlan tags from frame\n"
+ "Strip any inner vlan tag \n")
+{
+ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
+
+ if (!pbrms)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (!no)
+ pbrms->action_vlan_flags = PBR_MAP_STRIP_INNER_ANY;
+ else
+ pbrms->action_vlan_flags = 0;
+
+ pbr_map_check(pbrms, true);
+
+ return CMD_SUCCESS;
+}
+
+
+DEFPY(pbr_map_nexthop_group, pbr_map_nexthop_group_cmd,
+ "set nexthop-group NHGNAME$name",
+ "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;
+
+ if (!pbrms)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ 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 (pbrms->nhgrp_name && strcmp(name, pbrms->nhgrp_name) == 0)
+ return CMD_SUCCESS;
+
+ /* This is new/replacement config */
+ pbrms_clear_set_config(pbrms);
+
+ pbr_nht_set_seq_nhg(pbrms, name);
+
+ pbr_map_check(pbrms, true);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(no_pbr_map_nexthop_group, no_pbr_map_nexthop_group_cmd,
+ "no set nexthop-group [NHGNAME$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);
+
+ if (!pbrms)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ pbrms_clear_set_config(pbrms);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd,
+ "set nexthop\
+ <\
+ <A.B.C.D|X:X::X:X>$addr [INTERFACE$intf]\
+ |INTERFACE$intf\
+ >\
+ [nexthop-vrf NAME$vrf_name]",
+ "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"
+ "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 = NULL;
+
+ if (!pbrms)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (vrf_name)
+ vrf = vrf_lookup_by_name(vrf_name);
+ else
+ vrf = vrf_lookup_by_id(VRF_DEFAULT);
+
+ if (!vrf) {
+ vty_out(vty, "Specified VRF: %s is non-existent\n", vrf_name);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ memset(&nhop, 0, sizeof(nhop));
+ nhop.vrf_id = vrf->vrf_id;
+
+ if (intf) {
+ struct interface *ifp = NULL;
+ struct interface *ifptmp;
+ struct vrf *vrftmp;
+ int count = 0;
+
+ if (vrf_is_backend_netns() && vrf_name) {
+ ifp = if_lookup_by_name_vrf(intf, vrf);
+ } else {
+ RB_FOREACH (vrftmp, vrf_name_head, &vrfs_by_name) {
+ ifptmp = if_lookup_by_name_vrf(intf, vrftmp);
+ if (ifptmp) {
+ ifp = ifptmp;
+ count++;
+ if (!vrf_is_backend_netns())
+ break;
+ }
+ }
+ }
+
+ if (!ifp) {
+ vty_out(vty, "Specified Intf %s does not exist\n",
+ intf);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ if (count > 1) {
+ vty_out(vty,
+ "Specified Intf %s exists in multiple VRFs\n",
+ intf);
+ vty_out(vty, "You must specify the nexthop-vrf\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ if (ifp->vrf->vrf_id != vrf->vrf_id)
+ vty_out(vty,
+ "Specified Intf %s is not in vrf %s but is in vrf %s, using actual vrf\n",
+ ifp->name, vrf->name, ifp->vrf->name);
+ nhop.ifindex = ifp->ifindex;
+ nhop.vrf_id = ifp->vrf->vrf_id;
+ }
+
+ if (addr) {
+ 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;
+ else
+ nhop.type = NEXTHOP_TYPE_IPV4;
+ } else {
+ nhop.gate.ipv6 = addr->sin6.sin6_addr;
+ if (intf)
+ nhop.type = NEXTHOP_TYPE_IPV6_IFINDEX;
+ else {
+ if (IN6_IS_ADDR_LINKLOCAL(&nhop.gate.ipv6)) {
+ vty_out(vty,
+ "Specified a v6 LL with no interface, rejecting\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ nhop.type = NEXTHOP_TYPE_IPV6;
+ }
+ }
+ } else
+ nhop.type = NEXTHOP_TYPE_IFINDEX;
+
+ if (pbrms->nhg)
+ nh = nexthop_exists(pbrms->nhg, &nhop);
+
+ if (nh) /* Same config re-entered */
+ goto done;
+
+ /* This is new/replacement config */
+ pbrms_clear_set_config(pbrms);
+
+ pbr_nht_add_individual_nexthop(pbrms, &nhop);
+
+ pbr_map_check(pbrms, true);
+
+done:
+ if (nhop.type == NEXTHOP_TYPE_IFINDEX
+ || (nhop.type == NEXTHOP_TYPE_IPV6_IFINDEX
+ && IN6_IS_ADDR_LINKLOCAL(&nhop.gate.ipv6))) {
+ struct interface *ifp;
+
+ ifp = if_lookup_by_index(nhop.ifindex, nhop.vrf_id);
+ if (ifp)
+ pbr_nht_nexthop_interface_update(ifp);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(no_pbr_map_nexthop, no_pbr_map_nexthop_cmd,
+ "no set nexthop\
+ [<\
+ <A.B.C.D|X:X::X:X>$addr [INTERFACE$intf]\
+ |INTERFACE$intf\
+ >\
+ [nexthop-vrf NAME$vrf_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"
+ "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);
+
+ if (!pbrms)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ pbrms_clear_set_config(pbrms);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(pbr_map_vrf, pbr_map_vrf_cmd,
+ "set vrf <NAME$vrf_name|unchanged>",
+ "Set for the PBR-MAP\n"
+ "Specify the VRF for this map\n"
+ "The VRF Name\n"
+ "Use the interface's VRF for lookup\n")
+{
+ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
+
+ if (!pbrms)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ /*
+ * If an equivalent set vrf * exists, just return success.
+ */
+ if (vrf_name && pbrms->vrf_lookup
+ && strncmp(pbrms->vrf_name, vrf_name, sizeof(pbrms->vrf_name)) == 0)
+ return CMD_SUCCESS;
+ else if (!vrf_name && pbrms->vrf_unchanged) /* Unchanged already set */
+ return CMD_SUCCESS;
+
+ if (vrf_name && !pbr_vrf_lookup_by_name(vrf_name)) {
+ vty_out(vty, "Specified: %s is non-existent\n", vrf_name);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /* This is new/replacement config */
+ pbrms_clear_set_config(pbrms);
+
+ if (vrf_name) {
+ pbrms->vrf_lookup = true;
+ strlcpy(pbrms->vrf_name, vrf_name, sizeof(pbrms->vrf_name));
+ } else
+ pbrms->vrf_unchanged = true;
+
+ pbr_map_check(pbrms, true);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(no_pbr_map_vrf, no_pbr_map_vrf_cmd,
+ "no set vrf [<NAME$vrf_name|unchanged>]",
+ NO_STR
+ "Set for the PBR-MAP\n"
+ "Specify the VRF for this map\n"
+ "The VRF Name\n"
+ "Use the interface's VRF for lookup\n")
+{
+ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
+
+ if (!pbrms)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ pbrms_clear_set_config(pbrms);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (pbr_policy,
+ pbr_policy_cmd,
+ "[no] pbr-policy PBRMAP$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;
+
+ old_pbrm = NULL;
+ pbrm = pbrm_find(mapname);
+
+ if (!pbr_ifp) {
+ /* we don't want one and we don't have one, so... */
+ if (no)
+ return CMD_SUCCESS;
+
+ /* Some one could have fat fingered the interface name */
+ pbr_ifp = pbr_if_new(ifp);
+ }
+
+ if (no) {
+ if (strcmp(pbr_ifp->mapname, mapname) == 0) {
+ pbr_ifp->mapname[0] = '\0';
+ if (pbrm)
+ pbr_map_interface_delete(pbrm, ifp);
+ }
+ } else {
+ if (strcmp(pbr_ifp->mapname, "") != 0) {
+ old_pbrm = pbrm_find(pbr_ifp->mapname);
+
+ /*
+ * So if we have an old pbrm we should only
+ * delete it if we are actually deleting and
+ * moving to a new pbrm
+ */
+ if (old_pbrm && old_pbrm != pbrm)
+ pbr_map_interface_delete(old_pbrm, ifp);
+ }
+ snprintf(pbr_ifp->mapname, sizeof(pbr_ifp->mapname),
+ "%s", mapname);
+
+ /*
+ * So only reinstall if the old_pbrm and this pbrm are
+ * different.
+ */
+ if (pbrm && pbrm != old_pbrm)
+ pbr_map_add_interface(pbrm, ifp);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (show_pbr,
+ show_pbr_cmd,
+ "show pbr",
+ SHOW_STR
+ PBR_STR)
+{
+ pbr_nht_write_table_range(vty);
+ pbr_nht_write_rule_range(vty);
+
+ return CMD_SUCCESS;
+}
+
+static void
+pbrms_nexthop_group_write_individual_nexthop(
+ struct vty *vty, const struct pbr_map_sequence *pbrms)
+{
+ struct pbr_nexthop_group_cache find;
+ struct pbr_nexthop_group_cache *pnhgc;
+ struct pbr_nexthop_cache lookup;
+ struct pbr_nexthop_cache *pnhc;
+
+ memset(&find, 0, sizeof(find));
+ strlcpy(find.name, pbrms->internal_nhg_name, sizeof(find.name));
+
+ pnhgc = hash_lookup(pbr_nhg_hash, &find);
+ assert(pnhgc);
+
+ lookup.nexthop = *pbrms->nhg->nexthop;
+ pnhc = hash_lookup(pnhgc->nhh, &lookup);
+
+ nexthop_group_write_nexthop_simple(
+ vty, pbrms->nhg->nexthop,
+ pnhc->nexthop.ifindex != 0 ? pnhc->intf_name : NULL);
+ if (pnhc->nexthop.vrf_id != VRF_DEFAULT)
+ vty_out(vty, " nexthop-vrf %s", pnhc->vrf_name);
+
+ vty_out(vty, "\n");
+}
+
+static void vty_show_pbrms(struct vty *vty,
+ const struct pbr_map_sequence *pbrms, bool detail)
+{
+ char rbuf[64];
+
+ if (pbrms->reason)
+ pbr_map_reason_string(pbrms->reason, rbuf, sizeof(rbuf));
+
+ vty_out(vty, " Seq: %u rule: %u\n", pbrms->seqno, pbrms->ruleno);
+
+ if (detail)
+ vty_out(vty, " Installed: %" PRIu64 "(%u) Reason: %s\n",
+ pbrms->installed, pbrms->unique,
+ pbrms->reason ? rbuf : "Valid");
+ else
+ vty_out(vty, " Installed: %s Reason: %s\n",
+ pbrms->installed ? "yes" : "no",
+ pbrms->reason ? rbuf : "Valid");
+
+ if (pbrms->ip_proto) {
+ struct protoent *p;
+
+ p = getprotobynumber(pbrms->ip_proto);
+ vty_out(vty, " IP Protocol Match: %s\n", p->p_name);
+ }
+
+ if (pbrms->src)
+ vty_out(vty, " SRC IP Match: %pFX\n", pbrms->src);
+ if (pbrms->dst)
+ vty_out(vty, " DST IP Match: %pFX\n", pbrms->dst);
+ if (pbrms->src_prt)
+ vty_out(vty, " SRC Port Match: %u\n", pbrms->src_prt);
+ if (pbrms->dst_prt)
+ vty_out(vty, " DST Port Match: %u\n", pbrms->dst_prt);
+ if (pbrms->dsfield & PBR_DSFIELD_DSCP)
+ vty_out(vty, " DSCP Match: %u\n",
+ (pbrms->dsfield & PBR_DSFIELD_DSCP) >> 2);
+ if (pbrms->dsfield & PBR_DSFIELD_ECN)
+ vty_out(vty, " ECN Match: %u\n",
+ pbrms->dsfield & PBR_DSFIELD_ECN);
+ if (pbrms->mark)
+ vty_out(vty, " MARK Match: %u\n", pbrms->mark);
+
+ if (pbrms->action_queue_id != PBR_MAP_UNDEFINED_QUEUE_ID)
+ vty_out(vty, " Set Queue ID %u\n",
+ pbrms->action_queue_id);
+
+ if (pbrms->action_vlan_id != 0)
+ vty_out(vty, " Set VLAN ID %u\n", pbrms->action_vlan_id);
+ if (pbrms->action_vlan_flags == PBR_MAP_STRIP_INNER_ANY)
+ vty_out(vty, " Strip VLAN ID\n");
+ if (pbrms->action_pcp)
+ vty_out(vty, " Set PCP %u\n", pbrms->action_pcp);
+
+
+ if (pbrms->nhgrp_name) {
+ vty_out(vty, " Nexthop-Group: %s\n", pbrms->nhgrp_name);
+
+ if (detail)
+ vty_out(vty,
+ " Installed: %u(%d) Tableid: %u\n",
+ pbrms->nhs_installed,
+ pbr_nht_get_installed(pbrms->nhgrp_name),
+ pbr_nht_get_table(pbrms->nhgrp_name));
+ else
+ vty_out(vty, " Installed: %s Tableid: %u\n",
+ pbr_nht_get_installed(pbrms->nhgrp_name) ? "yes"
+ : "no",
+ pbr_nht_get_table(pbrms->nhgrp_name));
+
+ } else if (pbrms->nhg) {
+ vty_out(vty, " ");
+ pbrms_nexthop_group_write_individual_nexthop(vty, pbrms);
+ if (detail)
+ vty_out(vty,
+ " Installed: %u(%d) Tableid: %u\n",
+ pbrms->nhs_installed,
+ pbr_nht_get_installed(pbrms->internal_nhg_name),
+ pbr_nht_get_table(pbrms->internal_nhg_name));
+ else
+ vty_out(vty, " Installed: %s Tableid: %u\n",
+ pbr_nht_get_installed(pbrms->internal_nhg_name)
+ ? "yes"
+ : "no",
+ pbr_nht_get_table(pbrms->internal_nhg_name));
+
+ } else if (pbrms->vrf_unchanged) {
+ vty_out(vty, " VRF Unchanged (use interface vrf)\n");
+ } else if (pbrms->vrf_lookup) {
+ vty_out(vty, " VRF Lookup: %s\n", pbrms->vrf_name);
+ } else {
+ vty_out(vty, " Nexthop-Group: Unknown Installed: no\n");
+ }
+}
+
+static void vty_json_pbrms(json_object *j, struct vty *vty,
+ const struct pbr_map_sequence *pbrms)
+{
+ json_object *jpbrm, *nexthop_group;
+ char *nhg_name = pbrms->nhgrp_name ? pbrms->nhgrp_name
+ : pbrms->internal_nhg_name;
+ char rbuf[64];
+
+ jpbrm = json_object_new_object();
+
+ json_object_int_add(jpbrm, "id", pbrms->unique);
+
+ if (pbrms->reason)
+ pbr_map_reason_string(pbrms->reason, rbuf, sizeof(rbuf));
+
+ json_object_int_add(jpbrm, "sequenceNumber", pbrms->seqno);
+ json_object_int_add(jpbrm, "ruleNumber", pbrms->ruleno);
+ json_object_boolean_add(jpbrm, "vrfUnchanged", pbrms->vrf_unchanged);
+ json_object_boolean_add(jpbrm, "installed",
+ pbr_nht_get_installed(nhg_name));
+ json_object_string_add(jpbrm, "installedReason",
+ pbrms->reason ? rbuf : "Valid");
+
+ if (nhg_name) {
+ nexthop_group = json_object_new_object();
+
+ json_object_int_add(nexthop_group, "tableId",
+ pbr_nht_get_table(nhg_name));
+ json_object_string_add(nexthop_group, "name", nhg_name);
+ json_object_boolean_add(nexthop_group, "installed",
+ pbr_nht_get_installed(nhg_name));
+ json_object_int_add(nexthop_group, "installedInternally",
+ pbrms->nhs_installed);
+
+ json_object_object_add(jpbrm, "nexthopGroup", nexthop_group);
+ }
+
+ if (pbrms->vrf_lookup)
+ json_object_string_add(jpbrm, "vrfName", pbrms->vrf_name);
+
+ if (pbrms->src)
+ json_object_string_addf(jpbrm, "matchSrc", "%pFX", pbrms->src);
+ if (pbrms->dst)
+ json_object_string_addf(jpbrm, "matchDst", "%pFX", pbrms->dst);
+ if (pbrms->mark)
+ json_object_int_add(jpbrm, "matchMark", pbrms->mark);
+ if (pbrms->dsfield & PBR_DSFIELD_DSCP)
+ json_object_int_add(jpbrm, "matchDscp",
+ (pbrms->dsfield & PBR_DSFIELD_DSCP) >> 2);
+ if (pbrms->dsfield & PBR_DSFIELD_ECN)
+ json_object_int_add(jpbrm, "matchEcn",
+ pbrms->dsfield & PBR_DSFIELD_ECN);
+
+ json_object_array_add(j, jpbrm);
+}
+
+static void vty_show_pbr_map(struct vty *vty, const struct pbr_map *pbrm,
+ bool detail)
+{
+ struct pbr_map_sequence *pbrms;
+ struct listnode *node;
+
+ vty_out(vty, " pbr-map %s valid: %s\n", pbrm->name,
+ pbrm->valid ? "yes" : "no");
+
+ for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms))
+ vty_show_pbrms(vty, pbrms, detail);
+}
+
+static void vty_json_pbr_map(json_object *j, struct vty *vty,
+ const struct pbr_map *pbrm)
+{
+ struct pbr_map_sequence *pbrms;
+ struct listnode *node;
+ json_object *jpbrms;
+
+ json_object_string_add(j, "name", pbrm->name);
+ json_object_boolean_add(j, "valid", pbrm->valid);
+
+ jpbrms = json_object_new_array();
+
+ for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms))
+ vty_json_pbrms(jpbrms, vty, pbrms);
+
+ json_object_object_add(j, "policies", jpbrms);
+}
+
+DEFPY (show_pbr_map,
+ show_pbr_map_cmd,
+ "show pbr map [NAME$name] [detail$detail|json$json]",
+ SHOW_STR
+ PBR_STR
+ "PBR Map\n"
+ "PBR Map Name\n"
+ "Detailed information\n"
+ JSON_STR)
+{
+ struct pbr_map *pbrm;
+ json_object *j = NULL;
+
+ if (json)
+ j = json_object_new_array();
+
+ RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) {
+ json_object *this_map = NULL;
+ if (name && strcmp(name, pbrm->name) != 0)
+ continue;
+
+ if (j)
+ this_map = json_object_new_object();
+
+ if (this_map) {
+ vty_json_pbr_map(this_map, vty, pbrm);
+
+ json_object_array_add(j, this_map);
+ continue;
+ }
+
+ vty_show_pbr_map(vty, pbrm, detail);
+ }
+
+ if (j)
+ vty_json(vty, j);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(show_pbr_nexthop_group,
+ show_pbr_nexthop_group_cmd,
+ "show pbr nexthop-groups [WORD$word] [json$json]",
+ SHOW_STR
+ PBR_STR
+ "Nexthop Groups\n"
+ "Optional Name of the nexthop group\n"
+ JSON_STR)
+{
+ json_object *j = NULL;
+
+ if (json)
+ j = json_object_new_array();
+
+ if (j) {
+ pbr_nht_json_nexthop_group(j, word);
+
+ vty_json(vty, j);
+ } else
+ 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
+ PBR_STR
+ "PBR Interface\n"
+ "PBR Interface Name\n"
+ JSON_STR)
+{
+ struct interface *ifp;
+ struct vrf *vrf;
+ struct pbr_interface *pbr_ifp;
+ json_object *j = NULL;
+
+ if (json)
+ j = json_object_new_array();
+
+ RB_FOREACH(vrf, vrf_name_head, &vrfs_by_name) {
+ FOR_ALL_INTERFACES(vrf, ifp) {
+ struct pbr_map *pbrm;
+ json_object *this_iface = NULL;
+
+ if (j)
+ this_iface = json_object_new_object();
+
+ if (!ifp->info) {
+ json_object_free(this_iface);
+ continue;
+ }
+
+ if (name && strcmp(ifp->name, name) != 0) {
+ json_object_free(this_iface);
+ continue;
+ }
+
+ pbr_ifp = ifp->info;
+
+ if (strcmp(pbr_ifp->mapname, "") == 0) {
+ json_object_free(this_iface);
+ continue;
+ }
+
+ pbrm = pbrm_find(pbr_ifp->mapname);
+
+ if (this_iface) {
+ json_object_string_add(this_iface, "name",
+ ifp->name);
+ json_object_int_add(this_iface, "index",
+ ifp->ifindex);
+ json_object_string_add(this_iface, "policy",
+ pbr_ifp->mapname);
+ json_object_boolean_add(this_iface, "valid",
+ pbrm);
+
+ json_object_array_add(j, this_iface);
+ continue;
+ }
+
+ 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");
+ }
+ }
+
+ if (j)
+ vty_json(vty, j);
+
+ return CMD_SUCCESS;
+}
+
+/* PBR debugging CLI ------------------------------------------------------- */
+
+static struct cmd_node debug_node = {
+ .name = "debug",
+ .node = DEBUG_NODE,
+ .prompt = "",
+ .config_write = pbr_debug_config_write,
+};
+
+DEFPY(debug_pbr,
+ debug_pbr_cmd,
+ "[no] debug pbr [{map$map|zebra$zebra|nht$nht|events$events}]",
+ NO_STR
+ DEBUG_STR
+ PBR_STR
+ "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
+ PBR_STR)
+{
+ vty_out(vty, "PBR debugging status:\n");
+
+ pbr_debug_config_write_helper(vty, false);
+
+ return CMD_SUCCESS;
+}
+
+/* ------------------------------------------------------------------------- */
+
+
+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_vty_config_start(vty, ifp);
+
+ if (ifp->desc)
+ vty_out(vty, " description %s\n", ifp->desc);
+
+ pbr_map_write_interfaces(vty, ifp);
+
+ if_vty_config_end(vty);
+ }
+ }
+
+ return 1;
+}
+
+static int pbr_vty_map_config_write(struct vty *vty);
+/* PBR map node structure. */
+static struct cmd_node pbr_map_node = {
+ .name = "pbr-map",
+ .node = PBRMAP_NODE,
+ .parent_node = CONFIG_NODE,
+ .prompt = "%s(config-pbr-map)# ",
+ .config_write = pbr_vty_map_config_write,
+};
+
+static int pbr_vty_map_config_write_sequence(struct vty *vty,
+ struct pbr_map *pbrm,
+ struct pbr_map_sequence *pbrms)
+{
+ vty_out(vty, "pbr-map %s seq %u\n", pbrm->name, pbrms->seqno);
+
+ if (pbrms->src)
+ vty_out(vty, " match src-ip %pFX\n", pbrms->src);
+
+ if (pbrms->dst)
+ vty_out(vty, " match dst-ip %pFX\n", pbrms->dst);
+
+ if (pbrms->src_prt)
+ vty_out(vty, " match src-port %u\n", pbrms->src_prt);
+ if (pbrms->dst_prt)
+ vty_out(vty, " match dst-port %u\n", pbrms->dst_prt);
+
+ if (pbrms->ip_proto) {
+ struct protoent *p;
+
+ p = getprotobynumber(pbrms->ip_proto);
+ vty_out(vty, " match ip-protocol %s\n", p->p_name);
+ }
+
+ if (pbrms->dsfield & PBR_DSFIELD_DSCP)
+ vty_out(vty, " match dscp %u\n",
+ (pbrms->dsfield & PBR_DSFIELD_DSCP) >> 2);
+
+ if (pbrms->dsfield & PBR_DSFIELD_ECN)
+ vty_out(vty, " match ecn %u\n",
+ pbrms->dsfield & PBR_DSFIELD_ECN);
+
+ if (pbrms->mark)
+ vty_out(vty, " match mark %u\n", pbrms->mark);
+
+
+ if (pbrms->action_queue_id != PBR_MAP_UNDEFINED_QUEUE_ID)
+ vty_out(vty, " set queue-id %d\n", pbrms->action_queue_id);
+
+ if (pbrms->action_pcp)
+ vty_out(vty, " set pcp %d\n", pbrms->action_pcp);
+
+ if (pbrms->action_vlan_id)
+ vty_out(vty, " set vlan %u\n", pbrms->action_vlan_id);
+
+ if (pbrms->action_vlan_flags == PBR_MAP_STRIP_INNER_ANY)
+ vty_out(vty, " strip vlan any\n");
+
+ if (pbrms->vrf_unchanged)
+ vty_out(vty, " set vrf unchanged\n");
+
+ if (pbrms->vrf_lookup)
+ vty_out(vty, " set vrf %s\n", pbrms->vrf_name);
+
+ if (pbrms->nhgrp_name)
+ vty_out(vty, " set nexthop-group %s\n", pbrms->nhgrp_name);
+
+ if (pbrms->nhg) {
+ vty_out(vty, " set ");
+ pbrms_nexthop_group_write_individual_nexthop(vty, pbrms);
+ }
+
+ vty_out(vty, "exit\n");
+ 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;
+}
+
+static void pbr_map_completer(vector comps, struct cmd_token *token)
+{
+ struct pbr_map *pbrm;
+
+ RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps)
+ vector_set(comps, XSTRDUP(MTYPE_COMPLETION, pbrm->name));
+}
+
+static const struct cmd_variable_handler pbr_map_name[] = {
+ {
+ .tokenname = "PBRMAP", .completions = pbr_map_completer,
+ },
+ {
+ .completions = NULL
+ }
+};
+
+extern struct zebra_privs_t pbr_privs;
+
+void pbr_vty_init(void)
+{
+ cmd_variable_handler_register(pbr_map_name);
+
+ vrf_cmd_init(NULL);
+
+ if_cmd_init(pbr_interface_config_write);
+
+ install_node(&pbr_map_node);
+
+ /* debug */
+ install_node(&debug_node);
+ install_element(ENABLE_NODE, &debug_pbr_cmd);
+ install_element(CONFIG_NODE, &debug_pbr_cmd);
+ install_element(ENABLE_NODE, &show_debugging_pbr_cmd);
+
+ install_default(PBRMAP_NODE);
+
+ install_element(CONFIG_NODE, &pbr_map_cmd);
+ install_element(CONFIG_NODE, &no_pbr_map_cmd);
+ install_element(CONFIG_NODE, &pbr_set_table_range_cmd);
+ install_element(CONFIG_NODE, &no_pbr_set_table_range_cmd);
+ install_element(INTERFACE_NODE, &pbr_policy_cmd);
+ install_element(PBRMAP_NODE, &pbr_map_match_ip_proto_cmd);
+ install_element(PBRMAP_NODE, &pbr_map_match_src_port_cmd);
+ install_element(PBRMAP_NODE, &pbr_map_match_dst_port_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_match_dscp_cmd);
+ install_element(PBRMAP_NODE, &pbr_map_match_ecn_cmd);
+ install_element(PBRMAP_NODE, &pbr_map_match_mark_cmd);
+ install_element(PBRMAP_NODE, &pbr_map_action_queue_id_cmd);
+ install_element(PBRMAP_NODE, &pbr_map_action_strip_vlan_cmd);
+ install_element(PBRMAP_NODE, &pbr_map_action_vlan_id_cmd);
+ install_element(PBRMAP_NODE, &pbr_map_action_pcp_cmd);
+ install_element(PBRMAP_NODE, &pbr_map_nexthop_group_cmd);
+ install_element(PBRMAP_NODE, &no_pbr_map_nexthop_group_cmd);
+ install_element(PBRMAP_NODE, &pbr_map_nexthop_cmd);
+ install_element(PBRMAP_NODE, &no_pbr_map_nexthop_cmd);
+ install_element(PBRMAP_NODE, &pbr_map_vrf_cmd);
+ install_element(PBRMAP_NODE, &no_pbr_map_vrf_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);
+}
diff --git a/pbrd/pbr_vty.h b/pbrd/pbr_vty.h
new file mode 100644
index 0000000..6e345fd
--- /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 0000000..4506dc1
--- /dev/null
+++ b/pbrd/pbr_zebra.c
@@ -0,0 +1,616 @@
+/*
+ * 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 <zebra.h>
+
+#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"
+#include "pbr_vrf.h"
+
+DEFINE_MTYPE_STATIC(PBRD, PBR_INTERFACE, "PBR Interface");
+
+/* Zebra structure to hold current status. */
+struct zclient *zclient;
+
+struct pbr_interface *pbr_if_new(struct interface *ifp)
+{
+ struct pbr_interface *pbr_ifp;
+
+ assert(ifp);
+ assert(!ifp->info);
+
+ pbr_ifp = XCALLOC(MTYPE_PBR_INTERFACE, sizeof(*pbr_ifp));
+
+ ifp->info = pbr_ifp;
+ return pbr_ifp;
+}
+
+void pbr_if_del(struct interface *ifp)
+{
+ XFREE(MTYPE_PBR_INTERFACE, ifp->info);
+}
+
+/* Interface addition message from zebra. */
+int pbr_ifp_create(struct interface *ifp)
+{
+ DEBUGD(&pbr_dbg_zebra, "%s: %s", __func__, ifp->name);
+
+ if (!ifp->info)
+ pbr_if_new(ifp);
+
+ pbr_nht_interface_update(ifp);
+ /* Update nexthops tracked from a `set nexthop` command */
+ pbr_nht_nexthop_interface_update(ifp);
+
+ pbr_map_policy_interface_update(ifp, true);
+
+ return 0;
+}
+
+int pbr_ifp_destroy(struct interface *ifp)
+{
+ DEBUGD(&pbr_dbg_zebra, "%s: %s", __func__, ifp->name);
+
+ pbr_map_policy_interface_update(ifp, false);
+
+ return 0;
+}
+
+static int interface_address_add(ZAPI_CALLBACK_ARGS)
+{
+ struct connected *c;
+ char buf[PREFIX_STRLEN];
+
+ c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id);
+
+ DEBUGD(&pbr_dbg_zebra, "%s: %s added %s", __func__,
+ c ? c->ifp->name : "Unknown",
+ c ? prefix2str(c->address, buf, sizeof(buf)) : "Unknown");
+
+ return 0;
+}
+
+static int interface_address_delete(ZAPI_CALLBACK_ARGS)
+{
+ struct connected *c;
+
+ c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id);
+
+ if (!c)
+ return 0;
+
+ DEBUGD(&pbr_dbg_zebra, "%s: %s deleted %pFX", __func__, c->ifp->name,
+ c->address);
+
+ connected_free(&c);
+ return 0;
+}
+
+int pbr_ifp_up(struct interface *ifp)
+{
+ DEBUGD(&pbr_dbg_zebra, "%s: %s is up", __func__, ifp->name);
+
+ pbr_nht_nexthop_interface_update(ifp);
+
+ return 0;
+}
+
+int pbr_ifp_down(struct interface *ifp)
+{
+ DEBUGD(&pbr_dbg_zebra, "%s: %s is down", __func__, ifp->name);
+
+ pbr_nht_nexthop_interface_update(ifp);
+
+ return 0;
+}
+
+static int interface_vrf_update(ZAPI_CALLBACK_ARGS)
+{
+ struct interface *ifp;
+ vrf_id_t new_vrf_id;
+
+ ifp = zebra_interface_vrf_update_read(zclient->ibuf, vrf_id,
+ &new_vrf_id);
+
+ if (!ifp) {
+ DEBUGD(&pbr_dbg_zebra, "%s: VRF change interface not found",
+ __func__);
+
+ return 0;
+ }
+
+ DEBUGD(&pbr_dbg_zebra, "%s: %s VRF change %u -> %u", __func__,
+ ifp->name, vrf_id, new_vrf_id);
+
+ if_update_to_new_vrf(ifp, new_vrf_id);
+
+ return 0;
+}
+
+static int route_notify_owner(ZAPI_CALLBACK_ARGS)
+{
+ struct prefix p;
+ enum zapi_route_notify_owner note;
+ uint32_t table_id;
+
+ if (!zapi_route_notify_decode(zclient->ibuf, &p, &table_id, &note,
+ NULL, NULL))
+ return -1;
+
+ switch (note) {
+ case ZAPI_ROUTE_FAIL_INSTALL:
+ DEBUGD(&pbr_dbg_zebra,
+ "%s: [%pFX] Route install failure for table: %u",
+ __func__, &p, table_id);
+ break;
+ case ZAPI_ROUTE_BETTER_ADMIN_WON:
+ DEBUGD(&pbr_dbg_zebra,
+ "%s: [%pFX] Route better admin distance won for table: %u",
+ __func__, &p, table_id);
+ break;
+ case ZAPI_ROUTE_INSTALLED:
+ DEBUGD(&pbr_dbg_zebra,
+ "%s: [%pFX] Route installed succeeded for table: %u",
+ __func__, &p, table_id);
+ pbr_nht_route_installed_for_table(table_id);
+ break;
+ case ZAPI_ROUTE_REMOVED:
+ DEBUGD(&pbr_dbg_zebra,
+ "%s: [%pFX] Route Removed succeeded for table: %u",
+ __func__, &p, table_id);
+ pbr_nht_route_removed_for_table(table_id);
+ break;
+ case ZAPI_ROUTE_REMOVE_FAIL:
+ DEBUGD(&pbr_dbg_zebra,
+ "%s: [%pFX] Route remove fail for table: %u", __func__,
+ &p, table_id);
+ break;
+ }
+
+ return 0;
+}
+
+static int rule_notify_owner(ZAPI_CALLBACK_ARGS)
+{
+ uint32_t seqno, priority, unique;
+ enum zapi_rule_notify_owner note;
+ struct pbr_map_sequence *pbrms;
+ struct pbr_map_interface *pmi;
+ char ifname[INTERFACE_NAMSIZ + 1];
+ uint64_t installed;
+
+ if (!zapi_rule_notify_decode(zclient->ibuf, &seqno, &priority, &unique,
+ ifname, &note))
+ return -1;
+
+ pmi = NULL;
+ pbrms = pbrms_lookup_unique(unique, ifname, &pmi);
+ if (!pbrms) {
+ DEBUGD(&pbr_dbg_zebra,
+ "%s: Failure to lookup pbrms based upon %u", __func__,
+ unique);
+ return 0;
+ }
+
+ installed = 1 << pmi->install_bit;
+
+ switch (note) {
+ case ZAPI_RULE_FAIL_INSTALL:
+ pbrms->installed &= ~installed;
+ break;
+ case ZAPI_RULE_INSTALLED:
+ pbrms->installed |= installed;
+ break;
+ case ZAPI_RULE_FAIL_REMOVE:
+ /* Don't change state on rule removal failure */
+ break;
+ case ZAPI_RULE_REMOVED:
+ pbrms->installed &= ~installed;
+ break;
+ }
+
+ DEBUGD(&pbr_dbg_zebra, "%s: Received %s: %" PRIu64, __func__,
+ zapi_rule_notify_owner2str(note), pbrms->installed);
+
+ pbr_map_final_interface_deletion(pbrms->parent, pmi);
+
+ return 0;
+}
+
+static void zebra_connected(struct zclient *zclient)
+{
+ DEBUGD(&pbr_dbg_zebra, "%s: Registering for fun and profit", __func__);
+ 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;
+
+ DEBUGD(&pbr_dbg_zebra, " Encoding %pFX", &api->prefix);
+
+ i = 0;
+ for (ALL_NEXTHOPS(nhg, nhop)) {
+ api_nh = &api->nexthops[i];
+ api_nh->vrf_id = nhop->vrf_id;
+ api_nh->type = nhop->type;
+ api_nh->weight = nhop->weight;
+ 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,
+ IPV6_MAX_BYTELEN);
+ break;
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ api_nh->ifindex = nhop->ifindex;
+ memcpy(&api_nh->gate.ipv6, &nhop->gate.ipv6,
+ IPV6_MAX_BYTELEN);
+ 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;
+
+ DEBUGD(&pbr_dbg_zebra, "%s for Table: %d", __func__, pnhgc->table_id);
+
+ 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",
+ __func__);
+ break;
+ case AFI_UNSPEC:
+ DEBUGD(&pbr_dbg_zebra,
+ "%s: Asked to install unspecified route type", __func__);
+ 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;
+
+ DEBUGD(&pbr_dbg_zebra, "%s for Table: %d", __func__, pnhgc->table_id);
+
+ 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",
+ __func__);
+ break;
+ case AFI_UNSPEC:
+ DEBUGD(&pbr_dbg_zebra,
+ "%s: Asked to delete unspecified route type", __func__);
+ break;
+ }
+}
+
+static int pbr_zebra_nexthop_update(ZAPI_CALLBACK_ARGS)
+{
+ struct zapi_route nhr;
+ struct prefix matched;
+ uint32_t i;
+
+ if (!zapi_nexthop_update_decode(zclient->ibuf, &matched, &nhr)) {
+ zlog_err("Failure to decode Nexthop update message");
+ return 0;
+ }
+
+ if (DEBUG_MODE_CHECK(&pbr_dbg_zebra, DEBUG_MODE_ALL)) {
+
+ DEBUGD(&pbr_dbg_zebra,
+ "%s: Received Nexthop update: %pFX against %pFX",
+ __func__, &matched, &nhr.prefix);
+
+ DEBUGD(&pbr_dbg_zebra, "%s: (Nexthops(%u)", __func__,
+ nhr.nexthop_num);
+
+ for (i = 0; i < nhr.nexthop_num; i++) {
+ DEBUGD(&pbr_dbg_zebra,
+ "%s: Type: %d: vrf: %d, ifindex: %d gate: %pI4",
+ __func__, nhr.nexthops[i].type,
+ nhr.nexthops[i].vrf_id, nhr.nexthops[i].ifindex,
+ &nhr.nexthops[i].gate.ipv4);
+ }
+ }
+
+ nhr.prefix = matched;
+ pbr_nht_nexthop_update(&nhr);
+ return 1;
+}
+
+extern struct zebra_privs_t pbr_privs;
+
+static zclient_handler *const pbr_handlers[] = {
+ [ZEBRA_INTERFACE_ADDRESS_ADD] = interface_address_add,
+ [ZEBRA_INTERFACE_ADDRESS_DELETE] = interface_address_delete,
+ [ZEBRA_INTERFACE_VRF_UPDATE] = interface_vrf_update,
+ [ZEBRA_ROUTE_NOTIFY_OWNER] = route_notify_owner,
+ [ZEBRA_RULE_NOTIFY_OWNER] = rule_notify_owner,
+ [ZEBRA_NEXTHOP_UPDATE] = pbr_zebra_nexthop_update,
+};
+
+void pbr_zebra_init(void)
+{
+ struct zclient_options opt = { .receive_notify = true };
+
+ zclient = zclient_new(master, &opt, pbr_handlers,
+ array_size(pbr_handlers));
+
+ zclient_init(zclient, ZEBRA_ROUTE_PBR, 0, &pbr_privs);
+ zclient->zebra_connected = zebra_connected;
+}
+
+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 = IPV4_MAX_BITLEN;
+ break;
+ case NEXTHOP_TYPE_IPV6:
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ p.family = AF_INET6;
+ memcpy(&p.u.prefix6, &nhop->gate.ipv6, IPV6_MAX_BYTELEN);
+ p.prefixlen = IPV6_MAX_BITLEN;
+ if (IN6_IS_ADDR_LINKLOCAL(&nhop->gate.ipv6))
+ /*
+ * Don't bother tracking link locals, just track their
+ * interface state.
+ */
+ return;
+ break;
+ }
+
+ if (zclient_send_rnh(zclient, command, &p, SAFI_UNICAST, false, false,
+ nhop->vrf_id)
+ == ZCLIENT_SEND_FAILURE) {
+ zlog_warn("%s: Failure to send nexthop to zebra", __func__);
+ }
+}
+
+static void pbr_encode_pbr_map_sequence_prefix(struct stream *s,
+ struct prefix *p,
+ unsigned 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_vrf(struct stream *s,
+ const struct pbr_map_sequence *pbrms,
+ const struct interface *ifp)
+{
+ struct pbr_vrf *pbr_vrf;
+
+ if (pbrms->vrf_unchanged)
+ pbr_vrf = ifp->vrf->info;
+ else
+ pbr_vrf = pbr_vrf_lookup_by_name(pbrms->vrf_name);
+
+ if (!pbr_vrf) {
+ DEBUGD(&pbr_dbg_zebra, "%s: VRF not found", __func__);
+ return;
+ }
+
+ stream_putl(s, pbr_vrf->vrf->data.l.table_id);
+}
+
+static void pbr_encode_pbr_map_sequence(struct stream *s,
+ struct pbr_map_sequence *pbrms,
+ struct interface *ifp)
+{
+ unsigned char family;
+
+ family = AF_INET;
+ if (pbrms->family)
+ family = pbrms->family;
+
+ stream_putl(s, pbrms->seqno);
+ stream_putl(s, pbrms->ruleno);
+ stream_putl(s, pbrms->unique);
+ stream_putc(s, pbrms->ip_proto); /* The ip_proto */
+ pbr_encode_pbr_map_sequence_prefix(s, pbrms->src, family);
+ stream_putw(s, pbrms->src_prt);
+ pbr_encode_pbr_map_sequence_prefix(s, pbrms->dst, family);
+ stream_putw(s, pbrms->dst_prt);
+ stream_putc(s, pbrms->dsfield);
+ stream_putl(s, pbrms->mark);
+
+ stream_putl(s, pbrms->action_queue_id);
+
+ stream_putw(s, pbrms->action_vlan_id);
+ stream_putw(s, pbrms->action_vlan_flags);
+ stream_putw(s, pbrms->action_pcp);
+
+ if (pbrms->vrf_unchanged || pbrms->vrf_lookup)
+ pbr_encode_pbr_map_sequence_vrf(s, pbrms, ifp);
+ else 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_put(s, ifp->name, INTERFACE_NAMSIZ);
+}
+
+bool pbr_send_pbr_map(struct pbr_map_sequence *pbrms,
+ struct pbr_map_interface *pmi, bool install, bool changed)
+{
+ struct pbr_map *pbrm = pbrms->parent;
+ struct stream *s;
+ uint64_t is_installed = (uint64_t)1 << pmi->install_bit;
+
+ is_installed &= pbrms->installed;
+
+ DEBUGD(&pbr_dbg_zebra, "%s: for %s %d(%" PRIu64 ")", __func__,
+ pbrm->name, install, is_installed);
+
+ /*
+ * If we are installed and asked to do so again and the config
+ * has not changed, just return.
+ *
+ * If we are not installed and asked
+ * to delete just return.
+ */
+ if (install && is_installed && !changed)
+ return false;
+
+ if (!install && !is_installed)
+ return false;
+
+ s = zclient->obuf;
+ stream_reset(s);
+
+ zclient_create_header(s,
+ install ? ZEBRA_RULE_ADD : ZEBRA_RULE_DELETE,
+ VRF_DEFAULT);
+
+ /*
+ * We are sending one item at a time at the moment
+ */
+ stream_putl(s, 1);
+
+ DEBUGD(&pbr_dbg_zebra, "%s: %s %s seq %u %d %s %u", __func__,
+ install ? "Installing" : "Deleting", pbrm->name, pbrms->seqno,
+ install, pmi->ifp->name, pmi->delete);
+
+ pbr_encode_pbr_map_sequence(s, pbrms, pmi->ifp);
+
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ zclient_send_message(zclient);
+
+ return true;
+}
diff --git a/pbrd/pbr_zebra.h b/pbrd/pbr_zebra.h
new file mode 100644
index 0000000..d0f9ff9
--- /dev/null
+++ b/pbrd/pbr_zebra.h
@@ -0,0 +1,52 @@
+/*
+ * 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 struct thread_master *master;
+
+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 bool pbr_send_pbr_map(struct pbr_map_sequence *pbrms,
+ struct pbr_map_interface *pmi, bool install,
+ bool changed);
+
+extern struct pbr_interface *pbr_if_new(struct interface *ifp);
+
+extern int pbr_ifp_create(struct interface *ifp);
+extern int pbr_ifp_up(struct interface *ifp);
+extern int pbr_ifp_down(struct interface *ifp);
+extern int pbr_ifp_destroy(struct interface *ifp);
+
+/* Free the ifp->info pointer */
+extern void pbr_if_del(struct interface *ifp);
+
+#endif
diff --git a/pbrd/subdir.am b/pbrd/subdir.am
new file mode 100644
index 0000000..bbe3f2a
--- /dev/null
+++ b/pbrd/subdir.am
@@ -0,0 +1,43 @@
+#
+# pbrd
+#
+
+if PBRD
+noinst_LIBRARIES += pbrd/libpbr.a
+sbin_PROGRAMS += pbrd/pbrd
+vtysh_scan += \
+ pbrd/pbr_vty.c \
+ pbrd/pbr_debug.c \
+ # end
+vtysh_daemons += pbrd
+man8 += $(MANBUILD)/frr-pbrd.8
+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_debug.c \
+ pbrd/pbr_vrf.c \
+ # end
+
+noinst_HEADERS += \
+ pbrd/pbr_map.h \
+ pbrd/pbr_memory.h \
+ pbrd/pbr_nht.h \
+ pbrd/pbr_vty.h \
+ pbrd/pbr_zebra.h \
+ pbrd/pbr_debug.h \
+ pbrd/pbr_vrf.h \
+ # end
+
+clippy_scan += \
+ pbrd/pbr_debug.c \
+ pbrd/pbr_vty.c \
+ # end
+
+pbrd_pbrd_SOURCES = pbrd/pbr_main.c
+pbrd_pbrd_LDADD = pbrd/libpbr.a lib/libfrr.la $(LIBCAP)
+