isisd: Add support for SRv6 Adjacency SIDs

An SRv6 adjacency SID is a SID that is associated with a particular
adjacency. Adjacency SIDs are advertised using the SRv6 End.X SID
Sub-TLV (RFC 9352 section #8.1) or SRv6 LAN End.X SID Sub-TLV (RFC 9352
section #8.2).

This commit defines the following Adj SIDs management functions:
* srv6_endx_sid_add_single: add a new SRv6 Adjacency SID
* srv6_endx_sid_del: delete an SRv6 Adjacency SID
* isis_srv6_endx_sid_find: lookup SRv6 End.X SID by type

It also attaches some callbacks to the hooks isis_adj_state_change_hook,
isis_adj_ip_enabled_hook, isis_adj_ip_disabled_hook, which are
responsible for installing/removing an SRv6 Adjacency SID automatically
when the state of an IS-IS adjacency changes.

Signed-off-by: Carmine Scarpitta <carmine.scarpitta@uniroma2.it>
This commit is contained in:
Carmine Scarpitta 2023-06-03 02:08:08 +02:00
parent 7db1a90475
commit ecb2675f1e
6 changed files with 375 additions and 0 deletions

View File

@ -98,6 +98,8 @@ struct isis_adjacency {
struct list *adj_sids; /* Segment Routing Adj-SIDs. */
uint32_t snmp_idx;
struct listnode *snmp_list_node;
struct list *srv6_endx_sids; /* SRv6 End.X SIDs. */
};
struct isis_threeway_adj;

View File

@ -11,14 +11,18 @@
#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.
@ -97,6 +101,7 @@ bool isis_srv6_locator_unset(struct isis_area *area)
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) {
@ -119,6 +124,10 @@ bool isis_srv6_locator_unset(struct isis_area *area)
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);
@ -298,6 +307,296 @@ 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.
*
@ -444,6 +743,11 @@ void isis_srv6_area_term(struct isis_area *area)
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);
}
/**
@ -451,4 +755,8 @@ void isis_srv6_init(void)
*/
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);
}

View File

@ -120,6 +120,9 @@ struct isis_srv6_db {
/* List of SRv6 SIDs allocated by the IS-IS instance */
struct list *srv6_sids;
/* List of SRv6 End.X SIDs allocated by the IS-IS instance */
struct list *srv6_endx_sids;
/* Area SRv6 configuration. */
struct {
/* Administrative status of SRv6 */
@ -164,4 +167,12 @@ void isis_srv6_end_sid2subtlv(const struct isis_srv6_sid *sid,
void isis_srv6_locator2tlv(const struct isis_srv6_locator *loc,
struct isis_srv6_locator_tlv *loc_tlv);
void srv6_endx_sid_add_single(struct isis_adjacency *adj, bool backup,
struct list *nexthops);
void srv6_endx_sid_add(struct isis_adjacency *adj);
void srv6_endx_sid_del(struct srv6_adjacency *sra);
struct srv6_adjacency *isis_srv6_endx_sid_find(struct isis_adjacency *adj,
enum srv6_adj_type type);
void isis_area_delete_backup_srv6_endx_sids(struct isis_area *area, int level);
#endif /* _FRR_ISIS_SRV6_H */

View File

@ -7913,6 +7913,44 @@ void isis_tlvs_del_lan_adj_sid(struct isis_ext_subtlvs *exts,
UNSET_SUBTLV(exts, EXT_LAN_ADJ_SID);
}
/* Add IS-IS SRv6 End.X SID subTLVs */
void isis_tlvs_add_srv6_endx_sid(struct isis_ext_subtlvs *exts,
struct isis_srv6_endx_sid_subtlv *adj)
{
append_item(&exts->srv6_endx_sid, (struct isis_item *)adj);
SET_SUBTLV(exts, EXT_SRV6_ENDX_SID);
}
/* Delete IS-IS SRv6 End.X SID subTLVs */
void isis_tlvs_del_srv6_endx_sid(struct isis_ext_subtlvs *exts,
struct isis_srv6_endx_sid_subtlv *adj)
{
isis_free_subsubtlvs(adj->subsubtlvs);
delete_item(&exts->srv6_endx_sid, (struct isis_item *)adj);
XFREE(MTYPE_ISIS_SUBTLV, adj);
if (exts->srv6_endx_sid.count == 0)
UNSET_SUBTLV(exts, EXT_SRV6_ENDX_SID);
}
/* Add IS-IS SRv6 LAN End.X SID subTLVs */
void isis_tlvs_add_srv6_lan_endx_sid(struct isis_ext_subtlvs *exts,
struct isis_srv6_lan_endx_sid_subtlv *lan)
{
append_item(&exts->srv6_lan_endx_sid, (struct isis_item *)lan);
SET_SUBTLV(exts, EXT_SRV6_LAN_ENDX_SID);
}
/* Delete IS-IS SRv6 LAN End.X SID subTLVs */
void isis_tlvs_del_srv6_lan_endx_sid(struct isis_ext_subtlvs *exts,
struct isis_srv6_lan_endx_sid_subtlv *lan)
{
isis_free_subsubtlvs(lan->subsubtlvs);
delete_item(&exts->srv6_lan_endx_sid, (struct isis_item *)lan);
XFREE(MTYPE_ISIS_SUBTLV, lan);
if (exts->srv6_lan_endx_sid.count == 0)
UNSET_SUBTLV(exts, EXT_SRV6_LAN_ENDX_SID);
}
void isis_tlvs_del_asla_flex_algo(struct isis_ext_subtlvs *ext,
struct isis_asla_subtlvs *asla)
{

View File

@ -891,4 +891,13 @@ void isis_subtlvs_add_srv6_end_sid(struct isis_subtlvs *subtlvs,
struct isis_srv6_sid *sid);
void isis_tlvs_add_srv6_locator(struct isis_tlvs *tlvs, uint16_t mtid,
struct isis_srv6_locator *loc);
void isis_tlvs_add_srv6_endx_sid(struct isis_ext_subtlvs *exts,
struct isis_srv6_endx_sid_subtlv *adj);
void isis_tlvs_del_srv6_endx_sid(struct isis_ext_subtlvs *exts,
struct isis_srv6_endx_sid_subtlv *adj);
void isis_tlvs_add_srv6_lan_endx_sid(struct isis_ext_subtlvs *exts,
struct isis_srv6_lan_endx_sid_subtlv *lan);
void isis_tlvs_del_srv6_lan_endx_sid(struct isis_ext_subtlvs *exts,
struct isis_srv6_lan_endx_sid_subtlv *lan);
#endif

View File

@ -1126,6 +1126,7 @@ static int isis_zebra_process_srv6_locator_chunk(ZAPI_CALLBACK_ARGS)
struct srv6_locator_chunk *c;
struct srv6_locator_chunk *chunk = srv6_locator_chunk_alloc();
struct isis_srv6_sid *sid;
struct isis_adjacency *adj;
enum srv6_endpoint_behavior_codepoint behavior;
bool allocated = false;
@ -1181,6 +1182,12 @@ static int isis_zebra_process_srv6_locator_chunk(ZAPI_CALLBACK_ARGS)
/* Store the SID */
listnode_add(area->srv6db.srv6_sids, sid);
/* Create SRv6 End.X SIDs from existing IS-IS Adjacencies */
for (ALL_LIST_ELEMENTS_RO(area->adjacency_list, node, adj)) {
if (adj->ll_ipv6_count > 0)
srv6_endx_sid_add(adj);
}
/* Regenerate LSPs to advertise the new locator and the SID */
lsp_regenerate_schedule(area, area->is_type, 0);