diff options
Diffstat (limited to 'isisd/isis_te.c')
-rw-r--r-- | isisd/isis_te.c | 1887 |
1 files changed, 1887 insertions, 0 deletions
diff --git a/isisd/isis_te.c b/isisd/isis_te.c new file mode 100644 index 0000000..0093279 --- /dev/null +++ b/isisd/isis_te.c @@ -0,0 +1,1887 @@ +/* + * 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 + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <zebra.h> +#include <math.h> + +#include "linklist.h" +#include "thread.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"); + +/*------------------------------------------------------------------------* + * 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 */ + for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) + isis_link_params_update(circuit, circuit->interface); +} + +/** + * 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_del_all(&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); +} + +/* 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 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 + ext->status = 0; + } + + 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; + + /* Sanity Check */ + if (!adj || !adj->circuit) + return 0; + + 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: + 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; + } + + /* Update LSP */ + lsp_regenerate_schedule(circuit->area, circuit->is_type, 0); + + return 0; +} + +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; + + /* Sanity Check */ + if (!adj || !adj->circuit || !adj->circuit->ext) + return 0; + + 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; + } + + /* Update LSP */ + lsp_regenerate_schedule(circuit->area, circuit->is_type, 0); + + return 0; +} + +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)) + 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 < SR_ALGORITHM_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); + } + } + } + + /* 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; + uint64_t key = 0; + + /* 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 = ((uint64_t)ntohl(std->local.s_addr)) & 0xffffffff; + else if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR6)) + key = ((uint64_t)ntohl(std->local6.s6_addr32[2]) << 32 + | (uint64_t)ntohl(std->local6.s6_addr32[3])); + else if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ID)) + key = ((uint64_t)std->remote_id << 32) + | (((uint64_t)std->local_id) & 0xffffffff); + else + key = 0; + + /* Stop here if we don't got a valid key */ + if (key == 0) + 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 (%" PRIu64 + ") from Extended Reach. %pI4", + edge->status == NEW ? "Create" : "Found", edge->key, + &attr->standard.local); + else if (CHECK_FLAG(edge->attributes->flags, LS_ATTR_LOCAL_ADDR6)) + te_debug(" |- %s Edge (%" PRIu64 + ") from Extended Reach. %pI6", + edge->status == NEW ? "Create" : "Found", edge->key, + &attr->standard.local6); + else + te_debug(" |- %s Edge (%" PRIu64 ")", + edge->status == NEW ? "Create" : "Found", edge->key); + + 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_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; + } + } + } + + 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 %s", sysid_print(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 */ + te_debug(" |- Link Edge (%" PRIu64 ") to destination vertex (%s)", + edge->key, 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; + + 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 + 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); + 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 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 %s", lsp->area->area_tag, + sysid_print(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 %s. Abort!", + sysid_print(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) */ + 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); + + /* 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 %s", + lsp->area->area_tag, sysid_print(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; + default: + 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 (vrf_name) { + 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); + 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; + uint64_t 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 = ((uint64_t)ntohl(ip_addr.s_addr)) & 0xffffffff; + 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 = (uint64_t)ntohl(ip6_addr.s6_addr32[3]) + | ((uint64_t)ntohl(ip6_addr.s6_addr32[2]) << 32); + 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 (vrf_name) { + 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; +} |