rv->area = area;
rv->initial_sync_state = FABRICD_SYNC_PENDING;
- rv->spftree = isis_spftree_new(area, &area->lspdb[IS_LEVEL_2 - 1],
- area->isis->sysid, ISIS_LEVEL2,
- SPFTREE_IPV4, F_SPFTREE_HOPCOUNT_METRIC);
+ rv->spftree =
+ isis_spftree_new(area, &area->lspdb[IS_LEVEL_2 - 1],
+ area->isis->sysid, ISIS_LEVEL2, SPFTREE_IPV4,
+ SPF_TYPE_FORWARD, F_SPFTREE_HOPCOUNT_METRIC);
rv->neighbors = skiplist_new(0, neighbor_entry_list_cmp,
neighbor_entry_del_void);
rv->neighbors_neighbors = hash_create(neighbor_entry_hash_key,
struct isis_spftree *isis_spftree_new(struct isis_area *area,
struct lspdb_head *lspdb,
const uint8_t *sysid, int level,
- enum spf_tree_id tree_id, uint8_t flags)
+ enum spf_tree_id tree_id,
+ enum spf_type type, uint8_t flags)
{
struct isis_spftree *tree;
tree->last_run_monotime = 0;
tree->last_run_duration = 0;
tree->runcount = 0;
+ tree->type = type;
memcpy(tree->sysid, sysid, ISIS_SYS_ID_LEN);
tree->level = level;
tree->tree_id = tree_id;
if (area->spftree[tree][level - 1])
continue;
- area->spftree[tree][level - 1] = isis_spftree_new(
- area, &area->lspdb[level - 1],
- area->isis->sysid, level, tree, 0);
+ area->spftree[tree][level - 1] =
+ isis_spftree_new(area, &area->lspdb[level - 1],
+ area->isis->sysid, level, tree,
+ SPF_TYPE_FORWARD, 0);
}
}
}
}
}
+struct spf_adj_find_reverse_metric_args {
+ const uint8_t *id_self;
+ uint32_t reverse_metric;
+};
+
+static int spf_adj_find_reverse_metric_cb(const uint8_t *id, uint32_t metric,
+ bool oldmetric,
+ struct isis_ext_subtlvs *subtlvs,
+ void *arg)
+{
+ struct spf_adj_find_reverse_metric_args *args = arg;
+
+ if (memcmp(id, args->id_self, ISIS_SYS_ID_LEN))
+ return LSP_ITER_CONTINUE;
+
+ args->reverse_metric = metric;
+
+ return LSP_ITER_STOP;
+}
+
+/*
+ * Change all SPF adjacencies to use the link cost in the direction from the
+ * next hop back towards root in place of the link cost in the direction away
+ * from root towards the next hop.
+ */
+static void spf_adj_get_reverse_metrics(struct isis_spftree *spftree)
+{
+ struct isis_spf_adj *sadj;
+ struct listnode *node, *nnode;
+
+ for (ALL_LIST_ELEMENTS(spftree->sadj_list, node, nnode, sadj)) {
+ uint8_t lspid[ISIS_SYS_ID_LEN + 2];
+ struct isis_lsp *lsp_adj;
+ const uint8_t *id_self;
+ struct spf_adj_find_reverse_metric_args args;
+
+ /* Skip pseudonodes. */
+ if (LSP_PSEUDO_ID(sadj->id))
+ continue;
+
+ /* Find LSP of the corresponding adjacency. */
+ memcpy(lspid, sadj->id, ISIS_SYS_ID_LEN);
+ LSP_PSEUDO_ID(lspid) = 0;
+ LSP_FRAGMENT(lspid) = 0;
+ lsp_adj = lsp_search(spftree->lspdb, lspid);
+ if (lsp_adj == NULL || lsp_adj->hdr.rem_lifetime == 0) {
+ /* Delete one-way adjacency. */
+ listnode_delete(spftree->sadj_list, sadj);
+ continue;
+ }
+
+ /* Find root node in the LSP of the adjacent router. */
+ if (CHECK_FLAG(sadj->flags, F_ISIS_SPF_ADJ_BROADCAST))
+ id_self = sadj->lan.desig_is_id;
+ else
+ id_self = spftree->sysid;
+ args.id_self = id_self;
+ args.reverse_metric = UINT32_MAX;
+ isis_lsp_iterate_is_reach(lsp_adj, spftree->mtid,
+ spf_adj_find_reverse_metric_cb,
+ &args);
+ if (args.reverse_metric == UINT32_MAX) {
+ /* Delete one-way adjacency. */
+ listnode_delete(spftree->sadj_list, sadj);
+ continue;
+ }
+ sadj->metric = args.reverse_metric;
+ }
+}
+
static void spf_adj_list_parse_tlv(struct isis_spftree *spftree,
struct list *adj_list, const uint8_t *id,
const uint8_t *desig_is_id,
if (!CHECK_FLAG(spftree->flags, F_SPFTREE_NO_ADJACENCIES))
list_delete(&adj_list);
+
+ if (spftree->type == SPF_TYPE_REVERSE)
+ spf_adj_get_reverse_metrics(spftree);
}
/*
if (!spftree)
spftree = isis_spftree_new(area, &area->lspdb[IS_LEVEL_2 - 1],
sysid, ISIS_LEVEL2, SPFTREE_IPV4,
+ SPF_TYPE_FORWARD,
F_SPFTREE_HOPCOUNT_METRIC);
init_spt(spftree, ISIS_MT_IPV4_UNICAST);
struct isis_spftree;
+enum spf_type {
+ SPF_TYPE_FORWARD = 1,
+ SPF_TYPE_REVERSE,
+};
+
struct isis_spf_adj {
uint8_t id[ISIS_SYS_ID_LEN + 1];
struct isis_adjacency *adj;
struct isis_spftree *isis_spftree_new(struct isis_area *area,
struct lspdb_head *lspdb,
const uint8_t *sysid, int level,
- enum spf_tree_id tree_id, uint8_t flags);
+ enum spf_tree_id tree_id,
+ enum spf_type type, uint8_t flags);
void isis_spf_invalidate_routes(struct isis_spftree *tree);
void isis_spf_verify_routes(struct isis_area *area,
struct isis_spftree **trees);
time_t last_run_monotime; /* last run as monotime for scheduling */
time_t last_run_duration; /* last run duration in msec */
+ enum spf_type type;
uint8_t sysid[ISIS_SYS_ID_LEN];
uint16_t mtid;
int family;
enum test_type {
TEST_SPF = 1,
+ TEST_REVERSE_SPF,
};
#define F_DISPLAY_LSPDB 0x01
static void test_run_spf(struct vty *vty, const struct isis_topology *topology,
const struct isis_test_node *root,
struct isis_area *area, struct lspdb_head *lspdb,
- int level, int tree)
+ int level, int tree, bool reverse)
{
struct isis_spftree *spftree;
+ enum spf_type spf_type;
/* Run SPF. */
+ spf_type = reverse ? SPF_TYPE_REVERSE : SPF_TYPE_FORWARD;
spftree = isis_spftree_new(area, lspdb, root->sysid, level, tree,
- F_SPFTREE_NO_ADJACENCIES);
+ spf_type, F_SPFTREE_NO_ADJACENCIES);
isis_run_spf(spftree);
/* Print the SPT and the corresponding routing table. */
case TEST_SPF:
test_run_spf(vty, topology, root, area,
&area->lspdb[level - 1], level,
- tree);
+ tree, false);
+ break;
+ case TEST_REVERSE_SPF:
+ test_run_spf(vty, topology, root, area,
+ &area->lspdb[level - 1], level,
+ tree, true);
break;
}
}
}
DEFUN(test_isis, test_isis_cmd,
- "test isis topology (1-13) root HOSTNAME spf\
+ "test isis topology (1-13) root HOSTNAME\
+ <\
+ spf\
+ |reverse-spf\
+ >\
[display-lspdb] [<ipv4-only|ipv6-only>] [<level-1-only|level-2-only>]",
"Test command\n"
"IS-IS routing protocol\n"
"SPF root\n"
"SPF root hostname\n"
"Normal Shortest Path First\n"
+ "Reverse Shortest Path First\n"
"Display the LSPDB\n"
"Do IPv4 processing only\n"
"Do IPv6 processing only\n"
uint16_t topology_number;
const struct isis_topology *topology;
const struct isis_test_node *root;
+ enum test_type test_type;
uint8_t flags = 0;
int idx = 0;
return CMD_WARNING;
}
+ /* Parse test information. */
+ if (argv_find(argv, argc, "spf", &idx))
+ test_type = TEST_SPF;
+ else if (argv_find(argv, argc, "reverse-spf", &idx))
+ test_type = TEST_REVERSE_SPF;
+ else
+ return CMD_WARNING;
+
/* Parse control flags. */
if (argv_find(argv, argc, "display-lspdb", &idx))
SET_FLAG(flags, F_DISPLAY_LSPDB);
else if (argv_find(argv, argc, "level-2-only", &idx))
SET_FLAG(flags, F_LEVEL2_ONLY);
- return test_run(vty, topology, root, TEST_SPF, flags);
+ return test_run(vty, topology, root, test_type, flags);
}
static void vty_do_exit(int isexit)
test isis topology 11 root rt1 spf
test isis topology 12 root rt1 spf ipv4-only
test isis topology 13 root rt1 spf ipv4-only
+
+test isis topology 4 root rt1 reverse-spf ipv4-only
+test isis topology 11 root rt1 reverse-spf
10.0.255.6/32 30 - rt3 - \r
10.0.255.7/32 40 - rt3 - \r
\r
+test# \r
+test# test isis topology 4 root rt1 reverse-spf ipv4-only\r
+IS-IS paths to level-1 routers that speak IP\r
+Vertex Type Metric Next-Hop Interface Parent\r
+rt1 \r
+10.0.255.1/32 IP internal 0 rt1(4)\r
+rt2 TE-IS 10 rt2 - rt1(4)\r
+rt3 TE-IS 10 rt3 - rt1(4)\r
+rt4 TE-IS 20 rt2 - rt2(4)\r
+rt5 TE-IS 20 rt3 - rt3(4)\r
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)\r
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)\r
+rt6 TE-IS 30 rt2 - rt4(4)\r
+rt7 TE-IS 30 rt3 - rt5(4)\r
+10.0.255.4/32 IP TE 30 rt2 - rt4(4)\r
+10.0.255.5/32 IP TE 30 rt3 - rt5(4)\r
+rt8 TE-IS 40 rt2 - rt6(4)\r
+10.0.255.6/32 IP TE 40 rt2 - rt6(4)\r
+10.0.255.7/32 IP TE 40 rt3 - rt7(4)\r
+10.0.255.8/32 IP TE 50 rt2 - rt8(4)\r
+\r
+IS-IS L1 IPv4 routing table:\r
+\r
+ Prefix Metric Interface Nexthop Label(s) \r
+ -----------------------------------------------------\r
+ 10.0.255.2/32 20 - rt2 - \r
+ 10.0.255.3/32 20 - rt3 - \r
+ 10.0.255.4/32 30 - rt2 - \r
+ 10.0.255.5/32 30 - rt3 - \r
+ 10.0.255.6/32 40 - rt2 - \r
+ 10.0.255.7/32 40 - rt3 - \r
+ 10.0.255.8/32 50 - rt2 - \r
+\r
+test# test isis topology 11 root rt1 reverse-spf\r
+IS-IS paths to level-1 routers that speak IP\r
+Vertex Type Metric Next-Hop Interface Parent\r
+rt1 \r
+10.0.255.1/32 IP internal 0 rt1(4)\r
+rt2 TE-IS 10 rt1(4)\r
+rt3 TE-IS 10 rt3 - rt1(4)\r
+rt2 pseudo_TE-IS 20 rt3 - rt3(4)\r
+rt4 TE-IS 20 rt2(4)\r
+rt5 TE-IS 20 rt3 - rt3(4)\r
+10.0.255.2/32 IP TE 20 rt2(4)\r
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)\r
+rt6 TE-IS 30 rt3 - rt4(4)\r
+ rt5(4)\r
+10.0.255.4/32 IP TE 30 rt4(4)\r
+10.0.255.5/32 IP TE 30 rt3 - rt5(4)\r
+10.0.255.6/32 IP TE 40 rt3 - rt6(4)\r
+\r
+IS-IS L1 IPv4 routing table:\r
+\r
+ Prefix Metric Interface Nexthop Label(s) \r
+ -----------------------------------------------------\r
+ 10.0.255.3/32 20 - rt3 - \r
+ 10.0.255.5/32 30 - rt3 - \r
+ 10.0.255.6/32 40 - rt3 - \r
+\r
+IS-IS paths to level-1 routers that speak IPv6\r
+Vertex Type Metric Next-Hop Interface Parent\r
+rt1 \r
+2001:db8::1/128 IP6 internal 0 rt1(4)\r
+rt2 TE-IS 10 rt1(4)\r
+rt3 TE-IS 10 rt3 - rt1(4)\r
+rt2 pseudo_TE-IS 20 rt3 - rt3(4)\r
+rt4 TE-IS 20 rt2(4)\r
+rt5 TE-IS 20 rt3 - rt3(4)\r
+2001:db8::2/128 IP6 internal 20 rt2(4)\r
+2001:db8::3/128 IP6 internal 20 rt3 - rt3(4)\r
+rt6 TE-IS 30 rt3 - rt4(4)\r
+ rt5(4)\r
+2001:db8::4/128 IP6 internal 30 rt4(4)\r
+2001:db8::5/128 IP6 internal 30 rt3 - rt5(4)\r
+2001:db8::6/128 IP6 internal 40 rt3 - rt6(4)\r
+\r
+IS-IS L1 IPv6 routing table:\r
+\r
+ Prefix Metric Interface Nexthop Label(s) \r
+ -------------------------------------------------------\r
+ 2001:db8::3/128 20 - rt3 - \r
+ 2001:db8::5/128 30 - rt3 - \r
+ 2001:db8::6/128 40 - rt3 - \r
+\r
test#
end.