summaryrefslogtreecommitdiffstats
path: root/staticd/static_zebra.c
diff options
context:
space:
mode:
Diffstat (limited to 'staticd/static_zebra.c')
-rw-r--r--staticd/static_zebra.c584
1 files changed, 584 insertions, 0 deletions
diff --git a/staticd/static_zebra.c b/staticd/static_zebra.c
new file mode 100644
index 0000000..6abbdad
--- /dev/null
+++ b/staticd/static_zebra.c
@@ -0,0 +1,584 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Zebra connect code.
+ * Copyright (C) 2018 Cumulus Networks, Inc.
+ * Donald Sharp
+ */
+#include <zebra.h>
+
+#include "frrevent.h"
+#include "command.h"
+#include "network.h"
+#include "prefix.h"
+#include "routemap.h"
+#include "table.h"
+#include "srcdest_table.h"
+#include "stream.h"
+#include "memory.h"
+#include "zclient.h"
+#include "filter.h"
+#include "plist.h"
+#include "log.h"
+#include "nexthop.h"
+#include "nexthop_group.h"
+#include "hash.h"
+#include "jhash.h"
+
+#include "static_vrf.h"
+#include "static_routes.h"
+#include "static_zebra.h"
+#include "static_nht.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;
+uint32_t zebra_ecmp_count = MULTIPATH_NUM;
+
+/* Interface addition message from zebra. */
+static int static_ifp_create(struct interface *ifp)
+{
+ static_ifindex_update(ifp, true);
+
+ return 0;
+}
+
+static int static_ifp_destroy(struct interface *ifp)
+{
+ static_ifindex_update(ifp, false);
+ return 0;
+}
+
+static int interface_address_add(ZAPI_CALLBACK_ARGS)
+{
+ zebra_interface_address_read(cmd, zclient->ibuf, vrf_id);
+
+ return 0;
+}
+
+static int interface_address_delete(ZAPI_CALLBACK_ARGS)
+{
+ struct connected *c;
+
+ c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id);
+
+ if (!c)
+ return 0;
+
+ connected_free(&c);
+ return 0;
+}
+
+static int static_ifp_up(struct interface *ifp)
+{
+ /* Install any static reliant on this interface coming up */
+ static_install_intf_nh(ifp);
+ static_ifindex_update(ifp, true);
+
+ return 0;
+}
+
+static int static_ifp_down(struct interface *ifp)
+{
+ static_ifindex_update(ifp, false);
+
+ return 0;
+}
+
+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,
+ &safi))
+ return -1;
+
+ switch (note) {
+ case ZAPI_ROUTE_FAIL_INSTALL:
+ 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, 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, safi, vrf_id, STATIC_INSTALLED);
+ break;
+ case ZAPI_ROUTE_REMOVED:
+ static_nht_mark_state(&p, safi, vrf_id, STATIC_NOT_INSTALLED);
+ break;
+ case ZAPI_ROUTE_REMOVE_FAIL:
+ 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);
+
+ 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.
+ */
+static bool
+static_nexthop_is_local(vrf_id_t vrfid, struct prefix *addr, int family)
+{
+ if (family == AF_INET) {
+ if (if_address_is_local(&addr->u.prefix4, AF_INET, vrfid))
+ return true;
+ } else if (family == AF_INET6) {
+ if (if_address_is_local(&addr->u.prefix6, AF_INET6, vrfid))
+ return true;
+ }
+ return false;
+}
+static int static_zebra_nexthop_update(ZAPI_CALLBACK_ARGS)
+{
+ struct static_nht_data *nhtd, lookup;
+ struct zapi_route nhr;
+ struct prefix matched;
+ afi_t afi = AFI_IP;
+
+ if (!zapi_nexthop_update_decode(zclient->ibuf, &matched, &nhr)) {
+ zlog_err("Failure to decode nexthop update message");
+ return 1;
+ }
+
+ if (zclient->bfd_integration)
+ bfd_nht_update(&matched, &nhr);
+
+ if (matched.family == AF_INET6)
+ afi = AFI_IP6;
+
+ if (nhr.type == ZEBRA_ROUTE_CONNECT) {
+ if (static_nexthop_is_local(vrf_id, &matched,
+ nhr.prefix.family))
+ nhr.nexthop_num = 0;
+ }
+
+ memset(&lookup, 0, sizeof(lookup));
+ lookup.nh = matched;
+ lookup.nh_vrf_id = vrf_id;
+ lookup.safi = nhr.safi;
+
+ nhtd = static_nht_hash_find(static_nht_hash, &lookup);
+
+ if (nhtd) {
+ nhtd->nh_num = nhr.nexthop_num;
+
+ static_nht_reset_start(&matched, afi, nhr.safi,
+ nhtd->nh_vrf_id);
+ static_nht_update(NULL, &matched, nhr.nexthop_num, afi,
+ nhr.safi, nhtd->nh_vrf_id);
+ } else
+ zlog_err("No nhtd?");
+
+ return 1;
+}
+
+static void static_zebra_capabilities(struct zclient_capabilities *cap)
+{
+ mpls_enabled = cap->mpls_enabled;
+ zebra_ecmp_count = cap->ecmp;
+}
+
+static struct static_nht_data *
+static_nht_hash_getref(const struct static_nht_data *ref)
+{
+ struct static_nht_data *nhtd;
+
+ nhtd = static_nht_hash_find(static_nht_hash, ref);
+ if (!nhtd) {
+ nhtd = XCALLOC(MTYPE_STATIC_NHT_DATA, sizeof(*nhtd));
+
+ prefix_copy(&nhtd->nh, &ref->nh);
+ nhtd->nh_vrf_id = ref->nh_vrf_id;
+ nhtd->safi = ref->safi;
+
+ static_nht_hash_add(static_nht_hash, nhtd);
+ }
+
+ nhtd->refcount++;
+ return nhtd;
+}
+
+static bool static_nht_hash_decref(struct static_nht_data **nhtd_p)
+{
+ struct static_nht_data *nhtd = *nhtd_p;
+
+ *nhtd_p = NULL;
+
+ if (--nhtd->refcount > 0)
+ return true;
+
+ static_nht_hash_del(static_nht_hash, nhtd);
+ XFREE(MTYPE_STATIC_NHT_DATA, nhtd);
+ return false;
+}
+
+static void static_nht_hash_clear(void)
+{
+ struct static_nht_data *nhtd;
+
+ while ((nhtd = static_nht_hash_pop(static_nht_hash)))
+ XFREE(MTYPE_STATIC_NHT_DATA, nhtd);
+}
+
+static bool static_zebra_nht_get_prefix(const struct static_nexthop *nh,
+ struct prefix *p)
+{
+ switch (nh->type) {
+ case STATIC_IFNAME:
+ case STATIC_BLACKHOLE:
+ 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;
+ 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;
+ return true;
+ }
+
+ 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;
+
+ if (nh->nh_registered) {
+ /* nh->nh_registered means we own a reference on the nhtd */
+ nhtd = static_nht_hash_find(static_nht_hash, &lookup);
+
+ 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)
+ DEBUGD(&static_dbg_route,
+ "Reusing registered nexthop(%pFX) for %pRN %d",
+ &lookup.nh, rn, nhtd->nh_num);
+ } else {
+ /* !reg && !nh->nh_registered */
+ zlog_warn("trying to unregister nexthop %pFX twice",
+ &lookup.nh);
+ return;
+ }
+
+ nh->nh_registered = reg;
+
+ 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;
+ }
+
+ if (nhtd->registered)
+ /* have no data, but did send register */
+ return;
+
+ cmd = ZEBRA_NEXTHOP_REGISTER;
+ DEBUGD(&static_dbg_route, "Registering nexthop(%pFX) for %pRN",
+ &lookup.nh, rn);
+ } else {
+ bool was_zebra_registered;
+
+ was_zebra_registered = nhtd->registered;
+ if (static_nht_hash_decref(&nhtd))
+ /* still got references alive */
+ return;
+
+ /* NB: nhtd is now NULL. */
+ if (!was_zebra_registered)
+ return;
+
+ cmd = ZEBRA_NEXTHOP_UNREGISTER;
+ DEBUGD(&static_dbg_route,
+ "Unregistering nexthop(%pFX) for %pRN", &lookup.nh, rn);
+ }
+
+ 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)
+{
+ struct route_node *rn = pn->rn;
+ struct static_route_info *si = rn->info;
+ struct static_nexthop *nh;
+ const struct prefix *p, *src_pp;
+ struct zapi_nexthop *api_nh;
+ struct zapi_route api;
+ uint32_t nh_num = 0;
+
+ p = src_pp = NULL;
+ srcdest_rnode_prefixes(rn, &p, &src_pp);
+
+ memset(&api, 0, sizeof(api));
+ api.vrf_id = si->svrf->vrf->vrf_id;
+ api.type = ZEBRA_ROUTE_STATIC;
+ api.safi = si->safi;
+ memcpy(&api.prefix, p, sizeof(api.prefix));
+
+ if (src_pp) {
+ SET_FLAG(api.message, ZAPI_MESSAGE_SRCPFX);
+ memcpy(&api.src_prefix, src_pp, sizeof(api.src_prefix));
+ }
+ SET_FLAG(api.flags, ZEBRA_FLAG_RR_USE_DISTANCE);
+ SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION);
+ SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP);
+ if (pn->distance) {
+ SET_FLAG(api.message, ZAPI_MESSAGE_DISTANCE);
+ api.distance = pn->distance;
+ }
+ if (pn->tag) {
+ SET_FLAG(api.message, ZAPI_MESSAGE_TAG);
+ api.tag = pn->tag;
+ }
+ if (pn->table_id != 0) {
+ SET_FLAG(api.message, ZAPI_MESSAGE_TABLEID);
+ api.tableid = pn->table_id;
+ }
+ frr_each(static_nexthop_list, &pn->nexthop_list, nh) {
+ /* Don't overrun the nexthop array */
+ if (nh_num == zebra_ecmp_count)
+ break;
+
+ 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)
+ SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK);
+ if (nh->color != 0) {
+ SET_FLAG(api.message, ZAPI_MESSAGE_SRTE);
+ api_nh->srte_color = nh->color;
+ }
+
+ nh->state = STATIC_SENT_TO_ZEBRA;
+
+ switch (nh->type) {
+ case STATIC_IFNAME:
+ if (nh->ifindex == IFINDEX_INTERNAL)
+ continue;
+ api_nh->ifindex = nh->ifindex;
+ api_nh->type = NEXTHOP_TYPE_IFINDEX;
+ break;
+ case STATIC_IPV4_GATEWAY:
+ if (!nh->nh_valid)
+ continue;
+ api_nh->type = NEXTHOP_TYPE_IPV4;
+ api_nh->gate = nh->addr;
+ break;
+ case STATIC_IPV4_GATEWAY_IFNAME:
+ if (nh->ifindex == IFINDEX_INTERNAL)
+ continue;
+ api_nh->ifindex = nh->ifindex;
+ api_nh->type = NEXTHOP_TYPE_IPV4_IFINDEX;
+ api_nh->gate = nh->addr;
+ break;
+ case STATIC_IPV6_GATEWAY:
+ if (!nh->nh_valid)
+ continue;
+ api_nh->type = NEXTHOP_TYPE_IPV6;
+ api_nh->gate = nh->addr;
+ break;
+ case STATIC_IPV6_GATEWAY_IFNAME:
+ if (nh->ifindex == IFINDEX_INTERNAL)
+ continue;
+ api_nh->type = NEXTHOP_TYPE_IPV6_IFINDEX;
+ api_nh->ifindex = nh->ifindex;
+ api_nh->gate = nh->addr;
+ break;
+ case STATIC_BLACKHOLE:
+ api_nh->type = NEXTHOP_TYPE_BLACKHOLE;
+ switch (nh->bh_type) {
+ case STATIC_BLACKHOLE_DROP:
+ case STATIC_BLACKHOLE_NULL:
+ api_nh->bh_type = BLACKHOLE_NULL;
+ break;
+ case STATIC_BLACKHOLE_REJECT:
+ api_nh->bh_type = BLACKHOLE_REJECT;
+ }
+ break;
+ }
+
+ if (nh->snh_label.num_labels) {
+ int i;
+
+ SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL);
+ api_nh->label_num = nh->snh_label.num_labels;
+ for (i = 0; i < api_nh->label_num; i++)
+ api_nh->labels[i] = nh->snh_label.label[i];
+ }
+ if (nh->snh_seg.num_segs) {
+ int i;
+
+ api_nh->seg6local_action =
+ ZEBRA_SEG6_LOCAL_ACTION_UNSPEC;
+ SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_SEG6);
+ SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION);
+ api.safi = SAFI_UNICAST;
+
+ api_nh->seg_num = nh->snh_seg.num_segs;
+ for (i = 0; i < api_nh->seg_num; i++)
+ memcpy(&api_nh->seg6_segs[i],
+ &nh->snh_seg.seg[i],
+ sizeof(struct in6_addr));
+ }
+ nh_num++;
+ }
+
+ api.nexthop_num = nh_num;
+
+ /*
+ * If we have been given an install but nothing is valid
+ * go ahead and delete the route for double plus fun
+ */
+ if (!nh_num && install)
+ install = false;
+
+ zclient_route_send(install ?
+ ZEBRA_ROUTE_ADD : ZEBRA_ROUTE_DELETE,
+ zclient, &api);
+}
+
+static zclient_handler *const static_handlers[] = {
+ [ZEBRA_INTERFACE_ADDRESS_ADD] = interface_address_add,
+ [ZEBRA_INTERFACE_ADDRESS_DELETE] = interface_address_delete,
+ [ZEBRA_ROUTE_NOTIFY_OWNER] = route_notify_owner,
+ [ZEBRA_NEXTHOP_UPDATE] = static_zebra_nexthop_update,
+};
+
+void static_zebra_init(void)
+{
+ struct zclient_options opt = { .receive_notify = true };
+
+ if_zapi_callbacks(static_ifp_create, static_ifp_up,
+ static_ifp_down, static_ifp_destroy);
+
+ zclient = zclient_new(master, &opt, static_handlers,
+ array_size(static_handlers));
+
+ zclient_init(zclient, ZEBRA_ROUTE_STATIC, 0, &static_privs);
+ zclient->zebra_capabilities = static_zebra_capabilities;
+ zclient->zebra_connected = zebra_connected;
+
+ 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);
+ zclient_free(zclient);
+ zclient = NULL;
+}
+
+void static_zebra_vrf_register(struct vrf *vrf)
+{
+ if (vrf->vrf_id == VRF_DEFAULT)
+ return;
+ zclient_send_reg_requests(zclient, vrf->vrf_id);
+}
+
+void static_zebra_vrf_unregister(struct vrf *vrf)
+{
+ if (vrf->vrf_id == VRF_DEFAULT)
+ return;
+ zclient_send_dereg_requests(zclient, vrf->vrf_id);
+}