summaryrefslogtreecommitdiffstats
path: root/staticd
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-09 13:16:35 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-09 13:16:35 +0000
commite2bbf175a2184bd76f6c54ccf8456babeb1a46fc (patch)
treef0b76550d6e6f500ada964a3a4ee933a45e5a6f1 /staticd
parentInitial commit. (diff)
downloadfrr-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 'staticd')
-rw-r--r--staticd/.gitignore2
-rw-r--r--staticd/Makefile10
-rw-r--r--staticd/static_bfd.c379
-rw-r--r--staticd/static_debug.c123
-rw-r--r--staticd/static_debug.h67
-rw-r--r--staticd/static_main.c183
-rw-r--r--staticd/static_nb.c246
-rw-r--r--staticd/static_nb.h207
-rw-r--r--staticd/static_nb_config.c1338
-rw-r--r--staticd/static_nht.c211
-rw-r--r--staticd/static_nht.h54
-rw-r--r--staticd/static_routes.c792
-rw-r--r--staticd/static_routes.h273
-rw-r--r--staticd/static_vrf.c152
-rw-r--r--staticd/static_vrf.h40
-rw-r--r--staticd/static_vty.c1658
-rw-r--r--staticd/static_vty.h36
-rw-r--r--staticd/static_zebra.c584
-rw-r--r--staticd/static_zebra.h29
-rw-r--r--staticd/subdir.am44
20 files changed, 6428 insertions, 0 deletions
diff --git a/staticd/.gitignore b/staticd/.gitignore
new file mode 100644
index 0000000..af89530
--- /dev/null
+++ b/staticd/.gitignore
@@ -0,0 +1,2 @@
+libstatic.a
+staticd
diff --git a/staticd/Makefile b/staticd/Makefile
new file mode 100644
index 0000000..ecd33df
--- /dev/null
+++ b/staticd/Makefile
@@ -0,0 +1,10 @@
+all: ALWAYS
+ @$(MAKE) -s -C .. staticd/staticd
+%: ALWAYS
+ @$(MAKE) -s -C .. staticd/$@
+
+Makefile:
+ #nothing
+ALWAYS:
+.PHONY: ALWAYS makefiles
+.SUFFIXES:
diff --git a/staticd/static_bfd.c b/staticd/static_bfd.c
new file mode 100644
index 0000000..507c64e
--- /dev/null
+++ b/staticd/static_bfd.c
@@ -0,0 +1,379 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Static daemon BFD integration.
+ *
+ * Copyright (C) 2020-2022 Network Device Education Foundation, Inc. ("NetDEF")
+ * Rafael Zalamena
+ */
+
+#include <zebra.h>
+
+#include "lib/bfd.h"
+#include "lib/printfrr.h"
+#include "lib/srcdest_table.h"
+
+#include "staticd/static_routes.h"
+#include "staticd/static_zebra.h"
+#include "staticd/static_debug.h"
+
+#include "lib/openbsd-queue.h"
+
+/*
+ * Next hop BFD monitoring settings.
+ */
+static void static_next_hop_bfd_change(struct static_nexthop *sn,
+ const struct bfd_session_status *bss)
+{
+ switch (bss->state) {
+ case BSS_UNKNOWN:
+ /* FALLTHROUGH: no known state yet. */
+ case BSS_ADMIN_DOWN:
+ /* NOTHING: we or the remote end administratively shutdown. */
+ break;
+ case BSS_DOWN:
+ /* Peer went down, remove this next hop. */
+ DEBUGD(&static_dbg_bfd,
+ "%s: next hop is down, remove it from RIB", __func__);
+ sn->path_down = true;
+ static_zebra_route_add(sn->pn, true);
+ break;
+ case BSS_UP:
+ /* Peer is back up, add this next hop. */
+ DEBUGD(&static_dbg_bfd, "%s: next hop is up, add it to RIB",
+ __func__);
+ sn->path_down = false;
+ static_zebra_route_add(sn->pn, true);
+ break;
+ }
+}
+
+static void static_next_hop_bfd_updatecb(
+ __attribute__((unused)) struct bfd_session_params *bsp,
+ const struct bfd_session_status *bss, void *arg)
+{
+ static_next_hop_bfd_change(arg, bss);
+}
+
+static inline int
+static_next_hop_type_to_family(const struct static_nexthop *sn)
+{
+ switch (sn->type) {
+ case STATIC_IPV4_GATEWAY_IFNAME:
+ case STATIC_IPV6_GATEWAY_IFNAME:
+ case STATIC_IPV4_GATEWAY:
+ case STATIC_IPV6_GATEWAY:
+ if (sn->type == STATIC_IPV4_GATEWAY ||
+ sn->type == STATIC_IPV4_GATEWAY_IFNAME)
+ return AF_INET;
+ else
+ return AF_INET6;
+ break;
+ case STATIC_IFNAME:
+ case STATIC_BLACKHOLE:
+ default:
+ zlog_err("%s: invalid next hop type", __func__);
+ break;
+ }
+
+ return AF_UNSPEC;
+}
+
+void static_next_hop_bfd_monitor_enable(struct static_nexthop *sn,
+ const struct lyd_node *dnode)
+{
+ bool use_interface;
+ bool use_profile;
+ bool use_source;
+ bool onlink;
+ bool mhop;
+ int family;
+ struct ipaddr source;
+ struct vrf *vrf = NULL;
+
+ use_interface = false;
+ use_source = yang_dnode_exists(dnode, "./source");
+ use_profile = yang_dnode_exists(dnode, "./profile");
+ onlink = yang_dnode_exists(dnode, "../onlink") &&
+ yang_dnode_get_bool(dnode, "../onlink");
+ mhop = yang_dnode_get_bool(dnode, "./multi-hop");
+ vrf = vrf_lookup_by_name(yang_dnode_get_string(dnode, "../vrf"));
+
+ family = static_next_hop_type_to_family(sn);
+ if (family == AF_UNSPEC)
+ return;
+
+ if (sn->type == STATIC_IPV4_GATEWAY_IFNAME ||
+ sn->type == STATIC_IPV6_GATEWAY_IFNAME)
+ use_interface = true;
+
+ /* Reconfigure or allocate new memory. */
+ if (sn->bsp == NULL)
+ sn->bsp = bfd_sess_new(static_next_hop_bfd_updatecb, sn);
+
+ /* Configure the session. */
+ if (use_source)
+ yang_dnode_get_ip(&source, dnode, "./source");
+
+ if (onlink || mhop == false)
+ bfd_sess_set_auto_source(sn->bsp, false);
+ else
+ bfd_sess_set_auto_source(sn->bsp, !use_source);
+
+ /* Configure the session.*/
+ if (family == AF_INET)
+ bfd_sess_set_ipv4_addrs(sn->bsp,
+ use_source ? &source.ip._v4_addr : NULL,
+ &sn->addr.ipv4);
+ else if (family == AF_INET6)
+ bfd_sess_set_ipv6_addrs(sn->bsp,
+ use_source ? &source.ip._v6_addr : NULL,
+ &sn->addr.ipv6);
+
+ bfd_sess_set_interface(sn->bsp, use_interface ? sn->ifname : NULL);
+
+ bfd_sess_set_profile(sn->bsp, use_profile ? yang_dnode_get_string(
+ dnode, "./profile")
+ : NULL);
+ if (vrf && vrf->vrf_id != VRF_UNKNOWN)
+ bfd_sess_set_vrf(sn->bsp, vrf->vrf_id);
+
+ bfd_sess_set_hop_count(sn->bsp, (onlink || mhop == false) ? 1 : 254);
+
+ /* Install or update the session. */
+ bfd_sess_install(sn->bsp);
+
+ /* Update current path status. */
+ sn->path_down = (bfd_sess_status(sn->bsp) != BSS_UP);
+}
+
+void static_next_hop_bfd_monitor_disable(struct static_nexthop *sn)
+{
+ bfd_sess_free(&sn->bsp);
+
+ /* Reset path status. */
+ sn->path_down = false;
+}
+
+void static_next_hop_bfd_source(struct static_nexthop *sn,
+ const struct ipaddr *source)
+{
+ int family;
+
+ if (sn->bsp == NULL)
+ return;
+
+ family = static_next_hop_type_to_family(sn);
+ if (family == AF_UNSPEC)
+ return;
+
+ bfd_sess_set_auto_source(sn->bsp, false);
+ if (family == AF_INET)
+ bfd_sess_set_ipv4_addrs(sn->bsp, &source->ip._v4_addr,
+ &sn->addr.ipv4);
+ else if (family == AF_INET6)
+ bfd_sess_set_ipv6_addrs(sn->bsp, &source->ip._v6_addr,
+ &sn->addr.ipv6);
+
+ bfd_sess_install(sn->bsp);
+}
+
+void static_next_hop_bfd_auto_source(struct static_nexthop *sn)
+{
+ if (sn->bsp == NULL)
+ return;
+
+ bfd_sess_set_auto_source(sn->bsp, true);
+ bfd_sess_install(sn->bsp);
+}
+
+void static_next_hop_bfd_multi_hop(struct static_nexthop *sn, bool mhop)
+{
+ if (sn->bsp == NULL)
+ return;
+
+ bfd_sess_set_hop_count(sn->bsp, mhop ? 254 : 1);
+ bfd_sess_install(sn->bsp);
+}
+
+void static_next_hop_bfd_profile(struct static_nexthop *sn, const char *name)
+{
+ if (sn->bsp == NULL)
+ return;
+
+ bfd_sess_set_profile(sn->bsp, name);
+ bfd_sess_install(sn->bsp);
+}
+
+void static_bfd_initialize(struct zclient *zc, struct event_loop *tm)
+{
+ /* Initialize BFD integration library. */
+ bfd_protocol_integration_init(zc, tm);
+}
+
+/*
+ * Display functions
+ */
+static void static_bfd_show_nexthop_json(struct vty *vty,
+ struct json_object *jo,
+ const struct static_nexthop *sn)
+{
+ const struct prefix *dst_p, *src_p;
+ struct json_object *jo_nh;
+
+ jo_nh = json_object_new_object();
+
+ srcdest_rnode_prefixes(sn->rn, &dst_p, &src_p);
+ if (src_p)
+ json_object_string_addf(jo_nh, "from", "%pFX", src_p);
+
+ json_object_string_addf(jo_nh, "prefix", "%pFX", dst_p);
+ json_object_string_add(jo_nh, "vrf", sn->nh_vrfname);
+
+ json_object_boolean_add(jo_nh, "installed", !sn->path_down);
+
+ json_object_array_add(jo, jo_nh);
+}
+
+static void static_bfd_show_path_json(struct vty *vty, struct json_object *jo,
+ struct route_table *rt)
+{
+ struct route_node *rn;
+
+ for (rn = route_top(rt); rn; rn = srcdest_route_next(rn)) {
+ struct static_route_info *si = static_route_info_from_rnode(rn);
+ struct static_path *sp;
+
+ if (si == NULL)
+ continue;
+
+ frr_each (static_path_list, &si->path_list, sp) {
+ struct static_nexthop *sn;
+
+ frr_each (static_nexthop_list, &sp->nexthop_list, sn) {
+ /* Skip non configured BFD sessions. */
+ if (sn->bsp == NULL)
+ continue;
+
+ static_bfd_show_nexthop_json(vty, jo, sn);
+ }
+ }
+ }
+}
+
+static void static_bfd_show_json(struct vty *vty)
+{
+ struct json_object *jo, *jo_path, *jo_afi_safi;
+ struct vrf *vrf;
+
+ jo = json_object_new_object();
+ jo_path = json_object_new_object();
+
+ json_object_object_add(jo, "path-list", jo_path);
+ RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
+ const struct static_vrf *svrf = vrf->info;
+ struct route_table *rt;
+
+ jo_afi_safi = json_object_new_array();
+ json_object_object_add(jo_path, "ipv4-unicast", jo_afi_safi);
+ rt = svrf->stable[AFI_IP][SAFI_UNICAST];
+ if (rt)
+ static_bfd_show_path_json(vty, jo_afi_safi, rt);
+
+ jo_afi_safi = json_object_new_array();
+ json_object_object_add(jo_path, "ipv4-multicast", jo_afi_safi);
+ rt = svrf->stable[AFI_IP][SAFI_MULTICAST];
+ if (rt)
+ static_bfd_show_path_json(vty, jo_afi_safi, rt);
+
+ jo_afi_safi = json_object_new_array();
+ json_object_object_add(jo_path, "ipv6-unicast", jo_afi_safi);
+ rt = svrf->stable[AFI_IP6][SAFI_UNICAST];
+ if (rt)
+ static_bfd_show_path_json(vty, jo_afi_safi, rt);
+ }
+
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(jo, 0));
+ json_object_free(jo);
+}
+
+static void static_bfd_show_nexthop(struct vty *vty,
+ const struct static_nexthop *sn)
+{
+ vty_out(vty, " %pRN", sn->rn);
+
+ if (sn->bsp == NULL) {
+ vty_out(vty, "\n");
+ return;
+ }
+
+ if (sn->type == STATIC_IPV4_GATEWAY ||
+ sn->type == STATIC_IPV4_GATEWAY_IFNAME)
+ vty_out(vty, " peer %pI4", &sn->addr.ipv4);
+ else if (sn->type == STATIC_IPV6_GATEWAY ||
+ sn->type == STATIC_IPV6_GATEWAY_IFNAME)
+ vty_out(vty, " peer %pI6", &sn->addr.ipv6);
+ else
+ vty_out(vty, " peer unknown");
+
+ vty_out(vty, " (status: %s)\n",
+ sn->path_down ? "uninstalled" : "installed");
+}
+
+static void static_bfd_show_path(struct vty *vty, struct route_table *rt)
+{
+ struct route_node *rn;
+
+ for (rn = route_top(rt); rn; rn = srcdest_route_next(rn)) {
+ struct static_route_info *si = static_route_info_from_rnode(rn);
+ struct static_path *sp;
+
+ if (si == NULL)
+ continue;
+
+ frr_each (static_path_list, &si->path_list, sp) {
+ struct static_nexthop *sn;
+
+ frr_each (static_nexthop_list, &sp->nexthop_list, sn) {
+ /* Skip non configured BFD sessions. */
+ if (sn->bsp == NULL)
+ continue;
+
+ static_bfd_show_nexthop(vty, sn);
+ }
+ }
+ }
+}
+
+void static_bfd_show(struct vty *vty, bool json)
+{
+ struct vrf *vrf;
+
+ if (json) {
+ static_bfd_show_json(vty);
+ return;
+ }
+
+ vty_out(vty, "Showing BFD monitored static routes:\n");
+ vty_out(vty, "\n Next hops:\n");
+ RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
+ const struct static_vrf *svrf = vrf->info;
+ struct route_table *rt;
+
+ vty_out(vty, " VRF %s IPv4 Unicast:\n", vrf->name);
+ rt = svrf->stable[AFI_IP][SAFI_UNICAST];
+ if (rt)
+ static_bfd_show_path(vty, rt);
+
+ vty_out(vty, "\n VRF %s IPv4 Multicast:\n", vrf->name);
+ rt = svrf->stable[AFI_IP][SAFI_MULTICAST];
+ if (rt)
+ static_bfd_show_path(vty, rt);
+
+ vty_out(vty, "\n VRF %s IPv6 Unicast:\n", vrf->name);
+ rt = svrf->stable[AFI_IP6][SAFI_UNICAST];
+ if (rt)
+ static_bfd_show_path(vty, rt);
+ }
+
+ vty_out(vty, "\n");
+}
diff --git a/staticd/static_debug.c b/staticd/static_debug.c
new file mode 100644
index 0000000..a65752c
--- /dev/null
+++ b/staticd/static_debug.c
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Staticd debug related functions
+ * Copyright (C) 2019 Volta Networks Inc.
+ * Mark Stapp
+ */
+
+#include <zebra.h>
+
+#include "lib/command.h"
+#include "lib/debug.h"
+#include "lib/bfd.h"
+
+#include "static_debug.h"
+
+/*
+ * Debug infra: a debug struct for each category, and a corresponding
+ * string.
+ */
+
+/* clang-format off */
+struct debug static_dbg_events = {0, "Staticd events"};
+struct debug static_dbg_route = {0, "Staticd route"};
+struct debug static_dbg_bfd = {0, "Staticd bfd"};
+
+struct debug *static_debug_arr[] = {
+ &static_dbg_events,
+ &static_dbg_route,
+ &static_dbg_bfd
+};
+
+const char *static_debugs_conflines[] = {
+ "debug static events",
+ "debug static route",
+ "debug static bfd"
+};
+/* clang-format on */
+
+
+/*
+ * Set or unset all staticd debugs
+ *
+ * flags
+ * The flags to set
+ *
+ * set
+ * Whether to set or unset the specified flags
+ */
+static void static_debug_set_all(uint32_t flags, bool set)
+{
+ for (unsigned int i = 0; i < array_size(static_debug_arr); i++) {
+ DEBUG_FLAGS_SET(static_debug_arr[i], flags, set);
+
+ /* if all modes have been turned off, don't preserve options */
+ if (!DEBUG_MODE_CHECK(static_debug_arr[i], DEBUG_MODE_ALL))
+ DEBUG_CLEAR(static_debug_arr[i]);
+ }
+}
+
+static int static_debug_config_write_helper(struct vty *vty, bool config)
+{
+ uint32_t mode = DEBUG_MODE_ALL;
+
+ if (config)
+ mode = DEBUG_MODE_CONF;
+
+ for (unsigned int i = 0; i < array_size(static_debug_arr); i++)
+ if (DEBUG_MODE_CHECK(static_debug_arr[i], mode))
+ vty_out(vty, "%s\n", static_debugs_conflines[i]);
+
+ return 0;
+}
+
+int static_config_write_debug(struct vty *vty)
+{
+ return static_debug_config_write_helper(vty, true);
+}
+
+int static_debug_status_write(struct vty *vty)
+{
+ return static_debug_config_write_helper(vty, false);
+}
+
+/*
+ * Set debugging status.
+ *
+ * vtynode
+ * vty->node
+ *
+ * onoff
+ * Whether to turn the specified debugs on or off
+ *
+ * events
+ * Debug general internal events
+ *
+ */
+void static_debug_set(int vtynode, bool onoff, bool events, bool route,
+ bool bfd)
+{
+ uint32_t mode = DEBUG_NODE2MODE(vtynode);
+
+ if (events)
+ DEBUG_MODE_SET(&static_dbg_events, mode, onoff);
+ if (route)
+ DEBUG_MODE_SET(&static_dbg_route, mode, onoff);
+ if (bfd) {
+ DEBUG_MODE_SET(&static_dbg_bfd, mode, onoff);
+ bfd_protocol_integration_set_debug(onoff);
+ }
+}
+
+/*
+ * Debug lib initialization
+ */
+
+struct debug_callbacks static_dbg_cbs = {
+ .debug_set_all = static_debug_set_all
+};
+
+void static_debug_init(void)
+{
+ debug_init(&static_dbg_cbs);
+}
diff --git a/staticd/static_debug.h b/staticd/static_debug.h
new file mode 100644
index 0000000..c910068
--- /dev/null
+++ b/staticd/static_debug.h
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Staticd debug related functions
+ * Copyright (C) 2019 Volta Networks Inc.
+ * Mark Stapp
+ */
+
+#ifndef _STATIC_DEBUG_H
+#define _STATIC_DEBUG_H
+
+#include <zebra.h>
+
+#include "lib/debug.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* staticd debugging records */
+extern struct debug static_dbg_events;
+extern struct debug static_dbg_route;
+extern struct debug static_dbg_bfd;
+
+/*
+ * Initialize staticd debugging.
+ *
+ * Installs VTY commands and registers callbacks.
+ */
+void static_debug_init(void);
+
+/*
+ * Print staticd debugging configuration.
+ *
+ * vty
+ * VTY to print debugging configuration to.
+ */
+int static_config_write_debug(struct vty *vty);
+
+/*
+ * Print staticd debugging configuration, human readable form.
+ *
+ * vty
+ * VTY to print debugging configuration to.
+ */
+int static_debug_status_write(struct vty *vty);
+
+/*
+ * Set debugging status.
+ *
+ * vtynode
+ * vty->node
+ *
+ * onoff
+ * Whether to turn the specified debugs on or off
+ *
+ * events
+ * Debug general internal events
+ *
+ */
+void static_debug_set(int vtynode, bool onoff, bool events, bool route,
+ bool bfd);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _STATIC_DEBUG_H */
diff --git a/staticd/static_main.c b/staticd/static_main.c
new file mode 100644
index 0000000..165fb4d
--- /dev/null
+++ b/staticd/static_main.c
@@ -0,0 +1,183 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * STATICd - main code
+ * Copyright (C) 2018 Cumulus Networks, Inc.
+ * Donald Sharp
+ */
+#include <zebra.h>
+
+#include <lib/version.h>
+#include "getopt.h"
+#include "frrevent.h"
+#include "command.h"
+#include "log.h"
+#include "memory.h"
+#include "privs.h"
+#include "sigevent.h"
+#include "libfrr.h"
+#include "vrf.h"
+#include "nexthop.h"
+#include "filter.h"
+#include "routing_nb.h"
+
+#include "static_vrf.h"
+#include "static_vty.h"
+#include "static_routes.h"
+#include "static_zebra.h"
+#include "static_debug.h"
+#include "static_nb.h"
+
+#include "mgmt_be_client.h"
+
+char backup_config_file[256];
+
+bool mpls_enabled;
+
+zebra_capabilities_t _caps_p[] = {
+};
+
+struct zebra_privs_t static_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;
+
+struct mgmt_be_client *mgmt_be_client;
+
+static struct frr_daemon_info staticd_di;
+
+/* SIGHUP handler. */
+static void sighup(void)
+{
+ zlog_info("SIGHUP received and ignored");
+}
+
+/* SIGINT / SIGTERM handler. */
+static void sigint(void)
+{
+ zlog_notice("Terminating on signal");
+
+ /* Disable BFD events to avoid wasting processing. */
+ bfd_protocol_integration_set_shutdown(true);
+
+ mgmt_be_client_destroy(mgmt_be_client);
+
+ static_vrf_terminate();
+
+ static_zebra_stop();
+ frr_fini();
+
+ exit(0);
+}
+
+/* SIGUSR1 handler. */
+static void sigusr1(void)
+{
+ zlog_rotate();
+}
+
+struct frr_signal_t static_signals[] = {
+ {
+ .signal = SIGHUP,
+ .handler = &sighup,
+ },
+ {
+ .signal = SIGUSR1,
+ .handler = &sigusr1,
+ },
+ {
+ .signal = SIGINT,
+ .handler = &sigint,
+ },
+ {
+ .signal = SIGTERM,
+ .handler = &sigint,
+ },
+};
+
+static const struct frr_yang_module_info *const staticd_yang_modules[] = {
+ &frr_interface_info,
+ &frr_vrf_info,
+ &frr_routing_info,
+ &frr_staticd_info,
+};
+
+#define STATIC_VTY_PORT 2616
+
+/*
+ * NOTE: .flags == FRR_NO_SPLIT_CONFIG to avoid reading split config, mgmtd will
+ * do this for us now
+ */
+FRR_DAEMON_INFO(staticd, STATIC, .vty_port = STATIC_VTY_PORT,
+
+ .proghelp = "Implementation of STATIC.",
+
+ .signals = static_signals,
+ .n_signals = array_size(static_signals),
+
+ .privs = &static_privs, .yang_modules = staticd_yang_modules,
+ .n_yang_modules = array_size(staticd_yang_modules),
+
+ .flags = FRR_NO_SPLIT_CONFIG);
+
+int main(int argc, char **argv, char **envp)
+{
+ frr_preinit(&staticd_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();
+
+ static_debug_init();
+ static_vrf_init();
+
+ static_zebra_init();
+ static_vty_init();
+
+ /* Initialize MGMT backend functionalities */
+ mgmt_be_client = mgmt_be_client_create("staticd", NULL, 0, master);
+
+ hook_register(routing_conf_event,
+ routing_control_plane_protocols_name_validate);
+
+ routing_control_plane_protocols_register_vrf_dependency();
+
+ /*
+ * We set FRR_NO_SPLIT_CONFIG flag to avoid reading our config, but we
+ * still need to write one if vtysh tells us to. Setting the host
+ * config filename does this.
+ */
+ host_config_set(config_default);
+
+ frr_config_fork();
+ frr_run(master);
+
+ /* Not reached. */
+ return 0;
+}
diff --git a/staticd/static_nb.c b/staticd/static_nb.c
new file mode 100644
index 0000000..1c69a58
--- /dev/null
+++ b/staticd/static_nb.c
@@ -0,0 +1,246 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2018 Vmware
+ * Vishal Dhingra
+ */
+#include <zebra.h>
+
+#include "northbound.h"
+#include "libfrr.h"
+#include "static_nb.h"
+#include "static_vty.h"
+
+/* clang-format off */
+
+const struct frr_yang_module_info frr_staticd_info = {
+ .name = "frr-staticd",
+ .nodes = {
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd",
+ .cbs = {
+ .cli_show = static_cli_show,
+ .cli_show_end = static_cli_show_end,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list",
+ .cbs = {
+ .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_create,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_destroy,
+ .cli_cmp = static_route_list_cli_cmp,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list",
+ .cbs = {
+ .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_create,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_destroy,
+ .cli_cmp = static_path_list_cli_cmp,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/tag",
+ .cbs = {
+ .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_tag_modify,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop",
+ .cbs = {
+ .apply_finish = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_apply_finish,
+ .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_create,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_destroy,
+ .pre_validate = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_pre_validate,
+ .cli_show = static_nexthop_cli_show,
+ .cli_cmp = static_nexthop_cli_cmp,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/bh-type",
+ .cbs = {
+ .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_bh_type_modify,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/onlink",
+ .cbs = {
+ .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_onlink_modify,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/srte-color",
+ .cbs = {
+ .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_color_modify,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_color_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/srv6-segs-stack/entry",
+ .cbs = {
+ .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_create,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_destroy,
+
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/srv6-segs-stack/entry/seg",
+ .cbs = {
+ .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_modify,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry",
+ .cbs = {
+ .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_create,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_destroy,
+
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/label",
+ .cbs = {
+ .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_modify,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/ttl",
+ .cbs = {
+ .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_modify,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/traffic-class",
+ .cbs = {
+ .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_modify,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/bfd-monitoring",
+ .cbs = {
+ .create = route_next_hop_bfd_create,
+ .destroy = route_next_hop_bfd_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/bfd-monitoring/source",
+ .cbs = {
+ .modify = route_next_hop_bfd_source_modify,
+ .destroy = route_next_hop_bfd_source_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/bfd-monitoring/multi-hop",
+ .cbs = {
+ .modify = route_next_hop_bfd_multi_hop_modify,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/bfd-monitoring/profile",
+ .cbs = {
+ .modify = route_next_hop_bfd_profile_modify,
+ .destroy = route_next_hop_bfd_profile_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list",
+ .cbs = {
+ .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_create,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_destroy,
+ .cli_cmp = static_src_list_cli_cmp,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list",
+ .cbs = {
+ .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_create,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_destroy,
+ .cli_cmp = static_path_list_cli_cmp,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/tag",
+ .cbs = {
+ .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_tag_modify,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop",
+ .cbs = {
+ .apply_finish = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_apply_finish,
+ .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_create,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_destroy,
+ .pre_validate = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_pre_validate,
+ .cli_show = static_src_nexthop_cli_show,
+ .cli_cmp = static_nexthop_cli_cmp,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/bh-type",
+ .cbs = {
+ .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_bh_type_modify,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/onlink",
+ .cbs = {
+ .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_onlink_modify,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/srte-color",
+ .cbs = {
+ .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_modify,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/srv6-segs-stack/entry",
+ .cbs = {
+ .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_create,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/srv6-segs-stack/entry/seg",
+ .cbs = {
+ .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_modify,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry",
+ .cbs = {
+ .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_create,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/label",
+ .cbs = {
+ .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_modify,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/ttl",
+ .cbs = {
+ .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_modify,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/traffic-class",
+ .cbs = {
+ .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_modify,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_destroy,
+ }
+ },
+ {
+ .xpath = NULL,
+ },
+ }
+};
diff --git a/staticd/static_nb.h b/staticd/static_nb.h
new file mode 100644
index 0000000..9f80653
--- /dev/null
+++ b/staticd/static_nb.h
@@ -0,0 +1,207 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2018 Vmware
+ * Vishal Dhingra
+ */
+#ifndef _FRR_STATIC_NB_H_
+#define _FRR_STATIC_NB_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern const struct frr_yang_module_info frr_staticd_info;
+
+/* Mandatory callbacks. */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_create(
+ struct nb_cb_create_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_destroy(
+ struct nb_cb_destroy_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_create(
+ struct nb_cb_create_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_destroy(
+ struct nb_cb_destroy_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_tag_modify(
+ struct nb_cb_modify_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_create(
+ struct nb_cb_create_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_destroy(
+ struct nb_cb_destroy_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_bh_type_modify(
+ struct nb_cb_modify_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_onlink_modify(
+ struct nb_cb_modify_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_color_modify(
+ struct nb_cb_modify_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_color_destroy(
+ struct nb_cb_destroy_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_create(
+ struct nb_cb_create_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_destroy(
+ struct nb_cb_destroy_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_modify(
+ struct nb_cb_modify_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_destroy(
+ struct nb_cb_destroy_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_create(
+ struct nb_cb_create_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_destroy(
+ struct nb_cb_destroy_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_modify(
+ struct nb_cb_modify_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_destroy(
+ struct nb_cb_destroy_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_modify(
+ struct nb_cb_modify_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_destroy(
+ struct nb_cb_destroy_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_modify(
+ struct nb_cb_modify_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_destroy(
+ struct nb_cb_destroy_args *args);
+int route_next_hop_bfd_create(struct nb_cb_create_args *args);
+int route_next_hop_bfd_destroy(struct nb_cb_destroy_args *args);
+int route_next_hop_bfd_source_modify(struct nb_cb_modify_args *args);
+int route_next_hop_bfd_source_destroy(struct nb_cb_destroy_args *args);
+int route_next_hop_bfd_profile_modify(struct nb_cb_modify_args *args);
+int route_next_hop_bfd_profile_destroy(struct nb_cb_destroy_args *args);
+int route_next_hop_bfd_multi_hop_modify(struct nb_cb_modify_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_create(
+ struct nb_cb_create_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_destroy(
+ struct nb_cb_destroy_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_create(
+ struct nb_cb_create_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_destroy(
+ struct nb_cb_destroy_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_tag_modify(
+ struct nb_cb_modify_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_create(
+ struct nb_cb_create_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_destroy(
+ struct nb_cb_destroy_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_bh_type_modify(
+ struct nb_cb_modify_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_onlink_modify(
+ struct nb_cb_modify_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_modify(
+ struct nb_cb_modify_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_destroy(
+ struct nb_cb_destroy_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_create(
+ struct nb_cb_create_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_destroy(
+ struct nb_cb_destroy_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_modify(
+ struct nb_cb_modify_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_destroy(
+ struct nb_cb_destroy_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_create(
+ struct nb_cb_create_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_destroy(
+ struct nb_cb_destroy_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_modify(
+ struct nb_cb_modify_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_destroy(
+ struct nb_cb_destroy_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_modify(
+ struct nb_cb_modify_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_destroy(
+ struct nb_cb_destroy_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_modify(
+ struct nb_cb_modify_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_destroy(
+ struct nb_cb_destroy_args *args);
+
+/* Optional 'apply_finish' callbacks. */
+
+void routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_apply_finish(
+ struct nb_cb_apply_finish_args *args);
+void routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_apply_finish(
+ struct nb_cb_apply_finish_args *args);
+
+/* Optional 'pre_validate' callbacks. */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_pre_validate(
+ struct nb_cb_pre_validate_args *args);
+
+/*
+ * Callback registered with routing_nb lib to validate only
+ * one instance of staticd is allowed
+ */
+int routing_control_plane_protocols_name_validate(
+ struct nb_cb_create_args *args);
+
+/* xpath macros */
+/* route-list */
+#define FRR_STATIC_ROUTE_INFO_KEY_XPATH \
+ "/frr-routing:routing/control-plane-protocols/" \
+ "control-plane-protocol[type='%s'][name='%s'][vrf='%s']/" \
+ "frr-staticd:staticd/route-list[prefix='%s'][afi-safi='%s']/" \
+ "path-list[table-id='%u'][distance='%u']"
+
+#define FRR_STATIC_ROUTE_INFO_KEY_NO_DISTANCE_XPATH \
+ "/frr-routing:routing/control-plane-protocols/" \
+ "control-plane-protocol[type='%s'][name='%s'][vrf='%s']/" \
+ "frr-staticd:staticd/route-list[prefix='%s'][afi-safi='%s']/" \
+ "path-list[table-id='%u']"
+
+
+#define FRR_STATIC_ROUTE_PATH_TAG_XPATH "/tag"
+
+/* route-list/frr-nexthops */
+#define FRR_STATIC_ROUTE_NH_KEY_XPATH \
+ "/frr-nexthops/" \
+ "nexthop[nh-type='%s'][vrf='%s'][gateway='%s'][interface='%s']"
+
+#define FRR_STATIC_ROUTE_NH_ONLINK_XPATH "/onlink"
+
+#define FRR_STATIC_ROUTE_NH_COLOR_XPATH "/srte-color"
+
+#define FRR_STATIC_ROUTE_NH_BH_XPATH "/bh-type"
+
+#define FRR_STATIC_ROUTE_NH_LABEL_XPATH "/mpls-label-stack"
+
+#define FRR_STATIC_ROUTE_NHLB_KEY_XPATH "/entry[id='%u']/label"
+
+#define FRR_STATIC_ROUTE_NH_SRV6_SEGS_XPATH "/srv6-segs-stack"
+
+#define FRR_STATIC_ROUTE_NH_SRV6_KEY_SEG_XPATH "/entry[id='%u']/seg"
+
+/* route-list/srclist */
+#define FRR_S_ROUTE_SRC_INFO_KEY_XPATH \
+ "/frr-routing:routing/control-plane-protocols/" \
+ "control-plane-protocol[type='%s'][name='%s'][vrf='%s']/" \
+ "frr-staticd:staticd/route-list[prefix='%s'][afi-safi='%s']/" \
+ "src-list[src-prefix='%s']/path-list[table-id='%u'][distance='%u']"
+
+#define FRR_S_ROUTE_SRC_INFO_KEY_NO_DISTANCE_XPATH \
+ "/frr-routing:routing/control-plane-protocols/" \
+ "control-plane-protocol[type='%s'][name='%s'][vrf='%s']/" \
+ "frr-staticd:staticd/route-list[prefix='%s'][afi-safi='%s']/" \
+ "src-list[src-prefix='%s']/path-list[table-id='%u']"
+
+/* route-list/frr-nexthops */
+#define FRR_DEL_S_ROUTE_NH_KEY_XPATH \
+ FRR_STATIC_ROUTE_INFO_KEY_XPATH \
+ FRR_STATIC_ROUTE_NH_KEY_XPATH
+
+/* route-list/frr-nexthops */
+#define FRR_DEL_S_ROUTE_NH_KEY_NO_DISTANCE_XPATH \
+ FRR_STATIC_ROUTE_INFO_KEY_NO_DISTANCE_XPATH \
+ FRR_STATIC_ROUTE_NH_KEY_XPATH
+
+/* route-list/src/src-list/frr-nexthops*/
+#define FRR_DEL_S_ROUTE_SRC_NH_KEY_XPATH \
+ FRR_S_ROUTE_SRC_INFO_KEY_XPATH \
+ FRR_STATIC_ROUTE_NH_KEY_XPATH
+
+/* route-list/src/src-list/frr-nexthops*/
+#define FRR_DEL_S_ROUTE_SRC_NH_KEY_NO_DISTANCE_XPATH \
+ FRR_S_ROUTE_SRC_INFO_KEY_NO_DISTANCE_XPATH \
+ FRR_STATIC_ROUTE_NH_KEY_XPATH
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/staticd/static_nb_config.c b/staticd/static_nb_config.c
new file mode 100644
index 0000000..ede2e38
--- /dev/null
+++ b/staticd/static_nb_config.c
@@ -0,0 +1,1338 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2018 Vmware
+ * Vishal Dhingra
+ */
+#include <zebra.h>
+
+#include "northbound.h"
+#include "libfrr.h"
+#include "log.h"
+#include "lib_errors.h"
+#include "prefix.h"
+#include "table.h"
+#include "vrf.h"
+#include "nexthop.h"
+#include "srcdest_table.h"
+
+#include "static_vrf.h"
+#include "static_routes.h"
+#include "static_nb.h"
+
+
+static int static_path_list_create(struct nb_cb_create_args *args)
+{
+ struct route_node *rn;
+ struct static_path *pn;
+ const struct lyd_node *vrf_dnode;
+ const char *vrf;
+ uint8_t distance;
+ uint32_t table_id;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ vrf_dnode = yang_dnode_get_parent(args->dnode,
+ "control-plane-protocol");
+ vrf = yang_dnode_get_string(vrf_dnode, "./vrf");
+ table_id = yang_dnode_get_uint32(args->dnode, "./table-id");
+
+ /*
+ * TableId is not applicable for VRF. Consider the case of
+ * l3mdev, there is one uint32_t space to work with.
+ * A l3mdev device points at a specific table that it
+ * relates to and a set of interfaces it belongs to.
+ */
+ if (table_id && (strcmp(vrf, vrf_get_default_name()) != 0)
+ && !vrf_is_backend_netns()) {
+ snprintf(
+ args->errmsg, args->errmsg_len,
+ "%% table param only available when running on netns-based vrfs");
+ return NB_ERR_VALIDATION;
+ }
+ break;
+ case NB_EV_ABORT:
+ case NB_EV_PREPARE:
+ break;
+ case NB_EV_APPLY:
+ rn = nb_running_get_entry(args->dnode, NULL, true);
+ distance = yang_dnode_get_uint8(args->dnode, "./distance");
+ table_id = yang_dnode_get_uint32(args->dnode, "./table-id");
+ pn = static_add_path(rn, table_id, distance);
+ nb_running_set_entry(args->dnode, pn);
+ }
+
+ return NB_OK;
+}
+
+static int static_path_list_destroy(struct nb_cb_destroy_args *args)
+{
+ struct static_path *pn;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ pn = nb_running_unset_entry(args->dnode);
+ static_del_path(pn);
+ break;
+ }
+
+ return NB_OK;
+}
+
+static int static_path_list_tag_modify(struct nb_cb_modify_args *args)
+{
+ struct static_path *pn;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_ABORT:
+ case NB_EV_PREPARE:
+ break;
+ case NB_EV_APPLY:
+ pn = nb_running_get_entry(args->dnode, NULL, true);
+ pn->tag = yang_dnode_get_uint32(args->dnode, NULL);
+ static_install_path(pn);
+ break;
+ }
+
+ return NB_OK;
+}
+
+struct nexthop_iter {
+ uint32_t count;
+ bool blackhole;
+};
+
+static int nexthop_iter_cb(const struct lyd_node *dnode, void *arg)
+{
+ struct nexthop_iter *iter = arg;
+ enum static_nh_type nh_type;
+
+ nh_type = yang_dnode_get_enum(dnode, "./nh-type");
+
+ if (nh_type == STATIC_BLACKHOLE)
+ iter->blackhole = true;
+
+ iter->count++;
+
+ return YANG_ITER_CONTINUE;
+}
+
+static bool static_nexthop_create(struct nb_cb_create_args *args)
+{
+ const struct lyd_node *pn_dnode;
+ struct nexthop_iter iter;
+ struct static_path *pn;
+ struct ipaddr ipaddr;
+ struct static_nexthop *nh;
+ enum static_nh_type nh_type;
+ const char *ifname;
+ const char *nh_vrf;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ ifname = yang_dnode_get_string(args->dnode, "./interface");
+ nh_type = yang_dnode_get_enum(args->dnode, "./nh-type");
+ if (ifname != NULL && nh_type != STATIC_BLACKHOLE) {
+ if (strcasecmp(ifname, "Null0") == 0
+ || strcasecmp(ifname, "reject") == 0
+ || strcasecmp(ifname, "blackhole") == 0) {
+ snprintf(args->errmsg, args->errmsg_len,
+ "%s: Nexthop interface name can not be from reserved keywords(Null0, reject, blackhole)",
+ ifname);
+ return NB_ERR_VALIDATION;
+ }
+ }
+
+ iter.count = 0;
+ iter.blackhole = false;
+
+ pn_dnode = yang_dnode_get_parent(args->dnode, "path-list");
+ yang_dnode_iterate(nexthop_iter_cb, &iter, pn_dnode,
+ "./frr-nexthops/nexthop");
+
+ if (iter.blackhole && iter.count > 1) {
+ snprintf(
+ args->errmsg, args->errmsg_len,
+ "Route cannot have blackhole and non-blackhole nexthops simultaneously");
+ return NB_ERR_VALIDATION;
+ } else if (iter.count > zebra_ecmp_count) {
+ snprintf(args->errmsg, args->errmsg_len,
+ "Route cannot have more than %d ECMP nexthops",
+ zebra_ecmp_count);
+ return NB_ERR_VALIDATION;
+ }
+ break;
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ yang_dnode_get_ip(&ipaddr, args->dnode, "./gateway");
+ nh_type = yang_dnode_get_enum(args->dnode, "./nh-type");
+ ifname = yang_dnode_get_string(args->dnode, "./interface");
+ nh_vrf = yang_dnode_get_string(args->dnode, "./vrf");
+ pn = nb_running_get_entry(args->dnode, NULL, true);
+
+ if (!static_add_nexthop_validate(nh_vrf, nh_type, &ipaddr))
+ flog_warn(
+ EC_LIB_NB_CB_CONFIG_VALIDATE,
+ "Warning!! Local connected address is configured as Gateway IP((%s))",
+ yang_dnode_get_string(args->dnode,
+ "./gateway"));
+ nh = static_add_nexthop(pn, nh_type, &ipaddr, ifname, nh_vrf,
+ 0);
+ nb_running_set_entry(args->dnode, nh);
+ break;
+ }
+
+ return NB_OK;
+}
+
+static bool static_nexthop_destroy(struct nb_cb_destroy_args *args)
+{
+ struct static_nexthop *nh;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ nh = nb_running_unset_entry(args->dnode);
+ static_delete_nexthop(nh);
+ break;
+ }
+
+ return NB_OK;
+}
+
+static int nexthop_srv6_segs_stack_entry_create(struct nb_cb_create_args *args)
+{
+ struct static_nexthop *nh;
+ uint32_t pos;
+ uint8_t index;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ nh = nb_running_get_entry(args->dnode, NULL, true);
+ pos = yang_get_list_pos(args->dnode);
+ if (!pos) {
+ flog_warn(EC_LIB_NB_CB_CONFIG_APPLY,
+ "libyang returns invalid seg position");
+ return NB_ERR;
+ }
+ /* Mapping to array = list-index -1 */
+ index = pos - 1;
+ memset(&nh->snh_seg.seg[index], 0, sizeof(struct in6_addr));
+ nh->snh_seg.num_segs++;
+ break;
+ }
+
+ return NB_OK;
+}
+
+static int nexthop_srv6_segs_stack_entry_destroy(struct nb_cb_destroy_args *args)
+{
+ struct static_nexthop *nh;
+ uint32_t pos;
+ uint8_t index;
+ int old_num_segs;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ nh = nb_running_get_entry(args->dnode, NULL, true);
+ pos = yang_get_list_pos(args->dnode);
+ if (!pos) {
+ flog_warn(EC_LIB_NB_CB_CONFIG_APPLY,
+ "libyang returns invalid seg position");
+ return NB_ERR;
+ }
+ index = pos - 1;
+ old_num_segs = nh->snh_seg.num_segs;
+ memset(&nh->snh_seg.seg[index], 0, sizeof(struct in6_addr));
+ nh->snh_seg.num_segs--;
+
+ if (old_num_segs != nh->snh_seg.num_segs)
+ nh->state = STATIC_START;
+ break;
+ }
+
+ return NB_OK;
+}
+
+static int static_nexthop_srv6_segs_modify(struct nb_cb_modify_args *args)
+{
+ struct static_nexthop *nh;
+ uint32_t pos;
+ uint8_t index;
+ struct in6_addr old_seg;
+ struct in6_addr cli_seg;
+
+ nh = nb_running_get_entry(args->dnode, NULL, true);
+ pos = yang_get_list_pos(lyd_parent(args->dnode));
+ if (!pos) {
+ flog_warn(EC_LIB_NB_CB_CONFIG_APPLY,
+ "libyang returns invalid seg position");
+ return NB_ERR;
+ }
+ /* Mapping to array = list-index -1 */
+ index = pos - 1;
+
+ old_seg = nh->snh_seg.seg[index];
+ yang_dnode_get_ipv6(&cli_seg, args->dnode, NULL);
+
+ memcpy(&nh->snh_seg.seg[index], &cli_seg, sizeof(struct in6_addr));
+
+ if (memcmp(&old_seg, &nh->snh_seg.seg[index],
+ sizeof(struct in6_addr)) != 0)
+ nh->state = STATIC_START;
+
+ return NB_OK;
+}
+
+static int nexthop_mpls_label_stack_entry_create(struct nb_cb_create_args *args)
+{
+ struct static_nexthop *nh;
+ uint32_t pos;
+ uint8_t index;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ if (!mpls_enabled) {
+ snprintf(
+ args->errmsg, args->errmsg_len,
+ "%% MPLS not turned on in kernel ignoring static route");
+ return NB_ERR_VALIDATION;
+ }
+ break;
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ nh = nb_running_get_entry(args->dnode, NULL, true);
+ pos = yang_get_list_pos(args->dnode);
+ if (!pos) {
+ flog_warn(EC_LIB_NB_CB_CONFIG_APPLY,
+ "libyang returns invalid label position");
+ return NB_ERR;
+ }
+ /* Mapping to array = list-index -1 */
+ index = pos - 1;
+ nh->snh_label.label[index] = 0;
+ nh->snh_label.num_labels++;
+ break;
+ }
+
+ return NB_OK;
+}
+
+static int
+nexthop_mpls_label_stack_entry_destroy(struct nb_cb_destroy_args *args)
+{
+ struct static_nexthop *nh;
+ uint32_t pos;
+ uint8_t index;
+ uint old_num_labels;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ nh = nb_running_get_entry(args->dnode, NULL, true);
+ pos = yang_get_list_pos(args->dnode);
+ if (!pos) {
+ flog_warn(EC_LIB_NB_CB_CONFIG_APPLY,
+ "libyang returns invalid label position");
+ return NB_ERR;
+ }
+ index = pos - 1;
+ old_num_labels = nh->snh_label.num_labels;
+ nh->snh_label.label[index] = 0;
+ nh->snh_label.num_labels--;
+
+ if (old_num_labels != nh->snh_label.num_labels)
+ nh->state = STATIC_START;
+ break;
+ }
+
+ return NB_OK;
+}
+
+static int static_nexthop_mpls_label_modify(struct nb_cb_modify_args *args)
+{
+ struct static_nexthop *nh;
+ uint32_t pos;
+ uint8_t index;
+ mpls_label_t old_label;
+
+ nh = nb_running_get_entry(args->dnode, NULL, true);
+ pos = yang_get_list_pos(lyd_parent(args->dnode));
+ if (!pos) {
+ flog_warn(EC_LIB_NB_CB_CONFIG_APPLY,
+ "libyang returns invalid label position");
+ return NB_ERR;
+ }
+ /* Mapping to array = list-index -1 */
+ index = pos - 1;
+
+ old_label = nh->snh_label.label[index];
+ nh->snh_label.label[index] = yang_dnode_get_uint32(args->dnode, NULL);
+
+ if (old_label != nh->snh_label.label[index])
+ nh->state = STATIC_START;
+
+ return NB_OK;
+}
+
+static int static_nexthop_onlink_modify(struct nb_cb_modify_args *args)
+{
+ struct static_nexthop *nh;
+ enum static_nh_type nh_type;
+ bool old_onlink;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ nh_type = yang_dnode_get_enum(args->dnode, "../nh-type");
+ if ((nh_type != STATIC_IPV4_GATEWAY_IFNAME)
+ && (nh_type != STATIC_IPV6_GATEWAY_IFNAME)) {
+ snprintf(
+ args->errmsg, args->errmsg_len,
+ "nexthop type is not the ipv4 or ipv6 interface type");
+ return NB_ERR_VALIDATION;
+ }
+ break;
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ nh = nb_running_get_entry(args->dnode, NULL, true);
+ old_onlink = nh->onlink;
+ nh->onlink = yang_dnode_get_bool(args->dnode, NULL);
+
+ if (old_onlink != nh->onlink)
+ nh->state = STATIC_START;
+ break;
+ }
+
+ return NB_OK;
+}
+
+static int static_nexthop_color_modify(struct nb_cb_modify_args *args)
+{
+ struct static_nexthop *nh;
+ uint32_t old_color;
+
+ nh = nb_running_get_entry(args->dnode, NULL, true);
+ old_color = nh->color;
+ nh->color = yang_dnode_get_uint32(args->dnode, NULL);
+
+ if (old_color != nh->color)
+ nh->state = STATIC_START;
+
+ return NB_OK;
+}
+
+static int static_nexthop_color_destroy(struct nb_cb_destroy_args *args)
+{
+ struct static_nexthop *nh;
+ uint32_t old_color;
+
+ nh = nb_running_get_entry(args->dnode, NULL, true);
+ old_color = nh->color;
+ nh->color = 0;
+
+ if (old_color != nh->color)
+ nh->state = STATIC_START;
+
+ return NB_OK;
+}
+
+static int static_nexthop_bh_type_modify(struct nb_cb_modify_args *args)
+{
+ struct static_nexthop *nh;
+ enum static_nh_type nh_type;
+ const char *nh_ifname;
+ const char *nh_vrf;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ nh_type = yang_dnode_get_enum(args->dnode, "../nh-type");
+ nh_ifname = yang_dnode_get_string(args->dnode, "../interface");
+ nh_vrf = yang_dnode_get_string(args->dnode, "../vrf");
+ if (nh_ifname && nh_vrf) {
+ struct vrf *vrf = vrf_lookup_by_name(nh_vrf);
+
+ if (!vrf) {
+ snprintf(args->errmsg, args->errmsg_len,
+ "nexthop vrf %s not found", nh_vrf);
+ return NB_ERR_VALIDATION;
+ }
+
+ struct interface *ifp = if_lookup_by_name(nh_ifname,
+ vrf->vrf_id);
+
+ if (ifp && (!strmatch(nh_ifname, "blackhole") ||
+ !strmatch(nh_ifname, "reject"))) {
+ snprintf(args->errmsg, args->errmsg_len,
+ "nexthop interface name must be (reject, blackhole)");
+ return NB_ERR_VALIDATION;
+ }
+ }
+ if (nh_type != STATIC_BLACKHOLE) {
+ snprintf(args->errmsg, args->errmsg_len,
+ "nexthop type is not the blackhole type");
+ return NB_ERR_VALIDATION;
+ }
+ break;
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ nh = nb_running_get_entry(args->dnode, NULL, true);
+ nh->bh_type = yang_dnode_get_enum(args->dnode, NULL);
+ break;
+ }
+
+ return NB_OK;
+}
+
+void routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_apply_finish(
+ struct nb_cb_apply_finish_args *args)
+{
+ struct static_nexthop *nh;
+
+ nh = nb_running_get_entry(args->dnode, NULL, true);
+
+ static_install_nexthop(nh);
+}
+
+void routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_apply_finish(
+ struct nb_cb_apply_finish_args *args)
+{
+ struct static_nexthop *nh;
+
+ nh = nb_running_get_entry(args->dnode, NULL, true);
+
+ static_install_nexthop(nh);
+}
+
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_pre_validate(
+ struct nb_cb_pre_validate_args *args)
+{
+ const struct lyd_node *mls_dnode;
+ uint32_t count;
+
+ mls_dnode = yang_dnode_get(args->dnode, "./mpls-label-stack");
+ count = yang_get_list_elements_count(lyd_child(mls_dnode));
+
+ if (count > MPLS_MAX_LABELS) {
+ snprintf(args->errmsg, args->errmsg_len,
+ "Too many labels, Enter %d or fewer",
+ MPLS_MAX_LABELS);
+ return NB_ERR_VALIDATION;
+ }
+ return NB_OK;
+}
+
+int routing_control_plane_protocols_name_validate(
+ struct nb_cb_create_args *args)
+{
+ const char *name;
+
+ name = yang_dnode_get_string(args->dnode, "./name");
+ if (!strmatch(name, "staticd")) {
+ snprintf(args->errmsg, args->errmsg_len,
+ "static routing supports only one instance with name staticd");
+ return NB_ERR_VALIDATION;
+ }
+ return NB_OK;
+}
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_create(
+ struct nb_cb_create_args *args)
+{
+ struct vrf *vrf;
+ struct static_vrf *s_vrf;
+ struct route_node *rn;
+ const struct lyd_node *vrf_dnode;
+ struct prefix prefix;
+ const char *afi_safi;
+ afi_t prefix_afi;
+ afi_t afi;
+ safi_t safi;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ yang_dnode_get_prefix(&prefix, args->dnode, "./prefix");
+ afi_safi = yang_dnode_get_string(args->dnode, "./afi-safi");
+ yang_afi_safi_identity2value(afi_safi, &afi, &safi);
+ prefix_afi = family2afi(prefix.family);
+ if (afi != prefix_afi) {
+ flog_warn(
+ EC_LIB_NB_CB_CONFIG_VALIDATE,
+ "route node %s creation failed",
+ yang_dnode_get_string(args->dnode, "./prefix"));
+ return NB_ERR_VALIDATION;
+ }
+ break;
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ vrf_dnode = yang_dnode_get_parent(args->dnode,
+ "control-plane-protocol");
+ vrf = nb_running_get_entry(vrf_dnode, NULL, true);
+ s_vrf = vrf->info;
+
+ yang_dnode_get_prefix(&prefix, args->dnode, "./prefix");
+ afi_safi = yang_dnode_get_string(args->dnode, "./afi-safi");
+ yang_afi_safi_identity2value(afi_safi, &afi, &safi);
+
+ rn = static_add_route(afi, safi, &prefix, NULL, s_vrf);
+ if (vrf->vrf_id == VRF_UNKNOWN)
+ snprintf(
+ args->errmsg, args->errmsg_len,
+ "Static Route to %s not installed currently because dependent config not fully available",
+ yang_dnode_get_string(args->dnode, "./prefix"));
+ nb_running_set_entry(args->dnode, rn);
+ break;
+ }
+ return NB_OK;
+}
+
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ struct route_node *rn;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ rn = nb_running_unset_entry(args->dnode);
+ static_del_route(rn);
+ break;
+ }
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_create(
+ struct nb_cb_create_args *args)
+{
+ return static_path_list_create(args);
+}
+
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ return static_path_list_destroy(args);
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/tag
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_tag_modify(
+ struct nb_cb_modify_args *args)
+{
+ return static_path_list_tag_modify(args);
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_create(
+ struct nb_cb_create_args *args)
+{
+ return static_nexthop_create(args);
+}
+
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ return static_nexthop_destroy(args);
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/bh-type
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_bh_type_modify(
+ struct nb_cb_modify_args *args)
+{
+ return static_nexthop_bh_type_modify(args);
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/onlink
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_onlink_modify(
+ struct nb_cb_modify_args *args)
+{
+ return static_nexthop_onlink_modify(args);
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/srte-color
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_color_modify(
+ struct nb_cb_modify_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ if (static_nexthop_color_modify(args) != NB_OK)
+ return NB_ERR;
+
+ break;
+ }
+ return NB_OK;
+}
+
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_color_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ if (static_nexthop_color_destroy(args) != NB_OK)
+ return NB_ERR;
+ break;
+ }
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/srv6-segs-stack/entry
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_create(
+ struct nb_cb_create_args *args)
+{
+ return nexthop_srv6_segs_stack_entry_create(args);
+}
+
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ return nexthop_srv6_segs_stack_entry_destroy(args);
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/srv6-segs-stack/entry/seg
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_modify(
+ struct nb_cb_modify_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ if (static_nexthop_srv6_segs_modify(args) != NB_OK)
+ return NB_ERR;
+ break;
+ }
+ return NB_OK;
+}
+
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ /*
+ * No operation is required in this call back.
+ * nexthop_srv6_segs_stack_entry_destroy() will take care
+ * to reset the seg vaue.
+ */
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ case NB_EV_APPLY:
+ break;
+ }
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_create(
+ struct nb_cb_create_args *args)
+{
+ return nexthop_mpls_label_stack_entry_create(args);
+}
+
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ return nexthop_mpls_label_stack_entry_destroy(args);
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/label
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_modify(
+ struct nb_cb_modify_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ if (static_nexthop_mpls_label_modify(args) != NB_OK)
+ return NB_ERR;
+ break;
+ }
+ return NB_OK;
+}
+
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ /*
+ * No operation is required in this call back.
+ * nexthop_mpls_label_stack_entry_destroy() will take care
+ * to reset the label vaue.
+ */
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ case NB_EV_APPLY:
+ break;
+ }
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/ttl
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_modify(
+ struct nb_cb_modify_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ case NB_EV_APPLY:
+ break;
+ }
+
+ return NB_OK;
+}
+
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ case NB_EV_APPLY:
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/traffic-class
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_modify(
+ struct nb_cb_modify_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ case NB_EV_APPLY:
+ break;
+ }
+
+ return NB_OK;
+}
+
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ case NB_EV_APPLY:
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/bfd-monitoring
+ */
+int route_next_hop_bfd_create(struct nb_cb_create_args *args)
+{
+ struct static_nexthop *sn;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ sn = nb_running_get_entry(args->dnode, NULL, true);
+ static_next_hop_bfd_monitor_enable(sn, args->dnode);
+ return NB_OK;
+}
+
+int route_next_hop_bfd_destroy(struct nb_cb_destroy_args *args)
+{
+ struct static_nexthop *sn;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ sn = nb_running_get_entry(args->dnode, NULL, true);
+ static_next_hop_bfd_monitor_disable(sn);
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/bfd-monitoring/source
+ */
+int route_next_hop_bfd_source_modify(struct nb_cb_modify_args *args)
+{
+ struct static_nexthop *sn;
+ struct ipaddr source;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ sn = nb_running_get_entry(args->dnode, NULL, true);
+ yang_dnode_get_ip(&source, args->dnode, NULL);
+ static_next_hop_bfd_source(sn, &source);
+ return NB_OK;
+}
+
+int route_next_hop_bfd_source_destroy(struct nb_cb_destroy_args *args)
+{
+ struct static_nexthop *sn;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ sn = nb_running_get_entry(args->dnode, NULL, true);
+ static_next_hop_bfd_auto_source(sn);
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/bfd-monitoring/multi-hop
+ */
+int route_next_hop_bfd_multi_hop_modify(struct nb_cb_modify_args *args)
+{
+ struct static_nexthop *sn;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ sn = nb_running_get_entry(args->dnode, NULL, true);
+ static_next_hop_bfd_multi_hop(sn,
+ yang_dnode_get_bool(args->dnode, NULL));
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/bfd-monitoring/profile
+ */
+int route_next_hop_bfd_profile_modify(struct nb_cb_modify_args *args)
+{
+ struct static_nexthop *sn;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ sn = nb_running_get_entry(args->dnode, NULL, true);
+ static_next_hop_bfd_profile(sn,
+ yang_dnode_get_string(args->dnode, NULL));
+
+ return NB_OK;
+}
+
+int route_next_hop_bfd_profile_destroy(struct nb_cb_destroy_args *args)
+{
+ struct static_nexthop *sn;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ sn = nb_running_get_entry(args->dnode, NULL, true);
+ static_next_hop_bfd_profile(sn, NULL);
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_create(
+ struct nb_cb_create_args *args)
+{
+ struct static_vrf *s_vrf;
+ struct route_node *rn;
+ struct route_node *src_rn;
+ struct prefix_ipv6 src_prefix = {};
+ struct stable_info *info;
+ afi_t afi;
+ safi_t safi = SAFI_UNICAST;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ rn = nb_running_get_entry(args->dnode, NULL, true);
+ info = route_table_get_info(rn->table);
+ s_vrf = info->svrf;
+ yang_dnode_get_ipv6p(&src_prefix, args->dnode, "./src-prefix");
+ afi = family2afi(src_prefix.family);
+ src_rn =
+ static_add_route(afi, safi, &rn->p, &src_prefix, s_vrf);
+ nb_running_set_entry(args->dnode, src_rn);
+ break;
+ }
+ return NB_OK;
+}
+
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ struct route_node *src_rn;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ src_rn = nb_running_unset_entry(args->dnode);
+ static_del_route(src_rn);
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_create(
+ struct nb_cb_create_args *args)
+{
+ return static_path_list_create(args);
+}
+
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ return static_path_list_destroy(args);
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/tag
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_tag_modify(
+ struct nb_cb_modify_args *args)
+{
+ return static_path_list_tag_modify(args);
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_create(
+ struct nb_cb_create_args *args)
+{
+ return static_nexthop_create(args);
+}
+
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ return static_nexthop_destroy(args);
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/bh-type
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_bh_type_modify(
+ struct nb_cb_modify_args *args)
+{
+ return static_nexthop_bh_type_modify(args);
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/onlink
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_onlink_modify(
+ struct nb_cb_modify_args *args)
+{
+ return static_nexthop_onlink_modify(args);
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/srte-color
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_modify(
+ struct nb_cb_modify_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ if (static_nexthop_color_modify(args) != NB_OK)
+ return NB_ERR;
+
+ break;
+ }
+ return NB_OK;
+}
+
+
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ if (static_nexthop_color_destroy(args) != NB_OK)
+ return NB_ERR;
+ break;
+ }
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/srv6-segs-stack/entry
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_create(
+ struct nb_cb_create_args *args)
+{
+ return nexthop_srv6_segs_stack_entry_create(args);
+}
+
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ return nexthop_srv6_segs_stack_entry_destroy(args);
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/srv6-segs-stack/entry/seg
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_modify(
+ struct nb_cb_modify_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ if (static_nexthop_srv6_segs_modify(args) != NB_OK)
+ return NB_ERR;
+ break;
+ }
+ return NB_OK;
+}
+
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ /*
+ * No operation is required in this call back.
+ * nexthop_mpls_seg_stack_entry_destroy() will take care
+ * to reset the seg vaue.
+ */
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ case NB_EV_APPLY:
+ break;
+ }
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_create(
+ struct nb_cb_create_args *args)
+{
+ return nexthop_mpls_label_stack_entry_create(args);
+}
+
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ return nexthop_mpls_label_stack_entry_destroy(args);
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/label
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_modify(
+ struct nb_cb_modify_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ if (static_nexthop_mpls_label_modify(args) != NB_OK)
+ return NB_ERR;
+ break;
+ }
+ return NB_OK;
+}
+
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ /*
+ * No operation is required in this call back.
+ * nexthop_mpls_label_stack_entry_destroy() will take care
+ * to reset the label vaue.
+ */
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ case NB_EV_APPLY:
+ break;
+ }
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/ttl
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_modify(
+ struct nb_cb_modify_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ case NB_EV_APPLY:
+ break;
+ }
+
+ return NB_OK;
+}
+
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ case NB_EV_APPLY:
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/traffic-class
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_modify(
+ struct nb_cb_modify_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ case NB_EV_APPLY:
+ break;
+ }
+
+ return NB_OK;
+}
+
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ case NB_EV_APPLY:
+ break;
+ }
+
+ return NB_OK;
+}
diff --git a/staticd/static_nht.c b/staticd/static_nht.c
new file mode 100644
index 0000000..ebc5ea1
--- /dev/null
+++ b/staticd/static_nht.c
@@ -0,0 +1,211 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Static NHT code.
+ * Copyright (C) 2018 Cumulus Networks, Inc.
+ * Donald Sharp
+ */
+#include <zebra.h>
+
+#include "prefix.h"
+#include "table.h"
+#include "vrf.h"
+#include "nexthop.h"
+#include "srcdest_table.h"
+
+#include "static_vrf.h"
+#include "static_routes.h"
+#include "static_zebra.h"
+#include "static_nht.h"
+
+static void static_nht_update_path(struct static_path *pn, struct prefix *nhp,
+ uint32_t nh_num, vrf_id_t nh_vrf_id,
+ struct vrf *vrf)
+{
+ struct static_nexthop *nh;
+
+ frr_each(static_nexthop_list, &pn->nexthop_list, nh) {
+ if (nh->nh_vrf_id != nh_vrf_id)
+ continue;
+
+ if (nh->type != STATIC_IPV4_GATEWAY
+ && nh->type != STATIC_IPV4_GATEWAY_IFNAME
+ && nh->type != STATIC_IPV6_GATEWAY
+ && nh->type != STATIC_IPV6_GATEWAY_IFNAME)
+ continue;
+
+ if (nhp->family == AF_INET
+ && nhp->u.prefix4.s_addr == nh->addr.ipv4.s_addr)
+ nh->nh_valid = !!nh_num;
+
+ if (nhp->family == AF_INET6
+ && memcmp(&nhp->u.prefix6, &nh->addr.ipv6, IPV6_MAX_BYTELEN)
+ == 0)
+ nh->nh_valid = !!nh_num;
+
+ if (nh->state == STATIC_START)
+ static_zebra_route_add(pn, true);
+ }
+}
+
+static void static_nht_update_safi(struct prefix *sp, struct prefix *nhp,
+ uint32_t nh_num, afi_t afi, safi_t safi,
+ struct vrf *vrf, vrf_id_t nh_vrf_id)
+{
+ struct route_table *stable;
+ struct static_vrf *svrf;
+ struct route_node *rn;
+ struct static_path *pn;
+ struct static_route_info *si;
+
+ svrf = vrf->info;
+ if (!svrf)
+ return;
+
+ stable = static_vrf_static_table(afi, safi, svrf);
+ if (!stable)
+ return;
+
+ if (sp) {
+ rn = srcdest_rnode_lookup(stable, sp, NULL);
+ if (rn && rn->info) {
+ si = static_route_info_from_rnode(rn);
+ frr_each(static_path_list, &si->path_list, pn) {
+ static_nht_update_path(pn, nhp, nh_num,
+ nh_vrf_id, vrf);
+ }
+ route_unlock_node(rn);
+ }
+ return;
+ }
+
+ for (rn = route_top(stable); rn; rn = route_next(rn)) {
+ si = static_route_info_from_rnode(rn);
+ if (!si)
+ continue;
+ frr_each(static_path_list, &si->path_list, pn) {
+ static_nht_update_path(pn, nhp, nh_num, nh_vrf_id, vrf);
+ }
+ }
+}
+
+void static_nht_update(struct prefix *sp, struct prefix *nhp, uint32_t nh_num,
+ afi_t afi, safi_t safi, vrf_id_t nh_vrf_id)
+{
+
+ struct vrf *vrf;
+
+ RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name)
+ static_nht_update_safi(sp, nhp, nh_num, afi, safi, vrf,
+ nh_vrf_id);
+}
+
+static void static_nht_reset_start_safi(struct prefix *nhp, afi_t afi,
+ safi_t safi, struct vrf *vrf,
+ vrf_id_t nh_vrf_id)
+{
+ struct static_vrf *svrf;
+ struct route_table *stable;
+ struct static_nexthop *nh;
+ struct static_path *pn;
+ struct route_node *rn;
+ struct static_route_info *si;
+
+ svrf = vrf->info;
+ if (!svrf)
+ return;
+
+ stable = static_vrf_static_table(afi, safi, svrf);
+ if (!stable)
+ return;
+
+ for (rn = route_top(stable); rn; rn = route_next(rn)) {
+ si = static_route_info_from_rnode(rn);
+ if (!si)
+ continue;
+ frr_each(static_path_list, &si->path_list, pn) {
+ frr_each(static_nexthop_list, &pn->nexthop_list, nh) {
+ if (nh->nh_vrf_id != nh_vrf_id)
+ continue;
+
+ if (nhp->family == AF_INET
+ && nhp->u.prefix4.s_addr
+ != nh->addr.ipv4.s_addr)
+ continue;
+
+ if (nhp->family == AF_INET6
+ && memcmp(&nhp->u.prefix6, &nh->addr.ipv6,
+ 16)
+ != 0)
+ continue;
+
+ /*
+ * We've been told that a nexthop we
+ * depend on has changed in some manner,
+ * so reset the state machine to allow
+ * us to start over.
+ */
+ nh->state = STATIC_START;
+ }
+ }
+ }
+}
+
+void static_nht_reset_start(struct prefix *nhp, afi_t afi, safi_t safi,
+ vrf_id_t nh_vrf_id)
+{
+ struct vrf *vrf;
+
+ RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name)
+ static_nht_reset_start_safi(nhp, afi, safi, vrf, nh_vrf_id);
+}
+
+static void static_nht_mark_state_safi(struct prefix *sp, afi_t afi,
+ safi_t safi, struct vrf *vrf,
+ enum static_install_states state)
+{
+ struct static_vrf *svrf;
+ struct route_table *stable;
+ struct route_node *rn;
+ struct static_nexthop *nh;
+ struct static_path *pn;
+ struct static_route_info *si;
+
+ svrf = vrf->info;
+ if (!svrf)
+ return;
+
+ stable = static_vrf_static_table(afi, safi, svrf);
+ if (!stable)
+ return;
+
+ rn = srcdest_rnode_lookup(stable, sp, NULL);
+ if (!rn)
+ return;
+ si = rn->info;
+ if (si) {
+ frr_each(static_path_list, &si->path_list, pn) {
+ frr_each(static_nexthop_list, &pn->nexthop_list, nh) {
+ nh->state = state;
+ }
+ }
+ }
+
+ route_unlock_node(rn);
+}
+
+void static_nht_mark_state(struct prefix *sp, safi_t safi, vrf_id_t vrf_id,
+ enum static_install_states state)
+{
+ struct vrf *vrf;
+
+ afi_t afi = AFI_IP;
+
+ if (sp->family == AF_INET6)
+ afi = AFI_IP6;
+
+ vrf = vrf_lookup_by_id(vrf_id);
+ if (!vrf || !vrf->info)
+ return;
+
+ static_nht_mark_state_safi(sp, afi, safi, vrf, state);
+}
diff --git a/staticd/static_nht.h b/staticd/static_nht.h
new file mode 100644
index 0000000..74f4401
--- /dev/null
+++ b/staticd/static_nht.h
@@ -0,0 +1,54 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Static NHT header.
+ * Copyright (C) 2018 Cumulus Networks, Inc.
+ * Donald Sharp
+ */
+#ifndef __STATIC_NHT_H__
+#define __STATIC_NHT_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * When we get notification that nexthop tracking has an answer for
+ * us call this function to find the nexthop we are tracking so it
+ * can be installed or removed.
+ *
+ * sp -> The route we are looking at. If NULL then look at all
+ * routes.
+ * nhp -> The nexthop that is being tracked.
+ * nh_num -> number of valid nexthops.
+ * afi -> The afi we are working in.
+ * vrf_id -> The vrf the nexthop is in.
+ */
+extern void static_nht_update(struct prefix *sp, struct prefix *nhp,
+ uint32_t nh_num, afi_t afi, safi_t safi,
+ vrf_id_t vrf_id);
+
+/*
+ * For the given tracked nexthop, nhp, mark all routes that use
+ * this route as in starting state again.
+ */
+extern void static_nht_reset_start(struct prefix *nhp, afi_t afi, safi_t safi,
+ vrf_id_t nh_vrf_id);
+
+/*
+ * For the given prefix, sp, mark it as in a particular state
+ */
+extern void static_nht_mark_state(struct prefix *sp, safi_t safi,
+ vrf_id_t vrf_id,
+ enum static_install_states state);
+
+/*
+ * For the given nexthop, returns the string
+ */
+extern void static_get_nh_str(struct static_nexthop *nh, char *nexthop,
+ size_t size);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/staticd/static_routes.c b/staticd/static_routes.c
new file mode 100644
index 0000000..1fbbf7e
--- /dev/null
+++ b/staticd/static_routes.c
@@ -0,0 +1,792 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * STATICd - route code
+ * Copyright (C) 2018 Cumulus Networks, Inc.
+ * Donald Sharp
+ */
+#include <zebra.h>
+
+#include <lib/nexthop.h>
+#include <lib/memory.h>
+#include <lib/srcdest_table.h>
+#include <lib/if.h>
+#include <lib/vty.h>
+#include <lib/vrf.h>
+#include <lib/memory.h>
+
+#include "printfrr.h"
+
+#include "static_vrf.h"
+#include "static_routes.h"
+#include "static_zebra.h"
+#include "static_debug.h"
+
+DEFINE_MGROUP(STATIC, "staticd");
+
+DEFINE_MTYPE_STATIC(STATIC, STATIC_ROUTE, "Static Route Info");
+DEFINE_MTYPE_STATIC(STATIC, STATIC_PATH, "Static Path");
+DEFINE_MTYPE_STATIC(STATIC, STATIC_NEXTHOP, "Static Nexthop");
+
+void zebra_stable_node_cleanup(struct route_table *table,
+ struct route_node *node)
+{
+ struct static_nexthop *nh;
+ struct static_path *pn;
+ struct static_route_info *si;
+ struct route_table *src_table;
+ struct route_node *src_node;
+ struct static_path *src_pn;
+ struct static_route_info *src_si;
+
+ si = node->info;
+
+ if (si) {
+ frr_each_safe(static_path_list, &si->path_list, pn) {
+ frr_each_safe(static_nexthop_list, &pn->nexthop_list,
+ nh) {
+ static_nexthop_list_del(&pn->nexthop_list, nh);
+ XFREE(MTYPE_STATIC_NEXTHOP, nh);
+ }
+ static_path_list_del(&si->path_list, pn);
+ XFREE(MTYPE_STATIC_PATH, pn);
+ }
+
+ /* clean up for dst table */
+ src_table = srcdest_srcnode_table(node);
+ if (src_table) {
+ /* This means the route_node is part of the top
+ * hierarchy and refers to a destination prefix.
+ */
+ for (src_node = route_top(src_table); src_node;
+ src_node = route_next(src_node)) {
+ src_si = src_node->info;
+
+ frr_each_safe(static_path_list,
+ &src_si->path_list, src_pn) {
+ frr_each_safe(static_nexthop_list,
+ &src_pn->nexthop_list,
+ nh) {
+ static_nexthop_list_del(
+ &src_pn->nexthop_list,
+ nh);
+ XFREE(MTYPE_STATIC_NEXTHOP, nh);
+ }
+ static_path_list_del(&src_si->path_list,
+ src_pn);
+ XFREE(MTYPE_STATIC_PATH, src_pn);
+ }
+
+ XFREE(MTYPE_STATIC_ROUTE, src_node->info);
+ }
+ }
+
+ XFREE(MTYPE_STATIC_ROUTE, node->info);
+ }
+}
+
+/* Install static path into rib. */
+void static_install_path(struct static_path *pn)
+{
+ struct static_nexthop *nh;
+
+ frr_each(static_nexthop_list, &pn->nexthop_list, nh)
+ static_zebra_nht_register(nh, true);
+
+ if (static_nexthop_list_count(&pn->nexthop_list))
+ static_zebra_route_add(pn, true);
+}
+
+/* Uninstall static path from RIB. */
+static void static_uninstall_path(struct static_path *pn)
+{
+ if (static_nexthop_list_count(&pn->nexthop_list))
+ static_zebra_route_add(pn, true);
+ else
+ static_zebra_route_add(pn, false);
+}
+
+struct route_node *static_add_route(afi_t afi, safi_t safi, struct prefix *p,
+ struct prefix_ipv6 *src_p,
+ struct static_vrf *svrf)
+{
+ struct route_node *rn;
+ struct static_route_info *si;
+ struct route_table *stable = svrf->stable[afi][safi];
+
+ assert(stable);
+
+ /* Lookup static route prefix. */
+ rn = srcdest_rnode_get(stable, p, src_p);
+
+ si = XCALLOC(MTYPE_STATIC_ROUTE, sizeof(struct static_route_info));
+
+ si->svrf = svrf;
+ si->safi = safi;
+ static_path_list_init(&(si->path_list));
+
+ rn->info = si;
+
+ return rn;
+}
+
+/* To delete the srcnodes */
+static void static_del_src_route(struct route_node *rn)
+{
+ struct static_path *pn;
+ struct static_route_info *si;
+
+ si = rn->info;
+
+ frr_each_safe(static_path_list, &si->path_list, pn) {
+ static_del_path(pn);
+ }
+
+ XFREE(MTYPE_STATIC_ROUTE, rn->info);
+ route_unlock_node(rn);
+}
+
+void static_del_route(struct route_node *rn)
+{
+ struct static_path *pn;
+ struct static_route_info *si;
+ struct route_table *src_table;
+ struct route_node *src_node;
+
+ si = rn->info;
+
+ frr_each_safe(static_path_list, &si->path_list, pn) {
+ static_del_path(pn);
+ }
+
+ /* clean up for dst table */
+ src_table = srcdest_srcnode_table(rn);
+ if (src_table) {
+ /* This means the route_node is part of the top hierarchy
+ * and refers to a destination prefix.
+ */
+ for (src_node = route_top(src_table); src_node;
+ src_node = route_next(src_node)) {
+ static_del_src_route(src_node);
+ }
+ }
+ XFREE(MTYPE_STATIC_ROUTE, rn->info);
+ route_unlock_node(rn);
+}
+
+bool static_add_nexthop_validate(const char *nh_vrf_name,
+ enum static_nh_type type,
+ struct ipaddr *ipaddr)
+{
+ struct vrf *vrf;
+
+ vrf = vrf_lookup_by_name(nh_vrf_name);
+ if (!vrf)
+ return true;
+
+ switch (type) {
+ case STATIC_IPV4_GATEWAY:
+ case STATIC_IPV4_GATEWAY_IFNAME:
+ if (if_address_is_local(&ipaddr->ipaddr_v4, AF_INET,
+ vrf->vrf_id))
+ return false;
+ break;
+ case STATIC_IPV6_GATEWAY:
+ case STATIC_IPV6_GATEWAY_IFNAME:
+ if (if_address_is_local(&ipaddr->ipaddr_v6, AF_INET6,
+ vrf->vrf_id))
+ return false;
+ break;
+ case STATIC_IFNAME:
+ case STATIC_BLACKHOLE:
+ break;
+ }
+
+ return true;
+}
+
+struct static_path *static_add_path(struct route_node *rn, uint32_t table_id,
+ uint8_t distance)
+{
+ struct static_path *pn;
+ struct static_route_info *si;
+
+ route_lock_node(rn);
+
+ /* Make new static route structure. */
+ pn = XCALLOC(MTYPE_STATIC_PATH, sizeof(struct static_path));
+
+ pn->rn = rn;
+ pn->distance = distance;
+ pn->table_id = table_id;
+ static_nexthop_list_init(&(pn->nexthop_list));
+
+ si = rn->info;
+ static_path_list_add_head(&(si->path_list), pn);
+
+ return pn;
+}
+
+void static_del_path(struct static_path *pn)
+{
+ struct route_node *rn = pn->rn;
+ struct static_route_info *si;
+ struct static_nexthop *nh;
+
+ si = rn->info;
+
+ static_path_list_del(&si->path_list, pn);
+
+ frr_each_safe(static_nexthop_list, &pn->nexthop_list, nh) {
+ static_delete_nexthop(nh);
+ }
+
+ route_unlock_node(rn);
+
+ XFREE(MTYPE_STATIC_PATH, pn);
+}
+
+struct static_nexthop *static_add_nexthop(struct static_path *pn,
+ enum static_nh_type type,
+ struct ipaddr *ipaddr,
+ const char *ifname,
+ const char *nh_vrf, uint32_t color)
+{
+ struct route_node *rn = pn->rn;
+ struct static_nexthop *nh;
+ struct static_vrf *nh_svrf;
+ struct interface *ifp;
+ struct static_nexthop *cp;
+
+ route_lock_node(rn);
+
+ nh_svrf = static_vrf_lookup_by_name(nh_vrf);
+
+ /* Make new static route structure. */
+ nh = XCALLOC(MTYPE_STATIC_NEXTHOP, sizeof(struct static_nexthop));
+
+ /* Copy back pointers. */
+ nh->rn = rn;
+ nh->pn = pn;
+
+ nh->type = type;
+ nh->color = color;
+
+ if (nh->type == STATIC_BLACKHOLE)
+ nh->bh_type = STATIC_BLACKHOLE_NULL;
+
+ nh->nh_vrf_id = nh_svrf ? nh_svrf->vrf->vrf_id : VRF_UNKNOWN;
+ strlcpy(nh->nh_vrfname, nh_vrf, sizeof(nh->nh_vrfname));
+
+ if (ifname)
+ strlcpy(nh->ifname, ifname, sizeof(nh->ifname));
+ nh->ifindex = IFINDEX_INTERNAL;
+
+ switch (type) {
+ case STATIC_IPV4_GATEWAY:
+ case STATIC_IPV4_GATEWAY_IFNAME:
+ nh->addr.ipv4 = ipaddr->ipaddr_v4;
+ break;
+ case STATIC_IPV6_GATEWAY:
+ case STATIC_IPV6_GATEWAY_IFNAME:
+ nh->addr.ipv6 = ipaddr->ipaddr_v6;
+ break;
+ case STATIC_IFNAME:
+ case STATIC_BLACKHOLE:
+ break;
+ }
+ /*
+ * Add new static route information to the tree with sort by
+ * gateway address.
+ */
+ frr_each(static_nexthop_list, &pn->nexthop_list, cp) {
+ if (nh->type == STATIC_IPV4_GATEWAY
+ && cp->type == STATIC_IPV4_GATEWAY) {
+ if (ntohl(nh->addr.ipv4.s_addr)
+ < ntohl(cp->addr.ipv4.s_addr))
+ break;
+ if (ntohl(nh->addr.ipv4.s_addr)
+ > ntohl(cp->addr.ipv4.s_addr))
+ continue;
+ }
+ }
+ static_nexthop_list_add_after(&(pn->nexthop_list), cp, nh);
+
+ if (nh->nh_vrf_id == VRF_UNKNOWN) {
+ zlog_warn(
+ "Static Route to %pFX not installed currently because dependent config not fully available",
+ &rn->p);
+ return nh;
+ }
+
+ /* check whether interface exists in system & install if it does */
+ switch (nh->type) {
+ case STATIC_IPV4_GATEWAY:
+ case STATIC_IPV6_GATEWAY:
+ case STATIC_BLACKHOLE:
+ break;
+ case STATIC_IPV4_GATEWAY_IFNAME:
+ case STATIC_IPV6_GATEWAY_IFNAME:
+ case STATIC_IFNAME:
+ ifp = if_lookup_by_name(ifname, nh->nh_vrf_id);
+ if (ifp && ifp->ifindex != IFINDEX_INTERNAL)
+ nh->ifindex = ifp->ifindex;
+ else
+ zlog_warn(
+ "Static Route using %s interface not installed because the interface does not exist in specified vrf",
+ ifname);
+ break;
+ }
+
+ return nh;
+}
+
+void static_install_nexthop(struct static_nexthop *nh)
+{
+ struct static_path *pn = nh->pn;
+ struct route_node *rn = pn->rn;
+ struct interface *ifp;
+
+ if (nh->nh_vrf_id == VRF_UNKNOWN) {
+ char nexthop_str[NEXTHOP_STR];
+
+ static_get_nh_str(nh, nexthop_str, sizeof(nexthop_str));
+ DEBUGD(&static_dbg_route,
+ "Static Route %pFX not installed for %s vrf %s is unknown",
+ &rn->p, nexthop_str, nh->nh_vrfname);
+ return;
+ }
+
+ /* check whether interface exists in system & install if it does */
+ switch (nh->type) {
+ case STATIC_IPV4_GATEWAY:
+ case STATIC_IPV6_GATEWAY:
+ static_zebra_nht_register(nh, true);
+ break;
+ case STATIC_IPV4_GATEWAY_IFNAME:
+ case STATIC_IPV6_GATEWAY_IFNAME:
+ static_zebra_nht_register(nh, true);
+ break;
+ case STATIC_BLACKHOLE:
+ static_install_path(pn);
+ break;
+ case STATIC_IFNAME:
+ ifp = if_lookup_by_name(nh->ifname, nh->nh_vrf_id);
+ if (ifp && ifp->ifindex != IFINDEX_INTERNAL)
+ static_install_path(pn);
+
+ break;
+ }
+}
+
+void static_delete_nexthop(struct static_nexthop *nh)
+{
+ struct static_path *pn = nh->pn;
+ struct route_node *rn = pn->rn;
+
+ static_nexthop_list_del(&(pn->nexthop_list), nh);
+ /* Remove BFD session/configuration if any. */
+ bfd_sess_free(&nh->bsp);
+
+ if (nh->nh_vrf_id == VRF_UNKNOWN)
+ goto EXIT;
+
+ static_zebra_nht_register(nh, false);
+ /*
+ * If we have other si nodes then route replace
+ * else delete the route
+ */
+ static_uninstall_path(pn);
+
+EXIT:
+ route_unlock_node(rn);
+ /* Free static route configuration. */
+ XFREE(MTYPE_STATIC_NEXTHOP, nh);
+}
+
+static void static_ifindex_update_nh(struct interface *ifp, bool up,
+ struct route_node *rn,
+ struct static_path *pn,
+ struct static_nexthop *nh,
+ struct static_vrf *svrf, safi_t safi)
+{
+ if (!nh->ifname[0])
+ return;
+ if (up) {
+ if (strcmp(nh->ifname, ifp->name))
+ return;
+ if (nh->nh_vrf_id != ifp->vrf->vrf_id)
+ return;
+ nh->ifindex = ifp->ifindex;
+ } else {
+ if (nh->ifindex != ifp->ifindex)
+ return;
+ if (nh->nh_vrf_id != ifp->vrf->vrf_id)
+ return;
+ nh->ifindex = IFINDEX_INTERNAL;
+ }
+
+ /* Remove previously configured route if any. */
+ static_uninstall_path(pn);
+ static_install_path(pn);
+}
+
+static void static_ifindex_update_af(struct interface *ifp, bool up, afi_t afi,
+ safi_t safi)
+{
+ struct route_table *stable;
+ struct route_node *rn;
+ struct static_nexthop *nh;
+ struct static_path *pn;
+ struct vrf *vrf;
+ struct static_route_info *si;
+
+ RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
+ struct static_vrf *svrf;
+
+ svrf = vrf->info;
+
+ stable = static_vrf_static_table(afi, safi, svrf);
+ if (!stable)
+ continue;
+ for (rn = route_top(stable); rn; rn = srcdest_route_next(rn)) {
+ si = static_route_info_from_rnode(rn);
+ if (!si)
+ continue;
+ frr_each(static_path_list, &si->path_list, pn) {
+ frr_each(static_nexthop_list,
+ &pn->nexthop_list, nh) {
+ static_ifindex_update_nh(ifp, up, rn,
+ pn, nh, svrf,
+ safi);
+ }
+ }
+ }
+ }
+}
+
+/*
+ * This function looks at a svrf's stable and notices if any of the
+ * nexthops we are using are part of the vrf coming up.
+ * If we are using them then cleanup the nexthop vrf id
+ * to be the new value and then re-installs them
+ *
+ *
+ * stable -> The table we are looking at.
+ * svrf -> The newly changed vrf.
+ * afi -> The afi to look at
+ * safi -> the safi to look at
+ */
+static void static_fixup_vrf(struct static_vrf *svrf,
+ struct route_table *stable, afi_t afi, safi_t safi)
+{
+ struct route_node *rn;
+ struct static_nexthop *nh;
+ struct interface *ifp;
+ struct static_path *pn;
+ struct static_route_info *si;
+
+ for (rn = route_top(stable); rn; rn = route_next(rn)) {
+ si = static_route_info_from_rnode(rn);
+ if (!si)
+ continue;
+ frr_each(static_path_list, &si->path_list, pn) {
+ frr_each(static_nexthop_list, &pn->nexthop_list, nh) {
+ if (strcmp(svrf->vrf->name, nh->nh_vrfname)
+ != 0)
+ continue;
+
+ nh->nh_vrf_id = svrf->vrf->vrf_id;
+ nh->nh_registered = false;
+ if (nh->ifindex) {
+ ifp = if_lookup_by_name(nh->ifname,
+ nh->nh_vrf_id);
+ if (ifp)
+ nh->ifindex = ifp->ifindex;
+ else
+ continue;
+ }
+
+ static_install_path(pn);
+ }
+ }
+ }
+}
+
+/*
+ * This function enables static routes in a svrf as it
+ * is coming up. It sets the new vrf_id as appropriate.
+ *
+ * svrf -> The svrf that is being brought up and enabled by the kernel
+ * stable -> The stable we are looking at.
+ * afi -> the afi in question
+ * safi -> the safi in question
+ */
+static void static_enable_vrf(struct static_vrf *svrf,
+ struct route_table *stable, afi_t afi,
+ safi_t safi)
+{
+ struct route_node *rn;
+ struct static_nexthop *nh;
+ struct interface *ifp;
+ struct static_path *pn;
+ struct static_route_info *si;
+
+ for (rn = route_top(stable); rn; rn = route_next(rn)) {
+ si = static_route_info_from_rnode(rn);
+ if (!si)
+ continue;
+ frr_each(static_path_list, &si->path_list, pn) {
+ frr_each(static_nexthop_list, &pn->nexthop_list, nh) {
+ if (nh->ifindex) {
+ ifp = if_lookup_by_name(nh->ifname,
+ nh->nh_vrf_id);
+ if (ifp)
+ nh->ifindex = ifp->ifindex;
+ else
+ continue;
+ }
+ if (nh->nh_vrf_id == VRF_UNKNOWN)
+ continue;
+ static_install_path(pn);
+ }
+ }
+ }
+}
+
+/*
+ * When a vrf is being enabled by the kernel, go through all the
+ * static routes in the system that use this vrf (both nexthops vrfs
+ * and the routes vrf )
+ *
+ * enable_svrf -> the vrf being enabled
+ */
+void static_fixup_vrf_ids(struct static_vrf *enable_svrf)
+{
+ struct route_table *stable;
+ struct vrf *vrf;
+ afi_t afi;
+ safi_t safi;
+
+ RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
+ struct static_vrf *svrf;
+
+ svrf = vrf->info;
+ /* Install any static routes configured for this VRF. */
+ FOREACH_AFI_SAFI (afi, safi) {
+ stable = svrf->stable[afi][safi];
+ if (!stable)
+ continue;
+
+ static_fixup_vrf(enable_svrf, stable, afi, safi);
+
+ if (enable_svrf == svrf)
+ static_enable_vrf(svrf, stable, afi, safi);
+ }
+ }
+}
+
+/*
+ * Look at the specified stable and if any of the routes in
+ * this table are using the svrf as the nexthop, uninstall
+ * those routes.
+ *
+ * svrf -> the vrf being disabled
+ * stable -> the table we need to look at.
+ * afi -> the afi in question
+ * safi -> the safi in question
+ */
+static void static_cleanup_vrf(struct static_vrf *svrf,
+ struct route_table *stable,
+ afi_t afi, safi_t safi)
+{
+ struct route_node *rn;
+ struct static_nexthop *nh;
+ struct static_path *pn;
+ struct static_route_info *si;
+
+ for (rn = route_top(stable); rn; rn = route_next(rn)) {
+ si = static_route_info_from_rnode(rn);
+ if (!si)
+ continue;
+ frr_each(static_path_list, &si->path_list, pn) {
+ frr_each(static_nexthop_list, &pn->nexthop_list, nh) {
+ if (strcmp(svrf->vrf->name, nh->nh_vrfname)
+ != 0)
+ continue;
+
+ static_uninstall_path(pn);
+ }
+ }
+ }
+}
+
+/*
+ * Look at all static routes in this table and uninstall
+ * them.
+ *
+ * stable -> The table to uninstall from
+ * afi -> The afi in question
+ * safi -> the safi in question
+ */
+static void static_disable_vrf(struct route_table *stable,
+ afi_t afi, safi_t safi)
+{
+ struct route_node *rn;
+ struct static_nexthop *nh;
+ struct static_path *pn;
+ struct static_route_info *si;
+
+ for (rn = route_top(stable); rn; rn = route_next(rn)) {
+ si = static_route_info_from_rnode(rn);
+ if (!si)
+ continue;
+ frr_each(static_path_list, &si->path_list, pn) {
+ frr_each(static_nexthop_list, &pn->nexthop_list, nh) {
+ static_uninstall_path(pn);
+ }
+ }
+ }
+}
+
+/*
+ * When the disable_svrf is shutdown by the kernel, we call
+ * this function and it cleans up all static routes using
+ * this vrf as a nexthop as well as all static routes
+ * in it's stables.
+ *
+ * disable_svrf - The vrf being disabled
+ */
+void static_cleanup_vrf_ids(struct static_vrf *disable_svrf)
+{
+ struct vrf *vrf;
+ afi_t afi;
+ safi_t safi;
+
+ RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
+ struct static_vrf *svrf;
+
+ svrf = vrf->info;
+
+ /* Uninstall any static routes configured for this VRF. */
+ FOREACH_AFI_SAFI (afi, safi) {
+ struct route_table *stable;
+
+ stable = svrf->stable[afi][safi];
+ if (!stable)
+ continue;
+
+ static_cleanup_vrf(disable_svrf, stable, afi, safi);
+
+ if (disable_svrf == svrf)
+ static_disable_vrf(stable, afi, safi);
+ }
+ }
+}
+
+/*
+ * This function enables static routes when an interface it relies
+ * on in a different vrf is coming up.
+ *
+ * stable -> The stable we are looking at.
+ * ifp -> interface coming up
+ * afi -> the afi in question
+ * safi -> the safi in question
+ */
+static void static_fixup_intf_nh(struct route_table *stable,
+ struct interface *ifp,
+ afi_t afi, safi_t safi)
+{
+ struct route_node *rn;
+ struct static_nexthop *nh;
+ struct static_path *pn;
+ struct static_route_info *si;
+
+ for (rn = route_top(stable); rn; rn = route_next(rn)) {
+ si = static_route_info_from_rnode(rn);
+ if (!si)
+ continue;
+ frr_each(static_path_list, &si->path_list, pn) {
+ frr_each(static_nexthop_list, &pn->nexthop_list, nh) {
+ if (nh->nh_vrf_id != ifp->vrf->vrf_id)
+ continue;
+
+ if (nh->ifindex != ifp->ifindex)
+ continue;
+
+ static_install_path(pn);
+ }
+ }
+ }
+}
+
+/*
+ * This function enables static routes that rely on an interface in
+ * a different vrf when that interface comes up.
+ */
+void static_install_intf_nh(struct interface *ifp)
+{
+ struct route_table *stable;
+ struct vrf *vrf;
+ afi_t afi;
+ safi_t safi;
+
+ RB_FOREACH(vrf, vrf_name_head, &vrfs_by_name) {
+ struct static_vrf *svrf = vrf->info;
+
+ /* Not needed if same vrf since happens naturally */
+ if (vrf->vrf_id == ifp->vrf->vrf_id)
+ continue;
+
+ /* Install any static routes configured for this interface. */
+ FOREACH_AFI_SAFI (afi, safi) {
+ stable = svrf->stable[afi][safi];
+ if (!stable)
+ continue;
+
+ static_fixup_intf_nh(stable, ifp, afi, safi);
+ }
+ }
+}
+
+/* called from if_{add,delete}_update, i.e. when ifindex becomes [in]valid */
+void static_ifindex_update(struct interface *ifp, bool up)
+{
+ static_ifindex_update_af(ifp, up, AFI_IP, SAFI_UNICAST);
+ static_ifindex_update_af(ifp, up, AFI_IP, SAFI_MULTICAST);
+ static_ifindex_update_af(ifp, up, AFI_IP6, SAFI_UNICAST);
+ static_ifindex_update_af(ifp, up, AFI_IP6, SAFI_MULTICAST);
+}
+
+struct stable_info *static_get_stable_info(struct route_node *rn)
+{
+ struct route_table *table;
+
+ table = srcdest_rnode_table(rn);
+ return table->info;
+}
+
+void static_get_nh_str(struct static_nexthop *nh, char *nexthop, size_t size)
+{
+ switch (nh->type) {
+ case STATIC_IFNAME:
+ snprintfrr(nexthop, size, "ifindex : %s", nh->ifname);
+ break;
+ case STATIC_IPV4_GATEWAY:
+ snprintfrr(nexthop, size, "ip4 : %pI4", &nh->addr.ipv4);
+ break;
+ case STATIC_IPV4_GATEWAY_IFNAME:
+ snprintfrr(nexthop, size, "ip4-ifindex : %pI4 : %s",
+ &nh->addr.ipv4, nh->ifname);
+ break;
+ case STATIC_BLACKHOLE:
+ snprintfrr(nexthop, size, "blackhole : %d", nh->bh_type);
+ break;
+ case STATIC_IPV6_GATEWAY:
+ snprintfrr(nexthop, size, "ip6 : %pI6", &nh->addr.ipv6);
+ break;
+ case STATIC_IPV6_GATEWAY_IFNAME:
+ snprintfrr(nexthop, size, "ip6-ifindex : %pI6 : %s",
+ &nh->addr.ipv6, nh->ifname);
+ break;
+ };
+}
diff --git a/staticd/static_routes.h b/staticd/static_routes.h
new file mode 100644
index 0000000..548148b
--- /dev/null
+++ b/staticd/static_routes.h
@@ -0,0 +1,273 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * STATICd - static routes header
+ * Copyright (C) 2018 Cumulus Networks, Inc.
+ * Donald Sharp
+ */
+#ifndef __STATIC_ROUTES_H__
+#define __STATIC_ROUTES_H__
+
+#include "lib/bfd.h"
+#include "lib/mpls.h"
+#include "lib/srv6.h"
+#include "table.h"
+#include "memory.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+DECLARE_MGROUP(STATIC);
+
+#include "staticd/static_vrf.h"
+
+/* Static route label information */
+struct static_nh_label {
+ uint8_t num_labels;
+ uint8_t reserved[3];
+ mpls_label_t label[MPLS_MAX_LABELS];
+};
+
+/* Static route seg information */
+struct static_nh_seg {
+ int num_segs;
+ struct in6_addr seg[SRV6_MAX_SIDS];
+};
+
+enum static_blackhole_type {
+ STATIC_BLACKHOLE_DROP = 0,
+ STATIC_BLACKHOLE_NULL,
+ STATIC_BLACKHOLE_REJECT
+};
+
+/*
+ * The order for below macros should be in sync with
+ * yang model typedef nexthop-type
+ */
+enum static_nh_type {
+ STATIC_IFNAME = 1,
+ STATIC_IPV4_GATEWAY,
+ STATIC_IPV4_GATEWAY_IFNAME,
+ STATIC_IPV6_GATEWAY,
+ STATIC_IPV6_GATEWAY_IFNAME,
+ STATIC_BLACKHOLE,
+};
+
+/*
+ * Route Creation gives us:
+ * START -> Initial State, only exit is when we send the route to
+ * zebra for installation
+ * When we send the route to Zebra move to SENT_TO_ZEBRA
+ * SENT_TO_ZEBRA -> A way to notice that we've sent the route to zebra
+ * But have not received a response on it's status yet
+ * After The response from zebra we move to INSTALLED or FAILED
+ * INSTALLED -> Route was accepted
+ * FAILED -> Route was rejected
+ * When we receive notification about a nexthop that a route uses
+ * We move the route back to START and initiate the process again.
+ */
+enum static_install_states {
+ STATIC_START,
+ STATIC_SENT_TO_ZEBRA,
+ STATIC_INSTALLED,
+ STATIC_NOT_INSTALLED,
+};
+
+PREDECL_DLIST(static_path_list);
+PREDECL_DLIST(static_nexthop_list);
+
+/* Static route information */
+struct static_route_info {
+ struct static_vrf *svrf;
+ safi_t safi;
+ /* path list */
+ struct static_path_list_head path_list;
+};
+
+/* Static path information */
+struct static_path {
+ /* Route node back pointer. */
+ struct route_node *rn;
+ /* Linkage for static path lists */
+ struct static_path_list_item list;
+ /* Administrative distance. */
+ uint8_t distance;
+ /* Tag */
+ route_tag_t tag;
+ /* Table-id */
+ uint32_t table_id;
+ /* Nexthop list */
+ struct static_nexthop_list_head nexthop_list;
+};
+
+DECLARE_DLIST(static_path_list, struct static_path, list);
+
+/* Static route information. */
+struct static_nexthop {
+ /* Path back pointer. */
+ struct static_path *pn;
+ /* For linked list. */
+ struct static_nexthop_list_item list;
+
+ /* VRF identifier. */
+ vrf_id_t nh_vrf_id;
+ char nh_vrfname[VRF_NAMSIZ + 1];
+
+ /*
+ * States that we walk the route through
+ * To know where we are.
+ */
+ enum static_install_states state;
+
+ /* Flag for this static route's type. */
+ enum static_nh_type type;
+
+ /*
+ * Nexthop value.
+ */
+ enum static_blackhole_type bh_type;
+ union g_addr addr;
+ ifindex_t ifindex;
+ bool nh_registered;
+ bool nh_valid;
+
+ char ifname[INTERFACE_NAMSIZ + 1];
+
+ /* Label information */
+ struct static_nh_label snh_label;
+
+ /* SRv6 Seg information */
+ struct static_nh_seg snh_seg;
+
+ /*
+ * Whether to pretend the nexthop is directly attached to the specified
+ * link. Only meaningful when both a gateway address and interface name
+ * are specified.
+ */
+ bool onlink;
+
+ /* SR-TE color */
+ uint32_t color;
+
+ /** BFD integration data. */
+ struct bfd_session_params *bsp;
+ /** Back pointer for route node. */
+ struct route_node *rn;
+ /** Path connection status. */
+ bool path_down;
+};
+
+DECLARE_DLIST(static_nexthop_list, struct static_nexthop, list);
+
+
+/*
+ * rib_dest_from_rnode
+ */
+static inline struct static_route_info *
+static_route_info_from_rnode(struct route_node *rn)
+{
+ return (struct static_route_info *)(rn->info);
+}
+
+static inline void static_get_nh_type(enum static_nh_type stype, char *type,
+ size_t size)
+{
+ switch (stype) {
+ case STATIC_IFNAME:
+ strlcpy(type, "ifindex", size);
+ break;
+ case STATIC_IPV4_GATEWAY:
+ strlcpy(type, "ip4", size);
+ break;
+ case STATIC_IPV4_GATEWAY_IFNAME:
+ strlcpy(type, "ip4-ifindex", size);
+ break;
+ case STATIC_BLACKHOLE:
+ strlcpy(type, "blackhole", size);
+ break;
+ case STATIC_IPV6_GATEWAY:
+ strlcpy(type, "ip6", size);
+ break;
+ case STATIC_IPV6_GATEWAY_IFNAME:
+ strlcpy(type, "ip6-ifindex", size);
+ break;
+ };
+}
+
+extern bool mpls_enabled;
+extern uint32_t zebra_ecmp_count;
+
+extern struct zebra_privs_t static_privs;
+
+void static_fixup_vrf_ids(struct static_vrf *svrf);
+
+extern struct static_nexthop *
+static_add_nexthop(struct static_path *pn, enum static_nh_type type,
+ struct ipaddr *ipaddr, const char *ifname,
+ const char *nh_vrf, uint32_t color);
+extern void static_install_nexthop(struct static_nexthop *nh);
+
+extern void static_delete_nexthop(struct static_nexthop *nh);
+
+extern void static_cleanup_vrf_ids(struct static_vrf *disable_svrf);
+
+extern void static_install_intf_nh(struct interface *ifp);
+
+extern void static_ifindex_update(struct interface *ifp, bool up);
+
+extern void static_install_path(struct static_path *pn);
+
+extern struct route_node *static_add_route(afi_t afi, safi_t safi,
+ struct prefix *p,
+ struct prefix_ipv6 *src_p,
+ struct static_vrf *svrf);
+extern void static_del_route(struct route_node *rn);
+
+extern struct static_path *static_add_path(struct route_node *rn,
+ uint32_t table_id, uint8_t distance);
+extern void static_del_path(struct static_path *pn);
+
+extern bool static_add_nexthop_validate(const char *nh_vrf_name,
+ enum static_nh_type type,
+ struct ipaddr *ipaddr);
+extern struct stable_info *static_get_stable_info(struct route_node *rn);
+
+extern void zebra_stable_node_cleanup(struct route_table *table,
+ struct route_node *node);
+
+/*
+ * Max string return via API static_get_nh_str in size_t
+ */
+
+#define NEXTHOP_STR (INET6_ADDRSTRLEN + INTERFACE_NAMSIZ + 25)
+/*
+ * For the given nexthop, returns the string
+ * nexthop : returns the formatted string in nexthop
+ * size : max size of formatted string
+ */
+extern void static_get_nh_str(struct static_nexthop *nh, char *nexthop,
+ size_t size);
+
+/*
+ * BFD integration.
+ */
+extern void static_next_hop_bfd_source(struct static_nexthop *sn,
+ const struct ipaddr *source);
+extern void static_next_hop_bfd_auto_source(struct static_nexthop *sn);
+extern void static_next_hop_bfd_monitor_enable(struct static_nexthop *sn,
+ const struct lyd_node *dnode);
+extern void static_next_hop_bfd_monitor_disable(struct static_nexthop *sn);
+extern void static_next_hop_bfd_profile(struct static_nexthop *sn,
+ const char *name);
+extern void static_next_hop_bfd_multi_hop(struct static_nexthop *sn, bool mhop);
+
+/** Call this function after zebra client initialization. */
+extern void static_bfd_initialize(struct zclient *zc, struct event_loop *tm);
+
+extern void static_bfd_show(struct vty *vty, bool isjson);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/staticd/static_vrf.c b/staticd/static_vrf.c
new file mode 100644
index 0000000..a67dce2
--- /dev/null
+++ b/staticd/static_vrf.c
@@ -0,0 +1,152 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * STATICd - vrf code
+ * Copyright (C) 2018 Cumulus Networks, Inc.
+ * Donald Sharp
+ */
+#include <zebra.h>
+
+#include "vrf.h"
+#include "nexthop.h"
+#include "table.h"
+#include "srcdest_table.h"
+#include "northbound_cli.h"
+
+#include "static_vrf.h"
+#include "static_routes.h"
+#include "static_zebra.h"
+
+DEFINE_MTYPE_STATIC(STATIC, STATIC_RTABLE_INFO, "Static Route Table Info");
+
+static struct static_vrf *static_vrf_alloc(void)
+{
+ struct route_table *table;
+ struct static_vrf *svrf;
+ struct stable_info *info;
+ safi_t safi;
+ afi_t afi;
+
+ svrf = XCALLOC(MTYPE_STATIC_RTABLE_INFO, sizeof(struct static_vrf));
+
+ for (afi = AFI_IP; afi <= AFI_IP6; afi++) {
+ for (safi = SAFI_UNICAST; safi <= SAFI_MULTICAST; safi++) {
+ if (afi == AFI_IP6)
+ table = srcdest_table_init();
+ else
+ table = route_table_init();
+
+ info = XCALLOC(MTYPE_STATIC_RTABLE_INFO,
+ sizeof(struct stable_info));
+ info->svrf = svrf;
+ info->afi = afi;
+ info->safi = safi;
+ route_table_set_info(table, info);
+
+ table->cleanup = zebra_stable_node_cleanup;
+ svrf->stable[afi][safi] = table;
+ }
+ }
+ return svrf;
+}
+
+static int static_vrf_new(struct vrf *vrf)
+{
+ struct static_vrf *svrf;
+
+ svrf = static_vrf_alloc();
+ vrf->info = svrf;
+ svrf->vrf = vrf;
+
+ return 0;
+}
+
+static int static_vrf_enable(struct vrf *vrf)
+{
+ static_zebra_vrf_register(vrf);
+
+ static_fixup_vrf_ids(vrf->info);
+
+ return 0;
+}
+
+static int static_vrf_disable(struct vrf *vrf)
+{
+ static_zebra_vrf_unregister(vrf);
+ return 0;
+}
+
+static int static_vrf_delete(struct vrf *vrf)
+{
+ struct route_table *table;
+ struct static_vrf *svrf;
+ safi_t safi;
+ afi_t afi;
+ void *info;
+
+ svrf = vrf->info;
+ for (afi = AFI_IP; afi <= AFI_IP6; afi++) {
+ for (safi = SAFI_UNICAST; safi <= SAFI_MULTICAST; safi++) {
+ table = svrf->stable[afi][safi];
+ info = route_table_get_info(table);
+ route_table_finish(table);
+ XFREE(MTYPE_STATIC_RTABLE_INFO, info);
+ svrf->stable[afi][safi] = NULL;
+ }
+ }
+ XFREE(MTYPE_STATIC_RTABLE_INFO, svrf);
+ return 0;
+}
+
+/* Lookup the static routing table in a VRF. */
+struct route_table *static_vrf_static_table(afi_t afi, safi_t safi,
+ struct static_vrf *svrf)
+{
+ if (!svrf)
+ return NULL;
+
+ if (afi >= AFI_MAX || safi >= SAFI_MAX)
+ return NULL;
+
+ return svrf->stable[afi][safi];
+}
+
+struct static_vrf *static_vrf_lookup_by_name(const char *name)
+{
+ struct vrf *vrf;
+
+ if (!name)
+ name = VRF_DEFAULT_NAME;
+
+ vrf = vrf_lookup_by_name(name);
+ if (vrf)
+ return ((struct static_vrf *)vrf->info);
+
+ return NULL;
+}
+
+static int static_vrf_config_write(struct vty *vty)
+{
+ struct lyd_node *dnode;
+ int written = 0;
+
+ dnode = yang_dnode_get(running_config->dnode, "/frr-routing:routing");
+ if (dnode) {
+ nb_cli_show_dnode_cmds(vty, dnode, false);
+ written = 1;
+ }
+
+ return written;
+}
+
+void static_vrf_init(void)
+{
+ vrf_init(static_vrf_new, static_vrf_enable, static_vrf_disable,
+ static_vrf_delete);
+
+ vrf_cmd_init(static_vrf_config_write);
+}
+
+void static_vrf_terminate(void)
+{
+ vrf_terminate();
+}
diff --git a/staticd/static_vrf.h b/staticd/static_vrf.h
new file mode 100644
index 0000000..8f55775
--- /dev/null
+++ b/staticd/static_vrf.h
@@ -0,0 +1,40 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * STATICd - vrf header
+ * Copyright (C) 2018 Cumulus Networks, Inc.
+ * Donald Sharp
+ */
+#ifndef __STATIC_VRF_H__
+#define __STATIC_VRF_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct static_vrf {
+ struct vrf *vrf;
+
+ struct route_table *stable[AFI_MAX][SAFI_MAX];
+};
+
+struct stable_info {
+ struct static_vrf *svrf;
+ afi_t afi;
+ safi_t safi;
+};
+
+#define GET_STABLE_VRF_ID(info) info->svrf->vrf->vrf_id
+
+struct static_vrf *static_vrf_lookup_by_name(const char *vrf_name);
+
+void static_vrf_init(void);
+
+struct route_table *static_vrf_static_table(afi_t afi, safi_t safi,
+ struct static_vrf *svrf);
+extern void static_vrf_terminate(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/staticd/static_vty.c b/staticd/static_vty.c
new file mode 100644
index 0000000..e94acba
--- /dev/null
+++ b/staticd/static_vty.c
@@ -0,0 +1,1658 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * STATICd - vty code
+ * Copyright (C) 2018 Cumulus Networks, Inc.
+ * Donald Sharp
+ */
+#include <zebra.h>
+
+#include "command.h"
+#include "vty.h"
+#include "vrf.h"
+#include "prefix.h"
+#include "nexthop.h"
+#include "table.h"
+#include "srcdest_table.h"
+#include "mgmt_be_client.h"
+#include "mpls.h"
+#include "northbound.h"
+#include "libfrr.h"
+#include "routing_nb.h"
+#include "northbound_cli.h"
+
+#include "static_vrf.h"
+#include "static_vty.h"
+#include "static_routes.h"
+#include "static_debug.h"
+#include "staticd/static_vty_clippy.c"
+#include "static_nb.h"
+
+#define STATICD_STR "Static route daemon\n"
+
+/** All possible route parameters available in CLI. */
+struct static_route_args {
+ /** "no" command? */
+ bool delete;
+ /** Is VRF obtained from XPath? */
+ bool xpath_vrf;
+
+ bool onlink;
+ afi_t afi;
+ safi_t safi;
+
+ const char *vrf;
+ const char *nexthop_vrf;
+ const char *prefix;
+ const char *prefix_mask;
+ const char *source;
+ const char *gateway;
+ const char *interface_name;
+ const char *segs;
+ const char *flag;
+ const char *tag;
+ const char *distance;
+ const char *label;
+ const char *table;
+ const char *color;
+
+ bool bfd;
+ bool bfd_multi_hop;
+ const char *bfd_source;
+ const char *bfd_profile;
+
+ const char *input;
+};
+
+static int static_route_nb_run(struct vty *vty, struct static_route_args *args)
+{
+ int ret;
+ struct prefix p, src;
+ struct in_addr mask;
+ enum static_nh_type type;
+ const char *bh_type;
+ char xpath_prefix[XPATH_MAXLEN];
+ char xpath_nexthop[XPATH_MAXLEN];
+ char xpath_mpls[XPATH_MAXLEN];
+ char xpath_label[XPATH_MAXLEN];
+ char xpath_segs[XPATH_MAXLEN];
+ char xpath_seg[XPATH_MAXLEN];
+ char ab_xpath[XPATH_MAXLEN];
+ char buf_prefix[PREFIX_STRLEN];
+ char buf_src_prefix[PREFIX_STRLEN] = {};
+ char buf_nh_type[PREFIX_STRLEN] = {};
+ char buf_tag[PREFIX_STRLEN];
+ uint8_t label_stack_id = 0;
+ uint8_t segs_stack_id = 0;
+ char *orig_label = NULL, *orig_seg = NULL;
+ const char *buf_gate_str;
+ uint8_t distance = ZEBRA_STATIC_DISTANCE_DEFAULT;
+ route_tag_t tag = 0;
+ uint32_t table_id = 0;
+ const struct lyd_node *dnode;
+ const struct lyd_node *vrf_dnode;
+
+ if (args->xpath_vrf) {
+ vrf_dnode = yang_dnode_get(vty->candidate_config->dnode,
+ VTY_CURR_XPATH);
+ if (vrf_dnode == NULL) {
+ vty_out(vty,
+ "%% Failed to get vrf dnode in candidate db\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ args->vrf = yang_dnode_get_string(vrf_dnode, "./name");
+ } else {
+ if (args->vrf == NULL)
+ args->vrf = VRF_DEFAULT_NAME;
+ }
+ if (args->nexthop_vrf == NULL)
+ args->nexthop_vrf = args->vrf;
+
+ if (args->interface_name &&
+ !strcasecmp(args->interface_name, "Null0")) {
+ args->flag = "Null0";
+ args->interface_name = NULL;
+ }
+
+ assert(!!str2prefix(args->prefix, &p));
+
+ switch (args->afi) {
+ case AFI_IP:
+ /* Cisco like mask notation. */
+ if (args->prefix_mask) {
+ assert(inet_pton(AF_INET, args->prefix_mask, &mask) ==
+ 1);
+ p.prefixlen = ip_masklen(mask);
+ }
+ break;
+ case AFI_IP6:
+ /* srcdest routing */
+ if (args->source)
+ assert(!!str2prefix(args->source, &src));
+ break;
+ case AFI_L2VPN:
+ case AFI_UNSPEC:
+ case AFI_MAX:
+ break;
+ }
+
+ /* Apply mask for given prefix. */
+ apply_mask(&p);
+ prefix2str(&p, buf_prefix, sizeof(buf_prefix));
+
+ if (args->bfd && args->gateway == NULL) {
+ vty_out(vty, "%% Route monitoring requires a gateway\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (args->source)
+ prefix2str(&src, buf_src_prefix, sizeof(buf_src_prefix));
+ if (args->gateway)
+ buf_gate_str = args->gateway;
+ else
+ buf_gate_str = "";
+
+ if (args->gateway == NULL && args->interface_name == NULL) {
+ type = STATIC_BLACKHOLE;
+ /* If this is blackhole/reject flagged route, then
+ * specify interface_name with the value of what was really
+ * entered.
+ * interface_name will be validated later in NB functions
+ * to check if we don't create blackhole/reject routes that
+ * match the real interface names.
+ * E.g.: `ip route 10.0.0.1/32 bla` will create a blackhole
+ * route despite the real interface named `bla` exists.
+ */
+ if (args->input)
+ args->interface_name = args->input;
+ } else if (args->gateway && args->interface_name) {
+ if (args->afi == AFI_IP)
+ type = STATIC_IPV4_GATEWAY_IFNAME;
+ else
+ type = STATIC_IPV6_GATEWAY_IFNAME;
+ } else if (args->interface_name)
+ type = STATIC_IFNAME;
+ else {
+ if (args->afi == AFI_IP)
+ type = STATIC_IPV4_GATEWAY;
+ else
+ type = STATIC_IPV6_GATEWAY;
+ }
+
+ /* Administrative distance. */
+ if (args->distance)
+ distance = strtol(args->distance, NULL, 10);
+
+ /* tag */
+ if (args->tag)
+ tag = strtoul(args->tag, NULL, 10);
+
+ /* TableID */
+ if (args->table)
+ table_id = strtol(args->table, NULL, 10);
+
+ static_get_nh_type(type, buf_nh_type, sizeof(buf_nh_type));
+ if (!args->delete) {
+ if (args->source)
+ snprintf(ab_xpath, sizeof(ab_xpath),
+ FRR_DEL_S_ROUTE_SRC_NH_KEY_NO_DISTANCE_XPATH,
+ "frr-staticd:staticd", "staticd", args->vrf,
+ buf_prefix,
+ yang_afi_safi_value2identity(args->afi,
+ args->safi),
+ buf_src_prefix, table_id, buf_nh_type,
+ args->nexthop_vrf, buf_gate_str,
+ args->interface_name);
+ else
+ snprintf(ab_xpath, sizeof(ab_xpath),
+ FRR_DEL_S_ROUTE_NH_KEY_NO_DISTANCE_XPATH,
+ "frr-staticd:staticd", "staticd", args->vrf,
+ buf_prefix,
+ yang_afi_safi_value2identity(args->afi,
+ args->safi),
+ table_id, buf_nh_type, args->nexthop_vrf,
+ buf_gate_str, args->interface_name);
+
+ /*
+ * If there's already the same nexthop but with a different
+ * distance, then remove it for the replacement.
+ */
+ dnode = yang_dnode_get(vty->candidate_config->dnode, ab_xpath);
+ if (dnode) {
+ dnode = yang_get_subtree_with_no_sibling(dnode);
+ assert(dnode);
+ yang_dnode_get_path(dnode, ab_xpath, XPATH_MAXLEN);
+
+ nb_cli_enqueue_change(vty, ab_xpath, NB_OP_DESTROY,
+ NULL);
+ }
+
+ /* route + path procesing */
+ if (args->source)
+ snprintf(xpath_prefix, sizeof(xpath_prefix),
+ FRR_S_ROUTE_SRC_INFO_KEY_XPATH,
+ "frr-staticd:staticd", "staticd", args->vrf,
+ buf_prefix,
+ yang_afi_safi_value2identity(args->afi,
+ args->safi),
+ buf_src_prefix, table_id, distance);
+ else
+ snprintf(xpath_prefix, sizeof(xpath_prefix),
+ FRR_STATIC_ROUTE_INFO_KEY_XPATH,
+ "frr-staticd:staticd", "staticd", args->vrf,
+ buf_prefix,
+ yang_afi_safi_value2identity(args->afi,
+ args->safi),
+ table_id, distance);
+
+ nb_cli_enqueue_change(vty, xpath_prefix, NB_OP_CREATE, NULL);
+
+ /* Tag processing */
+ snprintf(buf_tag, sizeof(buf_tag), "%u", tag);
+ strlcpy(ab_xpath, xpath_prefix, sizeof(ab_xpath));
+ strlcat(ab_xpath, FRR_STATIC_ROUTE_PATH_TAG_XPATH,
+ sizeof(ab_xpath));
+ nb_cli_enqueue_change(vty, ab_xpath, NB_OP_MODIFY, buf_tag);
+
+ /* nexthop processing */
+
+ snprintf(ab_xpath, sizeof(ab_xpath),
+ FRR_STATIC_ROUTE_NH_KEY_XPATH, buf_nh_type,
+ args->nexthop_vrf, buf_gate_str, args->interface_name);
+ strlcpy(xpath_nexthop, xpath_prefix, sizeof(xpath_nexthop));
+ strlcat(xpath_nexthop, ab_xpath, sizeof(xpath_nexthop));
+ nb_cli_enqueue_change(vty, xpath_nexthop, NB_OP_CREATE, NULL);
+
+ if (type == STATIC_BLACKHOLE) {
+ strlcpy(ab_xpath, xpath_nexthop, sizeof(ab_xpath));
+ strlcat(ab_xpath, FRR_STATIC_ROUTE_NH_BH_XPATH,
+ sizeof(ab_xpath));
+
+ /* Route flags */
+ if (args->flag) {
+ switch (args->flag[0]) {
+ case 'r':
+ bh_type = "reject";
+ break;
+ case 'b':
+ bh_type = "unspec";
+ break;
+ case 'N':
+ bh_type = "null";
+ break;
+ default:
+ bh_type = NULL;
+ break;
+ }
+ nb_cli_enqueue_change(vty, ab_xpath,
+ NB_OP_MODIFY, bh_type);
+ } else {
+ nb_cli_enqueue_change(vty, ab_xpath,
+ NB_OP_MODIFY, "null");
+ }
+ }
+ if (type == STATIC_IPV4_GATEWAY_IFNAME
+ || type == STATIC_IPV6_GATEWAY_IFNAME) {
+ strlcpy(ab_xpath, xpath_nexthop, sizeof(ab_xpath));
+ strlcat(ab_xpath, FRR_STATIC_ROUTE_NH_ONLINK_XPATH,
+ sizeof(ab_xpath));
+
+ if (args->onlink)
+ nb_cli_enqueue_change(vty, ab_xpath,
+ NB_OP_MODIFY, "true");
+ else
+ nb_cli_enqueue_change(vty, ab_xpath,
+ NB_OP_MODIFY, "false");
+ }
+ if (type == STATIC_IPV4_GATEWAY ||
+ type == STATIC_IPV6_GATEWAY ||
+ type == STATIC_IPV4_GATEWAY_IFNAME ||
+ type == STATIC_IPV6_GATEWAY_IFNAME) {
+ strlcpy(ab_xpath, xpath_nexthop, sizeof(ab_xpath));
+ strlcat(ab_xpath, FRR_STATIC_ROUTE_NH_COLOR_XPATH,
+ sizeof(ab_xpath));
+ if (args->color)
+ nb_cli_enqueue_change(vty, ab_xpath,
+ NB_OP_MODIFY,
+ args->color);
+ }
+ if (args->label) {
+ /* copy of label string (start) */
+ char *ostr;
+ /* pointer to next segment */
+ char *nump;
+
+ strlcpy(xpath_mpls, xpath_nexthop, sizeof(xpath_mpls));
+ strlcat(xpath_mpls, FRR_STATIC_ROUTE_NH_LABEL_XPATH,
+ sizeof(xpath_mpls));
+
+ nb_cli_enqueue_change(vty, xpath_mpls, NB_OP_DESTROY,
+ NULL);
+
+ orig_label = ostr = XSTRDUP(MTYPE_TMP, args->label);
+ while ((nump = strsep(&ostr, "/")) != NULL) {
+ snprintf(ab_xpath, sizeof(ab_xpath),
+ FRR_STATIC_ROUTE_NHLB_KEY_XPATH,
+ label_stack_id);
+ strlcpy(xpath_label, xpath_mpls,
+ sizeof(xpath_label));
+ strlcat(xpath_label, ab_xpath,
+ sizeof(xpath_label));
+ nb_cli_enqueue_change(vty, xpath_label,
+ NB_OP_MODIFY, nump);
+ label_stack_id++;
+ }
+ } else {
+ strlcpy(xpath_mpls, xpath_nexthop, sizeof(xpath_mpls));
+ strlcat(xpath_mpls, FRR_STATIC_ROUTE_NH_LABEL_XPATH,
+ sizeof(xpath_mpls));
+ nb_cli_enqueue_change(vty, xpath_mpls, NB_OP_DESTROY,
+ NULL);
+ }
+ if (args->segs) {
+ /* copy of seg string (start) */
+ char *ostr;
+ /* pointer to next segment */
+ char *nump;
+
+ strlcpy(xpath_segs, xpath_nexthop, sizeof(xpath_segs));
+ strlcat(xpath_segs, FRR_STATIC_ROUTE_NH_SRV6_SEGS_XPATH,
+ sizeof(xpath_segs));
+
+ nb_cli_enqueue_change(vty, xpath_segs, NB_OP_DESTROY,
+ NULL);
+
+ orig_seg = ostr = XSTRDUP(MTYPE_TMP, args->segs);
+ while ((nump = strsep(&ostr, "/")) != NULL) {
+ snprintf(ab_xpath, sizeof(ab_xpath),
+ FRR_STATIC_ROUTE_NH_SRV6_KEY_SEG_XPATH,
+ segs_stack_id);
+ strlcpy(xpath_seg, xpath_segs,
+ sizeof(xpath_seg));
+ strlcat(xpath_seg, ab_xpath, sizeof(xpath_seg));
+ nb_cli_enqueue_change(vty, xpath_seg,
+ NB_OP_MODIFY, nump);
+ segs_stack_id++;
+ }
+ } else {
+ strlcpy(xpath_segs, xpath_nexthop, sizeof(xpath_segs));
+ strlcat(xpath_segs, FRR_STATIC_ROUTE_NH_SRV6_SEGS_XPATH,
+ sizeof(xpath_segs));
+ nb_cli_enqueue_change(vty, xpath_segs, NB_OP_DESTROY,
+ NULL);
+ }
+ if (args->bfd) {
+ char xpath_bfd[XPATH_MAXLEN];
+
+ if (args->bfd_source) {
+ strlcpy(xpath_bfd, xpath_nexthop,
+ sizeof(xpath_bfd));
+ strlcat(xpath_bfd,
+ "/frr-staticd:bfd-monitoring/source",
+ sizeof(xpath_bfd));
+ nb_cli_enqueue_change(vty, xpath_bfd,
+ NB_OP_MODIFY,
+ args->bfd_source);
+ }
+
+ strlcpy(xpath_bfd, xpath_nexthop, sizeof(xpath_bfd));
+ strlcat(xpath_bfd,
+ "/frr-staticd:bfd-monitoring/multi-hop",
+ sizeof(xpath_bfd));
+ nb_cli_enqueue_change(vty, xpath_bfd, NB_OP_MODIFY,
+ args->bfd_multi_hop ? "true"
+ : "false");
+
+ if (args->bfd_profile) {
+ strlcpy(xpath_bfd, xpath_nexthop,
+ sizeof(xpath_bfd));
+ strlcat(xpath_bfd,
+ "/frr-staticd:bfd-monitoring/profile",
+ sizeof(xpath_bfd));
+ nb_cli_enqueue_change(vty, xpath_bfd,
+ NB_OP_MODIFY,
+ args->bfd_profile);
+ }
+ }
+
+ ret = nb_cli_apply_changes(vty, "%s", xpath_prefix);
+
+ if (orig_label)
+ XFREE(MTYPE_TMP, orig_label);
+ if (orig_seg)
+ XFREE(MTYPE_TMP, orig_seg);
+ } else {
+ if (args->source) {
+ if (args->distance)
+ snprintf(ab_xpath, sizeof(ab_xpath),
+ FRR_DEL_S_ROUTE_SRC_NH_KEY_XPATH,
+ "frr-staticd:staticd", "staticd",
+ args->vrf, buf_prefix,
+ yang_afi_safi_value2identity(
+ args->afi, args->safi),
+ buf_src_prefix, table_id, distance,
+ buf_nh_type, args->nexthop_vrf,
+ buf_gate_str, args->interface_name);
+ else
+ snprintf(
+ ab_xpath, sizeof(ab_xpath),
+ FRR_DEL_S_ROUTE_SRC_NH_KEY_NO_DISTANCE_XPATH,
+ "frr-staticd:staticd", "staticd",
+ args->vrf, buf_prefix,
+ yang_afi_safi_value2identity(
+ args->afi, args->safi),
+ buf_src_prefix, table_id, buf_nh_type,
+ args->nexthop_vrf, buf_gate_str,
+ args->interface_name);
+ } else {
+ if (args->distance)
+ snprintf(ab_xpath, sizeof(ab_xpath),
+ FRR_DEL_S_ROUTE_NH_KEY_XPATH,
+ "frr-staticd:staticd", "staticd",
+ args->vrf, buf_prefix,
+ yang_afi_safi_value2identity(
+ args->afi, args->safi),
+ table_id, distance, buf_nh_type,
+ args->nexthop_vrf, buf_gate_str,
+ args->interface_name);
+ else
+ snprintf(
+ ab_xpath, sizeof(ab_xpath),
+ FRR_DEL_S_ROUTE_NH_KEY_NO_DISTANCE_XPATH,
+ "frr-staticd:staticd", "staticd",
+ args->vrf, buf_prefix,
+ yang_afi_safi_value2identity(
+ args->afi, args->safi),
+ table_id, buf_nh_type,
+ args->nexthop_vrf, buf_gate_str,
+ args->interface_name);
+ }
+
+ dnode = yang_dnode_get(vty->candidate_config->dnode, ab_xpath);
+ if (!dnode) {
+ vty_out(vty,
+ "%% Refusing to remove a non-existent route\n");
+ return CMD_SUCCESS;
+ }
+
+ dnode = yang_get_subtree_with_no_sibling(dnode);
+ assert(dnode);
+ yang_dnode_get_path(dnode, ab_xpath, XPATH_MAXLEN);
+
+ nb_cli_enqueue_change(vty, ab_xpath, NB_OP_DESTROY, NULL);
+ ret = nb_cli_apply_changes(vty, "%s", ab_xpath);
+ }
+
+ return ret;
+}
+
+/* Static unicast routes for multicast RPF lookup. */
+DEFPY_YANG (ip_mroute_dist,
+ ip_mroute_dist_cmd,
+ "[no] ip mroute A.B.C.D/M$prefix <A.B.C.D$gate|INTERFACE$ifname> [{"
+ "(1-255)$distance"
+ "|bfd$bfd [{multi-hop$bfd_multi_hop|source A.B.C.D$bfd_source|profile BFDPROF$bfd_profile}]"
+ "}]",
+ NO_STR
+ IP_STR
+ "Configure static unicast route into MRIB for multicast RPF lookup\n"
+ "IP destination prefix (e.g. 10.0.0.0/8)\n"
+ "Nexthop address\n"
+ "Nexthop interface name\n"
+ "Distance\n"
+ BFD_INTEGRATION_STR
+ BFD_INTEGRATION_MULTI_HOP_STR
+ BFD_INTEGRATION_SOURCE_STR
+ BFD_INTEGRATION_SOURCEV4_STR
+ BFD_PROFILE_STR
+ BFD_PROFILE_NAME_STR)
+{
+ struct static_route_args args = {
+ .delete = !!no,
+ .afi = AFI_IP,
+ .safi = SAFI_MULTICAST,
+ .prefix = prefix_str,
+ .gateway = gate_str,
+ .interface_name = ifname,
+ .distance = distance_str,
+ .bfd = !!bfd,
+ .bfd_multi_hop = !!bfd_multi_hop,
+ .bfd_source = bfd_source_str,
+ .bfd_profile = bfd_profile,
+ };
+
+ return static_route_nb_run(vty, &args);
+}
+
+/* Static route configuration. */
+DEFPY_YANG(ip_route_blackhole,
+ ip_route_blackhole_cmd,
+ "[no] ip route\
+ <A.B.C.D/M$prefix|A.B.C.D$prefix A.B.C.D$mask> \
+ <reject|blackhole>$flag \
+ [{ \
+ tag (1-4294967295) \
+ |(1-255)$distance \
+ |vrf NAME \
+ |label WORD \
+ |table (1-4294967295) \
+ }]",
+ NO_STR IP_STR
+ "Establish static routes\n"
+ "IP destination prefix (e.g. 10.0.0.0/8)\n"
+ "IP destination prefix\n"
+ "IP destination prefix mask\n"
+ "Emit an ICMP unreachable when matched\n"
+ "Silently discard pkts when matched\n"
+ "Set tag for this route\n"
+ "Tag value\n"
+ "Distance value for this route\n"
+ VRF_CMD_HELP_STR
+ MPLS_LABEL_HELPSTR
+ "Table to configure\n"
+ "The table number to configure\n")
+{
+ int idx_flag = 0;
+
+ struct static_route_args args = {
+ .delete = !!no,
+ .afi = AFI_IP,
+ .safi = SAFI_UNICAST,
+ .prefix = prefix,
+ .prefix_mask = mask_str,
+ .flag = flag,
+ .tag = tag_str,
+ .distance = distance_str,
+ .label = label,
+ .table = table_str,
+ .vrf = vrf,
+ };
+
+ if (flag && argv_find(argv, argc, flag, &idx_flag))
+ args.input = argv[idx_flag]->arg;
+
+ return static_route_nb_run(vty, &args);
+}
+
+DEFPY_YANG(ip_route_blackhole_vrf,
+ ip_route_blackhole_vrf_cmd,
+ "[no] ip route\
+ <A.B.C.D/M$prefix|A.B.C.D$prefix A.B.C.D$mask> \
+ <reject|blackhole>$flag \
+ [{ \
+ tag (1-4294967295) \
+ |(1-255)$distance \
+ |label WORD \
+ |table (1-4294967295) \
+ }]",
+ NO_STR IP_STR
+ "Establish static routes\n"
+ "IP destination prefix (e.g. 10.0.0.0/8)\n"
+ "IP destination prefix\n"
+ "IP destination prefix mask\n"
+ "Emit an ICMP unreachable when matched\n"
+ "Silently discard pkts when matched\n"
+ "Set tag for this route\n"
+ "Tag value\n"
+ "Distance value for this route\n"
+ MPLS_LABEL_HELPSTR
+ "Table to configure\n"
+ "The table number to configure\n")
+{
+ int idx_flag = 0;
+
+ struct static_route_args args = {
+ .delete = !!no,
+ .afi = AFI_IP,
+ .safi = SAFI_UNICAST,
+ .prefix = prefix,
+ .prefix_mask = mask_str,
+ .flag = flag,
+ .tag = tag_str,
+ .distance = distance_str,
+ .label = label,
+ .table = table_str,
+ .xpath_vrf = true,
+ };
+
+ /*
+ * Coverity is complaining that prefix could
+ * be dereferenced, but we know that prefix will
+ * valid. Add an assert to make it happy
+ */
+ assert(args.prefix);
+
+ if (flag && argv_find(argv, argc, flag, &idx_flag))
+ args.input = argv[idx_flag]->arg;
+
+ return static_route_nb_run(vty, &args);
+}
+
+DEFPY_YANG(ip_route_address_interface,
+ ip_route_address_interface_cmd,
+ "[no] ip route\
+ <A.B.C.D/M$prefix|A.B.C.D$prefix A.B.C.D$mask> \
+ A.B.C.D$gate \
+ <INTERFACE|Null0>$ifname \
+ [{ \
+ tag (1-4294967295) \
+ |(1-255)$distance \
+ |vrf NAME \
+ |label WORD \
+ |table (1-4294967295) \
+ |nexthop-vrf NAME \
+ |onlink$onlink \
+ |color (1-4294967295) \
+ |bfd$bfd [{multi-hop$bfd_multi_hop|source A.B.C.D$bfd_source|profile BFDPROF$bfd_profile}] \
+ }]",
+ NO_STR IP_STR
+ "Establish static routes\n"
+ "IP destination prefix (e.g. 10.0.0.0/8)\n"
+ "IP destination prefix\n"
+ "IP destination prefix mask\n"
+ "IP gateway address\n"
+ "IP gateway interface name\n"
+ "Null interface\n"
+ "Set tag for this route\n"
+ "Tag value\n"
+ "Distance value for this route\n"
+ VRF_CMD_HELP_STR
+ MPLS_LABEL_HELPSTR
+ "Table to configure\n"
+ "The table number to configure\n"
+ VRF_CMD_HELP_STR
+ "Treat the nexthop as directly attached to the interface\n"
+ "SR-TE color\n"
+ "The SR-TE color to configure\n"
+ BFD_INTEGRATION_STR
+ BFD_INTEGRATION_MULTI_HOP_STR
+ BFD_INTEGRATION_SOURCE_STR
+ BFD_INTEGRATION_SOURCEV4_STR
+ BFD_PROFILE_STR
+ BFD_PROFILE_NAME_STR)
+{
+ struct static_route_args args = {
+ .delete = !!no,
+ .afi = AFI_IP,
+ .safi = SAFI_UNICAST,
+ .prefix = prefix,
+ .prefix_mask = mask_str,
+ .gateway = gate_str,
+ .interface_name = ifname,
+ .tag = tag_str,
+ .distance = distance_str,
+ .label = label,
+ .table = table_str,
+ .color = color_str,
+ .onlink = !!onlink,
+ .vrf = vrf,
+ .nexthop_vrf = nexthop_vrf,
+ .bfd = !!bfd,
+ .bfd_multi_hop = !!bfd_multi_hop,
+ .bfd_source = bfd_source_str,
+ .bfd_profile = bfd_profile,
+ };
+
+ return static_route_nb_run(vty, &args);
+}
+
+DEFPY_YANG(ip_route_address_interface_vrf,
+ ip_route_address_interface_vrf_cmd,
+ "[no] ip route\
+ <A.B.C.D/M$prefix|A.B.C.D$prefix A.B.C.D$mask> \
+ A.B.C.D$gate \
+ <INTERFACE|Null0>$ifname \
+ [{ \
+ tag (1-4294967295) \
+ |(1-255)$distance \
+ |label WORD \
+ |table (1-4294967295) \
+ |nexthop-vrf NAME \
+ |onlink$onlink \
+ |color (1-4294967295) \
+ |bfd$bfd [{multi-hop$bfd_multi_hop|source A.B.C.D$bfd_source|profile BFDPROF$bfd_profile}] \
+ }]",
+ NO_STR IP_STR
+ "Establish static routes\n"
+ "IP destination prefix (e.g. 10.0.0.0/8)\n"
+ "IP destination prefix\n"
+ "IP destination prefix mask\n"
+ "IP gateway address\n"
+ "IP gateway interface name\n"
+ "Null interface\n"
+ "Set tag for this route\n"
+ "Tag value\n"
+ "Distance value for this route\n"
+ MPLS_LABEL_HELPSTR
+ "Table to configure\n"
+ "The table number to configure\n"
+ VRF_CMD_HELP_STR
+ "Treat the nexthop as directly attached to the interface\n"
+ "SR-TE color\n"
+ "The SR-TE color to configure\n"
+ BFD_INTEGRATION_STR
+ BFD_INTEGRATION_MULTI_HOP_STR
+ BFD_INTEGRATION_SOURCE_STR
+ BFD_INTEGRATION_SOURCEV4_STR
+ BFD_PROFILE_STR
+ BFD_PROFILE_NAME_STR)
+{
+ struct static_route_args args = {
+ .delete = !!no,
+ .afi = AFI_IP,
+ .safi = SAFI_UNICAST,
+ .prefix = prefix,
+ .prefix_mask = mask_str,
+ .gateway = gate_str,
+ .interface_name = ifname,
+ .tag = tag_str,
+ .distance = distance_str,
+ .label = label,
+ .table = table_str,
+ .color = color_str,
+ .onlink = !!onlink,
+ .xpath_vrf = true,
+ .nexthop_vrf = nexthop_vrf,
+ .bfd = !!bfd,
+ .bfd_multi_hop = !!bfd_multi_hop,
+ .bfd_source = bfd_source_str,
+ .bfd_profile = bfd_profile,
+ };
+
+ return static_route_nb_run(vty, &args);
+}
+
+DEFPY_YANG(ip_route,
+ ip_route_cmd,
+ "[no] ip route\
+ <A.B.C.D/M$prefix|A.B.C.D$prefix A.B.C.D$mask> \
+ <A.B.C.D$gate|<INTERFACE|Null0>$ifname> \
+ [{ \
+ tag (1-4294967295) \
+ |(1-255)$distance \
+ |vrf NAME \
+ |label WORD \
+ |table (1-4294967295) \
+ |nexthop-vrf NAME \
+ |color (1-4294967295) \
+ |bfd$bfd [{multi-hop$bfd_multi_hop|source A.B.C.D$bfd_source|profile BFDPROF$bfd_profile}] \
+ }]",
+ NO_STR IP_STR
+ "Establish static routes\n"
+ "IP destination prefix (e.g. 10.0.0.0/8)\n"
+ "IP destination prefix\n"
+ "IP destination prefix mask\n"
+ "IP gateway address\n"
+ "IP gateway interface name\n"
+ "Null interface\n"
+ "Set tag for this route\n"
+ "Tag value\n"
+ "Distance value for this route\n"
+ VRF_CMD_HELP_STR
+ MPLS_LABEL_HELPSTR
+ "Table to configure\n"
+ "The table number to configure\n"
+ VRF_CMD_HELP_STR
+ "SR-TE color\n"
+ "The SR-TE color to configure\n"
+ BFD_INTEGRATION_STR
+ BFD_INTEGRATION_MULTI_HOP_STR
+ BFD_INTEGRATION_SOURCE_STR
+ BFD_INTEGRATION_SOURCEV4_STR
+ BFD_PROFILE_STR
+ BFD_PROFILE_NAME_STR)
+{
+ struct static_route_args args = {
+ .delete = !!no,
+ .afi = AFI_IP,
+ .safi = SAFI_UNICAST,
+ .prefix = prefix,
+ .prefix_mask = mask_str,
+ .gateway = gate_str,
+ .interface_name = ifname,
+ .tag = tag_str,
+ .distance = distance_str,
+ .label = label,
+ .table = table_str,
+ .color = color_str,
+ .vrf = vrf,
+ .nexthop_vrf = nexthop_vrf,
+ .bfd = !!bfd,
+ .bfd_multi_hop = !!bfd_multi_hop,
+ .bfd_source = bfd_source_str,
+ .bfd_profile = bfd_profile,
+ };
+
+ return static_route_nb_run(vty, &args);
+}
+
+DEFPY_YANG(ip_route_vrf,
+ ip_route_vrf_cmd,
+ "[no] ip route\
+ <A.B.C.D/M$prefix|A.B.C.D$prefix A.B.C.D$mask> \
+ <A.B.C.D$gate|<INTERFACE|Null0>$ifname> \
+ [{ \
+ tag (1-4294967295) \
+ |(1-255)$distance \
+ |label WORD \
+ |table (1-4294967295) \
+ |nexthop-vrf NAME \
+ |color (1-4294967295) \
+ |bfd$bfd [{multi-hop$bfd_multi_hop|source A.B.C.D$bfd_source|profile BFDPROF$bfd_profile}] \
+ }]",
+ NO_STR IP_STR
+ "Establish static routes\n"
+ "IP destination prefix (e.g. 10.0.0.0/8)\n"
+ "IP destination prefix\n"
+ "IP destination prefix mask\n"
+ "IP gateway address\n"
+ "IP gateway interface name\n"
+ "Null interface\n"
+ "Set tag for this route\n"
+ "Tag value\n"
+ "Distance value for this route\n"
+ MPLS_LABEL_HELPSTR
+ "Table to configure\n"
+ "The table number to configure\n"
+ VRF_CMD_HELP_STR
+ "SR-TE color\n"
+ "The SR-TE color to configure\n"
+ BFD_INTEGRATION_STR
+ BFD_INTEGRATION_MULTI_HOP_STR
+ BFD_INTEGRATION_SOURCE_STR
+ BFD_INTEGRATION_SOURCEV4_STR
+ BFD_PROFILE_STR
+ BFD_PROFILE_NAME_STR)
+{
+ struct static_route_args args = {
+ .delete = !!no,
+ .afi = AFI_IP,
+ .safi = SAFI_UNICAST,
+ .prefix = prefix,
+ .prefix_mask = mask_str,
+ .gateway = gate_str,
+ .interface_name = ifname,
+ .tag = tag_str,
+ .distance = distance_str,
+ .label = label,
+ .table = table_str,
+ .color = color_str,
+ .xpath_vrf = true,
+ .nexthop_vrf = nexthop_vrf,
+ .bfd = !!bfd,
+ .bfd_multi_hop = !!bfd_multi_hop,
+ .bfd_source = bfd_source_str,
+ .bfd_profile = bfd_profile,
+ };
+
+ return static_route_nb_run(vty, &args);
+}
+
+DEFPY_YANG(ipv6_route_blackhole,
+ ipv6_route_blackhole_cmd,
+ "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \
+ <reject|blackhole>$flag \
+ [{ \
+ tag (1-4294967295) \
+ |(1-255)$distance \
+ |vrf NAME \
+ |label WORD \
+ |table (1-4294967295) \
+ }]",
+ NO_STR
+ IPV6_STR
+ "Establish static routes\n"
+ "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+ "IPv6 source-dest route\n"
+ "IPv6 source prefix\n"
+ "Emit an ICMP unreachable when matched\n"
+ "Silently discard pkts when matched\n"
+ "Set tag for this route\n"
+ "Tag value\n"
+ "Distance value for this prefix\n"
+ VRF_CMD_HELP_STR
+ MPLS_LABEL_HELPSTR
+ "Table to configure\n"
+ "The table number to configure\n")
+{
+ int idx_flag = 0;
+
+ struct static_route_args args = {
+ .delete = !!no,
+ .afi = AFI_IP6,
+ .safi = SAFI_UNICAST,
+ .prefix = prefix_str,
+ .source = from_str,
+ .flag = flag,
+ .tag = tag_str,
+ .distance = distance_str,
+ .label = label,
+ .table = table_str,
+ .vrf = vrf,
+ };
+
+ if (flag && argv_find(argv, argc, flag, &idx_flag))
+ args.input = argv[idx_flag]->arg;
+
+ return static_route_nb_run(vty, &args);
+}
+
+DEFPY_YANG(ipv6_route_blackhole_vrf,
+ ipv6_route_blackhole_vrf_cmd,
+ "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \
+ <reject|blackhole>$flag \
+ [{ \
+ tag (1-4294967295) \
+ |(1-255)$distance \
+ |label WORD \
+ |table (1-4294967295) \
+ }]",
+ NO_STR
+ IPV6_STR
+ "Establish static routes\n"
+ "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+ "IPv6 source-dest route\n"
+ "IPv6 source prefix\n"
+ "Emit an ICMP unreachable when matched\n"
+ "Silently discard pkts when matched\n"
+ "Set tag for this route\n"
+ "Tag value\n"
+ "Distance value for this prefix\n"
+ MPLS_LABEL_HELPSTR
+ "Table to configure\n"
+ "The table number to configure\n")
+{
+ int idx_flag = 0;
+
+ struct static_route_args args = {
+ .delete = !!no,
+ .afi = AFI_IP6,
+ .safi = SAFI_UNICAST,
+ .prefix = prefix_str,
+ .source = from_str,
+ .flag = flag,
+ .tag = tag_str,
+ .distance = distance_str,
+ .label = label,
+ .table = table_str,
+ .xpath_vrf = true,
+ };
+
+ /*
+ * Coverity is complaining that prefix could
+ * be dereferenced, but we know that prefix will
+ * valid. Add an assert to make it happy
+ */
+ assert(args.prefix);
+
+ if (flag && argv_find(argv, argc, flag, &idx_flag))
+ args.input = argv[idx_flag]->arg;
+
+ return static_route_nb_run(vty, &args);
+}
+
+DEFPY_YANG(ipv6_route_address_interface, ipv6_route_address_interface_cmd,
+ "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \
+ X:X::X:X$gate \
+ <INTERFACE|Null0>$ifname \
+ [{ \
+ tag (1-4294967295) \
+ |(1-255)$distance \
+ |vrf NAME \
+ |label WORD \
+ |table (1-4294967295) \
+ |nexthop-vrf NAME \
+ |onlink$onlink \
+ |color (1-4294967295) \
+ |bfd$bfd [{multi-hop$bfd_multi_hop|source X:X::X:X$bfd_source|profile BFDPROF$bfd_profile}] \
+ |segments WORD \
+ }]",
+ NO_STR IPV6_STR
+ "Establish static routes\n"
+ "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+ "IPv6 source-dest route\n"
+ "IPv6 source prefix\n"
+ "IPv6 gateway address\n"
+ "IPv6 gateway interface name\n"
+ "Null interface\n"
+ "Set tag for this route\n"
+ "Tag value\n"
+ "Distance value for this prefix\n" VRF_CMD_HELP_STR MPLS_LABEL_HELPSTR
+ "Table to configure\n"
+ "The table number to configure\n" VRF_CMD_HELP_STR
+ "Treat the nexthop as directly attached to the interface\n"
+ "SR-TE color\n"
+ "The SR-TE color to configure\n" BFD_INTEGRATION_STR
+ BFD_INTEGRATION_MULTI_HOP_STR BFD_INTEGRATION_SOURCE_STR
+ BFD_INTEGRATION_SOURCEV4_STR BFD_PROFILE_STR
+ BFD_PROFILE_NAME_STR "Value of segs\n"
+ "Segs (SIDs)\n")
+{
+ struct static_route_args args = {
+ .delete = !!no,
+ .afi = AFI_IP6,
+ .safi = SAFI_UNICAST,
+ .prefix = prefix_str,
+ .source = from_str,
+ .gateway = gate_str,
+ .interface_name = ifname,
+ .tag = tag_str,
+ .distance = distance_str,
+ .label = label,
+ .table = table_str,
+ .color = color_str,
+ .onlink = !!onlink,
+ .vrf = vrf,
+ .nexthop_vrf = nexthop_vrf,
+ .bfd = !!bfd,
+ .bfd_multi_hop = !!bfd_multi_hop,
+ .bfd_source = bfd_source_str,
+ .bfd_profile = bfd_profile,
+ .segs = segments,
+ };
+
+ return static_route_nb_run(vty, &args);
+}
+
+DEFPY_YANG(ipv6_route_address_interface_vrf,
+ ipv6_route_address_interface_vrf_cmd,
+ "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \
+ X:X::X:X$gate \
+ <INTERFACE|Null0>$ifname \
+ [{ \
+ tag (1-4294967295) \
+ |(1-255)$distance \
+ |label WORD \
+ |table (1-4294967295) \
+ |nexthop-vrf NAME \
+ |onlink$onlink \
+ |color (1-4294967295) \
+ |bfd$bfd [{multi-hop$bfd_multi_hop|source X:X::X:X$bfd_source|profile BFDPROF$bfd_profile}] \
+ |segments WORD \
+ }]",
+ NO_STR IPV6_STR
+ "Establish static routes\n"
+ "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+ "IPv6 source-dest route\n"
+ "IPv6 source prefix\n"
+ "IPv6 gateway address\n"
+ "IPv6 gateway interface name\n"
+ "Null interface\n"
+ "Set tag for this route\n"
+ "Tag value\n"
+ "Distance value for this prefix\n" MPLS_LABEL_HELPSTR
+ "Table to configure\n"
+ "The table number to configure\n" VRF_CMD_HELP_STR
+ "Treat the nexthop as directly attached to the interface\n"
+ "SR-TE color\n"
+ "The SR-TE color to configure\n" BFD_INTEGRATION_STR
+ BFD_INTEGRATION_MULTI_HOP_STR BFD_INTEGRATION_SOURCE_STR
+ BFD_INTEGRATION_SOURCEV4_STR BFD_PROFILE_STR
+ BFD_PROFILE_NAME_STR "Value of segs\n"
+ "Segs (SIDs)\n")
+{
+ struct static_route_args args = {
+ .delete = !!no,
+ .afi = AFI_IP6,
+ .safi = SAFI_UNICAST,
+ .prefix = prefix_str,
+ .source = from_str,
+ .gateway = gate_str,
+ .interface_name = ifname,
+ .tag = tag_str,
+ .distance = distance_str,
+ .label = label,
+ .table = table_str,
+ .color = color_str,
+ .onlink = !!onlink,
+ .xpath_vrf = true,
+ .nexthop_vrf = nexthop_vrf,
+ .bfd = !!bfd,
+ .bfd_multi_hop = !!bfd_multi_hop,
+ .bfd_source = bfd_source_str,
+ .bfd_profile = bfd_profile,
+ .segs = segments,
+ };
+
+ return static_route_nb_run(vty, &args);
+}
+
+DEFPY_YANG(ipv6_route, ipv6_route_cmd,
+ "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \
+ <X:X::X:X$gate|<INTERFACE|Null0>$ifname> \
+ [{ \
+ tag (1-4294967295) \
+ |(1-255)$distance \
+ |vrf NAME \
+ |label WORD \
+ |table (1-4294967295) \
+ |nexthop-vrf NAME \
+ |color (1-4294967295) \
+ |bfd$bfd [{multi-hop$bfd_multi_hop|source X:X::X:X$bfd_source|profile BFDPROF$bfd_profile}] \
+ |segments WORD \
+ }]",
+ NO_STR IPV6_STR
+ "Establish static routes\n"
+ "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+ "IPv6 source-dest route\n"
+ "IPv6 source prefix\n"
+ "IPv6 gateway address\n"
+ "IPv6 gateway interface name\n"
+ "Null interface\n"
+ "Set tag for this route\n"
+ "Tag value\n"
+ "Distance value for this prefix\n" VRF_CMD_HELP_STR MPLS_LABEL_HELPSTR
+ "Table to configure\n"
+ "The table number to configure\n" VRF_CMD_HELP_STR "SR-TE color\n"
+ "The SR-TE color to configure\n" BFD_INTEGRATION_STR
+ BFD_INTEGRATION_MULTI_HOP_STR BFD_INTEGRATION_SOURCE_STR
+ BFD_INTEGRATION_SOURCEV4_STR BFD_PROFILE_STR
+ BFD_PROFILE_NAME_STR "Value of segs\n"
+ "Segs (SIDs)\n")
+{
+ struct static_route_args args = {
+ .delete = !!no,
+ .afi = AFI_IP6,
+ .safi = SAFI_UNICAST,
+ .prefix = prefix_str,
+ .source = from_str,
+ .gateway = gate_str,
+ .interface_name = ifname,
+ .tag = tag_str,
+ .distance = distance_str,
+ .label = label,
+ .table = table_str,
+ .color = color_str,
+ .vrf = vrf,
+ .nexthop_vrf = nexthop_vrf,
+ .bfd = !!bfd,
+ .bfd_multi_hop = !!bfd_multi_hop,
+ .bfd_source = bfd_source_str,
+ .bfd_profile = bfd_profile,
+ .segs = segments,
+
+ };
+
+ return static_route_nb_run(vty, &args);
+}
+
+DEFPY_YANG(ipv6_route_vrf, ipv6_route_vrf_cmd,
+ "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \
+ <X:X::X:X$gate|<INTERFACE|Null0>$ifname> \
+ [{ \
+ tag (1-4294967295) \
+ |(1-255)$distance \
+ |label WORD \
+ |table (1-4294967295) \
+ |nexthop-vrf NAME \
+ |color (1-4294967295) \
+ |bfd$bfd [{multi-hop$bfd_multi_hop|source X:X::X:X$bfd_source|profile BFDPROF$bfd_profile}] \
+ |segments WORD \
+ }]",
+ NO_STR IPV6_STR
+ "Establish static routes\n"
+ "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+ "IPv6 source-dest route\n"
+ "IPv6 source prefix\n"
+ "IPv6 gateway address\n"
+ "IPv6 gateway interface name\n"
+ "Null interface\n"
+ "Set tag for this route\n"
+ "Tag value\n"
+ "Distance value for this prefix\n" MPLS_LABEL_HELPSTR
+ "Table to configure\n"
+ "The table number to configure\n" VRF_CMD_HELP_STR "SR-TE color\n"
+ "The SR-TE color to configure\n" BFD_INTEGRATION_STR
+ BFD_INTEGRATION_MULTI_HOP_STR BFD_INTEGRATION_SOURCE_STR
+ BFD_INTEGRATION_SOURCEV4_STR BFD_PROFILE_STR
+ BFD_PROFILE_NAME_STR "Value of segs\n"
+ "Segs (SIDs)\n")
+{
+ struct static_route_args args = {
+ .delete = !!no,
+ .afi = AFI_IP6,
+ .safi = SAFI_UNICAST,
+ .prefix = prefix_str,
+ .source = from_str,
+ .gateway = gate_str,
+ .interface_name = ifname,
+ .tag = tag_str,
+ .distance = distance_str,
+ .label = label,
+ .table = table_str,
+ .color = color_str,
+ .xpath_vrf = true,
+ .nexthop_vrf = nexthop_vrf,
+ .bfd = !!bfd,
+ .bfd_multi_hop = !!bfd_multi_hop,
+ .bfd_source = bfd_source_str,
+ .bfd_profile = bfd_profile,
+ .segs = segments,
+ };
+
+ return static_route_nb_run(vty, &args);
+}
+
+void static_cli_show(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ const char *vrf;
+
+ vrf = yang_dnode_get_string(dnode, "../vrf");
+ if (strcmp(vrf, VRF_DEFAULT_NAME))
+ vty_out(vty, "vrf %s\n", vrf);
+}
+
+void static_cli_show_end(struct vty *vty, const struct lyd_node *dnode)
+{
+ const char *vrf;
+
+ vrf = yang_dnode_get_string(dnode, "../vrf");
+ if (strcmp(vrf, VRF_DEFAULT_NAME))
+ vty_out(vty, "exit-vrf\n");
+}
+
+struct mpls_label_iter {
+ struct vty *vty;
+ bool first;
+};
+
+static int mpls_label_iter_cb(const struct lyd_node *dnode, void *arg)
+{
+ struct mpls_label_iter *iter = arg;
+
+ if (yang_dnode_exists(dnode, "./label")) {
+ if (iter->first)
+ vty_out(iter->vty, " label %s",
+ yang_dnode_get_string(dnode, "./label"));
+ else
+ vty_out(iter->vty, "/%s",
+ yang_dnode_get_string(dnode, "./label"));
+ iter->first = false;
+ }
+
+ return YANG_ITER_CONTINUE;
+}
+
+struct srv6_seg_iter {
+ struct vty *vty;
+ bool first;
+};
+
+static int srv6_seg_iter_cb(const struct lyd_node *dnode, void *arg)
+{
+ struct srv6_seg_iter *iter = arg;
+ char buffer[INET6_ADDRSTRLEN];
+ struct in6_addr cli_seg;
+
+ if (yang_dnode_exists(dnode, "./seg")) {
+ if (iter->first) {
+ yang_dnode_get_ipv6(&cli_seg, dnode, "./seg");
+ if (inet_ntop(AF_INET6, &cli_seg, buffer,
+ INET6_ADDRSTRLEN) == NULL) {
+ return 1;
+ }
+ vty_out(iter->vty, " segments %s", buffer);
+ } else {
+ yang_dnode_get_ipv6(&cli_seg, dnode, "./seg");
+ if (inet_ntop(AF_INET6, &cli_seg, buffer,
+ INET6_ADDRSTRLEN) == NULL) {
+ return 1;
+ }
+ vty_out(iter->vty, "/%s", buffer);
+ }
+ iter->first = false;
+ }
+
+ return YANG_ITER_CONTINUE;
+}
+
+static void nexthop_cli_show(struct vty *vty, const struct lyd_node *route,
+ const struct lyd_node *src,
+ const struct lyd_node *path,
+ const struct lyd_node *nexthop, bool show_defaults)
+{
+ const char *vrf;
+ const char *afi_safi;
+ afi_t afi;
+ safi_t safi;
+ enum static_nh_type nh_type;
+ enum static_blackhole_type bh_type;
+ uint32_t tag;
+ uint8_t distance;
+ struct mpls_label_iter iter;
+ struct srv6_seg_iter seg_iter;
+ const char *nexthop_vrf;
+ uint32_t table_id;
+ bool onlink;
+
+ vrf = yang_dnode_get_string(route, "../../vrf");
+
+ afi_safi = yang_dnode_get_string(route, "./afi-safi");
+ yang_afi_safi_identity2value(afi_safi, &afi, &safi);
+
+ if (afi == AFI_IP)
+ vty_out(vty, "%sip",
+ strmatch(vrf, VRF_DEFAULT_NAME) ? "" : " ");
+ else
+ vty_out(vty, "%sipv6",
+ strmatch(vrf, VRF_DEFAULT_NAME) ? "" : " ");
+
+ if (safi == SAFI_UNICAST)
+ vty_out(vty, " route");
+ else
+ vty_out(vty, " mroute");
+
+ vty_out(vty, " %s", yang_dnode_get_string(route, "./prefix"));
+
+ if (src)
+ vty_out(vty, " from %s",
+ yang_dnode_get_string(src, "./src-prefix"));
+
+ nh_type = yang_dnode_get_enum(nexthop, "./nh-type");
+ switch (nh_type) {
+ case STATIC_IFNAME:
+ vty_out(vty, " %s",
+ yang_dnode_get_string(nexthop, "./interface"));
+ break;
+ case STATIC_IPV4_GATEWAY:
+ case STATIC_IPV6_GATEWAY:
+ vty_out(vty, " %s",
+ yang_dnode_get_string(nexthop, "./gateway"));
+ break;
+ case STATIC_IPV4_GATEWAY_IFNAME:
+ case STATIC_IPV6_GATEWAY_IFNAME:
+ vty_out(vty, " %s",
+ yang_dnode_get_string(nexthop, "./gateway"));
+ vty_out(vty, " %s",
+ yang_dnode_get_string(nexthop, "./interface"));
+ break;
+ case STATIC_BLACKHOLE:
+ bh_type = yang_dnode_get_enum(nexthop, "./bh-type");
+ switch (bh_type) {
+ case STATIC_BLACKHOLE_DROP:
+ vty_out(vty, " blackhole");
+ break;
+ case STATIC_BLACKHOLE_NULL:
+ vty_out(vty, " Null0");
+ break;
+ case STATIC_BLACKHOLE_REJECT:
+ vty_out(vty, " reject");
+ break;
+ }
+ break;
+ }
+
+ if (yang_dnode_exists(path, "./tag")) {
+ tag = yang_dnode_get_uint32(path, "./tag");
+ if (tag != 0 || show_defaults)
+ vty_out(vty, " tag %" PRIu32, tag);
+ }
+
+ distance = yang_dnode_get_uint8(path, "./distance");
+ if (distance != ZEBRA_STATIC_DISTANCE_DEFAULT || show_defaults)
+ vty_out(vty, " %" PRIu8, distance);
+
+ iter.vty = vty;
+ iter.first = true;
+ yang_dnode_iterate(mpls_label_iter_cb, &iter, nexthop,
+ "./mpls-label-stack/entry");
+
+ seg_iter.vty = vty;
+ seg_iter.first = true;
+ yang_dnode_iterate(srv6_seg_iter_cb, &seg_iter, nexthop,
+ "./srv6-segs-stack/entry");
+
+ nexthop_vrf = yang_dnode_get_string(nexthop, "./vrf");
+ if (strcmp(vrf, nexthop_vrf))
+ vty_out(vty, " nexthop-vrf %s", nexthop_vrf);
+
+ table_id = yang_dnode_get_uint32(path, "./table-id");
+ if (table_id || show_defaults)
+ vty_out(vty, " table %" PRIu32, table_id);
+
+ if (yang_dnode_exists(nexthop, "./onlink")) {
+ onlink = yang_dnode_get_bool(nexthop, "./onlink");
+ if (onlink)
+ vty_out(vty, " onlink");
+ }
+
+ if (yang_dnode_exists(nexthop, "./srte-color"))
+ vty_out(vty, " color %s",
+ yang_dnode_get_string(nexthop, "./srte-color"));
+
+ if (yang_dnode_exists(nexthop, "./bfd-monitoring")) {
+ const struct lyd_node *bfd_dnode =
+ yang_dnode_get(nexthop, "./bfd-monitoring");
+
+ if (yang_dnode_get_bool(bfd_dnode, "./multi-hop")) {
+ vty_out(vty, " bfd multi-hop");
+
+ if (yang_dnode_exists(bfd_dnode, "./source"))
+ vty_out(vty, " source %s",
+ yang_dnode_get_string(bfd_dnode,
+ "./source"));
+ } else
+ vty_out(vty, " bfd");
+
+ if (yang_dnode_exists(bfd_dnode, "./profile"))
+ vty_out(vty, " profile %s",
+ yang_dnode_get_string(bfd_dnode, "./profile"));
+ }
+
+ vty_out(vty, "\n");
+}
+
+void static_nexthop_cli_show(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ const struct lyd_node *path = yang_dnode_get_parent(dnode, "path-list");
+ const struct lyd_node *route =
+ yang_dnode_get_parent(path, "route-list");
+
+ nexthop_cli_show(vty, route, NULL, path, dnode, show_defaults);
+}
+
+void static_src_nexthop_cli_show(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ const struct lyd_node *path = yang_dnode_get_parent(dnode, "path-list");
+ const struct lyd_node *src = yang_dnode_get_parent(path, "src-list");
+ const struct lyd_node *route = yang_dnode_get_parent(src, "route-list");
+
+ nexthop_cli_show(vty, route, src, path, dnode, show_defaults);
+}
+
+int static_nexthop_cli_cmp(const struct lyd_node *dnode1,
+ const struct lyd_node *dnode2)
+{
+ enum static_nh_type nh_type1, nh_type2;
+ struct prefix prefix1, prefix2;
+ const char *vrf1, *vrf2;
+ int ret = 0;
+
+ nh_type1 = yang_dnode_get_enum(dnode1, "./nh-type");
+ nh_type2 = yang_dnode_get_enum(dnode2, "./nh-type");
+
+ if (nh_type1 != nh_type2)
+ return (int)nh_type1 - (int)nh_type2;
+
+ switch (nh_type1) {
+ case STATIC_IFNAME:
+ ret = if_cmp_name_func(
+ yang_dnode_get_string(dnode1, "./interface"),
+ yang_dnode_get_string(dnode2, "./interface"));
+ break;
+ case STATIC_IPV4_GATEWAY:
+ case STATIC_IPV6_GATEWAY:
+ yang_dnode_get_prefix(&prefix1, dnode1, "./gateway");
+ yang_dnode_get_prefix(&prefix2, dnode2, "./gateway");
+ ret = prefix_cmp(&prefix1, &prefix2);
+ break;
+ case STATIC_IPV4_GATEWAY_IFNAME:
+ case STATIC_IPV6_GATEWAY_IFNAME:
+ yang_dnode_get_prefix(&prefix1, dnode1, "./gateway");
+ yang_dnode_get_prefix(&prefix2, dnode2, "./gateway");
+ ret = prefix_cmp(&prefix1, &prefix2);
+ if (!ret)
+ ret = if_cmp_name_func(
+ yang_dnode_get_string(dnode1, "./interface"),
+ yang_dnode_get_string(dnode2, "./interface"));
+ break;
+ case STATIC_BLACKHOLE:
+ /* There's only one blackhole nexthop per route */
+ ret = 0;
+ break;
+ }
+
+ if (ret)
+ return ret;
+
+ vrf1 = yang_dnode_get_string(dnode1, "./vrf");
+ if (strmatch(vrf1, "default"))
+ vrf1 = "";
+ vrf2 = yang_dnode_get_string(dnode2, "./vrf");
+ if (strmatch(vrf2, "default"))
+ vrf2 = "";
+
+ return if_cmp_name_func(vrf1, vrf2);
+}
+
+int static_route_list_cli_cmp(const struct lyd_node *dnode1,
+ const struct lyd_node *dnode2)
+{
+ const char *afi_safi1, *afi_safi2;
+ afi_t afi1, afi2;
+ safi_t safi1, safi2;
+ struct prefix prefix1, prefix2;
+
+ afi_safi1 = yang_dnode_get_string(dnode1, "./afi-safi");
+ yang_afi_safi_identity2value(afi_safi1, &afi1, &safi1);
+
+ afi_safi2 = yang_dnode_get_string(dnode2, "./afi-safi");
+ yang_afi_safi_identity2value(afi_safi2, &afi2, &safi2);
+
+ if (afi1 != afi2)
+ return (int)afi1 - (int)afi2;
+
+ if (safi1 != safi2)
+ return (int)safi1 - (int)safi2;
+
+ yang_dnode_get_prefix(&prefix1, dnode1, "./prefix");
+ yang_dnode_get_prefix(&prefix2, dnode2, "./prefix");
+
+ return prefix_cmp(&prefix1, &prefix2);
+}
+
+int static_src_list_cli_cmp(const struct lyd_node *dnode1,
+ const struct lyd_node *dnode2)
+{
+ struct prefix prefix1, prefix2;
+
+ yang_dnode_get_prefix(&prefix1, dnode1, "./src-prefix");
+ yang_dnode_get_prefix(&prefix2, dnode2, "./src-prefix");
+
+ return prefix_cmp(&prefix1, &prefix2);
+}
+
+int static_path_list_cli_cmp(const struct lyd_node *dnode1,
+ const struct lyd_node *dnode2)
+{
+ uint32_t table_id1, table_id2;
+ uint8_t distance1, distance2;
+
+ table_id1 = yang_dnode_get_uint32(dnode1, "./table-id");
+ table_id2 = yang_dnode_get_uint32(dnode2, "./table-id");
+
+ if (table_id1 != table_id2)
+ return (int)table_id1 - (int)table_id2;
+
+ distance1 = yang_dnode_get_uint8(dnode1, "./distance");
+ distance2 = yang_dnode_get_uint8(dnode2, "./distance");
+
+ return (int)distance1 - (int)distance2;
+}
+
+#ifndef INCLUDE_MGMTD_CMDDEFS_ONLY
+
+DEFPY_YANG(debug_staticd, debug_staticd_cmd,
+ "[no] debug static [{events$events|route$route|bfd$bfd}]",
+ NO_STR DEBUG_STR STATICD_STR
+ "Debug events\n"
+ "Debug route\n"
+ "Debug bfd\n")
+{
+ /* If no specific category, change all */
+ if (strmatch(argv[argc - 1]->text, "static"))
+ static_debug_set(vty->node, !no, true, true, true);
+ else
+ static_debug_set(vty->node, !no, !!events, !!route, !!bfd);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(staticd_show_bfd_routes, staticd_show_bfd_routes_cmd,
+ "show bfd static route [json]$isjson",
+ SHOW_STR
+ BFD_INTEGRATION_STR
+ STATICD_STR
+ ROUTE_STR
+ JSON_STR)
+{
+ static_bfd_show(vty, !!isjson);
+ return CMD_SUCCESS;
+}
+
+DEFUN_NOSH (show_debugging_static,
+ show_debugging_static_cmd,
+ "show debugging [static]",
+ SHOW_STR
+ DEBUG_STR
+ "Static Information\n")
+{
+ vty_out(vty, "Staticd debugging status\n");
+
+ static_debug_status_write(vty);
+
+ cmd_show_lib_debugs(vty);
+
+ return CMD_SUCCESS;
+}
+
+static struct cmd_node debug_node = {
+ .name = "debug",
+ .node = DEBUG_NODE,
+ .prompt = "",
+ .config_write = static_config_write_debug,
+};
+
+#endif /* ifndef INCLUDE_MGMTD_CMDDEFS_ONLY */
+
+void static_vty_init(void)
+{
+#ifndef INCLUDE_MGMTD_CMDDEFS_ONLY
+ install_node(&debug_node);
+ install_element(ENABLE_NODE, &debug_staticd_cmd);
+ install_element(CONFIG_NODE, &debug_staticd_cmd);
+ install_element(ENABLE_NODE, &show_debugging_static_cmd);
+ install_element(ENABLE_NODE, &staticd_show_bfd_routes_cmd);
+#endif /* ifndef INCLUDE_MGMTD_CMDDEFS_ONLY */
+
+ install_element(CONFIG_NODE, &ip_mroute_dist_cmd);
+
+ install_element(CONFIG_NODE, &ip_route_blackhole_cmd);
+ install_element(VRF_NODE, &ip_route_blackhole_vrf_cmd);
+ install_element(CONFIG_NODE, &ip_route_address_interface_cmd);
+ install_element(VRF_NODE, &ip_route_address_interface_vrf_cmd);
+ install_element(CONFIG_NODE, &ip_route_cmd);
+ install_element(VRF_NODE, &ip_route_vrf_cmd);
+
+ install_element(CONFIG_NODE, &ipv6_route_blackhole_cmd);
+ install_element(VRF_NODE, &ipv6_route_blackhole_vrf_cmd);
+ install_element(CONFIG_NODE, &ipv6_route_address_interface_cmd);
+ install_element(VRF_NODE, &ipv6_route_address_interface_vrf_cmd);
+ install_element(CONFIG_NODE, &ipv6_route_cmd);
+ install_element(VRF_NODE, &ipv6_route_vrf_cmd);
+
+ mgmt_be_client_lib_vty_init();
+}
diff --git a/staticd/static_vty.h b/staticd/static_vty.h
new file mode 100644
index 0000000..77e52b5
--- /dev/null
+++ b/staticd/static_vty.h
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * STATICd - vty header
+ * Copyright (C) 2018 Cumulus Networks, Inc.
+ * Donald Sharp
+ */
+#ifndef __STATIC_VTY_H__
+#define __STATIC_VTY_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void static_cli_show(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults);
+void static_cli_show_end(struct vty *vty, const struct lyd_node *dnode);
+void static_nexthop_cli_show(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults);
+void static_src_nexthop_cli_show(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults);
+int static_nexthop_cli_cmp(const struct lyd_node *dnode1,
+ const struct lyd_node *dnode2);
+int static_route_list_cli_cmp(const struct lyd_node *dnode1,
+ const struct lyd_node *dnode2);
+int static_src_list_cli_cmp(const struct lyd_node *dnode1,
+ const struct lyd_node *dnode2);
+int static_path_list_cli_cmp(const struct lyd_node *dnode1,
+ const struct lyd_node *dnode2);
+
+void static_vty_init(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/staticd/static_zebra.c b/staticd/static_zebra.c
new file mode 100644
index 0000000..6abbdad
--- /dev/null
+++ b/staticd/static_zebra.c
@@ -0,0 +1,584 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Zebra connect code.
+ * Copyright (C) 2018 Cumulus Networks, Inc.
+ * Donald Sharp
+ */
+#include <zebra.h>
+
+#include "frrevent.h"
+#include "command.h"
+#include "network.h"
+#include "prefix.h"
+#include "routemap.h"
+#include "table.h"
+#include "srcdest_table.h"
+#include "stream.h"
+#include "memory.h"
+#include "zclient.h"
+#include "filter.h"
+#include "plist.h"
+#include "log.h"
+#include "nexthop.h"
+#include "nexthop_group.h"
+#include "hash.h"
+#include "jhash.h"
+
+#include "static_vrf.h"
+#include "static_routes.h"
+#include "static_zebra.h"
+#include "static_nht.h"
+#include "static_vty.h"
+#include "static_debug.h"
+
+DEFINE_MTYPE_STATIC(STATIC, STATIC_NHT_DATA, "Static Nexthop tracking data");
+PREDECL_HASH(static_nht_hash);
+
+struct static_nht_data {
+ struct static_nht_hash_item itm;
+
+ struct prefix nh;
+ safi_t safi;
+
+ vrf_id_t nh_vrf_id;
+
+ uint32_t refcount;
+ uint8_t nh_num;
+ bool registered;
+};
+
+static int static_nht_data_cmp(const struct static_nht_data *nhtd1,
+ const struct static_nht_data *nhtd2)
+{
+ if (nhtd1->nh_vrf_id != nhtd2->nh_vrf_id)
+ return numcmp(nhtd1->nh_vrf_id, nhtd2->nh_vrf_id);
+ if (nhtd1->safi != nhtd2->safi)
+ return numcmp(nhtd1->safi, nhtd2->safi);
+
+ return prefix_cmp(&nhtd1->nh, &nhtd2->nh);
+}
+
+static unsigned int static_nht_data_hash(const struct static_nht_data *nhtd)
+{
+ unsigned int key = 0;
+
+ key = prefix_hash_key(&nhtd->nh);
+ return jhash_2words(nhtd->nh_vrf_id, nhtd->safi, key);
+}
+
+DECLARE_HASH(static_nht_hash, struct static_nht_data, itm, static_nht_data_cmp,
+ static_nht_data_hash);
+
+static struct static_nht_hash_head static_nht_hash[1];
+
+/* Zebra structure to hold current status. */
+struct zclient *zclient;
+uint32_t zebra_ecmp_count = MULTIPATH_NUM;
+
+/* Interface addition message from zebra. */
+static int static_ifp_create(struct interface *ifp)
+{
+ static_ifindex_update(ifp, true);
+
+ return 0;
+}
+
+static int static_ifp_destroy(struct interface *ifp)
+{
+ static_ifindex_update(ifp, false);
+ 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 static_ifp_up(struct interface *ifp)
+{
+ /* Install any static reliant on this interface coming up */
+ static_install_intf_nh(ifp);
+ static_ifindex_update(ifp, true);
+
+ return 0;
+}
+
+static int static_ifp_down(struct interface *ifp)
+{
+ static_ifindex_update(ifp, false);
+
+ return 0;
+}
+
+static int route_notify_owner(ZAPI_CALLBACK_ARGS)
+{
+ struct prefix p;
+ enum zapi_route_notify_owner note;
+ uint32_t table_id;
+ safi_t safi;
+
+ if (!zapi_route_notify_decode(zclient->ibuf, &p, &table_id, &note, NULL,
+ &safi))
+ return -1;
+
+ switch (note) {
+ case ZAPI_ROUTE_FAIL_INSTALL:
+ static_nht_mark_state(&p, safi, vrf_id, STATIC_NOT_INSTALLED);
+ zlog_warn("%s: Route %pFX failed to install for table: %u",
+ __func__, &p, table_id);
+ break;
+ case ZAPI_ROUTE_BETTER_ADMIN_WON:
+ static_nht_mark_state(&p, safi, vrf_id, STATIC_NOT_INSTALLED);
+ zlog_warn(
+ "%s: Route %pFX over-ridden by better route for table: %u",
+ __func__, &p, table_id);
+ break;
+ case ZAPI_ROUTE_INSTALLED:
+ static_nht_mark_state(&p, safi, vrf_id, STATIC_INSTALLED);
+ break;
+ case ZAPI_ROUTE_REMOVED:
+ static_nht_mark_state(&p, safi, vrf_id, STATIC_NOT_INSTALLED);
+ break;
+ case ZAPI_ROUTE_REMOVE_FAIL:
+ static_nht_mark_state(&p, safi, vrf_id, STATIC_INSTALLED);
+ zlog_warn("%s: Route %pFX failure to remove for table: %u",
+ __func__, &p, table_id);
+ break;
+ }
+
+ return 0;
+}
+
+static void zebra_connected(struct zclient *zclient)
+{
+ zclient_send_reg_requests(zclient, VRF_DEFAULT);
+
+ static_fixup_vrf_ids(vrf_info_lookup(VRF_DEFAULT));
+}
+
+/* API to check whether the configured nexthop address is
+ * one of its local connected address or not.
+ */
+static bool
+static_nexthop_is_local(vrf_id_t vrfid, struct prefix *addr, int family)
+{
+ if (family == AF_INET) {
+ if (if_address_is_local(&addr->u.prefix4, AF_INET, vrfid))
+ return true;
+ } else if (family == AF_INET6) {
+ if (if_address_is_local(&addr->u.prefix6, AF_INET6, vrfid))
+ return true;
+ }
+ return false;
+}
+static int static_zebra_nexthop_update(ZAPI_CALLBACK_ARGS)
+{
+ struct static_nht_data *nhtd, lookup;
+ struct zapi_route nhr;
+ struct prefix matched;
+ afi_t afi = AFI_IP;
+
+ if (!zapi_nexthop_update_decode(zclient->ibuf, &matched, &nhr)) {
+ zlog_err("Failure to decode nexthop update message");
+ return 1;
+ }
+
+ if (zclient->bfd_integration)
+ bfd_nht_update(&matched, &nhr);
+
+ if (matched.family == AF_INET6)
+ afi = AFI_IP6;
+
+ if (nhr.type == ZEBRA_ROUTE_CONNECT) {
+ if (static_nexthop_is_local(vrf_id, &matched,
+ nhr.prefix.family))
+ nhr.nexthop_num = 0;
+ }
+
+ memset(&lookup, 0, sizeof(lookup));
+ lookup.nh = matched;
+ lookup.nh_vrf_id = vrf_id;
+ lookup.safi = nhr.safi;
+
+ nhtd = static_nht_hash_find(static_nht_hash, &lookup);
+
+ if (nhtd) {
+ nhtd->nh_num = nhr.nexthop_num;
+
+ static_nht_reset_start(&matched, afi, nhr.safi,
+ nhtd->nh_vrf_id);
+ static_nht_update(NULL, &matched, nhr.nexthop_num, afi,
+ nhr.safi, nhtd->nh_vrf_id);
+ } else
+ zlog_err("No nhtd?");
+
+ return 1;
+}
+
+static void static_zebra_capabilities(struct zclient_capabilities *cap)
+{
+ mpls_enabled = cap->mpls_enabled;
+ zebra_ecmp_count = cap->ecmp;
+}
+
+static struct static_nht_data *
+static_nht_hash_getref(const struct static_nht_data *ref)
+{
+ struct static_nht_data *nhtd;
+
+ nhtd = static_nht_hash_find(static_nht_hash, ref);
+ if (!nhtd) {
+ nhtd = XCALLOC(MTYPE_STATIC_NHT_DATA, sizeof(*nhtd));
+
+ prefix_copy(&nhtd->nh, &ref->nh);
+ nhtd->nh_vrf_id = ref->nh_vrf_id;
+ nhtd->safi = ref->safi;
+
+ static_nht_hash_add(static_nht_hash, nhtd);
+ }
+
+ nhtd->refcount++;
+ return nhtd;
+}
+
+static bool static_nht_hash_decref(struct static_nht_data **nhtd_p)
+{
+ struct static_nht_data *nhtd = *nhtd_p;
+
+ *nhtd_p = NULL;
+
+ if (--nhtd->refcount > 0)
+ return true;
+
+ static_nht_hash_del(static_nht_hash, nhtd);
+ XFREE(MTYPE_STATIC_NHT_DATA, nhtd);
+ return false;
+}
+
+static void static_nht_hash_clear(void)
+{
+ struct static_nht_data *nhtd;
+
+ while ((nhtd = static_nht_hash_pop(static_nht_hash)))
+ XFREE(MTYPE_STATIC_NHT_DATA, nhtd);
+}
+
+static bool static_zebra_nht_get_prefix(const struct static_nexthop *nh,
+ struct prefix *p)
+{
+ switch (nh->type) {
+ case STATIC_IFNAME:
+ case STATIC_BLACKHOLE:
+ p->family = AF_UNSPEC;
+ return false;
+
+ case STATIC_IPV4_GATEWAY:
+ case STATIC_IPV4_GATEWAY_IFNAME:
+ p->family = AF_INET;
+ p->prefixlen = IPV4_MAX_BITLEN;
+ p->u.prefix4 = nh->addr.ipv4;
+ return true;
+
+ case STATIC_IPV6_GATEWAY:
+ case STATIC_IPV6_GATEWAY_IFNAME:
+ p->family = AF_INET6;
+ p->prefixlen = IPV6_MAX_BITLEN;
+ p->u.prefix6 = nh->addr.ipv6;
+ return true;
+ }
+
+ assertf(0, "BUG: someone forgot to add nexthop type %u", nh->type);
+ return false;
+}
+
+void static_zebra_nht_register(struct static_nexthop *nh, bool reg)
+{
+ struct static_path *pn = nh->pn;
+ struct route_node *rn = pn->rn;
+ struct static_route_info *si = static_route_info_from_rnode(rn);
+ struct static_nht_data *nhtd, lookup = {};
+ uint32_t cmd;
+
+ if (!static_zebra_nht_get_prefix(nh, &lookup.nh))
+ return;
+ lookup.nh_vrf_id = nh->nh_vrf_id;
+ lookup.safi = si->safi;
+
+ if (nh->nh_registered) {
+ /* nh->nh_registered means we own a reference on the nhtd */
+ nhtd = static_nht_hash_find(static_nht_hash, &lookup);
+
+ assertf(nhtd, "BUG: NH %pFX registered but not in hashtable",
+ &lookup.nh);
+ } else if (reg) {
+ nhtd = static_nht_hash_getref(&lookup);
+
+ if (nhtd->refcount > 1)
+ DEBUGD(&static_dbg_route,
+ "Reusing registered nexthop(%pFX) for %pRN %d",
+ &lookup.nh, rn, nhtd->nh_num);
+ } else {
+ /* !reg && !nh->nh_registered */
+ zlog_warn("trying to unregister nexthop %pFX twice",
+ &lookup.nh);
+ return;
+ }
+
+ nh->nh_registered = reg;
+
+ if (reg) {
+ if (nhtd->nh_num) {
+ /* refresh with existing data */
+ afi_t afi = prefix_afi(&lookup.nh);
+
+ if (nh->state == STATIC_NOT_INSTALLED)
+ nh->state = STATIC_START;
+ static_nht_update(&rn->p, &nhtd->nh, nhtd->nh_num, afi,
+ si->safi, nh->nh_vrf_id);
+ return;
+ }
+
+ if (nhtd->registered)
+ /* have no data, but did send register */
+ return;
+
+ cmd = ZEBRA_NEXTHOP_REGISTER;
+ DEBUGD(&static_dbg_route, "Registering nexthop(%pFX) for %pRN",
+ &lookup.nh, rn);
+ } else {
+ bool was_zebra_registered;
+
+ was_zebra_registered = nhtd->registered;
+ if (static_nht_hash_decref(&nhtd))
+ /* still got references alive */
+ return;
+
+ /* NB: nhtd is now NULL. */
+ if (!was_zebra_registered)
+ return;
+
+ cmd = ZEBRA_NEXTHOP_UNREGISTER;
+ DEBUGD(&static_dbg_route,
+ "Unregistering nexthop(%pFX) for %pRN", &lookup.nh, rn);
+ }
+
+ if (zclient_send_rnh(zclient, cmd, &lookup.nh, si->safi, false, false,
+ nh->nh_vrf_id) == ZCLIENT_SEND_FAILURE)
+ zlog_warn("%s: Failure to send nexthop %pFX for %pRN to zebra",
+ __func__, &lookup.nh, rn);
+ else if (reg)
+ nhtd->registered = true;
+}
+
+extern void static_zebra_route_add(struct static_path *pn, bool install)
+{
+ struct route_node *rn = pn->rn;
+ struct static_route_info *si = rn->info;
+ struct static_nexthop *nh;
+ const struct prefix *p, *src_pp;
+ struct zapi_nexthop *api_nh;
+ struct zapi_route api;
+ uint32_t nh_num = 0;
+
+ p = src_pp = NULL;
+ srcdest_rnode_prefixes(rn, &p, &src_pp);
+
+ memset(&api, 0, sizeof(api));
+ api.vrf_id = si->svrf->vrf->vrf_id;
+ api.type = ZEBRA_ROUTE_STATIC;
+ api.safi = si->safi;
+ memcpy(&api.prefix, p, sizeof(api.prefix));
+
+ if (src_pp) {
+ SET_FLAG(api.message, ZAPI_MESSAGE_SRCPFX);
+ memcpy(&api.src_prefix, src_pp, sizeof(api.src_prefix));
+ }
+ SET_FLAG(api.flags, ZEBRA_FLAG_RR_USE_DISTANCE);
+ SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION);
+ SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP);
+ if (pn->distance) {
+ SET_FLAG(api.message, ZAPI_MESSAGE_DISTANCE);
+ api.distance = pn->distance;
+ }
+ if (pn->tag) {
+ SET_FLAG(api.message, ZAPI_MESSAGE_TAG);
+ api.tag = pn->tag;
+ }
+ if (pn->table_id != 0) {
+ SET_FLAG(api.message, ZAPI_MESSAGE_TABLEID);
+ api.tableid = pn->table_id;
+ }
+ frr_each(static_nexthop_list, &pn->nexthop_list, nh) {
+ /* Don't overrun the nexthop array */
+ if (nh_num == zebra_ecmp_count)
+ break;
+
+ api_nh = &api.nexthops[nh_num];
+ if (nh->nh_vrf_id == VRF_UNKNOWN)
+ continue;
+ /* Skip next hop which peer is down. */
+ if (nh->path_down)
+ continue;
+
+ api_nh->vrf_id = nh->nh_vrf_id;
+ if (nh->onlink)
+ SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK);
+ if (nh->color != 0) {
+ SET_FLAG(api.message, ZAPI_MESSAGE_SRTE);
+ api_nh->srte_color = nh->color;
+ }
+
+ nh->state = STATIC_SENT_TO_ZEBRA;
+
+ switch (nh->type) {
+ case STATIC_IFNAME:
+ if (nh->ifindex == IFINDEX_INTERNAL)
+ continue;
+ api_nh->ifindex = nh->ifindex;
+ api_nh->type = NEXTHOP_TYPE_IFINDEX;
+ break;
+ case STATIC_IPV4_GATEWAY:
+ if (!nh->nh_valid)
+ continue;
+ api_nh->type = NEXTHOP_TYPE_IPV4;
+ api_nh->gate = nh->addr;
+ break;
+ case STATIC_IPV4_GATEWAY_IFNAME:
+ if (nh->ifindex == IFINDEX_INTERNAL)
+ continue;
+ api_nh->ifindex = nh->ifindex;
+ api_nh->type = NEXTHOP_TYPE_IPV4_IFINDEX;
+ api_nh->gate = nh->addr;
+ break;
+ case STATIC_IPV6_GATEWAY:
+ if (!nh->nh_valid)
+ continue;
+ api_nh->type = NEXTHOP_TYPE_IPV6;
+ api_nh->gate = nh->addr;
+ break;
+ case STATIC_IPV6_GATEWAY_IFNAME:
+ if (nh->ifindex == IFINDEX_INTERNAL)
+ continue;
+ api_nh->type = NEXTHOP_TYPE_IPV6_IFINDEX;
+ api_nh->ifindex = nh->ifindex;
+ api_nh->gate = nh->addr;
+ break;
+ case STATIC_BLACKHOLE:
+ api_nh->type = NEXTHOP_TYPE_BLACKHOLE;
+ switch (nh->bh_type) {
+ case STATIC_BLACKHOLE_DROP:
+ case STATIC_BLACKHOLE_NULL:
+ api_nh->bh_type = BLACKHOLE_NULL;
+ break;
+ case STATIC_BLACKHOLE_REJECT:
+ api_nh->bh_type = BLACKHOLE_REJECT;
+ }
+ break;
+ }
+
+ if (nh->snh_label.num_labels) {
+ int i;
+
+ SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL);
+ api_nh->label_num = nh->snh_label.num_labels;
+ for (i = 0; i < api_nh->label_num; i++)
+ api_nh->labels[i] = nh->snh_label.label[i];
+ }
+ if (nh->snh_seg.num_segs) {
+ int i;
+
+ api_nh->seg6local_action =
+ ZEBRA_SEG6_LOCAL_ACTION_UNSPEC;
+ SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_SEG6);
+ SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION);
+ api.safi = SAFI_UNICAST;
+
+ api_nh->seg_num = nh->snh_seg.num_segs;
+ for (i = 0; i < api_nh->seg_num; i++)
+ memcpy(&api_nh->seg6_segs[i],
+ &nh->snh_seg.seg[i],
+ sizeof(struct in6_addr));
+ }
+ nh_num++;
+ }
+
+ api.nexthop_num = nh_num;
+
+ /*
+ * If we have been given an install but nothing is valid
+ * go ahead and delete the route for double plus fun
+ */
+ if (!nh_num && install)
+ install = false;
+
+ zclient_route_send(install ?
+ ZEBRA_ROUTE_ADD : ZEBRA_ROUTE_DELETE,
+ zclient, &api);
+}
+
+static zclient_handler *const static_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] = static_zebra_nexthop_update,
+};
+
+void static_zebra_init(void)
+{
+ struct zclient_options opt = { .receive_notify = true };
+
+ if_zapi_callbacks(static_ifp_create, static_ifp_up,
+ static_ifp_down, static_ifp_destroy);
+
+ zclient = zclient_new(master, &opt, static_handlers,
+ array_size(static_handlers));
+
+ zclient_init(zclient, ZEBRA_ROUTE_STATIC, 0, &static_privs);
+ zclient->zebra_capabilities = static_zebra_capabilities;
+ zclient->zebra_connected = zebra_connected;
+
+ static_nht_hash_init(static_nht_hash);
+ static_bfd_initialize(zclient, master);
+}
+
+/* static_zebra_stop used by tests/lib/test_grpc.cpp */
+void static_zebra_stop(void)
+{
+ static_nht_hash_clear();
+ static_nht_hash_fini(static_nht_hash);
+
+ if (!zclient)
+ return;
+ zclient_stop(zclient);
+ zclient_free(zclient);
+ zclient = NULL;
+}
+
+void static_zebra_vrf_register(struct vrf *vrf)
+{
+ if (vrf->vrf_id == VRF_DEFAULT)
+ return;
+ zclient_send_reg_requests(zclient, vrf->vrf_id);
+}
+
+void static_zebra_vrf_unregister(struct vrf *vrf)
+{
+ if (vrf->vrf_id == VRF_DEFAULT)
+ return;
+ zclient_send_dereg_requests(zclient, vrf->vrf_id);
+}
diff --git a/staticd/static_zebra.h b/staticd/static_zebra.h
new file mode 100644
index 0000000..c4f4ebd
--- /dev/null
+++ b/staticd/static_zebra.h
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Zebra connect library for staticd
+ * Copyright (C) 2018 Cumulus Networks, Inc.
+ * Donald Sharp
+ */
+#ifndef __STATIC_ZEBRA_H__
+#define __STATIC_ZEBRA_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern struct event_loop *master;
+
+extern void static_zebra_nht_register(struct static_nexthop *nh, bool reg);
+
+extern void static_zebra_route_add(struct static_path *pn, bool install);
+extern void static_zebra_init(void);
+/* static_zebra_stop used by tests/lib/test_grpc.cpp */
+extern void static_zebra_stop(void);
+extern void static_zebra_vrf_register(struct vrf *vrf);
+extern void static_zebra_vrf_unregister(struct vrf *vrf);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/staticd/subdir.am b/staticd/subdir.am
new file mode 100644
index 0000000..07ebe3c
--- /dev/null
+++ b/staticd/subdir.am
@@ -0,0 +1,44 @@
+#
+# staticd
+#
+
+if STATICD
+noinst_LIBRARIES += staticd/libstatic.a
+sbin_PROGRAMS += staticd/staticd
+vtysh_daemons += staticd
+man8 += $(MANBUILD)/frr-staticd.8
+endif
+
+staticd_libstatic_a_SOURCES = \
+ staticd/static_bfd.c \
+ staticd/static_debug.c \
+ staticd/static_nht.c \
+ staticd/static_routes.c \
+ staticd/static_zebra.c \
+ staticd/static_vrf.c \
+ staticd/static_vty.c \
+ staticd/static_nb.c \
+ staticd/static_nb_config.c \
+ # end
+
+noinst_HEADERS += \
+ staticd/static_debug.h \
+ staticd/static_nht.h \
+ staticd/static_zebra.h \
+ staticd/static_routes.h \
+ staticd/static_vty.h \
+ staticd/static_vrf.h \
+ staticd/static_nb.h \
+ # end
+
+clippy_scan += \
+ staticd/static_vty.c \
+ # end
+
+staticd_staticd_SOURCES = staticd/static_main.c
+staticd_staticd_LDADD = staticd/libstatic.a lib/libfrr.la $(LIBCAP) $(LIBYANG_LIBS)
+
+nodist_staticd_staticd_SOURCES = \
+ yang/frr-bfdd.yang.c \
+ yang/frr-staticd.yang.c \
+ # end