diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-09 13:16:35 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-09 13:16:35 +0000 |
commit | e2bbf175a2184bd76f6c54ccf8456babeb1a46fc (patch) | |
tree | f0b76550d6e6f500ada964a3a4ee933a45e5a6f1 /isisd/isis_srv6.c | |
parent | Initial commit. (diff) | |
download | frr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.tar.xz frr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.zip |
Adding upstream version 9.1.upstream/9.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'isisd/isis_srv6.c')
-rw-r--r-- | isisd/isis_srv6.c | 862 |
1 files changed, 862 insertions, 0 deletions
diff --git a/isisd/isis_srv6.c b/isisd/isis_srv6.c new file mode 100644 index 0000000..1b0c706 --- /dev/null +++ b/isisd/isis_srv6.c @@ -0,0 +1,862 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * This is an implementation of Segment Routing over IPv6 (SRv6) for IS-IS + * as per RFC 9352 + * https://datatracker.ietf.org/doc/html/rfc9352 + * + * Copyright (C) 2023 Carmine Scarpitta - University of Rome Tor Vergata + */ + +#include <zebra.h> + +#include "srv6.h" +#include "termtable.h" +#include "lib/lib_errors.h" + +#include "isisd/isisd.h" +#include "isisd/isis_adjacency.h" +#include "isisd/isis_misc.h" +#include "isisd/isis_route.h" +#include "isisd/isis_srv6.h" +#include "isisd/isis_zebra.h" + +/* Local variables and functions */ +DEFINE_MTYPE_STATIC(ISISD, ISIS_SRV6_SID, "ISIS SRv6 Segment ID"); +DEFINE_MTYPE_STATIC(ISISD, ISIS_SRV6_INFO, "ISIS SRv6 information"); + +/** + * Fill in SRv6 SID Structure Sub-Sub-TLV with information from an SRv6 SID. + * + * @param sid SRv6 SID configuration + * @param structure_subsubtlv SRv6 SID Structure Sub-Sub-TLV to be updated + */ +void isis_srv6_sid_structure2subsubtlv( + const struct isis_srv6_sid *sid, + struct isis_srv6_sid_structure_subsubtlv *structure_subsubtlv) +{ + /* Set Locator Block length */ + structure_subsubtlv->loc_block_len = sid->structure.loc_block_len; + + /* Set Locator Node length */ + structure_subsubtlv->loc_node_len = sid->structure.loc_node_len; + + /* Set Function length */ + structure_subsubtlv->func_len = sid->structure.func_len; + + /* Set Argument length */ + structure_subsubtlv->arg_len = sid->structure.arg_len; +} + +/** + * Fill in SRv6 End SID Sub-TLV with information from an SRv6 SID. + * + * @param sid SRv6 SID configuration + * @param sid_subtlv SRv6 End SID Sub-TLV to be updated + */ +void isis_srv6_end_sid2subtlv(const struct isis_srv6_sid *sid, + struct isis_srv6_end_sid_subtlv *sid_subtlv) +{ + /* Set SRv6 SID flags */ + sid_subtlv->flags = sid->flags; + + /* Set SRv6 SID behavior */ + sid_subtlv->behavior = sid->behavior; + + /* Set SRv6 SID value */ + sid_subtlv->sid = sid->sid; +} + +/** + * Fill in SRv6 Locator TLV with information from an SRv6 locator. + * + * @param loc SRv6 Locator configuration + * @param loc_tlv SRv6 Locator TLV to be updated + */ +void isis_srv6_locator2tlv(const struct isis_srv6_locator *loc, + struct isis_srv6_locator_tlv *loc_tlv) +{ + /* Set SRv6 Locator metric */ + loc_tlv->metric = loc->metric; + + /* Set SRv6 Locator flags */ + loc_tlv->flags = loc->flags; + + /* Set SRv6 Locator algorithm */ + loc_tlv->algorithm = loc->algorithm; + + /* Set SRv6 Locator prefix */ + loc_tlv->prefix = loc->prefix; +} + +/** + * Unset the SRv6 locator for a given IS-IS area. + * + * @param area IS-IS area + * + * @result True on success, False otherwise + */ +bool isis_srv6_locator_unset(struct isis_area *area) +{ + int ret; + struct listnode *node, *nnode; + struct srv6_locator_chunk *chunk; + struct isis_srv6_sid *sid; + struct srv6_adjacency *sra; + + if (strncmp(area->srv6db.config.srv6_locator_name, "", + sizeof(area->srv6db.config.srv6_locator_name)) == 0) { + sr_debug("SRv6 locator not set"); + return true; + } + + /* Delete SRv6 SIDs */ + for (ALL_LIST_ELEMENTS(area->srv6db.srv6_sids, node, nnode, sid)) { + sr_debug( + "Deleting SRv6 SID (locator %s, sid %pI6) from IS-IS area %s", + area->srv6db.config.srv6_locator_name, &sid->sid, + area->area_tag); + + /* Uninstall the SRv6 SID from the forwarding plane through + * Zebra */ + isis_zebra_srv6_sid_uninstall(area, sid); + + listnode_delete(area->srv6db.srv6_sids, sid); + isis_srv6_sid_free(sid); + } + + /* Uninstall all local Adjacency-SIDs. */ + for (ALL_LIST_ELEMENTS(area->srv6db.srv6_endx_sids, node, nnode, sra)) + srv6_endx_sid_del(sra); + + /* Inform Zebra that we are releasing the SRv6 locator */ + ret = isis_zebra_srv6_manager_release_locator_chunk( + area->srv6db.config.srv6_locator_name); + if (ret < 0) + return false; + + /* Delete chunks */ + for (ALL_LIST_ELEMENTS(area->srv6db.srv6_locator_chunks, node, nnode, + chunk)) { + sr_debug( + "Releasing chunk of locator %s (prefix %pFX) for IS-IS area %s", + area->srv6db.config.srv6_locator_name, &chunk->prefix, + area->area_tag); + + listnode_delete(area->srv6db.srv6_locator_chunks, chunk); + srv6_locator_chunk_free(&chunk); + } + + /* Clear locator name */ + memset(area->srv6db.config.srv6_locator_name, 0, + sizeof(area->srv6db.config.srv6_locator_name)); + + /* Regenerate LSPs to advertise that the SRv6 locator no longer exists + */ + lsp_regenerate_schedule(area, area->is_type, 0); + + return true; +} + +/** + * Set the interface used to install SRv6 SIDs into the data plane. + * + * @param area IS-IS area + */ +void isis_srv6_interface_set(struct isis_area *area, const char *ifname) +{ + struct listnode *node; + struct isis_srv6_sid *sid; + + if (!ifname) + return; + + if (!strncmp(ifname, area->srv6db.config.srv6_ifname, IF_NAMESIZE)) { + /* The interface has not changed, nothing to do */ + return; + } + + sr_debug("SRv6 interface for IS-IS area %s changed (old interface: %s, new interface: %s)", area->area_tag, area->srv6db.config.srv6_ifname, ifname); + + /* Walk through all SIDs and uninstall them from the data plane */ + for (ALL_LIST_ELEMENTS_RO(area->srv6db.srv6_sids, node, sid)) { + sr_debug("Uninstalling SID %pI6 from the data plane", &sid->sid); + isis_zebra_srv6_sid_uninstall(area, sid); + } + + strlcpy(area->srv6db.config.srv6_ifname, ifname, sizeof(area->srv6db.config.srv6_ifname)); + + if (!if_lookup_by_name(area->srv6db.config.srv6_ifname, VRF_DEFAULT)) { + sr_debug("Interface %s not yet exist in data plane, deferring SIDs installation until it's created", area->srv6db.config.srv6_ifname); + return; + } + + /* Walk through all SIDs and re-install them into the data plane with the newly configured interface */ + for (ALL_LIST_ELEMENTS_RO(area->srv6db.srv6_sids, node, sid)) { + sr_debug("Installing SID %pI6 from the data plane", &sid->sid); + isis_zebra_srv6_sid_install(area, sid); + } +} + +/** + * Encode SID function in the SRv6 SID. + * + * @param sid + * @param func + * @param offset + * @param len + */ +static void encode_sid_func(struct in6_addr *sid, uint32_t func, uint8_t offset, + uint8_t len) +{ + for (uint8_t idx = 0; idx < len; idx++) { + uint8_t tidx = offset + idx; + sid->s6_addr[tidx / 8] &= ~(0x1 << (7 - tidx % 8)); + if (func >> (len - 1 - idx) & 0x1) + sid->s6_addr[tidx / 8] |= 0x1 << (7 - tidx % 8); + } +} + +static bool sid_exist(struct isis_area *area, const struct in6_addr *sid) +{ + struct listnode *node; + struct isis_srv6_sid *s; + struct srv6_adjacency *sra; + + for (ALL_LIST_ELEMENTS_RO(area->srv6db.srv6_sids, node, s)) + if (sid_same(&s->sid, sid)) + return true; + for (ALL_LIST_ELEMENTS_RO(area->srv6db.srv6_endx_sids, node, sra)) + if (sid_same(&sra->sid, sid)) + return true; + return false; +} + +/** + * Request a SID from the SRv6 locator. + * + * @param area IS-IS area + * @param chunk SRv6 locator chunk + * @param sid_func The FUNCTION part of the SID to be allocated (a negative + * number will allocate the first available SID) + * + * @return First available SID on success or in6addr_any if the SRv6 + * locator chunk is full + */ +static struct in6_addr +srv6_locator_request_sid(struct isis_area *area, + struct srv6_locator_chunk *chunk, int sid_func) +{ + struct in6_addr sid; + uint8_t offset = 0; + uint8_t func_len = 0; + uint32_t func_max; + bool allocated = false; + + if (!area || !chunk) + return in6addr_any; + + sr_debug("ISIS-SRv6 (%s): requested new SID from locator %s", + area->area_tag, chunk->locator_name); + + /* Let's build the SID, step by step. A SID has the following structure + (defined in RFC 8986): LOCATOR:FUNCTION:ARGUMENT.*/ + + /* First, we encode the LOCATOR in the L most significant bits. */ + sid = chunk->prefix.prefix; + + /* The next part of the SID is the FUNCTION. Let's compute the length + * and the offset of the FUNCTION in the SID */ + func_len = chunk->function_bits_length; + offset = chunk->block_bits_length + chunk->node_bits_length; + + /* Then, encode the FUNCTION */ + if (sid_func >= 0) { + /* SID FUNCTION has been specified. We need to allocate a SID + * with the requested FUNCTION. */ + encode_sid_func(&sid, sid_func, offset, func_len); + if (sid_exist(area, &sid)) { + zlog_warn( + "ISIS-SRv6 (%s): the requested SID %pI6 is already used", + area->area_tag, &sid); + return sid; + } + allocated = true; + } else { + /* SID FUNCTION not specified. We need to choose a FUNCTION that + * is not already used. So let's iterate through all possible + * functions and get the first available one. */ + func_max = (1 << func_len) - 1; + for (uint32_t func = 1; func < func_max; func++) { + encode_sid_func(&sid, func, offset, func_len); + if (sid_exist(area, &sid)) + continue; + allocated = true; + break; + } + } + + if (!allocated) { + /* We ran out of available SIDs */ + zlog_warn("ISIS-SRv6 (%s): no SIDs available in locator %s", + area->area_tag, chunk->locator_name); + return in6addr_any; + } + + sr_debug("ISIS-SRv6 (%s): allocating new SID %pI6", area->area_tag, + &sid); + + return sid; +} + +/** + * Allocate an SRv6 SID from an SRv6 locator. + * + * @param area IS-IS area + * @param chunk SRv6 locator chunk + * @param behavior SRv6 Endpoint Behavior bound to the SID + * + * @result the allocated SID on success, NULL otherwise + */ +struct isis_srv6_sid * +isis_srv6_sid_alloc(struct isis_area *area, struct srv6_locator_chunk *chunk, + enum srv6_endpoint_behavior_codepoint behavior, + int sid_func) +{ + struct isis_srv6_sid *sid = NULL; + + if (!area || !chunk) + return NULL; + + sid = XCALLOC(MTYPE_ISIS_SRV6_SID, sizeof(struct isis_srv6_sid)); + + sid->sid = srv6_locator_request_sid(area, chunk, sid_func); + if (IPV6_ADDR_SAME(&sid->sid, &in6addr_any)) { + isis_srv6_sid_free(sid); + return NULL; + } + + sid->behavior = behavior; + sid->structure.loc_block_len = chunk->block_bits_length; + sid->structure.loc_node_len = chunk->node_bits_length; + sid->structure.func_len = chunk->function_bits_length; + sid->structure.arg_len = chunk->argument_bits_length; + sid->locator = chunk; + sid->area = area; + + return sid; +} + +void isis_srv6_sid_free(struct isis_srv6_sid *sid) +{ + XFREE(MTYPE_ISIS_SRV6_SID, sid); +} + +/** + * Delete all backup SRv6 End.X SIDs. + * + * @param area IS-IS area + * @param level IS-IS level + */ +void isis_area_delete_backup_srv6_endx_sids(struct isis_area *area, int level) +{ + struct srv6_adjacency *sra; + struct listnode *node, *nnode; + + for (ALL_LIST_ELEMENTS(area->srv6db.srv6_endx_sids, node, nnode, sra)) + if (sra->type == ISIS_SRV6_LAN_BACKUP && + (sra->adj->level & level)) + srv6_endx_sid_del(sra); +} + +/* --- SRv6 End.X SID management functions ------------------- */ + +/** + * Add new local End.X SID. + * + * @param adj IS-IS Adjacency + * @param backup True to initialize backup Adjacency SID + * @param nexthops List of backup nexthops (for backup End.X SIDs only) + */ +void srv6_endx_sid_add_single(struct isis_adjacency *adj, bool backup, + struct list *nexthops) +{ + struct isis_circuit *circuit = adj->circuit; + struct isis_area *area = circuit->area; + struct srv6_adjacency *sra; + struct isis_srv6_endx_sid_subtlv *adj_sid; + struct isis_srv6_lan_endx_sid_subtlv *ladj_sid; + struct in6_addr nexthop; + uint8_t flags = 0; + struct srv6_locator_chunk *chunk; + uint32_t behavior; + + if (!area || !area->srv6db.srv6_locator_chunks || + list_isempty(area->srv6db.srv6_locator_chunks)) + return; + + sr_debug("ISIS-SRv6 (%s): Add %s End.X SID", area->area_tag, + backup ? "Backup" : "Primary"); + + /* Determine nexthop IP address */ + if (!circuit->ipv6_router || !adj->ll_ipv6_count) + return; + + chunk = (struct srv6_locator_chunk *)listgetdata( + listhead(area->srv6db.srv6_locator_chunks)); + if (!chunk) + return; + + nexthop = adj->ll_ipv6_addrs[0]; + + /* Prepare SRv6 End.X as per RFC9352 section #8.1 */ + if (backup) + SET_FLAG(flags, EXT_SUBTLV_LINK_SRV6_ENDX_SID_BFLG); + + if (circuit->ext == NULL) + circuit->ext = isis_alloc_ext_subtlvs(); + + behavior = (CHECK_FLAG(chunk->flags, SRV6_LOCATOR_USID)) + ? SRV6_ENDPOINT_BEHAVIOR_END_X_NEXT_CSID + : SRV6_ENDPOINT_BEHAVIOR_END_X; + + sra = XCALLOC(MTYPE_ISIS_SRV6_INFO, sizeof(*sra)); + sra->type = backup ? ISIS_SRV6_LAN_BACKUP : ISIS_SRV6_ADJ_NORMAL; + sra->behavior = behavior; + sra->locator = chunk; + sra->structure.loc_block_len = chunk->block_bits_length; + sra->structure.loc_node_len = chunk->node_bits_length; + sra->structure.func_len = chunk->function_bits_length; + sra->structure.arg_len = chunk->argument_bits_length; + sra->nexthop = nexthop; + + sra->sid = srv6_locator_request_sid(area, chunk, -1); + if (IPV6_ADDR_SAME(&sra->sid, &in6addr_any)) { + XFREE(MTYPE_ISIS_SRV6_INFO, sra); + return; + } + + switch (circuit->circ_type) { + /* SRv6 LAN End.X SID for Broadcast interface section #8.2 */ + case CIRCUIT_T_BROADCAST: + ladj_sid = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*ladj_sid)); + memcpy(ladj_sid->neighbor_id, adj->sysid, + sizeof(ladj_sid->neighbor_id)); + ladj_sid->flags = flags; + ladj_sid->algorithm = SR_ALGORITHM_SPF; + ladj_sid->weight = 0; + ladj_sid->behavior = sra->behavior; + ladj_sid->sid = sra->sid; + ladj_sid->subsubtlvs = isis_alloc_subsubtlvs( + ISIS_CONTEXT_SUBSUBTLV_SRV6_ENDX_SID); + ladj_sid->subsubtlvs->srv6_sid_structure = XCALLOC( + MTYPE_ISIS_SUBSUBTLV, + sizeof(*ladj_sid->subsubtlvs->srv6_sid_structure)); + ladj_sid->subsubtlvs->srv6_sid_structure->loc_block_len = + sra->structure.loc_block_len; + ladj_sid->subsubtlvs->srv6_sid_structure->loc_node_len = + sra->structure.loc_node_len; + ladj_sid->subsubtlvs->srv6_sid_structure->func_len = + sra->structure.func_len; + ladj_sid->subsubtlvs->srv6_sid_structure->arg_len = + sra->structure.arg_len; + isis_tlvs_add_srv6_lan_endx_sid(circuit->ext, ladj_sid); + sra->u.lendx_sid = ladj_sid; + break; + /* SRv6 End.X SID for Point to Point interface section #8.1 */ + case CIRCUIT_T_P2P: + adj_sid = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*adj_sid)); + adj_sid->flags = flags; + adj_sid->algorithm = SR_ALGORITHM_SPF; + adj_sid->weight = 0; + adj_sid->behavior = sra->behavior; + adj_sid->sid = sra->sid; + adj_sid->subsubtlvs = isis_alloc_subsubtlvs( + ISIS_CONTEXT_SUBSUBTLV_SRV6_ENDX_SID); + adj_sid->subsubtlvs->srv6_sid_structure = XCALLOC( + MTYPE_ISIS_SUBSUBTLV, + sizeof(*adj_sid->subsubtlvs->srv6_sid_structure)); + adj_sid->subsubtlvs->srv6_sid_structure->loc_block_len = + sra->structure.loc_block_len; + adj_sid->subsubtlvs->srv6_sid_structure->loc_node_len = + sra->structure.loc_node_len; + adj_sid->subsubtlvs->srv6_sid_structure->func_len = + sra->structure.func_len; + adj_sid->subsubtlvs->srv6_sid_structure->arg_len = + sra->structure.arg_len; + isis_tlvs_add_srv6_endx_sid(circuit->ext, adj_sid); + sra->u.endx_sid = adj_sid; + break; + default: + flog_err(EC_LIB_DEVELOPMENT, "%s: unexpected circuit type: %u", + __func__, circuit->circ_type); + exit(1); + } + + /* Add Adjacency-SID in SRDB */ + sra->adj = adj; + listnode_add(area->srv6db.srv6_endx_sids, sra); + listnode_add(adj->srv6_endx_sids, sra); + + isis_zebra_srv6_adj_sid_install(sra); +} + +/** + * Add Primary and Backup local SRv6 End.X SID. + * + * @param adj IS-IS Adjacency + */ +void srv6_endx_sid_add(struct isis_adjacency *adj) +{ + srv6_endx_sid_add_single(adj, false, NULL); +} + +/** + * Delete local SRv6 End.X SID. + * + * @param sra SRv6 Adjacency + */ +void srv6_endx_sid_del(struct srv6_adjacency *sra) +{ + struct isis_circuit *circuit = sra->adj->circuit; + struct isis_area *area = circuit->area; + + sr_debug("ISIS-SRv6 (%s): Delete SRv6 End.X SID", area->area_tag); + + isis_zebra_srv6_adj_sid_uninstall(sra); + + /* Release dynamic SRv6 SID and remove subTLVs */ + switch (circuit->circ_type) { + case CIRCUIT_T_BROADCAST: + isis_tlvs_del_srv6_lan_endx_sid(circuit->ext, sra->u.lendx_sid); + break; + case CIRCUIT_T_P2P: + isis_tlvs_del_srv6_endx_sid(circuit->ext, sra->u.endx_sid); + break; + default: + flog_err(EC_LIB_DEVELOPMENT, "%s: unexpected circuit type: %u", + __func__, circuit->circ_type); + exit(1); + } + + if (sra->type == ISIS_SRV6_LAN_BACKUP && sra->backup_nexthops) { + sra->backup_nexthops->del = + (void (*)(void *))isis_nexthop_delete; + list_delete(&sra->backup_nexthops); + } + + /* Remove Adjacency-SID from the SRDB */ + listnode_delete(area->srv6db.srv6_endx_sids, sra); + listnode_delete(sra->adj->srv6_endx_sids, sra); + XFREE(MTYPE_ISIS_SRV6_INFO, sra); +} + +/** + * Lookup SRv6 End.X SID by type. + * + * @param adj IS-IS Adjacency + * @param type SRv6 End.X SID type + */ +struct srv6_adjacency *isis_srv6_endx_sid_find(struct isis_adjacency *adj, + enum srv6_adj_type type) +{ + struct srv6_adjacency *sra; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(adj->srv6_endx_sids, node, sra)) + if (sra->type == type) + return sra; + + return NULL; +} + +/** + * Remove all SRv6 End.X SIDs associated to an adjacency that is going down. + * + * @param adj IS-IS Adjacency + * + * @return 0 + */ +static int srv6_adj_state_change(struct isis_adjacency *adj) +{ + struct srv6_adjacency *sra; + struct listnode *node, *nnode; + + if (!adj->circuit->area->srv6db.config.enabled) + return 0; + + if (adj->adj_state == ISIS_ADJ_UP) + return 0; + + for (ALL_LIST_ELEMENTS(adj->srv6_endx_sids, node, nnode, sra)) + srv6_endx_sid_del(sra); + + return 0; +} + +/** + * When IS-IS Adjacency got one or more IPv6 addresses, add new + * IPv6 address to corresponding SRv6 End.X SID accordingly. + * + * @param adj IS-IS Adjacency + * @param family Inet Family (IPv4 or IPv6) + * @param global Indicate if it concerns the Local or Global IPv6 addresses + * + * @return 0 + */ +static int srv6_adj_ip_enabled(struct isis_adjacency *adj, int family, + bool global) +{ + if (!adj->circuit->area->srv6db.config.enabled || global || + family != AF_INET6) + return 0; + + srv6_endx_sid_add(adj); + + return 0; +} + +/** + * When IS-IS Adjacency doesn't have any IPv6 addresses anymore, + * delete the corresponding SRv6 End.X SID(s) accordingly. + * + * @param adj IS-IS Adjacency + * @param family Inet Family (IPv4 or IPv6) + * @param global Indicate if it concerns the Local or Global IPv6 addresses + * + * @return 0 + */ +static int srv6_adj_ip_disabled(struct isis_adjacency *adj, int family, + bool global) +{ + struct srv6_adjacency *sra; + struct listnode *node, *nnode; + + if (!adj->circuit->area->srv6db.config.enabled || global || + family != AF_INET6) + return 0; + + for (ALL_LIST_ELEMENTS(adj->srv6_endx_sids, node, nnode, sra)) + srv6_endx_sid_del(sra); + + return 0; +} + +/** + * Show Segment Routing over IPv6 (SRv6) 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 isis_lsp *lsp; + struct ttable *tt; + + vty_out(vty, " IS-IS %s SRv6-Nodes:\n\n", circuit_t2string(level)); + + /* Prepare table. */ + tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]); + ttable_add_row( + tt, + "System ID|Algorithm|SRH Max SL|SRH Max End Pop|SRH Max H.encaps|SRH Max End D"); + tt->style.cell.rpad = 2; + tt->style.corner = '+'; + ttable_restyle(tt); + ttable_rowseps(tt, 0, BOTTOM, true, '-'); + + frr_each (lspdb, &area->lspdb[level - 1], lsp) { + struct isis_router_cap *cap; + + if (!lsp->tlvs) + continue; + cap = lsp->tlvs->router_cap; + if (!cap) + continue; + + ttable_add_row(tt, "%pSY|%s|%u|%u|%u|%u", lsp->hdr.lsp_id, + cap->algo[0] == SR_ALGORITHM_SPF ? "SPF" + : "S-SPF", + cap->srv6_msd.max_seg_left_msd, + cap->srv6_msd.max_end_pop_msd, + cap->srv6_msd.max_h_encaps_msd, + cap->srv6_msd.max_end_d_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_srv6_node, show_srv6_node_cmd, + "show " PROTO_NAME " segment-routing srv6 node", + SHOW_STR + PROTO_HELP + "Segment-Routing\n" + "Segment-Routing over IPv6 (SRv6)\n" + "SRv6 node\n") +{ + struct listnode *node, *inode; + struct isis_area *area; + struct isis *isis; + + for (ALL_LIST_ELEMENTS_RO(im->isis, inode, isis)) { + for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { + vty_out(vty, "Area %s:\n", + area->area_tag ? area->area_tag : "null"); + if (!area->srv6db.config.enabled) { + vty_out(vty, " SRv6 is disabled\n"); + continue; + } + for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; + level++) + show_node(vty, area, level); + } + } + + return CMD_SUCCESS; +} + +int isis_srv6_ifp_up_notify(struct interface *ifp) +{ + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + struct listnode *node, *node2; + struct isis_area *area; + struct isis_srv6_sid *sid; + + if (!isis) + return 0; + + /* Walk through all areas of the ISIS instance */ + for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { + /* Skip area, if SRv6 is not enabled */ + if (!area->srv6db.config.enabled) + continue; + + /* Skip area if the interface is not the one configured for SRv6 */ + if (strncmp(area->srv6db.config.srv6_ifname, ifp->name, IF_NAMESIZE)) + continue; + + sr_debug("Interface %s went up. Installing SIDs for area %s in data plane", ifp->name, area->area_tag); + + /* Walk through all SIDs and re-install them into the data plane with the newly configured interface */ + for (ALL_LIST_ELEMENTS_RO(area->srv6db.srv6_sids, node2, sid)) { + sr_debug("Installing SID %pI6 from the data plane", &sid->sid); + isis_zebra_srv6_sid_install(area, sid); + } + } + + return 0; +} + +/** + * IS-IS SRv6 initialization for given area. + * + * @param area IS-IS area + */ +void isis_srv6_area_init(struct isis_area *area) +{ + struct isis_srv6_db *srv6db; + + if (!area) + return; + + srv6db = &area->srv6db; + + sr_debug("ISIS-SRv6 (%s): Initialize Segment Routing SRv6 DB", + area->area_tag); + + /* Initialize SRv6 Data Base */ + memset(srv6db, 0, sizeof(*srv6db)); + srv6db->srv6_endx_sids = list_new(); + + /* Pull defaults from the YANG module */ +#ifndef FABRICD + srv6db->config.enabled = yang_get_default_bool("%s/enabled", ISIS_SRV6); + srv6db->config.max_seg_left_msd = + yang_get_default_uint8("%s/msd/node-msd/max-segs-left", + ISIS_SRV6); + srv6db->config.max_end_pop_msd = + yang_get_default_uint8("%s/msd/node-msd/max-end-pop", ISIS_SRV6); + srv6db->config.max_h_encaps_msd = + yang_get_default_uint8("%s/msd/node-msd/max-h-encaps", + ISIS_SRV6); + srv6db->config.max_end_d_msd = + yang_get_default_uint8("%s/msd/node-msd/max-end-d", ISIS_SRV6); + strlcpy(srv6db->config.srv6_ifname, yang_get_default_string("%s/interface", ISIS_SRV6), sizeof(srv6db->config.srv6_ifname)); +#else + srv6db->config.enabled = false; + srv6db->config.max_seg_left_msd = ISIS_DEFAULT_SRV6_MAX_SEG_LEFT_MSD; + srv6db->config.max_end_pop_msd = ISIS_DEFAULT_SRV6_MAX_END_POP_MSD; + srv6db->config.max_h_encaps_msd = ISIS_DEFAULT_SRV6_MAX_H_ENCAPS_MSD; + srv6db->config.max_end_d_msd = ISIS_DEFAULT_SRV6_MAX_END_D_MSD; + strlcpy(srv6db->config.srv6_ifname, ISIS_DEFAULT_SRV6_IFNAME, sizeof(srv6db->config.srv6_ifname)); +#endif + + /* Initialize SRv6 Locator chunks list */ + srv6db->srv6_locator_chunks = list_new(); + + /* Initialize SRv6 SIDs list */ + srv6db->srv6_sids = list_new(); + srv6db->srv6_sids->del = (void (*)(void *))isis_srv6_sid_free; +} + +/** + * Terminate IS-IS SRv6 for the given area. + * + * @param area IS-IS area + */ +void isis_srv6_area_term(struct isis_area *area) +{ + struct isis_srv6_db *srv6db = &area->srv6db; + struct srv6_adjacency *sra; + struct listnode *node, *nnode; + struct srv6_locator_chunk *chunk; + + sr_debug("ISIS-SRv6 (%s): Terminate SRv6", area->area_tag); + + /* Uninstall all local SRv6 End.X SIDs */ + if (area->srv6db.config.enabled) + for (ALL_LIST_ELEMENTS(area->srv6db.srv6_endx_sids, node, nnode, + sra)) + srv6_endx_sid_del(sra); + + /* Free SRv6 Locator chunks list */ + for (ALL_LIST_ELEMENTS(srv6db->srv6_locator_chunks, node, nnode, chunk)) + srv6_locator_chunk_free(&chunk); + list_delete(&srv6db->srv6_locator_chunks); + + /* Free SRv6 SIDs list */ + list_delete(&srv6db->srv6_sids); + list_delete(&srv6db->srv6_endx_sids); +} + +/** + * IS-IS SRv6 global initialization. + */ +void isis_srv6_init(void) +{ + install_element(VIEW_NODE, &show_srv6_node_cmd); + + /* Register hooks. */ + hook_register(isis_adj_state_change_hook, srv6_adj_state_change); + hook_register(isis_adj_ip_enabled_hook, srv6_adj_ip_enabled); + hook_register(isis_adj_ip_disabled_hook, srv6_adj_ip_disabled); +} + +/** + * IS-IS SRv6 global terminate. + */ +void isis_srv6_term(void) +{ + /* Unregister hooks. */ + hook_unregister(isis_adj_state_change_hook, srv6_adj_state_change); + hook_unregister(isis_adj_ip_enabled_hook, srv6_adj_ip_enabled); + hook_unregister(isis_adj_ip_disabled_hook, srv6_adj_ip_disabled); +} |