diff options
Diffstat (limited to 'staticd')
-rw-r--r-- | staticd/.gitignore | 2 | ||||
-rw-r--r-- | staticd/Makefile | 10 | ||||
-rw-r--r-- | staticd/static_bfd.c | 379 | ||||
-rw-r--r-- | staticd/static_debug.c | 123 | ||||
-rw-r--r-- | staticd/static_debug.h | 67 | ||||
-rw-r--r-- | staticd/static_main.c | 183 | ||||
-rw-r--r-- | staticd/static_nb.c | 246 | ||||
-rw-r--r-- | staticd/static_nb.h | 207 | ||||
-rw-r--r-- | staticd/static_nb_config.c | 1338 | ||||
-rw-r--r-- | staticd/static_nht.c | 211 | ||||
-rw-r--r-- | staticd/static_nht.h | 54 | ||||
-rw-r--r-- | staticd/static_routes.c | 792 | ||||
-rw-r--r-- | staticd/static_routes.h | 273 | ||||
-rw-r--r-- | staticd/static_vrf.c | 152 | ||||
-rw-r--r-- | staticd/static_vrf.h | 40 | ||||
-rw-r--r-- | staticd/static_vty.c | 1658 | ||||
-rw-r--r-- | staticd/static_vty.h | 36 | ||||
-rw-r--r-- | staticd/static_zebra.c | 584 | ||||
-rw-r--r-- | staticd/static_zebra.h | 29 | ||||
-rw-r--r-- | staticd/subdir.am | 44 |
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, ¬e, 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 |