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 | |
parent | isisd: Add functions to install/remove adj SIDs (diff) | |
download | frr-ecb2675f1ef4819f3e6f9e729ad0f6e2a59da898.tar.xz frr-ecb2675f1ef4819f3e6f9e729ad0f6e2a59da898.zip |
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')
-rw-r--r-- | isisd/isis_adjacency.h | 2 | ||||
-rw-r--r-- | isisd/isis_srv6.c | 308 | ||||
-rw-r--r-- | isisd/isis_srv6.h | 11 | ||||
-rw-r--r-- | isisd/isis_tlvs.c | 38 | ||||
-rw-r--r-- | isisd/isis_tlvs.h | 9 | ||||
-rw-r--r-- | isisd/isis_zebra.c | 7 |
6 files changed, 375 insertions, 0 deletions
diff --git a/isisd/isis_adjacency.h b/isisd/isis_adjacency.h index c0c8e6814..dc181055f 100644 --- a/isisd/isis_adjacency.h +++ b/isisd/isis_adjacency.h @@ -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; diff --git a/isisd/isis_srv6.c b/isisd/isis_srv6.c index a26552d4e..d1983693d 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); } diff --git a/isisd/isis_srv6.h b/isisd/isis_srv6.h index 5db844737..c6eb0739e 100644 --- a/isisd/isis_srv6.h +++ b/isisd/isis_srv6.h @@ -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 */ diff --git a/isisd/isis_tlvs.c b/isisd/isis_tlvs.c index fb88aa13d..cd7b3b884 100644 --- a/isisd/isis_tlvs.c +++ b/isisd/isis_tlvs.c @@ -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) { diff --git a/isisd/isis_tlvs.h b/isisd/isis_tlvs.h index fee0b5a36..6ecd4c5f6 100644 --- a/isisd/isis_tlvs.h +++ b/isisd/isis_tlvs.h @@ -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 diff --git a/isisd/isis_zebra.c b/isisd/isis_zebra.c index 2428ab95b..cce0bd061 100644 --- a/isisd/isis_zebra.c +++ b/isisd/isis_zebra.c @@ -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); |