summaryrefslogtreecommitdiff
path: root/isisd/isis_srv6.c
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/isis_srv6.c
parent7db1a9047553ab11a10c15350d0fa8f1688476ec (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.c308
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);
}