summaryrefslogtreecommitdiffstats
path: root/isisd
diff options
context:
space:
mode:
authorCarmine Scarpitta <carmine.scarpitta@uniroma2.it>2023-06-03 02:08:08 +0200
committerCarmine Scarpitta <carmine.scarpitta@uniroma2.it>2023-09-11 22:11:47 +0200
commitecb2675f1ef4819f3e6f9e729ad0f6e2a59da898 (patch)
tree01e55cb00f70de803431691373d7e895ae5e0851 /isisd
parentisisd: Add functions to install/remove adj SIDs (diff)
downloadfrr-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.h2
-rw-r--r--isisd/isis_srv6.c308
-rw-r--r--isisd/isis_srv6.h11
-rw-r--r--isisd/isis_tlvs.c38
-rw-r--r--isisd/isis_tlvs.h9
-rw-r--r--isisd/isis_zebra.c7
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);