]>
Commit | Line | Data |
---|---|---|
8e6fb83b CF |
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" | |
b30e837b | 29 | #include "isisd/isis_spf.h" |
92ed0cde CF |
30 | #include "isisd/isis_tlvs.h" |
31 | #include "isisd/isis_lsp.h" | |
1f5be499 | 32 | #include "isisd/isis_spf_private.h" |
9b39405f | 33 | #include "isisd/isis_tx_queue.h" |
dc0dacfc | 34 | #include "isisd/isis_csm.h" |
8e6fb83b CF |
35 | |
36 | DEFINE_MTYPE_STATIC(ISISD, FABRICD_STATE, "ISIS OpenFabric") | |
1f5be499 | 37 | DEFINE_MTYPE_STATIC(ISISD, FABRICD_NEIGHBOR, "ISIS OpenFabric Neighbor Entry") |
1eb7c3a1 | 38 | DEFINE_MTYPE_STATIC(ISISD, FABRICD_FLOODING_INFO, "ISIS OpenFabric Flooding Log") |
8e6fb83b CF |
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 { | |
92ed0cde CF |
52 | struct isis_area *area; |
53 | ||
8e6fb83b CF |
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; | |
b30e837b CF |
58 | |
59 | struct isis_spftree *spftree; | |
1f5be499 CF |
60 | struct skiplist *neighbors; |
61 | struct hash *neighbors_neighbors; | |
92ed0cde CF |
62 | |
63 | uint8_t tier; | |
64 | uint8_t tier_config; | |
75e0ec94 CF |
65 | uint8_t tier_pending; |
66 | struct thread *tier_calculation_timer; | |
67 | struct thread *tier_set_timer; | |
e923107c CF |
68 | |
69 | int csnp_delay; | |
70 | bool always_send_csnp; | |
8e6fb83b CF |
71 | }; |
72 | ||
1f5be499 CF |
73 | /* Code related to maintaining the neighbor lists */ |
74 | ||
75 | struct neighbor_entry { | |
dc0dacfc CF |
76 | uint8_t id[ISIS_SYS_ID_LEN]; |
77 | struct isis_adjacency *adj; | |
1f5be499 CF |
78 | bool present; |
79 | }; | |
80 | ||
dc0dacfc CF |
81 | static struct neighbor_entry *neighbor_entry_new(const uint8_t *id, |
82 | struct isis_adjacency *adj) | |
1f5be499 | 83 | { |
89cdc4df RM |
84 | struct neighbor_entry *rv = XMALLOC(MTYPE_FABRICD_NEIGHBOR, |
85 | sizeof(*rv)); | |
1f5be499 | 86 | |
dc0dacfc CF |
87 | memcpy(rv->id, id, sizeof(rv->id)); |
88 | rv->adj = adj; | |
89 | ||
1f5be499 CF |
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 | ||
d8b87afe | 111 | static unsigned neighbor_entry_hash_key(const void *np) |
1f5be499 | 112 | { |
d8b87afe | 113 | const struct neighbor_entry *n = np; |
1f5be499 | 114 | |
dc0dacfc | 115 | return jhash(n->id, sizeof(n->id), 0x55aa5a5a); |
1f5be499 CF |
116 | } |
117 | ||
74df8d6d | 118 | static bool neighbor_entry_hash_cmp(const void *a, const void *b) |
1f5be499 CF |
119 | { |
120 | const struct neighbor_entry *na = a, *nb = b; | |
121 | ||
dc0dacfc | 122 | return memcmp(na->id, nb->id, sizeof(na->id)) == 0; |
1f5be499 CF |
123 | } |
124 | ||
125 | static int neighbor_entry_list_cmp(void *a, void *b) | |
126 | { | |
127 | struct neighbor_entry *na = a, *nb = b; | |
128 | ||
dc0dacfc | 129 | return -memcmp(na->id, nb->id, sizeof(na->id)); |
1f5be499 CF |
130 | } |
131 | ||
9d224819 CF |
132 | static struct neighbor_entry *neighbor_entry_lookup_list(struct skiplist *list, |
133 | const uint8_t *id) | |
134 | { | |
89cdc4df | 135 | struct neighbor_entry n = { {0} }; |
9d224819 | 136 | |
dc0dacfc | 137 | memcpy(n.id, id, sizeof(n.id)); |
9d224819 CF |
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 | { | |
dc0dacfc | 153 | struct neighbor_entry n = {{0}}; |
9d224819 | 154 | |
dc0dacfc | 155 | memcpy(n.id, id, sizeof(n.id)); |
9d224819 CF |
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 | ||
2cd971af | 165 | static int fabricd_handle_adj_state_change(struct isis_adjacency *arg) |
1f5be499 | 166 | { |
2cd971af CF |
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); | |
1f5be499 CF |
174 | |
175 | struct listnode *node; | |
dc0dacfc CF |
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 | ||
2cd971af CF |
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; | |
1f5be499 CF |
200 | struct isis_vertex *v; |
201 | ||
202 | for (ALL_QUEUE_ELEMENTS_RO(&f->spftree->paths, node, v)) { | |
dc0dacfc | 203 | if (v->d_N < 2 || !VTYPE_IS(v->type)) |
1f5be499 CF |
204 | continue; |
205 | ||
206 | if (v->d_N > 2) | |
207 | break; | |
208 | ||
dc0dacfc CF |
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); | |
1f5be499 CF |
214 | } |
215 | } | |
216 | ||
b30e837b | 217 | struct fabricd *fabricd_new(struct isis_area *area) |
8e6fb83b CF |
218 | { |
219 | struct fabricd *rv = XCALLOC(MTYPE_FABRICD_STATE, sizeof(*rv)); | |
220 | ||
92ed0cde | 221 | rv->area = area; |
8e6fb83b | 222 | rv->initial_sync_state = FABRICD_SYNC_PENDING; |
1f5be499 | 223 | |
b30e837b | 224 | rv->spftree = isis_spftree_new(area); |
1f5be499 CF |
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 | ||
92ed0cde | 231 | rv->tier = rv->tier_config = ISIS_TIER_UNDEFINED; |
e923107c CF |
232 | |
233 | rv->csnp_delay = FABRICD_DEFAULT_CSNP_DELAY; | |
8e6fb83b CF |
234 | return rv; |
235 | }; | |
236 | ||
b30e837b CF |
237 | void fabricd_finish(struct fabricd *f) |
238 | { | |
239 | if (f->initial_sync_timeout) | |
240 | thread_cancel(f->initial_sync_timeout); | |
241 | ||
75e0ec94 CF |
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 | ||
b30e837b | 248 | isis_spftree_del(f->spftree); |
1f5be499 CF |
249 | neighbor_lists_clear(f); |
250 | skiplist_free(f->neighbors); | |
251 | hash_free(f->neighbors_neighbors); | |
b30e837b CF |
252 | } |
253 | ||
8e6fb83b CF |
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 | ||
df0ba689 CF |
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 | ||
8e6fb83b CF |
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 | } | |
b30e837b | 343 | |
75e0ec94 CF |
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 | ||
92ed0cde CF |
461 | static void fabricd_set_tier(struct fabricd *f, uint8_t tier) |
462 | { | |
463 | if (f->tier == tier) | |
464 | return; | |
465 | ||
75e0ec94 | 466 | zlog_info("OpenFabric: Set own tier to %" PRIu8, tier); |
92ed0cde CF |
467 | f->tier = tier; |
468 | ||
75e0ec94 | 469 | fabricd_bump_tier_calculation_timer(f); |
92ed0cde CF |
470 | lsp_regenerate_schedule(f->area, ISIS_LEVEL2, 0); |
471 | } | |
472 | ||
b30e837b CF |
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); | |
2cd971af | 481 | neighbors_neighbors_update(f); |
75e0ec94 | 482 | fabricd_bump_tier_calculation_timer(f); |
b30e837b CF |
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 | } | |
92ed0cde CF |
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 | ||
e923107c CF |
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 | ||
92ed0cde CF |
535 | return written; |
536 | } | |
9d224819 | 537 | |
dc0dacfc | 538 | static void move_to_queue(struct isis_lsp *lsp, struct neighbor_entry *n, |
1eb7c3a1 | 539 | enum isis_tx_type type, struct isis_circuit *circuit) |
9d224819 | 540 | { |
9d224819 | 541 | n->present = false; |
d4cff91a | 542 | |
1eb7c3a1 CF |
543 | if (n->adj && n->adj->circuit == circuit) |
544 | return; | |
545 | ||
ddb33326 | 546 | if (isis->debugs & DEBUG_FLOODING) { |
dc0dacfc CF |
547 | zlog_debug("OpenFabric: Adding %s to %s", |
548 | print_sys_hostname(n->id), | |
549 | (type == TX_LSP_NORMAL) ? "RF" : "DNR"); | |
d4cff91a CF |
550 | } |
551 | ||
dc0dacfc CF |
552 | if (n->adj) |
553 | isis_tx_queue_add(n->adj->circuit->tx_queue, lsp, type); | |
1eb7c3a1 | 554 | |
89cdc4df RM |
555 | uint8_t *neighbor_id = XMALLOC(MTYPE_FABRICD_FLOODING_INFO, |
556 | sizeof(n->id)); | |
1eb7c3a1 CF |
557 | |
558 | memcpy(neighbor_id, n->id, sizeof(n->id)); | |
559 | listnode_add(lsp->flooding_neighbors[type], neighbor_id); | |
9d224819 CF |
560 | } |
561 | ||
e3b78da8 | 562 | static void mark_neighbor_as_present(struct hash_bucket *bucket, void *arg) |
9d224819 | 563 | { |
e3b78da8 | 564 | struct neighbor_entry *n = bucket->data; |
9d224819 CF |
565 | |
566 | n->present = true; | |
567 | } | |
568 | ||
e3b78da8 | 569 | static void handle_firsthops(struct hash_bucket *bucket, void *arg) |
9d224819 CF |
570 | { |
571 | struct isis_lsp *lsp = arg; | |
572 | struct fabricd *f = lsp->area->fabricd; | |
e3b78da8 | 573 | struct isis_vertex *vertex = bucket->data; |
9d224819 CF |
574 | |
575 | struct neighbor_entry *n; | |
576 | ||
577 | n = neighbor_entry_lookup_list(f->neighbors, vertex->N.id); | |
d4cff91a | 578 | if (n) { |
ddb33326 | 579 | if (isis->debugs & DEBUG_FLOODING) { |
d4cff91a | 580 | zlog_debug("Removing %s from NL as its in the reverse path", |
dc0dacfc | 581 | print_sys_hostname(n->id)); |
d4cff91a | 582 | } |
9d224819 | 583 | n->present = false; |
d4cff91a | 584 | } |
9d224819 CF |
585 | |
586 | n = neighbor_entry_lookup_hash(f->neighbors_neighbors, vertex->N.id); | |
d4cff91a | 587 | if (n) { |
ddb33326 | 588 | if (isis->debugs & DEBUG_FLOODING) { |
d4cff91a | 589 | zlog_debug("Removing %s from NN as its in the reverse path", |
dc0dacfc | 590 | print_sys_hostname(n->id)); |
d4cff91a | 591 | } |
9d224819 | 592 | n->present = false; |
d4cff91a | 593 | } |
9d224819 CF |
594 | } |
595 | ||
dc0dacfc CF |
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 | ||
1eb7c3a1 CF |
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 | { | |
a6b60da9 CF |
618 | lsp->flooding_time = time(NULL); |
619 | ||
1eb7c3a1 CF |
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(); | |
89cdc4df RM |
629 | lsp->flooding_neighbors[type]->del = |
630 | fabricd_free_lsp_flooding_info; | |
1eb7c3a1 CF |
631 | } |
632 | ||
633 | if (circuit) { | |
634 | lsp->flooding_interface = XSTRDUP(MTYPE_FABRICD_FLOODING_INFO, | |
635 | circuit->interface->name); | |
636 | } | |
a6b60da9 CF |
637 | |
638 | lsp->flooding_circuit_scoped = false; | |
1eb7c3a1 CF |
639 | } |
640 | ||
641 | void fabricd_lsp_flood(struct isis_lsp *lsp, struct isis_circuit *circuit) | |
9d224819 CF |
642 | { |
643 | struct fabricd *f = lsp->area->fabricd; | |
644 | assert(f); | |
645 | ||
1eb7c3a1 CF |
646 | fabricd_lsp_reset_flooding_info(lsp, circuit); |
647 | ||
9d224819 CF |
648 | void *cursor = NULL; |
649 | struct neighbor_entry *n; | |
650 | ||
e5b0aaf1 | 651 | /* Mark all elements in NL as present */ |
89cdc4df | 652 | while (!skiplist_next(f->neighbors, NULL, (void **)&n, &cursor)) |
9d224819 | 653 | n->present = true; |
9d224819 CF |
654 | |
655 | /* Mark all elements in NN as present */ | |
656 | hash_iterate(f->neighbors_neighbors, mark_neighbor_as_present, NULL); | |
657 | ||
89cdc4df RM |
658 | struct isis_vertex *originator = |
659 | isis_find_vertex(&f->spftree->paths, | |
660 | lsp->hdr.lsp_id, | |
661 | VTYPE_NONPSEUDO_TE_IS); | |
9d224819 CF |
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 | ||
dc0dacfc | 674 | struct isis_lsp *nlsp = lsp_for_neighbor(f, n); |
9d224819 | 675 | if (!nlsp || !nlsp->tlvs) { |
ddb33326 | 676 | if (isis->debugs & DEBUG_FLOODING) { |
d4cff91a | 677 | zlog_debug("Moving %s to DNR as it has no LSP", |
dc0dacfc | 678 | print_sys_hostname(n->id)); |
d4cff91a CF |
679 | } |
680 | ||
1eb7c3a1 | 681 | move_to_queue(lsp, n, TX_LSP_CIRCUIT_SCOPED, circuit); |
9d224819 CF |
682 | continue; |
683 | } | |
684 | ||
ddb33326 | 685 | if (isis->debugs & DEBUG_FLOODING) { |
d4cff91a | 686 | zlog_debug("Considering %s from NL...", |
dc0dacfc | 687 | print_sys_hostname(n->id)); |
d4cff91a CF |
688 | } |
689 | ||
9d224819 CF |
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) { | |
ddb33326 | 702 | if (isis->debugs & DEBUG_FLOODING) { |
d4cff91a | 703 | zlog_debug("Found neighbor %s in NN, removing it from NN and setting reflood.", |
dc0dacfc | 704 | print_sys_hostname(nn->id)); |
d4cff91a CF |
705 | } |
706 | ||
9d224819 CF |
707 | nn->present = false; |
708 | need_reflood = true; | |
709 | } | |
710 | } | |
711 | ||
dc0dacfc | 712 | move_to_queue(lsp, n, need_reflood ? |
1eb7c3a1 CF |
713 | TX_LSP_NORMAL : TX_LSP_CIRCUIT_SCOPED, |
714 | circuit); | |
9d224819 | 715 | } |
d4cff91a | 716 | |
ddb33326 | 717 | if (isis->debugs & DEBUG_FLOODING) { |
d4cff91a CF |
718 | zlog_debug("OpenFabric: Flooding algorithm complete."); |
719 | } | |
9d224819 | 720 | } |
df0ba689 | 721 | |
e923107c | 722 | void fabricd_trigger_csnp(struct isis_area *area, bool circuit_scoped) |
df0ba689 CF |
723 | { |
724 | struct fabricd *f = area->fabricd; | |
725 | ||
726 | if (!f) | |
727 | return; | |
728 | ||
e923107c CF |
729 | if (!circuit_scoped && !f->always_send_csnp) |
730 | return; | |
731 | ||
df0ba689 CF |
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, | |
e923107c | 741 | isis_jitter(f->csnp_delay, CSNP_JITTER), |
df0ba689 CF |
742 | &circuit->t_send_csnp[ISIS_LEVEL2 - 1]); |
743 | } | |
744 | } | |
41415888 CF |
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 | } | |
1eb7c3a1 CF |
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 | } | |
a6b60da9 CF |
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 | } | |
e923107c CF |
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 | } | |
2cd971af CF |
803 | |
804 | void fabricd_init(void) | |
805 | { | |
806 | hook_register(isis_adj_state_change_hook, | |
807 | fabricd_handle_adj_state_change); | |
808 | } |