summaryrefslogtreecommitdiffstats
path: root/sharpd
diff options
context:
space:
mode:
Diffstat (limited to 'sharpd')
-rw-r--r--sharpd/.gitignore2
-rw-r--r--sharpd/Makefile10
-rw-r--r--sharpd/sharp_globals.h65
-rw-r--r--sharpd/sharp_logpump.c143
-rw-r--r--sharpd/sharp_main.c181
-rw-r--r--sharpd/sharp_nht.c225
-rw-r--r--sharpd/sharp_nht.h29
-rw-r--r--sharpd/sharp_vty.c1452
-rw-r--r--sharpd/sharp_vty.h17
-rw-r--r--sharpd/sharp_zebra.c1089
-rw-r--r--sharpd/sharp_zebra.h73
-rw-r--r--sharpd/subdir.am32
12 files changed, 3318 insertions, 0 deletions
diff --git a/sharpd/.gitignore b/sharpd/.gitignore
new file mode 100644
index 0000000..91b9f2e
--- /dev/null
+++ b/sharpd/.gitignore
@@ -0,0 +1,2 @@
+sharpd
+sharpd.conf
diff --git a/sharpd/Makefile b/sharpd/Makefile
new file mode 100644
index 0000000..6a904d1
--- /dev/null
+++ b/sharpd/Makefile
@@ -0,0 +1,10 @@
+all: ALWAYS
+ @$(MAKE) -s -C .. sharpd/sharpd
+%: ALWAYS
+ @$(MAKE) -s -C .. sharpd/$@
+
+Makefile:
+ #nothing
+ALWAYS:
+.PHONY: ALWAYS makefiles
+.SUFFIXES:
diff --git a/sharpd/sharp_globals.h b/sharpd/sharp_globals.h
new file mode 100644
index 0000000..76b043b
--- /dev/null
+++ b/sharpd/sharp_globals.h
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * SHARP - code to track globals
+ * Copyright (C) 2019 Cumulus Networks, Inc.
+ * Donald Sharp
+ */
+#ifndef __SHARP_GLOBAL_H__
+#define __SHARP_GLOBAL_H__
+
+#include "lib/srv6.h"
+
+DECLARE_MGROUP(SHARPD);
+
+struct sharp_routes {
+ /* The original prefix for route installation */
+ struct prefix orig_prefix;
+
+ /* The nexthop info we are using for installation */
+ struct nexthop nhop;
+ struct nexthop backup_nhop;
+ uint32_t nhgid;
+ struct nexthop_group nhop_group;
+ struct nexthop_group backup_nhop_group;
+
+ uint32_t total_routes;
+ uint32_t installed_routes;
+ uint32_t removed_routes;
+ int32_t repeat;
+
+ /* ZAPI_ROUTE's flag */
+ uint32_t flags;
+
+ uint8_t inst;
+ vrf_id_t vrf_id;
+
+ struct timeval t_start;
+ struct timeval t_end;
+
+ char opaque[ZAPI_MESSAGE_OPAQUE_LENGTH];
+};
+
+struct sharp_srv6_locator {
+ /* name of locator */
+ char name[SRV6_LOCNAME_SIZE];
+
+ /* list of struct prefix_ipv6 */
+ struct list *chunks;
+};
+
+struct sharp_global {
+ /* Global data about route install/deletions */
+ struct sharp_routes r;
+
+ /* The list of nexthops that we are watching and data about them */
+ struct list *nhs;
+
+ /* Traffic Engineering Database */
+ struct ls_ted *ted;
+
+ /* list of sharp_srv6_locator */
+ struct list *srv6_locators;
+};
+
+extern struct sharp_global sg;
+#endif
diff --git a/sharpd/sharp_logpump.c b/sharpd/sharp_logpump.c
new file mode 100644
index 0000000..5474e80
--- /dev/null
+++ b/sharpd/sharp_logpump.c
@@ -0,0 +1,143 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * testing log message generator
+ * Copyright (C) 2019-2020 David Lamparter for NetDEF, Inc.
+ */
+
+#include <zebra.h>
+
+#include "vty.h"
+#include "command.h"
+#include "prefix.h"
+#include "nexthop.h"
+#include "log.h"
+#include "frrevent.h"
+#include "vrf.h"
+#include "zclient.h"
+#include "frr_pthread.h"
+
+#include "sharpd/sharp_vty.h"
+
+/* this is quite hacky, but then again it's a test tool and it does its job. */
+static struct frr_pthread *lpt;
+
+static unsigned long lp_duration;
+static unsigned lp_frequency;
+static unsigned lp_burst;
+static size_t lp_ctr, lp_expect;
+static struct rusage lp_rusage;
+static struct vty *lp_vty;
+
+extern struct event_loop *master;
+
+static void logpump_done(struct event *thread)
+{
+ double x;
+
+ vty_out(lp_vty, "\nlogpump done\n");
+ vty_out(lp_vty, "%9zu messages written\n", lp_ctr);
+ x = (double)lp_ctr / (double)lp_expect * 100.;
+ vty_out(lp_vty, "%9zu messages targeted = %5.1lf%%\n", lp_expect, x);
+
+ x = lp_rusage.ru_utime.tv_sec * 1000000 + lp_rusage.ru_utime.tv_usec;
+ x /= (double)lp_ctr;
+ vty_out(lp_vty, "%6llu.%06u usr %9.1lfns/msg\n",
+ (unsigned long long)lp_rusage.ru_utime.tv_sec,
+ (unsigned)lp_rusage.ru_utime.tv_usec, x * 1000.);
+
+ x = lp_rusage.ru_stime.tv_sec * 1000000 + lp_rusage.ru_stime.tv_usec;
+ x /= (double)lp_ctr;
+ vty_out(lp_vty, "%6llu.%06u sys %9.1lfns/msg\n",
+ (unsigned long long)lp_rusage.ru_stime.tv_sec,
+ (unsigned)lp_rusage.ru_stime.tv_usec, x * 1000.);
+
+ frr_pthread_stop(lpt, NULL);
+ frr_pthread_destroy(lpt);
+ lpt = NULL;
+}
+
+static void *logpump_run(void *arg)
+{
+ struct timespec start, next, now;
+ unsigned long delta, period;
+
+ period = 1000000000L / lp_frequency;
+
+ zlog_tls_buffer_init();
+
+ clock_gettime(CLOCK_MONOTONIC, &start);
+ next = start;
+ do {
+ for (size_t inburst = 0; inburst < lp_burst; inburst++)
+ zlog_debug("log pump: %zu (burst %zu)",
+ lp_ctr++, inburst);
+
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ delta = (now.tv_sec - start.tv_sec) * 1000000000L
+ + (now.tv_nsec - start.tv_nsec);
+
+ next.tv_nsec += period;
+ if (next.tv_nsec > 1000000000L) {
+ next.tv_sec++;
+ next.tv_nsec -= 1000000000L;
+ }
+#ifdef HAVE_CLOCK_NANOSLEEP
+ clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &next, NULL);
+#else
+ struct timespec slpdur;
+
+ slpdur.tv_sec = next.tv_sec - now.tv_sec;
+ slpdur.tv_nsec = next.tv_nsec - now.tv_nsec;
+ if (slpdur.tv_nsec < 0) {
+ slpdur.tv_sec--;
+ slpdur.tv_nsec += 1000000000L;
+ }
+
+ nanosleep(&slpdur, NULL);
+#endif
+ } while (delta < lp_duration);
+
+ zlog_tls_buffer_fini();
+
+#ifdef RUSAGE_THREAD
+ getrusage(RUSAGE_THREAD, &lp_rusage);
+#else
+ getrusage(RUSAGE_SELF, &lp_rusage);
+#endif
+
+ event_add_timer_msec(master, logpump_done, NULL, 0, NULL);
+ return NULL;
+}
+
+static int logpump_halt(struct frr_pthread *fpt, void **res)
+{
+ return 0;
+}
+
+/* default frr_pthread attributes */
+static const struct frr_pthread_attr attr = {
+ .start = logpump_run,
+ .stop = logpump_halt,
+};
+
+void sharp_logpump_run(struct vty *vty, unsigned duration, unsigned frequency,
+ unsigned burst)
+{
+ if (lpt != NULL) {
+ vty_out(vty, "logpump already running\n");
+ return;
+ }
+
+ vty_out(vty, "starting logpump...\n");
+ vty_out(vty, "keep this VTY open and press Enter to see results\n");
+
+ lp_vty = vty;
+ lp_duration = duration * 1000000000UL;
+ lp_frequency = frequency;
+ lp_burst = burst;
+ lp_expect = duration * frequency * burst;
+ lp_ctr = 0;
+
+ lpt = frr_pthread_new(&attr, "logpump", "logpump");
+ frr_pthread_run(lpt, NULL);
+}
diff --git a/sharpd/sharp_main.c b/sharpd/sharp_main.c
new file mode 100644
index 0000000..fa85c2b
--- /dev/null
+++ b/sharpd/sharp_main.c
@@ -0,0 +1,181 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * SHARP - main code
+ * Copyright (C) Cumulus Networks, Inc.
+ * Donald Sharp
+ */
+#include <zebra.h>
+
+#include <lib/version.h>
+#include "getopt.h"
+#include "frrevent.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_group.h"
+#include "link_state.h"
+
+#include "sharp_zebra.h"
+#include "sharp_vty.h"
+#include "sharp_globals.h"
+#include "sharp_nht.h"
+
+DEFINE_MGROUP(SHARPD, "sharpd");
+
+zebra_capabilities_t _caps_p[] = {
+};
+
+struct zebra_privs_t sharp_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 event_loop *master;
+
+/* SIGHUP handler. */
+static void sighup(void)
+{
+ zlog_info("SIGHUP received");
+}
+
+/* SIGINT / SIGTERM handler. */
+static void sigint(void)
+{
+ zlog_notice("Terminating on signal");
+
+ frr_fini();
+
+ exit(0);
+}
+
+/* SIGUSR1 handler. */
+static void sigusr1(void)
+{
+ zlog_rotate();
+}
+
+struct frr_signal_t sharp_signals[] = {
+ {
+ .signal = SIGHUP,
+ .handler = &sighup,
+ },
+ {
+ .signal = SIGUSR1,
+ .handler = &sigusr1,
+ },
+ {
+ .signal = SIGINT,
+ .handler = &sigint,
+ },
+ {
+ .signal = SIGTERM,
+ .handler = &sigint,
+ },
+};
+
+#define SHARP_VTY_PORT 2614
+
+static const struct frr_yang_module_info *const sharpd_yang_modules[] = {
+ &frr_filter_info,
+ &frr_interface_info,
+ &frr_route_map_info,
+ &frr_vrf_info,
+};
+
+FRR_DAEMON_INFO(sharpd, SHARP, .vty_port = SHARP_VTY_PORT,
+
+ .proghelp = "Implementation of a Sharp of routes daemon.",
+
+ .signals = sharp_signals,
+ .n_signals = array_size(sharp_signals),
+
+ .privs = &sharp_privs, .yang_modules = sharpd_yang_modules,
+ .n_yang_modules = array_size(sharpd_yang_modules),
+);
+
+struct sharp_global sg;
+
+static void sharp_global_init(void)
+{
+ memset(&sg, 0, sizeof(sg));
+ sg.nhs = list_new();
+ sg.ted = NULL;
+ sg.srv6_locators = list_new();
+}
+
+static void sharp_start_configuration(void)
+{
+ zlog_debug("Configuration has started to be read");
+}
+
+static void sharp_end_configuration(void)
+{
+ zlog_debug("Configuration has finished being read");
+}
+
+int main(int argc, char **argv, char **envp)
+{
+ frr_preinit(&sharpd_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();
+
+ cmd_init_config_callbacks(sharp_start_configuration,
+ sharp_end_configuration);
+ sharp_global_init();
+
+ sharp_nhgroup_init();
+ vrf_init(NULL, NULL, NULL, NULL);
+
+ sharp_zebra_init();
+
+ /* Get configuration file. */
+ sharp_vty_init();
+
+ frr_config_fork();
+ frr_run(master);
+
+ /* Not reached. */
+ return 0;
+}
diff --git a/sharpd/sharp_nht.c b/sharpd/sharp_nht.c
new file mode 100644
index 0000000..fa78805
--- /dev/null
+++ b/sharpd/sharp_nht.c
@@ -0,0 +1,225 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * SHARP - code to track nexthops
+ * Copyright (C) Cumulus Networks, Inc.
+ * Donald Sharp
+ */
+#include <zebra.h>
+
+#include "memory.h"
+#include "nexthop.h"
+#include "nexthop_group.h"
+#include "vty.h"
+#include "typesafe.h"
+#include "zclient.h"
+
+#include "sharp_nht.h"
+#include "sharp_globals.h"
+#include "sharp_zebra.h"
+
+DEFINE_MTYPE_STATIC(SHARPD, NH_TRACKER, "Nexthop Tracker");
+DEFINE_MTYPE_STATIC(SHARPD, NHG, "Nexthop Group");
+
+struct sharp_nh_tracker *sharp_nh_tracker_get(struct prefix *p)
+{
+ struct listnode *node;
+ struct sharp_nh_tracker *nht;
+
+ for (ALL_LIST_ELEMENTS_RO(sg.nhs, node, nht)) {
+ if (prefix_same(&nht->p, p))
+ break;
+ }
+
+ if (nht)
+ return nht;
+
+ nht = XCALLOC(MTYPE_NH_TRACKER, sizeof(*nht));
+ prefix_copy(&nht->p, p);
+
+ listnode_add(sg.nhs, nht);
+ return nht;
+}
+
+void sharp_nh_tracker_dump(struct vty *vty)
+{
+ struct listnode *node;
+ struct sharp_nh_tracker *nht;
+
+ for (ALL_LIST_ELEMENTS_RO(sg.nhs, node, nht))
+ vty_out(vty, "%pFX: Nexthops: %u Updates: %u\n", &nht->p,
+ nht->nhop_num, nht->updates);
+}
+
+PREDECL_RBTREE_UNIQ(sharp_nhg_rb);
+
+struct sharp_nhg {
+ struct sharp_nhg_rb_item mylistitem;
+
+ uint32_t id;
+
+#define NHG_NAME_LEN 256
+ char name[NHG_NAME_LEN];
+
+ bool installed;
+};
+
+static uint32_t nhg_id;
+
+static uint32_t sharp_get_next_nhid(void)
+{
+ zlog_debug("NHG ID assigned: %u", nhg_id);
+ return nhg_id++;
+}
+
+struct sharp_nhg_rb_head nhg_head;
+
+static int sharp_nhg_compare_func(const struct sharp_nhg *a,
+ const struct sharp_nhg *b)
+{
+ return strncmp(a->name, b->name, NHG_NAME_LEN);
+}
+
+DECLARE_RBTREE_UNIQ(sharp_nhg_rb, struct sharp_nhg, mylistitem,
+ sharp_nhg_compare_func);
+
+static struct sharp_nhg *sharp_nhgroup_find_id(uint32_t id)
+{
+ struct sharp_nhg *lookup;
+
+ /* Yea its just a for loop, I don't want add complexity
+ * to sharpd with another RB tree for just IDs
+ */
+
+ frr_each (sharp_nhg_rb, &nhg_head, lookup) {
+ if (lookup->id == id)
+ return lookup;
+ }
+
+ return NULL;
+}
+
+static void sharp_nhgroup_add_cb(const char *name)
+{
+ struct sharp_nhg *snhg;
+
+ snhg = XCALLOC(MTYPE_NHG, sizeof(*snhg));
+ snhg->id = sharp_get_next_nhid();
+ strlcpy(snhg->name, name, sizeof(snhg->name));
+
+ sharp_nhg_rb_add(&nhg_head, snhg);
+}
+
+static void sharp_nhgroup_modify_cb(const struct nexthop_group_cmd *nhgc)
+{
+ struct sharp_nhg lookup;
+ struct sharp_nhg *snhg;
+ struct nexthop_group_cmd *bnhgc = NULL;
+
+ strlcpy(lookup.name, nhgc->name, sizeof(lookup.name));
+ snhg = sharp_nhg_rb_find(&nhg_head, &lookup);
+
+ if (!nhgc->nhg.nexthop)
+ return;
+
+ if (nhgc->backup_list_name[0])
+ bnhgc = nhgc_find(nhgc->backup_list_name);
+
+ nhg_add(snhg->id, &nhgc->nhg, (bnhgc ? &bnhgc->nhg : NULL));
+}
+
+static void sharp_nhgroup_add_nexthop_cb(const struct nexthop_group_cmd *nhgc,
+ const struct nexthop *nhop)
+{
+ struct sharp_nhg lookup;
+ struct sharp_nhg *snhg;
+ struct nexthop_group_cmd *bnhgc = NULL;
+
+ strlcpy(lookup.name, nhgc->name, sizeof(lookup.name));
+ snhg = sharp_nhg_rb_find(&nhg_head, &lookup);
+
+ if (nhgc->backup_list_name[0])
+ bnhgc = nhgc_find(nhgc->backup_list_name);
+
+ nhg_add(snhg->id, &nhgc->nhg, (bnhgc ? &bnhgc->nhg : NULL));
+}
+
+static void sharp_nhgroup_del_nexthop_cb(const struct nexthop_group_cmd *nhgc,
+ const struct nexthop *nhop)
+{
+ struct sharp_nhg lookup;
+ struct sharp_nhg *snhg;
+ struct nexthop_group_cmd *bnhgc = NULL;
+
+ strlcpy(lookup.name, nhgc->name, sizeof(lookup.name));
+ snhg = sharp_nhg_rb_find(&nhg_head, &lookup);
+
+ if (nhgc->backup_list_name[0])
+ bnhgc = nhgc_find(nhgc->backup_list_name);
+
+ nhg_add(snhg->id, &nhgc->nhg, (bnhgc ? &bnhgc->nhg : NULL));
+}
+
+static void sharp_nhgroup_delete_cb(const char *name)
+{
+ struct sharp_nhg lookup;
+ struct sharp_nhg *snhg;
+
+ strlcpy(lookup.name, name, sizeof(lookup.name));
+ snhg = sharp_nhg_rb_find(&nhg_head, &lookup);
+ if (!snhg)
+ return;
+
+ nhg_del(snhg->id);
+ sharp_nhg_rb_del(&nhg_head, snhg);
+ XFREE(MTYPE_NHG, snhg);
+}
+
+uint32_t sharp_nhgroup_get_id(const char *name)
+{
+ struct sharp_nhg lookup;
+ struct sharp_nhg *snhg;
+
+ strlcpy(lookup.name, name, sizeof(lookup.name));
+ snhg = sharp_nhg_rb_find(&nhg_head, &lookup);
+ if (!snhg)
+ return 0;
+
+ return snhg->id;
+}
+
+void sharp_nhgroup_id_set_installed(uint32_t id, bool installed)
+{
+ struct sharp_nhg *snhg;
+
+ snhg = sharp_nhgroup_find_id(id);
+ if (!snhg) {
+ zlog_debug("%s: nhg %u not found", __func__, id);
+ return;
+ }
+
+ snhg->installed = installed;
+}
+
+bool sharp_nhgroup_id_is_installed(uint32_t id)
+{
+ struct sharp_nhg *snhg;
+
+ snhg = sharp_nhgroup_find_id(id);
+ if (!snhg) {
+ zlog_debug("%s: nhg %u not found", __func__, id);
+ return false;
+ }
+
+ return snhg->installed;
+}
+
+void sharp_nhgroup_init(void)
+{
+ sharp_nhg_rb_init(&nhg_head);
+ nhg_id = zclient_get_nhg_start(ZEBRA_ROUTE_SHARP);
+
+ nexthop_group_init(sharp_nhgroup_add_cb, sharp_nhgroup_modify_cb,
+ sharp_nhgroup_add_nexthop_cb,
+ sharp_nhgroup_del_nexthop_cb,
+ sharp_nhgroup_delete_cb);
+}
diff --git a/sharpd/sharp_nht.h b/sharpd/sharp_nht.h
new file mode 100644
index 0000000..5523f28
--- /dev/null
+++ b/sharpd/sharp_nht.h
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * SHARP - code to track nexthops
+ * Copyright (C) Cumulus Networks, Inc.
+ * Donald Sharp
+ */
+#ifndef __SHARP_NHT_H__
+#define __SHARP_NHT_H__
+
+struct sharp_nh_tracker {
+ /* What are we watching */
+ struct prefix p;
+
+ /* Number of valid nexthops */
+ uint32_t nhop_num;
+
+ uint32_t updates;
+};
+
+extern struct sharp_nh_tracker *sharp_nh_tracker_get(struct prefix *p);
+
+extern void sharp_nh_tracker_dump(struct vty *vty);
+
+extern uint32_t sharp_nhgroup_get_id(const char *name);
+extern void sharp_nhgroup_id_set_installed(uint32_t id, bool installed);
+extern bool sharp_nhgroup_id_is_installed(uint32_t id);
+
+extern void sharp_nhgroup_init(void);
+#endif
diff --git a/sharpd/sharp_vty.c b/sharpd/sharp_vty.c
new file mode 100644
index 0000000..cf79bac
--- /dev/null
+++ b/sharpd/sharp_vty.c
@@ -0,0 +1,1452 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * SHARP - vty code
+ * Copyright (C) Cumulus Networks, Inc.
+ * Donald Sharp
+ */
+#include <zebra.h>
+
+#include "vty.h"
+#include "command.h"
+#include "prefix.h"
+#include "nexthop.h"
+#include "log.h"
+#include "vrf.h"
+#include "zclient.h"
+#include "nexthop_group.h"
+#include "linklist.h"
+#include "link_state.h"
+#include "cspf.h"
+#include "tc.h"
+
+#include "sharpd/sharp_globals.h"
+#include "sharpd/sharp_zebra.h"
+#include "sharpd/sharp_nht.h"
+#include "sharpd/sharp_vty.h"
+#include "sharpd/sharp_vty_clippy.c"
+
+DEFINE_MTYPE_STATIC(SHARPD, SRV6_LOCATOR, "SRv6 Locator");
+
+DEFPY(watch_redistribute, watch_redistribute_cmd,
+ "sharp watch [vrf NAME$vrf_name] redistribute " FRR_REDIST_STR_SHARPD,
+ "Sharp routing Protocol\n"
+ "Watch for changes\n"
+ "The vrf we would like to watch if non-default\n"
+ "The NAME of the vrf\n"
+ "Redistribute into Sharp\n"
+ FRR_REDIST_HELP_STR_SHARPD)
+{
+ struct vrf *vrf;
+ int source;
+
+ if (!vrf_name)
+ vrf_name = VRF_DEFAULT_NAME;
+ vrf = vrf_lookup_by_name(vrf_name);
+ if (!vrf) {
+ vty_out(vty, "The vrf NAME specified: %s does not exist\n",
+ vrf_name);
+ return CMD_WARNING;
+ }
+
+ source = proto_redistnum(AFI_IP, argv[argc-1]->text);
+ sharp_redistribute_vrf(vrf, source);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(watch_nexthop_v6, watch_nexthop_v6_cmd,
+ "sharp watch [vrf NAME$vrf_name] <nexthop$n X:X::X:X$nhop|import$import X:X::X:X/M$inhop> [connected$connected]",
+ "Sharp routing Protocol\n"
+ "Watch for changes\n"
+ "The vrf we would like to watch if non-default\n"
+ "The NAME of the vrf\n"
+ "Watch for nexthop changes\n"
+ "The v6 nexthop to signal for watching\n"
+ "Watch for import check changes\n"
+ "The v6 prefix to signal for watching\n"
+ "Should the route be connected\n")
+{
+ struct vrf *vrf;
+ struct prefix p;
+ bool type_import;
+
+ if (!vrf_name)
+ vrf_name = VRF_DEFAULT_NAME;
+ vrf = vrf_lookup_by_name(vrf_name);
+ if (!vrf) {
+ vty_out(vty, "The vrf NAME specified: %s does not exist\n",
+ vrf_name);
+ return CMD_WARNING;
+ }
+
+ memset(&p, 0, sizeof(p));
+
+ if (n) {
+ type_import = false;
+ p.prefixlen = IPV6_MAX_BITLEN;
+ memcpy(&p.u.prefix6, &nhop, IPV6_MAX_BYTELEN);
+ p.family = AF_INET6;
+ } else {
+ type_import = true;
+ prefix_copy(&p, inhop);
+ }
+
+ sharp_nh_tracker_get(&p);
+ sharp_zebra_nexthop_watch(&p, vrf->vrf_id, type_import,
+ true, !!connected);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(watch_nexthop_v4, watch_nexthop_v4_cmd,
+ "sharp watch [vrf NAME$vrf_name] <nexthop$n A.B.C.D$nhop|import$import A.B.C.D/M$inhop> [connected$connected]",
+ "Sharp routing Protocol\n"
+ "Watch for changes\n"
+ "The vrf we would like to watch if non-default\n"
+ "The NAME of the vrf\n"
+ "Watch for nexthop changes\n"
+ "The v4 address to signal for watching\n"
+ "Watch for import check changes\n"
+ "The v4 prefix for import check to watch\n"
+ "Should the route be connected\n")
+{
+ struct vrf *vrf;
+ struct prefix p;
+ bool type_import;
+
+ if (!vrf_name)
+ vrf_name = VRF_DEFAULT_NAME;
+ vrf = vrf_lookup_by_name(vrf_name);
+ if (!vrf) {
+ vty_out(vty, "The vrf NAME specified: %s does not exist\n",
+ vrf_name);
+ return CMD_WARNING;
+ }
+
+ memset(&p, 0, sizeof(p));
+
+ if (n) {
+ type_import = false;
+ p.prefixlen = IPV4_MAX_BITLEN;
+ p.u.prefix4 = nhop;
+ p.family = AF_INET;
+ }
+ else {
+ type_import = true;
+ prefix_copy(&p, inhop);
+ }
+
+ sharp_nh_tracker_get(&p);
+ sharp_zebra_nexthop_watch(&p, vrf->vrf_id, type_import,
+ true, !!connected);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(sharp_nht_data_dump,
+ sharp_nht_data_dump_cmd,
+ "sharp data nexthop",
+ "Sharp routing Protocol\n"
+ "Data about what is going on\n"
+ "Nexthop information\n")
+{
+ sharp_nh_tracker_dump(vty);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (install_routes_data_dump,
+ install_routes_data_dump_cmd,
+ "sharp data route",
+ "Sharp routing Protocol\n"
+ "Data about what is going on\n"
+ "Route Install/Removal Information\n")
+{
+ struct timeval r;
+
+ timersub(&sg.r.t_end, &sg.r.t_start, &r);
+ vty_out(vty, "Prefix: %pFX Total: %u %u %u Time: %jd.%ld\n",
+ &sg.r.orig_prefix, sg.r.total_routes, sg.r.installed_routes,
+ sg.r.removed_routes, (intmax_t)r.tv_sec, (long)r.tv_usec);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (install_routes,
+ install_routes_cmd,
+ "sharp install routes [vrf NAME$vrf_name]\
+ <A.B.C.D$start4|X:X::X:X$start6>\
+ <nexthop <A.B.C.D$nexthop4|X:X::X:X$nexthop6>|\
+ nexthop-group NHGNAME$nexthop_group>\
+ [backup$backup <A.B.C.D$backup_nexthop4|X:X::X:X$backup_nexthop6>] \
+ (1-1000000)$routes [instance (0-255)$instance] [repeat (2-1000)$rpt] [opaque WORD] [no-recurse$norecurse]",
+ "Sharp routing Protocol\n"
+ "install some routes\n"
+ "Routes to install\n"
+ "The vrf we would like to install into if non-default\n"
+ "The NAME of the vrf\n"
+ "v4 Address to start /32 generation at\n"
+ "v6 Address to start /32 generation at\n"
+ "Nexthop to use(Can be an IPv4 or IPv6 address)\n"
+ "V4 Nexthop address to use\n"
+ "V6 Nexthop address to use\n"
+ "Nexthop-Group to use\n"
+ "The Name of the nexthop-group\n"
+ "Backup nexthop to use(Can be an IPv4 or IPv6 address)\n"
+ "Backup V4 Nexthop address to use\n"
+ "Backup V6 Nexthop address to use\n"
+ "How many to create\n"
+ "Instance to use\n"
+ "Instance\n"
+ "Should we repeat this command\n"
+ "How many times to repeat this command\n"
+ "What opaque data to send down\n"
+ "The opaque data\n"
+ "No recursive nexthops\n")
+{
+ struct vrf *vrf;
+ struct prefix prefix;
+ uint32_t rts;
+ uint32_t nhgid = 0;
+
+ sg.r.total_routes = routes;
+ sg.r.installed_routes = 0;
+ sg.r.flags = 0;
+
+ if (rpt >= 2)
+ sg.r.repeat = rpt * 2;
+ else
+ sg.r.repeat = 0;
+
+ memset(&prefix, 0, sizeof(prefix));
+ memset(&sg.r.orig_prefix, 0, sizeof(sg.r.orig_prefix));
+ nexthop_del_srv6_seg6local(&sg.r.nhop);
+ nexthop_del_srv6_seg6(&sg.r.nhop);
+ memset(&sg.r.nhop, 0, sizeof(sg.r.nhop));
+ memset(&sg.r.nhop_group, 0, sizeof(sg.r.nhop_group));
+ memset(&sg.r.backup_nhop, 0, sizeof(sg.r.nhop));
+ memset(&sg.r.backup_nhop_group, 0, sizeof(sg.r.nhop_group));
+
+ if (start4.s_addr != INADDR_ANY) {
+ prefix.family = AF_INET;
+ prefix.prefixlen = IPV4_MAX_BITLEN;
+ prefix.u.prefix4 = start4;
+ } else {
+ prefix.family = AF_INET6;
+ prefix.prefixlen = IPV6_MAX_BITLEN;
+ prefix.u.prefix6 = start6;
+ }
+ sg.r.orig_prefix = prefix;
+
+ if (!vrf_name)
+ vrf_name = VRF_DEFAULT_NAME;
+
+ vrf = vrf_lookup_by_name(vrf_name);
+ if (!vrf) {
+ vty_out(vty, "The vrf NAME specified: %s does not exist\n",
+ vrf_name);
+ return CMD_WARNING;
+ }
+
+ /* Explicit backup not available with named nexthop-group */
+ if (backup && nexthop_group) {
+ vty_out(vty, "%% Invalid: cannot specify both nexthop-group and backup\n");
+ return CMD_WARNING;
+ }
+
+ if (nexthop_group) {
+ struct nexthop_group_cmd *nhgc = nhgc_find(nexthop_group);
+ if (!nhgc) {
+ vty_out(vty,
+ "Specified Nexthop Group: %s does not exist\n",
+ nexthop_group);
+ return CMD_WARNING;
+ }
+
+ nhgid = sharp_nhgroup_get_id(nexthop_group);
+ sg.r.nhgid = nhgid;
+ sg.r.nhop_group.nexthop = nhgc->nhg.nexthop;
+
+ /* Use group's backup nexthop info if present */
+ if (nhgc->backup_list_name[0]) {
+ struct nexthop_group_cmd *bnhgc =
+ nhgc_find(nhgc->backup_list_name);
+
+ if (!bnhgc) {
+ vty_out(vty, "%% Backup group %s not found for group %s\n",
+ nhgc->backup_list_name,
+ nhgc->name);
+ return CMD_WARNING;
+ }
+
+ sg.r.backup_nhop.vrf_id = vrf->vrf_id;
+ sg.r.backup_nhop_group.nexthop = bnhgc->nhg.nexthop;
+ }
+ } else {
+ if (nexthop4.s_addr != INADDR_ANY) {
+ sg.r.nhop.gate.ipv4 = nexthop4;
+ sg.r.nhop.type = NEXTHOP_TYPE_IPV4;
+ } else {
+ sg.r.nhop.gate.ipv6 = nexthop6;
+ sg.r.nhop.type = NEXTHOP_TYPE_IPV6;
+ }
+
+ sg.r.nhop.vrf_id = vrf->vrf_id;
+ sg.r.nhop_group.nexthop = &sg.r.nhop;
+ }
+
+ /* Use single backup nexthop if specified */
+ if (backup) {
+ /* Set flag and index in primary nexthop */
+ SET_FLAG(sg.r.nhop.flags, NEXTHOP_FLAG_HAS_BACKUP);
+ sg.r.nhop.backup_num = 1;
+ sg.r.nhop.backup_idx[0] = 0;
+
+ if (backup_nexthop4.s_addr != INADDR_ANY) {
+ sg.r.backup_nhop.gate.ipv4 = backup_nexthop4;
+ sg.r.backup_nhop.type = NEXTHOP_TYPE_IPV4;
+ } else {
+ sg.r.backup_nhop.gate.ipv6 = backup_nexthop6;
+ sg.r.backup_nhop.type = NEXTHOP_TYPE_IPV6;
+ }
+
+ sg.r.backup_nhop.vrf_id = vrf->vrf_id;
+ sg.r.backup_nhop_group.nexthop = &sg.r.backup_nhop;
+ }
+
+ if (opaque)
+ strlcpy(sg.r.opaque, opaque, ZAPI_MESSAGE_OPAQUE_LENGTH);
+ else
+ sg.r.opaque[0] = '\0';
+
+ /* Default is to ask for recursive nexthop resolution */
+ if (norecurse == NULL)
+ SET_FLAG(sg.r.flags, ZEBRA_FLAG_ALLOW_RECURSION);
+
+ sg.r.inst = instance;
+ sg.r.vrf_id = vrf->vrf_id;
+ rts = routes;
+ sharp_install_routes_helper(&prefix, sg.r.vrf_id, sg.r.inst, nhgid,
+ &sg.r.nhop_group, &sg.r.backup_nhop_group,
+ rts, sg.r.flags, sg.r.opaque);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (install_seg6_routes,
+ install_seg6_routes_cmd,
+ "sharp install seg6-routes [vrf NAME$vrf_name]\
+ <A.B.C.D$start4|X:X::X:X$start6>\
+ nexthop-seg6 X:X::X:X$seg6_nh6 encap X:X::X:X$seg6_seg\
+ (1-1000000)$routes [repeat (2-1000)$rpt]",
+ "Sharp routing Protocol\n"
+ "install some routes\n"
+ "Routes to install\n"
+ "The vrf we would like to install into if non-default\n"
+ "The NAME of the vrf\n"
+ "v4 Address to start /32 generation at\n"
+ "v6 Address to start /32 generation at\n"
+ "Nexthop-seg6 to use\n"
+ "V6 Nexthop address to use\n"
+ "Encap mode\n"
+ "Segment List to use\n"
+ "How many to create\n"
+ "Should we repeat this command\n"
+ "How many times to repeat this command\n")
+{
+ struct vrf *vrf;
+ struct prefix prefix;
+ uint32_t route_flags = 0;
+
+ sg.r.total_routes = routes;
+ sg.r.installed_routes = 0;
+
+ if (rpt >= 2)
+ sg.r.repeat = rpt * 2;
+ else
+ sg.r.repeat = 0;
+
+ memset(&prefix, 0, sizeof(prefix));
+ memset(&sg.r.orig_prefix, 0, sizeof(sg.r.orig_prefix));
+ nexthop_del_srv6_seg6local(&sg.r.nhop);
+ nexthop_del_srv6_seg6(&sg.r.nhop);
+ memset(&sg.r.nhop, 0, sizeof(sg.r.nhop));
+ memset(&sg.r.nhop_group, 0, sizeof(sg.r.nhop_group));
+ memset(&sg.r.backup_nhop, 0, sizeof(sg.r.nhop));
+ memset(&sg.r.backup_nhop_group, 0, sizeof(sg.r.nhop_group));
+ sg.r.opaque[0] = '\0';
+ sg.r.inst = 0;
+
+ if (start4.s_addr != INADDR_ANY) {
+ prefix.family = AF_INET;
+ prefix.prefixlen = IPV4_MAX_BITLEN;
+ prefix.u.prefix4 = start4;
+ } else {
+ prefix.family = AF_INET6;
+ prefix.prefixlen = IPV6_MAX_BITLEN;
+ prefix.u.prefix6 = start6;
+ }
+ sg.r.orig_prefix = prefix;
+
+ if (!vrf_name)
+ vrf_name = VRF_DEFAULT_NAME;
+
+ vrf = vrf_lookup_by_name(vrf_name);
+ if (!vrf) {
+ vty_out(vty, "The vrf NAME specified: %s does not exist\n",
+ vrf_name);
+ return CMD_WARNING;
+ }
+
+ sg.r.nhop.type = NEXTHOP_TYPE_IPV6;
+ sg.r.nhop.gate.ipv6 = seg6_nh6;
+ sg.r.nhop.vrf_id = vrf->vrf_id;
+ sg.r.nhop_group.nexthop = &sg.r.nhop;
+ nexthop_add_srv6_seg6(&sg.r.nhop, &seg6_seg, 1);
+
+ sg.r.vrf_id = vrf->vrf_id;
+ sharp_install_routes_helper(&prefix, sg.r.vrf_id, sg.r.inst, 0,
+ &sg.r.nhop_group, &sg.r.backup_nhop_group,
+ routes, route_flags, sg.r.opaque);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (install_seg6local_routes,
+ install_seg6local_routes_cmd,
+ "sharp install seg6local-routes [vrf NAME$vrf_name]\
+ X:X::X:X$start6\
+ nexthop-seg6local NAME$seg6l_oif\
+ <End$seg6l_end|\
+ End_X$seg6l_endx X:X::X:X$seg6l_endx_nh6|\
+ End_T$seg6l_endt (1-4294967295)$seg6l_endt_table|\
+ End_DX4$seg6l_enddx4 A.B.C.D$seg6l_enddx4_nh4|\
+ End_DT6$seg6l_enddt6 (1-4294967295)$seg6l_enddt6_table|\
+ End_DT4$seg6l_enddt4 (1-4294967295)$seg6l_enddt4_table|\
+ End_DT46$seg6l_enddt46 (1-4294967295)$seg6l_enddt46_table>\
+ (1-1000000)$routes [repeat (2-1000)$rpt]",
+ "Sharp routing Protocol\n"
+ "install some routes\n"
+ "Routes to install\n"
+ "The vrf we would like to install into if non-default\n"
+ "The NAME of the vrf\n"
+ "v6 Address to start /32 generation at\n"
+ "Nexthop-seg6local to use\n"
+ "Output device to use\n"
+ "SRv6 End function to use\n"
+ "SRv6 End.X function to use\n"
+ "V6 Nexthop address to use\n"
+ "SRv6 End.T function to use\n"
+ "Redirect table id to use\n"
+ "SRv6 End.DX4 function to use\n"
+ "V4 Nexthop address to use\n"
+ "SRv6 End.DT6 function to use\n"
+ "Redirect table id to use\n"
+ "SRv6 End.DT4 function to use\n"
+ "Redirect table id to use\n"
+ "SRv6 End.DT46 function to use\n"
+ "Redirect table id to use\n"
+ "How many to create\n"
+ "Should we repeat this command\n"
+ "How many times to repeat this command\n")
+{
+ struct vrf *vrf;
+ uint32_t route_flags = 0;
+ struct seg6local_context ctx = {};
+ enum seg6local_action_t action;
+
+ sg.r.total_routes = routes;
+ sg.r.installed_routes = 0;
+
+ if (rpt >= 2)
+ sg.r.repeat = rpt * 2;
+ else
+ sg.r.repeat = 0;
+
+ memset(&sg.r.orig_prefix, 0, sizeof(sg.r.orig_prefix));
+ nexthop_del_srv6_seg6local(&sg.r.nhop);
+ nexthop_del_srv6_seg6(&sg.r.nhop);
+ memset(&sg.r.nhop, 0, sizeof(sg.r.nhop));
+ memset(&sg.r.nhop_group, 0, sizeof(sg.r.nhop_group));
+ memset(&sg.r.backup_nhop, 0, sizeof(sg.r.nhop));
+ memset(&sg.r.backup_nhop_group, 0, sizeof(sg.r.nhop_group));
+ sg.r.opaque[0] = '\0';
+ sg.r.inst = 0;
+ sg.r.orig_prefix.family = AF_INET6;
+ sg.r.orig_prefix.prefixlen = 128;
+ sg.r.orig_prefix.u.prefix6 = start6;
+
+ if (!vrf_name)
+ vrf_name = VRF_DEFAULT_NAME;
+
+ vrf = vrf_lookup_by_name(vrf_name);
+ if (!vrf) {
+ vty_out(vty, "The vrf NAME specified: %s does not exist\n",
+ vrf_name);
+ return CMD_WARNING;
+ }
+
+ if (seg6l_enddx4) {
+ action = ZEBRA_SEG6_LOCAL_ACTION_END_DX4;
+ ctx.nh4 = seg6l_enddx4_nh4;
+ } else if (seg6l_endx) {
+ action = ZEBRA_SEG6_LOCAL_ACTION_END_X;
+ ctx.nh6 = seg6l_endx_nh6;
+ } else if (seg6l_endt) {
+ action = ZEBRA_SEG6_LOCAL_ACTION_END_T;
+ ctx.table = seg6l_endt_table;
+ } else if (seg6l_enddt6) {
+ action = ZEBRA_SEG6_LOCAL_ACTION_END_DT6;
+ ctx.table = seg6l_enddt6_table;
+ } else if (seg6l_enddt4) {
+ action = ZEBRA_SEG6_LOCAL_ACTION_END_DT4;
+ ctx.table = seg6l_enddt4_table;
+ } else if (seg6l_enddt46) {
+ action = ZEBRA_SEG6_LOCAL_ACTION_END_DT46;
+ ctx.table = seg6l_enddt46_table;
+ } else {
+ action = ZEBRA_SEG6_LOCAL_ACTION_END;
+ }
+
+ sg.r.nhop.type = NEXTHOP_TYPE_IFINDEX;
+ sg.r.nhop.ifindex = ifname2ifindex(seg6l_oif, vrf->vrf_id);
+ sg.r.nhop.vrf_id = vrf->vrf_id;
+ sg.r.nhop_group.nexthop = &sg.r.nhop;
+ nexthop_add_srv6_seg6local(&sg.r.nhop, action, &ctx);
+
+ sg.r.vrf_id = vrf->vrf_id;
+ sharp_install_routes_helper(&sg.r.orig_prefix, sg.r.vrf_id, sg.r.inst,
+ 0, &sg.r.nhop_group,
+ &sg.r.backup_nhop_group, routes,
+ route_flags, sg.r.opaque);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(vrf_label, vrf_label_cmd,
+ "sharp label <ip$ipv4|ipv6$ipv6> vrf NAME$vrf_name label (0-100000)$label",
+ "Sharp Routing Protocol\n"
+ "Give a vrf a label\n"
+ "Pop and forward for IPv4\n"
+ "Pop and forward for IPv6\n"
+ VRF_CMD_HELP_STR
+ "The label to use, 0 specifies remove the label installed from previous\n"
+ "Specified range to use\n")
+{
+ struct vrf *vrf;
+ afi_t afi = (ipv4) ? AFI_IP : AFI_IP6;
+
+ if (strcmp(vrf_name, "default") == 0)
+ vrf = vrf_lookup_by_id(VRF_DEFAULT);
+ else
+ vrf = vrf_lookup_by_name(vrf_name);
+
+ if (!vrf) {
+ vty_out(vty, "Unable to find vrf you silly head\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (label == 0)
+ label = MPLS_LABEL_NONE;
+
+ vrf_label_add(vrf->vrf_id, afi, label);
+ return CMD_SUCCESS;
+}
+
+DEFPY (remove_routes,
+ remove_routes_cmd,
+ "sharp remove routes [vrf NAME$vrf_name] <A.B.C.D$start4|X:X::X:X$start6> (1-1000000)$routes [instance (0-255)$instance]",
+ "Sharp Routing Protocol\n"
+ "Remove some routes\n"
+ "Routes to remove\n"
+ "The vrf we would like to remove from if non-default\n"
+ "The NAME of the vrf\n"
+ "v4 Starting spot\n"
+ "v6 Starting spot\n"
+ "Routes to uninstall\n"
+ "instance to use\n"
+ "Value of instance\n")
+{
+ struct vrf *vrf;
+ struct prefix prefix;
+
+ sg.r.total_routes = routes;
+ sg.r.removed_routes = 0;
+ uint32_t rts;
+
+ memset(&prefix, 0, sizeof(prefix));
+
+ if (start4.s_addr != INADDR_ANY) {
+ prefix.family = AF_INET;
+ prefix.prefixlen = IPV4_MAX_BITLEN;
+ prefix.u.prefix4 = start4;
+ } else {
+ prefix.family = AF_INET6;
+ prefix.prefixlen = IPV6_MAX_BITLEN;
+ prefix.u.prefix6 = start6;
+ }
+
+ vrf = vrf_lookup_by_name(vrf_name ? vrf_name : VRF_DEFAULT_NAME);
+ if (!vrf) {
+ vty_out(vty, "The vrf NAME specified: %s does not exist\n",
+ vrf_name ? vrf_name : VRF_DEFAULT_NAME);
+ return CMD_WARNING;
+ }
+
+ sg.r.inst = instance;
+ sg.r.vrf_id = vrf->vrf_id;
+ rts = routes;
+ sharp_remove_routes_helper(&prefix, sg.r.vrf_id,
+ sg.r.inst, rts);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_NOSH (show_debugging_sharpd,
+ show_debugging_sharpd_cmd,
+ "show debugging [sharp]",
+ SHOW_STR
+ DEBUG_STR
+ "Sharp Information\n")
+{
+ vty_out(vty, "Sharp debugging status:\n");
+
+ cmd_show_lib_debugs(vty);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (sharp_lsp_prefix_v4, sharp_lsp_prefix_v4_cmd,
+ "sharp lsp [update]$update (0-100000)$inlabel\
+ nexthop-group NHGNAME$nhgname\
+ [prefix A.B.C.D/M$pfx\
+ " FRR_IP_REDIST_STR_ZEBRA "$type_str [instance (0-255)$instance]]",
+ "Sharp Routing Protocol\n"
+ "Add an LSP\n"
+ "Update an LSP\n"
+ "The ingress label to use\n"
+ "Use nexthops from a nexthop-group\n"
+ "The nexthop-group name\n"
+ "Label a prefix\n"
+ "The v4 prefix to label\n"
+ FRR_IP_REDIST_HELP_STR_ZEBRA
+ "Instance to use\n"
+ "Instance\n")
+{
+ struct nexthop_group_cmd *nhgc = NULL;
+ struct nexthop_group_cmd *backup_nhgc = NULL;
+ struct nexthop_group *backup_nhg = NULL;
+ struct prefix p = {};
+ int type = 0;
+ bool update_p;
+
+ update_p = (update != NULL);
+
+ /* We're offered a v4 prefix */
+ if (pfx->family > 0 && type_str) {
+ p.family = pfx->family;
+ p.prefixlen = pfx->prefixlen;
+ p.u.prefix4 = pfx->prefix;
+
+ type = proto_redistnum(AFI_IP, type_str);
+ if (type < 0) {
+ vty_out(vty, "%% Unknown route type '%s'\n", type_str);
+ return CMD_WARNING;
+ }
+ } else if (pfx->family > 0 || type_str) {
+ vty_out(vty, "%% Must supply both prefix and type\n");
+ return CMD_WARNING;
+ }
+
+ nhgc = nhgc_find(nhgname);
+ if (!nhgc) {
+ vty_out(vty, "%% Nexthop-group '%s' does not exist\n",
+ nhgname);
+ return CMD_WARNING;
+ }
+
+ if (nhgc->nhg.nexthop == NULL) {
+ vty_out(vty, "%% Nexthop-group '%s' is empty\n", nhgname);
+ return CMD_WARNING;
+ }
+
+ /* Use group's backup nexthop info if present */
+ if (nhgc->backup_list_name[0]) {
+ backup_nhgc = nhgc_find(nhgc->backup_list_name);
+
+ if (!backup_nhgc) {
+ vty_out(vty,
+ "%% Backup group %s not found for group %s\n",
+ nhgc->backup_list_name,
+ nhgname);
+ return CMD_WARNING;
+ }
+ backup_nhg = &(backup_nhgc->nhg);
+ }
+
+ if (sharp_install_lsps_helper(true /*install*/, update_p,
+ pfx->family > 0 ? &p : NULL,
+ type, instance, inlabel,
+ &(nhgc->nhg), backup_nhg) == 0)
+ return CMD_SUCCESS;
+ else {
+ vty_out(vty, "%% LSP install failed!\n");
+ return CMD_WARNING;
+ }
+}
+
+DEFPY(sharp_remove_lsp_prefix_v4, sharp_remove_lsp_prefix_v4_cmd,
+ "sharp remove lsp \
+ (0-100000)$inlabel\
+ [nexthop-group NHGNAME$nhgname] \
+ [prefix A.B.C.D/M$pfx\
+ " FRR_IP_REDIST_STR_ZEBRA "$type_str [instance (0-255)$instance]]",
+ "Sharp Routing Protocol\n"
+ "Remove data\n"
+ "Remove an LSP\n"
+ "The ingress label\n"
+ "Use nexthops from a nexthop-group\n"
+ "The nexthop-group name\n"
+ "Specify a v4 prefix\n"
+ "The v4 prefix to label\n"
+ FRR_IP_REDIST_HELP_STR_ZEBRA
+ "Routing instance\n"
+ "Instance to use\n")
+{
+ struct nexthop_group_cmd *nhgc = NULL;
+ struct prefix p = {};
+ int type = 0;
+ struct nexthop_group *nhg = NULL;
+
+ /* We're offered a v4 prefix */
+ if (pfx->family > 0 && type_str) {
+ p.family = pfx->family;
+ p.prefixlen = pfx->prefixlen;
+ p.u.prefix4 = pfx->prefix;
+
+ type = proto_redistnum(AFI_IP, type_str);
+ if (type < 0) {
+ vty_out(vty, "%% Unknown route type '%s'\n", type_str);
+ return CMD_WARNING;
+ }
+ } else if (pfx->family > 0 || type_str) {
+ vty_out(vty, "%% Must supply both prefix and type\n");
+ return CMD_WARNING;
+ }
+
+ if (nhgname) {
+ nhgc = nhgc_find(nhgname);
+ if (!nhgc) {
+ vty_out(vty, "%% Nexthop-group '%s' does not exist\n",
+ nhgname);
+ return CMD_WARNING;
+ }
+
+ if (nhgc->nhg.nexthop == NULL) {
+ vty_out(vty, "%% Nexthop-group '%s' is empty\n",
+ nhgname);
+ return CMD_WARNING;
+ }
+ nhg = &(nhgc->nhg);
+ }
+
+ if (sharp_install_lsps_helper(false /*!install*/, false,
+ pfx->family > 0 ? &p : NULL,
+ type, instance, inlabel, nhg, NULL) == 0)
+ return CMD_SUCCESS;
+ else {
+ vty_out(vty, "%% LSP remove failed!\n");
+ return CMD_WARNING;
+ }
+}
+
+DEFPY (logpump,
+ logpump_cmd,
+ "sharp logpump duration (1-60) frequency (1-1000000) burst (1-1000)",
+ "Sharp Routing Protocol\n"
+ "Generate bulk log messages for testing\n"
+ "Duration of run (s)\n"
+ "Duration of run (s)\n"
+ "Frequency of bursts (s^-1)\n"
+ "Frequency of bursts (s^-1)\n"
+ "Number of log messages per each burst\n"
+ "Number of log messages per each burst\n")
+{
+ sharp_logpump_run(vty, duration, frequency, burst);
+ return CMD_SUCCESS;
+}
+
+DEFPY (create_session,
+ create_session_cmd,
+ "sharp create session (1-1024)",
+ "Sharp Routing Protocol\n"
+ "Create data\n"
+ "Create a test session\n"
+ "Session ID\n")
+{
+ if (sharp_zclient_create(session) != 0) {
+ vty_out(vty, "%% Client session error\n");
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (remove_session,
+ remove_session_cmd,
+ "sharp remove session (1-1024)",
+ "Sharp Routing Protocol\n"
+ "Remove data\n"
+ "Remove a test session\n"
+ "Session ID\n")
+{
+ sharp_zclient_delete(session);
+ return CMD_SUCCESS;
+}
+
+DEFPY (send_opaque,
+ send_opaque_cmd,
+ "sharp send opaque type (1-255) (1-1000)$count",
+ SHARP_STR
+ "Send messages for testing\n"
+ "Send opaque messages\n"
+ "Type code to send\n"
+ "Type code to send\n"
+ "Number of messages to send\n")
+{
+ sharp_opaque_send(type, 0, 0, 0, count);
+ return CMD_SUCCESS;
+}
+
+DEFPY (send_opaque_unicast,
+ send_opaque_unicast_cmd,
+ "sharp send opaque unicast type (1-255) \
+ " FRR_IP_REDIST_STR_ZEBRA "$proto_str \
+ [{instance (0-1000) | session (1-1000)}] (1-1000)$count",
+ SHARP_STR
+ "Send messages for testing\n"
+ "Send opaque messages\n"
+ "Send unicast messages\n"
+ "Type code to send\n"
+ "Type code to send\n"
+ FRR_IP_REDIST_HELP_STR_ZEBRA
+ "Daemon instance\n"
+ "Daemon instance\n"
+ "Session ID\n"
+ "Session ID\n"
+ "Number of messages to send\n")
+{
+ uint32_t proto;
+
+ proto = proto_redistnum(AFI_IP, proto_str);
+
+ sharp_opaque_send(type, proto, instance, session, count);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (send_opaque_reg,
+ send_opaque_reg_cmd,
+ "sharp send opaque <reg$reg | unreg> \
+ " FRR_IP_REDIST_STR_ZEBRA "$proto_str \
+ [{instance (0-1000) | session (1-1000)}] type (1-1000)",
+ SHARP_STR
+ "Send messages for testing\n"
+ "Send opaque messages\n"
+ "Send opaque registration\n"
+ "Send opaque unregistration\n"
+ FRR_IP_REDIST_HELP_STR_ZEBRA
+ "Daemon instance\n"
+ "Daemon instance\n"
+ "Session ID\n"
+ "Session ID\n"
+ "Opaque sub-type code\n"
+ "Opaque sub-type code\n")
+{
+ int proto;
+
+ proto = proto_redistnum(AFI_IP, proto_str);
+
+ sharp_opaque_reg_send((reg != NULL), proto, instance, session, type);
+ return CMD_SUCCESS;
+}
+
+/* Opaque notifications - register or unregister */
+DEFPY (send_opaque_notif_reg,
+ send_opaque_notif_reg_cmd,
+ "sharp send opaque notify <reg$reg | unreg> type (1-1000)",
+ SHARP_STR
+ "Send messages for testing\n"
+ "Send opaque messages\n"
+ "Opaque notification messages\n"
+ "Send notify registration\n"
+ "Send notify unregistration\n"
+ "Opaque sub-type code\n"
+ "Opaque sub-type code\n")
+{
+ sharp_zebra_opaque_notif_reg((reg != NULL), type);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (neigh_discover,
+ neigh_discover_cmd,
+ "sharp neigh discover [vrf NAME$vrf_name] <A.B.C.D$dst4|X:X::X:X$dst6> IFNAME$ifname",
+ SHARP_STR
+ "Discover neighbours\n"
+ "Send an ARP/NDP request\n"
+ VRF_CMD_HELP_STR
+ "v4 Destination address\n"
+ "v6 Destination address\n"
+ "Interface name\n")
+{
+ struct vrf *vrf;
+ struct interface *ifp;
+ struct prefix prefix;
+
+ memset(&prefix, 0, sizeof(prefix));
+
+ if (dst4.s_addr != INADDR_ANY) {
+ prefix.family = AF_INET;
+ prefix.prefixlen = IPV4_MAX_BITLEN;
+ prefix.u.prefix4 = dst4;
+ } else {
+ prefix.family = AF_INET6;
+ prefix.prefixlen = 128;
+ prefix.u.prefix6 = dst6;
+ }
+
+ vrf = vrf_lookup_by_name(vrf_name ? vrf_name : VRF_DEFAULT_NAME);
+ if (!vrf) {
+ vty_out(vty, "The vrf NAME specified: %s does not exist\n",
+ vrf_name ? vrf_name : VRF_DEFAULT_NAME);
+ return CMD_WARNING;
+ }
+
+ ifp = if_lookup_by_name_vrf(ifname, vrf);
+ if (ifp == NULL) {
+ vty_out(vty, "%% Can't find interface %s\n", ifname);
+ return CMD_WARNING;
+ }
+
+ sharp_zebra_send_arp(ifp, &prefix);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (import_te,
+ import_te_cmd,
+ "sharp import-te",
+ SHARP_STR
+ "Import Traffic Engineering\n")
+{
+ sg.ted = ls_ted_new(1, "Sharp", 0);
+ sharp_zebra_register_te();
+
+ return CMD_SUCCESS;
+}
+
+static void sharp_srv6_locator_chunk_free(struct prefix_ipv6 *chunk)
+{
+ prefix_ipv6_free(&chunk);
+}
+
+DEFPY (sharp_srv6_manager_get_locator_chunk,
+ sharp_srv6_manager_get_locator_chunk_cmd,
+ "sharp srv6-manager get-locator-chunk NAME$locator_name",
+ SHARP_STR
+ "Segment-Routing IPv6\n"
+ "Get SRv6 locator-chunk\n"
+ "SRv6 Locator name\n")
+{
+ int ret;
+ struct listnode *node;
+ struct sharp_srv6_locator *loc;
+ struct sharp_srv6_locator *loc_found = NULL;
+
+ for (ALL_LIST_ELEMENTS_RO(sg.srv6_locators, node, loc)) {
+ if (strcmp(loc->name, locator_name))
+ continue;
+ loc_found = loc;
+ break;
+ }
+ if (!loc_found) {
+ loc = XCALLOC(MTYPE_SRV6_LOCATOR,
+ sizeof(struct sharp_srv6_locator));
+ loc->chunks = list_new();
+ loc->chunks->del =
+ (void (*)(void *))sharp_srv6_locator_chunk_free;
+ snprintf(loc->name, SRV6_LOCNAME_SIZE, "%s", locator_name);
+ listnode_add(sg.srv6_locators, loc);
+ }
+
+ ret = sharp_zebra_srv6_manager_get_locator_chunk(locator_name);
+ if (ret < 0)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_sharp_ted,
+ show_sharp_ted_cmd,
+ "show sharp ted [<vertex [A.B.C.D]|edge [A.B.C.D]|subnet [A.B.C.D/M]>] [verbose|json]",
+ SHOW_STR
+ SHARP_STR
+ "Traffic Engineering Database\n"
+ "MPLS-TE Vertex\n"
+ "MPLS-TE router ID (as an IP address)\n"
+ "MPLS-TE Edge\n"
+ "MPLS-TE Edge ID (as an IP address)\n"
+ "MPLS-TE Subnet\n"
+ "MPLS-TE Subnet ID (as an IP prefix)\n"
+ "Verbose output\n"
+ JSON_STR)
+{
+ int idx = 0;
+ struct in_addr ip_addr;
+ struct prefix pref;
+ struct ls_vertex *vertex;
+ struct ls_edge *edge;
+ struct ls_subnet *subnet;
+ uint64_t key;
+ struct ls_edge_key ekey;
+ bool verbose = false;
+ bool uj = use_json(argc, argv);
+ json_object *json = NULL;
+
+ if (sg.ted == NULL) {
+ vty_out(vty, "MPLS-TE import is not enabled\n");
+ return CMD_WARNING;
+ }
+
+ if (uj)
+ json = json_object_new_object();
+
+ if (argv[argc - 1]->arg && strmatch(argv[argc - 1]->text, "verbose"))
+ verbose = true;
+
+ if (argv_find(argv, argc, "vertex", &idx)) {
+ /* Show Vertex */
+ if (argv_find(argv, argc, "A.B.C.D", &idx)) {
+ if (!inet_aton(argv[idx + 1]->arg, &ip_addr)) {
+ vty_out(vty,
+ "Specified Router ID %s is invalid\n",
+ argv[idx + 1]->arg);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ /* Get the Vertex from the Link State Database */
+ key = ((uint64_t)ip_addr.s_addr) & 0xffffffff;
+ vertex = ls_find_vertex_by_key(sg.ted, key);
+ if (!vertex) {
+ vty_out(vty, "No vertex found for ID %pI4\n",
+ &ip_addr);
+ return CMD_WARNING;
+ }
+ } else
+ vertex = NULL;
+
+ if (vertex)
+ ls_show_vertex(vertex, vty, json, verbose);
+ else
+ ls_show_vertices(sg.ted, vty, json, verbose);
+
+ } else if (argv_find(argv, argc, "edge", &idx)) {
+ /* Show Edge */
+ if (argv_find(argv, argc, "A.B.C.D", &idx)) {
+ if (!inet_aton(argv[idx]->arg, &ip_addr)) {
+ vty_out(vty,
+ "Specified Edge ID %s is invalid\n",
+ argv[idx]->arg);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ /* Get the Edge from the Link State Database */
+ ekey.family = AF_INET;
+ IPV4_ADDR_COPY(&ekey.k.addr, &ip_addr);
+ edge = ls_find_edge_by_key(sg.ted, ekey);
+ if (!edge) {
+ vty_out(vty, "No edge found for ID %pI4\n",
+ &ip_addr);
+ return CMD_WARNING;
+ }
+ } else
+ edge = NULL;
+
+ if (edge)
+ ls_show_edge(edge, vty, json, verbose);
+ else
+ ls_show_edges(sg.ted, vty, json, verbose);
+
+ } else if (argv_find(argv, argc, "subnet", &idx)) {
+ /* Show Subnet */
+ if (argv_find(argv, argc, "A.B.C.D/M", &idx)) {
+ if (!str2prefix(argv[idx]->arg, &pref)) {
+ vty_out(vty, "Invalid prefix format %s\n",
+ argv[idx]->arg);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ /* Get the Subnet from the Link State Database */
+ subnet = ls_find_subnet(sg.ted, &pref);
+ if (!subnet) {
+ vty_out(vty, "No subnet found for ID %pFX\n",
+ &pref);
+ return CMD_WARNING;
+ }
+ } else
+ subnet = NULL;
+
+ if (subnet)
+ ls_show_subnet(subnet, vty, json, verbose);
+ else
+ ls_show_subnets(sg.ted, vty, json, verbose);
+
+ } else {
+ /* Show the complete TED */
+ ls_show_ted(sg.ted, vty, json, verbose);
+ }
+
+ if (uj)
+ vty_json(vty, json);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (sharp_srv6_manager_release_locator_chunk,
+ sharp_srv6_manager_release_locator_chunk_cmd,
+ "sharp srv6-manager release-locator-chunk NAME$locator_name",
+ SHARP_STR
+ "Segment-Routing IPv6\n"
+ "Release SRv6 locator-chunk\n"
+ "SRv6 Locator name\n")
+{
+ int ret;
+ struct listnode *loc_node;
+ struct sharp_srv6_locator *loc;
+
+ for (ALL_LIST_ELEMENTS_RO(sg.srv6_locators, loc_node, loc)) {
+ if (!strcmp(loc->name, locator_name)) {
+ list_delete_all_node(loc->chunks);
+ list_delete(&loc->chunks);
+ listnode_delete(sg.srv6_locators, loc);
+ XFREE(MTYPE_SRV6_LOCATOR, loc);
+ break;
+ }
+ }
+
+ ret = sharp_zebra_srv6_manager_release_locator_chunk(locator_name);
+ if (ret < 0)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (show_sharp_segment_routing_srv6,
+ show_sharp_segment_routing_srv6_cmd,
+ "show sharp segment-routing srv6 [json]",
+ SHOW_STR
+ SHARP_STR
+ "Segment-Routing\n"
+ "Segment-Routing IPv6\n"
+ JSON_STR)
+{
+ char str[256];
+ struct listnode *loc_node;
+ struct listnode *chunk_node;
+ struct sharp_srv6_locator *loc;
+ struct prefix_ipv6 *chunk;
+ bool uj = use_json(argc, argv);
+ json_object *jo_locs = NULL;
+ json_object *jo_loc = NULL;
+ json_object *jo_chunks = NULL;
+
+ if (uj) {
+ jo_locs = json_object_new_array();
+ for (ALL_LIST_ELEMENTS_RO(sg.srv6_locators, loc_node, loc)) {
+ jo_loc = json_object_new_object();
+ json_object_array_add(jo_locs, jo_loc);
+ json_object_string_add(jo_loc, "name", loc->name);
+ jo_chunks = json_object_new_array();
+ json_object_object_add(jo_loc, "chunks", jo_chunks);
+ for (ALL_LIST_ELEMENTS_RO(loc->chunks, chunk_node,
+ chunk)) {
+ prefix2str(chunk, str, sizeof(str));
+ json_array_string_add(jo_chunks, str);
+ }
+ }
+
+ vty_json(vty, jo_locs);
+ } else {
+ for (ALL_LIST_ELEMENTS_RO(sg.srv6_locators, loc_node, loc)) {
+ vty_out(vty, "Locator %s has %d prefix chunks\n",
+ loc->name, listcount(loc->chunks));
+ for (ALL_LIST_ELEMENTS_RO(loc->chunks, chunk_node,
+ chunk)) {
+ prefix2str(chunk, str, sizeof(str));
+ vty_out(vty, " %s\n", str);
+ }
+ vty_out(vty, "\n");
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (show_sharp_cspf,
+ show_sharp_cspf_cmd,
+ "show sharp cspf source <A.B.C.D$src4|X:X::X:X$src6> \
+ destination <A.B.C.D$dst4|X:X::X:X$dst6> \
+ <metric|te-metric|delay> (0-16777215)$cost \
+ [rsv-bw (0-7)$cos BANDWIDTH$bw]",
+ SHOW_STR
+ SHARP_STR
+ "Constraint Shortest Path First path computation\n"
+ "Source of the path\n"
+ "IPv4 Source address in dot decimal A.B.C.D\n"
+ "IPv6 Source address as X:X:X:X\n"
+ "Destination of the path\n"
+ "IPv4 Destination address in dot decimal A.B.C.D\n"
+ "IPv6 Destination address as X:X:X:X\n"
+ "Maximum Metric\n"
+ "Maximum TE Metric\n"
+ "Maxim Delay\n"
+ "Value of Maximum cost\n"
+ "Reserved Bandwidth of this path\n"
+ "Class of Service or Priority level\n"
+ "Bytes/second (IEEE floating point format)\n")
+{
+
+ struct cspf *algo;
+ struct constraints csts;
+ struct c_path *path;
+ struct listnode *node;
+ struct ls_edge *edge;
+ int idx;
+
+ if (sg.ted == NULL) {
+ vty_out(vty, "MPLS-TE import is not enabled\n");
+ return CMD_WARNING;
+ }
+
+ if ((src4.s_addr != INADDR_ANY && dst4.s_addr == INADDR_ANY) ||
+ (src4.s_addr == INADDR_ANY && dst4.s_addr != INADDR_ANY)) {
+ vty_out(vty, "Don't mix IPv4 and IPv6 addresses\n");
+ return CMD_WARNING;
+ }
+
+ idx = 6;
+ memset(&csts, 0, sizeof(struct constraints));
+ if (argv_find(argv, argc, "metric", &idx)) {
+ csts.ctype = CSPF_METRIC;
+ csts.cost = cost;
+ }
+ idx = 6;
+ if (argv_find(argv, argc, "te-metric", &idx)) {
+ csts.ctype = CSPF_TE_METRIC;
+ csts.cost = cost;
+ }
+ idx = 6;
+ if (argv_find(argv, argc, "delay", &idx)) {
+ csts.ctype = CSPF_DELAY;
+ csts.cost = cost;
+ }
+ if (argc > 9) {
+ if (sscanf(bw, "%g", &csts.bw) != 1) {
+ vty_out(vty, "Bandwidth constraints: fscanf: %s\n",
+ safe_strerror(errno));
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ csts.cos = cos;
+ }
+
+ /* Initialize and call point-to-point Path computation */
+ if (src4.s_addr != INADDR_ANY)
+ algo = cspf_init_v4(NULL, sg.ted, src4, dst4, &csts);
+ else
+ algo = cspf_init_v6(NULL, sg.ted, src6, dst6, &csts);
+ path = compute_p2p_path(algo, sg.ted);
+ cspf_del(algo);
+
+ if (!path) {
+ vty_out(vty, "Path computation failed without error\n");
+ return CMD_SUCCESS;
+ }
+ if (path->status != SUCCESS) {
+ vty_out(vty, "Path computation failed: %d\n", path->status);
+ cpath_del(path);
+ return CMD_SUCCESS;
+ }
+
+ vty_out(vty, "Path computation success\n");
+ vty_out(vty, "\tCost: %d\n", path->weight);
+ vty_out(vty, "\tEdges:");
+ for (ALL_LIST_ELEMENTS_RO(path->edges, node, edge)) {
+ if (src4.s_addr != INADDR_ANY)
+ vty_out(vty, " %pI4",
+ &edge->attributes->standard.remote);
+ else
+ vty_out(vty, " %pI6",
+ &edge->attributes->standard.remote6);
+ }
+ vty_out(vty, "\n");
+ cpath_del(path);
+ return CMD_SUCCESS;
+}
+
+static struct interface *if_lookup_vrf_all(const char *ifname)
+{
+ struct interface *ifp;
+ struct vrf *vrf;
+
+ RB_FOREACH(vrf, vrf_name_head, &vrfs_by_name) {
+ ifp = if_lookup_by_name(ifname, vrf->vrf_id);
+ if (ifp)
+ return ifp;
+ }
+
+ return NULL;
+}
+
+DEFPY (sharp_interface_protodown,
+ sharp_interface_protodown_cmd,
+ "sharp interface IFNAME$ifname protodown",
+ SHARP_STR
+ INTERFACE_STR
+ IFNAME_STR
+ "Set interface protodown\n")
+{
+ struct interface *ifp;
+
+ ifp = if_lookup_vrf_all(ifname);
+
+ if (!ifp) {
+ vty_out(vty, "%% Can't find interface %s\n", ifname);
+ return CMD_WARNING;
+ }
+
+ if (sharp_zebra_send_interface_protodown(ifp, true) != 0)
+ return CMD_WARNING;
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (no_sharp_interface_protodown,
+ no_sharp_interface_protodown_cmd,
+ "no sharp interface IFNAME$ifname protodown",
+ NO_STR
+ SHARP_STR
+ INTERFACE_STR
+ IFNAME_STR
+ "Set interface protodown\n")
+{
+ struct interface *ifp;
+
+ ifp = if_lookup_vrf_all(ifname);
+
+ if (!ifp) {
+ vty_out(vty, "%% Can't find interface %s\n", ifname);
+ return CMD_WARNING;
+ }
+
+ if (sharp_zebra_send_interface_protodown(ifp, false) != 0)
+ return CMD_WARNING;
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (tc_filter_rate,
+ tc_filter_rate_cmd,
+ "sharp tc dev IFNAME$ifname \
+ source <A.B.C.D/M|X:X::X:X/M>$src \
+ destination <A.B.C.D/M|X:X::X:X/M>$dst \
+ ip-protocol <tcp|udp>$ip_proto \
+ src-port (1-65535)$src_port \
+ dst-port (1-65535)$dst_port \
+ rate RATE$ratestr",
+ SHARP_STR
+ "Traffic control\n"
+ "TC interface (for qdisc, class, filter)\n"
+ "TC interface name\n"
+ "TC filter source\n"
+ "TC filter source IPv4 prefix\n"
+ "TC filter source IPv6 prefix\n"
+ "TC filter destination\n"
+ "TC filter destination IPv4 prefix\n"
+ "TC filter destination IPv6 prefix\n"
+ "TC filter IP protocol\n"
+ "TC filter IP protocol TCP\n"
+ "TC filter IP protocol UDP\n"
+ "TC filter source port\n"
+ "TC filter source port\n"
+ "TC filter destination port\n"
+ "TC filter destination port\n"
+ "TC rate\n"
+ "TC rate number (bits/s) or rate string (suffixed with Bps or bit)\n")
+{
+ struct interface *ifp;
+ struct protoent *p;
+ uint64_t rate;
+
+ ifp = if_lookup_vrf_all(ifname);
+
+ if (!ifp) {
+ vty_out(vty, "%% Can't find interface %s\n", ifname);
+ return CMD_WARNING;
+ }
+
+ p = getprotobyname(ip_proto);
+ if (!p) {
+ vty_out(vty, "Unable to convert %s to proto id\n", ip_proto);
+ return CMD_WARNING;
+ }
+
+ if (tc_getrate(ratestr, &rate) != 0) {
+ vty_out(vty, "Unable to convert %s to rate\n", ratestr);
+ return CMD_WARNING;
+ }
+
+ if (sharp_zebra_send_tc_filter_rate(ifp, src, dst, p->p_proto, src_port,
+ dst_port, rate) != 0)
+ return CMD_WARNING;
+
+ return CMD_SUCCESS;
+}
+
+void sharp_vty_init(void)
+{
+ install_element(ENABLE_NODE, &install_routes_data_dump_cmd);
+ install_element(ENABLE_NODE, &install_routes_cmd);
+ install_element(ENABLE_NODE, &install_seg6_routes_cmd);
+ install_element(ENABLE_NODE, &install_seg6local_routes_cmd);
+ install_element(ENABLE_NODE, &remove_routes_cmd);
+ install_element(ENABLE_NODE, &vrf_label_cmd);
+ install_element(ENABLE_NODE, &sharp_nht_data_dump_cmd);
+ install_element(ENABLE_NODE, &watch_redistribute_cmd);
+ install_element(ENABLE_NODE, &watch_nexthop_v6_cmd);
+ install_element(ENABLE_NODE, &watch_nexthop_v4_cmd);
+ install_element(ENABLE_NODE, &sharp_lsp_prefix_v4_cmd);
+ install_element(ENABLE_NODE, &sharp_remove_lsp_prefix_v4_cmd);
+ install_element(ENABLE_NODE, &logpump_cmd);
+ install_element(ENABLE_NODE, &create_session_cmd);
+ install_element(ENABLE_NODE, &remove_session_cmd);
+ install_element(ENABLE_NODE, &send_opaque_cmd);
+ install_element(ENABLE_NODE, &send_opaque_unicast_cmd);
+ install_element(ENABLE_NODE, &send_opaque_reg_cmd);
+ install_element(ENABLE_NODE, &send_opaque_notif_reg_cmd);
+ install_element(ENABLE_NODE, &neigh_discover_cmd);
+ install_element(ENABLE_NODE, &import_te_cmd);
+
+ install_element(ENABLE_NODE, &show_debugging_sharpd_cmd);
+ install_element(ENABLE_NODE, &show_sharp_ted_cmd);
+ install_element(ENABLE_NODE, &show_sharp_cspf_cmd);
+
+ install_element(ENABLE_NODE, &sharp_srv6_manager_get_locator_chunk_cmd);
+ install_element(ENABLE_NODE,
+ &sharp_srv6_manager_release_locator_chunk_cmd);
+ install_element(ENABLE_NODE, &show_sharp_segment_routing_srv6_cmd);
+
+ install_element(ENABLE_NODE, &sharp_interface_protodown_cmd);
+ install_element(ENABLE_NODE, &no_sharp_interface_protodown_cmd);
+
+ install_element(ENABLE_NODE, &tc_filter_rate_cmd);
+
+ return;
+}
diff --git a/sharpd/sharp_vty.h b/sharpd/sharp_vty.h
new file mode 100644
index 0000000..460e4f5
--- /dev/null
+++ b/sharpd/sharp_vty.h
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * VTY library for SHARP
+ * Copyright (C) Cumulus Networks, Inc.
+ * Donald Sharp
+ */
+#ifndef __SHARP_VTY_H__
+#define __SHARP_VTY_H__
+
+extern void sharp_vty_init(void);
+
+struct vty;
+
+extern void sharp_logpump_run(struct vty *vty, unsigned duration,
+ unsigned frequency, unsigned burst);
+
+#endif
diff --git a/sharpd/sharp_zebra.c b/sharpd/sharp_zebra.c
new file mode 100644
index 0000000..c095fec
--- /dev/null
+++ b/sharpd/sharp_zebra.c
@@ -0,0 +1,1089 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Zebra connect code.
+ * Copyright (C) Cumulus Networks, Inc.
+ * Donald Sharp
+ */
+#include <zebra.h>
+
+#include "frrevent.h"
+#include "command.h"
+#include "network.h"
+#include "prefix.h"
+#include "stream.h"
+#include "memory.h"
+#include "zclient.h"
+#include "nexthop.h"
+#include "nexthop_group.h"
+#include "link_state.h"
+#include "tc.h"
+
+#include "sharp_globals.h"
+#include "sharp_nht.h"
+#include "sharp_zebra.h"
+
+/* Zebra structure to hold current status. */
+struct zclient *zclient = NULL;
+
+/* For registering threads. */
+extern struct event_loop *master;
+
+/* Privs info */
+extern struct zebra_privs_t sharp_privs;
+
+DEFINE_MTYPE_STATIC(SHARPD, ZC, "Test zclients");
+
+/* Struct to hold list of test zclients */
+struct sharp_zclient {
+ struct sharp_zclient *prev;
+ struct sharp_zclient *next;
+ struct zclient *client;
+};
+
+/* Head of test zclient list */
+static struct sharp_zclient *sharp_clients_head;
+
+static int sharp_opaque_handler(ZAPI_CALLBACK_ARGS);
+
+/* Utility to add a test zclient struct to the list */
+static void add_zclient(struct zclient *client)
+{
+ struct sharp_zclient *node;
+
+ node = XCALLOC(MTYPE_ZC, sizeof(struct sharp_zclient));
+
+ node->client = client;
+
+ node->next = sharp_clients_head;
+ if (sharp_clients_head)
+ sharp_clients_head->prev = node;
+ sharp_clients_head = node;
+}
+
+/* Interface addition message from zebra. */
+static int sharp_ifp_create(struct interface *ifp)
+{
+ return 0;
+}
+
+static int sharp_ifp_destroy(struct interface *ifp)
+{
+ return 0;
+}
+
+static int interface_address_add(ZAPI_CALLBACK_ARGS)
+{
+ zebra_interface_address_read(cmd, zclient->ibuf, vrf_id);
+
+ 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;
+
+ connected_free(&c);
+ return 0;
+}
+
+static int sharp_ifp_up(struct interface *ifp)
+{
+ return 0;
+}
+
+static int sharp_ifp_down(struct interface *ifp)
+{
+ return 0;
+}
+
+int sharp_install_lsps_helper(bool install_p, bool update_p,
+ const struct prefix *p, uint8_t type,
+ int instance, uint32_t in_label,
+ const struct nexthop_group *nhg,
+ const struct nexthop_group *backup_nhg)
+{
+ struct zapi_labels zl = {};
+ struct zapi_nexthop *znh;
+ const struct nexthop *nh;
+ int i, cmd, ret;
+
+ zl.type = ZEBRA_LSP_SHARP;
+ zl.local_label = in_label;
+
+ if (p) {
+ SET_FLAG(zl.message, ZAPI_LABELS_FTN);
+ prefix_copy(&zl.route.prefix, p);
+ zl.route.type = type;
+ zl.route.instance = instance;
+ }
+
+ /* List of nexthops is optional for delete */
+ i = 0;
+ if (nhg) {
+ for (ALL_NEXTHOPS_PTR(nhg, nh)) {
+ znh = &zl.nexthops[i];
+
+ /* Must have labels to be useful */
+ if (nh->nh_label == NULL ||
+ nh->nh_label->num_labels == 0)
+ continue;
+
+ if (nh->type == NEXTHOP_TYPE_IFINDEX ||
+ nh->type == NEXTHOP_TYPE_BLACKHOLE)
+ /* Hmm - can't really deal with these types */
+ continue;
+
+ ret = zapi_nexthop_from_nexthop(znh, nh);
+ if (ret < 0)
+ return -1;
+
+ i++;
+ if (i >= MULTIPATH_NUM)
+ break;
+ }
+ }
+
+ /* Whoops - no nexthops isn't very useful for install */
+ if (i == 0 && install_p)
+ return -1;
+
+ zl.nexthop_num = i;
+
+ /* Add optional backup nexthop info. Since these are used by index,
+ * we can't just skip over an invalid backup nexthop: we will
+ * invalidate the entire operation.
+ */
+ if (backup_nhg != NULL) {
+ i = 0;
+ for (ALL_NEXTHOPS_PTR(backup_nhg, nh)) {
+ znh = &zl.backup_nexthops[i];
+
+ /* Must have labels to be useful */
+ if (nh->nh_label == NULL ||
+ nh->nh_label->num_labels == 0)
+ return -1;
+
+ if (nh->type == NEXTHOP_TYPE_IFINDEX ||
+ nh->type == NEXTHOP_TYPE_BLACKHOLE)
+ /* Hmm - can't really deal with these types */
+ return -1;
+
+ ret = zapi_nexthop_from_nexthop(znh, nh);
+ if (ret < 0)
+ return -1;
+
+ i++;
+ if (i >= MULTIPATH_NUM)
+ break;
+ }
+
+ if (i > 0)
+ SET_FLAG(zl.message, ZAPI_LABELS_HAS_BACKUPS);
+
+ zl.backup_nexthop_num = i;
+ }
+
+
+ if (install_p) {
+ if (update_p)
+ cmd = ZEBRA_MPLS_LABELS_REPLACE;
+ else
+ cmd = ZEBRA_MPLS_LABELS_ADD;
+ } else {
+ cmd = ZEBRA_MPLS_LABELS_DELETE;
+ }
+
+ if (zebra_send_mpls_labels(zclient, cmd, &zl) == ZCLIENT_SEND_FAILURE)
+ return -1;
+
+ return 0;
+}
+
+enum where_to_restart {
+ SHARP_INSTALL_ROUTES_RESTART,
+ SHARP_DELETE_ROUTES_RESTART,
+};
+
+struct buffer_delay {
+ struct prefix p;
+ uint32_t count;
+ uint32_t routes;
+ vrf_id_t vrf_id;
+ uint8_t instance;
+ uint32_t nhgid;
+ uint32_t flags;
+ const struct nexthop_group *nhg;
+ const struct nexthop_group *backup_nhg;
+ enum where_to_restart restart;
+ char *opaque;
+} wb;
+
+/*
+ * route_add - Encodes a route to zebra
+ *
+ * This function returns true when the route was buffered
+ * by the underlying stream system
+ */
+static bool route_add(const struct prefix *p, vrf_id_t vrf_id, uint8_t instance,
+ uint32_t nhgid, const struct nexthop_group *nhg,
+ const struct nexthop_group *backup_nhg, uint32_t flags,
+ char *opaque)
+{
+ struct zapi_route api;
+ struct zapi_nexthop *api_nh;
+ struct nexthop *nh;
+ int i = 0;
+
+ memset(&api, 0, sizeof(api));
+ api.vrf_id = vrf_id;
+ api.type = ZEBRA_ROUTE_SHARP;
+ api.instance = instance;
+ api.safi = SAFI_UNICAST;
+ memcpy(&api.prefix, p, sizeof(*p));
+
+ api.flags = flags;
+ SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP);
+
+ /* Only send via ID if nhgroup has been successfully installed */
+ if (nhgid && sharp_nhgroup_id_is_installed(nhgid)) {
+ SET_FLAG(api.message, ZAPI_MESSAGE_NHG);
+ api.nhgid = nhgid;
+ } else {
+ for (ALL_NEXTHOPS_PTR(nhg, nh)) {
+ /* Check if we set a VNI label */
+ if (nh->nh_label &&
+ (nh->nh_label_type == ZEBRA_LSP_EVPN))
+ SET_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE);
+
+ api_nh = &api.nexthops[i];
+
+ zapi_nexthop_from_nexthop(api_nh, nh);
+
+ i++;
+ }
+ api.nexthop_num = i;
+ }
+
+ /* Include backup nexthops, if present */
+ if (backup_nhg && backup_nhg->nexthop) {
+ SET_FLAG(api.message, ZAPI_MESSAGE_BACKUP_NEXTHOPS);
+
+ i = 0;
+ for (ALL_NEXTHOPS_PTR(backup_nhg, nh)) {
+ api_nh = &api.backup_nexthops[i];
+
+ zapi_backup_nexthop_from_nexthop(api_nh, nh);
+
+ i++;
+ }
+
+ api.backup_nexthop_num = i;
+ }
+
+ if (strlen(opaque)) {
+ SET_FLAG(api.message, ZAPI_MESSAGE_OPAQUE);
+ api.opaque.length = strlen(opaque) + 1;
+ assert(api.opaque.length <= ZAPI_MESSAGE_OPAQUE_LENGTH);
+ memcpy(api.opaque.data, opaque, api.opaque.length);
+ }
+
+ if (zclient_route_send(ZEBRA_ROUTE_ADD, zclient, &api) ==
+ ZCLIENT_SEND_BUFFERED)
+ return true;
+ else
+ return false;
+}
+
+/*
+ * route_delete - Encodes a route for deletion to zebra
+ *
+ * This function returns true when the route sent was
+ * buffered by the underlying stream system.
+ */
+static bool route_delete(struct prefix *p, vrf_id_t vrf_id, uint8_t instance)
+{
+ struct zapi_route api;
+
+ memset(&api, 0, sizeof(api));
+ api.vrf_id = vrf_id;
+ api.type = ZEBRA_ROUTE_SHARP;
+ api.safi = SAFI_UNICAST;
+ api.instance = instance;
+ memcpy(&api.prefix, p, sizeof(*p));
+
+ if (zclient_route_send(ZEBRA_ROUTE_DELETE, zclient, &api) ==
+ ZCLIENT_SEND_BUFFERED)
+ return true;
+ else
+ return false;
+}
+
+static void sharp_install_routes_restart(struct prefix *p, uint32_t count,
+ vrf_id_t vrf_id, uint8_t instance,
+ uint32_t nhgid,
+ const struct nexthop_group *nhg,
+ const struct nexthop_group *backup_nhg,
+ uint32_t routes, uint32_t flags,
+ char *opaque)
+{
+ uint32_t temp, i;
+ bool v4 = false;
+
+ if (p->family == AF_INET) {
+ v4 = true;
+ temp = ntohl(p->u.prefix4.s_addr);
+ } else
+ temp = ntohl(p->u.val32[3]);
+
+ for (i = count; i < routes; i++) {
+ bool buffered = route_add(p, vrf_id, (uint8_t)instance, nhgid,
+ nhg, backup_nhg, flags, opaque);
+ if (v4)
+ p->u.prefix4.s_addr = htonl(++temp);
+ else
+ p->u.val32[3] = htonl(++temp);
+
+ if (buffered) {
+ wb.p = *p;
+ wb.count = i + 1;
+ wb.routes = routes;
+ wb.vrf_id = vrf_id;
+ wb.instance = instance;
+ wb.nhgid = nhgid;
+ wb.nhg = nhg;
+ wb.flags = flags;
+ wb.backup_nhg = backup_nhg;
+ wb.opaque = opaque;
+ wb.restart = SHARP_INSTALL_ROUTES_RESTART;
+
+ return;
+ }
+ }
+}
+
+void sharp_install_routes_helper(struct prefix *p, vrf_id_t vrf_id,
+ uint8_t instance, uint32_t nhgid,
+ const struct nexthop_group *nhg,
+ const struct nexthop_group *backup_nhg,
+ uint32_t routes, uint32_t flags, char *opaque)
+{
+ zlog_debug("Inserting %u routes", routes);
+
+ /* Only use backup route/nexthops if present */
+ if (backup_nhg && (backup_nhg->nexthop == NULL))
+ backup_nhg = NULL;
+
+ monotime(&sg.r.t_start);
+ sharp_install_routes_restart(p, 0, vrf_id, instance, nhgid, nhg,
+ backup_nhg, routes, flags, opaque);
+}
+
+static void sharp_remove_routes_restart(struct prefix *p, uint32_t count,
+ vrf_id_t vrf_id, uint8_t instance,
+ uint32_t routes)
+{
+ uint32_t temp, i;
+ bool v4 = false;
+
+ if (p->family == AF_INET) {
+ v4 = true;
+ temp = ntohl(p->u.prefix4.s_addr);
+ } else
+ temp = ntohl(p->u.val32[3]);
+
+ for (i = count; i < routes; i++) {
+ bool buffered = route_delete(p, vrf_id, (uint8_t)instance);
+
+ if (v4)
+ p->u.prefix4.s_addr = htonl(++temp);
+ else
+ p->u.val32[3] = htonl(++temp);
+
+ if (buffered) {
+ wb.p = *p;
+ wb.count = i + 1;
+ wb.vrf_id = vrf_id;
+ wb.instance = instance;
+ wb.routes = routes;
+ wb.restart = SHARP_DELETE_ROUTES_RESTART;
+
+ return;
+ }
+ }
+}
+
+void sharp_remove_routes_helper(struct prefix *p, vrf_id_t vrf_id,
+ uint8_t instance, uint32_t routes)
+{
+ zlog_debug("Removing %u routes", routes);
+
+ monotime(&sg.r.t_start);
+
+ sharp_remove_routes_restart(p, 0, vrf_id, instance, routes);
+}
+
+static void handle_repeated(bool installed)
+{
+ struct prefix p = sg.r.orig_prefix;
+ sg.r.repeat--;
+
+ if (sg.r.repeat <= 0)
+ return;
+
+ if (installed) {
+ sg.r.removed_routes = 0;
+ sharp_remove_routes_helper(&p, sg.r.vrf_id, sg.r.inst,
+ sg.r.total_routes);
+ }
+
+ if (!installed) {
+ sg.r.installed_routes = 0;
+ sharp_install_routes_helper(
+ &p, sg.r.vrf_id, sg.r.inst, sg.r.nhgid,
+ &sg.r.nhop_group, &sg.r.backup_nhop_group,
+ sg.r.total_routes, sg.r.flags, sg.r.opaque);
+ }
+}
+
+static void sharp_zclient_buffer_ready(void)
+{
+ switch (wb.restart) {
+ case SHARP_INSTALL_ROUTES_RESTART:
+ sharp_install_routes_restart(
+ &wb.p, wb.count, wb.vrf_id, wb.instance, wb.nhgid,
+ wb.nhg, wb.backup_nhg, wb.routes, wb.flags, wb.opaque);
+ return;
+ case SHARP_DELETE_ROUTES_RESTART:
+ sharp_remove_routes_restart(&wb.p, wb.count, wb.vrf_id,
+ wb.instance, wb.routes);
+ return;
+ }
+}
+
+static int route_notify_owner(ZAPI_CALLBACK_ARGS)
+{
+ struct timeval r;
+ struct prefix p;
+ enum zapi_route_notify_owner note;
+ uint32_t table;
+
+ if (!zapi_route_notify_decode(zclient->ibuf, &p, &table, &note, NULL,
+ NULL))
+ return -1;
+
+ switch (note) {
+ case ZAPI_ROUTE_INSTALLED:
+ sg.r.installed_routes++;
+ if (sg.r.total_routes == sg.r.installed_routes) {
+ monotime(&sg.r.t_end);
+ timersub(&sg.r.t_end, &sg.r.t_start, &r);
+ zlog_debug("Installed All Items %jd.%ld",
+ (intmax_t)r.tv_sec, (long)r.tv_usec);
+ handle_repeated(true);
+ }
+ break;
+ case ZAPI_ROUTE_FAIL_INSTALL:
+ zlog_debug("Failed install of route");
+ break;
+ case ZAPI_ROUTE_BETTER_ADMIN_WON:
+ zlog_debug("Better Admin Distance won over us");
+ break;
+ case ZAPI_ROUTE_REMOVED:
+ sg.r.removed_routes++;
+ if (sg.r.total_routes == sg.r.removed_routes) {
+ monotime(&sg.r.t_end);
+ timersub(&sg.r.t_end, &sg.r.t_start, &r);
+ zlog_debug("Removed all Items %jd.%ld",
+ (intmax_t)r.tv_sec, (long)r.tv_usec);
+ handle_repeated(false);
+ }
+ break;
+ case ZAPI_ROUTE_REMOVE_FAIL:
+ zlog_debug("Route removal Failure");
+ break;
+ }
+ return 0;
+}
+
+static void zebra_connected(struct zclient *zclient)
+{
+ zclient_send_reg_requests(zclient, VRF_DEFAULT);
+
+ /*
+ * Do not actually turn this on yet
+ * This is just the start of the infrastructure needed here
+ * This can be fixed at a later time.
+ *
+ * zebra_redistribute_send(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP,
+ * ZEBRA_ROUTE_ALL, 0, VRF_DEFAULT);
+ */
+}
+
+void vrf_label_add(vrf_id_t vrf_id, afi_t afi, mpls_label_t label)
+{
+ zclient_send_vrf_label(zclient, vrf_id, afi, label, ZEBRA_LSP_SHARP);
+}
+
+void nhg_add(uint32_t id, const struct nexthop_group *nhg,
+ const struct nexthop_group *backup_nhg)
+{
+ struct zapi_nhg api_nhg = {};
+ struct zapi_nexthop *api_nh;
+ struct nexthop *nh;
+ bool is_valid = true;
+
+ api_nhg.id = id;
+
+ api_nhg.resilience = nhg->nhgr;
+
+ for (ALL_NEXTHOPS_PTR(nhg, nh)) {
+ if (api_nhg.nexthop_num >= MULTIPATH_NUM) {
+ zlog_warn(
+ "%s: number of nexthops greater than max multipath size, truncating",
+ __func__);
+ break;
+ }
+
+ /* Unresolved nexthops will lead to failure - only send
+ * nexthops that zebra will consider valid.
+ */
+ if (nh->ifindex == 0)
+ continue;
+
+ api_nh = &api_nhg.nexthops[api_nhg.nexthop_num];
+
+ zapi_nexthop_from_nexthop(api_nh, nh);
+ api_nhg.nexthop_num++;
+ }
+
+ if (api_nhg.nexthop_num == 0) {
+ zlog_debug("%s: nhg %u not sent: no valid nexthops", __func__,
+ id);
+ is_valid = false;
+ goto done;
+ }
+
+ if (backup_nhg) {
+ for (ALL_NEXTHOPS_PTR(backup_nhg, nh)) {
+ if (api_nhg.backup_nexthop_num >= MULTIPATH_NUM) {
+ zlog_warn(
+ "%s: number of backup nexthops greater than max multipath size, truncating",
+ __func__);
+ break;
+ }
+
+ /* Unresolved nexthop: will be rejected by zebra.
+ * That causes a problem, since the primary nexthops
+ * rely on array indexing into the backup nexthops. If
+ * that array isn't valid, the backup indexes won't be
+ * valid.
+ */
+ if (nh->ifindex == 0) {
+ zlog_debug("%s: nhg %u: invalid backup nexthop",
+ __func__, id);
+ is_valid = false;
+ break;
+ }
+
+ api_nh = &api_nhg.backup_nexthops
+ [api_nhg.backup_nexthop_num];
+
+ zapi_backup_nexthop_from_nexthop(api_nh, nh);
+ api_nhg.backup_nexthop_num++;
+ }
+ }
+
+done:
+ if (is_valid)
+ zclient_nhg_send(zclient, ZEBRA_NHG_ADD, &api_nhg);
+}
+
+void nhg_del(uint32_t id)
+{
+ struct zapi_nhg api_nhg = {};
+
+ api_nhg.id = id;
+
+ zclient_nhg_send(zclient, ZEBRA_NHG_DEL, &api_nhg);
+}
+
+void sharp_zebra_nexthop_watch(struct prefix *p, vrf_id_t vrf_id, bool import,
+ bool watch, bool connected)
+{
+ int command;
+
+ command = ZEBRA_NEXTHOP_REGISTER;
+
+ if (!watch)
+ command = ZEBRA_NEXTHOP_UNREGISTER;
+
+ if (zclient_send_rnh(zclient, command, p, SAFI_UNICAST, connected,
+ false, vrf_id) == ZCLIENT_SEND_FAILURE)
+ zlog_warn("%s: Failure to send nexthop to zebra", __func__);
+}
+
+static int sharp_debug_nexthops(struct zapi_route *api)
+{
+ int i;
+
+ if (api->nexthop_num == 0) {
+ zlog_debug(" Not installed");
+ return 0;
+ }
+
+ for (i = 0; i < api->nexthop_num; i++) {
+ struct zapi_nexthop *znh = &api->nexthops[i];
+
+ switch (znh->type) {
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ case NEXTHOP_TYPE_IPV4:
+ zlog_debug(
+ " Nexthop %pI4, type: %d, ifindex: %d, vrf: %d, label_num: %d",
+ &znh->gate.ipv4.s_addr, znh->type, znh->ifindex,
+ znh->vrf_id, znh->label_num);
+ break;
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ case NEXTHOP_TYPE_IPV6:
+ zlog_debug(
+ " Nexthop %pI6, type: %d, ifindex: %d, vrf: %d, label_num: %d",
+ &znh->gate.ipv6, znh->type, znh->ifindex,
+ znh->vrf_id, znh->label_num);
+ break;
+ case NEXTHOP_TYPE_IFINDEX:
+ zlog_debug(" Nexthop IFINDEX: %d, ifindex: %d",
+ znh->type, znh->ifindex);
+ break;
+ case NEXTHOP_TYPE_BLACKHOLE:
+ zlog_debug(" Nexthop blackhole");
+ break;
+ }
+ }
+
+ return i;
+}
+static int sharp_nexthop_update(ZAPI_CALLBACK_ARGS)
+{
+ struct sharp_nh_tracker *nht;
+ struct zapi_route nhr;
+ struct prefix matched;
+
+ if (!zapi_nexthop_update_decode(zclient->ibuf, &matched, &nhr)) {
+ zlog_err("%s: Decode of update failed", __func__);
+ return 0;
+ }
+
+ zlog_debug("Received update for %pFX actual match: %pFX metric: %u",
+ &matched, &nhr.prefix, nhr.metric);
+
+ nht = sharp_nh_tracker_get(&matched);
+ nht->nhop_num = nhr.nexthop_num;
+ nht->updates++;
+
+ sharp_debug_nexthops(&nhr);
+
+ return 0;
+}
+
+static int sharp_redistribute_route(ZAPI_CALLBACK_ARGS)
+{
+ struct zapi_route api;
+
+ if (zapi_route_decode(zclient->ibuf, &api) < 0)
+ zlog_warn("%s: Decode of redistribute failed: %d", __func__,
+ ZEBRA_REDISTRIBUTE_ROUTE_ADD);
+
+ zlog_debug("%s: %pFX (%s)", zserv_command_string(cmd), &api.prefix,
+ zebra_route_string(api.type));
+
+ sharp_debug_nexthops(&api);
+
+ return 0;
+}
+
+void sharp_redistribute_vrf(struct vrf *vrf, int type)
+{
+ zebra_redistribute_send(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP, type,
+ 0, vrf->vrf_id);
+}
+
+static zclient_handler *const sharp_opaque_handlers[] = {
+ [ZEBRA_OPAQUE_MESSAGE] = sharp_opaque_handler,
+};
+
+/* Add a zclient with a specified session id, for testing. */
+int sharp_zclient_create(uint32_t session_id)
+{
+ struct zclient *client;
+ struct sharp_zclient *node;
+
+ /* Check for duplicates */
+ for (node = sharp_clients_head; node != NULL; node = node->next) {
+ if (node->client->session_id == session_id)
+ return -1;
+ }
+
+ client = zclient_new(master, &zclient_options_default,
+ sharp_opaque_handlers,
+ array_size(sharp_opaque_handlers));
+ client->sock = -1;
+ client->session_id = session_id;
+
+ zclient_init(client, ZEBRA_ROUTE_SHARP, 0, &sharp_privs);
+
+ /* Enqueue on the list of test clients */
+ add_zclient(client);
+
+ return 0;
+}
+
+/* Delete one of the extra test zclients */
+int sharp_zclient_delete(uint32_t session_id)
+{
+ struct sharp_zclient *node;
+
+ /* Search for session */
+ for (node = sharp_clients_head; node != NULL; node = node->next) {
+ if (node->client->session_id == session_id) {
+ /* Dequeue from list */
+ if (node->next)
+ node->next->prev = node->prev;
+ if (node->prev)
+ node->prev->next = node->next;
+ if (node == sharp_clients_head)
+ sharp_clients_head = node->next;
+
+ /* Clean up zclient */
+ zclient_stop(node->client);
+ zclient_free(node->client);
+
+ /* Free memory */
+ XFREE(MTYPE_ZC, node);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static const char *const type2txt[] = {"Generic", "Vertex", "Edge", "Subnet"};
+static const char *const status2txt[] = {"Unknown", "New", "Update",
+ "Delete", "Sync", "Orphan"};
+/* Handler for opaque messages */
+static int sharp_opaque_handler(ZAPI_CALLBACK_ARGS)
+{
+ struct stream *s;
+ struct zapi_opaque_msg info;
+ struct ls_element *lse;
+
+ s = zclient->ibuf;
+
+ if (zclient_opaque_decode(s, &info) != 0)
+ return -1;
+
+ zlog_debug("%s: [%u] received opaque type %u", __func__,
+ zclient->session_id, info.type);
+
+ if (info.type == LINK_STATE_UPDATE) {
+ lse = ls_stream2ted(sg.ted, s, true);
+ if (lse) {
+ zlog_debug(" |- Got %s %s from Link State Database",
+ status2txt[lse->status],
+ type2txt[lse->type]);
+ lse->status = SYNC;
+ } else
+ zlog_debug(
+ "%s: Error to convert Stream into Link State",
+ __func__);
+ }
+
+ return 0;
+}
+
+/* Handler for opaque notification messages */
+static int sharp_opq_notify_handler(ZAPI_CALLBACK_ARGS)
+{
+ struct stream *s;
+ struct zapi_opaque_notif_info info;
+
+ s = zclient->ibuf;
+
+ if (zclient_opaque_notif_decode(s, &info) != 0)
+ return -1;
+
+ if (info.reg)
+ zlog_debug("%s: received opaque notification REG, type %u => %d/%d/%d",
+ __func__, info.msg_type, info.proto, info.instance,
+ info.session_id);
+ else
+ zlog_debug("%s: received opaque notification UNREG, type %u",
+ __func__, info.msg_type);
+
+ return 0;
+}
+
+/*
+ * Send OPAQUE messages, using subtype 'type'.
+ */
+void sharp_opaque_send(uint32_t type, uint32_t proto, uint32_t instance,
+ uint32_t session_id, uint32_t count)
+{
+ uint8_t buf[32];
+ int ret;
+ uint32_t i;
+
+ /* Prepare a small payload */
+ for (i = 0; i < sizeof(buf); i++) {
+ if (type < 255)
+ buf[i] = type;
+ else
+ buf[i] = 255;
+ }
+
+ /* Send some messages - broadcast and unicast are supported */
+ for (i = 0; i < count; i++) {
+ if (proto == 0)
+ ret = zclient_send_opaque(zclient, type, buf,
+ sizeof(buf));
+ else
+ ret = zclient_send_opaque_unicast(zclient, type, proto,
+ instance, session_id,
+ buf, sizeof(buf));
+ if (ret == ZCLIENT_SEND_FAILURE) {
+ zlog_debug("%s: send_opaque() failed => %d", __func__,
+ ret);
+ break;
+ }
+ }
+}
+
+/*
+ * Register/unregister for opaque notifications from zebra about 'type'.
+ */
+void sharp_zebra_opaque_notif_reg(bool is_reg, uint32_t type)
+{
+ if (is_reg)
+ zclient_opaque_request_notify(zclient, type);
+ else
+ zclient_opaque_drop_notify(zclient, type);
+}
+
+/*
+ * Send OPAQUE registration messages, using subtype 'type'.
+ */
+void sharp_opaque_reg_send(bool is_reg, uint32_t proto, uint32_t instance,
+ uint32_t session_id, uint32_t type)
+{
+ struct stream *s;
+
+ s = zclient->obuf;
+ stream_reset(s);
+
+ if (is_reg)
+ zclient_create_header(s, ZEBRA_OPAQUE_REGISTER, VRF_DEFAULT);
+ else
+ zclient_create_header(s, ZEBRA_OPAQUE_UNREGISTER, VRF_DEFAULT);
+
+ /* Send sub-type */
+ stream_putl(s, type);
+
+ /* Add zclient info */
+ stream_putc(s, proto);
+ stream_putw(s, instance);
+ stream_putl(s, session_id);
+
+ /* Put length at the first point of the stream. */
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ (void)zclient_send_message(zclient);
+}
+
+/* Link State registration */
+void sharp_zebra_register_te(void)
+{
+ /* First register to received Link State Update messages */
+ zclient_register_opaque(zclient, LINK_STATE_UPDATE);
+
+ /* Then, request initial TED with SYNC message */
+ ls_request_sync(zclient);
+}
+
+void sharp_zebra_send_arp(const struct interface *ifp, const struct prefix *p)
+{
+ zclient_send_neigh_discovery_req(zclient, ifp, p);
+}
+
+static int nhg_notify_owner(ZAPI_CALLBACK_ARGS)
+{
+ enum zapi_nhg_notify_owner note;
+ uint32_t id;
+
+ if (!zapi_nhg_notify_decode(zclient->ibuf, &id, &note))
+ return -1;
+
+ switch (note) {
+ case ZAPI_NHG_INSTALLED:
+ sharp_nhgroup_id_set_installed(id, true);
+ zlog_debug("Installed nhg %u", id);
+ break;
+ case ZAPI_NHG_FAIL_INSTALL:
+ zlog_debug("Failed install of nhg %u", id);
+ break;
+ case ZAPI_NHG_REMOVED:
+ zlog_debug("Removed nhg %u", id);
+ break;
+ case ZAPI_NHG_REMOVE_FAIL:
+ zlog_debug("Failed removal of nhg %u", id);
+ break;
+ }
+
+ return 0;
+}
+
+int sharp_zebra_srv6_manager_get_locator_chunk(const char *locator_name)
+{
+ return srv6_manager_get_locator_chunk(zclient, locator_name);
+}
+
+int sharp_zebra_srv6_manager_release_locator_chunk(const char *locator_name)
+{
+ return srv6_manager_release_locator_chunk(zclient, locator_name);
+}
+
+static int sharp_zebra_process_srv6_locator_chunk(ZAPI_CALLBACK_ARGS)
+{
+ struct stream *s = NULL;
+ struct srv6_locator_chunk s6c = {};
+ struct listnode *node, *nnode;
+ struct sharp_srv6_locator *loc;
+
+ s = zclient->ibuf;
+ zapi_srv6_locator_chunk_decode(s, &s6c);
+
+ for (ALL_LIST_ELEMENTS(sg.srv6_locators, node, nnode, loc)) {
+ struct prefix_ipv6 *chunk = NULL;
+ struct listnode *chunk_node;
+ struct prefix_ipv6 *c;
+
+ if (strcmp(loc->name, s6c.locator_name) != 0) {
+ zlog_err("%s: Locator name unmatch %s:%s", __func__,
+ loc->name, s6c.locator_name);
+ continue;
+ }
+
+ for (ALL_LIST_ELEMENTS_RO(loc->chunks, chunk_node, c))
+ if (!prefix_cmp(c, &s6c.prefix))
+ return 0;
+
+ chunk = prefix_ipv6_new();
+ *chunk = s6c.prefix;
+ listnode_add(loc->chunks, chunk);
+ return 0;
+ }
+
+ zlog_err("%s: can't get locator_chunk!!", __func__);
+ return 0;
+}
+
+int sharp_zebra_send_interface_protodown(struct interface *ifp, bool down)
+{
+ zlog_debug("Sending zebra to set %s protodown %s", ifp->name,
+ down ? "on" : "off");
+
+ if (zclient_send_interface_protodown(zclient, ifp->vrf->vrf_id, ifp,
+ down) == ZCLIENT_SEND_FAILURE)
+ return -1;
+
+ return 0;
+}
+
+int sharp_zebra_send_tc_filter_rate(struct interface *ifp,
+ const struct prefix *source,
+ const struct prefix *destination,
+ uint8_t ip_proto, uint16_t src_port,
+ uint16_t dst_port, uint64_t rate)
+{
+#define SHARPD_TC_HANDLE 0x0001
+ struct stream *s;
+
+ s = zclient->obuf;
+
+ struct tc_qdisc q = {.ifindex = ifp->ifindex, .kind = TC_QDISC_HTB};
+
+ zapi_tc_qdisc_encode(ZEBRA_TC_QDISC_INSTALL, s, &q);
+ if (zclient_send_message(zclient) == ZCLIENT_SEND_FAILURE)
+ return -1;
+
+ struct tc_class c = {.ifindex = ifp->ifindex,
+ .handle = SHARPD_TC_HANDLE & 0xffff,
+ .kind = TC_QDISC_HTB,
+ .u.htb.ceil = rate,
+ .u.htb.rate = rate};
+
+ zapi_tc_class_encode(ZEBRA_TC_CLASS_ADD, s, &c);
+ if (zclient_send_message(zclient) == ZCLIENT_SEND_FAILURE)
+ return -1;
+
+ struct tc_filter f = {.ifindex = ifp->ifindex,
+ .handle = SHARPD_TC_HANDLE,
+ .priority = 0x1,
+ .kind = TC_FILTER_FLOWER,
+ .u.flower.filter_bm = 0};
+
+#ifdef ETH_P_IP
+ f.protocol = ETH_P_IP;
+#else
+ f.protocol = 0x0800;
+#endif
+
+ f.u.flower.filter_bm |= TC_FLOWER_IP_PROTOCOL;
+ f.u.flower.ip_proto = ip_proto;
+ f.u.flower.filter_bm |= TC_FLOWER_SRC_IP;
+ prefix_copy(&f.u.flower.src_ip, source);
+ f.u.flower.filter_bm |= TC_FLOWER_DST_IP;
+ prefix_copy(&f.u.flower.dst_ip, destination);
+ f.u.flower.filter_bm |= TC_FLOWER_SRC_PORT;
+ f.u.flower.src_port_min = f.u.flower.src_port_max = src_port;
+ f.u.flower.filter_bm |= TC_FLOWER_DST_PORT;
+ f.u.flower.dst_port_min = f.u.flower.dst_port_max = dst_port;
+ f.u.flower.classid = SHARPD_TC_HANDLE & 0xffff;
+
+ zapi_tc_filter_encode(ZEBRA_TC_FILTER_ADD, s, &f);
+ if (zclient_send_message(zclient) == ZCLIENT_SEND_FAILURE)
+ return -1;
+
+ return 0;
+}
+
+static zclient_handler *const sharp_handlers[] = {
+ [ZEBRA_INTERFACE_ADDRESS_ADD] = interface_address_add,
+ [ZEBRA_INTERFACE_ADDRESS_DELETE] = interface_address_delete,
+ [ZEBRA_ROUTE_NOTIFY_OWNER] = route_notify_owner,
+ [ZEBRA_NEXTHOP_UPDATE] = sharp_nexthop_update,
+ [ZEBRA_NHG_NOTIFY_OWNER] = nhg_notify_owner,
+ [ZEBRA_REDISTRIBUTE_ROUTE_ADD] = sharp_redistribute_route,
+ [ZEBRA_REDISTRIBUTE_ROUTE_DEL] = sharp_redistribute_route,
+ [ZEBRA_OPAQUE_MESSAGE] = sharp_opaque_handler,
+ [ZEBRA_OPAQUE_NOTIFY] = sharp_opq_notify_handler,
+ [ZEBRA_SRV6_MANAGER_GET_LOCATOR_CHUNK] =
+ sharp_zebra_process_srv6_locator_chunk,
+};
+
+void sharp_zebra_init(void)
+{
+ struct zclient_options opt = {.receive_notify = true};
+
+ if_zapi_callbacks(sharp_ifp_create, sharp_ifp_up, sharp_ifp_down,
+ sharp_ifp_destroy);
+
+ zclient = zclient_new(master, &opt, sharp_handlers,
+ array_size(sharp_handlers));
+
+ zclient_init(zclient, ZEBRA_ROUTE_SHARP, 0, &sharp_privs);
+ zclient->zebra_connected = zebra_connected;
+ zclient->zebra_buffer_write_ready = sharp_zclient_buffer_ready;
+}
diff --git a/sharpd/sharp_zebra.h b/sharpd/sharp_zebra.h
new file mode 100644
index 0000000..025b4d8
--- /dev/null
+++ b/sharpd/sharp_zebra.h
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Zebra connect library for SHARP
+ * Copyright (C) Cumulus Networks, Inc.
+ * Donald Sharp
+ */
+#ifndef __SHARP_ZEBRA_H__
+#define __SHARP_ZEBRA_H__
+
+extern void sharp_zebra_init(void);
+
+/* Add and delete extra zapi client sessions, for testing */
+int sharp_zclient_create(uint32_t session_id);
+int sharp_zclient_delete(uint32_t session_id);
+
+extern void vrf_label_add(vrf_id_t vrf_id, afi_t afi, mpls_label_t label);
+extern void nhg_add(uint32_t id, const struct nexthop_group *nhg,
+ const struct nexthop_group *backup_nhg);
+extern void nhg_del(uint32_t id);
+extern void sharp_zebra_nexthop_watch(struct prefix *p, vrf_id_t vrf_id,
+ bool import, bool watch, bool connected);
+
+extern void sharp_install_routes_helper(struct prefix *p, vrf_id_t vrf_id,
+ uint8_t instance, uint32_t nhgid,
+ const struct nexthop_group *nhg,
+ const struct nexthop_group *backup_nhg,
+ uint32_t routes, uint32_t flags,
+ char *opaque);
+extern void sharp_remove_routes_helper(struct prefix *p, vrf_id_t vrf_id,
+ uint8_t instance, uint32_t routes);
+
+int sharp_install_lsps_helper(bool install_p, bool update_p,
+ const struct prefix *p, uint8_t type,
+ int instance, uint32_t in_label,
+ const struct nexthop_group *nhg,
+ const struct nexthop_group *backup_nhg);
+
+/* Send OPAQUE messages, using subtype 'type'. */
+void sharp_opaque_send(uint32_t type, uint32_t proto, uint32_t instance,
+ uint32_t session_id, uint32_t count);
+
+/* Send OPAQUE registration or notification registration messages,
+ * for opaque subtype 'type'.
+ */
+void sharp_opaque_reg_send(bool is_reg, uint32_t proto, uint32_t instance,
+ uint32_t session_id, uint32_t type);
+
+/* Register/unregister for opaque notifications from zebra about 'type'. */
+void sharp_zebra_opaque_notif_reg(bool is_reg, uint32_t type);
+
+extern void sharp_zebra_send_arp(const struct interface *ifp,
+ const struct prefix *p);
+
+/* Register Link State Opaque messages */
+extern void sharp_zebra_register_te(void);
+
+extern void sharp_redistribute_vrf(struct vrf *vrf, int source);
+
+extern int sharp_zebra_srv6_manager_get_locator_chunk(const char *lname);
+extern int sharp_zebra_srv6_manager_release_locator_chunk(const char *lname);
+extern void sharp_install_seg6local_route_helper(struct prefix *p,
+ uint8_t instance,
+ enum seg6local_action_t act,
+ struct seg6local_context *ctx);
+
+extern int sharp_zebra_send_interface_protodown(struct interface *ifp,
+ bool down);
+extern int sharp_zebra_send_tc_filter_rate(struct interface *ifp,
+ const struct prefix *source,
+ const struct prefix *destination,
+ uint8_t ip_proto, uint16_t src_port,
+ uint16_t dst_port, uint64_t rate);
+#endif
diff --git a/sharpd/subdir.am b/sharpd/subdir.am
new file mode 100644
index 0000000..3eb8d1d
--- /dev/null
+++ b/sharpd/subdir.am
@@ -0,0 +1,32 @@
+#
+# sharpd
+#
+
+if SHARPD
+noinst_LIBRARIES += sharpd/libsharp.a
+sbin_PROGRAMS += sharpd/sharpd
+vtysh_daemons += sharpd
+man8 += $(MANBUILD)/frr-sharpd.8
+endif
+
+sharpd_libsharp_a_SOURCES = \
+ sharpd/sharp_nht.c \
+ sharpd/sharp_zebra.c \
+ sharpd/sharp_vty.c \
+ sharpd/sharp_logpump.c \
+ # end
+
+noinst_HEADERS += \
+ sharpd/sharp_nht.h \
+ sharpd/sharp_vty.h \
+ sharpd/sharp_globals.h \
+ sharpd/sharp_zebra.h \
+ # end
+
+clippy_scan += \
+ sharpd/sharp_vty.c \
+ # end
+
+sharpd_sharpd_SOURCES = sharpd/sharp_main.c
+sharpd_sharpd_LDADD = sharpd/libsharp.a lib/libfrr.la $(LIBCAP)
+