2022-11-28 10:34:10 +01:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
/*
|
|
|
|
* This is an implementation of Segment Routing over IPv6 (SRv6) for IS-IS
|
|
|
|
* as per RFC 9352
|
|
|
|
* https://datatracker.ietf.org/doc/html/rfc9352
|
|
|
|
*
|
|
|
|
* Copyright (C) 2023 Carmine Scarpitta - University of Rome Tor Vergata
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <zebra.h>
|
2023-01-16 11:24:29 +01:00
|
|
|
|
2023-02-02 12:30:35 +01:00
|
|
|
#include "srv6.h"
|
2023-02-02 11:29:09 +01:00
|
|
|
#include "termtable.h"
|
|
|
|
|
2023-01-16 11:24:29 +01:00
|
|
|
#include "isisd/isisd.h"
|
2023-02-02 11:29:09 +01:00
|
|
|
#include "isisd/isis_misc.h"
|
2023-01-16 11:24:29 +01:00
|
|
|
#include "isisd/isis_srv6.h"
|
2023-03-10 23:50:46 +01:00
|
|
|
#include "isisd/isis_zebra.h"
|
|
|
|
|
2023-03-14 02:42:23 +01:00
|
|
|
/* Local variables and functions */
|
|
|
|
DEFINE_MTYPE_STATIC(ISISD, ISIS_SRV6_SID, "ISIS SRv6 Segment ID");
|
|
|
|
|
2023-03-10 23:50:46 +01:00
|
|
|
/**
|
|
|
|
* Unset the SRv6 locator for a given IS-IS area.
|
|
|
|
*
|
|
|
|
* @param area IS-IS area
|
|
|
|
*
|
|
|
|
* @result True on success, False otherwise
|
|
|
|
*/
|
|
|
|
bool isis_srv6_locator_unset(struct isis_area *area)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct listnode *node, *nnode;
|
|
|
|
struct srv6_locator_chunk *chunk;
|
|
|
|
|
|
|
|
if (strncmp(area->srv6db.config.srv6_locator_name, "",
|
|
|
|
sizeof(area->srv6db.config.srv6_locator_name)) == 0) {
|
|
|
|
sr_debug("SRv6 locator not set");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Inform Zebra that we are releasing the SRv6 locator */
|
|
|
|
ret = isis_zebra_srv6_manager_release_locator_chunk(
|
|
|
|
area->srv6db.config.srv6_locator_name);
|
|
|
|
if (ret < 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/* Delete chunks */
|
|
|
|
for (ALL_LIST_ELEMENTS(area->srv6db.srv6_locator_chunks, node, nnode,
|
|
|
|
chunk)) {
|
|
|
|
sr_debug(
|
|
|
|
"Releasing chunk of locator %s (prefix %pFX) for IS-IS area %s",
|
|
|
|
area->srv6db.config.srv6_locator_name, &chunk->prefix,
|
|
|
|
area->area_tag);
|
|
|
|
|
|
|
|
listnode_delete(area->srv6db.srv6_locator_chunks, chunk);
|
|
|
|
srv6_locator_chunk_free(&chunk);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Clear locator name */
|
|
|
|
memset(area->srv6db.config.srv6_locator_name, 0,
|
|
|
|
sizeof(area->srv6db.config.srv6_locator_name));
|
|
|
|
|
|
|
|
/* Regenerate LSPs to advertise that the SRv6 locator no longer exists
|
|
|
|
*/
|
|
|
|
lsp_regenerate_schedule(area, area->is_type, 0);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2023-01-16 11:24:29 +01:00
|
|
|
|
2023-03-15 08:49:39 +01:00
|
|
|
void isis_srv6_sid_free(struct isis_srv6_sid *sid)
|
|
|
|
{
|
|
|
|
XFREE(MTYPE_ISIS_SRV6_SID, sid);
|
|
|
|
}
|
|
|
|
|
2023-02-02 11:29:09 +01:00
|
|
|
/**
|
|
|
|
* Show Segment Routing over IPv6 (SRv6) Node.
|
|
|
|
*
|
|
|
|
* @param vty VTY output
|
|
|
|
* @param area IS-IS area
|
|
|
|
* @param level IS-IS level
|
|
|
|
*/
|
|
|
|
static void show_node(struct vty *vty, struct isis_area *area, int level)
|
|
|
|
{
|
|
|
|
struct isis_lsp *lsp;
|
|
|
|
struct ttable *tt;
|
|
|
|
|
|
|
|
vty_out(vty, " IS-IS %s SRv6-Nodes:\n\n", circuit_t2string(level));
|
|
|
|
|
|
|
|
/* Prepare table. */
|
|
|
|
tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
|
|
|
|
ttable_add_row(
|
|
|
|
tt,
|
|
|
|
"System ID|Algorithm|SRH Max SL|SRH Max End Pop|SRH Max H.encaps|SRH Max End D");
|
|
|
|
tt->style.cell.rpad = 2;
|
|
|
|
tt->style.corner = '+';
|
|
|
|
ttable_restyle(tt);
|
|
|
|
ttable_rowseps(tt, 0, BOTTOM, true, '-');
|
|
|
|
|
|
|
|
frr_each (lspdb, &area->lspdb[level - 1], lsp) {
|
|
|
|
struct isis_router_cap *cap;
|
|
|
|
|
|
|
|
if (!lsp->tlvs)
|
|
|
|
continue;
|
|
|
|
cap = lsp->tlvs->router_cap;
|
|
|
|
if (!cap)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
ttable_add_row(tt, "%pSY|%s|%u|%u|%u|%u", lsp->hdr.lsp_id,
|
|
|
|
cap->algo[0] == SR_ALGORITHM_SPF ? "SPF"
|
|
|
|
: "S-SPF",
|
|
|
|
cap->srv6_msd.max_seg_left_msd,
|
|
|
|
cap->srv6_msd.max_end_pop_msd,
|
|
|
|
cap->srv6_msd.max_h_encaps_msd,
|
|
|
|
cap->srv6_msd.max_end_d_msd);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Dump the generated table. */
|
|
|
|
if (tt->nrows > 1) {
|
|
|
|
char *table;
|
|
|
|
|
|
|
|
table = ttable_dump(tt, "\n");
|
|
|
|
vty_out(vty, "%s\n", table);
|
|
|
|
XFREE(MTYPE_TMP, table);
|
|
|
|
}
|
|
|
|
ttable_del(tt);
|
|
|
|
}
|
|
|
|
|
|
|
|
DEFUN(show_srv6_node, show_srv6_node_cmd,
|
|
|
|
"show " PROTO_NAME " segment-routing srv6 node",
|
|
|
|
SHOW_STR
|
|
|
|
PROTO_HELP
|
|
|
|
"Segment-Routing\n"
|
|
|
|
"Segment-Routing over IPv6 (SRv6)\n"
|
|
|
|
"SRv6 node\n")
|
|
|
|
{
|
|
|
|
struct listnode *node, *inode;
|
|
|
|
struct isis_area *area;
|
|
|
|
struct isis *isis;
|
|
|
|
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(im->isis, inode, isis)) {
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) {
|
|
|
|
vty_out(vty, "Area %s:\n",
|
|
|
|
area->area_tag ? area->area_tag : "null");
|
|
|
|
if (!area->srv6db.config.enabled) {
|
|
|
|
vty_out(vty, " SRv6 is disabled\n");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS;
|
|
|
|
level++)
|
|
|
|
show_node(vty, area, level);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return CMD_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2023-01-16 11:24:29 +01:00
|
|
|
/**
|
|
|
|
* IS-IS SRv6 initialization for given area.
|
|
|
|
*
|
|
|
|
* @param area IS-IS area
|
|
|
|
*/
|
|
|
|
void isis_srv6_area_init(struct isis_area *area)
|
|
|
|
{
|
|
|
|
struct isis_srv6_db *srv6db;
|
|
|
|
|
|
|
|
if (!area)
|
|
|
|
return;
|
|
|
|
|
|
|
|
srv6db = &area->srv6db;
|
|
|
|
|
|
|
|
sr_debug("ISIS-SRv6 (%s): Initialize Segment Routing SRv6 DB",
|
|
|
|
area->area_tag);
|
|
|
|
|
|
|
|
/* Initialize SRv6 Data Base */
|
|
|
|
memset(srv6db, 0, sizeof(*srv6db));
|
2023-01-16 12:03:32 +01:00
|
|
|
|
|
|
|
/* Pull defaults from the YANG module */
|
|
|
|
srv6db->config.enabled = yang_get_default_bool("%s/enabled", ISIS_SRV6);
|
2023-01-18 12:21:16 +01:00
|
|
|
|
|
|
|
srv6db->config.max_seg_left_msd = SRV6_MAX_SEG_LEFT;
|
|
|
|
srv6db->config.max_end_pop_msd = SRV6_MAX_END_POP;
|
|
|
|
srv6db->config.max_h_encaps_msd = SRV6_MAX_H_ENCAPS;
|
|
|
|
srv6db->config.max_end_d_msd = SRV6_MAX_END_D;
|
2023-02-02 12:30:35 +01:00
|
|
|
|
|
|
|
/* Initialize SRv6 Locator chunks list */
|
|
|
|
srv6db->srv6_locator_chunks = list_new();
|
2023-02-02 12:32:10 +01:00
|
|
|
|
|
|
|
/* Initialize SRv6 SIDs list */
|
|
|
|
srv6db->srv6_sids = list_new();
|
|
|
|
srv6db->srv6_sids->del = (void (*)(void *))isis_srv6_sid_free;
|
2023-01-16 11:24:29 +01:00
|
|
|
}
|
2022-11-28 10:59:35 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Terminate IS-IS SRv6 for the given area.
|
|
|
|
*
|
|
|
|
* @param area IS-IS area
|
|
|
|
*/
|
|
|
|
void isis_srv6_area_term(struct isis_area *area)
|
|
|
|
{
|
2023-02-02 12:30:35 +01:00
|
|
|
struct isis_srv6_db *srv6db = &area->srv6db;
|
|
|
|
struct listnode *node, *nnode;
|
|
|
|
struct srv6_locator_chunk *chunk;
|
|
|
|
|
2022-11-28 10:59:35 +01:00
|
|
|
sr_debug("ISIS-SRv6 (%s): Terminate SRv6", area->area_tag);
|
2023-02-02 12:30:35 +01:00
|
|
|
|
|
|
|
/* Free SRv6 Locator chunks list */
|
|
|
|
for (ALL_LIST_ELEMENTS(srv6db->srv6_locator_chunks, node, nnode, chunk))
|
|
|
|
srv6_locator_chunk_free(&chunk);
|
|
|
|
list_delete(&srv6db->srv6_locator_chunks);
|
2023-02-02 12:32:10 +01:00
|
|
|
|
|
|
|
/* Free SRv6 SIDs list */
|
|
|
|
list_delete(&srv6db->srv6_sids);
|
2022-11-28 10:59:35 +01:00
|
|
|
}
|
2022-11-30 15:06:42 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* IS-IS SRv6 global initialization.
|
|
|
|
*/
|
|
|
|
void isis_srv6_init(void)
|
|
|
|
{
|
2023-02-02 11:29:09 +01:00
|
|
|
install_element(VIEW_NODE, &show_srv6_node_cmd);
|
2022-11-30 15:06:42 +01:00
|
|
|
}
|
2022-11-30 15:09:00 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* IS-IS SRv6 global terminate.
|
|
|
|
*/
|
|
|
|
void isis_srv6_term(void)
|
|
|
|
{
|
|
|
|
}
|