summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--isisd/isis_adjacency.c68
-rw-r--r--isisd/isis_adjacency.h4
-rw-r--r--isisd/isis_cli.c4
-rw-r--r--isisd/isis_errors.c6
-rw-r--r--isisd/isis_errors.h1
-rw-r--r--isisd/isis_lsp.c66
-rw-r--r--isisd/isis_main.c19
-rw-r--r--isisd/isis_nb.c3
-rw-r--r--isisd/isis_nb.h14
-rw-r--r--isisd/isis_nb_config.c262
-rw-r--r--isisd/isis_route.c3
-rw-r--r--isisd/isis_route.h2
-rw-r--r--isisd/isis_spf.c2
-rw-r--r--isisd/isis_sr.c1525
-rw-r--r--isisd/isis_sr.h259
-rw-r--r--isisd/isis_tlvs.c35
-rw-r--r--isisd/isis_tlvs.h9
-rw-r--r--isisd/isis_zebra.c280
-rw-r--r--isisd/isis_zebra.h16
-rw-r--r--isisd/isisd.c43
-rw-r--r--isisd/isisd.h8
-rw-r--r--isisd/subdir.am3
-rw-r--r--lib/mpls.h3
-rw-r--r--zebra/zebra_mpls.h7
24 files changed, 2531 insertions, 111 deletions
diff --git a/isisd/isis_adjacency.c b/isisd/isis_adjacency.c
index 4e0ee4448..acfe3e2e1 100644
--- a/isisd/isis_adjacency.c
+++ b/isisd/isis_adjacency.c
@@ -93,6 +93,7 @@ struct isis_adjacency *isis_new_adj(const uint8_t *id, const uint8_t *snpa,
.last_dis_change = time(NULL);
}
}
+ adj->adj_sids = list_new();
return adj;
}
@@ -122,6 +123,44 @@ struct isis_adjacency *isis_adj_lookup_snpa(const uint8_t *ssnpa,
return NULL;
}
+bool isis_adj_exists(const struct isis_area *area, int level,
+ const uint8_t *sysid)
+{
+ struct isis_circuit *circuit;
+ struct listnode *node;
+
+ for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) {
+ struct isis_adjacency *adj;
+ struct listnode *anode;
+ struct list *adjdb;
+
+ switch (circuit->circ_type) {
+ case CIRCUIT_T_BROADCAST:
+ adjdb = circuit->u.bc.adjdb[level - 1];
+ if (!adjdb)
+ continue;
+
+ for (ALL_LIST_ELEMENTS_RO(adjdb, anode, adj)) {
+ if (!memcmp(adj->sysid, sysid, ISIS_SYS_ID_LEN))
+ return true;
+ }
+ break;
+ case CIRCUIT_T_P2P:
+ adj = circuit->u.p2p.neighbor;
+ if (!adj)
+ break;
+
+ if (!memcmp(adj->sysid, sysid, ISIS_SYS_ID_LEN))
+ return true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return false;
+}
+
DEFINE_HOOK(isis_adj_state_change_hook, (struct isis_adjacency *adj), (adj))
void isis_delete_adj(void *arg)
@@ -145,6 +184,7 @@ void isis_delete_adj(void *arg)
XFREE(MTYPE_ISIS_ADJACENCY_INFO, adj->ipv6_addresses);
adj_mt_finish(adj);
+ list_delete(&adj->adj_sids);
XFREE(MTYPE_ISIS_ADJACENCY, adj);
return;
@@ -441,6 +481,9 @@ void isis_adj_print_vty(struct isis_adjacency *adj, struct vty *vty,
}
if (detail == ISIS_UI_LEVEL_DETAIL) {
+ struct sr_adjacency *sra;
+ struct listnode *anode;
+
level = adj->level;
vty_out(vty, "\n");
if (adj->circuit)
@@ -529,6 +572,31 @@ void isis_adj_print_vty(struct isis_adjacency *adj, struct vty *vty,
vty_out(vty, " %s\n", buf);
}
}
+ for (ALL_LIST_ELEMENTS_RO(adj->adj_sids, anode, sra)) {
+ const char *adj_type;
+ const char *backup;
+ uint32_t sid;
+
+ switch (sra->adj->circuit->circ_type) {
+ case CIRCUIT_T_BROADCAST:
+ adj_type = "LAN Adjacency-SID";
+ sid = sra->u.ladj_sid->sid;
+ break;
+ case CIRCUIT_T_P2P:
+ adj_type = "Adjacency-SID";
+ sid = sra->u.adj_sid->sid;
+ break;
+ default:
+ continue;
+ }
+ backup = (sra->type == ISIS_SR_LAN_BACKUP) ? " (backup)"
+ : "";
+
+ vty_out(vty, " %s %s%s: %u\n",
+ (sra->nexthop.family == AF_INET) ? "IPv4"
+ : "IPv6",
+ adj_type, backup, sid);
+ }
vty_out(vty, "\n");
}
return;
diff --git a/isisd/isis_adjacency.h b/isisd/isis_adjacency.h
index 834eba792..ec17446ad 100644
--- a/isisd/isis_adjacency.h
+++ b/isisd/isis_adjacency.h
@@ -69,6 +69,7 @@ struct isis_dis_record {
};
struct bfd_session;
+struct isis_area;
struct isis_adjacency {
uint8_t snpa[ETH_ALEN]; /* NeighbourSNPAAddress */
@@ -103,6 +104,7 @@ struct isis_adjacency {
uint16_t *mt_set; /* Topologies this adjacency is valid for */
unsigned int mt_count; /* Number of entries in mt_set */
struct bfd_session *bfd_session;
+ struct list *adj_sids; /* Segment Routing Adj-SIDs. */
};
struct isis_threeway_adj;
@@ -111,6 +113,8 @@ struct isis_adjacency *isis_adj_lookup(const uint8_t *sysid,
struct list *adjdb);
struct isis_adjacency *isis_adj_lookup_snpa(const uint8_t *ssnpa,
struct list *adjdb);
+bool isis_adj_exists(const struct isis_area *area, int level,
+ const uint8_t *sysid);
struct isis_adjacency *isis_new_adj(const uint8_t *id, const uint8_t *snpa,
int level, struct isis_circuit *circuit);
void isis_delete_adj(void *adj);
diff --git a/isisd/isis_cli.c b/isisd/isis_cli.c
index 0101e30ad..c421750a8 100644
--- a/isisd/isis_cli.c
+++ b/isisd/isis_cli.c
@@ -1480,7 +1480,7 @@ DEFPY (isis_sr_prefix_sid,
isis_sr_prefix_sid_cmd,
"segment-routing prefix\
<A.B.C.D/M|X:X::X:X/M>$prefix\
- <absolute$sid_type (16000-1048575)$sid_value|index$sid_type (0-65535)$sid_value>\
+ <absolute$sid_type (16-1048575)$sid_value|index$sid_type (0-65535)$sid_value>\
[<no-php-flag|explicit-null>$lh_behavior]",
SR_STR
"Prefix SID\n"
@@ -1518,7 +1518,7 @@ DEFPY (isis_sr_prefix_sid,
DEFPY (no_isis_sr_prefix_sid,
no_isis_sr_prefix_sid_cmd,
"no segment-routing prefix <A.B.C.D/M|X:X::X:X/M>$prefix\
- [<absolute$sid_type (16000-1048575)|index (0-65535)> [<no-php-flag|explicit-null>]]",
+ [<absolute$sid_type (16-1048575)|index (0-65535)> [<no-php-flag|explicit-null>]]",
NO_STR
SR_STR
"Prefix SID\n"
diff --git a/isisd/isis_errors.c b/isisd/isis_errors.c
index 755e70dbf..7530d0b96 100644
--- a/isisd/isis_errors.c
+++ b/isisd/isis_errors.c
@@ -38,6 +38,12 @@ static struct log_ref ferr_isis_err[] = {
.suggestion = "Ensure configuration is correct"
},
{
+ .code = EC_ISIS_SID_OVERFLOW,
+ .title = "SID index overflow",
+ .description = "Isis has detected that a SID index falls outside of its associated SRGB range",
+ .suggestion = "Configure a larger SRGB"
+ },
+ {
.code = END_FERR,
}
};
diff --git a/isisd/isis_errors.h b/isisd/isis_errors.h
index 073273760..d5674dbf3 100644
--- a/isisd/isis_errors.h
+++ b/isisd/isis_errors.h
@@ -26,6 +26,7 @@
enum isis_log_refs {
EC_ISIS_PACKET = ISIS_FERR_START,
EC_ISIS_CONFIG,
+ EC_ISIS_SID_OVERFLOW,
};
extern void isis_error_init(void);
diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c
index effd19ed7..e578f616f 100644
--- a/isisd/isis_lsp.c
+++ b/isisd/isis_lsp.c
@@ -55,6 +55,7 @@
#include "isisd/isis_mt.h"
#include "isisd/isis_tlvs.h"
#include "isisd/isis_te.h"
+#include "isisd/isis_sr.h"
#include "isisd/fabricd.h"
#include "isisd/isis_tx_queue.h"
#include "isisd/isis_nb.h"
@@ -763,9 +764,15 @@ static void lsp_build_ext_reach_ipv4(struct isis_lsp *lsp,
if (area->oldmetric)
isis_tlvs_add_oldstyle_ip_reach(lsp->tlvs, ipv4,
metric);
- if (area->newmetric)
- isis_tlvs_add_extended_ip_reach(lsp->tlvs, ipv4,
- metric);
+ if (area->newmetric) {
+ struct sr_prefix_cfg *pcfg = NULL;
+
+ if (area->srdb.enabled)
+ pcfg = isis_sr_cfg_prefix_find(area, ipv4);
+
+ isis_tlvs_add_extended_ip_reach(lsp->tlvs, ipv4, metric,
+ true, pcfg);
+ }
}
}
@@ -792,9 +799,14 @@ static void lsp_build_ext_reach_ipv6(struct isis_lsp *lsp,
metric = MAX_WIDE_PATH_METRIC;
if (!src_p || !src_p->prefixlen) {
+ struct sr_prefix_cfg *pcfg = NULL;
+
+ if (area->srdb.enabled)
+ pcfg = isis_sr_cfg_prefix_find(area, p);
+
isis_tlvs_add_ipv6_reach(lsp->tlvs,
isis_area_ipv6_topology(area),
- p, metric);
+ p, metric, true, pcfg);
} else if (isis_area_ipv6_dstsrc_enabled(area)) {
isis_tlvs_add_ipv6_dstsrc_reach(lsp->tlvs,
ISIS_MT_IPV6_DSTSRC,
@@ -910,6 +922,33 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area)
area->area_tag);
}
+ /* Add Router Capability TLV. */
+ if (isis->router_id != 0) {
+ struct isis_router_cap cap = {};
+
+ cap.router_id.s_addr = isis->router_id;
+
+ /* Add SR Sub-TLVs if SR is enabled. */
+ if (area->srdb.enabled) {
+ struct isis_sr_db *srdb = &area->srdb;
+ uint32_t range_size;
+
+ range_size = srdb->config.srgb_upper_bound
+ - srdb->config.srgb_lower_bound + 1;
+ cap.srgb.flags = ISIS_SUBTLV_SRGB_FLAG_I
+ | ISIS_SUBTLV_SRGB_FLAG_V;
+ cap.srgb.range_size = range_size;
+ cap.srgb.lower_bound = srdb->config.srgb_lower_bound;
+ cap.algo[0] = SR_ALGORITHM_SPF;
+ cap.algo[1] = SR_ALGORITHM_UNSET;
+ cap.msd = srdb->config.msd;
+ }
+
+ isis_tlvs_set_router_capability(lsp->tlvs, &cap);
+ lsp_debug("ISIS (%s): Adding Router Capabilities information",
+ area->area_tag);
+ }
+
/* IPv4 address and TE router ID TLVs.
* In case of the first one we don't follow "C" vendor,
* but "J" vendor behavior - one IPv4 address is put
@@ -996,13 +1035,21 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area)
}
if (area->newmetric) {
+ struct sr_prefix_cfg *pcfg = NULL;
+
lsp_debug(
"ISIS (%s): Adding te-style IP reachability for %s",
area->area_tag,
prefix2str(ipv4, buf,
sizeof(buf)));
+
+ if (area->srdb.enabled)
+ pcfg = isis_sr_cfg_prefix_find(
+ area, ipv4);
+
isis_tlvs_add_extended_ip_reach(
- lsp->tlvs, ipv4, metric);
+ lsp->tlvs, ipv4, metric, false,
+ pcfg);
}
}
}
@@ -1014,14 +1061,21 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area)
for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_non_link,
ipnode, ipv6)) {
+ struct sr_prefix_cfg *pcfg = NULL;
+
lsp_debug(
"ISIS (%s): Adding IPv6 reachability for %s",
area->area_tag,
prefix2str(ipv6, buf, sizeof(buf)));
+
+ if (area->srdb.enabled)
+ pcfg = isis_sr_cfg_prefix_find(area,
+ ipv6);
+
isis_tlvs_add_ipv6_reach(
lsp->tlvs,
isis_area_ipv6_topology(area), ipv6,
- metric);
+ metric, false, pcfg);
}
}
diff --git a/isisd/isis_main.c b/isisd/isis_main.c
index 4c841dffe..78654b2f1 100644
--- a/isisd/isis_main.c
+++ b/isisd/isis_main.c
@@ -83,7 +83,9 @@ struct zebra_privs_t isisd_privs = {
.cap_num_i = 0};
/* isisd options */
-struct option longopts[] = {{0}};
+static const struct option longopts[] = {
+ {"int_num", required_argument, NULL, 'I'},
+ {0}};
/* Master of threads. */
struct thread_master *master;
@@ -99,6 +101,7 @@ void sigusr1(void);
static __attribute__((__noreturn__)) void terminate(int i)
{
+ isis_sr_term();
isis_zebra_stop();
exit(i);
}
@@ -196,13 +199,16 @@ FRR_DAEMON_INFO(isisd, ISIS, .vty_port = ISISD_VTY_PORT,
int main(int argc, char **argv, char **envp)
{
int opt;
+ int instance = 1;
#ifdef FABRICD
frr_preinit(&fabricd_di, argc, argv);
#else
frr_preinit(&isisd_di, argc, argv);
#endif
- frr_opt_add("", longopts, "");
+ frr_opt_add(
+ "I:", longopts,
+ " -I, --int_num Set instance number (label-manager)\n");
/* Command line argument treatment. */
while (1) {
@@ -214,6 +220,12 @@ int main(int argc, char **argv, char **envp)
switch (opt) {
case 0:
break;
+ case 'I':
+ instance = atoi(optarg);
+ if (instance < 1 || instance > (unsigned short)-1)
+ zlog_err("Instance %i out of range (1..%u)",
+ instance, (unsigned short)-1);
+ break;
default:
frr_help_exit(1);
break;
@@ -242,13 +254,14 @@ int main(int argc, char **argv, char **envp)
isis_redist_init();
isis_route_map_init();
isis_mpls_te_init();
+ isis_sr_init();
lsp_init();
mt_init();
/* create the global 'isis' instance */
isis_new(1, VRF_DEFAULT);
- isis_zebra_init(master);
+ isis_zebra_init(master, instance);
isis_bfd_init();
fabricd_init();
diff --git a/isisd/isis_nb.c b/isisd/isis_nb.c
index 2dedebf98..8e976d9bc 100644
--- a/isisd/isis_nb.c
+++ b/isisd/isis_nb.c
@@ -464,6 +464,7 @@ const struct frr_yang_module_info frr_isisd_info = {
{
.xpath = "/frr-isisd:isis/instance/segment-routing/srgb",
.cbs = {
+ .apply_finish = isis_instance_segment_routing_srgb_apply_finish,
.cli_show = cli_show_isis_srgb,
},
},
@@ -492,6 +493,8 @@ const struct frr_yang_module_info frr_isisd_info = {
.cbs = {
.create = isis_instance_segment_routing_prefix_sid_map_prefix_sid_create,
.destroy = isis_instance_segment_routing_prefix_sid_map_prefix_sid_destroy,
+ .pre_validate = isis_instance_segment_routing_prefix_sid_map_prefix_sid_pre_validate,
+ .apply_finish = isis_instance_segment_routing_prefix_sid_map_prefix_sid_apply_finish,
.cli_show = cli_show_isis_prefix_sid,
},
},
diff --git a/isisd/isis_nb.h b/isisd/isis_nb.h
index 19ebee0e0..58f6c3892 100644
--- a/isisd/isis_nb.h
+++ b/isisd/isis_nb.h
@@ -183,11 +183,11 @@ int isis_instance_segment_routing_srgb_upper_bound_modify(
int isis_instance_segment_routing_msd_node_msd_modify(
struct nb_cb_modify_args *args);
int isis_instance_segment_routing_msd_node_msd_destroy(
- struct nb_cb_modify_args *args);
+ struct nb_cb_destroy_args *args);
int isis_instance_segment_routing_prefix_sid_map_prefix_sid_create(
- struct nb_cb_modify_args *args);
+ struct nb_cb_create_args *args);
int isis_instance_segment_routing_prefix_sid_map_prefix_sid_destroy(
- struct nb_cb_modify_args *args);
+ struct nb_cb_destroy_args *args);
int isis_instance_segment_routing_prefix_sid_map_prefix_sid_sid_value_type_modify(
struct nb_cb_modify_args *args);
int isis_instance_segment_routing_prefix_sid_map_prefix_sid_sid_value_modify(
@@ -279,6 +279,10 @@ struct yang_data *
lib_interface_isis_event_counters_authentication_fails_get_elem(
struct nb_cb_get_elem_args *args);
+/* Optional 'pre_validate' callbacks. */
+int isis_instance_segment_routing_prefix_sid_map_prefix_sid_pre_validate(
+ struct nb_cb_pre_validate_args *args);
+
/* Optional 'apply_finish' callbacks. */
void ietf_backoff_delay_apply_finish(struct nb_cb_apply_finish_args *args);
void area_password_apply_finish(struct nb_cb_apply_finish_args *args);
@@ -291,6 +295,10 @@ void default_info_origin_ipv6_apply_finish(
void redistribute_apply_finish(const struct lyd_node *dnode, int family);
void redistribute_ipv4_apply_finish(struct nb_cb_apply_finish_args *args);
void redistribute_ipv6_apply_finish(struct nb_cb_apply_finish_args *args);
+void isis_instance_segment_routing_srgb_apply_finish(
+ struct nb_cb_apply_finish_args *args);
+void isis_instance_segment_routing_prefix_sid_map_prefix_sid_apply_finish(
+ struct nb_cb_apply_finish_args *args);
/* Optional 'cli_show' callbacks. */
void cli_show_router_isis(struct vty *vty, struct lyd_node *dnode,
diff --git a/isisd/isis_nb_config.c b/isisd/isis_nb_config.c
index f97202c9a..ec3b7baa3 100644
--- a/isisd/isis_nb_config.c
+++ b/isisd/isis_nb_config.c
@@ -1405,35 +1405,78 @@ int isis_instance_mpls_te_router_address_destroy(
/*
* XPath: /frr-isisd:isis/instance/segment-routing/enabled
*/
-int isis_instance_segment_routing_enabled_modify(enum nb_event event,
- const struct lyd_node *dnode,
- union nb_resource *resource)
+int isis_instance_segment_routing_enabled_modify(
+ struct nb_cb_modify_args *args)
{
- switch (event) {
- case NB_EV_VALIDATE:
- case NB_EV_PREPARE:
- case NB_EV_ABORT:
- case NB_EV_APPLY:
- /* TODO: implement me. */
- break;
+ struct isis_area *area;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ area = nb_running_get_entry(args->dnode, NULL, true);
+ area->srdb.config.enabled = yang_dnode_get_bool(args->dnode, NULL);
+
+ if (area->srdb.config.enabled) {
+ if (IS_DEBUG_ISIS(DEBUG_EVENTS))
+ zlog_debug("SR: Segment Routing: OFF -> ON");
+
+ if (isis_sr_start(area) == 0)
+ area->srdb.enabled = true;
+ } else {
+ if (IS_DEBUG_ISIS(DEBUG_EVENTS))
+ zlog_debug("SR: Segment Routing: ON -> OFF");
+
+ isis_sr_stop(area);
+ area->srdb.enabled = false;
}
return NB_OK;
}
/*
+ * XPath: /frr-isisd:isis/instance/segment-routing/srgb
+ */
+void isis_instance_segment_routing_srgb_apply_finish(
+ struct nb_cb_apply_finish_args *args)
+{
+ struct isis_area *area;
+ uint32_t lower_bound, upper_bound;
+ int ret;
+
+ area = nb_running_get_entry(args->dnode, NULL, true);
+ lower_bound = yang_dnode_get_uint32(args->dnode, "./lower-bound");
+ upper_bound = yang_dnode_get_uint32(args->dnode, "./upper-bound");
+
+ ret = isis_sr_cfg_srgb_update(area, lower_bound, upper_bound);
+ if (area->srdb.config.enabled) {
+ if (ret == 0)
+ area->srdb.enabled = true;
+ else {
+ isis_sr_stop(area);
+ area->srdb.enabled = false;
+ }
+ }
+}
+
+/*
* XPath: /frr-isisd:isis/instance/segment-routing/srgb/lower-bound
*/
int isis_instance_segment_routing_srgb_lower_bound_modify(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource)
+ struct nb_cb_modify_args *args)
{
- switch (event) {
+ uint32_t lower_bound = yang_dnode_get_uint32(args->dnode, NULL);
+
+ switch (args->event) {
case NB_EV_VALIDATE:
+ if (!IS_MPLS_UNRESERVED_LABEL(lower_bound)) {
+ zlog_warn("Invalid SRGB lower bound: %" PRIu32,
+ lower_bound);
+ return NB_ERR_VALIDATION;
+ }
+ break;
case NB_EV_PREPARE:
case NB_EV_ABORT:
case NB_EV_APPLY:
- /* TODO: implement me. */
break;
}
@@ -1444,15 +1487,21 @@ int isis_instance_segment_routing_srgb_lower_bound_modify(
* XPath: /frr-isisd:isis/instance/segment-routing/srgb/upper-bound
*/
int isis_instance_segment_routing_srgb_upper_bound_modify(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource)
+ struct nb_cb_modify_args *args)
{
- switch (event) {
+ uint32_t upper_bound = yang_dnode_get_uint32(args->dnode, NULL);
+
+ switch (args->event) {
case NB_EV_VALIDATE:
+ if (!IS_MPLS_UNRESERVED_LABEL(upper_bound)) {
+ zlog_warn("Invalid SRGB upper bound: %" PRIu32,
+ upper_bound);
+ return NB_ERR_VALIDATION;
+ }
+ break;
case NB_EV_PREPARE:
case NB_EV_ABORT:
case NB_EV_APPLY:
- /* TODO: implement me. */
break;
}
@@ -1463,32 +1512,31 @@ int isis_instance_segment_routing_srgb_upper_bound_modify(
* XPath: /frr-isisd:isis/instance/segment-routing/msd/node-msd
*/
int isis_instance_segment_routing_msd_node_msd_modify(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource)
+ struct nb_cb_modify_args *args)
{
- switch (event) {
- case NB_EV_VALIDATE:
- case NB_EV_PREPARE:
- case NB_EV_ABORT:
- case NB_EV_APPLY:
- /* TODO: implement me. */
- break;
- }
+ struct isis_area *area;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ area = nb_running_get_entry(args->dnode, NULL, true);
+ area->srdb.config.msd = yang_dnode_get_uint8(args->dnode, NULL);
+ isis_sr_cfg_msd_update(area);
return NB_OK;
}
int isis_instance_segment_routing_msd_node_msd_destroy(
- enum nb_event event, const struct lyd_node *dnode)
+ struct nb_cb_destroy_args *args)
{
- switch (event) {
- case NB_EV_VALIDATE:
- case NB_EV_PREPARE:
- case NB_EV_ABORT:
- case NB_EV_APPLY:
- /* TODO: implement me. */
- break;
- }
+ struct isis_area *area;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ area = nb_running_get_entry(args->dnode, NULL, true);
+ area->srdb.config.msd = 0;
+ isis_sr_cfg_msd_update(area);
return NB_OK;
}
@@ -1497,52 +1545,102 @@ int isis_instance_segment_routing_msd_node_msd_destroy(
* XPath: /frr-isisd:isis/instance/segment-routing/prefix-sid-map/prefix-sid
*/
int isis_instance_segment_routing_prefix_sid_map_prefix_sid_create(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource)
+ struct nb_cb_create_args *args)
{
- switch (event) {
- case NB_EV_VALIDATE:
- case NB_EV_PREPARE:
- case NB_EV_ABORT:
- case NB_EV_APPLY:
- /* TODO: implement me. */
- break;
- }
+ struct isis_area *area;
+ struct prefix prefix;
+ struct sr_prefix_cfg *pcfg;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ area = nb_running_get_entry(args->dnode, NULL, true);
+ yang_dnode_get_prefix(&prefix, args->dnode, "./prefix");
+
+ pcfg = isis_sr_cfg_prefix_add(area, &prefix);
+ nb_running_set_entry(args->dnode, pcfg);
return NB_OK;
}
int isis_instance_segment_routing_prefix_sid_map_prefix_sid_destroy(
- enum nb_event event, const struct lyd_node *dnode)
+ struct nb_cb_destroy_args *args)
{
- switch (event) {
- case NB_EV_VALIDATE:
- case NB_EV_PREPARE:
- case NB_EV_ABORT:
- case NB_EV_APPLY:
- /* TODO: implement me. */
+ struct sr_prefix_cfg *pcfg;
+ struct isis_area *area;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ pcfg = nb_running_unset_entry(args->dnode);
+ area = pcfg->area;
+ isis_sr_cfg_prefix_del(pcfg);
+ lsp_regenerate_schedule(area, area->is_type, 0);
+
+ return NB_OK;
+}
+
+int isis_instance_segment_routing_prefix_sid_map_prefix_sid_pre_validate(
+ struct nb_cb_pre_validate_args *args)
+{
+ uint32_t srgb_lbound;
+ uint32_t srgb_ubound;
+ uint32_t srgb_range;
+ uint32_t sid;
+ enum sr_sid_value_type sid_type;
+
+ srgb_lbound = yang_dnode_get_uint32(args->dnode,
+ "../../srgb/lower-bound");
+ srgb_ubound = yang_dnode_get_uint32(args->dnode,
+ "../../srgb/upper-bound");
+ sid = yang_dnode_get_uint32(args->dnode, "./sid-value");
+ sid_type = yang_dnode_get_enum(args->dnode, "./sid-value-type");
+
+ srgb_range = srgb_ubound - srgb_lbound + 1;
+ switch (sid_type) {
+ case SR_SID_VALUE_TYPE_INDEX:
+ if (sid >= srgb_range) {
+ zlog_warn("SID index %u falls outside local SRGB range",
+ sid);
+ return NB_ERR_VALIDATION;
+ }
+ break;
+ case SR_SID_VALUE_TYPE_ABSOLUTE:
+ if (!IS_MPLS_UNRESERVED_LABEL(sid)) {
+ zlog_warn("Invalid absolute SID %u", sid);
+ return NB_ERR_VALIDATION;
+ }
break;
}
return NB_OK;
}
+void isis_instance_segment_routing_prefix_sid_map_prefix_sid_apply_finish(
+ struct nb_cb_apply_finish_args *args)
+{
+ struct sr_prefix_cfg *pcfg;
+ struct isis_area *area;
+
+ pcfg = nb_running_get_entry(args->dnode, NULL, true);
+ area = pcfg->area;
+ lsp_regenerate_schedule(area, area->is_type, 0);
+}
+
/*
* XPath:
* /frr-isisd:isis/instance/segment-routing/prefix-sid-map/prefix-sid/sid-value-type
*/
int isis_instance_segment_routing_prefix_sid_map_prefix_sid_sid_value_type_modify(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource)
+ struct nb_cb_modify_args *args)
{
- switch (event) {
- case NB_EV_VALIDATE:
- case NB_EV_PREPARE:
- case NB_EV_ABORT:
- case NB_EV_APPLY:
- /* TODO: implement me. */
- break;
- }
+ struct sr_prefix_cfg *pcfg;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ pcfg = nb_running_get_entry(args->dnode, NULL, true);
+ pcfg->sid_type = yang_dnode_get_enum(args->dnode, NULL);
return NB_OK;
}
@@ -1552,17 +1650,15 @@ int isis_instance_segment_routing_prefix_sid_map_prefix_sid_sid_value_type_modif
* /frr-isisd:isis/instance/segment-routing/prefix-sid-map/prefix-sid/sid-value
*/
int isis_instance_segment_routing_prefix_sid_map_prefix_sid_sid_value_modify(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource)
+ struct nb_cb_modify_args *args)
{
- switch (event) {
- case NB_EV_VALIDATE:
- case NB_EV_PREPARE:
- case NB_EV_ABORT:
- case NB_EV_APPLY:
- /* TODO: implement me. */
- break;
- }
+ struct sr_prefix_cfg *pcfg;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ pcfg = nb_running_get_entry(args->dnode, NULL, true);
+ pcfg->sid = yang_dnode_get_uint32(args->dnode, NULL);
return NB_OK;
}
@@ -1572,17 +1668,15 @@ int isis_instance_segment_routing_prefix_sid_map_prefix_sid_sid_value_modify(
* /frr-isisd:isis/instance/segment-routing/prefix-sid-map/prefix-sid/last-hop-behavior
*/
int isis_instance_segment_routing_prefix_sid_map_prefix_sid_last_hop_behavior_modify(
- enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource)
+ struct nb_cb_modify_args *args)
{
- switch (event) {
- case NB_EV_VALIDATE:
- case NB_EV_PREPARE:
- case NB_EV_ABORT:
- case NB_EV_APPLY:
- /* TODO: implement me. */
- break;
- }
+ struct sr_prefix_cfg *pcfg;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ pcfg = nb_running_get_entry(args->dnode, NULL, true);
+ pcfg->last_hop_behavior = yang_dnode_get_enum(args->dnode, NULL);
return NB_OK;
}
diff --git a/isisd/isis_route.c b/isisd/isis_route.c
index 516e3f90b..fa6af6c21 100644
--- a/isisd/isis_route.c
+++ b/isisd/isis_route.c
@@ -70,6 +70,7 @@ static struct isis_nexthop *isis_nexthop_create(int family, union g_addr *ip,
nexthop->family = family;
nexthop->ifindex = ifindex;
nexthop->ip = *ip;
+ isis_sr_nexthop_reset(&nexthop->sr);
return nexthop;
}
@@ -129,6 +130,7 @@ static void adjinfo2nexthop(int family, struct list *nexthops,
nh = isis_nexthop_create(
AF_INET, &ip,
adj->circuit->interface->ifindex);
+ memcpy(nh->sysid, adj->sysid, sizeof(nh->sysid));
listnode_add(nexthops, nh);
break;
}
@@ -143,6 +145,7 @@ static void adjinfo2nexthop(int family, struct list *nexthops,
nh = isis_nexthop_create(
AF_INET6, &ip,
adj->circuit->interface->ifindex);
+ memcpy(nh->sysid, adj->sysid, sizeof(nh->sysid));
listnode_add(nexthops, nh);
break;
}
diff --git a/isisd/isis_route.h b/isisd/isis_route.h
index 96bcdc350..0356668d7 100644
--- a/isisd/isis_route.h
+++ b/isisd/isis_route.h
@@ -31,6 +31,8 @@ struct isis_nexthop {
ifindex_t ifindex;
int family;
union g_addr ip;
+ uint8_t sysid[ISIS_SYS_ID_LEN];
+ struct sr_nexthop_info sr;
};
struct isis_route_info {
diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c
index 30b9f8861..3091650ef 100644
--- a/isisd/isis_spf.c
+++ b/isisd/isis_spf.c
@@ -1214,6 +1214,8 @@ static int isis_run_spf_cb(struct thread *thread)
isis_area_verify_routes(area);
+ isis_area_verify_sr(area);
+
/* walk all circuits and reset any spf specific flags */
struct listnode *node;
struct isis_circuit *circuit;
diff --git a/isisd/isis_sr.c b/isisd/isis_sr.c
new file mode 100644
index 000000000..54689ea48
--- /dev/null
+++ b/isisd/isis_sr.c
@@ -0,0 +1,1525 @@
+/*
+ * This is an implementation of Segment Routing for IS-IS
+ * as per draft draft-ietf-isis-segment-routing-extensions-25
+ *
+ * Copyright (C) 2019 Orange Labs http://www.orange.com
+ *
+ * Author: Olivier Dugeon <olivier.dugeon@orange.com>
+ * Contributor: Renato Westphal <renato@opensourcerouting.org> for NetDEF
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "if.h"
+#include "linklist.h"
+#include "log.h"
+#include "command.h"
+#include "termtable.h"
+#include "memory.h"
+#include "prefix.h"
+#include "table.h"
+#include "vty.h"
+#include "zclient.h"
+#include "lib/lib_errors.h"
+
+#include "isisd/isisd.h"
+#include "isisd/isis_spf.h"
+#include "isisd/isis_spf_private.h"
+#include "isisd/isis_adjacency.h"
+#include "isisd/isis_route.h"
+#include "isisd/isis_mt.h"
+#include "isisd/isis_sr.h"
+#include "isisd/isis_tlvs.h"
+#include "isisd/isis_misc.h"
+#include "isisd/isis_zebra.h"
+#include "isisd/isis_errors.h"
+
+DEFINE_MTYPE_STATIC(ISISD, ISIS_SR_INFO, "ISIS segment routing information")
+
+static void isis_sr_prefix_uninstall(struct sr_prefix *srp);
+static void isis_sr_prefix_reinstall(struct sr_prefix *srp,
+ bool replace_semantics);
+
+/*----------------------------------------------------------------------------*/
+
+static inline int sr_prefix_sid_compare(const struct sr_prefix *a,
+ const struct sr_prefix *b)
+{
+ return prefix_cmp(&a->prefix, &b->prefix);
+}
+DECLARE_RBTREE_UNIQ(tree_sr_node_prefix, struct sr_prefix, node_entry,
+ sr_prefix_sid_compare)
+DECLARE_RBTREE_UNIQ(tree_sr_area_prefix, struct sr_prefix, area_entry,
+ sr_prefix_sid_compare)
+
+static inline int sr_prefix_sid_cfg_compare(const struct sr_prefix_cfg *a,
+ const struct sr_prefix_cfg *b)
+{
+ return prefix_cmp(&a->prefix, &b->prefix);
+}
+DECLARE_RBTREE_UNIQ(tree_sr_prefix_cfg, struct sr_prefix_cfg, entry,
+ sr_prefix_sid_cfg_compare)
+
+static inline int sr_node_compare(const struct sr_node *a,
+ const struct sr_node *b)
+{
+ return memcmp(a->sysid, b->sysid, ISIS_SYS_ID_LEN);
+}
+DECLARE_RBTREE_UNIQ(tree_sr_node, struct sr_node, entry, sr_node_compare)
+
+/*----------------------------------------------------------------------------*/
+
+/* Returns true if the interface/address pair corresponds to a Node-SID. */
+static bool isis_sr_prefix_is_node_sid(const struct interface *ifp,
+ const struct prefix *prefix)
+{
+ return (if_is_loopback(ifp) && is_host_route(prefix));
+}
+
+/* Handle changes in the local SRGB configuration. */
+int isis_sr_cfg_srgb_update(struct isis_area *area, uint32_t lower_bound,
+ uint32_t upper_bound)
+{
+ struct isis_sr_db *srdb = &area->srdb;
+
+ /* First release the old SRGB. */
+ if (srdb->config.enabled)
+ isis_zebra_release_label_range(srdb->config.srgb_lower_bound,
+ srdb->config.srgb_upper_bound);
+
+ srdb->config.srgb_lower_bound = lower_bound;
+ srdb->config.srgb_upper_bound = upper_bound;
+
+ if (srdb->enabled) {
+ struct sr_prefix *srp;
+
+ /* Request new SRGB if SR is enabled. */
+ if (isis_zebra_request_label_range(
+ srdb->config.srgb_lower_bound,
+ srdb->config.srgb_upper_bound
+ - srdb->config.srgb_lower_bound + 1))
+ return -1;
+
+ /* Reinstall local Prefix-SIDs to update their input labels. */
+ for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) {
+ frr_each (tree_sr_area_prefix,
+ &area->srdb.prefix_sids[level - 1], srp) {
+ isis_sr_prefix_reinstall(srp, false);
+ }
+ }
+
+ lsp_regenerate_schedule(area, area->is_type, 0);
+ } else if (srdb->config.enabled) {
+ /* Try to enable SR again using the new SRGB. */
+ if (isis_sr_start(area) == 0)
+ area->srdb.enabled = true;
+ }
+
+ return 0;
+}
+
+/* Handle changes in the local MSD configuration. */
+void isis_sr_cfg_msd_update(struct isis_area *area)
+{
+ lsp_regenerate_schedule(area, area->is_type, 0);
+}
+
+/* Handle new Prefix-SID configuration. */
+struct sr_prefix_cfg *isis_sr_cfg_prefix_add(struct isis_area *area,
+ const struct prefix *prefix)
+{
+ struct sr_prefix_cfg *pcfg;
+ struct interface *ifp;
+
+ pcfg = XCALLOC(MTYPE_ISIS_SR_INFO, sizeof(*pcfg));
+ pcfg->prefix = *prefix;
+ pcfg->area = area;
+
+ /* Pull defaults from the YANG module. */
+ pcfg->sid_type = yang_get_default_enum(
+ "%s/prefix-sid-map/prefix-sid/sid-value-type", ISIS_SR);
+ pcfg->last_hop_behavior = yang_get_default_enum(
+ "%s/prefix-sid-map/prefix-sid/last-hop-behavior", ISIS_SR);
+
+ /* Set the N-flag when appropriate. */
+ ifp = if_lookup_prefix(prefix, VRF_DEFAULT);
+ if (ifp && isis_sr_prefix_is_node_sid(ifp, prefix))
+ pcfg->node_sid = true;
+
+ /* Save prefix-sid configuration. */
+ tree_sr_prefix_cfg_add(&area->srdb.config.prefix_sids, pcfg);
+
+ return pcfg;
+}
+
+/* Handle removal of locally configured Prefix-SID. */
+void isis_sr_cfg_prefix_del(struct sr_prefix_cfg *pcfg)
+{
+ struct isis_area *area;
+
+ area = pcfg->area;
+ tree_sr_prefix_cfg_del(&area->srdb.config.prefix_sids, pcfg);
+ XFREE(MTYPE_ISIS_SR_INFO, pcfg);
+}
+
+/* Lookup Prefix-SID in the local configuration. */
+struct sr_prefix_cfg *isis_sr_cfg_prefix_find(struct isis_area *area,
+ union prefixconstptr prefix)
+{
+ struct sr_prefix_cfg pcfg = {};
+
+ prefix_copy(&pcfg.prefix, prefix.p);
+ return tree_sr_prefix_cfg_find(&area->srdb.config.prefix_sids, &pcfg);
+}
+
+/* Fill in Prefix-SID Sub-TLV according to the corresponding configuration. */
+void isis_sr_prefix_cfg2subtlv(const struct sr_prefix_cfg *pcfg, bool external,
+ struct isis_prefix_sid *psid)
+{
+ /* Set SID algorithm. */
+ psid->algorithm = SR_ALGORITHM_SPF;
+
+ /* Set SID flags. */
+ psid->flags = 0;
+ switch (pcfg->last_hop_behavior) {
+ case SR_LAST_HOP_BEHAVIOR_EXP_NULL:
+ SET_FLAG(psid->flags, ISIS_PREFIX_SID_NO_PHP);
+ SET_FLAG(psid->flags, ISIS_PREFIX_SID_EXPLICIT_NULL);
+ break;
+ case SR_LAST_HOP_BEHAVIOR_NO_PHP:
+ SET_FLAG(psid->flags, ISIS_PREFIX_SID_NO_PHP);
+ UNSET_FLAG(psid->flags, ISIS_PREFIX_SID_EXPLICIT_NULL);
+ break;
+ case SR_LAST_HOP_BEHAVIOR_PHP:
+ UNSET_FLAG(psid->flags, ISIS_PREFIX_SID_NO_PHP);
+ UNSET_FLAG(psid->flags, ISIS_PREFIX_SID_EXPLICIT_NULL);
+ break;
+ }
+ if (external)
+ SET_FLAG(psid->flags, ISIS_PREFIX_SID_READVERTISED);
+ if (pcfg->node_sid)
+ SET_FLAG(psid->flags, ISIS_PREFIX_SID_NODE);
+
+ /* Set SID value. */
+ psid->value = pcfg->sid;
+ if (pcfg->sid_type == SR_SID_VALUE_TYPE_ABSOLUTE) {
+ SET_FLAG(psid->flags, ISIS_PREFIX_SID_VALUE);
+ SET_FLAG(psid->flags, ISIS_PREFIX_SID_LOCAL);
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+static struct sr_prefix *isis_sr_prefix_add(struct isis_area *area,
+ struct sr_node *srn,
+ union prefixconstptr prefix,
+ bool local,
+ const struct isis_prefix_sid *psid)
+{
+ struct sr_prefix *srp;
+
+ srp = XCALLOC(MTYPE_ISIS_SR_INFO, sizeof(*srp));
+ prefix_copy(&srp->prefix, prefix.p);
+ srp->sid = *psid;
+ srp->local_label = MPLS_INVALID_LABEL;
+ if (local) {
+ srp->type = ISIS_SR_PREFIX_LOCAL;
+ isis_sr_nexthop_reset(&srp->u.local.info);
+ } else {
+ srp->type = ISIS_SR_PREFIX_REMOTE;
+ srp->u.remote.rinfo = NULL;
+ }
+ srp->srn = srn;
+ tree_sr_node_prefix_add(&srn->prefix_sids, srp);
+ /* TODO: this might fail if we have Anycast SIDs in the IS-IS area. */
+ tree_sr_area_prefix_add(&area->srdb.prefix_sids[srn->level - 1], srp);
+
+ return srp;
+}
+
+static void isis_sr_prefix_del(struct isis_area *area, struct sr_node *srn,
+ struct sr_prefix *srp)
+{
+ isis_sr_prefix_uninstall(srp);
+ tree_sr_node_prefix_del(&srn->prefix_sids, srp);
+ tree_sr_area_prefix_del(&area->srdb.prefix_sids[srn->level - 1], srp);
+ XFREE(MTYPE_ISIS_SR_INFO, srp);
+}
+
+static struct sr_prefix *isis_sr_prefix_find_area(struct isis_area *area,
+ int level,
+ union prefixconstptr prefix)
+{
+ struct sr_prefix srp = {};
+
+ prefix_copy(&srp.prefix, prefix.p);
+ return tree_sr_area_prefix_find(&area->srdb.prefix_sids[level - 1],
+ &srp);
+}
+
+static struct sr_prefix *isis_sr_prefix_find_node(struct sr_node *srn,
+ union prefixconstptr prefix)
+{
+ struct sr_prefix srp = {};
+
+ prefix_copy(&srp.prefix, prefix.p);
+ return tree_sr_node_prefix_find(&srn->prefix_sids, &srp);
+}
+
+static struct sr_node *isis_sr_node_add(struct isis_area *area, int level,
+ const uint8_t *sysid,
+ const struct isis_router_cap *cap)
+{
+ struct sr_node *srn;
+
+ srn = XCALLOC(MTYPE_ISIS_SR_INFO, sizeof(*srn));
+ srn->level = level;
+ memcpy(srn->sysid, sysid, ISIS_SYS_ID_LEN);
+ srn->cap = *cap;
+ srn->area = area;
+ tree_sr_node_prefix_init(&srn->prefix_sids);
+ tree_sr_node_add(&area->srdb.sr_nodes[level - 1], srn);
+
+ return srn;
+}
+
+static void isis_sr_node_del(struct isis_area *area, int level,
+ struct sr_node *srn)
+{
+ /* Remove and uninstall Prefix-SIDs. */
+ while (tree_sr_node_prefix_count(&srn->prefix_sids) > 0) {
+ struct sr_prefix *srp;
+
+ srp = tree_sr_node_prefix_first(&srn->prefix_sids);
+ isis_sr_prefix_del(area, srn, srp);
+ }
+
+ tree_sr_node_del(&area->srdb.sr_nodes[level - 1], srn);
+ XFREE(MTYPE_ISIS_SR_INFO, srn);
+}
+
+static struct sr_node *isis_sr_node_find(struct isis_area *area, int level,
+ const uint8_t *sysid)
+{
+ struct sr_node srn = {};
+
+ memcpy(srn.sysid, sysid, ISIS_SYS_ID_LEN);
+ return tree_sr_node_find(&area->srdb.sr_nodes[level - 1], &srn);
+}
+
+static void isis_sr_adj_srgb_update(struct isis_area *area, uint8_t *sysid,
+ int level)
+{
+ struct sr_prefix *srp;
+
+ frr_each (tree_sr_area_prefix, &area->srdb.prefix_sids[level - 1],
+ srp) {
+ struct listnode *node;
+ struct isis_nexthop *nh;
+
+ if (srp->type == ISIS_SR_PREFIX_LOCAL)
+ continue;
+
+ if (srp->u.remote.rinfo == NULL)
+ continue;
+
+ for (ALL_LIST_ELEMENTS_RO(srp->u.remote.rinfo->nexthops, node,
+ nh)) {
+ if (memcmp(nh->sysid, sysid, ISIS_SYS_ID_LEN) != 0)
+ continue;
+
+ /*
+ * Reinstall all Prefix-SID nexthops using route replace
+ * semantics.
+ */
+ isis_sr_prefix_reinstall(srp, true);
+ break;
+ }
+ }
+}
+
+void isis_sr_nexthop_update(struct sr_nexthop_info *srnh, mpls_label_t label)
+{
+ srnh->label = label;
+ if (srnh->uptime == 0)
+ srnh->uptime = time(NULL);
+}
+
+void isis_sr_nexthop_reset(struct sr_nexthop_info *srnh)
+{
+ srnh->label = MPLS_INVALID_LABEL;
+ srnh->uptime = 0;
+}
+
+/*----------------------------------------------------------------------------*/
+
+/* Lookup IS-IS route in the SPT. */
+static struct isis_route_info *
+isis_sr_prefix_lookup_route(struct isis_area *area, enum spf_tree_id tree_id,
+ struct sr_prefix *srp)
+{
+ struct route_node *rn;
+ int level = srp->srn->level;
+
+ rn = route_node_lookup(area->spftree[tree_id][level - 1]->route_table,
+ &srp->prefix);
+ if (rn) {
+ route_unlock_node(rn);
+ if (rn->info)
+ return rn->info;
+ }
+
+ return NULL;
+}
+
+/* Calculate Prefix-SID input label. */
+static mpls_label_t isis_sr_prefix_in_label(const struct sr_prefix *srp)
+{
+ const struct sr_node *srn = srp->srn;
+ struct isis_area *area = srn->area;
+
+ /* Absolute SID value. */
+ if (CHECK_FLAG(srp->sid.flags,
+ ISIS_PREFIX_SID_VALUE | ISIS_PREFIX_SID_LOCAL))
+ return srp->sid.value;
+
+ /* Index SID value. */
+ if (srp->sid.value >= (area->srdb.config.srgb_upper_bound
+ - area->srdb.config.srgb_lower_bound + 1)) {
+ flog_warn(EC_ISIS_SID_OVERFLOW,
+ "%s: SID index %u falls outside local SRGB range",
+ __func__, srp->sid.value);
+ return MPLS_INVALID_LABEL;
+ }
+
+ return (area->srdb.config.srgb_lower_bound + srp->sid.value);
+}
+
+/* Calculate Prefix-SID output label. */
+static mpls_label_t isis_sr_prefix_out_label(const struct sr_prefix *srp,
+ const struct sr_node *srn_nexthop,
+ const uint8_t *sysid)
+{
+ const struct sr_node *srn = srp->srn;
+
+ /* Is the adjacency the last hop? */
+ if (memcmp(sysid, srn->sysid, ISIS_SYS_ID_LEN) == 0) {
+ if (!CHECK_FLAG(srp->sid.flags, ISIS_PREFIX_SID_NO_PHP))
+ return MPLS_LABEL_IMPLICIT_NULL;
+
+ if (CHECK_FLAG(srp->sid.flags, ISIS_PREFIX_SID_EXPLICIT_NULL)) {
+ if (srp->prefix.family == AF_INET)
+ return MPLS_LABEL_IPV4_EXPLICIT_NULL;
+ else
+ return MPLS_LABEL_IPV6_EXPLICIT_NULL;
+ }
+ /* Fallthrough */
+ }
+
+ /* Absolute SID value. */
+ if (CHECK_FLAG(srp->sid.flags,
+ ISIS_PREFIX_SID_VALUE | ISIS_PREFIX_SID_LOCAL)) {
+ /*
+ * V/L SIDs have local significance, so only adjacent routers
+ * can use them.
+ */
+ if (srp->srn != srn_nexthop)
+ return MPLS_INVALID_LABEL;
+ return srp->sid.value;
+ }
+
+ /* Index SID value. */
+ if (srp->sid.value >= srn_nexthop->cap.srgb.range_size) {
+ flog_warn(EC_ISIS_SID_OVERFLOW,
+ "%s: SID index %u falls outside remote SRGB range",
+ __func__, srp->sid.value);
+ return MPLS_INVALID_LABEL;
+ }
+
+ return (srn_nexthop->cap.srgb.lower_bound + srp->sid.value);
+}
+
+/* Process local Prefix-SID and install it if possible. */
+static int isis_sr_prefix_install_local(struct sr_prefix *srp)
+{
+ const struct sr_node *srn = srp->srn;
+ struct isis_area *area = srn->area;
+ mpls_label_t local_label;
+
+ /*
+ * No need to install LSP to local Prefix-SID unless the
+ * no-PHP option is configured.
+ */
+ if (!CHECK_FLAG(srp->sid.flags, ISIS_PREFIX_SID_NO_PHP)
+ || CHECK_FLAG(srp->sid.flags, ISIS_PREFIX_SID_EXPLICIT_NULL))
+ return -1;
+
+ if (IS_DEBUG_ISIS(DEBUG_SR)) {
+ zlog_debug("ISIS-SR (%s) installing Prefix-SID %pFX %s %u (%s)",
+ area->area_tag, &srp->prefix,
+ CHECK_FLAG(srp->sid.flags, ISIS_PREFIX_SID_VALUE)
+ ? "label"
+ : "index",
+ srp->sid.value, circuit_t2string(srn->level));
+ zlog_debug(" nexthop self");
+ }
+
+ /* Calculate local label. */
+ local_label = isis_sr_prefix_in_label(srp);
+ if (local_label == MPLS_INVALID_LABEL)
+ return -1;
+
+ /* Update internal state. */
+ srp->local_label = local_label;
+ isis_sr_nexthop_update(&srp->u.local.info, MPLS_LABEL_IMPLICIT_NULL);
+
+ /* Install Prefix-SID in the forwarding plane. */
+ isis_zebra_install_prefix_sid(srp);
+
+ return 0;
+}
+
+/* Process remote Prefix-SID and install it if possible. */
+static int isis_sr_prefix_install_remote(struct sr_prefix *srp)
+{
+ const struct sr_node *srn = srp->srn;
+ struct isis_area *area = srn->area;
+ enum spf_tree_id tree_id;
+ struct listnode *node;
+ struct isis_nexthop *nexthop;
+ mpls_label_t local_label;
+ size_t nexthop_num = 0;
+
+ /* Lookup associated IS-IS route. */
+ tree_id = (srp->prefix.family == AF_INET) ? SPFTREE_IPV4 : SPFTREE_IPV6;
+ srp->u.remote.rinfo = isis_sr_prefix_lookup_route(area, tree_id, srp);
+ if (!srp->u.remote.rinfo)
+ /* SPF hasn't converged for this route yet. */
+ return -1;
+
+ if (IS_DEBUG_ISIS(DEBUG_SR))
+ zlog_debug("ISIS-SR (%s) installing Prefix-SID %pFX %s %u (%s)",
+ area->area_tag, &srp->prefix,
+ CHECK_FLAG(srp->sid.flags, ISIS_PREFIX_SID_VALUE)
+ ? "label"
+ : "index",
+ srp->sid.value, circuit_t2string(srn->level));
+
+ /* Calculate local label. */
+ local_label = isis_sr_prefix_in_label(srp);
+ if (local_label == MPLS_INVALID_LABEL)
+ return -1;
+
+ for (ALL_LIST_ELEMENTS_RO(srp->u.remote.rinfo->nexthops, node,
+ nexthop)) {
+ struct sr_node *srn_nexthop;
+ mpls_label_t remote_label;
+
+ /* Check if the nexthop advertised a SRGB. */
+ srn_nexthop =
+ isis_sr_node_find(area, srn->level, nexthop->sysid);
+ if (!srn_nexthop)
+ goto next;
+
+ /*
+ * Check if the nexthop can handle SR-MPLS encapsulated IPv4 or
+ * IPv6 packets.
+ */
+ if ((nexthop->family == AF_INET
+ && !IS_SR_IPV4(srn_nexthop->cap.srgb))
+ || (nexthop->family == AF_INET6
+ && !IS_SR_IPV6(srn_nexthop->cap.srgb)))
+ goto next;
+
+ remote_label = isis_sr_prefix_out_label(srp, srn_nexthop,
+ nexthop->sysid);
+ if (remote_label == MPLS_INVALID_LABEL)
+ goto next;
+
+ if (IS_DEBUG_ISIS(DEBUG_SR)) {
+ static char buf[INET6_ADDRSTRLEN];
+
+ inet_ntop(nexthop->family, &nexthop->ip, buf,
+ sizeof(buf));
+ zlog_debug(" nexthop %s label %u", buf, remote_label);
+ }
+
+ isis_sr_nexthop_update(&nexthop->sr, remote_label);
+ nexthop_num++;
+ continue;
+ next:
+ isis_sr_nexthop_reset(&nexthop->sr);
+ }
+ if (nexthop_num == 0) {
+ if (IS_DEBUG_ISIS(DEBUG_SR))
+ zlog_debug(" no valid nexthops");
+ return -1;
+ }
+
+ /* Update internal state. */
+ srp->local_label = local_label;
+
+ /* Install Prefix-SID in the forwarding plane. */
+ isis_zebra_install_prefix_sid(srp);
+
+ return 0;
+}
+
+/* Process local or remote Prefix-SID and install it if possible. */
+static void isis_sr_prefix_install(struct sr_prefix *srp)
+{
+ const struct sr_node *srn = srp->srn;
+ struct isis_area *area = srn->area;
+ int ret;
+
+ /* L1 routes are preferred over the L2 ones. */
+ if (area->is_type == IS_LEVEL_1_AND_2) {
+ struct sr_prefix *srp_l1, *srp_l2;
+
+ switch (srn->level) {
+ case ISIS_LEVEL1:
+ srp_l2 = isis_sr_prefix_find_area(area, ISIS_LEVEL2,
+ &srp->prefix);
+ if (srp_l2)
+ isis_sr_prefix_uninstall(srp_l2);
+ break;
+ case ISIS_LEVEL2:
+ srp_l1 = isis_sr_prefix_find_area(area, ISIS_LEVEL1,
+ &srp->prefix);
+ if (srp_l1)
+ return;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (srp->type == ISIS_SR_PREFIX_LOCAL)
+ ret = isis_sr_prefix_install_local(srp);
+ else
+ ret = isis_sr_prefix_install_remote(srp);
+ if (ret != 0)
+ isis_sr_prefix_uninstall(srp);
+}
+
+/* Uninstall local or remote Prefix-SID. */
+static void isis_sr_prefix_uninstall(struct sr_prefix *srp)
+{
+ const struct sr_node *srn = srp->srn;
+ struct listnode *node;
+ struct isis_nexthop *nexthop;
+
+ if (srp->local_label == MPLS_INVALID_LABEL)
+ return;
+
+ if (IS_DEBUG_ISIS(DEBUG_SR))
+ zlog_debug(
+ "ISIS-SR (%s) uninstalling Prefix-SID %pFX %s %u (%s)",
+ srn->area->area_tag, &srp->prefix,
+ CHECK_FLAG(srp->sid.flags, ISIS_PREFIX_SID_VALUE)
+ ? "label"
+ : "index",
+ srp->sid.value, circuit_t2string(srn->level));
+
+
+ /* Uninstall Prefix-SID from the forwarding plane. */
+ isis_zebra_uninstall_prefix_sid(srp);
+
+ /* Reset internal state. */
+ srp->local_label = MPLS_INVALID_LABEL;
+ switch (srp->type) {
+ case ISIS_SR_PREFIX_LOCAL:
+ isis_sr_nexthop_reset(&srp->u.local.info);
+ break;
+ case ISIS_SR_PREFIX_REMOTE:
+ if (srp->u.remote.rinfo) {
+ for (ALL_LIST_ELEMENTS_RO(srp->u.remote.rinfo->nexthops,
+ node, nexthop))
+ isis_sr_nexthop_reset(&nexthop->sr);
+ }
+ break;
+ }
+}
+
+/* Reinstall local or remote Prefix-SID. */
+static void isis_sr_prefix_reinstall(struct sr_prefix *srp,
+ bool replace_semantics)
+{
+ /*
+ * Route replace semantics can be used only when we know for sure that
+ * the Prefix-SID input label hasn't changed. Otherwise we need to
+ * uninstall the Prefix-SID first using the old input label before
+ * reinstalling it.
+ */
+ if (!replace_semantics)
+ isis_sr_prefix_uninstall(srp);
+
+ isis_sr_prefix_install(srp);
+}
+
+/*----------------------------------------------------------------------------*/
+
+/* Parse all SR-related information from the given Router Capabilities TLV. */
+static struct sr_node *
+isis_sr_parse_router_cap_tlv(struct isis_area *area, int level,
+ const uint8_t *sysid,
+ const struct isis_router_cap *router_cap)
+{
+ struct sr_node *srn;
+
+ if (!router_cap || router_cap->srgb.range_size == 0)
+ return NULL;
+
+ srn = isis_sr_node_find(area, level, sysid);
+ if (srn) {
+ if (memcmp(&srn->cap, router_cap, sizeof(srn->cap)) != 0) {
+ srn->cap = *router_cap;
+ SET_FLAG(srn->parse_flags, F_ISIS_SR_NODE_MODIFIED);
+ } else
+ SET_FLAG(srn->parse_flags, F_ISIS_SR_NODE_UNCHANGED);
+ } else {
+ srn = isis_sr_node_add(area, level, sysid, router_cap);
+ SET_FLAG(srn->parse_flags, F_ISIS_SR_NODE_NEW);
+ }
+
+ return srn;
+}
+
+/* Parse list of Prefix-SID Sub-TLVs. */
+static void isis_sr_parse_prefix_sid_subtlvs(struct sr_node *srn,
+ union prefixconstptr prefix,
+ bool local,
+ struct isis_item_list *prefix_sids)
+{
+ struct isis_area *area = srn->area;
+ struct isis_item *i;
+
+ for (i = prefix_sids->head; i; i = i->next) {
+ struct isis_prefix_sid *psid = (struct isis_prefix_sid *)i;
+ struct sr_prefix *srp;
+
+ if (psid->algorithm != SR_ALGORITHM_SPF)
+ continue;
+
+ srp = isis_sr_prefix_find_node(srn, prefix);
+ if (srp) {
+ if (srp->sid.flags != psid->flags
+ || srp->sid.algorithm != psid->algorithm
+ || srp->sid.value != psid->value) {
+ srp->sid = *psid;
+ SET_FLAG(srp->parse_flags,
+ F_ISIS_SR_PREFIX_SID_MODIFIED);
+ } else
+ SET_FLAG(srp->parse_flags,
+ F_ISIS_SR_PREFIX_SID_UNCHANGED);
+ } else {
+ srp = isis_sr_prefix_add(area, srn, prefix, local,
+ psid);
+ SET_FLAG(srp->parse_flags, F_ISIS_SR_PREFIX_SID_NEW);
+ }
+ /*
+ * Stop the Prefix-SID iteration since we only support the SPF
+ * algorithm for now.
+ */
+ break;
+ }
+}
+
+/* Parse all SR-related information from the given LSP. */
+static void isis_sr_parse_lsp(struct isis_area *area, int level,
+ struct sr_node **srn, struct isis_lsp *lsp)
+{
+ struct isis_item_list *items;
+ struct isis_item *i;
+ bool local = lsp->own_lsp;
+
+ if (lsp->hdr.seqno == 0) {
+ zlog_warn("%s: lsp with 0 seq_num - ignore", __func__);
+ return;
+ }
+
+ /* Parse the Router Capability TLV. */
+ if (*srn == NULL) {
+ *srn = isis_sr_parse_router_cap_tlv(
+ area, level, lsp->hdr.lsp_id, lsp->tlvs->router_cap);
+ if (!*srn)
+ return;
+ }
+
+ /* Parse the Extended IP Reachability TLV. */
+ items = &lsp->tlvs->extended_ip_reach;
+ for (i = items->head; i; i = i->next) {
+ struct isis_extended_ip_reach *ir;
+
+ ir = (struct isis_extended_ip_reach *)i;
+ if (!ir->subtlvs)
+ continue;
+
+ isis_sr_parse_prefix_sid_subtlvs(*srn, &ir->prefix, local,
+ &ir->subtlvs->prefix_sids);
+ }
+
+ /* Parse Multi Topology Reachable IPv6 Prefixes TLV. */
+ items = isis_lookup_mt_items(&lsp->tlvs->mt_ipv6_reach,
+ ISIS_MT_IPV6_UNICAST);
+ for (i = items ? items->head : NULL; i; i = i->next) {
+ struct isis_ipv6_reach *ir;
+
+ ir = (struct isis_ipv6_reach *)i;
+ if (!ir->subtlvs)
+ continue;
+
+ isis_sr_parse_prefix_sid_subtlvs(*srn, &ir->prefix, local,
+ &ir->subtlvs->prefix_sids);
+ }
+}
+
+/* Parse all SR-related information from the entire LSPDB. */
+static void isis_sr_parse_lspdb(struct isis_area *area)
+{
+ struct isis_lsp *lsp;
+
+ for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) {
+ frr_each (lspdb, &area->lspdb[level - 1], lsp) {
+ struct isis_lsp *frag;
+ struct listnode *node;
+ struct sr_node *srn = NULL;
+
+ if (LSP_PSEUDO_ID(lsp->hdr.lsp_id))
+ continue;
+ if (!lsp->tlvs)
+ continue;
+
+ isis_sr_parse_lsp(area, level, &srn, lsp);
+ for (ALL_LIST_ELEMENTS_RO(lsp->lspu.frags, node, frag))
+ isis_sr_parse_lsp(area, level, &srn, frag);
+ }
+ }
+}
+
+/* Process any new/deleted/modified Prefix-SID in the LSPDB. */
+static void isis_sr_process_prefix_changes(struct sr_node *srn,
+ struct sr_prefix *srp)
+{
+ struct isis_area *area = srn->area;
+
+ /* Log any Prefix-SID change in the LSPDB. */
+ if (IS_DEBUG_ISIS(DEBUG_SR)) {
+ if (CHECK_FLAG(srp->parse_flags, F_ISIS_SR_PREFIX_SID_NEW))
+ zlog_debug(
+ "ISIS-SR (%s) Prefix-SID created: %pFX (sysid %s)",
+ area->area_tag, &srp->prefix,
+ sysid_print(srn->sysid));
+ else if (CHECK_FLAG(srp->parse_flags,
+ F_ISIS_SR_PREFIX_SID_MODIFIED))
+ zlog_debug(
+ "ISIS-SR (%s) Prefix-SID modified: %pFX (sysid %s)",
+ area->area_tag, &srp->prefix,
+ sysid_print(srn->sysid));
+ else if (!CHECK_FLAG(srp->parse_flags,
+ F_ISIS_SR_PREFIX_SID_UNCHANGED))
+ zlog_debug(
+ "ISIS-SR (%s) Prefix-SID removed: %pFX (sysid %s)",
+ area->area_tag, &srp->prefix,
+ sysid_print(srn->sysid));
+ }
+
+ /* Install/reinstall/uninstall Prefix-SID if necessary. */
+ if (CHECK_FLAG(srp->parse_flags, F_ISIS_SR_PREFIX_SID_NEW))
+ isis_sr_prefix_install(srp);
+ else if (CHECK_FLAG(srp->parse_flags, F_ISIS_SR_PREFIX_SID_MODIFIED))
+ isis_sr_prefix_reinstall(srp, false);
+ else if (!CHECK_FLAG(srp->parse_flags,
+ F_ISIS_SR_PREFIX_SID_UNCHANGED)) {
+ isis_sr_prefix_del(area, srn, srp);
+ return;
+ }
+
+ srp->parse_flags = 0;
+}
+
+/* Process any new/deleted/modified SRGB in the LSPDB. */
+static void isis_sr_process_node_changes(struct isis_area *area, int level,
+ struct sr_node *srn)
+{
+ struct sr_prefix *srp;
+ uint8_t sysid[ISIS_SYS_ID_LEN];
+ bool adjacent;
+
+ memcpy(sysid, srn->sysid, sizeof(sysid));
+
+ /* Log any SRGB change in the LSPDB. */
+ if (IS_DEBUG_ISIS(DEBUG_SR)) {
+ if (CHECK_FLAG(srn->parse_flags, F_ISIS_SR_NODE_NEW))
+ zlog_debug("ISIS-SR (%s) SRGB created (sysid %s)",
+ area->area_tag, sysid_print(sysid));
+ else if (CHECK_FLAG(srn->parse_flags, F_ISIS_SR_NODE_MODIFIED))
+ zlog_debug("ISIS-SR (%s) SRGB modified (sysid %s)",
+ area->area_tag, sysid_print(sysid));
+ else if (!CHECK_FLAG(srn->parse_flags,
+ F_ISIS_SR_NODE_UNCHANGED))
+ zlog_debug("ISIS-SR (%s) SRGB removed (sysid %s)",
+ area->area_tag, sysid_print(sysid));
+ }
+
+ /*
+ * If an adjacent router's SRGB was changed or created, then reinstall
+ * all Prefix-SIDs from all nodes.
+ */
+ adjacent = isis_adj_exists(area, level, sysid);
+ if (CHECK_FLAG(srn->parse_flags,
+ F_ISIS_SR_NODE_NEW | F_ISIS_SR_NODE_MODIFIED)) {
+ if (adjacent)
+ isis_sr_adj_srgb_update(area, sysid, level);
+ } else if (!CHECK_FLAG(srn->parse_flags, F_ISIS_SR_NODE_UNCHANGED)) {
+ isis_sr_node_del(area, level, srn);
+
+ if (adjacent)
+ isis_sr_adj_srgb_update(area, sysid, level);
+ return;
+ }
+
+ srn->parse_flags = 0;
+
+ frr_each_safe (tree_sr_node_prefix, &srn->prefix_sids, srp)
+ isis_sr_process_prefix_changes(srn, srp);
+}
+
+/* Parse and process all SR-related Sub-TLVs after running the SPF algorithm. */
+void isis_area_verify_sr(struct isis_area *area)
+{
+ struct sr_node *srn;
+
+ if (!area->srdb.enabled)
+ return;
+
+ /* Parse LSPDB to detect new/deleted/modified SR (sub-)TLVs. */
+ isis_sr_parse_lspdb(area);
+
+ /* Process possible SR-related changes in the LDPSB. */
+ for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) {
+ frr_each_safe (tree_sr_node, &area->srdb.sr_nodes[level - 1],
+ srn)
+ isis_sr_process_node_changes(area, level, srn);
+ }
+}
+
+/*
+ * Once a route is updated in the SPT, reinstall or uninstall its corresponding
+ * Prefix-SID (if any).
+ */
+static int isis_sr_route_update(struct isis_area *area, struct prefix *prefix,
+ struct isis_route_info *route_info)
+{
+ struct sr_prefix *srp;
+
+ if (!area->srdb.enabled)
+ return 0;
+
+ switch (area->is_type) {
+ case IS_LEVEL_1:
+ srp = isis_sr_prefix_find_area(area, ISIS_LEVEL1, prefix);
+ break;
+ case IS_LEVEL_2:
+ srp = isis_sr_prefix_find_area(area, ISIS_LEVEL2, prefix);
+ break;
+ case IS_LEVEL_1_AND_2:
+ srp = isis_sr_prefix_find_area(area, ISIS_LEVEL1, prefix);
+ if (!srp)
+ srp = isis_sr_prefix_find_area(area, ISIS_LEVEL2,
+ prefix);
+ break;
+ default:
+ flog_err(EC_LIB_DEVELOPMENT, "%s: unknown area level",
+ __func__);
+ exit(1);
+ }
+
+ if (!srp || srp->type == ISIS_SR_PREFIX_LOCAL)
+ return 0;
+
+ if (CHECK_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ACTIVE)) {
+ isis_sr_prefix_reinstall(srp, true);
+ srp->u.remote.rinfo = route_info;
+ } else {
+ isis_sr_prefix_uninstall(srp);
+ srp->u.remote.rinfo = NULL;
+ }
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+
+/* Install or uninstall (LAN)-Adj-SID. */
+static void isis_sr_adj_sid_install_uninstall(bool install,
+ const struct sr_adjacency *sra)
+{
+ struct zapi_labels zl;
+ struct zapi_nexthop_label *znh;
+ int cmd;
+
+ cmd = install ? ZEBRA_MPLS_LABELS_ADD : ZEBRA_MPLS_LABELS_DELETE;
+
+ memset(&zl, 0, sizeof(zl));
+ zl.type = ZEBRA_LSP_ISIS_SR;
+ zl.local_label = sra->nexthop.label;
+ zl.nexthop_num = 1;
+ znh = &zl.nexthops[0];
+ znh->family = sra->nexthop.family;
+ znh->address = sra->nexthop.address;
+ znh->type = (sra->nexthop.family == AF_INET)
+ ? NEXTHOP_TYPE_IPV4_IFINDEX
+ : NEXTHOP_TYPE_IPV6_IFINDEX;
+ znh->ifindex = sra->adj->circuit->interface->ifindex;
+ znh->label = MPLS_LABEL_IMPLICIT_NULL;
+
+ (void)zebra_send_mpls_labels(zclient, cmd, &zl);
+}
+
+/* Add new local Adj-SID. */
+static void isis_sr_adj_sid_add_single(struct isis_adjacency *adj, int family,
+ bool backup)
+{
+ struct isis_circuit *circuit = adj->circuit;
+ struct isis_area *area = circuit->area;
+ struct sr_adjacency *sra;
+ struct isis_adj_sid *adj_sid;
+ struct isis_lan_adj_sid *ladj_sid;
+ union g_addr nexthop = {};
+ uint8_t flags;
+ mpls_label_t local_label;
+
+ switch (family) {
+ case AF_INET:
+ if (!circuit->ip_router)
+ return;
+
+ nexthop.ipv4 = adj->ipv4_addresses[0];
+ break;
+ case AF_INET6:
+ if (!circuit->ipv6_router)
+ return;
+
+ nexthop.ipv6 = adj->ipv6_addresses[0];
+ break;
+ default:
+ flog_err(EC_LIB_DEVELOPMENT,
+ "%s: unexpected address-family: %u", __func__, family);
+ exit(1);
+ }
+
+ flags = EXT_SUBTLV_LINK_ADJ_SID_VFLG | EXT_SUBTLV_LINK_ADJ_SID_LFLG;
+ if (family == AF_INET6)
+ SET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_FFLG);
+ if (backup)
+ SET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_BFLG);
+
+ local_label = isis_zebra_request_dynamic_label();
+ if (circuit->ext == NULL)
+ circuit->ext = isis_alloc_ext_subtlvs();
+
+ if (IS_DEBUG_ISIS(DEBUG_SR)) {
+ char buf[INET6_ADDRSTRLEN];
+
+ inet_ntop(family, &nexthop, buf, sizeof(buf));
+ zlog_debug("ISIS-SR (%s) installing Adj-SID %s%%%s label %u",
+ area->area_tag, buf, circuit->interface->name,
+ local_label);
+ }
+
+ sra = XCALLOC(MTYPE_ISIS_SR_INFO, sizeof(*sra));
+ sra->type = backup ? ISIS_SR_LAN_BACKUP : ISIS_SR_ADJ_NORMAL;
+ sra->nexthop.family = family;
+ sra->nexthop.address = nexthop;
+ sra->nexthop.label = local_label;
+ switch (circuit->circ_type) {
+ case CIRCUIT_T_BROADCAST:
+ ladj_sid = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*ladj_sid));
+ ladj_sid->family = family;
+ ladj_sid->flags = flags;
+ ladj_sid->weight = 0;
+ memcpy(ladj_sid->neighbor_id, adj->sysid,
+ sizeof(ladj_sid->neighbor_id));
+ ladj_sid->sid = local_label;
+ isis_tlvs_add_lan_adj_sid(circuit->ext, ladj_sid);
+ sra->u.ladj_sid = ladj_sid;
+ break;
+ case CIRCUIT_T_P2P:
+ adj_sid = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*adj_sid));
+ adj_sid->family = family;
+ adj_sid->flags = flags;
+ adj_sid->weight = 0;
+ adj_sid->sid = local_label;
+ isis_tlvs_add_adj_sid(circuit->ext, adj_sid);
+ sra->u.adj_sid = adj_sid;
+ break;
+ default:
+ flog_err(EC_LIB_DEVELOPMENT, "%s: unexpected circuit type: %u",
+ __func__, circuit->circ_type);
+ exit(1);
+ }
+ sra->adj = adj;
+ listnode_add(area->srdb.adj_sids, sra);
+ listnode_add(adj->adj_sids, sra);
+
+ isis_sr_adj_sid_install_uninstall(true, sra);
+}
+
+static void isis_sr_adj_sid_add(struct isis_adjacency *adj, int family)
+{
+ isis_sr_adj_sid_add_single(adj, family, false);
+ isis_sr_adj_sid_add_single(adj, family, true);
+}
+
+/* Delete local Adj-SID. */
+static void isis_sr_adj_sid_del(struct sr_adjacency *sra)
+{
+ struct isis_circuit *circuit = sra->adj->circuit;
+ struct isis_area *area = circuit->area;
+
+ if (IS_DEBUG_ISIS(DEBUG_SR)) {
+ char buf[INET6_ADDRSTRLEN];
+
+ inet_ntop(sra->nexthop.family, &sra->nexthop.address, buf,
+ sizeof(buf));
+ zlog_debug("ISIS-SR (%s) uninstalling Adj-SID %s%%%s",
+ area->area_tag, buf, circuit->interface->name);
+ }
+
+ isis_sr_adj_sid_install_uninstall(false, sra);
+
+ switch (circuit->circ_type) {
+ case CIRCUIT_T_BROADCAST:
+ isis_zebra_release_dynamic_label(sra->u.ladj_sid->sid);
+ isis_tlvs_del_lan_adj_sid(circuit->ext, sra->u.ladj_sid);
+ break;
+ case CIRCUIT_T_P2P:
+ isis_zebra_release_dynamic_label(sra->u.adj_sid->sid);
+ isis_tlvs_del_adj_sid(circuit->ext, sra->u.adj_sid);
+ break;
+ default:
+ flog_err(EC_LIB_DEVELOPMENT, "%s: unexpected circuit type: %u",
+ __func__, circuit->circ_type);
+ exit(1);
+ }
+
+ listnode_delete(area->srdb.adj_sids, sra);
+ listnode_delete(sra->adj->adj_sids, sra);
+ XFREE(MTYPE_ISIS_SR_INFO, sra);
+}
+
+/* Remove all Adj-SIDs associated to an adjacency that is going down. */
+static int isis_sr_adj_state_change(struct isis_adjacency *adj)
+{
+ struct sr_adjacency *sra;
+ struct listnode *node, *nnode;
+
+ if (!adj->circuit->area->srdb.enabled)
+ return 0;
+
+ if (adj->adj_state == ISIS_ADJ_UP)
+ return 0;
+
+ for (ALL_LIST_ELEMENTS(adj->adj_sids, node, nnode, sra))
+ isis_sr_adj_sid_del(sra);
+
+ return 0;
+}
+
+/*
+ * Adjacency now has one or more IPv4/IPv6 addresses. Add new IPv4 or IPv6
+ * Adj-SID accordingly.
+ */
+static int isis_sr_adj_ip_enabled(struct isis_adjacency *adj, int family)
+{
+ if (!adj->circuit->area->srdb.enabled)
+ return 0;
+
+ isis_sr_adj_sid_add(adj, family);
+
+ return 0;
+}
+
+/*
+ * Adjacency doesn't have any IPv4 or IPv6 addresses anymore. Delete the
+ * corresponding Adj-SID(s) accordingly.
+ */
+static int isis_sr_adj_ip_disabled(struct isis_adjacency *adj, int family)
+{
+ struct sr_adjacency *sra;
+ struct listnode *node, *nnode;
+
+ if (!adj->circuit->area->srdb.enabled)
+ return 0;
+
+ for (ALL_LIST_ELEMENTS(adj->adj_sids, node, nnode, sra))
+ if (sra->nexthop.family == family)
+ isis_sr_adj_sid_del(sra);
+
+ return 0;
+}
+
+static int isis_sr_if_new_hook(struct interface *ifp)
+{
+ struct isis_circuit *circuit;
+ struct isis_area *area;
+ struct connected *connected;
+ struct listnode *node;
+
+ circuit = circuit_scan_by_ifp(ifp);
+ if (!circuit)
+ return 0;
+
+ area = circuit->area;
+ if (!area)
+ return 0;
+
+ /*
+ * Update the Node-SID flag of the configured Prefix-SID mappings if
+ * necessary. This needs to be done here since isisd reads the startup
+ * configuration before receiving interface information from zebra.
+ */
+ FOR_ALL_INTERFACES_ADDRESSES (ifp, connected, node) {
+ struct sr_prefix_cfg *pcfg;
+
+ pcfg = isis_sr_cfg_prefix_find(area, connected->address);
+ if (!pcfg)
+ continue;
+
+ if (isis_sr_prefix_is_node_sid(ifp, &pcfg->prefix)
+ && !pcfg->node_sid) {
+ pcfg->node_sid = true;
+ lsp_regenerate_schedule(area, area->is_type, 0);
+ }
+ }
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static void isis_sr_show_prefix_sid_local(struct vty *vty, struct ttable *tt,
+ const struct isis_area *area,
+ const struct sr_prefix *srp)
+{
+ const struct sr_nexthop_info *srnh = &srp->u.local.info;
+ char buf_prefix[BUFSIZ];
+ char buf_llabel[BUFSIZ];
+ char buf_rlabel[BUFSIZ];
+ char buf_uptime[BUFSIZ];
+
+ if (srp->local_label != MPLS_INVALID_LABEL)
+ label2str(srp->local_label, buf_llabel, sizeof(buf_llabel));
+ else
+ snprintf(buf_llabel, sizeof(buf_llabel), "-");
+ if (srnh->label != MPLS_INVALID_LABEL) {
+ label2str(srnh->label, buf_rlabel, sizeof(buf_rlabel));
+ log_uptime(srnh->uptime, buf_uptime, sizeof(buf_uptime));
+ } else {
+ snprintf(buf_rlabel, sizeof(buf_rlabel), "-");
+ snprintf(buf_uptime, sizeof(buf_uptime), "-");
+ }
+
+ ttable_add_row(tt, "%s|%u|%s|local|%s|%s",
+ prefix2str(&srp->prefix, buf_prefix, sizeof(buf_prefix)),
+ srp->sid.value, buf_llabel, buf_rlabel, buf_uptime);
+}
+
+static void isis_sr_show_prefix_sid_remote(struct vty *vty, struct ttable *tt,
+ const struct isis_area *area,
+ const struct sr_prefix *srp)
+{
+ struct isis_nexthop *nexthop;
+ struct listnode *node;
+ char buf_prefix[BUFSIZ];
+ char buf_llabel[BUFSIZ];
+ char buf_nhop[BUFSIZ];
+ char buf_iface[BUFSIZ];
+ char buf_rlabel[BUFSIZ];
+ char buf_uptime[BUFSIZ];
+ bool first = true;
+
+ (void)prefix2str(&srp->prefix, buf_prefix, sizeof(buf_prefix));
+ if (srp->local_label != MPLS_INVALID_LABEL)
+ label2str(srp->local_label, buf_llabel, sizeof(buf_llabel));
+ else
+ snprintf(buf_llabel, sizeof(buf_llabel), "-");
+
+ if (!srp->u.remote.rinfo) {
+ ttable_add_row(tt, "%s|%u|%s|-|-|-", buf_prefix, srp->sid.value,
+ buf_llabel);
+ return;
+ }
+
+ for (ALL_LIST_ELEMENTS_RO(srp->u.remote.rinfo->nexthops, node,
+ nexthop)) {
+ struct interface *ifp;
+
+ inet_ntop(nexthop->family, &nexthop->ip, buf_nhop,
+ sizeof(buf_nhop));
+ ifp = if_lookup_by_index(nexthop->ifindex, VRF_DEFAULT);
+ if (ifp)
+ strlcpy(buf_iface, ifp->name, sizeof(buf_iface));
+ else
+ snprintf(buf_iface, sizeof(buf_iface), "ifindex %u",
+ nexthop->ifindex);
+ if (nexthop->sr.label == MPLS_INVALID_LABEL) {
+ snprintf(buf_rlabel, sizeof(buf_rlabel), "-");
+ snprintf(buf_uptime, sizeof(buf_uptime), "-");
+ } else {
+ label2str(nexthop->sr.label, buf_rlabel,
+ sizeof(buf_rlabel));
+ log_uptime(nexthop->sr.uptime, buf_uptime,
+ sizeof(buf_uptime));
+ }
+
+ if (first)
+ ttable_add_row(tt, "%s|%u|%s|%s, %s|%s|%s", buf_prefix,
+ srp->sid.value, buf_llabel, buf_nhop,
+ buf_iface, buf_rlabel, buf_uptime);
+ else
+ ttable_add_row(tt, "|||%s, %s|%s|%s", buf_nhop,
+ buf_iface, buf_rlabel, buf_uptime);
+ first = false;
+ }
+}
+
+static void isis_sr_show_prefix_sids(struct vty *vty, struct isis_area *area,
+ int level)
+{
+ struct sr_prefix *srp;
+ struct ttable *tt;
+
+ if (tree_sr_area_prefix_count(&area->srdb.prefix_sids[level - 1]) == 0)
+ return;
+
+ vty_out(vty, " IS-IS %s Prefix-SIDs:\n\n", circuit_t2string(level));
+
+ /* Prepare table. */
+ tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
+ ttable_add_row(tt, "Prefix|SID|In Label|Nexthop|Out Label|Uptime");
+ tt->style.cell.rpad = 2;
+ tt->style.corner = '+';
+ ttable_restyle(tt);
+ ttable_rowseps(tt, 0, BOTTOM, true, '-');
+
+ frr_each (tree_sr_area_prefix, &area->srdb.prefix_sids[level - 1],
+ srp) {
+ switch (srp->type) {
+ case ISIS_SR_PREFIX_LOCAL:
+ isis_sr_show_prefix_sid_local(vty, tt, area, srp);
+ break;
+ case ISIS_SR_PREFIX_REMOTE:
+ isis_sr_show_prefix_sid_remote(vty, tt, area, srp);
+ break;
+ }
+ }
+
+ /* 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_sr_prefix_sids, show_sr_prefix_sids_cmd,
+ "show isis segment-routing prefix-sids",
+ SHOW_STR PROTO_HELP
+ "Segment-Routing\n"
+ "Segment-Routing Prefix-SIDs\n")
+{
+ struct listnode *node;
+ struct isis_area *area;
+
+ for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) {
+ vty_out(vty, "Area %s:\n",
+ area->area_tag ? area->area_tag : "null");
+
+ for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++)
+ isis_sr_show_prefix_sids(vty, area, level);
+ }
+
+ return CMD_SUCCESS;
+}
+
+/*----------------------------------------------------------------------------*/
+
+/* Try to enable SR on the given IS-IS area. */
+int isis_sr_start(struct isis_area *area)
+{
+ struct isis_sr_db *srdb = &area->srdb;
+ struct isis_circuit *circuit;
+ struct listnode *node;
+
+ /*
+ * Request SGRB to the label manager. If the allocation fails, return
+ * an error to disable SR until a new SRGB is successfully allocated.
+ */
+ if (isis_zebra_request_label_range(
+ srdb->config.srgb_lower_bound,
+ srdb->config.srgb_upper_bound
+ - srdb->config.srgb_lower_bound + 1))
+ return -1;
+
+ if (IS_DEBUG_ISIS(DEBUG_SR))
+ zlog_debug("ISIS-SR (%s) Starting Segment Routing",
+ area->area_tag);
+
+ /* Create Adj-SIDs for existing adjacencies. */
+ for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) {
+ struct isis_adjacency *adj;
+ struct listnode *anode;
+
+ switch (circuit->circ_type) {
+ case CIRCUIT_T_BROADCAST:
+ for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS;
+ level++) {
+ for (ALL_LIST_ELEMENTS_RO(
+ circuit->u.bc.adjdb[level - 1],
+ anode, adj)) {
+ if (adj->ipv4_address_count > 0)
+ isis_sr_adj_sid_add(adj,
+ AF_INET);
+ if (adj->ipv6_address_count > 0)
+ isis_sr_adj_sid_add(adj,
+ AF_INET6);
+ }
+ }
+ break;
+ case CIRCUIT_T_P2P:
+ adj = circuit->u.p2p.neighbor;
+ if (adj && adj->ipv4_address_count > 0)
+ isis_sr_adj_sid_add(adj, AF_INET);
+ if (adj && adj->ipv6_address_count > 0)
+ isis_sr_adj_sid_add(adj, AF_INET6);
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* Regenerate LSPs. */
+ lsp_regenerate_schedule(area, area->is_type, 0);
+
+ return 0;
+}
+
+/* Disable SR on the given IS-IS area. */
+void isis_sr_stop(struct isis_area *area)
+{
+ struct isis_sr_db *srdb = &area->srdb;
+ struct sr_adjacency *sra;
+ struct listnode *node, *nnode;
+
+ if (IS_DEBUG_ISIS(DEBUG_SR))
+ zlog_debug("ISIS-SR (%s) Stopping Segment Routing",
+ area->area_tag);
+
+ /* Uninstall Adj-SIDs. */
+ for (ALL_LIST_ELEMENTS(area->srdb.adj_sids, node, nnode, sra))
+ isis_sr_adj_sid_del(sra);
+
+ /* Uninstall Prefix-SIDs. */
+ for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) {
+ while (tree_sr_node_count(&srdb->sr_nodes[level - 1]) > 0) {
+ struct sr_node *srn;
+
+ srn = tree_sr_node_first(&srdb->sr_nodes[level - 1]);
+ isis_sr_node_del(area, level, srn);
+ }
+ }
+
+ /* Release SRGB. */
+ isis_zebra_release_label_range(srdb->config.srgb_lower_bound,
+ srdb->config.srgb_upper_bound);
+
+ /* Regenerate LSPs. */
+ lsp_regenerate_schedule(area, area->is_type, 0);
+}
+
+void isis_sr_area_init(struct isis_area *area)
+{
+ struct isis_sr_db *srdb = &area->srdb;
+
+ memset(srdb, 0, sizeof(*srdb));
+ srdb->enabled = false;
+ srdb->adj_sids = list_new();
+
+ for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) {
+ tree_sr_node_init(&srdb->sr_nodes[level - 1]);
+ tree_sr_area_prefix_init(&srdb->prefix_sids[level - 1]);
+ }
+
+ /* Pull defaults from the YANG module. */
+#ifndef FABRICD
+ srdb->config.enabled = yang_get_default_bool("%s/enabled", ISIS_SR);
+ srdb->config.srgb_lower_bound =
+ yang_get_default_uint32("%s/srgb/lower-bound", ISIS_SR);
+ srdb->config.srgb_upper_bound =
+ yang_get_default_uint32("%s/srgb/upper-bound", ISIS_SR);
+#else
+ srdb->config.enabled = false;
+ srdb->config.srgb_lower_bound = SRGB_LOWER_BOUND;
+ srdb->config.srgb_upper_bound = SRGB_UPPER_BOUND;
+#endif
+ srdb->config.msd = 0;
+ tree_sr_prefix_cfg_init(&srdb->config.prefix_sids);
+}
+
+void isis_sr_area_term(struct isis_area *area)
+{
+ struct isis_sr_db *srdb = &area->srdb;
+
+ /* Stop Segment Routing */
+ if (area->srdb.enabled)
+ isis_sr_stop(area);
+
+ /* Clear Prefix-SID configuration. */
+ while (tree_sr_prefix_cfg_count(&srdb->config.prefix_sids) > 0) {
+ struct sr_prefix_cfg *pcfg;
+
+ pcfg = tree_sr_prefix_cfg_first(&srdb->config.prefix_sids);
+ isis_sr_cfg_prefix_del(pcfg);
+ }
+}
+
+void isis_sr_init(void)
+{
+ install_element(VIEW_NODE, &show_sr_prefix_sids_cmd);
+
+ /* Register hooks. */
+ hook_register(isis_adj_state_change_hook, isis_sr_adj_state_change);
+ hook_register(isis_adj_ip_enabled_hook, isis_sr_adj_ip_enabled);
+ hook_register(isis_adj_ip_disabled_hook, isis_sr_adj_ip_disabled);
+ hook_register(isis_route_update_hook, isis_sr_route_update);
+ hook_register(isis_if_new_hook, isis_sr_if_new_hook);
+}
+
+void isis_sr_term(void)
+{
+ /* Unregister hooks. */
+ hook_unregister(isis_adj_state_change_hook, isis_sr_adj_state_change);
+ hook_unregister(isis_adj_ip_enabled_hook, isis_sr_adj_ip_enabled);
+ hook_unregister(isis_adj_ip_disabled_hook, isis_sr_adj_ip_disabled);
+ hook_unregister(isis_route_update_hook, isis_sr_route_update);
+ hook_unregister(isis_if_new_hook, isis_sr_if_new_hook);
+}
diff --git a/isisd/isis_sr.h b/isisd/isis_sr.h
new file mode 100644
index 000000000..286ebeb95
--- /dev/null
+++ b/isisd/isis_sr.h
@@ -0,0 +1,259 @@
+/*
+ * This is an implementation of Segment Routing for IS-IS
+ * as per draft draft-ietf-isis-segment-routing-extensions-25
+ *
+ * Copyright (C) 2019 Orange Labs http://www.orange.com
+ *
+ * Author: Olivier Dugeon <olivier.dugeon@orange.com>
+ * Contributor: Renato Westphal <renato@opensourcerouting.org> for NetDEF
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _FRR_ISIS_SR_H
+#define _FRR_ISIS_SR_H
+
+#include "lib/linklist.h"
+#include "lib/mpls.h"
+#include "lib/nexthop.h"
+#include "lib/typesafe.h"
+
+#include "isisd/isis_tlvs.h"
+
+/*
+ * Segment Routing information is transported through the following Sub-TLVs:
+ *
+ * Sub-TLV Name Value TLVs
+ * ---------------------------------------------------------------------
+ * SID Label 1
+ *
+ * Prefix Segment Identifier 3 135, 235, 236 and 237
+ *
+ * Adjacency Segment Identifier 31 22, 23, 141, 222 and 223
+ * LAN Adjacency Segment Identifier 32 22, 23, 141, 222 and 223
+ *
+ * Segment Routing Capability 2 242
+ * Segment Routing Algorithm 19 242
+ * Node Maximum Stack Depth (MSD) 23 242
+ *
+ * Sub-TLV definitions, serialization and de-serialization are defined
+ * in isis_tlvs.[c,h].
+ */
+
+#define SRGB_LOWER_BOUND 16000
+#define SRGB_UPPER_BOUND 23999
+
+PREDECL_RBTREE_UNIQ(tree_sr_node)
+PREDECL_RBTREE_UNIQ(tree_sr_node_prefix)
+PREDECL_RBTREE_UNIQ(tree_sr_area_prefix)
+PREDECL_RBTREE_UNIQ(tree_sr_prefix_cfg)
+
+/* SR Adj-SID type. */
+enum sr_adj_type {
+ ISIS_SR_ADJ_NORMAL = 0,
+ ISIS_SR_LAN_BACKUP,
+};
+
+/* SR Adjacency. */
+struct sr_adjacency {
+ /* Adjacency type. */
+ enum sr_adj_type type;
+
+ /* Adj-SID nexthop information. */
+ struct {
+ int family;
+ union g_addr address;
+ mpls_label_t label;
+ } nexthop;
+
+ /* (LAN-)Adj-SID Sub-TLV. */
+ union {
+ struct isis_adj_sid *adj_sid;
+ struct isis_lan_adj_sid *ladj_sid;
+ } u;
+
+ /* Back pointer to IS-IS adjacency. */
+ struct isis_adjacency *adj;
+};
+
+/* SR Prefix-SID type. */
+enum sr_prefix_type {
+ ISIS_SR_PREFIX_LOCAL = 0,
+ ISIS_SR_PREFIX_REMOTE,
+};
+
+/* SR Nexthop Information. */
+struct sr_nexthop_info {
+ mpls_label_t label;
+ time_t uptime;
+};
+
+/* SR Prefix-SID. */
+struct sr_prefix {
+ /* RB-tree entries. */
+ struct tree_sr_node_prefix_item node_entry;
+ struct tree_sr_area_prefix_item area_entry;
+
+ /* IP prefix. */
+ struct prefix prefix;
+
+ /* SID value, algorithm and flags. */
+ struct isis_prefix_sid sid;
+
+ /* Local label value. */
+ mpls_label_t local_label;
+
+ /* Prefix-SID type. */
+ enum sr_prefix_type type;
+ union {
+ struct {
+ /* Information about this local Prefix-SID. */
+ struct sr_nexthop_info info;
+ } local;
+ struct {
+ /* Route associated to this remote Prefix-SID. */
+ struct isis_route_info *rinfo;
+ } remote;
+ } u;
+
+ /* Backpointer to SR node. */
+ struct sr_node *srn;
+
+ /* Flags used while the LSPDB is being parsed. */
+ uint8_t parse_flags;
+#define F_ISIS_SR_PREFIX_SID_NEW 0x01
+#define F_ISIS_SR_PREFIX_SID_MODIFIED 0x02
+#define F_ISIS_SR_PREFIX_SID_UNCHANGED 0x04
+};
+
+/* SR node. */
+struct sr_node {
+ /* RB-tree entry. */
+ struct tree_sr_node_item entry;
+
+ /* IS-IS level: ISIS_LEVEL1 or ISIS_LEVEL2. */
+ int level;
+
+ /* IS-IS node identifier. */
+ uint8_t sysid[ISIS_SYS_ID_LEN];
+
+ /* IS-IS node capabilities (SRGB, SR Algorithms, etc). */
+ struct isis_router_cap cap;
+
+ /* List of Prefix-SIDs advertised by this node. */
+ struct tree_sr_node_prefix_head prefix_sids;
+
+ /* Backpointer to IS-IS area. */
+ struct isis_area *area;
+
+ /* Flags used while the LSPDB is being parsed. */
+ uint8_t parse_flags;
+#define F_ISIS_SR_NODE_NEW 0x01
+#define F_ISIS_SR_NODE_MODIFIED 0x02
+#define F_ISIS_SR_NODE_UNCHANGED 0x04
+};
+
+/* NOTE: these values must be in sync with the YANG module. */
+enum sr_sid_value_type {
+ SR_SID_VALUE_TYPE_INDEX = 0,
+ SR_SID_VALUE_TYPE_ABSOLUTE = 1,
+};
+
+/* NOTE: these values must be in sync with the YANG module. */
+enum sr_last_hop_behavior {
+ SR_LAST_HOP_BEHAVIOR_EXP_NULL = 0,
+ SR_LAST_HOP_BEHAVIOR_NO_PHP = 1,
+ SR_LAST_HOP_BEHAVIOR_PHP = 2,
+};
+
+/* SR Prefix-SID configuration. */
+struct sr_prefix_cfg {
+ /* RB-tree entry. */
+ struct tree_sr_prefix_cfg_item entry;
+
+ /* IP prefix. */
+ struct prefix prefix;
+
+ /* SID value. */
+ uint32_t sid;
+
+ /* SID value type. */
+ enum sr_sid_value_type sid_type;
+
+ /* SID last hop behavior. */
+ enum sr_last_hop_behavior last_hop_behavior;
+
+ /* Does this Prefix-SID refer to a loopback address (Node-SID)? */
+ bool node_sid;
+
+ /* Backpointer to IS-IS area. */
+ struct isis_area *area;
+};
+
+/* Per-area IS-IS Segment Routing information. */
+struct isis_sr_db {
+ /* Operational status of Segment Routing. */
+ bool enabled;
+
+ /* Adj-SIDs. */
+ struct list *adj_sids;
+
+ /* SR information from all nodes. */
+ struct tree_sr_node_head sr_nodes[ISIS_LEVELS];
+
+ /* Prefix-SIDs. */
+ struct tree_sr_area_prefix_head prefix_sids[ISIS_LEVELS];
+
+ /* Area SR configuration. */
+ struct {
+ /* Administrative status of Segment Routing. */
+ bool enabled;
+
+ /* Segment Routing Global Block lower & upper bound. */
+ uint32_t srgb_lower_bound;
+ uint32_t srgb_upper_bound;
+
+ /* Maximum SID Depth supported by the node. */
+ uint8_t msd;
+
+ /* Prefix-SID mappings. */
+ struct tree_sr_prefix_cfg_head prefix_sids;
+ } config;
+};
+
+/* Prototypes. */
+extern int isis_sr_cfg_srgb_update(struct isis_area *area, uint32_t lower_bound,
+ uint32_t upper_bound);
+extern void isis_sr_cfg_msd_update(struct isis_area *area);
+extern struct sr_prefix_cfg *
+isis_sr_cfg_prefix_add(struct isis_area *area, const struct prefix *prefix);
+extern void isis_sr_cfg_prefix_del(struct sr_prefix_cfg *pcfg);
+extern struct sr_prefix_cfg *
+isis_sr_cfg_prefix_find(struct isis_area *area, union prefixconstptr prefix);
+extern void isis_sr_prefix_cfg2subtlv(const struct sr_prefix_cfg *pcfg,
+ bool external,
+ struct isis_prefix_sid *psid);
+extern void isis_sr_nexthop_update(struct sr_nexthop_info *srnh,
+ mpls_label_t label);
+extern void isis_sr_nexthop_reset(struct sr_nexthop_info *srnh);
+extern void isis_area_verify_sr(struct isis_area *area);
+extern int isis_sr_start(struct isis_area *area);
+extern void isis_sr_stop(struct isis_area *area);
+extern void isis_sr_area_init(struct isis_area *area);
+extern void isis_sr_area_term(struct isis_area *area);
+extern void isis_sr_init(void);
+extern void isis_sr_term(void);
+
+#endif /* _FRR_ISIS_SR_H */
diff --git a/isisd/isis_tlvs.c b/isisd/isis_tlvs.c
index 761005d0c..be88ee85a 100644
--- a/isisd/isis_tlvs.c
+++ b/isisd/isis_tlvs.c
@@ -43,9 +43,10 @@
#include "isisd/isis_pdu.h"
#include "isisd/isis_lsp.h"
#include "isisd/isis_te.h"
+#include "isisd/isis_sr.h"
DEFINE_MTYPE_STATIC(ISISD, ISIS_TLV, "ISIS TLVs")
-DEFINE_MTYPE_STATIC(ISISD, ISIS_SUBTLV, "ISIS Sub-TLVs")
+DEFINE_MTYPE(ISISD, ISIS_SUBTLV, "ISIS Sub-TLVs")
DEFINE_MTYPE_STATIC(ISISD, ISIS_MT_ITEM_LIST, "ISIS MT Item Lists")
typedef int (*unpack_tlv_func)(enum isis_tlv_context context, uint8_t tlv_type,
@@ -887,7 +888,11 @@ static int unpack_item_prefix_sid(uint16_t mtid, uint8_t len, struct stream *s,
if (sid.flags & ISIS_PREFIX_SID_VALUE) {
sid.value = stream_get3(s);
- sid.value &= MPLS_LABEL_VALUE_MASK;
+ if (!IS_MPLS_UNRESERVED_LABEL(sid.value)) {
+ sbuf_push(log, indent, "Invalid absolute SID %u\n",
+ sid.value);
+ return 1;
+ }
} else {
sid.value = stream_getl(s);
}
@@ -2623,7 +2628,7 @@ static void format_tlv_router_cap(const struct isis_router_cap *router_cap,
sbuf_push(buf, indent, " Algorithm: %s",
router_cap->algo[0] == 0 ? "0: SPF"
: "0: Strict SPF");
- for (int i = 0; i < SR_ALGORITHM_COUNT; i++)
+ for (int i = 1; i < SR_ALGORITHM_COUNT; i++)
if (router_cap->algo[i] != SR_ALGORITHM_UNSET)
sbuf_push(buf, indent, " %s",
router_cap->algo[1] == 0
@@ -4630,24 +4635,42 @@ void isis_tlvs_del_lan_adj_sid(struct isis_ext_subtlvs *exts,
}
void isis_tlvs_add_extended_ip_reach(struct isis_tlvs *tlvs,
- struct prefix_ipv4 *dest, uint32_t metric)
+ struct prefix_ipv4 *dest, uint32_t metric,
+ bool external, struct sr_prefix_cfg *pcfg)
{
struct isis_extended_ip_reach *r = XCALLOC(MTYPE_ISIS_TLV, sizeof(*r));
r->metric = metric;
memcpy(&r->prefix, dest, sizeof(*dest));
apply_mask_ipv4(&r->prefix);
+ if (pcfg) {
+ struct isis_prefix_sid *psid =
+ XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*psid));
+
+ isis_sr_prefix_cfg2subtlv(pcfg, external, psid);
+ r->subtlvs = isis_alloc_subtlvs(ISIS_CONTEXT_SUBTLV_IP_REACH);
+ append_item(&r->subtlvs->prefix_sids, (struct isis_item *)psid);
+ }
append_item(&tlvs->extended_ip_reach, (struct isis_item *)r);
}
void isis_tlvs_add_ipv6_reach(struct isis_tlvs *tlvs, uint16_t mtid,
- struct prefix_ipv6 *dest, uint32_t metric)
+ struct prefix_ipv6 *dest, uint32_t metric,
+ bool external, struct sr_prefix_cfg *pcfg)
{
struct isis_ipv6_reach *r = XCALLOC(MTYPE_ISIS_TLV, sizeof(*r));
r->metric = metric;
memcpy(&r->prefix, dest, sizeof(*dest));
apply_mask_ipv6(&r->prefix);
+ if (pcfg) {
+ struct isis_prefix_sid *psid =
+ XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*psid));
+
+ isis_sr_prefix_cfg2subtlv(pcfg, external, psid);
+ r->subtlvs = isis_alloc_subtlvs(ISIS_CONTEXT_SUBTLV_IP_REACH);
+ append_item(&r->subtlvs->prefix_sids, (struct isis_item *)psid);
+ }
struct isis_item_list *l;
l = (mtid == ISIS_MT_IPV4_UNICAST)
@@ -4661,7 +4684,7 @@ void isis_tlvs_add_ipv6_dstsrc_reach(struct isis_tlvs *tlvs, uint16_t mtid,
struct prefix_ipv6 *src,
uint32_t metric)
{
- isis_tlvs_add_ipv6_reach(tlvs, mtid, dest, metric);
+ isis_tlvs_add_ipv6_reach(tlvs, mtid, dest, metric, false, NULL);
struct isis_item_list *l = isis_get_mt_items(&tlvs->mt_ipv6_reach,
mtid);
diff --git a/isisd/isis_tlvs.h b/isisd/isis_tlvs.h
index 2948728e2..c3b25669b 100644
--- a/isisd/isis_tlvs.h
+++ b/isisd/isis_tlvs.h
@@ -28,8 +28,11 @@
#include "openbsd-tree.h"
#include "prefix.h"
+DECLARE_MTYPE(ISIS_SUBTLV)
+
struct lspdb_head;
struct isis_subtlvs;
+struct sr_prefix_cfg;
struct isis_area_address;
struct isis_area_address {
@@ -580,9 +583,11 @@ void isis_tlvs_set_te_router_id(struct isis_tlvs *tlvs,
void isis_tlvs_add_oldstyle_ip_reach(struct isis_tlvs *tlvs,
struct prefix_ipv4 *dest, uint8_t metric);
void isis_tlvs_add_extended_ip_reach(struct isis_tlvs *tlvs,
- struct prefix_ipv4 *dest, uint32_t metric);
+ struct prefix_ipv4 *dest, uint32_t metric,
+ bool external, struct sr_prefix_cfg *pcfg);
void isis_tlvs_add_ipv6_reach(struct isis_tlvs *tlvs, uint16_t mtid,
- struct prefix_ipv6 *dest, uint32_t metric);
+ struct prefix_ipv6 *dest, uint32_t metric,
+ bool external, struct sr_prefix_cfg *pcfg);
void isis_tlvs_add_ipv6_dstsrc_reach(struct isis_tlvs *tlvs, uint16_t mtid,
struct prefix_ipv6 *dest,
struct prefix_ipv6 *src,
diff --git a/isisd/isis_zebra.c b/isisd/isis_zebra.c
index 630264768..3ce756231 100644
--- a/isisd/isis_zebra.c
+++ b/isisd/isis_zebra.c
@@ -50,8 +50,16 @@
#include "isisd/isis_route.h"
#include "isisd/isis_zebra.h"
#include "isisd/isis_te.h"
+#include "isisd/isis_sr.h"
-struct zclient *zclient = NULL;
+struct zclient *zclient;
+static struct zclient *zclient_sync;
+
+/* List of chunks of labels externally assigned by zebra. */
+static struct list *label_chunk_list;
+static struct listnode *current_label_chunk;
+
+static void isis_zebra_label_manager_connect(void);
/* Router-id update message from zebra. */
static int isis_router_id_update_zebra(ZAPI_CALLBACK_ARGS)
@@ -245,6 +253,88 @@ void isis_zebra_route_del_route(struct prefix *prefix,
zclient_route_send(ZEBRA_ROUTE_DELETE, zclient, &api);
}
+/* Install Prefix-SID in the forwarding plane. */
+void isis_zebra_install_prefix_sid(const struct sr_prefix *srp)
+{
+ struct zapi_labels zl;
+ struct zapi_nexthop_label *znh;
+ struct listnode *node;
+ struct isis_nexthop *nexthop;
+ struct interface *ifp;
+
+ /* Prepare message. */
+ memset(&zl, 0, sizeof(zl));
+ zl.type = ZEBRA_LSP_ISIS_SR;
+ zl.local_label = srp->local_label;
+
+ switch (srp->type) {
+ case ISIS_SR_PREFIX_LOCAL:
+ ifp = if_lookup_by_name("lo", VRF_DEFAULT);
+ if (!ifp) {
+ zlog_warn(
+ "%s: couldn't install Prefix-SID %pFX: loopback interface not found",
+ __func__, &srp->prefix);
+ return;
+ }
+
+ znh = &zl.nexthops[zl.nexthop_num++];
+ znh->type = NEXTHOP_TYPE_IFINDEX;
+ znh->ifindex = ifp->ifindex;
+ znh->label = MPLS_LABEL_IMPLICIT_NULL;
+ break;
+ case ISIS_SR_PREFIX_REMOTE:
+ /* Update route in the RIB too. */
+ SET_FLAG(zl.message, ZAPI_LABELS_FTN);
+ zl.route.prefix = srp->prefix;
+ zl.route.type = ZEBRA_ROUTE_ISIS;
+ zl.route.instance = 0;
+
+ for (ALL_LIST_ELEMENTS_RO(srp->u.remote.rinfo->nexthops, node,
+ nexthop)) {
+ if (nexthop->sr.label == MPLS_INVALID_LABEL)
+ continue;
+
+ if (zl.nexthop_num >= MULTIPATH_NUM)
+ break;
+
+ znh = &zl.nexthops[zl.nexthop_num++];
+ znh->type = (srp->prefix.family == AF_INET)
+ ? NEXTHOP_TYPE_IPV4_IFINDEX
+ : NEXTHOP_TYPE_IPV6_IFINDEX;
+ znh->family = nexthop->family;
+ znh->address = nexthop->ip;
+ znh->ifindex = nexthop->ifindex;
+ znh->label = nexthop->sr.label;
+ }
+ break;
+ }
+
+ /* Send message to zebra. */
+ (void)zebra_send_mpls_labels(zclient, ZEBRA_MPLS_LABELS_REPLACE, &zl);
+}
+
+/* Uninstall Prefix-SID from the forwarding plane. */
+void isis_zebra_uninstall_prefix_sid(const struct sr_prefix *srp)
+{
+ struct zapi_labels zl;
+
+ /* Prepare message. */
+ memset(&zl, 0, sizeof(zl));
+ zl.type = ZEBRA_LSP_ISIS_SR;
+ zl.local_label = srp->local_label;
+
+ if (srp->type == ISIS_SR_PREFIX_REMOTE) {
+ /* Update route in the RIB too. */
+ SET_FLAG(zl.message, ZAPI_LABELS_FTN);
+ zl.route.prefix = srp->prefix;
+ zl.route.type = ZEBRA_ROUTE_ISIS;
+ zl.route.instance = 0;
+ }
+
+ /* Send message to zebra. */
+ (void)zebra_send_mpls_labels(zclient, ZEBRA_MPLS_LABELS_DELETE, &zl);
+}
+
static int isis_zebra_read(ZAPI_CALLBACK_ARGS)
{
struct zapi_route api;
@@ -302,13 +392,192 @@ void isis_zebra_redistribute_unset(afi_t afi, int type)
type, 0, VRF_DEFAULT);
}
+/* Label Manager Requests. */
+int isis_zebra_request_label_range(uint32_t base, uint32_t chunk_size)
+{
+ int ret;
+ uint32_t start, end;
+
+ if (zclient_sync->sock == -1)
+ isis_zebra_label_manager_connect();
+
+ ret = lm_get_label_chunk(zclient_sync, 0, base, chunk_size, &start,
+ &end);
+ if (ret < 0) {
+ zlog_warn("%s: error getting label range!", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+void isis_zebra_release_label_range(uint32_t start, uint32_t end)
+{
+ int ret;
+
+ if (zclient_sync->sock == -1)
+ isis_zebra_label_manager_connect();
+
+ ret = lm_release_label_chunk(zclient_sync, start, end);
+ if (ret < 0)
+ zlog_warn("%s: error releasing label range!", __func__);
+}
+
+static int isis_zebra_get_label_chunk(void)
+{
+ int ret;
+ uint32_t start, end;
+ struct label_chunk *new_label_chunk;
+
+ if (zclient_sync->sock == -1)
+ isis_zebra_label_manager_connect();
+
+ ret = lm_get_label_chunk(zclient_sync, 0, MPLS_LABEL_BASE_ANY,
+ CHUNK_SIZE, &start, &end);
+ if (ret < 0) {
+ zlog_warn("%s: error getting label chunk!", __func__);
+ return -1;
+ }
+
+ new_label_chunk = calloc(1, sizeof(struct label_chunk));
+ if (!new_label_chunk) {
+ zlog_warn("%s: error trying to allocate label chunk %u - %u",
+ __func__, start, end);
+ return -1;
+ }
+
+ new_label_chunk->start = start;
+ new_label_chunk->end = end;
+ new_label_chunk->used_mask = 0;
+
+ listnode_add(label_chunk_list, (void *)new_label_chunk);
+
+ /* let's update current if needed */
+ if (!current_label_chunk)
+ current_label_chunk = listtail(label_chunk_list);
+
+ return 0;
+}
+
+mpls_label_t isis_zebra_request_dynamic_label(void)
+{
+ struct label_chunk *label_chunk;
+ uint32_t i, size;
+ uint64_t pos;
+ uint32_t label = MPLS_INVALID_LABEL;
+
+ while (current_label_chunk) {
+ label_chunk = listgetdata(current_label_chunk);
+ if (!label_chunk)
+ goto end;
+
+ /* try to get next free label in currently used label chunk */
+ size = label_chunk->end - label_chunk->start + 1;
+ for (i = 0, pos = 1; i < size; i++, pos <<= 1) {
+ if (!(pos & label_chunk->used_mask)) {
+ label_chunk->used_mask |= pos;
+ label = label_chunk->start + i;
+ goto end;
+ }
+ }
+ current_label_chunk = listnextnode(current_label_chunk);
+ }
+
+end:
+ /*
+ * we moved till the last chunk, or were not able to find a label, so
+ * let's ask for another one.
+ */
+ if (!current_label_chunk
+ || current_label_chunk == listtail(label_chunk_list)
+ || label == MPLS_INVALID_LABEL) {
+ if (isis_zebra_get_label_chunk() != 0)
+ zlog_warn("%s: error getting label chunk!", __func__);
+ }
+
+ return label;
+}
+
+static void isis_zebra_del_label_chunk(void *val)
+{
+ free(val);
+}
+
+static int isis_zebra_release_label_chunk(uint32_t start, uint32_t end)
+{
+ int ret;
+
+ ret = lm_release_label_chunk(zclient_sync, start, end);
+ if (ret < 0) {
+ zlog_warn("%s: error releasing label chunk!", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+void isis_zebra_release_dynamic_label(mpls_label_t label)
+{
+ struct listnode *node;
+ struct label_chunk *label_chunk;
+ uint64_t pos;
+
+ for (ALL_LIST_ELEMENTS_RO(label_chunk_list, node, label_chunk)) {
+ if (!(label <= label_chunk->end && label >= label_chunk->start))
+ continue;
+
+ pos = 1ULL << (label - label_chunk->start);
+ label_chunk->used_mask &= ~pos;
+
+ /*
+ * If nobody is using this chunk and it's not
+ * current_label_chunk, then free it.
+ */
+ if (!label_chunk->used_mask && (current_label_chunk != node)) {
+ if (isis_zebra_release_label_chunk(label_chunk->start,
+ label_chunk->end)
+ != 0)
+ zlog_warn("%s: error releasing label chunk!",
+ __func__);
+ else {
+ listnode_delete(label_chunk_list, label_chunk);
+ isis_zebra_del_label_chunk(label_chunk);
+ }
+ }
+ break;
+ }
+}
+
+static void isis_zebra_label_manager_connect(void)
+{
+ /* Connect to label manager. */
+ while (zclient_socket_connect(zclient_sync) < 0) {
+ zlog_warn("%s: error connecting synchronous zclient!",
+ __func__);
+ sleep(1);
+ }
+ set_nonblocking(zclient_sync->sock);
+ while (lm_label_manager_connect(zclient_sync, 0) != 0) {
+ zlog_warn("%s: error connecting to label manager!", __func__);
+ sleep(1);
+ }
+
+ label_chunk_list = list_new();
+ label_chunk_list->del = isis_zebra_del_label_chunk;
+ while (isis_zebra_get_label_chunk() != 0) {
+ zlog_warn("%s: error getting first label chunk!", __func__);
+ sleep(1);
+ }
+}
+
static void isis_zebra_connected(struct zclient *zclient)
{
zclient_send_reg_requests(zclient, VRF_DEFAULT);
}
-void isis_zebra_init(struct thread_master *master)
+void isis_zebra_init(struct thread_master *master, int instance)
{
+ /* Initialize asynchronous zclient. */
zclient = zclient_new(master, &zclient_options_default);
zclient_init(zclient, PROTO_TYPE, 0, &isisd_privs);
zclient->zebra_connected = isis_zebra_connected;
@@ -319,7 +588,12 @@ void isis_zebra_init(struct thread_master *master)
zclient->redistribute_route_add = isis_zebra_read;
zclient->redistribute_route_del = isis_zebra_read;
- return;
+ /* Initialize special zclient for synchronous message exchanges. */
+ zclient_sync = zclient_new(master, &zclient_options_default);
+ zclient_sync->sock = -1;
+ zclient_sync->redist_default = ZEBRA_ROUTE_ISIS;
+ zclient_sync->instance = instance;
+ zclient_sync->privs = &isisd_privs;
}
void isis_zebra_stop(void)
diff --git a/isisd/isis_zebra.h b/isisd/isis_zebra.h
index d00f348c8..cca2b0881 100644
--- a/isisd/isis_zebra.h
+++ b/isisd/isis_zebra.h
@@ -24,10 +24,18 @@
extern struct zclient *zclient;
-void isis_zebra_init(struct thread_master *);
+struct label_chunk {
+ uint32_t start;
+ uint32_t end;
+ uint64_t used_mask;
+};
+#define CHUNK_SIZE 64
+
+void isis_zebra_init(struct thread_master *master, int instance);
void isis_zebra_stop(void);
struct isis_route_info;
+struct sr_prefix;
void isis_zebra_route_add_route(struct prefix *prefix,
struct prefix_ipv6 *src_p,
@@ -35,8 +43,14 @@ void isis_zebra_route_add_route(struct prefix *prefix,
void isis_zebra_route_del_route(struct prefix *prefix,
struct prefix_ipv6 *src_p,
struct isis_route_info *route_info);
+void isis_zebra_install_prefix_sid(const struct sr_prefix *srp);
+void isis_zebra_uninstall_prefix_sid(const struct sr_prefix *srp);
int isis_distribute_list_update(int routetype);
void isis_zebra_redistribute_set(afi_t afi, int type);
void isis_zebra_redistribute_unset(afi_t afi, int type);
+int isis_zebra_request_label_range(uint32_t base, uint32_t chunk_size);
+void isis_zebra_release_label_range(uint32_t start, uint32_t end);
+mpls_label_t isis_zebra_request_dynamic_label(void);
+void isis_zebra_release_dynamic_label(mpls_label_t label);
#endif /* _ZEBRA_ISIS_ZEBRA_H */
diff --git a/isisd/isisd.c b/isisd/isisd.c
index 2a8c503ae..caf50addd 100644
--- a/isisd/isisd.c
+++ b/isisd/isisd.c
@@ -56,6 +56,7 @@
#include "isisd/isis_events.h"
#include "isisd/isis_te.h"
#include "isisd/isis_mt.h"
+#include "isisd/isis_sr.h"
#include "isisd/fabricd.h"
#include "isisd/isis_nb.h"
@@ -128,6 +129,8 @@ struct isis_area *isis_area_create(const char *area_tag)
thread_add_timer(master, lsp_tick, area, 1, &area->t_tick);
flags_initialize(&area->flags);
+ isis_sr_area_init(area);
+
/*
* Default values
*/
@@ -271,6 +274,8 @@ int isis_area_destroy(const char *area_tag)
isis_area_invalidate_routes(area, area->is_type);
isis_area_verify_routes(area);
+ isis_sr_area_term(area);
+
spftree_area_del(area);
THREAD_TIMER_OFF(area->spf_timer[0]);
@@ -748,6 +753,9 @@ void print_debug(struct vty *vty, int flags, int onoff)
onoffs);
if (flags & DEBUG_SPF_EVENTS)
vty_out(vty, "IS-IS SPF events debugging is %s\n", onoffs);
+ if (flags & DEBUG_SR)
+ vty_out(vty, "IS-IS Segment Routing events debugging is %s\n",
+ onoffs);
if (flags & DEBUG_UPDATE_PACKETS)
vty_out(vty, "IS-IS Update related packet debugging is %s\n",
onoffs);
@@ -812,6 +820,10 @@ static int config_write_debug(struct vty *vty)
vty_out(vty, "debug " PROTO_NAME " spf-events\n");
write++;
}
+ if (flags & DEBUG_SR) {
+ vty_out(vty, "debug " PROTO_NAME " sr-events\n");
+ write++;
+ }
if (flags & DEBUG_UPDATE_PACKETS) {
vty_out(vty, "debug " PROTO_NAME " update-packets\n");
write++;
@@ -1011,6 +1023,33 @@ DEFUN (no_debug_isis_spfevents,
return CMD_SUCCESS;
}
+DEFUN (debug_isis_srevents,
+ debug_isis_srevents_cmd,
+ "debug " PROTO_NAME " sr-events",
+ DEBUG_STR
+ PROTO_HELP
+ "IS-IS Segment Routing Events\n")
+{
+ isis->debugs |= DEBUG_SR;
+ print_debug(vty, DEBUG_SR, 1);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_isis_srevents,
+ no_debug_isis_srevents_cmd,
+ "no debug " PROTO_NAME " sr-events",
+ NO_STR
+ UNDEBUG_STR
+ PROTO_HELP
+ "IS-IS Segment Routing Events\n")
+{
+ isis->debugs &= ~DEBUG_SR;
+ print_debug(vty, DEBUG_SR, 0);
+
+ return CMD_SUCCESS;
+}
+
DEFUN (debug_isis_rtevents,
debug_isis_rtevents_cmd,
"debug " PROTO_NAME " route-events",
@@ -2198,6 +2237,8 @@ void isis_init(void)
install_element(ENABLE_NODE, &no_debug_isis_upd_cmd);
install_element(ENABLE_NODE, &debug_isis_spfevents_cmd);
install_element(ENABLE_NODE, &no_debug_isis_spfevents_cmd);
+ install_element(ENABLE_NODE, &debug_isis_srevents_cmd);
+ install_element(ENABLE_NODE, &no_debug_isis_srevents_cmd);
install_element(ENABLE_NODE, &debug_isis_rtevents_cmd);
install_element(ENABLE_NODE, &no_debug_isis_rtevents_cmd);
install_element(ENABLE_NODE, &debug_isis_events_cmd);
@@ -2223,6 +2264,8 @@ void isis_init(void)
install_element(CONFIG_NODE, &no_debug_isis_upd_cmd);
install_element(CONFIG_NODE, &debug_isis_spfevents_cmd);
install_element(CONFIG_NODE, &no_debug_isis_spfevents_cmd);
+ install_element(CONFIG_NODE, &debug_isis_srevents_cmd);
+ install_element(CONFIG_NODE, &no_debug_isis_srevents_cmd);
install_element(CONFIG_NODE, &debug_isis_rtevents_cmd);
install_element(CONFIG_NODE, &no_debug_isis_rtevents_cmd);
install_element(CONFIG_NODE, &debug_isis_events_cmd);
diff --git a/isisd/isisd.h b/isisd/isisd.h
index 53776b2ec..56ea0993f 100644
--- a/isisd/isisd.h
+++ b/isisd/isisd.h
@@ -30,6 +30,7 @@
#include "isisd/isis_redist.h"
#include "isisd/isis_pdu_counter.h"
#include "isisd/isis_circuit.h"
+#include "isisd/isis_sr.h"
#include "isis_flags.h"
#include "isis_lsp.h"
#include "isis_memory.h"
@@ -165,6 +166,8 @@ struct isis_area {
struct list *mt_settings;
/* MPLS-TE settings */
struct mpls_te_area *mta;
+ /* Segment Routing information */
+ struct isis_sr_db srdb;
int ipv6_circuits;
bool purge_originator;
/* Counters */
@@ -218,6 +221,10 @@ int isis_area_passwd_cleartext_set(struct isis_area *area, int level,
int isis_area_passwd_hmac_md5_set(struct isis_area *area, int level,
const char *passwd, uint8_t snp_auth);
+/* YANG paths */
+#define ISIS_INSTANCE "/frr-isisd:isis/instance"
+#define ISIS_SR "/frr-isisd:isis/instance/segment-routing"
+
/* Master of threads. */
extern struct thread_master *master;
@@ -233,6 +240,7 @@ extern struct thread_master *master;
#define DEBUG_FLOODING (1<<9)
#define DEBUG_BFD (1<<10)
#define DEBUG_TX_QUEUE (1<<11)
+#define DEBUG_SR (1<<12)
#define lsp_debug(...) \
do { \
diff --git a/isisd/subdir.am b/isisd/subdir.am
index 94f9116d3..9e855ad8c 100644
--- a/isisd/subdir.am
+++ b/isisd/subdir.am
@@ -11,6 +11,7 @@ vtysh_scan += \
isisd/isis_redist.c \
isisd/isis_spf.c \
isisd/isis_te.c \
+ isisd/isis_sr.c \
isisd/isis_vty_fabricd.c \
isisd/isisd.c \
# end
@@ -48,6 +49,7 @@ noinst_HEADERS += \
isisd/isis_routemap.h \
isisd/isis_spf.h \
isisd/isis_spf_private.h \
+ isisd/isis_sr.h \
isisd/isis_te.h \
isisd/isis_tlvs.h \
isisd/isis_tx_queue.h \
@@ -77,6 +79,7 @@ LIBISIS_SOURCES = \
isisd/isis_route.c \
isisd/isis_routemap.c \
isisd/isis_spf.c \
+ isisd/isis_sr.c \
isisd/isis_te.c \
isisd/isis_tlvs.c \
isisd/isis_tx_queue.c \
diff --git a/lib/mpls.h b/lib/mpls.h
index 05cf2935e..126dbf753 100644
--- a/lib/mpls.h
+++ b/lib/mpls.h
@@ -127,7 +127,8 @@ enum lsp_types_t {
ZEBRA_LSP_LDP = 2, /* LDP LSP. */
ZEBRA_LSP_BGP = 3, /* BGP LSP. */
ZEBRA_LSP_OSPF_SR = 4,/* OSPF Segment Routing LSP. */
- ZEBRA_LSP_SHARP = 5, /* Identifier for test protocol */
+ ZEBRA_LSP_ISIS_SR = 5,/* IS-IS Segment Routing LSP. */
+ ZEBRA_LSP_SHARP = 6, /* Identifier for test protocol */
};
/* Functions for basic label operations. */
diff --git a/zebra/zebra_mpls.h b/zebra/zebra_mpls.h
index 33cb61434..e468fb9c1 100644
--- a/zebra/zebra_mpls.h
+++ b/zebra/zebra_mpls.h
@@ -430,6 +430,7 @@ static inline uint8_t lsp_distance(enum lsp_types_t type)
case ZEBRA_LSP_NONE:
case ZEBRA_LSP_SHARP:
case ZEBRA_LSP_OSPF_SR:
+ case ZEBRA_LSP_ISIS_SR:
return 150;
}
@@ -457,6 +458,8 @@ static inline enum lsp_types_t lsp_type_from_re_type(int re_type)
return ZEBRA_LSP_BGP;
case ZEBRA_ROUTE_OSPF:
return ZEBRA_LSP_OSPF_SR;
+ case ZEBRA_ROUTE_ISIS:
+ return ZEBRA_LSP_ISIS_SR;
case ZEBRA_ROUTE_SHARP:
return ZEBRA_LSP_SHARP;
default:
@@ -478,6 +481,8 @@ static inline int re_type_from_lsp_type(enum lsp_types_t lsp_type)
return ZEBRA_ROUTE_BGP;
case ZEBRA_LSP_OSPF_SR:
return ZEBRA_ROUTE_OSPF;
+ case ZEBRA_LSP_ISIS_SR:
+ return ZEBRA_ROUTE_ISIS;
case ZEBRA_LSP_NONE:
return ZEBRA_ROUTE_KERNEL;
case ZEBRA_LSP_SHARP:
@@ -505,6 +510,8 @@ static inline const char *nhlfe_type2str(enum lsp_types_t lsp_type)
return "BGP";
case ZEBRA_LSP_OSPF_SR:
return "SR (OSPF)";
+ case ZEBRA_LSP_ISIS_SR:
+ return "SR (IS-IS)";
case ZEBRA_LSP_SHARP:
return "SHARP";
case ZEBRA_LSP_NONE: