]> git.proxmox.com Git - mirror_frr.git/blob - isisd/fabricd.c
lib: enforce vrf_name_to_id by returning default_vrf when name is null
[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,
85 sizeof(*rv));
86
87 memcpy(rv->id, id, sizeof(rv->id));
88 rv->adj = adj;
89
90 return rv;
91 }
92
93 static void neighbor_entry_del(struct neighbor_entry *neighbor)
94 {
95 XFREE(MTYPE_FABRICD_NEIGHBOR, neighbor);
96 }
97
98 static void neighbor_entry_del_void(void *arg)
99 {
100 neighbor_entry_del((struct neighbor_entry *)arg);
101 }
102
103 static 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
111 static 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
118 static 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
125 static 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
132 static 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
150 static 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
165 static 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
195 static 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
217 struct 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
237 void 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
254 static 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
266 void 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
293 bool 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
307 bool 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
317 struct 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
326 void 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
344 static void fabricd_bump_tier_calculation_timer(struct fabricd *f);
345 static void fabricd_set_tier(struct fabricd *f, uint8_t tier);
346
347 static 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
407 static 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
416 static 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
436 static 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
461 static 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
473 void 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
485 struct 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
495 void 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
506 uint8_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
516 int 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
538 static 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
562 static 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
569 static 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
596 static 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
610 static void fabricd_free_lsp_flooding_info(void *val)
611 {
612 XFREE(MTYPE_FABRICD_FLOODING_INFO, val);
613 }
614
615 static 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
641 void 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
722 void 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
746 struct 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
770 void 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
782 void 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
792 void 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
804 void fabricd_init(void)
805 {
806 hook_register(isis_adj_state_change_hook,
807 fabricd_handle_adj_state_change);
808 }