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"
34 #include "isisd/isis_csm.h"
36 DEFINE_MTYPE_STATIC(ISISD
, FABRICD_STATE
, "ISIS OpenFabric")
37 DEFINE_MTYPE_STATIC(ISISD
, FABRICD_NEIGHBOR
, "ISIS OpenFabric Neighbor Entry")
38 DEFINE_MTYPE_STATIC(ISISD
, FABRICD_FLOODING_INFO
, "ISIS OpenFabric Flooding Log")
40 /* Tracks initial synchronization as per section 2.4
42 * We declare the sync complete once we have seen at least one
43 * CSNP and there are no more LSPs with SSN or SRM set.
45 enum fabricd_sync_state
{
52 struct isis_area
*area
;
54 enum fabricd_sync_state initial_sync_state
;
55 time_t initial_sync_start
;
56 struct isis_circuit
*initial_sync_circuit
;
57 struct thread
*initial_sync_timeout
;
59 struct isis_spftree
*spftree
;
60 struct skiplist
*neighbors
;
61 struct hash
*neighbors_neighbors
;
66 struct thread
*tier_calculation_timer
;
67 struct thread
*tier_set_timer
;
70 bool always_send_csnp
;
73 /* Code related to maintaining the neighbor lists */
75 struct neighbor_entry
{
76 uint8_t id
[ISIS_SYS_ID_LEN
];
77 struct isis_adjacency
*adj
;
81 static struct neighbor_entry
*neighbor_entry_new(const uint8_t *id
,
82 struct isis_adjacency
*adj
)
84 struct neighbor_entry
*rv
= XMALLOC(MTYPE_FABRICD_NEIGHBOR
,
87 memcpy(rv
->id
, id
, sizeof(rv
->id
));
93 static void neighbor_entry_del(struct neighbor_entry
*neighbor
)
95 XFREE(MTYPE_FABRICD_NEIGHBOR
, neighbor
);
98 static void neighbor_entry_del_void(void *arg
)
100 neighbor_entry_del((struct neighbor_entry
*)arg
);
103 static void neighbor_lists_clear(struct fabricd
*f
)
105 while (!skiplist_empty(f
->neighbors
))
106 skiplist_delete_first(f
->neighbors
);
108 hash_clean(f
->neighbors_neighbors
, neighbor_entry_del_void
);
111 static unsigned neighbor_entry_hash_key(const void *np
)
113 const struct neighbor_entry
*n
= np
;
115 return jhash(n
->id
, sizeof(n
->id
), 0x55aa5a5a);
118 static bool neighbor_entry_hash_cmp(const void *a
, const void *b
)
120 const struct neighbor_entry
*na
= a
, *nb
= b
;
122 return memcmp(na
->id
, nb
->id
, sizeof(na
->id
)) == 0;
125 static int neighbor_entry_list_cmp(void *a
, void *b
)
127 struct neighbor_entry
*na
= a
, *nb
= b
;
129 return -memcmp(na
->id
, nb
->id
, sizeof(na
->id
));
132 static struct neighbor_entry
*neighbor_entry_lookup_list(struct skiplist
*list
,
135 struct neighbor_entry n
= { {0} };
137 memcpy(n
.id
, id
, sizeof(n
.id
));
139 struct neighbor_entry
*rv
;
141 if (skiplist_search(list
, &n
, (void**)&rv
))
150 static struct neighbor_entry
*neighbor_entry_lookup_hash(struct hash
*hash
,
153 struct neighbor_entry n
= {{0}};
155 memcpy(n
.id
, id
, sizeof(n
.id
));
157 struct neighbor_entry
*rv
= hash_lookup(hash
, &n
);
159 if (!rv
|| !rv
->present
)
165 static int fabricd_handle_adj_state_change(struct isis_adjacency
*arg
)
167 struct fabricd
*f
= arg
->circuit
->area
->fabricd
;
172 while (!skiplist_empty(f
->neighbors
))
173 skiplist_delete_first(f
->neighbors
);
175 struct listnode
*node
;
176 struct isis_circuit
*circuit
;
178 for (ALL_LIST_ELEMENTS_RO(f
->area
->circuit_list
, node
, circuit
)) {
179 if (circuit
->state
!= C_STATE_UP
)
182 struct isis_adjacency
*adj
= circuit
->u
.p2p
.neighbor
;
184 if (!adj
|| adj
->adj_state
!= ISIS_ADJ_UP
)
187 struct neighbor_entry
*n
= neighbor_entry_new(adj
->sysid
, adj
);
189 skiplist_insert(f
->neighbors
, n
, n
);
195 static void neighbors_neighbors_update(struct fabricd
*f
)
197 hash_clean(f
->neighbors_neighbors
, neighbor_entry_del_void
);
199 struct listnode
*node
;
200 struct isis_vertex
*v
;
202 for (ALL_QUEUE_ELEMENTS_RO(&f
->spftree
->paths
, node
, v
)) {
203 if (v
->d_N
< 2 || !VTYPE_IS(v
->type
))
209 struct neighbor_entry
*n
= neighbor_entry_new(v
->N
.id
, NULL
);
210 struct neighbor_entry
*inserted
;
211 inserted
= hash_get(f
->neighbors_neighbors
, n
,
213 assert(inserted
== n
);
217 struct fabricd
*fabricd_new(struct isis_area
*area
)
219 struct fabricd
*rv
= XCALLOC(MTYPE_FABRICD_STATE
, sizeof(*rv
));
222 rv
->initial_sync_state
= FABRICD_SYNC_PENDING
;
224 rv
->spftree
= isis_spftree_new(area
);
225 rv
->neighbors
= skiplist_new(0, neighbor_entry_list_cmp
,
226 neighbor_entry_del_void
);
227 rv
->neighbors_neighbors
= hash_create(neighbor_entry_hash_key
,
228 neighbor_entry_hash_cmp
,
229 "Fabricd Neighbors");
231 rv
->tier
= rv
->tier_config
= ISIS_TIER_UNDEFINED
;
233 rv
->csnp_delay
= FABRICD_DEFAULT_CSNP_DELAY
;
237 void fabricd_finish(struct fabricd
*f
)
239 if (f
->initial_sync_timeout
)
240 thread_cancel(f
->initial_sync_timeout
);
242 if (f
->tier_calculation_timer
)
243 thread_cancel(f
->tier_calculation_timer
);
245 if (f
->tier_set_timer
)
246 thread_cancel(f
->tier_set_timer
);
248 isis_spftree_del(f
->spftree
);
249 neighbor_lists_clear(f
);
250 skiplist_free(f
->neighbors
);
251 hash_free(f
->neighbors_neighbors
);
254 static int fabricd_initial_sync_timeout(struct thread
*thread
)
256 struct fabricd
*f
= THREAD_ARG(thread
);
258 zlog_info("OpenFabric: Initial synchronization on %s timed out!",
259 f
->initial_sync_circuit
->interface
->name
);
260 f
->initial_sync_state
= FABRICD_SYNC_PENDING
;
261 f
->initial_sync_circuit
= NULL
;
262 f
->initial_sync_timeout
= NULL
;
266 void fabricd_initial_sync_hello(struct isis_circuit
*circuit
)
268 struct fabricd
*f
= circuit
->area
->fabricd
;
273 if (f
->initial_sync_state
> FABRICD_SYNC_PENDING
)
276 f
->initial_sync_state
= FABRICD_SYNC_STARTED
;
278 long timeout
= 2 * circuit
->hello_interval
[1] * circuit
->hello_multiplier
[1];
280 f
->initial_sync_circuit
= circuit
;
281 if (f
->initial_sync_timeout
)
284 thread_add_timer(master
, fabricd_initial_sync_timeout
, f
,
285 timeout
, &f
->initial_sync_timeout
);
286 f
->initial_sync_start
= monotime(NULL
);
288 zlog_info("OpenFabric: Started initial synchronization with %s on %s",
289 sysid_print(circuit
->u
.p2p
.neighbor
->sysid
),
290 circuit
->interface
->name
);
293 bool fabricd_initial_sync_is_in_progress(struct isis_area
*area
)
295 struct fabricd
*f
= area
->fabricd
;
300 if (f
->initial_sync_state
> FABRICD_SYNC_PENDING
301 && f
->initial_sync_state
< FABRICD_SYNC_COMPLETE
)
307 bool fabricd_initial_sync_is_complete(struct isis_area
*area
)
309 struct fabricd
*f
= area
->fabricd
;
314 return f
->initial_sync_state
== FABRICD_SYNC_COMPLETE
;
317 struct isis_circuit
*fabricd_initial_sync_circuit(struct isis_area
*area
)
319 struct fabricd
*f
= area
->fabricd
;
323 return f
->initial_sync_circuit
;
326 void fabricd_initial_sync_finish(struct isis_area
*area
)
328 struct fabricd
*f
= area
->fabricd
;
333 if (monotime(NULL
) - f
->initial_sync_start
< 5)
336 zlog_info("OpenFabric: Initial synchronization on %s complete.",
337 f
->initial_sync_circuit
->interface
->name
);
338 f
->initial_sync_state
= FABRICD_SYNC_COMPLETE
;
339 f
->initial_sync_circuit
= NULL
;
340 thread_cancel(f
->initial_sync_timeout
);
341 f
->initial_sync_timeout
= NULL
;
344 static void fabricd_bump_tier_calculation_timer(struct fabricd
*f
);
345 static void fabricd_set_tier(struct fabricd
*f
, uint8_t tier
);
347 static uint8_t fabricd_calculate_fabric_tier(struct isis_area
*area
)
349 struct isis_spftree
*local_tree
= fabricd_spftree(area
);
350 struct listnode
*node
;
352 struct isis_vertex
*furthest_t0
= NULL
,
353 *second_furthest_t0
= NULL
;
355 struct isis_vertex
*v
;
357 for (ALL_QUEUE_ELEMENTS_RO(&local_tree
->paths
, node
, v
)) {
358 struct isis_lsp
*lsp
= lsp_for_vertex(local_tree
, v
);
360 if (!lsp
|| !lsp
->tlvs
361 || !lsp
->tlvs
->spine_leaf
362 || !lsp
->tlvs
->spine_leaf
->has_tier
363 || lsp
->tlvs
->spine_leaf
->tier
!= 0)
366 second_furthest_t0
= furthest_t0
;
370 if (!second_furthest_t0
) {
371 zlog_info("OpenFabric: Could not find two T0 routers");
372 return ISIS_TIER_UNDEFINED
;
375 zlog_info("OpenFabric: Found %s as furthest t0 from local system, dist == %"
376 PRIu32
, rawlspid_print(furthest_t0
->N
.id
), furthest_t0
->d_N
);
378 struct isis_spftree
*remote_tree
=
379 isis_run_hopcount_spf(area
, furthest_t0
->N
.id
, NULL
);
381 struct isis_vertex
*furthest_from_remote
=
382 isis_vertex_queue_last(&remote_tree
->paths
);
384 if (!furthest_from_remote
) {
385 zlog_info("OpenFabric: Found no furthest node in remote spf");
386 isis_spftree_del(remote_tree
);
387 return ISIS_TIER_UNDEFINED
;
389 zlog_info("OpenFabric: Found %s as furthest from remote dist == %"
390 PRIu32
, rawlspid_print(furthest_from_remote
->N
.id
),
391 furthest_from_remote
->d_N
);
394 int64_t tier
= furthest_from_remote
->d_N
- furthest_t0
->d_N
;
395 isis_spftree_del(remote_tree
);
397 if (tier
< 0 || tier
>= ISIS_TIER_UNDEFINED
) {
398 zlog_info("OpenFabric: Calculated tier %" PRId64
" seems implausible",
400 return ISIS_TIER_UNDEFINED
;
403 zlog_info("OpenFabric: Calculated %" PRId64
" as tier", tier
);
407 static int fabricd_tier_set_timer(struct thread
*thread
)
409 struct fabricd
*f
= THREAD_ARG(thread
);
410 f
->tier_set_timer
= NULL
;
412 fabricd_set_tier(f
, f
->tier_pending
);
416 static int fabricd_tier_calculation_cb(struct thread
*thread
)
418 struct fabricd
*f
= THREAD_ARG(thread
);
419 uint8_t tier
= ISIS_TIER_UNDEFINED
;
420 f
->tier_calculation_timer
= NULL
;
422 tier
= fabricd_calculate_fabric_tier(f
->area
);
423 if (tier
== ISIS_TIER_UNDEFINED
)
426 zlog_info("OpenFabric: Got tier %" PRIu8
" from algorithm. Arming timer.",
428 f
->tier_pending
= tier
;
429 thread_add_timer(master
, fabricd_tier_set_timer
, f
,
430 f
->area
->lsp_gen_interval
[ISIS_LEVEL2
- 1],
436 static void fabricd_bump_tier_calculation_timer(struct fabricd
*f
)
438 /* Cancel timer if we already know our tier */
439 if (f
->tier
!= ISIS_TIER_UNDEFINED
440 || f
->tier_set_timer
) {
441 if (f
->tier_calculation_timer
) {
442 thread_cancel(f
->tier_calculation_timer
);
443 f
->tier_calculation_timer
= NULL
;
448 /* If we need to calculate the tier, wait some
449 * time for the topology to settle before running
451 if (f
->tier_calculation_timer
) {
452 thread_cancel(f
->tier_calculation_timer
);
453 f
->tier_calculation_timer
= NULL
;
456 thread_add_timer(master
, fabricd_tier_calculation_cb
, f
,
457 2 * f
->area
->lsp_gen_interval
[ISIS_LEVEL2
- 1],
458 &f
->tier_calculation_timer
);
461 static void fabricd_set_tier(struct fabricd
*f
, uint8_t tier
)
466 zlog_info("OpenFabric: Set own tier to %" PRIu8
, tier
);
469 fabricd_bump_tier_calculation_timer(f
);
470 lsp_regenerate_schedule(f
->area
, ISIS_LEVEL2
, 0);
473 void fabricd_run_spf(struct isis_area
*area
)
475 struct fabricd
*f
= area
->fabricd
;
480 isis_run_hopcount_spf(area
, isis
->sysid
, f
->spftree
);
481 neighbors_neighbors_update(f
);
482 fabricd_bump_tier_calculation_timer(f
);
485 struct isis_spftree
*fabricd_spftree(struct isis_area
*area
)
487 struct fabricd
*f
= area
->fabricd
;
495 void fabricd_configure_tier(struct isis_area
*area
, uint8_t tier
)
497 struct fabricd
*f
= area
->fabricd
;
499 if (!f
|| f
->tier_config
== tier
)
502 f
->tier_config
= tier
;
503 fabricd_set_tier(f
, tier
);
506 uint8_t fabricd_tier(struct isis_area
*area
)
508 struct fabricd
*f
= area
->fabricd
;
511 return ISIS_TIER_UNDEFINED
;
516 int fabricd_write_settings(struct isis_area
*area
, struct vty
*vty
)
518 struct fabricd
*f
= area
->fabricd
;
524 if (f
->tier_config
!= ISIS_TIER_UNDEFINED
) {
525 vty_out(vty
, " fabric-tier %" PRIu8
"\n", f
->tier_config
);
529 if (f
->csnp_delay
!= FABRICD_DEFAULT_CSNP_DELAY
530 || f
->always_send_csnp
) {
531 vty_out(vty
, " triggered-csnp-delay %d%s\n", f
->csnp_delay
,
532 f
->always_send_csnp
? " always" : "");
538 static void move_to_queue(struct isis_lsp
*lsp
, struct neighbor_entry
*n
,
539 enum isis_tx_type type
, struct isis_circuit
*circuit
)
543 if (n
->adj
&& n
->adj
->circuit
== circuit
)
546 if (isis
->debugs
& DEBUG_FLOODING
) {
547 zlog_debug("OpenFabric: Adding %s to %s",
548 print_sys_hostname(n
->id
),
549 (type
== TX_LSP_NORMAL
) ? "RF" : "DNR");
553 isis_tx_queue_add(n
->adj
->circuit
->tx_queue
, lsp
, type
);
555 uint8_t *neighbor_id
= XMALLOC(MTYPE_FABRICD_FLOODING_INFO
,
558 memcpy(neighbor_id
, n
->id
, sizeof(n
->id
));
559 listnode_add(lsp
->flooding_neighbors
[type
], neighbor_id
);
562 static void mark_neighbor_as_present(struct hash_bucket
*bucket
, void *arg
)
564 struct neighbor_entry
*n
= bucket
->data
;
569 static void handle_firsthops(struct hash_bucket
*bucket
, void *arg
)
571 struct isis_lsp
*lsp
= arg
;
572 struct fabricd
*f
= lsp
->area
->fabricd
;
573 struct isis_vertex
*vertex
= bucket
->data
;
575 struct neighbor_entry
*n
;
577 n
= neighbor_entry_lookup_list(f
->neighbors
, vertex
->N
.id
);
579 if (isis
->debugs
& DEBUG_FLOODING
) {
580 zlog_debug("Removing %s from NL as its in the reverse path",
581 print_sys_hostname(n
->id
));
586 n
= neighbor_entry_lookup_hash(f
->neighbors_neighbors
, vertex
->N
.id
);
588 if (isis
->debugs
& DEBUG_FLOODING
) {
589 zlog_debug("Removing %s from NN as its in the reverse path",
590 print_sys_hostname(n
->id
));
596 static struct isis_lsp
*lsp_for_neighbor(struct fabricd
*f
,
597 struct neighbor_entry
*n
)
599 uint8_t id
[ISIS_SYS_ID_LEN
+ 1] = {0};
601 memcpy(id
, n
->id
, sizeof(n
->id
));
603 struct isis_vertex vertex
= {0};
605 isis_vertex_id_init(&vertex
, id
, VTYPE_NONPSEUDO_TE_IS
);
607 return lsp_for_vertex(f
->spftree
, &vertex
);
610 static void fabricd_free_lsp_flooding_info(void *val
)
612 XFREE(MTYPE_FABRICD_FLOODING_INFO
, val
);
615 static void fabricd_lsp_reset_flooding_info(struct isis_lsp
*lsp
,
616 struct isis_circuit
*circuit
)
618 lsp
->flooding_time
= time(NULL
);
620 XFREE(MTYPE_FABRICD_FLOODING_INFO
, lsp
->flooding_interface
);
621 for (enum isis_tx_type type
= TX_LSP_NORMAL
;
622 type
<= TX_LSP_CIRCUIT_SCOPED
; type
++) {
623 if (lsp
->flooding_neighbors
[type
]) {
624 list_delete_all_node(lsp
->flooding_neighbors
[type
]);
628 lsp
->flooding_neighbors
[type
] = list_new();
629 lsp
->flooding_neighbors
[type
]->del
=
630 fabricd_free_lsp_flooding_info
;
634 lsp
->flooding_interface
= XSTRDUP(MTYPE_FABRICD_FLOODING_INFO
,
635 circuit
->interface
->name
);
638 lsp
->flooding_circuit_scoped
= false;
641 void fabricd_lsp_flood(struct isis_lsp
*lsp
, struct isis_circuit
*circuit
)
643 struct fabricd
*f
= lsp
->area
->fabricd
;
646 fabricd_lsp_reset_flooding_info(lsp
, circuit
);
649 struct neighbor_entry
*n
;
651 /* Mark all elements in NL as present */
652 while (!skiplist_next(f
->neighbors
, NULL
, (void **)&n
, &cursor
))
655 /* Mark all elements in NN as present */
656 hash_iterate(f
->neighbors_neighbors
, mark_neighbor_as_present
, NULL
);
658 struct isis_vertex
*originator
=
659 isis_find_vertex(&f
->spftree
->paths
,
661 VTYPE_NONPSEUDO_TE_IS
);
663 /* Remove all IS from NL and NN in the shortest path
664 * to the IS that originated the LSP */
666 hash_iterate(originator
->firsthops
, handle_firsthops
, lsp
);
668 /* Iterate over all remaining IS in NL */
670 while (!skiplist_next(f
->neighbors
, NULL
, (void **)&n
, &cursor
)) {
674 struct isis_lsp
*nlsp
= lsp_for_neighbor(f
, n
);
675 if (!nlsp
|| !nlsp
->tlvs
) {
676 if (isis
->debugs
& DEBUG_FLOODING
) {
677 zlog_debug("Moving %s to DNR as it has no LSP",
678 print_sys_hostname(n
->id
));
681 move_to_queue(lsp
, n
, TX_LSP_CIRCUIT_SCOPED
, circuit
);
685 if (isis
->debugs
& DEBUG_FLOODING
) {
686 zlog_debug("Considering %s from NL...",
687 print_sys_hostname(n
->id
));
690 /* For all neighbors of the NL IS check whether they are present
691 * in NN. If yes, remove from NN and set need_reflood. */
692 bool need_reflood
= false;
693 struct isis_extended_reach
*er
;
694 for (er
= (struct isis_extended_reach
*)nlsp
->tlvs
->extended_reach
.head
;
696 struct neighbor_entry
*nn
;
698 nn
= neighbor_entry_lookup_hash(f
->neighbors_neighbors
,
702 if (isis
->debugs
& DEBUG_FLOODING
) {
703 zlog_debug("Found neighbor %s in NN, removing it from NN and setting reflood.",
704 print_sys_hostname(nn
->id
));
712 move_to_queue(lsp
, n
, need_reflood
?
713 TX_LSP_NORMAL
: TX_LSP_CIRCUIT_SCOPED
,
717 if (isis
->debugs
& DEBUG_FLOODING
) {
718 zlog_debug("OpenFabric: Flooding algorithm complete.");
722 void fabricd_trigger_csnp(struct isis_area
*area
, bool circuit_scoped
)
724 struct fabricd
*f
= area
->fabricd
;
729 if (!circuit_scoped
&& !f
->always_send_csnp
)
732 struct listnode
*node
;
733 struct isis_circuit
*circuit
;
735 for (ALL_LIST_ELEMENTS_RO(area
->circuit_list
, node
, circuit
)) {
736 if (!circuit
->t_send_csnp
[1])
739 thread_cancel(circuit
->t_send_csnp
[ISIS_LEVEL2
- 1]);
740 thread_add_timer_msec(master
, send_l2_csnp
, circuit
,
741 isis_jitter(f
->csnp_delay
, CSNP_JITTER
),
742 &circuit
->t_send_csnp
[ISIS_LEVEL2
- 1]);
746 struct list
*fabricd_ip_addrs(struct isis_circuit
*circuit
)
748 if (circuit
->ip_addrs
&& listcount(circuit
->ip_addrs
))
749 return circuit
->ip_addrs
;
751 if (!fabricd
|| !circuit
->area
|| !circuit
->area
->circuit_list
)
754 struct listnode
*node
;
755 struct isis_circuit
*c
;
757 for (ALL_LIST_ELEMENTS_RO(circuit
->area
->circuit_list
, node
, c
)) {
758 if (c
->circ_type
!= CIRCUIT_T_LOOPBACK
)
761 if (!c
->ip_addrs
|| !listcount(c
->ip_addrs
))
770 void fabricd_lsp_free(struct isis_lsp
*lsp
)
772 XFREE(MTYPE_FABRICD_FLOODING_INFO
, lsp
->flooding_interface
);
773 for (enum isis_tx_type type
= TX_LSP_NORMAL
;
774 type
<= TX_LSP_CIRCUIT_SCOPED
; type
++) {
775 if (!lsp
->flooding_neighbors
[type
])
778 list_delete(&lsp
->flooding_neighbors
[type
]);
782 void fabricd_update_lsp_no_flood(struct isis_lsp
*lsp
,
783 struct isis_circuit
*circuit
)
788 fabricd_lsp_reset_flooding_info(lsp
, circuit
);
789 lsp
->flooding_circuit_scoped
= true;
792 void fabricd_configure_triggered_csnp(struct isis_area
*area
, int delay
,
793 bool always_send_csnp
)
795 struct fabricd
*f
= area
->fabricd
;
800 f
->csnp_delay
= delay
;
801 f
->always_send_csnp
= always_send_csnp
;
804 void fabricd_init(void)
806 hook_register(isis_adj_state_change_hook
,
807 fabricd_handle_adj_state_change
);