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: 47dca3e3f3fcc657ab01243d590bec7e235240c9 $
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: 47dca3e3f3fcc657ab01243d590bec7e235240c9 $")
26
27#include <freeradius-devel/server/base.h>
28#include <freeradius-devel/server/module_rlm.h>
29#include <freeradius-devel/util/debug.h>
30#include <freeradius-devel/unlang/xlat_func.h>
31
32typedef struct {
33 tmpl_t *delay; //!< How long we delay for.
34 bool relative; //!< Whether the delay is relative to the start of request processing.
35 bool force_reschedule; //!< Whether we should force rescheduling of the request.
36 rlm_rcode_t rcode; //!< The return code to use when the delay is complete.
38
39/*
40 * A mapping of configuration file names to internal variables.
41 */
42static const conf_parser_t module_config[] = {
44 { FR_CONF_OFFSET("relative", rlm_delay_t, relative), .dflt = "no" },
45 { FR_CONF_OFFSET("force_reschedule", rlm_delay_t, force_reschedule), .dflt = "no" },
46 { FR_CONF_OFFSET("rcode", rlm_delay_t, rcode),
47 .func = cf_table_parse_int, .uctx = &(cf_table_parse_ctx_t){ .table = rcode_table, .len = &rcode_table_len }, .dflt = "notset" },
49};
50
55
56/** Called when the timeout has expired
57 *
58 * Marks the request as resumable, and prints the delayed delay time.
59 */
60static void _delay_done(module_ctx_t const *mctx, request_t *request, fr_retry_t const *retry)
61{
62 rlm_delay_retry_t *yielded = talloc_get_type_abort(mctx->rctx, rlm_delay_retry_t);
63
64 RDEBUG2("Delay done");
65
66 /*
67 * timeout should never be *before* the scheduled time,
68 * if it is, something is very broken.
69 */
70 if (!fr_cond_assert(fr_time_gteq(retry->updated, yielded->when))) REDEBUG("Unexpected resume time");
71}
72
73static void _xlat_delay_done(xlat_ctx_t const *xctx, request_t *request, fr_time_t fired)
74{
75 fr_time_t *yielded = talloc_get_type_abort(xctx->rctx, fr_time_t);
76
77 RDEBUG2("Delay done");
78
79 /*
80 * timeout should never be *before* the scheduled time,
81 * if it is, something is very broken.
82 */
83 if (!fr_cond_assert(fr_time_gt(fired, *yielded))) REDEBUG("Unexpected resume time");
84
86}
87
88static int delay_add(request_t *request, fr_time_t *resume_at, fr_time_t now,
89 fr_time_delta_t delay, bool force_reschedule, bool relative)
90{
91 /*
92 * Delay is zero (and reschedule is not forced)
93 */
94 if (!force_reschedule && !fr_time_delta_ispos(delay)) return 1;
95
96 /*
97 * Process the delay relative to the start of packet processing
98 */
99 if (relative) {
100 *resume_at = fr_time_add(request->packet->timestamp, delay);
101 } else {
102 *resume_at = fr_time_add(now, delay);
103 }
104
105 /*
106 * If resume_at is in the past (and reschedule is not forced), just return noop
107 */
108 if (!force_reschedule && fr_time_lteq(*resume_at, now)) return 1;
109
110 if (fr_time_gt(*resume_at, now)) {
111 RDEBUG2("Delaying request by ~%pVs", fr_box_time_delta(fr_time_sub(*resume_at, now)));
112 } else {
113 RDEBUG2("Rescheduling request");
114 }
115
116 return 0;
117}
118
119/** Called resume_at the delay is complete, and we're running from the interpreter
120 *
121 */
123{
124 rlm_delay_retry_t *yielded = talloc_get_type_abort(mctx->rctx, rlm_delay_retry_t);
126
127 /*
128 * Print how long the delay *really* was.
129 */
130 RDEBUG3("Request delayed by %pV", fr_box_time_delta(fr_time_sub(fr_time(), yielded->when)));
131 talloc_free(yielded);
132
133 /*
134 * Be transparent, don't alter the rcode
135 */
138}
139
140static unlang_action_t CC_HINT(nonnull) mod_delay(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
141{
143 fr_time_delta_t delay;
144 rlm_delay_retry_t *yielded;
145 fr_time_t resume_at;
146
147 if (inst->delay) {
148 if (tmpl_aexpand_type(request, &delay, FR_TYPE_TIME_DELTA,
149 request, inst->delay) < 0) {
150 RPEDEBUG("Failed parsing %s as delay time", inst->delay->name);
152 }
153
154 /*
155 * Cap delays. So that no matter what crazy
156 * thing the admin does, it doesn't cause an
157 * issue.
158 */
159 if (fr_time_delta_cmp(delay, fr_time_delta_from_sec(0)) < 0) {
160 RWDEBUG("Delay is too small. Limiting to 0s");
161 delay = fr_time_delta_wrap(0);
162
163 } else if (fr_time_delta_cmp(delay, fr_time_delta_from_sec(30)) > 0) {
164 RWDEBUG("Delay is too large. Limiting to 30s");
165 delay = fr_time_delta_from_sec(30);
166 }
167
168 } else {
169 delay = fr_time_delta_wrap(0);
170 }
171
172 /*
173 * Record the time that we yielded the request
174 */
175 MEM(yielded = talloc(unlang_interpret_frame_talloc_ctx(request), rlm_delay_retry_t));
176 yielded->when = fr_time();
177
178 /*
179 * Setup the delay for this request
180 */
181 if (delay_add(request, &resume_at, yielded->when, delay,
182 inst->force_reschedule, inst->relative) != 0) {
184 }
185
186 RDEBUG3("Current time %pVs, resume time %pVs",
187 fr_box_time(yielded->when), fr_box_time(resume_at));
188
189 /*
190 *
191 */
192 yielded->retry_cfg = (fr_retry_config_t) {
193 .mrd = delay,
194 .mrc = 1,
195 };
196
198 yielded, &yielded->retry_cfg);
199}
200
202 xlat_ctx_t const *xctx,
203 request_t *request, UNUSED fr_value_box_list_t *in)
204{
205 fr_time_t *yielded_at = talloc_get_type_abort(xctx->rctx, fr_time_t);
206 fr_time_delta_t delayed;
207 fr_value_box_t *vb;
208
209 delayed = fr_time_sub(fr_time(), *yielded_at);
210 talloc_free(yielded_at);
211
212 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_TIME_DELTA, NULL));
213 vb->vb_time_delta = delayed;
214
215 RDEBUG3("Request delayed by %pVs", vb);
216
218
219 return XLAT_ACTION_DONE;
220}
221
222static void xlat_delay_cancel(UNUSED xlat_ctx_t const *xctx, request_t *request, UNUSED fr_signal_t action)
223{
224 RDEBUG2("Cancelling delay");
225}
226
228 { .single = true, .type = FR_TYPE_TIME_DELTA },
230};
231
232/** Xlat to delay the request
233 *
234 * Example (delay 2 seconds):
235@verbatim
236%delay(2)
237@endverbatim
238 *
239 * @ingroup xlat_functions
240 */
242 xlat_ctx_t const *xctx,
243 request_t *request, fr_value_box_list_t *in)
244{
245 rlm_delay_t const *inst = talloc_get_type_abort(xctx->mctx->mi->data, rlm_delay_t);
246 fr_time_t resume_at, *yielded_at;
247 fr_value_box_t *delay = fr_value_box_list_head(in);
248
249 /*
250 * Record the time that we yielded the request
251 */
252 MEM(yielded_at = talloc(request, fr_time_t));
253 *yielded_at = fr_time();
254
255 /*
256 * If there's no input delay, just yield and
257 * immediately re-enqueue the request.
258 * This is very useful for testing.
259 */
260 if (!delay) {
261 if (!fr_cond_assert(delay_add(request, &resume_at, *yielded_at, fr_time_delta_wrap(0), true, true) == 0)) {
262 return XLAT_ACTION_FAIL;
263 }
264 goto yield;
265 }
266
267 if (delay_add(request, &resume_at, *yielded_at, delay->vb_time_delta,
268 inst->force_reschedule, inst->relative) != 0) {
269 RDEBUG2("Not adding delay");
270 talloc_free(yielded_at);
271 return XLAT_ACTION_DONE;
272 }
273
274yield:
275 RDEBUG3("Current time %pVs, resume time %pVs", fr_box_time(*yielded_at), fr_box_time(resume_at));
276
277 if (unlang_xlat_timeout_add(request, _xlat_delay_done, yielded_at, resume_at) < 0) {
278 RPEDEBUG("Adding event failed");
279 return XLAT_ACTION_FAIL;
280 }
281
283}
284
285static int mod_bootstrap(module_inst_ctx_t const *mctx)
286{
287 xlat_t *xlat;
288
289 xlat = module_rlm_xlat_register(mctx->mi->boot, mctx, NULL, xlat_delay, FR_TYPE_TIME_DELTA);
291 return 0;
292}
293
296 .common = {
297 .magic = MODULE_MAGIC_INIT,
298 .name = "delay",
299 .flags = 0,
300 .inst_size = sizeof(rlm_delay_t),
302 .bootstrap = mod_bootstrap
303 },
304 .method_group = {
305 .bindings = (module_method_binding_t[]){
306 { .section = SECTION_NAME(CF_IDENT_ANY, CF_IDENT_ANY), .method = mod_delay },
308 }
309 }
310};
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:506
#define UNUSED
Definition build.h:336
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:1635
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:657
cf_parse_t func
Override default parsing behaviour for the specified type with a custom parsing function.
Definition cf_parse.h:611
#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:280
#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:253
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:594
#define CF_IDENT_ANY
Definition cf_util.h:75
static int fr_dcursor_insert(fr_dcursor_t *cursor, void *v)
Insert directly after the current item.
Definition dcursor.h:435
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:141
#define MEM(x)
Definition debug.h:46
static fr_slen_t in
Definition dict.h:882
#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:241
talloc_free(hp)
void unlang_interpret_mark_runnable(request_t *request)
Mark a request as resumable.
Definition interpret.c:1636
TALLOC_CTX * unlang_interpret_frame_talloc_ctx(request_t *request)
Get a talloc_ctx which is valid only for this frame.
Definition interpret.c:1681
#define RWDEBUG(fmt,...)
Definition log.h:373
#define RDEBUG3(fmt,...)
Definition log.h:355
#define RPEDEBUG(fmt,...)
Definition log.h:388
#define fr_time()
Definition event.c:60
@ 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:247
module_t common
Common fields presented by all modules.
Definition module_rlm.h:39
static const conf_parser_t config[]
Definition base.c:163
#define REDEBUG(fmt,...)
#define RDEBUG2(fmt,...)
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:61
#define RETURN_UNLANG_FAIL
Definition rcode.h:63
rlm_rcode_t
Return codes indicating the result of the module call.
Definition rcode.h:44
@ RLM_MODULE_NOT_SET
Error resolving rcode (should not be returned by modules).
Definition rcode.h:45
#define RETURN_UNLANG_NOOP
Definition rcode.h:69
rlm_rcode_t rcode
The return code to use when the delay is complete.
Definition rlm_delay.c:36
tmpl_t * delay
How long we delay for.
Definition rlm_delay.c:33
bool relative
Whether the delay is relative to the start of request processing.
Definition rlm_delay.c:34
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:122
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:88
static int mod_bootstrap(module_inst_ctx_t const *mctx)
Definition rlm_delay.c:285
fr_retry_config_t retry_cfg
Definition rlm_delay.c:52
bool force_reschedule
Whether we should force rescheduling of the request.
Definition rlm_delay.c:35
static void _xlat_delay_done(xlat_ctx_t const *xctx, request_t *request, fr_time_t fired)
Definition rlm_delay.c:73
module_rlm_t rlm_delay
Definition rlm_delay.c:295
static void xlat_delay_cancel(UNUSED xlat_ctx_t const *xctx, request_t *request, UNUSED fr_signal_t action)
Definition rlm_delay.c:222
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:201
static xlat_arg_parser_t const xlat_delay_args[]
Definition rlm_delay.c:227
fr_time_t when
Definition rlm_delay.c:53
static const conf_parser_t module_config[]
Definition rlm_delay.c:42
static unlang_action_t mod_delay(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_delay.c:140
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:60
#define SECTION_NAME(_name1, _name2)
Define a section name consisting of a verb and a noun.
Definition section.h:39
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:293
void * boot
Data allocated during the boostrap phase.
Definition module.h:296
#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:1073
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:369
eap_aka_sim_process_conf_t * inst
#define talloc_get_type_abort_const
Definition talloc.h:110
#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:543
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:150
unsigned int 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 consumed 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:644
#define fr_box_time_delta(_val)
Definition value.h:366
int nonnull(2, 5))
#define fr_box_time(_val)
Definition value.h:349
static size_t char ** out
Definition value.h:1030
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