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: 4dcd6fd6ef3c59d7bea11c3935cfc625ae554d5f $")
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->updated = now;
48 
49  /*
50  * Only 1 retry, the timeout is MRD, not IRT.
51  */
52  if (config->mrc == 1) {
53  r->next = fr_time_add(now, config->mrd);
54  r->rt = config->mrd; /* mostly set for debug messages */
55  return;
56  }
57 
58  /*
59  * Initial:
60  *
61  * RT = IRT + RAND * IRT
62  * = IRT * (1 + RAND)
63  */
64  scale = fr_rand();
65  scale += ((uint64_t) 1) << 32; /* multiple it by 1 * 2^32 */
66  scale -= ((uint64_t) 1) << 31; /* scale it -2^31..+2^31 */
67 
68  delay = uint128_mul64(scale, fr_time_delta_unwrap(r->config->irt));
70 
71  r->rt = rt;
72  r->next = fr_time_add(now, rt);
73 }
74 
75 /** Initialize a retransmission counter
76  *
77  * @param[in,out] r the retransmission structure
78  * @param now the current time
79  * @return
80  * - FR_RETRTY_CONTINUE - continue retransmitting
81  * - FR_RETRY_MRC - stop, maximum retransmission count has been reached
82  * - FR_RETRY_MDR - stop, maximum retransmission duration has been reached.
83  */
85 {
86  uint64_t scale;
87  fr_time_delta_t rt;
88  uint128_t delay;
89 
90  /*
91  * Increment retransmission counter
92  */
93  r->count++;
94  r->updated = now;
95 
96  /*
97  * We retried too many times. Fail.
98  */
99  if (r->config->mrc && (r->count > r->config->mrc)) {
100  return FR_RETRY_MRC;
101  }
102 
103 redo:
104  /*
105  * Cap delay at MRD
106  */
107  if (fr_time_delta_ispos(r->config->mrd)) {
108  fr_time_t end;
109 
110  end = fr_time_add(r->start, r->config->mrd);
111  if (fr_time_gt(now, end)) {
112  return FR_RETRY_MRD;
113  }
114  }
115 
116  /*
117  * RFC 5080 Section 2.2.1
118  *
119  * RAND gives a random number between -0.1 and +0.1
120  *
121  * Our random number generator returns 0..2^32, so we
122  * have to scale everything relative to that.
123  *
124  * RT = 2*RTprev + RAND*RTprev
125  * = RTprev * (2 + RAND)
126  */
127  scale = fr_rand();
128  scale -= ((uint64_t) 1) << 31; /* scale it -2^31..+2^31 */
129  scale += ((uint64_t) 1) << 33; /* multiple it by 2 * 2^32 */
130 
131  delay = uint128_mul64(scale, fr_time_delta_unwrap(r->rt));
133 
134  /*
135  * Cap delay at MRT.
136  *
137  * RT = MRT + RAND * MRT
138  * = MRT * (1 + RAND)
139  */
140  if (fr_time_delta_ispos(r->config->mrt) && (fr_time_delta_gt(rt, r->config->mrt))) {
141  scale = fr_rand();
142  scale -= ((uint64_t) 1) << 31; /* scale it -2^31..+2^31 */
143  scale += ((uint64_t) 1) << 32; /* multiple it by 1 * 2^32 */
144 
145  delay = uint128_mul64(scale, fr_time_delta_unwrap(r->config->mrt));
147  }
148 
149  /*
150  * And finally set the retransmission timer.
151  */
152  r->rt = rt;
153 
154  /*
155  * Add in the retransmission delay. Note that we send
156  * the packet at "next + rt", and not "now + rt". That
157  * way the timer won't drift.
158  */
159  r->next = fr_time_add(r->next, rt);
160 
161  /*
162  * The "next" retransmission time is in the past, AND
163  * we're already halfway through the time after that.
164  * Skip this retransmission, and set the time for the
165  * next one.
166  *
167  * i.e. if we weren't serviced for one event, just skip
168  * it, and go to the next one.
169  */
170  if (fr_time_lt(fr_time_add(r->next, fr_time_delta_wrap((fr_time_delta_unwrap(rt) / 2))), now)) goto redo;
171 
172  return FR_RETRY_CONTINUE;
173 }
#define RCSID(id)
Definition: build.h:444
static const conf_parser_t config[]
Definition: base.c:188
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
#define fr_time_delta_wrap(_time)
Definition: time.h:152
#define fr_time_delta_ispos(_a)
Definition: time.h:288
#define fr_time_add(_a, _b)
Add a time/time delta together.
Definition: time.h:196
#define fr_time_gt(_a, _b)
Definition: time.h:237
#define fr_time_lt(_a, _b)
Definition: time.h:239
#define fr_time_delta_gt(_a, _b)
Definition: time.h:281
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:84
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:43
fr_time_delta_t irt
Initial transmission time.
Definition: retry.h:33
fr_time_delta_t rt
retransmit interval
Definition: retry.h:46
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:42
fr_retry_state_t
Definition: retry.h:55
@ FR_RETRY_MRC
reached maximum retransmission count
Definition: retry.h:57
@ FR_RETRY_CONTINUE
Definition: retry.h:56
@ FR_RETRY_MRD
reached maximum retransmission duration
Definition: retry.h:58
uint32_t count
number of sent packets
Definition: retry.h:47
fr_time_delta_t mrd
Maximum retransmission duration.
Definition: retry.h:35
fr_time_t updated
last update, really a cached "now".
Definition: retry.h:45
fr_time_t next
when the next timer should be set
Definition: retry.h:44