isisd: add support for segment routing

This is an implementation of the IS-IS SR draft [1] for FRR.

The following features are supported:
* IPv4 and IPv6 Prefix-SIDs;
* IPv4 and IPv6 Adj-SIDs and LAN-Adj-SIDs;
* Index and absolute labels;
* The no-php and explicit-null Prefix-SID flags;
* Full integration with the Label Manager.

Known limitations:
* No support for Anycast-SIDs;
* No support for the SID/Label Binding TLV (required for LDP interop).
* No support for persistent Adj-SIDs;
* No support for multiple SRGBs.

[1] draft-ietf-isis-segment-routing-extensions-25

Signed-off-by: Olivier Dugeon <olivier.dugeon@orange.com>
Signed-off-by: Renato Westphal <renato@opensourcerouting.org>
This commit is contained in:
Renato Westphal 2019-08-03 22:02:37 -03:00 committed by Olivier Dugeon
parent 8f6c893629
commit 26f6acafc3
24 changed files with 2533 additions and 113 deletions

View file

@ -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;

View file

@ -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);

View file

@ -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"

View file

@ -37,6 +37,12 @@ static struct log_ref ferr_isis_err[] = {
.description = "Isis has detected an error within configuration for the router",
.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,
}

View file

@ -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);

View file

@ -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);
}
}

View file

@ -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();

View file

@ -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,
},
},

View file

@ -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,

View file

@ -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,34 +1545,86 @@ 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. */
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(
struct nb_cb_destroy_args *args)
{
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;
}
int isis_instance_segment_routing_prefix_sid_map_prefix_sid_destroy(
enum nb_event event, const struct lyd_node *dnode)
void isis_instance_segment_routing_prefix_sid_map_prefix_sid_apply_finish(
struct nb_cb_apply_finish_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;
struct isis_area *area;
return NB_OK;
pcfg = nb_running_get_entry(args->dnode, NULL, true);
area = pcfg->area;
lsp_regenerate_schedule(area, area->is_type, 0);
}
/*
@ -1532,17 +1632,15 @@ int isis_instance_segment_routing_prefix_sid_map_prefix_sid_destroy(
* /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;
}

View file

@ -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;
}

View file

@ -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 {

View file

@ -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;

1525
isisd/isis_sr.c Normal file

File diff suppressed because it is too large Load diff

259
isisd/isis_sr.h Normal file
View file

@ -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 */

View file

@ -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);

View file

@ -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,

View file

@ -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)

View file

@ -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 */

View file

@ -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);

View file

@ -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 { \

View file

@ -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 \

View file

@ -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. */

View file

@ -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: