The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
retry.c
Go to the documentation of this file.
1/*
2 * This library is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2.1 of the License, or (at your option) any later version.
6 *
7 * This library is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with this library; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/** Handle RFC standard retransmissions
18 *
19 * @file src/lib/util/retry.c
20 *
21 * @copyright 2020 Network RADIUS SAS
22 */
23
24RCSID("$Id: e8d2a078c8bda913bee9f8696cd16f916da0068e $")
25
26#include <freeradius-devel/util/retry.h>
27#include <freeradius-devel/util/rand.h>
28#include <freeradius-devel/util/uint128.h>
29
30/** Initialize a retransmission counter
31 *
32 * @param[in,out] r the retransmission structure
33 * @param now when the retransmission starts
34 * @param config the counters to track. They shouldn't change while the retransmission is happening
35 */
37{
38 int64_t tenth;
39 uint64_t scale;
41 uint128_t delay;
42
43 memset(r, 0, sizeof(*r));
44
45 r->config = config;
46 r->count = 1;
47 r->start = now;
48 r->end = fr_time_add(now, config->mrd);
49 r->updated = now;
51
52 /*
53 * Ensure that we always have an end time.
54 *
55 * If there's no MRD. We artificially force the end to a day. If we're still retrying after
56 * that, it's likely good reason to give up. The rest of the server enforces much shorter
57 * lifetimes on requests.
58 */
59 if (fr_time_cmp(r->start, r->end) == 0) {
60 if (!config->mrc) {
61 r->end = fr_time_add(now, fr_time_delta_from_sec(86400));
62 } else {
63 r->end = fr_time_add(now, fr_time_delta_mul(config->mrt, config->mrc));
64 }
65 }
66
67 /*
68 * Only 1 retry, the timeout is MRD, not IRT.
69 */
70 if (config->mrc == 1) {
71 r->next = r->end;
72 r->rt = config->mrd; /* mostly set for debug messages */
73 return;
74 }
75
76 /*
77 * Initial:
78 *
79 * RT = IRT + RAND * IRT
80 * = IRT * (1 + RAND)
81 */
82 tenth = fr_rand();
83 tenth -= ((uint64_t) 1) << 31; /* scale it -2^31..+2^31 */
84 tenth /= 5; /* convert [-.499,+.499] to [-.0999,+.0999] */
85 scale = (((uint64_t) 1) << 32) + tenth; /* make it 1.RAND modulo 2^32 */
86
87 delay = uint128_mul64(scale, fr_time_delta_unwrap(r->config->irt));
89
90 r->rt = rt;
91 r->next = fr_time_add(now, rt);
92
93 /*
94 * Cap the "next" timer at the end.
95 */
96 if (fr_time_cmp(r->next, r->end) > 0) {
97 r->next = r->end;
98 }
99}
100
101/** Initialize a retransmission counter
102 *
103 * @param[in,out] r the retransmission structure
104 * @param now the current time
105 * @return
106 * - FR_RETRTY_CONTINUE - continue retransmitting
107 * - FR_RETRY_MRC - stop, maximum retransmission count has been reached
108 * - FR_RETRY_MDR - stop, maximum retransmission duration has been reached.
109 */
111{
112 int64_t tenth;
113 uint64_t scale;
115 uint128_t delay;
116
117 /*
118 * Increment retransmission counter
119 */
120 r->count++;
121 r->updated = now;
122
123 /*
124 * We retried too many times. Fail.
125 */
126 if (r->config->mrc && (r->count > r->config->mrc)) {
127 /*
128 * A count of 1 is really a simple duration.
129 */
130 if (r->config->mrc == 1) {
131 r->state = FR_RETRY_MRD;
132 return FR_RETRY_MRD;
133 }
134
135 r->state = FR_RETRY_MRC;
136 return FR_RETRY_MRC;
137 }
138
139redo:
140 /*
141 * Cap delay at the end.
142 *
143 * Note that this code can still return MRD, even if MRD
144 * wasn't set. The initialization function above
145 * artificially caps MRD at one day.
146 */
147 if (fr_time_cmp(now, r->end) >= 0) {
148 r->state = FR_RETRY_MRD;
149 return FR_RETRY_MRD;
150 }
151
152 /*
153 * RFC 5080 Section 2.2.1
154 *
155 * RAND gives a random number between -0.1 and +0.1
156 *
157 * Our random number generator returns 0..2^32, so we
158 * have to scale everything relative to that.
159 *
160 * RT = 2*RTprev + RAND*RTprev
161 * = RTprev * (2 + RAND)
162 */
163 tenth = fr_rand();
164 tenth -= ((uint64_t) 1) << 31; /* scale it -2^31..+2^31 */
165 tenth /= 5; /* convert [-.499,+.499] to [-.0999,+.0999] */
166 scale = (((uint64_t) 1) << 33) + tenth; /* make it 2.RAND modulo 2^32 */
167
168 delay = uint128_mul64(scale, fr_time_delta_unwrap(r->rt));
170
171 /*
172 * Cap delay at MRT.
173 *
174 * RT = MRT + RAND * MRT
175 * = MRT * (1 + RAND)
176 */
177 if (fr_time_delta_ispos(r->config->mrt) && (fr_time_delta_gt(rt, r->config->mrt))) {
178 tenth = fr_rand();
179 tenth -= ((uint64_t) 1) << 31; /* scale it -2^31..+2^31 */
180 tenth /= 5; /* convert [-.499,+.499] to [-.0999,+.0999] */
181 scale = (((uint64_t) 1) << 32) + tenth; /* make it 1.RAND modulo 2^32 */
182
183 delay = uint128_mul64(scale, fr_time_delta_unwrap(r->config->mrt));
185 }
186
187 /*
188 * And finally set the retransmission timer.
189 */
190 r->rt = rt;
191
192 /*
193 * Add in the retransmission delay. Note that we send
194 * the packet at "next + rt", and not "now + rt". That
195 * way the timer won't drift.
196 */
197 r->next = fr_time_add(r->next, rt);
198
199 /*
200 * The "next" retransmission time is in the past, AND
201 * we're already halfway through the time after that.
202 * Skip this retransmission, and set the time for the
203 * next one.
204 *
205 * i.e. if we weren't serviced for one event, just skip
206 * it, and go to the next one.
207 */
208 if (fr_time_lt(fr_time_add(r->next, fr_time_delta_wrap((fr_time_delta_unwrap(rt) / 2))), now)) goto redo;
209
210 /*
211 * Cap the "next" timer at when we stop sending.
212 */
213 if (fr_time_cmp(r->next, r->end) > 0) {
214 r->next = r->end;
215 }
216
217 return FR_RETRY_CONTINUE;
218}
#define RCSID(id)
Definition build.h:487
static const conf_parser_t config[]
Definition base.c:169
uint32_t fr_rand(void)
Return a 32-bit random number.
Definition rand.c:105
static int64_t fr_time_delta_unwrap(fr_time_delta_t time)
Definition time.h:154
static fr_time_delta_t fr_time_delta_from_sec(int64_t sec)
Definition time.h:590
#define fr_time_delta_wrap(_time)
Definition time.h:152
#define fr_time_delta_ispos(_a)
Definition time.h:290
static fr_time_delta_t fr_time_delta_mul(fr_time_delta_t a, int64_t b)
Definition time.h:273
#define fr_time_add(_a, _b)
Add a time/time delta together.
Definition time.h:196
#define fr_time_lt(_a, _b)
Definition time.h:239
#define fr_time_delta_gt(_a, _b)
Definition time.h:283
static int8_t fr_time_cmp(fr_time_t a, fr_time_t b)
Compare two fr_time_t values.
Definition time.h:916
A time delta, a difference in time measured in nanoseconds.
Definition time.h:80
"server local" time.
Definition time.h:69
static uint64_t uint128_to_64(uint128_t a)
Returns the low bits of a 128bit integer.
Definition uint128.h:279
static uint128_t uint128_mul64(uint64_t u, uint64_t v)
Multiply two unsigned 64bit integers to get an unsigned 128bit integer.
Definition uint128.h:144
static uint128_t uint128_rshift(uint128_t num, uint8_t bits)
Right shift 128 bit integer.
Definition uint128.h:210
fr_retry_state_t fr_retry_next(fr_retry_t *r, fr_time_t now)
Initialize a retransmission counter.
Definition retry.c:110
void fr_retry_init(fr_retry_t *r, fr_time_t now, fr_retry_config_t const *config)
Initialize a retransmission counter.
Definition retry.c:36
fr_time_t start
when we started the retransmission
Definition retry.h:53
fr_time_delta_t irt
Initial transmission time.
Definition retry.h:33
fr_time_delta_t rt
retransmit interval
Definition retry.h:57
fr_time_delta_t mrt
Maximum retransmission time.
Definition retry.h:34
uint32_t mrc
Maximum retransmission count.
Definition retry.h:36
fr_retry_config_t const * config
master configuration
Definition retry.h:52
fr_retry_state_t state
so callers can see what state it's in.
Definition retry.h:60
fr_retry_state_t
Definition retry.h:45
@ FR_RETRY_MRC
reached maximum retransmission count
Definition retry.h:47
@ FR_RETRY_CONTINUE
Definition retry.h:46
@ FR_RETRY_MRD
reached maximum retransmission duration
Definition retry.h:48
uint32_t count
number of sent packets
Definition retry.h:58
fr_time_t end
when we will end the retransmissions
Definition retry.h:54
fr_time_t updated
last update, really a cached "now".
Definition retry.h:56
fr_time_t next
when the next timer should be set
Definition retry.h:55