2019-08-04 03:02:37 +02:00
|
|
|
/*
|
|
|
|
* This is an implementation of Segment Routing for IS-IS
|
|
|
|
* as per draft draft-ietf-isis-segment-routing-extensions-25
|
|
|
|
*
|
|
|
|
* Copyright (C) 2019 Orange Labs http://www.orange.com
|
|
|
|
*
|
|
|
|
* Author: Olivier Dugeon <olivier.dugeon@orange.com>
|
|
|
|
* Contributor: Renato Westphal <renato@opensourcerouting.org> for NetDEF
|
|
|
|
*
|
|
|
|
* This program 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 of the License, or (at your option)
|
|
|
|
* any later version.
|
|
|
|
*
|
|
|
|
* This program 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 "if.h"
|
|
|
|
#include "linklist.h"
|
|
|
|
#include "log.h"
|
|
|
|
#include "command.h"
|
|
|
|
#include "termtable.h"
|
|
|
|
#include "memory.h"
|
|
|
|
#include "prefix.h"
|
|
|
|
#include "table.h"
|
|
|
|
#include "vty.h"
|
|
|
|
#include "zclient.h"
|
|
|
|
#include "lib/lib_errors.h"
|
|
|
|
|
|
|
|
#include "isisd/isisd.h"
|
|
|
|
#include "isisd/isis_spf.h"
|
|
|
|
#include "isisd/isis_spf_private.h"
|
|
|
|
#include "isisd/isis_adjacency.h"
|
|
|
|
#include "isisd/isis_route.h"
|
|
|
|
#include "isisd/isis_mt.h"
|
|
|
|
#include "isisd/isis_sr.h"
|
|
|
|
#include "isisd/isis_tlvs.h"
|
|
|
|
#include "isisd/isis_misc.h"
|
|
|
|
#include "isisd/isis_zebra.h"
|
|
|
|
#include "isisd/isis_errors.h"
|
|
|
|
|
|
|
|
DEFINE_MTYPE_STATIC(ISISD, ISIS_SR_INFO, "ISIS segment routing information")
|
|
|
|
|
|
|
|
static void isis_sr_prefix_uninstall(struct sr_prefix *srp);
|
|
|
|
static void isis_sr_prefix_reinstall(struct sr_prefix *srp,
|
|
|
|
bool replace_semantics);
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
static inline int sr_prefix_sid_compare(const struct sr_prefix *a,
|
|
|
|
const struct sr_prefix *b)
|
|
|
|
{
|
|
|
|
return prefix_cmp(&a->prefix, &b->prefix);
|
|
|
|
}
|
2020-04-30 11:23:15 +02:00
|
|
|
DECLARE_RBTREE_UNIQ(srdb_node_prefix, struct sr_prefix, node_entry,
|
2019-08-04 03:02:37 +02:00
|
|
|
sr_prefix_sid_compare)
|
2020-04-30 11:23:15 +02:00
|
|
|
DECLARE_RBTREE_UNIQ(srdb_area_prefix, struct sr_prefix, area_entry,
|
2019-08-04 03:02:37 +02:00
|
|
|
sr_prefix_sid_compare)
|
|
|
|
|
|
|
|
static inline int sr_prefix_sid_cfg_compare(const struct sr_prefix_cfg *a,
|
|
|
|
const struct sr_prefix_cfg *b)
|
|
|
|
{
|
|
|
|
return prefix_cmp(&a->prefix, &b->prefix);
|
|
|
|
}
|
2020-04-30 11:23:15 +02:00
|
|
|
DECLARE_RBTREE_UNIQ(srdb_prefix_cfg, struct sr_prefix_cfg, entry,
|
2019-08-04 03:02:37 +02:00
|
|
|
sr_prefix_sid_cfg_compare)
|
|
|
|
|
|
|
|
static inline int sr_node_compare(const struct sr_node *a,
|
|
|
|
const struct sr_node *b)
|
|
|
|
{
|
|
|
|
return memcmp(a->sysid, b->sysid, ISIS_SYS_ID_LEN);
|
|
|
|
}
|
2020-04-30 11:23:15 +02:00
|
|
|
DECLARE_RBTREE_UNIQ(srdb_node, struct sr_node, entry, sr_node_compare)
|
2019-08-04 03:02:37 +02:00
|
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
/* Returns true if the interface/address pair corresponds to a Node-SID. */
|
|
|
|
static bool isis_sr_prefix_is_node_sid(const struct interface *ifp,
|
|
|
|
const struct prefix *prefix)
|
|
|
|
{
|
|
|
|
return (if_is_loopback(ifp) && is_host_route(prefix));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Handle changes in the local SRGB configuration. */
|
|
|
|
int isis_sr_cfg_srgb_update(struct isis_area *area, uint32_t lower_bound,
|
|
|
|
uint32_t upper_bound)
|
|
|
|
{
|
|
|
|
struct isis_sr_db *srdb = &area->srdb;
|
|
|
|
|
|
|
|
/* First release the old SRGB. */
|
|
|
|
if (srdb->config.enabled)
|
|
|
|
isis_zebra_release_label_range(srdb->config.srgb_lower_bound,
|
|
|
|
srdb->config.srgb_upper_bound);
|
|
|
|
|
|
|
|
srdb->config.srgb_lower_bound = lower_bound;
|
|
|
|
srdb->config.srgb_upper_bound = upper_bound;
|
|
|
|
|
|
|
|
if (srdb->enabled) {
|
|
|
|
struct sr_prefix *srp;
|
|
|
|
|
|
|
|
/* Request new SRGB if SR is enabled. */
|
|
|
|
if (isis_zebra_request_label_range(
|
|
|
|
srdb->config.srgb_lower_bound,
|
|
|
|
srdb->config.srgb_upper_bound
|
|
|
|
- srdb->config.srgb_lower_bound + 1))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* Reinstall local Prefix-SIDs to update their input labels. */
|
|
|
|
for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) {
|
2020-04-30 11:23:15 +02:00
|
|
|
frr_each (srdb_area_prefix,
|
2019-08-04 03:02:37 +02:00
|
|
|
&area->srdb.prefix_sids[level - 1], srp) {
|
|
|
|
isis_sr_prefix_reinstall(srp, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
lsp_regenerate_schedule(area, area->is_type, 0);
|
|
|
|
} else if (srdb->config.enabled) {
|
|
|
|
/* Try to enable SR again using the new SRGB. */
|
|
|
|
if (isis_sr_start(area) == 0)
|
|
|
|
area->srdb.enabled = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Handle changes in the local MSD configuration. */
|
|
|
|
void isis_sr_cfg_msd_update(struct isis_area *area)
|
|
|
|
{
|
|
|
|
lsp_regenerate_schedule(area, area->is_type, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Handle new Prefix-SID configuration. */
|
|
|
|
struct sr_prefix_cfg *isis_sr_cfg_prefix_add(struct isis_area *area,
|
|
|
|
const struct prefix *prefix)
|
|
|
|
{
|
|
|
|
struct sr_prefix_cfg *pcfg;
|
|
|
|
struct interface *ifp;
|
|
|
|
|
|
|
|
pcfg = XCALLOC(MTYPE_ISIS_SR_INFO, sizeof(*pcfg));
|
|
|
|
pcfg->prefix = *prefix;
|
|
|
|
pcfg->area = area;
|
|
|
|
|
|
|
|
/* Pull defaults from the YANG module. */
|
|
|
|
pcfg->sid_type = yang_get_default_enum(
|
|
|
|
"%s/prefix-sid-map/prefix-sid/sid-value-type", ISIS_SR);
|
|
|
|
pcfg->last_hop_behavior = yang_get_default_enum(
|
|
|
|
"%s/prefix-sid-map/prefix-sid/last-hop-behavior", ISIS_SR);
|
|
|
|
|
|
|
|
/* Set the N-flag when appropriate. */
|
|
|
|
ifp = if_lookup_prefix(prefix, VRF_DEFAULT);
|
|
|
|
if (ifp && isis_sr_prefix_is_node_sid(ifp, prefix))
|
|
|
|
pcfg->node_sid = true;
|
|
|
|
|
|
|
|
/* Save prefix-sid configuration. */
|
2020-04-30 11:23:15 +02:00
|
|
|
srdb_prefix_cfg_add(&area->srdb.config.prefix_sids, pcfg);
|
2019-08-04 03:02:37 +02:00
|
|
|
|
|
|
|
return pcfg;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Handle removal of locally configured Prefix-SID. */
|
|
|
|
void isis_sr_cfg_prefix_del(struct sr_prefix_cfg *pcfg)
|
|
|
|
{
|
|
|
|
struct isis_area *area;
|
|
|
|
|
|
|
|
area = pcfg->area;
|
2020-04-30 11:23:15 +02:00
|
|
|
srdb_prefix_cfg_del(&area->srdb.config.prefix_sids, pcfg);
|
2019-08-04 03:02:37 +02:00
|
|
|
XFREE(MTYPE_ISIS_SR_INFO, pcfg);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Lookup Prefix-SID in the local configuration. */
|
|
|
|
struct sr_prefix_cfg *isis_sr_cfg_prefix_find(struct isis_area *area,
|
|
|
|
union prefixconstptr prefix)
|
|
|
|
{
|
|
|
|
struct sr_prefix_cfg pcfg = {};
|
|
|
|
|
|
|
|
prefix_copy(&pcfg.prefix, prefix.p);
|
2020-04-30 11:23:15 +02:00
|
|
|
return srdb_prefix_cfg_find(&area->srdb.config.prefix_sids, &pcfg);
|
2019-08-04 03:02:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Fill in Prefix-SID Sub-TLV according to the corresponding configuration. */
|
|
|
|
void isis_sr_prefix_cfg2subtlv(const struct sr_prefix_cfg *pcfg, bool external,
|
|
|
|
struct isis_prefix_sid *psid)
|
|
|
|
{
|
|
|
|
/* Set SID algorithm. */
|
|
|
|
psid->algorithm = SR_ALGORITHM_SPF;
|
|
|
|
|
|
|
|
/* Set SID flags. */
|
|
|
|
psid->flags = 0;
|
|
|
|
switch (pcfg->last_hop_behavior) {
|
|
|
|
case SR_LAST_HOP_BEHAVIOR_EXP_NULL:
|
|
|
|
SET_FLAG(psid->flags, ISIS_PREFIX_SID_NO_PHP);
|
|
|
|
SET_FLAG(psid->flags, ISIS_PREFIX_SID_EXPLICIT_NULL);
|
|
|
|
break;
|
|
|
|
case SR_LAST_HOP_BEHAVIOR_NO_PHP:
|
|
|
|
SET_FLAG(psid->flags, ISIS_PREFIX_SID_NO_PHP);
|
|
|
|
UNSET_FLAG(psid->flags, ISIS_PREFIX_SID_EXPLICIT_NULL);
|
|
|
|
break;
|
|
|
|
case SR_LAST_HOP_BEHAVIOR_PHP:
|
|
|
|
UNSET_FLAG(psid->flags, ISIS_PREFIX_SID_NO_PHP);
|
|
|
|
UNSET_FLAG(psid->flags, ISIS_PREFIX_SID_EXPLICIT_NULL);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (external)
|
|
|
|
SET_FLAG(psid->flags, ISIS_PREFIX_SID_READVERTISED);
|
|
|
|
if (pcfg->node_sid)
|
|
|
|
SET_FLAG(psid->flags, ISIS_PREFIX_SID_NODE);
|
|
|
|
|
|
|
|
/* Set SID value. */
|
|
|
|
psid->value = pcfg->sid;
|
|
|
|
if (pcfg->sid_type == SR_SID_VALUE_TYPE_ABSOLUTE) {
|
|
|
|
SET_FLAG(psid->flags, ISIS_PREFIX_SID_VALUE);
|
|
|
|
SET_FLAG(psid->flags, ISIS_PREFIX_SID_LOCAL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
static struct sr_prefix *isis_sr_prefix_add(struct isis_area *area,
|
|
|
|
struct sr_node *srn,
|
|
|
|
union prefixconstptr prefix,
|
|
|
|
bool local,
|
|
|
|
const struct isis_prefix_sid *psid)
|
|
|
|
{
|
|
|
|
struct sr_prefix *srp;
|
|
|
|
|
|
|
|
srp = XCALLOC(MTYPE_ISIS_SR_INFO, sizeof(*srp));
|
|
|
|
prefix_copy(&srp->prefix, prefix.p);
|
|
|
|
srp->sid = *psid;
|
|
|
|
srp->local_label = MPLS_INVALID_LABEL;
|
|
|
|
if (local) {
|
|
|
|
srp->type = ISIS_SR_PREFIX_LOCAL;
|
|
|
|
isis_sr_nexthop_reset(&srp->u.local.info);
|
|
|
|
} else {
|
|
|
|
srp->type = ISIS_SR_PREFIX_REMOTE;
|
|
|
|
srp->u.remote.rinfo = NULL;
|
|
|
|
}
|
|
|
|
srp->srn = srn;
|
2020-04-30 11:23:15 +02:00
|
|
|
srdb_node_prefix_add(&srn->prefix_sids, srp);
|
2019-08-04 03:02:37 +02:00
|
|
|
/* TODO: this might fail if we have Anycast SIDs in the IS-IS area. */
|
2020-04-30 11:23:15 +02:00
|
|
|
srdb_area_prefix_add(&area->srdb.prefix_sids[srn->level - 1], srp);
|
2019-08-04 03:02:37 +02:00
|
|
|
|
|
|
|
return srp;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void isis_sr_prefix_del(struct isis_area *area, struct sr_node *srn,
|
|
|
|
struct sr_prefix *srp)
|
|
|
|
{
|
|
|
|
isis_sr_prefix_uninstall(srp);
|
2020-04-30 11:23:15 +02:00
|
|
|
srdb_node_prefix_del(&srn->prefix_sids, srp);
|
|
|
|
srdb_area_prefix_del(&area->srdb.prefix_sids[srn->level - 1], srp);
|
2019-08-04 03:02:37 +02:00
|
|
|
XFREE(MTYPE_ISIS_SR_INFO, srp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct sr_prefix *isis_sr_prefix_find_area(struct isis_area *area,
|
|
|
|
int level,
|
|
|
|
union prefixconstptr prefix)
|
|
|
|
{
|
|
|
|
struct sr_prefix srp = {};
|
|
|
|
|
|
|
|
prefix_copy(&srp.prefix, prefix.p);
|
2020-04-30 11:23:15 +02:00
|
|
|
return srdb_area_prefix_find(&area->srdb.prefix_sids[level - 1],
|
2019-08-04 03:02:37 +02:00
|
|
|
&srp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct sr_prefix *isis_sr_prefix_find_node(struct sr_node *srn,
|
|
|
|
union prefixconstptr prefix)
|
|
|
|
{
|
|
|
|
struct sr_prefix srp = {};
|
|
|
|
|
|
|
|
prefix_copy(&srp.prefix, prefix.p);
|
2020-04-30 11:23:15 +02:00
|
|
|
return srdb_node_prefix_find(&srn->prefix_sids, &srp);
|
2019-08-04 03:02:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct sr_node *isis_sr_node_add(struct isis_area *area, int level,
|
|
|
|
const uint8_t *sysid,
|
|
|
|
const struct isis_router_cap *cap)
|
|
|
|
{
|
|
|
|
struct sr_node *srn;
|
|
|
|
|
|
|
|
srn = XCALLOC(MTYPE_ISIS_SR_INFO, sizeof(*srn));
|
|
|
|
srn->level = level;
|
|
|
|
memcpy(srn->sysid, sysid, ISIS_SYS_ID_LEN);
|
|
|
|
srn->cap = *cap;
|
|
|
|
srn->area = area;
|
2020-04-30 11:23:15 +02:00
|
|
|
srdb_node_prefix_init(&srn->prefix_sids);
|
|
|
|
srdb_node_add(&area->srdb.sr_nodes[level - 1], srn);
|
2019-08-04 03:02:37 +02:00
|
|
|
|
|
|
|
return srn;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void isis_sr_node_del(struct isis_area *area, int level,
|
|
|
|
struct sr_node *srn)
|
|
|
|
{
|
|
|
|
/* Remove and uninstall Prefix-SIDs. */
|
2020-04-30 11:23:15 +02:00
|
|
|
while (srdb_node_prefix_count(&srn->prefix_sids) > 0) {
|
2019-08-04 03:02:37 +02:00
|
|
|
struct sr_prefix *srp;
|
|
|
|
|
2020-04-30 11:23:15 +02:00
|
|
|
srp = srdb_node_prefix_first(&srn->prefix_sids);
|
2019-08-04 03:02:37 +02:00
|
|
|
isis_sr_prefix_del(area, srn, srp);
|
|
|
|
}
|
|
|
|
|
2020-04-30 11:23:15 +02:00
|
|
|
srdb_node_del(&area->srdb.sr_nodes[level - 1], srn);
|
2019-08-04 03:02:37 +02:00
|
|
|
XFREE(MTYPE_ISIS_SR_INFO, srn);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct sr_node *isis_sr_node_find(struct isis_area *area, int level,
|
|
|
|
const uint8_t *sysid)
|
|
|
|
{
|
|
|
|
struct sr_node srn = {};
|
|
|
|
|
|
|
|
memcpy(srn.sysid, sysid, ISIS_SYS_ID_LEN);
|
2020-04-30 11:23:15 +02:00
|
|
|
return srdb_node_find(&area->srdb.sr_nodes[level - 1], &srn);
|
2019-08-04 03:02:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void isis_sr_adj_srgb_update(struct isis_area *area, uint8_t *sysid,
|
|
|
|
int level)
|
|
|
|
{
|
|
|
|
struct sr_prefix *srp;
|
|
|
|
|
2020-04-30 11:23:15 +02:00
|
|
|
frr_each (srdb_area_prefix, &area->srdb.prefix_sids[level - 1],
|
2019-08-04 03:02:37 +02:00
|
|
|
srp) {
|
|
|
|
struct listnode *node;
|
|
|
|
struct isis_nexthop *nh;
|
|
|
|
|
|
|
|
if (srp->type == ISIS_SR_PREFIX_LOCAL)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (srp->u.remote.rinfo == NULL)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(srp->u.remote.rinfo->nexthops, node,
|
|
|
|
nh)) {
|
|
|
|
if (memcmp(nh->sysid, sysid, ISIS_SYS_ID_LEN) != 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Reinstall all Prefix-SID nexthops using route replace
|
|
|
|
* semantics.
|
|
|
|
*/
|
|
|
|
isis_sr_prefix_reinstall(srp, true);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void isis_sr_nexthop_update(struct sr_nexthop_info *srnh, mpls_label_t label)
|
|
|
|
{
|
|
|
|
srnh->label = label;
|
|
|
|
if (srnh->uptime == 0)
|
|
|
|
srnh->uptime = time(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
void isis_sr_nexthop_reset(struct sr_nexthop_info *srnh)
|
|
|
|
{
|
|
|
|
srnh->label = MPLS_INVALID_LABEL;
|
|
|
|
srnh->uptime = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
/* Lookup IS-IS route in the SPT. */
|
|
|
|
static struct isis_route_info *
|
|
|
|
isis_sr_prefix_lookup_route(struct isis_area *area, enum spf_tree_id tree_id,
|
|
|
|
struct sr_prefix *srp)
|
|
|
|
{
|
|
|
|
struct route_node *rn;
|
|
|
|
int level = srp->srn->level;
|
|
|
|
|
|
|
|
rn = route_node_lookup(area->spftree[tree_id][level - 1]->route_table,
|
|
|
|
&srp->prefix);
|
|
|
|
if (rn) {
|
|
|
|
route_unlock_node(rn);
|
|
|
|
if (rn->info)
|
|
|
|
return rn->info;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Calculate Prefix-SID input label. */
|
|
|
|
static mpls_label_t isis_sr_prefix_in_label(const struct sr_prefix *srp)
|
|
|
|
{
|
|
|
|
const struct sr_node *srn = srp->srn;
|
|
|
|
struct isis_area *area = srn->area;
|
|
|
|
|
|
|
|
/* Absolute SID value. */
|
|
|
|
if (CHECK_FLAG(srp->sid.flags,
|
|
|
|
ISIS_PREFIX_SID_VALUE | ISIS_PREFIX_SID_LOCAL))
|
|
|
|
return srp->sid.value;
|
|
|
|
|
|
|
|
/* Index SID value. */
|
|
|
|
if (srp->sid.value >= (area->srdb.config.srgb_upper_bound
|
|
|
|
- area->srdb.config.srgb_lower_bound + 1)) {
|
|
|
|
flog_warn(EC_ISIS_SID_OVERFLOW,
|
|
|
|
"%s: SID index %u falls outside local SRGB range",
|
|
|
|
__func__, srp->sid.value);
|
|
|
|
return MPLS_INVALID_LABEL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (area->srdb.config.srgb_lower_bound + srp->sid.value);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Calculate Prefix-SID output label. */
|
|
|
|
static mpls_label_t isis_sr_prefix_out_label(const struct sr_prefix *srp,
|
|
|
|
const struct sr_node *srn_nexthop,
|
|
|
|
const uint8_t *sysid)
|
|
|
|
{
|
|
|
|
const struct sr_node *srn = srp->srn;
|
|
|
|
|
|
|
|
/* Is the adjacency the last hop? */
|
|
|
|
if (memcmp(sysid, srn->sysid, ISIS_SYS_ID_LEN) == 0) {
|
|
|
|
if (!CHECK_FLAG(srp->sid.flags, ISIS_PREFIX_SID_NO_PHP))
|
|
|
|
return MPLS_LABEL_IMPLICIT_NULL;
|
|
|
|
|
|
|
|
if (CHECK_FLAG(srp->sid.flags, ISIS_PREFIX_SID_EXPLICIT_NULL)) {
|
|
|
|
if (srp->prefix.family == AF_INET)
|
|
|
|
return MPLS_LABEL_IPV4_EXPLICIT_NULL;
|
|
|
|
else
|
|
|
|
return MPLS_LABEL_IPV6_EXPLICIT_NULL;
|
|
|
|
}
|
|
|
|
/* Fallthrough */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Absolute SID value. */
|
|
|
|
if (CHECK_FLAG(srp->sid.flags,
|
|
|
|
ISIS_PREFIX_SID_VALUE | ISIS_PREFIX_SID_LOCAL)) {
|
|
|
|
/*
|
|
|
|
* V/L SIDs have local significance, so only adjacent routers
|
|
|
|
* can use them.
|
|
|
|
*/
|
|
|
|
if (srp->srn != srn_nexthop)
|
|
|
|
return MPLS_INVALID_LABEL;
|
|
|
|
return srp->sid.value;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Index SID value. */
|
|
|
|
if (srp->sid.value >= srn_nexthop->cap.srgb.range_size) {
|
|
|
|
flog_warn(EC_ISIS_SID_OVERFLOW,
|
|
|
|
"%s: SID index %u falls outside remote SRGB range",
|
|
|
|
__func__, srp->sid.value);
|
|
|
|
return MPLS_INVALID_LABEL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (srn_nexthop->cap.srgb.lower_bound + srp->sid.value);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Process local Prefix-SID and install it if possible. */
|
|
|
|
static int isis_sr_prefix_install_local(struct sr_prefix *srp)
|
|
|
|
{
|
|
|
|
const struct sr_node *srn = srp->srn;
|
|
|
|
struct isis_area *area = srn->area;
|
|
|
|
mpls_label_t local_label;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* No need to install LSP to local Prefix-SID unless the
|
|
|
|
* no-PHP option is configured.
|
|
|
|
*/
|
|
|
|
if (!CHECK_FLAG(srp->sid.flags, ISIS_PREFIX_SID_NO_PHP)
|
|
|
|
|| CHECK_FLAG(srp->sid.flags, ISIS_PREFIX_SID_EXPLICIT_NULL))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (IS_DEBUG_ISIS(DEBUG_SR)) {
|
|
|
|
zlog_debug("ISIS-SR (%s) installing Prefix-SID %pFX %s %u (%s)",
|
|
|
|
area->area_tag, &srp->prefix,
|
|
|
|
CHECK_FLAG(srp->sid.flags, ISIS_PREFIX_SID_VALUE)
|
|
|
|
? "label"
|
|
|
|
: "index",
|
|
|
|
srp->sid.value, circuit_t2string(srn->level));
|
|
|
|
zlog_debug(" nexthop self");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Calculate local label. */
|
|
|
|
local_label = isis_sr_prefix_in_label(srp);
|
|
|
|
if (local_label == MPLS_INVALID_LABEL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* Update internal state. */
|
|
|
|
srp->local_label = local_label;
|
|
|
|
isis_sr_nexthop_update(&srp->u.local.info, MPLS_LABEL_IMPLICIT_NULL);
|
|
|
|
|
|
|
|
/* Install Prefix-SID in the forwarding plane. */
|
|
|
|
isis_zebra_install_prefix_sid(srp);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Process remote Prefix-SID and install it if possible. */
|
|
|
|
static int isis_sr_prefix_install_remote(struct sr_prefix *srp)
|
|
|
|
{
|
|
|
|
const struct sr_node *srn = srp->srn;
|
|
|
|
struct isis_area *area = srn->area;
|
|
|
|
enum spf_tree_id tree_id;
|
|
|
|
struct listnode *node;
|
|
|
|
struct isis_nexthop *nexthop;
|
|
|
|
mpls_label_t local_label;
|
|
|
|
size_t nexthop_num = 0;
|
|
|
|
|
|
|
|
/* Lookup associated IS-IS route. */
|
|
|
|
tree_id = (srp->prefix.family == AF_INET) ? SPFTREE_IPV4 : SPFTREE_IPV6;
|
|
|
|
srp->u.remote.rinfo = isis_sr_prefix_lookup_route(area, tree_id, srp);
|
|
|
|
if (!srp->u.remote.rinfo)
|
|
|
|
/* SPF hasn't converged for this route yet. */
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (IS_DEBUG_ISIS(DEBUG_SR))
|
|
|
|
zlog_debug("ISIS-SR (%s) installing Prefix-SID %pFX %s %u (%s)",
|
|
|
|
area->area_tag, &srp->prefix,
|
|
|
|
CHECK_FLAG(srp->sid.flags, ISIS_PREFIX_SID_VALUE)
|
|
|
|
? "label"
|
|
|
|
: "index",
|
|
|
|
srp->sid.value, circuit_t2string(srn->level));
|
|
|
|
|
|
|
|
/* Calculate local label. */
|
|
|
|
local_label = isis_sr_prefix_in_label(srp);
|
|
|
|
if (local_label == MPLS_INVALID_LABEL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(srp->u.remote.rinfo->nexthops, node,
|
|
|
|
nexthop)) {
|
|
|
|
struct sr_node *srn_nexthop;
|
|
|
|
mpls_label_t remote_label;
|
|
|
|
|
|
|
|
/* Check if the nexthop advertised a SRGB. */
|
|
|
|
srn_nexthop =
|
|
|
|
isis_sr_node_find(area, srn->level, nexthop->sysid);
|
|
|
|
if (!srn_nexthop)
|
|
|
|
goto next;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check if the nexthop can handle SR-MPLS encapsulated IPv4 or
|
|
|
|
* IPv6 packets.
|
|
|
|
*/
|
|
|
|
if ((nexthop->family == AF_INET
|
|
|
|
&& !IS_SR_IPV4(srn_nexthop->cap.srgb))
|
|
|
|
|| (nexthop->family == AF_INET6
|
|
|
|
&& !IS_SR_IPV6(srn_nexthop->cap.srgb)))
|
|
|
|
goto next;
|
|
|
|
|
|
|
|
remote_label = isis_sr_prefix_out_label(srp, srn_nexthop,
|
|
|
|
nexthop->sysid);
|
|
|
|
if (remote_label == MPLS_INVALID_LABEL)
|
|
|
|
goto next;
|
|
|
|
|
|
|
|
if (IS_DEBUG_ISIS(DEBUG_SR)) {
|
|
|
|
static char buf[INET6_ADDRSTRLEN];
|
|
|
|
|
|
|
|
inet_ntop(nexthop->family, &nexthop->ip, buf,
|
|
|
|
sizeof(buf));
|
|
|
|
zlog_debug(" nexthop %s label %u", buf, remote_label);
|
|
|
|
}
|
|
|
|
|
|
|
|
isis_sr_nexthop_update(&nexthop->sr, remote_label);
|
|
|
|
nexthop_num++;
|
|
|
|
continue;
|
|
|
|
next:
|
|
|
|
isis_sr_nexthop_reset(&nexthop->sr);
|
|
|
|
}
|
|
|
|
if (nexthop_num == 0) {
|
|
|
|
if (IS_DEBUG_ISIS(DEBUG_SR))
|
|
|
|
zlog_debug(" no valid nexthops");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update internal state. */
|
|
|
|
srp->local_label = local_label;
|
|
|
|
|
|
|
|
/* Install Prefix-SID in the forwarding plane. */
|
|
|
|
isis_zebra_install_prefix_sid(srp);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Process local or remote Prefix-SID and install it if possible. */
|
|
|
|
static void isis_sr_prefix_install(struct sr_prefix *srp)
|
|
|
|
{
|
|
|
|
const struct sr_node *srn = srp->srn;
|
|
|
|
struct isis_area *area = srn->area;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* L1 routes are preferred over the L2 ones. */
|
|
|
|
if (area->is_type == IS_LEVEL_1_AND_2) {
|
|
|
|
struct sr_prefix *srp_l1, *srp_l2;
|
|
|
|
|
|
|
|
switch (srn->level) {
|
|
|
|
case ISIS_LEVEL1:
|
|
|
|
srp_l2 = isis_sr_prefix_find_area(area, ISIS_LEVEL2,
|
|
|
|
&srp->prefix);
|
|
|
|
if (srp_l2)
|
|
|
|
isis_sr_prefix_uninstall(srp_l2);
|
|
|
|
break;
|
|
|
|
case ISIS_LEVEL2:
|
|
|
|
srp_l1 = isis_sr_prefix_find_area(area, ISIS_LEVEL1,
|
|
|
|
&srp->prefix);
|
|
|
|
if (srp_l1)
|
|
|
|
return;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (srp->type == ISIS_SR_PREFIX_LOCAL)
|
|
|
|
ret = isis_sr_prefix_install_local(srp);
|
|
|
|
else
|
|
|
|
ret = isis_sr_prefix_install_remote(srp);
|
|
|
|
if (ret != 0)
|
|
|
|
isis_sr_prefix_uninstall(srp);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Uninstall local or remote Prefix-SID. */
|
|
|
|
static void isis_sr_prefix_uninstall(struct sr_prefix *srp)
|
|
|
|
{
|
|
|
|
const struct sr_node *srn = srp->srn;
|
|
|
|
struct listnode *node;
|
|
|
|
struct isis_nexthop *nexthop;
|
|
|
|
|
|
|
|
if (srp->local_label == MPLS_INVALID_LABEL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (IS_DEBUG_ISIS(DEBUG_SR))
|
|
|
|
zlog_debug(
|
|
|
|
"ISIS-SR (%s) uninstalling Prefix-SID %pFX %s %u (%s)",
|
|
|
|
srn->area->area_tag, &srp->prefix,
|
|
|
|
CHECK_FLAG(srp->sid.flags, ISIS_PREFIX_SID_VALUE)
|
|
|
|
? "label"
|
|
|
|
: "index",
|
|
|
|
srp->sid.value, circuit_t2string(srn->level));
|
|
|
|
|
|
|
|
|
|
|
|
/* Uninstall Prefix-SID from the forwarding plane. */
|
|
|
|
isis_zebra_uninstall_prefix_sid(srp);
|
|
|
|
|
|
|
|
/* Reset internal state. */
|
|
|
|
srp->local_label = MPLS_INVALID_LABEL;
|
|
|
|
switch (srp->type) {
|
|
|
|
case ISIS_SR_PREFIX_LOCAL:
|
|
|
|
isis_sr_nexthop_reset(&srp->u.local.info);
|
|
|
|
break;
|
|
|
|
case ISIS_SR_PREFIX_REMOTE:
|
|
|
|
if (srp->u.remote.rinfo) {
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(srp->u.remote.rinfo->nexthops,
|
|
|
|
node, nexthop))
|
|
|
|
isis_sr_nexthop_reset(&nexthop->sr);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Reinstall local or remote Prefix-SID. */
|
|
|
|
static void isis_sr_prefix_reinstall(struct sr_prefix *srp,
|
|
|
|
bool replace_semantics)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Route replace semantics can be used only when we know for sure that
|
|
|
|
* the Prefix-SID input label hasn't changed. Otherwise we need to
|
|
|
|
* uninstall the Prefix-SID first using the old input label before
|
|
|
|
* reinstalling it.
|
|
|
|
*/
|
|
|
|
if (!replace_semantics)
|
|
|
|
isis_sr_prefix_uninstall(srp);
|
|
|
|
|
|
|
|
isis_sr_prefix_install(srp);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
/* Parse all SR-related information from the given Router Capabilities TLV. */
|
|
|
|
static struct sr_node *
|
|
|
|
isis_sr_parse_router_cap_tlv(struct isis_area *area, int level,
|
|
|
|
const uint8_t *sysid,
|
|
|
|
const struct isis_router_cap *router_cap)
|
|
|
|
{
|
|
|
|
struct sr_node *srn;
|
|
|
|
|
|
|
|
if (!router_cap || router_cap->srgb.range_size == 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
srn = isis_sr_node_find(area, level, sysid);
|
|
|
|
if (srn) {
|
|
|
|
if (memcmp(&srn->cap, router_cap, sizeof(srn->cap)) != 0) {
|
|
|
|
srn->cap = *router_cap;
|
2020-04-30 11:23:15 +02:00
|
|
|
srn->state = SRDB_STATE_MODIFIED;
|
2019-08-04 03:02:37 +02:00
|
|
|
} else
|
2020-04-30 11:23:15 +02:00
|
|
|
srn->state = SRDB_STATE_UNCHANGED;
|
2019-08-04 03:02:37 +02:00
|
|
|
} else {
|
|
|
|
srn = isis_sr_node_add(area, level, sysid, router_cap);
|
2020-04-30 11:23:15 +02:00
|
|
|
srn->state = SRDB_STATE_NEW;
|
2019-08-04 03:02:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return srn;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Parse list of Prefix-SID Sub-TLVs. */
|
|
|
|
static void isis_sr_parse_prefix_sid_subtlvs(struct sr_node *srn,
|
|
|
|
union prefixconstptr prefix,
|
|
|
|
bool local,
|
|
|
|
struct isis_item_list *prefix_sids)
|
|
|
|
{
|
|
|
|
struct isis_area *area = srn->area;
|
|
|
|
struct isis_item *i;
|
|
|
|
|
|
|
|
for (i = prefix_sids->head; i; i = i->next) {
|
|
|
|
struct isis_prefix_sid *psid = (struct isis_prefix_sid *)i;
|
|
|
|
struct sr_prefix *srp;
|
|
|
|
|
|
|
|
if (psid->algorithm != SR_ALGORITHM_SPF)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
srp = isis_sr_prefix_find_node(srn, prefix);
|
|
|
|
if (srp) {
|
|
|
|
if (srp->sid.flags != psid->flags
|
|
|
|
|| srp->sid.algorithm != psid->algorithm
|
|
|
|
|| srp->sid.value != psid->value) {
|
|
|
|
srp->sid = *psid;
|
2020-04-30 11:23:15 +02:00
|
|
|
srp->state = SRDB_STATE_MODIFIED;
|
2019-08-04 03:02:37 +02:00
|
|
|
} else
|
2020-04-30 11:23:15 +02:00
|
|
|
srp->state = SRDB_STATE_UNCHANGED;
|
2019-08-04 03:02:37 +02:00
|
|
|
} else {
|
|
|
|
srp = isis_sr_prefix_add(area, srn, prefix, local,
|
|
|
|
psid);
|
2020-04-30 11:23:15 +02:00
|
|
|
srp->state = SRDB_STATE_NEW;
|
2019-08-04 03:02:37 +02:00
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Stop the Prefix-SID iteration since we only support the SPF
|
|
|
|
* algorithm for now.
|
|
|
|
*/
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Parse all SR-related information from the given LSP. */
|
|
|
|
static void isis_sr_parse_lsp(struct isis_area *area, int level,
|
|
|
|
struct sr_node **srn, struct isis_lsp *lsp)
|
|
|
|
{
|
|
|
|
struct isis_item_list *items;
|
|
|
|
struct isis_item *i;
|
|
|
|
bool local = lsp->own_lsp;
|
|
|
|
|
|
|
|
if (lsp->hdr.seqno == 0) {
|
|
|
|
zlog_warn("%s: lsp with 0 seq_num - ignore", __func__);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Parse the Router Capability TLV. */
|
|
|
|
if (*srn == NULL) {
|
|
|
|
*srn = isis_sr_parse_router_cap_tlv(
|
|
|
|
area, level, lsp->hdr.lsp_id, lsp->tlvs->router_cap);
|
|
|
|
if (!*srn)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Parse the Extended IP Reachability TLV. */
|
|
|
|
items = &lsp->tlvs->extended_ip_reach;
|
|
|
|
for (i = items->head; i; i = i->next) {
|
|
|
|
struct isis_extended_ip_reach *ir;
|
|
|
|
|
|
|
|
ir = (struct isis_extended_ip_reach *)i;
|
|
|
|
if (!ir->subtlvs)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
isis_sr_parse_prefix_sid_subtlvs(*srn, &ir->prefix, local,
|
|
|
|
&ir->subtlvs->prefix_sids);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Parse Multi Topology Reachable IPv6 Prefixes TLV. */
|
|
|
|
items = isis_lookup_mt_items(&lsp->tlvs->mt_ipv6_reach,
|
|
|
|
ISIS_MT_IPV6_UNICAST);
|
|
|
|
for (i = items ? items->head : NULL; i; i = i->next) {
|
|
|
|
struct isis_ipv6_reach *ir;
|
|
|
|
|
|
|
|
ir = (struct isis_ipv6_reach *)i;
|
|
|
|
if (!ir->subtlvs)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
isis_sr_parse_prefix_sid_subtlvs(*srn, &ir->prefix, local,
|
|
|
|
&ir->subtlvs->prefix_sids);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Parse all SR-related information from the entire LSPDB. */
|
|
|
|
static void isis_sr_parse_lspdb(struct isis_area *area)
|
|
|
|
{
|
|
|
|
struct isis_lsp *lsp;
|
|
|
|
|
|
|
|
for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) {
|
|
|
|
frr_each (lspdb, &area->lspdb[level - 1], lsp) {
|
|
|
|
struct isis_lsp *frag;
|
|
|
|
struct listnode *node;
|
|
|
|
struct sr_node *srn = NULL;
|
|
|
|
|
|
|
|
if (LSP_PSEUDO_ID(lsp->hdr.lsp_id))
|
|
|
|
continue;
|
|
|
|
if (!lsp->tlvs)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
isis_sr_parse_lsp(area, level, &srn, lsp);
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(lsp->lspu.frags, node, frag))
|
|
|
|
isis_sr_parse_lsp(area, level, &srn, frag);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Process any new/deleted/modified Prefix-SID in the LSPDB. */
|
|
|
|
static void isis_sr_process_prefix_changes(struct sr_node *srn,
|
|
|
|
struct sr_prefix *srp)
|
|
|
|
{
|
|
|
|
struct isis_area *area = srn->area;
|
|
|
|
|
|
|
|
/* Log any Prefix-SID change in the LSPDB. */
|
|
|
|
if (IS_DEBUG_ISIS(DEBUG_SR)) {
|
2020-04-30 11:23:15 +02:00
|
|
|
if (srp->state == SRDB_STATE_NEW)
|
2019-08-04 03:02:37 +02:00
|
|
|
zlog_debug(
|
|
|
|
"ISIS-SR (%s) Prefix-SID created: %pFX (sysid %s)",
|
|
|
|
area->area_tag, &srp->prefix,
|
|
|
|
sysid_print(srn->sysid));
|
2020-04-30 11:23:15 +02:00
|
|
|
else if (srp->state == SRDB_STATE_MODIFIED)
|
2019-08-04 03:02:37 +02:00
|
|
|
zlog_debug(
|
|
|
|
"ISIS-SR (%s) Prefix-SID modified: %pFX (sysid %s)",
|
|
|
|
area->area_tag, &srp->prefix,
|
|
|
|
sysid_print(srn->sysid));
|
2020-04-30 11:23:15 +02:00
|
|
|
else if (srp->state != SRDB_STATE_UNCHANGED)
|
2019-08-04 03:02:37 +02:00
|
|
|
zlog_debug(
|
|
|
|
"ISIS-SR (%s) Prefix-SID removed: %pFX (sysid %s)",
|
|
|
|
area->area_tag, &srp->prefix,
|
|
|
|
sysid_print(srn->sysid));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Install/reinstall/uninstall Prefix-SID if necessary. */
|
2020-04-30 11:23:15 +02:00
|
|
|
switch (srp->state) {
|
|
|
|
case SRDB_STATE_NEW:
|
2019-08-04 03:02:37 +02:00
|
|
|
isis_sr_prefix_install(srp);
|
2020-04-30 11:23:15 +02:00
|
|
|
break;
|
|
|
|
case SRDB_STATE_MODIFIED:
|
2019-08-04 03:02:37 +02:00
|
|
|
isis_sr_prefix_reinstall(srp, false);
|
2020-04-30 11:23:15 +02:00
|
|
|
break;
|
|
|
|
case SRDB_STATE_UNCHANGED:
|
|
|
|
break;
|
|
|
|
default:
|
2019-08-04 03:02:37 +02:00
|
|
|
isis_sr_prefix_del(area, srn, srp);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-04-30 11:23:15 +02:00
|
|
|
srp->state = SRDB_STATE_VALIDATED;
|
2019-08-04 03:02:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Process any new/deleted/modified SRGB in the LSPDB. */
|
|
|
|
static void isis_sr_process_node_changes(struct isis_area *area, int level,
|
|
|
|
struct sr_node *srn)
|
|
|
|
{
|
|
|
|
struct sr_prefix *srp;
|
|
|
|
uint8_t sysid[ISIS_SYS_ID_LEN];
|
|
|
|
bool adjacent;
|
|
|
|
|
|
|
|
memcpy(sysid, srn->sysid, sizeof(sysid));
|
|
|
|
|
|
|
|
/* Log any SRGB change in the LSPDB. */
|
|
|
|
if (IS_DEBUG_ISIS(DEBUG_SR)) {
|
2020-04-30 11:23:15 +02:00
|
|
|
if (srn->state == SRDB_STATE_NEW)
|
2019-08-04 03:02:37 +02:00
|
|
|
zlog_debug("ISIS-SR (%s) SRGB created (sysid %s)",
|
|
|
|
area->area_tag, sysid_print(sysid));
|
2020-04-30 11:23:15 +02:00
|
|
|
else if (srn->state == SRDB_STATE_MODIFIED)
|
2019-08-04 03:02:37 +02:00
|
|
|
zlog_debug("ISIS-SR (%s) SRGB modified (sysid %s)",
|
|
|
|
area->area_tag, sysid_print(sysid));
|
2020-04-30 11:23:15 +02:00
|
|
|
else if (srn->state != SRDB_STATE_UNCHANGED)
|
2019-08-04 03:02:37 +02:00
|
|
|
zlog_debug("ISIS-SR (%s) SRGB removed (sysid %s)",
|
|
|
|
area->area_tag, sysid_print(sysid));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If an adjacent router's SRGB was changed or created, then reinstall
|
|
|
|
* all Prefix-SIDs from all nodes.
|
|
|
|
*/
|
|
|
|
adjacent = isis_adj_exists(area, level, sysid);
|
2020-04-30 11:23:15 +02:00
|
|
|
switch (srn->state) {
|
|
|
|
case SRDB_STATE_NEW:
|
|
|
|
case SRDB_STATE_MODIFIED:
|
2019-08-04 03:02:37 +02:00
|
|
|
if (adjacent)
|
|
|
|
isis_sr_adj_srgb_update(area, sysid, level);
|
2020-04-30 11:23:15 +02:00
|
|
|
break;
|
|
|
|
case SRDB_STATE_UNCHANGED:
|
|
|
|
break;
|
|
|
|
default:
|
2019-08-04 03:02:37 +02:00
|
|
|
isis_sr_node_del(area, level, srn);
|
|
|
|
|
|
|
|
if (adjacent)
|
|
|
|
isis_sr_adj_srgb_update(area, sysid, level);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-04-30 11:23:15 +02:00
|
|
|
srn->state = SRDB_STATE_VALIDATED;
|
2019-08-04 03:02:37 +02:00
|
|
|
|
2020-04-30 11:23:15 +02:00
|
|
|
frr_each_safe (srdb_node_prefix, &srn->prefix_sids, srp)
|
2019-08-04 03:02:37 +02:00
|
|
|
isis_sr_process_prefix_changes(srn, srp);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Parse and process all SR-related Sub-TLVs after running the SPF algorithm. */
|
|
|
|
void isis_area_verify_sr(struct isis_area *area)
|
|
|
|
{
|
|
|
|
struct sr_node *srn;
|
|
|
|
|
|
|
|
if (!area->srdb.enabled)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Parse LSPDB to detect new/deleted/modified SR (sub-)TLVs. */
|
|
|
|
isis_sr_parse_lspdb(area);
|
|
|
|
|
|
|
|
/* Process possible SR-related changes in the LDPSB. */
|
|
|
|
for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) {
|
2020-04-30 11:23:15 +02:00
|
|
|
frr_each_safe (srdb_node, &area->srdb.sr_nodes[level - 1],
|
2019-08-04 03:02:37 +02:00
|
|
|
srn)
|
|
|
|
isis_sr_process_node_changes(area, level, srn);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Once a route is updated in the SPT, reinstall or uninstall its corresponding
|
|
|
|
* Prefix-SID (if any).
|
|
|
|
*/
|
|
|
|
static int isis_sr_route_update(struct isis_area *area, struct prefix *prefix,
|
|
|
|
struct isis_route_info *route_info)
|
|
|
|
{
|
|
|
|
struct sr_prefix *srp;
|
|
|
|
|
|
|
|
if (!area->srdb.enabled)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
switch (area->is_type) {
|
|
|
|
case IS_LEVEL_1:
|
|
|
|
srp = isis_sr_prefix_find_area(area, ISIS_LEVEL1, prefix);
|
|
|
|
break;
|
|
|
|
case IS_LEVEL_2:
|
|
|
|
srp = isis_sr_prefix_find_area(area, ISIS_LEVEL2, prefix);
|
|
|
|
break;
|
|
|
|
case IS_LEVEL_1_AND_2:
|
|
|
|
srp = isis_sr_prefix_find_area(area, ISIS_LEVEL1, prefix);
|
|
|
|
if (!srp)
|
|
|
|
srp = isis_sr_prefix_find_area(area, ISIS_LEVEL2,
|
|
|
|
prefix);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
flog_err(EC_LIB_DEVELOPMENT, "%s: unknown area level",
|
|
|
|
__func__);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!srp || srp->type == ISIS_SR_PREFIX_LOCAL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (CHECK_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ACTIVE)) {
|
|
|
|
isis_sr_prefix_reinstall(srp, true);
|
|
|
|
srp->u.remote.rinfo = route_info;
|
|
|
|
} else {
|
|
|
|
isis_sr_prefix_uninstall(srp);
|
|
|
|
srp->u.remote.rinfo = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
/* Install or uninstall (LAN)-Adj-SID. */
|
|
|
|
static void isis_sr_adj_sid_install_uninstall(bool install,
|
|
|
|
const struct sr_adjacency *sra)
|
|
|
|
{
|
|
|
|
struct zapi_labels zl;
|
2020-03-03 16:59:33 +01:00
|
|
|
struct zapi_nexthop *znh;
|
2019-08-04 03:02:37 +02:00
|
|
|
int cmd;
|
|
|
|
|
|
|
|
cmd = install ? ZEBRA_MPLS_LABELS_ADD : ZEBRA_MPLS_LABELS_DELETE;
|
|
|
|
|
|
|
|
memset(&zl, 0, sizeof(zl));
|
|
|
|
zl.type = ZEBRA_LSP_ISIS_SR;
|
|
|
|
zl.local_label = sra->nexthop.label;
|
|
|
|
zl.nexthop_num = 1;
|
|
|
|
znh = &zl.nexthops[0];
|
2020-03-03 16:59:33 +01:00
|
|
|
znh->gate = sra->nexthop.address;
|
2019-08-04 03:02:37 +02:00
|
|
|
znh->type = (sra->nexthop.family == AF_INET)
|
|
|
|
? NEXTHOP_TYPE_IPV4_IFINDEX
|
|
|
|
: NEXTHOP_TYPE_IPV6_IFINDEX;
|
|
|
|
znh->ifindex = sra->adj->circuit->interface->ifindex;
|
2020-03-03 16:59:33 +01:00
|
|
|
znh->label_num = 1;
|
|
|
|
znh->labels[0] = MPLS_LABEL_IMPLICIT_NULL;
|
2019-08-04 03:02:37 +02:00
|
|
|
|
|
|
|
(void)zebra_send_mpls_labels(zclient, cmd, &zl);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add new local Adj-SID. */
|
|
|
|
static void isis_sr_adj_sid_add_single(struct isis_adjacency *adj, int family,
|
|
|
|
bool backup)
|
|
|
|
{
|
|
|
|
struct isis_circuit *circuit = adj->circuit;
|
|
|
|
struct isis_area *area = circuit->area;
|
|
|
|
struct sr_adjacency *sra;
|
|
|
|
struct isis_adj_sid *adj_sid;
|
|
|
|
struct isis_lan_adj_sid *ladj_sid;
|
|
|
|
union g_addr nexthop = {};
|
|
|
|
uint8_t flags;
|
|
|
|
mpls_label_t local_label;
|
|
|
|
|
|
|
|
switch (family) {
|
|
|
|
case AF_INET:
|
|
|
|
if (!circuit->ip_router)
|
|
|
|
return;
|
|
|
|
|
|
|
|
nexthop.ipv4 = adj->ipv4_addresses[0];
|
|
|
|
break;
|
|
|
|
case AF_INET6:
|
|
|
|
if (!circuit->ipv6_router)
|
|
|
|
return;
|
|
|
|
|
|
|
|
nexthop.ipv6 = adj->ipv6_addresses[0];
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
flog_err(EC_LIB_DEVELOPMENT,
|
|
|
|
"%s: unexpected address-family: %u", __func__, family);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
flags = EXT_SUBTLV_LINK_ADJ_SID_VFLG | EXT_SUBTLV_LINK_ADJ_SID_LFLG;
|
|
|
|
if (family == AF_INET6)
|
|
|
|
SET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_FFLG);
|
|
|
|
if (backup)
|
|
|
|
SET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_BFLG);
|
|
|
|
|
|
|
|
local_label = isis_zebra_request_dynamic_label();
|
|
|
|
if (circuit->ext == NULL)
|
|
|
|
circuit->ext = isis_alloc_ext_subtlvs();
|
|
|
|
|
|
|
|
if (IS_DEBUG_ISIS(DEBUG_SR)) {
|
|
|
|
char buf[INET6_ADDRSTRLEN];
|
|
|
|
|
|
|
|
inet_ntop(family, &nexthop, buf, sizeof(buf));
|
|
|
|
zlog_debug("ISIS-SR (%s) installing Adj-SID %s%%%s label %u",
|
|
|
|
area->area_tag, buf, circuit->interface->name,
|
|
|
|
local_label);
|
|
|
|
}
|
|
|
|
|
|
|
|
sra = XCALLOC(MTYPE_ISIS_SR_INFO, sizeof(*sra));
|
|
|
|
sra->type = backup ? ISIS_SR_LAN_BACKUP : ISIS_SR_ADJ_NORMAL;
|
|
|
|
sra->nexthop.family = family;
|
|
|
|
sra->nexthop.address = nexthop;
|
|
|
|
sra->nexthop.label = local_label;
|
|
|
|
switch (circuit->circ_type) {
|
|
|
|
case CIRCUIT_T_BROADCAST:
|
|
|
|
ladj_sid = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*ladj_sid));
|
|
|
|
ladj_sid->family = family;
|
|
|
|
ladj_sid->flags = flags;
|
|
|
|
ladj_sid->weight = 0;
|
|
|
|
memcpy(ladj_sid->neighbor_id, adj->sysid,
|
|
|
|
sizeof(ladj_sid->neighbor_id));
|
|
|
|
ladj_sid->sid = local_label;
|
|
|
|
isis_tlvs_add_lan_adj_sid(circuit->ext, ladj_sid);
|
|
|
|
sra->u.ladj_sid = ladj_sid;
|
|
|
|
break;
|
|
|
|
case CIRCUIT_T_P2P:
|
|
|
|
adj_sid = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*adj_sid));
|
|
|
|
adj_sid->family = family;
|
|
|
|
adj_sid->flags = flags;
|
|
|
|
adj_sid->weight = 0;
|
|
|
|
adj_sid->sid = local_label;
|
|
|
|
isis_tlvs_add_adj_sid(circuit->ext, adj_sid);
|
|
|
|
sra->u.adj_sid = adj_sid;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
flog_err(EC_LIB_DEVELOPMENT, "%s: unexpected circuit type: %u",
|
|
|
|
__func__, circuit->circ_type);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
sra->adj = adj;
|
|
|
|
listnode_add(area->srdb.adj_sids, sra);
|
|
|
|
listnode_add(adj->adj_sids, sra);
|
|
|
|
|
|
|
|
isis_sr_adj_sid_install_uninstall(true, sra);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void isis_sr_adj_sid_add(struct isis_adjacency *adj, int family)
|
|
|
|
{
|
|
|
|
isis_sr_adj_sid_add_single(adj, family, false);
|
|
|
|
isis_sr_adj_sid_add_single(adj, family, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Delete local Adj-SID. */
|
|
|
|
static void isis_sr_adj_sid_del(struct sr_adjacency *sra)
|
|
|
|
{
|
|
|
|
struct isis_circuit *circuit = sra->adj->circuit;
|
|
|
|
struct isis_area *area = circuit->area;
|
|
|
|
|
|
|
|
if (IS_DEBUG_ISIS(DEBUG_SR)) {
|
|
|
|
char buf[INET6_ADDRSTRLEN];
|
|
|
|
|
|
|
|
inet_ntop(sra->nexthop.family, &sra->nexthop.address, buf,
|
|
|
|
sizeof(buf));
|
|
|
|
zlog_debug("ISIS-SR (%s) uninstalling Adj-SID %s%%%s",
|
|
|
|
area->area_tag, buf, circuit->interface->name);
|
|
|
|
}
|
|
|
|
|
|
|
|
isis_sr_adj_sid_install_uninstall(false, sra);
|
|
|
|
|
|
|
|
switch (circuit->circ_type) {
|
|
|
|
case CIRCUIT_T_BROADCAST:
|
|
|
|
isis_zebra_release_dynamic_label(sra->u.ladj_sid->sid);
|
|
|
|
isis_tlvs_del_lan_adj_sid(circuit->ext, sra->u.ladj_sid);
|
|
|
|
break;
|
|
|
|
case CIRCUIT_T_P2P:
|
|
|
|
isis_zebra_release_dynamic_label(sra->u.adj_sid->sid);
|
|
|
|
isis_tlvs_del_adj_sid(circuit->ext, sra->u.adj_sid);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
flog_err(EC_LIB_DEVELOPMENT, "%s: unexpected circuit type: %u",
|
|
|
|
__func__, circuit->circ_type);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
listnode_delete(area->srdb.adj_sids, sra);
|
|
|
|
listnode_delete(sra->adj->adj_sids, sra);
|
|
|
|
XFREE(MTYPE_ISIS_SR_INFO, sra);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Remove all Adj-SIDs associated to an adjacency that is going down. */
|
|
|
|
static int isis_sr_adj_state_change(struct isis_adjacency *adj)
|
|
|
|
{
|
|
|
|
struct sr_adjacency *sra;
|
|
|
|
struct listnode *node, *nnode;
|
|
|
|
|
|
|
|
if (!adj->circuit->area->srdb.enabled)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (adj->adj_state == ISIS_ADJ_UP)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
for (ALL_LIST_ELEMENTS(adj->adj_sids, node, nnode, sra))
|
|
|
|
isis_sr_adj_sid_del(sra);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Adjacency now has one or more IPv4/IPv6 addresses. Add new IPv4 or IPv6
|
|
|
|
* Adj-SID accordingly.
|
|
|
|
*/
|
|
|
|
static int isis_sr_adj_ip_enabled(struct isis_adjacency *adj, int family)
|
|
|
|
{
|
|
|
|
if (!adj->circuit->area->srdb.enabled)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
isis_sr_adj_sid_add(adj, family);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Adjacency doesn't have any IPv4 or IPv6 addresses anymore. Delete the
|
|
|
|
* corresponding Adj-SID(s) accordingly.
|
|
|
|
*/
|
|
|
|
static int isis_sr_adj_ip_disabled(struct isis_adjacency *adj, int family)
|
|
|
|
{
|
|
|
|
struct sr_adjacency *sra;
|
|
|
|
struct listnode *node, *nnode;
|
|
|
|
|
|
|
|
if (!adj->circuit->area->srdb.enabled)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
for (ALL_LIST_ELEMENTS(adj->adj_sids, node, nnode, sra))
|
|
|
|
if (sra->nexthop.family == family)
|
|
|
|
isis_sr_adj_sid_del(sra);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int isis_sr_if_new_hook(struct interface *ifp)
|
|
|
|
{
|
|
|
|
struct isis_circuit *circuit;
|
|
|
|
struct isis_area *area;
|
|
|
|
struct connected *connected;
|
|
|
|
struct listnode *node;
|
|
|
|
|
|
|
|
circuit = circuit_scan_by_ifp(ifp);
|
|
|
|
if (!circuit)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
area = circuit->area;
|
|
|
|
if (!area)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Update the Node-SID flag of the configured Prefix-SID mappings if
|
|
|
|
* necessary. This needs to be done here since isisd reads the startup
|
|
|
|
* configuration before receiving interface information from zebra.
|
|
|
|
*/
|
|
|
|
FOR_ALL_INTERFACES_ADDRESSES (ifp, connected, node) {
|
|
|
|
struct sr_prefix_cfg *pcfg;
|
|
|
|
|
|
|
|
pcfg = isis_sr_cfg_prefix_find(area, connected->address);
|
|
|
|
if (!pcfg)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (isis_sr_prefix_is_node_sid(ifp, &pcfg->prefix)
|
|
|
|
&& !pcfg->node_sid) {
|
|
|
|
pcfg->node_sid = true;
|
|
|
|
lsp_regenerate_schedule(area, area->is_type, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
static void isis_sr_show_prefix_sid_local(struct vty *vty, struct ttable *tt,
|
|
|
|
const struct isis_area *area,
|
|
|
|
const struct sr_prefix *srp)
|
|
|
|
{
|
|
|
|
const struct sr_nexthop_info *srnh = &srp->u.local.info;
|
|
|
|
char buf_prefix[BUFSIZ];
|
|
|
|
char buf_llabel[BUFSIZ];
|
|
|
|
char buf_rlabel[BUFSIZ];
|
|
|
|
char buf_uptime[BUFSIZ];
|
|
|
|
|
|
|
|
if (srp->local_label != MPLS_INVALID_LABEL)
|
|
|
|
label2str(srp->local_label, buf_llabel, sizeof(buf_llabel));
|
|
|
|
else
|
|
|
|
snprintf(buf_llabel, sizeof(buf_llabel), "-");
|
|
|
|
if (srnh->label != MPLS_INVALID_LABEL) {
|
|
|
|
label2str(srnh->label, buf_rlabel, sizeof(buf_rlabel));
|
|
|
|
log_uptime(srnh->uptime, buf_uptime, sizeof(buf_uptime));
|
|
|
|
} else {
|
|
|
|
snprintf(buf_rlabel, sizeof(buf_rlabel), "-");
|
|
|
|
snprintf(buf_uptime, sizeof(buf_uptime), "-");
|
|
|
|
}
|
|
|
|
|
|
|
|
ttable_add_row(tt, "%s|%u|%s|local|%s|%s",
|
|
|
|
prefix2str(&srp->prefix, buf_prefix, sizeof(buf_prefix)),
|
|
|
|
srp->sid.value, buf_llabel, buf_rlabel, buf_uptime);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void isis_sr_show_prefix_sid_remote(struct vty *vty, struct ttable *tt,
|
|
|
|
const struct isis_area *area,
|
|
|
|
const struct sr_prefix *srp)
|
|
|
|
{
|
|
|
|
struct isis_nexthop *nexthop;
|
|
|
|
struct listnode *node;
|
|
|
|
char buf_prefix[BUFSIZ];
|
|
|
|
char buf_llabel[BUFSIZ];
|
|
|
|
char buf_nhop[BUFSIZ];
|
|
|
|
char buf_iface[BUFSIZ];
|
|
|
|
char buf_rlabel[BUFSIZ];
|
|
|
|
char buf_uptime[BUFSIZ];
|
|
|
|
bool first = true;
|
|
|
|
|
|
|
|
(void)prefix2str(&srp->prefix, buf_prefix, sizeof(buf_prefix));
|
|
|
|
if (srp->local_label != MPLS_INVALID_LABEL)
|
|
|
|
label2str(srp->local_label, buf_llabel, sizeof(buf_llabel));
|
|
|
|
else
|
|
|
|
snprintf(buf_llabel, sizeof(buf_llabel), "-");
|
|
|
|
|
|
|
|
if (!srp->u.remote.rinfo) {
|
|
|
|
ttable_add_row(tt, "%s|%u|%s|-|-|-", buf_prefix, srp->sid.value,
|
|
|
|
buf_llabel);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(srp->u.remote.rinfo->nexthops, node,
|
|
|
|
nexthop)) {
|
|
|
|
struct interface *ifp;
|
|
|
|
|
|
|
|
inet_ntop(nexthop->family, &nexthop->ip, buf_nhop,
|
|
|
|
sizeof(buf_nhop));
|
|
|
|
ifp = if_lookup_by_index(nexthop->ifindex, VRF_DEFAULT);
|
|
|
|
if (ifp)
|
|
|
|
strlcpy(buf_iface, ifp->name, sizeof(buf_iface));
|
|
|
|
else
|
|
|
|
snprintf(buf_iface, sizeof(buf_iface), "ifindex %u",
|
|
|
|
nexthop->ifindex);
|
|
|
|
if (nexthop->sr.label == MPLS_INVALID_LABEL) {
|
|
|
|
snprintf(buf_rlabel, sizeof(buf_rlabel), "-");
|
|
|
|
snprintf(buf_uptime, sizeof(buf_uptime), "-");
|
|
|
|
} else {
|
|
|
|
label2str(nexthop->sr.label, buf_rlabel,
|
|
|
|
sizeof(buf_rlabel));
|
|
|
|
log_uptime(nexthop->sr.uptime, buf_uptime,
|
|
|
|
sizeof(buf_uptime));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (first)
|
|
|
|
ttable_add_row(tt, "%s|%u|%s|%s, %s|%s|%s", buf_prefix,
|
|
|
|
srp->sid.value, buf_llabel, buf_nhop,
|
|
|
|
buf_iface, buf_rlabel, buf_uptime);
|
|
|
|
else
|
|
|
|
ttable_add_row(tt, "|||%s, %s|%s|%s", buf_nhop,
|
|
|
|
buf_iface, buf_rlabel, buf_uptime);
|
|
|
|
first = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void isis_sr_show_prefix_sids(struct vty *vty, struct isis_area *area,
|
|
|
|
int level)
|
|
|
|
{
|
|
|
|
struct sr_prefix *srp;
|
|
|
|
struct ttable *tt;
|
|
|
|
|
2020-04-30 11:23:15 +02:00
|
|
|
if (srdb_area_prefix_count(&area->srdb.prefix_sids[level - 1]) == 0)
|
2019-08-04 03:02:37 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
vty_out(vty, " IS-IS %s Prefix-SIDs:\n\n", circuit_t2string(level));
|
|
|
|
|
|
|
|
/* Prepare table. */
|
|
|
|
tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
|
|
|
|
ttable_add_row(tt, "Prefix|SID|In Label|Nexthop|Out Label|Uptime");
|
|
|
|
tt->style.cell.rpad = 2;
|
|
|
|
tt->style.corner = '+';
|
|
|
|
ttable_restyle(tt);
|
|
|
|
ttable_rowseps(tt, 0, BOTTOM, true, '-');
|
|
|
|
|
2020-04-30 11:23:15 +02:00
|
|
|
frr_each (srdb_area_prefix, &area->srdb.prefix_sids[level - 1],
|
2019-08-04 03:02:37 +02:00
|
|
|
srp) {
|
|
|
|
switch (srp->type) {
|
|
|
|
case ISIS_SR_PREFIX_LOCAL:
|
|
|
|
isis_sr_show_prefix_sid_local(vty, tt, area, srp);
|
|
|
|
break;
|
|
|
|
case ISIS_SR_PREFIX_REMOTE:
|
|
|
|
isis_sr_show_prefix_sid_remote(vty, tt, area, srp);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Dump the generated table. */
|
|
|
|
if (tt->nrows > 1) {
|
|
|
|
char *table;
|
|
|
|
|
|
|
|
table = ttable_dump(tt, "\n");
|
|
|
|
vty_out(vty, "%s\n", table);
|
|
|
|
XFREE(MTYPE_TMP, table);
|
|
|
|
}
|
|
|
|
ttable_del(tt);
|
|
|
|
}
|
|
|
|
|
|
|
|
DEFUN(show_sr_prefix_sids, show_sr_prefix_sids_cmd,
|
|
|
|
"show isis segment-routing prefix-sids",
|
|
|
|
SHOW_STR PROTO_HELP
|
|
|
|
"Segment-Routing\n"
|
|
|
|
"Segment-Routing Prefix-SIDs\n")
|
|
|
|
{
|
|
|
|
struct listnode *node;
|
|
|
|
struct isis_area *area;
|
|
|
|
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) {
|
|
|
|
vty_out(vty, "Area %s:\n",
|
|
|
|
area->area_tag ? area->area_tag : "null");
|
|
|
|
|
|
|
|
for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++)
|
|
|
|
isis_sr_show_prefix_sids(vty, area, level);
|
|
|
|
}
|
|
|
|
|
|
|
|
return CMD_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
/* Try to enable SR on the given IS-IS area. */
|
|
|
|
int isis_sr_start(struct isis_area *area)
|
|
|
|
{
|
|
|
|
struct isis_sr_db *srdb = &area->srdb;
|
|
|
|
struct isis_circuit *circuit;
|
|
|
|
struct listnode *node;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Request SGRB to the label manager. If the allocation fails, return
|
|
|
|
* an error to disable SR until a new SRGB is successfully allocated.
|
|
|
|
*/
|
|
|
|
if (isis_zebra_request_label_range(
|
|
|
|
srdb->config.srgb_lower_bound,
|
|
|
|
srdb->config.srgb_upper_bound
|
|
|
|
- srdb->config.srgb_lower_bound + 1))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (IS_DEBUG_ISIS(DEBUG_SR))
|
|
|
|
zlog_debug("ISIS-SR (%s) Starting Segment Routing",
|
|
|
|
area->area_tag);
|
|
|
|
|
|
|
|
/* Create Adj-SIDs for existing adjacencies. */
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) {
|
|
|
|
struct isis_adjacency *adj;
|
|
|
|
struct listnode *anode;
|
|
|
|
|
|
|
|
switch (circuit->circ_type) {
|
|
|
|
case CIRCUIT_T_BROADCAST:
|
|
|
|
for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS;
|
|
|
|
level++) {
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(
|
|
|
|
circuit->u.bc.adjdb[level - 1],
|
|
|
|
anode, adj)) {
|
|
|
|
if (adj->ipv4_address_count > 0)
|
|
|
|
isis_sr_adj_sid_add(adj,
|
|
|
|
AF_INET);
|
|
|
|
if (adj->ipv6_address_count > 0)
|
|
|
|
isis_sr_adj_sid_add(adj,
|
|
|
|
AF_INET6);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case CIRCUIT_T_P2P:
|
|
|
|
adj = circuit->u.p2p.neighbor;
|
|
|
|
if (adj && adj->ipv4_address_count > 0)
|
|
|
|
isis_sr_adj_sid_add(adj, AF_INET);
|
|
|
|
if (adj && adj->ipv6_address_count > 0)
|
|
|
|
isis_sr_adj_sid_add(adj, AF_INET6);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Regenerate LSPs. */
|
|
|
|
lsp_regenerate_schedule(area, area->is_type, 0);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Disable SR on the given IS-IS area. */
|
|
|
|
void isis_sr_stop(struct isis_area *area)
|
|
|
|
{
|
|
|
|
struct isis_sr_db *srdb = &area->srdb;
|
|
|
|
struct sr_adjacency *sra;
|
|
|
|
struct listnode *node, *nnode;
|
|
|
|
|
|
|
|
if (IS_DEBUG_ISIS(DEBUG_SR))
|
|
|
|
zlog_debug("ISIS-SR (%s) Stopping Segment Routing",
|
|
|
|
area->area_tag);
|
|
|
|
|
|
|
|
/* Uninstall Adj-SIDs. */
|
|
|
|
for (ALL_LIST_ELEMENTS(area->srdb.adj_sids, node, nnode, sra))
|
|
|
|
isis_sr_adj_sid_del(sra);
|
|
|
|
|
|
|
|
/* Uninstall Prefix-SIDs. */
|
|
|
|
for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) {
|
2020-04-30 11:23:15 +02:00
|
|
|
while (srdb_node_count(&srdb->sr_nodes[level - 1]) > 0) {
|
2019-08-04 03:02:37 +02:00
|
|
|
struct sr_node *srn;
|
|
|
|
|
2020-04-30 11:23:15 +02:00
|
|
|
srn = srdb_node_first(&srdb->sr_nodes[level - 1]);
|
2019-08-04 03:02:37 +02:00
|
|
|
isis_sr_node_del(area, level, srn);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Release SRGB. */
|
|
|
|
isis_zebra_release_label_range(srdb->config.srgb_lower_bound,
|
|
|
|
srdb->config.srgb_upper_bound);
|
|
|
|
|
|
|
|
/* Regenerate LSPs. */
|
|
|
|
lsp_regenerate_schedule(area, area->is_type, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void isis_sr_area_init(struct isis_area *area)
|
|
|
|
{
|
|
|
|
struct isis_sr_db *srdb = &area->srdb;
|
|
|
|
|
|
|
|
memset(srdb, 0, sizeof(*srdb));
|
|
|
|
srdb->enabled = false;
|
|
|
|
srdb->adj_sids = list_new();
|
|
|
|
|
|
|
|
for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) {
|
2020-04-30 11:23:15 +02:00
|
|
|
srdb_node_init(&srdb->sr_nodes[level - 1]);
|
|
|
|
srdb_area_prefix_init(&srdb->prefix_sids[level - 1]);
|
2019-08-04 03:02:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Pull defaults from the YANG module. */
|
|
|
|
#ifndef FABRICD
|
|
|
|
srdb->config.enabled = yang_get_default_bool("%s/enabled", ISIS_SR);
|
|
|
|
srdb->config.srgb_lower_bound =
|
|
|
|
yang_get_default_uint32("%s/srgb/lower-bound", ISIS_SR);
|
|
|
|
srdb->config.srgb_upper_bound =
|
|
|
|
yang_get_default_uint32("%s/srgb/upper-bound", ISIS_SR);
|
|
|
|
#else
|
|
|
|
srdb->config.enabled = false;
|
|
|
|
srdb->config.srgb_lower_bound = SRGB_LOWER_BOUND;
|
|
|
|
srdb->config.srgb_upper_bound = SRGB_UPPER_BOUND;
|
|
|
|
#endif
|
|
|
|
srdb->config.msd = 0;
|
2020-04-30 11:23:15 +02:00
|
|
|
srdb_prefix_cfg_init(&srdb->config.prefix_sids);
|
2019-08-04 03:02:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void isis_sr_area_term(struct isis_area *area)
|
|
|
|
{
|
|
|
|
struct isis_sr_db *srdb = &area->srdb;
|
|
|
|
|
|
|
|
/* Stop Segment Routing */
|
|
|
|
if (area->srdb.enabled)
|
|
|
|
isis_sr_stop(area);
|
|
|
|
|
|
|
|
/* Clear Prefix-SID configuration. */
|
2020-04-30 11:23:15 +02:00
|
|
|
while (srdb_prefix_cfg_count(&srdb->config.prefix_sids) > 0) {
|
2019-08-04 03:02:37 +02:00
|
|
|
struct sr_prefix_cfg *pcfg;
|
|
|
|
|
2020-04-30 11:23:15 +02:00
|
|
|
pcfg = srdb_prefix_cfg_first(&srdb->config.prefix_sids);
|
2019-08-04 03:02:37 +02:00
|
|
|
isis_sr_cfg_prefix_del(pcfg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void isis_sr_init(void)
|
|
|
|
{
|
|
|
|
install_element(VIEW_NODE, &show_sr_prefix_sids_cmd);
|
|
|
|
|
|
|
|
/* Register hooks. */
|
|
|
|
hook_register(isis_adj_state_change_hook, isis_sr_adj_state_change);
|
|
|
|
hook_register(isis_adj_ip_enabled_hook, isis_sr_adj_ip_enabled);
|
|
|
|
hook_register(isis_adj_ip_disabled_hook, isis_sr_adj_ip_disabled);
|
|
|
|
hook_register(isis_route_update_hook, isis_sr_route_update);
|
|
|
|
hook_register(isis_if_new_hook, isis_sr_if_new_hook);
|
|
|
|
}
|
|
|
|
|
|
|
|
void isis_sr_term(void)
|
|
|
|
{
|
|
|
|
/* Unregister hooks. */
|
|
|
|
hook_unregister(isis_adj_state_change_hook, isis_sr_adj_state_change);
|
|
|
|
hook_unregister(isis_adj_ip_enabled_hook, isis_sr_adj_ip_enabled);
|
|
|
|
hook_unregister(isis_adj_ip_disabled_hook, isis_sr_adj_ip_disabled);
|
|
|
|
hook_unregister(isis_route_update_hook, isis_sr_route_update);
|
|
|
|
hook_unregister(isis_if_new_hook, isis_sr_if_new_hook);
|
|
|
|
}
|