]> git.proxmox.com Git - mirror_frr.git/blame_incremental - isisd/fabricd.c
isisd: implemented the 'sequence-number-skipped' notification
[mirror_frr.git] / isisd / fabricd.c
... / ...
CommitLineData
1/*
2 * IS-IS Rout(e)ing protocol - OpenFabric extensions
3 *
4 * Copyright (C) 2018 Christian Franke
5 *
6 * This file is part of FreeRangeRouting (FRR)
7 *
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
11 * later version.
12 *
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.
17 *
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
21 */
22#include <zebra.h>
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"
35
36DEFINE_MTYPE_STATIC(ISISD, FABRICD_STATE, "ISIS OpenFabric")
37DEFINE_MTYPE_STATIC(ISISD, FABRICD_NEIGHBOR, "ISIS OpenFabric Neighbor Entry")
38DEFINE_MTYPE_STATIC(ISISD, FABRICD_FLOODING_INFO, "ISIS OpenFabric Flooding Log")
39
40/* Tracks initial synchronization as per section 2.4
41 *
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.
44 */
45enum fabricd_sync_state {
46 FABRICD_SYNC_PENDING,
47 FABRICD_SYNC_STARTED,
48 FABRICD_SYNC_COMPLETE
49};
50
51struct fabricd {
52 struct isis_area *area;
53
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;
58
59 struct isis_spftree *spftree;
60 struct skiplist *neighbors;
61 struct hash *neighbors_neighbors;
62
63 uint8_t tier;
64 uint8_t tier_config;
65 uint8_t tier_pending;
66 struct thread *tier_calculation_timer;
67 struct thread *tier_set_timer;
68
69 int csnp_delay;
70 bool always_send_csnp;
71};
72
73/* Code related to maintaining the neighbor lists */
74
75struct neighbor_entry {
76 uint8_t id[ISIS_SYS_ID_LEN];
77 struct isis_adjacency *adj;
78 bool present;
79};
80
81static struct neighbor_entry *neighbor_entry_new(const uint8_t *id,
82 struct isis_adjacency *adj)
83{
84 struct neighbor_entry *rv = XMALLOC(MTYPE_FABRICD_NEIGHBOR,
85 sizeof(*rv));
86
87 memcpy(rv->id, id, sizeof(rv->id));
88 rv->adj = adj;
89
90 return rv;
91}
92
93static void neighbor_entry_del(struct neighbor_entry *neighbor)
94{
95 XFREE(MTYPE_FABRICD_NEIGHBOR, neighbor);
96}
97
98static void neighbor_entry_del_void(void *arg)
99{
100 neighbor_entry_del((struct neighbor_entry *)arg);
101}
102
103static void neighbor_lists_clear(struct fabricd *f)
104{
105 while (!skiplist_empty(f->neighbors))
106 skiplist_delete_first(f->neighbors);
107
108 hash_clean(f->neighbors_neighbors, neighbor_entry_del_void);
109}
110
111static unsigned neighbor_entry_hash_key(void *np)
112{
113 struct neighbor_entry *n = np;
114
115 return jhash(n->id, sizeof(n->id), 0x55aa5a5a);
116}
117
118static bool neighbor_entry_hash_cmp(const void *a, const void *b)
119{
120 const struct neighbor_entry *na = a, *nb = b;
121
122 return memcmp(na->id, nb->id, sizeof(na->id)) == 0;
123}
124
125static int neighbor_entry_list_cmp(void *a, void *b)
126{
127 struct neighbor_entry *na = a, *nb = b;
128
129 return -memcmp(na->id, nb->id, sizeof(na->id));
130}
131
132static struct neighbor_entry *neighbor_entry_lookup_list(struct skiplist *list,
133 const uint8_t *id)
134{
135 struct neighbor_entry n = { {0} };
136
137 memcpy(n.id, id, sizeof(n.id));
138
139 struct neighbor_entry *rv;
140
141 if (skiplist_search(list, &n, (void**)&rv))
142 return NULL;
143
144 if (!rv->present)
145 return NULL;
146
147 return rv;
148}
149
150static struct neighbor_entry *neighbor_entry_lookup_hash(struct hash *hash,
151 const uint8_t *id)
152{
153 struct neighbor_entry n = {{0}};
154
155 memcpy(n.id, id, sizeof(n.id));
156
157 struct neighbor_entry *rv = hash_lookup(hash, &n);
158
159 if (!rv || !rv->present)
160 return NULL;
161
162 return rv;
163}
164
165static int fabricd_handle_adj_state_change(struct isis_adjacency *arg)
166{
167 struct fabricd *f = arg->circuit->area->fabricd;
168
169 if (!f)
170 return 0;
171
172 while (!skiplist_empty(f->neighbors))
173 skiplist_delete_first(f->neighbors);
174
175 struct listnode *node;
176 struct isis_circuit *circuit;
177
178 for (ALL_LIST_ELEMENTS_RO(f->area->circuit_list, node, circuit)) {
179 if (circuit->state != C_STATE_UP)
180 continue;
181
182 struct isis_adjacency *adj = circuit->u.p2p.neighbor;
183
184 if (!adj || adj->adj_state != ISIS_ADJ_UP)
185 continue;
186
187 struct neighbor_entry *n = neighbor_entry_new(adj->sysid, adj);
188
189 skiplist_insert(f->neighbors, n, n);
190 }
191
192 return 0;
193}
194
195static void neighbors_neighbors_update(struct fabricd *f)
196{
197 hash_clean(f->neighbors_neighbors, neighbor_entry_del_void);
198
199 struct listnode *node;
200 struct isis_vertex *v;
201
202 for (ALL_QUEUE_ELEMENTS_RO(&f->spftree->paths, node, v)) {
203 if (v->d_N < 2 || !VTYPE_IS(v->type))
204 continue;
205
206 if (v->d_N > 2)
207 break;
208
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,
212 hash_alloc_intern);
213 assert(inserted == n);
214 }
215}
216
217struct fabricd *fabricd_new(struct isis_area *area)
218{
219 struct fabricd *rv = XCALLOC(MTYPE_FABRICD_STATE, sizeof(*rv));
220
221 rv->area = area;
222 rv->initial_sync_state = FABRICD_SYNC_PENDING;
223
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");
230
231 rv->tier = rv->tier_config = ISIS_TIER_UNDEFINED;
232
233 rv->csnp_delay = FABRICD_DEFAULT_CSNP_DELAY;
234 return rv;
235};
236
237void fabricd_finish(struct fabricd *f)
238{
239 if (f->initial_sync_timeout)
240 thread_cancel(f->initial_sync_timeout);
241
242 if (f->tier_calculation_timer)
243 thread_cancel(f->tier_calculation_timer);
244
245 if (f->tier_set_timer)
246 thread_cancel(f->tier_set_timer);
247
248 isis_spftree_del(f->spftree);
249 neighbor_lists_clear(f);
250 skiplist_free(f->neighbors);
251 hash_free(f->neighbors_neighbors);
252}
253
254static int fabricd_initial_sync_timeout(struct thread *thread)
255{
256 struct fabricd *f = THREAD_ARG(thread);
257
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;
263 return 0;
264}
265
266void fabricd_initial_sync_hello(struct isis_circuit *circuit)
267{
268 struct fabricd *f = circuit->area->fabricd;
269
270 if (!f)
271 return;
272
273 if (f->initial_sync_state > FABRICD_SYNC_PENDING)
274 return;
275
276 f->initial_sync_state = FABRICD_SYNC_STARTED;
277
278 long timeout = 2 * circuit->hello_interval[1] * circuit->hello_multiplier[1];
279
280 f->initial_sync_circuit = circuit;
281 if (f->initial_sync_timeout)
282 return;
283
284 thread_add_timer(master, fabricd_initial_sync_timeout, f,
285 timeout, &f->initial_sync_timeout);
286 f->initial_sync_start = monotime(NULL);
287
288 zlog_info("OpenFabric: Started initial synchronization with %s on %s",
289 sysid_print(circuit->u.p2p.neighbor->sysid),
290 circuit->interface->name);
291}
292
293bool fabricd_initial_sync_is_in_progress(struct isis_area *area)
294{
295 struct fabricd *f = area->fabricd;
296
297 if (!f)
298 return false;
299
300 if (f->initial_sync_state > FABRICD_SYNC_PENDING
301 && f->initial_sync_state < FABRICD_SYNC_COMPLETE)
302 return true;
303
304 return false;
305}
306
307bool fabricd_initial_sync_is_complete(struct isis_area *area)
308{
309 struct fabricd *f = area->fabricd;
310
311 if (!f)
312 return false;
313
314 return f->initial_sync_state == FABRICD_SYNC_COMPLETE;
315}
316
317struct isis_circuit *fabricd_initial_sync_circuit(struct isis_area *area)
318{
319 struct fabricd *f = area->fabricd;
320 if (!f)
321 return NULL;
322
323 return f->initial_sync_circuit;
324}
325
326void fabricd_initial_sync_finish(struct isis_area *area)
327{
328 struct fabricd *f = area->fabricd;
329
330 if (!f)
331 return;
332
333 if (monotime(NULL) - f->initial_sync_start < 5)
334 return;
335
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;
342}
343
344static void fabricd_bump_tier_calculation_timer(struct fabricd *f);
345static void fabricd_set_tier(struct fabricd *f, uint8_t tier);
346
347static uint8_t fabricd_calculate_fabric_tier(struct isis_area *area)
348{
349 struct isis_spftree *local_tree = fabricd_spftree(area);
350 struct listnode *node;
351
352 struct isis_vertex *furthest_t0 = NULL,
353 *second_furthest_t0 = NULL;
354
355 struct isis_vertex *v;
356
357 for (ALL_QUEUE_ELEMENTS_RO(&local_tree->paths, node, v)) {
358 struct isis_lsp *lsp = lsp_for_vertex(local_tree, v);
359
360 if (!lsp || !lsp->tlvs
361 || !lsp->tlvs->spine_leaf
362 || !lsp->tlvs->spine_leaf->has_tier
363 || lsp->tlvs->spine_leaf->tier != 0)
364 continue;
365
366 second_furthest_t0 = furthest_t0;
367 furthest_t0 = v;
368 }
369
370 if (!second_furthest_t0) {
371 zlog_info("OpenFabric: Could not find two T0 routers");
372 return ISIS_TIER_UNDEFINED;
373 }
374
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);
377
378 struct isis_spftree *remote_tree =
379 isis_run_hopcount_spf(area, furthest_t0->N.id, NULL);
380
381 struct isis_vertex *furthest_from_remote =
382 isis_vertex_queue_last(&remote_tree->paths);
383
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;
388 } else {
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);
392 }
393
394 int64_t tier = furthest_from_remote->d_N - furthest_t0->d_N;
395 isis_spftree_del(remote_tree);
396
397 if (tier < 0 || tier >= ISIS_TIER_UNDEFINED) {
398 zlog_info("OpenFabric: Calculated tier %" PRId64 " seems implausible",
399 tier);
400 return ISIS_TIER_UNDEFINED;
401 }
402
403 zlog_info("OpenFabric: Calculated %" PRId64 " as tier", tier);
404 return tier;
405}
406
407static int fabricd_tier_set_timer(struct thread *thread)
408{
409 struct fabricd *f = THREAD_ARG(thread);
410 f->tier_set_timer = NULL;
411
412 fabricd_set_tier(f, f->tier_pending);
413 return 0;
414}
415
416static int fabricd_tier_calculation_cb(struct thread *thread)
417{
418 struct fabricd *f = THREAD_ARG(thread);
419 uint8_t tier = ISIS_TIER_UNDEFINED;
420 f->tier_calculation_timer = NULL;
421
422 tier = fabricd_calculate_fabric_tier(f->area);
423 if (tier == ISIS_TIER_UNDEFINED)
424 return 0;
425
426 zlog_info("OpenFabric: Got tier %" PRIu8 " from algorithm. Arming timer.",
427 tier);
428 f->tier_pending = tier;
429 thread_add_timer(master, fabricd_tier_set_timer, f,
430 f->area->lsp_gen_interval[ISIS_LEVEL2 - 1],
431 &f->tier_set_timer);
432
433 return 0;
434}
435
436static void fabricd_bump_tier_calculation_timer(struct fabricd *f)
437{
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;
444 }
445 return;
446 }
447
448 /* If we need to calculate the tier, wait some
449 * time for the topology to settle before running
450 * the calculation */
451 if (f->tier_calculation_timer) {
452 thread_cancel(f->tier_calculation_timer);
453 f->tier_calculation_timer = NULL;
454 }
455
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);
459}
460
461static void fabricd_set_tier(struct fabricd *f, uint8_t tier)
462{
463 if (f->tier == tier)
464 return;
465
466 zlog_info("OpenFabric: Set own tier to %" PRIu8, tier);
467 f->tier = tier;
468
469 fabricd_bump_tier_calculation_timer(f);
470 lsp_regenerate_schedule(f->area, ISIS_LEVEL2, 0);
471}
472
473void fabricd_run_spf(struct isis_area *area)
474{
475 struct fabricd *f = area->fabricd;
476
477 if (!f)
478 return;
479
480 isis_run_hopcount_spf(area, isis->sysid, f->spftree);
481 neighbors_neighbors_update(f);
482 fabricd_bump_tier_calculation_timer(f);
483}
484
485struct isis_spftree *fabricd_spftree(struct isis_area *area)
486{
487 struct fabricd *f = area->fabricd;
488
489 if (!f)
490 return NULL;
491
492 return f->spftree;
493}
494
495void fabricd_configure_tier(struct isis_area *area, uint8_t tier)
496{
497 struct fabricd *f = area->fabricd;
498
499 if (!f || f->tier_config == tier)
500 return;
501
502 f->tier_config = tier;
503 fabricd_set_tier(f, tier);
504}
505
506uint8_t fabricd_tier(struct isis_area *area)
507{
508 struct fabricd *f = area->fabricd;
509
510 if (!f)
511 return ISIS_TIER_UNDEFINED;
512
513 return f->tier;
514}
515
516int fabricd_write_settings(struct isis_area *area, struct vty *vty)
517{
518 struct fabricd *f = area->fabricd;
519 int written = 0;
520
521 if (!f)
522 return written;
523
524 if (f->tier_config != ISIS_TIER_UNDEFINED) {
525 vty_out(vty, " fabric-tier %" PRIu8 "\n", f->tier_config);
526 written++;
527 }
528
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" : "");
533 }
534
535 return written;
536}
537
538static void move_to_queue(struct isis_lsp *lsp, struct neighbor_entry *n,
539 enum isis_tx_type type, struct isis_circuit *circuit)
540{
541 n->present = false;
542
543 if (n->adj && n->adj->circuit == circuit)
544 return;
545
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");
550 }
551
552 if (n->adj)
553 isis_tx_queue_add(n->adj->circuit->tx_queue, lsp, type);
554
555 uint8_t *neighbor_id = XMALLOC(MTYPE_FABRICD_FLOODING_INFO,
556 sizeof(n->id));
557
558 memcpy(neighbor_id, n->id, sizeof(n->id));
559 listnode_add(lsp->flooding_neighbors[type], neighbor_id);
560}
561
562static void mark_neighbor_as_present(struct hash_backet *backet, void *arg)
563{
564 struct neighbor_entry *n = backet->data;
565
566 n->present = true;
567}
568
569static void handle_firsthops(struct hash_backet *backet, void *arg)
570{
571 struct isis_lsp *lsp = arg;
572 struct fabricd *f = lsp->area->fabricd;
573 struct isis_vertex *vertex = backet->data;
574
575 struct neighbor_entry *n;
576
577 n = neighbor_entry_lookup_list(f->neighbors, vertex->N.id);
578 if (n) {
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));
582 }
583 n->present = false;
584 }
585
586 n = neighbor_entry_lookup_hash(f->neighbors_neighbors, vertex->N.id);
587 if (n) {
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));
591 }
592 n->present = false;
593 }
594}
595
596static struct isis_lsp *lsp_for_neighbor(struct fabricd *f,
597 struct neighbor_entry *n)
598{
599 uint8_t id[ISIS_SYS_ID_LEN + 1] = {0};
600
601 memcpy(id, n->id, sizeof(n->id));
602
603 struct isis_vertex vertex = {0};
604
605 isis_vertex_id_init(&vertex, id, VTYPE_NONPSEUDO_TE_IS);
606
607 return lsp_for_vertex(f->spftree, &vertex);
608}
609
610static void fabricd_free_lsp_flooding_info(void *val)
611{
612 XFREE(MTYPE_FABRICD_FLOODING_INFO, val);
613}
614
615static void fabricd_lsp_reset_flooding_info(struct isis_lsp *lsp,
616 struct isis_circuit *circuit)
617{
618 lsp->flooding_time = time(NULL);
619
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]);
625 continue;
626 }
627
628 lsp->flooding_neighbors[type] = list_new();
629 lsp->flooding_neighbors[type]->del =
630 fabricd_free_lsp_flooding_info;
631 }
632
633 if (circuit) {
634 lsp->flooding_interface = XSTRDUP(MTYPE_FABRICD_FLOODING_INFO,
635 circuit->interface->name);
636 }
637
638 lsp->flooding_circuit_scoped = false;
639}
640
641void fabricd_lsp_flood(struct isis_lsp *lsp, struct isis_circuit *circuit)
642{
643 struct fabricd *f = lsp->area->fabricd;
644 assert(f);
645
646 fabricd_lsp_reset_flooding_info(lsp, circuit);
647
648 void *cursor = NULL;
649 struct neighbor_entry *n;
650
651 /* Mark all elements in NL as present */
652 while (!skiplist_next(f->neighbors, NULL, (void **)&n, &cursor))
653 n->present = true;
654
655 /* Mark all elements in NN as present */
656 hash_iterate(f->neighbors_neighbors, mark_neighbor_as_present, NULL);
657
658 struct isis_vertex *originator =
659 isis_find_vertex(&f->spftree->paths,
660 lsp->hdr.lsp_id,
661 VTYPE_NONPSEUDO_TE_IS);
662
663 /* Remove all IS from NL and NN in the shortest path
664 * to the IS that originated the LSP */
665 if (originator)
666 hash_iterate(originator->firsthops, handle_firsthops, lsp);
667
668 /* Iterate over all remaining IS in NL */
669 cursor = NULL;
670 while (!skiplist_next(f->neighbors, NULL, (void **)&n, &cursor)) {
671 if (!n->present)
672 continue;
673
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));
679 }
680
681 move_to_queue(lsp, n, TX_LSP_CIRCUIT_SCOPED, circuit);
682 continue;
683 }
684
685 if (isis->debugs & DEBUG_FLOODING) {
686 zlog_debug("Considering %s from NL...",
687 print_sys_hostname(n->id));
688 }
689
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;
695 er; er = er->next) {
696 struct neighbor_entry *nn;
697
698 nn = neighbor_entry_lookup_hash(f->neighbors_neighbors,
699 er->id);
700
701 if (nn) {
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));
705 }
706
707 nn->present = false;
708 need_reflood = true;
709 }
710 }
711
712 move_to_queue(lsp, n, need_reflood ?
713 TX_LSP_NORMAL : TX_LSP_CIRCUIT_SCOPED,
714 circuit);
715 }
716
717 if (isis->debugs & DEBUG_FLOODING) {
718 zlog_debug("OpenFabric: Flooding algorithm complete.");
719 }
720}
721
722void fabricd_trigger_csnp(struct isis_area *area, bool circuit_scoped)
723{
724 struct fabricd *f = area->fabricd;
725
726 if (!f)
727 return;
728
729 if (!circuit_scoped && !f->always_send_csnp)
730 return;
731
732 struct listnode *node;
733 struct isis_circuit *circuit;
734
735 for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) {
736 if (!circuit->t_send_csnp[1])
737 continue;
738
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]);
743 }
744}
745
746struct list *fabricd_ip_addrs(struct isis_circuit *circuit)
747{
748 if (circuit->ip_addrs && listcount(circuit->ip_addrs))
749 return circuit->ip_addrs;
750
751 if (!fabricd || !circuit->area || !circuit->area->circuit_list)
752 return NULL;
753
754 struct listnode *node;
755 struct isis_circuit *c;
756
757 for (ALL_LIST_ELEMENTS_RO(circuit->area->circuit_list, node, c)) {
758 if (c->circ_type != CIRCUIT_T_LOOPBACK)
759 continue;
760
761 if (!c->ip_addrs || !listcount(c->ip_addrs))
762 return NULL;
763
764 return c->ip_addrs;
765 }
766
767 return NULL;
768}
769
770void fabricd_lsp_free(struct isis_lsp *lsp)
771{
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])
776 continue;
777
778 list_delete(&lsp->flooding_neighbors[type]);
779 }
780}
781
782void fabricd_update_lsp_no_flood(struct isis_lsp *lsp,
783 struct isis_circuit *circuit)
784{
785 if (!fabricd)
786 return;
787
788 fabricd_lsp_reset_flooding_info(lsp, circuit);
789 lsp->flooding_circuit_scoped = true;
790}
791
792void fabricd_configure_triggered_csnp(struct isis_area *area, int delay,
793 bool always_send_csnp)
794{
795 struct fabricd *f = area->fabricd;
796
797 if (!f)
798 return;
799
800 f->csnp_delay = delay;
801 f->always_send_csnp = always_send_csnp;
802}
803
804void fabricd_init(void)
805{
806 hook_register(isis_adj_state_change_hook,
807 fabricd_handle_adj_state_change);
808}