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