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