The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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 
24 RCSID("$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;
39  fr_time_delta_t rt;
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;
111  fr_time_delta_t rt;
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 
136 redo:
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:481
static const conf_parser_t config[]
Definition: base.c:183
uint32_t fr_rand(void)
Return a 32-bit random number.
Definition: rand.c:106
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