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: 45d70d64b5b2bc1577ac658f5b55e969cb131a59 $")
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 uint64_t scale;
40 uint128_t delay;
41
42 memset(r, 0, sizeof(*r));
43
44 r->config = config;
45 r->count = 1;
46 r->start = now;
47 r->end = fr_time_add(now, config->mrd);
48 r->updated = now;
50
51 /*
52 * Ensure that we always have an end time.
53 *
54 * If there's no MRD. We artificially force the end to a day. If we're still retrying after
55 * that, it's likely good reason to give up. The rest of the server enforces much shorter
56 * lifetimes on requests.
57 */
58 if (fr_time_cmp(r->start, r->end) == 0) {
59 if (!config->mrc) {
60 r->end = fr_time_add(now, fr_time_delta_from_sec(86400));
61 } else {
62 r->end = fr_time_add(now, fr_time_delta_mul(config->mrt, config->mrc));
63 }
64 }
65
66 /*
67 * Only 1 retry, the timeout is MRD, not IRT.
68 */
69 if (config->mrc == 1) {
70 r->next = r->end;
71 r->rt = config->mrd; /* mostly set for debug messages */
72 return;
73 }
74
75 /*
76 * Initial:
77 *
78 * RT = IRT + RAND * IRT
79 * = IRT * (1 + RAND)
80 */
81 scale = fr_rand();
82 scale += ((uint64_t) 1) << 32; /* multiple it by 1 * 2^32 */
83 scale -= ((uint64_t) 1) << 31; /* scale it -2^31..+2^31 */
84
85 delay = uint128_mul64(scale, fr_time_delta_unwrap(r->config->irt));
87
88 r->rt = rt;
89 r->next = fr_time_add(now, rt);
90
91 /*
92 * Cap the "next" timer at the end.
93 */
94 if (fr_time_cmp(r->next, r->end) > 0) {
95 r->next = r->end;
96 }
97}
98
99/** Initialize a retransmission counter
100 *
101 * @param[in,out] r the retransmission structure
102 * @param now the current time
103 * @return
104 * - FR_RETRTY_CONTINUE - continue retransmitting
105 * - FR_RETRY_MRC - stop, maximum retransmission count has been reached
106 * - FR_RETRY_MDR - stop, maximum retransmission duration has been reached.
107 */
109{
110 uint64_t scale;
112 uint128_t delay;
113
114 /*
115 * Increment retransmission counter
116 */
117 r->count++;
118 r->updated = now;
119
120 /*
121 * We retried too many times. Fail.
122 */
123 if (r->config->mrc && (r->count > r->config->mrc)) {
124 /*
125 * A count of 1 is really a simple duration.
126 */
127 if (r->config->mrc == 1) {
128 r->state = FR_RETRY_MRD;
129 return FR_RETRY_MRD;
130 }
131
132 r->state = FR_RETRY_MRC;
133 return FR_RETRY_MRC;
134 }
135
136redo:
137 /*
138 * Cap delay at the end.
139 *
140 * Note that this code can still return MRD, even if MRD
141 * wasn't set. The initialization function above
142 * artificially caps MRD at one day.
143 */
144 if (fr_time_cmp(now, r->end) >= 0) {
145 r->state = FR_RETRY_MRD;
146 return FR_RETRY_MRD;
147 }
148
149 /*
150 * RFC 5080 Section 2.2.1
151 *
152 * RAND gives a random number between -0.1 and +0.1
153 *
154 * Our random number generator returns 0..2^32, so we
155 * have to scale everything relative to that.
156 *
157 * RT = 2*RTprev + RAND*RTprev
158 * = RTprev * (2 + RAND)
159 */
160 scale = fr_rand();
161 scale -= ((uint64_t) 1) << 31; /* scale it -2^31..+2^31 */
162 scale += ((uint64_t) 1) << 33; /* multiple it by 2 * 2^32 */
163
164 delay = uint128_mul64(scale, fr_time_delta_unwrap(r->rt));
166
167 /*
168 * Cap delay at MRT.
169 *
170 * RT = MRT + RAND * MRT
171 * = MRT * (1 + RAND)
172 */
173 if (fr_time_delta_ispos(r->config->mrt) && (fr_time_delta_gt(rt, r->config->mrt))) {
174 scale = fr_rand();
175 scale -= ((uint64_t) 1) << 31; /* scale it -2^31..+2^31 */
176 scale += ((uint64_t) 1) << 32; /* multiple it by 1 * 2^32 */
177
178 delay = uint128_mul64(scale, fr_time_delta_unwrap(r->config->mrt));
180 }
181
182 /*
183 * And finally set the retransmission timer.
184 */
185 r->rt = rt;
186
187 /*
188 * Add in the retransmission delay. Note that we send
189 * the packet at "next + rt", and not "now + rt". That
190 * way the timer won't drift.
191 */
192 r->next = fr_time_add(r->next, rt);
193
194 /*
195 * The "next" retransmission time is in the past, AND
196 * we're already halfway through the time after that.
197 * Skip this retransmission, and set the time for the
198 * next one.
199 *
200 * i.e. if we weren't serviced for one event, just skip
201 * it, and go to the next one.
202 */
203 if (fr_time_lt(fr_time_add(r->next, fr_time_delta_wrap((fr_time_delta_unwrap(rt) / 2))), now)) goto redo;
204
205 /*
206 * Cap the "next" timer at when we stop sending.
207 */
208 if (fr_time_cmp(r->next, r->end) > 0) {
209 r->next = r->end;
210 }
211
212 return FR_RETRY_CONTINUE;
213}
#define RCSID(id)
Definition build.h:483
static const conf_parser_t config[]
Definition base.c:183
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:108
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