2 * This is an implementation of the IETF SPF delay algorithm
3 * as explained in draft-ietf-rtgwg-backoff-algo-04
5 * Created: 25-01-2017 by S. Litkowski
7 * Copyright (C) 2017 Orange Labs http://www.orange.com/
8 * Copyright (C) 2017 by Christian Franke, Open Source Routing / NetDEF Inc.
10 * This file is part of FreeRangeRouting (FRR)
12 * FRR is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 2, or (at your option) any
17 * FRR is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with FRR; see the file COPYING. If not, write to the Free
24 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
30 #include "spf_backoff.h"
37 DEFINE_MTYPE_STATIC(LIB
, SPF_BACKOFF
, "SPF backoff")
38 DEFINE_MTYPE_STATIC(LIB
, SPF_BACKOFF_NAME
, "SPF backoff name")
40 static bool debug_spf_backoff
= false;
41 #define backoff_debug(...) \
44 if (debug_spf_backoff) \
45 zlog_debug(__VA_ARGS__); \
49 enum spf_backoff_state
{
51 SPF_BACKOFF_SHORT_WAIT
,
56 struct thread_master
*m
;
58 /* Timers as per draft */
66 enum spf_backoff_state state
;
67 struct thread
*t_holddown
;
68 struct thread
*t_timetolearn
;
72 struct timeval first_event_time
;
73 struct timeval last_event_time
;
77 spf_backoff_state2str(enum spf_backoff_state state
)
81 case SPF_BACKOFF_QUIET
:
83 case SPF_BACKOFF_SHORT_WAIT
:
85 case SPF_BACKOFF_LONG_WAIT
:
92 spf_backoff_new(struct thread_master
*m
,
100 struct spf_backoff
*rv
;
102 rv
= XCALLOC(MTYPE_SPF_BACKOFF
, sizeof(*rv
));
105 rv
->init_delay
= init_delay
;
106 rv
->short_delay
= short_delay
;
107 rv
->long_delay
= long_delay
;
108 rv
->holddown
= holddown
;
109 rv
->timetolearn
= timetolearn
;
111 rv
->state
= SPF_BACKOFF_QUIET
;
113 rv
->name
= XSTRDUP(MTYPE_SPF_BACKOFF_NAME
, name
);
118 spf_backoff_free(struct spf_backoff
*backoff
)
123 THREAD_TIMER_OFF(backoff
->t_holddown
);
124 THREAD_TIMER_OFF(backoff
->t_timetolearn
);
125 XFREE(MTYPE_SPF_BACKOFF_NAME
, backoff
->name
);
127 XFREE(MTYPE_SPF_BACKOFF
, backoff
);
131 spf_backoff_timetolearn_elapsed(struct thread
*thread
)
133 struct spf_backoff
*backoff
= THREAD_ARG(thread
);
135 backoff
->t_timetolearn
= NULL
;
136 backoff
->state
= SPF_BACKOFF_LONG_WAIT
;
137 backoff_debug("SPF Back-off(%s) TIMETOLEARN elapsed, move to state %s",
138 backoff
->name
, spf_backoff_state2str(backoff
->state
));
143 spf_backoff_holddown_elapsed(struct thread
*thread
)
145 struct spf_backoff
*backoff
= THREAD_ARG(thread
);
147 backoff
->t_holddown
= NULL
;
148 THREAD_TIMER_OFF(backoff
->t_timetolearn
);
149 timerclear(&backoff
->first_event_time
);
150 backoff
->state
= SPF_BACKOFF_QUIET
;
151 backoff_debug("SPF Back-off(%s) HOLDDOWN elapsed, move to state %s",
152 backoff
->name
, spf_backoff_state2str(backoff
->state
));
156 long spf_backoff_schedule(struct spf_backoff
*backoff
)
161 gettimeofday(&now
, NULL
);
163 backoff_debug("SPF Back-off(%s) schedule called in state %s",
164 backoff
->name
, spf_backoff_state2str(backoff
->state
));
166 backoff
->last_event_time
= now
;
168 switch (backoff
->state
)
170 case SPF_BACKOFF_QUIET
:
171 backoff
->state
= SPF_BACKOFF_SHORT_WAIT
;
172 THREAD_TIMER_MSEC_ON(backoff
->m
, backoff
->t_timetolearn
,
173 spf_backoff_timetolearn_elapsed
, backoff
,
174 backoff
->timetolearn
);
175 THREAD_TIMER_MSEC_ON(backoff
->m
, backoff
->t_holddown
,
176 spf_backoff_holddown_elapsed
, backoff
,
178 backoff
->first_event_time
= now
;
179 rv
= backoff
->init_delay
;
181 case SPF_BACKOFF_SHORT_WAIT
:
182 case SPF_BACKOFF_LONG_WAIT
:
183 THREAD_TIMER_OFF(backoff
->t_holddown
);
184 THREAD_TIMER_MSEC_ON(backoff
->m
, backoff
->t_holddown
,
185 spf_backoff_holddown_elapsed
, backoff
,
187 if (backoff
->state
== SPF_BACKOFF_SHORT_WAIT
)
188 rv
= backoff
->short_delay
;
190 rv
= backoff
->long_delay
;
193 zlog_warn("SPF Back-off(%s) in unknown state", backoff
->name
);
194 rv
= backoff
->init_delay
;
197 backoff_debug("SPF Back-off(%s) changed state to %s and returned %ld delay",
198 backoff
->name
, spf_backoff_state2str(backoff
->state
), rv
);
203 timeval_format(struct timeval
*tv
)
207 static char timebuf
[256];
209 if (!tv
->tv_sec
&& !tv
->tv_usec
)
212 tm
= localtime_r(&tv
->tv_sec
, &tm_store
);
213 if (!tm
|| strftime(timebuf
, sizeof(timebuf
),
214 "%Z %a %Y-%m-%d %H:%M:%S", tm
) == 0)
219 size_t offset
= strlen(timebuf
);
220 snprintf(timebuf
+ offset
, sizeof(timebuf
) - offset
, ".%ld", tv
->tv_usec
);
226 spf_backoff_show(struct spf_backoff
*backoff
, struct vty
*vty
,
229 vty_out(vty
, "%sCurrent state: %s%s", prefix
,
230 spf_backoff_state2str(backoff
->state
), VTY_NEWLINE
);
231 vty_out(vty
, "%sInit timer: %ld msec%s", prefix
,
232 backoff
->init_delay
, VTY_NEWLINE
);
233 vty_out(vty
, "%sShort timer: %ld msec%s", prefix
,
234 backoff
->short_delay
, VTY_NEWLINE
);
235 vty_out(vty
, "%sLong timer: %ld msec%s", prefix
,
236 backoff
->long_delay
, VTY_NEWLINE
);
237 vty_out(vty
, "%sHolddown timer: %ld msec%s", prefix
,
238 backoff
->holddown
, VTY_NEWLINE
);
239 if (backoff
->t_holddown
)
241 struct timeval remain
= thread_timer_remain(backoff
->t_holddown
);
242 vty_out(vty
, "%s Still runs for %ld msec%s",
243 prefix
, remain
.tv_sec
* 1000 + remain
.tv_usec
/1000, VTY_NEWLINE
);
247 vty_out(vty
, "%s Inactive%s", prefix
, VTY_NEWLINE
);
250 vty_out(vty
, "%sTimeToLearn timer: %ld msec%s", prefix
,
251 backoff
->timetolearn
, VTY_NEWLINE
);
252 if (backoff
->t_timetolearn
)
254 struct timeval remain
= thread_timer_remain(backoff
->t_timetolearn
);
255 vty_out(vty
, "%s Still runs for %ld msec%s",
256 prefix
, remain
.tv_sec
* 1000 + remain
.tv_usec
/1000, VTY_NEWLINE
);
260 vty_out(vty
, "%s Inactive%s", prefix
, VTY_NEWLINE
);
263 vty_out(vty
, "%sFirst event: %s%s", prefix
,
264 timeval_format(&backoff
->first_event_time
), VTY_NEWLINE
);
265 vty_out(vty
, "%sLast event: %s%s", prefix
,
266 timeval_format(&backoff
->last_event_time
), VTY_NEWLINE
);
269 DEFUN(spf_backoff_debug
,
270 spf_backoff_debug_cmd
,
271 "debug spf-delay-ietf",
273 "SPF Back-off Debugging\n")
275 debug_spf_backoff
= true;
279 DEFUN(no_spf_backoff_debug
,
280 no_spf_backoff_debug_cmd
,
281 "no debug spf-delay-ietf",
284 "SPF Back-off Debugging\n")
286 debug_spf_backoff
= false;
291 spf_backoff_write_config(struct vty
*vty
)
295 if (debug_spf_backoff
)
297 vty_out(vty
, "debug spf-delay-ietf%s", VTY_NEWLINE
);
305 spf_backoff_cmd_init(void)
307 install_element(ENABLE_NODE
, &spf_backoff_debug_cmd
);
308 install_element(CONFIG_NODE
, &spf_backoff_debug_cmd
);
309 install_element(ENABLE_NODE
, &no_spf_backoff_debug_cmd
);
310 install_element(CONFIG_NODE
, &no_spf_backoff_debug_cmd
);
314 spf_backoff_init_delay(struct spf_backoff
*backoff
)
316 return backoff
->init_delay
;
320 spf_backoff_short_delay(struct spf_backoff
*backoff
)
322 return backoff
->short_delay
;
326 spf_backoff_long_delay(struct spf_backoff
*backoff
)
328 return backoff
->long_delay
;
332 spf_backoff_holddown(struct spf_backoff
*backoff
)
334 return backoff
->holddown
;
338 spf_backoff_timetolearn(struct spf_backoff
*backoff
)
340 return backoff
->timetolearn
;