diff --git a/doc/user/isisd.rst b/doc/user/isisd.rst index f0c64b9bb0..dc598ea5bf 100644 --- a/doc/user/isisd.rst +++ b/doc/user/isisd.rst @@ -473,15 +473,16 @@ Traffic Engineering Segment Routing =============== -This is an EXPERIMENTAL support of Segment Routing as per draft -`draft-ietf-isis-segment-routing-extensions-25.txt` for MPLS dataplane. -It supports IPv4, IPv6 and ECMP and has been tested against Cisco & Juniper -routers. +This is an EXPERIMENTAL support of Segment Routing as per RFC8667 +for MPLS dataplane. It supports IPv4, IPv6 and ECMP and has been +tested against Cisco & Juniper routers. Known limitations: - No support for level redistribution (L1 to L2 or L2 to L1) - No support for binding SID - No support for SRMS + - No support for SRLB + - Only one SRGB and default SPF Algorithm is supported .. index:: [no] segment-routing on .. clicmd:: [no] segment-routing on @@ -491,14 +492,15 @@ Known limitations: .. index:: [no] segment-routing global-block (0-1048575) (0-1048575) .. clicmd:: [no] segment-routing global-block (0-1048575) (0-1048575) - Seet the Segment Routing Global Block i.e. the label range used by MPLS + Set the Segment Routing Global Block i.e. the label range used by MPLS to store label in the MPLS FIB. .. index:: [no] segment-routing node-msd (1-16) .. clicmd:: [no] segment-routing node-msd (1-16) Set the Maximum Stack Depth supported by the router. The value depend of the - MPLS dataplane. E.g. for Linux kernel, since version 4.13 it is 32. + MPLS dataplane. E.g. for Linux kernel, since version 4.13 the maximum value + is 32. .. index:: [no] segment-routing prefix [no-php-flag|explicit-null] .. clicmd:: [no] segment-routing prefix dnode, NULL, true); area->srdb.config.msd = yang_dnode_get_uint8(args->dnode, NULL); - isis_sr_cfg_msd_update(area); + + /* Update and regenerate LSP */ + lsp_regenerate_schedule(area, area->is_type, 0); return NB_OK; } @@ -1536,7 +1538,9 @@ int isis_instance_segment_routing_msd_node_msd_destroy( area = nb_running_get_entry(args->dnode, NULL, true); area->srdb.config.msd = 0; - isis_sr_cfg_msd_update(area); + + /* Update and regenerate LSP */ + lsp_regenerate_schedule(area, area->is_type, 0); return NB_OK; } diff --git a/isisd/isis_sr.c b/isisd/isis_sr.c index 9bdb2c40e8..c24c0608b2 100644 --- a/isisd/isis_sr.c +++ b/isisd/isis_sr.c @@ -1,8 +1,7 @@ /* - * This is an implementation of Segment Routing for IS-IS - * as per draft draft-ietf-isis-segment-routing-extensions-25 + * This is an implementation of Segment Routing for IS-IS as per RFC 8667 * - * Copyright (C) 2019 Orange Labs http://www.orange.com + * Copyright (C) 2019 Orange http://www.orange.com * * Author: Olivier Dugeon * Contributor: Renato Westphal for NetDEF @@ -48,54 +47,96 @@ #include "isisd/isis_zebra.h" #include "isisd/isis_errors.h" +/* Local variables and functions */ 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 void sr_prefix_uninstall(struct sr_prefix *srp); +static void sr_prefix_reinstall(struct sr_prefix *srp, bool make_before_break); -/*----------------------------------------------------------------------------*/ +/* --- RB-Tree Management functions ----------------------------------------- */ +/** + * SR Prefix comparison for RB-Tree. + * + * @param a First SR prefix + * @param b Second SR prefix + * + * @return -1 (a < b), 0 (a == b) or +1 (a > b) + */ static inline int sr_prefix_sid_compare(const struct sr_prefix *a, const struct sr_prefix *b) { return prefix_cmp(&a->prefix, &b->prefix); } -DECLARE_RBTREE_UNIQ(tree_sr_node_prefix, struct sr_prefix, node_entry, +DECLARE_RBTREE_UNIQ(srdb_node_prefix, struct sr_prefix, node_entry, sr_prefix_sid_compare) -DECLARE_RBTREE_UNIQ(tree_sr_area_prefix, struct sr_prefix, area_entry, +DECLARE_RBTREE_UNIQ(srdb_area_prefix, struct sr_prefix, area_entry, sr_prefix_sid_compare) +/** + * Configured SR Prefix comparison for RB-Tree. + * + * @param a First SR prefix + * @param b Second SR prefix + * + * @return -1 (a < b), 0 (a == b) or +1 (a > b) + */ 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); } -DECLARE_RBTREE_UNIQ(tree_sr_prefix_cfg, struct sr_prefix_cfg, entry, +DECLARE_RBTREE_UNIQ(srdb_prefix_cfg, struct sr_prefix_cfg, entry, sr_prefix_sid_cfg_compare) +/** + * SR Node comparison for RB-Tree. + * + * @param a First SR node + * @param b Second SR node + * + * @return -1 (a < b), 0 (a == b) or +1 (a > b) + */ 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); } -DECLARE_RBTREE_UNIQ(tree_sr_node, struct sr_node, entry, sr_node_compare) +DECLARE_RBTREE_UNIQ(srdb_node, struct sr_node, entry, sr_node_compare) -/*----------------------------------------------------------------------------*/ +/* --- Functions used for Yang model and CLI to configure Segment Routing --- */ -/* 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) +/** + * Check if prefix correspond to a Node SID. + * + * @param ifp Interface + * @param prefix Prefix to be checked + * + * @return True if the interface/address pair corresponds to a Node-SID + */ +static bool 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. */ +/** + * Update local SRGB configuration. SRGB is reserved though Label Manager. + * This function trigger the update of local Prefix-SID installation. + * + * @param area IS-IS area + * @param lower_bound Lower bound of SRGB + * @param upper_bound Upper bound of SRGB + * + * @return 0 on success, -1 otherwise + */ 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; + sr_debug("ISIS-Sr (%s): Update SRGB", area->area_tag); + /* First release the old SRGB. */ if (srdb->config.enabled) isis_zebra_release_label_range(srdb->config.srgb_lower_bound, @@ -114,11 +155,15 @@ int isis_sr_cfg_srgb_update(struct isis_area *area, uint32_t lower_bound, - srdb->config.srgb_lower_bound + 1)) return -1; + sr_debug(" |- Got new SRGB %u/%u", + srdb->config.srgb_lower_bound, + srdb->config.srgb_upper_bound); + /* Reinstall local Prefix-SIDs to update their input labels. */ for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) { - frr_each (tree_sr_area_prefix, + frr_each (srdb_area_prefix, &area->srdb.prefix_sids[level - 1], srp) { - isis_sr_prefix_reinstall(srp, false); + sr_prefix_reinstall(srp, false); } } @@ -132,19 +177,22 @@ int isis_sr_cfg_srgb_update(struct isis_area *area, uint32_t lower_bound, 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. */ +/** + * Add new Prefix-SID configuration to the SRDB. + * + * @param area IS-IS area + * @param prefix Prefix to be added + * + * @return Newly added Prefix-SID configuration structure + */ 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; + sr_debug("ISIS-Sr (%s): Add local prefix %pFX", area->area_tag, prefix); + pcfg = XCALLOC(MTYPE_ISIS_SR_INFO, sizeof(*pcfg)); pcfg->prefix = *prefix; pcfg->area = area; @@ -157,36 +205,57 @@ struct sr_prefix_cfg *isis_sr_cfg_prefix_add(struct isis_area *area, /* Set the N-flag when appropriate. */ ifp = if_lookup_prefix(prefix, VRF_DEFAULT); - if (ifp && isis_sr_prefix_is_node_sid(ifp, prefix)) + if (ifp && sr_prefix_is_node_sid(ifp, prefix)) pcfg->node_sid = true; /* Save prefix-sid configuration. */ - tree_sr_prefix_cfg_add(&area->srdb.config.prefix_sids, pcfg); + srdb_prefix_cfg_add(&area->srdb.config.prefix_sids, pcfg); return pcfg; } -/* Handle removal of locally configured Prefix-SID. */ +/** + * Removal of locally configured Prefix-SID. + * + * @param pcfg Configured Prefix-SID + */ void isis_sr_cfg_prefix_del(struct sr_prefix_cfg *pcfg) { - struct isis_area *area; + struct isis_area *area = pcfg->area; - area = pcfg->area; - tree_sr_prefix_cfg_del(&area->srdb.config.prefix_sids, pcfg); + sr_debug("ISIS-Sr (%s): Delete local Prefix-SID %pFX %s %u", + area->area_tag, &pcfg->prefix, + pcfg->sid_type == SR_SID_VALUE_TYPE_INDEX ? "index" : "label", + pcfg->sid); + + srdb_prefix_cfg_del(&area->srdb.config.prefix_sids, pcfg); XFREE(MTYPE_ISIS_SR_INFO, pcfg); } -/* Lookup Prefix-SID in the local configuration. */ +/** + * Lookup for Prefix-SID in the local configuration. + * + * @param area IS-IS area + * @param prefix Prefix to lookup + * + * @return Configured Prefix-SID structure if found, NULL otherwise + */ 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); - return tree_sr_prefix_cfg_find(&area->srdb.config.prefix_sids, &pcfg); + return srdb_prefix_cfg_find(&area->srdb.config.prefix_sids, &pcfg); } -/* Fill in Prefix-SID Sub-TLV according to the corresponding configuration. */ +/** + * Fill in Prefix-SID Sub-TLV according to the corresponding configuration. + * + * @param pcfg Prefix-SID configuration + * @param external False if prefix is locally configured, true otherwise + * @param psid Prefix-SID sub-TLV to be updated + */ void isis_sr_prefix_cfg2subtlv(const struct sr_prefix_cfg *pcfg, bool external, struct isis_prefix_sid *psid) { @@ -222,20 +291,30 @@ void isis_sr_prefix_cfg2subtlv(const struct sr_prefix_cfg *pcfg, bool external, } } -/*----------------------------------------------------------------------------*/ +/* --- Segment Routing Prefix Management functions -------------------------- */ -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) +/** + * Add Segment Routing Prefix to a given Segment Routing Node. + * + * @param area IS-IS area + * @param srn Segment Routing Node + * @param prefix Prefix to be added + * @param local True if prefix is locally configured, false otherwise + * @param psid Prefix-SID sub-TLVs + * + * @return New Segment Routing Prefix structure + */ +static struct sr_prefix *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; + srp->input_label = MPLS_INVALID_LABEL; if (local) { srp->type = ISIS_SR_PREFIX_LOCAL; isis_sr_nexthop_reset(&srp->u.local.info); @@ -244,45 +323,89 @@ static struct sr_prefix *isis_sr_prefix_add(struct isis_area *area, srp->u.remote.rinfo = NULL; } srp->srn = srn; - tree_sr_node_prefix_add(&srn->prefix_sids, srp); + srdb_node_prefix_add(&srn->prefix_sids, srp); /* TODO: this might fail if we have Anycast SIDs in the IS-IS area. */ - tree_sr_area_prefix_add(&area->srdb.prefix_sids[srn->level - 1], srp); + srdb_area_prefix_add(&area->srdb.prefix_sids[srn->level - 1], srp); + + sr_debug(" |- Added new SR Prefix-SID %pFX %s %u to SR Node %s", + &srp->prefix, IS_SID_VALUE(srp->sid.flags) ? "label" : "index", + srp->sid.value, sysid_print(srn->sysid)); return srp; } -static void isis_sr_prefix_del(struct isis_area *area, struct sr_node *srn, - struct sr_prefix *srp) +/** + * Remove given Segment Prefix from given Segment Routing Node. + * Prefix-SID is un-installed first. + * + * @param area IS-IS area + * @param srn Segment Routing Node + * @param srp Segment Routing Prefix + */ +static void sr_prefix_del(struct isis_area *area, struct sr_node *srn, + struct sr_prefix *srp) { - isis_sr_prefix_uninstall(srp); - tree_sr_node_prefix_del(&srn->prefix_sids, srp); - tree_sr_area_prefix_del(&area->srdb.prefix_sids[srn->level - 1], srp); + sr_debug(" |- Delete SR Prefix-SID %pFX %s %u to SR Node %s", + &srp->prefix, IS_SID_VALUE(srp->sid.flags) ? "label" : "index", + srp->sid.value, sysid_print(srn->sysid)); + + sr_prefix_uninstall(srp); + srdb_node_prefix_del(&srn->prefix_sids, srp); + srdb_area_prefix_del(&area->srdb.prefix_sids[srn->level - 1], srp); XFREE(MTYPE_ISIS_SR_INFO, srp); } -static struct sr_prefix *isis_sr_prefix_find_area(struct isis_area *area, - int level, - union prefixconstptr prefix) +/** + * Find Segment Routing Prefix by Area. + * + * @param area IS-IS area + * @param level IS-IS level + * @param prefix Prefix to lookup + * + * @return Segment Routing Prefix structure if found, NULL otherwise + */ +static struct sr_prefix *sr_prefix_find_by_area(struct isis_area *area, + int level, + union prefixconstptr prefix) { struct sr_prefix srp = {}; prefix_copy(&srp.prefix, prefix.p); - return tree_sr_area_prefix_find(&area->srdb.prefix_sids[level - 1], - &srp); + return srdb_area_prefix_find(&area->srdb.prefix_sids[level - 1], &srp); } -static struct sr_prefix *isis_sr_prefix_find_node(struct sr_node *srn, - union prefixconstptr prefix) +/** + * Find Segment Routing Prefix by Segment Routing Node. + * + * @param srn Segment Routing Node + * @param prefix Prefix to lookup + * + * @return Segment Routing Prefix structure if found, NULL otherwise + */ +static struct sr_prefix *sr_prefix_find_by_node(struct sr_node *srn, + union prefixconstptr prefix) { struct sr_prefix srp = {}; prefix_copy(&srp.prefix, prefix.p); - return tree_sr_node_prefix_find(&srn->prefix_sids, &srp); + return srdb_node_prefix_find(&srn->prefix_sids, &srp); } -static struct sr_node *isis_sr_node_add(struct isis_area *area, int level, - const uint8_t *sysid, - const struct isis_router_cap *cap) +/* --- Segment Routing Node Management functions ---------------------------- */ + +/** + * Add Segment Routing Node to the Segment Routing Data Base. + * + * @param area IS-IS area + * @param level IS-IS level + * @param sysid Node System ID + * @param cap Segment Routing Capability sub-TLVs + * + * @return New Segment Routing Node structure + */ +static struct sr_node *sr_node_add(struct isis_area *area, int level, + const uint8_t *sysid, + const struct isis_router_cap *cap) { struct sr_node *srn; @@ -291,43 +414,74 @@ static struct sr_node *isis_sr_node_add(struct isis_area *area, int level, memcpy(srn->sysid, sysid, ISIS_SYS_ID_LEN); srn->cap = *cap; srn->area = area; - tree_sr_node_prefix_init(&srn->prefix_sids); - tree_sr_node_add(&area->srdb.sr_nodes[level - 1], srn); + srdb_node_prefix_init(&srn->prefix_sids); + srdb_node_add(&area->srdb.sr_nodes[level - 1], srn); + + sr_debug(" |- Added new SR Node %s", sysid_print(srn->sysid)); return srn; } -static void isis_sr_node_del(struct isis_area *area, int level, - struct sr_node *srn) +static void sr_node_del(struct isis_area *area, int level, struct sr_node *srn) +/** + * Remove Segment Routing Node from the Segment Routing Data Base. + * All Prefix-SID attached to this Segment Routing Node are removed first. + * + * @param area IS-IS area + * @param level IS-IS level + * @param srn Segment Routing Node to be deleted + */ { + + sr_debug(" |- Delete SR Node %s", sysid_print(srn->sysid)); + /* Remove and uninstall Prefix-SIDs. */ - while (tree_sr_node_prefix_count(&srn->prefix_sids) > 0) { + while (srdb_node_prefix_count(&srn->prefix_sids) > 0) { struct sr_prefix *srp; - srp = tree_sr_node_prefix_first(&srn->prefix_sids); - isis_sr_prefix_del(area, srn, srp); + srp = srdb_node_prefix_first(&srn->prefix_sids); + sr_prefix_del(area, srn, srp); } - tree_sr_node_del(&area->srdb.sr_nodes[level - 1], srn); + srdb_node_del(&area->srdb.sr_nodes[level - 1], srn); XFREE(MTYPE_ISIS_SR_INFO, srn); } -static struct sr_node *isis_sr_node_find(struct isis_area *area, int level, - const uint8_t *sysid) +/** + * Find Segment Routing Node in the Segment Routing Data Base per system ID. + * + * @param area IS-IS area + * @param level IS-IS level + * @param sysid Node System ID to lookup + * + * @return Segment Routing Node structure if found, NULL otherwise + */ +static struct sr_node *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); - return tree_sr_node_find(&area->srdb.sr_nodes[level - 1], &srn); + return srdb_node_find(&area->srdb.sr_nodes[level - 1], &srn); } -static void isis_sr_adj_srgb_update(struct isis_area *area, uint8_t *sysid, - int level) +/** + * Update Segment Routing Node following an SRGB update. This function + * is called when a neighbor SR Node has updated its SRGB. + * + * @param area IS-IS area + * @param level IS-IS level + * @param sysid Segment Routing Node system ID + */ +static void sr_node_srgb_update(struct isis_area *area, int level, + uint8_t *sysid) { struct sr_prefix *srp; - frr_each (tree_sr_area_prefix, &area->srdb.prefix_sids[level - 1], - srp) { + sr_debug("ISIS-Sr (%s): Update neighbors SR Node with new SRGB", + area->area_tag); + + frr_each (srdb_area_prefix, &area->srdb.prefix_sids[level - 1], srp) { struct listnode *node; struct isis_nexthop *nh; @@ -343,15 +497,25 @@ static void isis_sr_adj_srgb_update(struct isis_area *area, uint8_t *sysid, continue; /* - * Reinstall all Prefix-SID nexthops using route replace - * semantics. + * The Prefix-SID input label hasn't changed. We could + * re-install all Prefix-SID with "Make Before Break" + * option. Zebra layer will update output label(s) by + * adding new entry before removing the old one(s). */ - isis_sr_prefix_reinstall(srp, true); + sr_prefix_reinstall(srp, true); break; } } } +/* --- Segment Routing Nexthop information Management functions ------------- */ + +/** + * Update Segment Routing Nexthop. + * + * @param srnh Segment Routing next hop + * @param label Output MPLS label + */ void isis_sr_nexthop_update(struct sr_nexthop_info *srnh, mpls_label_t label) { srnh->label = label; @@ -359,18 +523,31 @@ void isis_sr_nexthop_update(struct sr_nexthop_info *srnh, mpls_label_t label) srnh->uptime = time(NULL); } +/** + * Reset Segment Routing Nexthop. + * + * @param srnh Segment Routing Nexthop + */ void isis_sr_nexthop_reset(struct sr_nexthop_info *srnh) { srnh->label = MPLS_INVALID_LABEL; srnh->uptime = 0; } -/*----------------------------------------------------------------------------*/ +/* --- Segment Routing Prefix-SID Management functions to configure LFIB ---- */ -/* 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) +/** + * Lookup IS-IS route in the Shortest Path Tree. + * + * @param area IS-IS area + * @param tree_id Shortest Path Tree identifier + * @param srp Segment Routing Prefix to lookup + * + * @return Route Information for this prefix if found, NULL otherwise + */ +static struct isis_route_info *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; @@ -386,18 +563,24 @@ isis_sr_prefix_lookup_route(struct isis_area *area, enum spf_tree_id tree_id, return NULL; } -/* Calculate Prefix-SID input label. */ -static mpls_label_t isis_sr_prefix_in_label(const struct sr_prefix *srp) +/** + * Compute input label for the given Prefix-SID. + * + * @param srp Segment Routing Prefix + * + * @return MPLS label or MPLS_INVALID_LABEL in case of SRGB overflow + */ +static mpls_label_t 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. */ + /* Return SID value as MPLS label if it is an Absolute SID */ if (CHECK_FLAG(srp->sid.flags, ISIS_PREFIX_SID_VALUE | ISIS_PREFIX_SID_LOCAL)) return srp->sid.value; - /* Index SID value. */ + /* Check that SID index falls inside the SRGB */ if (srp->sid.value >= (area->srdb.config.srgb_upper_bound - area->srdb.config.srgb_lower_bound + 1)) { flog_warn(EC_ISIS_SID_OVERFLOW, @@ -406,21 +589,32 @@ static mpls_label_t isis_sr_prefix_in_label(const struct sr_prefix *srp) return MPLS_INVALID_LABEL; } + /* Return MPLS label as SID index + SRGB_lower_bound as per RFC 8667 */ 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) +/** + * Compute output label for the given Prefix-SID. + * + * @param srp Segment Routing Prefix + * @param srn_nexthop Segment Routing nexthop node + * @param sysid System ID of the SR node which advertised the Prefix-SID + * + * @return MPLS label or MPLS_INVALID_LABEL in case of error + */ +static mpls_label_t 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? */ + /* Check if the nexthop SR Node is the last hop? */ if (memcmp(sysid, srn->sysid, ISIS_SYS_ID_LEN) == 0) { + /* SR-Node doesn't request NO-PHP. Return Implicit NULL label */ if (!CHECK_FLAG(srp->sid.flags, ISIS_PREFIX_SID_NO_PHP)) return MPLS_LABEL_IMPLICIT_NULL; + /* SR-Node requests Implicit NULL Label */ if (CHECK_FLAG(srp->sid.flags, ISIS_PREFIX_SID_EXPLICIT_NULL)) { if (srp->prefix.family == AF_INET) return MPLS_LABEL_IPV4_EXPLICIT_NULL; @@ -430,19 +624,19 @@ static mpls_label_t isis_sr_prefix_out_label(const struct sr_prefix *srp, /* Fallthrough */ } - /* Absolute SID value. */ + /* Return SID value as MPLS label if it is an Absolute SID */ 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. + * can use them (RFC8667 section #2.1.1.1) */ if (srp->srn != srn_nexthop) return MPLS_INVALID_LABEL; return srp->sid.value; } - /* Index SID value. */ + /* Check that SID index falls inside the SRGB */ 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", @@ -450,88 +644,92 @@ static mpls_label_t isis_sr_prefix_out_label(const struct sr_prefix *srp, return MPLS_INVALID_LABEL; } + /* Return MPLS label as SID index + SRGB_lower_bound as per RFC 8667 */ 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) +/** + * Process local Prefix-SID and install it if possible. Input label is + * computed before installing it in LFIB. + * + * @param srp Segment Routing Prefix + * + * @return 0 on success, -1 otherwise + */ +static int sr_prefix_install_local(struct sr_prefix *srp) { + mpls_label_t input_label; 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 need to install Label for 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"); - } + sr_debug(" |- Installing Prefix-SID %pFX %s %u (%s) with nexthop self", + &srp->prefix, IS_SID_VALUE(srp->sid.flags) ? "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) + /* Compute input label and check that is valid. */ + input_label = sr_prefix_in_label(srp); + if (input_label == MPLS_INVALID_LABEL) return -1; /* Update internal state. */ - srp->local_label = local_label; + srp->input_label = input_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); + isis_zebra_send_prefix_sid(ZEBRA_MPLS_LABELS_REPLACE, srp); return 0; } -/* Process remote Prefix-SID and install it if possible. */ -static int isis_sr_prefix_install_remote(struct sr_prefix *srp) +/** + * Process remote Prefix-SID and install it if possible. Input and Output + * labels are computed before installing them in LFIB. + * + * @param srp Segment Routing Prefix + * + * @return 0 on success, -1 otherwise + */ +static int 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; + mpls_label_t input_label; size_t nexthop_num = 0; - /* Lookup associated IS-IS route. */ + /* Lookup to 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); + srp->u.remote.rinfo = 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) + /* Compute input label and check that is valid. */ + input_label = sr_prefix_in_label(srp); + if (input_label == MPLS_INVALID_LABEL) return -1; + sr_debug(" |- Installing Prefix-SID %pFX %s %u (%s)", &srp->prefix, + IS_SID_VALUE(srp->sid.flags) ? "label" : "index", + srp->sid.value, circuit_t2string(srn->level)); + + /* Process all SPF nexthops */ for (ALL_LIST_ELEMENTS_RO(srp->u.remote.rinfo->nexthops, node, nexthop)) { struct sr_node *srn_nexthop; - mpls_label_t remote_label; + mpls_label_t output_label; /* Check if the nexthop advertised a SRGB. */ - srn_nexthop = - isis_sr_node_find(area, srn->level, nexthop->sysid); + srn_nexthop = sr_node_find(area, srn->level, nexthop->sysid); if (!srn_nexthop) goto next; @@ -545,9 +743,10 @@ static int isis_sr_prefix_install_remote(struct sr_prefix *srp) && !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) + /* Compute output label and check if it is valid */ + output_label = + sr_prefix_out_label(srp, srn_nexthop, nexthop->sysid); + if (output_label == MPLS_INVALID_LABEL) goto next; if (IS_DEBUG_ISIS(DEBUG_SR)) { @@ -555,51 +754,61 @@ static int isis_sr_prefix_install_remote(struct sr_prefix *srp) inet_ntop(nexthop->family, &nexthop->ip, buf, sizeof(buf)); - zlog_debug(" nexthop %s label %u", buf, remote_label); + zlog_debug(" |- nexthop %s label %u", buf, + output_label); } - isis_sr_nexthop_update(&nexthop->sr, remote_label); + isis_sr_nexthop_update(&nexthop->sr, output_label); nexthop_num++; continue; next: isis_sr_nexthop_reset(&nexthop->sr); } + + /* Check that we found at least one valid nexthop */ if (nexthop_num == 0) { - if (IS_DEBUG_ISIS(DEBUG_SR)) - zlog_debug(" no valid nexthops"); + sr_debug(" |- no valid nexthops"); return -1; } /* Update internal state. */ - srp->local_label = local_label; + srp->input_label = input_label; /* Install Prefix-SID in the forwarding plane. */ - isis_zebra_install_prefix_sid(srp); + isis_zebra_send_prefix_sid(ZEBRA_MPLS_LABELS_REPLACE, srp); return 0; } -/* Process local or remote Prefix-SID and install it if possible. */ -static void isis_sr_prefix_install(struct sr_prefix *srp) +/** + * Process local or remote Prefix-SID and install it if possible. + * + * @param srp Segment Routing Prefix + */ +static void sr_prefix_install(struct sr_prefix *srp) { const struct sr_node *srn = srp->srn; struct isis_area *area = srn->area; int ret; + sr_debug("ISIS-Sr (%s): Install Prefix-SID %pFX %s %u", area->area_tag, + &srp->prefix, IS_SID_VALUE(srp->sid.flags) ? "label" : "index", + srp->sid.value); + /* 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); + srp_l2 = sr_prefix_find_by_area(area, ISIS_LEVEL2, + &srp->prefix); if (srp_l2) - isis_sr_prefix_uninstall(srp_l2); + sr_prefix_uninstall(srp_l2); break; case ISIS_LEVEL2: - srp_l1 = isis_sr_prefix_find_area(area, ISIS_LEVEL1, - &srp->prefix); + srp_l1 = sr_prefix_find_by_area(area, ISIS_LEVEL1, + &srp->prefix); if (srp_l1) return; break; @@ -608,39 +817,38 @@ static void isis_sr_prefix_install(struct sr_prefix *srp) } } + /* Install corresponding LFIB entry */ if (srp->type == ISIS_SR_PREFIX_LOCAL) - ret = isis_sr_prefix_install_local(srp); + ret = sr_prefix_install_local(srp); else - ret = isis_sr_prefix_install_remote(srp); + ret = sr_prefix_install_remote(srp); if (ret != 0) - isis_sr_prefix_uninstall(srp); + sr_prefix_uninstall(srp); } -/* Uninstall local or remote Prefix-SID. */ -static void isis_sr_prefix_uninstall(struct sr_prefix *srp) +/** + * Uninstall local or remote Prefix-SID. + * + * @param srp Segment Routing Prefix + */ +static void 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) + /* Check that Input Label is valid */ + if (srp->input_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)); - + sr_debug("ISIS-Sr: Un-install Prefix-SID %pFX %s %u", &srp->prefix, + IS_SID_VALUE(srp->sid.flags) ? "label" : "index", + srp->sid.value); /* Uninstall Prefix-SID from the forwarding plane. */ - isis_zebra_uninstall_prefix_sid(srp); + isis_zebra_send_prefix_sid(ZEBRA_MPLS_LABELS_DELETE, srp); /* Reset internal state. */ - srp->local_label = MPLS_INVALID_LABEL; + srp->input_label = MPLS_INVALID_LABEL; switch (srp->type) { case ISIS_SR_PREFIX_LOCAL: isis_sr_nexthop_reset(&srp->u.local.info); @@ -655,81 +863,114 @@ static void isis_sr_prefix_uninstall(struct sr_prefix *srp) } } -/* Reinstall local or remote Prefix-SID. */ -static void isis_sr_prefix_reinstall(struct sr_prefix *srp, - bool replace_semantics) +/** + * Reinstall local or remote Prefix-SID. + * + * @param srp Segment Routing Prefix + */ +static inline void sr_prefix_reinstall(struct sr_prefix *srp, + bool make_before_break) { /* - * Route replace semantics can be used only when we know for sure that + * Make Before Break 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); + if (!make_before_break) + sr_prefix_uninstall(srp); - isis_sr_prefix_install(srp); + /* New input label is computed in sr_prefix_install() function */ + sr_prefix_install(srp); } -/*----------------------------------------------------------------------------*/ +/* --- IS-IS LSP Parse functions -------------------------------------------- */ -/* Parse all SR-related information from the given Router Capabilities TLV. */ +/** + * Parse all SR-related information from the given Router Capabilities TLV. + * + * @param area IS-IS area + * @param level IS-IS level + * @param sysid System ID of the LSP + * @param router_cap Router Capability subTLVs + * + * @return Segment Routing Node structure for this System ID + */ 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) +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); + sr_debug("ISIS-Sr (%s): Parse Router Capability TLV", area->area_tag); + + srn = sr_node_find(area, level, sysid); if (srn) { if (memcmp(&srn->cap, router_cap, sizeof(srn->cap)) != 0) { srn->cap = *router_cap; - SET_FLAG(srn->parse_flags, F_ISIS_SR_NODE_MODIFIED); + srn->state = SRDB_STATE_MODIFIED; } else - SET_FLAG(srn->parse_flags, F_ISIS_SR_NODE_UNCHANGED); + srn->state = SRDB_STATE_UNCHANGED; + sr_debug(" |- Found %s SR Node %s", + srn->state == SRDB_STATE_MODIFIED ? "Modified" + : "Unchanged", + sysid_print(srn->sysid)); } else { - srn = isis_sr_node_add(area, level, sysid, router_cap); - SET_FLAG(srn->parse_flags, F_ISIS_SR_NODE_NEW); + srn = sr_node_add(area, level, sysid, router_cap); + srn->state = SRDB_STATE_NEW; } 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) +/** + * Parse list of Prefix-SID Sub-TLVs. + * + * @param srn Segment Routing Node + * @param prefix Prefix to be parsed + * @param local True if prefix comes from own LSP, false otherwise + * @param prefix_sids Prefix SID subTLVs + */ +static void 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; + sr_debug("ISIS-Sr (%s): Parse Prefix SID TLV", area->area_tag); + + /* Parse list of Prefix SID subTLVs */ for (i = prefix_sids->head; i; i = i->next) { struct isis_prefix_sid *psid = (struct isis_prefix_sid *)i; struct sr_prefix *srp; + /* Only SPF algorithm is supported right now */ if (psid->algorithm != SR_ALGORITHM_SPF) continue; - srp = isis_sr_prefix_find_node(srn, prefix); + /* Compute corresponding Segment Routing Prefix */ + srp = sr_prefix_find_by_node(srn, prefix); if (srp) { if (srp->sid.flags != psid->flags || srp->sid.algorithm != psid->algorithm || srp->sid.value != psid->value) { srp->sid = *psid; - SET_FLAG(srp->parse_flags, - F_ISIS_SR_PREFIX_SID_MODIFIED); + srp->state = SRDB_STATE_MODIFIED; } else - SET_FLAG(srp->parse_flags, - F_ISIS_SR_PREFIX_SID_UNCHANGED); + srp->state = SRDB_STATE_UNCHANGED; + sr_debug(" |- Found %s Prefix-SID %pFX", + srp->state == SRDB_STATE_MODIFIED + ? "Modified" + : "Unchanged", + &srp->prefix); + } else { - srp = isis_sr_prefix_add(area, srn, prefix, local, - psid); - SET_FLAG(srp->parse_flags, F_ISIS_SR_PREFIX_SID_NEW); + srp = sr_prefix_add(area, srn, prefix, local, psid); + srp->state = SRDB_STATE_NEW; } /* * Stop the Prefix-SID iteration since we only support the SPF @@ -739,23 +980,34 @@ static void isis_sr_parse_prefix_sid_subtlvs(struct sr_node *srn, } } -/* 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) +/** + * Parse all SR-related information from the given LSP. + * + * @param area IS-IS area + * @param level IS-IS level + * @param srn Segment Routing Node + * @param lsp IS-IS LSP + */ +static void 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; + /* Check LSP sequence number */ if (lsp->hdr.seqno == 0) { zlog_warn("%s: lsp with 0 seq_num - ignore", __func__); return; } + sr_debug("ISIS-Sr (%s): Parse LSP from node %s", area->area_tag, + sysid_print(lsp->hdr.lsp_id)); + /* 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); + *srn = parse_router_cap_tlv(area, level, lsp->hdr.lsp_id, + lsp->tlvs->router_cap); if (!*srn) return; } @@ -769,8 +1021,8 @@ static void isis_sr_parse_lsp(struct isis_area *area, int level, if (!ir->subtlvs) continue; - isis_sr_parse_prefix_sid_subtlvs(*srn, &ir->prefix, local, - &ir->subtlvs->prefix_sids); + parse_prefix_sid_subtlvs(*srn, &ir->prefix, local, + &ir->subtlvs->prefix_sids); } /* Parse Multi Topology Reachable IPv6 Prefixes TLV. */ @@ -783,78 +1035,88 @@ static void isis_sr_parse_lsp(struct isis_area *area, int level, if (!ir->subtlvs) continue; - isis_sr_parse_prefix_sid_subtlvs(*srn, &ir->prefix, local, - &ir->subtlvs->prefix_sids); + 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) +/** + * Parse all SR-related information from the entire LSPDB. + * + * @param area IS-IS area + */ +static void parse_lspdb(struct isis_area *area) { struct isis_lsp *lsp; + sr_debug("ISIS-Sr (%s): Parse LSP Data Base", area->area_tag); + + /* Process all LSP from Level 1 & 2 */ 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; + /* Skip Pseudo ID LSP and LSP without TLVs */ if (LSP_PSEUDO_ID(lsp->hdr.lsp_id)) continue; if (!lsp->tlvs) continue; - isis_sr_parse_lsp(area, level, &srn, lsp); + /* Parse LSP, then fragment */ + parse_lsp(area, level, &srn, lsp); for (ALL_LIST_ELEMENTS_RO(lsp->lspu.frags, node, frag)) - isis_sr_parse_lsp(area, level, &srn, frag); + 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) +/** + * Process any new/deleted/modified Prefix-SID in the LSPDB. + * + * @param srn Segment Routing Node + * @param srp Segment Routing Prefix + */ +static void 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)) { - if (CHECK_FLAG(srp->parse_flags, F_ISIS_SR_PREFIX_SID_NEW)) - zlog_debug( - "ISIS-SR (%s) Prefix-SID created: %pFX (sysid %s)", - area->area_tag, &srp->prefix, - sysid_print(srn->sysid)); - else if (CHECK_FLAG(srp->parse_flags, - F_ISIS_SR_PREFIX_SID_MODIFIED)) - zlog_debug( - "ISIS-SR (%s) Prefix-SID modified: %pFX (sysid %s)", - area->area_tag, &srp->prefix, - sysid_print(srn->sysid)); - else if (!CHECK_FLAG(srp->parse_flags, - F_ISIS_SR_PREFIX_SID_UNCHANGED)) - 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. */ - if (CHECK_FLAG(srp->parse_flags, F_ISIS_SR_PREFIX_SID_NEW)) - isis_sr_prefix_install(srp); - else if (CHECK_FLAG(srp->parse_flags, F_ISIS_SR_PREFIX_SID_MODIFIED)) - isis_sr_prefix_reinstall(srp, false); - else if (!CHECK_FLAG(srp->parse_flags, - F_ISIS_SR_PREFIX_SID_UNCHANGED)) { - isis_sr_prefix_del(area, srn, srp); + switch (srp->state) { + case SRDB_STATE_NEW: + sr_debug("ISIS-Sr (%s): Created Prefix-SID %pFX for SR node %s", + area->area_tag, &srp->prefix, sysid_print(srn->sysid)); + sr_prefix_install(srp); + break; + case SRDB_STATE_MODIFIED: + sr_debug( + "ISIS-Sr (%s): Modified Prefix-SID %pFX for SR node %s", + area->area_tag, &srp->prefix, sysid_print(srn->sysid)); + sr_prefix_reinstall(srp, false); + break; + case SRDB_STATE_UNCHANGED: + break; + default: + sr_debug("ISIS-Sr (%s): Removed Prefix-SID %pFX for SR node %s", + area->area_tag, &srp->prefix, sysid_print(srn->sysid)); + sr_prefix_del(area, srn, srp); return; } - srp->parse_flags = 0; + /* Validate SRDB State for next LSPDB parsing */ + srp->state = SRDB_STATE_VALIDATED; } -/* 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) +/** + * Process any new/deleted/modified SRGB in the LSPDB. + * + * @param area IS-IS area + * @param level IS-IS level + * @param srn Segment Routing Node + */ +static void process_node_changes(struct isis_area *area, int level, + struct sr_node *srn) { struct sr_prefix *srp; uint8_t sysid[ISIS_SYS_ID_LEN]; @@ -862,44 +1124,46 @@ static void isis_sr_process_node_changes(struct isis_area *area, int level, memcpy(sysid, srn->sysid, sizeof(sysid)); - /* Log any SRGB change in the LSPDB. */ - if (IS_DEBUG_ISIS(DEBUG_SR)) { - if (CHECK_FLAG(srn->parse_flags, F_ISIS_SR_NODE_NEW)) - zlog_debug("ISIS-SR (%s) SRGB created (sysid %s)", - area->area_tag, sysid_print(sysid)); - else if (CHECK_FLAG(srn->parse_flags, F_ISIS_SR_NODE_MODIFIED)) - zlog_debug("ISIS-SR (%s) SRGB modified (sysid %s)", - area->area_tag, sysid_print(sysid)); - else if (!CHECK_FLAG(srn->parse_flags, - F_ISIS_SR_NODE_UNCHANGED)) - 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. + * If an neighbor router's SRGB was changed or created, then reinstall + * all Prefix-SIDs from all nodes that use this neighbor as nexthop. */ adjacent = isis_adj_exists(area, level, sysid); - if (CHECK_FLAG(srn->parse_flags, - F_ISIS_SR_NODE_NEW | F_ISIS_SR_NODE_MODIFIED)) { + switch (srn->state) { + case SRDB_STATE_NEW: + case SRDB_STATE_MODIFIED: + sr_debug("ISIS-Sr (%s): Create/Update SR node %s", + area->area_tag, sysid_print(srn->sysid)); if (adjacent) - isis_sr_adj_srgb_update(area, sysid, level); - } else if (!CHECK_FLAG(srn->parse_flags, F_ISIS_SR_NODE_UNCHANGED)) { - isis_sr_node_del(area, level, srn); + sr_node_srgb_update(area, level, sysid); + break; + case SRDB_STATE_UNCHANGED: + break; + default: + /* SR capabilities have been removed. Delete SR-Node */ + sr_debug("ISIS-Sr (%s): Remove SR node %s", area->area_tag, + sysid_print(srn->sysid)); + sr_node_del(area, level, srn); + /* and Update remaining Prefix-SID from all remaining SR Node */ if (adjacent) - isis_sr_adj_srgb_update(area, sysid, level); + sr_node_srgb_update(area, level, sysid); return; } - srn->parse_flags = 0; + /* Validate SRDB State for next LSPDB parsing */ + srn->state = SRDB_STATE_VALIDATED; - frr_each_safe (tree_sr_node_prefix, &srn->prefix_sids, srp) - isis_sr_process_prefix_changes(srn, srp); + /* Finally, process all Prefix-SID of this SR Node */ + frr_each_safe (srdb_node_prefix, &srn->prefix_sids, srp) + process_prefix_changes(srn, srp); } -/* Parse and process all SR-related Sub-TLVs after running the SPF algorithm. */ +/** + * Parse and process all SR-related Sub-TLVs after running the SPF algorithm. + * + * @param area IS-IS area + */ void isis_area_verify_sr(struct isis_area *area) { struct sr_node *srn; @@ -908,40 +1172,48 @@ void isis_area_verify_sr(struct isis_area *area) return; /* Parse LSPDB to detect new/deleted/modified SR (sub-)TLVs. */ - isis_sr_parse_lspdb(area); + parse_lspdb(area); /* Process possible SR-related changes in the LDPSB. */ for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) { - frr_each_safe (tree_sr_node, &area->srdb.sr_nodes[level - 1], - srn) - isis_sr_process_node_changes(area, level, srn); + frr_each_safe (srdb_node, &area->srdb.sr_nodes[level - 1], srn) + process_node_changes(area, level, srn); } } -/* +/** * Once a route is updated in the SPT, reinstall or uninstall its corresponding * Prefix-SID (if any). + * + * @param area IS-IS area + * @param prefix Prefix to be updated + * @param route_info New Route Information + * + * @return 0 */ -static int isis_sr_route_update(struct isis_area *area, struct prefix *prefix, - struct isis_route_info *route_info) +static int 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; + sr_debug("ISIS-Sr (%s): Update route for prefix %pFX", area->area_tag, + prefix); + + /* Lookup to Segment Routing Prefix for this prefix */ switch (area->is_type) { case IS_LEVEL_1: - srp = isis_sr_prefix_find_area(area, ISIS_LEVEL1, prefix); + srp = sr_prefix_find_by_area(area, ISIS_LEVEL1, prefix); break; case IS_LEVEL_2: - srp = isis_sr_prefix_find_area(area, ISIS_LEVEL2, prefix); + srp = sr_prefix_find_by_area(area, ISIS_LEVEL2, prefix); break; case IS_LEVEL_1_AND_2: - srp = isis_sr_prefix_find_area(area, ISIS_LEVEL1, prefix); + srp = sr_prefix_find_by_area(area, ISIS_LEVEL1, prefix); if (!srp) - srp = isis_sr_prefix_find_area(area, ISIS_LEVEL2, - prefix); + srp = sr_prefix_find_by_area(area, ISIS_LEVEL2, prefix); break; default: flog_err(EC_LIB_DEVELOPMENT, "%s: unknown area level", @@ -949,51 +1221,38 @@ static int isis_sr_route_update(struct isis_area *area, struct prefix *prefix, exit(1); } + /* Skip NULL or local Segment Routing Prefix */ if (!srp || srp->type == ISIS_SR_PREFIX_LOCAL) return 0; + /* Install or unintall Prefix-SID if route is Active or not */ if (CHECK_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ACTIVE)) { - isis_sr_prefix_reinstall(srp, true); + /* + * The Prefix-SID input label hasn't changed. We could use the + * "Make Before Break" option. Zebra layer will update output + * label by adding new label(s) before removing old one(s). + */ + sr_prefix_reinstall(srp, true); srp->u.remote.rinfo = route_info; } else { - isis_sr_prefix_uninstall(srp); + sr_prefix_uninstall(srp); srp->u.remote.rinfo = NULL; } return 0; } -/*----------------------------------------------------------------------------*/ +/* --- Segment Routing Adjacency-SID management functions ------------------- */ -/* 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; - struct zapi_nexthop *znh; - 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]; - znh->gate = sra->nexthop.address; - znh->type = (sra->nexthop.family == AF_INET) - ? NEXTHOP_TYPE_IPV4_IFINDEX - : NEXTHOP_TYPE_IPV6_IFINDEX; - znh->ifindex = sra->adj->circuit->interface->ifindex; - znh->label_num = 1; - znh->labels[0] = MPLS_LABEL_IMPLICIT_NULL; - - (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) +/** + * Add new local Adjacency-SID. + * + * @param adj IS-IS Adjacency + * @param family Inet Family (IPv4 or IPv6) + * @param backup True to initialize backup Adjacency SID + */ +static void 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; @@ -1002,8 +1261,12 @@ static void isis_sr_adj_sid_add_single(struct isis_adjacency *adj, int family, struct isis_lan_adj_sid *ladj_sid; union g_addr nexthop = {}; uint8_t flags; - mpls_label_t local_label; + mpls_label_t input_label; + sr_debug("ISIS-Sr (%s): Add %s Adjacency SID", area->area_tag, + backup ? "Backup" : "Primary"); + + /* Determine nexthop IP address */ switch (family) { case AF_INET: if (!circuit->ip_router) @@ -1023,31 +1286,24 @@ static void isis_sr_adj_sid_add_single(struct isis_adjacency *adj, int family, exit(1); } + /* Prepare Segment Routing Adjacency as per RFC8667 section #2.2 */ 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(); + input_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; + sra->nexthop.label = input_label; switch (circuit->circ_type) { + /* LAN Adjacency-SID for Broadcast interface section #2.2.2 */ case CIRCUIT_T_BROADCAST: ladj_sid = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*ladj_sid)); ladj_sid->family = family; @@ -1055,16 +1311,17 @@ static void isis_sr_adj_sid_add_single(struct isis_adjacency *adj, int family, ladj_sid->weight = 0; memcpy(ladj_sid->neighbor_id, adj->sysid, sizeof(ladj_sid->neighbor_id)); - ladj_sid->sid = local_label; + ladj_sid->sid = input_label; isis_tlvs_add_lan_adj_sid(circuit->ext, ladj_sid); sra->u.ladj_sid = ladj_sid; break; + /* Adjacency-SID for Point to Point interface section #2.2.1 */ 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; + adj_sid->sid = input_label; isis_tlvs_add_adj_sid(circuit->ext, adj_sid); sra->u.adj_sid = adj_sid; break; @@ -1073,36 +1330,42 @@ static void isis_sr_adj_sid_add_single(struct isis_adjacency *adj, int family, __func__, circuit->circ_type); exit(1); } + + /* Add Adjacency-SID in SRDB */ sra->adj = adj; listnode_add(area->srdb.adj_sids, sra); listnode_add(adj->adj_sids, sra); - isis_sr_adj_sid_install_uninstall(true, sra); + isis_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_ADD, sra); } -static void isis_sr_adj_sid_add(struct isis_adjacency *adj, int family) +/** + * Add Primary and Backup local Adjacency SID. + * + * @param adj IS-IS Adjacency + * @param family Inet Family (IPv4 or IPv6) + */ +static void 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); + sr_adj_sid_add_single(adj, family, false); + sr_adj_sid_add_single(adj, family, true); } -/* Delete local Adj-SID. */ -static void isis_sr_adj_sid_del(struct sr_adjacency *sra) +/** + * Delete local Adj-SID. + * + * @param sra Segment Routing Adjacency + */ +static void 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]; + sr_debug("ISIS-Sr (%s): Delete Adjacency SID", area->area_tag); - 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); + isis_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_DELETE, sra); + /* Release dynamic label and remove subTLVs */ switch (circuit->circ_type) { case CIRCUIT_T_BROADCAST: isis_zebra_release_dynamic_label(sra->u.ladj_sid->sid); @@ -1118,13 +1381,20 @@ static void isis_sr_adj_sid_del(struct sr_adjacency *sra) exit(1); } + /* Remove Adjacency-SID from the SRDB */ 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) +/** + * Remove all Adjacency-SIDs associated to an adjacency that is going down. + * + * @param adj IS-IS Adjacency + * + * @return 0 + */ +static int sr_adj_state_change(struct isis_adjacency *adj) { struct sr_adjacency *sra; struct listnode *node, *nnode; @@ -1136,30 +1406,40 @@ static int isis_sr_adj_state_change(struct isis_adjacency *adj) return 0; for (ALL_LIST_ELEMENTS(adj->adj_sids, node, nnode, sra)) - isis_sr_adj_sid_del(sra); + sr_adj_sid_del(sra); return 0; } -/* - * Adjacency now has one or more IPv4/IPv6 addresses. Add new IPv4 or IPv6 - * Adj-SID accordingly. +/** + * When IS-IS Adjacency got one or more IPv4/IPv6 addresses, add new IPv4 or + * IPv6 address to corresponding Adjacency-SID accordingly. + * + * @param adj IS-IS Adjacency + * @param family Inet Family (IPv4 or IPv6) + * + * @return 0 */ -static int isis_sr_adj_ip_enabled(struct isis_adjacency *adj, int family) +static int 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); + 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. +/** + * When IS-IS Adjacency doesn't have any IPv4 or IPv6 addresses anymore, + * delete the corresponding Adjacency-SID(s) accordingly. + * + * @param adj IS-IS Adjacency + * @param family Inet Family (IPv4 or IPv6) + * + * @return 0 */ -static int isis_sr_adj_ip_disabled(struct isis_adjacency *adj, int family) +static int sr_adj_ip_disabled(struct isis_adjacency *adj, int family) { struct sr_adjacency *sra; struct listnode *node, *nnode; @@ -1169,18 +1449,26 @@ static int isis_sr_adj_ip_disabled(struct isis_adjacency *adj, int family) for (ALL_LIST_ELEMENTS(adj->adj_sids, node, nnode, sra)) if (sra->nexthop.family == family) - isis_sr_adj_sid_del(sra); + sr_adj_sid_del(sra); return 0; } -static int isis_sr_if_new_hook(struct interface *ifp) +/** + * Activate local Prefix-SID when loopback interface goes up for IS-IS. + * + * @param ifp Loopback Interface + * + * @return 0 + */ +static int sr_if_new_hook(struct interface *ifp) { struct isis_circuit *circuit; struct isis_area *area; struct connected *connected; struct listnode *node; + /* Get corresponding circuit */ circuit = circuit_scan_by_ifp(ifp); if (!circuit) return 0; @@ -1201,7 +1489,7 @@ static int isis_sr_if_new_hook(struct interface *ifp) if (!pcfg) continue; - if (isis_sr_prefix_is_node_sid(ifp, &pcfg->prefix) + if (sr_prefix_is_node_sid(ifp, &pcfg->prefix) && !pcfg->node_sid) { pcfg->node_sid = true; lsp_regenerate_schedule(area, area->is_type, 0); @@ -1211,58 +1499,113 @@ static int isis_sr_if_new_hook(struct interface *ifp) return 0; } -/*----------------------------------------------------------------------------*/ +/* --- Segment Routing Show information functions --------------------------- */ -static void isis_sr_show_prefix_sid_local(struct vty *vty, struct ttable *tt, - const struct isis_area *area, - const struct sr_prefix *srp) +/** + * Show LFIB operation in human readable format. + * + * @param buf Buffer to store string output. Must be pre-allocate + * @param size Size of the buffer + * @param label_in Input Label + * @param label_out Output Label + * + * @return String containing LFIB operation in human readable format + */ +static char *sr_op2str(char *buf, size_t size, mpls_label_t label_in, + mpls_label_t label_out) +{ + if (size < 24) + return NULL; + + if (label_in == MPLS_INVALID_LABEL) { + snprintf(buf, size, "no-op."); + return buf; + } + + switch (label_out) { + case MPLS_LABEL_IMPLICIT_NULL: + snprintf(buf, size, "Pop(%u)", label_in); + break; + case MPLS_LABEL_IPV4_EXPLICIT_NULL: + case MPLS_LABEL_IPV6_EXPLICIT_NULL: + snprintf(buf, size, "Swap(%u, null)", label_in); + break; + case MPLS_INVALID_LABEL: + snprintf(buf, size, "no-op."); + break; + default: + snprintf(buf, size, "Swap(%u, %u)", label_in, label_out); + break; + } + return buf; +} + +/** + * Show Local Prefix-SID. + * + * @param vty VTY output + * @param tt Table format + * @param area IS-IS area + * @param srp Segment Routing Prefix + */ +static void 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_oper[BUFSIZ]; + char buf_iface[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)); + struct interface *ifp; + ifp = if_lookup_prefix(&srp->prefix, VRF_DEFAULT); + if (ifp) + strlcpy(buf_iface, ifp->name, sizeof(buf_iface)); + else + snprintf(buf_iface, sizeof(buf_iface), "-"); log_uptime(srnh->uptime, buf_uptime, sizeof(buf_uptime)); } else { - snprintf(buf_rlabel, sizeof(buf_rlabel), "-"); + snprintf(buf_iface, sizeof(buf_iface), "-"); snprintf(buf_uptime, sizeof(buf_uptime), "-"); } + sr_op2str(buf_oper, sizeof(buf_oper), srp->input_label, + MPLS_LABEL_IMPLICIT_NULL); - ttable_add_row(tt, "%s|%u|%s|local|%s|%s", + ttable_add_row(tt, "%s|%u|%s|-|%s|%s", prefix2str(&srp->prefix, buf_prefix, sizeof(buf_prefix)), - srp->sid.value, buf_llabel, buf_rlabel, buf_uptime); + srp->sid.value, buf_oper, buf_iface, 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) +/** + * Show Remote Prefix-SID. + * + * @param vty VTY output + * @param tt Table format + * @param area IS-IS area + * @param srp Segment Routing Prefix + */ +static void 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_oper[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); + sr_op2str(buf_oper, sizeof(buf_oper), + srp->input_label, + MPLS_LABEL_IMPLICIT_NULL)); return; } @@ -1278,54 +1621,58 @@ static void isis_sr_show_prefix_sid_remote(struct vty *vty, struct ttable *tt, else snprintf(buf_iface, sizeof(buf_iface), "ifindex %u", nexthop->ifindex); - if (nexthop->sr.label == MPLS_INVALID_LABEL) { - snprintf(buf_rlabel, sizeof(buf_rlabel), "-"); + if (nexthop->sr.label == MPLS_INVALID_LABEL) snprintf(buf_uptime, sizeof(buf_uptime), "-"); - } else { - label2str(nexthop->sr.label, buf_rlabel, - sizeof(buf_rlabel)); + else log_uptime(nexthop->sr.uptime, buf_uptime, sizeof(buf_uptime)); - } + sr_op2str(buf_oper, sizeof(buf_oper), srp->input_label, + nexthop->sr.label); 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); + ttable_add_row(tt, "%s|%u|%s|%s|%s|%s", buf_prefix, + srp->sid.value, buf_oper, buf_nhop, + buf_iface, buf_uptime); else - ttable_add_row(tt, "|||%s, %s|%s|%s", buf_nhop, - buf_iface, buf_rlabel, buf_uptime); + ttable_add_row(tt, "|||%s|%s|%s|%s", buf_oper, buf_nhop, + buf_iface, buf_uptime); first = false; } } -static void isis_sr_show_prefix_sids(struct vty *vty, struct isis_area *area, - int level) +/** + * Show Prefix-SIDs. + * + * @param vty VTY output + * @param area IS-IS area + * @param level IS-IS level + */ +static void show_prefix_sids(struct vty *vty, struct isis_area *area, int level) { struct sr_prefix *srp; struct ttable *tt; - if (tree_sr_area_prefix_count(&area->srdb.prefix_sids[level - 1]) == 0) + if (srdb_area_prefix_count(&area->srdb.prefix_sids[level - 1]) == 0) 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"); + ttable_add_row(tt, "Prefix|SID|Label Op.|Nexthop|Interface|Uptime"); tt->style.cell.rpad = 2; tt->style.corner = '+'; ttable_restyle(tt); ttable_rowseps(tt, 0, BOTTOM, true, '-'); - frr_each (tree_sr_area_prefix, &area->srdb.prefix_sids[level - 1], - srp) { + /* Process all Prefix-SID from the SRDB */ + frr_each (srdb_area_prefix, &area->srdb.prefix_sids[level - 1], srp) { switch (srp->type) { case ISIS_SR_PREFIX_LOCAL: - isis_sr_show_prefix_sid_local(vty, tt, area, srp); + show_prefix_sid_local(vty, tt, area, srp); break; case ISIS_SR_PREFIX_REMOTE: - isis_sr_show_prefix_sid_remote(vty, tt, area, srp); + show_prefix_sid_remote(vty, tt, area, srp); break; } } @@ -1341,6 +1688,9 @@ static void isis_sr_show_prefix_sids(struct vty *vty, struct isis_area *area, ttable_del(tt); } +/** + * Declaration of new show commands. + */ DEFUN(show_sr_prefix_sids, show_sr_prefix_sids_cmd, "show isis segment-routing prefix-sids", SHOW_STR PROTO_HELP @@ -1355,15 +1705,89 @@ DEFUN(show_sr_prefix_sids, show_sr_prefix_sids_cmd, area->area_tag ? area->area_tag : "null"); for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) - isis_sr_show_prefix_sids(vty, area, level); + show_prefix_sids(vty, area, level); } return CMD_SUCCESS; } -/*----------------------------------------------------------------------------*/ +/** + * Show Segment Routing Node. + * + * @param vty VTY output + * @param area IS-IS area + * @param level IS-IS level + */ +static void show_node(struct vty *vty, struct isis_area *area, int level) +{ + struct sr_node *srn; + struct ttable *tt; -/* Try to enable SR on the given IS-IS area. */ + if (srdb_area_prefix_count(&area->srdb.prefix_sids[level - 1]) == 0) + return; + + vty_out(vty, " IS-IS %s SR-Node:\n\n", circuit_t2string(level)); + + /* Prepare table. */ + tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]); + ttable_add_row(tt, "System ID|SRGB|Algorithm|MSD"); + tt->style.cell.rpad = 2; + tt->style.corner = '+'; + ttable_restyle(tt); + ttable_rowseps(tt, 0, BOTTOM, true, '-'); + + /* Process all SR-Node from the SRDB */ + frr_each (srdb_node, &area->srdb.sr_nodes[level - 1], srn) { + ttable_add_row(tt, "%s|%u - %u|%s|%u", sysid_print(srn->sysid), + srn->cap.srgb.lower_bound, + srn->cap.srgb.lower_bound + + srn->cap.srgb.range_size - 1, + srn->cap.algo[0] == SR_ALGORITHM_SPF ? "SPF" + : "S-SPF", + srn->cap.msd); + } + + /* 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_node, show_sr_node_cmd, + "show isis segment-routing node", + SHOW_STR PROTO_HELP + "Segment-Routing\n" + "Segment-Routing node\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++) + show_node(vty, area, level); + } + + return CMD_SUCCESS; +} + + +/* --- IS-IS Segment Routing Management function ---------------------------- */ + +/** + * Enable SR on the given IS-IS area. + * + * @param area IS-IS area + * + * @return 0 on success, -1 otherwise + */ int isis_sr_start(struct isis_area *area) { struct isis_sr_db *srdb = &area->srdb; @@ -1380,11 +1804,10 @@ int isis_sr_start(struct isis_area *area) - 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); + sr_debug("ISIS-Sr: Starting Segment Routing for area %s", + area->area_tag); - /* Create Adj-SIDs for existing adjacencies. */ + /* Create Adjacency-SIDs from existing IS-IS Adjacencies. */ for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) { struct isis_adjacency *adj; struct listnode *anode; @@ -1397,54 +1820,55 @@ int isis_sr_start(struct isis_area *area) circuit->u.bc.adjdb[level - 1], anode, adj)) { if (adj->ipv4_address_count > 0) - isis_sr_adj_sid_add(adj, - AF_INET); + sr_adj_sid_add(adj, AF_INET); if (adj->ipv6_address_count > 0) - isis_sr_adj_sid_add(adj, - AF_INET6); + 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); + sr_adj_sid_add(adj, AF_INET); if (adj && adj->ipv6_address_count > 0) - isis_sr_adj_sid_add(adj, AF_INET6); + sr_adj_sid_add(adj, AF_INET6); break; default: break; } } - /* Regenerate LSPs. */ + /* Regenerate LSPs to advertise Segment Routing capabilities. */ lsp_regenerate_schedule(area, area->is_type, 0); return 0; } -/* Disable SR on the given IS-IS area. */ +/** + * Disable SR on the given IS-IS area. + * + * @param area 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); + sr_debug("ISIS-Sr: Stopping Segment Routing for area %s", + area->area_tag); - /* Uninstall Adj-SIDs. */ + /* Uninstall all local Adjacency-SIDs. */ for (ALL_LIST_ELEMENTS(area->srdb.adj_sids, node, nnode, sra)) - isis_sr_adj_sid_del(sra); + sr_adj_sid_del(sra); - /* Uninstall Prefix-SIDs. */ + /* Uninstall all Prefix-SIDs from all SR Node. */ for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) { - while (tree_sr_node_count(&srdb->sr_nodes[level - 1]) > 0) { + while (srdb_node_count(&srdb->sr_nodes[level - 1]) > 0) { struct sr_node *srn; - srn = tree_sr_node_first(&srdb->sr_nodes[level - 1]); - isis_sr_node_del(area, level, srn); + srn = srdb_node_first(&srdb->sr_nodes[level - 1]); + sr_node_del(area, level, srn); } } @@ -1452,21 +1876,30 @@ void isis_sr_stop(struct isis_area *area) isis_zebra_release_label_range(srdb->config.srgb_lower_bound, srdb->config.srgb_upper_bound); - /* Regenerate LSPs. */ + /* Regenerate LSPs to advertise that the Node is no more SR enable. */ lsp_regenerate_schedule(area, area->is_type, 0); } +/** + * IS-IS Segment Routing initialization for given area. + * + * @param area IS-IS area + */ void isis_sr_area_init(struct isis_area *area) { struct isis_sr_db *srdb = &area->srdb; + sr_debug("ISIS-Sr (%s): Initialize Segment Routing SRDB", + area->area_tag); + + /* Initialize Segment Routing Data Base */ memset(srdb, 0, sizeof(*srdb)); srdb->enabled = false; srdb->adj_sids = list_new(); for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) { - tree_sr_node_init(&srdb->sr_nodes[level - 1]); - tree_sr_area_prefix_init(&srdb->prefix_sids[level - 1]); + srdb_node_init(&srdb->sr_nodes[level - 1]); + srdb_area_prefix_init(&srdb->prefix_sids[level - 1]); } /* Pull defaults from the YANG module. */ @@ -1482,9 +1915,14 @@ void isis_sr_area_init(struct isis_area *area) srdb->config.srgb_upper_bound = SRGB_UPPER_BOUND; #endif srdb->config.msd = 0; - tree_sr_prefix_cfg_init(&srdb->config.prefix_sids); + srdb_prefix_cfg_init(&srdb->config.prefix_sids); } +/** + * Terminate IS-IS Segment Routing for the given area. + * + * @param area IS-IS area + */ void isis_sr_area_term(struct isis_area *area) { struct isis_sr_db *srdb = &area->srdb; @@ -1494,32 +1932,39 @@ void isis_sr_area_term(struct isis_area *area) isis_sr_stop(area); /* Clear Prefix-SID configuration. */ - while (tree_sr_prefix_cfg_count(&srdb->config.prefix_sids) > 0) { + while (srdb_prefix_cfg_count(&srdb->config.prefix_sids) > 0) { struct sr_prefix_cfg *pcfg; - pcfg = tree_sr_prefix_cfg_first(&srdb->config.prefix_sids); + pcfg = srdb_prefix_cfg_first(&srdb->config.prefix_sids); isis_sr_cfg_prefix_del(pcfg); } } +/** + * IS-IS Segment Routing global initialization. + */ void isis_sr_init(void) { install_element(VIEW_NODE, &show_sr_prefix_sids_cmd); + install_element(VIEW_NODE, &show_sr_node_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); + hook_register(isis_adj_state_change_hook, sr_adj_state_change); + hook_register(isis_adj_ip_enabled_hook, sr_adj_ip_enabled); + hook_register(isis_adj_ip_disabled_hook, sr_adj_ip_disabled); + hook_register(isis_route_update_hook, sr_route_update); + hook_register(isis_if_new_hook, sr_if_new_hook); } +/** + * IS-IS Segment Routing global terminate. + */ 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); + hook_unregister(isis_adj_state_change_hook, sr_adj_state_change); + hook_unregister(isis_adj_ip_enabled_hook, sr_adj_ip_enabled); + hook_unregister(isis_adj_ip_disabled_hook, sr_adj_ip_disabled); + hook_unregister(isis_route_update_hook, sr_route_update); + hook_unregister(isis_if_new_hook, sr_if_new_hook); } diff --git a/isisd/isis_sr.h b/isisd/isis_sr.h index 286ebeb953..dec329ab48 100644 --- a/isisd/isis_sr.h +++ b/isisd/isis_sr.h @@ -1,8 +1,7 @@ /* - * This is an implementation of Segment Routing for IS-IS - * as per draft draft-ietf-isis-segment-routing-extensions-25 + * This is an implementation of Segment Routing for IS-IS as per RFC 8667 * - * Copyright (C) 2019 Orange Labs http://www.orange.com + * Copyright (C) 2019 Orange http://www.orange.com * * Author: Olivier Dugeon * Contributor: Renato Westphal for NetDEF @@ -55,30 +54,31 @@ #define SRGB_LOWER_BOUND 16000 #define SRGB_UPPER_BOUND 23999 -PREDECL_RBTREE_UNIQ(tree_sr_node) -PREDECL_RBTREE_UNIQ(tree_sr_node_prefix) -PREDECL_RBTREE_UNIQ(tree_sr_area_prefix) -PREDECL_RBTREE_UNIQ(tree_sr_prefix_cfg) +/* Segment Routing Data Base (SRDB) RB-Tree structure */ +PREDECL_RBTREE_UNIQ(srdb_node) +PREDECL_RBTREE_UNIQ(srdb_node_prefix) +PREDECL_RBTREE_UNIQ(srdb_area_prefix) +PREDECL_RBTREE_UNIQ(srdb_prefix_cfg) -/* SR Adj-SID type. */ +/* Segment Routing Adjacency-SID type. */ enum sr_adj_type { ISIS_SR_ADJ_NORMAL = 0, ISIS_SR_LAN_BACKUP, }; -/* SR Adjacency. */ +/* Segment Routing Adjacency. */ struct sr_adjacency { /* Adjacency type. */ enum sr_adj_type type; - /* Adj-SID nexthop information. */ + /* Adjacency-SID nexthop information. */ struct { int family; union g_addr address; mpls_label_t label; } nexthop; - /* (LAN-)Adj-SID Sub-TLV. */ + /* (LAN-)Adjacency-SID Sub-TLV. */ union { struct isis_adj_sid *adj_sid; struct isis_lan_adj_sid *ladj_sid; @@ -88,32 +88,40 @@ struct sr_adjacency { struct isis_adjacency *adj; }; -/* SR Prefix-SID type. */ +/* Segment Routing Prefix-SID type. */ enum sr_prefix_type { ISIS_SR_PREFIX_LOCAL = 0, ISIS_SR_PREFIX_REMOTE, }; -/* SR Nexthop Information. */ +/* Segment Routing Nexthop Information. */ struct sr_nexthop_info { mpls_label_t label; time_t uptime; }; -/* SR Prefix-SID. */ +/* State of Object (SR-Node and SR-Prefix) stored in SRDB */ +enum srdb_state { + SRDB_STATE_VALIDATED = 0, + SRDB_STATE_NEW, + SRDB_STATE_MODIFIED, + SRDB_STATE_UNCHANGED +}; + +/* Segment Routing Prefix-SID. */ struct sr_prefix { - /* RB-tree entries. */ - struct tree_sr_node_prefix_item node_entry; - struct tree_sr_area_prefix_item area_entry; + /* SRDB RB-tree entries. */ + struct srdb_node_prefix_item node_entry; + struct srdb_area_prefix_item area_entry; /* IP prefix. */ struct prefix prefix; - /* SID value, algorithm and flags. */ + /* SID value, algorithm and flags subTLVs. */ struct isis_prefix_sid sid; - /* Local label value. */ - mpls_label_t local_label; + /* Input label value. */ + mpls_label_t input_label; /* Prefix-SID type. */ enum sr_prefix_type type; @@ -128,20 +136,17 @@ struct sr_prefix { } remote; } u; - /* Backpointer to SR node. */ + /* Backpointer to Segment Routing node. */ struct sr_node *srn; - /* Flags used while the LSPDB is being parsed. */ - uint8_t parse_flags; -#define F_ISIS_SR_PREFIX_SID_NEW 0x01 -#define F_ISIS_SR_PREFIX_SID_MODIFIED 0x02 -#define F_ISIS_SR_PREFIX_SID_UNCHANGED 0x04 + /* SR-Prefix State used while the LSPDB is being parsed. */ + enum srdb_state state; }; -/* SR node. */ +/* Segment Routing node. */ struct sr_node { - /* RB-tree entry. */ - struct tree_sr_node_item entry; + /* SRDB RB-tree entry. */ + struct srdb_node_item entry; /* IS-IS level: ISIS_LEVEL1 or ISIS_LEVEL2. */ int level; @@ -149,39 +154,38 @@ struct sr_node { /* IS-IS node identifier. */ uint8_t sysid[ISIS_SYS_ID_LEN]; - /* IS-IS node capabilities (SRGB, SR Algorithms, etc). */ + /* Segment Routing node capabilities (SRGB, SR Algorithms) subTLVs. */ struct isis_router_cap cap; /* List of Prefix-SIDs advertised by this node. */ - struct tree_sr_node_prefix_head prefix_sids; + struct srdb_node_prefix_head prefix_sids; /* Backpointer to IS-IS area. */ struct isis_area *area; - /* Flags used while the LSPDB is being parsed. */ - uint8_t parse_flags; -#define F_ISIS_SR_NODE_NEW 0x01 -#define F_ISIS_SR_NODE_MODIFIED 0x02 -#define F_ISIS_SR_NODE_UNCHANGED 0x04 + /* SR-Node State used while the LSPDB is being parsed. */ + enum srdb_state state; }; -/* NOTE: these values must be in sync with the YANG module. */ +/* SID type. NOTE: these values must be in sync with the YANG module. */ enum sr_sid_value_type { SR_SID_VALUE_TYPE_INDEX = 0, SR_SID_VALUE_TYPE_ABSOLUTE = 1, }; -/* NOTE: these values must be in sync with the YANG module. */ +#define IS_SID_VALUE(flag) CHECK_FLAG(flag, ISIS_PREFIX_SID_VALUE) + +/* Last Hop Behavior. NOTE: these values must be in sync with the YANG module */ enum sr_last_hop_behavior { SR_LAST_HOP_BEHAVIOR_EXP_NULL = 0, SR_LAST_HOP_BEHAVIOR_NO_PHP = 1, SR_LAST_HOP_BEHAVIOR_PHP = 2, }; -/* SR Prefix-SID configuration. */ +/* Segment Routing Prefix-SID configuration. */ struct sr_prefix_cfg { - /* RB-tree entry. */ - struct tree_sr_prefix_cfg_item entry; + /* SRDB RB-tree entry. */ + struct srdb_prefix_cfg_item entry; /* IP prefix. */ struct prefix prefix; @@ -202,21 +206,21 @@ struct sr_prefix_cfg { struct isis_area *area; }; -/* Per-area IS-IS Segment Routing information. */ +/* Per-area IS-IS Segment Routing Data Base (SRDB). */ struct isis_sr_db { - /* Operational status of Segment Routing. */ + /* Global Operational status of Segment Routing. */ bool enabled; - /* Adj-SIDs. */ + /* List of local Adjacency-SIDs. */ struct list *adj_sids; - /* SR information from all nodes. */ - struct tree_sr_node_head sr_nodes[ISIS_LEVELS]; + /* Segment Routing Node information per IS-IS level. */ + struct srdb_node_head sr_nodes[ISIS_LEVELS]; - /* Prefix-SIDs. */ - struct tree_sr_area_prefix_head prefix_sids[ISIS_LEVELS]; + /* Segment Routing Prefix-SIDs per IS-IS level. */ + struct srdb_area_prefix_head prefix_sids[ISIS_LEVELS]; - /* Area SR configuration. */ + /* Area Segment Routing configuration. */ struct { /* Administrative status of Segment Routing. */ bool enabled; @@ -229,14 +233,13 @@ struct isis_sr_db { uint8_t msd; /* Prefix-SID mappings. */ - struct tree_sr_prefix_cfg_head prefix_sids; + struct srdb_prefix_cfg_head prefix_sids; } config; }; /* Prototypes. */ extern int isis_sr_cfg_srgb_update(struct isis_area *area, uint32_t lower_bound, uint32_t upper_bound); -extern void isis_sr_cfg_msd_update(struct isis_area *area); extern struct sr_prefix_cfg * isis_sr_cfg_prefix_add(struct isis_area *area, const struct prefix *prefix); extern void isis_sr_cfg_prefix_del(struct sr_prefix_cfg *pcfg); diff --git a/isisd/isis_tlvs.c b/isisd/isis_tlvs.c index be88ee85a6..923956fa6d 100644 --- a/isisd/isis_tlvs.c +++ b/isisd/isis_tlvs.c @@ -284,7 +284,7 @@ static void format_item_ext_subtlvs(struct isis_ext_subtlvs *exts, sbuf_push(buf, indent, "Unidir. Utilized Bandwidth: %g (Bytes/sec)\n", exts->use_bw); - /* Segment Routing Adjacency */ + /* Segment Routing Adjacency as per RFC8667 section #2.2.1 */ if (IS_SUBTLV(exts, EXT_ADJ_SID)) { struct isis_adj_sid *adj; @@ -315,6 +315,7 @@ static void format_item_ext_subtlvs(struct isis_ext_subtlvs *exts, : '0'); } } + /* Segment Routing LAN-Adjacency as per RFC8667 section #2.2.2 */ if (IS_SUBTLV(exts, EXT_LAN_ADJ_SID)) { struct isis_lan_adj_sid *lan; @@ -476,6 +477,7 @@ static int pack_item_ext_subtlvs(struct isis_ext_subtlvs *exts, stream_putc(s, ISIS_SUBTLV_DEF_SIZE); stream_putf(s, exts->use_bw); } + /* Segment Routing Adjacency as per RFC8667 section #2.2.1 */ if (IS_SUBTLV(exts, EXT_ADJ_SID)) { struct isis_adj_sid *adj; @@ -495,6 +497,7 @@ static int pack_item_ext_subtlvs(struct isis_ext_subtlvs *exts, } } + /* Segment Routing LAN-Adjacency as per RFC8667 section #2.2.2 */ if (IS_SUBTLV(exts, EXT_LAN_ADJ_SID)) { struct isis_lan_adj_sid *lan; @@ -721,7 +724,7 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, SET_SUBTLV(exts, EXT_USE_BW); } break; - /* Segment Routing Adjacency */ + /* Segment Routing Adjacency as per RFC8667 section #2.2.1 */ case ISIS_SUBTLV_ADJ_SID: if (subtlv_len != ISIS_SUBTLV_ADJ_SID_SIZE && subtlv_len != ISIS_SUBTLV_ADJ_SID_SIZE + 1) { @@ -749,6 +752,7 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, SET_SUBTLV(exts, EXT_ADJ_SID); } break; + /* Segment Routing LAN-Adjacency as per RFC8667 section 2.2.2 */ case ISIS_SUBTLV_LAN_ADJ_SID: if (subtlv_len != ISIS_SUBTLV_LAN_ADJ_SID_SIZE && subtlv_len != ISIS_SUBTLV_LAN_ADJ_SID_SIZE + 1) { @@ -789,7 +793,7 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, return 0; } -/* Functions for Sub-TLV 3 SR Prefix-SID */ +/* Functions for Sub-TLV 3 SR Prefix-SID as per RFC8667 section 2.1 */ static struct isis_item *copy_item_prefix_sid(struct isis_item *i) { struct isis_prefix_sid *sid = (struct isis_prefix_sid *)i; @@ -2585,7 +2589,7 @@ out: return 1; } -/* Functions related to TLV 242 Router Capability */ +/* Functions related to TLV 242 Router Capability as per RFC7981 */ static struct isis_router_cap *copy_tlv_router_cap( const struct isis_router_cap *router_cap) { @@ -2614,7 +2618,7 @@ static void format_tlv_router_cap(const struct isis_router_cap *router_cap, router_cap->flags & ISIS_ROUTER_CAP_FLAG_D ? '1' : '0', router_cap->flags & ISIS_ROUTER_CAP_FLAG_S ? '1' : '0'); - /* SR Global Block */ + /* Segment Routing Global Block as per RFC8667 section #3.1 */ if (router_cap->srgb.range_size != 0) sbuf_push(buf, indent, " Segment Routing: I:%s V:%s, SRGB Base: %d Range: %d\n", @@ -2623,7 +2627,7 @@ static void format_tlv_router_cap(const struct isis_router_cap *router_cap, router_cap->srgb.lower_bound, router_cap->srgb.range_size); - /* SR Algorithms */ + /* Segment Routing Algorithms as per RFC8667 section #3.2 */ if (router_cap->algo[0] != SR_ALGORITHM_UNSET) { sbuf_push(buf, indent, " Algorithm: %s", router_cap->algo[0] == 0 ? "0: SPF" @@ -2637,7 +2641,7 @@ static void format_tlv_router_cap(const struct isis_router_cap *router_cap, sbuf_push(buf, indent, "\n"); } - /* SR Node MSSD */ + /* Segment Routing Node MSD as per RFC8491 section #2 */ if (router_cap->msd != 0) sbuf_push(buf, indent, " Node MSD: %d\n", router_cap->msd); } @@ -2674,7 +2678,7 @@ static int pack_tlv_router_cap(const struct isis_router_cap *router_cap, stream_put_ipv4(s, router_cap->router_id.s_addr); stream_putc(s, router_cap->flags); - /* Add SRGB if set */ + /* Add SRGB if set as per RFC8667 section #3.1 */ if ((router_cap->srgb.range_size != 0) && (router_cap->srgb.lower_bound != 0)) { stream_putc(s, ISIS_SUBTLV_SID_LABEL_RANGE); @@ -2685,7 +2689,7 @@ static int pack_tlv_router_cap(const struct isis_router_cap *router_cap, stream_putc(s, ISIS_SUBTLV_SID_LABEL_SIZE); stream_put3(s, router_cap->srgb.lower_bound); - /* Then SR Algorithm if set */ + /* Then SR Algorithm if set as per RFC8667 section #3.2 */ for (nb_algo = 0; nb_algo < SR_ALGORITHM_COUNT; nb_algo++) if (router_cap->algo[nb_algo] == SR_ALGORITHM_UNSET) break; @@ -2695,7 +2699,7 @@ static int pack_tlv_router_cap(const struct isis_router_cap *router_cap, for (int i = 0; i < nb_algo; i++) stream_putc(s, router_cap->algo[i]); } - /* And finish with MSD if set */ + /* And finish with MSD if set as per RFC8491 section #2 */ if (router_cap->msd != 0) { stream_putc(s, ISIS_SUBTLV_NODE_MSD); stream_putc(s, ISIS_SUBTLV_NODE_MSD_SIZE); @@ -4602,6 +4606,7 @@ void isis_tlvs_add_oldstyle_ip_reach(struct isis_tlvs *tlvs, append_item(&tlvs->oldstyle_ip_reach, (struct isis_item *)r); } +/* Add IS-IS SR Adjacency-SID subTLVs */ void isis_tlvs_add_adj_sid(struct isis_ext_subtlvs *exts, struct isis_adj_sid *adj) { @@ -4609,6 +4614,7 @@ void isis_tlvs_add_adj_sid(struct isis_ext_subtlvs *exts, SET_SUBTLV(exts, EXT_ADJ_SID); } +/* Delete IS-IS SR Adjacency-SID subTLVs */ void isis_tlvs_del_adj_sid(struct isis_ext_subtlvs *exts, struct isis_adj_sid *adj) { @@ -4618,6 +4624,7 @@ void isis_tlvs_del_adj_sid(struct isis_ext_subtlvs *exts, UNSET_SUBTLV(exts, EXT_ADJ_SID); } +/* Add IS-IS SR LAN-Adjacency-SID subTLVs */ void isis_tlvs_add_lan_adj_sid(struct isis_ext_subtlvs *exts, struct isis_lan_adj_sid *lan) { @@ -4625,6 +4632,7 @@ void isis_tlvs_add_lan_adj_sid(struct isis_ext_subtlvs *exts, SET_SUBTLV(exts, EXT_LAN_ADJ_SID); } +/* Delete IS-IS SR LAN-Adjacency-SID subTLVs */ void isis_tlvs_del_lan_adj_sid(struct isis_ext_subtlvs *exts, struct isis_lan_adj_sid *lan) { diff --git a/isisd/isis_tlvs.h b/isisd/isis_tlvs.h index c3b25669b6..f468d85bbd 100644 --- a/isisd/isis_tlvs.h +++ b/isisd/isis_tlvs.h @@ -135,10 +135,7 @@ struct isis_threeway_adj { uint32_t neighbor_circuit_id; }; -/* - * Segment Routing subTLV's as per - * draft-ietf-isis-segment-routing-extension-25 - */ +/* Segment Routing subTLV's as per RFC8667 */ #define ISIS_SUBTLV_SRGB_FLAG_I 0x80 #define ISIS_SUBTLV_SRGB_FLAG_V 0x40 #define IS_SR_IPV4(srgb) (srgb.flags & ISIS_SUBTLV_SRGB_FLAG_I) @@ -216,7 +213,7 @@ struct isis_router_cap { struct in_addr router_id; uint8_t flags; - /* draft-ietf-segment-routing-extensions-25 */ + /* RFC 8667 section #3 */ struct isis_srgb srgb; uint8_t algo[SR_ALGORITHM_COUNT]; /* RFC 8491 */ @@ -344,7 +341,7 @@ struct isis_subtlvs { /* draft-baker-ipv6-isis-dst-src-routing-06 */ struct prefix_ipv6 *source_prefix; - /* draft-ietf-isis-segment-routing-extensions-25 */ + /* RFC 8667 section #2.4 */ struct isis_item_list prefix_sids; }; @@ -394,15 +391,17 @@ enum isis_tlv_type { /* RFC 5307 */ ISIS_SUBTLV_LLRI = 4, + /* RFC 8491 */ + ISIS_SUBTLV_NODE_MSD = 23, + /* RFC 5316 */ ISIS_SUBTLV_RAS = 24, ISIS_SUBTLV_RIP = 25, - /* draft-isis-segment-routing-extension-25 */ + /* RFC 8667 section #2 */ ISIS_SUBTLV_SID_LABEL = 1, ISIS_SUBTLV_SID_LABEL_RANGE = 2, ISIS_SUBTLV_ALGORITHM = 19, - ISIS_SUBTLV_NODE_MSD = 23, ISIS_SUBTLV_PREFIX_SID = 3, ISIS_SUBTLV_ADJ_SID = 31, ISIS_SUBTLV_LAN_ADJ_SID = 32, @@ -421,21 +420,26 @@ enum isis_tlv_type { /* subTLVs size for TE and SR */ enum ext_subtlv_size { + /* RFC 5307 */ ISIS_SUBTLV_LLRI_SIZE = 8, + /* RFC 5305 & RFC 6119 */ ISIS_SUBTLV_UNRSV_BW_SIZE = 32, ISIS_SUBTLV_TE_METRIC_SIZE = 3, ISIS_SUBTLV_IPV6_ADDR_SIZE = 16, - /* draft-isis-segment-routing-extension-25 */ + /* RFC 8491 */ + ISIS_SUBTLV_NODE_MSD_SIZE = 2, + + /* RFC 8667 section #2 */ ISIS_SUBTLV_SID_LABEL_SIZE = 3, ISIS_SUBTLV_SID_LABEL_RANGE_SIZE = 9, ISIS_SUBTLV_ALGORITHM_SIZE = 4, - ISIS_SUBTLV_NODE_MSD_SIZE = 2, ISIS_SUBTLV_ADJ_SID_SIZE = 5, ISIS_SUBTLV_LAN_ADJ_SID_SIZE = 11, ISIS_SUBTLV_PREFIX_SID_SIZE = 5, + /* RFC 7810 */ ISIS_SUBTLV_MM_DELAY_SIZE = 8, ISIS_SUBTLV_HDR_SIZE = 2, diff --git a/isisd/isis_zebra.c b/isisd/isis_zebra.c index d4b9d2a621..e0bf0cee14 100644 --- a/isisd/isis_zebra.c +++ b/isisd/isis_zebra.c @@ -49,6 +49,7 @@ #include "isisd/isis_lsp.h" #include "isisd/isis_route.h" #include "isisd/isis_zebra.h" +#include "isisd/isis_adjacency.h" #include "isisd/isis_te.h" #include "isisd/isis_sr.h" @@ -253,8 +254,12 @@ void isis_zebra_route_del_route(struct prefix *prefix, zclient_route_send(ZEBRA_ROUTE_DELETE, zclient, &api); } -/* Install Prefix-SID in the forwarding plane. */ -void isis_zebra_install_prefix_sid(const struct sr_prefix *srp) +/** + * Install Prefix-SID in the forwarding plane through Zebra. + * + * @param srp Segment Routing Prefix-SID + */ +static void isis_zebra_prefix_install_prefix_sid(const struct sr_prefix *srp) { struct zapi_labels zl; struct zapi_nexthop *znh; @@ -265,7 +270,7 @@ void isis_zebra_install_prefix_sid(const struct sr_prefix *srp) /* Prepare message. */ memset(&zl, 0, sizeof(zl)); zl.type = ZEBRA_LSP_ISIS_SR; - zl.local_label = srp->local_label; + zl.local_label = srp->input_label; switch (srp->type) { case ISIS_SR_PREFIX_LOCAL: @@ -314,15 +319,19 @@ void isis_zebra_install_prefix_sid(const struct sr_prefix *srp) (void)zebra_send_mpls_labels(zclient, ZEBRA_MPLS_LABELS_REPLACE, &zl); } -/* Uninstall Prefix-SID from the forwarding plane. */ -void isis_zebra_uninstall_prefix_sid(const struct sr_prefix *srp) +/** + * Uninstall Prefix-SID from the forwarding plane through Zebra. + * + * @param srp Segment Routing Prefix-SID + */ +static void isis_zebra_uninstall_prefix_sid(const struct sr_prefix *srp) { struct zapi_labels zl; /* Prepare message. */ memset(&zl, 0, sizeof(zl)); zl.type = ZEBRA_LSP_ISIS_SR; - zl.local_label = srp->local_label; + zl.local_label = srp->input_label; if (srp->type == ISIS_SR_PREFIX_REMOTE) { /* Update route in the RIB too. */ @@ -336,6 +345,69 @@ void isis_zebra_uninstall_prefix_sid(const struct sr_prefix *srp) (void)zebra_send_mpls_labels(zclient, ZEBRA_MPLS_LABELS_DELETE, &zl); } +/** + * Send Prefix-SID to ZEBRA for installation or deletion. + * + * @param cmd ZEBRA_MPLS_LABELS_REPLACE or ZEBRA_ROUTE_DELETE + * @param srp Segment Routing Prefix-SID + */ +void isis_zebra_send_prefix_sid(int cmd, const struct sr_prefix *srp) +{ + + if (cmd != ZEBRA_MPLS_LABELS_REPLACE + && cmd != ZEBRA_MPLS_LABELS_DELETE) { + flog_warn(EC_LIB_DEVELOPMENT, "%s: wrong ZEBRA command", + __func__); + return; + } + + sr_debug(" |- %s label %u for prefix %pFX", + cmd == ZEBRA_MPLS_LABELS_REPLACE ? "Update" : "Delete", + srp->input_label, &srp->prefix); + + if (cmd == ZEBRA_MPLS_LABELS_REPLACE) + isis_zebra_prefix_install_prefix_sid(srp); + else + isis_zebra_uninstall_prefix_sid(srp); +} + +/** + * Send (LAN)-Adjacency-SID to ZEBRA for installation or deletion. + * + * @param cmd ZEBRA_MPLS_LABELS_ADD or ZEBRA_ROUTE_DELETE + * @param sra Segment Routing Adjacency-SID + */ +void isis_zebra_send_adjacency_sid(int cmd, const struct sr_adjacency *sra) +{ + struct zapi_labels zl; + struct zapi_nexthop *znh; + + if (cmd != ZEBRA_MPLS_LABELS_ADD && cmd != ZEBRA_MPLS_LABELS_DELETE) { + flog_warn(EC_LIB_DEVELOPMENT, "%s: wrong ZEBRA command", + __func__); + return; + } + + sr_debug(" |- %s label %u for interface %s", + cmd == ZEBRA_MPLS_LABELS_ADD ? "Add" : "Delete", + sra->nexthop.label, sra->adj->circuit->interface->name); + + 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]; + znh->gate = sra->nexthop.address; + znh->type = (sra->nexthop.family == AF_INET) + ? NEXTHOP_TYPE_IPV4_IFINDEX + : NEXTHOP_TYPE_IPV6_IFINDEX; + znh->ifindex = sra->adj->circuit->interface->ifindex; + znh->label_num = 1; + znh->labels[0] = MPLS_LABEL_IMPLICIT_NULL; + + (void)zebra_send_mpls_labels(zclient, cmd, &zl); +} + static int isis_zebra_read(ZAPI_CALLBACK_ARGS) { struct zapi_route api; diff --git a/isisd/isis_zebra.h b/isisd/isis_zebra.h index cca2b08811..b143d34626 100644 --- a/isisd/isis_zebra.h +++ b/isisd/isis_zebra.h @@ -36,6 +36,7 @@ void isis_zebra_stop(void); struct isis_route_info; struct sr_prefix; +struct sr_adjacency; void isis_zebra_route_add_route(struct prefix *prefix, struct prefix_ipv6 *src_p, @@ -43,8 +44,8 @@ void isis_zebra_route_add_route(struct prefix *prefix, void isis_zebra_route_del_route(struct prefix *prefix, struct prefix_ipv6 *src_p, struct isis_route_info *route_info); -void isis_zebra_install_prefix_sid(const struct sr_prefix *srp); -void isis_zebra_uninstall_prefix_sid(const struct sr_prefix *srp); +void isis_zebra_send_prefix_sid(int cmd, const struct sr_prefix *srp); +void isis_zebra_send_adjacency_sid(int cmd, const struct sr_adjacency *sra); int isis_distribute_list_update(int routetype); void isis_zebra_redistribute_set(afi_t afi, int type); void isis_zebra_redistribute_unset(afi_t afi, int type); diff --git a/isisd/isisd.h b/isisd/isisd.h index 56ea0993fd..439428d797 100644 --- a/isisd/isisd.h +++ b/isisd/isisd.h @@ -254,6 +254,12 @@ extern struct thread_master *master; zlog_debug(__VA_ARGS__); \ } while (0) +#define sr_debug(...) \ + do { \ + if (IS_DEBUG_ISIS(DEBUG_SR)) \ + zlog_debug(__VA_ARGS__); \ + } while (0) + #define DEBUG_TE DEBUG_LSP_GEN #define IS_DEBUG_ISIS(x) (isis->debugs & x)