diff options
Diffstat (limited to 'staticd/static_zebra.c')
-rw-r--r-- | staticd/static_zebra.c | 584 |
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, ¬e, 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); +} |