]> git.proxmox.com Git - mirror_frr.git/blobdiff - isisd/isis_spf.c
lib: enforce vrf_name_to_id by returning default_vrf when name is null
[mirror_frr.git] / isisd / isis_spf.c
index 556f2890cfbc22caee61a27ed0783f72bb3e799c..b6f751463a7ce187478f50f557ba319a456c0213 100644 (file)
 #include "command.h"
 #include "memory.h"
 #include "prefix.h"
-#include "hash.h"
 #include "if.h"
 #include "table.h"
 #include "spf_backoff.h"
-#include "jhash.h"
-#include "skiplist.h"
+#include "srcdest_table.h"
 
 #include "isis_constants.h"
 #include "isis_common.h"
 #include "isis_csm.h"
 #include "isis_mt.h"
 #include "isis_tlvs.h"
+#include "fabricd.h"
+#include "isis_spf_private.h"
 
 DEFINE_MTYPE_STATIC(ISISD, ISIS_SPF_RUN, "ISIS SPF Run Info");
 
-enum vertextype {
-       VTYPE_PSEUDO_IS = 1,
-       VTYPE_PSEUDO_TE_IS,
-       VTYPE_NONPSEUDO_IS,
-       VTYPE_NONPSEUDO_TE_IS,
-       VTYPE_ES,
-       VTYPE_IPREACH_INTERNAL,
-       VTYPE_IPREACH_EXTERNAL,
-       VTYPE_IPREACH_TE,
-       VTYPE_IP6REACH_INTERNAL,
-       VTYPE_IP6REACH_EXTERNAL
-};
-
-#define VTYPE_IS(t) ((t) >= VTYPE_PSEUDO_IS && (t) <= VTYPE_NONPSEUDO_TE_IS)
-#define VTYPE_ES(t) ((t) == VTYPE_ES)
-#define VTYPE_IP(t) ((t) >= VTYPE_IPREACH_INTERNAL && (t) <= VTYPE_IP6REACH_EXTERNAL)
-
-/*
- * Triple <N, d(N), {Adj(N)}>
- */
-union isis_N {
-       uint8_t id[ISIS_SYS_ID_LEN + 1];
-       struct prefix prefix;
-};
-struct isis_vertex {
-       enum vertextype type;
-       union isis_N N;
-       uint32_t d_N;     /* d(N) Distance from this IS      */
-       uint16_t depth; /* The depth in the imaginary tree */
-       struct list *Adj_N;    /* {Adj(N)} next hop or neighbor list */
-       struct list *parents;  /* list of parents for ECMP */
-       uint64_t insert_counter;
-};
-
-/* Vertex Queue and associated functions */
-
-struct isis_vertex_queue {
-       union {
-               struct skiplist *slist;
-               struct list *list;
-       } l;
-       struct hash *hash;
-       uint64_t insert_counter;
-};
-
-static unsigned isis_vertex_queue_hash_key(void *vp)
-{
-       struct isis_vertex *vertex = vp;
-
-       if (VTYPE_IP(vertex->type))
-               return prefix_hash_key(&vertex->N.prefix);
-
-       return jhash(vertex->N.id, ISIS_SYS_ID_LEN + 1, 0x55aa5a5a);
-}
-
-static int isis_vertex_queue_hash_cmp(const void *a, const void *b)
-{
-       const struct isis_vertex *va = a, *vb = b;
-
-       if (va->type != vb->type)
-               return 0;
-
-       if (VTYPE_IP(va->type))
-               return prefix_cmp(&va->N.prefix, &vb->N.prefix) == 0;
-
-       return memcmp(va->N.id, vb->N.id, ISIS_SYS_ID_LEN + 1) == 0;
-}
-
-/*
- * Compares vertizes for sorting in the TENT list. Returns true
- * if candidate should be considered before current, false otherwise.
- */
-static int isis_vertex_queue_tent_cmp(void *a, void *b)
-{
-       struct isis_vertex *va = a;
-       struct isis_vertex *vb = b;
-
-       if (va->d_N < vb->d_N)
-               return -1;
-
-       if (va->d_N > vb->d_N)
-               return 1;
-
-       if (va->type < vb->type)
-               return -1;
-
-       if (va->type > vb->type)
-               return 1;
-
-       if (va->insert_counter < vb->insert_counter)
-               return -1;
-
-       if (va->insert_counter > vb->insert_counter)
-               return 1;
-
-       return 0;
-}
-
-static struct skiplist *isis_vertex_queue_skiplist(void)
-{
-       return skiplist_new(0, isis_vertex_queue_tent_cmp, NULL);
-}
-
-static void isis_vertex_queue_init(struct isis_vertex_queue *queue,
-                                  const char *name, bool ordered)
-{
-       if (ordered) {
-               queue->insert_counter = 1;
-               queue->l.slist = isis_vertex_queue_skiplist();
-       } else {
-               queue->insert_counter = 0;
-               queue->l.list = list_new();
-       }
-       queue->hash = hash_create(isis_vertex_queue_hash_key,
-                                 isis_vertex_queue_hash_cmp, name);
-}
-
-static void isis_vertex_del(struct isis_vertex *vertex);
-
-static void isis_vertex_queue_clear(struct isis_vertex_queue *queue)
-{
-       hash_clean(queue->hash, NULL);
-
-       if (queue->insert_counter) {
-               struct isis_vertex *vertex;
-               while (0 == skiplist_first(queue->l.slist, NULL,
-                                          (void **)&vertex)) {
-                       isis_vertex_del(vertex);
-                       skiplist_delete_first(queue->l.slist);
-               }
-               queue->insert_counter = 1;
-       } else {
-               queue->l.list->del = (void (*)(void *))isis_vertex_del;
-               list_delete_all_node(queue->l.list);
-               queue->l.list->del = NULL;
-       }
-}
-
-static void isis_vertex_queue_free(struct isis_vertex_queue *queue)
-{
-       isis_vertex_queue_clear(queue);
-
-       hash_free(queue->hash);
-       queue->hash = NULL;
-
-       if (queue->insert_counter) {
-               skiplist_free(queue->l.slist);
-               queue->l.slist = NULL;
-       } else
-               list_delete_and_null(&queue->l.list);
-}
-
-static unsigned int isis_vertex_queue_count(struct isis_vertex_queue *queue)
-{
-       return hashcount(queue->hash);
-}
-
-static void isis_vertex_queue_append(struct isis_vertex_queue *queue,
-                                    struct isis_vertex *vertex)
-{
-       assert(!queue->insert_counter);
-
-       listnode_add(queue->l.list, vertex);
-
-       struct isis_vertex *inserted;
-
-       inserted = hash_get(queue->hash, vertex, hash_alloc_intern);
-       assert(inserted == vertex);
-}
-
-static void isis_vertex_queue_insert(struct isis_vertex_queue *queue,
-                                    struct isis_vertex *vertex)
-{
-       assert(queue->insert_counter);
-       vertex->insert_counter = queue->insert_counter++;
-       assert(queue->insert_counter != (uint64_t)-1);
-
-       skiplist_insert(queue->l.slist, vertex, vertex);
-
-       struct isis_vertex *inserted;
-       inserted = hash_get(queue->hash, vertex, hash_alloc_intern);
-       assert(inserted == vertex);
-}
-
-static struct isis_vertex *
-isis_vertex_queue_pop(struct isis_vertex_queue *queue)
-{
-       assert(queue->insert_counter);
-
-       struct isis_vertex *rv;
-
-       if (skiplist_first(queue->l.slist, NULL, (void **)&rv))
-               return NULL;
-
-       skiplist_delete_first(queue->l.slist);
-       hash_release(queue->hash, rv);
-
-       return rv;
-}
-
-static void isis_vertex_queue_delete(struct isis_vertex_queue *queue,
-                                    struct isis_vertex *vertex)
-{
-       assert(queue->insert_counter);
-
-       skiplist_delete(queue->l.slist, vertex, vertex);
-       hash_release(queue->hash, vertex);
-}
-
-#define ALL_QUEUE_ELEMENTS_RO(queue, node, data)                               \
-       ALL_LIST_ELEMENTS_RO((queue)->l.list, node, data)
-
-
-/* End of vertex queue definitions */
-
-struct isis_spftree {
-       struct isis_vertex_queue paths; /* the SPT */
-       struct isis_vertex_queue tents; /* TENT */
-       struct isis_area *area;    /* back pointer to area */
-       unsigned int runcount;     /* number of runs since uptime */
-       time_t last_run_timestamp; /* last run timestamp as wall time for display */
-       time_t last_run_monotime;  /* last run as monotime for scheduling */
-       time_t last_run_duration;  /* last run duration in msec */
-
-       uint16_t mtid;
-       int family;
-       int level;
-};
-
-
 /*
  *  supports the given af ?
  */
@@ -392,58 +162,42 @@ static const char *vtype2string(enum vertextype vtype)
        return NULL; /* Not reached */
 }
 
-static const char *vid2string(struct isis_vertex *vertex, char *buff, int size)
+const char *vid2string(struct isis_vertex *vertex, char *buff, int size)
 {
        if (VTYPE_IS(vertex->type) || VTYPE_ES(vertex->type)) {
                return print_sys_hostname(vertex->N.id);
        }
 
        if (VTYPE_IP(vertex->type)) {
-               prefix2str((struct prefix *)&vertex->N.prefix, buff, size);
+               srcdest2str(&vertex->N.ip.dest,
+                           &vertex->N.ip.src,
+                           buff, size);
                return buff;
        }
 
        return "UNKNOWN";
 }
 
-static void isis_vertex_id_init(struct isis_vertex *vertex, union isis_N *n,
-                               enum vertextype vtype)
-{
-       vertex->type = vtype;
-
-       if (VTYPE_IS(vtype) || VTYPE_ES(vtype)) {
-               memcpy(vertex->N.id, n->id, ISIS_SYS_ID_LEN + 1);
-       } else if (VTYPE_IP(vtype)) {
-               memcpy(&vertex->N.prefix, &n->prefix, sizeof(struct prefix));
-       } else {
-               zlog_err("WTF!");
-       }
-}
-
-static struct isis_vertex *isis_vertex_new(union isis_N *n,
+static struct isis_vertex *isis_vertex_new(struct isis_spftree *spftree,
+                                          void *id,
                                           enum vertextype vtype)
 {
        struct isis_vertex *vertex;
 
        vertex = XCALLOC(MTYPE_ISIS_VERTEX, sizeof(struct isis_vertex));
 
-       isis_vertex_id_init(vertex, n, vtype);
+       isis_vertex_id_init(vertex, id, vtype);
 
        vertex->Adj_N = list_new();
        vertex->parents = list_new();
 
-       return vertex;
-}
-
-static void isis_vertex_del(struct isis_vertex *vertex)
-{
-       list_delete_and_null(&vertex->Adj_N);
-       list_delete_and_null(&vertex->parents);
-
-       memset(vertex, 0, sizeof(struct isis_vertex));
-       XFREE(MTYPE_ISIS_VERTEX, vertex);
+       if (spftree->hopcount_metric) {
+               vertex->firsthops = hash_create(isis_vertex_queue_hash_key,
+                                               isis_vertex_queue_hash_cmp,
+                                               NULL);
+       }
 
-       return;
+       return vertex;
 }
 
 static void isis_vertex_adj_del(struct isis_vertex *vertex,
@@ -465,13 +219,10 @@ struct isis_spftree *isis_spftree_new(struct isis_area *area)
        struct isis_spftree *tree;
 
        tree = XCALLOC(MTYPE_ISIS_SPFTREE, sizeof(struct isis_spftree));
-       if (tree == NULL) {
-               zlog_err("ISIS-Spf: isis_spftree_new Out of memory!");
-               return NULL;
-       }
 
        isis_vertex_queue_init(&tree->tents, "IS-IS SPF tents", true);
        isis_vertex_queue_init(&tree->paths, "IS-IS SPF paths", false);
+       tree->route_table = srcdest_table_init();
        tree->area = area;
        tree->last_run_timestamp = 0;
        tree->last_run_monotime = 0;
@@ -484,8 +235,10 @@ void isis_spftree_del(struct isis_spftree *spftree)
 {
        isis_vertex_queue_free(&spftree->tents);
        isis_vertex_queue_free(&spftree->paths);
-       XFREE(MTYPE_ISIS_SPFTREE, spftree);
+       route_table_finish(spftree->route_table);
+       spftree->route_table = NULL;
 
+       XFREE(MTYPE_ISIS_SPFTREE, spftree);
        return;
 }
 
@@ -504,67 +257,47 @@ static void isis_spftree_adj_del(struct isis_spftree *spftree,
 
 void spftree_area_init(struct isis_area *area)
 {
-       if (area->is_type & IS_LEVEL_1) {
-               if (area->spftree[0] == NULL)
-                       area->spftree[0] = isis_spftree_new(area);
-               if (area->spftree6[0] == NULL)
-                       area->spftree6[0] = isis_spftree_new(area);
-       }
+       for (int tree = SPFTREE_IPV4; tree < SPFTREE_COUNT; tree++) {
+               for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) {
+                       if (!(area->is_type & level))
+                               continue;
+                       if (area->spftree[tree][level - 1])
+                               continue;
 
-       if (area->is_type & IS_LEVEL_2) {
-               if (area->spftree[1] == NULL)
-                       area->spftree[1] = isis_spftree_new(area);
-               if (area->spftree6[1] == NULL)
-                       area->spftree6[1] = isis_spftree_new(area);
+                       area->spftree[tree][level - 1] = isis_spftree_new(area);
+               }
        }
-
-       return;
 }
 
 void spftree_area_del(struct isis_area *area)
 {
-       if (area->is_type & IS_LEVEL_1) {
-               if (area->spftree[0] != NULL) {
-                       isis_spftree_del(area->spftree[0]);
-                       area->spftree[0] = NULL;
-               }
-               if (area->spftree6[0]) {
-                       isis_spftree_del(area->spftree6[0]);
-                       area->spftree6[0] = NULL;
-               }
-       }
+       for (int tree = SPFTREE_IPV4; tree < SPFTREE_COUNT; tree++) {
+               for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) {
+                       if (!(area->is_type & level))
+                               continue;
+                       if (!area->spftree[tree][level - 1])
+                               continue;
 
-       if (area->is_type & IS_LEVEL_2) {
-               if (area->spftree[1] != NULL) {
-                       isis_spftree_del(area->spftree[1]);
-                       area->spftree[1] = NULL;
-               }
-               if (area->spftree6[1] != NULL) {
-                       isis_spftree_del(area->spftree6[1]);
-                       area->spftree6[1] = NULL;
+                       isis_spftree_del(area->spftree[tree][level - 1]);
                }
        }
-
-       return;
 }
 
 void spftree_area_adj_del(struct isis_area *area, struct isis_adjacency *adj)
 {
-       if (area->is_type & IS_LEVEL_1) {
-               if (area->spftree[0] != NULL)
-                       isis_spftree_adj_del(area->spftree[0], adj);
-               if (area->spftree6[0] != NULL)
-                       isis_spftree_adj_del(area->spftree6[0], adj);
-       }
-
-       if (area->is_type & IS_LEVEL_2) {
-               if (area->spftree[1] != NULL)
-                       isis_spftree_adj_del(area->spftree[1], adj);
-               if (area->spftree6[1] != NULL)
-                       isis_spftree_adj_del(area->spftree6[1], adj);
+       for (int tree = SPFTREE_IPV4; tree < SPFTREE_COUNT; tree++) {
+               for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) {
+                       if (!(area->is_type & level))
+                               continue;
+                       if (!area->spftree[tree][level - 1])
+                               continue;
+                       isis_spftree_adj_del(area->spftree[tree][level - 1],
+                                            adj);
+               }
        }
 
-       return;
+       if (fabricd_spftree(area) != NULL)
+               isis_spftree_adj_del(fabricd_spftree(area), adj);
 }
 
 /*
@@ -595,19 +328,15 @@ static struct isis_vertex *isis_spf_add_root(struct isis_spftree *spftree,
        struct isis_vertex *vertex;
        struct isis_lsp *lsp;
 #ifdef EXTREME_DEBUG
-       char buff[PREFIX2STR_BUFFER];
+       char buff[VID2STR_BUFFER];
 #endif /* EXTREME_DEBUG */
-       union isis_N n;
-
-       memcpy(n.id, sysid, ISIS_SYS_ID_LEN);
-       LSP_PSEUDO_ID(n.id) = 0;
 
        lsp = isis_root_system_lsp(spftree->area, spftree->level, sysid);
        if (lsp == NULL)
                zlog_warn("ISIS-Spf: could not find own l%d LSP!",
                          spftree->level);
 
-       vertex = isis_vertex_new(&n,
+       vertex = isis_vertex_new(spftree, sysid,
                                 spftree->area->oldmetric
                                         ? VTYPE_NONPSEUDO_IS
                                         : VTYPE_NONPSEUDO_TE_IS);
@@ -623,14 +352,24 @@ static struct isis_vertex *isis_spf_add_root(struct isis_spftree *spftree,
        return vertex;
 }
 
-static struct isis_vertex *isis_find_vertex(struct isis_vertex_queue *queue,
-                                           union isis_N *n,
-                                           enum vertextype vtype)
+static void vertex_add_parent_firsthop(struct hash_backet *backet, void *arg)
+{
+       struct isis_vertex *vertex = arg;
+       struct isis_vertex *hop = backet->data;
+
+       hash_get(vertex->firsthops, hop, hash_alloc_intern);
+}
+
+static void vertex_update_firsthops(struct isis_vertex *vertex,
+                                   struct isis_vertex *parent)
 {
-       struct isis_vertex querier;
+       if (vertex->d_N <= 2)
+               hash_get(vertex->firsthops, vertex, hash_alloc_intern);
 
-       isis_vertex_id_init(&querier, n, vtype);
-       return hash_lookup(queue->hash, &querier);
+       if (vertex->d_N < 2 || !parent)
+               return;
+
+       hash_iterate(parent->firsthops, vertex_add_parent_firsthop, vertex);
 }
 
 /*
@@ -646,12 +385,12 @@ static struct isis_vertex *isis_spf_add2tent(struct isis_spftree *spftree,
        struct listnode *node;
        struct isis_adjacency *parent_adj;
 #ifdef EXTREME_DEBUG
-       char buff[PREFIX2STR_BUFFER];
+       char buff[VID2STR_BUFFER];
 #endif
 
        assert(isis_find_vertex(&spftree->paths, id, vtype) == NULL);
        assert(isis_find_vertex(&spftree->tents, id, vtype) == NULL);
-       vertex = isis_vertex_new(id, vtype);
+       vertex = isis_vertex_new(spftree, id, vtype);
        vertex->d_N = cost;
        vertex->depth = depth;
 
@@ -659,6 +398,9 @@ static struct isis_vertex *isis_spf_add2tent(struct isis_spftree *spftree,
                listnode_add(vertex->parents, parent);
        }
 
+       if (spftree->hopcount_metric)
+               vertex_update_firsthops(vertex, parent);
+
        if (parent && parent->Adj_N && listcount(parent->Adj_N) > 0) {
                for (ALL_LIST_ELEMENTS_RO(parent->Adj_N, node, parent_adj))
                        listnode_add(vertex->Adj_N, parent_adj);
@@ -719,15 +461,20 @@ static void process_N(struct isis_spftree *spftree, enum vertextype vtype,
 {
        struct isis_vertex *vertex;
 #ifdef EXTREME_DEBUG
-       char buff[PREFIX2STR_BUFFER];
+       char buff[VID2STR_BUFFER];
 #endif
 
        assert(spftree && parent);
 
-       struct prefix p;
+       if (spftree->hopcount_metric
+           && !VTYPE_IS(vtype))
+               return;
+
+       struct prefix_pair p;
        if (vtype >= VTYPE_IPREACH_INTERNAL) {
-               prefix_copy(&p, id);
-               apply_mask(&p);
+               memcpy(&p, id, sizeof(p));
+               apply_mask(&p.dest);
+               apply_mask((struct prefix *)&p.src);
                id = &p;
        }
 
@@ -775,6 +522,8 @@ static void process_N(struct isis_spftree *spftree, enum vertextype vtype,
                                if (listnode_lookup(vertex->Adj_N, parent_adj)
                                    == NULL)
                                        listnode_add(vertex->Adj_N, parent_adj);
+                       if (spftree->hopcount_metric)
+                               vertex_update_firsthops(vertex, parent);
                        /*      2) */
                        if (listcount(vertex->Adj_N) > ISIS_MAX_PATH_SPLITS)
                                remove_excess_adjs(vertex->Adj_N);
@@ -814,6 +563,7 @@ static int isis_spf_process_lsp(struct isis_spftree *spftree,
        enum vertextype vtype;
        static const uint8_t null_sysid[ISIS_SYS_ID_LEN];
        struct isis_mt_router_info *mt_router_info = NULL;
+       struct prefix_pair ip_info;
 
        if (!lsp->tlvs)
                return ISIS_OK;
@@ -853,6 +603,9 @@ lspfragloop:
                        for (r = (struct isis_oldstyle_reach *)
                                         lsp->tlvs->oldstyle_reach.head;
                             r; r = r->next) {
+                               if (fabricd)
+                                       continue;
+
                                /* C.2.6 a) */
                                /* Two way connectivity */
                                if (!memcmp(r->id, root_sysid, ISIS_SYS_ID_LEN))
@@ -889,7 +642,7 @@ lspfragloop:
                        if (!pseudo_lsp
                            && !memcmp(er->id, null_sysid, ISIS_SYS_ID_LEN))
                                continue;
-                       dist = cost + er->metric;
+                       dist = cost + (spftree->hopcount_metric ? 1 : er->metric);
                        process_N(spftree,
                                  LSP_PSEUDO_ID(er->id) ? VTYPE_PSEUDO_TE_IS
                                                        : VTYPE_NONPSEUDO_TE_IS,
@@ -897,7 +650,7 @@ lspfragloop:
                }
        }
 
-       if (!pseudo_lsp && spftree->family == AF_INET
+       if (!fabricd && !pseudo_lsp && spftree->family == AF_INET
            && spftree->mtid == ISIS_MT_IPV4_UNICAST) {
                struct isis_item_list *reachs[] = {
                        &lsp->tlvs->oldstyle_ip_reach,
@@ -907,12 +660,17 @@ lspfragloop:
                        vtype = i ? VTYPE_IPREACH_EXTERNAL
                                  : VTYPE_IPREACH_INTERNAL;
 
+                       memset(&ip_info, 0, sizeof(ip_info));
+                       ip_info.dest.family = AF_INET;
+
                        struct isis_oldstyle_ip_reach *r;
                        for (r = (struct isis_oldstyle_ip_reach *)reachs[i]
                                         ->head;
                             r; r = r->next) {
                                dist = cost + r->metric;
-                               process_N(spftree, vtype, (void *)&r->prefix,
+                               ip_info.dest.u.prefix4 = r->prefix.prefix;
+                               ip_info.dest.prefixlen = r->prefix.prefixlen;
+                               process_N(spftree, vtype, &ip_info,
                                          dist, depth + 1, parent);
                        }
                }
@@ -926,6 +684,9 @@ lspfragloop:
                        ipv4_reachs = isis_lookup_mt_items(
                                &lsp->tlvs->mt_ip_reach, spftree->mtid);
 
+               memset(&ip_info, 0, sizeof(ip_info));
+               ip_info.dest.family = AF_INET;
+
                struct isis_extended_ip_reach *r;
                for (r = ipv4_reachs
                                 ? (struct isis_extended_ip_reach *)
@@ -933,7 +694,9 @@ lspfragloop:
                                 : NULL;
                     r; r = r->next) {
                        dist = cost + r->metric;
-                       process_N(spftree, VTYPE_IPREACH_TE, (void *)&r->prefix,
+                       ip_info.dest.u.prefix4 = r->prefix.prefix;
+                       ip_info.dest.prefixlen = r->prefix.prefixlen;
+                       process_N(spftree, VTYPE_IPREACH_TE, &ip_info,
                                  dist, depth + 1, parent);
                }
        }
@@ -954,7 +717,28 @@ lspfragloop:
                        dist = cost + r->metric;
                        vtype = r->external ? VTYPE_IP6REACH_EXTERNAL
                                            : VTYPE_IP6REACH_INTERNAL;
-                       process_N(spftree, vtype, (void *)&r->prefix, dist,
+                       memset(&ip_info, 0, sizeof(ip_info));
+                       ip_info.dest.family = AF_INET6;
+                       ip_info.dest.u.prefix6 = r->prefix.prefix;
+                       ip_info.dest.prefixlen = r->prefix.prefixlen;
+
+                       if (r->subtlvs
+                           && r->subtlvs->source_prefix
+                           && r->subtlvs->source_prefix->prefixlen) {
+                               if (spftree->tree_id != SPFTREE_DSTSRC) {
+                                       char buff[VID2STR_BUFFER];
+                                       zlog_warn("Ignoring dest-src route %s in non dest-src topology",
+                                               srcdest2str(
+                                                       &ip_info.dest,
+                                                       r->subtlvs->source_prefix,
+                                                       buff, sizeof(buff)
+                                               )
+                                       );
+                                       continue;
+                               }
+                               ip_info.src = *r->subtlvs->source_prefix;
+                       }
+                       process_N(spftree, vtype, &ip_info, dist,
                                  depth + 1, parent);
                }
        }
@@ -983,7 +767,7 @@ static int isis_spf_preload_tent(struct isis_spftree *spftree,
        struct list *adj_list;
        struct list *adjdb;
        struct prefix_ipv4 *ipv4;
-       struct prefix prefix;
+       struct prefix_pair ip_info;
        int retval = ISIS_OK;
        uint8_t lsp_id[ISIS_SYS_ID_LEN + 2];
        static uint8_t null_lsp_id[ISIS_SYS_ID_LEN + 2];
@@ -1006,28 +790,30 @@ static int isis_spf_preload_tent(struct isis_spftree *spftree,
                /*
                 * Add IP(v6) addresses of this circuit
                 */
-               if (spftree->family == AF_INET) {
-                       prefix.family = AF_INET;
+               if (spftree->family == AF_INET && !spftree->hopcount_metric) {
+                       memset(&ip_info, 0, sizeof(ip_info));
+                       ip_info.dest.family = AF_INET;
                        for (ALL_LIST_ELEMENTS_RO(circuit->ip_addrs, ipnode,
                                                  ipv4)) {
-                               prefix.u.prefix4 = ipv4->prefix;
-                               prefix.prefixlen = ipv4->prefixlen;
-                               apply_mask(&prefix);
+                               ip_info.dest.u.prefix4 = ipv4->prefix;
+                               ip_info.dest.prefixlen = ipv4->prefixlen;
+                               apply_mask(&ip_info.dest);
                                isis_spf_add_local(spftree,
                                                   VTYPE_IPREACH_INTERNAL,
-                                                  &prefix, NULL, 0, parent);
+                                                  &ip_info, NULL, 0, parent);
                        }
                }
-               if (spftree->family == AF_INET6) {
-                       prefix.family = AF_INET6;
+               if (spftree->family == AF_INET6 && !spftree->hopcount_metric) {
+                       memset(&ip_info, 0, sizeof(ip_info));
+                       ip_info.dest.family = AF_INET6;
                        for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_non_link,
                                                  ipnode, ipv6)) {
-                               prefix.prefixlen = ipv6->prefixlen;
-                               prefix.u.prefix6 = ipv6->prefix;
-                               apply_mask(&prefix);
+                               ip_info.dest.u.prefix6 = ipv6->prefix;
+                               ip_info.dest.prefixlen = ipv6->prefixlen;
+                               apply_mask(&ip_info.dest);
                                isis_spf_add_local(spftree,
                                                   VTYPE_IP6REACH_INTERNAL,
-                                                  &prefix, NULL, 0, parent);
+                                                  &ip_info, NULL, 0, parent);
                        }
                }
                if (circuit->circ_type == CIRCUIT_T_BROADCAST) {
@@ -1038,7 +824,7 @@ static int isis_spf_preload_tent(struct isis_spftree *spftree,
                        adjdb = circuit->u.bc.adjdb[spftree->level - 1];
                        isis_adj_build_up_list(adjdb, adj_list);
                        if (listcount(adj_list) == 0) {
-                               list_delete_and_null(&adj_list);
+                               list_delete(&adj_list);
                                if (isis->debugs & DEBUG_SPF_EVENTS)
                                        zlog_debug(
                                                "ISIS-Spf: no L%d adjacencies on circuit %s",
@@ -1061,6 +847,7 @@ static int isis_spf_preload_tent(struct isis_spftree *spftree,
                                        LSP_PSEUDO_ID(lsp_id) = 0;
                                        isis_spf_add_local(
                                                spftree, VTYPE_ES, lsp_id, adj,
+                                               spftree->hopcount_metric ? 1 :
                                                circuit->te_metric
                                                        [spftree->level - 1],
                                                parent);
@@ -1078,6 +865,7 @@ static int isis_spf_preload_tent(struct isis_spftree *spftree,
                                                        ? VTYPE_NONPSEUDO_IS
                                                        : VTYPE_NONPSEUDO_TE_IS,
                                                lsp_id, adj,
+                                               spftree->hopcount_metric ? 1 :
                                                circuit->te_metric
                                                        [spftree->level - 1],
                                                parent);
@@ -1099,10 +887,10 @@ static int isis_spf_preload_tent(struct isis_spftree *spftree,
                                case ISIS_SYSTYPE_UNKNOWN:
                                default:
                                        zlog_warn(
-                                               "isis_spf_preload_tent unknow adj type");
+                                               "isis_spf_preload_tent unknown adj type");
                                }
                        }
-                       list_delete_and_null(&adj_list);
+                       list_delete(&adj_list);
                        /*
                         * Add the pseudonode
                         */
@@ -1147,10 +935,10 @@ static int isis_spf_preload_tent(struct isis_spftree *spftree,
                                        circuit->circuit_id);
                                continue;
                        }
-                       isis_spf_process_lsp(
-                               spftree, lsp,
-                               circuit->te_metric[spftree->level - 1], 0,
-                               root_sysid, parent);
+                       isis_spf_process_lsp(spftree, lsp,
+                                            spftree->hopcount_metric ?
+                                            1 : circuit->te_metric[spftree->level - 1],
+                                            0, root_sysid, parent);
                } else if (circuit->circ_type == CIRCUIT_T_P2P) {
                        adj = circuit->u.p2p.neighbor;
                        if (!adj || adj->adj_state != ISIS_ADJ_UP)
@@ -1163,6 +951,7 @@ static int isis_spf_preload_tent(struct isis_spftree *spftree,
                                LSP_PSEUDO_ID(lsp_id) = 0;
                                isis_spf_add_local(
                                        spftree, VTYPE_ES, lsp_id, adj,
+                                       spftree->hopcount_metric ? 1 :
                                        circuit->te_metric[spftree->level - 1],
                                        parent);
                                break;
@@ -1182,6 +971,7 @@ static int isis_spf_preload_tent(struct isis_spftree *spftree,
                                                        ? VTYPE_NONPSEUDO_IS
                                                        : VTYPE_NONPSEUDO_TE_IS,
                                                lsp_id, adj,
+                                               spftree->hopcount_metric ? 1 :
                                                circuit->te_metric
                                                        [spftree->level - 1],
                                                parent);
@@ -1210,7 +1000,7 @@ static int isis_spf_preload_tent(struct isis_spftree *spftree,
 static void add_to_paths(struct isis_spftree *spftree,
                         struct isis_vertex *vertex)
 {
-       char buff[PREFIX2STR_BUFFER];
+       char buff[VID2STR_BUFFER];
 
        if (isis_find_vertex(&spftree->paths, &vertex->N, vertex->type))
                return;
@@ -1225,10 +1015,11 @@ static void add_to_paths(struct isis_spftree *spftree,
 
        if (VTYPE_IP(vertex->type)) {
                if (listcount(vertex->Adj_N) > 0)
-                       isis_route_create((struct prefix *)&vertex->N.prefix,
+                       isis_route_create(&vertex->N.ip.dest,
+                                         &vertex->N.ip.src,
                                          vertex->d_N, vertex->depth,
                                          vertex->Adj_N, spftree->area,
-                                         spftree->level);
+                                         spftree->route_table);
                else if (isis->debugs & DEBUG_SPF_EVENTS)
                        zlog_debug(
                                "ISIS-Spf: no adjacencies do not install route for "
@@ -1241,7 +1032,8 @@ static void add_to_paths(struct isis_spftree *spftree,
 }
 
 static void init_spt(struct isis_spftree *spftree, int mtid, int level,
-                    int family)
+                    int family, enum spf_tree_id tree_id,
+                    bool hopcount_metric)
 {
        isis_vertex_queue_clear(&spftree->tents);
        isis_vertex_queue_clear(&spftree->paths);
@@ -1249,53 +1041,108 @@ static void init_spt(struct isis_spftree *spftree, int mtid, int level,
        spftree->mtid = mtid;
        spftree->level = level;
        spftree->family = family;
-       return;
+       spftree->tree_id = tree_id;
+       spftree->hopcount_metric = hopcount_metric;
 }
 
-static int isis_run_spf(struct isis_area *area, int level, int family,
+static void isis_spf_loop(struct isis_spftree *spftree,
+                         uint8_t *root_sysid)
+{
+       struct isis_vertex *vertex;
+       struct isis_lsp *lsp;
+
+       while (isis_vertex_queue_count(&spftree->tents)) {
+               vertex = isis_vertex_queue_pop(&spftree->tents);
+
+#ifdef EXTREME_DEBUG
+               zlog_debug(
+                       "ISIS-Spf: get TENT node %s %s depth %d dist %d to PATHS",
+                       print_sys_hostname(vertex->N.id),
+                       vtype2string(vertex->type), vertex->depth, vertex->d_N);
+#endif /* EXTREME_DEBUG */
+
+               add_to_paths(spftree, vertex);
+               if (!VTYPE_IS(vertex->type))
+                       continue;
+
+               lsp = lsp_for_vertex(spftree, vertex);
+               if (!lsp) {
+                       zlog_warn("ISIS-Spf: No LSP found for %s",
+                                 isis_format_id(vertex->N.id,
+                                                sizeof(vertex->N.id)));
+                       continue;
+               }
+
+               isis_spf_process_lsp(spftree, lsp, vertex->d_N, vertex->depth,
+                                    root_sysid, vertex);
+       }
+}
+
+struct isis_spftree *isis_run_hopcount_spf(struct isis_area *area,
+                                          uint8_t *sysid,
+                                          struct isis_spftree *spftree)
+{
+       if (!spftree)
+               spftree = isis_spftree_new(area);
+
+       init_spt(spftree, ISIS_MT_IPV4_UNICAST, ISIS_LEVEL2,
+                AF_INET, SPFTREE_IPV4, true);
+       if (!memcmp(sysid, isis->sysid, ISIS_SYS_ID_LEN)) {
+               /* If we are running locally, initialize with information from adjacencies */
+               struct isis_vertex *root = isis_spf_add_root(spftree, sysid);
+               isis_spf_preload_tent(spftree, sysid, root);
+       } else {
+               isis_vertex_queue_insert(&spftree->tents, isis_vertex_new(
+                                        spftree, sysid,
+                                        VTYPE_NONPSEUDO_TE_IS));
+       }
+
+       isis_spf_loop(spftree, sysid);
+
+       return spftree;
+}
+
+static int isis_run_spf(struct isis_area *area, int level,
+                       enum spf_tree_id tree_id,
                        uint8_t *sysid, struct timeval *nowtv)
 {
        int retval = ISIS_OK;
-       struct isis_vertex *vertex;
        struct isis_vertex *root_vertex;
-       struct isis_spftree *spftree = NULL;
-       uint8_t lsp_id[ISIS_SYS_ID_LEN + 2];
-       struct isis_lsp *lsp;
-       struct route_table *table = NULL;
+       struct isis_spftree *spftree = area->spftree[tree_id][level - 1];
        struct timeval time_now;
        unsigned long long start_time, end_time;
-       uint16_t mtid;
+       uint16_t mtid = 0;
 
        /* Get time that can't roll backwards. */
        start_time = nowtv->tv_sec;
        start_time = (start_time * 1000000) + nowtv->tv_usec;
 
-       if (family == AF_INET)
-               spftree = area->spftree[level - 1];
-       else if (family == AF_INET6)
-               spftree = area->spftree6[level - 1];
+       int family = -1;
+       switch (tree_id) {
+       case SPFTREE_IPV4:
+               family = AF_INET;
+               mtid = ISIS_MT_IPV4_UNICAST;
+               break;
+       case SPFTREE_IPV6:
+               family = AF_INET6;
+               mtid = isis_area_ipv6_topology(area);
+               break;
+       case SPFTREE_DSTSRC:
+               family = AF_INET6;
+               mtid = ISIS_MT_IPV6_DSTSRC;
+               break;
+       case SPFTREE_COUNT:
+               assert(!"isis_run_spf should never be called with SPFTREE_COUNT as argument!");
+               return ISIS_WARNING;
+       }
+
        assert(spftree);
        assert(sysid);
 
-       /* Make all routes in current route table inactive. */
-       if (family == AF_INET)
-               table = area->route_table[level - 1];
-       else if (family == AF_INET6)
-               table = area->route_table6[level - 1];
-
-       isis_route_invalidate_table(area, table);
-
-       /* We only support ipv4-unicast and ipv6-unicast as topologies for now
-        */
-       if (family == AF_INET6)
-               mtid = isis_area_ipv6_topology(area);
-       else
-               mtid = ISIS_MT_IPV4_UNICAST;
-
        /*
         * C.2.5 Step 0
         */
-       init_spt(spftree, mtid, level, family);
+       init_spt(spftree, mtid, level, family, tree_id, false);
        /*              a) */
        root_vertex = isis_spf_add_root(spftree, sysid);
        /*              b) */
@@ -1315,34 +1162,8 @@ static int isis_run_spf(struct isis_area *area, int level, int family,
                          print_sys_hostname(sysid));
        }
 
-       while (isis_vertex_queue_count(&spftree->tents)) {
-               vertex = isis_vertex_queue_pop(&spftree->tents);
-
-#ifdef EXTREME_DEBUG
-               zlog_debug(
-                       "ISIS-Spf: get TENT node %s %s depth %d dist %d to PATHS",
-                       print_sys_hostname(vertex->N.id),
-                       vtype2string(vertex->type), vertex->depth, vertex->d_N);
-#endif /* EXTREME_DEBUG */
-
-               add_to_paths(spftree, vertex);
-               if (VTYPE_IS(vertex->type)) {
-                       memcpy(lsp_id, vertex->N.id, ISIS_SYS_ID_LEN + 1);
-                       LSP_FRAGMENT(lsp_id) = 0;
-                       lsp = lsp_search(lsp_id, area->lspdb[level - 1]);
-                       if (lsp && lsp->hdr.rem_lifetime != 0) {
-                               isis_spf_process_lsp(spftree, lsp, vertex->d_N,
-                                                    vertex->depth, sysid,
-                                                    vertex);
-                       } else {
-                               zlog_warn("ISIS-Spf: No LSP found for %s",
-                                         rawlspid_print(lsp_id));
-                       }
-               }
-       }
-
+       isis_spf_loop(spftree, sysid);
 out:
-       isis_route_validate(area);
        spftree->runcount++;
        spftree->last_run_timestamp = time(NULL);
        spftree->last_run_monotime = monotime(&time_now);
@@ -1353,6 +1174,23 @@ out:
        return retval;
 }
 
+void isis_spf_verify_routes(struct isis_area *area, struct isis_spftree **trees)
+{
+       if (area->is_type == IS_LEVEL_1) {
+               isis_route_verify_table(area, trees[0]->route_table);
+       } else if (area->is_type == IS_LEVEL_2) {
+               isis_route_verify_table(area, trees[1]->route_table);
+       } else {
+               isis_route_verify_merge(area, trees[0]->route_table,
+                                       trees[1]->route_table);
+       }
+}
+
+void isis_spf_invalidate_routes(struct isis_spftree *tree)
+{
+       isis_route_invalidate_table(tree->area, tree->route_table);
+}
+
 static int isis_run_spf_cb(struct thread *thread)
 {
        struct isis_spf_run *run = THREAD_ARG(thread);
@@ -1370,16 +1208,32 @@ static int isis_run_spf_cb(struct thread *thread)
                return ISIS_WARNING;
        }
 
+       isis_area_invalidate_routes(area, level);
+
        if (isis->debugs & DEBUG_SPF_EVENTS)
                zlog_debug("ISIS-Spf (%s) L%d SPF needed, periodic SPF",
                           area->area_tag, level);
 
        if (area->ip_circuits)
-               retval = isis_run_spf(area, level, AF_INET, isis->sysid,
+               retval = isis_run_spf(area, level, SPFTREE_IPV4, isis->sysid,
                                      &thread->real);
        if (area->ipv6_circuits)
-               retval = isis_run_spf(area, level, AF_INET6, isis->sysid,
+               retval = isis_run_spf(area, level, SPFTREE_IPV6, isis->sysid,
                                      &thread->real);
+       if (area->ipv6_circuits
+           && isis_area_ipv6_dstsrc_enabled(area))
+               retval = isis_run_spf(area, level, SPFTREE_DSTSRC, isis->sysid,
+                                     &thread->real);
+
+       isis_area_verify_routes(area);
+
+       /* walk all circuits and reset any spf specific flags */
+       struct listnode *node;
+       struct isis_circuit *circuit;
+       for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit))
+               UNSET_FLAG(circuit->flags, ISIS_CIRCUIT_FLAPPED_AFTER_SPF);
+
+       fabricd_run_spf(area);
 
        return retval;
 }
@@ -1394,19 +1248,22 @@ static struct isis_spf_run *isis_run_spf_arg(struct isis_area *area, int level)
        return run;
 }
 
-int isis_spf_schedule(struct isis_area *area, int level)
+int _isis_spf_schedule(struct isis_area *area, int level,
+                      const char *func, const char *file, int line)
 {
-       struct isis_spftree *spftree = area->spftree[level - 1];
+       struct isis_spftree *spftree = area->spftree[SPFTREE_IPV4][level - 1];
        time_t now = monotime(NULL);
        int diff = now - spftree->last_run_monotime;
 
        assert(diff >= 0);
        assert(area->is_type & level);
 
-       if (isis->debugs & DEBUG_SPF_EVENTS)
+       if (isis->debugs & DEBUG_SPF_EVENTS) {
                zlog_debug(
-                       "ISIS-Spf (%s) L%d SPF schedule called, lastrun %d sec ago",
-                       area->area_tag, level, diff);
+                       "ISIS-Spf (%s) L%d SPF schedule called, lastrun %d sec ago"
+                       " Caller: %s %s:%d",
+                       area->area_tag, level, diff, func, file, line);
+       }
 
        if (area->spf_delay_ietf[level - 1]) {
                /* Need to call schedule function also if spf delay is running
@@ -1451,7 +1308,7 @@ static void isis_print_paths(struct vty *vty, struct isis_vertex_queue *queue,
 {
        struct listnode *node;
        struct isis_vertex *vertex;
-       char buff[PREFIX2STR_BUFFER];
+       char buff[VID2STR_BUFFER];
 
        vty_out(vty,
                "Vertex               Type         Metric Next-Hop             Interface Parent\n");
@@ -1517,14 +1374,53 @@ static void isis_print_paths(struct vty *vty, struct isis_vertex_queue *queue,
        }
 }
 
+static void isis_print_spftree(struct vty *vty, int level,
+                              struct isis_area *area,
+                              enum spf_tree_id tree_id)
+{
+       const char *tree_id_text = NULL;
+
+       switch (tree_id) {
+       case SPFTREE_IPV4:
+               tree_id_text = "that speak IP";
+               break;
+       case SPFTREE_IPV6:
+               tree_id_text = "that speak IPv6";
+               break;
+       case SPFTREE_DSTSRC:
+               tree_id_text = "that support IPv6 dst-src routing";
+               break;
+       case SPFTREE_COUNT:
+               assert(!"isis_print_spftree shouldn't be called with SPFTREE_COUNT as type");
+               return;
+       }
+
+       if (!area->spftree[tree_id][level - 1]
+           || !isis_vertex_queue_count(
+                   &area->spftree[tree_id][level - 1]->paths))
+               return;
+
+       vty_out(vty, "IS-IS paths to level-%d routers %s\n",
+               level, tree_id_text);
+       isis_print_paths(vty, &area->spftree[tree_id][level - 1]->paths,
+                        isis->sysid);
+       vty_out(vty, "\n");
+}
+
 DEFUN (show_isis_topology,
        show_isis_topology_cmd,
-       "show isis topology [<level-1|level-2>]",
-       SHOW_STR
-       "IS-IS information\n"
+       "show " PROTO_NAME " topology"
+#ifndef FABRICD
+       " [<level-1|level-2>]"
+#endif
+       , SHOW_STR
+       PROTO_HELP
        "IS-IS paths to Intermediate Systems\n"
+#ifndef FABRICD
        "Paths to all level-1 routers in the area\n"
-       "Paths to all level-2 routers in the domain\n")
+       "Paths to all level-2 routers in the domain\n"
+#endif
+       )
 {
        int levels;
        struct listnode *node;
@@ -1548,26 +1444,25 @@ DEFUN (show_isis_topology,
                        if ((level & levels) == 0)
                                continue;
 
-                       if (area->ip_circuits > 0 && area->spftree[level - 1]
-                           && isis_vertex_queue_count(&area->spftree[level - 1]->paths) > 0) {
-                               vty_out(vty,
-                                       "IS-IS paths to level-%d routers that speak IP\n",
-                                       level);
-                               isis_print_paths(
-                                       vty, &area->spftree[level - 1]->paths,
-                                       isis->sysid);
-                               vty_out(vty, "\n");
+                       if (area->ip_circuits > 0) {
+                               isis_print_spftree(vty, level, area,
+                                                  SPFTREE_IPV4);
                        }
-                       if (area->ipv6_circuits > 0 && area->spftree6[level - 1]
-                           && isis_vertex_queue_count(&area->spftree6[level - 1]->paths) > 0) {
-                               vty_out(vty,
-                                       "IS-IS paths to level-%d routers that speak IPv6\n",
-                                       level);
-                               isis_print_paths(
-                                       vty, &area->spftree6[level - 1]->paths,
-                                       isis->sysid);
-                               vty_out(vty, "\n");
+                       if (area->ipv6_circuits > 0) {
+                               isis_print_spftree(vty, level, area,
+                                                  SPFTREE_IPV6);
                        }
+                       if (isis_area_ipv6_dstsrc_enabled(area)) {
+                               isis_print_spftree(vty, level, area,
+                                                  SPFTREE_DSTSRC);
+                       }
+               }
+
+               if (fabricd_spftree(area)) {
+                       vty_out(vty,
+                               "IS-IS paths to level-2 routers with hop-by-hop metric\n");
+                       isis_print_paths(vty, &fabricd_spftree(area)->paths, isis->sysid);
+                       vty_out(vty, "\n");
                }
 
                vty_out(vty, "\n");