]>
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; | |
e6685141 | 43 | struct event *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 | 51 | uint8_t tier_pending; |
e6685141 DS |
52 | struct event *tier_calculation_timer; |
53 | struct event *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 | |
329f87b3 HS |
210 | rv->spftree = isis_spftree_new( |
211 | area, &area->lspdb[IS_LEVEL_2 - 1], area->isis->sysid, | |
212 | ISIS_LEVEL2, SPFTREE_IPV4, SPF_TYPE_FORWARD, | |
213 | F_SPFTREE_HOPCOUNT_METRIC, SR_ALGORITHM_SPF); | |
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 | { | |
e16d030c | 228 | EVENT_OFF(f->initial_sync_timeout); |
b30e837b | 229 | |
e16d030c | 230 | EVENT_OFF(f->tier_calculation_timer); |
75e0ec94 | 231 | |
e16d030c | 232 | EVENT_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 | ||
e6685141 | 240 | static void fabricd_initial_sync_timeout(struct event *thread) |
8e6fb83b | 241 | { |
e16d030c | 242 | struct fabricd *f = EVENT_ARG(thread); |
8e6fb83b | 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 | ||
907a2395 DS |
270 | event_add_timer(master, fabricd_initial_sync_timeout, f, timeout, |
271 | &f->initial_sync_timeout); | |
8e6fb83b CF |
272 | f->initial_sync_start = monotime(NULL); |
273 | ||
15d133c9 DS |
274 | if (IS_DEBUG_ADJ_PACKETS) |
275 | zlog_debug( | |
5d39a819 OD |
276 | "OpenFabric: Started initial synchronization with %pSY on %s", |
277 | circuit->u.p2p.neighbor->sysid, | |
15d133c9 | 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; | |
e16d030c | 328 | EVENT_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 | ||
5d39a819 OD |
362 | zlog_info( |
363 | "OpenFabric: Found %pLS as furthest t0 from local system, dist == %u", | |
364 | furthest_t0->N.id, furthest_t0->d_N); | |
75e0ec94 CF |
365 | |
366 | struct isis_spftree *remote_tree = | |
367 | isis_run_hopcount_spf(area, furthest_t0->N.id, NULL); | |
368 | ||
369 | struct isis_vertex *furthest_from_remote = | |
370 | isis_vertex_queue_last(&remote_tree->paths); | |
371 | ||
372 | if (!furthest_from_remote) { | |
373 | zlog_info("OpenFabric: Found no furthest node in remote spf"); | |
374 | isis_spftree_del(remote_tree); | |
375 | return ISIS_TIER_UNDEFINED; | |
376 | } else { | |
5d39a819 OD |
377 | zlog_info( |
378 | "OpenFabric: Found %pLS as furthest from remote dist == %u", | |
379 | furthest_from_remote->N.id, furthest_from_remote->d_N); | |
75e0ec94 CF |
380 | } |
381 | ||
382 | int64_t tier = furthest_from_remote->d_N - furthest_t0->d_N; | |
383 | isis_spftree_del(remote_tree); | |
384 | ||
385 | if (tier < 0 || tier >= ISIS_TIER_UNDEFINED) { | |
386 | zlog_info("OpenFabric: Calculated tier %" PRId64 " seems implausible", | |
387 | tier); | |
388 | return ISIS_TIER_UNDEFINED; | |
389 | } | |
390 | ||
391 | zlog_info("OpenFabric: Calculated %" PRId64 " as tier", tier); | |
392 | return tier; | |
393 | } | |
394 | ||
e6685141 | 395 | static void fabricd_tier_set_timer(struct event *thread) |
75e0ec94 | 396 | { |
e16d030c | 397 | struct fabricd *f = EVENT_ARG(thread); |
75e0ec94 CF |
398 | |
399 | fabricd_set_tier(f, f->tier_pending); | |
75e0ec94 CF |
400 | } |
401 | ||
e6685141 | 402 | static void fabricd_tier_calculation_cb(struct event *thread) |
75e0ec94 | 403 | { |
e16d030c | 404 | struct fabricd *f = EVENT_ARG(thread); |
75e0ec94 | 405 | uint8_t tier = ISIS_TIER_UNDEFINED; |
75e0ec94 CF |
406 | |
407 | tier = fabricd_calculate_fabric_tier(f->area); | |
408 | if (tier == ISIS_TIER_UNDEFINED) | |
cc9f21da | 409 | return; |
75e0ec94 | 410 | |
6cde4b45 | 411 | zlog_info("OpenFabric: Got tier %hhu from algorithm. Arming timer.", |
75e0ec94 CF |
412 | tier); |
413 | f->tier_pending = tier; | |
907a2395 DS |
414 | event_add_timer(master, fabricd_tier_set_timer, f, |
415 | f->area->lsp_gen_interval[ISIS_LEVEL2 - 1], | |
416 | &f->tier_set_timer); | |
75e0ec94 CF |
417 | } |
418 | ||
419 | static void fabricd_bump_tier_calculation_timer(struct fabricd *f) | |
420 | { | |
421 | /* Cancel timer if we already know our tier */ | |
b3d6bc6e | 422 | if (f->tier != ISIS_TIER_UNDEFINED || f->tier_set_timer) { |
e16d030c | 423 | EVENT_OFF(f->tier_calculation_timer); |
75e0ec94 CF |
424 | return; |
425 | } | |
426 | ||
427 | /* If we need to calculate the tier, wait some | |
428 | * time for the topology to settle before running | |
429 | * the calculation */ | |
e16d030c | 430 | EVENT_OFF(f->tier_calculation_timer); |
75e0ec94 | 431 | |
907a2395 DS |
432 | event_add_timer(master, fabricd_tier_calculation_cb, f, |
433 | 2 * f->area->lsp_gen_interval[ISIS_LEVEL2 - 1], | |
434 | &f->tier_calculation_timer); | |
75e0ec94 CF |
435 | } |
436 | ||
92ed0cde CF |
437 | static void fabricd_set_tier(struct fabricd *f, uint8_t tier) |
438 | { | |
439 | if (f->tier == tier) | |
440 | return; | |
441 | ||
6cde4b45 | 442 | zlog_info("OpenFabric: Set own tier to %hhu", tier); |
92ed0cde CF |
443 | f->tier = tier; |
444 | ||
75e0ec94 | 445 | fabricd_bump_tier_calculation_timer(f); |
92ed0cde CF |
446 | lsp_regenerate_schedule(f->area, ISIS_LEVEL2, 0); |
447 | } | |
448 | ||
b30e837b CF |
449 | void fabricd_run_spf(struct isis_area *area) |
450 | { | |
451 | struct fabricd *f = area->fabricd; | |
452 | ||
453 | if (!f) | |
454 | return; | |
455 | ||
eab88f36 | 456 | isis_run_hopcount_spf(area, area->isis->sysid, f->spftree); |
2cd971af | 457 | neighbors_neighbors_update(f); |
75e0ec94 | 458 | fabricd_bump_tier_calculation_timer(f); |
b30e837b CF |
459 | } |
460 | ||
461 | struct isis_spftree *fabricd_spftree(struct isis_area *area) | |
462 | { | |
463 | struct fabricd *f = area->fabricd; | |
464 | ||
465 | if (!f) | |
466 | return NULL; | |
467 | ||
468 | return f->spftree; | |
469 | } | |
92ed0cde CF |
470 | |
471 | void fabricd_configure_tier(struct isis_area *area, uint8_t tier) | |
472 | { | |
473 | struct fabricd *f = area->fabricd; | |
474 | ||
475 | if (!f || f->tier_config == tier) | |
476 | return; | |
477 | ||
478 | f->tier_config = tier; | |
479 | fabricd_set_tier(f, tier); | |
480 | } | |
481 | ||
482 | uint8_t fabricd_tier(struct isis_area *area) | |
483 | { | |
484 | struct fabricd *f = area->fabricd; | |
485 | ||
486 | if (!f) | |
487 | return ISIS_TIER_UNDEFINED; | |
488 | ||
489 | return f->tier; | |
490 | } | |
491 | ||
492 | int fabricd_write_settings(struct isis_area *area, struct vty *vty) | |
493 | { | |
494 | struct fabricd *f = area->fabricd; | |
495 | int written = 0; | |
496 | ||
497 | if (!f) | |
498 | return written; | |
499 | ||
500 | if (f->tier_config != ISIS_TIER_UNDEFINED) { | |
6cde4b45 | 501 | vty_out(vty, " fabric-tier %hhu\n", f->tier_config); |
92ed0cde CF |
502 | written++; |
503 | } | |
504 | ||
e923107c CF |
505 | if (f->csnp_delay != FABRICD_DEFAULT_CSNP_DELAY |
506 | || f->always_send_csnp) { | |
507 | vty_out(vty, " triggered-csnp-delay %d%s\n", f->csnp_delay, | |
508 | f->always_send_csnp ? " always" : ""); | |
509 | } | |
510 | ||
92ed0cde CF |
511 | return written; |
512 | } | |
9d224819 | 513 | |
dc0dacfc | 514 | static void move_to_queue(struct isis_lsp *lsp, struct neighbor_entry *n, |
1eb7c3a1 | 515 | enum isis_tx_type type, struct isis_circuit *circuit) |
9d224819 | 516 | { |
9d224819 | 517 | n->present = false; |
d4cff91a | 518 | |
1eb7c3a1 CF |
519 | if (n->adj && n->adj->circuit == circuit) |
520 | return; | |
521 | ||
e740f9c1 | 522 | if (IS_DEBUG_FLOODING) { |
dc0dacfc CF |
523 | zlog_debug("OpenFabric: Adding %s to %s", |
524 | print_sys_hostname(n->id), | |
525 | (type == TX_LSP_NORMAL) ? "RF" : "DNR"); | |
d4cff91a CF |
526 | } |
527 | ||
dc0dacfc CF |
528 | if (n->adj) |
529 | isis_tx_queue_add(n->adj->circuit->tx_queue, lsp, type); | |
1eb7c3a1 | 530 | |
89cdc4df RM |
531 | uint8_t *neighbor_id = XMALLOC(MTYPE_FABRICD_FLOODING_INFO, |
532 | sizeof(n->id)); | |
1eb7c3a1 CF |
533 | |
534 | memcpy(neighbor_id, n->id, sizeof(n->id)); | |
535 | listnode_add(lsp->flooding_neighbors[type], neighbor_id); | |
9d224819 CF |
536 | } |
537 | ||
e3b78da8 | 538 | static void mark_neighbor_as_present(struct hash_bucket *bucket, void *arg) |
9d224819 | 539 | { |
e3b78da8 | 540 | struct neighbor_entry *n = bucket->data; |
9d224819 CF |
541 | |
542 | n->present = true; | |
543 | } | |
544 | ||
e3b78da8 | 545 | static void handle_firsthops(struct hash_bucket *bucket, void *arg) |
9d224819 CF |
546 | { |
547 | struct isis_lsp *lsp = arg; | |
548 | struct fabricd *f = lsp->area->fabricd; | |
e3b78da8 | 549 | struct isis_vertex *vertex = bucket->data; |
9d224819 CF |
550 | |
551 | struct neighbor_entry *n; | |
552 | ||
553 | n = neighbor_entry_lookup_list(f->neighbors, vertex->N.id); | |
d4cff91a | 554 | if (n) { |
e740f9c1 | 555 | if (IS_DEBUG_FLOODING) { |
d4cff91a | 556 | zlog_debug("Removing %s from NL as its in the reverse path", |
dc0dacfc | 557 | print_sys_hostname(n->id)); |
d4cff91a | 558 | } |
9d224819 | 559 | n->present = false; |
d4cff91a | 560 | } |
9d224819 CF |
561 | |
562 | n = neighbor_entry_lookup_hash(f->neighbors_neighbors, vertex->N.id); | |
d4cff91a | 563 | if (n) { |
e740f9c1 | 564 | if (IS_DEBUG_FLOODING) { |
d4cff91a | 565 | zlog_debug("Removing %s from NN as its in the reverse path", |
dc0dacfc | 566 | print_sys_hostname(n->id)); |
d4cff91a | 567 | } |
9d224819 | 568 | n->present = false; |
d4cff91a | 569 | } |
9d224819 CF |
570 | } |
571 | ||
dc0dacfc CF |
572 | static struct isis_lsp *lsp_for_neighbor(struct fabricd *f, |
573 | struct neighbor_entry *n) | |
574 | { | |
575 | uint8_t id[ISIS_SYS_ID_LEN + 1] = {0}; | |
576 | ||
577 | memcpy(id, n->id, sizeof(n->id)); | |
578 | ||
579 | struct isis_vertex vertex = {0}; | |
580 | ||
581 | isis_vertex_id_init(&vertex, id, VTYPE_NONPSEUDO_TE_IS); | |
582 | ||
583 | return lsp_for_vertex(f->spftree, &vertex); | |
584 | } | |
585 | ||
1eb7c3a1 CF |
586 | static void fabricd_free_lsp_flooding_info(void *val) |
587 | { | |
588 | XFREE(MTYPE_FABRICD_FLOODING_INFO, val); | |
589 | } | |
590 | ||
591 | static void fabricd_lsp_reset_flooding_info(struct isis_lsp *lsp, | |
592 | struct isis_circuit *circuit) | |
593 | { | |
a6b60da9 CF |
594 | lsp->flooding_time = time(NULL); |
595 | ||
1eb7c3a1 CF |
596 | XFREE(MTYPE_FABRICD_FLOODING_INFO, lsp->flooding_interface); |
597 | for (enum isis_tx_type type = TX_LSP_NORMAL; | |
598 | type <= TX_LSP_CIRCUIT_SCOPED; type++) { | |
599 | if (lsp->flooding_neighbors[type]) { | |
600 | list_delete_all_node(lsp->flooding_neighbors[type]); | |
601 | continue; | |
602 | } | |
603 | ||
604 | lsp->flooding_neighbors[type] = list_new(); | |
89cdc4df RM |
605 | lsp->flooding_neighbors[type]->del = |
606 | fabricd_free_lsp_flooding_info; | |
1eb7c3a1 CF |
607 | } |
608 | ||
609 | if (circuit) { | |
610 | lsp->flooding_interface = XSTRDUP(MTYPE_FABRICD_FLOODING_INFO, | |
611 | circuit->interface->name); | |
612 | } | |
a6b60da9 CF |
613 | |
614 | lsp->flooding_circuit_scoped = false; | |
1eb7c3a1 CF |
615 | } |
616 | ||
617 | void fabricd_lsp_flood(struct isis_lsp *lsp, struct isis_circuit *circuit) | |
9d224819 CF |
618 | { |
619 | struct fabricd *f = lsp->area->fabricd; | |
620 | assert(f); | |
621 | ||
1eb7c3a1 CF |
622 | fabricd_lsp_reset_flooding_info(lsp, circuit); |
623 | ||
9d224819 CF |
624 | void *cursor = NULL; |
625 | struct neighbor_entry *n; | |
626 | ||
e5b0aaf1 | 627 | /* Mark all elements in NL as present */ |
89cdc4df | 628 | while (!skiplist_next(f->neighbors, NULL, (void **)&n, &cursor)) |
9d224819 | 629 | n->present = true; |
9d224819 CF |
630 | |
631 | /* Mark all elements in NN as present */ | |
632 | hash_iterate(f->neighbors_neighbors, mark_neighbor_as_present, NULL); | |
633 | ||
89cdc4df RM |
634 | struct isis_vertex *originator = |
635 | isis_find_vertex(&f->spftree->paths, | |
636 | lsp->hdr.lsp_id, | |
637 | VTYPE_NONPSEUDO_TE_IS); | |
9d224819 CF |
638 | |
639 | /* Remove all IS from NL and NN in the shortest path | |
640 | * to the IS that originated the LSP */ | |
641 | if (originator) | |
642 | hash_iterate(originator->firsthops, handle_firsthops, lsp); | |
643 | ||
644 | /* Iterate over all remaining IS in NL */ | |
645 | cursor = NULL; | |
646 | while (!skiplist_next(f->neighbors, NULL, (void **)&n, &cursor)) { | |
647 | if (!n->present) | |
648 | continue; | |
649 | ||
dc0dacfc | 650 | struct isis_lsp *nlsp = lsp_for_neighbor(f, n); |
9d224819 | 651 | if (!nlsp || !nlsp->tlvs) { |
e740f9c1 | 652 | if (IS_DEBUG_FLOODING) { |
d4cff91a | 653 | zlog_debug("Moving %s to DNR as it has no LSP", |
dc0dacfc | 654 | print_sys_hostname(n->id)); |
d4cff91a CF |
655 | } |
656 | ||
1eb7c3a1 | 657 | move_to_queue(lsp, n, TX_LSP_CIRCUIT_SCOPED, circuit); |
9d224819 CF |
658 | continue; |
659 | } | |
660 | ||
e740f9c1 | 661 | if (IS_DEBUG_FLOODING) { |
d4cff91a | 662 | zlog_debug("Considering %s from NL...", |
dc0dacfc | 663 | print_sys_hostname(n->id)); |
d4cff91a CF |
664 | } |
665 | ||
9d224819 CF |
666 | /* For all neighbors of the NL IS check whether they are present |
667 | * in NN. If yes, remove from NN and set need_reflood. */ | |
668 | bool need_reflood = false; | |
669 | struct isis_extended_reach *er; | |
670 | for (er = (struct isis_extended_reach *)nlsp->tlvs->extended_reach.head; | |
671 | er; er = er->next) { | |
672 | struct neighbor_entry *nn; | |
673 | ||
674 | nn = neighbor_entry_lookup_hash(f->neighbors_neighbors, | |
675 | er->id); | |
676 | ||
677 | if (nn) { | |
e740f9c1 | 678 | if (IS_DEBUG_FLOODING) { |
d4cff91a | 679 | zlog_debug("Found neighbor %s in NN, removing it from NN and setting reflood.", |
dc0dacfc | 680 | print_sys_hostname(nn->id)); |
d4cff91a CF |
681 | } |
682 | ||
9d224819 CF |
683 | nn->present = false; |
684 | need_reflood = true; | |
685 | } | |
686 | } | |
687 | ||
dc0dacfc | 688 | move_to_queue(lsp, n, need_reflood ? |
1eb7c3a1 CF |
689 | TX_LSP_NORMAL : TX_LSP_CIRCUIT_SCOPED, |
690 | circuit); | |
9d224819 | 691 | } |
d4cff91a | 692 | |
e740f9c1 | 693 | if (IS_DEBUG_FLOODING) { |
d4cff91a CF |
694 | zlog_debug("OpenFabric: Flooding algorithm complete."); |
695 | } | |
9d224819 | 696 | } |
df0ba689 | 697 | |
e923107c | 698 | void fabricd_trigger_csnp(struct isis_area *area, bool circuit_scoped) |
df0ba689 CF |
699 | { |
700 | struct fabricd *f = area->fabricd; | |
701 | ||
702 | if (!f) | |
703 | return; | |
704 | ||
e923107c CF |
705 | if (!circuit_scoped && !f->always_send_csnp) |
706 | return; | |
707 | ||
df0ba689 CF |
708 | struct listnode *node; |
709 | struct isis_circuit *circuit; | |
710 | ||
711 | for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) { | |
712 | if (!circuit->t_send_csnp[1]) | |
713 | continue; | |
714 | ||
e16d030c | 715 | EVENT_OFF(circuit->t_send_csnp[ISIS_LEVEL2 - 1]); |
907a2395 DS |
716 | event_add_timer_msec(master, send_l2_csnp, circuit, |
717 | isis_jitter(f->csnp_delay, CSNP_JITTER), | |
718 | &circuit->t_send_csnp[ISIS_LEVEL2 - 1]); | |
df0ba689 CF |
719 | } |
720 | } | |
41415888 CF |
721 | |
722 | struct list *fabricd_ip_addrs(struct isis_circuit *circuit) | |
723 | { | |
91a5bbc4 | 724 | if (listcount(circuit->ip_addrs)) |
41415888 CF |
725 | return circuit->ip_addrs; |
726 | ||
727 | if (!fabricd || !circuit->area || !circuit->area->circuit_list) | |
728 | return NULL; | |
729 | ||
730 | struct listnode *node; | |
731 | struct isis_circuit *c; | |
732 | ||
733 | for (ALL_LIST_ELEMENTS_RO(circuit->area->circuit_list, node, c)) { | |
734 | if (c->circ_type != CIRCUIT_T_LOOPBACK) | |
735 | continue; | |
736 | ||
91a5bbc4 | 737 | if (!listcount(c->ip_addrs)) |
41415888 CF |
738 | return NULL; |
739 | ||
740 | return c->ip_addrs; | |
741 | } | |
742 | ||
743 | return NULL; | |
744 | } | |
1eb7c3a1 CF |
745 | |
746 | void fabricd_lsp_free(struct isis_lsp *lsp) | |
747 | { | |
748 | XFREE(MTYPE_FABRICD_FLOODING_INFO, lsp->flooding_interface); | |
749 | for (enum isis_tx_type type = TX_LSP_NORMAL; | |
750 | type <= TX_LSP_CIRCUIT_SCOPED; type++) { | |
751 | if (!lsp->flooding_neighbors[type]) | |
752 | continue; | |
753 | ||
754 | list_delete(&lsp->flooding_neighbors[type]); | |
755 | } | |
756 | } | |
a6b60da9 CF |
757 | |
758 | void fabricd_update_lsp_no_flood(struct isis_lsp *lsp, | |
759 | struct isis_circuit *circuit) | |
760 | { | |
761 | if (!fabricd) | |
762 | return; | |
763 | ||
764 | fabricd_lsp_reset_flooding_info(lsp, circuit); | |
765 | lsp->flooding_circuit_scoped = true; | |
766 | } | |
e923107c CF |
767 | |
768 | void fabricd_configure_triggered_csnp(struct isis_area *area, int delay, | |
769 | bool always_send_csnp) | |
770 | { | |
771 | struct fabricd *f = area->fabricd; | |
772 | ||
773 | if (!f) | |
774 | return; | |
775 | ||
776 | f->csnp_delay = delay; | |
777 | f->always_send_csnp = always_send_csnp; | |
778 | } | |
2cd971af CF |
779 | |
780 | void fabricd_init(void) | |
781 | { | |
782 | hook_register(isis_adj_state_change_hook, | |
783 | fabricd_handle_adj_state_change); | |
784 | } |