diff --git a/lib/zclient.c b/lib/zclient.c index 17026d13a6..deb5f519bd 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -4083,3 +4083,11 @@ uint32_t zclient_get_nhg_start(uint32_t proto) return ZEBRA_NHG_SPACING * proto; } + +/* + * Where do routing protocols IDs start overall (first ID after zebra) + */ +uint32_t zclient_get_nhg_lower_bound() +{ + return ZEBRA_NHG_SPACING * (ZEBRA_ROUTE_CONNECT + 1); +} diff --git a/lib/zclient.h b/lib/zclient.h index b7850cdec7..db5e1ce5b9 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -692,6 +692,7 @@ extern struct zclient_options zclient_options_default; */ #define ZEBRA_NHG_SPACING 50000000 extern uint32_t zclient_get_nhg_start(uint32_t proto); +extern uint32_t zclient_get_nhg_lower_bound(void); extern struct zclient *zclient_new(struct thread_master *m, struct zclient_options *opt); diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 146650f602..07135b7fc8 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -125,6 +125,31 @@ static bool kernel_nexthops_supported(void) && zebra_nhg_kernel_nexthops_enabled()); } +/* + * Some people may only want to use NHGs created by protos and not + * implicitly created by Zebra. This check accounts for that. + */ +static bool proto_nexthops_only(void) +{ + return zebra_nhg_proto_nexthops_only(); +} + +/* Is this a proto created NHG? */ +static bool is_proto_nhg(uint32_t id, int type) +{ + /* If type is available, use it as the source of truth */ + if (type) { + if (type != ZEBRA_ROUTE_NHG) + return true; + return false; + } + + if (id >= zclient_get_nhg_lower_bound()) + return true; + + return false; +} + /* * The ipv4_ll data structure is used for all 5549 * additions to the kernel. Let's figure out the @@ -1748,7 +1773,10 @@ ssize_t netlink_route_multipath_msg_encode(int cmd, nl_attr_nest_end(&req->n, nest); } - if ((!fpm && kernel_nexthops_supported()) || (fpm && force_nhg)) { + if ((!fpm && kernel_nexthops_supported() + && (!proto_nexthops_only() + || is_proto_nhg(dplane_ctx_get_nhe_id(ctx), 0))) + || (fpm && force_nhg)) { /* Kernel supports nexthop objects */ if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug("%s: %pFX nhg_id is %u", __func__, p, @@ -2073,6 +2101,16 @@ ssize_t netlink_nexthop_msg_encode(uint16_t cmd, char label_buf[256]; int num_labels = 0; + /* + * Nothing to do if the kernel doesn't support nexthop objects or + * we dont want to install this type of NHG + */ + if (!kernel_nexthops_supported() + || (proto_nexthops_only() + && !is_proto_nhg(dplane_ctx_get_nhe_id(ctx), + dplane_ctx_get_nhe_type(ctx)))) + return 0; + label_buf[0] = '\0'; if (buflen < sizeof(*req)) diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index 59df45420b..dbf5adafe1 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -53,6 +53,7 @@ uint32_t id_counter; /* */ static bool g_nexthops_enabled = true; +static bool proto_nexthops_only = false; static struct nhg_hash_entry *depends_find(const struct nexthop *nh, afi_t afi, int type); @@ -2661,6 +2662,24 @@ bool zebra_nhg_kernel_nexthops_enabled(void) return g_nexthops_enabled; } +/* + * Global control to only use kernel nexthops for protocol created NHGs. + * There are some use cases where you may not want zebra to implicitly + * create kernel nexthops for all routes and only create them for NHGs + * passed down by upper level protos. + * + * Default is off. + */ +void zebra_nhg_set_proto_nexthops_only(bool set) +{ + proto_nexthops_only = set; +} + +bool zebra_nhg_proto_nexthops_only(void) +{ + return proto_nexthops_only; +} + /* Add NHE from upper level proto */ struct nhg_hash_entry *zebra_nhg_proto_add(uint32_t id, int type, struct nexthop_group *nhg, afi_t afi) diff --git a/zebra/zebra_nhg.h b/zebra/zebra_nhg.h index 44d768648f..02858779b3 100644 --- a/zebra/zebra_nhg.h +++ b/zebra/zebra_nhg.h @@ -182,6 +182,10 @@ struct nhg_ctx { void zebra_nhg_enable_kernel_nexthops(bool set); bool zebra_nhg_kernel_nexthops_enabled(void); +/* Global control for zebra to only use proto-owned nexthops */ +void zebra_nhg_set_proto_nexthops_only(bool set); +bool zebra_nhg_proto_nexthops_only(void); + /** * NHE abstracted tree functions. * Use these where possible instead of direct access. diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index baf7d2c14d..7e73b3f724 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -1566,6 +1566,19 @@ DEFPY_HIDDEN(nexthop_group_use_enable, return CMD_SUCCESS; } +DEFPY_HIDDEN (proto_nexthop_group_only, + proto_nexthop_group_only_cmd, + "[no] zebra nexthop proto only", + NO_STR + ZEBRA_STR + "Nexthop configuration\n" + "Configure exclusive use of proto nexthops\n" + "Only use proto nexthops\n") +{ + zebra_nhg_set_proto_nexthops_only(!no); + return CMD_SUCCESS; +} + DEFUN (no_ip_nht_default_route, no_ip_nht_default_route_cmd, "no ip nht resolve-via-default", @@ -3448,6 +3461,9 @@ static int config_write_protocol(struct vty *vty) if (!zebra_nhg_kernel_nexthops_enabled()) vty_out(vty, "no zebra nexthop kernel enable\n"); + if (zebra_nhg_proto_nexthops_only()) + vty_out(vty, "zebra nexthop proto only\n"); + #ifdef HAVE_NETLINK /* Include netlink info */ netlink_config_write_helper(vty); @@ -3882,6 +3898,7 @@ void zebra_vty_init(void) install_element(CONFIG_NODE, &zebra_packet_process_cmd); install_element(CONFIG_NODE, &no_zebra_packet_process_cmd); install_element(CONFIG_NODE, &nexthop_group_use_enable_cmd); + install_element(CONFIG_NODE, &proto_nexthop_group_only_cmd); install_element(VIEW_NODE, &show_nexthop_group_cmd); install_element(VIEW_NODE, &show_interface_nexthop_group_cmd);