]> git.proxmox.com Git - mirror_frr.git/blob - isisd/fabricd.c
fabricd: make triggered csnp delay configurable
[mirror_frr.git] / isisd / fabricd.c
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
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")
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 */
45 enum fabricd_sync_state {
46 FABRICD_SYNC_PENDING,
47 FABRICD_SYNC_STARTED,
48 FABRICD_SYNC_COMPLETE
49 };
50
51 struct 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
75 struct neighbor_entry {
76 uint8_t id[ISIS_SYS_ID_LEN];
77 struct isis_adjacency *adj;
78 bool present;
79 };
80
81 static 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, sizeof(*rv));
85
86 memcpy(rv->id, id, sizeof(rv->id));
87 rv->adj = adj;
88
89 return rv;
90 }
91
92 static void neighbor_entry_del(struct neighbor_entry *neighbor)
93 {
94 XFREE(MTYPE_FABRICD_NEIGHBOR, neighbor);
95 }
96
97 static void neighbor_entry_del_void(void *arg)
98 {
99 neighbor_entry_del((struct neighbor_entry *)arg);
100 }
101
102 static void neighbor_lists_clear(struct fabricd *f)
103 {
104 while (!skiplist_empty(f->neighbors))
105 skiplist_delete_first(f->neighbors);
106
107 hash_clean(f->neighbors_neighbors, neighbor_entry_del_void);
108 }
109
110 static unsigned neighbor_entry_hash_key(void *np)
111 {
112 struct neighbor_entry *n = np;
113
114 return jhash(n->id, sizeof(n->id), 0x55aa5a5a);
115 }
116
117 static bool neighbor_entry_hash_cmp(const void *a, const void *b)
118 {
119 const struct neighbor_entry *na = a, *nb = b;
120
121 return memcmp(na->id, nb->id, sizeof(na->id)) == 0;
122 }
123
124 static int neighbor_entry_list_cmp(void *a, void *b)
125 {
126 struct neighbor_entry *na = a, *nb = b;
127
128 return -memcmp(na->id, nb->id, sizeof(na->id));
129 }
130
131 static struct neighbor_entry *neighbor_entry_lookup_list(struct skiplist *list,
132 const uint8_t *id)
133 {
134 struct neighbor_entry n = {{0}};
135
136 memcpy(n.id, id, sizeof(n.id));
137
138 struct neighbor_entry *rv;
139
140 if (skiplist_search(list, &n, (void**)&rv))
141 return NULL;
142
143 if (!rv->present)
144 return NULL;
145
146 return rv;
147 }
148
149 static struct neighbor_entry *neighbor_entry_lookup_hash(struct hash *hash,
150 const uint8_t *id)
151 {
152 struct neighbor_entry n = {{0}};
153
154 memcpy(n.id, id, sizeof(n.id));
155
156 struct neighbor_entry *rv = hash_lookup(hash, &n);
157
158 if (!rv || !rv->present)
159 return NULL;
160
161 return rv;
162 }
163
164 static void neighbor_lists_update(struct fabricd *f)
165 {
166 neighbor_lists_clear(f);
167
168 struct listnode *node;
169 struct isis_circuit *circuit;
170
171 for (ALL_LIST_ELEMENTS_RO(f->area->circuit_list, node, circuit)) {
172 if (circuit->state != C_STATE_UP)
173 continue;
174
175 struct isis_adjacency *adj = circuit->u.p2p.neighbor;
176
177 if (!adj || adj->adj_state != ISIS_ADJ_UP)
178 continue;
179
180 struct neighbor_entry *n = neighbor_entry_new(adj->sysid, adj);
181
182 skiplist_insert(f->neighbors, n, n);
183 }
184
185 struct isis_vertex *v;
186
187 for (ALL_QUEUE_ELEMENTS_RO(&f->spftree->paths, node, v)) {
188 if (v->d_N < 2 || !VTYPE_IS(v->type))
189 continue;
190
191 if (v->d_N > 2)
192 break;
193
194 struct neighbor_entry *n = neighbor_entry_new(v->N.id, NULL);
195 struct neighbor_entry *inserted;
196 inserted = hash_get(f->neighbors_neighbors, n,
197 hash_alloc_intern);
198 assert(inserted == n);
199 }
200 }
201
202 struct fabricd *fabricd_new(struct isis_area *area)
203 {
204 struct fabricd *rv = XCALLOC(MTYPE_FABRICD_STATE, sizeof(*rv));
205
206 rv->area = area;
207 rv->initial_sync_state = FABRICD_SYNC_PENDING;
208
209 rv->spftree = isis_spftree_new(area);
210 rv->neighbors = skiplist_new(0, neighbor_entry_list_cmp,
211 neighbor_entry_del_void);
212 rv->neighbors_neighbors = hash_create(neighbor_entry_hash_key,
213 neighbor_entry_hash_cmp,
214 "Fabricd Neighbors");
215
216 rv->tier = rv->tier_config = ISIS_TIER_UNDEFINED;
217
218 rv->csnp_delay = FABRICD_DEFAULT_CSNP_DELAY;
219 return rv;
220 };
221
222 void fabricd_finish(struct fabricd *f)
223 {
224 if (f->initial_sync_timeout)
225 thread_cancel(f->initial_sync_timeout);
226
227 if (f->tier_calculation_timer)
228 thread_cancel(f->tier_calculation_timer);
229
230 if (f->tier_set_timer)
231 thread_cancel(f->tier_set_timer);
232
233 isis_spftree_del(f->spftree);
234 neighbor_lists_clear(f);
235 skiplist_free(f->neighbors);
236 hash_free(f->neighbors_neighbors);
237 }
238
239 static int fabricd_initial_sync_timeout(struct thread *thread)
240 {
241 struct fabricd *f = THREAD_ARG(thread);
242
243 zlog_info("OpenFabric: Initial synchronization on %s timed out!",
244 f->initial_sync_circuit->interface->name);
245 f->initial_sync_state = FABRICD_SYNC_PENDING;
246 f->initial_sync_circuit = NULL;
247 f->initial_sync_timeout = NULL;
248 return 0;
249 }
250
251 void fabricd_initial_sync_hello(struct isis_circuit *circuit)
252 {
253 struct fabricd *f = circuit->area->fabricd;
254
255 if (!f)
256 return;
257
258 if (f->initial_sync_state > FABRICD_SYNC_PENDING)
259 return;
260
261 f->initial_sync_state = FABRICD_SYNC_STARTED;
262
263 long timeout = 2 * circuit->hello_interval[1] * circuit->hello_multiplier[1];
264
265 f->initial_sync_circuit = circuit;
266 if (f->initial_sync_timeout)
267 return;
268
269 thread_add_timer(master, fabricd_initial_sync_timeout, f,
270 timeout, &f->initial_sync_timeout);
271 f->initial_sync_start = monotime(NULL);
272
273 zlog_info("OpenFabric: Started initial synchronization with %s on %s",
274 sysid_print(circuit->u.p2p.neighbor->sysid),
275 circuit->interface->name);
276 }
277
278 bool fabricd_initial_sync_is_in_progress(struct isis_area *area)
279 {
280 struct fabricd *f = area->fabricd;
281
282 if (!f)
283 return false;
284
285 if (f->initial_sync_state > FABRICD_SYNC_PENDING
286 && f->initial_sync_state < FABRICD_SYNC_COMPLETE)
287 return true;
288
289 return false;
290 }
291
292 bool fabricd_initial_sync_is_complete(struct isis_area *area)
293 {
294 struct fabricd *f = area->fabricd;
295
296 if (!f)
297 return false;
298
299 return f->initial_sync_state == FABRICD_SYNC_COMPLETE;
300 }
301
302 struct isis_circuit *fabricd_initial_sync_circuit(struct isis_area *area)
303 {
304 struct fabricd *f = area->fabricd;
305 if (!f)
306 return NULL;
307
308 return f->initial_sync_circuit;
309 }
310
311 void fabricd_initial_sync_finish(struct isis_area *area)
312 {
313 struct fabricd *f = area->fabricd;
314
315 if (!f)
316 return;
317
318 if (monotime(NULL) - f->initial_sync_start < 5)
319 return;
320
321 zlog_info("OpenFabric: Initial synchronization on %s complete.",
322 f->initial_sync_circuit->interface->name);
323 f->initial_sync_state = FABRICD_SYNC_COMPLETE;
324 f->initial_sync_circuit = NULL;
325 thread_cancel(f->initial_sync_timeout);
326 f->initial_sync_timeout = NULL;
327 }
328
329 static void fabricd_bump_tier_calculation_timer(struct fabricd *f);
330 static void fabricd_set_tier(struct fabricd *f, uint8_t tier);
331
332 static uint8_t fabricd_calculate_fabric_tier(struct isis_area *area)
333 {
334 struct isis_spftree *local_tree = fabricd_spftree(area);
335 struct listnode *node;
336
337 struct isis_vertex *furthest_t0 = NULL,
338 *second_furthest_t0 = NULL;
339
340 struct isis_vertex *v;
341
342 for (ALL_QUEUE_ELEMENTS_RO(&local_tree->paths, node, v)) {
343 struct isis_lsp *lsp = lsp_for_vertex(local_tree, v);
344
345 if (!lsp || !lsp->tlvs
346 || !lsp->tlvs->spine_leaf
347 || !lsp->tlvs->spine_leaf->has_tier
348 || lsp->tlvs->spine_leaf->tier != 0)
349 continue;
350
351 second_furthest_t0 = furthest_t0;
352 furthest_t0 = v;
353 }
354
355 if (!second_furthest_t0) {
356 zlog_info("OpenFabric: Could not find two T0 routers");
357 return ISIS_TIER_UNDEFINED;
358 }
359
360 zlog_info("OpenFabric: Found %s as furthest t0 from local system, dist == %"
361 PRIu32, rawlspid_print(furthest_t0->N.id), furthest_t0->d_N);
362
363 struct isis_spftree *remote_tree =
364 isis_run_hopcount_spf(area, furthest_t0->N.id, NULL);
365
366 struct isis_vertex *furthest_from_remote =
367 isis_vertex_queue_last(&remote_tree->paths);
368
369 if (!furthest_from_remote) {
370 zlog_info("OpenFabric: Found no furthest node in remote spf");
371 isis_spftree_del(remote_tree);
372 return ISIS_TIER_UNDEFINED;
373 } else {
374 zlog_info("OpenFabric: Found %s as furthest from remote dist == %"
375 PRIu32, rawlspid_print(furthest_from_remote->N.id),
376 furthest_from_remote->d_N);
377 }
378
379 int64_t tier = furthest_from_remote->d_N - furthest_t0->d_N;
380 isis_spftree_del(remote_tree);
381
382 if (tier < 0 || tier >= ISIS_TIER_UNDEFINED) {
383 zlog_info("OpenFabric: Calculated tier %" PRId64 " seems implausible",
384 tier);
385 return ISIS_TIER_UNDEFINED;
386 }
387
388 zlog_info("OpenFabric: Calculated %" PRId64 " as tier", tier);
389 return tier;
390 }
391
392 static int fabricd_tier_set_timer(struct thread *thread)
393 {
394 struct fabricd *f = THREAD_ARG(thread);
395 f->tier_set_timer = NULL;
396
397 fabricd_set_tier(f, f->tier_pending);
398 return 0;
399 }
400
401 static int fabricd_tier_calculation_cb(struct thread *thread)
402 {
403 struct fabricd *f = THREAD_ARG(thread);
404 uint8_t tier = ISIS_TIER_UNDEFINED;
405 f->tier_calculation_timer = NULL;
406
407 tier = fabricd_calculate_fabric_tier(f->area);
408 if (tier == ISIS_TIER_UNDEFINED)
409 return 0;
410
411 zlog_info("OpenFabric: Got tier %" PRIu8 " from algorithm. Arming timer.",
412 tier);
413 f->tier_pending = tier;
414 thread_add_timer(master, fabricd_tier_set_timer, f,
415 f->area->lsp_gen_interval[ISIS_LEVEL2 - 1],
416 &f->tier_set_timer);
417
418 return 0;
419 }
420
421 static void fabricd_bump_tier_calculation_timer(struct fabricd *f)
422 {
423 /* Cancel timer if we already know our tier */
424 if (f->tier != ISIS_TIER_UNDEFINED
425 || f->tier_set_timer) {
426 if (f->tier_calculation_timer) {
427 thread_cancel(f->tier_calculation_timer);
428 f->tier_calculation_timer = NULL;
429 }
430 return;
431 }
432
433 /* If we need to calculate the tier, wait some
434 * time for the topology to settle before running
435 * the calculation */
436 if (f->tier_calculation_timer) {
437 thread_cancel(f->tier_calculation_timer);
438 f->tier_calculation_timer = NULL;
439 }
440
441 thread_add_timer(master, fabricd_tier_calculation_cb, f,
442 2 * f->area->lsp_gen_interval[ISIS_LEVEL2 - 1],
443 &f->tier_calculation_timer);
444 }
445
446 static void fabricd_set_tier(struct fabricd *f, uint8_t tier)
447 {
448 if (f->tier == tier)
449 return;
450
451 zlog_info("OpenFabric: Set own tier to %" PRIu8, tier);
452 f->tier = tier;
453
454 fabricd_bump_tier_calculation_timer(f);
455 lsp_regenerate_schedule(f->area, ISIS_LEVEL2, 0);
456 }
457
458 void fabricd_run_spf(struct isis_area *area)
459 {
460 struct fabricd *f = area->fabricd;
461
462 if (!f)
463 return;
464
465 isis_run_hopcount_spf(area, isis->sysid, f->spftree);
466 neighbor_lists_update(f);
467 fabricd_bump_tier_calculation_timer(f);
468 }
469
470 struct isis_spftree *fabricd_spftree(struct isis_area *area)
471 {
472 struct fabricd *f = area->fabricd;
473
474 if (!f)
475 return NULL;
476
477 return f->spftree;
478 }
479
480 void fabricd_configure_tier(struct isis_area *area, uint8_t tier)
481 {
482 struct fabricd *f = area->fabricd;
483
484 if (!f || f->tier_config == tier)
485 return;
486
487 f->tier_config = tier;
488 fabricd_set_tier(f, tier);
489 }
490
491 uint8_t fabricd_tier(struct isis_area *area)
492 {
493 struct fabricd *f = area->fabricd;
494
495 if (!f)
496 return ISIS_TIER_UNDEFINED;
497
498 return f->tier;
499 }
500
501 int fabricd_write_settings(struct isis_area *area, struct vty *vty)
502 {
503 struct fabricd *f = area->fabricd;
504 int written = 0;
505
506 if (!f)
507 return written;
508
509 if (f->tier_config != ISIS_TIER_UNDEFINED) {
510 vty_out(vty, " fabric-tier %" PRIu8 "\n", f->tier_config);
511 written++;
512 }
513
514 if (f->csnp_delay != FABRICD_DEFAULT_CSNP_DELAY
515 || f->always_send_csnp) {
516 vty_out(vty, " triggered-csnp-delay %d%s\n", f->csnp_delay,
517 f->always_send_csnp ? " always" : "");
518 }
519
520 return written;
521 }
522
523 static void move_to_queue(struct isis_lsp *lsp, struct neighbor_entry *n,
524 enum isis_tx_type type, struct isis_circuit *circuit)
525 {
526 n->present = false;
527
528 if (n->adj && n->adj->circuit == circuit)
529 return;
530
531 if (isis->debugs & DEBUG_FLOODING) {
532 zlog_debug("OpenFabric: Adding %s to %s",
533 print_sys_hostname(n->id),
534 (type == TX_LSP_NORMAL) ? "RF" : "DNR");
535 }
536
537 if (n->adj)
538 isis_tx_queue_add(n->adj->circuit->tx_queue, lsp, type);
539
540 uint8_t *neighbor_id = XMALLOC(MTYPE_FABRICD_FLOODING_INFO, sizeof(n->id));
541
542 memcpy(neighbor_id, n->id, sizeof(n->id));
543 listnode_add(lsp->flooding_neighbors[type], neighbor_id);
544 }
545
546 static void mark_neighbor_as_present(struct hash_backet *backet, void *arg)
547 {
548 struct neighbor_entry *n = backet->data;
549
550 n->present = true;
551 }
552
553 static void handle_firsthops(struct hash_backet *backet, void *arg)
554 {
555 struct isis_lsp *lsp = arg;
556 struct fabricd *f = lsp->area->fabricd;
557 struct isis_vertex *vertex = backet->data;
558
559 struct neighbor_entry *n;
560
561 n = neighbor_entry_lookup_list(f->neighbors, vertex->N.id);
562 if (n) {
563 if (isis->debugs & DEBUG_FLOODING) {
564 zlog_debug("Removing %s from NL as its in the reverse path",
565 print_sys_hostname(n->id));
566 }
567 n->present = false;
568 }
569
570 n = neighbor_entry_lookup_hash(f->neighbors_neighbors, vertex->N.id);
571 if (n) {
572 if (isis->debugs & DEBUG_FLOODING) {
573 zlog_debug("Removing %s from NN as its in the reverse path",
574 print_sys_hostname(n->id));
575 }
576 n->present = false;
577 }
578 }
579
580 static struct isis_lsp *lsp_for_neighbor(struct fabricd *f,
581 struct neighbor_entry *n)
582 {
583 uint8_t id[ISIS_SYS_ID_LEN + 1] = {0};
584
585 memcpy(id, n->id, sizeof(n->id));
586
587 struct isis_vertex vertex = {0};
588
589 isis_vertex_id_init(&vertex, id, VTYPE_NONPSEUDO_TE_IS);
590
591 return lsp_for_vertex(f->spftree, &vertex);
592 }
593
594 static void fabricd_free_lsp_flooding_info(void *val)
595 {
596 XFREE(MTYPE_FABRICD_FLOODING_INFO, val);
597 }
598
599 static void fabricd_lsp_reset_flooding_info(struct isis_lsp *lsp,
600 struct isis_circuit *circuit)
601 {
602 lsp->flooding_time = time(NULL);
603
604 XFREE(MTYPE_FABRICD_FLOODING_INFO, lsp->flooding_interface);
605 for (enum isis_tx_type type = TX_LSP_NORMAL;
606 type <= TX_LSP_CIRCUIT_SCOPED; type++) {
607 if (lsp->flooding_neighbors[type]) {
608 list_delete_all_node(lsp->flooding_neighbors[type]);
609 continue;
610 }
611
612 lsp->flooding_neighbors[type] = list_new();
613 lsp->flooding_neighbors[type]->del = fabricd_free_lsp_flooding_info;
614 }
615
616 if (circuit) {
617 lsp->flooding_interface = XSTRDUP(MTYPE_FABRICD_FLOODING_INFO,
618 circuit->interface->name);
619 }
620
621 lsp->flooding_circuit_scoped = false;
622 }
623
624 void fabricd_lsp_flood(struct isis_lsp *lsp, struct isis_circuit *circuit)
625 {
626 struct fabricd *f = lsp->area->fabricd;
627 assert(f);
628
629 fabricd_lsp_reset_flooding_info(lsp, circuit);
630
631 void *cursor = NULL;
632 struct neighbor_entry *n;
633
634 /* Mark all elements in NL as present and move T0s into DNR */
635 while (!skiplist_next(f->neighbors, NULL, (void **)&n, &cursor)) {
636 n->present = true;
637
638 struct isis_lsp *node_lsp = lsp_for_neighbor(f, n);
639 if (!node_lsp
640 || !node_lsp->tlvs
641 || !node_lsp->tlvs->spine_leaf
642 || !node_lsp->tlvs->spine_leaf->has_tier
643 || node_lsp->tlvs->spine_leaf->tier != 0) {
644 continue;
645 }
646
647 if (isis->debugs & DEBUG_FLOODING) {
648 zlog_debug("Moving %s to DNR because it's T0",
649 rawlspid_print(node_lsp->hdr.lsp_id));
650 }
651
652 move_to_queue(lsp, n, TX_LSP_CIRCUIT_SCOPED, circuit);
653 }
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 = isis_find_vertex(&f->spftree->paths,
659 lsp->hdr.lsp_id,
660 VTYPE_NONPSEUDO_TE_IS);
661
662 /* Remove all IS from NL and NN in the shortest path
663 * to the IS that originated the LSP */
664 if (originator)
665 hash_iterate(originator->firsthops, handle_firsthops, lsp);
666
667 /* Iterate over all remaining IS in NL */
668 cursor = NULL;
669 while (!skiplist_next(f->neighbors, NULL, (void **)&n, &cursor)) {
670 if (!n->present)
671 continue;
672
673 struct isis_lsp *nlsp = lsp_for_neighbor(f, n);
674 if (!nlsp || !nlsp->tlvs) {
675 if (isis->debugs & DEBUG_FLOODING) {
676 zlog_debug("Moving %s to DNR as it has no LSP",
677 print_sys_hostname(n->id));
678 }
679
680 move_to_queue(lsp, n, TX_LSP_CIRCUIT_SCOPED, circuit);
681 continue;
682 }
683
684 if (isis->debugs & DEBUG_FLOODING) {
685 zlog_debug("Considering %s from NL...",
686 print_sys_hostname(n->id));
687 }
688
689 /* For all neighbors of the NL IS check whether they are present
690 * in NN. If yes, remove from NN and set need_reflood. */
691 bool need_reflood = false;
692 struct isis_extended_reach *er;
693 for (er = (struct isis_extended_reach *)nlsp->tlvs->extended_reach.head;
694 er; er = er->next) {
695 struct neighbor_entry *nn;
696
697 nn = neighbor_entry_lookup_hash(f->neighbors_neighbors,
698 er->id);
699
700 if (nn) {
701 if (isis->debugs & DEBUG_FLOODING) {
702 zlog_debug("Found neighbor %s in NN, removing it from NN and setting reflood.",
703 print_sys_hostname(nn->id));
704 }
705
706 nn->present = false;
707 need_reflood = true;
708 }
709 }
710
711 move_to_queue(lsp, n, need_reflood ?
712 TX_LSP_NORMAL : TX_LSP_CIRCUIT_SCOPED,
713 circuit);
714 }
715
716 if (isis->debugs & DEBUG_FLOODING) {
717 zlog_debug("OpenFabric: Flooding algorithm complete.");
718 }
719 }
720
721 void fabricd_trigger_csnp(struct isis_area *area, bool circuit_scoped)
722 {
723 struct fabricd *f = area->fabricd;
724
725 if (!f)
726 return;
727
728 if (!circuit_scoped && !f->always_send_csnp)
729 return;
730
731 struct listnode *node;
732 struct isis_circuit *circuit;
733
734 for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) {
735 if (!circuit->t_send_csnp[1])
736 continue;
737
738 thread_cancel(circuit->t_send_csnp[ISIS_LEVEL2 - 1]);
739 thread_add_timer_msec(master, send_l2_csnp, circuit,
740 isis_jitter(f->csnp_delay, CSNP_JITTER),
741 &circuit->t_send_csnp[ISIS_LEVEL2 - 1]);
742 }
743 }
744
745 struct list *fabricd_ip_addrs(struct isis_circuit *circuit)
746 {
747 if (circuit->ip_addrs && listcount(circuit->ip_addrs))
748 return circuit->ip_addrs;
749
750 if (!fabricd || !circuit->area || !circuit->area->circuit_list)
751 return NULL;
752
753 struct listnode *node;
754 struct isis_circuit *c;
755
756 for (ALL_LIST_ELEMENTS_RO(circuit->area->circuit_list, node, c)) {
757 if (c->circ_type != CIRCUIT_T_LOOPBACK)
758 continue;
759
760 if (!c->ip_addrs || !listcount(c->ip_addrs))
761 return NULL;
762
763 return c->ip_addrs;
764 }
765
766 return NULL;
767 }
768
769 void fabricd_lsp_free(struct isis_lsp *lsp)
770 {
771 XFREE(MTYPE_FABRICD_FLOODING_INFO, lsp->flooding_interface);
772 for (enum isis_tx_type type = TX_LSP_NORMAL;
773 type <= TX_LSP_CIRCUIT_SCOPED; type++) {
774 if (!lsp->flooding_neighbors[type])
775 continue;
776
777 list_delete(&lsp->flooding_neighbors[type]);
778 }
779 }
780
781 void fabricd_update_lsp_no_flood(struct isis_lsp *lsp,
782 struct isis_circuit *circuit)
783 {
784 if (!fabricd)
785 return;
786
787 fabricd_lsp_reset_flooding_info(lsp, circuit);
788 lsp->flooding_circuit_scoped = true;
789 }
790
791 void fabricd_configure_triggered_csnp(struct isis_area *area, int delay,
792 bool always_send_csnp)
793 {
794 struct fabricd *f = area->fabricd;
795
796 if (!f)
797 return;
798
799 f->csnp_delay = delay;
800 f->always_send_csnp = always_send_csnp;
801 }