diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-09 13:16:35 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-09 13:16:35 +0000 |
commit | e2bbf175a2184bd76f6c54ccf8456babeb1a46fc (patch) | |
tree | f0b76550d6e6f500ada964a3a4ee933a45e5a6f1 /sharpd | |
parent | Initial commit. (diff) | |
download | frr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.tar.xz frr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.zip |
Adding upstream version 9.1.upstream/9.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sharpd')
-rw-r--r-- | sharpd/.gitignore | 2 | ||||
-rw-r--r-- | sharpd/Makefile | 10 | ||||
-rw-r--r-- | sharpd/sharp_globals.h | 65 | ||||
-rw-r--r-- | sharpd/sharp_logpump.c | 143 | ||||
-rw-r--r-- | sharpd/sharp_main.c | 181 | ||||
-rw-r--r-- | sharpd/sharp_nht.c | 225 | ||||
-rw-r--r-- | sharpd/sharp_nht.h | 29 | ||||
-rw-r--r-- | sharpd/sharp_vty.c | 1452 | ||||
-rw-r--r-- | sharpd/sharp_vty.h | 17 | ||||
-rw-r--r-- | sharpd/sharp_zebra.c | 1089 | ||||
-rw-r--r-- | sharpd/sharp_zebra.h | 73 | ||||
-rw-r--r-- | sharpd/subdir.am | 32 |
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, ¬e, 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, ¬e)) + 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) + |