diff options
| author | Carmine Scarpitta <carmine.scarpitta@uniroma2.it> | 2023-06-03 02:08:08 +0200 | 
|---|---|---|
| committer | Carmine Scarpitta <carmine.scarpitta@uniroma2.it> | 2023-09-11 22:11:47 +0200 | 
| commit | ecb2675f1ef4819f3e6f9e729ad0f6e2a59da898 (patch) | |
| tree | 01e55cb00f70de803431691373d7e895ae5e0851 /isisd/isis_srv6.c | |
| parent | 7db1a9047553ab11a10c15350d0fa8f1688476ec (diff) | |
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>
Diffstat (limited to 'isisd/isis_srv6.c')
| -rw-r--r-- | isisd/isis_srv6.c | 308 | 
1 files changed, 308 insertions, 0 deletions
diff --git a/isisd/isis_srv6.c b/isisd/isis_srv6.c index a26552d4e9..d1983693d8 100644 --- a/isisd/isis_srv6.c +++ b/isisd/isis_srv6.c @@ -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); @@ -299,6 +308,296 @@ void isis_srv6_sid_free(struct 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 @@ -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);  }  | 
