summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--zebra/if_netlink.c6
-rw-r--r--zebra/kernel_netlink.c8
-rw-r--r--zebra/rt_netlink.c288
-rw-r--r--zebra/rt_netlink.h4
-rw-r--r--zebra/zebra_errors.c9
-rw-r--r--zebra/zebra_errors.h1
-rw-r--r--zebra/zebra_nhg.c201
-rw-r--r--zebra/zebra_nhg.h16
-rw-r--r--zebra/zebra_rib.c6
-rw-r--r--zebra/zebra_router.c10
10 files changed, 490 insertions, 59 deletions
diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c
index b402cc4965..52d91007b4 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 7889c70cff..a81788028e 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 b36fbb2008..b30805b982 100644
--- a/zebra/rt_netlink.c
+++ b/zebra/rt_netlink.c
@@ -27,6 +27,7 @@
#include <linux/mpls_iptunnel.h>
#include <linux/neighbour.h>
#include <linux/rtnetlink.h>
+#include <linux/nexthop.h>
/* 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 29e0152bb2..2b4b145149 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 e14f1ee58c..4f97f3669f 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 44d61fc9b0..73bb53a773 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 172212c652..89f6691dab 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, &copy->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 227e87256d..126f342c54 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 c8684c1a93..3b3bf921bc 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 100d17ecde..408fc16dd1 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");
}