]>
Commit | Line | Data |
---|---|---|
acddc0ed | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
8e6fb83b CF |
2 | /* |
3 | * IS-IS Rout(e)ing protocol - OpenFabric extensions | |
4 | * | |
5 | * Copyright (C) 2018 Christian Franke | |
6 | * | |
8678d638 | 7 | * This file is part of FRRouting (FRR) |
8e6fb83b CF |
8 | */ |
9 | #include <zebra.h> | |
10 | #include "isisd/fabricd.h" | |
11 | #include "isisd/isisd.h" | |
8e6fb83b CF |
12 | #include "isisd/isis_circuit.h" |
13 | #include "isisd/isis_misc.h" | |
14 | #include "isisd/isis_adjacency.h" | |
b30e837b | 15 | #include "isisd/isis_spf.h" |
92ed0cde CF |
16 | #include "isisd/isis_tlvs.h" |
17 | #include "isisd/isis_lsp.h" | |
1f5be499 | 18 | #include "isisd/isis_spf_private.h" |
9b39405f | 19 | #include "isisd/isis_tx_queue.h" |
dc0dacfc | 20 | #include "isisd/isis_csm.h" |
8e6fb83b | 21 | |
bf8d3d6a DL |
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"); | |
8e6fb83b CF |
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 { | |
92ed0cde CF |
38 | struct isis_area *area; |
39 | ||
8e6fb83b CF |
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; | |
b30e837b CF |
44 | |
45 | struct isis_spftree *spftree; | |
1f5be499 CF |
46 | struct skiplist *neighbors; |
47 | struct hash *neighbors_neighbors; | |
92ed0cde CF |
48 | |
49 | uint8_t tier; | |
50 | uint8_t tier_config; | |
75e0ec94 CF |
51 | uint8_t tier_pending; |
52 | struct thread *tier_calculation_timer; | |
53 | struct thread *tier_set_timer; | |
e923107c CF |
54 | |
55 | int csnp_delay; | |
56 | bool always_send_csnp; | |
8e6fb83b CF |
57 | }; |
58 | ||
1f5be499 CF |
59 | /* Code related to maintaining the neighbor lists */ |
60 | ||
61 | struct neighbor_entry { | |
dc0dacfc CF |
62 | uint8_t id[ISIS_SYS_ID_LEN]; |
63 | struct isis_adjacency *adj; | |
1f5be499 CF |
64 | bool present; |
65 | }; | |
66 | ||
dc0dacfc CF |
67 | static struct neighbor_entry *neighbor_entry_new(const uint8_t *id, |
68 | struct isis_adjacency *adj) | |
1f5be499 | 69 | { |
89cdc4df RM |
70 | struct neighbor_entry *rv = XMALLOC(MTYPE_FABRICD_NEIGHBOR, |
71 | sizeof(*rv)); | |
1f5be499 | 72 | |
dc0dacfc CF |
73 | memcpy(rv->id, id, sizeof(rv->id)); |
74 | rv->adj = adj; | |
75 | ||
1f5be499 CF |
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 | ||
d8b87afe | 97 | static unsigned neighbor_entry_hash_key(const void *np) |
1f5be499 | 98 | { |
d8b87afe | 99 | const struct neighbor_entry *n = np; |
1f5be499 | 100 | |
dc0dacfc | 101 | return jhash(n->id, sizeof(n->id), 0x55aa5a5a); |
1f5be499 CF |
102 | } |
103 | ||
74df8d6d | 104 | static bool neighbor_entry_hash_cmp(const void *a, const void *b) |
1f5be499 CF |
105 | { |
106 | const struct neighbor_entry *na = a, *nb = b; | |
107 | ||
dc0dacfc | 108 | return memcmp(na->id, nb->id, sizeof(na->id)) == 0; |
1f5be499 CF |
109 | } |
110 | ||
1a4189d4 | 111 | static int neighbor_entry_list_cmp(const void *a, const void *b) |
1f5be499 | 112 | { |
1a4189d4 | 113 | const struct neighbor_entry *na = a, *nb = b; |
1f5be499 | 114 | |
dc0dacfc | 115 | return -memcmp(na->id, nb->id, sizeof(na->id)); |
1f5be499 CF |
116 | } |
117 | ||
9d224819 CF |
118 | static struct neighbor_entry *neighbor_entry_lookup_list(struct skiplist *list, |
119 | const uint8_t *id) | |
120 | { | |
89cdc4df | 121 | struct neighbor_entry n = { {0} }; |
9d224819 | 122 | |
dc0dacfc | 123 | memcpy(n.id, id, sizeof(n.id)); |
9d224819 CF |
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 | { | |
dc0dacfc | 139 | struct neighbor_entry n = {{0}}; |
9d224819 | 140 | |
dc0dacfc | 141 | memcpy(n.id, id, sizeof(n.id)); |
9d224819 CF |
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 | ||
2cd971af | 151 | static int fabricd_handle_adj_state_change(struct isis_adjacency *arg) |
1f5be499 | 152 | { |
2cd971af CF |
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); | |
1f5be499 CF |
160 | |
161 | struct listnode *node; | |
dc0dacfc CF |
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 | ||
2cd971af CF |
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; | |
1f5be499 CF |
186 | struct isis_vertex *v; |
187 | ||
188 | for (ALL_QUEUE_ELEMENTS_RO(&f->spftree->paths, node, v)) { | |
dc0dacfc | 189 | if (v->d_N < 2 || !VTYPE_IS(v->type)) |
1f5be499 CF |
190 | continue; |
191 | ||
192 | if (v->d_N > 2) | |
193 | break; | |
194 | ||
dc0dacfc CF |
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); | |
1f5be499 CF |
200 | } |
201 | } | |
202 | ||
b30e837b | 203 | struct fabricd *fabricd_new(struct isis_area *area) |
8e6fb83b CF |
204 | { |
205 | struct fabricd *rv = XCALLOC(MTYPE_FABRICD_STATE, sizeof(*rv)); | |
206 | ||
92ed0cde | 207 | rv->area = area; |
8e6fb83b | 208 | rv->initial_sync_state = FABRICD_SYNC_PENDING; |
1f5be499 | 209 | |
75aa7aa1 RW |
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); | |
1f5be499 CF |
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 | ||
92ed0cde | 220 | rv->tier = rv->tier_config = ISIS_TIER_UNDEFINED; |
e923107c CF |
221 | |
222 | rv->csnp_delay = FABRICD_DEFAULT_CSNP_DELAY; | |
8e6fb83b CF |
223 | return rv; |
224 | }; | |
225 | ||
b30e837b CF |
226 | void fabricd_finish(struct fabricd *f) |
227 | { | |
fa935aa7 | 228 | THREAD_OFF(f->initial_sync_timeout); |
b30e837b | 229 | |
fa935aa7 | 230 | THREAD_OFF(f->tier_calculation_timer); |
75e0ec94 | 231 | |
fa935aa7 | 232 | THREAD_OFF(f->tier_set_timer); |
75e0ec94 | 233 | |
b30e837b | 234 | isis_spftree_del(f->spftree); |
1f5be499 CF |
235 | neighbor_lists_clear(f); |
236 | skiplist_free(f->neighbors); | |
237 | hash_free(f->neighbors_neighbors); | |
b30e837b CF |
238 | } |
239 | ||
cc9f21da | 240 | static void fabricd_initial_sync_timeout(struct thread *thread) |
8e6fb83b CF |
241 | { |
242 | struct fabricd *f = THREAD_ARG(thread); | |
243 | ||
15d133c9 DS |
244 | if (IS_DEBUG_ADJ_PACKETS) |
245 | zlog_debug( | |
246 | "OpenFabric: Initial synchronization on %s timed out!", | |
247 | f->initial_sync_circuit->interface->name); | |
8e6fb83b CF |
248 | f->initial_sync_state = FABRICD_SYNC_PENDING; |
249 | f->initial_sync_circuit = NULL; | |
8e6fb83b CF |
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 | ||
15d133c9 DS |
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); | |
8e6fb83b CF |
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 | ||
df0ba689 CF |
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 | ||
8e6fb83b CF |
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; | |
fa935aa7 | 328 | THREAD_OFF(f->initial_sync_timeout); |
8e6fb83b | 329 | } |
b30e837b | 330 | |
75e0ec94 CF |
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 | ||
6cde4b45 | 362 | zlog_info("OpenFabric: Found %s as furthest t0 from local system, dist == %u", rawlspid_print(furthest_t0->N.id), furthest_t0->d_N); |
75e0ec94 CF |
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 { | |
6cde4b45 | 375 | zlog_info("OpenFabric: Found %s as furthest from remote dist == %u", rawlspid_print(furthest_from_remote->N.id), |
75e0ec94 CF |
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 | ||
cc9f21da | 392 | static void fabricd_tier_set_timer(struct thread *thread) |
75e0ec94 CF |
393 | { |
394 | struct fabricd *f = THREAD_ARG(thread); | |
75e0ec94 CF |
395 | |
396 | fabricd_set_tier(f, f->tier_pending); | |
75e0ec94 CF |
397 | } |
398 | ||
cc9f21da | 399 | static void fabricd_tier_calculation_cb(struct thread *thread) |
75e0ec94 CF |
400 | { |
401 | struct fabricd *f = THREAD_ARG(thread); | |
402 | uint8_t tier = ISIS_TIER_UNDEFINED; | |
75e0ec94 CF |
403 | |
404 | tier = fabricd_calculate_fabric_tier(f->area); | |
405 | if (tier == ISIS_TIER_UNDEFINED) | |
cc9f21da | 406 | return; |
75e0ec94 | 407 | |
6cde4b45 | 408 | zlog_info("OpenFabric: Got tier %hhu from algorithm. Arming timer.", |
75e0ec94 CF |
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 | ||
75e0ec94 CF |
415 | } |
416 | ||
417 | static void fabricd_bump_tier_calculation_timer(struct fabricd *f) | |
418 | { | |
419 | /* Cancel timer if we already know our tier */ | |
b3d6bc6e | 420 | if (f->tier != ISIS_TIER_UNDEFINED || f->tier_set_timer) { |
fa935aa7 | 421 | THREAD_OFF(f->tier_calculation_timer); |
75e0ec94 CF |
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 */ | |
fa935aa7 | 428 | THREAD_OFF(f->tier_calculation_timer); |
75e0ec94 CF |
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 | ||
92ed0cde CF |
435 | static void fabricd_set_tier(struct fabricd *f, uint8_t tier) |
436 | { | |
437 | if (f->tier == tier) | |
438 | return; | |
439 | ||
6cde4b45 | 440 | zlog_info("OpenFabric: Set own tier to %hhu", tier); |
92ed0cde CF |
441 | f->tier = tier; |
442 | ||
75e0ec94 | 443 | fabricd_bump_tier_calculation_timer(f); |
92ed0cde CF |
444 | lsp_regenerate_schedule(f->area, ISIS_LEVEL2, 0); |
445 | } | |
446 | ||
b30e837b CF |
447 | void fabricd_run_spf(struct isis_area *area) |
448 | { | |
449 | struct fabricd *f = area->fabricd; | |
450 | ||
451 | if (!f) | |
452 | return; | |
453 | ||
eab88f36 | 454 | isis_run_hopcount_spf(area, area->isis->sysid, f->spftree); |
2cd971af | 455 | neighbors_neighbors_update(f); |
75e0ec94 | 456 | fabricd_bump_tier_calculation_timer(f); |
b30e837b CF |
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 | } | |
92ed0cde CF |
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) { | |
6cde4b45 | 499 | vty_out(vty, " fabric-tier %hhu\n", f->tier_config); |
92ed0cde CF |
500 | written++; |
501 | } | |
502 | ||
e923107c CF |
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 | ||
92ed0cde CF |
509 | return written; |
510 | } | |
9d224819 | 511 | |
dc0dacfc | 512 | static void move_to_queue(struct isis_lsp *lsp, struct neighbor_entry *n, |
1eb7c3a1 | 513 | enum isis_tx_type type, struct isis_circuit *circuit) |
9d224819 | 514 | { |
9d224819 | 515 | n->present = false; |
d4cff91a | 516 | |
1eb7c3a1 CF |
517 | if (n->adj && n->adj->circuit == circuit) |
518 | return; | |
519 | ||
e740f9c1 | 520 | if (IS_DEBUG_FLOODING) { |
dc0dacfc CF |
521 | zlog_debug("OpenFabric: Adding %s to %s", |
522 | print_sys_hostname(n->id), | |
523 | (type == TX_LSP_NORMAL) ? "RF" : "DNR"); | |
d4cff91a CF |
524 | } |
525 | ||
dc0dacfc CF |
526 | if (n->adj) |
527 | isis_tx_queue_add(n->adj->circuit->tx_queue, lsp, type); | |
1eb7c3a1 | 528 | |
89cdc4df RM |
529 | uint8_t *neighbor_id = XMALLOC(MTYPE_FABRICD_FLOODING_INFO, |
530 | sizeof(n->id)); | |
1eb7c3a1 CF |
531 | |
532 | memcpy(neighbor_id, n->id, sizeof(n->id)); | |
533 | listnode_add(lsp->flooding_neighbors[type], neighbor_id); | |
9d224819 CF |
534 | } |
535 | ||
e3b78da8 | 536 | static void mark_neighbor_as_present(struct hash_bucket *bucket, void *arg) |
9d224819 | 537 | { |
e3b78da8 | 538 | struct neighbor_entry *n = bucket->data; |
9d224819 CF |
539 | |
540 | n->present = true; | |
541 | } | |
542 | ||
e3b78da8 | 543 | static void handle_firsthops(struct hash_bucket *bucket, void *arg) |
9d224819 CF |
544 | { |
545 | struct isis_lsp *lsp = arg; | |
546 | struct fabricd *f = lsp->area->fabricd; | |
e3b78da8 | 547 | struct isis_vertex *vertex = bucket->data; |
9d224819 CF |
548 | |
549 | struct neighbor_entry *n; | |
550 | ||
551 | n = neighbor_entry_lookup_list(f->neighbors, vertex->N.id); | |
d4cff91a | 552 | if (n) { |
e740f9c1 | 553 | if (IS_DEBUG_FLOODING) { |
d4cff91a | 554 | zlog_debug("Removing %s from NL as its in the reverse path", |
dc0dacfc | 555 | print_sys_hostname(n->id)); |
d4cff91a | 556 | } |
9d224819 | 557 | n->present = false; |
d4cff91a | 558 | } |
9d224819 CF |
559 | |
560 | n = neighbor_entry_lookup_hash(f->neighbors_neighbors, vertex->N.id); | |
d4cff91a | 561 | if (n) { |
e740f9c1 | 562 | if (IS_DEBUG_FLOODING) { |
d4cff91a | 563 | zlog_debug("Removing %s from NN as its in the reverse path", |
dc0dacfc | 564 | print_sys_hostname(n->id)); |
d4cff91a | 565 | } |
9d224819 | 566 | n->present = false; |
d4cff91a | 567 | } |
9d224819 CF |
568 | } |
569 | ||
dc0dacfc CF |
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 | ||
1eb7c3a1 CF |
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 | { | |
a6b60da9 CF |
592 | lsp->flooding_time = time(NULL); |
593 | ||
1eb7c3a1 CF |
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(); | |
89cdc4df RM |
603 | lsp->flooding_neighbors[type]->del = |
604 | fabricd_free_lsp_flooding_info; | |
1eb7c3a1 CF |
605 | } |
606 | ||
607 | if (circuit) { | |
608 | lsp->flooding_interface = XSTRDUP(MTYPE_FABRICD_FLOODING_INFO, | |
609 | circuit->interface->name); | |
610 | } | |
a6b60da9 CF |
611 | |
612 | lsp->flooding_circuit_scoped = false; | |
1eb7c3a1 CF |
613 | } |
614 | ||
615 | void fabricd_lsp_flood(struct isis_lsp *lsp, struct isis_circuit *circuit) | |
9d224819 CF |
616 | { |
617 | struct fabricd *f = lsp->area->fabricd; | |
618 | assert(f); | |
619 | ||
1eb7c3a1 CF |
620 | fabricd_lsp_reset_flooding_info(lsp, circuit); |
621 | ||
9d224819 CF |
622 | void *cursor = NULL; |
623 | struct neighbor_entry *n; | |
624 | ||
e5b0aaf1 | 625 | /* Mark all elements in NL as present */ |
89cdc4df | 626 | while (!skiplist_next(f->neighbors, NULL, (void **)&n, &cursor)) |
9d224819 | 627 | n->present = true; |
9d224819 CF |
628 | |
629 | /* Mark all elements in NN as present */ | |
630 | hash_iterate(f->neighbors_neighbors, mark_neighbor_as_present, NULL); | |
631 | ||
89cdc4df RM |
632 | struct isis_vertex *originator = |
633 | isis_find_vertex(&f->spftree->paths, | |
634 | lsp->hdr.lsp_id, | |
635 | VTYPE_NONPSEUDO_TE_IS); | |
9d224819 CF |
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 | ||
dc0dacfc | 648 | struct isis_lsp *nlsp = lsp_for_neighbor(f, n); |
9d224819 | 649 | if (!nlsp || !nlsp->tlvs) { |
e740f9c1 | 650 | if (IS_DEBUG_FLOODING) { |
d4cff91a | 651 | zlog_debug("Moving %s to DNR as it has no LSP", |
dc0dacfc | 652 | print_sys_hostname(n->id)); |
d4cff91a CF |
653 | } |
654 | ||
1eb7c3a1 | 655 | move_to_queue(lsp, n, TX_LSP_CIRCUIT_SCOPED, circuit); |
9d224819 CF |
656 | continue; |
657 | } | |
658 | ||
e740f9c1 | 659 | if (IS_DEBUG_FLOODING) { |
d4cff91a | 660 | zlog_debug("Considering %s from NL...", |
dc0dacfc | 661 | print_sys_hostname(n->id)); |
d4cff91a CF |
662 | } |
663 | ||
9d224819 CF |
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) { | |
e740f9c1 | 676 | if (IS_DEBUG_FLOODING) { |
d4cff91a | 677 | zlog_debug("Found neighbor %s in NN, removing it from NN and setting reflood.", |
dc0dacfc | 678 | print_sys_hostname(nn->id)); |
d4cff91a CF |
679 | } |
680 | ||
9d224819 CF |
681 | nn->present = false; |
682 | need_reflood = true; | |
683 | } | |
684 | } | |
685 | ||
dc0dacfc | 686 | move_to_queue(lsp, n, need_reflood ? |
1eb7c3a1 CF |
687 | TX_LSP_NORMAL : TX_LSP_CIRCUIT_SCOPED, |
688 | circuit); | |
9d224819 | 689 | } |
d4cff91a | 690 | |
e740f9c1 | 691 | if (IS_DEBUG_FLOODING) { |
d4cff91a CF |
692 | zlog_debug("OpenFabric: Flooding algorithm complete."); |
693 | } | |
9d224819 | 694 | } |
df0ba689 | 695 | |
e923107c | 696 | void fabricd_trigger_csnp(struct isis_area *area, bool circuit_scoped) |
df0ba689 CF |
697 | { |
698 | struct fabricd *f = area->fabricd; | |
699 | ||
700 | if (!f) | |
701 | return; | |
702 | ||
e923107c CF |
703 | if (!circuit_scoped && !f->always_send_csnp) |
704 | return; | |
705 | ||
df0ba689 CF |
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 | ||
fa935aa7 | 713 | THREAD_OFF(circuit->t_send_csnp[ISIS_LEVEL2 - 1]); |
df0ba689 | 714 | thread_add_timer_msec(master, send_l2_csnp, circuit, |
e923107c | 715 | isis_jitter(f->csnp_delay, CSNP_JITTER), |
df0ba689 CF |
716 | &circuit->t_send_csnp[ISIS_LEVEL2 - 1]); |
717 | } | |
718 | } | |
41415888 CF |
719 | |
720 | struct list *fabricd_ip_addrs(struct isis_circuit *circuit) | |
721 | { | |
91a5bbc4 | 722 | if (listcount(circuit->ip_addrs)) |
41415888 CF |
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 | ||
91a5bbc4 | 735 | if (!listcount(c->ip_addrs)) |
41415888 CF |
736 | return NULL; |
737 | ||
738 | return c->ip_addrs; | |
739 | } | |
740 | ||
741 | return NULL; | |
742 | } | |
1eb7c3a1 CF |
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 | } | |
a6b60da9 CF |
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 | } | |
e923107c CF |
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 | } | |
2cd971af CF |
777 | |
778 | void fabricd_init(void) | |
779 | { | |
780 | hook_register(isis_adj_state_change_hook, | |
781 | fabricd_handle_adj_state_change); | |
782 | } |