diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-09 13:16:35 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-09 13:16:35 +0000 |
commit | e2bbf175a2184bd76f6c54ccf8456babeb1a46fc (patch) | |
tree | f0b76550d6e6f500ada964a3a4ee933a45e5a6f1 /bgpd/bgp_mplsvpn.c | |
parent | Initial commit. (diff) | |
download | frr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.tar.xz frr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.zip |
Adding upstream version 9.1.upstream/9.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'bgpd/bgp_mplsvpn.c')
-rw-r--r-- | bgpd/bgp_mplsvpn.c | 4277 |
1 files changed, 4277 insertions, 0 deletions
diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c new file mode 100644 index 0000000..18d9f64 --- /dev/null +++ b/bgpd/bgp_mplsvpn.c @@ -0,0 +1,4277 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* MPLS-VPN + * Copyright (C) 2000 Kunihiro Ishiguro <kunihiro@zebra.org> + */ + +#include <zebra.h> + +#include "command.h" +#include "prefix.h" +#include "log.h" +#include "memory.h" +#include "stream.h" +#include "queue.h" +#include "filter.h" +#include "mpls.h" +#include "json.h" +#include "zclient.h" + +#include "bgpd/bgpd.h" +#include "bgpd/bgp_debug.h" +#include "bgpd/bgp_errors.h" +#include "bgpd/bgp_table.h" +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_attr.h" +#include "bgpd/bgp_label.h" +#include "bgpd/bgp_mplsvpn.h" +#include "bgpd/bgp_packet.h" +#include "bgpd/bgp_vty.h" +#include "bgpd/bgp_vpn.h" +#include "bgpd/bgp_community.h" +#include "bgpd/bgp_ecommunity.h" +#include "bgpd/bgp_zebra.h" +#include "bgpd/bgp_nexthop.h" +#include "bgpd/bgp_nht.h" +#include "bgpd/bgp_evpn.h" +#include "bgpd/bgp_memory.h" + +#ifdef ENABLE_BGP_VNC +#include "bgpd/rfapi/rfapi_backend.h" +#endif + +DEFINE_MTYPE_STATIC(BGPD, MPLSVPN_NH_LABEL_BIND_CACHE, + "BGP MPLSVPN nexthop label bind cache"); + +/* + * Definitions and external declarations. + */ +extern struct zclient *zclient; + +extern int argv_find_and_parse_vpnvx(struct cmd_token **argv, int argc, + int *index, afi_t *afi) +{ + int ret = 0; + if (argv_find(argv, argc, "vpnv4", index)) { + ret = 1; + if (afi) + *afi = AFI_IP; + } else if (argv_find(argv, argc, "vpnv6", index)) { + ret = 1; + if (afi) + *afi = AFI_IP6; + } + return ret; +} + +uint32_t decode_label(mpls_label_t *label_pnt) +{ + uint32_t l; + uint8_t *pnt = (uint8_t *)label_pnt; + + l = ((uint32_t)*pnt++ << 12); + l |= (uint32_t)*pnt++ << 4; + l |= (uint32_t)((*pnt & 0xf0) >> 4); + return l; +} + +void encode_label(mpls_label_t label, mpls_label_t *label_pnt) +{ + uint8_t *pnt = (uint8_t *)label_pnt; + if (pnt == NULL) + return; + if (label == BGP_PREVENT_VRF_2_VRF_LEAK) { + *label_pnt = label; + return; + } + *pnt++ = (label >> 12) & 0xff; + *pnt++ = (label >> 4) & 0xff; + *pnt++ = ((label << 4) + 1) & 0xff; /* S=1 */ +} + +int bgp_nlri_parse_vpn(struct peer *peer, struct attr *attr, + struct bgp_nlri *packet) +{ + struct prefix p; + uint8_t psize = 0; + uint8_t prefixlen; + uint16_t type; + struct rd_as rd_as; + struct rd_ip rd_ip; + struct prefix_rd prd = {0}; + mpls_label_t label = {0}; + afi_t afi; + safi_t safi; + bool addpath_capable; + uint32_t addpath_id; + int ret = 0; + + /* Make prefix_rd */ + prd.family = AF_UNSPEC; + prd.prefixlen = 64; + + struct stream *data = stream_new(packet->length); + stream_put(data, packet->nlri, packet->length); + afi = packet->afi; + safi = packet->safi; + addpath_id = 0; + + addpath_capable = bgp_addpath_encode_rx(peer, afi, safi); + +#define VPN_PREFIXLEN_MIN_BYTES (3 + 8) /* label + RD */ + while (STREAM_READABLE(data) > 0) { + /* Clear prefix structure. */ + memset(&p, 0, sizeof(p)); + + if (addpath_capable) { + STREAM_GET(&addpath_id, data, BGP_ADDPATH_ID_LEN); + addpath_id = ntohl(addpath_id); + } + + if (STREAM_READABLE(data) < 1) { + flog_err( + EC_BGP_UPDATE_RCV, + "%s [Error] Update packet error / VPN (truncated NLRI of size %u; no prefix length)", + peer->host, packet->length); + ret = BGP_NLRI_PARSE_ERROR_PACKET_LENGTH; + goto done; + } + + /* Fetch prefix length. */ + STREAM_GETC(data, prefixlen); + p.family = afi2family(packet->afi); + psize = PSIZE(prefixlen); + + if (prefixlen < VPN_PREFIXLEN_MIN_BYTES * 8) { + flog_err( + EC_BGP_UPDATE_RCV, + "%s [Error] Update packet error / VPN (prefix length %d less than VPN min length)", + peer->host, prefixlen); + ret = BGP_NLRI_PARSE_ERROR_PREFIX_LENGTH; + goto done; + } + + /* sanity check against packet data */ + if (STREAM_READABLE(data) < psize) { + flog_err( + EC_BGP_UPDATE_RCV, + "%s [Error] Update packet error / VPN (prefix length %d exceeds packet size %u)", + peer->host, prefixlen, packet->length); + ret = BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW; + goto done; + } + + /* sanity check against storage for the IP address portion */ + if ((psize - VPN_PREFIXLEN_MIN_BYTES) > (ssize_t)sizeof(p.u)) { + flog_err( + EC_BGP_UPDATE_RCV, + "%s [Error] Update packet error / VPN (psize %d exceeds storage size %zu)", + peer->host, + prefixlen - VPN_PREFIXLEN_MIN_BYTES * 8, + sizeof(p.u)); + ret = BGP_NLRI_PARSE_ERROR_PACKET_LENGTH; + goto done; + } + + /* Sanity check against max bitlen of the address family */ + if ((psize - VPN_PREFIXLEN_MIN_BYTES) > prefix_blen(&p)) { + flog_err( + EC_BGP_UPDATE_RCV, + "%s [Error] Update packet error / VPN (psize %d exceeds family (%u) max byte len %u)", + peer->host, + prefixlen - VPN_PREFIXLEN_MIN_BYTES * 8, + p.family, prefix_blen(&p)); + ret = BGP_NLRI_PARSE_ERROR_PACKET_LENGTH; + goto done; + } + + /* Copy label to prefix. */ + if (STREAM_READABLE(data) < BGP_LABEL_BYTES) { + flog_err( + EC_BGP_UPDATE_RCV, + "%s [Error] Update packet error / VPN (truncated NLRI of size %u; no label)", + peer->host, packet->length); + ret = BGP_NLRI_PARSE_ERROR_PACKET_LENGTH; + goto done; + } + + STREAM_GET(&label, data, BGP_LABEL_BYTES); + bgp_set_valid_label(&label); + + /* Copy routing distinguisher to rd. */ + if (STREAM_READABLE(data) < 8) { + flog_err( + EC_BGP_UPDATE_RCV, + "%s [Error] Update packet error / VPN (truncated NLRI of size %u; no RD)", + peer->host, packet->length); + ret = BGP_NLRI_PARSE_ERROR_PACKET_LENGTH; + goto done; + } + STREAM_GET(&prd.val, data, 8); + + /* Decode RD type. */ + type = decode_rd_type(prd.val); + + switch (type) { + case RD_TYPE_AS: + decode_rd_as(&prd.val[2], &rd_as); + break; + + case RD_TYPE_AS4: + decode_rd_as4(&prd.val[2], &rd_as); + break; + + case RD_TYPE_IP: + decode_rd_ip(&prd.val[2], &rd_ip); + break; + +#ifdef ENABLE_BGP_VNC + case RD_TYPE_VNC_ETH: + break; +#endif + + default: + flog_err(EC_BGP_UPDATE_RCV, "Unknown RD type %d", type); + break; /* just report */ + } + + /* exclude label & RD */ + p.prefixlen = prefixlen - VPN_PREFIXLEN_MIN_BYTES * 8; + STREAM_GET(p.u.val, data, psize - VPN_PREFIXLEN_MIN_BYTES); + + if (attr) { + bgp_update(peer, &p, addpath_id, attr, packet->afi, + SAFI_MPLS_VPN, ZEBRA_ROUTE_BGP, + BGP_ROUTE_NORMAL, &prd, &label, 1, 0, NULL); + } else { + bgp_withdraw(peer, &p, addpath_id, packet->afi, + SAFI_MPLS_VPN, ZEBRA_ROUTE_BGP, + BGP_ROUTE_NORMAL, &prd, &label, 1, NULL); + } + } + /* Packet length consistency check. */ + if (STREAM_READABLE(data) != 0) { + flog_err( + EC_BGP_UPDATE_RCV, + "%s [Error] Update packet error / VPN (%zu data remaining after parsing)", + peer->host, STREAM_READABLE(data)); + return BGP_NLRI_PARSE_ERROR_PACKET_LENGTH; + } + + goto done; + +stream_failure: + flog_err( + EC_BGP_UPDATE_RCV, + "%s [Error] Update packet error / VPN (NLRI of size %u - length error)", + peer->host, packet->length); + ret = BGP_NLRI_PARSE_ERROR_PACKET_LENGTH; + +done: + stream_free(data); + return ret; + +#undef VPN_PREFIXLEN_MIN_BYTES +} + +/* + * This function informs zebra of the label this vrf sets on routes + * leaked to VPN. Zebra should install this label in the kernel with + * an action of "pop label and then use this vrf's IP FIB to route the PDU." + * + * Sending this vrf-label association is qualified by a) whether vrf->vpn + * exporting is active ("export vpn" is enabled, vpn-policy RD and RT list + * are set) and b) whether vpn-policy label is set. + * + * If any of these conditions do not hold, then we send MPLS_LABEL_NONE + * for this vrf, which zebra interprets to mean "delete this vrf-label + * association." + */ +void vpn_leak_zebra_vrf_label_update(struct bgp *bgp, afi_t afi) +{ + mpls_label_t label = MPLS_LABEL_NONE; + int debug = BGP_DEBUG(vpn, VPN_LEAK_LABEL); + + if (bgp->vrf_id == VRF_UNKNOWN) { + if (debug) { + zlog_debug( + "%s: vrf %s: afi %s: vrf_id not set, can't set zebra vrf label", + __func__, bgp->name_pretty, afi2str(afi)); + } + return; + } + + if (vpn_leak_to_vpn_active(bgp, afi, NULL)) { + label = bgp->vpn_policy[afi].tovpn_label; + } + + if (debug) { + zlog_debug("%s: vrf %s: afi %s: setting label %d for vrf id %d", + __func__, bgp->name_pretty, afi2str(afi), label, + bgp->vrf_id); + } + + if (label == BGP_PREVENT_VRF_2_VRF_LEAK) + label = MPLS_LABEL_NONE; + zclient_send_vrf_label(zclient, bgp->vrf_id, afi, label, ZEBRA_LSP_BGP); + bgp->vpn_policy[afi].tovpn_zebra_vrf_label_last_sent = label; +} + +/* + * If zebra tells us vrf has become unconfigured, tell zebra not to + * use this label to forward to the vrf anymore + */ +void vpn_leak_zebra_vrf_label_withdraw(struct bgp *bgp, afi_t afi) +{ + mpls_label_t label = MPLS_LABEL_NONE; + int debug = BGP_DEBUG(vpn, VPN_LEAK_LABEL); + + if (bgp->vrf_id == VRF_UNKNOWN) { + if (debug) { + zlog_debug( + "%s: vrf_id not set, can't delete zebra vrf label", + __func__); + } + return; + } + + if (debug) { + zlog_debug("%s: deleting label for vrf %s (id=%d)", __func__, + bgp->name_pretty, bgp->vrf_id); + } + + zclient_send_vrf_label(zclient, bgp->vrf_id, afi, label, ZEBRA_LSP_BGP); + bgp->vpn_policy[afi].tovpn_zebra_vrf_label_last_sent = label; +} + +/* + * This function informs zebra of the srv6-function this vrf sets on routes + * leaked to VPN. Zebra should install this srv6-function in the kernel with + * an action of "End.DT4/6's IP FIB to route the PDU." + */ +void vpn_leak_zebra_vrf_sid_update_per_af(struct bgp *bgp, afi_t afi) +{ + int debug = BGP_DEBUG(vpn, VPN_LEAK_LABEL); + enum seg6local_action_t act; + struct seg6local_context ctx = {}; + struct in6_addr *tovpn_sid = NULL; + struct in6_addr *tovpn_sid_ls = NULL; + struct vrf *vrf; + + if (bgp->vrf_id == VRF_UNKNOWN) { + if (debug) + zlog_debug("%s: vrf %s: afi %s: vrf_id not set, can't set zebra vrf label", + __func__, bgp->name_pretty, afi2str(afi)); + return; + } + + tovpn_sid = bgp->vpn_policy[afi].tovpn_sid; + if (!tovpn_sid) { + if (debug) + zlog_debug("%s: vrf %s: afi %s: sid not set", __func__, + bgp->name_pretty, afi2str(afi)); + return; + } + + if (debug) + zlog_debug("%s: vrf %s: afi %s: setting sid %pI6 for vrf id %d", + __func__, bgp->name_pretty, afi2str(afi), tovpn_sid, + bgp->vrf_id); + + vrf = vrf_lookup_by_id(bgp->vrf_id); + if (!vrf) + return; + + ctx.table = vrf->data.l.table_id; + act = afi == AFI_IP ? ZEBRA_SEG6_LOCAL_ACTION_END_DT4 + : ZEBRA_SEG6_LOCAL_ACTION_END_DT6; + zclient_send_localsid(zclient, tovpn_sid, bgp->vrf_id, act, &ctx); + + tovpn_sid_ls = XCALLOC(MTYPE_BGP_SRV6_SID, sizeof(struct in6_addr)); + *tovpn_sid_ls = *tovpn_sid; + bgp->vpn_policy[afi].tovpn_zebra_vrf_sid_last_sent = tovpn_sid_ls; +} + +/* + * This function informs zebra of the srv6-function this vrf sets on routes + * leaked to VPN. Zebra should install this srv6-function in the kernel with + * an action of "End.DT46's IP FIB to route the PDU." + */ +void vpn_leak_zebra_vrf_sid_update_per_vrf(struct bgp *bgp) +{ + int debug = BGP_DEBUG(vpn, VPN_LEAK_LABEL); + enum seg6local_action_t act; + struct seg6local_context ctx = {}; + struct in6_addr *tovpn_sid = NULL; + struct in6_addr *tovpn_sid_ls = NULL; + struct vrf *vrf; + + if (bgp->vrf_id == VRF_UNKNOWN) { + if (debug) + zlog_debug( + "%s: vrf %s: vrf_id not set, can't set zebra vrf label", + __func__, bgp->name_pretty); + return; + } + + tovpn_sid = bgp->tovpn_sid; + if (!tovpn_sid) { + if (debug) + zlog_debug("%s: vrf %s: sid not set", __func__, + bgp->name_pretty); + return; + } + + if (debug) + zlog_debug("%s: vrf %s: setting sid %pI6 for vrf id %d", + __func__, bgp->name_pretty, tovpn_sid, bgp->vrf_id); + + vrf = vrf_lookup_by_id(bgp->vrf_id); + if (!vrf) + return; + + ctx.table = vrf->data.l.table_id; + act = ZEBRA_SEG6_LOCAL_ACTION_END_DT46; + zclient_send_localsid(zclient, tovpn_sid, bgp->vrf_id, act, &ctx); + + tovpn_sid_ls = XCALLOC(MTYPE_BGP_SRV6_SID, sizeof(struct in6_addr)); + *tovpn_sid_ls = *tovpn_sid; + bgp->tovpn_zebra_vrf_sid_last_sent = tovpn_sid_ls; +} + +/* + * This function informs zebra of the srv6-function this vrf sets on routes + * leaked to VPN. Zebra should install this srv6-function in the kernel with + * an action of "End.DT4/6/46's IP FIB to route the PDU." + */ +void vpn_leak_zebra_vrf_sid_update(struct bgp *bgp, afi_t afi) +{ + int debug = BGP_DEBUG(vpn, VPN_LEAK_LABEL); + + if (bgp->vpn_policy[afi].tovpn_sid) + return vpn_leak_zebra_vrf_sid_update_per_af(bgp, afi); + + if (bgp->tovpn_sid) + return vpn_leak_zebra_vrf_sid_update_per_vrf(bgp); + + if (debug) + zlog_debug("%s: vrf %s: afi %s: sid not set", __func__, + bgp->name_pretty, afi2str(afi)); +} + +/* + * If zebra tells us vrf has become unconfigured, tell zebra not to + * use this srv6-function to forward to the vrf anymore + */ +void vpn_leak_zebra_vrf_sid_withdraw_per_af(struct bgp *bgp, afi_t afi) +{ + int debug = BGP_DEBUG(vpn, VPN_LEAK_LABEL); + + if (bgp->vrf_id == VRF_UNKNOWN) { + if (debug) + zlog_debug("%s: vrf %s: afi %s: vrf_id not set, can't set zebra vrf label", + __func__, bgp->name_pretty, afi2str(afi)); + return; + } + + if (debug) + zlog_debug("%s: deleting sid for vrf %s afi (id=%d)", __func__, + bgp->name_pretty, bgp->vrf_id); + + zclient_send_localsid(zclient, + bgp->vpn_policy[afi].tovpn_zebra_vrf_sid_last_sent, + bgp->vrf_id, ZEBRA_SEG6_LOCAL_ACTION_UNSPEC, NULL); + XFREE(MTYPE_BGP_SRV6_SID, + bgp->vpn_policy[afi].tovpn_zebra_vrf_sid_last_sent); +} + +/* + * If zebra tells us vrf has become unconfigured, tell zebra not to + * use this srv6-function to forward to the vrf anymore + */ +void vpn_leak_zebra_vrf_sid_withdraw_per_vrf(struct bgp *bgp) +{ + int debug = BGP_DEBUG(vpn, VPN_LEAK_LABEL); + + if (bgp->vrf_id == VRF_UNKNOWN) { + if (debug) + zlog_debug( + "%s: vrf %s: vrf_id not set, can't set zebra vrf label", + __func__, bgp->name_pretty); + return; + } + + if (debug) + zlog_debug("%s: deleting sid for vrf %s (id=%d)", __func__, + bgp->name_pretty, bgp->vrf_id); + + zclient_send_localsid(zclient, bgp->tovpn_zebra_vrf_sid_last_sent, + bgp->vrf_id, ZEBRA_SEG6_LOCAL_ACTION_UNSPEC, + NULL); + XFREE(MTYPE_BGP_SRV6_SID, bgp->tovpn_zebra_vrf_sid_last_sent); +} + +/* + * If zebra tells us vrf has become unconfigured, tell zebra not to + * use this srv6-function to forward to the vrf anymore + */ +void vpn_leak_zebra_vrf_sid_withdraw(struct bgp *bgp, afi_t afi) +{ + if (bgp->vpn_policy[afi].tovpn_zebra_vrf_sid_last_sent) + vpn_leak_zebra_vrf_sid_withdraw_per_af(bgp, afi); + + if (bgp->tovpn_zebra_vrf_sid_last_sent) + vpn_leak_zebra_vrf_sid_withdraw_per_vrf(bgp); +} + +int vpn_leak_label_callback( + mpls_label_t label, + void *labelid, + bool allocated) +{ + struct vpn_policy *vp = (struct vpn_policy *)labelid; + int debug = BGP_DEBUG(vpn, VPN_LEAK_LABEL); + + if (debug) + zlog_debug("%s: label=%u, allocated=%d", + __func__, label, allocated); + + if (!allocated) { + /* + * previously-allocated label is now invalid + */ + if (CHECK_FLAG(vp->flags, BGP_VPN_POLICY_TOVPN_LABEL_AUTO) && + (vp->tovpn_label != MPLS_LABEL_NONE)) { + + vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, + vp->afi, bgp_get_default(), vp->bgp); + vp->tovpn_label = MPLS_LABEL_NONE; + vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, + vp->afi, bgp_get_default(), vp->bgp); + } + return 0; + } + + /* + * New label allocation + */ + if (!CHECK_FLAG(vp->flags, BGP_VPN_POLICY_TOVPN_LABEL_AUTO)) { + + /* + * not currently configured for auto label, reject allocation + */ + return -1; + } + + if (vp->tovpn_label != MPLS_LABEL_NONE) { + if (label == vp->tovpn_label) { + /* already have same label, accept but do nothing */ + return 0; + } + /* Shouldn't happen: different label allocation */ + flog_err(EC_BGP_LABEL, + "%s: %s had label %u but got new assignment %u", + __func__, vp->bgp->name_pretty, vp->tovpn_label, + label); + /* use new one */ + } + + vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, + vp->afi, bgp_get_default(), vp->bgp); + vp->tovpn_label = label; + vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, + vp->afi, bgp_get_default(), vp->bgp); + + return 0; +} + +static void sid_register(struct bgp *bgp, const struct in6_addr *sid, + const char *locator_name) +{ + struct bgp_srv6_function *func; + func = XCALLOC(MTYPE_BGP_SRV6_FUNCTION, + sizeof(struct bgp_srv6_function)); + func->sid = *sid; + snprintf(func->locator_name, sizeof(func->locator_name), + "%s", locator_name); + listnode_add(bgp->srv6_functions, func); +} + +void sid_unregister(struct bgp *bgp, const struct in6_addr *sid) +{ + struct listnode *node, *nnode; + struct bgp_srv6_function *func; + + for (ALL_LIST_ELEMENTS(bgp->srv6_functions, node, nnode, func)) + if (sid_same(&func->sid, sid)) { + listnode_delete(bgp->srv6_functions, func); + XFREE(MTYPE_BGP_SRV6_FUNCTION, func); + } +} + +static bool sid_exist(struct bgp *bgp, const struct in6_addr *sid) +{ + struct listnode *node; + struct bgp_srv6_function *func; + + for (ALL_LIST_ELEMENTS_RO(bgp->srv6_functions, node, func)) + if (sid_same(&func->sid, sid)) + return true; + return false; +} + +/* + * This function generates a new SID based on bgp->srv6_locator_chunks and + * index. The locator and generated SID are stored in arguments sid_locator + * and sid, respectively. + * + * if index != 0: try to allocate as index-mode + * else: try to allocate as auto-mode + */ +static uint32_t alloc_new_sid(struct bgp *bgp, uint32_t index, + struct srv6_locator_chunk *sid_locator_chunk, + struct in6_addr *sid) +{ + int debug = BGP_DEBUG(vpn, VPN_LEAK_LABEL); + struct listnode *node; + struct srv6_locator_chunk *chunk; + bool alloced = false; + int label = 0; + uint8_t offset = 0; + uint8_t func_len = 0, shift_len = 0; + uint32_t index_max = 0; + + if (!bgp || !sid_locator_chunk || !sid) + return false; + + for (ALL_LIST_ELEMENTS_RO(bgp->srv6_locator_chunks, node, chunk)) { + if (chunk->function_bits_length > + BGP_PREFIX_SID_SRV6_MAX_FUNCTION_LENGTH) { + if (debug) + zlog_debug( + "%s: invalid SRv6 Locator chunk (%pFX): Function Length must be less or equal to %d", + __func__, &chunk->prefix, + BGP_PREFIX_SID_SRV6_MAX_FUNCTION_LENGTH); + continue; + } + + index_max = (1 << chunk->function_bits_length) - 1; + + if (index > index_max) { + if (debug) + zlog_debug( + "%s: skipped SRv6 Locator chunk (%pFX): Function Length is too short to support specified index (%u)", + __func__, &chunk->prefix, index); + continue; + } + + *sid = chunk->prefix.prefix; + *sid_locator_chunk = *chunk; + offset = chunk->block_bits_length + chunk->node_bits_length; + func_len = chunk->function_bits_length; + shift_len = BGP_PREFIX_SID_SRV6_MAX_FUNCTION_LENGTH - func_len; + + if (index != 0) { + label = index << shift_len; + if (label < MPLS_LABEL_UNRESERVED_MIN) { + if (debug) + zlog_debug( + "%s: skipped to allocate SRv6 SID (%pFX): Label (%u) is too small to use", + __func__, &chunk->prefix, + label); + continue; + } + + transpose_sid(sid, label, offset, func_len); + if (sid_exist(bgp, sid)) + continue; + alloced = true; + break; + } + + for (uint32_t i = 1; i < index_max; i++) { + label = i << shift_len; + if (label < MPLS_LABEL_UNRESERVED_MIN) { + if (debug) + zlog_debug( + "%s: skipped to allocate SRv6 SID (%pFX): Label (%u) is too small to use", + __func__, &chunk->prefix, + label); + continue; + } + transpose_sid(sid, label, offset, func_len); + if (sid_exist(bgp, sid)) + continue; + alloced = true; + break; + } + } + + if (!alloced) + return 0; + + sid_register(bgp, sid, bgp->srv6_locator_name); + return label; +} + +void ensure_vrf_tovpn_sid_per_af(struct bgp *bgp_vpn, struct bgp *bgp_vrf, + afi_t afi) +{ + int debug = BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF); + struct srv6_locator_chunk *tovpn_sid_locator; + struct in6_addr *tovpn_sid; + uint32_t tovpn_sid_index = 0, tovpn_sid_transpose_label; + bool tovpn_sid_auto = false; + + if (debug) + zlog_debug("%s: try to allocate new SID for vrf %s: afi %s", + __func__, bgp_vrf->name_pretty, afi2str(afi)); + + /* skip when tovpn sid is already allocated on vrf instance */ + if (bgp_vrf->vpn_policy[afi].tovpn_sid) + return; + + /* + * skip when bgp vpn instance ins't allocated + * or srv6 locator chunk isn't allocated + */ + if (!bgp_vpn || !bgp_vpn->srv6_locator_chunks) + return; + + tovpn_sid_index = bgp_vrf->vpn_policy[afi].tovpn_sid_index; + tovpn_sid_auto = CHECK_FLAG(bgp_vrf->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_SID_AUTO); + + /* skip when VPN isn't configured on vrf-instance */ + if (tovpn_sid_index == 0 && !tovpn_sid_auto) + return; + + /* check invalid case both configured index and auto */ + if (tovpn_sid_index != 0 && tovpn_sid_auto) { + zlog_err("%s: index-mode and auto-mode both selected. ignored.", + __func__); + return; + } + + tovpn_sid_locator = srv6_locator_chunk_alloc(); + tovpn_sid = XCALLOC(MTYPE_BGP_SRV6_SID, sizeof(struct in6_addr)); + + tovpn_sid_transpose_label = alloc_new_sid(bgp_vpn, tovpn_sid_index, + tovpn_sid_locator, tovpn_sid); + + if (tovpn_sid_transpose_label == 0) { + if (debug) + zlog_debug( + "%s: not allocated new sid for vrf %s: afi %s", + __func__, bgp_vrf->name_pretty, afi2str(afi)); + srv6_locator_chunk_free(&tovpn_sid_locator); + XFREE(MTYPE_BGP_SRV6_SID, tovpn_sid); + return; + } + + if (debug) + zlog_debug("%s: new sid %pI6 allocated for vrf %s: afi %s", + __func__, tovpn_sid, bgp_vrf->name_pretty, + afi2str(afi)); + + bgp_vrf->vpn_policy[afi].tovpn_sid = tovpn_sid; + bgp_vrf->vpn_policy[afi].tovpn_sid_locator = tovpn_sid_locator; + bgp_vrf->vpn_policy[afi].tovpn_sid_transpose_label = + tovpn_sid_transpose_label; +} + +void ensure_vrf_tovpn_sid_per_vrf(struct bgp *bgp_vpn, struct bgp *bgp_vrf) +{ + int debug = BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF); + struct srv6_locator_chunk *tovpn_sid_locator; + struct in6_addr *tovpn_sid; + uint32_t tovpn_sid_index = 0, tovpn_sid_transpose_label; + bool tovpn_sid_auto = false; + + if (debug) + zlog_debug("%s: try to allocate new SID for vrf %s", __func__, + bgp_vrf->name_pretty); + + /* skip when tovpn sid is already allocated on vrf instance */ + if (bgp_vrf->tovpn_sid) + return; + + /* + * skip when bgp vpn instance ins't allocated + * or srv6 locator chunk isn't allocated + */ + if (!bgp_vpn || !bgp_vpn->srv6_locator_chunks) + return; + + tovpn_sid_index = bgp_vrf->tovpn_sid_index; + tovpn_sid_auto = CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_TOVPN_SID_AUTO); + + /* skip when VPN isn't configured on vrf-instance */ + if (tovpn_sid_index == 0 && !tovpn_sid_auto) + return; + + /* check invalid case both configured index and auto */ + if (tovpn_sid_index != 0 && tovpn_sid_auto) { + zlog_err("%s: index-mode and auto-mode both selected. ignored.", + __func__); + return; + } + + tovpn_sid_locator = srv6_locator_chunk_alloc(); + tovpn_sid = XCALLOC(MTYPE_BGP_SRV6_SID, sizeof(struct in6_addr)); + + tovpn_sid_transpose_label = alloc_new_sid(bgp_vpn, tovpn_sid_index, + tovpn_sid_locator, tovpn_sid); + + if (tovpn_sid_transpose_label == 0) { + if (debug) + zlog_debug("%s: not allocated new sid for vrf %s", + __func__, bgp_vrf->name_pretty); + srv6_locator_chunk_free(&tovpn_sid_locator); + XFREE(MTYPE_BGP_SRV6_SID, tovpn_sid); + return; + } + + if (debug) + zlog_debug("%s: new sid %pI6 allocated for vrf %s", __func__, + tovpn_sid, bgp_vrf->name_pretty); + + bgp_vrf->tovpn_sid = tovpn_sid; + bgp_vrf->tovpn_sid_locator = tovpn_sid_locator; + bgp_vrf->tovpn_sid_transpose_label = tovpn_sid_transpose_label; +} + +void ensure_vrf_tovpn_sid(struct bgp *bgp_vpn, struct bgp *bgp_vrf, afi_t afi) +{ + /* per-af sid */ + if (bgp_vrf->vpn_policy[afi].tovpn_sid_index != 0 || + CHECK_FLAG(bgp_vrf->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_SID_AUTO)) + return ensure_vrf_tovpn_sid_per_af(bgp_vpn, bgp_vrf, afi); + + /* per-vrf sid */ + if (bgp_vrf->tovpn_sid_index != 0 || + CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_TOVPN_SID_AUTO)) + return ensure_vrf_tovpn_sid_per_vrf(bgp_vpn, bgp_vrf); +} + +void delete_vrf_tovpn_sid_per_af(struct bgp *bgp_vpn, struct bgp *bgp_vrf, + afi_t afi) +{ + int debug = BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF); + uint32_t tovpn_sid_index = 0; + bool tovpn_sid_auto = false; + + if (debug) + zlog_debug("%s: try to remove SID for vrf %s: afi %s", __func__, + bgp_vrf->name_pretty, afi2str(afi)); + + tovpn_sid_index = bgp_vrf->vpn_policy[afi].tovpn_sid_index; + tovpn_sid_auto = CHECK_FLAG(bgp_vrf->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_SID_AUTO); + + /* skip when VPN is configured on vrf-instance */ + if (tovpn_sid_index != 0 || tovpn_sid_auto) + return; + + srv6_locator_chunk_free(&bgp_vrf->vpn_policy[afi].tovpn_sid_locator); + + if (bgp_vrf->vpn_policy[afi].tovpn_sid) { + sid_unregister(bgp_vpn, bgp_vrf->vpn_policy[afi].tovpn_sid); + XFREE(MTYPE_BGP_SRV6_SID, bgp_vrf->vpn_policy[afi].tovpn_sid); + } + bgp_vrf->vpn_policy[afi].tovpn_sid_transpose_label = 0; +} + +void delete_vrf_tovpn_sid_per_vrf(struct bgp *bgp_vpn, struct bgp *bgp_vrf) +{ + int debug = BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF); + uint32_t tovpn_sid_index = 0; + bool tovpn_sid_auto = false; + + if (debug) + zlog_debug("%s: try to remove SID for vrf %s", __func__, + bgp_vrf->name_pretty); + + tovpn_sid_index = bgp_vrf->tovpn_sid_index; + tovpn_sid_auto = + CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VPN_POLICY_TOVPN_SID_AUTO); + + /* skip when VPN is configured on vrf-instance */ + if (tovpn_sid_index != 0 || tovpn_sid_auto) + return; + + srv6_locator_chunk_free(&bgp_vrf->tovpn_sid_locator); + + if (bgp_vrf->tovpn_sid) { + sid_unregister(bgp_vpn, bgp_vrf->tovpn_sid); + XFREE(MTYPE_BGP_SRV6_SID, bgp_vrf->tovpn_sid); + } + bgp_vrf->tovpn_sid_transpose_label = 0; +} + +void delete_vrf_tovpn_sid(struct bgp *bgp_vpn, struct bgp *bgp_vrf, afi_t afi) +{ + delete_vrf_tovpn_sid_per_af(bgp_vpn, bgp_vrf, afi); + delete_vrf_tovpn_sid_per_vrf(bgp_vpn, bgp_vrf); +} + +/* + * This function embeds upper `len` bits of `label` in `sid`, + * starting at offset `offset` as seen from the MSB of `sid`. + * + * e.g. Given that `label` is 0x12345 and `len` is 16, + * then `label` will be embedded in `sid` as follows: + * + * <---- len -----> + * label: 0001 0002 0003 0004 0005 + * sid: .... 0001 0002 0003 0004 + * <---- len -----> + * ^ + * | + * offset from MSB + * + * e.g. Given that `label` is 0x12345 and `len` is 8, + * `label` will be embedded in `sid` as follows: + * + * <- len -> + * label: 0001 0002 0003 0004 0005 + * sid: .... 0001 0002 0000 0000 + * <- len -> + * ^ + * | + * offset from MSB + */ +void transpose_sid(struct in6_addr *sid, uint32_t label, uint8_t offset, + uint8_t len) +{ + for (uint8_t idx = 0; idx < len; idx++) { + uint8_t tidx = offset + idx; + sid->s6_addr[tidx / 8] &= ~(0x1 << (7 - tidx % 8)); + if (label >> (19 - idx) & 0x1) + sid->s6_addr[tidx / 8] |= 0x1 << (7 - tidx % 8); + } +} + +static bool labels_same(struct bgp_path_info *bpi, mpls_label_t *label, + uint32_t n) +{ + if (!bpi->extra) { + if (!n) + return true; + else + return false; + } + + return bgp_labels_same((const mpls_label_t *)bpi->extra->label, + bpi->extra->num_labels, + (const mpls_label_t *)label, n); +} + +/* + * make encoded route labels match specified encoded label set + */ +static void setlabels(struct bgp_path_info *bpi, + mpls_label_t *label, /* array of labels */ + uint32_t num_labels) +{ + if (num_labels) + assert(label); + assert(num_labels <= BGP_MAX_LABELS); + + if (!num_labels) { + if (bpi->extra) + bpi->extra->num_labels = 0; + return; + } + + struct bgp_path_info_extra *extra = bgp_path_info_extra_get(bpi); + uint32_t i; + + for (i = 0; i < num_labels; ++i) { + extra->label[i] = label[i]; + if (!bgp_is_valid_label(&label[i])) { + bgp_set_valid_label(&extra->label[i]); + } + } + extra->num_labels = num_labels; +} + +static bool leak_update_nexthop_valid(struct bgp *to_bgp, struct bgp_dest *bn, + struct attr *new_attr, afi_t afi, + safi_t safi, + struct bgp_path_info *source_bpi, + struct bgp_path_info *bpi, + struct bgp *bgp_orig, + const struct prefix *p, int debug) +{ + struct bgp_path_info *bpi_ultimate; + struct bgp *bgp_nexthop; + bool nh_valid; + + bpi_ultimate = bgp_get_imported_bpi_ultimate(source_bpi); + + if (bpi->extra && bpi->extra->vrfleak && bpi->extra->vrfleak->bgp_orig) + bgp_nexthop = bpi->extra->vrfleak->bgp_orig; + else + bgp_nexthop = bgp_orig; + + /* + * No nexthop tracking for redistributed routes, for + * EVPN-imported routes that get leaked, or for routes + * leaked between VRFs with accept-own community. + */ + if (bpi_ultimate->sub_type == BGP_ROUTE_REDISTRIBUTE || + is_pi_family_evpn(bpi_ultimate) || + CHECK_FLAG(bpi_ultimate->flags, BGP_PATH_ACCEPT_OWN)) + nh_valid = true; + else + /* + * TBD do we need to do anything about the + * 'connected' parameter? + */ + nh_valid = bgp_find_or_add_nexthop(to_bgp, bgp_nexthop, afi, + safi, bpi, NULL, 0, p); + + /* + * If you are using SRv6 VPN instead of MPLS, it need to check + * the SID allocation. If the sid is not allocated, the rib + * will be invalid. + */ + if (to_bgp->srv6_enabled && + (!new_attr->srv6_l3vpn && !new_attr->srv6_vpn)) { + nh_valid = false; + } + + if (debug) + zlog_debug("%s: %pFX nexthop is %svalid (in %s)", __func__, p, + (nh_valid ? "" : "not "), bgp_nexthop->name_pretty); + + return nh_valid; +} + +/* + * returns pointer to new bgp_path_info upon success + */ +static struct bgp_path_info * +leak_update(struct bgp *to_bgp, struct bgp_dest *bn, + struct attr *new_attr, /* already interned */ + afi_t afi, safi_t safi, struct bgp_path_info *source_bpi, + mpls_label_t *label, uint32_t num_labels, struct bgp *bgp_orig, + struct prefix *nexthop_orig, int nexthop_self_flag, int debug) +{ + const struct prefix *p = bgp_dest_get_prefix(bn); + struct bgp_path_info *bpi; + struct bgp_path_info *new; + struct bgp_path_info_extra *extra; + struct bgp_path_info *parent = source_bpi; + + if (debug) + zlog_debug( + "%s: entry: leak-to=%s, p=%pBD, type=%d, sub_type=%d", + __func__, to_bgp->name_pretty, bn, source_bpi->type, + source_bpi->sub_type); + + /* + * Routes that are redistributed into BGP from zebra do not get + * nexthop tracking, unless MPLS allocation per nexthop is + * performed. In the default case nexthop tracking does not apply, + * if those routes are subsequently imported to other RIBs within + * BGP, the leaked routes do not carry the original + * BGP_ROUTE_REDISTRIBUTE sub_type. Therefore, in order to determine + * if the route we are currently leaking should have nexthop + * tracking, we must find the ultimate parent so we can check its + * sub_type. + * + * As of now, source_bpi may at most be a second-generation route + * (only one hop back to ultimate parent for vrf-vpn-vrf scheme). + * Using a loop here supports more complex intra-bgp import-export + * schemes that could be implemented in the future. + * + */ + + /* + * match parent + */ + for (bpi = bgp_dest_get_bgp_path_info(bn); bpi; bpi = bpi->next) { + if (bpi->extra && bpi->extra->vrfleak && + bpi->extra->vrfleak->parent == parent) + break; + } + + if (bpi) { + bool labelssame = labels_same(bpi, label, num_labels); + + if (CHECK_FLAG(source_bpi->flags, BGP_PATH_REMOVED) + && CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) { + if (debug) { + zlog_debug( + "%s: ->%s(s_flags: 0x%x b_flags: 0x%x): %pFX: Found route, being removed, not leaking", + __func__, to_bgp->name_pretty, + source_bpi->flags, bpi->flags, p); + } + return NULL; + } + + if (attrhash_cmp(bpi->attr, new_attr) && labelssame + && !CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) { + + bgp_attr_unintern(&new_attr); + if (debug) + zlog_debug( + "%s: ->%s: %pBD: Found route, no change", + __func__, to_bgp->name_pretty, bn); + return NULL; + } + + /* If the RT was changed via extended communities as an + * import/export list, we should withdraw implicitly the old + * path from VRFs. + * For instance, RT list was modified using route-maps: + * route-map test permit 10 + * set extcommunity rt none + */ + if (CHECK_FLAG(bpi->attr->flag, + ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES)) && + CHECK_FLAG(new_attr->flag, + ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES))) { + if (!ecommunity_cmp( + bgp_attr_get_ecommunity(bpi->attr), + bgp_attr_get_ecommunity(new_attr))) { + vpn_leak_to_vrf_withdraw(bpi); + bgp_aggregate_decrement(to_bgp, p, bpi, afi, + safi); + bgp_path_info_delete(bn, bpi); + } + } + + /* attr is changed */ + bgp_path_info_set_flag(bn, bpi, BGP_PATH_ATTR_CHANGED); + + /* Rewrite BGP route information. */ + if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) + bgp_path_info_restore(bn, bpi); + else + bgp_aggregate_decrement(to_bgp, p, bpi, afi, safi); + bgp_attr_unintern(&bpi->attr); + bpi->attr = new_attr; + bpi->uptime = monotime(NULL); + + /* + * rewrite labels + */ + if (!labelssame) + setlabels(bpi, label, num_labels); + + + if (nexthop_self_flag) + bgp_path_info_set_flag(bn, bpi, BGP_PATH_ANNC_NH_SELF); + + if (CHECK_FLAG(source_bpi->flags, BGP_PATH_ACCEPT_OWN)) + bgp_path_info_set_flag(bn, bpi, BGP_PATH_ACCEPT_OWN); + + if (leak_update_nexthop_valid(to_bgp, bn, new_attr, afi, safi, + source_bpi, bpi, bgp_orig, p, + debug)) + bgp_path_info_set_flag(bn, bpi, BGP_PATH_VALID); + else + bgp_path_info_unset_flag(bn, bpi, BGP_PATH_VALID); + + /* Process change. */ + bgp_aggregate_increment(to_bgp, p, bpi, afi, safi); + bgp_process(to_bgp, bn, afi, safi); + + if (debug) + zlog_debug("%s: ->%s: %pBD Found route, changed attr", + __func__, to_bgp->name_pretty, bn); + + bgp_dest_unlock_node(bn); + + return bpi; + } + + if (CHECK_FLAG(source_bpi->flags, BGP_PATH_REMOVED)) { + if (debug) { + zlog_debug( + "%s: ->%s(s_flags: 0x%x): %pFX: New route, being removed, not leaking", + __func__, to_bgp->name_pretty, + source_bpi->flags, p); + } + return NULL; + } + + new = info_make(ZEBRA_ROUTE_BGP, BGP_ROUTE_IMPORTED, 0, + to_bgp->peer_self, new_attr, bn); + + bgp_path_info_extra_get(new); + if (!new->extra->vrfleak) + new->extra->vrfleak = + XCALLOC(MTYPE_BGP_ROUTE_EXTRA_VRFLEAK, + sizeof(struct bgp_path_info_extra_vrfleak)); + + if (source_bpi->peer) { + extra = bgp_path_info_extra_get(new); + extra->vrfleak->peer_orig = peer_lock(source_bpi->peer); + } + + if (nexthop_self_flag) + bgp_path_info_set_flag(bn, new, BGP_PATH_ANNC_NH_SELF); + + if (CHECK_FLAG(source_bpi->flags, BGP_PATH_ACCEPT_OWN)) + bgp_path_info_set_flag(bn, new, BGP_PATH_ACCEPT_OWN); + + if (num_labels) + setlabels(new, label, num_labels); + + new->extra->vrfleak->parent = bgp_path_info_lock(parent); + bgp_dest_lock_node( + (struct bgp_dest *)parent->net); + if (bgp_orig) + new->extra->vrfleak->bgp_orig = bgp_lock(bgp_orig); + if (nexthop_orig) + new->extra->vrfleak->nexthop_orig = *nexthop_orig; + + if (leak_update_nexthop_valid(to_bgp, bn, new_attr, afi, safi, + source_bpi, new, bgp_orig, p, debug)) + bgp_path_info_set_flag(bn, new, BGP_PATH_VALID); + else + bgp_path_info_unset_flag(bn, new, BGP_PATH_VALID); + + bgp_aggregate_increment(to_bgp, p, new, afi, safi); + bgp_path_info_add(bn, new); + + bgp_process(to_bgp, bn, afi, safi); + + if (debug) + zlog_debug("%s: ->%s: %pBD: Added new route", __func__, + to_bgp->name_pretty, bn); + + bgp_dest_unlock_node(bn); + + return new; +} + +void bgp_mplsvpn_path_nh_label_unlink(struct bgp_path_info *pi) +{ + struct bgp_label_per_nexthop_cache *blnc; + + if (!pi) + return; + + if (!CHECK_FLAG(pi->flags, BGP_PATH_MPLSVPN_LABEL_NH)) + return; + + blnc = pi->mplsvpn.blnc.label_nexthop_cache; + + if (!blnc) + return; + + LIST_REMOVE(pi, mplsvpn.blnc.label_nh_thread); + pi->mplsvpn.blnc.label_nexthop_cache->path_count--; + pi->mplsvpn.blnc.label_nexthop_cache = NULL; + UNSET_FLAG(pi->flags, BGP_PATH_MPLSVPN_LABEL_NH); + + if (LIST_EMPTY(&(blnc->paths))) + bgp_label_per_nexthop_free(blnc); +} + +/* Called upon reception of a ZAPI Message from zebra, about + * a new available label. + */ +static int bgp_mplsvpn_get_label_per_nexthop_cb(mpls_label_t label, + void *context, bool allocated) +{ + struct bgp_label_per_nexthop_cache *blnc = context; + mpls_label_t old_label; + int debug = BGP_DEBUG(vpn, VPN_LEAK_LABEL); + struct bgp_path_info *pi; + struct bgp_table *table; + + old_label = blnc->label; + + if (debug) + zlog_debug("%s: label=%u, allocated=%d, nexthop=%pFX", __func__, + label, allocated, &blnc->nexthop); + if (allocated) + /* update the entry with the new label */ + blnc->label = label; + else + /* + * previously-allocated label is now invalid + * eg: zebra deallocated the labels and notifies it + */ + blnc->label = MPLS_INVALID_LABEL; + + if (old_label == blnc->label) + return 0; /* no change */ + + /* update paths */ + if (blnc->label != MPLS_INVALID_LABEL) + bgp_zebra_send_nexthop_label(ZEBRA_MPLS_LABELS_ADD, blnc->label, + blnc->nh->ifindex, + blnc->nh->vrf_id, ZEBRA_LSP_BGP, + &blnc->nexthop, 0, NULL); + + LIST_FOREACH (pi, &(blnc->paths), mplsvpn.blnc.label_nh_thread) { + if (!pi->net) + continue; + table = bgp_dest_table(pi->net); + if (!table) + continue; + vpn_leak_from_vrf_update(blnc->to_bgp, table->bgp, pi); + } + + return 0; +} + +/* Get a per label nexthop value: + * - Find and return a per label nexthop from the cache + * - else allocate a new per label nexthop cache entry and request a + * label to zebra. Return MPLS_INVALID_LABEL + */ +static mpls_label_t +_vpn_leak_from_vrf_get_per_nexthop_label(struct bgp_path_info *pi, + struct bgp *to_bgp, + struct bgp *from_bgp, afi_t afi) +{ + struct bgp_nexthop_cache *bnc = pi->nexthop; + struct bgp_label_per_nexthop_cache *blnc; + struct bgp_label_per_nexthop_cache_head *tree; + struct prefix *nh_pfx = NULL; + struct prefix nh_gate = {0}; + + /* extract the nexthop from the BNC nexthop cache */ + switch (bnc->nexthop->type) { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + /* the nexthop is recursive */ + nh_gate.family = AF_INET; + nh_gate.prefixlen = IPV4_MAX_BITLEN; + IPV4_ADDR_COPY(&nh_gate.u.prefix4, &bnc->nexthop->gate.ipv4); + nh_pfx = &nh_gate; + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + /* the nexthop is recursive */ + nh_gate.family = AF_INET6; + nh_gate.prefixlen = IPV6_MAX_BITLEN; + IPV6_ADDR_COPY(&nh_gate.u.prefix6, &bnc->nexthop->gate.ipv6); + nh_pfx = &nh_gate; + break; + case NEXTHOP_TYPE_IFINDEX: + /* the nexthop is direcly connected */ + nh_pfx = &bnc->prefix; + break; + case NEXTHOP_TYPE_BLACKHOLE: + assert(!"Blackhole nexthop. Already checked by the caller."); + } + + /* find or allocate a nexthop label cache entry */ + tree = &from_bgp->mpls_labels_per_nexthop[family2afi(nh_pfx->family)]; + blnc = bgp_label_per_nexthop_find(tree, nh_pfx); + if (!blnc) { + blnc = bgp_label_per_nexthop_new(tree, nh_pfx); + blnc->to_bgp = to_bgp; + /* request a label to zebra for this nexthop + * the response from zebra will trigger the callback + */ + bgp_lp_get(LP_TYPE_NEXTHOP, blnc, + bgp_mplsvpn_get_label_per_nexthop_cb); + } + + if (pi->mplsvpn.blnc.label_nexthop_cache == blnc) + /* no change */ + return blnc->label; + + /* Unlink from any existing nexthop cache. Free the entry if unused. + */ + bgp_mplsvpn_path_nh_label_unlink(pi); + + /* updates NHT pi list reference */ + LIST_INSERT_HEAD(&(blnc->paths), pi, mplsvpn.blnc.label_nh_thread); + pi->mplsvpn.blnc.label_nexthop_cache = blnc; + pi->mplsvpn.blnc.label_nexthop_cache->path_count++; + SET_FLAG(pi->flags, BGP_PATH_MPLSVPN_LABEL_NH); + blnc->last_update = monotime(NULL); + + /* then add or update the selected nexthop */ + if (!blnc->nh) + blnc->nh = nexthop_dup(bnc->nexthop, NULL); + else if (!nexthop_same(bnc->nexthop, blnc->nh)) { + nexthop_free(blnc->nh); + blnc->nh = nexthop_dup(bnc->nexthop, NULL); + if (blnc->label != MPLS_INVALID_LABEL) { + bgp_zebra_send_nexthop_label( + ZEBRA_MPLS_LABELS_REPLACE, blnc->label, + bnc->nexthop->ifindex, bnc->nexthop->vrf_id, + ZEBRA_LSP_BGP, &blnc->nexthop, 0, NULL); + } + } + + return blnc->label; +} + +/* Filter out all the cases where a per nexthop label is not possible: + * - return an invalid label when the nexthop is invalid + * - return the per VRF label when the per nexthop label is not supported + * Otherwise, find or request a per label nexthop. + */ +static mpls_label_t +vpn_leak_from_vrf_get_per_nexthop_label(afi_t afi, struct bgp_path_info *pi, + struct bgp *from_bgp, + struct bgp *to_bgp) +{ + struct bgp_path_info *bpi_ultimate = bgp_get_imported_bpi_ultimate(pi); + struct bgp *bgp_nexthop = NULL; + bool nh_valid; + afi_t nh_afi; + bool is_bgp_static_route; + + is_bgp_static_route = bpi_ultimate->sub_type == BGP_ROUTE_STATIC && + bpi_ultimate->type == ZEBRA_ROUTE_BGP; + + if (is_bgp_static_route == false && afi == AFI_IP && + CHECK_FLAG(pi->attr->flag, ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP)) && + (pi->attr->nexthop.s_addr == INADDR_ANY || + !ipv4_unicast_valid(&pi->attr->nexthop))) { + /* IPv4 nexthop in standard BGP encoding format. + * Format of address is not valid (not any, not unicast). + * Fallback to the per VRF label. + */ + bgp_mplsvpn_path_nh_label_unlink(pi); + return from_bgp->vpn_policy[afi].tovpn_label; + } + + if (is_bgp_static_route == false && afi == AFI_IP && + pi->attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV4 && + (pi->attr->mp_nexthop_global_in.s_addr == INADDR_ANY || + !ipv4_unicast_valid(&pi->attr->mp_nexthop_global_in))) { + /* IPv4 nexthop is in MP-BGP encoding format. + * Format of address is not valid (not any, not unicast). + * Fallback to the per VRF label. + */ + bgp_mplsvpn_path_nh_label_unlink(pi); + return from_bgp->vpn_policy[afi].tovpn_label; + } + + if (is_bgp_static_route == false && afi == AFI_IP6 && + (pi->attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL || + pi->attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL) && + (IN6_IS_ADDR_UNSPECIFIED(&pi->attr->mp_nexthop_global) || + IN6_IS_ADDR_LOOPBACK(&pi->attr->mp_nexthop_global) || + IN6_IS_ADDR_MULTICAST(&pi->attr->mp_nexthop_global))) { + /* IPv6 nexthop is in MP-BGP encoding format. + * Format of address is not valid + * Fallback to the per VRF label. + */ + bgp_mplsvpn_path_nh_label_unlink(pi); + return from_bgp->vpn_policy[afi].tovpn_label; + } + + /* Check the next-hop reachability. + * Get the bgp instance where the bgp_path_info originates. + */ + if (pi->extra && pi->extra->vrfleak && pi->extra->vrfleak->bgp_orig) + bgp_nexthop = pi->extra->vrfleak->bgp_orig; + else + bgp_nexthop = from_bgp; + + nh_afi = BGP_ATTR_NH_AFI(afi, pi->attr); + nh_valid = bgp_find_or_add_nexthop(from_bgp, bgp_nexthop, nh_afi, + SAFI_UNICAST, pi, NULL, 0, NULL); + + if (!nh_valid && is_bgp_static_route && + !CHECK_FLAG(from_bgp->flags, BGP_FLAG_IMPORT_CHECK)) { + /* "network" prefixes not routable, but since 'no bgp network + * import-check' is configured, they are always valid in the BGP + * table. Fallback to the per-vrf label + */ + bgp_mplsvpn_path_nh_label_unlink(pi); + return from_bgp->vpn_policy[afi].tovpn_label; + } + + if (!nh_valid || !pi->nexthop || pi->nexthop->nexthop_num == 0 || + !pi->nexthop->nexthop) { + /* invalid next-hop: + * do not send the per-vrf label + * otherwise, when the next-hop becomes valid, + * we will have 2 BGP updates: + * - one with the per-vrf label + * - the second with the per-nexthop label + */ + bgp_mplsvpn_path_nh_label_unlink(pi); + return MPLS_INVALID_LABEL; + } + + if (pi->nexthop->nexthop_num > 1 || + pi->nexthop->nexthop->type == NEXTHOP_TYPE_BLACKHOLE) { + /* Blackhole or ECMP routes + * is not compatible with per-nexthop label. + * Fallback to per-vrf label. + */ + bgp_mplsvpn_path_nh_label_unlink(pi); + return from_bgp->vpn_policy[afi].tovpn_label; + } + + return _vpn_leak_from_vrf_get_per_nexthop_label(pi, to_bgp, from_bgp, + afi); +} + +/* cf vnc_import_bgp_add_route_mode_nvegroup() and add_vnc_route() */ +void vpn_leak_from_vrf_update(struct bgp *to_bgp, /* to */ + struct bgp *from_bgp, /* from */ + struct bgp_path_info *path_vrf) /* route */ +{ + int debug = BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF); + const struct prefix *p = bgp_dest_get_prefix(path_vrf->net); + afi_t afi = family2afi(p->family); + struct attr static_attr = {0}; + struct attr *new_attr = NULL; + safi_t safi = SAFI_MPLS_VPN; + mpls_label_t label_val; + mpls_label_t label; + struct bgp_dest *bn; + const char *debugmsg; + int nexthop_self_flag = 0; + + if (debug) + zlog_debug("%s: from vrf %s", __func__, from_bgp->name_pretty); + + if (debug && bgp_attr_get_ecommunity(path_vrf->attr)) { + char *s = ecommunity_ecom2str( + bgp_attr_get_ecommunity(path_vrf->attr), + ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + + zlog_debug("%s: %s path_vrf->type=%d, EC{%s}", __func__, + from_bgp->name, path_vrf->type, s); + XFREE(MTYPE_ECOMMUNITY_STR, s); + } + + if (!to_bgp) + return; + + if (!afi) { + if (debug) + zlog_debug("%s: can't get afi of prefix", __func__); + return; + } + + /* Is this route exportable into the VPN table? */ + if (!is_route_injectable_into_vpn(path_vrf)) + return; + + if (!vpn_leak_to_vpn_active(from_bgp, afi, &debugmsg)) { + if (debug) + zlog_debug("%s: %s skipping: %s", __func__, + from_bgp->name, debugmsg); + return; + } + + /* shallow copy */ + static_attr = *path_vrf->attr; + + /* + * route map handling + */ + if (from_bgp->vpn_policy[afi].rmap[BGP_VPN_POLICY_DIR_TOVPN]) { + struct bgp_path_info info; + route_map_result_t ret; + + memset(&info, 0, sizeof(info)); + info.peer = to_bgp->peer_self; + info.attr = &static_attr; + ret = route_map_apply(from_bgp->vpn_policy[afi] + .rmap[BGP_VPN_POLICY_DIR_TOVPN], + p, &info); + if (RMAP_DENYMATCH == ret) { + bgp_attr_flush(&static_attr); /* free any added parts */ + if (debug) + zlog_debug( + "%s: vrf %s route map \"%s\" says DENY, returning", + __func__, from_bgp->name_pretty, + from_bgp->vpn_policy[afi] + .rmap[BGP_VPN_POLICY_DIR_TOVPN] + ->name); + return; + } + } + + if (debug && bgp_attr_get_ecommunity(&static_attr)) { + char *s = ecommunity_ecom2str( + bgp_attr_get_ecommunity(&static_attr), + ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + + zlog_debug("%s: post route map static_attr.ecommunity{%s}", + __func__, s); + XFREE(MTYPE_ECOMMUNITY_STR, s); + } + + /* + * Add the vpn-policy rt-list + */ + struct ecommunity *old_ecom; + struct ecommunity *new_ecom; + + /* Export with the 'from' instance's export RTs. */ + /* If doing VRF-to-VRF leaking, strip existing RTs first. */ + old_ecom = bgp_attr_get_ecommunity(&static_attr); + if (old_ecom) { + new_ecom = ecommunity_dup(old_ecom); + if (CHECK_FLAG(from_bgp->af_flags[afi][SAFI_UNICAST], + BGP_CONFIG_VRF_TO_VRF_EXPORT)) + ecommunity_strip_rts(new_ecom); + new_ecom = ecommunity_merge( + new_ecom, from_bgp->vpn_policy[afi] + .rtlist[BGP_VPN_POLICY_DIR_TOVPN]); + if (!old_ecom->refcnt) + ecommunity_free(&old_ecom); + } else { + new_ecom = ecommunity_dup( + from_bgp->vpn_policy[afi] + .rtlist[BGP_VPN_POLICY_DIR_TOVPN]); + } + bgp_attr_set_ecommunity(&static_attr, new_ecom); + + if (debug && bgp_attr_get_ecommunity(&static_attr)) { + char *s = ecommunity_ecom2str( + bgp_attr_get_ecommunity(&static_attr), + ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + + zlog_debug("%s: post merge static_attr.ecommunity{%s}", + __func__, s); + XFREE(MTYPE_ECOMMUNITY_STR, s); + } + + community_strip_accept_own(&static_attr); + + /* Nexthop */ + /* if policy nexthop not set, use 0 */ + if (CHECK_FLAG(from_bgp->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_NEXTHOP_SET)) { + struct prefix *nexthop = + &from_bgp->vpn_policy[afi].tovpn_nexthop; + + switch (nexthop->family) { + case AF_INET: + /* prevent mp_nexthop_global_in <- self in bgp_route.c + */ + static_attr.nexthop.s_addr = nexthop->u.prefix4.s_addr; + + static_attr.mp_nexthop_global_in = nexthop->u.prefix4; + static_attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4; + break; + + case AF_INET6: + static_attr.mp_nexthop_global = nexthop->u.prefix6; + static_attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL; + break; + + default: + assert(0); + } + } else { + if (!CHECK_FLAG(from_bgp->af_flags[afi][SAFI_UNICAST], + BGP_CONFIG_VRF_TO_VRF_EXPORT)) { + if (afi == AFI_IP && + !BGP_ATTR_NEXTHOP_AFI_IP6(path_vrf->attr)) { + /* + * For ipv4, copy to multiprotocol + * nexthop field + */ + static_attr.mp_nexthop_global_in = + static_attr.nexthop; + static_attr.mp_nexthop_len = + BGP_ATTR_NHLEN_IPV4; + /* + * XXX Leave static_attr.nexthop + * intact for NHT + */ + static_attr.flag &= + ~ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP); + } + } else { + /* Update based on next-hop family to account for + * RFC 5549 (BGP unnumbered) scenario. Note that + * specific action is only needed for the case of + * IPv4 nexthops as the attr has been copied + * otherwise. + */ + if (afi == AFI_IP + && !BGP_ATTR_NEXTHOP_AFI_IP6(path_vrf->attr)) { + static_attr.mp_nexthop_global_in.s_addr = + static_attr.nexthop.s_addr; + static_attr.mp_nexthop_len = + BGP_ATTR_NHLEN_IPV4; + static_attr.flag |= + ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP); + } + } + nexthop_self_flag = 1; + } + + if (CHECK_FLAG(from_bgp->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_LABEL_PER_NEXTHOP)) + /* per nexthop label mode */ + label_val = vpn_leak_from_vrf_get_per_nexthop_label( + afi, path_vrf, from_bgp, to_bgp); + else + /* per VRF label mode */ + label_val = from_bgp->vpn_policy[afi].tovpn_label; + + if (label_val == MPLS_INVALID_LABEL && + CHECK_FLAG(from_bgp->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_LABEL_PER_NEXTHOP)) { + /* no valid label for the moment + * when the 'bgp_mplsvpn_get_label_per_nexthop_cb' callback gets + * a valid label value, it will call the current function again. + */ + if (debug) + zlog_debug( + "%s: %s skipping: waiting for a valid per-label nexthop.", + __func__, from_bgp->name_pretty); + bgp_attr_flush(&static_attr); + return; + } + if (label_val == MPLS_LABEL_NONE) + encode_label(MPLS_LABEL_IMPLICIT_NULL, &label); + else + encode_label(label_val, &label); + + /* Set originator ID to "me" */ + SET_FLAG(static_attr.flag, ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID)); + static_attr.originator_id = to_bgp->router_id; + + /* Set SID for SRv6 VPN */ + if (from_bgp->vpn_policy[afi].tovpn_sid_locator) { + struct srv6_locator_chunk *locator = + from_bgp->vpn_policy[afi].tovpn_sid_locator; + encode_label( + from_bgp->vpn_policy[afi].tovpn_sid_transpose_label, + &label); + static_attr.srv6_l3vpn = XCALLOC(MTYPE_BGP_SRV6_L3VPN, + sizeof(struct bgp_attr_srv6_l3vpn)); + static_attr.srv6_l3vpn->sid_flags = 0x00; + static_attr.srv6_l3vpn->endpoint_behavior = + afi == AFI_IP + ? (CHECK_FLAG(locator->flags, SRV6_LOCATOR_USID) + ? SRV6_ENDPOINT_BEHAVIOR_END_DT4_USID + : SRV6_ENDPOINT_BEHAVIOR_END_DT4) + : (CHECK_FLAG(locator->flags, SRV6_LOCATOR_USID) + ? SRV6_ENDPOINT_BEHAVIOR_END_DT6_USID + : SRV6_ENDPOINT_BEHAVIOR_END_DT6); + static_attr.srv6_l3vpn->loc_block_len = + from_bgp->vpn_policy[afi] + .tovpn_sid_locator->block_bits_length; + static_attr.srv6_l3vpn->loc_node_len = + from_bgp->vpn_policy[afi] + .tovpn_sid_locator->node_bits_length; + static_attr.srv6_l3vpn->func_len = + from_bgp->vpn_policy[afi] + .tovpn_sid_locator->function_bits_length; + static_attr.srv6_l3vpn->arg_len = + from_bgp->vpn_policy[afi] + .tovpn_sid_locator->argument_bits_length; + static_attr.srv6_l3vpn->transposition_len = + from_bgp->vpn_policy[afi] + .tovpn_sid_locator->function_bits_length; + static_attr.srv6_l3vpn->transposition_offset = + from_bgp->vpn_policy[afi] + .tovpn_sid_locator->block_bits_length + + from_bgp->vpn_policy[afi] + .tovpn_sid_locator->node_bits_length; + ; + memcpy(&static_attr.srv6_l3vpn->sid, + &from_bgp->vpn_policy[afi] + .tovpn_sid_locator->prefix.prefix, + sizeof(struct in6_addr)); + } else if (from_bgp->tovpn_sid_locator) { + struct srv6_locator_chunk *locator = + from_bgp->tovpn_sid_locator; + encode_label(from_bgp->tovpn_sid_transpose_label, &label); + static_attr.srv6_l3vpn = + XCALLOC(MTYPE_BGP_SRV6_L3VPN, + sizeof(struct bgp_attr_srv6_l3vpn)); + static_attr.srv6_l3vpn->sid_flags = 0x00; + static_attr.srv6_l3vpn->endpoint_behavior = + CHECK_FLAG(locator->flags, SRV6_LOCATOR_USID) + ? SRV6_ENDPOINT_BEHAVIOR_END_DT46_USID + : SRV6_ENDPOINT_BEHAVIOR_END_DT46; + static_attr.srv6_l3vpn->loc_block_len = + from_bgp->tovpn_sid_locator->block_bits_length; + static_attr.srv6_l3vpn->loc_node_len = + from_bgp->tovpn_sid_locator->node_bits_length; + static_attr.srv6_l3vpn->func_len = + from_bgp->tovpn_sid_locator->function_bits_length; + static_attr.srv6_l3vpn->arg_len = + from_bgp->tovpn_sid_locator->argument_bits_length; + static_attr.srv6_l3vpn->transposition_len = + from_bgp->tovpn_sid_locator->function_bits_length; + static_attr.srv6_l3vpn->transposition_offset = + from_bgp->tovpn_sid_locator->block_bits_length + + from_bgp->tovpn_sid_locator->node_bits_length; + memcpy(&static_attr.srv6_l3vpn->sid, + &from_bgp->tovpn_sid_locator->prefix.prefix, + sizeof(struct in6_addr)); + } + + + new_attr = bgp_attr_intern( + &static_attr); /* hashed refcounted everything */ + bgp_attr_flush(&static_attr); /* free locally-allocated parts */ + + if (debug && bgp_attr_get_ecommunity(new_attr)) { + char *s = ecommunity_ecom2str(bgp_attr_get_ecommunity(new_attr), + ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + + zlog_debug("%s: new_attr->ecommunity{%s}", __func__, s); + XFREE(MTYPE_ECOMMUNITY_STR, s); + } + + /* Now new_attr is an allocated interned attr */ + + bn = bgp_afi_node_get(to_bgp->rib[afi][safi], afi, safi, p, + &(from_bgp->vpn_policy[afi].tovpn_rd)); + + struct bgp_path_info *new_info; + + new_info = + leak_update(to_bgp, bn, new_attr, afi, safi, path_vrf, &label, + 1, from_bgp, NULL, nexthop_self_flag, debug); + + /* + * Routes actually installed in the vpn RIB must also be + * offered to all vrfs (because now they originate from + * the vpn RIB). + * + * Acceptance into other vrfs depends on rt-lists. + * Originating vrf will not accept the looped back route + * because of loop checking. + */ + if (new_info) + vpn_leak_to_vrf_update(from_bgp, new_info, NULL); + else + bgp_dest_unlock_node(bn); +} + +void vpn_leak_from_vrf_withdraw(struct bgp *to_bgp, /* to */ + struct bgp *from_bgp, /* from */ + struct bgp_path_info *path_vrf) /* route */ +{ + int debug = BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF); + const struct prefix *p = bgp_dest_get_prefix(path_vrf->net); + afi_t afi = family2afi(p->family); + safi_t safi = SAFI_MPLS_VPN; + struct bgp_path_info *bpi; + struct bgp_dest *bn; + const char *debugmsg; + + if (debug) { + zlog_debug( + "%s: entry: leak-from=%s, p=%pBD, type=%d, sub_type=%d", + __func__, from_bgp->name_pretty, path_vrf->net, + path_vrf->type, path_vrf->sub_type); + } + + if (!to_bgp) + return; + + if (!afi) { + if (debug) + zlog_debug("%s: can't get afi of prefix", __func__); + return; + } + + /* Is this route exportable into the VPN table? */ + if (!is_route_injectable_into_vpn(path_vrf)) + return; + + if (!vpn_leak_to_vpn_active(from_bgp, afi, &debugmsg)) { + if (debug) + zlog_debug("%s: skipping: %s", __func__, debugmsg); + return; + } + + if (debug) + zlog_debug("%s: withdrawing (path_vrf=%p)", __func__, path_vrf); + + bn = bgp_afi_node_get(to_bgp->rib[afi][safi], afi, safi, p, + &(from_bgp->vpn_policy[afi].tovpn_rd)); + + if (!bn) + return; + /* + * vrf -> vpn + * match original bpi imported from + */ + for (bpi = bgp_dest_get_bgp_path_info(bn); bpi; bpi = bpi->next) { + if (bpi->extra && bpi->extra->vrfleak && + bpi->extra->vrfleak->parent == path_vrf) { + break; + } + } + + if (bpi) { + /* withdraw from looped vrfs as well */ + vpn_leak_to_vrf_withdraw(bpi); + + bgp_aggregate_decrement(to_bgp, p, bpi, afi, safi); + bgp_path_info_delete(bn, bpi); + bgp_process(to_bgp, bn, afi, safi); + } + bgp_dest_unlock_node(bn); +} + +void vpn_leak_from_vrf_withdraw_all(struct bgp *to_bgp, struct bgp *from_bgp, + afi_t afi) +{ + int debug = BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF); + struct bgp_dest *pdest; + safi_t safi = SAFI_MPLS_VPN; + + /* + * Walk vpn table, delete bpi with bgp_orig == from_bgp + */ + for (pdest = bgp_table_top(to_bgp->rib[afi][safi]); pdest; + pdest = bgp_route_next(pdest)) { + + struct bgp_table *table; + struct bgp_dest *bn; + struct bgp_path_info *bpi; + + /* This is the per-RD table of prefixes */ + table = bgp_dest_get_bgp_table_info(pdest); + + if (!table) + continue; + + for (bn = bgp_table_top(table); bn; bn = bgp_route_next(bn)) { + bpi = bgp_dest_get_bgp_path_info(bn); + if (debug && bpi) { + zlog_debug("%s: looking at prefix %pBD", + __func__, bn); + } + + for (; bpi; bpi = bpi->next) { + if (debug) + zlog_debug("%s: type %d, sub_type %d", + __func__, bpi->type, + bpi->sub_type); + if (bpi->sub_type != BGP_ROUTE_IMPORTED) + continue; + if (!bpi->extra || !bpi->extra->vrfleak) + continue; + if ((struct bgp *)bpi->extra->vrfleak->bgp_orig == + from_bgp) { + /* delete route */ + if (debug) + zlog_debug("%s: deleting it", + __func__); + /* withdraw from leak-to vrfs as well */ + vpn_leak_to_vrf_withdraw(bpi); + bgp_aggregate_decrement( + to_bgp, bgp_dest_get_prefix(bn), + bpi, afi, safi); + bgp_path_info_delete(bn, bpi); + bgp_process(to_bgp, bn, afi, safi); + bgp_mplsvpn_path_nh_label_unlink( + bpi->extra->vrfleak->parent); + } + } + } + } +} + +void vpn_leak_from_vrf_update_all(struct bgp *to_bgp, struct bgp *from_bgp, + afi_t afi) +{ + struct bgp_dest *bn; + struct bgp_path_info *bpi; + int debug = BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF); + + if (debug) + zlog_debug("%s: entry, afi=%d, vrf=%s", __func__, afi, + from_bgp->name_pretty); + + for (bn = bgp_table_top(from_bgp->rib[afi][SAFI_UNICAST]); bn; + bn = bgp_route_next(bn)) { + + if (debug) + zlog_debug("%s: node=%p", __func__, bn); + + for (bpi = bgp_dest_get_bgp_path_info(bn); bpi; + bpi = bpi->next) { + if (debug) + zlog_debug( + "%s: calling vpn_leak_from_vrf_update", + __func__); + vpn_leak_from_vrf_update(to_bgp, from_bgp, bpi); + } + } +} + +static struct bgp *bgp_lookup_by_rd(struct bgp_path_info *bpi, + struct prefix_rd *rd, afi_t afi) +{ + struct listnode *node, *nnode; + struct bgp *bgp; + + if (!rd) + return NULL; + + /* If ACCEPT_OWN is not enabled for this path - return. */ + if (!CHECK_FLAG(bpi->flags, BGP_PATH_ACCEPT_OWN)) + return NULL; + + for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) { + if (bgp->inst_type != BGP_INSTANCE_TYPE_VRF) + continue; + + if (!CHECK_FLAG(bgp->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_RD_SET)) + continue; + + /* Check if we have source VRF by RD value */ + if (memcmp(&bgp->vpn_policy[afi].tovpn_rd.val, rd->val, + ECOMMUNITY_SIZE) == 0) + return bgp; + } + + return NULL; +} + +static void vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */ + struct bgp *from_bgp, /* from */ + struct bgp_path_info *path_vpn, + struct prefix_rd *prd) +{ + const struct prefix *p = bgp_dest_get_prefix(path_vpn->net); + afi_t afi = family2afi(p->family); + + struct attr static_attr = {0}; + struct attr *new_attr = NULL; + struct bgp_dest *bn; + safi_t safi = SAFI_UNICAST; + const char *debugmsg; + struct prefix nexthop_orig; + mpls_label_t *pLabels = NULL; + uint32_t num_labels = 0; + int nexthop_self_flag = 1; + struct bgp_path_info *bpi_ultimate = NULL; + int origin_local = 0; + struct bgp *src_vrf; + struct interface *ifp; + char rd_buf[RD_ADDRSTRLEN]; + int debug = BGP_DEBUG(vpn, VPN_LEAK_TO_VRF); + + if (!vpn_leak_from_vpn_active(to_bgp, afi, &debugmsg)) { + if (debug) + zlog_debug( + "%s: from vpn (%s) to vrf (%s), skipping: %s", + __func__, from_bgp->name_pretty, + to_bgp->name_pretty, debugmsg); + return; + } + + /* + * For VRF-2-VRF route-leaking, + * the source will be the originating VRF. + * + * If ACCEPT_OWN mechanism is enabled, then we SHOULD(?) + * get the source VRF (BGP) by looking at the RD. + */ + struct bgp *src_bgp = bgp_lookup_by_rd(path_vpn, prd, afi); + + if (path_vpn->extra && path_vpn->extra->vrfleak && + path_vpn->extra->vrfleak->bgp_orig) + src_vrf = path_vpn->extra->vrfleak->bgp_orig; + else if (src_bgp) + src_vrf = src_bgp; + else + src_vrf = from_bgp; + + /* Check for intersection of route targets */ + if (!ecommunity_include( + to_bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_FROMVPN], + bgp_attr_get_ecommunity(path_vpn->attr))) { + if (debug) + zlog_debug( + "from vpn (%s) to vrf (%s), skipping after no intersection of route targets", + from_bgp->name_pretty, to_bgp->name_pretty); + return; + } + + rd_buf[0] = '\0'; + if (debug && prd) + prefix_rd2str(prd, rd_buf, sizeof(rd_buf), to_bgp->asnotation); + + /* A route MUST NOT ever be accepted back into its source VRF, even if + * it carries one or more RTs that match that VRF. + */ + if (CHECK_FLAG(path_vpn->flags, BGP_PATH_ACCEPT_OWN) && prd && + memcmp(&prd->val, &to_bgp->vpn_policy[afi].tovpn_rd.val, + ECOMMUNITY_SIZE) == 0) { + if (debug) + zlog_debug( + "%s: skipping import, match RD (%s) of src VRF (%s) and the prefix (%pFX)", + __func__, rd_buf, to_bgp->name_pretty, p); + return; + } + + if (debug) + zlog_debug("%s: updating RD %s, %pFX to %s", __func__, rd_buf, + p, to_bgp->name_pretty); + + /* shallow copy */ + static_attr = *path_vpn->attr; + + struct ecommunity *old_ecom; + struct ecommunity *new_ecom; + + /* If doing VRF-to-VRF leaking, strip RTs. */ + old_ecom = bgp_attr_get_ecommunity(&static_attr); + if (old_ecom && CHECK_FLAG(to_bgp->af_flags[afi][safi], + BGP_CONFIG_VRF_TO_VRF_IMPORT)) { + new_ecom = ecommunity_dup(old_ecom); + ecommunity_strip_rts(new_ecom); + bgp_attr_set_ecommunity(&static_attr, new_ecom); + + if (new_ecom->size == 0) { + ecommunity_free(&new_ecom); + bgp_attr_set_ecommunity(&static_attr, NULL); + } + + if (!old_ecom->refcnt) + ecommunity_free(&old_ecom); + } + + community_strip_accept_own(&static_attr); + + /* + * Nexthop: stash and clear + * + * Nexthop is valid in context of VPN core, but not in destination vrf. + * Stash it for later label resolution by vrf ingress path and then + * overwrite with 0, i.e., "me", for the sake of vrf advertisement. + */ + uint8_t nhfamily = NEXTHOP_FAMILY(path_vpn->attr->mp_nexthop_len); + + memset(&nexthop_orig, 0, sizeof(nexthop_orig)); + nexthop_orig.family = nhfamily; + + /* If the path has accept-own community and the source VRF + * is valid, reset next-hop to self, to allow importing own + * routes between different VRFs on the same node. + * Set the nh ifindex to VRF's interface, not the real interface. + * Let the kernel to decide with double lookup the real next-hop + * interface when installing the route. + */ + if (src_bgp) { + subgroup_announce_reset_nhop(nhfamily, &static_attr); + ifp = if_get_vrf_loopback(src_vrf->vrf_id); + if (ifp) + static_attr.nh_ifindex = ifp->ifindex; + } + + switch (nhfamily) { + case AF_INET: + /* save */ + nexthop_orig.u.prefix4 = path_vpn->attr->mp_nexthop_global_in; + nexthop_orig.prefixlen = IPV4_MAX_BITLEN; + + if (CHECK_FLAG(to_bgp->af_flags[afi][safi], + BGP_CONFIG_VRF_TO_VRF_IMPORT)) { + static_attr.nexthop.s_addr = + nexthop_orig.u.prefix4.s_addr; + + static_attr.mp_nexthop_global_in = + path_vpn->attr->mp_nexthop_global_in; + static_attr.mp_nexthop_len = + path_vpn->attr->mp_nexthop_len; + } + static_attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP); + break; + case AF_INET6: + /* save */ + nexthop_orig.u.prefix6 = path_vpn->attr->mp_nexthop_global; + nexthop_orig.prefixlen = IPV6_MAX_BITLEN; + + if (CHECK_FLAG(to_bgp->af_flags[afi][safi], + BGP_CONFIG_VRF_TO_VRF_IMPORT)) { + static_attr.mp_nexthop_global = nexthop_orig.u.prefix6; + } + break; + } + + /* + * route map handling + */ + if (to_bgp->vpn_policy[afi].rmap[BGP_VPN_POLICY_DIR_FROMVPN]) { + struct bgp_path_info info; + route_map_result_t ret; + + memset(&info, 0, sizeof(info)); + info.peer = to_bgp->peer_self; + info.attr = &static_attr; + info.extra = path_vpn->extra; /* Used for source-vrf filter */ + ret = route_map_apply(to_bgp->vpn_policy[afi] + .rmap[BGP_VPN_POLICY_DIR_FROMVPN], + p, &info); + if (RMAP_DENYMATCH == ret) { + bgp_attr_flush(&static_attr); /* free any added parts */ + if (debug) + zlog_debug( + "%s: vrf %s vpn-policy route map \"%s\" says DENY, returning", + __func__, to_bgp->name_pretty, + to_bgp->vpn_policy[afi] + .rmap[BGP_VPN_POLICY_DIR_FROMVPN] + ->name); + return; + } + /* + * if route-map changed nexthop, don't nexthop-self on output + */ + if (!CHECK_FLAG(static_attr.rmap_change_flags, + BATTR_RMAP_NEXTHOP_UNCHANGED)) + nexthop_self_flag = 0; + } + + new_attr = bgp_attr_intern(&static_attr); + bgp_attr_flush(&static_attr); + + bn = bgp_afi_node_get(to_bgp->rib[afi][safi], afi, safi, p, NULL); + + /* + * ensure labels are copied + * + * However, there is a special case: if the route originated in + * another local VRF (as opposed to arriving via VPN), then the + * nexthop is reached by hairpinning through this router (me) + * using IP forwarding only (no LSP). Therefore, the route + * imported to the VRF should not have labels attached. Note + * that nexthop tracking is also involved: eliminating the + * labels for these routes enables the non-labeled nexthops + * from the originating VRF to be considered valid for this route. + */ + if (!CHECK_FLAG(to_bgp->af_flags[afi][safi], + BGP_CONFIG_VRF_TO_VRF_IMPORT)) { + /* work back to original route */ + bpi_ultimate = bgp_get_imported_bpi_ultimate(path_vpn); + + /* + * if original route was unicast, + * then it did not arrive over vpn + */ + if (bpi_ultimate->net) { + struct bgp_table *table; + + table = bgp_dest_table(bpi_ultimate->net); + if (table && (table->safi == SAFI_UNICAST)) + origin_local = 1; + } + + /* copy labels */ + if (!origin_local && path_vpn->extra + && path_vpn->extra->num_labels) { + num_labels = path_vpn->extra->num_labels; + if (num_labels > BGP_MAX_LABELS) + num_labels = BGP_MAX_LABELS; + pLabels = path_vpn->extra->label; + } + } + + if (debug) + zlog_debug("%s: pfx %pBD: num_labels %d", __func__, + path_vpn->net, num_labels); + + if (!leak_update(to_bgp, bn, new_attr, afi, safi, path_vpn, pLabels, + num_labels, src_vrf, &nexthop_orig, nexthop_self_flag, + debug)) + bgp_dest_unlock_node(bn); +} + +bool vpn_leak_to_vrf_no_retain_filter_check(struct bgp *from_bgp, + struct attr *attr, afi_t afi) +{ + struct ecommunity *ecom_route_target = bgp_attr_get_ecommunity(attr); + int debug = BGP_DEBUG(vpn, VPN_LEAK_TO_VRF); + struct listnode *node; + const char *debugmsg; + struct bgp *to_bgp; + + /* Loop over BGP instances */ + for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, to_bgp)) { + if (!vpn_leak_from_vpn_active(to_bgp, afi, &debugmsg)) { + if (debug) + zlog_debug( + "%s: from vpn (%s) to vrf (%s) afi %s, skipping: %s", + __func__, from_bgp->name_pretty, + to_bgp->name_pretty, afi2str(afi), + debugmsg); + continue; + } + + /* Check for intersection of route targets */ + if (!ecommunity_include( + to_bgp->vpn_policy[afi] + .rtlist[BGP_VPN_POLICY_DIR_FROMVPN], + ecom_route_target)) { + if (debug) + zlog_debug( + "%s: from vpn (%s) to vrf (%s) afi %s %s, skipping after no intersection of route targets", + __func__, from_bgp->name_pretty, + to_bgp->name_pretty, afi2str(afi), + ecommunity_str(ecom_route_target)); + continue; + } + return false; + } + + if (debug) + zlog_debug( + "%s: from vpn (%s) afi %s %s, no import - must be filtered", + __func__, from_bgp->name_pretty, afi2str(afi), + ecommunity_str(ecom_route_target)); + + return true; +} + +void vpn_leak_to_vrf_update(struct bgp *from_bgp, + struct bgp_path_info *path_vpn, + struct prefix_rd *prd) +{ + struct listnode *mnode, *mnnode; + struct bgp *bgp; + + int debug = BGP_DEBUG(vpn, VPN_LEAK_TO_VRF); + + if (debug) + zlog_debug("%s: start (path_vpn=%p)", __func__, path_vpn); + + /* Loop over VRFs */ + for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) { + if (!path_vpn->extra || !path_vpn->extra->vrfleak || + path_vpn->extra->vrfleak->bgp_orig != bgp) { /* no loop */ + vpn_leak_to_vrf_update_onevrf(bgp, from_bgp, path_vpn, + prd); + } + } +} + +void vpn_leak_to_vrf_withdraw(struct bgp_path_info *path_vpn) +{ + const struct prefix *p; + afi_t afi; + safi_t safi = SAFI_UNICAST; + struct bgp *bgp; + struct listnode *mnode, *mnnode; + struct bgp_dest *bn; + struct bgp_path_info *bpi; + const char *debugmsg; + + int debug = BGP_DEBUG(vpn, VPN_LEAK_TO_VRF); + + if (debug) + zlog_debug("%s: entry: p=%pBD, type=%d, sub_type=%d", __func__, + path_vpn->net, path_vpn->type, path_vpn->sub_type); + + if (debug) + zlog_debug("%s: start (path_vpn=%p)", __func__, path_vpn); + + if (!path_vpn->net) { +#ifdef ENABLE_BGP_VNC + /* BGP_ROUTE_RFP routes do not have path_vpn->net set (yet) */ + if (path_vpn->type == ZEBRA_ROUTE_BGP + && path_vpn->sub_type == BGP_ROUTE_RFP) { + + return; + } +#endif + if (debug) + zlog_debug( + "%s: path_vpn->net unexpectedly NULL, no prefix, bailing", + __func__); + return; + } + + p = bgp_dest_get_prefix(path_vpn->net); + afi = family2afi(p->family); + + /* Loop over VRFs */ + for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) { + if (!vpn_leak_from_vpn_active(bgp, afi, &debugmsg)) { + if (debug) + zlog_debug("%s: from %s, skipping: %s", + __func__, bgp->name_pretty, + debugmsg); + continue; + } + + /* Check for intersection of route targets */ + if (!ecommunity_include( + bgp->vpn_policy[afi] + .rtlist[BGP_VPN_POLICY_DIR_FROMVPN], + bgp_attr_get_ecommunity(path_vpn->attr))) { + + continue; + } + + if (debug) + zlog_debug("%s: withdrawing from vrf %s", __func__, + bgp->name_pretty); + + bn = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, p, NULL); + + for (bpi = bgp_dest_get_bgp_path_info(bn); bpi; + bpi = bpi->next) { + if (bpi->extra && bpi->extra->vrfleak && + (struct bgp_path_info *)bpi->extra->vrfleak->parent == + path_vpn) { + break; + } + } + + if (bpi) { + if (debug) + zlog_debug("%s: deleting bpi %p", __func__, + bpi); + bgp_aggregate_decrement(bgp, p, bpi, afi, safi); + bgp_path_info_delete(bn, bpi); + bgp_process(bgp, bn, afi, safi); + } + bgp_dest_unlock_node(bn); + } +} + +void vpn_leak_to_vrf_withdraw_all(struct bgp *to_bgp, afi_t afi) +{ + struct bgp_dest *bn; + struct bgp_path_info *bpi; + safi_t safi = SAFI_UNICAST; + int debug = BGP_DEBUG(vpn, VPN_LEAK_TO_VRF); + + if (debug) + zlog_debug("%s: entry", __func__); + /* + * Walk vrf table, delete bpi with bgp_orig in a different vrf + */ + for (bn = bgp_table_top(to_bgp->rib[afi][safi]); bn; + bn = bgp_route_next(bn)) { + + for (bpi = bgp_dest_get_bgp_path_info(bn); bpi; + bpi = bpi->next) { + if (bpi->extra && bpi->extra->vrfleak && + bpi->extra->vrfleak->bgp_orig != to_bgp && + bpi->extra->vrfleak->parent && + is_pi_family_vpn(bpi->extra->vrfleak->parent)) { + /* delete route */ + bgp_aggregate_decrement(to_bgp, + bgp_dest_get_prefix(bn), + bpi, afi, safi); + bgp_path_info_delete(bn, bpi); + bgp_process(to_bgp, bn, afi, safi); + } + } + } +} + +void vpn_leak_no_retain(struct bgp *to_bgp, struct bgp *vpn_from, afi_t afi) +{ + struct bgp_dest *pdest; + safi_t safi = SAFI_MPLS_VPN; + + assert(vpn_from); + + /* + * Walk vpn table + */ + for (pdest = bgp_table_top(vpn_from->rib[afi][safi]); pdest; + pdest = bgp_route_next(pdest)) { + struct bgp_table *table; + struct bgp_dest *bn; + struct bgp_path_info *bpi; + + /* This is the per-RD table of prefixes */ + table = bgp_dest_get_bgp_table_info(pdest); + + if (!table) + continue; + + for (bn = bgp_table_top(table); bn; bn = bgp_route_next(bn)) { + for (bpi = bgp_dest_get_bgp_path_info(bn); bpi; + bpi = bpi->next) { + if (bpi->extra && bpi->extra->vrfleak && + bpi->extra->vrfleak->bgp_orig == to_bgp) + continue; + + if (bpi->sub_type != BGP_ROUTE_NORMAL) + continue; + + if (!vpn_leak_to_vrf_no_retain_filter_check( + vpn_from, bpi->attr, afi)) + /* do not filter */ + continue; + + bgp_unlink_nexthop(bpi); + bgp_rib_remove(bn, bpi, bpi->peer, afi, safi); + } + } + } +} + +void vpn_leak_to_vrf_update_all(struct bgp *to_bgp, struct bgp *vpn_from, + afi_t afi) +{ + struct bgp_dest *pdest; + safi_t safi = SAFI_MPLS_VPN; + + assert(vpn_from); + + /* + * Walk vpn table + */ + for (pdest = bgp_table_top(vpn_from->rib[afi][safi]); pdest; + pdest = bgp_route_next(pdest)) { + struct bgp_table *table; + struct bgp_dest *bn; + struct bgp_path_info *bpi; + + /* This is the per-RD table of prefixes */ + table = bgp_dest_get_bgp_table_info(pdest); + + if (!table) + continue; + + for (bn = bgp_table_top(table); bn; bn = bgp_route_next(bn)) { + + for (bpi = bgp_dest_get_bgp_path_info(bn); bpi; + bpi = bpi->next) { + if (bpi->extra && bpi->extra->vrfleak && + bpi->extra->vrfleak->bgp_orig == to_bgp) + continue; + + vpn_leak_to_vrf_update_onevrf(to_bgp, vpn_from, + bpi, NULL); + } + } + } +} + +/* + * This function is called for definition/deletion/change to a route-map + */ +static void vpn_policy_routemap_update(struct bgp *bgp, const char *rmap_name) +{ + int debug = BGP_DEBUG(vpn, VPN_LEAK_RMAP_EVENT); + afi_t afi; + struct route_map *rmap; + + if (bgp->inst_type != BGP_INSTANCE_TYPE_DEFAULT + && bgp->inst_type != BGP_INSTANCE_TYPE_VRF) { + + return; + } + + rmap = route_map_lookup_by_name(rmap_name); /* NULL if deleted */ + + for (afi = 0; afi < AFI_MAX; ++afi) { + + if (bgp->vpn_policy[afi].rmap_name[BGP_VPN_POLICY_DIR_TOVPN] + && !strcmp(rmap_name, + bgp->vpn_policy[afi] + .rmap_name[BGP_VPN_POLICY_DIR_TOVPN])) { + + if (debug) + zlog_debug( + "%s: rmap \"%s\" matches vrf-policy tovpn for as %d afi %s", + __func__, rmap_name, bgp->as, + afi2str(afi)); + + vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, afi, + bgp_get_default(), bgp); + if (debug) + zlog_debug("%s: after vpn_leak_prechange", + __func__); + + /* in case of definition/deletion */ + bgp->vpn_policy[afi].rmap[BGP_VPN_POLICY_DIR_TOVPN] = + rmap; + + vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi, + bgp_get_default(), bgp); + + if (debug) + zlog_debug("%s: after vpn_leak_postchange", + __func__); + } + + if (bgp->vpn_policy[afi].rmap_name[BGP_VPN_POLICY_DIR_FROMVPN] + && !strcmp(rmap_name, + bgp->vpn_policy[afi] + .rmap_name[BGP_VPN_POLICY_DIR_FROMVPN])) { + + if (debug) { + zlog_debug("%s: rmap \"%s\" matches vrf-policy fromvpn for as %d afi %s", + __func__, rmap_name, bgp->as, + afi2str(afi)); + } + + vpn_leak_prechange(BGP_VPN_POLICY_DIR_FROMVPN, afi, + bgp_get_default(), bgp); + + /* in case of definition/deletion */ + bgp->vpn_policy[afi].rmap[BGP_VPN_POLICY_DIR_FROMVPN] = + rmap; + + vpn_leak_postchange(BGP_VPN_POLICY_DIR_FROMVPN, afi, + bgp_get_default(), bgp); + } + } +} + +/* This API is used during router-id change, reflect VPNs + * auto RD and RT values and readvertise routes to VPN table. + */ +void vpn_handle_router_id_update(struct bgp *bgp, bool withdraw, + bool is_config) +{ + afi_t afi; + int debug = (BGP_DEBUG(vpn, VPN_LEAK_TO_VRF) + | BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF)); + char *vname; + const char *export_name; + char buf[RD_ADDRSTRLEN]; + struct bgp *bgp_import; + struct listnode *node; + struct ecommunity *ecom; + enum vpn_policy_direction idir, edir; + + /* + * Router-id change that is not explicitly configured + * (a change from zebra, frr restart for example) + * should not replace a configured vpn RD/RT. + */ + if (!is_config) { + if (debug) + zlog_debug("%s: skipping non explicit router-id change", + __func__); + return; + } + + if (bgp->inst_type != BGP_INSTANCE_TYPE_DEFAULT + && bgp->inst_type != BGP_INSTANCE_TYPE_VRF) + return; + + export_name = bgp->name ? bgp->name : VRF_DEFAULT_NAME; + idir = BGP_VPN_POLICY_DIR_FROMVPN; + edir = BGP_VPN_POLICY_DIR_TOVPN; + + for (afi = 0; afi < AFI_MAX; ++afi) { + if (!vpn_leak_to_vpn_active(bgp, afi, NULL)) + continue; + + if (withdraw) { + vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, + afi, bgp_get_default(), bgp); + if (debug) + zlog_debug("%s: %s after to_vpn vpn_leak_prechange", + __func__, export_name); + + /* Remove import RT from VRFs */ + ecom = bgp->vpn_policy[afi].rtlist[edir]; + for (ALL_LIST_ELEMENTS_RO(bgp->vpn_policy[afi]. + export_vrf, node, vname)) { + if (strcmp(vname, VRF_DEFAULT_NAME) == 0) + bgp_import = bgp_get_default(); + else + bgp_import = bgp_lookup_by_name(vname); + if (!bgp_import) + continue; + + ecommunity_del_val( + bgp_import->vpn_policy[afi] + .rtlist[idir], + (struct ecommunity_val *)ecom->val); + } + } else { + /* New router-id derive auto RD and RT and export + * to VPN + */ + form_auto_rd(bgp->router_id, bgp->vrf_rd_id, + &bgp->vrf_prd_auto); + bgp->vpn_policy[afi].tovpn_rd = bgp->vrf_prd_auto; + prefix_rd2str(&bgp->vpn_policy[afi].tovpn_rd, buf, + sizeof(buf), bgp->asnotation); + + /* free up pre-existing memory if any and allocate + * the ecommunity attribute with new RD/RT + */ + if (bgp->vpn_policy[afi].rtlist[edir]) + ecommunity_free( + &bgp->vpn_policy[afi].rtlist[edir]); + bgp->vpn_policy[afi].rtlist[edir] = ecommunity_str2com( + buf, ECOMMUNITY_ROUTE_TARGET, 0); + + /* Update import_vrf rt_list */ + ecom = bgp->vpn_policy[afi].rtlist[edir]; + for (ALL_LIST_ELEMENTS_RO(bgp->vpn_policy[afi]. + export_vrf, node, vname)) { + if (strcmp(vname, VRF_DEFAULT_NAME) == 0) + bgp_import = bgp_get_default(); + else + bgp_import = bgp_lookup_by_name(vname); + if (!bgp_import) + continue; + if (bgp_import->vpn_policy[afi].rtlist[idir]) + bgp_import->vpn_policy[afi].rtlist[idir] + = ecommunity_merge( + bgp_import->vpn_policy[afi] + .rtlist[idir], ecom); + else + bgp_import->vpn_policy[afi].rtlist[idir] + = ecommunity_dup(ecom); + } + + /* Update routes to VPN */ + vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, + afi, bgp_get_default(), + bgp); + if (debug) + zlog_debug("%s: %s after to_vpn vpn_leak_postchange", + __func__, export_name); + } + } +} + +void vpn_policy_routemap_event(const char *rmap_name) +{ + int debug = BGP_DEBUG(vpn, VPN_LEAK_RMAP_EVENT); + struct listnode *mnode, *mnnode; + struct bgp *bgp; + + if (debug) + zlog_debug("%s: entry", __func__); + + if (bm->bgp == NULL) /* may be called during cleanup */ + return; + + for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) + vpn_policy_routemap_update(bgp, rmap_name); +} + +void vrf_import_from_vrf(struct bgp *to_bgp, struct bgp *from_bgp, + afi_t afi, safi_t safi) +{ + const char *export_name; + enum vpn_policy_direction idir, edir; + char *vname, *tmp_name; + char buf[RD_ADDRSTRLEN]; + struct ecommunity *ecom; + bool first_export = false; + int debug; + struct listnode *node; + bool is_inst_match = false; + + export_name = to_bgp->name ? to_bgp->name : VRF_DEFAULT_NAME; + idir = BGP_VPN_POLICY_DIR_FROMVPN; + edir = BGP_VPN_POLICY_DIR_TOVPN; + + debug = (BGP_DEBUG(vpn, VPN_LEAK_TO_VRF) | + BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF)); + + /* + * Cross-ref both VRFs. Also, note if this is the first time + * any VRF is importing from "import_vrf". + */ + vname = (from_bgp->name ? XSTRDUP(MTYPE_TMP, from_bgp->name) + : XSTRDUP(MTYPE_TMP, VRF_DEFAULT_NAME)); + + /* Check the import_vrf list of destination vrf for the source vrf name, + * insert otherwise. + */ + for (ALL_LIST_ELEMENTS_RO(to_bgp->vpn_policy[afi].import_vrf, + node, tmp_name)) { + if (strcmp(vname, tmp_name) == 0) { + is_inst_match = true; + break; + } + } + if (!is_inst_match) + listnode_add(to_bgp->vpn_policy[afi].import_vrf, + vname); + else + XFREE(MTYPE_TMP, vname); + + /* Check if the source vrf already exports to any vrf, + * first time export requires to setup auto derived RD/RT values. + * Add the destination vrf name to export vrf list if it is + * not present. + */ + is_inst_match = false; + vname = XSTRDUP(MTYPE_TMP, export_name); + if (!listcount(from_bgp->vpn_policy[afi].export_vrf)) { + first_export = true; + } else { + for (ALL_LIST_ELEMENTS_RO(from_bgp->vpn_policy[afi].export_vrf, + node, tmp_name)) { + if (strcmp(vname, tmp_name) == 0) { + is_inst_match = true; + break; + } + } + } + if (!is_inst_match) + listnode_add(from_bgp->vpn_policy[afi].export_vrf, + vname); + else + XFREE(MTYPE_TMP, vname); + + /* Update import RT for current VRF using export RT of the VRF we're + * importing from. First though, make sure "import_vrf" has that + * set. + */ + if (first_export) { + form_auto_rd(from_bgp->router_id, from_bgp->vrf_rd_id, + &from_bgp->vrf_prd_auto); + from_bgp->vpn_policy[afi].tovpn_rd = from_bgp->vrf_prd_auto; + SET_FLAG(from_bgp->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_RD_SET); + prefix_rd2str(&from_bgp->vpn_policy[afi].tovpn_rd, buf, + sizeof(buf), from_bgp->asnotation); + from_bgp->vpn_policy[afi].rtlist[edir] = + ecommunity_str2com(buf, ECOMMUNITY_ROUTE_TARGET, 0); + SET_FLAG(from_bgp->af_flags[afi][safi], + BGP_CONFIG_VRF_TO_VRF_EXPORT); + from_bgp->vpn_policy[afi].tovpn_label = + BGP_PREVENT_VRF_2_VRF_LEAK; + } + ecom = from_bgp->vpn_policy[afi].rtlist[edir]; + if (to_bgp->vpn_policy[afi].rtlist[idir]) + to_bgp->vpn_policy[afi].rtlist[idir] = + ecommunity_merge(to_bgp->vpn_policy[afi] + .rtlist[idir], ecom); + else + to_bgp->vpn_policy[afi].rtlist[idir] = ecommunity_dup(ecom); + SET_FLAG(to_bgp->af_flags[afi][safi], BGP_CONFIG_VRF_TO_VRF_IMPORT); + + if (debug) { + const char *from_name; + char *ecom1, *ecom2; + + from_name = from_bgp->name ? from_bgp->name : + VRF_DEFAULT_NAME; + + ecom1 = ecommunity_ecom2str( + to_bgp->vpn_policy[afi].rtlist[idir], + ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + + ecom2 = ecommunity_ecom2str( + to_bgp->vpn_policy[afi].rtlist[edir], + ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + + zlog_debug( + "%s from %s to %s first_export %u import-rt %s export-rt %s", + __func__, from_name, export_name, first_export, ecom1, + ecom2); + + ecommunity_strfree(&ecom1); + ecommunity_strfree(&ecom2); + } + + /* Does "import_vrf" first need to export its routes or that + * is already done and we just need to import those routes + * from the global table? + */ + if (first_export) + vpn_leak_postchange(edir, afi, bgp_get_default(), from_bgp); + else + vpn_leak_postchange(idir, afi, bgp_get_default(), to_bgp); +} + +void vrf_unimport_from_vrf(struct bgp *to_bgp, struct bgp *from_bgp, + afi_t afi, safi_t safi) +{ + const char *export_name, *tmp_name; + enum vpn_policy_direction idir, edir; + char *vname; + struct ecommunity *ecom = NULL; + struct listnode *node; + int debug; + + export_name = to_bgp->name ? to_bgp->name : VRF_DEFAULT_NAME; + tmp_name = from_bgp->name ? from_bgp->name : VRF_DEFAULT_NAME; + idir = BGP_VPN_POLICY_DIR_FROMVPN; + edir = BGP_VPN_POLICY_DIR_TOVPN; + + debug = (BGP_DEBUG(vpn, VPN_LEAK_TO_VRF) | + BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF)); + + /* Were we importing from "import_vrf"? */ + for (ALL_LIST_ELEMENTS_RO(to_bgp->vpn_policy[afi].import_vrf, node, + vname)) { + if (strcmp(vname, tmp_name) == 0) + break; + } + + /* + * We do not check in the cli if the passed in bgp + * instance is actually imported into us before + * we call this function. As such if we do not + * find this in the import_vrf list than + * we just need to return safely. + */ + if (!vname) + return; + + if (debug) + zlog_debug("%s from %s to %s", __func__, tmp_name, export_name); + + /* Remove "import_vrf" from our import list. */ + listnode_delete(to_bgp->vpn_policy[afi].import_vrf, vname); + XFREE(MTYPE_TMP, vname); + + /* Remove routes imported from "import_vrf". */ + /* TODO: In the current logic, we have to first remove all + * imported routes and then (if needed) import back routes + */ + vpn_leak_prechange(idir, afi, bgp_get_default(), to_bgp); + + if (to_bgp->vpn_policy[afi].import_vrf->count == 0) { + if (!to_bgp->vpn_policy[afi].rmap[idir]) + UNSET_FLAG(to_bgp->af_flags[afi][safi], + BGP_CONFIG_VRF_TO_VRF_IMPORT); + if (to_bgp->vpn_policy[afi].rtlist[idir]) + ecommunity_free(&to_bgp->vpn_policy[afi].rtlist[idir]); + } else { + ecom = from_bgp->vpn_policy[afi].rtlist[edir]; + if (ecom) + ecommunity_del_val(to_bgp->vpn_policy[afi].rtlist[idir], + (struct ecommunity_val *)ecom->val); + vpn_leak_postchange(idir, afi, bgp_get_default(), to_bgp); + } + + /* + * What? + * So SA is assuming that since the ALL_LIST_ELEMENTS_RO + * below is checking for NULL that export_vrf can be + * NULL, consequently it is complaining( like a cabbage ) + * that we could dereference and crash in the listcount(..) + * check below. + * So make it happy, under protest, with liberty and justice + * for all. + */ + assert(from_bgp->vpn_policy[afi].export_vrf); + + /* Remove us from "import_vrf's" export list. If no other VRF + * is importing from "import_vrf", cleanup appropriately. + */ + for (ALL_LIST_ELEMENTS_RO(from_bgp->vpn_policy[afi].export_vrf, + node, vname)) { + if (strcmp(vname, export_name) == 0) + break; + } + + /* + * If we have gotten to this point then the vname must + * exist. If not, we are in a world of trouble and + * have slag sitting around. + * + * import_vrf and export_vrf must match in having + * the in/out names as appropriate. + * export_vrf list could have been cleaned up + * as part of no router bgp source instnace. + */ + if (!vname) + return; + + listnode_delete(from_bgp->vpn_policy[afi].export_vrf, vname); + XFREE(MTYPE_TMP, vname); + + if (!listcount(from_bgp->vpn_policy[afi].export_vrf)) { + vpn_leak_prechange(edir, afi, bgp_get_default(), from_bgp); + ecommunity_free(&from_bgp->vpn_policy[afi].rtlist[edir]); + UNSET_FLAG(from_bgp->af_flags[afi][safi], + BGP_CONFIG_VRF_TO_VRF_EXPORT); + memset(&from_bgp->vpn_policy[afi].tovpn_rd, 0, + sizeof(struct prefix_rd)); + UNSET_FLAG(from_bgp->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_RD_SET); + from_bgp->vpn_policy[afi].tovpn_label = MPLS_LABEL_NONE; + + } +} + +/* For testing purpose, static route of MPLS-VPN. */ +DEFUN (vpnv4_network, + vpnv4_network_cmd, + "network A.B.C.D/M rd ASN:NN_OR_IP-ADDRESS:NN <tag|label> (0-1048575)", + "Specify a network to announce via BGP\n" + "IPv4 prefix\n" + "Specify Route Distinguisher\n" + "VPN Route Distinguisher\n" + "VPN NLRI label (tag)\n" + "VPN NLRI label (tag)\n" + "Label value\n") +{ + int idx_ipv4_prefixlen = 1; + int idx_ext_community = 3; + int idx_label = 5; + + return bgp_static_set(vty, false, argv[idx_ipv4_prefixlen]->arg, + argv[idx_ext_community]->arg, + argv[idx_label]->arg, AFI_IP, SAFI_MPLS_VPN, NULL, + 0, 0, 0, NULL, NULL, NULL, NULL); +} + +DEFUN (vpnv4_network_route_map, + vpnv4_network_route_map_cmd, + "network A.B.C.D/M rd ASN:NN_OR_IP-ADDRESS:NN <tag|label> (0-1048575) route-map RMAP_NAME", + "Specify a network to announce via BGP\n" + "IPv4 prefix\n" + "Specify Route Distinguisher\n" + "VPN Route Distinguisher\n" + "VPN NLRI label (tag)\n" + "VPN NLRI label (tag)\n" + "Label value\n" + "route map\n" + "route map name\n") +{ + int idx_ipv4_prefixlen = 1; + int idx_ext_community = 3; + int idx_label = 5; + int idx_rmap = 7; + + return bgp_static_set(vty, false, argv[idx_ipv4_prefixlen]->arg, + argv[idx_ext_community]->arg, argv[idx_label]->arg, + AFI_IP, SAFI_MPLS_VPN, argv[idx_rmap]->arg, 0, 0, + 0, NULL, NULL, NULL, NULL); +} + +/* For testing purpose, static route of MPLS-VPN. */ +DEFUN (no_vpnv4_network, + no_vpnv4_network_cmd, + "no network A.B.C.D/M rd ASN:NN_OR_IP-ADDRESS:NN <tag|label> (0-1048575)", + NO_STR + "Specify a network to announce via BGP\n" + "IPv4 prefix\n" + "Specify Route Distinguisher\n" + "VPN Route Distinguisher\n" + "VPN NLRI label (tag)\n" + "VPN NLRI label (tag)\n" + "Label value\n") +{ + int idx_ipv4_prefixlen = 2; + int idx_ext_community = 4; + int idx_label = 6; + + return bgp_static_set(vty, true, argv[idx_ipv4_prefixlen]->arg, + argv[idx_ext_community]->arg, + argv[idx_label]->arg, AFI_IP, SAFI_MPLS_VPN, NULL, + 0, 0, 0, NULL, NULL, NULL, NULL); +} + +DEFUN (vpnv6_network, + vpnv6_network_cmd, + "network X:X::X:X/M rd ASN:NN_OR_IP-ADDRESS:NN <tag|label> (0-1048575) [route-map RMAP_NAME]", + "Specify a network to announce via BGP\n" + "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n" + "Specify Route Distinguisher\n" + "VPN Route Distinguisher\n" + "VPN NLRI label (tag)\n" + "VPN NLRI label (tag)\n" + "Label value\n" + "route map\n" + "route map name\n") +{ + int idx_ipv6_prefix = 1; + int idx_ext_community = 3; + int idx_label = 5; + int idx_rmap = 7; + + if (argc == 8) + return bgp_static_set(vty, false, argv[idx_ipv6_prefix]->arg, + argv[idx_ext_community]->arg, + argv[idx_label]->arg, AFI_IP6, + SAFI_MPLS_VPN, argv[idx_rmap]->arg, 0, 0, + 0, NULL, NULL, NULL, NULL); + else + return bgp_static_set(vty, false, argv[idx_ipv6_prefix]->arg, + argv[idx_ext_community]->arg, + argv[idx_label]->arg, AFI_IP6, + SAFI_MPLS_VPN, NULL, 0, 0, 0, NULL, NULL, + NULL, NULL); +} + +/* For testing purpose, static route of MPLS-VPN. */ +DEFUN (no_vpnv6_network, + no_vpnv6_network_cmd, + "no network X:X::X:X/M rd ASN:NN_OR_IP-ADDRESS:NN <tag|label> (0-1048575)", + NO_STR + "Specify a network to announce via BGP\n" + "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n" + "Specify Route Distinguisher\n" + "VPN Route Distinguisher\n" + "VPN NLRI label (tag)\n" + "VPN NLRI label (tag)\n" + "Label value\n") +{ + int idx_ipv6_prefix = 2; + int idx_ext_community = 4; + int idx_label = 6; + + return bgp_static_set(vty, true, argv[idx_ipv6_prefix]->arg, + argv[idx_ext_community]->arg, + argv[idx_label]->arg, AFI_IP6, SAFI_MPLS_VPN, + NULL, 0, 0, 0, NULL, NULL, NULL, NULL); +} + +int bgp_show_mpls_vpn(struct vty *vty, afi_t afi, struct prefix_rd *prd, + enum bgp_show_type type, void *output_arg, int tags, + bool use_json) +{ + struct bgp *bgp; + struct bgp_table *table; + uint16_t show_flags = 0; + + if (use_json) + SET_FLAG(show_flags, BGP_SHOW_OPT_JSON); + + bgp = bgp_get_default(); + if (bgp == NULL) { + if (!use_json) + vty_out(vty, "No BGP process is configured\n"); + else + vty_out(vty, "{}\n"); + return CMD_WARNING; + } + table = bgp->rib[afi][SAFI_MPLS_VPN]; + return bgp_show_table_rd(vty, bgp, afi, SAFI_MPLS_VPN, table, prd, type, + output_arg, show_flags); +} + +DEFUN (show_bgp_ip_vpn_all_rd, + show_bgp_ip_vpn_all_rd_cmd, + "show bgp "BGP_AFI_CMD_STR" vpn all [rd <ASN:NN_OR_IP-ADDRESS:NN|all>] [json]", + SHOW_STR + BGP_STR + BGP_VPNVX_HELP_STR + "Display VPN NLRI specific information\n" + "Display VPN NLRI specific information\n" + "Display information for a route distinguisher\n" + "VPN Route Distinguisher\n" + "All VPN Route Distinguishers\n" + JSON_STR) +{ + int ret; + struct prefix_rd prd; + afi_t afi; + int idx = 0; + + if (argv_find_and_parse_afi(argv, argc, &idx, &afi)) { + /* Constrain search if user supplies RD && RD != "all" */ + if (argv_find(argv, argc, "rd", &idx) + && strcmp(argv[idx + 1]->arg, "all")) { + ret = str2prefix_rd(argv[idx + 1]->arg, &prd); + if (!ret) { + vty_out(vty, + "%% Malformed Route Distinguisher\n"); + return CMD_WARNING; + } + return bgp_show_mpls_vpn(vty, afi, &prd, + bgp_show_type_normal, NULL, 0, + use_json(argc, argv)); + } else { + return bgp_show_mpls_vpn(vty, afi, NULL, + bgp_show_type_normal, NULL, 0, + use_json(argc, argv)); + } + } + return CMD_SUCCESS; +} + +ALIAS(show_bgp_ip_vpn_all_rd, + show_bgp_ip_vpn_rd_cmd, + "show bgp "BGP_AFI_CMD_STR" vpn rd <ASN:NN_OR_IP-ADDRESS:NN|all> [json]", + SHOW_STR + BGP_STR + BGP_VPNVX_HELP_STR + "Display VPN NLRI specific information\n" + "Display information for a route distinguisher\n" + "VPN Route Distinguisher\n" + "All VPN Route Distinguishers\n" + JSON_STR) + +#ifdef KEEP_OLD_VPN_COMMANDS +DEFUN (show_ip_bgp_vpn_rd, + show_ip_bgp_vpn_rd_cmd, + "show ip bgp "BGP_AFI_CMD_STR" vpn rd <ASN:NN_OR_IP-ADDRESS:NN|all>", + SHOW_STR + IP_STR + BGP_STR + BGP_AFI_HELP_STR + BGP_AF_MODIFIER_STR + "Display information for a route distinguisher\n" + "VPN Route Distinguisher\n" + "All VPN Route Distinguishers\n") +{ + int idx_ext_community = argc - 1; + int ret; + struct prefix_rd prd; + afi_t afi; + int idx = 0; + + if (argv_find_and_parse_vpnvx(argv, argc, &idx, &afi)) { + if (!strcmp(argv[idx_ext_community]->arg, "all")) + return bgp_show_mpls_vpn(vty, afi, NULL, + bgp_show_type_normal, NULL, 0, + 0); + ret = str2prefix_rd(argv[idx_ext_community]->arg, &prd); + if (!ret) { + vty_out(vty, "%% Malformed Route Distinguisher\n"); + return CMD_WARNING; + } + return bgp_show_mpls_vpn(vty, afi, &prd, bgp_show_type_normal, + NULL, 0, 0); + } + return CMD_SUCCESS; +} + +DEFUN (show_ip_bgp_vpn_all, + show_ip_bgp_vpn_all_cmd, + "show [ip] bgp <vpnv4|vpnv6>", + SHOW_STR + IP_STR + BGP_STR + BGP_VPNVX_HELP_STR) +{ + afi_t afi; + int idx = 0; + + if (argv_find_and_parse_vpnvx(argv, argc, &idx, &afi)) + return bgp_show_mpls_vpn(vty, afi, NULL, bgp_show_type_normal, + NULL, 0, 0); + return CMD_SUCCESS; +} + +DEFUN (show_ip_bgp_vpn_all_tags, + show_ip_bgp_vpn_all_tags_cmd, + "show [ip] bgp <vpnv4|vpnv6> all tags", + SHOW_STR + IP_STR + BGP_STR + BGP_VPNVX_HELP_STR + "Display information about all VPNv4/VPNV6 NLRIs\n" + "Display BGP tags for prefixes\n") +{ + afi_t afi; + int idx = 0; + + if (argv_find_and_parse_vpnvx(argv, argc, &idx, &afi)) + return bgp_show_mpls_vpn(vty, afi, NULL, bgp_show_type_normal, + NULL, 1, 0); + return CMD_SUCCESS; +} + +DEFUN (show_ip_bgp_vpn_rd_tags, + show_ip_bgp_vpn_rd_tags_cmd, + "show [ip] bgp <vpnv4|vpnv6> rd <ASN:NN_OR_IP-ADDRESS:NN|all> tags", + SHOW_STR + IP_STR + BGP_STR + BGP_VPNVX_HELP_STR + "Display information for a route distinguisher\n" + "VPN Route Distinguisher\n" + "All VPN Route Distinguishers\n" + "Display BGP tags for prefixes\n") +{ + int idx_ext_community = 5; + int ret; + struct prefix_rd prd; + afi_t afi; + int idx = 0; + + if (argv_find_and_parse_vpnvx(argv, argc, &idx, &afi)) { + if (!strcmp(argv[idx_ext_community]->arg, "all")) + return bgp_show_mpls_vpn(vty, afi, NULL, + bgp_show_type_normal, NULL, 1, + 0); + ret = str2prefix_rd(argv[idx_ext_community]->arg, &prd); + if (!ret) { + vty_out(vty, "%% Malformed Route Distinguisher\n"); + return CMD_WARNING; + } + return bgp_show_mpls_vpn(vty, afi, &prd, bgp_show_type_normal, + NULL, 1, 0); + } + return CMD_SUCCESS; +} + +DEFUN (show_ip_bgp_vpn_all_neighbor_routes, + show_ip_bgp_vpn_all_neighbor_routes_cmd, + "show [ip] bgp <vpnv4|vpnv6> all neighbors A.B.C.D routes [json]", + SHOW_STR + IP_STR + BGP_STR + BGP_VPNVX_HELP_STR + "Display information about all VPNv4/VPNv6 NLRIs\n" + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" + "Display routes learned from neighbor\n" + JSON_STR) +{ + int idx_ipv4 = 6; + union sockunion su; + struct peer *peer; + int ret; + bool uj = use_json(argc, argv); + afi_t afi; + int idx = 0; + + if (argv_find_and_parse_vpnvx(argv, argc, &idx, &afi)) { + ret = str2sockunion(argv[idx_ipv4]->arg, &su); + if (ret < 0) { + if (uj) { + json_object *json_no = NULL; + json_no = json_object_new_object(); + json_object_string_add(json_no, "warning", + "Malformed address"); + vty_out(vty, "%s\n", + json_object_to_json_string(json_no)); + json_object_free(json_no); + } else + vty_out(vty, "Malformed address: %s\n", + argv[idx_ipv4]->arg); + return CMD_WARNING; + } + + peer = peer_lookup(NULL, &su); + if (!peer || !peer->afc[afi][SAFI_MPLS_VPN]) { + if (uj) { + json_object *json_no = NULL; + json_no = json_object_new_object(); + json_object_string_add( + json_no, "warning", + "No such neighbor or address family"); + vty_out(vty, "%s\n", + json_object_to_json_string(json_no)); + json_object_free(json_no); + } else + vty_out(vty, + "%% No such neighbor or address family\n"); + return CMD_WARNING; + } + + return bgp_show_mpls_vpn(vty, afi, NULL, bgp_show_type_neighbor, + &su, 0, uj); + } + return CMD_SUCCESS; +} + +DEFUN (show_ip_bgp_vpn_rd_neighbor_routes, + show_ip_bgp_vpn_rd_neighbor_routes_cmd, + "show [ip] bgp <vpnv4|vpnv6> rd <ASN:NN_OR_IP-ADDRESS:NN|all> neighbors A.B.C.D routes [json]", + SHOW_STR + IP_STR + BGP_STR + BGP_VPNVX_HELP_STR + "Display information for a route distinguisher\n" + "VPN Route Distinguisher\n" + "All VPN Route Distinguishers\n" + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" + "Display routes learned from neighbor\n" + JSON_STR) +{ + int idx_ext_community = 5; + int idx_ipv4 = 7; + int ret; + union sockunion su; + struct peer *peer; + struct prefix_rd prd; + bool prefix_rd_all = false; + bool uj = use_json(argc, argv); + afi_t afi; + int idx = 0; + + if (argv_find_and_parse_vpnvx(argv, argc, &idx, &afi)) { + if (!strcmp(argv[idx_ext_community]->arg, "all")) + prefix_rd_all = true; + else { + ret = str2prefix_rd(argv[idx_ext_community]->arg, &prd); + if (!ret) { + if (uj) { + json_object *json_no = NULL; + json_no = json_object_new_object(); + json_object_string_add( + json_no, "warning", + "Malformed Route Distinguisher"); + vty_out(vty, "%s\n", + json_object_to_json_string( + json_no)); + json_object_free(json_no); + } else + vty_out(vty, + "%% Malformed Route Distinguisher\n"); + return CMD_WARNING; + } + } + + ret = str2sockunion(argv[idx_ipv4]->arg, &su); + if (ret < 0) { + if (uj) { + json_object *json_no = NULL; + json_no = json_object_new_object(); + json_object_string_add(json_no, "warning", + "Malformed address"); + vty_out(vty, "%s\n", + json_object_to_json_string(json_no)); + json_object_free(json_no); + } else + vty_out(vty, "Malformed address: %s\n", + argv[idx_ext_community]->arg); + return CMD_WARNING; + } + + peer = peer_lookup(NULL, &su); + if (!peer || !peer->afc[afi][SAFI_MPLS_VPN]) { + if (uj) { + json_object *json_no = NULL; + json_no = json_object_new_object(); + json_object_string_add( + json_no, "warning", + "No such neighbor or address family"); + vty_out(vty, "%s\n", + json_object_to_json_string(json_no)); + json_object_free(json_no); + } else + vty_out(vty, + "%% No such neighbor or address family\n"); + return CMD_WARNING; + } + + if (prefix_rd_all) + return bgp_show_mpls_vpn(vty, afi, NULL, + bgp_show_type_neighbor, &su, 0, + uj); + else + return bgp_show_mpls_vpn(vty, afi, &prd, + bgp_show_type_neighbor, &su, 0, + uj); + } + return CMD_SUCCESS; +} + +DEFUN (show_ip_bgp_vpn_all_neighbor_advertised_routes, + show_ip_bgp_vpn_all_neighbor_advertised_routes_cmd, + "show [ip] bgp <vpnv4|vpnv6> all neighbors A.B.C.D advertised-routes [json]", + SHOW_STR + IP_STR + BGP_STR + BGP_VPNVX_HELP_STR + "Display information about all VPNv4/VPNv6 NLRIs\n" + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" + "Display the routes advertised to a BGP neighbor\n" + JSON_STR) +{ + int idx_ipv4 = 6; + int ret; + struct peer *peer; + union sockunion su; + bool uj = use_json(argc, argv); + afi_t afi; + int idx = 0; + + if (argv_find_and_parse_vpnvx(argv, argc, &idx, &afi)) { + ret = str2sockunion(argv[idx_ipv4]->arg, &su); + if (ret < 0) { + if (uj) { + json_object *json_no = NULL; + json_no = json_object_new_object(); + json_object_string_add(json_no, "warning", + "Malformed address"); + vty_out(vty, "%s\n", + json_object_to_json_string(json_no)); + json_object_free(json_no); + } else + vty_out(vty, "Malformed address: %s\n", + argv[idx_ipv4]->arg); + return CMD_WARNING; + } + peer = peer_lookup(NULL, &su); + if (!peer || !peer->afc[afi][SAFI_MPLS_VPN]) { + if (uj) { + json_object *json_no = NULL; + json_no = json_object_new_object(); + json_object_string_add( + json_no, "warning", + "No such neighbor or address family"); + vty_out(vty, "%s\n", + json_object_to_json_string(json_no)); + json_object_free(json_no); + } else + vty_out(vty, + "%% No such neighbor or address family\n"); + return CMD_WARNING; + } + return show_adj_route_vpn(vty, peer, NULL, AFI_IP, + SAFI_MPLS_VPN, uj); + } + return CMD_SUCCESS; +} + +DEFUN (show_ip_bgp_vpn_rd_neighbor_advertised_routes, + show_ip_bgp_vpn_rd_neighbor_advertised_routes_cmd, + "show [ip] bgp <vpnv4|vpnv6> rd <ASN:NN_OR_IP-ADDRESS:NN|all> neighbors A.B.C.D advertised-routes [json]", + SHOW_STR + IP_STR + BGP_STR + BGP_VPNVX_HELP_STR + "Display information for a route distinguisher\n" + "VPN Route Distinguisher\n" + "All VPN Route Distinguishers\n" + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" + "Display the routes advertised to a BGP neighbor\n" + JSON_STR) +{ + int idx_ext_community = 5; + int idx_ipv4 = 7; + int ret; + struct peer *peer; + struct prefix_rd prd; + union sockunion su; + bool uj = use_json(argc, argv); + afi_t afi; + int idx = 0; + + if (argv_find_and_parse_vpnvx(argv, argc, &idx, &afi)) { + ret = str2sockunion(argv[idx_ipv4]->arg, &su); + if (ret < 0) { + if (uj) { + json_object *json_no = NULL; + json_no = json_object_new_object(); + json_object_string_add(json_no, "warning", + "Malformed address"); + vty_out(vty, "%s\n", + json_object_to_json_string(json_no)); + json_object_free(json_no); + } else + vty_out(vty, "Malformed address: %s\n", + argv[idx_ext_community]->arg); + return CMD_WARNING; + } + peer = peer_lookup(NULL, &su); + if (!peer || !peer->afc[afi][SAFI_MPLS_VPN]) { + if (uj) { + json_object *json_no = NULL; + json_no = json_object_new_object(); + json_object_string_add( + json_no, "warning", + "No such neighbor or address family"); + vty_out(vty, "%s\n", + json_object_to_json_string(json_no)); + json_object_free(json_no); + } else + vty_out(vty, + "%% No such neighbor or address family\n"); + return CMD_WARNING; + } + + if (!strcmp(argv[idx_ext_community]->arg, "all")) + return show_adj_route_vpn(vty, peer, NULL, AFI_IP, + SAFI_MPLS_VPN, uj); + ret = str2prefix_rd(argv[idx_ext_community]->arg, &prd); + if (!ret) { + if (uj) { + json_object *json_no = NULL; + json_no = json_object_new_object(); + json_object_string_add( + json_no, "warning", + "Malformed Route Distinguisher"); + vty_out(vty, "%s\n", + json_object_to_json_string(json_no)); + json_object_free(json_no); + } else + vty_out(vty, + "%% Malformed Route Distinguisher\n"); + return CMD_WARNING; + } + + return show_adj_route_vpn(vty, peer, &prd, AFI_IP, + SAFI_MPLS_VPN, uj); + } + return CMD_SUCCESS; +} +#endif /* KEEP_OLD_VPN_COMMANDS */ + +void bgp_mplsvpn_init(void) +{ + install_element(BGP_VPNV4_NODE, &vpnv4_network_cmd); + install_element(BGP_VPNV4_NODE, &vpnv4_network_route_map_cmd); + install_element(BGP_VPNV4_NODE, &no_vpnv4_network_cmd); + + install_element(BGP_VPNV6_NODE, &vpnv6_network_cmd); + install_element(BGP_VPNV6_NODE, &no_vpnv6_network_cmd); + + install_element(VIEW_NODE, &show_bgp_ip_vpn_all_rd_cmd); + install_element(VIEW_NODE, &show_bgp_ip_vpn_rd_cmd); +#ifdef KEEP_OLD_VPN_COMMANDS + install_element(VIEW_NODE, &show_ip_bgp_vpn_rd_cmd); + install_element(VIEW_NODE, &show_ip_bgp_vpn_all_cmd); + install_element(VIEW_NODE, &show_ip_bgp_vpn_all_tags_cmd); + install_element(VIEW_NODE, &show_ip_bgp_vpn_rd_tags_cmd); + install_element(VIEW_NODE, &show_ip_bgp_vpn_all_neighbor_routes_cmd); + install_element(VIEW_NODE, &show_ip_bgp_vpn_rd_neighbor_routes_cmd); + install_element(VIEW_NODE, + &show_ip_bgp_vpn_all_neighbor_advertised_routes_cmd); + install_element(VIEW_NODE, + &show_ip_bgp_vpn_rd_neighbor_advertised_routes_cmd); +#endif /* KEEP_OLD_VPN_COMMANDS */ +} + +vrf_id_t get_first_vrf_for_redirect_with_rt(struct ecommunity *eckey) +{ + struct listnode *mnode, *mnnode; + struct bgp *bgp; + afi_t afi = AFI_IP; + + if (eckey->unit_size == IPV6_ECOMMUNITY_SIZE) + afi = AFI_IP6; + + for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) { + struct ecommunity *ec; + + if (bgp->inst_type != BGP_INSTANCE_TYPE_VRF) + continue; + + ec = bgp->vpn_policy[afi].import_redirect_rtlist; + + if (ec && eckey->unit_size != ec->unit_size) + continue; + + if (ecommunity_include(ec, eckey)) + return bgp->vrf_id; + } + return VRF_UNKNOWN; +} + +/* + * The purpose of this function is to process leaks that were deferred + * from earlier per-vrf configuration due to not-yet-existing default + * vrf, in other words, configuration such as: + * + * router bgp MMM vrf FOO + * address-family ipv4 unicast + * rd vpn export 1:1 + * exit-address-family + * + * router bgp NNN + * ... + * + * This function gets called when the default instance ("router bgp NNN") + * is created. + */ +void vpn_leak_postchange_all(void) +{ + struct listnode *next; + struct bgp *bgp; + struct bgp *bgp_default = bgp_get_default(); + + assert(bgp_default); + + /* First, do any exporting from VRFs to the single VPN RIB */ + for (ALL_LIST_ELEMENTS_RO(bm->bgp, next, bgp)) { + + if (bgp->inst_type != BGP_INSTANCE_TYPE_VRF) + continue; + + vpn_leak_postchange( + BGP_VPN_POLICY_DIR_TOVPN, + AFI_IP, + bgp_default, + bgp); + + vpn_leak_postchange( + BGP_VPN_POLICY_DIR_TOVPN, + AFI_IP6, + bgp_default, + bgp); + } + + /* Now, do any importing to VRFs from the single VPN RIB */ + for (ALL_LIST_ELEMENTS_RO(bm->bgp, next, bgp)) { + + if (bgp->inst_type != BGP_INSTANCE_TYPE_VRF) + continue; + + vpn_leak_postchange( + BGP_VPN_POLICY_DIR_FROMVPN, + AFI_IP, + bgp_default, + bgp); + + vpn_leak_postchange( + BGP_VPN_POLICY_DIR_FROMVPN, + AFI_IP6, + bgp_default, + bgp); + } +} + +/* When a bgp vrf instance is unconfigured, remove its routes + * from the VPN table and this vrf could be importing routes from other + * bgp vrf instnaces, unimport them. + * VRF X and VRF Y are exporting routes to each other. + * When VRF X is deleted, unimport its routes from all target vrfs, + * also VRF Y should unimport its routes from VRF X table. + * This will ensure VPN table is cleaned up appropriately. + */ +void bgp_vpn_leak_unimport(struct bgp *from_bgp) +{ + struct bgp *bgp_default = bgp_get_default(); + struct bgp *to_bgp; + const char *tmp_name; + char *vname; + struct listnode *node, *next; + safi_t safi = SAFI_UNICAST; + afi_t afi; + bool is_vrf_leak_bind; + int debug; + + if (from_bgp->inst_type != BGP_INSTANCE_TYPE_VRF) + return; + + debug = (BGP_DEBUG(vpn, VPN_LEAK_TO_VRF) | + BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF)); + + tmp_name = from_bgp->name ? from_bgp->name : VRF_DEFAULT_NAME; + + for (afi = 0; afi < AFI_MAX; ++afi) { + /* vrf leak is for IPv4 and IPv6 Unicast only */ + if (afi != AFI_IP && afi != AFI_IP6) + continue; + + for (ALL_LIST_ELEMENTS_RO(bm->bgp, next, to_bgp)) { + if (from_bgp == to_bgp) + continue; + + /* Unimport and remove source vrf from the + * other vrfs import list. + */ + struct vpn_policy *to_vpolicy; + + is_vrf_leak_bind = false; + to_vpolicy = &(to_bgp->vpn_policy[afi]); + for (ALL_LIST_ELEMENTS_RO(to_vpolicy->import_vrf, node, + vname)) { + if (strcmp(vname, tmp_name) == 0) { + is_vrf_leak_bind = true; + break; + } + } + /* skip this bgp instance as there is no leak to this + * vrf instance. + */ + if (!is_vrf_leak_bind) + continue; + + if (debug) + zlog_debug("%s: unimport routes from %s to_bgp %s afi %s import vrfs count %u", + __func__, from_bgp->name_pretty, + to_bgp->name_pretty, afi2str(afi), + to_vpolicy->import_vrf->count); + + vrf_unimport_from_vrf(to_bgp, from_bgp, afi, safi); + + /* readd vrf name as unimport removes import vrf name + * from the destination vrf's import list where the + * `import vrf` configuration still exist. + */ + vname = XSTRDUP(MTYPE_TMP, tmp_name); + listnode_add(to_bgp->vpn_policy[afi].import_vrf, + vname); + SET_FLAG(to_bgp->af_flags[afi][safi], + BGP_CONFIG_VRF_TO_VRF_IMPORT); + + /* If to_bgp exports its routes to the bgp vrf + * which is being deleted, un-import the + * to_bgp routes from VPN. + */ + for (ALL_LIST_ELEMENTS_RO(to_bgp->vpn_policy[afi] + .export_vrf, node, + vname)) { + if (strcmp(vname, tmp_name) == 0) { + vrf_unimport_from_vrf(from_bgp, to_bgp, + afi, safi); + break; + } + } + } + + if (bgp_default && + !CHECK_FLAG(bgp_default->af_flags[afi][SAFI_MPLS_VPN], + BGP_VPNVX_RETAIN_ROUTE_TARGET_ALL)) { + /* 'from_bgp' instance will be deleted + * so force to unset importation to update VPN labels + */ + UNSET_FLAG(from_bgp->af_flags[afi][SAFI_UNICAST], + BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT); + vpn_leak_no_retain(from_bgp, bgp_default, afi); + } + } + return; +} + +/* When a router bgp is configured, there could be a bgp vrf + * instance importing routes from this newly configured + * bgp vrf instance. Export routes from configured + * bgp vrf to VPN. + * VRF Y has import from bgp vrf x, + * when a bgp vrf x instance is created, export its routes + * to VRF Y instance. + */ +void bgp_vpn_leak_export(struct bgp *from_bgp) +{ + afi_t afi; + const char *export_name; + char *vname; + struct listnode *node, *next; + struct ecommunity *ecom; + enum vpn_policy_direction idir, edir; + safi_t safi = SAFI_UNICAST; + struct bgp *to_bgp; + int debug; + + debug = (BGP_DEBUG(vpn, VPN_LEAK_TO_VRF) | + BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF)); + + idir = BGP_VPN_POLICY_DIR_FROMVPN; + edir = BGP_VPN_POLICY_DIR_TOVPN; + + export_name = from_bgp->name ? from_bgp->name : VRF_DEFAULT_NAME; + + for (afi = 0; afi < AFI_MAX; ++afi) { + /* vrf leak is for IPv4 and IPv6 Unicast only */ + if (afi != AFI_IP && afi != AFI_IP6) + continue; + + for (ALL_LIST_ELEMENTS_RO(bm->bgp, next, to_bgp)) { + if (from_bgp == to_bgp) + continue; + + /* bgp instance has import list, check to see if newly + * configured bgp instance is the list. + */ + struct vpn_policy *to_vpolicy; + + to_vpolicy = &(to_bgp->vpn_policy[afi]); + for (ALL_LIST_ELEMENTS_RO(to_vpolicy->import_vrf, + node, vname)) { + if (strcmp(vname, export_name) != 0) + continue; + + if (debug) + zlog_debug("%s: found from_bgp %s in to_bgp %s import list, import routes.", + __func__, + export_name, to_bgp->name_pretty); + + ecom = from_bgp->vpn_policy[afi].rtlist[edir]; + /* remove import rt, it will be readded + * as part of import from vrf. + */ + if (ecom) + ecommunity_del_val( + to_vpolicy->rtlist[idir], + (struct ecommunity_val *) + ecom->val); + vrf_import_from_vrf(to_bgp, from_bgp, + afi, safi); + break; + + } + } + } +} + +/* The nexthops values are compared to + * find in the tree the appropriate cache entry + */ +int bgp_mplsvpn_nh_label_bind_cmp( + const struct bgp_mplsvpn_nh_label_bind_cache *a, + const struct bgp_mplsvpn_nh_label_bind_cache *b) +{ + if (prefix_cmp(&a->nexthop, &b->nexthop)) + return 1; + if (a->orig_label > b->orig_label) + return 1; + if (a->orig_label < b->orig_label) + return -1; + return 0; +} + +static void bgp_mplsvpn_nh_label_bind_send_nexthop_label( + struct bgp_mplsvpn_nh_label_bind_cache *bmnc, int cmd) +{ + struct prefix pfx_nh, *p = NULL; + uint32_t num_labels = 0, lsp_num_labels; + mpls_label_t label[MPLS_MAX_LABELS]; + struct nexthop *nh; + ifindex_t ifindex = IFINDEX_INTERNAL; + vrf_id_t vrf_id = VRF_DEFAULT; + uint32_t i; + + if (bmnc->nh == NULL) + return; + nh = bmnc->nh; + switch (nh->type) { + case NEXTHOP_TYPE_IFINDEX: + p = &bmnc->nexthop; + label[num_labels] = bmnc->orig_label; + num_labels += 1; + ifindex = nh->ifindex; + vrf_id = nh->vrf_id; + break; + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + if (nh->type == NEXTHOP_TYPE_IPV4 || + nh->type == NEXTHOP_TYPE_IPV4_IFINDEX) { + pfx_nh.family = AF_INET; + pfx_nh.prefixlen = IPV4_MAX_BITLEN; + IPV4_ADDR_COPY(&pfx_nh.u.prefix4, &nh->gate.ipv4); + } else { + pfx_nh.family = AF_INET6; + pfx_nh.prefixlen = IPV6_MAX_BITLEN; + IPV6_ADDR_COPY(&pfx_nh.u.prefix6, &nh->gate.ipv6); + } + p = &pfx_nh; + if (nh->nh_label) { + if (nh->nh_label->num_labels + 1 > MPLS_MAX_LABELS) { + /* label stack overflow. no label switching will be performed + */ + flog_err(EC_BGP_LABEL, + "%s [Error] BGP label %u->%u to %pFX, forged label stack too big: %u. Abort LSP installation", + bmnc->bgp_vpn->name_pretty, + bmnc->new_label, bmnc->orig_label, + &bmnc->nexthop, + nh->nh_label->num_labels + 1); + return; + } + lsp_num_labels = nh->nh_label->num_labels; + for (i = 0; i < lsp_num_labels; i++) + label[num_labels + i] = nh->nh_label->label[i]; + num_labels = lsp_num_labels; + } + label[num_labels] = bmnc->orig_label; + num_labels += 1; + if (nh->type == NEXTHOP_TYPE_IPV4_IFINDEX || + nh->type == NEXTHOP_TYPE_IPV6_IFINDEX) { + ifindex = nh->ifindex; + vrf_id = nh->vrf_id; + } + break; + case NEXTHOP_TYPE_BLACKHOLE: + return; + } + bgp_zebra_send_nexthop_label(cmd, bmnc->new_label, ifindex, vrf_id, + ZEBRA_LSP_BGP, p, num_labels, &label[0]); +} + +void bgp_mplsvpn_nh_label_bind_free( + struct bgp_mplsvpn_nh_label_bind_cache *bmnc) +{ + if (bmnc->allocation_in_progress) { + bmnc->allocation_in_progress = false; + bgp_mplsvpn_nh_label_bind_cache_del( + &bmnc->bgp_vpn->mplsvpn_nh_label_bind, bmnc); + return; + } + if (bmnc->new_label != MPLS_INVALID_LABEL) { + bgp_mplsvpn_nh_label_bind_send_nexthop_label( + bmnc, ZEBRA_MPLS_LABELS_DELETE); + bgp_lp_release(LP_TYPE_BGP_L3VPN_BIND, bmnc, bmnc->new_label); + } + bgp_mplsvpn_nh_label_bind_cache_del( + &bmnc->bgp_vpn->mplsvpn_nh_label_bind, bmnc); + + if (bmnc->nh) + nexthop_free(bmnc->nh); + + XFREE(MTYPE_MPLSVPN_NH_LABEL_BIND_CACHE, bmnc); +} + +struct bgp_mplsvpn_nh_label_bind_cache * +bgp_mplsvpn_nh_label_bind_new(struct bgp_mplsvpn_nh_label_bind_cache_head *tree, + struct prefix *p, mpls_label_t orig_label) +{ + struct bgp_mplsvpn_nh_label_bind_cache *bmnc; + + bmnc = XCALLOC(MTYPE_MPLSVPN_NH_LABEL_BIND_CACHE, + sizeof(struct bgp_mplsvpn_nh_label_bind_cache)); + bmnc->new_label = MPLS_INVALID_LABEL; + prefix_copy(&bmnc->nexthop, p); + bmnc->orig_label = orig_label; + + LIST_INIT(&(bmnc->paths)); + bgp_mplsvpn_nh_label_bind_cache_add(tree, bmnc); + + return bmnc; +} + +struct bgp_mplsvpn_nh_label_bind_cache *bgp_mplsvpn_nh_label_bind_find( + struct bgp_mplsvpn_nh_label_bind_cache_head *tree, struct prefix *p, + mpls_label_t orig_label) +{ + struct bgp_mplsvpn_nh_label_bind_cache bmnc = {0}; + + if (!tree) + return NULL; + prefix_copy(&bmnc.nexthop, p); + bmnc.orig_label = orig_label; + + return bgp_mplsvpn_nh_label_bind_cache_find(tree, &bmnc); +} + +/* Called to check if the incoming l3vpn path entry + * has mpls label information + */ +bool bgp_mplsvpn_path_uses_valid_mpls_label(struct bgp_path_info *pi) +{ + if (pi->attr && pi->attr->srv6_l3vpn) + /* srv6 sid */ + return false; + + if (pi->attr && + CHECK_FLAG(pi->attr->flag, ATTR_FLAG_BIT(BGP_ATTR_PREFIX_SID)) && + pi->attr->label_index != BGP_INVALID_LABEL_INDEX) + /* prefix_sid attribute */ + return false; + + if (!pi->extra || !bgp_is_valid_label(&pi->extra->label[0])) + /* invalid MPLS label */ + return false; + return true; +} + +mpls_label_t bgp_mplsvpn_nh_label_bind_get_label(struct bgp_path_info *pi) +{ + mpls_label_t label; + struct bgp_mplsvpn_nh_label_bind_cache *bmnc; + + bmnc = pi->mplsvpn.bmnc.nh_label_bind_cache; + if (!bmnc || bmnc->new_label == MPLS_INVALID_LABEL) + /* allocation in progress + * or path not eligible for local label + */ + return MPLS_INVALID_LABEL; + + label = mpls_lse_encode(bmnc->new_label, 0, 0, 1); + bgp_set_valid_label(&label); + + return label; +} + +/* Called upon reception of a ZAPI Message from zebra, about + * a new available label. + */ +static int bgp_mplsvpn_nh_label_bind_get_local_label_cb(mpls_label_t label, + void *context, + bool allocated) +{ + struct bgp_mplsvpn_nh_label_bind_cache *bmnc = context; + struct bgp_table *table; + struct bgp_path_info *pi; + + if (BGP_DEBUG(labelpool, LABELPOOL)) + zlog_debug("%s: label=%u, allocated=%d, nexthop=%pFX, label %u", + __func__, label, allocated, &bmnc->nexthop, + bmnc->orig_label); + if (allocated) + /* update the entry with the new label */ + bmnc->new_label = label; + else + /* + * previously-allocated label is now invalid + * eg: zebra deallocated the labels and notifies it + */ + bmnc->new_label = MPLS_INVALID_LABEL; + + if (!bmnc->allocation_in_progress) { + bgp_mplsvpn_nh_label_bind_free(bmnc); + return 0; + } + bmnc->allocation_in_progress = false; + + if (bmnc->new_label != MPLS_INVALID_LABEL) + /* + * Create the LSP : <local_label -> bmnc->orig_label, + * via bmnc->prefix, interface bnc->nexthop->ifindex + */ + bgp_mplsvpn_nh_label_bind_send_nexthop_label( + bmnc, ZEBRA_MPLS_LABELS_ADD); + + LIST_FOREACH (pi, &(bmnc->paths), mplsvpn.bmnc.nh_label_bind_thread) { + /* we can advertise it */ + if (!pi->net) + continue; + table = bgp_dest_table(pi->net); + if (!table) + continue; + SET_FLAG(pi->net->flags, BGP_NODE_LABEL_CHANGED); + bgp_process(table->bgp, pi->net, table->afi, table->safi); + } + + return 0; +} + +void bgp_mplsvpn_path_nh_label_bind_unlink(struct bgp_path_info *pi) +{ + struct bgp_mplsvpn_nh_label_bind_cache *bmnc; + + if (!pi) + return; + + if (!CHECK_FLAG(pi->flags, BGP_PATH_MPLSVPN_NH_LABEL_BIND)) + return; + + bmnc = pi->mplsvpn.bmnc.nh_label_bind_cache; + + if (!bmnc) + return; + + LIST_REMOVE(pi, mplsvpn.bmnc.nh_label_bind_thread); + pi->mplsvpn.bmnc.nh_label_bind_cache->path_count--; + pi->mplsvpn.bmnc.nh_label_bind_cache = NULL; + SET_FLAG(pi->flags, BGP_PATH_MPLSVPN_NH_LABEL_BIND); + + if (LIST_EMPTY(&(bmnc->paths))) + bgp_mplsvpn_nh_label_bind_free(bmnc); +} + +void bgp_mplsvpn_nh_label_bind_register_local_label(struct bgp *bgp, + struct bgp_dest *dest, + struct bgp_path_info *pi) +{ + struct bgp_mplsvpn_nh_label_bind_cache *bmnc; + struct bgp_mplsvpn_nh_label_bind_cache_head *tree; + + tree = &bgp->mplsvpn_nh_label_bind; + bmnc = bgp_mplsvpn_nh_label_bind_find( + tree, &pi->nexthop->prefix, decode_label(&pi->extra->label[0])); + if (!bmnc) { + bmnc = bgp_mplsvpn_nh_label_bind_new( + tree, &pi->nexthop->prefix, + decode_label(&pi->extra->label[0])); + bmnc->bgp_vpn = bgp; + bmnc->allocation_in_progress = true; + bgp_lp_get(LP_TYPE_BGP_L3VPN_BIND, bmnc, + bgp_mplsvpn_nh_label_bind_get_local_label_cb); + } + + if (pi->mplsvpn.bmnc.nh_label_bind_cache == bmnc) + /* no change */ + return; + + bgp_mplsvpn_path_nh_label_bind_unlink(pi); + + /* updates NHT pi list reference */ + LIST_INSERT_HEAD(&(bmnc->paths), pi, mplsvpn.bmnc.nh_label_bind_thread); + pi->mplsvpn.bmnc.nh_label_bind_cache = bmnc; + pi->mplsvpn.bmnc.nh_label_bind_cache->path_count++; + SET_FLAG(pi->flags, BGP_PATH_MPLSVPN_NH_LABEL_BIND); + bmnc->last_update = monotime(NULL); + + /* Add or update the selected nexthop */ + if (!bmnc->nh) + bmnc->nh = nexthop_dup(pi->nexthop->nexthop, NULL); + else if (!nexthop_same(pi->nexthop->nexthop, bmnc->nh)) { + nexthop_free(bmnc->nh); + bmnc->nh = nexthop_dup(pi->nexthop->nexthop, NULL); + if (bmnc->new_label != MPLS_INVALID_LABEL) + bgp_mplsvpn_nh_label_bind_send_nexthop_label( + bmnc, ZEBRA_MPLS_LABELS_REPLACE); + } +} + +static void show_bgp_mplsvpn_nh_label_bind_internal(struct vty *vty, + struct bgp *bgp, + bool detail) +{ + struct bgp_mplsvpn_nh_label_bind_cache_head *tree; + struct bgp_mplsvpn_nh_label_bind_cache *iter; + afi_t afi; + safi_t safi; + struct bgp_dest *dest; + struct bgp_path_info *path; + struct bgp *bgp_path; + struct bgp_table *table; + time_t tbuf; + char buf[32]; + + vty_out(vty, "Current BGP mpls-vpn nexthop label bind cache, %s\n", + bgp->name_pretty); + + tree = &bgp->mplsvpn_nh_label_bind; + frr_each (bgp_mplsvpn_nh_label_bind_cache, tree, iter) { + if (iter->nexthop.family == AF_INET) + vty_out(vty, " %pI4", &iter->nexthop.u.prefix4); + else + vty_out(vty, " %pI6", &iter->nexthop.u.prefix6); + vty_out(vty, ", label %u, local label %u #paths %u\n", + iter->orig_label, iter->new_label, iter->path_count); + if (iter->nh) + vty_out(vty, " interface %s\n", + ifindex2ifname(iter->nh->ifindex, + iter->nh->vrf_id)); + tbuf = time(NULL) - (monotime(NULL) - iter->last_update); + vty_out(vty, " Last update: %s", ctime_r(&tbuf, buf)); + if (!detail) + continue; + vty_out(vty, " Paths:\n"); + LIST_FOREACH (path, &(iter->paths), + mplsvpn.bmnc.nh_label_bind_thread) { + dest = path->net; + table = bgp_dest_table(dest); + assert(dest && table); + afi = family2afi(bgp_dest_get_prefix(dest)->family); + safi = table->safi; + bgp_path = table->bgp; + + vty_out(vty, " %d/%d %pBD %s flags 0x%x\n", afi, + safi, dest, bgp_path->name_pretty, path->flags); + } + } +} + + +DEFUN(show_bgp_mplsvpn_nh_label_bind, show_bgp_mplsvpn_nh_label_bind_cmd, + "show bgp [<view|vrf> VIEWVRFNAME] mplsvpn-nh-label-bind [detail]", + SHOW_STR BGP_STR BGP_INSTANCE_HELP_STR + "BGP mplsvpn nexthop label binding entries\n" + "Show detailed information\n") +{ + int idx = 0; + char *vrf = NULL; + struct bgp *bgp; + bool detail = false; + + if (argv_find(argv, argc, "vrf", &idx)) { + vrf = argv[++idx]->arg; + bgp = bgp_lookup_by_name(vrf); + } else + bgp = bgp_get_default(); + + if (!bgp) + return CMD_SUCCESS; + + if (argv_find(argv, argc, "detail", &idx)) + detail = true; + + show_bgp_mplsvpn_nh_label_bind_internal(vty, bgp, detail); + return CMD_SUCCESS; +} + +void bgp_mplsvpn_nexthop_init(void) +{ + install_element(VIEW_NODE, &show_bgp_mplsvpn_nh_label_bind_cmd); +} |