2 * IS-IS Rout(e)ing protocol - OpenFabric extensions
4 * Copyright (C) 2018 Christian Franke
6 * This file is part of FreeRangeRouting (FRR)
8 * FRR is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2, or (at your option) any
13 * FRR is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with this program; see the file COPYING; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 #include "isisd/fabricd.h"
24 #include "isisd/isisd.h"
25 #include "isisd/isis_memory.h"
26 #include "isisd/isis_circuit.h"
27 #include "isisd/isis_misc.h"
28 #include "isisd/isis_adjacency.h"
29 #include "isisd/isis_spf.h"
30 #include "isisd/isis_tlvs.h"
31 #include "isisd/isis_lsp.h"
32 #include "isisd/isis_spf_private.h"
33 #include "isisd/isis_tx_queue.h"
35 DEFINE_MTYPE_STATIC(ISISD
, FABRICD_STATE
, "ISIS OpenFabric")
36 DEFINE_MTYPE_STATIC(ISISD
, FABRICD_NEIGHBOR
, "ISIS OpenFabric Neighbor Entry")
38 /* Tracks initial synchronization as per section 2.4
40 * We declare the sync complete once we have seen at least one
41 * CSNP and there are no more LSPs with SSN or SRM set.
43 enum fabricd_sync_state
{
50 struct isis_area
*area
;
52 enum fabricd_sync_state initial_sync_state
;
53 time_t initial_sync_start
;
54 struct isis_circuit
*initial_sync_circuit
;
55 struct thread
*initial_sync_timeout
;
57 struct isis_spftree
*spftree
;
58 struct skiplist
*neighbors
;
59 struct hash
*neighbors_neighbors
;
64 struct thread
*tier_calculation_timer
;
65 struct thread
*tier_set_timer
;
68 /* Code related to maintaining the neighbor lists */
70 struct neighbor_entry
{
71 struct isis_vertex
*vertex
;
75 static struct neighbor_entry
*neighbor_entry_new(struct isis_vertex
*vertex
)
77 struct neighbor_entry
*rv
= XMALLOC(MTYPE_FABRICD_NEIGHBOR
, sizeof(*rv
));
83 static void neighbor_entry_del(struct neighbor_entry
*neighbor
)
85 XFREE(MTYPE_FABRICD_NEIGHBOR
, neighbor
);
88 static void neighbor_entry_del_void(void *arg
)
90 neighbor_entry_del((struct neighbor_entry
*)arg
);
93 static void neighbor_lists_clear(struct fabricd
*f
)
95 while (!skiplist_empty(f
->neighbors
))
96 skiplist_delete_first(f
->neighbors
);
98 hash_clean(f
->neighbors_neighbors
, neighbor_entry_del_void
);
101 static unsigned neighbor_entry_hash_key(void *np
)
103 struct neighbor_entry
*n
= np
;
105 return jhash(n
->vertex
->N
.id
, ISIS_SYS_ID_LEN
, 0x55aa5a5a);
108 static int neighbor_entry_hash_cmp(const void *a
, const void *b
)
110 const struct neighbor_entry
*na
= a
, *nb
= b
;
112 return memcmp(na
->vertex
->N
.id
, nb
->vertex
->N
.id
, ISIS_SYS_ID_LEN
) == 0;
115 static int neighbor_entry_list_cmp(void *a
, void *b
)
117 struct neighbor_entry
*na
= a
, *nb
= b
;
119 return -memcmp(na
->vertex
->N
.id
, nb
->vertex
->N
.id
, ISIS_SYS_ID_LEN
);
122 static struct neighbor_entry
*neighbor_entry_lookup_list(struct skiplist
*list
,
125 struct isis_vertex querier
;
126 isis_vertex_id_init(&querier
, id
, VTYPE_NONPSEUDO_TE_IS
);
128 struct neighbor_entry n
= {
132 struct neighbor_entry
*rv
;
134 if (skiplist_search(list
, &n
, (void**)&rv
))
143 static struct neighbor_entry
*neighbor_entry_lookup_hash(struct hash
*hash
,
146 struct isis_vertex querier
;
147 isis_vertex_id_init(&querier
, id
, VTYPE_NONPSEUDO_TE_IS
);
149 struct neighbor_entry n
= {
153 struct neighbor_entry
*rv
= hash_lookup(hash
, &n
);
155 if (!rv
|| !rv
->present
)
161 static void neighbor_lists_update(struct fabricd
*f
)
163 neighbor_lists_clear(f
);
165 struct listnode
*node
;
166 struct isis_vertex
*v
;
168 for (ALL_QUEUE_ELEMENTS_RO(&f
->spftree
->paths
, node
, v
)) {
169 if (!v
->d_N
|| !VTYPE_IS(v
->type
))
175 struct neighbor_entry
*n
= neighbor_entry_new(v
);
177 skiplist_insert(f
->neighbors
, n
, n
);
179 struct neighbor_entry
*inserted
;
180 inserted
= hash_get(f
->neighbors_neighbors
, n
, hash_alloc_intern
);
181 assert(inserted
== n
);
186 struct fabricd
*fabricd_new(struct isis_area
*area
)
188 struct fabricd
*rv
= XCALLOC(MTYPE_FABRICD_STATE
, sizeof(*rv
));
191 rv
->initial_sync_state
= FABRICD_SYNC_PENDING
;
193 rv
->spftree
= isis_spftree_new(area
);
194 rv
->neighbors
= skiplist_new(0, neighbor_entry_list_cmp
,
195 neighbor_entry_del_void
);
196 rv
->neighbors_neighbors
= hash_create(neighbor_entry_hash_key
,
197 neighbor_entry_hash_cmp
,
198 "Fabricd Neighbors");
200 rv
->tier
= rv
->tier_config
= ISIS_TIER_UNDEFINED
;
204 void fabricd_finish(struct fabricd
*f
)
206 if (f
->initial_sync_timeout
)
207 thread_cancel(f
->initial_sync_timeout
);
209 if (f
->tier_calculation_timer
)
210 thread_cancel(f
->tier_calculation_timer
);
212 if (f
->tier_set_timer
)
213 thread_cancel(f
->tier_set_timer
);
215 isis_spftree_del(f
->spftree
);
216 neighbor_lists_clear(f
);
217 skiplist_free(f
->neighbors
);
218 hash_free(f
->neighbors_neighbors
);
221 static int fabricd_initial_sync_timeout(struct thread
*thread
)
223 struct fabricd
*f
= THREAD_ARG(thread
);
225 zlog_info("OpenFabric: Initial synchronization on %s timed out!",
226 f
->initial_sync_circuit
->interface
->name
);
227 f
->initial_sync_state
= FABRICD_SYNC_PENDING
;
228 f
->initial_sync_circuit
= NULL
;
229 f
->initial_sync_timeout
= NULL
;
233 void fabricd_initial_sync_hello(struct isis_circuit
*circuit
)
235 struct fabricd
*f
= circuit
->area
->fabricd
;
240 if (f
->initial_sync_state
> FABRICD_SYNC_PENDING
)
243 f
->initial_sync_state
= FABRICD_SYNC_STARTED
;
245 long timeout
= 2 * circuit
->hello_interval
[1] * circuit
->hello_multiplier
[1];
247 f
->initial_sync_circuit
= circuit
;
248 if (f
->initial_sync_timeout
)
251 thread_add_timer(master
, fabricd_initial_sync_timeout
, f
,
252 timeout
, &f
->initial_sync_timeout
);
253 f
->initial_sync_start
= monotime(NULL
);
255 zlog_info("OpenFabric: Started initial synchronization with %s on %s",
256 sysid_print(circuit
->u
.p2p
.neighbor
->sysid
),
257 circuit
->interface
->name
);
260 bool fabricd_initial_sync_is_in_progress(struct isis_area
*area
)
262 struct fabricd
*f
= area
->fabricd
;
267 if (f
->initial_sync_state
> FABRICD_SYNC_PENDING
268 && f
->initial_sync_state
< FABRICD_SYNC_COMPLETE
)
274 struct isis_circuit
*fabricd_initial_sync_circuit(struct isis_area
*area
)
276 struct fabricd
*f
= area
->fabricd
;
280 return f
->initial_sync_circuit
;
283 void fabricd_initial_sync_finish(struct isis_area
*area
)
285 struct fabricd
*f
= area
->fabricd
;
290 if (monotime(NULL
) - f
->initial_sync_start
< 5)
293 zlog_info("OpenFabric: Initial synchronization on %s complete.",
294 f
->initial_sync_circuit
->interface
->name
);
295 f
->initial_sync_state
= FABRICD_SYNC_COMPLETE
;
296 f
->initial_sync_circuit
= NULL
;
297 thread_cancel(f
->initial_sync_timeout
);
298 f
->initial_sync_timeout
= NULL
;
301 static void fabricd_bump_tier_calculation_timer(struct fabricd
*f
);
302 static void fabricd_set_tier(struct fabricd
*f
, uint8_t tier
);
304 static uint8_t fabricd_calculate_fabric_tier(struct isis_area
*area
)
306 struct isis_spftree
*local_tree
= fabricd_spftree(area
);
307 struct listnode
*node
;
309 struct isis_vertex
*furthest_t0
= NULL
,
310 *second_furthest_t0
= NULL
;
312 struct isis_vertex
*v
;
314 for (ALL_QUEUE_ELEMENTS_RO(&local_tree
->paths
, node
, v
)) {
315 struct isis_lsp
*lsp
= lsp_for_vertex(local_tree
, v
);
317 if (!lsp
|| !lsp
->tlvs
318 || !lsp
->tlvs
->spine_leaf
319 || !lsp
->tlvs
->spine_leaf
->has_tier
320 || lsp
->tlvs
->spine_leaf
->tier
!= 0)
323 second_furthest_t0
= furthest_t0
;
327 if (!second_furthest_t0
) {
328 zlog_info("OpenFabric: Could not find two T0 routers");
329 return ISIS_TIER_UNDEFINED
;
332 zlog_info("OpenFabric: Found %s as furthest t0 from local system, dist == %"
333 PRIu32
, rawlspid_print(furthest_t0
->N
.id
), furthest_t0
->d_N
);
335 struct isis_spftree
*remote_tree
=
336 isis_run_hopcount_spf(area
, furthest_t0
->N
.id
, NULL
);
338 struct isis_vertex
*furthest_from_remote
=
339 isis_vertex_queue_last(&remote_tree
->paths
);
341 if (!furthest_from_remote
) {
342 zlog_info("OpenFabric: Found no furthest node in remote spf");
343 isis_spftree_del(remote_tree
);
344 return ISIS_TIER_UNDEFINED
;
346 zlog_info("OpenFabric: Found %s as furthest from remote dist == %"
347 PRIu32
, rawlspid_print(furthest_from_remote
->N
.id
),
348 furthest_from_remote
->d_N
);
351 int64_t tier
= furthest_from_remote
->d_N
- furthest_t0
->d_N
;
352 isis_spftree_del(remote_tree
);
354 if (tier
< 0 || tier
>= ISIS_TIER_UNDEFINED
) {
355 zlog_info("OpenFabric: Calculated tier %" PRId64
" seems implausible",
357 return ISIS_TIER_UNDEFINED
;
360 zlog_info("OpenFabric: Calculated %" PRId64
" as tier", tier
);
364 static int fabricd_tier_set_timer(struct thread
*thread
)
366 struct fabricd
*f
= THREAD_ARG(thread
);
367 f
->tier_set_timer
= NULL
;
369 fabricd_set_tier(f
, f
->tier_pending
);
373 static int fabricd_tier_calculation_cb(struct thread
*thread
)
375 struct fabricd
*f
= THREAD_ARG(thread
);
376 uint8_t tier
= ISIS_TIER_UNDEFINED
;
377 f
->tier_calculation_timer
= NULL
;
379 tier
= fabricd_calculate_fabric_tier(f
->area
);
380 if (tier
== ISIS_TIER_UNDEFINED
)
383 zlog_info("OpenFabric: Got tier %" PRIu8
" from algorithm. Arming timer.",
385 f
->tier_pending
= tier
;
386 thread_add_timer(master
, fabricd_tier_set_timer
, f
,
387 f
->area
->lsp_gen_interval
[ISIS_LEVEL2
- 1],
393 static void fabricd_bump_tier_calculation_timer(struct fabricd
*f
)
395 /* Cancel timer if we already know our tier */
396 if (f
->tier
!= ISIS_TIER_UNDEFINED
397 || f
->tier_set_timer
) {
398 if (f
->tier_calculation_timer
) {
399 thread_cancel(f
->tier_calculation_timer
);
400 f
->tier_calculation_timer
= NULL
;
405 /* If we need to calculate the tier, wait some
406 * time for the topology to settle before running
408 if (f
->tier_calculation_timer
) {
409 thread_cancel(f
->tier_calculation_timer
);
410 f
->tier_calculation_timer
= NULL
;
413 thread_add_timer(master
, fabricd_tier_calculation_cb
, f
,
414 2 * f
->area
->lsp_gen_interval
[ISIS_LEVEL2
- 1],
415 &f
->tier_calculation_timer
);
418 static void fabricd_set_tier(struct fabricd
*f
, uint8_t tier
)
423 zlog_info("OpenFabric: Set own tier to %" PRIu8
, tier
);
426 fabricd_bump_tier_calculation_timer(f
);
427 lsp_regenerate_schedule(f
->area
, ISIS_LEVEL2
, 0);
430 void fabricd_run_spf(struct isis_area
*area
)
432 struct fabricd
*f
= area
->fabricd
;
437 isis_run_hopcount_spf(area
, isis
->sysid
, f
->spftree
);
438 neighbor_lists_update(f
);
439 fabricd_bump_tier_calculation_timer(f
);
442 struct isis_spftree
*fabricd_spftree(struct isis_area
*area
)
444 struct fabricd
*f
= area
->fabricd
;
452 void fabricd_configure_tier(struct isis_area
*area
, uint8_t tier
)
454 struct fabricd
*f
= area
->fabricd
;
456 if (!f
|| f
->tier_config
== tier
)
459 f
->tier_config
= tier
;
460 fabricd_set_tier(f
, tier
);
463 uint8_t fabricd_tier(struct isis_area
*area
)
465 struct fabricd
*f
= area
->fabricd
;
468 return ISIS_TIER_UNDEFINED
;
473 int fabricd_write_settings(struct isis_area
*area
, struct vty
*vty
)
475 struct fabricd
*f
= area
->fabricd
;
481 if (f
->tier_config
!= ISIS_TIER_UNDEFINED
) {
482 vty_out(vty
, " fabric-tier %" PRIu8
"\n", f
->tier_config
);
489 static void move_to_dnr(struct isis_lsp
*lsp
, struct neighbor_entry
*n
)
491 struct isis_adjacency
*adj
= listnode_head(n
->vertex
->Adj_N
);
495 isis_tx_queue_add(adj
->circuit
->tx_queue
, lsp
,
496 TX_LSP_CIRCUIT_SCOPED
);
500 static void move_to_rf(struct isis_lsp
*lsp
, struct neighbor_entry
*n
)
502 struct isis_adjacency
*adj
= listnode_head(n
->vertex
->Adj_N
);
506 isis_tx_queue_add(adj
->circuit
->tx_queue
, lsp
,
511 static void mark_neighbor_as_present(struct hash_backet
*backet
, void *arg
)
513 struct neighbor_entry
*n
= backet
->data
;
518 static void handle_firsthops(struct hash_backet
*backet
, void *arg
)
520 struct isis_lsp
*lsp
= arg
;
521 struct fabricd
*f
= lsp
->area
->fabricd
;
522 struct isis_vertex
*vertex
= backet
->data
;
524 struct neighbor_entry
*n
;
526 n
= neighbor_entry_lookup_list(f
->neighbors
, vertex
->N
.id
);
530 n
= neighbor_entry_lookup_hash(f
->neighbors_neighbors
, vertex
->N
.id
);
535 void fabricd_lsp_flood(struct isis_lsp
*lsp
)
537 struct fabricd
*f
= lsp
->area
->fabricd
;
541 struct neighbor_entry
*n
;
543 /* Mark all elements in NL as present and move T0s into DNR */
544 while (!skiplist_next(f
->neighbors
, NULL
, (void **)&n
, &cursor
)) {
547 struct isis_lsp
*lsp
= lsp_for_vertex(f
->spftree
, n
->vertex
);
548 if (!lsp
|| !lsp
->tlvs
|| !lsp
->tlvs
->spine_leaf
)
551 if (!lsp
->tlvs
->spine_leaf
->has_tier
552 || lsp
->tlvs
->spine_leaf
->tier
!= 0)
558 /* Mark all elements in NN as present */
559 hash_iterate(f
->neighbors_neighbors
, mark_neighbor_as_present
, NULL
);
561 struct isis_vertex
*originator
= isis_find_vertex(&f
->spftree
->paths
,
563 VTYPE_NONPSEUDO_TE_IS
);
565 /* Remove all IS from NL and NN in the shortest path
566 * to the IS that originated the LSP */
568 hash_iterate(originator
->firsthops
, handle_firsthops
, lsp
);
570 /* Iterate over all remaining IS in NL */
572 while (!skiplist_next(f
->neighbors
, NULL
, (void **)&n
, &cursor
)) {
576 struct isis_lsp
*nlsp
= lsp_for_vertex(f
->spftree
, n
->vertex
);
577 if (!nlsp
|| !nlsp
->tlvs
) {
582 /* For all neighbors of the NL IS check whether they are present
583 * in NN. If yes, remove from NN and set need_reflood. */
584 bool need_reflood
= false;
585 struct isis_extended_reach
*er
;
586 for (er
= (struct isis_extended_reach
*)nlsp
->tlvs
->extended_reach
.head
;
588 struct neighbor_entry
*nn
;
590 nn
= neighbor_entry_lookup_hash(f
->neighbors_neighbors
,