]> git.puffer.fish Git - matthieu/frr.git/commitdiff
isisd: add support for segment routing
authorRenato Westphal <renato@opensourcerouting.org>
Sun, 4 Aug 2019 01:02:37 +0000 (22:02 -0300)
committerOlivier Dugeon <olivier.dugeon@orange.com>
Thu, 30 Apr 2020 10:15:47 +0000 (12:15 +0200)
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>
24 files changed:
isisd/isis_adjacency.c
isisd/isis_adjacency.h
isisd/isis_cli.c
isisd/isis_errors.c
isisd/isis_errors.h
isisd/isis_lsp.c
isisd/isis_main.c
isisd/isis_nb.c
isisd/isis_nb.h
isisd/isis_nb_config.c
isisd/isis_route.c
isisd/isis_route.h
isisd/isis_spf.c
isisd/isis_sr.c [new file with mode: 0644]
isisd/isis_sr.h [new file with mode: 0644]
isisd/isis_tlvs.c
isisd/isis_tlvs.h
isisd/isis_zebra.c
isisd/isis_zebra.h
isisd/isisd.c
isisd/isisd.h
isisd/subdir.am
lib/mpls.h
zebra/zebra_mpls.h

index 4e0ee4448be14a033f2ba0038f52465838a832e3..acfe3e2e1f7af52991782fa514d7116167be6b39 100644 (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;
index 834eba7925eb9490ea1ac17389aea4ecf6bb8380..ec17446ade7793200103e5c97ec10a8384a6118c 100644 (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);
index 0101e30ad3e1d840818eac45dd950860833c24b2..c421750a825a54bb25cd2e0ef7d6eda249b82893 100644 (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"
index 755e70dbf60298577e065ea97006022943885c7e..7530d0b966fe347cd7af53e1afe44937c0488621 100644 (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,
        }
index 07327376070fcff073820e1fda54f2f21d1775b8..d5674dbf3021480c558806296a23540a07f05298 100644 (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);
index effd19ed7d81cf5d316c978c489b07c19982b64c..e578f616f4a0180d3e6245d05e6d4a76ecddc88d 100644 (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);
                        }
                }
 
index 4c841dffe2fb6e75b5dea86957c68f8d8a218eac..78654b2f1ccd08cf7f1dd8b1ddb6d99f11bd5372 100644 (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();
 
index 2dedebf983685da2d4cf2d786c82df7f0982b600..8e976d9bc8f99b84393816e0fc230b49ed0fc1e3 100644 (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,
                        },
                },
index 19ebee0e05a0d7606cbfda4980b4eea17a4c1ee4..58f6c3892628ec379d13beda7ed05e247e7422b1 100644 (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,
index f97202c9a81c5bb51baa0498d97911bce7c3c0cb..ec3b7baa3101370c0cba52fe79d0afad055e3bfb 100644 (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,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;
 }
index 516e3f90b9a1f7b9dab982874b054ba8d2e5a7d4..fa6af6c216ff1b93a47d712fb8f2816012d763e7 100644 (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;
                        }
index 96bcdc3508cc6852302f4d16b6f714d031b336da..0356668d7e6cd6ed8e072fd34c07c1f3f1838d6d 100644 (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 {
index 30b9f886153426f9912e2763679150f155c2e617..3091650ef135c379ae48e179c739cf9e515fb832 100644 (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;
diff --git a/isisd/isis_sr.c b/isisd/isis_sr.c
new file mode 100644 (file)
index 0000000..54689ea
--- /dev/null
@@ -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 (file)
index 0000000..286ebeb
--- /dev/null
@@ -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 */
index 761005d0cdc8dd26c9f558334b7e7480dff7ae96..be88ee85a68efdbb635bbb60946471549f8a0376 100644 (file)
 #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);
 
index 2948728e2b49b4dba369e5c961278dc7349990bb..c3b25669b61679c822d4321fe708a1206a7c8cae 100644 (file)
 #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,
index 630264768f5ab79431749ca2c652664d9cedf9ba..3ce75623143de4a52a7be88e855a57a824a95292 100644 (file)
 #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)
index d00f348c8e310da35360982c01d412c15608ee2d..cca2b08811fcead86f06d54954a14ebf97046113 100644 (file)
 
 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 */
index 2a8c503ae057d1849145b35dba297cd856b5b79f..caf50addd46ef0dfd96edf48e10bd7174dff9d9c 100644 (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);
index 53776b2ec6a4ac7032e4d12c7af4b3c04affd07b..56ea0993fd3fb58cd212ed6395f339ef7fe28b2f 100644 (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 {                                                                   \
index 94f9116d3495d92185e7dd497535591ad825f806..9e855ad8cf2629e6719176d7fc1e20a0be25922a 100644 (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 \
index 05cf2935e8161fb9a578cd01f7a462b4aebe9690..126dbf753d47876fbc7ec09c1bbc894e7f81bca2 100644 (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. */
index 33cb614346a54fffc8e23a7799509d5482aa9945..e468fb9c1bbdce28779921c133025904b09480f6 100644 (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: