]> git.proxmox.com Git - mirror_frr.git/blame - isisd/fabricd.c
fabricd: implement asymmetric metric for tier 0 as per Section 6.2
[mirror_frr.git] / isisd / fabricd.c
CommitLineData
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"
8e6fb83b
CF
32
33DEFINE_MTYPE_STATIC(ISISD, FABRICD_STATE, "ISIS OpenFabric")
34
35/* Tracks initial synchronization as per section 2.4
36 *
37 * We declare the sync complete once we have seen at least one
38 * CSNP and there are no more LSPs with SSN or SRM set.
39 */
40enum fabricd_sync_state {
41 FABRICD_SYNC_PENDING,
42 FABRICD_SYNC_STARTED,
43 FABRICD_SYNC_COMPLETE
44};
45
46struct fabricd {
92ed0cde
CF
47 struct isis_area *area;
48
8e6fb83b
CF
49 enum fabricd_sync_state initial_sync_state;
50 time_t initial_sync_start;
51 struct isis_circuit *initial_sync_circuit;
52 struct thread *initial_sync_timeout;
b30e837b
CF
53
54 struct isis_spftree *spftree;
92ed0cde
CF
55
56 uint8_t tier;
57 uint8_t tier_config;
75e0ec94
CF
58 uint8_t tier_pending;
59 struct thread *tier_calculation_timer;
60 struct thread *tier_set_timer;
8e6fb83b
CF
61};
62
b30e837b 63struct fabricd *fabricd_new(struct isis_area *area)
8e6fb83b
CF
64{
65 struct fabricd *rv = XCALLOC(MTYPE_FABRICD_STATE, sizeof(*rv));
66
92ed0cde 67 rv->area = area;
8e6fb83b 68 rv->initial_sync_state = FABRICD_SYNC_PENDING;
b30e837b 69 rv->spftree = isis_spftree_new(area);
92ed0cde 70 rv->tier = rv->tier_config = ISIS_TIER_UNDEFINED;
8e6fb83b
CF
71 return rv;
72};
73
b30e837b
CF
74void fabricd_finish(struct fabricd *f)
75{
76 if (f->initial_sync_timeout)
77 thread_cancel(f->initial_sync_timeout);
78
75e0ec94
CF
79 if (f->tier_calculation_timer)
80 thread_cancel(f->tier_calculation_timer);
81
82 if (f->tier_set_timer)
83 thread_cancel(f->tier_set_timer);
84
b30e837b
CF
85 isis_spftree_del(f->spftree);
86}
87
8e6fb83b
CF
88static int fabricd_initial_sync_timeout(struct thread *thread)
89{
90 struct fabricd *f = THREAD_ARG(thread);
91
92 zlog_info("OpenFabric: Initial synchronization on %s timed out!",
93 f->initial_sync_circuit->interface->name);
94 f->initial_sync_state = FABRICD_SYNC_PENDING;
95 f->initial_sync_circuit = NULL;
96 f->initial_sync_timeout = NULL;
97 return 0;
98}
99
100void fabricd_initial_sync_hello(struct isis_circuit *circuit)
101{
102 struct fabricd *f = circuit->area->fabricd;
103
104 if (!f)
105 return;
106
107 if (f->initial_sync_state > FABRICD_SYNC_PENDING)
108 return;
109
110 f->initial_sync_state = FABRICD_SYNC_STARTED;
111
112 long timeout = 2 * circuit->hello_interval[1] * circuit->hello_multiplier[1];
113
114 f->initial_sync_circuit = circuit;
115 if (f->initial_sync_timeout)
116 return;
117
118 thread_add_timer(master, fabricd_initial_sync_timeout, f,
119 timeout, &f->initial_sync_timeout);
120 f->initial_sync_start = monotime(NULL);
121
122 zlog_info("OpenFabric: Started initial synchronization with %s on %s",
123 sysid_print(circuit->u.p2p.neighbor->sysid),
124 circuit->interface->name);
125}
126
127bool fabricd_initial_sync_is_in_progress(struct isis_area *area)
128{
129 struct fabricd *f = area->fabricd;
130
131 if (!f)
132 return false;
133
134 if (f->initial_sync_state > FABRICD_SYNC_PENDING
135 && f->initial_sync_state < FABRICD_SYNC_COMPLETE)
136 return true;
137
138 return false;
139}
140
141struct isis_circuit *fabricd_initial_sync_circuit(struct isis_area *area)
142{
143 struct fabricd *f = area->fabricd;
144 if (!f)
145 return NULL;
146
147 return f->initial_sync_circuit;
148}
149
150void fabricd_initial_sync_finish(struct isis_area *area)
151{
152 struct fabricd *f = area->fabricd;
153
154 if (!f)
155 return;
156
157 if (monotime(NULL) - f->initial_sync_start < 5)
158 return;
159
160 zlog_info("OpenFabric: Initial synchronization on %s complete.",
161 f->initial_sync_circuit->interface->name);
162 f->initial_sync_state = FABRICD_SYNC_COMPLETE;
163 f->initial_sync_circuit = NULL;
164 thread_cancel(f->initial_sync_timeout);
165 f->initial_sync_timeout = NULL;
166}
b30e837b 167
75e0ec94
CF
168static void fabricd_bump_tier_calculation_timer(struct fabricd *f);
169static void fabricd_set_tier(struct fabricd *f, uint8_t tier);
170
171static uint8_t fabricd_calculate_fabric_tier(struct isis_area *area)
172{
173 struct isis_spftree *local_tree = fabricd_spftree(area);
174 struct listnode *node;
175
176 struct isis_vertex *furthest_t0 = NULL,
177 *second_furthest_t0 = NULL;
178
179 struct isis_vertex *v;
180
181 for (ALL_QUEUE_ELEMENTS_RO(&local_tree->paths, node, v)) {
182 struct isis_lsp *lsp = lsp_for_vertex(local_tree, v);
183
184 if (!lsp || !lsp->tlvs
185 || !lsp->tlvs->spine_leaf
186 || !lsp->tlvs->spine_leaf->has_tier
187 || lsp->tlvs->spine_leaf->tier != 0)
188 continue;
189
190 second_furthest_t0 = furthest_t0;
191 furthest_t0 = v;
192 }
193
194 if (!second_furthest_t0) {
195 zlog_info("OpenFabric: Could not find two T0 routers");
196 return ISIS_TIER_UNDEFINED;
197 }
198
199 zlog_info("OpenFabric: Found %s as furthest t0 from local system, dist == %"
200 PRIu32, rawlspid_print(furthest_t0->N.id), furthest_t0->d_N);
201
202 struct isis_spftree *remote_tree =
203 isis_run_hopcount_spf(area, furthest_t0->N.id, NULL);
204
205 struct isis_vertex *furthest_from_remote =
206 isis_vertex_queue_last(&remote_tree->paths);
207
208 if (!furthest_from_remote) {
209 zlog_info("OpenFabric: Found no furthest node in remote spf");
210 isis_spftree_del(remote_tree);
211 return ISIS_TIER_UNDEFINED;
212 } else {
213 zlog_info("OpenFabric: Found %s as furthest from remote dist == %"
214 PRIu32, rawlspid_print(furthest_from_remote->N.id),
215 furthest_from_remote->d_N);
216 }
217
218 int64_t tier = furthest_from_remote->d_N - furthest_t0->d_N;
219 isis_spftree_del(remote_tree);
220
221 if (tier < 0 || tier >= ISIS_TIER_UNDEFINED) {
222 zlog_info("OpenFabric: Calculated tier %" PRId64 " seems implausible",
223 tier);
224 return ISIS_TIER_UNDEFINED;
225 }
226
227 zlog_info("OpenFabric: Calculated %" PRId64 " as tier", tier);
228 return tier;
229}
230
231static int fabricd_tier_set_timer(struct thread *thread)
232{
233 struct fabricd *f = THREAD_ARG(thread);
234 f->tier_set_timer = NULL;
235
236 fabricd_set_tier(f, f->tier_pending);
237 return 0;
238}
239
240static int fabricd_tier_calculation_cb(struct thread *thread)
241{
242 struct fabricd *f = THREAD_ARG(thread);
243 uint8_t tier = ISIS_TIER_UNDEFINED;
244 f->tier_calculation_timer = NULL;
245
246 tier = fabricd_calculate_fabric_tier(f->area);
247 if (tier == ISIS_TIER_UNDEFINED)
248 return 0;
249
250 zlog_info("OpenFabric: Got tier %" PRIu8 " from algorithm. Arming timer.",
251 tier);
252 f->tier_pending = tier;
253 thread_add_timer(master, fabricd_tier_set_timer, f,
254 f->area->lsp_gen_interval[ISIS_LEVEL2 - 1],
255 &f->tier_set_timer);
256
257 return 0;
258}
259
260static void fabricd_bump_tier_calculation_timer(struct fabricd *f)
261{
262 /* Cancel timer if we already know our tier */
263 if (f->tier != ISIS_TIER_UNDEFINED
264 || f->tier_set_timer) {
265 if (f->tier_calculation_timer) {
266 thread_cancel(f->tier_calculation_timer);
267 f->tier_calculation_timer = NULL;
268 }
269 return;
270 }
271
272 /* If we need to calculate the tier, wait some
273 * time for the topology to settle before running
274 * the calculation */
275 if (f->tier_calculation_timer) {
276 thread_cancel(f->tier_calculation_timer);
277 f->tier_calculation_timer = NULL;
278 }
279
280 thread_add_timer(master, fabricd_tier_calculation_cb, f,
281 2 * f->area->lsp_gen_interval[ISIS_LEVEL2 - 1],
282 &f->tier_calculation_timer);
283}
284
92ed0cde
CF
285static void fabricd_set_tier(struct fabricd *f, uint8_t tier)
286{
287 if (f->tier == tier)
288 return;
289
75e0ec94 290 zlog_info("OpenFabric: Set own tier to %" PRIu8, tier);
92ed0cde
CF
291 f->tier = tier;
292
75e0ec94 293 fabricd_bump_tier_calculation_timer(f);
92ed0cde
CF
294 lsp_regenerate_schedule(f->area, ISIS_LEVEL2, 0);
295}
296
b30e837b
CF
297void fabricd_run_spf(struct isis_area *area)
298{
299 struct fabricd *f = area->fabricd;
300
301 if (!f)
302 return;
303
304 isis_run_hopcount_spf(area, isis->sysid, f->spftree);
75e0ec94 305 fabricd_bump_tier_calculation_timer(f);
b30e837b
CF
306}
307
308struct isis_spftree *fabricd_spftree(struct isis_area *area)
309{
310 struct fabricd *f = area->fabricd;
311
312 if (!f)
313 return NULL;
314
315 return f->spftree;
316}
92ed0cde
CF
317
318void fabricd_configure_tier(struct isis_area *area, uint8_t tier)
319{
320 struct fabricd *f = area->fabricd;
321
322 if (!f || f->tier_config == tier)
323 return;
324
325 f->tier_config = tier;
326 fabricd_set_tier(f, tier);
327}
328
329uint8_t fabricd_tier(struct isis_area *area)
330{
331 struct fabricd *f = area->fabricd;
332
333 if (!f)
334 return ISIS_TIER_UNDEFINED;
335
336 return f->tier;
337}
338
339int fabricd_write_settings(struct isis_area *area, struct vty *vty)
340{
341 struct fabricd *f = area->fabricd;
342 int written = 0;
343
344 if (!f)
345 return written;
346
347 if (f->tier_config != ISIS_TIER_UNDEFINED) {
348 vty_out(vty, " fabric-tier %" PRIu8 "\n", f->tier_config);
349 written++;
350 }
351
352 return written;
353}