mirror_frr/isisd/isis_srv6.c
Carmine Scarpitta 7c203a05bf isisd: Release SRv6 SIDs to SID Manager
Currently, IS-IS allocates SIDs without interacting with Zebra.

Recently, the SRv6 implementation has been improved. Now, the daemons
need to interact with Zebra through ZAPI to obtain and release SIDs.

This commit extends IS-IS to release SIDs to Zebra when they are no
longer needed.

Signed-off-by: Carmine Scarpitta <cscarpit@cisco.com>
2024-06-18 18:33:29 +02:00

879 lines
24 KiB
C

// 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;
struct srv6_sid_ctx ctx = {};
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);
/*
* Inform the SID Manager that IS-IS will no longer use the SID, so
* that the SID Manager can remove the SID context ownership from IS-IS
* and release/free the SID context if it is not yes by other protocols.
*/
ctx.behavior = ZEBRA_SEG6_LOCAL_ACTION_END;
isis_zebra_release_srv6_sid(&ctx);
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)) {
/*
* Inform the SID Manager that IS-IS will no longer use the SID, so
* that the SID Manager can remove the SID context ownership from IS-IS
* and release/free the SID context if it is not yes by other protocols.
*/
ctx.behavior = ZEBRA_SEG6_LOCAL_ACTION_END_X;
ctx.nh6 = sra->nexthop;
isis_zebra_release_srv6_sid(&ctx);
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 */
srv6_locator_free(area->srv6db.srv6_locator);
area->srv6db.srv6_locator = NULL;
/* 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 locator SRv6 locator
* @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 is full
*/
static struct in6_addr srv6_locator_request_sid(struct isis_area *area,
struct srv6_locator *locator,
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 || !locator)
return in6addr_any;
sr_debug("ISIS-SRv6 (%s): requested new SID from locator %s",
area->area_tag, 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 = locator->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 = locator->function_bits_length;
offset = locator->block_bits_length + locator->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, 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 locator SRv6 locator
* @param behavior SRv6 Endpoint Behavior bound to the SID
* @param sid_value SRv6 SID value
*
* @result the allocated SID on success, NULL otherwise
*/
struct isis_srv6_sid *
isis_srv6_sid_alloc(struct isis_area *area, struct srv6_locator *locator,
enum srv6_endpoint_behavior_codepoint behavior,
struct in6_addr *sid_value)
{
struct isis_srv6_sid *sid = NULL;
if (!area || !locator || !sid_value)
return NULL;
sid = XCALLOC(MTYPE_ISIS_SRV6_SID, sizeof(struct isis_srv6_sid));
sid->sid = *sid_value;
sid->behavior = behavior;
sid->structure.loc_block_len = locator->block_bits_length;
sid->structure.loc_node_len = locator->node_bits_length;
sid->structure.func_len = locator->function_bits_length;
sid->structure.arg_len = locator->argument_bits_length;
sid->locator = locator;
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_ADJ_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)
* @param sid_value SID value associated to be associated with the adjacency
*/
void srv6_endx_sid_add_single(struct isis_adjacency *adj, bool backup,
struct list *nexthops, struct in6_addr *sid_value)
{
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 *locator;
uint32_t behavior;
if (!area || !area->srv6db.srv6_locator)
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;
locator = area->srv6db.srv6_locator;
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(locator->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_ADJ_BACKUP : ISIS_SRV6_ADJ_NORMAL;
sra->behavior = behavior;
sra->locator = locator;
sra->structure.loc_block_len = locator->block_bits_length;
sra->structure.loc_node_len = locator->node_bits_length;
sra->structure.func_len = locator->function_bits_length;
sra->structure.arg_len = locator->argument_bits_length;
sra->nexthop = nexthop;
sra->sid = *sid_value;
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, struct in6_addr *sid_value)
{
srv6_endx_sid_add_single(adj, false, NULL, sid_value);
}
/**
* 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_ADJ_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;
isis_zebra_request_srv6_sid_endx(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);
srv6_locator_free(area->srv6db.srv6_locator);
area->srv6db.srv6_locator = NULL;
/* 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);
}