diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index b402cc496571..52d91007b409 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -66,6 +66,7 @@ #include "zebra/zebra_ptm.h" #include "zebra/zebra_mpls.h" #include "zebra/kernel_netlink.h" +#include "zebra/rt_netlink.h" #include "zebra/if_netlink.h" #include "zebra/zebra_errors.h" #include "zebra/zebra_vxlan.h" @@ -1477,6 +1478,11 @@ int netlink_protodown(struct interface *ifp, bool down) void interface_list(struct zebra_ns *zns) { interface_lookup_netlink(zns); + /* We add routes for interface address, + * so we need to get the nexthop info + * from the kernel before we can do that + */ + netlink_nexthop_read(zns); interface_addr_lookup_netlink(zns); } diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index 7889c70cff0e..a81788028e05 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -295,13 +295,9 @@ static int netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id, case RTM_DELRULE: return netlink_rule_change(h, ns_id, startup); case RTM_NEWNEXTHOP: + return netlink_nexthop_change(h, ns_id, startup); case RTM_DELNEXTHOP: - case RTM_GETNEXTHOP: - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("Got a nexthop: %s(%d) message!", - nl_msg_type_to_str(h->nlmsg_type), - h->nlmsg_type); - break; + return netlink_nexthop_change(h, ns_id, startup); default: /* * If we have received this message then diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index b36fbb200810..b30805b9820d 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -27,6 +27,7 @@ #include #include #include +#include /* Hack for GNU libc version 2. */ #ifndef MSG_TRUNC @@ -62,6 +63,7 @@ #include "zebra/zebra_mpls.h" #include "zebra/kernel_netlink.h" #include "zebra/rt_netlink.h" +#include "zebra/zebra_nhg.h" #include "zebra/zebra_mroute.h" #include "zebra/zebra_vxlan.h" #include "zebra/zebra_errors.h" @@ -1920,6 +1922,292 @@ enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx) ZEBRA_DPLANE_REQUEST_SUCCESS : ZEBRA_DPLANE_REQUEST_FAILURE); } +/** + * netlink_nexthop_process_nh() - Parse the gatway/if info from a new nexthop + * + * @tb: Netlink RTA data + * @family: Address family in the nhmsg + * @ns_id: Namspace id + * + * Return: New nexthop + */ +static struct nexthop netlink_nexthop_process_nh(struct rtattr **tb, + unsigned char family, + ns_id_t ns_id) +{ + struct nexthop nh = {0}; + void *gate = NULL; + struct interface *ifp = NULL; + int if_index; + size_t sz; + + if_index = *(int *)RTA_DATA(tb[NHA_OIF]); + + if (tb[NHA_GATEWAY]) { + switch (family) { + case AF_INET: + nh.type = NEXTHOP_TYPE_IPV4_IFINDEX; + sz = 4; + break; + case AF_INET6: + nh.type = NEXTHOP_TYPE_IPV6_IFINDEX; + sz = 16; + break; + default: + flog_warn( + EC_ZEBRA_BAD_NHG_MESSAGE, + "Nexthop with bad address family (%d) received from kernel", + family); + // TODO: Different return value? + return nh; + } + gate = RTA_DATA(tb[NHA_GATEWAY]); + memcpy(&(nh.gate), gate, sz); + } else { + nh.type = NEXTHOP_TYPE_IFINDEX; + } + + nh.ifindex = if_index; + + ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id), nh.ifindex); + if (ifp) { + nh.vrf_id = ifp->vrf_id; + } else { + flog_warn( + EC_ZEBRA_UNKNOWN_INTERFACE, + "%s: Unknown nexthop interface %u received, defaulting to VRF_DEFAULT", + __PRETTY_FUNCTION__, nh.ifindex); + + nh.vrf_id = VRF_DEFAULT; + } + + if (tb[NHA_ENCAP] && tb[NHA_ENCAP_TYPE]) { + uint16_t encap_type = *(uint16_t *)RTA_DATA(tb[NHA_ENCAP_TYPE]); + int num_labels = 0; + mpls_label_t labels[MPLS_MAX_LABELS] = {0}; + + if (encap_type == LWTUNNEL_ENCAP_MPLS) { + num_labels = parse_encap_mpls(tb[NHA_ENCAP], labels); + } + + if (num_labels) { + nexthop_add_labels(&nh, ZEBRA_LSP_STATIC, num_labels, + labels); + } + } + + return nh; +} + +/** + * netlink_nexthop_process_group() - Iterate over nhmsg nexthop group + * + * @tb: Netlink RTA data + * + * Return: TODO: Not sure yet + */ +static int netlink_nexthop_process_group(struct rtattr **tb) +{ + int count; + struct nexthop_grp *n_grp = NULL; + + n_grp = RTA_DATA(tb[NHA_GROUP]); + count = (RTA_PAYLOAD(tb[NHA_GROUP]) / sizeof(*n_grp)); + + if (!count || (count * sizeof(*n_grp)) != RTA_PAYLOAD(tb[NHA_GROUP])) { + flog_warn(EC_ZEBRA_BAD_NHG_MESSAGE, + "Invalid nexthop group received from the kernel"); + return -1; + } + + // TODO: Need type for something? + // zlog_debug("group type: %d", + // *((uint16_t *)RTA_DATA(tb[NHA_GROUP_TYPE]))); + + + for (int i = 0; i < count; i++) { + // TODO: Lookup by id and if we already have entries + // for that id, delete it? + + /* We do not care about nexthop_grp.weight at + * this time. But we should figure out + * how to adapt this to our code in + * the future. + */ + } + + return count; +} + +/** + * netlink_nexthop_change() - Read in change about nexthops from the kernel + * + * @h: Netlink message header + * @ns_id: Namspace id + * @startup: Are we reading under startup conditions? + * + * Return: Result status + */ +int netlink_nexthop_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) +{ + int len; + /* nexthop group id */ + uint32_t id; + unsigned char family; + struct nhmsg *nhm = NULL; + /* struct for nexthop group abstraction */ + struct nexthop_group nhg = {0}; + /* zebra's version of nexthops */ + struct nexthop nh = {0}; + /* struct that goes into our tables */ + struct nhg_hash_entry *nhe = NULL; + struct rtattr *tb[NHA_MAX + 1]; + + + nhm = NLMSG_DATA(h); + + if (startup && h->nlmsg_type != RTM_NEWNEXTHOP) + return 0; + + len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct nhmsg)); + if (len < 0) { + zlog_warn( + "%s: Message received from netlink is of a broken size %d %zu", + __PRETTY_FUNCTION__, h->nlmsg_len, + (size_t)NLMSG_LENGTH(sizeof(struct nhmsg))); + return -1; + } + + memset(tb, 0, sizeof(tb)); + netlink_parse_rtattr(tb, NHA_MAX, RTM_NHA(nhm), len); + + + if (!tb[NHA_ID]) { + flog_warn( + EC_ZEBRA_BAD_NHG_MESSAGE, + "Nexthop group without an ID received from the kernel"); + return -1; + } + + /* We use the ID key'd nhg table for kernel updates */ + id = *((uint32_t *)RTA_DATA(tb[NHA_ID])); + family = nhm->nh_family; + + if (IS_ZEBRA_DEBUG_KERNEL) { + zlog_debug("Nexthop ID (%u) update from the kernel", id); + } + + /* Lookup via the id */ + nhe = zebra_nhg_lookup_id(id); + + if (h->nlmsg_type == RTM_NEWNEXTHOP) { + if (tb[NHA_GROUP]) { + /** + * If this is a group message its only going to have + * an array of nexthop IDs associated with it + */ + return -1; + netlink_nexthop_process_group(tb); + } else if (tb[NHA_BLACKHOLE]) { + /** + * This nexthop is just for blackhole-ing traffic, + * it should not have an OIF, GATEWAY, or ENCAP + */ + nh.type = NEXTHOP_TYPE_BLACKHOLE; + // TODO: Handle blackhole case + nh.bh_type = BLACKHOLE_UNSPEC; + } else if (tb[NHA_OIF]) { + /** + * This is a true new nexthop, so we need + * to parse the gateway and device info + */ + nh = netlink_nexthop_process_nh(tb, family, ns_id); + } + + + nexthop_group_add_sorted(&nhg, &nh); + + if (nhe) { + /* This is a change to a group we already have */ + nexthops_free(nhe->nhg.nexthop); + nhe->nhg.nexthop = NULL; + nexthop_group_copy(&nhe->nhg, &nhg); + } else { + /* This is a new nexthop group */ + nhe = zebra_nhg_find(&nhg, nh.vrf_id, id); + if (nhe) { + nhe->is_kernel_nh = true; + } else { + return -1; + } + } + } else if (h->nlmsg_type == RTM_DELNEXTHOP) { + if (!nhe) { + flog_warn( + EC_ZEBRA_BAD_NHG_MESSAGE, + "Kernel delete message received for nexthop group ID (%u) that we do not have in our ID table", + id); + return -1; + } + + // TODO: Run some active check on all route_entry's? + zebra_nhg_release(nhe); + } + + + return 0; +} + +/** + * netlink_request_nexthop() - Request nextop information from the kernel + * @zns: Zebra namespace + * @family: AF_* netlink family + * @type: RTM_* route type + * + * Return: Result status + */ +static int netlink_request_nexthop(struct zebra_ns *zns, int family, int type) +{ + struct { + struct nlmsghdr n; + struct nhmsg nhm; + } req; + + /* Form the request, specifying filter (rtattr) if needed. */ + memset(&req, 0, sizeof(req)); + req.n.nlmsg_type = type; + req.n.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST; + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg)); + req.nhm.nh_family = family; + + return netlink_request(&zns->netlink_cmd, &req.n); +} + +/** + * netlink_nexthop_read() - Nexthop read function using netlink interface + * + * @zns: Zebra name space + * + * Return: Result status + * Only called at bootstrap time. + */ +int netlink_nexthop_read(struct zebra_ns *zns) +{ + int ret; + struct zebra_dplane_info dp_info; + + zebra_dplane_info_from_zns(&dp_info, zns, true /*is_cmd*/); + + /* Get nexthop objects */ + ret = netlink_request_nexthop(zns, AF_UNSPEC, RTM_GETNEXTHOP); + if (ret < 0) + return ret; + ret = netlink_parse_info(netlink_nexthop_change, &zns->netlink_cmd, + &dp_info, 0, 1); + return 0; +} + + int kernel_neigh_update(int add, int ifindex, uint32_t addr, char *lla, int llalen, ns_id_t ns_id) { diff --git a/zebra/rt_netlink.h b/zebra/rt_netlink.h index 29e0152bb2c9..2b4b145149c8 100644 --- a/zebra/rt_netlink.h +++ b/zebra/rt_netlink.h @@ -69,6 +69,10 @@ extern int netlink_mpls_multipath(int cmd, struct zebra_dplane_ctx *ctx); extern int netlink_route_change(struct nlmsghdr *h, ns_id_t ns_id, int startup); extern int netlink_route_read(struct zebra_ns *zns); +extern int netlink_nexthop_change(struct nlmsghdr *h, ns_id_t ns_id, + int startup); +extern int netlink_nexthop_read(struct zebra_ns *zns); + extern int netlink_neigh_change(struct nlmsghdr *h, ns_id_t ns_id); extern int netlink_macfdb_read(struct zebra_ns *zns); extern int netlink_macfdb_read_for_bridge(struct zebra_ns *zns, diff --git a/zebra/zebra_errors.c b/zebra/zebra_errors.c index e14f1ee58c37..4f97f3669f0a 100644 --- a/zebra/zebra_errors.c +++ b/zebra/zebra_errors.c @@ -292,6 +292,15 @@ static struct log_ref ferr_zebra_err[] = { .suggestion = "Check to see if the entry already exists or if the netlink message was parsed incorrectly." }, + { + .code = EC_ZEBRA_NHG_SYNC, + .title = + "Zebra's Nexthop Groups are out of sync", + .description = + "Zebra's nexthop group tables are out of sync with the nexthop groups in the fib.", + .suggestion = + "Check the current status of the kernels nexthop groups and compare it to Zebra's." + }, /* Warnings */ { .code = EC_ZEBRAING_LM_PROTO_MISMATCH, diff --git a/zebra/zebra_errors.h b/zebra/zebra_errors.h index 44d61fc9b0d5..73bb53a7737a 100644 --- a/zebra/zebra_errors.h +++ b/zebra/zebra_errors.h @@ -73,6 +73,7 @@ enum zebra_log_refs { EC_ZEBRA_VTEP_ADD_FAILED, EC_ZEBRA_VNI_ADD_FAILED, EC_ZEBRA_NHG_TABLE_INSERT_FAILED, + EC_ZEBRA_NHG_SYNC, /* warnings */ EC_ZEBRA_NS_NOTIFY_READ, EC_ZEBRAING_LM_PROTO_MISMATCH, diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index 172212c65268..89f6691dab0e 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -35,23 +35,86 @@ #include "zebra/zebra_rnh.h" #include "zebra/zebra_routemap.h" #include "zebra/rt.h" +#include "zebra_errors.h" + +/** + * zebra_nhg_lookup_id() - Lookup the nexthop group id in the id table + * + * @id: ID to look for + * + * Return: Nexthop hash entry if found/NULL if not found + */ +struct nhg_hash_entry *zebra_nhg_lookup_id(uint32_t id) +{ + struct nhg_hash_entry lookup = {0}; + + lookup.id = id; + return hash_lookup(zrouter.nhgs_id, &lookup); +} + +/** + * zebra_nhg_insert_id() - Insert a nhe into the id hashed table + * + * @nhe: The entry directly from the other table + * + * Return: Result status + */ +int zebra_nhg_insert_id(struct nhg_hash_entry *nhe) +{ + if (hash_lookup(zrouter.nhgs_id, nhe)) { + flog_err( + EC_ZEBRA_NHG_TABLE_INSERT_FAILED, + "Failed inserting NHG id=%u into the ID hash table, entry already exists", + nhe->id); + return -1; + } + + hash_get(zrouter.nhgs_id, nhe, hash_alloc_intern); + + return 0; +} static void *zebra_nhg_alloc(void *arg) { + /* lock for getiing and setting the id */ + static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; + /* id counter to keep in sync with kernel */ + static uint32_t id_counter = 0; struct nhg_hash_entry *nhe; struct nhg_hash_entry *copy = arg; - nhe = XMALLOC(MTYPE_TMP, sizeof(struct nhg_hash_entry)); + nhe = XCALLOC(MTYPE_TMP, sizeof(struct nhg_hash_entry)); + + pthread_mutex_lock(&lock); /* Lock, set the id counter from kernel */ + if (copy->id) { + /* This is from the kernel if it has an id */ + if (copy->id > id_counter) { + /* Increase our counter so we don't try to create + * an ID that already exists + */ + id_counter = copy->id; + } + nhe->id = copy->id; + /* Mark as valid since from the kernel */ + SET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED); + SET_FLAG(nhe->flags, NEXTHOP_GROUP_VALID); + } else { + nhe->id = ++id_counter; + } + pthread_mutex_unlock(&lock); nhe->vrf_id = copy->vrf_id; nhe->refcnt = 0; + nhe->is_kernel_nh = false; nhe->dplane_ref = zebra_router_get_next_sequence(); nhe->nhg.nexthop = NULL; nexthop_group_copy(&nhe->nhg, ©->nhg); - nhe->refcnt = 1; + /* Add to id table as well */ + zebra_nhg_insert_id(nhe); + return nhe; } @@ -95,11 +158,15 @@ static uint32_t zebra_nhg_hash_key_nexthop_group(struct nexthop_group *nhg) uint32_t zebra_nhg_hash_key(const void *arg) { const struct nhg_hash_entry *nhe = arg; + int key = 0x5a351234; key = jhash_1word(nhe->vrf_id, key); - return jhash_1word(zebra_nhg_hash_key_nexthop_group(&nhe->nhg), key); + key = jhash_1word(zebra_nhg_hash_key_nexthop_group(&nhe->nhg), key); + + + return key; } uint32_t zebra_nhg_id_key(const void *arg) @@ -109,14 +176,6 @@ uint32_t zebra_nhg_id_key(const void *arg) return nhe->id; } -bool zebra_nhg_id_equal(const void *arg1, const void *arg2) -{ - const struct nhg_hash_entry *nhe1 = arg1; - const struct nhg_hash_entry *nhe2 = arg2; - - return (nhe1->id == nhe2->id); -} - bool zebra_nhg_hash_equal(const void *arg1, const void *arg2) { const struct nhg_hash_entry *nhe1 = arg1; @@ -149,60 +208,116 @@ bool zebra_nhg_hash_equal(const void *arg1, const void *arg2) return true; } -/** - * Helper function for lookup and get() - * since we are using two different tables. - * - * Avoiding code duplication hopefully. - */ -static struct nhg_hash_entry * -zebra_nhg_lookup_get(struct hash *hash_table, - struct nhg_hash_entry *lookup) +bool zebra_nhg_hash_id_equal(const void *arg1, const void *arg2) { - struct nhg_hash_entry *nhe; - - nhe = hash_lookup(hash_table, lookup); + const struct nhg_hash_entry *nhe1 = arg1; + const struct nhg_hash_entry *nhe2 = arg2; - if (!nhe) - nhe = hash_get(hash_table, lookup, zebra_nhg_alloc); - else - nhe->refcnt++; + return nhe1->id == nhe2->id; +} - return nhe; +struct nhg_hash_entry *zebra_nhg_find_id(uint32_t id, struct nexthop_group *nhg) +{ + // TODO: How this will work is yet to be determined + return NULL; } -void zebra_nhg_find_id(uint32_t id, struct nexthop_group *nhg) +/** + * zebra_nhg_find() - Find the zebra nhg in our table, or create it + * + * @nhg: Nexthop group we lookup with + * @vrf_id: VRF id + * @id: ID we lookup with, 0 means its from us and we need to give it + * an ID, otherwise its from the kernel as we use the ID it gave + * us. + * + * Return: Hash entry found or created + */ +struct nhg_hash_entry *zebra_nhg_find(struct nexthop_group *nhg, + vrf_id_t vrf_id, uint32_t id) { struct nhg_hash_entry lookup = {0}; + struct nhg_hash_entry *nhe = NULL; + lookup.id = id; + lookup.vrf_id = vrf_id; lookup.nhg = *nhg; - zebra_nhg_lookup_get(zrouter.nhgs_id, &lookup); + + nhe = hash_lookup(zrouter.nhgs, &lookup); + + if (!nhe) { + nhe = hash_get(zrouter.nhgs, &lookup, zebra_nhg_alloc); + } else { + if (id) { + /* Duplicate but with different ID from the kernel */ + + /* The kernel allows duplicate nexthops as long as they + * have different IDs. We are ignoring those to prevent + * syncing problems with the kernel changes. + */ + flog_warn( + EC_ZEBRA_DUPLICATE_NHG_MESSAGE, + "Nexthop Group from kernel with ID (%d) is a duplicate, ignoring", + id); + return NULL; + } + } + + return nhe; } -void zebra_nhg_find(struct nexthop_group *nhg, struct route_entry *re) +/** + * zebra_nhg_free() - Free the nexthop group hash entry + * + * arg: Nexthop group entry to free + */ +void zebra_nhg_free(void *arg) { - struct nhg_hash_entry lookup; + struct nhg_hash_entry *nhe = NULL; - memset(&lookup, 0, sizeof(lookup)); - lookup.vrf_id = re->vrf_id; - lookup.nhg = *nhg; + nhe = (struct nhg_hash_entry *)arg; - re->nhe = zebra_nhg_lookup_get(zrouter.nhgs, &lookup); + nexthops_free(nhe->nhg.nexthop); + XFREE(MTYPE_TMP, nhe); } -void zebra_nhg_release(struct route_entry *re) +/** + * zebra_nhg_release() - Release a nhe from the tables + * + * @nhe: Nexthop group hash entry + */ +void zebra_nhg_release(struct nhg_hash_entry *nhe) { - struct nhg_hash_entry lookup, *nhe; + if (nhe->refcnt) { + flog_err( + EC_ZEBRA_NHG_SYNC, + "Kernel deleted a nexthop group with ID (%u) that we are still using for a route", + nhe->id); + // TODO: Re-send to kernel + } - lookup.vrf_id = re->vrf_id; - lookup.nhg = *re->ng; + hash_release(zrouter.nhgs, nhe); + hash_release(zrouter.nhgs_id, nhe); + zebra_nhg_free(nhe); +} - nhe = hash_lookup(zrouter.nhgs, &lookup); +/** + * zebra_nhg_decrement_ref() - Decrement the reference count, release if unused + * + * @nhe: Nexthop group hash entry + * + * If the counter hits 0 and is not a nexthop group that was created by the + * kernel, we don't need to have it in our table anymore. + */ +void zebra_nhg_decrement_ref(struct nhg_hash_entry *nhe) +{ nhe->refcnt--; - if (nhe->refcnt == 0) - hash_release(zrouter.nhgs, nhe); + if (!nhe->is_kernel_nh && nhe->refcnt <= 0) { + zebra_nhg_release(nhe); + } + // re->ng = NULL; } diff --git a/zebra/zebra_nhg.h b/zebra/zebra_nhg.h index 227e87256d34..126f342c5448 100644 --- a/zebra/zebra_nhg.h +++ b/zebra/zebra_nhg.h @@ -29,6 +29,7 @@ struct nhg_hash_entry { uint32_t id; vrf_id_t vrf_id; + bool is_kernel_nh; struct nexthop_group nhg; @@ -54,15 +55,22 @@ struct nhg_hash_entry { void zebra_nhg_init(void); void zebra_nhg_terminate(void); +extern struct nhg_hash_entry *zebra_nhg_lookup_id(uint32_t id); +extern int zebra_nhg_insert_id(struct nhg_hash_entry *nhe); + extern uint32_t zebra_nhg_hash_key(const void *arg); extern uint32_t zebra_nhg_id_key(const void *arg); extern bool zebra_nhg_hash_equal(const void *arg1, const void *arg2); -extern bool zebra_nhg_id_equal(const void *arg1, const void *arg2); +extern bool zebra_nhg_hash_id_equal(const void *arg1, const void *arg2); -extern void zebra_nhg_find(struct nexthop_group *nhg, struct route_entry *re); -extern void zebra_nhg_find_id(uint32_t id, struct nexthop_group *nhg); -void zebra_nhg_release(struct route_entry *re); +extern struct nhg_hash_entry *zebra_nhg_find(struct nexthop_group *nhg, + vrf_id_t vrf_id, uint32_t id); +extern struct nhg_hash_entry *zebra_nhg_find_id(uint32_t id, + struct nexthop_group *nhg); +void zebra_nhg_free(void *arg); +void zebra_nhg_release(struct nhg_hash_entry *nhe); +void zebra_nhg_decrement_ref(struct nhg_hash_entry *nhe); extern int nexthop_active_update(struct route_node *rn, struct route_entry *re); #endif diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index c8684c1a9318..3b3bf921bc80 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -2411,8 +2411,9 @@ void rib_unlink(struct route_node *rn, struct route_entry *re) if (dest->selected_fib == re) dest->selected_fib = NULL; - zebra_nhg_release(re); + zebra_nhg_decrement_ref(re->nhe); + // TODO: We need to hold on nh's until refcnt is 0 right? nexthops_free(re->ng->nexthop); nexthop_group_delete(&re->ng); nexthops_free(re->fib_ng.nexthop); @@ -2658,7 +2659,8 @@ int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p, if (src_p) apply_mask_ipv6(src_p); - zebra_nhg_find(re->ng, re); + re->nhe = zebra_nhg_find(re->ng, re->vrf_id, 0); + re->nhe->refcnt++; /* Set default distance by route type. */ if (re->distance == 0) re->distance = route_distance(re->type); diff --git a/zebra/zebra_router.c b/zebra/zebra_router.c index 100d17ecded8..408fc16dd16f 100644 --- a/zebra/zebra_router.c +++ b/zebra/zebra_router.c @@ -218,6 +218,11 @@ void zebra_router_terminate(void) zebra_vxlan_disable(); zebra_mlag_terminate(); + hash_clean(zrouter.nhgs, zebra_nhg_free); + hash_free(zrouter.nhgs); + hash_clean(zrouter.nhgs_id, NULL); + hash_free(zrouter.nhgs_id); + hash_clean(zrouter.rules_hash, zebra_pbr_rules_free); hash_free(zrouter.rules_hash); @@ -254,13 +259,10 @@ void zebra_router_init(void) zebra_pbr_iptable_hash_equal, "IPtable Hash Entry"); - /* Index via hash and IDs so we can - * easily communicate to/from the kernel - */ zrouter.nhgs = hash_create_size(8, zebra_nhg_hash_key, zebra_nhg_hash_equal, "Zebra Router Nexthop Groups"); zrouter.nhgs_id = - hash_create_size(8, zebra_nhg_id_key, zebra_nhg_id_equal, + hash_create_size(8, zebra_nhg_id_key, zebra_nhg_hash_id_equal, "Zebra Router Nexthop Groups ID index"); }