From d9f5b2f50f53d625986dbd47cd12778c9f841f0c Mon Sep 17 00:00:00 2001 From: Stephen Worley Date: Mon, 25 Feb 2019 18:18:07 -0500 Subject: [PATCH] zebra: Add functionality to parse RTM_NEWNEXTHOP and RTM_DELNEXTHOP messages Add the functionality to parse new nexthop group messages from the kernel and insert them into the appropriate hash tables. Parsing is done at startup between interface and interface address lookup. Add functionality to parse changes to nexthops we already have. Add functionality to parse delete nexthop messages from the kernel and remove them from our table. Signed-off-by: Stephen Worley --- zebra/if_netlink.c | 6 + zebra/kernel_netlink.c | 8 +- zebra/rt_netlink.c | 288 +++++++++++++++++++++++++++++++++++++++++ zebra/rt_netlink.h | 4 + zebra/zebra_errors.c | 9 ++ zebra/zebra_errors.h | 1 + zebra/zebra_nhg.c | 201 ++++++++++++++++++++++------ zebra/zebra_nhg.h | 16 ++- zebra/zebra_rib.c | 6 +- zebra/zebra_router.c | 10 +- 10 files changed, 490 insertions(+), 59 deletions(-) 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"); }