mirror of
https://github.com/FRRouting/frr.git
synced 2025-04-30 13:37:17 +02:00

According to draft-ietf-lsr-isis-srv6-extensions draft, the End SID should be available in link state prefix information. Add the SID information in the link state prefix, by getting the END SID from the locator TLV information. Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>
2238 lines
61 KiB
C
2238 lines
61 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* IS-IS Rout(e)ing protocol - isis_te.c
|
|
*
|
|
* This is an implementation of RFC5305 & RFC 7810
|
|
*
|
|
* Author: Olivier Dugeon <olivier.dugeon@orange.com>
|
|
*
|
|
* Copyright (C) 2014 - 2019 Orange Labs http://www.orange.com
|
|
*/
|
|
|
|
#include <zebra.h>
|
|
#include <math.h>
|
|
|
|
#include "linklist.h"
|
|
#include "frrevent.h"
|
|
#include "vty.h"
|
|
#include "stream.h"
|
|
#include "memory.h"
|
|
#include "log.h"
|
|
#include "prefix.h"
|
|
#include "command.h"
|
|
#include "hash.h"
|
|
#include "if.h"
|
|
#include "vrf.h"
|
|
#include "checksum.h"
|
|
#include "md5.h"
|
|
#include "sockunion.h"
|
|
#include "network.h"
|
|
#include "sbuf.h"
|
|
#include "link_state.h"
|
|
#include "lib/json.h"
|
|
|
|
#include "isisd/isis_constants.h"
|
|
#include "isisd/isis_common.h"
|
|
#include "isisd/isis_flags.h"
|
|
#include "isisd/isis_circuit.h"
|
|
#include "isisd/isis_adjacency.h"
|
|
#include "isisd/isisd.h"
|
|
#include "isisd/isis_lsp.h"
|
|
#include "isisd/isis_pdu.h"
|
|
#include "isisd/isis_dynhn.h"
|
|
#include "isisd/isis_misc.h"
|
|
#include "isisd/isis_csm.h"
|
|
#include "isisd/isis_adjacency.h"
|
|
#include "isisd/isis_spf.h"
|
|
#include "isisd/isis_tlvs.h"
|
|
#include "isisd/isis_mt.h"
|
|
#include "isisd/isis_te.h"
|
|
#include "isisd/isis_zebra.h"
|
|
|
|
DEFINE_MTYPE_STATIC(ISISD, ISIS_MPLS_TE, "ISIS MPLS_TE parameters");
|
|
|
|
static void isis_mpls_te_circuit_ip_update(struct isis_circuit *circuit);
|
|
|
|
/*------------------------------------------------------------------------*
|
|
* Following are control functions for MPLS-TE parameters management.
|
|
*------------------------------------------------------------------------*/
|
|
|
|
/**
|
|
* Create MPLS Traffic Engineering structure which belongs to given area.
|
|
*
|
|
* @param area IS-IS Area
|
|
*/
|
|
void isis_mpls_te_create(struct isis_area *area)
|
|
{
|
|
struct listnode *node;
|
|
struct isis_circuit *circuit;
|
|
|
|
if (!area)
|
|
return;
|
|
|
|
if (area->mta == NULL) {
|
|
|
|
struct mpls_te_area *new;
|
|
|
|
zlog_debug("ISIS-TE(%s): Initialize MPLS Traffic Engineering",
|
|
area->area_tag);
|
|
|
|
new = XCALLOC(MTYPE_ISIS_MPLS_TE, sizeof(struct mpls_te_area));
|
|
|
|
/* Initialize MPLS_TE structure */
|
|
new->status = enable;
|
|
new->level = 0;
|
|
new->inter_as = off;
|
|
new->interas_areaid.s_addr = 0;
|
|
new->router_id.s_addr = 0;
|
|
new->ted = ls_ted_new(1, "ISIS", 0);
|
|
if (!new->ted)
|
|
zlog_warn("Unable to create Link State Data Base");
|
|
|
|
area->mta = new;
|
|
} else {
|
|
area->mta->status = enable;
|
|
}
|
|
|
|
/* Initialize Link State Database */
|
|
if (area->mta->ted)
|
|
isis_te_init_ted(area);
|
|
|
|
/* Update Extended TLVs according to Interface link parameters
|
|
* and neighbor IP addresses
|
|
*/
|
|
for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) {
|
|
isis_link_params_update(circuit, circuit->interface);
|
|
isis_mpls_te_circuit_ip_update(circuit);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Disable MPLS Traffic Engineering structure which belongs to given area.
|
|
*
|
|
* @param area IS-IS Area
|
|
*/
|
|
void isis_mpls_te_disable(struct isis_area *area)
|
|
{
|
|
struct listnode *node;
|
|
struct isis_circuit *circuit;
|
|
|
|
if (!area->mta)
|
|
return;
|
|
|
|
area->mta->status = disable;
|
|
|
|
/* Remove Link State Database */
|
|
ls_ted_clean(area->mta->ted);
|
|
|
|
/* Disable Extended SubTLVs on all circuit */
|
|
for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) {
|
|
if (!IS_EXT_TE(circuit->ext))
|
|
continue;
|
|
|
|
/* disable MPLS_TE Circuit keeping SR one's */
|
|
if (IS_SUBTLV(circuit->ext, EXT_ADJ_SID))
|
|
circuit->ext->status = EXT_ADJ_SID;
|
|
else if (IS_SUBTLV(circuit->ext, EXT_LAN_ADJ_SID))
|
|
circuit->ext->status = EXT_LAN_ADJ_SID;
|
|
else
|
|
circuit->ext->status = 0;
|
|
}
|
|
}
|
|
|
|
void isis_mpls_te_term(struct isis_area *area)
|
|
{
|
|
struct listnode *node;
|
|
struct isis_circuit *circuit;
|
|
|
|
if (!area->mta)
|
|
return;
|
|
|
|
zlog_info("TE(%s): Terminate MPLS TE", __func__);
|
|
/* Remove Link State Database */
|
|
ls_ted_del_all(&area->mta->ted);
|
|
|
|
/* Remove Extended SubTLVs */
|
|
zlog_info(" |- Remove Extended SubTLVS for all circuit");
|
|
for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) {
|
|
zlog_info(" |- Call isis_del_ext_subtlvs()");
|
|
isis_del_ext_subtlvs(circuit->ext);
|
|
circuit->ext = NULL;
|
|
}
|
|
|
|
zlog_info(" |- Free MTA structure at %p", area->mta);
|
|
XFREE(MTYPE_ISIS_MPLS_TE, area->mta);
|
|
}
|
|
|
|
static void isis_link_params_update_asla(struct isis_circuit *circuit,
|
|
struct interface *ifp)
|
|
{
|
|
struct isis_asla_subtlvs *asla;
|
|
struct listnode *node, *nnode;
|
|
struct isis_ext_subtlvs *ext = circuit->ext;
|
|
int i;
|
|
|
|
if (!HAS_LINK_PARAMS(ifp)) {
|
|
list_delete_all_node(ext->aslas);
|
|
return;
|
|
}
|
|
|
|
#ifndef FABRICD
|
|
/* RFC 8919 Application Specific Link-Attributes
|
|
* is required by flex-algo application ISIS_SABM_FLAG_X
|
|
*/
|
|
if (list_isempty(circuit->area->flex_algos->flex_algos))
|
|
isis_tlvs_free_asla(ext, ISIS_SABM_FLAG_X);
|
|
else
|
|
isis_tlvs_find_alloc_asla(ext, ISIS_SABM_FLAG_X);
|
|
#endif /* ifndef FABRICD */
|
|
|
|
if (list_isempty(ext->aslas))
|
|
return;
|
|
|
|
for (ALL_LIST_ELEMENTS(ext->aslas, node, nnode, asla)) {
|
|
asla->legacy = circuit->area->asla_legacy_flag;
|
|
RESET_SUBTLV(asla);
|
|
|
|
if (asla->legacy)
|
|
continue;
|
|
|
|
/* Fulfill ASLA subTLVs from interface link parameters */
|
|
if (IS_PARAM_SET(ifp->link_params, LP_ADM_GRP)) {
|
|
asla->admin_group = ifp->link_params->admin_grp;
|
|
SET_SUBTLV(asla, EXT_ADM_GRP);
|
|
} else
|
|
UNSET_SUBTLV(asla, EXT_ADM_GRP);
|
|
|
|
if (IS_PARAM_SET(ifp->link_params, LP_EXTEND_ADM_GRP)) {
|
|
admin_group_copy(&asla->ext_admin_group,
|
|
&ifp->link_params->ext_admin_grp);
|
|
SET_SUBTLV(asla, EXT_EXTEND_ADM_GRP);
|
|
} else
|
|
UNSET_SUBTLV(asla, EXT_EXTEND_ADM_GRP);
|
|
|
|
/* Send admin-group zero for better compatibility
|
|
* https://www.rfc-editor.org/rfc/rfc7308#section-2.3.2
|
|
*/
|
|
if (circuit->area->admin_group_send_zero &&
|
|
!IS_SUBTLV(asla, EXT_ADM_GRP) &&
|
|
!IS_SUBTLV(asla, EXT_EXTEND_ADM_GRP)) {
|
|
asla->admin_group = 0;
|
|
SET_SUBTLV(asla, EXT_ADM_GRP);
|
|
admin_group_clear(&asla->ext_admin_group);
|
|
admin_group_allow_explicit_zero(&asla->ext_admin_group);
|
|
SET_SUBTLV(asla, EXT_EXTEND_ADM_GRP);
|
|
}
|
|
|
|
if (IS_PARAM_SET(ifp->link_params, LP_TE_METRIC)) {
|
|
asla->te_metric = ifp->link_params->te_metric;
|
|
SET_SUBTLV(asla, EXT_TE_METRIC);
|
|
} else
|
|
UNSET_SUBTLV(asla, EXT_TE_METRIC);
|
|
|
|
if (IS_PARAM_SET(ifp->link_params, LP_DELAY)) {
|
|
asla->delay = ifp->link_params->av_delay;
|
|
SET_SUBTLV(asla, EXT_DELAY);
|
|
} else
|
|
UNSET_SUBTLV(asla, EXT_DELAY);
|
|
|
|
if (IS_PARAM_SET(ifp->link_params, LP_MM_DELAY)) {
|
|
asla->min_delay = ifp->link_params->min_delay;
|
|
asla->max_delay = ifp->link_params->max_delay;
|
|
SET_SUBTLV(asla, EXT_MM_DELAY);
|
|
} else {
|
|
UNSET_SUBTLV(asla, EXT_MM_DELAY);
|
|
}
|
|
|
|
if (asla->standard_apps == ISIS_SABM_FLAG_X)
|
|
/* Flex-Algo ASLA does not need the following TE
|
|
* sub-TLVs
|
|
*/
|
|
continue;
|
|
|
|
if (IS_PARAM_SET(ifp->link_params, LP_MAX_BW)) {
|
|
asla->max_bw = ifp->link_params->max_bw;
|
|
SET_SUBTLV(asla, EXT_MAX_BW);
|
|
} else
|
|
UNSET_SUBTLV(asla, EXT_MAX_BW);
|
|
|
|
if (IS_PARAM_SET(ifp->link_params, LP_MAX_RSV_BW)) {
|
|
asla->max_rsv_bw = ifp->link_params->max_rsv_bw;
|
|
SET_SUBTLV(asla, EXT_MAX_RSV_BW);
|
|
} else
|
|
UNSET_SUBTLV(asla, EXT_MAX_RSV_BW);
|
|
|
|
if (IS_PARAM_SET(ifp->link_params, LP_UNRSV_BW)) {
|
|
for (i = 0; i < MAX_CLASS_TYPE; i++)
|
|
asla->unrsv_bw[i] =
|
|
ifp->link_params->unrsv_bw[i];
|
|
SET_SUBTLV(asla, EXT_UNRSV_BW);
|
|
} else
|
|
UNSET_SUBTLV(asla, EXT_UNRSV_BW);
|
|
|
|
if (IS_PARAM_SET(ifp->link_params, LP_DELAY_VAR)) {
|
|
asla->delay_var = ifp->link_params->delay_var;
|
|
SET_SUBTLV(asla, EXT_DELAY_VAR);
|
|
} else
|
|
UNSET_SUBTLV(asla, EXT_DELAY_VAR);
|
|
|
|
if (IS_PARAM_SET(ifp->link_params, LP_PKT_LOSS)) {
|
|
asla->pkt_loss = ifp->link_params->pkt_loss;
|
|
SET_SUBTLV(asla, EXT_PKT_LOSS);
|
|
} else
|
|
UNSET_SUBTLV(asla, EXT_PKT_LOSS);
|
|
|
|
if (IS_PARAM_SET(ifp->link_params, LP_RES_BW)) {
|
|
asla->res_bw = ifp->link_params->res_bw;
|
|
SET_SUBTLV(asla, EXT_RES_BW);
|
|
} else
|
|
UNSET_SUBTLV(asla, EXT_RES_BW);
|
|
|
|
if (IS_PARAM_SET(ifp->link_params, LP_AVA_BW)) {
|
|
asla->ava_bw = ifp->link_params->ava_bw;
|
|
SET_SUBTLV(asla, EXT_AVA_BW);
|
|
} else
|
|
UNSET_SUBTLV(asla, EXT_AVA_BW);
|
|
|
|
if (IS_PARAM_SET(ifp->link_params, LP_USE_BW)) {
|
|
asla->use_bw = ifp->link_params->use_bw;
|
|
SET_SUBTLV(asla, EXT_USE_BW);
|
|
} else
|
|
UNSET_SUBTLV(asla, EXT_USE_BW);
|
|
}
|
|
|
|
|
|
for (ALL_LIST_ELEMENTS(ext->aslas, node, nnode, asla)) {
|
|
if (!asla->legacy && NO_SUBTLV(asla) &&
|
|
admin_group_nb_words(&asla->ext_admin_group) == 0)
|
|
/* remove ASLA without info from the list of ASLAs to
|
|
* not send void ASLA
|
|
*/
|
|
isis_tlvs_del_asla_flex_algo(ext, asla);
|
|
}
|
|
}
|
|
|
|
/* Main initialization / update function of the MPLS TE Circuit context */
|
|
/* Call when interface TE Link parameters are modified */
|
|
void isis_link_params_update(struct isis_circuit *circuit,
|
|
struct interface *ifp)
|
|
{
|
|
int i;
|
|
struct prefix_ipv4 *addr;
|
|
struct prefix_ipv6 *addr6;
|
|
struct isis_ext_subtlvs *ext;
|
|
|
|
/* Check if TE is enable or not */
|
|
if (!circuit->area || !IS_MPLS_TE(circuit->area->mta))
|
|
return;
|
|
|
|
/* Sanity Check */
|
|
if ((ifp == NULL) || (circuit->state != C_STATE_UP))
|
|
return;
|
|
|
|
te_debug("ISIS-TE(%s): Update circuit parameters for interface %s",
|
|
circuit->area->area_tag, ifp->name);
|
|
|
|
/* Check if MPLS TE Circuit context has not been already created */
|
|
if (circuit->ext == NULL) {
|
|
circuit->ext = isis_alloc_ext_subtlvs();
|
|
te_debug(" |- Allocated new Ext-subTLVs for interface %s",
|
|
ifp->name);
|
|
}
|
|
|
|
ext = circuit->ext;
|
|
|
|
/* Fulfill Extended subTLVs from interface link parameters */
|
|
if (HAS_LINK_PARAMS(ifp)) {
|
|
/* STD_TE metrics */
|
|
if (IS_PARAM_SET(ifp->link_params, LP_ADM_GRP)) {
|
|
ext->adm_group = ifp->link_params->admin_grp;
|
|
SET_SUBTLV(ext, EXT_ADM_GRP);
|
|
} else
|
|
UNSET_SUBTLV(ext, EXT_ADM_GRP);
|
|
|
|
if (IS_PARAM_SET(ifp->link_params, LP_EXTEND_ADM_GRP)) {
|
|
admin_group_copy(&ext->ext_admin_group,
|
|
&ifp->link_params->ext_admin_grp);
|
|
SET_SUBTLV(ext, EXT_EXTEND_ADM_GRP);
|
|
} else
|
|
UNSET_SUBTLV(ext, EXT_EXTEND_ADM_GRP);
|
|
|
|
/* Send admin-group zero for better compatibility
|
|
* https://www.rfc-editor.org/rfc/rfc7308#section-2.3.2
|
|
*/
|
|
if (circuit->area->admin_group_send_zero &&
|
|
!IS_SUBTLV(ext, EXT_ADM_GRP) &&
|
|
!IS_SUBTLV(ext, EXT_EXTEND_ADM_GRP)) {
|
|
ext->adm_group = 0;
|
|
SET_SUBTLV(ext, EXT_ADM_GRP);
|
|
admin_group_clear(&ext->ext_admin_group);
|
|
admin_group_allow_explicit_zero(&ext->ext_admin_group);
|
|
SET_SUBTLV(ext, EXT_EXTEND_ADM_GRP);
|
|
}
|
|
|
|
/* If known, register local IPv4 addr from ip_addr list */
|
|
if (listcount(circuit->ip_addrs) != 0) {
|
|
addr = (struct prefix_ipv4 *)listgetdata(
|
|
(struct listnode *)listhead(circuit->ip_addrs));
|
|
IPV4_ADDR_COPY(&ext->local_addr, &addr->prefix);
|
|
SET_SUBTLV(ext, EXT_LOCAL_ADDR);
|
|
} else
|
|
UNSET_SUBTLV(ext, EXT_LOCAL_ADDR);
|
|
|
|
/* If known, register local IPv6 addr from ip_addr list */
|
|
if (listcount(circuit->ipv6_non_link) != 0) {
|
|
addr6 = (struct prefix_ipv6 *)listgetdata(
|
|
(struct listnode *)listhead(
|
|
circuit->ipv6_non_link));
|
|
IPV6_ADDR_COPY(&ext->local_addr6, &addr6->prefix);
|
|
SET_SUBTLV(ext, EXT_LOCAL_ADDR6);
|
|
} else
|
|
UNSET_SUBTLV(ext, EXT_LOCAL_ADDR6);
|
|
|
|
/*
|
|
* Remote IPv4 and IPv6 addresses are now added in
|
|
* isis_mpls_te_adj_ip_enabled() to get the right IP address
|
|
* in particular for IPv6 to get the global IPv6 address and
|
|
* not the link-local IPv6 address.
|
|
*/
|
|
|
|
if (IS_PARAM_SET(ifp->link_params, LP_MAX_BW)) {
|
|
ext->max_bw = ifp->link_params->max_bw;
|
|
SET_SUBTLV(ext, EXT_MAX_BW);
|
|
} else
|
|
UNSET_SUBTLV(ext, EXT_MAX_BW);
|
|
|
|
if (IS_PARAM_SET(ifp->link_params, LP_MAX_RSV_BW)) {
|
|
ext->max_rsv_bw = ifp->link_params->max_rsv_bw;
|
|
SET_SUBTLV(ext, EXT_MAX_RSV_BW);
|
|
} else
|
|
UNSET_SUBTLV(ext, EXT_MAX_RSV_BW);
|
|
|
|
if (IS_PARAM_SET(ifp->link_params, LP_UNRSV_BW)) {
|
|
for (i = 0; i < MAX_CLASS_TYPE; i++)
|
|
ext->unrsv_bw[i] =
|
|
ifp->link_params->unrsv_bw[i];
|
|
SET_SUBTLV(ext, EXT_UNRSV_BW);
|
|
} else
|
|
UNSET_SUBTLV(ext, EXT_UNRSV_BW);
|
|
|
|
if (IS_PARAM_SET(ifp->link_params, LP_TE_METRIC)) {
|
|
ext->te_metric = ifp->link_params->te_metric;
|
|
SET_SUBTLV(ext, EXT_TE_METRIC);
|
|
} else
|
|
UNSET_SUBTLV(ext, EXT_TE_METRIC);
|
|
|
|
/* TE metric extensions */
|
|
if (IS_PARAM_SET(ifp->link_params, LP_DELAY)) {
|
|
ext->delay = ifp->link_params->av_delay;
|
|
SET_SUBTLV(ext, EXT_DELAY);
|
|
} else
|
|
UNSET_SUBTLV(ext, EXT_DELAY);
|
|
|
|
if (IS_PARAM_SET(ifp->link_params, LP_MM_DELAY)) {
|
|
ext->min_delay = ifp->link_params->min_delay;
|
|
ext->max_delay = ifp->link_params->max_delay;
|
|
SET_SUBTLV(ext, EXT_MM_DELAY);
|
|
} else
|
|
UNSET_SUBTLV(ext, EXT_MM_DELAY);
|
|
|
|
if (IS_PARAM_SET(ifp->link_params, LP_DELAY_VAR)) {
|
|
ext->delay_var = ifp->link_params->delay_var;
|
|
SET_SUBTLV(ext, EXT_DELAY_VAR);
|
|
} else
|
|
UNSET_SUBTLV(ext, EXT_DELAY_VAR);
|
|
|
|
if (IS_PARAM_SET(ifp->link_params, LP_PKT_LOSS)) {
|
|
ext->pkt_loss = ifp->link_params->pkt_loss;
|
|
SET_SUBTLV(ext, EXT_PKT_LOSS);
|
|
} else
|
|
UNSET_SUBTLV(ext, EXT_PKT_LOSS);
|
|
|
|
if (IS_PARAM_SET(ifp->link_params, LP_RES_BW)) {
|
|
ext->res_bw = ifp->link_params->res_bw;
|
|
SET_SUBTLV(ext, EXT_RES_BW);
|
|
} else
|
|
UNSET_SUBTLV(ext, EXT_RES_BW);
|
|
|
|
if (IS_PARAM_SET(ifp->link_params, LP_AVA_BW)) {
|
|
ext->ava_bw = ifp->link_params->ava_bw;
|
|
SET_SUBTLV(ext, EXT_AVA_BW);
|
|
} else
|
|
UNSET_SUBTLV(ext, EXT_AVA_BW);
|
|
|
|
if (IS_PARAM_SET(ifp->link_params, LP_USE_BW)) {
|
|
ext->use_bw = ifp->link_params->use_bw;
|
|
SET_SUBTLV(ext, EXT_USE_BW);
|
|
} else
|
|
UNSET_SUBTLV(ext, EXT_USE_BW);
|
|
|
|
/* INTER_AS */
|
|
if (IS_PARAM_SET(ifp->link_params, LP_RMT_AS)) {
|
|
ext->remote_as = ifp->link_params->rmt_as;
|
|
ext->remote_ip = ifp->link_params->rmt_ip;
|
|
SET_SUBTLV(ext, EXT_RMT_AS);
|
|
SET_SUBTLV(ext, EXT_RMT_IP);
|
|
} else {
|
|
/* reset inter-as TE params */
|
|
UNSET_SUBTLV(ext, EXT_RMT_AS);
|
|
UNSET_SUBTLV(ext, EXT_RMT_IP);
|
|
}
|
|
te_debug(" |- New MPLS-TE link parameters status 0x%x",
|
|
ext->status);
|
|
} else {
|
|
te_debug(" |- Reset Extended subTLVs status 0x%x",
|
|
ext->status);
|
|
/* Reset TE subTLVs keeping SR one's */
|
|
if (IS_SUBTLV(ext, EXT_ADJ_SID))
|
|
ext->status = EXT_ADJ_SID;
|
|
else if (IS_SUBTLV(ext, EXT_LAN_ADJ_SID))
|
|
ext->status = EXT_LAN_ADJ_SID;
|
|
else if (IS_SUBTLV(ext, EXT_SRV6_LAN_ENDX_SID))
|
|
ext->status = EXT_SRV6_LAN_ENDX_SID;
|
|
else if (IS_SUBTLV(ext, EXT_SRV6_ENDX_SID))
|
|
ext->status = EXT_SRV6_ENDX_SID;
|
|
else
|
|
ext->status = 0;
|
|
}
|
|
|
|
isis_link_params_update_asla(circuit, ifp);
|
|
|
|
return;
|
|
}
|
|
|
|
static int _isis_mpls_te_adj_ip_enabled(struct isis_adjacency *adj, int family,
|
|
bool global)
|
|
{
|
|
struct isis_circuit *circuit;
|
|
struct isis_ext_subtlvs *ext;
|
|
|
|
circuit = adj->circuit;
|
|
|
|
/* Check that MPLS TE is enabled */
|
|
if (!IS_MPLS_TE(circuit->area->mta) || !circuit->ext)
|
|
return 0;
|
|
|
|
ext = circuit->ext;
|
|
|
|
/* Determine nexthop IP address */
|
|
switch (family) {
|
|
case AF_INET:
|
|
if (!circuit->ip_router || !adj->ipv4_address_count)
|
|
UNSET_SUBTLV(ext, EXT_NEIGH_ADDR);
|
|
else {
|
|
IPV4_ADDR_COPY(&ext->neigh_addr,
|
|
&adj->ipv4_addresses[0]);
|
|
SET_SUBTLV(ext, EXT_NEIGH_ADDR);
|
|
}
|
|
break;
|
|
case AF_INET6:
|
|
/* Nothing to do for link-local addresses - ie. not global.
|
|
* https://datatracker.ietf.org/doc/html/rfc6119#section-3.1.1
|
|
* Because the IPv6 traffic engineering TLVs present in LSPs are
|
|
* propagated across networks, they MUST NOT use link-local
|
|
* addresses.
|
|
*/
|
|
if (!global)
|
|
return 0;
|
|
|
|
if (!circuit->ipv6_router || !adj->global_ipv6_count)
|
|
UNSET_SUBTLV(ext, EXT_NEIGH_ADDR6);
|
|
else {
|
|
IPV6_ADDR_COPY(&ext->neigh_addr6,
|
|
&adj->global_ipv6_addrs[0]);
|
|
SET_SUBTLV(ext, EXT_NEIGH_ADDR6);
|
|
}
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int isis_mpls_te_adj_ip_enabled(struct isis_adjacency *adj, int family,
|
|
bool global)
|
|
{
|
|
int ret;
|
|
|
|
/* Sanity Check */
|
|
if (!adj || !adj->circuit)
|
|
return 0;
|
|
|
|
ret = _isis_mpls_te_adj_ip_enabled(adj, family, global);
|
|
|
|
/* Update LSP */
|
|
lsp_regenerate_schedule(adj->circuit->area, adj->circuit->is_type, 0);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int _isis_mpls_te_adj_ip_disabled(struct isis_adjacency *adj, int family,
|
|
bool global)
|
|
{
|
|
struct isis_circuit *circuit;
|
|
struct isis_ext_subtlvs *ext;
|
|
|
|
circuit = adj->circuit;
|
|
|
|
/* Check that MPLS TE is enabled */
|
|
if (!IS_MPLS_TE(circuit->area->mta) || !circuit->ext)
|
|
return 0;
|
|
|
|
ext = circuit->ext;
|
|
|
|
/* Update MPLS TE IP address parameters if possible */
|
|
if (!IS_MPLS_TE(circuit->area->mta) || !IS_EXT_TE(ext))
|
|
return 0;
|
|
|
|
/* Determine nexthop IP address */
|
|
switch (family) {
|
|
case AF_INET:
|
|
UNSET_SUBTLV(ext, EXT_NEIGH_ADDR);
|
|
break;
|
|
case AF_INET6:
|
|
if (global)
|
|
UNSET_SUBTLV(ext, EXT_NEIGH_ADDR6);
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int isis_mpls_te_adj_ip_disabled(struct isis_adjacency *adj, int family,
|
|
bool global)
|
|
{
|
|
int ret;
|
|
|
|
/* Sanity Check */
|
|
if (!adj || !adj->circuit || !adj->circuit->ext)
|
|
return 0;
|
|
|
|
ret = _isis_mpls_te_adj_ip_disabled(adj, family, global);
|
|
|
|
/* Update LSP */
|
|
lsp_regenerate_schedule(adj->circuit->area, adj->circuit->is_type, 0);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void isis_mpls_te_circuit_ip_update(struct isis_circuit *circuit)
|
|
{
|
|
struct isis_adjacency *adj;
|
|
|
|
/* https://datatracker.ietf.org/doc/html/rfc6119#section-3.2.3
|
|
* This sub-TLV of the Extended IS Reachability TLV is used for point-
|
|
* to-point links
|
|
*/
|
|
if (circuit->circ_type != CIRCUIT_T_P2P)
|
|
return;
|
|
|
|
adj = circuit->u.p2p.neighbor;
|
|
|
|
if (!adj)
|
|
return;
|
|
|
|
/* Nothing to do for link-local addresses.
|
|
* https://datatracker.ietf.org/doc/html/rfc6119#section-3.1.1
|
|
* Because the IPv6 traffic engineering TLVs present in LSPs are
|
|
* propagated across networks, they MUST NOT use link-local addresses.
|
|
*/
|
|
if (adj->ipv4_address_count > 0)
|
|
_isis_mpls_te_adj_ip_enabled(adj, AF_INET, false);
|
|
else
|
|
_isis_mpls_te_adj_ip_disabled(adj, AF_INET, false);
|
|
|
|
if (adj->global_ipv6_count > 0)
|
|
_isis_mpls_te_adj_ip_enabled(adj, AF_INET6, true);
|
|
else
|
|
_isis_mpls_te_adj_ip_disabled(adj, AF_INET6, true);
|
|
}
|
|
|
|
|
|
int isis_mpls_te_update(struct interface *ifp)
|
|
{
|
|
struct isis_circuit *circuit;
|
|
uint8_t rc = 1;
|
|
|
|
/* Sanity Check */
|
|
if (ifp == NULL)
|
|
return rc;
|
|
|
|
/* Get circuit context from interface */
|
|
circuit = circuit_scan_by_ifp(ifp);
|
|
if (circuit == NULL)
|
|
return rc;
|
|
|
|
/* Update TE TLVs ... */
|
|
isis_link_params_update(circuit, ifp);
|
|
|
|
/* ... and LSP */
|
|
if (circuit->area &&
|
|
(IS_MPLS_TE(circuit->area->mta)
|
|
#ifndef FABRICD
|
|
|| !list_isempty(circuit->area->flex_algos->flex_algos)
|
|
#endif /* ifndef FABRICD */
|
|
))
|
|
lsp_regenerate_schedule(circuit->area, circuit->is_type, 0);
|
|
|
|
rc = 0;
|
|
return rc;
|
|
}
|
|
|
|
|
|
/**
|
|
* Export Link State information to consumer daemon through ZAPI Link State
|
|
* Opaque Message.
|
|
*
|
|
* @param type Type of Link State Element i.e. Vertex, Edge or Subnet
|
|
* @param link_state Pointer to Link State Vertex, Edge or Subnet
|
|
*
|
|
* @return 0 if success, -1 otherwise
|
|
*/
|
|
static int isis_te_export(uint8_t type, void *link_state)
|
|
{
|
|
struct ls_message msg = {};
|
|
int rc = 0;
|
|
|
|
switch (type) {
|
|
case LS_MSG_TYPE_NODE:
|
|
ls_vertex2msg(&msg, (struct ls_vertex *)link_state);
|
|
rc = ls_send_msg(zclient, &msg, NULL);
|
|
break;
|
|
case LS_MSG_TYPE_ATTRIBUTES:
|
|
ls_edge2msg(&msg, (struct ls_edge *)link_state);
|
|
rc = ls_send_msg(zclient, &msg, NULL);
|
|
break;
|
|
case LS_MSG_TYPE_PREFIX:
|
|
ls_subnet2msg(&msg, (struct ls_subnet *)link_state);
|
|
rc = ls_send_msg(zclient, &msg, NULL);
|
|
break;
|
|
default:
|
|
rc = -1;
|
|
break;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Parse LSP and build corresponding vertex. If vertex doesn't exist in the
|
|
* Link State Database it is created otherwise updated.
|
|
*
|
|
* @param ted Traffic Engineering Link State Database
|
|
* @param lsp IS-IS Link State PDU
|
|
*
|
|
* @return Link State Vertex or NULL in case of error
|
|
*/
|
|
static struct ls_vertex *lsp_to_vertex(struct ls_ted *ted, struct isis_lsp *lsp)
|
|
{
|
|
struct ls_vertex *vertex = NULL;
|
|
struct ls_node *old, lnode = {};
|
|
struct isis_tlvs *tlvs;
|
|
const struct in_addr inaddr_any = {.s_addr = INADDR_ANY};
|
|
|
|
/* Sanity check */
|
|
if (!ted || !lsp)
|
|
return NULL;
|
|
|
|
/* Compute Link State Node ID from IS-IS sysID ... */
|
|
if (lsp->level == ISIS_LEVEL1)
|
|
lnode.adv.origin = ISIS_L1;
|
|
else
|
|
lnode.adv.origin = ISIS_L2;
|
|
memcpy(&lnode.adv.id.iso.sys_id, &lsp->hdr.lsp_id, ISIS_SYS_ID_LEN);
|
|
lnode.adv.id.iso.level = lsp->level;
|
|
/* ... and search the corresponding vertex */
|
|
vertex = ls_find_vertex_by_id(ted, lnode.adv);
|
|
/* Create a new one if not found */
|
|
if (!vertex) {
|
|
old = ls_node_new(lnode.adv, inaddr_any, in6addr_any);
|
|
old->type = STANDARD;
|
|
vertex = ls_vertex_add(ted, old);
|
|
}
|
|
old = vertex->node;
|
|
te_debug(" |- %s Vertex (%" PRIu64 ") for node %s",
|
|
vertex->status == NEW ? "Create" : "Found", vertex->key,
|
|
print_sys_hostname(old->adv.id.iso.sys_id));
|
|
|
|
/* Fulfill Link State Node information */
|
|
tlvs = lsp->tlvs;
|
|
if (tlvs) {
|
|
if (tlvs->te_router_id) {
|
|
IPV4_ADDR_COPY(&lnode.router_id, tlvs->te_router_id);
|
|
SET_FLAG(lnode.flags, LS_NODE_ROUTER_ID);
|
|
}
|
|
if (tlvs->te_router_id_ipv6) {
|
|
IPV6_ADDR_COPY(&lnode.router_id6,
|
|
tlvs->te_router_id_ipv6);
|
|
SET_FLAG(lnode.flags, LS_NODE_ROUTER_ID6);
|
|
}
|
|
if (tlvs->hostname) {
|
|
strlcpy(lnode.name, tlvs->hostname, MAX_NAME_LENGTH);
|
|
SET_FLAG(lnode.flags, LS_NODE_NAME);
|
|
}
|
|
if (tlvs->router_cap) {
|
|
struct isis_router_cap *cap = tlvs->router_cap;
|
|
|
|
if (cap->srgb.lower_bound != 0
|
|
&& cap->srgb.range_size != 0) {
|
|
SET_FLAG(lnode.flags, LS_NODE_SR);
|
|
lnode.srgb.flag = cap->srgb.flags;
|
|
lnode.srgb.lower_bound = cap->srgb.lower_bound;
|
|
lnode.srgb.range_size = cap->srgb.range_size;
|
|
for (int i = 0; i < LIB_LS_SR_ALGO_COUNT; i++)
|
|
lnode.algo[i] = cap->algo[i];
|
|
}
|
|
|
|
if (cap->srlb.lower_bound != 0
|
|
&& cap->srlb.range_size != 0) {
|
|
lnode.srlb.lower_bound = cap->srlb.lower_bound;
|
|
lnode.srlb.range_size = cap->srlb.range_size;
|
|
SET_FLAG(lnode.flags, LS_NODE_SRLB);
|
|
}
|
|
if (cap->msd != 0) {
|
|
lnode.msd = cap->msd;
|
|
SET_FLAG(lnode.flags, LS_NODE_MSD);
|
|
}
|
|
if (cap->srv6_cap.is_srv6_capable) {
|
|
SET_FLAG(lnode.flags, LS_NODE_SRV6);
|
|
lnode.srv6_cap_flags = cap->srv6_cap.flags;
|
|
memcpy(&lnode.srv6_msd, &cap->srv6_msd,
|
|
sizeof(struct isis_srv6_msd));
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Update Link State Node information */
|
|
if (!ls_node_same(old, &lnode)) {
|
|
te_debug(" |- Update Link State Node information");
|
|
memcpy(old, &lnode, sizeof(struct ls_node));
|
|
if (vertex->status != NEW)
|
|
vertex->status = UPDATE;
|
|
}
|
|
|
|
/* Set self TED vertex if LSP corresponds to the own router */
|
|
if (lsp->own_lsp)
|
|
ted->self = vertex;
|
|
|
|
return vertex;
|
|
}
|
|
|
|
/**
|
|
* Get Link State Edge from Link State Attributes in TE Database.
|
|
* Edge structure is dynamically allocated and fulfill with Link State
|
|
* Attributes if not found.
|
|
*
|
|
* @param ted Link State Database
|
|
* @param attr Link State Attributes
|
|
*
|
|
* @return New Link State Edge if success, NULL otherwise
|
|
*/
|
|
static struct ls_edge *get_edge(struct ls_ted *ted, struct ls_attributes *attr)
|
|
{
|
|
struct ls_edge *edge;
|
|
struct ls_standard *std;
|
|
struct ls_edge_key key;
|
|
|
|
/* Check parameters */
|
|
if (!ted || !attr)
|
|
return NULL;
|
|
|
|
std = &attr->standard;
|
|
|
|
/* Compute keys in function of local address (IPv4/v6) or identifier */
|
|
if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR)) {
|
|
key.family = AF_INET;
|
|
IPV4_ADDR_COPY(&key.k.addr, &std->local);
|
|
} else if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR6)) {
|
|
key.family = AF_INET6;
|
|
IPV6_ADDR_COPY(&key.k.addr6, &std->local6);
|
|
} else if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ID)) {
|
|
key.family = AF_LOCAL;
|
|
key.k.link_id = (((uint64_t)std->local_id) & 0xffffffff) |
|
|
((uint64_t)std->remote_id << 32);
|
|
} else {
|
|
key.family = AF_UNSPEC;
|
|
}
|
|
|
|
/* Stop here if we don't got a valid key */
|
|
if (key.family == AF_UNSPEC)
|
|
return NULL;
|
|
|
|
/* Get corresponding Edge by key from Link State Data Base */
|
|
edge = ls_find_edge_by_key(ted, key);
|
|
|
|
/* and create new one if not exist */
|
|
if (!edge) {
|
|
edge = ls_edge_add(ted, attr);
|
|
/*
|
|
* Edge could be Null if no local ID is found in Attributes.
|
|
* Stop the processing as without any local ID it is not
|
|
* possible to store Edge in the TED.
|
|
*/
|
|
if (!edge)
|
|
return NULL;
|
|
}
|
|
|
|
if (CHECK_FLAG(edge->attributes->flags, LS_ATTR_LOCAL_ADDR))
|
|
te_debug(" |- %s Edge (%pI4) from Extended Reach. %pI4",
|
|
edge->status == NEW ? "Create" : "Found",
|
|
&edge->key.k.addr, &attr->standard.local);
|
|
else if (CHECK_FLAG(edge->attributes->flags, LS_ATTR_LOCAL_ADDR6))
|
|
te_debug(" |- %s Edge (%pI6) from Extended Reach. %pI6",
|
|
edge->status == NEW ? "Create" : "Found",
|
|
&edge->key.k.addr6, &attr->standard.local6);
|
|
else
|
|
te_debug(" |- %s Edge (%" PRIu64 ")",
|
|
edge->status == NEW ? "Create" : "Found",
|
|
edge->key.k.link_id);
|
|
|
|
return edge;
|
|
}
|
|
|
|
/**
|
|
* Get Link State Attributes from IS-IS Sub-TLVs. Structure is dynamically
|
|
* allocated and should be free once not use anymore.
|
|
*
|
|
* @param adv Link State Node ID
|
|
* @param tlvs IS-IS Sub TLVs
|
|
*
|
|
* @return New Link State attributes if success, NULL otherwise
|
|
*/
|
|
static struct ls_attributes *get_attributes(struct ls_node_id adv,
|
|
struct isis_ext_subtlvs *tlvs)
|
|
{
|
|
struct ls_attributes *attr;
|
|
struct in_addr local = {.s_addr = INADDR_ANY};
|
|
struct in6_addr local6 = in6addr_any;
|
|
uint32_t local_id = 0;
|
|
|
|
/* Got Local identifier */
|
|
if (CHECK_FLAG(tlvs->status, EXT_LOCAL_ADDR))
|
|
local.s_addr = tlvs->local_addr.s_addr;
|
|
|
|
if (CHECK_FLAG(tlvs->status, EXT_LOCAL_ADDR6))
|
|
memcpy(&local6, &tlvs->local_addr6, IPV6_MAX_BYTELEN);
|
|
|
|
if (CHECK_FLAG(tlvs->status, EXT_LLRI))
|
|
local_id = tlvs->local_llri;
|
|
|
|
/* Create LS Attributes */
|
|
attr = ls_attributes_new(adv, local, local6, local_id);
|
|
if (!attr)
|
|
return NULL;
|
|
|
|
/* Browse sub-TLV and fulfill Link State Attributes */
|
|
if (CHECK_FLAG(tlvs->status, EXT_ADM_GRP)) {
|
|
attr->standard.admin_group = tlvs->adm_group;
|
|
SET_FLAG(attr->flags, LS_ATTR_ADM_GRP);
|
|
}
|
|
if (CHECK_FLAG(tlvs->status, EXT_EXTEND_ADM_GRP)) {
|
|
admin_group_copy(&attr->ext_admin_group,
|
|
&tlvs->ext_admin_group);
|
|
SET_FLAG(attr->flags, LS_ATTR_EXT_ADM_GRP);
|
|
}
|
|
if (CHECK_FLAG(tlvs->status, EXT_LLRI)) {
|
|
attr->standard.local_id = tlvs->local_llri;
|
|
attr->standard.remote_id = tlvs->remote_llri;
|
|
SET_FLAG(attr->flags, LS_ATTR_LOCAL_ID);
|
|
SET_FLAG(attr->flags, LS_ATTR_NEIGH_ID);
|
|
}
|
|
if (CHECK_FLAG(tlvs->status, EXT_NEIGH_ADDR)) {
|
|
attr->standard.remote.s_addr = tlvs->neigh_addr.s_addr;
|
|
SET_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR);
|
|
}
|
|
if (CHECK_FLAG(tlvs->status, EXT_NEIGH_ADDR6)) {
|
|
memcpy(&attr->standard.remote6, &tlvs->neigh_addr6,
|
|
IPV6_MAX_BYTELEN);
|
|
SET_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR6);
|
|
}
|
|
if (CHECK_FLAG(tlvs->status, EXT_MAX_BW)) {
|
|
attr->standard.max_bw = tlvs->max_bw;
|
|
SET_FLAG(attr->flags, LS_ATTR_MAX_BW);
|
|
}
|
|
if (CHECK_FLAG(tlvs->status, EXT_MAX_RSV_BW)) {
|
|
attr->standard.max_rsv_bw = tlvs->max_rsv_bw;
|
|
SET_FLAG(attr->flags, LS_ATTR_MAX_RSV_BW);
|
|
}
|
|
if (CHECK_FLAG(tlvs->status, EXT_UNRSV_BW)) {
|
|
memcpy(&attr->standard.unrsv_bw, tlvs->unrsv_bw,
|
|
ISIS_SUBTLV_UNRSV_BW_SIZE);
|
|
SET_FLAG(attr->flags, LS_ATTR_UNRSV_BW);
|
|
}
|
|
if (CHECK_FLAG(tlvs->status, EXT_TE_METRIC)) {
|
|
attr->standard.te_metric = tlvs->te_metric;
|
|
SET_FLAG(attr->flags, LS_ATTR_TE_METRIC);
|
|
}
|
|
if (CHECK_FLAG(tlvs->status, EXT_RMT_AS)) {
|
|
attr->standard.remote_as = tlvs->remote_as;
|
|
SET_FLAG(attr->flags, LS_ATTR_REMOTE_AS);
|
|
}
|
|
if (CHECK_FLAG(tlvs->status, EXT_RMT_IP)) {
|
|
attr->standard.remote_addr = tlvs->remote_ip;
|
|
SET_FLAG(attr->flags, LS_ATTR_REMOTE_ADDR);
|
|
}
|
|
if (CHECK_FLAG(tlvs->status, EXT_DELAY)) {
|
|
attr->extended.delay = tlvs->delay;
|
|
SET_FLAG(attr->flags, LS_ATTR_DELAY);
|
|
}
|
|
if (CHECK_FLAG(tlvs->status, EXT_MM_DELAY)) {
|
|
attr->extended.min_delay = tlvs->min_delay;
|
|
attr->extended.max_delay = tlvs->max_delay;
|
|
SET_FLAG(attr->flags, LS_ATTR_MIN_MAX_DELAY);
|
|
}
|
|
if (CHECK_FLAG(tlvs->status, EXT_DELAY_VAR)) {
|
|
attr->extended.jitter = tlvs->delay_var;
|
|
SET_FLAG(attr->flags, LS_ATTR_JITTER);
|
|
}
|
|
if (CHECK_FLAG(tlvs->status, EXT_PKT_LOSS)) {
|
|
attr->extended.pkt_loss = tlvs->pkt_loss;
|
|
SET_FLAG(attr->flags, LS_ATTR_PACKET_LOSS);
|
|
}
|
|
if (CHECK_FLAG(tlvs->status, EXT_AVA_BW)) {
|
|
attr->extended.ava_bw = tlvs->ava_bw;
|
|
SET_FLAG(attr->flags, LS_ATTR_AVA_BW);
|
|
}
|
|
if (CHECK_FLAG(tlvs->status, EXT_RES_BW)) {
|
|
attr->extended.rsv_bw = tlvs->res_bw;
|
|
SET_FLAG(attr->flags, LS_ATTR_RSV_BW);
|
|
}
|
|
if (CHECK_FLAG(tlvs->status, EXT_USE_BW)) {
|
|
attr->extended.used_bw = tlvs->use_bw;
|
|
SET_FLAG(attr->flags, LS_ATTR_USE_BW);
|
|
}
|
|
if (CHECK_FLAG(tlvs->status, EXT_ADJ_SID)) {
|
|
struct isis_adj_sid *adj =
|
|
(struct isis_adj_sid *)tlvs->adj_sid.head;
|
|
int i;
|
|
for (; adj; adj = adj->next) {
|
|
i = adj->flags & EXT_SUBTLV_LINK_ADJ_SID_BFLG ? 1 : 0;
|
|
i += adj->flags & EXT_SUBTLV_LINK_ADJ_SID_FFLG ? 2 : 0;
|
|
attr->adj_sid[i].flags = adj->flags;
|
|
attr->adj_sid[i].weight = adj->weight;
|
|
attr->adj_sid[i].sid = adj->sid;
|
|
switch (i) {
|
|
case ADJ_PRI_IPV4:
|
|
SET_FLAG(attr->flags, LS_ATTR_ADJ_SID);
|
|
break;
|
|
case ADJ_BCK_IPV4:
|
|
SET_FLAG(attr->flags, LS_ATTR_BCK_ADJ_SID);
|
|
break;
|
|
case ADJ_PRI_IPV6:
|
|
SET_FLAG(attr->flags, LS_ATTR_ADJ_SID6);
|
|
break;
|
|
case ADJ_BCK_IPV6:
|
|
SET_FLAG(attr->flags, LS_ATTR_BCK_ADJ_SID6);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (CHECK_FLAG(tlvs->status, EXT_LAN_ADJ_SID)) {
|
|
struct isis_lan_adj_sid *ladj =
|
|
(struct isis_lan_adj_sid *)tlvs->lan_sid.head;
|
|
int i;
|
|
for (; ladj; ladj = ladj->next) {
|
|
i = ladj->flags & EXT_SUBTLV_LINK_ADJ_SID_BFLG ? 1 : 0;
|
|
i += ladj->flags & EXT_SUBTLV_LINK_ADJ_SID_FFLG ? 2 : 0;
|
|
attr->adj_sid[i].flags = ladj->flags;
|
|
attr->adj_sid[i].weight = ladj->weight;
|
|
attr->adj_sid[i].sid = ladj->sid;
|
|
memcpy(&attr->adj_sid[i].neighbor.sysid,
|
|
&ladj->neighbor_id, ISIS_SYS_ID_LEN);
|
|
switch (i) {
|
|
case ADJ_PRI_IPV4:
|
|
SET_FLAG(attr->flags, LS_ATTR_ADJ_SID);
|
|
break;
|
|
case ADJ_BCK_IPV4:
|
|
SET_FLAG(attr->flags, LS_ATTR_BCK_ADJ_SID);
|
|
break;
|
|
case ADJ_PRI_IPV6:
|
|
SET_FLAG(attr->flags, LS_ATTR_ADJ_SID6);
|
|
break;
|
|
case ADJ_BCK_IPV6:
|
|
SET_FLAG(attr->flags, LS_ATTR_BCK_ADJ_SID6);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (CHECK_FLAG(tlvs->status, EXT_SRV6_ENDX_SID)) {
|
|
struct isis_srv6_endx_sid_subtlv *endx =
|
|
(struct isis_srv6_endx_sid_subtlv *)
|
|
tlvs->srv6_endx_sid.head;
|
|
int i;
|
|
|
|
for (; endx; endx = endx->next) {
|
|
if (endx->flags & EXT_SUBTLV_LINK_SRV6_ENDX_SID_BFLG) {
|
|
i = 1;
|
|
SET_FLAG(attr->flags, LS_ATTR_BCK_ADJ_SRV6SID);
|
|
} else {
|
|
i = 0;
|
|
SET_FLAG(attr->flags, LS_ATTR_ADJ_SRV6SID);
|
|
}
|
|
attr->adj_srv6_sid[i].flags = endx->flags;
|
|
attr->adj_srv6_sid[i].weight = endx->weight;
|
|
memcpy(&attr->adj_srv6_sid[i].sid, &endx->sid,
|
|
sizeof(struct in6_addr));
|
|
attr->adj_srv6_sid[i].endpoint_behavior = endx->behavior;
|
|
}
|
|
}
|
|
if (CHECK_FLAG(tlvs->status, EXT_SRV6_LAN_ENDX_SID)) {
|
|
struct isis_srv6_lan_endx_sid_subtlv *lendx =
|
|
(struct isis_srv6_lan_endx_sid_subtlv *)
|
|
tlvs->srv6_lan_endx_sid.head;
|
|
int i;
|
|
|
|
for (; lendx; lendx = lendx->next) {
|
|
if (lendx->flags & EXT_SUBTLV_LINK_SRV6_ENDX_SID_BFLG) {
|
|
i = 1;
|
|
SET_FLAG(attr->flags, LS_ATTR_BCK_ADJ_SRV6SID);
|
|
} else {
|
|
i = 0;
|
|
SET_FLAG(attr->flags, LS_ATTR_ADJ_SRV6SID);
|
|
}
|
|
memcpy(&attr->adj_srv6_sid[i].neighbor.sysid,
|
|
&lendx->neighbor_id, ISIS_SYS_ID_LEN);
|
|
attr->adj_srv6_sid[i].flags = lendx->flags;
|
|
attr->adj_srv6_sid[i].weight = lendx->weight;
|
|
memcpy(&attr->adj_srv6_sid[i].sid, &lendx->sid,
|
|
sizeof(struct in6_addr));
|
|
attr->adj_srv6_sid[i].endpoint_behavior =
|
|
lendx->behavior;
|
|
}
|
|
}
|
|
return attr;
|
|
}
|
|
|
|
/**
|
|
* Parse Extended Reachability TLVs and create or update the corresponding
|
|
* Link State Edge and Attributes. Vertex connections are also updated if
|
|
* needed based on the remote IP address of the Edge and existing reverse Edge.
|
|
*
|
|
* @param id ID of Extended IS
|
|
* @param metric Metric of the link
|
|
* @param old_metric Boolean that indicate if it is an old metric (no TE)
|
|
* @param tlvs SubTlvs that contains TE information
|
|
* @param arg IS-IS TE argument (TED, Vertex, and export indication)
|
|
*
|
|
* @return 0 if success, -1 otherwise
|
|
*/
|
|
static int lsp_to_edge_cb(const uint8_t *id, uint32_t metric, bool old_metric,
|
|
struct isis_ext_subtlvs *tlvs, void *arg)
|
|
{
|
|
struct isis_te_args *args = (struct isis_te_args *)arg;
|
|
struct ls_vertex *vertex;
|
|
struct ls_edge *edge, *dst;
|
|
struct ls_attributes *attr;
|
|
|
|
te_debug(" |- Process Extended IS for %pSY", id);
|
|
|
|
/* Check parameters */
|
|
if (old_metric || !args || !tlvs)
|
|
return LSP_ITER_CONTINUE;
|
|
|
|
/* Initialize Link State Attributes */
|
|
vertex = args->vertex;
|
|
attr = get_attributes(vertex->node->adv, tlvs);
|
|
/*
|
|
* Attributes may be Null if no local ID has been found in the LSP.
|
|
* Stop processing here as without any local ID it is not possible to
|
|
* create corresponding Edge in the TED.
|
|
*/
|
|
if (!attr)
|
|
return LSP_ITER_CONTINUE;
|
|
|
|
attr->metric = metric;
|
|
SET_FLAG(attr->flags, LS_ATTR_METRIC);
|
|
|
|
/* Get corresponding Edge from Link State Data Base */
|
|
edge = get_edge(args->ted, attr);
|
|
/*
|
|
* Edge could be Null if no local ID has been found in Attributes.
|
|
* Stop processing here as without any local ID it is not possible to
|
|
* create corresponding Edge in the TED.
|
|
*/
|
|
if (!edge) {
|
|
ls_attributes_del(attr);
|
|
return LSP_ITER_CONTINUE;
|
|
}
|
|
|
|
/* Update Attribute fields if there are different */
|
|
if (edge->status != NEW) {
|
|
if (!ls_attributes_same(edge->attributes, attr)) {
|
|
te_debug(" |- Update Edge Attributes information");
|
|
ls_attributes_del(edge->attributes);
|
|
edge->attributes = attr;
|
|
edge->status = UPDATE;
|
|
} else {
|
|
if (edge->attributes != attr)
|
|
ls_attributes_del(attr);
|
|
edge->status = SYNC;
|
|
}
|
|
}
|
|
|
|
/* Try to update remote Link from remote address or reachability ID */
|
|
if (edge->key.family == AF_INET)
|
|
te_debug(" |- Link Edge (%pI4) to destination vertex (%s)",
|
|
&edge->key.k.addr, print_sys_hostname(id));
|
|
else if (edge->key.family == AF_INET6)
|
|
te_debug(" |- Link Edge (%pI6) to destination vertex (%s)",
|
|
&edge->key.k.addr6, print_sys_hostname(id));
|
|
else if (edge->key.family == AF_LOCAL)
|
|
te_debug(" |- Link Edge (%" PRIu64
|
|
") to destination vertex (%s)",
|
|
edge->key.k.link_id, print_sys_hostname(id));
|
|
else
|
|
te_debug(
|
|
" |- Link Edge (Unknown) to destination vertex (%s)",
|
|
print_sys_hostname(id));
|
|
|
|
dst = ls_find_edge_by_destination(args->ted, edge->attributes);
|
|
if (dst) {
|
|
/* Attach remote link if not set */
|
|
if (edge->source && dst->destination == NULL) {
|
|
vertex = edge->source;
|
|
if (vertex->incoming_edges)
|
|
listnode_add_sort_nodup(vertex->incoming_edges,
|
|
dst);
|
|
dst->destination = vertex;
|
|
}
|
|
/* and destination vertex to this edge if not set */
|
|
if (dst->source && edge->destination == NULL) {
|
|
vertex = dst->source;
|
|
if (vertex->incoming_edges)
|
|
listnode_add_sort_nodup(vertex->incoming_edges,
|
|
edge);
|
|
edge->destination = vertex;
|
|
}
|
|
} else {
|
|
/* Search dst. Vertex by Extended Reach. ID if not found */
|
|
if (edge->destination == NULL) {
|
|
vertex = ls_find_vertex_by_key(args->ted,
|
|
sysid_to_key(id));
|
|
if (vertex && vertex->incoming_edges)
|
|
listnode_add_sort_nodup(vertex->incoming_edges,
|
|
edge);
|
|
edge->destination = vertex;
|
|
}
|
|
}
|
|
|
|
/* Update status and Export Link State Edge if needed */
|
|
if (edge->status != SYNC) {
|
|
if (args->export)
|
|
isis_te_export(LS_MSG_TYPE_ATTRIBUTES, edge);
|
|
edge->status = SYNC;
|
|
}
|
|
|
|
return LSP_ITER_CONTINUE;
|
|
}
|
|
|
|
/**
|
|
* Parse Extended IP Reachability or MT IPv6 Reachability TLVs and create or
|
|
* update the corresponding Link State Subnet and Prefix.
|
|
*
|
|
* @param prefix Prefix associated to this subnet
|
|
* @param metric Metric of this prefix
|
|
* @param external Boolean to indicate if the prefix is external
|
|
* @param subtlvs Subtlvs if any (mostly Segment Routing ID)
|
|
* @param arg IS-IS TE argument (TED, Vertex, and export indication)
|
|
*
|
|
* @return 0 if success, -1 otherwise
|
|
*/
|
|
static int lsp_to_subnet_cb(const struct prefix *prefix, uint32_t metric,
|
|
bool external, struct isis_subtlvs *subtlvs,
|
|
void *arg)
|
|
{
|
|
struct isis_te_args *args = (struct isis_te_args *)arg;
|
|
struct ls_vertex *vertex;
|
|
struct ls_subnet *subnet;
|
|
struct ls_prefix *ls_pref;
|
|
struct listnode *node;
|
|
struct ls_edge *edge;
|
|
struct ls_standard *std = NULL;
|
|
struct prefix p;
|
|
|
|
/* Sanity Check */
|
|
if (!args || !prefix)
|
|
return LSP_ITER_CONTINUE;
|
|
|
|
if (args->srv6_locator)
|
|
te_debug(" |- Process SRv6 Locator %pFX", prefix);
|
|
else
|
|
te_debug(" |- Process Extended %s Reachability %pFX",
|
|
prefix->family == AF_INET ? "IP" : "IPv6", prefix);
|
|
|
|
vertex = args->vertex;
|
|
|
|
/*
|
|
* Prefix with mask different from /32 or /128 are advertised by at
|
|
* least 2 nodes. To avoid subnet attached to undetermined vertex, and
|
|
* gives the possibility to send the information to client e.g. BGP for
|
|
* Link State advertisement, we adjust the prefix with the corresponding
|
|
* IP address of the belonging interface when it is available. Other
|
|
* prefixes are kept unchanged.
|
|
*/
|
|
if (prefix->family == AF_INET && prefix->prefixlen < IPV4_MAX_BITLEN) {
|
|
std = NULL;
|
|
for (ALL_LIST_ELEMENTS_RO(vertex->outgoing_edges, node, edge)) {
|
|
if (!CHECK_FLAG(edge->attributes->flags,
|
|
LS_ATTR_LOCAL_ADDR))
|
|
continue;
|
|
|
|
p.u.prefix4 = edge->attributes->standard.local;
|
|
p.family = AF_INET;
|
|
p.prefixlen = prefix->prefixlen;
|
|
apply_mask_ipv4((struct prefix_ipv4 *)&p);
|
|
if (IPV4_ADDR_SAME(&p.u.prefix4, &prefix->u.prefix4)) {
|
|
std = &edge->attributes->standard;
|
|
break;
|
|
}
|
|
}
|
|
if (std)
|
|
p.u.prefix4 = std->local;
|
|
|
|
} else if (prefix->family == AF_INET6
|
|
&& prefix->prefixlen < IPV6_MAX_BITLEN) {
|
|
std = NULL;
|
|
for (ALL_LIST_ELEMENTS_RO(vertex->outgoing_edges, node, edge)) {
|
|
if (!CHECK_FLAG(edge->attributes->flags,
|
|
LS_ATTR_LOCAL_ADDR6))
|
|
continue;
|
|
|
|
p.u.prefix6 = edge->attributes->standard.local6;
|
|
p.family = AF_INET6;
|
|
p.prefixlen = prefix->prefixlen;
|
|
apply_mask_ipv6((struct prefix_ipv6 *)&p);
|
|
if (IPV6_ADDR_SAME(&p.u.prefix6, &prefix->u.prefix6)) {
|
|
std = &edge->attributes->standard;
|
|
break;
|
|
}
|
|
}
|
|
if (std)
|
|
p.u.prefix6 = std->local6;
|
|
}
|
|
if (!std)
|
|
prefix_copy(&p, prefix);
|
|
else {
|
|
/* Remove old subnet if any before prefix adjustment */
|
|
subnet = ls_find_subnet(args->ted, prefix);
|
|
if (subnet) {
|
|
if (args->export) {
|
|
subnet->status = DELETE;
|
|
isis_te_export(LS_MSG_TYPE_PREFIX, subnet);
|
|
}
|
|
te_debug(" |- Remove subnet with prefix %pFX",
|
|
&subnet->key);
|
|
ls_subnet_del_all(args->ted, subnet);
|
|
}
|
|
te_debug(" |- Adjust prefix %pFX with local address to: %pFX",
|
|
prefix, &p);
|
|
}
|
|
|
|
/* Search existing Subnet in TED ... */
|
|
subnet = ls_find_subnet(args->ted, &p);
|
|
/* ... and create a new Subnet if not found */
|
|
if (!subnet) {
|
|
ls_pref = ls_prefix_new(vertex->node->adv, &p);
|
|
subnet = ls_subnet_add(args->ted, ls_pref);
|
|
/* Stop processing if we are unable to create a new subnet */
|
|
if (!subnet)
|
|
return LSP_ITER_CONTINUE;
|
|
}
|
|
ls_pref = subnet->ls_pref;
|
|
|
|
te_debug(" |- %s Subnet from prefix %pFX",
|
|
subnet->status == NEW ? "Create" : "Found", &p);
|
|
|
|
/* Update Metric */
|
|
if (!CHECK_FLAG(ls_pref->flags, LS_PREF_METRIC)
|
|
|| (ls_pref->metric != metric)) {
|
|
ls_pref->metric = metric;
|
|
SET_FLAG(ls_pref->flags, LS_PREF_METRIC);
|
|
if (subnet->status != NEW)
|
|
subnet->status = UPDATE;
|
|
} else {
|
|
if (subnet->status == ORPHAN)
|
|
subnet->status = SYNC;
|
|
}
|
|
|
|
/* Update Prefix SID if any */
|
|
if (subtlvs && subtlvs->prefix_sids.count != 0) {
|
|
struct isis_prefix_sid *psid;
|
|
struct ls_sid sr = {};
|
|
|
|
psid = (struct isis_prefix_sid *)subtlvs->prefix_sids.head;
|
|
sr.algo = psid->algorithm;
|
|
sr.sid_flag = psid->flags;
|
|
sr.sid = psid->value;
|
|
|
|
if (!CHECK_FLAG(ls_pref->flags, LS_PREF_SR)
|
|
|| !memcmp(&ls_pref->sr, &sr, sizeof(struct ls_sid))) {
|
|
memcpy(&ls_pref->sr, &sr, sizeof(struct ls_sid));
|
|
SET_FLAG(ls_pref->flags, LS_PREF_SR);
|
|
if (subnet->status != NEW)
|
|
subnet->status = UPDATE;
|
|
} else {
|
|
if (subnet->status == ORPHAN)
|
|
subnet->status = SYNC;
|
|
}
|
|
} else {
|
|
if (CHECK_FLAG(ls_pref->flags, LS_PREF_SR)) {
|
|
UNSET_FLAG(ls_pref->flags, LS_PREF_SR);
|
|
if (subnet->status != NEW)
|
|
subnet->status = UPDATE;
|
|
} else {
|
|
if (subnet->status == ORPHAN)
|
|
subnet->status = SYNC;
|
|
}
|
|
}
|
|
|
|
/* Update SRv6 SID and locator if any */
|
|
if (subtlvs && subtlvs->srv6_end_sids.count != 0) {
|
|
struct isis_srv6_end_sid_subtlv *psid;
|
|
struct ls_srv6_sid sr = {};
|
|
|
|
psid = (struct isis_srv6_end_sid_subtlv *)
|
|
subtlvs->srv6_end_sids.head;
|
|
sr.behavior = psid->behavior;
|
|
sr.flags = psid->flags;
|
|
memcpy(&sr.sid, &psid->sid, sizeof(struct in6_addr));
|
|
|
|
if (!CHECK_FLAG(ls_pref->flags, LS_PREF_SRV6) ||
|
|
memcmp(&ls_pref->srv6, &sr, sizeof(struct ls_srv6_sid))) {
|
|
memcpy(&ls_pref->srv6, &sr, sizeof(struct ls_srv6_sid));
|
|
SET_FLAG(ls_pref->flags, LS_PREF_SRV6);
|
|
if (subnet->status != NEW)
|
|
subnet->status = UPDATE;
|
|
} else {
|
|
if (subnet->status == ORPHAN)
|
|
subnet->status = SYNC;
|
|
}
|
|
} else {
|
|
if (CHECK_FLAG(ls_pref->flags, LS_PREF_SRV6)) {
|
|
UNSET_FLAG(ls_pref->flags, LS_PREF_SRV6);
|
|
if (subnet->status != NEW)
|
|
subnet->status = UPDATE;
|
|
} else {
|
|
if (subnet->status == ORPHAN)
|
|
subnet->status = SYNC;
|
|
}
|
|
}
|
|
|
|
/* Update status and Export Link State Edge if needed */
|
|
if (subnet->status != SYNC) {
|
|
if (args->export)
|
|
isis_te_export(LS_MSG_TYPE_PREFIX, subnet);
|
|
subnet->status = SYNC;
|
|
}
|
|
|
|
return LSP_ITER_CONTINUE;
|
|
}
|
|
|
|
/**
|
|
* Parse ISIS LSP to fulfill the Link State Database
|
|
*
|
|
* @param ted Link State Database
|
|
* @param lsp ISIS Link State PDU
|
|
*/
|
|
static void isis_te_parse_lsp(struct mpls_te_area *mta, struct isis_lsp *lsp)
|
|
{
|
|
struct ls_ted *ted;
|
|
struct ls_vertex *vertex;
|
|
struct ls_edge *edge;
|
|
struct ls_subnet *subnet;
|
|
struct listnode *node;
|
|
struct isis_te_args args;
|
|
|
|
/* Sanity Check */
|
|
if (!IS_MPLS_TE(mta) || !mta->ted || !lsp)
|
|
return;
|
|
|
|
ted = mta->ted;
|
|
|
|
te_debug("ISIS-TE(%s): Parse LSP %pSY", lsp->area->area_tag,
|
|
lsp->hdr.lsp_id);
|
|
|
|
/* First parse LSP to obtain the corresponding Vertex */
|
|
vertex = lsp_to_vertex(ted, lsp);
|
|
if (!vertex) {
|
|
zlog_warn("Unable to build Vertex from LSP %pSY. Abort!",
|
|
lsp->hdr.lsp_id);
|
|
return;
|
|
}
|
|
|
|
/* Check if Vertex has been modified */
|
|
if (vertex->status != SYNC) {
|
|
/* Vertex is out of sync: export it if requested */
|
|
if (IS_EXPORT_TE(mta))
|
|
isis_te_export(LS_MSG_TYPE_NODE, vertex);
|
|
vertex->status = SYNC;
|
|
}
|
|
|
|
/* Mark outgoing Edges and Subnets as ORPHAN to detect deletion */
|
|
for (ALL_LIST_ELEMENTS_RO(vertex->outgoing_edges, node, edge))
|
|
edge->status = ORPHAN;
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(vertex->prefixes, node, subnet))
|
|
subnet->status = ORPHAN;
|
|
|
|
/* Process all Extended Reachability in LSP (all fragments) */
|
|
args.ted = ted;
|
|
args.vertex = vertex;
|
|
args.export = mta->export;
|
|
isis_lsp_iterate_is_reach(lsp, ISIS_MT_IPV4_UNICAST, lsp_to_edge_cb,
|
|
&args);
|
|
|
|
isis_lsp_iterate_is_reach(lsp, ISIS_MT_IPV6_UNICAST, lsp_to_edge_cb,
|
|
&args);
|
|
|
|
/* Process all Extended IP (v4 & v6) in LSP (all fragments) */
|
|
args.srv6_locator = false;
|
|
isis_lsp_iterate_ip_reach(lsp, AF_INET, ISIS_MT_IPV4_UNICAST,
|
|
lsp_to_subnet_cb, &args);
|
|
isis_lsp_iterate_ip_reach(lsp, AF_INET6, ISIS_MT_IPV6_UNICAST,
|
|
lsp_to_subnet_cb, &args);
|
|
isis_lsp_iterate_ip_reach(lsp, AF_INET6, ISIS_MT_IPV4_UNICAST,
|
|
lsp_to_subnet_cb, &args);
|
|
args.srv6_locator = true;
|
|
isis_lsp_iterate_srv6_locator(lsp, ISIS_MT_STANDARD, lsp_to_subnet_cb,
|
|
&args);
|
|
isis_lsp_iterate_srv6_locator(lsp, ISIS_MT_IPV6_UNICAST,
|
|
lsp_to_subnet_cb, &args);
|
|
|
|
/* Clean remaining Orphan Edges or Subnets */
|
|
if (IS_EXPORT_TE(mta))
|
|
ls_vertex_clean(ted, vertex, zclient);
|
|
else
|
|
ls_vertex_clean(ted, vertex, NULL);
|
|
}
|
|
|
|
/**
|
|
* Delete Link State Database Vertex, Edge & Prefix that correspond to this
|
|
* ISIS Link State PDU
|
|
*
|
|
* @param ted Link State Database
|
|
* @param lsp ISIS Link State PDU
|
|
*/
|
|
static void isis_te_delete_lsp(struct mpls_te_area *mta, struct isis_lsp *lsp)
|
|
{
|
|
struct ls_ted *ted;
|
|
struct ls_vertex *vertex = NULL;
|
|
struct ls_node lnode = {};
|
|
struct ls_edge *edge;
|
|
struct ls_subnet *subnet;
|
|
struct listnode *nnode, *node;
|
|
|
|
/* Sanity Check */
|
|
if (!IS_MPLS_TE(mta) || !mta->ted || !lsp)
|
|
return;
|
|
|
|
te_debug("ISIS-TE(%s): Delete Link State TED objects from LSP %pSY",
|
|
lsp->area->area_tag, lsp->hdr.lsp_id);
|
|
|
|
/* Compute Link State Node ID from IS-IS sysID ... */
|
|
if (lsp->level == ISIS_LEVEL1)
|
|
lnode.adv.origin = ISIS_L1;
|
|
else
|
|
lnode.adv.origin = ISIS_L2;
|
|
memcpy(&lnode.adv.id.iso.sys_id, &lsp->hdr.lsp_id, ISIS_SYS_ID_LEN);
|
|
lnode.adv.id.iso.level = lsp->level;
|
|
ted = mta->ted;
|
|
/* ... and search the corresponding vertex */
|
|
vertex = ls_find_vertex_by_id(ted, lnode.adv);
|
|
if (!vertex)
|
|
return;
|
|
|
|
te_debug(" |- Delete Vertex %s", vertex->node->name);
|
|
|
|
/*
|
|
* We can't use the ls_vertex_del_all() function if export TE is set,
|
|
* as we must first advertise the client daemons of each removal.
|
|
*/
|
|
/* Remove outgoing Edges */
|
|
for (ALL_LIST_ELEMENTS(vertex->outgoing_edges, node, nnode, edge)) {
|
|
if (IS_EXPORT_TE(mta)) {
|
|
edge->status = DELETE;
|
|
isis_te_export(LS_MSG_TYPE_ATTRIBUTES, edge);
|
|
}
|
|
ls_edge_del_all(ted, edge);
|
|
}
|
|
|
|
/* Disconnect incoming Edges */
|
|
for (ALL_LIST_ELEMENTS(vertex->incoming_edges, node, nnode, edge)) {
|
|
ls_disconnect(vertex, edge, false);
|
|
if (edge->source == NULL) {
|
|
if (IS_EXPORT_TE(mta)) {
|
|
edge->status = DELETE;
|
|
isis_te_export(LS_MSG_TYPE_ATTRIBUTES, edge);
|
|
}
|
|
ls_edge_del_all(ted, edge);
|
|
}
|
|
}
|
|
|
|
/* Remove subnets */
|
|
for (ALL_LIST_ELEMENTS(vertex->prefixes, node, nnode, subnet)) {
|
|
if (IS_EXPORT_TE(mta)) {
|
|
subnet->status = DELETE;
|
|
isis_te_export(LS_MSG_TYPE_PREFIX, subnet);
|
|
}
|
|
ls_subnet_del_all(ted, subnet);
|
|
}
|
|
|
|
/* Then remove Link State Node */
|
|
if (IS_EXPORT_TE(mta)) {
|
|
vertex->status = DELETE;
|
|
isis_te_export(LS_MSG_TYPE_NODE, vertex);
|
|
}
|
|
ls_node_del(vertex->node);
|
|
|
|
/* Finally, remove Vertex */
|
|
ls_vertex_del(ted, vertex);
|
|
}
|
|
|
|
/**
|
|
* Process ISIS LSP according to the event to add, update or remove
|
|
* corresponding vertex, edge and prefix in the Link State database.
|
|
* Since LSP could be fragmented, the function starts by searching the root LSP
|
|
* to retrieve the complete LSP, including eventual fragment before processing
|
|
* all of them.
|
|
*
|
|
* @param lsp ISIS Link State PDU
|
|
* @param event LSP event: ADD, UPD, INC & DEL (TICK are ignored)
|
|
*
|
|
*/
|
|
void isis_te_lsp_event(struct isis_lsp *lsp, enum lsp_event event)
|
|
{
|
|
struct isis_area *area;
|
|
struct isis_lsp *lsp0;
|
|
|
|
/* Sanity check */
|
|
if (!lsp || !lsp->area)
|
|
return;
|
|
|
|
area = lsp->area;
|
|
if (!IS_MPLS_TE(area->mta))
|
|
return;
|
|
|
|
/* Adjust LSP0 in case of fragment */
|
|
if (LSP_FRAGMENT(lsp->hdr.lsp_id))
|
|
lsp0 = lsp->lspu.zero_lsp;
|
|
else
|
|
lsp0 = lsp;
|
|
|
|
/* Then process event */
|
|
switch (event) {
|
|
case LSP_ADD:
|
|
case LSP_UPD:
|
|
case LSP_INC:
|
|
isis_te_parse_lsp(area->mta, lsp0);
|
|
break;
|
|
case LSP_DEL:
|
|
isis_te_delete_lsp(area->mta, lsp0);
|
|
break;
|
|
case LSP_UNKNOWN:
|
|
case LSP_TICK:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Send the whole Link State Traffic Engineering Database to the consumer that
|
|
* request it through a ZAPI Link State Synchronous Opaque Message.
|
|
*
|
|
* @param info ZAPI Opaque message
|
|
*
|
|
* @return 0 if success, -1 otherwise
|
|
*/
|
|
int isis_te_sync_ted(struct zapi_opaque_reg_info dst)
|
|
{
|
|
struct listnode *node, *inode;
|
|
struct isis *isis;
|
|
struct isis_area *area;
|
|
struct mpls_te_area *mta;
|
|
int rc = -1;
|
|
|
|
te_debug("ISIS-TE(%s): Received TED synchro from client %d", __func__,
|
|
dst.proto);
|
|
/* For each area, send TED if TE distribution is enabled */
|
|
for (ALL_LIST_ELEMENTS_RO(im->isis, inode, isis)) {
|
|
for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) {
|
|
mta = area->mta;
|
|
if (IS_MPLS_TE(mta) && IS_EXPORT_TE(mta)) {
|
|
te_debug(" |- Export TED from area %s",
|
|
area->area_tag);
|
|
rc = ls_sync_ted(mta->ted, zclient, &dst);
|
|
if (rc != 0)
|
|
return rc;
|
|
}
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Initialize the Link State database from the LSP already stored for this area
|
|
*
|
|
* @param area ISIS area
|
|
*/
|
|
void isis_te_init_ted(struct isis_area *area)
|
|
{
|
|
struct isis_lsp *lsp;
|
|
|
|
/* Iterate over all lsp. */
|
|
for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++)
|
|
frr_each (lspdb, &area->lspdb[level - 1], lsp)
|
|
isis_te_parse_lsp(area->mta, lsp);
|
|
}
|
|
|
|
/* Following are vty command functions */
|
|
#ifndef FABRICD
|
|
|
|
static void show_router_id(struct vty *vty, struct isis_area *area)
|
|
{
|
|
bool no_match = true;
|
|
|
|
vty_out(vty, "Area %s:\n", area->area_tag);
|
|
if (area->mta->router_id.s_addr != 0) {
|
|
vty_out(vty, " MPLS-TE IPv4 Router-Address: %pI4\n",
|
|
&area->mta->router_id);
|
|
no_match = false;
|
|
}
|
|
if (!IN6_IS_ADDR_UNSPECIFIED(&area->mta->router_id_ipv6)) {
|
|
vty_out(vty, " MPLS-TE IPv6 Router-Address: %pI6\n",
|
|
&area->mta->router_id_ipv6);
|
|
no_match = false;
|
|
}
|
|
if (no_match)
|
|
vty_out(vty, " N/A\n");
|
|
}
|
|
|
|
DEFUN(show_isis_mpls_te_router,
|
|
show_isis_mpls_te_router_cmd,
|
|
"show " PROTO_NAME " [vrf <NAME|all>] mpls-te router",
|
|
SHOW_STR
|
|
PROTO_HELP
|
|
VRF_CMD_HELP_STR "All VRFs\n"
|
|
MPLS_TE_STR "Router information\n")
|
|
{
|
|
|
|
struct listnode *anode, *inode;
|
|
struct isis_area *area;
|
|
struct isis *isis = NULL;
|
|
const char *vrf_name = VRF_DEFAULT_NAME;
|
|
bool all_vrf = false;
|
|
int idx_vrf = 0;
|
|
|
|
if (!im) {
|
|
vty_out(vty, "IS-IS Routing Process not enabled\n");
|
|
return CMD_SUCCESS;
|
|
}
|
|
ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
|
|
|
|
if (all_vrf) {
|
|
for (ALL_LIST_ELEMENTS_RO(im->isis, inode, isis)) {
|
|
for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, area)) {
|
|
if (!IS_MPLS_TE(area->mta))
|
|
continue;
|
|
|
|
show_router_id(vty, area);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
isis = isis_lookup_by_vrfname(vrf_name);
|
|
if (isis != NULL) {
|
|
for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, area)) {
|
|
if (!IS_MPLS_TE(area->mta))
|
|
continue;
|
|
|
|
show_router_id(vty, area);
|
|
}
|
|
}
|
|
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
static void show_ext_sub(struct vty *vty, char *name,
|
|
struct isis_ext_subtlvs *ext)
|
|
{
|
|
struct sbuf buf;
|
|
char ibuf[PREFIX2STR_BUFFER];
|
|
|
|
sbuf_init(&buf, NULL, 0);
|
|
|
|
if (!ext || ext->status == EXT_DISABLE)
|
|
return;
|
|
|
|
vty_out(vty, "-- MPLS-TE link parameters for %s --\n", name);
|
|
|
|
sbuf_reset(&buf);
|
|
|
|
if (IS_SUBTLV(ext, EXT_ADM_GRP))
|
|
sbuf_push(&buf, 4, "Administrative Group: 0x%x\n",
|
|
ext->adm_group);
|
|
if (IS_SUBTLV(ext, EXT_LLRI)) {
|
|
sbuf_push(&buf, 4, "Link Local ID: %u\n",
|
|
ext->local_llri);
|
|
sbuf_push(&buf, 4, "Link Remote ID: %u\n",
|
|
ext->remote_llri);
|
|
}
|
|
if (IS_SUBTLV(ext, EXT_LOCAL_ADDR))
|
|
sbuf_push(&buf, 4, "Local Interface IP Address(es): %pI4\n",
|
|
&ext->local_addr);
|
|
if (IS_SUBTLV(ext, EXT_NEIGH_ADDR))
|
|
sbuf_push(&buf, 4, "Remote Interface IP Address(es): %pI4\n",
|
|
&ext->neigh_addr);
|
|
if (IS_SUBTLV(ext, EXT_LOCAL_ADDR6))
|
|
sbuf_push(&buf, 4, "Local Interface IPv6 Address(es): %s\n",
|
|
inet_ntop(AF_INET6, &ext->local_addr6, ibuf,
|
|
PREFIX2STR_BUFFER));
|
|
if (IS_SUBTLV(ext, EXT_NEIGH_ADDR6))
|
|
sbuf_push(&buf, 4, "Remote Interface IPv6 Address(es): %s\n",
|
|
inet_ntop(AF_INET6, &ext->local_addr6, ibuf,
|
|
PREFIX2STR_BUFFER));
|
|
if (IS_SUBTLV(ext, EXT_MAX_BW))
|
|
sbuf_push(&buf, 4, "Maximum Bandwidth: %g (Bytes/sec)\n",
|
|
ext->max_bw);
|
|
if (IS_SUBTLV(ext, EXT_MAX_RSV_BW))
|
|
sbuf_push(&buf, 4,
|
|
"Maximum Reservable Bandwidth: %g (Bytes/sec)\n",
|
|
ext->max_rsv_bw);
|
|
if (IS_SUBTLV(ext, EXT_UNRSV_BW)) {
|
|
sbuf_push(&buf, 4, "Unreserved Bandwidth:\n");
|
|
for (int j = 0; j < MAX_CLASS_TYPE; j += 2) {
|
|
sbuf_push(&buf, 4 + 2,
|
|
"[%d]: %g (Bytes/sec),\t[%d]: %g (Bytes/sec)\n",
|
|
j, ext->unrsv_bw[j],
|
|
j + 1, ext->unrsv_bw[j + 1]);
|
|
}
|
|
}
|
|
if (IS_SUBTLV(ext, EXT_TE_METRIC))
|
|
sbuf_push(&buf, 4, "Traffic Engineering Metric: %u\n",
|
|
ext->te_metric);
|
|
if (IS_SUBTLV(ext, EXT_RMT_AS))
|
|
sbuf_push(&buf, 4,
|
|
"Inter-AS TE Remote AS number: %u\n",
|
|
ext->remote_as);
|
|
if (IS_SUBTLV(ext, EXT_RMT_IP))
|
|
sbuf_push(&buf, 4,
|
|
"Inter-AS TE Remote ASBR IP address: %pI4\n",
|
|
&ext->remote_ip);
|
|
if (IS_SUBTLV(ext, EXT_DELAY))
|
|
sbuf_push(&buf, 4,
|
|
"%s Average Link Delay: %u (micro-sec)\n",
|
|
IS_ANORMAL(ext->delay) ? "Anomalous" : "Normal",
|
|
ext->delay & TE_EXT_MASK);
|
|
if (IS_SUBTLV(ext, EXT_MM_DELAY)) {
|
|
sbuf_push(&buf, 4, "%s Min/Max Link Delay: %u / %u (micro-sec)\n",
|
|
IS_ANORMAL(ext->min_delay) ? "Anomalous" : "Normal",
|
|
ext->min_delay & TE_EXT_MASK,
|
|
ext->max_delay & TE_EXT_MASK);
|
|
}
|
|
if (IS_SUBTLV(ext, EXT_DELAY_VAR))
|
|
sbuf_push(&buf, 4,
|
|
"Delay Variation: %u (micro-sec)\n",
|
|
ext->delay_var & TE_EXT_MASK);
|
|
if (IS_SUBTLV(ext, EXT_PKT_LOSS))
|
|
sbuf_push(&buf, 4, "%s Link Packet Loss: %g (%%)\n",
|
|
IS_ANORMAL(ext->pkt_loss) ? "Anomalous" : "Normal",
|
|
(float)((ext->pkt_loss & TE_EXT_MASK)
|
|
* LOSS_PRECISION));
|
|
if (IS_SUBTLV(ext, EXT_RES_BW))
|
|
sbuf_push(&buf, 4,
|
|
"Unidirectional Residual Bandwidth: %g (Bytes/sec)\n",
|
|
ext->res_bw);
|
|
if (IS_SUBTLV(ext, EXT_AVA_BW))
|
|
sbuf_push(&buf, 4,
|
|
"Unidirectional Available Bandwidth: %g (Bytes/sec)\n",
|
|
ext->ava_bw);
|
|
if (IS_SUBTLV(ext, EXT_USE_BW))
|
|
sbuf_push(&buf, 4,
|
|
"Unidirectional Utilized Bandwidth: %g (Bytes/sec)\n",
|
|
ext->use_bw);
|
|
|
|
vty_multiline(vty, "", "%s", sbuf_buf(&buf));
|
|
vty_out(vty, "---------------\n\n");
|
|
|
|
sbuf_free(&buf);
|
|
return;
|
|
}
|
|
|
|
DEFUN (show_isis_mpls_te_interface,
|
|
show_isis_mpls_te_interface_cmd,
|
|
"show " PROTO_NAME " mpls-te interface [INTERFACE]",
|
|
SHOW_STR
|
|
PROTO_HELP
|
|
MPLS_TE_STR
|
|
"Interface information\n"
|
|
"Interface name\n")
|
|
{
|
|
struct listnode *anode, *cnode, *inode;
|
|
struct isis_area *area;
|
|
struct isis_circuit *circuit;
|
|
struct interface *ifp;
|
|
int idx_interface = 4;
|
|
struct isis *isis = NULL;
|
|
|
|
if (!im) {
|
|
vty_out(vty, "IS-IS Routing Process not enabled\n");
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
if (argc == idx_interface) {
|
|
/* Show All Interfaces. */
|
|
for (ALL_LIST_ELEMENTS_RO(im->isis, inode, isis)) {
|
|
for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode,
|
|
area)) {
|
|
|
|
if (!IS_MPLS_TE(area->mta))
|
|
continue;
|
|
|
|
vty_out(vty, "Area %s:\n", area->area_tag);
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(area->circuit_list,
|
|
cnode, circuit))
|
|
show_ext_sub(vty,
|
|
circuit->interface->name,
|
|
circuit->ext);
|
|
}
|
|
}
|
|
} else {
|
|
/* Interface name is specified. */
|
|
ifp = if_lookup_by_name(argv[idx_interface]->arg, VRF_DEFAULT);
|
|
if (ifp == NULL)
|
|
vty_out(vty, "No such interface name\n");
|
|
else {
|
|
circuit = circuit_scan_by_ifp(ifp);
|
|
if (!circuit)
|
|
vty_out(vty,
|
|
"ISIS is not enabled on circuit %s\n",
|
|
ifp->name);
|
|
else
|
|
show_ext_sub(vty, ifp->name, circuit->ext);
|
|
}
|
|
}
|
|
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* Search Vertex in TED that corresponds to the given string that represent
|
|
* the ISO system ID in the forms <systemid/hostname>[.<pseudo-id>-<framenent>]
|
|
*
|
|
* @param ted Link State Database
|
|
* @param id ISO System ID
|
|
* @param isis Main reference to the isis daemon
|
|
*
|
|
* @return Vertex if found, NULL otherwise
|
|
*/
|
|
static struct ls_vertex *vertex_for_arg(struct ls_ted *ted, const char *id,
|
|
struct isis *isis)
|
|
{
|
|
char sysid[255] = {0};
|
|
uint8_t number[3];
|
|
const char *pos;
|
|
uint8_t lspid[ISIS_SYS_ID_LEN + 2] = {0};
|
|
struct isis_dynhn *dynhn;
|
|
uint64_t key = 0;
|
|
|
|
if (!id)
|
|
return NULL;
|
|
|
|
/*
|
|
* extract fragment and pseudo id from the string argv
|
|
* in the forms:
|
|
* (a) <systemid/hostname>.<pseudo-id>-<framenent> or
|
|
* (b) <systemid/hostname>.<pseudo-id> or
|
|
* (c) <systemid/hostname> or
|
|
* Where systemid is in the form:
|
|
* xxxx.xxxx.xxxx
|
|
*/
|
|
strlcpy(sysid, id, sizeof(sysid));
|
|
if (strlen(id) > 3) {
|
|
pos = id + strlen(id) - 3;
|
|
if (strncmp(pos, "-", 1) == 0) {
|
|
memcpy(number, ++pos, 2);
|
|
lspid[ISIS_SYS_ID_LEN + 1] =
|
|
(uint8_t)strtol((char *)number, NULL, 16);
|
|
pos -= 4;
|
|
if (strncmp(pos, ".", 1) != 0)
|
|
return NULL;
|
|
}
|
|
if (strncmp(pos, ".", 1) == 0) {
|
|
memcpy(number, ++pos, 2);
|
|
lspid[ISIS_SYS_ID_LEN] =
|
|
(uint8_t)strtol((char *)number, NULL, 16);
|
|
sysid[pos - id - 1] = '\0';
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Try to find the lsp-id if the argv
|
|
* string is in
|
|
* the form
|
|
* hostname.<pseudo-id>-<fragment>
|
|
*/
|
|
if (sysid2buff(lspid, sysid)) {
|
|
key = sysid_to_key(lspid);
|
|
} else if ((dynhn = dynhn_find_by_name(isis, sysid))) {
|
|
memcpy(lspid, dynhn->id, ISIS_SYS_ID_LEN);
|
|
key = sysid_to_key(lspid);
|
|
} else if (strncmp(cmd_hostname_get(), sysid, 15) == 0) {
|
|
memcpy(lspid, isis->sysid, ISIS_SYS_ID_LEN);
|
|
key = sysid_to_key(lspid);
|
|
}
|
|
|
|
if (key == 0)
|
|
return NULL;
|
|
|
|
return ls_find_vertex_by_key(ted, key);
|
|
}
|
|
|
|
/**
|
|
* Show Link State Traffic Engineering Database extracted from IS-IS LSP.
|
|
*
|
|
* @param vty VTY output console
|
|
* @param argv Command line argument
|
|
* @param argc Number of command line argument
|
|
* @param ted Traffic Engineering Database
|
|
* @param isis isis Main reference to the isis daemon
|
|
*
|
|
* @return Command Success if OK, Command Warning otherwise
|
|
*/
|
|
static int show_ted(struct vty *vty, struct cmd_token *argv[], int argc,
|
|
struct isis_area *area, struct isis *isis)
|
|
{
|
|
int idx;
|
|
char *id;
|
|
struct in_addr ip_addr;
|
|
struct in6_addr ip6_addr;
|
|
struct prefix pref;
|
|
struct ls_ted *ted;
|
|
struct ls_vertex *vertex;
|
|
struct ls_edge *edge;
|
|
struct ls_subnet *subnet;
|
|
struct ls_edge_key key;
|
|
bool detail = false;
|
|
bool uj = use_json(argc, argv);
|
|
json_object *json = NULL;
|
|
|
|
if (!IS_MPLS_TE(area->mta) || !area->mta->ted) {
|
|
vty_out(vty, "MPLS-TE is disabled for Area %s\n",
|
|
area->area_tag ? area->area_tag : "null");
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
ted = area->mta->ted;
|
|
|
|
if (uj)
|
|
json = json_object_new_object();
|
|
else
|
|
vty_out(vty, "Area %s:\n",
|
|
area->area_tag ? area->area_tag : "null");
|
|
|
|
if (argv[argc - 1]->arg && strmatch(argv[argc - 1]->text, "detail"))
|
|
detail = true;
|
|
|
|
idx = 4;
|
|
if (argv_find(argv, argc, "vertex", &idx)) {
|
|
/* Show Vertex */
|
|
id = argv_find(argv, argc, "WORD", &idx) ? argv[idx]->arg
|
|
: NULL;
|
|
if (!id)
|
|
vertex = NULL;
|
|
else if (!strncmp(id, "self", 4))
|
|
vertex = ted->self;
|
|
else {
|
|
vertex = vertex_for_arg(ted, id, isis);
|
|
if (!vertex) {
|
|
vty_out(vty, "No vertex found for ID %s\n", id);
|
|
return CMD_WARNING;
|
|
}
|
|
}
|
|
|
|
if (vertex)
|
|
ls_show_vertex(vertex, vty, json, detail);
|
|
else
|
|
ls_show_vertices(ted, vty, json, detail);
|
|
|
|
} else if (argv_find(argv, argc, "edge", &idx)) {
|
|
/* Show Edge */
|
|
if (argv_find(argv, argc, "A.B.C.D", &idx)) {
|
|
if (!inet_pton(AF_INET, argv[idx]->arg, &ip_addr)) {
|
|
vty_out(vty,
|
|
"Specified Edge ID %s is invalid\n",
|
|
argv[idx]->arg);
|
|
return CMD_WARNING_CONFIG_FAILED;
|
|
}
|
|
/* Get the Edge from the Link State Database */
|
|
key.family = AF_INET;
|
|
IPV4_ADDR_COPY(&key.k.addr, &ip_addr);
|
|
edge = ls_find_edge_by_key(ted, key);
|
|
if (!edge) {
|
|
vty_out(vty, "No edge found for ID %pI4\n",
|
|
&ip_addr);
|
|
return CMD_WARNING;
|
|
}
|
|
} else if (argv_find(argv, argc, "X:X::X:X", &idx)) {
|
|
if (!inet_pton(AF_INET6, argv[idx]->arg, &ip6_addr)) {
|
|
vty_out(vty,
|
|
"Specified Edge ID %s is invalid\n",
|
|
argv[idx]->arg);
|
|
return CMD_WARNING_CONFIG_FAILED;
|
|
}
|
|
/* Get the Edge from the Link State Database */
|
|
key.family = AF_INET6;
|
|
IPV6_ADDR_COPY(&key.k.addr6, &ip6_addr);
|
|
edge = ls_find_edge_by_key(ted, key);
|
|
if (!edge) {
|
|
vty_out(vty, "No edge found for ID %pI6\n",
|
|
&ip6_addr);
|
|
return CMD_WARNING;
|
|
}
|
|
} else
|
|
edge = NULL;
|
|
|
|
if (edge)
|
|
ls_show_edge(edge, vty, json, detail);
|
|
else
|
|
ls_show_edges(ted, vty, json, detail);
|
|
|
|
} else if (argv_find(argv, argc, "subnet", &idx)) {
|
|
/* Show Subnet */
|
|
if (argv_find(argv, argc, "A.B.C.D/M", &idx)) {
|
|
if (!str2prefix(argv[idx]->arg, &pref)) {
|
|
vty_out(vty, "Invalid prefix format %s\n",
|
|
argv[idx]->arg);
|
|
return CMD_WARNING_CONFIG_FAILED;
|
|
}
|
|
/* Get the Subnet from the Link State Database */
|
|
subnet = ls_find_subnet(ted, &pref);
|
|
if (!subnet) {
|
|
vty_out(vty, "No subnet found for ID %pFX\n",
|
|
&pref);
|
|
return CMD_WARNING;
|
|
}
|
|
} else if (argv_find(argv, argc, "X:X::X:X/M", &idx)) {
|
|
if (!str2prefix(argv[idx]->arg, &pref)) {
|
|
vty_out(vty, "Invalid prefix format %s\n",
|
|
argv[idx]->arg);
|
|
return CMD_WARNING_CONFIG_FAILED;
|
|
}
|
|
/* Get the Subnet from the Link State Database */
|
|
subnet = ls_find_subnet(ted, &pref);
|
|
if (!subnet) {
|
|
vty_out(vty, "No subnet found for ID %pFX\n",
|
|
&pref);
|
|
return CMD_WARNING;
|
|
}
|
|
} else
|
|
subnet = NULL;
|
|
|
|
if (subnet)
|
|
ls_show_subnet(subnet, vty, json, detail);
|
|
else
|
|
ls_show_subnets(ted, vty, json, detail);
|
|
|
|
} else {
|
|
/* Show the complete TED */
|
|
ls_show_ted(ted, vty, json, detail);
|
|
}
|
|
|
|
if (uj)
|
|
vty_json(vty, json);
|
|
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* Show ISIS Traffic Engineering Database
|
|
*
|
|
* @param vty VTY output console
|
|
* @param argv Command line argument
|
|
* @param argc Number of command line argument
|
|
* @param isis isis Main reference to the isis daemon
|
|
|
|
* @return Command Success if OK, Command Warning otherwise
|
|
*/
|
|
static int show_isis_ted(struct vty *vty, struct cmd_token *argv[], int argc,
|
|
struct isis *isis)
|
|
{
|
|
struct listnode *node;
|
|
struct isis_area *area;
|
|
int rc;
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) {
|
|
rc = show_ted(vty, argv, argc, area, isis);
|
|
if (rc != CMD_SUCCESS)
|
|
return rc;
|
|
}
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
DEFUN(show_isis_mpls_te_db,
|
|
show_isis_mpls_te_db_cmd,
|
|
"show " PROTO_NAME " [vrf <NAME|all>] mpls-te database [<vertex [WORD]|edge [A.B.C.D|X:X::X:X]|subnet [A.B.C.D/M|X:X::X:X/M]>] [detail|json]",
|
|
SHOW_STR PROTO_HELP VRF_CMD_HELP_STR
|
|
"All VRFs\n"
|
|
MPLS_TE_STR
|
|
"MPLS-TE database\n"
|
|
"MPLS-TE Vertex\n"
|
|
"MPLS-TE Vertex ID (as an ISO ID, hostname or \"self\")\n"
|
|
"MPLS-TE Edge\n"
|
|
"MPLS-TE Edge ID (as an IPv4 address)\n"
|
|
"MPLS-TE Edge ID (as an IPv6 address)\n"
|
|
"MPLS-TE Subnet\n"
|
|
"MPLS-TE Subnet ID (as an IPv4 prefix)\n"
|
|
"MPLS-TE Subnet ID (as an IPv6 prefix)\n"
|
|
"Detailed information\n"
|
|
JSON_STR)
|
|
{
|
|
int idx_vrf = 0;
|
|
const char *vrf_name = VRF_DEFAULT_NAME;
|
|
bool all_vrf = false;
|
|
struct listnode *node;
|
|
struct isis *isis;
|
|
int rc = CMD_WARNING;
|
|
|
|
ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
|
|
|
|
if (all_vrf) {
|
|
for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) {
|
|
rc = show_isis_ted(vty, argv, argc, isis);
|
|
if (rc != CMD_SUCCESS)
|
|
return rc;
|
|
}
|
|
return CMD_SUCCESS;
|
|
}
|
|
isis = isis_lookup_by_vrfname(vrf_name);
|
|
if (isis)
|
|
rc = show_isis_ted(vty, argv, argc, isis);
|
|
|
|
return rc;
|
|
}
|
|
|
|
#endif /* #ifndef FRABRICD */
|
|
|
|
/* Initialize MPLS_TE */
|
|
void isis_mpls_te_init(void)
|
|
{
|
|
|
|
/* Register Circuit and Adjacency hook */
|
|
hook_register(isis_if_new_hook, isis_mpls_te_update);
|
|
hook_register(isis_adj_ip_enabled_hook, isis_mpls_te_adj_ip_enabled);
|
|
hook_register(isis_adj_ip_disabled_hook, isis_mpls_te_adj_ip_disabled);
|
|
|
|
#ifndef FABRICD
|
|
/* Register new VTY commands */
|
|
install_element(VIEW_NODE, &show_isis_mpls_te_router_cmd);
|
|
install_element(VIEW_NODE, &show_isis_mpls_te_interface_cmd);
|
|
install_element(VIEW_NODE, &show_isis_mpls_te_db_cmd);
|
|
#endif
|
|
|
|
return;
|
|
}
|