]> git.proxmox.com Git - mirror_frr.git/blob - isisd/fabricd.c
Merge pull request #12013 from SaiGomathiN/11895
[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_OFF(f->initial_sync_timeout);
242
243 THREAD_OFF(f->tier_calculation_timer);
244
245 THREAD_OFF(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 if (IS_DEBUG_ADJ_PACKETS)
258 zlog_debug(
259 "OpenFabric: Initial synchronization on %s timed out!",
260 f->initial_sync_circuit->interface->name);
261 f->initial_sync_state = FABRICD_SYNC_PENDING;
262 f->initial_sync_circuit = NULL;
263 }
264
265 void fabricd_initial_sync_hello(struct isis_circuit *circuit)
266 {
267 struct fabricd *f = circuit->area->fabricd;
268
269 if (!f)
270 return;
271
272 if (f->initial_sync_state > FABRICD_SYNC_PENDING)
273 return;
274
275 f->initial_sync_state = FABRICD_SYNC_STARTED;
276
277 long timeout = 2 * circuit->hello_interval[1] * circuit->hello_multiplier[1];
278
279 f->initial_sync_circuit = circuit;
280 if (f->initial_sync_timeout)
281 return;
282
283 thread_add_timer(master, fabricd_initial_sync_timeout, f,
284 timeout, &f->initial_sync_timeout);
285 f->initial_sync_start = monotime(NULL);
286
287 if (IS_DEBUG_ADJ_PACKETS)
288 zlog_debug(
289 "OpenFabric: Started initial synchronization with %s on %s",
290 sysid_print(circuit->u.p2p.neighbor->sysid),
291 circuit->interface->name);
292 }
293
294 bool fabricd_initial_sync_is_in_progress(struct isis_area *area)
295 {
296 struct fabricd *f = area->fabricd;
297
298 if (!f)
299 return false;
300
301 if (f->initial_sync_state > FABRICD_SYNC_PENDING
302 && f->initial_sync_state < FABRICD_SYNC_COMPLETE)
303 return true;
304
305 return false;
306 }
307
308 bool fabricd_initial_sync_is_complete(struct isis_area *area)
309 {
310 struct fabricd *f = area->fabricd;
311
312 if (!f)
313 return false;
314
315 return f->initial_sync_state == FABRICD_SYNC_COMPLETE;
316 }
317
318 struct isis_circuit *fabricd_initial_sync_circuit(struct isis_area *area)
319 {
320 struct fabricd *f = area->fabricd;
321 if (!f)
322 return NULL;
323
324 return f->initial_sync_circuit;
325 }
326
327 void fabricd_initial_sync_finish(struct isis_area *area)
328 {
329 struct fabricd *f = area->fabricd;
330
331 if (!f)
332 return;
333
334 if (monotime(NULL) - f->initial_sync_start < 5)
335 return;
336
337 zlog_info("OpenFabric: Initial synchronization on %s complete.",
338 f->initial_sync_circuit->interface->name);
339 f->initial_sync_state = FABRICD_SYNC_COMPLETE;
340 f->initial_sync_circuit = NULL;
341 THREAD_OFF(f->initial_sync_timeout);
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 == %u", rawlspid_print(furthest_t0->N.id), furthest_t0->d_N);
376
377 struct isis_spftree *remote_tree =
378 isis_run_hopcount_spf(area, furthest_t0->N.id, NULL);
379
380 struct isis_vertex *furthest_from_remote =
381 isis_vertex_queue_last(&remote_tree->paths);
382
383 if (!furthest_from_remote) {
384 zlog_info("OpenFabric: Found no furthest node in remote spf");
385 isis_spftree_del(remote_tree);
386 return ISIS_TIER_UNDEFINED;
387 } else {
388 zlog_info("OpenFabric: Found %s as furthest from remote dist == %u", rawlspid_print(furthest_from_remote->N.id),
389 furthest_from_remote->d_N);
390 }
391
392 int64_t tier = furthest_from_remote->d_N - furthest_t0->d_N;
393 isis_spftree_del(remote_tree);
394
395 if (tier < 0 || tier >= ISIS_TIER_UNDEFINED) {
396 zlog_info("OpenFabric: Calculated tier %" PRId64 " seems implausible",
397 tier);
398 return ISIS_TIER_UNDEFINED;
399 }
400
401 zlog_info("OpenFabric: Calculated %" PRId64 " as tier", tier);
402 return tier;
403 }
404
405 static void fabricd_tier_set_timer(struct thread *thread)
406 {
407 struct fabricd *f = THREAD_ARG(thread);
408
409 fabricd_set_tier(f, f->tier_pending);
410 }
411
412 static void fabricd_tier_calculation_cb(struct thread *thread)
413 {
414 struct fabricd *f = THREAD_ARG(thread);
415 uint8_t tier = ISIS_TIER_UNDEFINED;
416
417 tier = fabricd_calculate_fabric_tier(f->area);
418 if (tier == ISIS_TIER_UNDEFINED)
419 return;
420
421 zlog_info("OpenFabric: Got tier %hhu from algorithm. Arming timer.",
422 tier);
423 f->tier_pending = tier;
424 thread_add_timer(master, fabricd_tier_set_timer, f,
425 f->area->lsp_gen_interval[ISIS_LEVEL2 - 1],
426 &f->tier_set_timer);
427
428 }
429
430 static void fabricd_bump_tier_calculation_timer(struct fabricd *f)
431 {
432 /* Cancel timer if we already know our tier */
433 if (f->tier != ISIS_TIER_UNDEFINED || f->tier_set_timer) {
434 THREAD_OFF(f->tier_calculation_timer);
435 return;
436 }
437
438 /* If we need to calculate the tier, wait some
439 * time for the topology to settle before running
440 * the calculation */
441 THREAD_OFF(f->tier_calculation_timer);
442
443 thread_add_timer(master, fabricd_tier_calculation_cb, f,
444 2 * f->area->lsp_gen_interval[ISIS_LEVEL2 - 1],
445 &f->tier_calculation_timer);
446 }
447
448 static void fabricd_set_tier(struct fabricd *f, uint8_t tier)
449 {
450 if (f->tier == tier)
451 return;
452
453 zlog_info("OpenFabric: Set own tier to %hhu", tier);
454 f->tier = tier;
455
456 fabricd_bump_tier_calculation_timer(f);
457 lsp_regenerate_schedule(f->area, ISIS_LEVEL2, 0);
458 }
459
460 void fabricd_run_spf(struct isis_area *area)
461 {
462 struct fabricd *f = area->fabricd;
463
464 if (!f)
465 return;
466
467 isis_run_hopcount_spf(area, area->isis->sysid, f->spftree);
468 neighbors_neighbors_update(f);
469 fabricd_bump_tier_calculation_timer(f);
470 }
471
472 struct isis_spftree *fabricd_spftree(struct isis_area *area)
473 {
474 struct fabricd *f = area->fabricd;
475
476 if (!f)
477 return NULL;
478
479 return f->spftree;
480 }
481
482 void fabricd_configure_tier(struct isis_area *area, uint8_t tier)
483 {
484 struct fabricd *f = area->fabricd;
485
486 if (!f || f->tier_config == tier)
487 return;
488
489 f->tier_config = tier;
490 fabricd_set_tier(f, tier);
491 }
492
493 uint8_t fabricd_tier(struct isis_area *area)
494 {
495 struct fabricd *f = area->fabricd;
496
497 if (!f)
498 return ISIS_TIER_UNDEFINED;
499
500 return f->tier;
501 }
502
503 int fabricd_write_settings(struct isis_area *area, struct vty *vty)
504 {
505 struct fabricd *f = area->fabricd;
506 int written = 0;
507
508 if (!f)
509 return written;
510
511 if (f->tier_config != ISIS_TIER_UNDEFINED) {
512 vty_out(vty, " fabric-tier %hhu\n", f->tier_config);
513 written++;
514 }
515
516 if (f->csnp_delay != FABRICD_DEFAULT_CSNP_DELAY
517 || f->always_send_csnp) {
518 vty_out(vty, " triggered-csnp-delay %d%s\n", f->csnp_delay,
519 f->always_send_csnp ? " always" : "");
520 }
521
522 return written;
523 }
524
525 static void move_to_queue(struct isis_lsp *lsp, struct neighbor_entry *n,
526 enum isis_tx_type type, struct isis_circuit *circuit)
527 {
528 n->present = false;
529
530 if (n->adj && n->adj->circuit == circuit)
531 return;
532
533 if (IS_DEBUG_FLOODING) {
534 zlog_debug("OpenFabric: Adding %s to %s",
535 print_sys_hostname(n->id),
536 (type == TX_LSP_NORMAL) ? "RF" : "DNR");
537 }
538
539 if (n->adj)
540 isis_tx_queue_add(n->adj->circuit->tx_queue, lsp, type);
541
542 uint8_t *neighbor_id = XMALLOC(MTYPE_FABRICD_FLOODING_INFO,
543 sizeof(n->id));
544
545 memcpy(neighbor_id, n->id, sizeof(n->id));
546 listnode_add(lsp->flooding_neighbors[type], neighbor_id);
547 }
548
549 static void mark_neighbor_as_present(struct hash_bucket *bucket, void *arg)
550 {
551 struct neighbor_entry *n = bucket->data;
552
553 n->present = true;
554 }
555
556 static void handle_firsthops(struct hash_bucket *bucket, void *arg)
557 {
558 struct isis_lsp *lsp = arg;
559 struct fabricd *f = lsp->area->fabricd;
560 struct isis_vertex *vertex = bucket->data;
561
562 struct neighbor_entry *n;
563
564 n = neighbor_entry_lookup_list(f->neighbors, vertex->N.id);
565 if (n) {
566 if (IS_DEBUG_FLOODING) {
567 zlog_debug("Removing %s from NL as its in the reverse path",
568 print_sys_hostname(n->id));
569 }
570 n->present = false;
571 }
572
573 n = neighbor_entry_lookup_hash(f->neighbors_neighbors, vertex->N.id);
574 if (n) {
575 if (IS_DEBUG_FLOODING) {
576 zlog_debug("Removing %s from NN as its in the reverse path",
577 print_sys_hostname(n->id));
578 }
579 n->present = false;
580 }
581 }
582
583 static struct isis_lsp *lsp_for_neighbor(struct fabricd *f,
584 struct neighbor_entry *n)
585 {
586 uint8_t id[ISIS_SYS_ID_LEN + 1] = {0};
587
588 memcpy(id, n->id, sizeof(n->id));
589
590 struct isis_vertex vertex = {0};
591
592 isis_vertex_id_init(&vertex, id, VTYPE_NONPSEUDO_TE_IS);
593
594 return lsp_for_vertex(f->spftree, &vertex);
595 }
596
597 static void fabricd_free_lsp_flooding_info(void *val)
598 {
599 XFREE(MTYPE_FABRICD_FLOODING_INFO, val);
600 }
601
602 static void fabricd_lsp_reset_flooding_info(struct isis_lsp *lsp,
603 struct isis_circuit *circuit)
604 {
605 lsp->flooding_time = time(NULL);
606
607 XFREE(MTYPE_FABRICD_FLOODING_INFO, lsp->flooding_interface);
608 for (enum isis_tx_type type = TX_LSP_NORMAL;
609 type <= TX_LSP_CIRCUIT_SCOPED; type++) {
610 if (lsp->flooding_neighbors[type]) {
611 list_delete_all_node(lsp->flooding_neighbors[type]);
612 continue;
613 }
614
615 lsp->flooding_neighbors[type] = list_new();
616 lsp->flooding_neighbors[type]->del =
617 fabricd_free_lsp_flooding_info;
618 }
619
620 if (circuit) {
621 lsp->flooding_interface = XSTRDUP(MTYPE_FABRICD_FLOODING_INFO,
622 circuit->interface->name);
623 }
624
625 lsp->flooding_circuit_scoped = false;
626 }
627
628 void fabricd_lsp_flood(struct isis_lsp *lsp, struct isis_circuit *circuit)
629 {
630 struct fabricd *f = lsp->area->fabricd;
631 assert(f);
632
633 fabricd_lsp_reset_flooding_info(lsp, circuit);
634
635 void *cursor = NULL;
636 struct neighbor_entry *n;
637
638 /* Mark all elements in NL as present */
639 while (!skiplist_next(f->neighbors, NULL, (void **)&n, &cursor))
640 n->present = true;
641
642 /* Mark all elements in NN as present */
643 hash_iterate(f->neighbors_neighbors, mark_neighbor_as_present, NULL);
644
645 struct isis_vertex *originator =
646 isis_find_vertex(&f->spftree->paths,
647 lsp->hdr.lsp_id,
648 VTYPE_NONPSEUDO_TE_IS);
649
650 /* Remove all IS from NL and NN in the shortest path
651 * to the IS that originated the LSP */
652 if (originator)
653 hash_iterate(originator->firsthops, handle_firsthops, lsp);
654
655 /* Iterate over all remaining IS in NL */
656 cursor = NULL;
657 while (!skiplist_next(f->neighbors, NULL, (void **)&n, &cursor)) {
658 if (!n->present)
659 continue;
660
661 struct isis_lsp *nlsp = lsp_for_neighbor(f, n);
662 if (!nlsp || !nlsp->tlvs) {
663 if (IS_DEBUG_FLOODING) {
664 zlog_debug("Moving %s to DNR as it has no LSP",
665 print_sys_hostname(n->id));
666 }
667
668 move_to_queue(lsp, n, TX_LSP_CIRCUIT_SCOPED, circuit);
669 continue;
670 }
671
672 if (IS_DEBUG_FLOODING) {
673 zlog_debug("Considering %s from NL...",
674 print_sys_hostname(n->id));
675 }
676
677 /* For all neighbors of the NL IS check whether they are present
678 * in NN. If yes, remove from NN and set need_reflood. */
679 bool need_reflood = false;
680 struct isis_extended_reach *er;
681 for (er = (struct isis_extended_reach *)nlsp->tlvs->extended_reach.head;
682 er; er = er->next) {
683 struct neighbor_entry *nn;
684
685 nn = neighbor_entry_lookup_hash(f->neighbors_neighbors,
686 er->id);
687
688 if (nn) {
689 if (IS_DEBUG_FLOODING) {
690 zlog_debug("Found neighbor %s in NN, removing it from NN and setting reflood.",
691 print_sys_hostname(nn->id));
692 }
693
694 nn->present = false;
695 need_reflood = true;
696 }
697 }
698
699 move_to_queue(lsp, n, need_reflood ?
700 TX_LSP_NORMAL : TX_LSP_CIRCUIT_SCOPED,
701 circuit);
702 }
703
704 if (IS_DEBUG_FLOODING) {
705 zlog_debug("OpenFabric: Flooding algorithm complete.");
706 }
707 }
708
709 void fabricd_trigger_csnp(struct isis_area *area, bool circuit_scoped)
710 {
711 struct fabricd *f = area->fabricd;
712
713 if (!f)
714 return;
715
716 if (!circuit_scoped && !f->always_send_csnp)
717 return;
718
719 struct listnode *node;
720 struct isis_circuit *circuit;
721
722 for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) {
723 if (!circuit->t_send_csnp[1])
724 continue;
725
726 THREAD_OFF(circuit->t_send_csnp[ISIS_LEVEL2 - 1]);
727 thread_add_timer_msec(master, send_l2_csnp, circuit,
728 isis_jitter(f->csnp_delay, CSNP_JITTER),
729 &circuit->t_send_csnp[ISIS_LEVEL2 - 1]);
730 }
731 }
732
733 struct list *fabricd_ip_addrs(struct isis_circuit *circuit)
734 {
735 if (listcount(circuit->ip_addrs))
736 return circuit->ip_addrs;
737
738 if (!fabricd || !circuit->area || !circuit->area->circuit_list)
739 return NULL;
740
741 struct listnode *node;
742 struct isis_circuit *c;
743
744 for (ALL_LIST_ELEMENTS_RO(circuit->area->circuit_list, node, c)) {
745 if (c->circ_type != CIRCUIT_T_LOOPBACK)
746 continue;
747
748 if (!listcount(c->ip_addrs))
749 return NULL;
750
751 return c->ip_addrs;
752 }
753
754 return NULL;
755 }
756
757 void fabricd_lsp_free(struct isis_lsp *lsp)
758 {
759 XFREE(MTYPE_FABRICD_FLOODING_INFO, lsp->flooding_interface);
760 for (enum isis_tx_type type = TX_LSP_NORMAL;
761 type <= TX_LSP_CIRCUIT_SCOPED; type++) {
762 if (!lsp->flooding_neighbors[type])
763 continue;
764
765 list_delete(&lsp->flooding_neighbors[type]);
766 }
767 }
768
769 void fabricd_update_lsp_no_flood(struct isis_lsp *lsp,
770 struct isis_circuit *circuit)
771 {
772 if (!fabricd)
773 return;
774
775 fabricd_lsp_reset_flooding_info(lsp, circuit);
776 lsp->flooding_circuit_scoped = true;
777 }
778
779 void fabricd_configure_triggered_csnp(struct isis_area *area, int delay,
780 bool always_send_csnp)
781 {
782 struct fabricd *f = area->fabricd;
783
784 if (!f)
785 return;
786
787 f->csnp_delay = delay;
788 f->always_send_csnp = always_send_csnp;
789 }
790
791 void fabricd_init(void)
792 {
793 hook_register(isis_adj_state_change_hook,
794 fabricd_handle_adj_state_change);
795 }