The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
rlm_delay.c
Go to the documentation of this file.
1/*
2 * This program is is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or (at
5 * your option) any later version.
6 *
7 * This program 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
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: 56add7f478f536e637434e5323854cc521496b49 $
19 * @file rlm_delay.c
20 * @brief Add an artificial delay to requests.
21 *
22 * @copyright 2016 The FreeRADIUS server project
23 * @copyright 2016 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
24 */
25RCSID("$Id: 56add7f478f536e637434e5323854cc521496b49 $")
26
27#include <freeradius-devel/server/base.h>
28#include <freeradius-devel/server/module_rlm.h>
29#include <freeradius-devel/server/rcode.h>
30#include <freeradius-devel/util/debug.h>
31#include <freeradius-devel/server/map_proc.h>
32#include <freeradius-devel/util/time.h>
33#include <freeradius-devel/unlang/xlat_func.h>
34#include <freeradius-devel/unlang/action.h>
35
36typedef struct {
37 tmpl_t *delay; //!< How long we delay for.
38 bool relative; //!< Whether the delay is relative to the start of request processing.
39 bool force_reschedule; //!< Whether we should force rescheduling of the request.
40 rlm_rcode_t rcode; //!< The return code to use when the delay is complete.
42
43/*
44 * A mapping of configuration file names to internal variables.
45 */
46static const conf_parser_t module_config[] = {
48 { FR_CONF_OFFSET("relative", rlm_delay_t, relative), .dflt = "no" },
49 { FR_CONF_OFFSET("force_reschedule", rlm_delay_t, force_reschedule), .dflt = "no" },
50 { FR_CONF_OFFSET("rcode", rlm_delay_t, rcode),
51 .func = cf_table_parse_int, .uctx = &(cf_table_parse_ctx_t){ .table = rcode_table, .len = &rcode_table_len }, .dflt = "notset" },
53};
54
59
60/** Called when the timeout has expired
61 *
62 * Marks the request as resumable, and prints the delayed delay time.
63 */
64static void _delay_done(module_ctx_t const *mctx, request_t *request, fr_retry_t const *retry)
65{
66 rlm_delay_retry_t *yielded = talloc_get_type_abort(mctx->rctx, rlm_delay_retry_t);
67
68 RDEBUG2("Delay done");
69
70 /*
71 * timeout should never be *before* the scheduled time,
72 * if it is, something is very broken.
73 */
74 if (!fr_cond_assert(fr_time_gteq(retry->updated, yielded->when))) REDEBUG("Unexpected resume time");
75}
76
77static void _xlat_delay_done(xlat_ctx_t const *xctx, request_t *request, fr_time_t fired)
78{
79 fr_time_t *yielded = talloc_get_type_abort(xctx->rctx, fr_time_t);
80
81 RDEBUG2("Delay done");
82
83 /*
84 * timeout should never be *before* the scheduled time,
85 * if it is, something is very broken.
86 */
87 if (!fr_cond_assert(fr_time_gt(fired, *yielded))) REDEBUG("Unexpected resume time");
88
90}
91
92static int delay_add(request_t *request, fr_time_t *resume_at, fr_time_t now,
93 fr_time_delta_t delay, bool force_reschedule, bool relative)
94{
95 /*
96 * Delay is zero (and reschedule is not forced)
97 */
98 if (!force_reschedule && !fr_time_delta_ispos(delay)) return 1;
99
100 /*
101 * Process the delay relative to the start of packet processing
102 */
103 if (relative) {
104 *resume_at = fr_time_add(request->packet->timestamp, delay);
105 } else {
106 *resume_at = fr_time_add(now, delay);
107 }
108
109 /*
110 * If resume_at is in the past (and reschedule is not forced), just return noop
111 */
112 if (!force_reschedule && fr_time_lteq(*resume_at, now)) return 1;
113
114 if (fr_time_gt(*resume_at, now)) {
115 RDEBUG2("Delaying request by ~%pVs", fr_box_time_delta(fr_time_sub(*resume_at, now)));
116 } else {
117 RDEBUG2("Rescheduling request");
118 }
119
120 return 0;
121}
122
123/** Called resume_at the delay is complete, and we're running from the interpreter
124 *
125 */
127{
128 rlm_delay_retry_t *yielded = talloc_get_type_abort(mctx->rctx, rlm_delay_retry_t);
130
131 /*
132 * Print how long the delay *really* was.
133 */
134 RDEBUG3("Request delayed by %pV", fr_box_time_delta(fr_time_sub(fr_time(), yielded->when)));
135 talloc_free(yielded);
136
137 /*
138 * Be transparent, don't alter the rcode
139 */
142}
143
144static unlang_action_t CC_HINT(nonnull) mod_delay(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
145{
147 fr_time_delta_t delay;
148 rlm_delay_retry_t *yielded;
149 fr_time_t resume_at;
150
151 if (inst->delay) {
152 if (tmpl_aexpand_type(request, &delay, FR_TYPE_TIME_DELTA,
153 request, inst->delay) < 0) {
154 RPEDEBUG("Failed parsing %s as delay time", inst->delay->name);
156 }
157
158 /*
159 * Cap delays. So that no matter what crazy
160 * thing the admin does, it doesn't cause an
161 * issue.
162 */
163 if (fr_time_delta_cmp(delay, fr_time_delta_from_sec(0)) < 0) {
164 RWDEBUG("Delay is too small. Limiting to 0s");
165 delay = fr_time_delta_wrap(0);
166
167 } else if (fr_time_delta_cmp(delay, fr_time_delta_from_sec(30)) > 0) {
168 RWDEBUG("Delay is too large. Limiting to 30s");
169 delay = fr_time_delta_from_sec(30);
170 }
171
172 } else {
173 delay = fr_time_delta_wrap(0);
174 }
175
176 /*
177 * Record the time that we yielded the request
178 */
179 MEM(yielded = talloc(unlang_interpret_frame_talloc_ctx(request), rlm_delay_retry_t));
180 yielded->when = fr_time();
181
182 /*
183 * Setup the delay for this request
184 */
185 if (delay_add(request, &resume_at, yielded->when, delay,
186 inst->force_reschedule, inst->relative) != 0) {
188 }
189
190 RDEBUG3("Current time %pVs, resume time %pVs",
191 fr_box_time(yielded->when), fr_box_time(resume_at));
192
193 /*
194 *
195 */
196 yielded->retry_cfg = (fr_retry_config_t) {
197 .mrd = delay,
198 .mrc = 1,
199 };
200
202 yielded, &yielded->retry_cfg);
203}
204
206 xlat_ctx_t const *xctx,
207 request_t *request, UNUSED fr_value_box_list_t *in)
208{
209 fr_time_t *yielded_at = talloc_get_type_abort(xctx->rctx, fr_time_t);
210 fr_time_delta_t delayed;
211 fr_value_box_t *vb;
212
213 delayed = fr_time_sub(fr_time(), *yielded_at);
214 talloc_free(yielded_at);
215
216 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_TIME_DELTA, NULL));
217 vb->vb_time_delta = delayed;
218
219 RDEBUG3("Request delayed by %pVs", vb);
220
222
223 return XLAT_ACTION_DONE;
224}
225
226static void xlat_delay_cancel(UNUSED xlat_ctx_t const *xctx, request_t *request, UNUSED fr_signal_t action)
227{
228 RDEBUG2("Cancelling delay");
229}
230
232 { .single = true, .type = FR_TYPE_TIME_DELTA },
234};
235
236/** Xlat to delay the request
237 *
238 * Example (delay 2 seconds):
239@verbatim
240%delay(2)
241@endverbatim
242 *
243 * @ingroup xlat_functions
244 */
246 xlat_ctx_t const *xctx,
247 request_t *request, fr_value_box_list_t *in)
248{
249 rlm_delay_t const *inst = talloc_get_type_abort(xctx->mctx->mi->data, rlm_delay_t);
250 fr_time_t resume_at, *yielded_at;
251 fr_value_box_t *delay = fr_value_box_list_head(in);
252
253 /*
254 * Record the time that we yielded the request
255 */
256 MEM(yielded_at = talloc(request, fr_time_t));
257 *yielded_at = fr_time();
258
259 /*
260 * If there's no input delay, just yield and
261 * immediately re-enqueue the request.
262 * This is very useful for testing.
263 */
264 if (!delay) {
265 if (!fr_cond_assert(delay_add(request, &resume_at, *yielded_at, fr_time_delta_wrap(0), true, true) == 0)) {
266 return XLAT_ACTION_FAIL;
267 }
268 goto yield;
269 }
270
271 if (delay_add(request, &resume_at, *yielded_at, delay->vb_time_delta,
272 inst->force_reschedule, inst->relative) != 0) {
273 RDEBUG2("Not adding delay");
274 talloc_free(yielded_at);
275 return XLAT_ACTION_DONE;
276 }
277
278yield:
279 RDEBUG3("Current time %pVs, resume time %pVs", fr_box_time(*yielded_at), fr_box_time(resume_at));
280
281 if (unlang_xlat_timeout_add(request, _xlat_delay_done, yielded_at, resume_at) < 0) {
282 RPEDEBUG("Adding event failed");
283 return XLAT_ACTION_FAIL;
284 }
285
287}
288
289static int mod_bootstrap(module_inst_ctx_t const *mctx)
290{
291 xlat_t *xlat;
292
293 xlat = module_rlm_xlat_register(mctx->mi->boot, mctx, NULL, xlat_delay, FR_TYPE_TIME_DELTA);
295 return 0;
296}
297
300 .common = {
301 .magic = MODULE_MAGIC_INIT,
302 .name = "delay",
303 .flags = 0,
304 .inst_size = sizeof(rlm_delay_t),
306 .bootstrap = mod_bootstrap
307 },
308 .method_group = {
309 .bindings = (module_method_binding_t[]){
310 { .section = SECTION_NAME(CF_IDENT_ANY, CF_IDENT_ANY), .method = mod_delay },
312 }
313 }
314};
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition action.h:35
@ UNLANG_ACTION_EXECUTE_NEXT
Execute the next unlang_t.
Definition action.h:38
#define RCSID(id)
Definition build.h:485
#define UNUSED
Definition build.h:317
int cf_table_parse_int(UNUSED TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, conf_parser_t const *rule)
Generic function for parsing conf pair values as int.
Definition cf_parse.c:1592
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:658
cf_parse_t func
Override default parsing behaviour for the specified type with a custom parsing function.
Definition cf_parse.h:612
#define FR_CONF_OFFSET(_name, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition cf_parse.h:284
#define FR_CONF_OFFSET_HINT_TYPE(_name, _type, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition cf_parse.h:257
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:595
#define CF_IDENT_ANY
Definition cf_util.h:78
static int fr_dcursor_insert(fr_dcursor_t *cursor, void *v)
Insert directly after the current item.
Definition dcursor.h:437
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:139
#define MEM(x)
Definition debug.h:36
static fr_slen_t in
Definition dict.h:841
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition dl_module.h:63
static xlat_action_t xlat_delay(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in)
Xlat to delay the request.
Definition rlm_delay.c:245
void unlang_interpret_mark_runnable(request_t *request)
Mark a request as resumable.
Definition interpret.c:1616
TALLOC_CTX * unlang_interpret_frame_talloc_ctx(request_t *request)
Get a talloc_ctx which is valid only for this frame.
Definition interpret.c:1661
#define RWDEBUG(fmt,...)
Definition log.h:361
#define RDEBUG3(fmt,...)
Definition log.h:343
#define RPEDEBUG(fmt,...)
Definition log.h:376
talloc_free(reap)
@ FR_TYPE_TIME_DELTA
A period of time measured in nanoseconds.
module_instance_t const * mi
Instance of the module being instantiated.
Definition module_ctx.h:42
void * rctx
Resume ctx that a module previously set.
Definition module_ctx.h:45
module_instance_t * mi
Instance of the module being instantiated.
Definition module_ctx.h:51
Temporary structure to hold arguments for module calls.
Definition module_ctx.h:41
Temporary structure to hold arguments for instantiation calls.
Definition module_ctx.h:50
xlat_t * module_rlm_xlat_register(TALLOC_CTX *ctx, module_inst_ctx_t const *mctx, char const *name, xlat_func_t func, fr_type_t return_type)
Definition module_rlm.c:243
module_t common
Common fields presented by all modules.
Definition module_rlm.h:39
static const conf_parser_t config[]
Definition base.c:186
#define REDEBUG(fmt,...)
Definition radclient.h:52
#define RDEBUG2(fmt,...)
Definition radclient.h:54
size_t rcode_table_len
Definition rcode.c:48
fr_table_num_sorted_t const rcode_table[]
Definition rcode.c:35
#define RETURN_UNLANG_RCODE(_rcode)
Definition rcode.h:66
#define RETURN_UNLANG_FAIL
Definition rcode.h:57
rlm_rcode_t
Return codes indicating the result of the module call.
Definition rcode.h:40
@ RLM_MODULE_NOT_SET
Error resolving rcode (should not be returned by modules).
Definition rcode.h:52
#define RETURN_UNLANG_NOOP
Definition rcode.h:63
rlm_rcode_t rcode
The return code to use when the delay is complete.
Definition rlm_delay.c:40
tmpl_t * delay
How long we delay for.
Definition rlm_delay.c:37
bool relative
Whether the delay is relative to the start of request processing.
Definition rlm_delay.c:38
static unlang_action_t mod_delay_return(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Called resume_at the delay is complete, and we're running from the interpreter.
Definition rlm_delay.c:126
static int delay_add(request_t *request, fr_time_t *resume_at, fr_time_t now, fr_time_delta_t delay, bool force_reschedule, bool relative)
Definition rlm_delay.c:92
static int mod_bootstrap(module_inst_ctx_t const *mctx)
Definition rlm_delay.c:289
fr_retry_config_t retry_cfg
Definition rlm_delay.c:56
bool force_reschedule
Whether we should force rescheduling of the request.
Definition rlm_delay.c:39
static void _xlat_delay_done(xlat_ctx_t const *xctx, request_t *request, fr_time_t fired)
Definition rlm_delay.c:77
module_rlm_t rlm_delay
Definition rlm_delay.c:299
static void xlat_delay_cancel(UNUSED xlat_ctx_t const *xctx, request_t *request, UNUSED fr_signal_t action)
Definition rlm_delay.c:226
static xlat_action_t xlat_delay_resume(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, UNUSED fr_value_box_list_t *in)
Definition rlm_delay.c:205
static xlat_arg_parser_t const xlat_delay_args[]
Definition rlm_delay.c:231
fr_time_t when
Definition rlm_delay.c:57
static const conf_parser_t module_config[]
Definition rlm_delay.c:46
static unlang_action_t mod_delay(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_delay.c:144
static void _delay_done(module_ctx_t const *mctx, request_t *request, fr_retry_t const *retry)
Called when the timeout has expired.
Definition rlm_delay.c:64
#define SECTION_NAME(_name1, _name2)
Define a section name consisting of a verb and a noun.
Definition section.h:40
module_flags_t flags
Flags that control how a module starts up and how a module is called.
Definition module.h:236
void * data
Module's instance data.
Definition module.h:291
void * boot
Data allocated during the boostrap phase.
Definition module.h:294
#define MODULE_BINDING_TERMINATOR
Terminate a module binding list.
Definition module.h:152
Named methods exported by a module.
Definition module.h:174
#define tmpl_aexpand_type(_ctx, _out, _type, _request, _vpt)
Expand a tmpl to a C type, allocing a new buffer to hold the string.
Definition tmpl.h:1071
fr_signal_t
Signals that can be generated/processed by request signal handlers.
Definition signal.h:38
@ FR_SIGNAL_CANCEL
Request has been cancelled.
Definition signal.h:40
unlang_action_t unlang_module_yield_to_retry(request_t *request, module_method_t resume, unlang_module_retry_t retry, unlang_module_signal_t signal, fr_signal_t sigmask, void *rctx, fr_retry_config_t const *retry_cfg)
Yield a request back to the interpreter, with retries.
Definition module.c:372
eap_aka_sim_process_conf_t * inst
#define fr_time()
Allow us to arbitrarily manipulate time.
Definition state_test.c:8
#define talloc_get_type_abort_const
Definition talloc.h:287
#define fr_time_gteq(_a, _b)
Definition time.h:238
static int8_t fr_time_delta_cmp(fr_time_delta_t a, fr_time_delta_t b)
Compare two fr_time_delta_t values.
Definition time.h:930
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_lteq(_a, _b)
Definition time.h:240
#define fr_time_delta_ispos(_a)
Definition time.h:290
#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_sub(_a, _b)
Subtract one time from another.
Definition time.h:229
A time delta, a difference in time measured in nanoseconds.
Definition time.h:80
"server local" time.
Definition time.h:69
xlat_action_t unlang_xlat_yield(request_t *request, xlat_func_t resume, xlat_func_signal_t signal, fr_signal_t sigmask, void *rctx)
Yield a request back to the interpreter from within a module.
Definition xlat.c:560
int unlang_xlat_timeout_add(request_t *request, fr_unlang_xlat_timeout_t callback, void const *rctx, fr_time_t when)
Add a timeout for an xlat handler.
Definition xlat.c:151
uint8_t single
Argument must only contain a single box.
Definition xlat.h:148
#define XLAT_ARG_PARSER_TERMINATOR
Definition xlat.h:170
xlat_action_t
Definition xlat.h:37
@ XLAT_ACTION_FAIL
An xlat function failed.
Definition xlat.h:44
@ XLAT_ACTION_DONE
We're done evaluating this level of nesting.
Definition xlat.h:43
Definition for a single argument consumend by an xlat function.
Definition xlat.h:145
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:56
#define fr_value_box_alloc(_ctx, _type, _enumv)
Allocate a value box of a specific type.
Definition value.h:640
#define fr_box_time_delta(_val)
Definition value.h:362
int nonnull(2, 5))
#define fr_box_time(_val)
Definition value.h:345
static size_t char ** out
Definition value.h:1020
void * rctx
Resume context.
Definition xlat_ctx.h:54
module_ctx_t const * mctx
Synthesised module calling ctx.
Definition xlat_ctx.h:52
An xlat calling ctx.
Definition xlat_ctx.h:49
int xlat_func_args_set(xlat_t *x, xlat_arg_parser_t const args[])
Register the arguments of an xlat.
Definition xlat_func.c:363