// SPDX-License-Identifier: GPL-2.0-or-later /* * Zebra connect code. * Copyright (C) 2018 Cumulus Networks, Inc. * Donald Sharp */ #include #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" #include "zclient.h" #include "static_srv6.h" #include "lib_errors.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; uint16_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) { static_ifindex_update(ifp, true); static_ifp_srv6_sids_update(ifp, true); return 0; } static int static_ifp_down(struct interface *ifp) { static_ifindex_update(ifp, false); static_ifp_srv6_sids_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) { struct vrf *vrf; zebra_route_notify_send(ZEBRA_ROUTE_NOTIFY_REQUEST, zclient, true); zclient_send_reg_requests(zclient, VRF_DEFAULT); vrf = vrf_lookup_by_id(VRF_DEFAULT); assert(vrf); static_fixup_vrf_ids(vrf); } /* 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 void static_zebra_nexthop_update(struct vrf *vrf, struct prefix *matched, struct zapi_route *nhr) { struct static_nht_data *nhtd, lookup; afi_t afi = AFI_IP; 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->vrf_id, matched, nhr->prefix.family)) nhr->nexthop_num = 0; } memset(&lookup, 0, sizeof(lookup)); lookup.nh = *matched; lookup.nh_vrf_id = vrf->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?"); } 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_SENT_TO_ZEBRA) 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; if (!si->svrf->vrf || si->svrf->vrf->vrf_id == VRF_UNKNOWN) return; 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); } /** * Send SRv6 SID to ZEBRA for installation or deletion. * * @param cmd ZEBRA_ROUTE_ADD or ZEBRA_ROUTE_DELETE * @param sid SRv6 SID to install or delete * @param prefixlen Prefix length * @param oif Outgoing interface * @param action SID action * @param context SID context */ static void static_zebra_send_localsid(int cmd, const struct in6_addr *sid, uint16_t prefixlen, ifindex_t oif, enum seg6local_action_t action, const struct seg6local_context *context) { struct prefix_ipv6 p = {}; struct zapi_route api = {}; struct zapi_nexthop *znh; if (cmd != ZEBRA_ROUTE_ADD && cmd != ZEBRA_ROUTE_DELETE) { flog_warn(EC_LIB_DEVELOPMENT, "%s: wrong ZEBRA command", __func__); return; } if (prefixlen > IPV6_MAX_BITLEN) { flog_warn(EC_LIB_DEVELOPMENT, "%s: wrong prefixlen %u", __func__, prefixlen); return; } DEBUGD(&static_dbg_srv6, "%s: |- %s SRv6 SID %pI6 behavior %s", __func__, cmd == ZEBRA_ROUTE_ADD ? "Add" : "Delete", sid, seg6local_action2str(action)); p.family = AF_INET6; p.prefixlen = prefixlen; p.prefix = *sid; api.vrf_id = VRF_DEFAULT; api.type = ZEBRA_ROUTE_STATIC; api.instance = 0; api.safi = SAFI_UNICAST; memcpy(&api.prefix, &p, sizeof(p)); if (cmd == ZEBRA_ROUTE_DELETE) return (void)zclient_route_send(ZEBRA_ROUTE_DELETE, zclient, &api); SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION); SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP); znh = &api.nexthops[0]; memset(znh, 0, sizeof(*znh)); znh->type = NEXTHOP_TYPE_IFINDEX; znh->ifindex = oif; SET_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_SEG6LOCAL); znh->seg6local_action = action; memcpy(&znh->seg6local_ctx, context, sizeof(struct seg6local_context)); api.nexthop_num = 1; zclient_route_send(ZEBRA_ROUTE_ADD, zclient, &api); } /** * Install SRv6 SID in the forwarding plane through Zebra. * * @param sid SRv6 SID */ void static_zebra_srv6_sid_install(struct static_srv6_sid *sid) { enum seg6local_action_t action = ZEBRA_SEG6_LOCAL_ACTION_UNSPEC; struct seg6local_context ctx = {}; struct interface *ifp = NULL; struct vrf *vrf; if (!sid) return; if (CHECK_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_SENT_TO_ZEBRA)) return; if (!sid->locator) { zlog_err("Failed to install SID %pFX: missing locator information", &sid->addr); return; } switch (sid->behavior) { case SRV6_ENDPOINT_BEHAVIOR_END: action = ZEBRA_SEG6_LOCAL_ACTION_END; break; case SRV6_ENDPOINT_BEHAVIOR_END_NEXT_CSID: action = ZEBRA_SEG6_LOCAL_ACTION_END; SET_SRV6_FLV_OP(ctx.flv.flv_ops, ZEBRA_SEG6_LOCAL_FLV_OP_NEXT_CSID); ctx.flv.lcblock_len = sid->locator->block_bits_length; ctx.flv.lcnode_func_len = sid->locator->node_bits_length; break; case SRV6_ENDPOINT_BEHAVIOR_END_DT6: case SRV6_ENDPOINT_BEHAVIOR_END_DT6_USID: action = ZEBRA_SEG6_LOCAL_ACTION_END_DT6; vrf = vrf_lookup_by_name(sid->attributes.vrf_name); if (!vrf_is_enabled(vrf)) { zlog_warn("Failed to install SID %pFX: VRF %s is inactive", &sid->addr, sid->attributes.vrf_name); return; } ctx.table = vrf->data.l.table_id; ifp = if_get_vrf_loopback(vrf->vrf_id); if (!ifp) { zlog_warn("Failed to install SID %pFX: failed to get loopback for vrf %s", &sid->addr, sid->attributes.vrf_name); return; } break; case SRV6_ENDPOINT_BEHAVIOR_END_DT4: case SRV6_ENDPOINT_BEHAVIOR_END_DT4_USID: action = ZEBRA_SEG6_LOCAL_ACTION_END_DT4; vrf = vrf_lookup_by_name(sid->attributes.vrf_name); if (!vrf_is_enabled(vrf)) { zlog_warn("Failed to install SID %pFX: VRF %s is inactive", &sid->addr, sid->attributes.vrf_name); return; } ctx.table = vrf->data.l.table_id; ifp = if_get_vrf_loopback(vrf->vrf_id); if (!ifp) { zlog_warn("Failed to install SID %pFX: failed to get loopback for vrf %s", &sid->addr, sid->attributes.vrf_name); return; } break; case SRV6_ENDPOINT_BEHAVIOR_END_DT46: case SRV6_ENDPOINT_BEHAVIOR_END_DT46_USID: action = ZEBRA_SEG6_LOCAL_ACTION_END_DT46; vrf = vrf_lookup_by_name(sid->attributes.vrf_name); if (!vrf_is_enabled(vrf)) { zlog_warn("Failed to install SID %pFX: VRF %s is inactive", &sid->addr, sid->attributes.vrf_name); return; } ctx.table = vrf->data.l.table_id; ifp = if_get_vrf_loopback(vrf->vrf_id); if (!ifp) { zlog_warn("Failed to install SID %pFX: failed to get loopback for vrf %s", &sid->addr, sid->attributes.vrf_name); return; } break; case SRV6_ENDPOINT_BEHAVIOR_END_X: case SRV6_ENDPOINT_BEHAVIOR_END_X_NEXT_CSID: case SRV6_ENDPOINT_BEHAVIOR_OPAQUE: case SRV6_ENDPOINT_BEHAVIOR_RESERVED: zlog_warn("unsupported behavior: %u", sid->behavior); break; } ctx.block_len = sid->locator->block_bits_length; ctx.node_len = sid->locator->node_bits_length; ctx.function_len = sid->locator->function_bits_length; ctx.argument_len = sid->locator->argument_bits_length; /* Attach the SID to the SRv6 interface */ if (!ifp) { ifp = if_lookup_by_name(DEFAULT_SRV6_IFNAME, VRF_DEFAULT); if (!ifp) { zlog_warn("Failed to install SRv6 SID %pFX: %s interface not found", &sid->addr, DEFAULT_SRV6_IFNAME); return; } } /* Send the SID to zebra */ static_zebra_send_localsid(ZEBRA_ROUTE_ADD, &sid->addr.prefix, sid->addr.prefixlen, ifp->ifindex, action, &ctx); SET_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_SENT_TO_ZEBRA); } void static_zebra_srv6_sid_uninstall(struct static_srv6_sid *sid) { enum seg6local_action_t action = ZEBRA_SEG6_LOCAL_ACTION_UNSPEC; struct interface *ifp = NULL; struct seg6local_context ctx = {}; struct vrf *vrf; if (!sid) return; if (!CHECK_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_SENT_TO_ZEBRA)) return; if (!sid->locator) { zlog_err("Failed to uninstall SID %pFX: missing locator information", &sid->addr); return; } switch (sid->behavior) { case SRV6_ENDPOINT_BEHAVIOR_END: case SRV6_ENDPOINT_BEHAVIOR_END_NEXT_CSID: break; case SRV6_ENDPOINT_BEHAVIOR_END_DT6: case SRV6_ENDPOINT_BEHAVIOR_END_DT6_USID: vrf = vrf_lookup_by_name(sid->attributes.vrf_name); if (!vrf_is_enabled(vrf)) { zlog_warn("Failed to install SID %pFX: VRF %s is inactive", &sid->addr, sid->attributes.vrf_name); return; } ifp = if_get_vrf_loopback(vrf->vrf_id); if (!ifp) { zlog_warn("Failed to install SID %pFX: failed to get loopback for vrf %s", &sid->addr, sid->attributes.vrf_name); return; } break; case SRV6_ENDPOINT_BEHAVIOR_END_DT4: case SRV6_ENDPOINT_BEHAVIOR_END_DT4_USID: vrf = vrf_lookup_by_name(sid->attributes.vrf_name); if (!vrf_is_enabled(vrf)) { zlog_warn("Failed to install SID %pFX: VRF %s is inactive", &sid->addr, sid->attributes.vrf_name); return; } ifp = if_get_vrf_loopback(vrf->vrf_id); if (!ifp) { zlog_warn("Failed to install SID %pFX: failed to get loopback for vrf %s", &sid->addr, sid->attributes.vrf_name); return; } break; case SRV6_ENDPOINT_BEHAVIOR_END_DT46: case SRV6_ENDPOINT_BEHAVIOR_END_DT46_USID: vrf = vrf_lookup_by_name(sid->attributes.vrf_name); if (!vrf_is_enabled(vrf)) { zlog_warn("Failed to install SID %pFX: VRF %s is inactive", &sid->addr, sid->attributes.vrf_name); return; } ifp = if_get_vrf_loopback(vrf->vrf_id); if (!ifp) { zlog_warn("Failed to install SID %pFX: failed to get loopback for vrf %s", &sid->addr, sid->attributes.vrf_name); return; } break; case SRV6_ENDPOINT_BEHAVIOR_END_X: case SRV6_ENDPOINT_BEHAVIOR_END_X_NEXT_CSID: case SRV6_ENDPOINT_BEHAVIOR_OPAQUE: case SRV6_ENDPOINT_BEHAVIOR_RESERVED: zlog_warn("unsupported behavior: %u", sid->behavior); break; } /* The SID is attached to the SRv6 interface */ if (!ifp) { ifp = if_lookup_by_name(DEFAULT_SRV6_IFNAME, VRF_DEFAULT); if (!ifp) { zlog_warn("%s interface not found: nothing to uninstall", DEFAULT_SRV6_IFNAME); return; } } ctx.block_len = sid->locator->block_bits_length; ctx.node_len = sid->locator->node_bits_length; ctx.function_len = sid->locator->function_bits_length; ctx.argument_len = sid->locator->argument_bits_length; static_zebra_send_localsid(ZEBRA_ROUTE_DELETE, &sid->addr.prefix, sid->addr.prefixlen, ifp->ifindex, action, &ctx); UNSET_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_SENT_TO_ZEBRA); } extern void static_zebra_request_srv6_sid(struct static_srv6_sid *sid) { struct srv6_sid_ctx ctx = {}; int ret = 0; struct vrf *vrf; if (!sid) return; /* convert `srv6_endpoint_behavior_codepoint` to `seg6local_action_t` */ switch (sid->behavior) { case SRV6_ENDPOINT_BEHAVIOR_END: case SRV6_ENDPOINT_BEHAVIOR_END_NEXT_CSID: ctx.behavior = ZEBRA_SEG6_LOCAL_ACTION_END; break; case SRV6_ENDPOINT_BEHAVIOR_END_DT6: case SRV6_ENDPOINT_BEHAVIOR_END_DT6_USID: ctx.behavior = ZEBRA_SEG6_LOCAL_ACTION_END_DT6; /* process SRv6 SID attributes */ /* generate table ID from the VRF name, if configured */ if (sid->attributes.vrf_name[0] != '\0') { vrf = vrf_lookup_by_name(sid->attributes.vrf_name); if (!vrf_is_enabled(vrf)) return; ctx.vrf_id = vrf->vrf_id; } break; case SRV6_ENDPOINT_BEHAVIOR_END_DT4: case SRV6_ENDPOINT_BEHAVIOR_END_DT4_USID: ctx.behavior = ZEBRA_SEG6_LOCAL_ACTION_END_DT4; /* process SRv6 SID attributes */ /* generate table ID from the VRF name, if configured */ if (sid->attributes.vrf_name[0] != '\0') { vrf = vrf_lookup_by_name(sid->attributes.vrf_name); if (!vrf_is_enabled(vrf)) return; ctx.vrf_id = vrf->vrf_id; } break; case SRV6_ENDPOINT_BEHAVIOR_END_DT46: case SRV6_ENDPOINT_BEHAVIOR_END_DT46_USID: ctx.behavior = ZEBRA_SEG6_LOCAL_ACTION_END_DT46; /* process SRv6 SID attributes */ /* generate table ID from the VRF name, if configured */ if (sid->attributes.vrf_name[0] != '\0') { vrf = vrf_lookup_by_name(sid->attributes.vrf_name); if (!vrf_is_enabled(vrf)) return; ctx.vrf_id = vrf->vrf_id; } break; case SRV6_ENDPOINT_BEHAVIOR_END_X: case SRV6_ENDPOINT_BEHAVIOR_END_X_NEXT_CSID: case SRV6_ENDPOINT_BEHAVIOR_OPAQUE: case SRV6_ENDPOINT_BEHAVIOR_RESERVED: zlog_warn("unsupported behavior: %u", sid->behavior); return; } /* Request SRv6 SID from SID Manager */ ret = srv6_manager_get_sid(zclient, &ctx, &sid->addr.prefix, sid->locator->name, NULL); if (ret < 0) zlog_warn("%s: error getting SRv6 SID!", __func__); } extern void static_zebra_release_srv6_sid(struct static_srv6_sid *sid) { struct srv6_sid_ctx ctx = {}; struct vrf *vrf; int ret = 0; if (!sid || !CHECK_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_VALID)) return; /* convert `srv6_endpoint_behavior_codepoint` to `seg6local_action_t` */ switch (sid->behavior) { case SRV6_ENDPOINT_BEHAVIOR_END: case SRV6_ENDPOINT_BEHAVIOR_END_NEXT_CSID: ctx.behavior = ZEBRA_SEG6_LOCAL_ACTION_END; break; case SRV6_ENDPOINT_BEHAVIOR_END_DT6: case SRV6_ENDPOINT_BEHAVIOR_END_DT6_USID: ctx.behavior = ZEBRA_SEG6_LOCAL_ACTION_END_DT6; /* process SRv6 SID attributes */ /* generate table ID from the VRF name, if configured */ if (sid->attributes.vrf_name[0] != '\0') { vrf = vrf_lookup_by_name(sid->attributes.vrf_name); if (!vrf_is_enabled(vrf)) return; ctx.vrf_id = vrf->vrf_id; } break; case SRV6_ENDPOINT_BEHAVIOR_END_DT4: case SRV6_ENDPOINT_BEHAVIOR_END_DT4_USID: ctx.behavior = ZEBRA_SEG6_LOCAL_ACTION_END_DT4; /* process SRv6 SID attributes */ /* generate table ID from the VRF name, if configured */ if (sid->attributes.vrf_name[0] != '\0') { vrf = vrf_lookup_by_name(sid->attributes.vrf_name); if (!vrf_is_enabled(vrf)) return; ctx.vrf_id = vrf->vrf_id; } break; case SRV6_ENDPOINT_BEHAVIOR_END_DT46: case SRV6_ENDPOINT_BEHAVIOR_END_DT46_USID: ctx.behavior = ZEBRA_SEG6_LOCAL_ACTION_END_DT46; /* process SRv6 SID attributes */ /* generate table ID from the VRF name, if configured */ if (sid->attributes.vrf_name[0] != '\0') { vrf = vrf_lookup_by_name(sid->attributes.vrf_name); if (!vrf_is_enabled(vrf)) return; ctx.vrf_id = vrf->vrf_id; } break; case SRV6_ENDPOINT_BEHAVIOR_END_X: case SRV6_ENDPOINT_BEHAVIOR_END_X_NEXT_CSID: case SRV6_ENDPOINT_BEHAVIOR_OPAQUE: case SRV6_ENDPOINT_BEHAVIOR_RESERVED: zlog_warn("unsupported behavior: %u", sid->behavior); return; } /* remove the SRv6 SID from the zebra RIB */ ret = srv6_manager_release_sid(zclient, &ctx); if (ret == ZCLIENT_SEND_FAILURE) flog_err(EC_LIB_ZAPI_SOCKET, "zclient_send_get_srv6_sid() delete failed: %s", safe_strerror(errno)); } /** * Ask the SRv6 Manager (zebra) about a specific locator * * @param name Locator name * @return 0 on success, -1 otherwise */ int static_zebra_srv6_manager_get_locator(const char *name) { if (!name) return -1; /* * Send the Get Locator request to the SRv6 Manager and return the * result */ return srv6_manager_get_locator(zclient, name); } static void request_srv6_sids(struct static_srv6_locator *locator) { struct static_srv6_sid *sid; struct listnode *node; for (ALL_LIST_ELEMENTS_RO(srv6_sids, node, sid)) { if (sid->locator == locator) static_zebra_request_srv6_sid(sid); } } /** * Internal function to process an SRv6 locator * * @param locator The locator to be processed */ static int static_zebra_process_srv6_locator_internal(struct srv6_locator *locator) { struct static_srv6_locator *loc; struct listnode *node; struct static_srv6_sid *sid; if (!locator) return -1; DEBUGD(&static_dbg_srv6, "%s: Received SRv6 locator %s %pFX, loc-block-len=%u, loc-node-len=%u func-len=%u, arg-len=%u", __func__, locator->name, &locator->prefix, locator->block_bits_length, locator->node_bits_length, locator->function_bits_length, locator->argument_bits_length); /* If we are already aware about the locator, nothing to do */ loc = static_srv6_locator_lookup(locator->name); if (loc) return 0; loc = static_srv6_locator_alloc(locator->name); DEBUGD(&static_dbg_srv6, "%s: SRv6 locator (locator %s, prefix %pFX) set", __func__, locator->name, &locator->prefix); /* Store the locator prefix */ loc->prefix = locator->prefix; loc->block_bits_length = locator->block_bits_length; loc->node_bits_length = locator->node_bits_length; loc->function_bits_length = locator->function_bits_length; loc->argument_bits_length = locator->argument_bits_length; loc->flags = locator->flags; listnode_add(srv6_locators, loc); for (ALL_LIST_ELEMENTS_RO(srv6_sids, node, sid)) { if (strncmp(sid->locator_name, loc->name, sizeof(loc->name)) == 0) sid->locator = loc; } /* Request SIDs from the locator */ request_srv6_sids(loc); return 0; } /** * Callback to process an SRv6 locator received from SRv6 Manager (zebra). * * @result 0 on success, -1 otherwise */ static int static_zebra_process_srv6_locator_add(ZAPI_CALLBACK_ARGS) { struct srv6_locator loc = {}; if (!srv6_locators) return -1; /* Decode the SRv6 locator */ if (zapi_srv6_locator_decode(zclient->ibuf, &loc) < 0) return -1; return static_zebra_process_srv6_locator_internal(&loc); } /** * Callback to process a notification from SRv6 Manager (zebra) of an SRv6 * locator deleted. * * @result 0 on success, -1 otherwise */ static int static_zebra_process_srv6_locator_delete(ZAPI_CALLBACK_ARGS) { struct srv6_locator loc = {}; struct listnode *node2, *nnode2; struct static_srv6_sid *sid; struct static_srv6_locator *locator; if (!srv6_locators) return -1; /* Decode the received zebra message */ if (zapi_srv6_locator_decode(zclient->ibuf, &loc) < 0) return -1; DEBUGD(&static_dbg_srv6, "%s: SRv6 locator deleted in zebra: name %s, prefix %pFX, block_len %u, node_len %u, func_len %u, arg_len %u", __func__, loc.name, &loc.prefix, loc.block_bits_length, loc.node_bits_length, loc.function_bits_length, loc.argument_bits_length); locator = static_srv6_locator_lookup(loc.name); if (!locator) return 0; DEBUGD(&static_dbg_srv6, "%s: Deleting srv6 sids from locator %s", __func__, locator->name); /* Delete SRv6 SIDs */ for (ALL_LIST_ELEMENTS(srv6_sids, node2, nnode2, sid)) { if (sid->locator != locator) continue; DEBUGD(&static_dbg_srv6, "%s: Deleting SRv6 SID (locator %s, sid %pFX)", __func__, locator->name, &sid->addr); /* * Uninstall the SRv6 SID from the forwarding plane * through Zebra */ if (CHECK_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_SENT_TO_ZEBRA)) { static_zebra_srv6_sid_uninstall(sid); UNSET_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_SENT_TO_ZEBRA); } } listnode_delete(srv6_locators, locator); static_srv6_locator_free(locator); return 0; } static int static_zebra_srv6_sid_notify(ZAPI_CALLBACK_ARGS) { struct srv6_sid_ctx ctx; struct in6_addr sid_addr; enum zapi_srv6_sid_notify note; uint32_t sid_func; struct listnode *node; char buf[256]; struct static_srv6_sid *sid = NULL; char *loc_name; bool found = false; if (!srv6_locators) return -1; /* Decode the received notification message */ if (!zapi_srv6_sid_notify_decode(zclient->ibuf, &ctx, &sid_addr, &sid_func, NULL, ¬e, &loc_name)) { zlog_err("%s : error in msg decode", __func__); return -1; } DEBUGD(&static_dbg_srv6, "%s: received SRv6 SID notify: ctx %s sid_value %pI6 sid_func %u note %s", __func__, srv6_sid_ctx2str(buf, sizeof(buf), &ctx), &sid_addr, sid_func, zapi_srv6_sid_notify2str(note)); /* Handle notification */ switch (note) { case ZAPI_SRV6_SID_ALLOCATED: DEBUGD(&static_dbg_srv6, "%s: SRv6 SID %pI6 %s ALLOCATED", __func__, &sid_addr, srv6_sid_ctx2str(buf, sizeof(buf), &ctx)); for (ALL_LIST_ELEMENTS_RO(srv6_sids, node, sid)) { if (IPV6_ADDR_SAME(&sid->addr.prefix, &sid_addr)) { found = true; break; } } if (!found || !sid) { zlog_err("SRv6 SID %pI6 %s: not found", &sid_addr, srv6_sid_ctx2str(buf, sizeof(buf), &ctx)); return 0; } SET_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_VALID); /* * Install the new SRv6 End SID in the forwarding plane through * Zebra */ static_zebra_srv6_sid_install(sid); SET_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_SENT_TO_ZEBRA); break; case ZAPI_SRV6_SID_RELEASED: DEBUGD(&static_dbg_srv6, "%s: SRv6 SID %pI6 %s: RELEASED", __func__, &sid_addr, srv6_sid_ctx2str(buf, sizeof(buf), &ctx)); UNSET_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_VALID); break; case ZAPI_SRV6_SID_FAIL_ALLOC: zlog_err("SRv6 SID %pI6 %s: Failed to allocate", &sid_addr, srv6_sid_ctx2str(buf, sizeof(buf), &ctx)); /* Error will be logged by zebra module */ break; case ZAPI_SRV6_SID_FAIL_RELEASE: zlog_err("%s: SRv6 SID %pI6 %s failure to release", __func__, &sid_addr, srv6_sid_ctx2str(buf, sizeof(buf), &ctx)); /* Error will be logged by zebra module */ break; } return 0; } 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_SRV6_LOCATOR_ADD] = static_zebra_process_srv6_locator_add, [ZEBRA_SRV6_LOCATOR_DELETE] = static_zebra_process_srv6_locator_delete, [ZEBRA_SRV6_SID_NOTIFY] = static_zebra_srv6_sid_notify, }; void static_zebra_init(void) { hook_register_prio(if_real, 0, static_ifp_create); hook_register_prio(if_up, 0, static_ifp_up); hook_register_prio(if_down, 0, static_ifp_down); hook_register_prio(if_unreal, 0, static_ifp_destroy); zclient = zclient_new(master, &zclient_options_default, 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; zclient->nexthop_update = static_zebra_nexthop_update; 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); }