]> git.proxmox.com Git - mirror_frr.git/blobdiff - staticd/static_zebra.c
*: auto-convert to SPDX License IDs
[mirror_frr.git] / staticd / static_zebra.c
index ea17435b23781e3b086096edcc88d2a47af74134..f220b476fbbddbfae03a824813a03e48da188655 100644 (file)
@@ -1,21 +1,8 @@
+// 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);
@@ -104,31 +130,32 @@ static int route_notify_owner(ZAPI_CALLBACK_ARGS)
        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, &note,
-                                     NULL, NULL))
+       if (!zapi_route_notify_decode(zclient->ibuf, &p, &table_id, &note, 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;
@@ -136,19 +163,13 @@ static int route_notify_owner(ZAPI_CALLBACK_ARGS)
 
        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.
@@ -177,6 +198,9 @@ static int static_zebra_nexthop_update(ZAPI_CALLBACK_ARGS)
                return 1;
        }
 
+       if (zclient->bfd_integration)
+               bfd_nht_update(&matched, &nhr);
+
        if (matched.family == AF_INET6)
                afi = AFI_IP6;
 
@@ -187,17 +211,19 @@ static int static_zebra_nexthop_update(ZAPI_CALLBACK_ARGS)
        }
 
        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?");
 
@@ -210,176 +236,153 @@ static void static_zebra_capabilities(struct zclient_capabilities *cap)
        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)
@@ -428,6 +431,9 @@ 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)
@@ -531,14 +537,16 @@ void static_zebra_init(void)
        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);