+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Zebra connect code.
* Copyright (C) 2018 Cumulus Networks, Inc.
* Donald Sharp
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; see the file COPYING; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <zebra.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;
-static struct hash *static_nht_hash;
uint32_t zebra_ecmp_count = MULTIPATH_NUM;
-/* Inteface addition message from zebra. */
+/* Interface addition message from zebra. */
static int static_ifp_create(struct interface *ifp)
{
static_ifindex_update(ifp, true);
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, NULL))
+ 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, vrf_id, STATIC_NOT_INSTALLED);
+ 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, vrf_id, STATIC_NOT_INSTALLED);
+ 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, vrf_id, STATIC_INSTALLED);
+ static_nht_mark_state(&p, safi, vrf_id, STATIC_INSTALLED);
break;
case ZAPI_ROUTE_REMOVED:
- static_nht_mark_state(&p, vrf_id, STATIC_NOT_INSTALLED);
+ static_nht_mark_state(&p, safi, vrf_id, STATIC_NOT_INSTALLED);
break;
case ZAPI_ROUTE_REMOVE_FAIL:
- static_nht_mark_state(&p, vrf_id, STATIC_INSTALLED);
+ 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);
-}
-
-struct static_nht_data {
- struct prefix *nh;
- vrf_id_t nh_vrf_id;
-
- uint32_t refcount;
- uint8_t nh_num;
-};
+ 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.
return 1;
}
+ if (zclient->bfd_integration)
+ bfd_nht_update(&matched, &nhr);
+
if (matched.family == AF_INET6)
afi = AFI_IP6;
}
memset(&lookup, 0, sizeof(lookup));
- lookup.nh = &matched;
+ lookup.nh = matched;
lookup.nh_vrf_id = vrf_id;
+ lookup.safi = nhr.safi;
- nhtd = hash_lookup(static_nht_hash, &lookup);
+ nhtd = static_nht_hash_find(static_nht_hash, &lookup);
if (nhtd) {
nhtd->nh_num = nhr.nexthop_num;
- static_nht_reset_start(&matched, afi, nhtd->nh_vrf_id);
+ static_nht_reset_start(&matched, afi, nhr.safi,
+ nhtd->nh_vrf_id);
static_nht_update(NULL, &matched, nhr.nexthop_num, afi,
- nhtd->nh_vrf_id);
+ nhr.safi, nhtd->nh_vrf_id);
} else
zlog_err("No nhtd?");
zebra_ecmp_count = cap->ecmp;
}
-static unsigned int static_nht_hash_key(const void *data)
+static struct static_nht_data *
+static_nht_hash_getref(const struct static_nht_data *ref)
{
- const struct static_nht_data *nhtd = data;
- unsigned int key = 0;
+ struct static_nht_data *nhtd;
- key = prefix_hash_key(nhtd->nh);
- return jhash_1word(nhtd->nh_vrf_id, key);
-}
+ nhtd = static_nht_hash_find(static_nht_hash, ref);
+ if (!nhtd) {
+ nhtd = XCALLOC(MTYPE_STATIC_NHT_DATA, sizeof(*nhtd));
-static bool static_nht_hash_cmp(const void *d1, const void *d2)
-{
- const struct static_nht_data *nhtd1 = d1;
- const struct static_nht_data *nhtd2 = d2;
+ prefix_copy(&nhtd->nh, &ref->nh);
+ nhtd->nh_vrf_id = ref->nh_vrf_id;
+ nhtd->safi = ref->safi;
- if (nhtd1->nh_vrf_id != nhtd2->nh_vrf_id)
- return false;
+ static_nht_hash_add(static_nht_hash, nhtd);
+ }
- return prefix_same(nhtd1->nh, nhtd2->nh);
+ nhtd->refcount++;
+ return nhtd;
}
-static void *static_nht_hash_alloc(void *data)
+static bool static_nht_hash_decref(struct static_nht_data **nhtd_p)
{
- struct static_nht_data *copy = data;
- struct static_nht_data *new;
+ struct static_nht_data *nhtd = *nhtd_p;
- new = XMALLOC(MTYPE_TMP, sizeof(*new));
+ *nhtd_p = NULL;
- new->nh = prefix_new();
- prefix_copy(new->nh, copy->nh);
- new->refcount = 0;
- new->nh_num = 0;
- new->nh_vrf_id = copy->nh_vrf_id;
+ if (--nhtd->refcount > 0)
+ return true;
- return new;
+ static_nht_hash_del(static_nht_hash, nhtd);
+ XFREE(MTYPE_STATIC_NHT_DATA, nhtd);
+ return false;
}
-static void static_nht_hash_free(void *data)
+static void static_nht_hash_clear(void)
{
- struct static_nht_data *nhtd = data;
+ struct static_nht_data *nhtd;
- prefix_free(&nhtd->nh);
- XFREE(MTYPE_TMP, nhtd);
+ while ((nhtd = static_nht_hash_pop(static_nht_hash)))
+ XFREE(MTYPE_STATIC_NHT_DATA, nhtd);
}
-void static_zebra_nht_register(struct static_nexthop *nh, bool reg)
+static bool static_zebra_nht_get_prefix(const struct static_nexthop *nh,
+ struct prefix *p)
{
- struct static_path *pn = nh->pn;
- struct route_node *rn = pn->rn;
- struct static_nht_data *nhtd, lookup;
- uint32_t cmd;
- struct prefix p;
- afi_t afi = AFI_IP;
-
- cmd = (reg) ?
- ZEBRA_NEXTHOP_REGISTER : ZEBRA_NEXTHOP_UNREGISTER;
-
- if (nh->nh_registered && reg)
- return;
-
- if (!nh->nh_registered && !reg)
- return;
-
- memset(&p, 0, sizeof(p));
switch (nh->type) {
case STATIC_IFNAME:
case STATIC_BLACKHOLE:
- return;
+ 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;
- afi = AFI_IP;
- break;
+ 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;
- afi = AFI_IP6;
- break;
+ p->family = AF_INET6;
+ p->prefixlen = IPV6_MAX_BITLEN;
+ p->u.prefix6 = nh->addr.ipv6;
+ return true;
}
- memset(&lookup, 0, sizeof(lookup));
- lookup.nh = &p;
+ 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;
- nh->nh_registered = reg;
+ if (nh->nh_registered) {
+ /* nh->nh_registered means we own a reference on the nhtd */
+ nhtd = static_nht_hash_find(static_nht_hash, &lookup);
- if (reg) {
- nhtd = hash_get(static_nht_hash, &lookup,
- static_nht_hash_alloc);
- nhtd->refcount++;
+ 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) {
+ if (nhtd->refcount > 1)
DEBUGD(&static_dbg_route,
- "Already registered nexthop(%pFX) for %pRN %d",
- &p, rn, nhtd->nh_num);
- if (nhtd->nh_num)
- static_nht_update(&rn->p, nhtd->nh,
- nhtd->nh_num, afi,
- nh->nh_vrf_id);
- return;
- }
+ "Reusing registered nexthop(%pFX) for %pRN %d",
+ &lookup.nh, rn, nhtd->nh_num);
} else {
- nhtd = hash_lookup(static_nht_hash, &lookup);
- if (!nhtd)
- return;
+ /* !reg && !nh->nh_registered */
+ zlog_warn("trying to unregister nexthop %pFX twice",
+ &lookup.nh);
+ return;
+ }
+
+ nh->nh_registered = reg;
- nhtd->refcount--;
- if (nhtd->refcount >= 1)
+ 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;
+ }
- hash_release(static_nht_hash, nhtd);
- static_nht_hash_free(nhtd);
- }
+ if (nhtd->registered)
+ /* have no data, but did send register */
+ return;
- DEBUGD(&static_dbg_route, "%s nexthop(%pFX) for %pRN",
- reg ? "Registering" : "Unregistering", &p, rn);
+ cmd = ZEBRA_NEXTHOP_REGISTER;
+ DEBUGD(&static_dbg_route, "Registering nexthop(%pFX) for %pRN",
+ &lookup.nh, rn);
+ } else {
+ bool was_zebra_registered;
- if (zclient_send_rnh(zclient, cmd, &p, SAFI_UNICAST, false, false,
- nh->nh_vrf_id) == ZCLIENT_SEND_FAILURE)
- zlog_warn("%s: Failure to send nexthop to zebra", __func__);
-}
-/*
- * When nexthop gets updated via configuration then use the
- * already registered NH and resend the route to zebra
- */
-int static_zebra_nh_update(struct static_nexthop *nh)
-{
- struct static_path *pn = nh->pn;
- struct route_node *rn = pn->rn;
- struct static_nht_data *nhtd, lookup = {};
- struct prefix p = {};
- afi_t afi = AFI_IP;
+ was_zebra_registered = nhtd->registered;
+ if (static_nht_hash_decref(&nhtd))
+ /* still got references alive */
+ return;
- if (!nh->nh_registered)
- return 0;
+ /* NB: nhtd is now NULL. */
+ if (!was_zebra_registered)
+ return;
- switch (nh->type) {
- case STATIC_IFNAME:
- case STATIC_BLACKHOLE:
- return 0;
- case STATIC_IPV4_GATEWAY:
- case STATIC_IPV4_GATEWAY_IFNAME:
- p.family = AF_INET;
- p.prefixlen = IPV4_MAX_BITLEN;
- p.u.prefix4 = nh->addr.ipv4;
- afi = AFI_IP;
- break;
- case STATIC_IPV6_GATEWAY:
- case STATIC_IPV6_GATEWAY_IFNAME:
- p.family = AF_INET6;
- p.prefixlen = IPV6_MAX_BITLEN;
- p.u.prefix6 = nh->addr.ipv6;
- afi = AFI_IP6;
- break;
+ cmd = ZEBRA_NEXTHOP_UNREGISTER;
+ DEBUGD(&static_dbg_route,
+ "Unregistering nexthop(%pFX) for %pRN", &lookup.nh, rn);
}
- lookup.nh = &p;
- lookup.nh_vrf_id = nh->nh_vrf_id;
-
- nhtd = hash_lookup(static_nht_hash, &lookup);
- if (nhtd && nhtd->nh_num) {
- nh->state = STATIC_START;
- static_nht_update(&rn->p, nhtd->nh, nhtd->nh_num, afi,
- nh->nh_vrf_id);
- return 1;
- }
- return 0;
+ 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)
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)
zclient->zebra_capabilities = static_zebra_capabilities;
zclient->zebra_connected = zebra_connected;
- static_nht_hash = hash_create(static_nht_hash_key,
- static_nht_hash_cmp,
- "Static Nexthop Tracking hash");
+ 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);