// 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 #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); } } /** * 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_TTABLE, 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); }